shapes-ui 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/.github/workflows/pr-preview.yml +9 -2
  2. package/CHANGELOG.md +11 -0
  3. package/README.md +13 -0
  4. package/content/components/accordion.mdx +13 -0
  5. package/content/components/alert-dialog.mdx +34 -0
  6. package/content/components/autocomplete.mdx +62 -0
  7. package/content/components/avatar.mdx +11 -0
  8. package/content/components/button.mdx +8 -0
  9. package/content/components/checkbox.mdx +11 -0
  10. package/content/components/collapsible.mdx +11 -0
  11. package/content/components/combobox.mdx +33 -0
  12. package/content/components/context-menu.mdx +29 -0
  13. package/content/components/dialog.mdx +33 -0
  14. package/content/components/drawer.mdx +38 -0
  15. package/content/components/field.mdx +21 -0
  16. package/content/components/fieldset.mdx +10 -0
  17. package/content/components/form.mdx +8 -0
  18. package/content/components/input.mdx +4 -0
  19. package/content/components/menu.mdx +27 -0
  20. package/content/components/menubar.mdx +31 -0
  21. package/content/components/meter.mdx +14 -0
  22. package/content/components/navigation-menu.mdx +28 -0
  23. package/content/components/number-field.mdx +25 -0
  24. package/content/components/popover.mdx +22 -0
  25. package/content/components/preview-card.mdx +14 -2
  26. package/content/components/progress.mdx +15 -1
  27. package/content/components/radio.mdx +11 -0
  28. package/content/components/scroll-area.mdx +23 -0
  29. package/content/components/select.mdx +27 -0
  30. package/content/components/separator.mdx +29 -0
  31. package/content/components/slider.mdx +4 -0
  32. package/content/components/switch.mdx +4 -0
  33. package/content/components/tabs.mdx +15 -0
  34. package/content/components/toast.mdx +10 -0
  35. package/content/components/toggle-group.mdx +37 -0
  36. package/content/components/toggle.mdx +12 -0
  37. package/content/components/toolbar.mdx +22 -0
  38. package/content/components/tooltip.mdx +13 -0
  39. package/content/docs/installation.mdx +30 -0
  40. package/content-collections.ts +65 -1
  41. package/dist/cli.js +947 -101
  42. package/examples/__index.tsx +136 -68
  43. package/examples/autocomplete-align.tsx +39 -0
  44. package/examples/autocomplete-controlled.tsx +44 -0
  45. package/examples/autocomplete-groups.tsx +65 -0
  46. package/examples/autocomplete-no-clear.tsx +40 -0
  47. package/examples/avatar-demo.tsx +3 -3
  48. package/examples/input-group-with-button.tsx +1 -1
  49. package/examples/separator-demo.tsx +13 -0
  50. package/examples/separator-horizontal.tsx +18 -0
  51. package/package.json +19 -18
  52. package/public/base-ui.svg +1 -0
  53. package/src/assets/base-ui.svg +1 -0
  54. package/src/commands/add.ts +79 -38
  55. package/src/commands/cli.ts +50 -3
  56. package/src/commands/create.ts +262 -0
  57. package/src/commands/init.ts +45 -12
  58. package/src/commands/palette.ts +55 -0
  59. package/src/components/docs/layout/footer.tsx +2 -2
  60. package/src/components/docs/layout/header.tsx +7 -9
  61. package/src/components/docs/layout/mobile-menu.tsx +0 -1
  62. package/src/components/docs/layout/nav-list.tsx +2 -2
  63. package/src/components/docs/layout/page-header.tsx +52 -7
  64. package/src/components/docs/layout/split-layout.tsx +9 -10
  65. package/src/components/docs/layout/table-of-content.tsx +145 -0
  66. package/src/components/docs/markdown/components.tsx +142 -29
  67. package/src/components/docs/markdown/copy-button.tsx +41 -0
  68. package/src/components/docs/markdown/installation-block.tsx +5 -24
  69. package/src/components/docs/markdown/render-preview.tsx +1 -1
  70. package/src/components/ui/button-group.tsx +1 -1
  71. package/src/components/ui/scroll-area.tsx +11 -2
  72. package/src/lib/docs-headings.ts +72 -0
  73. package/src/routeTree.gen.ts +60 -3
  74. package/src/routes/__root.tsx +2 -2
  75. package/src/routes/components.$slug.tsx +20 -4
  76. package/src/routes/docs.$slug.tsx +78 -0
  77. package/src/routes/docs.tsx +15 -0
  78. package/src/styles/styles.css +1 -1
  79. package/src/utils/cli-utils.ts +8 -8
  80. package/src/utils/dependency-installer.ts +30 -0
  81. package/src/utils/package-manager.ts +4 -4
  82. package/src/utils/palette.ts +666 -0
  83. package/src/utils/schema.ts +6 -0
