UNPKG

fumadocs-openapi

Version:

Generate MDX docs for your OpenAPI spec

231 lines (228 loc) 9.09 kB
'use client'; import { cn } from "../../utils/cn.js"; import { Badge } from "../components/method-label.js"; import { Fragment, createContext, use, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime"; import { ChevronDown } from "lucide-react"; import { cva } from "class-variance-authority"; import { buttonVariants } from "fumadocs-ui/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "fumadocs-ui/components/ui/collapsible"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "fumadocs-ui/components/tabs"; import { Popover, PopoverContent, PopoverTrigger } from "fumadocs-ui/components/ui/popover"; //#region src/ui/schema/client.tsx const typeVariants = cva("text-sm text-start text-fd-muted-foreground font-mono", { variants: { variant: { trigger: "underline hover:text-fd-accent-foreground data-[state=open]:text-fd-accent-foreground" } } }); const PropertyContext = createContext({ renderRef: (props) => /* @__PURE__ */ jsx(RootRef, { ...props }) }); const DataContext = createContext(null); function useData() { return use(DataContext); } function useProperty() { return use(PropertyContext); } function SchemaUI({ name, required = false, as = "property", generated }) { const schema = generated.refs[generated.$root]; return /* @__PURE__ */ jsx(DataContext, { value: useMemo(() => generated, [generated]), children: as === "property" || !isExpandable(schema) ? /* @__PURE__ */ jsx(SchemaUIProperty, { name, $type: generated.$root, overrides: { required } }) : /* @__PURE__ */ jsx(SchemaUIContent, { $type: generated.$root }) }); } function SchemaUIContent({ $type }) { const { refs } = useData(); const schema = refs[$type]; let child = null; if (schema.type === "or" && schema.items.length > 0) child = /* @__PURE__ */ jsxs(Fragment$1, { children: [child, /* @__PURE__ */ jsxs(Tabs, { defaultValue: schema.items[0].$type, children: [/* @__PURE__ */ jsx(TabsList, { children: schema.items.map((item) => /* @__PURE__ */ jsx(TabsTrigger, { value: item.$type, children: item.name }, item.$type)) }), schema.items.map((item) => /* @__PURE__ */ jsx(TabsContent, { value: item.$type, forceMount: void 0, className: "py-0", children: /* @__PURE__ */ jsx(SchemaUIContent, { ...item }) }, item.$type))] })] }); if (schema.type === "object" && schema.props.length > 0) child = /* @__PURE__ */ jsxs(Fragment$1, { children: [child, schema.props.map((prop) => /* @__PURE__ */ jsx(SchemaUIProperty, { name: prop.name, $type: prop.$type, overrides: { required: prop.required } }, prop.name))] }); if (schema.type === "array") child = /* @__PURE__ */ jsxs(Fragment$1, { children: [child, /* @__PURE__ */ jsxs(Collapsible, { className: "my-2", children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, { className: cn(buttonVariants({ color: "secondary", size: "sm" }), "group px-3 py-2 data-[state=open]:rounded-b-none"), children: ["Array Item", /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 text-fd-muted-foreground group-data-[state=open]:rotate-180" })] }), /* @__PURE__ */ jsx(CollapsibleContent, { className: "-mt-px bg-fd-card px-3 rounded-lg rounded-tl-none border shadow-sm", children: /* @__PURE__ */ jsx(SchemaUIContent, { $type: schema.item.$type }) })] })] }); return child; } function SchemaUIProperty({ name, $type, overrides }) { const { renderRef } = useProperty(); const { refs } = useData(); const schema = refs[$type]; let type = schema.typeName; if ((schema.type === "or" || schema.type === "and") && schema.items.length > 0) type = renderRef({ text: schema.aliasName, pathName: name, $ref: $type, inlineUnion: true }); if (schema.type === "object" && schema.props.length > 0) type = renderRef({ text: schema.aliasName, pathName: name, $ref: $type }); if (schema.type === "array") type = renderRef({ text: schema.aliasName, pathName: name, $ref: schema.item.$type }); return /* @__PURE__ */ jsxs(Property, { name, type, deprecated: schema.deprecated, ...overrides, children: [schema.description, schema.infoTags && schema.infoTags.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-row gap-2 flex-wrap my-2 not-prose empty:hidden", children: schema.infoTags.map((tag, i) => /* @__PURE__ */ jsx(Fragment, { children: tag }, i)) })] }); } function SchemaUIPopover({ initialPath }) { const [path, setPath] = useState(initialPath); const ref = useRef(null); const last = path.findLast((item) => item.$ref !== void 0); useEffect(() => { const element = ref.current; if (!element || !element.parentElement) return; element.parentElement.scrollTop = 0; }, [last?.$ref]); const context = useMemo(() => ({ renderRef: (props) => /* @__PURE__ */ jsx(LinkRef, { ...props, onInsert: (name, $ref) => { setPath((path$1) => [...path$1, { name, $ref }]); } }) }), []); if (!last) return; return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", { className: "sticky top-0 flex flex-row flex-wrap items-center text-sm font-medium font-mono bg-fd-muted p-2", children: path.map((item, i) => { const className = cn(path.some((other, j) => j < i && other.$ref === item.$ref) && "text-orange-400", item.$ref && "hover:underline hover:text-fd-accent-foreground"); const node = item.$ref ? /* @__PURE__ */ jsx("button", { onClick: () => setPath((path$1) => path$1.slice(0, i + 1)), className, children: item.name }) : /* @__PURE__ */ jsx("span", { className, children: item.name }); return /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && ".", node] }, i); }) }), /* @__PURE__ */ jsx(PropertyContext, { value: context, children: /* @__PURE__ */ jsx("div", { ref, className: "px-2", children: /* @__PURE__ */ jsx(SchemaUIContent, { $type: last.$ref }) }) })] }); } function RootRef({ text, $ref, pathName, inlineUnion }) { const { refs } = useData(); const ref = useCallback((element) => { if (!element || element.style.getPropertyValue("--initial-height")) return; element.style.setProperty("--initial-height", `${element.clientHeight}px`); }, []); const schema = refs[$ref]; if (inlineUnion && (schema.type === "and" || schema.type === "or")) { const sep = schema.type === "and" ? "&" : "|"; return /* @__PURE__ */ jsx("span", { className: cn(typeVariants(), "flex flex-row gap-2 items-center flex-wrap"), children: schema.items.map((item, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("span", { children: sep }), /* @__PURE__ */ jsx(RootRef, { pathName, text: item.name, $ref: item.$type })] }, item.$type)) }); } if (!isExpandable(refs[$ref])) return /* @__PURE__ */ jsx("span", { className: cn(typeVariants()), children: text }); return /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, { className: cn(typeVariants({ variant: "trigger" })), children: text }), /* @__PURE__ */ jsx(PopoverContent, { ref, className: "w-[600px] min-h-(--initial-height,0) max-h-[460px] p-0", children: /* @__PURE__ */ jsx(SchemaUIPopover, { initialPath: [{ name: /* @__PURE__ */ jsxs(Fragment$1, { children: [pathName, schema.type === "array" && "[]"] }), $ref }] }) })] }); } function LinkRef({ $ref, pathName, onInsert, text }) { const { refs } = useData(); if (!isExpandable(refs[$ref])) return /* @__PURE__ */ jsx("span", { className: cn(typeVariants()), children: text }); return /* @__PURE__ */ jsx("button", { className: cn(typeVariants({ variant: "trigger" })), onClick: () => { onInsert(pathName, $ref); }, children: text }); } function Property({ name, type, required, deprecated, nested = false, className, ...props }) { return /* @__PURE__ */ jsxs("div", { className: cn("text-sm border-t", nested ? "p-3 border-x bg-fd-card last:rounded-b-xl first:rounded-tr-xl last:border-b" : "py-4 first:border-t-0", className), children: [/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 not-prose", children: [ /* @__PURE__ */ jsxs("span", { className: "font-medium font-mono text-fd-primary", children: [name, required ? /* @__PURE__ */ jsx("span", { className: "text-red-400", children: "*" }) : /* @__PURE__ */ jsx("span", { className: "text-fd-muted-foreground", children: "?" })] }), typeof type === "string" ? /* @__PURE__ */ jsx("span", { className: "text-sm font-mono text-fd-muted-foreground", children: type }) : type, deprecated && /* @__PURE__ */ jsx(Badge, { color: "yellow", className: "ms-auto text-xs", children: "Deprecated" }) ] }), /* @__PURE__ */ jsx("div", { className: "prose-no-margin pt-2.5 empty:hidden", children: props.children })] }); } function isExpandable(schema) { return schema.type !== "primitive"; } //#endregion export { SchemaUI }; //# sourceMappingURL=client.js.map