docs: create docs workspace (#5174)
* docs: migrate ui docs to docs universe * created yarn workspace * added eslint and tsconfig configurations * fix eslint configurations * fixed eslint configurations * shared tailwind configurations * added shared ui package * added more shared components * migrating more components * made details components shared * move InlineCode component * moved InputText * moved Loading component * Moved Modal component * moved Select components * Moved Tooltip component * moved Search components * moved ColorMode provider * Moved Notification components and providers * used icons package * use UI colors in api-reference * moved Navbar component * used Navbar and Search in UI docs * added Feedback to UI docs * general enhancements * fix color mode * added copy colors file from ui-preset * added features and enhancements to UI docs * move Sidebar component and provider * general fixes and preparations for deployment * update docusaurus version * adjusted versions * fix output directory * remove rootDirectory property * fix yarn.lock * moved code component * added vale for all docs MD and MDX * fix tests * fix vale error * fix deployment errors * change ignore commands * add output directory * fix docs test * general fixes * content fixes * fix announcement script * added changeset * fix vale checks * added nofilter option * fix vale error
This commit is contained in:
171
www/apps/ui/src/components/colors.tsx
Normal file
171
www/apps/ui/src/components/colors.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
"use client"
|
||||
|
||||
import { Copy, clx } from "@medusajs/ui"
|
||||
import React from "react"
|
||||
import { useColorMode } from "docs-ui"
|
||||
import { colors as allColors } from "../config/colors"
|
||||
|
||||
type Color = {
|
||||
name: string
|
||||
code: string
|
||||
}
|
||||
|
||||
type ColorsTable = {
|
||||
backgrounds: Color[]
|
||||
foregrounds: Color[]
|
||||
borders: Color[]
|
||||
buttons: Color[]
|
||||
code: Color[]
|
||||
tags: Color[]
|
||||
}
|
||||
|
||||
const PREFIXES: { [k: string]: keyof ColorsTable } = {
|
||||
"--bg": "backgrounds",
|
||||
"--fg": "foregrounds",
|
||||
"--border": "borders",
|
||||
"--button": "buttons",
|
||||
"--code": "code",
|
||||
"--tag": "tags",
|
||||
}
|
||||
|
||||
interface ColorBlockProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
colour: Color
|
||||
}
|
||||
|
||||
const ColorBlock = ({ colour, className, ...props }: ColorBlockProps) => {
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
|
||||
React.useEffect(() => setMounted(true), [])
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<div className="flex w-fit flex-row items-center gap-x-2">
|
||||
<div
|
||||
className={
|
||||
"border-medusa-border-base dark:border-medusa-border-base-dark h-[48px] w-[48px] rounded-lg border p-1"
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={clx(
|
||||
"bg-medusa-bg-component dark:bg-medusa-bg-component-dark h-full w-full animate-pulse rounded-[4px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="bg-medusa-bg-component dark:bg-medusa-bg-component-dark h-[20px] w-[85px] animate-pulse rounded-sm" />
|
||||
<div className="bg-medusa-bg-subtle dark:bg-medusa-bg-subtle-dark h-[20px] w-[120px] animate-pulse rounded-sm" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-x-2">
|
||||
<div
|
||||
className={
|
||||
"border-medusa-border-base dark:border-medusa-border-base-dark h-[48px] w-[48px] rounded-lg border p-1"
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={clx("h-full w-full rounded-[4px]", className)}
|
||||
style={{ background: colour.code }}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<p className="txt-compact-xsmall-plus text-medusa-fg-base dark:text-medusa-fg-base-dark text-start">
|
||||
{cssVarToTailwindClass(colour.name)}
|
||||
</p>
|
||||
<p className="txt-compact-xsmall text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark">
|
||||
{colour.code}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const cssVarToTailwindClass = (name: string) => {
|
||||
if (name.startsWith("--bg") || name.startsWith("--button")) {
|
||||
return name.replace("-", "bg-ui")
|
||||
}
|
||||
|
||||
if (name.startsWith("--fg")) {
|
||||
return name.replace("-", "text-ui")
|
||||
}
|
||||
|
||||
if (name.startsWith("--border")) {
|
||||
return name.replace("-", "border-ui")
|
||||
}
|
||||
|
||||
if (name.startsWith("--tag") || name.startsWith("--code")) {
|
||||
if (name.includes("bg")) {
|
||||
return name.replace("-", "bg-ui")
|
||||
}
|
||||
if (name.includes("border")) {
|
||||
return name.replace("-", "border-ui")
|
||||
}
|
||||
if (name.includes("icon") || name.includes("text")) {
|
||||
return name.replace("-", "text-ui")
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
const Colors = () => {
|
||||
const { colorMode } = useColorMode()
|
||||
|
||||
const colors: ColorsTable = {
|
||||
backgrounds: [],
|
||||
foregrounds: [],
|
||||
borders: [],
|
||||
buttons: [],
|
||||
code: [],
|
||||
tags: [],
|
||||
}
|
||||
|
||||
for (const [tag, value] of Object.entries(allColors[colorMode])) {
|
||||
const prefix = tag.match(/(--[a-zA-Z]+)/gi)
|
||||
if (prefix && Object.keys(PREFIXES).includes(prefix[0])) {
|
||||
if (!tag.includes("gradient")) {
|
||||
colors[PREFIXES[prefix[0]]].push({
|
||||
name: tag,
|
||||
code: value as string,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [, section] of Object.entries(colors)) {
|
||||
section.sort((a, b) => {
|
||||
return a.name < b.name ? -1 : 1
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{Object.entries(colors).map(([section, colors]) => (
|
||||
<div className="mb-16" key={`colours-section-${section}`}>
|
||||
<h2 className="h2-docs mb-4 mt-10 text-medusa-fg-base dark:text-medusa-fg-base-dark">
|
||||
{section.charAt(0).toUpperCase() + section.slice(1)}
|
||||
</h2>
|
||||
<hr className="mb-4" />
|
||||
<div className="xs:grid-cols-2 mb-8 grid grid-cols-1 gap-4 gap-y-10 sm:grid-cols-3 ">
|
||||
{colors.map((colour) => (
|
||||
<Copy
|
||||
content={cssVarToTailwindClass(colour.name)}
|
||||
key={`colours-section-${section}-${colour.name}`}
|
||||
>
|
||||
<ColorBlock colour={colour} />
|
||||
</Copy>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Colors }
|
||||
72
www/apps/ui/src/components/component-example.tsx
Normal file
72
www/apps/ui/src/components/component-example.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
"use client"
|
||||
|
||||
import { Spinner } from "@medusajs/icons"
|
||||
import * as React from "react"
|
||||
import { ExampleRegistry } from "../registries/example-registry"
|
||||
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/tabs"
|
||||
import { Feedback } from "./feedback"
|
||||
import clsx from "clsx"
|
||||
import { CodeBlock } from "docs-ui"
|
||||
|
||||
interface ComponentExampleProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: string
|
||||
}
|
||||
|
||||
export function ComponentExample({
|
||||
children,
|
||||
name,
|
||||
...props
|
||||
}: ComponentExampleProps) {
|
||||
const Preview = React.useMemo(() => {
|
||||
const Component = ExampleRegistry[name]?.component
|
||||
|
||||
if (!Component) {
|
||||
return <p>Component {name} not found in registry</p>
|
||||
}
|
||||
|
||||
return <Component />
|
||||
}, [name])
|
||||
|
||||
const CodeElement = children as React.ReactElement
|
||||
const Code = CodeElement.props.code
|
||||
|
||||
return (
|
||||
<div className="relative my-4 flex flex-col space-y-2" {...props}>
|
||||
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||
<div className="flex flex-col items-center justify-between pb-3">
|
||||
<TabsList className="">
|
||||
<TabsTrigger value="preview">Preview</TabsTrigger>
|
||||
<TabsTrigger value="code">Code</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="preview" className="relative">
|
||||
<div
|
||||
className={clsx(
|
||||
"bg-docs-bg border-medusa-border-base flex max-h-[400px] min-h-[400px]",
|
||||
"dark:bg-docs-bg-dark dark:border-medusa-border-base-dark",
|
||||
"w-full items-center justify-center overflow-auto rounded-md border px-10 py-5"
|
||||
)}
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark flex items-center text-sm">
|
||||
<Spinner className="animate-spin" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Preview}
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="code" className="relative ">
|
||||
<CodeBlock source={Code} lang="tsx" />
|
||||
</TabsContent>
|
||||
</div>
|
||||
</Tabs>
|
||||
<Feedback
|
||||
title={`example ${name}`}
|
||||
question="Was this example helpful?"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
48
www/apps/ui/src/components/component-props.tsx
Normal file
48
www/apps/ui/src/components/component-props.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Spinner } from "@medusajs/icons"
|
||||
import { Container } from "@medusajs/ui"
|
||||
import * as React from "react"
|
||||
|
||||
import { PropRegistry } from "@/registries/prop-registry"
|
||||
import { Feedback } from "./feedback"
|
||||
|
||||
type ComponentPropsProps = {
|
||||
component: string
|
||||
}
|
||||
|
||||
const ComponentProps = ({ component }: ComponentPropsProps) => {
|
||||
const Props = React.useMemo(() => {
|
||||
const Table = PropRegistry[component]?.table
|
||||
|
||||
if (!Table) {
|
||||
return (
|
||||
<div className="flex min-h-[200px] w-full items-center justify-center">
|
||||
<p className="txt-compact-small">
|
||||
No API reference found for{" "}
|
||||
<span className="txt-compact-small-plus">{component}</span>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <Table />
|
||||
}, [component])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container className="mb-6 mt-8 overflow-hidden p-0">
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark flex flex-1 items-center justify-center">
|
||||
<Spinner className="animate-spin" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Props}
|
||||
</React.Suspense>
|
||||
</Container>
|
||||
<Feedback title={`props of ${component}`} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { ComponentProps }
|
||||
34
www/apps/ui/src/components/feedback.tsx
Normal file
34
www/apps/ui/src/components/feedback.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
Feedback as UiFeedback,
|
||||
FeedbackProps as UiFeedbackProps,
|
||||
formatReportLink,
|
||||
} from "docs-ui"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { absoluteUrl } from "@/lib/absolute-url"
|
||||
import clsx from "clsx"
|
||||
|
||||
export type FeedbackProps = {
|
||||
title: string
|
||||
} & Partial<UiFeedbackProps>
|
||||
|
||||
export const Feedback = ({ title, ...props }: FeedbackProps) => {
|
||||
const pathname = usePathname()
|
||||
|
||||
return (
|
||||
<UiFeedback
|
||||
event="survey"
|
||||
pathName={absoluteUrl(pathname)}
|
||||
reportLink={formatReportLink("UI Docs", title)}
|
||||
extraData={{
|
||||
section: title,
|
||||
}}
|
||||
{...props}
|
||||
className={clsx(
|
||||
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
6
www/apps/ui/src/components/figma-icon.tsx
Normal file
6
www/apps/ui/src/components/figma-icon.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { BorderedIcon } from "docs-ui"
|
||||
import { basePathUrl } from "@/lib/base-path-url"
|
||||
|
||||
export const FigmaIcon = () => {
|
||||
return <BorderedIcon icon={basePathUrl("/images/figma.png")} />
|
||||
}
|
||||
94
www/apps/ui/src/components/hook-table.tsx
Normal file
94
www/apps/ui/src/components/hook-table.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import { InformationCircleSolid } from "@medusajs/icons"
|
||||
import { Table, Tooltip } from "@medusajs/ui"
|
||||
|
||||
import { HookData, HookDataMap } from "@/types/hooks"
|
||||
import { EnumType, FunctionType, ObjectType } from "@/types/props"
|
||||
|
||||
const HookTable = ({ props }: { props: HookDataMap }) => {
|
||||
return (
|
||||
<Table>
|
||||
<Table.Header className="border-t-0">
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Value</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body className="border-b-0 [&_tr:last-child]:border-b-0">
|
||||
{/* eslint-disable-next-line react/prop-types */}
|
||||
{props.map((propData, index) => (
|
||||
<Row key={index} {...propData} />
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
const Row = ({ value, type, description }: HookData) => {
|
||||
const isEnum = (t: unknown): t is EnumType => {
|
||||
return (t as EnumType).type !== undefined && (t as EnumType).type === "enum"
|
||||
}
|
||||
|
||||
const isObject = (t: unknown): t is ObjectType => {
|
||||
return (
|
||||
(t as ObjectType).type !== undefined &&
|
||||
(t as ObjectType).type === "object"
|
||||
)
|
||||
}
|
||||
|
||||
const isFunction = (t: unknown): t is FunctionType => {
|
||||
return (
|
||||
(t as FunctionType).type !== undefined &&
|
||||
(t as FunctionType).type === "function"
|
||||
)
|
||||
}
|
||||
|
||||
const isComplexType = isEnum(type) || isObject(type) || isFunction(type)
|
||||
|
||||
return (
|
||||
<Table.Row className="code-body">
|
||||
<Table.Cell>{value}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{!isComplexType && type.toString()}
|
||||
{isEnum(type) && (
|
||||
<Tooltip
|
||||
content={type.values.map((v) => `"${v}"`).join(" | ")}
|
||||
className="font-mono"
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span>enum</span>
|
||||
<InformationCircleSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isObject(type) && (
|
||||
<Tooltip
|
||||
content={<pre>{type.shape}</pre>}
|
||||
className="font-mono"
|
||||
maxWidth={500}
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span>{type.name}</span>
|
||||
<InformationCircleSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isFunction(type) && (
|
||||
<Tooltip
|
||||
content={<pre>{type.signature}</pre>}
|
||||
className="font-mono"
|
||||
maxWidth={500}
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span>function</span>
|
||||
<InformationCircleSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{description}</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
}
|
||||
|
||||
export { HookTable }
|
||||
48
www/apps/ui/src/components/hook-values.tsx
Normal file
48
www/apps/ui/src/components/hook-values.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Spinner } from "@medusajs/icons"
|
||||
import { Container } from "@medusajs/ui"
|
||||
import * as React from "react"
|
||||
|
||||
import { HookRegistry } from "@/registries/hook-registry"
|
||||
import { Feedback } from "./feedback"
|
||||
|
||||
type HookValuesProps = {
|
||||
hook: string
|
||||
}
|
||||
|
||||
const HookValues = ({ hook }: HookValuesProps) => {
|
||||
const Props = React.useMemo(() => {
|
||||
const Table = HookRegistry[hook]?.table
|
||||
|
||||
if (!Table) {
|
||||
return (
|
||||
<div className="flex min-h-[200px] w-full items-center justify-center">
|
||||
<p className="txt-compact-small">
|
||||
No API reference found for{" "}
|
||||
<span className="txt-compact-small-plus">{hook}</span>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <Table />
|
||||
}, [hook])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container className="mb-6 mt-8 overflow-hidden p-0">
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark flex flex-1 items-center justify-center">
|
||||
<Spinner className="animate-spin" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Props}
|
||||
</React.Suspense>
|
||||
</Container>
|
||||
<Feedback title={`props of ${hook}`} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { HookValues }
|
||||
94
www/apps/ui/src/components/icon-search.tsx
Normal file
94
www/apps/ui/src/components/icon-search.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
"use client"
|
||||
|
||||
import * as Icons from "@medusajs/icons"
|
||||
import { Container, Input, Text, Tooltip } from "@medusajs/ui"
|
||||
import clsx from "clsx"
|
||||
import * as React from "react"
|
||||
|
||||
const iconNames = Object.keys(Icons).filter((name) => name !== "default")
|
||||
|
||||
const IconSearch = React.memo(function IconSearch() {
|
||||
const [query, setQuery] = React.useState<string | undefined>("")
|
||||
|
||||
return (
|
||||
<div className="mt-8 flex flex-col gap-y-2">
|
||||
<Input
|
||||
type="search"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<Container>
|
||||
<SearchResults query={query} />
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const SearchResults = ({ query = "" }: { query?: string }) => {
|
||||
const cleanQuery = escapeStringRegexp(query.trim().replace(/\s/g, " "))
|
||||
const results = iconNames.filter((name) =>
|
||||
new RegExp(`\\b${cleanQuery}`, "gi").test(name)
|
||||
)
|
||||
|
||||
if (results.length === 0) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"text-medusa-text-muted dark:text-medusa-text-muted-dark",
|
||||
"flex min-h-[300px] items-center justify-center"
|
||||
)}
|
||||
>
|
||||
<Text>
|
||||
No results found for{" "}
|
||||
<Text weight={"plus"} asChild>
|
||||
<span>{query}</span>
|
||||
</Text>
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid w-full grid-cols-4 gap-8 md:grid-cols-6 lg:grid-cols-8">
|
||||
{results.map((name) => {
|
||||
return (
|
||||
<div
|
||||
key={name}
|
||||
className="flex h-full w-full items-center justify-center"
|
||||
>
|
||||
<Tooltip content={name}>
|
||||
<div
|
||||
className={clsx(
|
||||
"border-medusa-border-base dark:border-medusa-border-base-dark",
|
||||
"flex h-10 w-10 items-center justify-center rounded-lg border"
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Icon named {name}</span>
|
||||
<div
|
||||
className={clsx(
|
||||
"bg-medusa-bg-component text-medusa-fg-base",
|
||||
"dark:bg-medusa-bg-component-dark dark:text-medusa-fg-base-dark",
|
||||
"flex h-8 w-8 items-center justify-center rounded-[4px]"
|
||||
)}
|
||||
>
|
||||
{React.createElement(Icons[name as keyof typeof Icons])}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// https://github.com/sindresorhus/escape-string-regexp/blob/main/index.js
|
||||
function escapeStringRegexp(string: unknown) {
|
||||
if (typeof string !== "string") {
|
||||
throw new TypeError("Expected a string")
|
||||
}
|
||||
|
||||
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d")
|
||||
}
|
||||
|
||||
export { IconSearch }
|
||||
150
www/apps/ui/src/components/mdx-components.tsx
Normal file
150
www/apps/ui/src/components/mdx-components.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
"use client"
|
||||
|
||||
import { Text, clx } from "@medusajs/ui"
|
||||
import { useMDXComponent } from "next-contentlayer/hooks"
|
||||
import * as React from "react"
|
||||
|
||||
import { Colors } from "@/components/colors"
|
||||
import { ComponentExample } from "@/components/component-example"
|
||||
import { ComponentProps } from "@/components/component-props"
|
||||
import { HookValues } from "@/components/hook-values"
|
||||
import { IconSearch } from "@/components/icon-search"
|
||||
import { PackageInstall } from "@/components/package-install"
|
||||
import { Feedback } from "@/components/feedback"
|
||||
import { FigmaIcon } from "@/components/figma-icon"
|
||||
import clsx from "clsx"
|
||||
import { NextLink, Card, BorderedIcon, CodeMdx, CodeBlock } from "docs-ui"
|
||||
|
||||
interface MdxProps {
|
||||
code: string
|
||||
}
|
||||
|
||||
const components = {
|
||||
h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => {
|
||||
return (
|
||||
<h1
|
||||
className={clx(
|
||||
"h1-docs text-medusa-fg-base dark:text-medusa-fg-base-dark",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => {
|
||||
return (
|
||||
<h2
|
||||
className={clx(
|
||||
"h2-docs mb-4 mt-16 text-medusa-fg-base dark:text-medusa-fg-base-dark",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => {
|
||||
return (
|
||||
<h3
|
||||
className={clx(
|
||||
"h3-docs mb-2 mt-10 text-medusa-fg-base dark:text-medusa-fg-base-dark",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
p: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => {
|
||||
return (
|
||||
<Text
|
||||
className={clx(
|
||||
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark mb-docs_1.5",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
a: ({
|
||||
className,
|
||||
href,
|
||||
...props
|
||||
}: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
||||
const isInternal = href && href?.startsWith("/")
|
||||
|
||||
if (isInternal) {
|
||||
return <NextLink className={className} href={href} {...props} />
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className={clx(
|
||||
"txt-medium text-medusa-fg-interactive hover:text-medusa-fg-interactive-hover",
|
||||
"dark:text-medusa-fg-interactive-dark dark:hover:text-medusa-fg-interactive-hover-dark",
|
||||
className
|
||||
)}
|
||||
href={href}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
code: CodeMdx,
|
||||
ul: ({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLUListElement>) => {
|
||||
return (
|
||||
<ul
|
||||
{...props}
|
||||
className={clsx("list-disc px-docs_1 mb-docs_1.5", className)}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
)
|
||||
},
|
||||
li: ({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLElement>) => {
|
||||
return (
|
||||
<li
|
||||
className={clx(
|
||||
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Text>{children}</Text>
|
||||
</li>
|
||||
)
|
||||
},
|
||||
hr: ({ className, ...props }: React.HTMLAttributes<HTMLHRElement>) => {
|
||||
return <hr className={clx("mb-4", className)} {...props} />
|
||||
},
|
||||
HookValues,
|
||||
ComponentProps,
|
||||
CodeBlock,
|
||||
ComponentExample,
|
||||
PackageInstall,
|
||||
IconSearch,
|
||||
Feedback,
|
||||
Colors,
|
||||
Card,
|
||||
BorderedIcon,
|
||||
FigmaIcon,
|
||||
}
|
||||
|
||||
const Mdx = ({ code }: MdxProps) => {
|
||||
const Component = useMDXComponent(code)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Component components={components} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Mdx }
|
||||
26
www/apps/ui/src/components/navbar.tsx
Normal file
26
www/apps/ui/src/components/navbar.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import { docsConfig } from "@/config/docs"
|
||||
import { Navbar as UiNavbar, useSidebar } from "docs-ui"
|
||||
import { basePathUrl } from "@/lib/base-path-url"
|
||||
|
||||
const Navbar = () => {
|
||||
const { mobileSidebarOpen, setMobileSidebarOpen } = useSidebar()
|
||||
|
||||
return (
|
||||
<UiNavbar
|
||||
logo={{
|
||||
light: basePathUrl("/images/logo-icon.png"),
|
||||
dark: basePathUrl("/images/logo-icon-dark.png"),
|
||||
}}
|
||||
items={docsConfig.mainNav}
|
||||
mobileMenuButton={{
|
||||
setMobileSidebarOpen,
|
||||
mobileSidebarOpen,
|
||||
}}
|
||||
className="!z-[99]"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Navbar }
|
||||
35
www/apps/ui/src/components/package-install.tsx
Normal file
35
www/apps/ui/src/components/package-install.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { clx } from "@medusajs/ui"
|
||||
import { CodeTabs } from "docs-ui"
|
||||
|
||||
type PackageInstallProps = {
|
||||
packageName: string
|
||||
devDependency?: boolean
|
||||
version?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
const PackageInstall = ({
|
||||
packageName,
|
||||
devDependency = false,
|
||||
version,
|
||||
className,
|
||||
}: PackageInstallProps) => {
|
||||
const pkg = `${packageName}${version ? `@${version}` : ""}`
|
||||
|
||||
const yarn = `yarn add ${devDependency ? "-D " : ""}${pkg}`
|
||||
const npm = `npm install ${pkg} ${devDependency ? "--save-dev" : "--save"}`
|
||||
const pnpm = `pnpm add ${devDependency ? "-D " : ""}${pkg}`
|
||||
|
||||
return (
|
||||
<CodeTabs
|
||||
tabs={[
|
||||
{ code: { lang: "bash", source: yarn }, label: "npm", value: "npm" },
|
||||
{ code: { lang: "bash", source: npm }, label: "yarn", value: "yarn" },
|
||||
{ code: { lang: "bash", source: pnpm }, label: "pnpm", value: "pnpm" },
|
||||
]}
|
||||
className={clx("my-4", className)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { PackageInstall }
|
||||
129
www/apps/ui/src/components/props-table.tsx
Normal file
129
www/apps/ui/src/components/props-table.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
"use client"
|
||||
|
||||
import { InformationCircleSolid } from "@medusajs/icons"
|
||||
import { Table, Tooltip } from "@medusajs/ui"
|
||||
|
||||
import {
|
||||
EnumType,
|
||||
FunctionType,
|
||||
ObjectType,
|
||||
PropData,
|
||||
PropDataMap,
|
||||
} from "@/types/props"
|
||||
|
||||
type PropTableProps = {
|
||||
props: PropDataMap
|
||||
}
|
||||
|
||||
const PropTable = ({ props }: PropTableProps) => {
|
||||
return (
|
||||
<Table>
|
||||
<Table.Header className="border-t-0">
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
<Table.HeaderCell className="text-right">Default</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body className="border-b-0 [&_tr:last-child]:border-b-0">
|
||||
{/* eslint-disable-next-line react/prop-types */}
|
||||
{props.map((propData, index) => (
|
||||
<Row key={index} {...propData} />
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
const Row = ({ prop, type, defaultValue }: PropData) => {
|
||||
const isEnum = (t: unknown): t is EnumType => {
|
||||
return (t as EnumType).type !== undefined && (t as EnumType).type === "enum"
|
||||
}
|
||||
|
||||
const isObject = (t: unknown): t is ObjectType => {
|
||||
return (
|
||||
(t as ObjectType).type !== undefined &&
|
||||
(t as ObjectType).type === "object"
|
||||
)
|
||||
}
|
||||
|
||||
const isFunction = (t: unknown): t is FunctionType => {
|
||||
return (
|
||||
(t as FunctionType).type !== undefined &&
|
||||
(t as FunctionType).type === "function"
|
||||
)
|
||||
}
|
||||
|
||||
const defaultValueRenderer = (
|
||||
v: string | number | boolean | null | undefined
|
||||
) => {
|
||||
if (v === undefined) {
|
||||
return "-"
|
||||
}
|
||||
|
||||
if (typeof v === "boolean") {
|
||||
return v ? "true" : "false"
|
||||
}
|
||||
|
||||
if (v === null) {
|
||||
return "null"
|
||||
}
|
||||
|
||||
if (typeof v === "string") {
|
||||
return `"${v}"`
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
const isComplexType = isEnum(type) || isObject(type) || isFunction(type)
|
||||
|
||||
return (
|
||||
<Table.Row className="code-body">
|
||||
<Table.Cell>{prop}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{!isComplexType && type.toString()}
|
||||
{isEnum(type) && (
|
||||
<Tooltip
|
||||
content={type.values.map((v) => `"${v}"`).join(" | ")}
|
||||
className="font-mono"
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span>enum</span>
|
||||
<InformationCircleSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isObject(type) && (
|
||||
<Tooltip
|
||||
content={<pre>{type.shape}</pre>}
|
||||
className="font-mono"
|
||||
maxWidth={500}
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span>{type.name}</span>
|
||||
<InformationCircleSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isFunction(type) && (
|
||||
<Tooltip
|
||||
content={<pre>{type.signature}</pre>}
|
||||
className="font-mono"
|
||||
maxWidth={500}
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span>function</span>
|
||||
<InformationCircleSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell className="text-right">
|
||||
{defaultValueRenderer(defaultValue)}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
}
|
||||
|
||||
export { PropTable }
|
||||
57
www/apps/ui/src/components/scroll-area.tsx
Normal file
57
www/apps/ui/src/components/scroll-area.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
"use client"
|
||||
|
||||
import { clx } from "@medusajs/ui"
|
||||
import * as Primitives from "@radix-ui/react-scroll-area"
|
||||
import clsx from "clsx"
|
||||
|
||||
type ScrollbarProps = React.ComponentProps<typeof Primitives.Scrollbar>
|
||||
|
||||
const Scrollbar = (props: ScrollbarProps) => {
|
||||
return (
|
||||
<Primitives.Scrollbar
|
||||
className={clsx(
|
||||
"bg-medusa-bg-base dark:bg-medusa-bg-base-dark flex touch-none select-none p-0.5 transition-colors ease-out",
|
||||
"data-[orientation=horizontal]:h-2.5 data-[orientation=vertical]:w-2.5 data-[orientation=horizontal]:flex-col"
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type ThumbProps = React.ComponentProps<typeof Primitives.Thumb>
|
||||
|
||||
const Thumb = ({ className, ...props }: ThumbProps) => {
|
||||
return (
|
||||
<Primitives.Thumb
|
||||
className={clx(
|
||||
"bg-medusa-bg-component dark:bg-medusa-bg-component-dark relative flex-1 rounded-[10px] before:absolute before:left-1/2 before:top-1/2 before:h-full",
|
||||
"before:min-h-[44px] before:w-full before:min-w-[44px] before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type ScrollAreaProps = React.ComponentProps<typeof Primitives.Root>
|
||||
|
||||
const ScrollArea = ({ children, className }: ScrollAreaProps) => {
|
||||
return (
|
||||
<Primitives.Root
|
||||
className={clx("h-full w-full overflow-hidden", className)}
|
||||
>
|
||||
<Primitives.Viewport className="h-full w-full">
|
||||
{children}
|
||||
</Primitives.Viewport>
|
||||
<Scrollbar orientation="vertical">
|
||||
<Thumb />
|
||||
</Scrollbar>
|
||||
<Scrollbar orientation="horizontal">
|
||||
<Thumb />
|
||||
</Scrollbar>
|
||||
<Primitives.Corner />
|
||||
</Primitives.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { ScrollArea }
|
||||
59
www/apps/ui/src/components/tabs.tsx
Normal file
59
www/apps/ui/src/components/tabs.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
"use client"
|
||||
|
||||
import { clx } from "@medusajs/ui"
|
||||
import * as Primitives from "@radix-ui/react-tabs"
|
||||
import * as React from "react"
|
||||
|
||||
const Tabs = Primitives.Root
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof Primitives.List>,
|
||||
React.ComponentPropsWithoutRef<typeof Primitives.List>
|
||||
// eslint-disable-next-line react/prop-types
|
||||
>(({ className, ...props }, ref) => (
|
||||
<Primitives.List
|
||||
ref={ref}
|
||||
className={clx(
|
||||
"inline-flex h-10 w-full items-end justify-start rounded-none bg-transparent p-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsList.displayName = "TabsList"
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof Primitives.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof Primitives.Trigger>
|
||||
// eslint-disable-next-line react/prop-types
|
||||
>(({ className, ...props }, ref) => (
|
||||
<Primitives.Trigger
|
||||
ref={ref}
|
||||
className={clx(
|
||||
"text-medusa-fg-subtle txt-compact-small-plus rounded-full px-3 py-1.5 transition-all",
|
||||
"data-[state=active]:shadow-card-rest dark:data-[state=active]:shadow-card-rest-dark data-[state=active]:text-medusa-fg-base",
|
||||
"dark:text-medusa-fg-subtle-dark dark:data-[state=active]:text-medusa-fg-base-dark",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsTrigger.displayName = "TabsTrigger"
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof Primitives.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof Primitives.Content>
|
||||
// eslint-disable-next-line react/prop-types
|
||||
>(({ className, ...props }, ref) => (
|
||||
<Primitives.Content
|
||||
ref={ref}
|
||||
className={clx(
|
||||
"w-full rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 data-[state=active]:mt-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsContent.displayName = "TabsContent"
|
||||
|
||||
export { Tabs, TabsContent, TabsList, TabsTrigger }
|
||||
Reference in New Issue
Block a user