@@ -1,8 +1,10 @@
1
1
  import React from "react";
2
2
  import type { ComponentProps } from "react";
3
3
 
4
+ import { slugifyHeading } from "@/lib/docs-headings";
4
5
  import { cn } from "@/lib/utils";
5
6
 
7
+ import { CopyButton } from "./copy-button";
6
8
  import { InstallationBlock } from "./installation-block";
7
9
 
8
10
  // Lightweight helpers used by the markdown components.
@@ -11,28 +13,6 @@ function getIconForLanguageExtension(_ext: string) {
11
13
  return null;
12
14
  }
13
15
 
14
- function CopyButton({ value }: { value: string }) {
15
- const handleCopy = async () => {
16
- if (typeof navigator === "undefined" || !navigator.clipboard?.writeText) return;
17
- try {
18
- await navigator.clipboard.writeText(value);
19
- } catch {
20
- /* no-op */
21
- }
22
- };
23
-
24
- return (
25
- <button
26
- onClick={handleCopy}
27
- type="button"
28
- aria-label="Copy"
29
- className="ml-2 inline-flex items-center rounded-md bg-muted/30 px-2 py-1 text-sm"
30
- >
31
- Copy
32
- </button>
33
- );
34
- }
35
-
36
16
  function CodeBlockCommand({
37
17
  __npm__,
38
18
  __yarn__,
@@ -68,6 +48,113 @@ function CodeBlockCommand({
68
48
  );
69
49
  }
70
50
 
51
+ function UsageCodeBlock({ value, lang = "tsx" }: { value: string; lang?: "tsx" | "typescript" }) {
52
+ const [highlighted, setHighlighted] = React.useState<string | null>(null);
53
+
54
+ React.useEffect(() => {
55
+ let isActive = true;
56
+
57
+ async function highlight() {
58
+ try {
59
+ const { codeToHtml } = await import("shiki");
60
+ const html = await codeToHtml(value, {
61
+ lang,
62
+ themes: {
63
+ light: "github-light",
64
+ dark: "github-dark",
65
+ },
66
+ defaultColor: false,
67
+ transformers: [
68
+ {
69
+ name: "line-numbers",
70
+ pre(node: any) {
71
+ node.properties["data-line-numbers"] = "";
72
+ },
73
+ line(node: any, line: number) {
74
+ node.properties.className = ["line"];
75
+ node.children.unshift({
76
+ type: "element",
77
+ tagName: "span",
78
+ properties: {
79
+ className: ["line-number"],
80
+ },
81
+ children: [{ type: "text", value: String(line) }],
82
+ });
83
+ },
84
+ },
85
+ ],
86
+ });
87
+
88
+ if (isActive) {
89
+ setHighlighted(html);
90
+ }
91
+ } catch {
92
+ if (isActive) {
93
+ setHighlighted(null);
94
+ }
95
+ }
96
+ }
97
+
98
+ highlight();
99
+
100
+ return () => {
101
+ isActive = false;
102
+ };
103
+ }, [value, lang]);
104
+
105
+ const lines = value.trim().split("\n");
106
+
107
+ return (
108
+ <div className="not-prose overflow-hidden border ">
109
+ <div className="relative">
110
+ <CopyButton
111
+ value={value}
112
+ ariaLabel="Copy code"
113
+ className="absolute top-2 right-2 z-10 ml-0"
114
+ />
115
+
116
+ <div className="no-scrollbar max-h-112 min-w-0 overflow-auto px-4 py-3.5 font-mono text-xs">
117
+ {highlighted ? (
118
+ <div className="shiki-wrapper" dangerouslySetInnerHTML={{ __html: highlighted }} />
119
+ ) : (
120
+ <pre className="min-w-0 overflow-x-auto">
121
+ {lines.map((line, index) => (
122
+ <div key={`${index}-${line}`} className="grid grid-cols-[1.75rem_1fr] gap-4">
123
+ <span className="text-right text-muted-foreground/50 select-none">
124
+ {index + 1}
125
+ </span>
126
+ <code className="text-code-foreground block font-mono whitespace-pre">
127
+ {line || " "}
128
+ </code>
129
+ </div>
130
+ ))}
131
+ </pre>
132
+ )}
133
+ </div>
134
+ </div>
135
+ </div>
136
+ );
137
+ }
138
+
139
+ function UsageSection({
140
+ from,
141
+ imports,
142
+ anatomy,
143
+ }: {
144
+ from: string;
145
+ imports: string[];
146
+ anatomy: string;
147
+ }) {
148
+ const importCode = `import {\n${imports.map((item) => ` ${item},`).join("\n")}\n} from "${from}"`;
149
+
150
+ return (
151
+ <div className="my-6 space-y-5">
152
+ <UsageCodeBlock value={importCode} lang="typescript" />
153
+ <UsageCodeBlock value={anatomy} lang="tsx" />
154
+ </div>
155
+ );
156
+ }
157
+
71
158
  type TextProps = ComponentProps<"p">;
72
159
  type LinkProps = ComponentProps<"a">;
73
160
  type ListProps = ComponentProps<"ul">;
@@ -88,9 +175,34 @@ type TableRowProps = ComponentProps<"tr">;
88
175
  type TableHeaderProps = ComponentProps<"th">;
89
176
  type TableCellProps = ComponentProps<"td">;
90
177
 
178
+ function getNodeText(node: React.ReactNode): string {
179
+ if (typeof node === "string" || typeof node === "number") {
180
+ return String(node);
181
+ }
182
+
183
+ if (Array.isArray(node)) {
184
+ return node.map(getNodeText).join("");
185
+ }
186
+
187
+ if (React.isValidElement(node)) {
188
+ return getNodeText(node.props.children);
189
+ }
190
+
191
+ return "";
192
+ }
193
+
194
+ function getHeadingId(id: string | undefined, children: React.ReactNode) {
195
+ if (id) {
196
+ return id;
197
+ }
198
+
199
+ return slugifyHeading(getNodeText(children));
200
+ }
201
+
91
202
  const H1 = ({ className, ...props }: ComponentProps<"h1">) => (
92
203
  <h1
93
- className={cn("mt-2 scroll-m-28 font-heading text-3xl font-bold tracking-tight", className)}
204
+ id={getHeadingId(props.id, props.children)}
205
+ className={cn("mt-2 scroll-m-28 font-heading text-2xl font-bold tracking-tight", className)}
94
206
  {...props}
95
207
  />
96
208
  );
@@ -98,12 +210,7 @@ const H1 = ({ className, ...props }: ComponentProps<"h1">) => (
98
210
  const H2 = ({ className, ...props }: ComponentProps<"h2">) => {
99
211
  return (
100
212
  <h2
101
- id={props.children
102
- ?.toString()
103
- .replace(/ /g, "-")
104
- .replace(/'/g, "")
105
- .replace(/\?/g, "")
106
- .toLowerCase()}
213
+ id={getHeadingId(props.id, props.children)}
107
214
  className={cn(
108
215
  "[&+]*:[code]:text-xl mt-10 scroll-m-28 font-heading text-xl font-medium tracking-tight first:mt-0 lg:mt-12 [&+.steps]:!mt-0 [&+.steps>h3]:!mt-4 [&+h3]:!mt-6 [&+p]:!mt-4",
109
216
  className,
@@ -115,6 +222,7 @@ const H2 = ({ className, ...props }: ComponentProps<"h2">) => {
115
222
 
116
223
  const H3 = ({ className, ...props }: ComponentProps<"h3">) => (
117
224
  <h3
225
+ id={getHeadingId(props.id, props.children)}
118
226
  className={cn(
119
227
  "mt-12 scroll-m-28 font-heading text-lg font-medium tracking-tight [&+p]:!mt-4 *:[code]:text-xl",
120
228
  className,
@@ -125,6 +233,7 @@ const H3 = ({ className, ...props }: ComponentProps<"h3">) => (
125
233
 
126
234
  const H4 = ({ className, ...props }: ComponentProps<"h4">) => (
127
235
  <h4
236
+ id={getHeadingId(props.id, props.children)}
128
237
  className={cn("mt-8 scroll-m-28 font-heading text-base font-medium tracking-tight", className)}
129
238
  {...props}
130
239
  />
@@ -132,6 +241,7 @@ const H4 = ({ className, ...props }: ComponentProps<"h4">) => (
132
241
 
133
242
  const H5 = ({ className, ...props }: ComponentProps<"h5">) => (
134
243
  <h5
244
+ id={getHeadingId(props.id, props.children)}
135
245
  className={cn("mt-8 scroll-m-28 text-base font-medium tracking-tight", className)}
136
246
  {...props}
137
247
  />
@@ -139,6 +249,7 @@ const H5 = ({ className, ...props }: ComponentProps<"h5">) => (
139
249
 
140
250
  const H6 = ({ className, ...props }: ComponentProps<"h6">) => (
141
251
  <h6
252
+ id={getHeadingId(props.id, props.children)}
142
253
  className={cn("mt-8 scroll-m-28 text-base font-medium tracking-tight", className)}
143
254
  {...props}
144
255
  />
@@ -297,7 +408,9 @@ const Code = ({
297
408
  };
298
409
 
299
410
  export const mdxComponents = {
411
+ CopyButton,
300
412
  InstallationBlock,
413
+ UsageSection,
301
414
  h1: H1,
302
415
  h2: H2,
303
416
  h3: H3,
@@ -0,0 +1,41 @@
1
+ import { CheckIcon, CopyIcon } from "lucide-react";
2
+ import { useState } from "react";
3
+
4
+ import { Button } from "@/components/ui/button";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ export function CopyButton({
8
+ value,
9
+ className,
10
+ ariaLabel = "Copy",
11
+ }: {
12
+ value: string;
13
+ className?: string;
14
+ children?: React.ReactNode;
15
+ ariaLabel?: string;
16
+ }) {
17
+ const [copied, setCopied] = useState(false);
18
+
19
+ const handleCopy = async () => {
20
+ try {
21
+ await navigator.clipboard.writeText(value);
22
+ setCopied(true);
23
+ setTimeout(() => setCopied(false), 2000);
24
+ } catch (err) {
25
+ console.error("Failed to copy!", err);
26
+ }
27
+ };
28
+
29
+ return (
30
+ <Button
31
+ variant={"ghost"}
32
+ size={"icon-xs"}
33
+ onClick={handleCopy}
34
+ type="button"
35
+ aria-label={ariaLabel}
36
+ className={cn(className)}
37
+ >
38
+ {copied ? <CheckIcon className="text-success" /> : <CopyIcon />}
39
+ </Button>
40
+ );
41
+ }
@@ -1,11 +1,13 @@
1
1
  "use client";
2
2
 
3
3
  import { Tabs } from "@base-ui/react/tabs";
4
- import { TerminalIcon, CheckIcon, CopyIcon } from "lucide-react";
4
+ import { TerminalIcon } from "lucide-react";
5
5
  import * as React from "react";
6
6
 
7
7
  import { cn } from "@/lib/utils";
8
8
 
9
+ import { CopyButton } from "./copy-button";
10
+
9
11
  interface InstallationBlockProps {
10
12
  name: string;
11
13
  }
@@ -18,7 +20,6 @@ const PACKAGE_MANAGERS = [
18
20
  ];
19
21
 
20
22
  export function InstallationBlock({ name }: InstallationBlockProps) {
21
- const [copied, setCopied] = React.useState(false);
22
23
  const [activeTab, setActiveTab] = React.useState("npx");
23
24
  const [highlighted, setHighlighted] = React.useState<string | null>(null);
24
25
 
@@ -75,18 +76,8 @@ export function InstallationBlock({ name }: InstallationBlockProps) {
75
76
  };
76
77
  }, [currentCommand]);
77
78
 
78
- const copyToClipboard = async () => {
79
- try {
80
- await navigator.clipboard.writeText(currentCommand);
81
- setCopied(true);
82
- setTimeout(() => setCopied(false), 2000);
83
- } catch (err) {
84
- console.error("Failed to copy!", err);
85
- }
86
- };
87
-
88
79
  return (
89
- <div className="not-prose my-6 w-full overflow-hidden rounded-xl border bg-background">
80
+ <div className="not-prose my-6 w-full overflow-hidden border bg-background">
90
81
  <Tabs.Root value={activeTab} onValueChange={setActiveTab}>
91
82
  <div className="flex h-10 items-center justify-between border-b px-3">
92
83
  <div className="flex items-center gap-3">
@@ -109,17 +100,7 @@ export function InstallationBlock({ name }: InstallationBlockProps) {
109
100
  ))}
110
101
  </Tabs.List>
111
102
  </div>
112
- <button
113
- onClick={copyToClipboard}
114
- className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted/80 hover:text-foreground"
115
- aria-label="Copy command"
116
- >
117
- {copied ? (
118
- <CheckIcon className="h-3.5 w-3.5 text-success" />
119
- ) : (
120
- <CopyIcon className="h-3.5 w-3.5" />
121
- )}
122
- </button>
103
+ <CopyButton value={currentCommand} ariaLabel="Copy command" />
123
104
  </div>
124
105
 
125
106
  {PACKAGE_MANAGERS.map((pm) => (
@@ -104,7 +104,7 @@ export function RenderPreview({ name }: { name: string }) {
104
104
  const ExampleComponent = example.code;
105
105
 
106
106
  return (
107
- <Tabs.Root className={"my-2 flex flex-col gap-2"}>
107
+ <Tabs.Root className={"my-2 flex max-w-sm flex-col gap-2 md:max-w-full"}>
108
108
  <Tabs.List className={"flex gap-2"}>
109
109
  <Tabs.Tab
110
110
  value="preview"
@@ -43,7 +43,7 @@ function ButtonGroupText({ className, ...props }: React.ComponentProps<"div">) {
43
43
  <div
44
44
  data-slot="button-group-text"
45
45
  className={cn(
46
- "flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
46
+ "flex items-center gap-2 rounded-lg bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
47
47
  className,
48
48
  )}
49
49
  {...props}
@@ -1,17 +1,26 @@
1
1
  "use client";
2
2
 
3
3
  import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
4
+ import type { ReactNode, Ref } from "react";
4
5
 
5
6
  import { cn } from "@/lib/utils";
6
7
 
7
8
  function ScrollArea({
8
9
  className,
9
10
  children,
11
+ viewportRef,
10
12
  ...props
11
- }: ScrollAreaPrimitive.Root.Props & { children?: React.ReactNode }) {
13
+ }: ScrollAreaPrimitive.Root.Props & {
14
+ children?: ReactNode;
15
+ viewportRef?: Ref<HTMLDivElement>;
16
+ }) {
12
17
  return (
13
18
  <ScrollAreaPrimitive.Root className={cn("relative", className)} {...props}>
14
- <ScrollAreaPrimitive.Viewport className="h-full w-full">
19
+ <ScrollAreaPrimitive.Viewport
20
+ ref={viewportRef}
21
+ data-scroll-area-viewport
22
+ className="h-full w-full"
23
+ >
15
24
  <ScrollAreaPrimitive.Content>{children}</ScrollAreaPrimitive.Content>
16
25
  </ScrollAreaPrimitive.Viewport>
17
26
  <ScrollAreaScrollbar>
@@ -0,0 +1,72 @@
1
+ export type TocHeading = {
2
+ id: string;
3
+ title: string;
4
+ level: number;
5
+ };
6
+
7
+ export function slugifyHeading(value: string) {
8
+ return value
9
+ .trim()
10
+ .toLowerCase()
11
+ .replace(/`/g, "")
12
+ .replace(/\[(.*?)\]\((.*?)\)/g, "$1")
13
+ .replace(/<[^>]+>/g, "")
14
+ .replace(/&[a-z0-9#]+;/gi, "")
15
+ .replace(/[^a-z0-9\s-]/g, "")
16
+ .replace(/\s+/g, "-")
17
+ .replace(/-+/g, "-")
18
+ .replace(/^-|-$/g, "");
19
+ }
20
+
21
+ function sanitizeHeadingTitle(value: string) {
22
+ return value
23
+ .replace(/\s+#+\s*$/, "")
24
+ .replace(/`/g, "")
25
+ .replace(/\[(.*?)\]\((.*?)\)/g, "$1")
26
+ .replace(/[*_~]/g, "")
27
+ .replace(/<[^>]+>/g, "")
28
+ .replace(/&[a-z0-9#]+;/gi, "")
29
+ .trim();
30
+ }
31
+
32
+ export function extractHeadingsFromMarkdown(content: string): TocHeading[] {
33
+ const headings: TocHeading[] = [];
34
+ const lines = content.split("\n");
35
+ let inCodeFence = false;
36
+
37
+ for (const rawLine of lines) {
38
+ const line = rawLine.trim();
39
+
40
+ if (line.startsWith("```")) {
41
+ inCodeFence = !inCodeFence;
42
+ continue;
43
+ }
44
+
45
+ if (inCodeFence) {
46
+ continue;
47
+ }
48
+
49
+ const match = line.match(/^(#{1,6})\s+(.+)$/);
50
+
51
+ if (!match) {
52
+ continue;
53
+ }
54
+
55
+ const level = match[1]?.length ?? 0;
56
+ const title = sanitizeHeadingTitle(match[2] ?? "");
57
+
58
+ if (!title) {
59
+ continue;
60
+ }
61
+
62
+ const id = slugifyHeading(title);
63
+
64
+ if (!id) {
65
+ continue;
66
+ }
67
+
68
+ headings.push({ id, title, level });
69
+ }
70
+
71
+ return headings;
72
+ }
@@ -9,11 +9,18 @@
9
9
  // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
10
 
11
11
  import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as DocsRouteImport } from './routes/docs'
12
13
  import { Route as ComponentsRouteImport } from './routes/components'
13
14
  import { Route as IndexRouteImport } from './routes/index'
14
15
  import { Route as ComponentsIndexRouteImport } from './routes/components.index'
16
+ import { Route as DocsSlugRouteImport } from './routes/docs.$slug'
15
17
  import { Route as ComponentsSlugRouteImport } from './routes/components.$slug'
16
18
 
19
+ const DocsRoute = DocsRouteImport.update({
20
+ id: '/docs',
21
+ path: '/docs',
22
+ getParentRoute: () => rootRouteImport,
23
+ } as any)
17
24
  const ComponentsRoute = ComponentsRouteImport.update({
18
25
  id: '/components',
19
26
  path: '/components',
@@ -29,6 +36,11 @@ const ComponentsIndexRoute = ComponentsIndexRouteImport.update({
29
36
  path: '/',
30
37
  getParentRoute: () => ComponentsRoute,
31
38
  } as any)
39
+ const DocsSlugRoute = DocsSlugRouteImport.update({
40
+ id: '/$slug',
41
+ path: '/$slug',
42
+ getParentRoute: () => DocsRoute,
43
+ } as any)
32
44
  const ComponentsSlugRoute = ComponentsSlugRouteImport.update({
33
45
  id: '/$slug',
34
46
  path: '/$slug',
@@ -38,36 +50,63 @@ const ComponentsSlugRoute = ComponentsSlugRouteImport.update({
38
50
  export interface FileRoutesByFullPath {
39
51
  '/': typeof IndexRoute
40
52
  '/components': typeof ComponentsRouteWithChildren
53
+ '/docs': typeof DocsRouteWithChildren
41
54
  '/components/$slug': typeof ComponentsSlugRoute
55
+ '/docs/$slug': typeof DocsSlugRoute
42
56
  '/components/': typeof ComponentsIndexRoute
43
57
  }
44
58
  export interface FileRoutesByTo {
45
59
  '/': typeof IndexRoute
60
+ '/docs': typeof DocsRouteWithChildren
46
61
  '/components/$slug': typeof ComponentsSlugRoute
62
+ '/docs/$slug': typeof DocsSlugRoute
47
63
  '/components': typeof ComponentsIndexRoute
48
64
  }
49
65
  export interface FileRoutesById {
50
66
  __root__: typeof rootRouteImport
51
67
  '/': typeof IndexRoute
52
68
  '/components': typeof ComponentsRouteWithChildren
69
+ '/docs': typeof DocsRouteWithChildren
53
70
  '/components/$slug': typeof ComponentsSlugRoute
71
+ '/docs/$slug': typeof DocsSlugRoute
54
72
  '/components/': typeof ComponentsIndexRoute
55
73
  }
56
74
  export interface FileRouteTypes {
57
75
  fileRoutesByFullPath: FileRoutesByFullPath
58
- fullPaths: '/' | '/components' | '/components/$slug' | '/components/'
76
+ fullPaths:
77
+ | '/'
78
+ | '/components'
79
+ | '/docs'
80
+ | '/components/$slug'
81
+ | '/docs/$slug'
82
+ | '/components/'
59
83
  fileRoutesByTo: FileRoutesByTo
60
- to: '/' | '/components/$slug' | '/components'
61
- id: '__root__' | '/' | '/components' | '/components/$slug' | '/components/'
84
+ to: '/' | '/docs' | '/components/$slug' | '/docs/$slug' | '/components'
85
+ id:
86
+ | '__root__'
87
+ | '/'
88
+ | '/components'
89
+ | '/docs'
90
+ | '/components/$slug'
91
+ | '/docs/$slug'
92
+ | '/components/'
62
93
  fileRoutesById: FileRoutesById
63
94
  }
64
95
  export interface RootRouteChildren {
65
96
  IndexRoute: typeof IndexRoute
66
97
  ComponentsRoute: typeof ComponentsRouteWithChildren
98
+ DocsRoute: typeof DocsRouteWithChildren
67
99
  }
68
100
 
69
101
  declare module '@tanstack/react-router' {
70
102
  interface FileRoutesByPath {
103
+ '/docs': {
104
+ id: '/docs'
105
+ path: '/docs'
106
+ fullPath: '/docs'
107
+ preLoaderRoute: typeof DocsRouteImport
108
+ parentRoute: typeof rootRouteImport
109
+ }
71
110
  '/components': {
72
111
  id: '/components'
73
112
  path: '/components'
@@ -89,6 +128,13 @@ declare module '@tanstack/react-router' {
89
128
  preLoaderRoute: typeof ComponentsIndexRouteImport
90
129
  parentRoute: typeof ComponentsRoute
91
130
  }
131
+ '/docs/$slug': {
132
+ id: '/docs/$slug'
133
+ path: '/$slug'
134
+ fullPath: '/docs/$slug'
135
+ preLoaderRoute: typeof DocsSlugRouteImport
136
+ parentRoute: typeof DocsRoute
137
+ }
92
138
  '/components/$slug': {
93
139
  id: '/components/$slug'
94
140
  path: '/$slug'
@@ -113,9 +159,20 @@ const ComponentsRouteWithChildren = ComponentsRoute._addFileChildren(
113
159
  ComponentsRouteChildren,
114
160
  )
115
161
 
162
+ interface DocsRouteChildren {
163
+ DocsSlugRoute: typeof DocsSlugRoute
164
+ }
165
+
166
+ const DocsRouteChildren: DocsRouteChildren = {
167
+ DocsSlugRoute: DocsSlugRoute,
168
+ }
169
+
170
+ const DocsRouteWithChildren = DocsRoute._addFileChildren(DocsRouteChildren)
171
+
116
172
  const rootRouteChildren: RootRouteChildren = {
117
173
  IndexRoute: IndexRoute,
118
174
  ComponentsRoute: ComponentsRouteWithChildren,
175
+ DocsRoute: DocsRouteWithChildren,
119
176
  }
120
177
  export const routeTree = rootRouteImport
121
178
  ._addFileChildren(rootRouteChildren)
@@ -46,9 +46,9 @@ function RootDocument({ children }: { children: React.ReactNode }) {
46
46
  <head>
47
47
  <HeadContent />
48
48
  </head>
49
- <body className="flex h-dvh flex-col overflow-hidden">
49
+ <body className="flex h-dvh flex-col overflow-hidden bg-muted">
50
50
  <Header />
51
- <main className=" container mx-auto flex min-h-0 flex-1 border-x">{children}</main>
51
+ <main className=" container mx-auto flex min-h-0 xl:bg-card flex-1 px-4 xl:p-0 ">{children}</main>
52
52
  <Footer />
53
53
  <TanStackDevtools
54
54
  config={{
@@ -7,16 +7,27 @@ import { PageHeader } from "@/components/docs/layout/page-header";
7
7
  import { mdxComponents } from "@/components/docs/markdown/components";
8
8
  import { RenderPreview } from "@/components/docs/markdown/render-preview";
9
9
 
10
+ const markdownFiles = import.meta.glob("../../content/components/*.mdx", {
11
+ query: "?url",
12
+ import: "default",
13
+ eager: true,
14
+ }) as Record<string, string>;
15
+
10
16
  export const Route = createFileRoute("/components/$slug")({
11
17
  loader: ({ params }) => {
12
18
  const { slug } = params;
13
- const component = (allComponents as any[]).find((c) => c.slug === slug);
19
+ const component = allComponents?.find((c) => c.slug === slug);
14
20
 
15
21
  if (!component) {
16
22
  throw notFound();
17
23
  }
18
24
 
19
- return component;
25
+ const markdownLink = markdownFiles[`../../content/components/${component.slug}.mdx`];
26
+
27
+ return {
28
+ ...component,
29
+ markdownLink,
30
+ };
20
31
  },
21
32
  head: ({ loaderData }) => ({
22
33
  meta: [
@@ -51,8 +62,13 @@ function RouteComponent() {
51
62
 
52
63
  return (
53
64
  <div className=" flex flex-col gap-6 ">
54
- <PageHeader title={component?.title} subtitle={component?.description} />
55
- <div className="container mx-auto max-w-4xl min-w-0 p-6">
65
+ <PageHeader
66
+ title={component?.title}
67
+ subtitle={component?.description}
68
+ baseUILink={component?.referenceLink}
69
+ markdownLink={component?.markdownLink}
70
+ />
71
+ <div data-docs-content className="container mx-auto max-w-4xl min-w-0 p-6">
56
72
  {isMounted ? (
57
73
  <MDXContent code={component.mdx} components={{ ...mdxComponents, RenderPreview }} />
58
74
  ) : null}