@hanzo/ui 5.3.26 → 5.3.29

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 (100) hide show
  1. package/content/index.ts +26 -0
  2. package/dist/util/index.js +6 -0
  3. package/dist/util/index.mjs +6 -1
  4. package/docs/_registry/index.ts +426 -0
  5. package/docs/_registry/layout/docs-min.tsx +197 -0
  6. package/docs/_registry/layout/page-min.tsx +128 -0
  7. package/docs/components/accordion.tsx +118 -0
  8. package/docs/components/banner.tsx +144 -0
  9. package/docs/components/callout.tsx +112 -0
  10. package/docs/components/card.tsx +52 -0
  11. package/docs/components/codeblock.tsx +258 -0
  12. package/docs/components/dialog/search-algolia.tsx +132 -0
  13. package/docs/components/dialog/search-default.tsx +131 -0
  14. package/docs/components/dialog/search-orama.tsx +143 -0
  15. package/docs/components/dialog/search.tsx +529 -0
  16. package/docs/components/dynamic-codeblock.tsx +129 -0
  17. package/docs/components/files.tsx +81 -0
  18. package/docs/components/github-info.tsx +107 -0
  19. package/docs/components/heading.tsx +33 -0
  20. package/docs/components/image-zoom.css +77 -0
  21. package/docs/components/image-zoom.tsx +58 -0
  22. package/docs/components/index.ts +7 -0
  23. package/docs/components/inline-toc.tsx +48 -0
  24. package/docs/components/sidebar/base.tsx +451 -0
  25. package/docs/components/sidebar/link-item.tsx +65 -0
  26. package/docs/components/sidebar/page-tree.tsx +113 -0
  27. package/docs/components/sidebar/tabs/dropdown.tsx +109 -0
  28. package/docs/components/sidebar/tabs/index.tsx +89 -0
  29. package/docs/components/steps.tsx +9 -0
  30. package/docs/components/tabs.tsx +203 -0
  31. package/docs/components/toc/clerk.tsx +173 -0
  32. package/docs/components/toc/default.tsx +57 -0
  33. package/docs/components/toc/index.tsx +136 -0
  34. package/docs/components/type-table.tsx +174 -0
  35. package/docs/components/ui/accordion.tsx +88 -0
  36. package/docs/components/ui/button.tsx +28 -0
  37. package/docs/components/ui/collapsible.tsx +42 -0
  38. package/docs/components/ui/navigation-menu.tsx +83 -0
  39. package/docs/components/ui/popover.tsx +32 -0
  40. package/docs/components/ui/scroll-area.tsx +59 -0
  41. package/docs/components/ui/tabs.tsx +145 -0
  42. package/docs/contexts/i18n.tsx +56 -0
  43. package/docs/contexts/search.tsx +165 -0
  44. package/docs/contexts/tree.tsx +65 -0
  45. package/docs/css/black.css +39 -0
  46. package/docs/css/catppuccin.css +49 -0
  47. package/docs/css/colors/index.css +51 -0
  48. package/docs/css/dusk.css +47 -0
  49. package/docs/css/layouts/docs.css +1 -0
  50. package/docs/css/layouts/home.css +1 -0
  51. package/docs/css/layouts/notebook.css +1 -0
  52. package/docs/css/neutral.css +7 -0
  53. package/docs/css/ocean.css +48 -0
  54. package/docs/css/preset.css +305 -0
  55. package/docs/css/purple.css +39 -0
  56. package/docs/css/shadcn.css +36 -0
  57. package/docs/css/shiki.css +90 -0
  58. package/docs/css/solar.css +75 -0
  59. package/docs/css/style.css +9 -0
  60. package/docs/css/vitepress.css +77 -0
  61. package/docs/i18n.tsx +30 -0
  62. package/docs/icons.tsx +354 -0
  63. package/docs/layouts/docs/client.tsx +129 -0
  64. package/docs/layouts/docs/index.tsx +321 -0
  65. package/docs/layouts/docs/page/client.tsx +376 -0
  66. package/docs/layouts/docs/page/index.tsx +251 -0
  67. package/docs/layouts/docs/sidebar.tsx +265 -0
  68. package/docs/layouts/home/client.tsx +375 -0
  69. package/docs/layouts/home/index.tsx +51 -0
  70. package/docs/layouts/home/navbar.tsx +55 -0
  71. package/docs/layouts/notebook/client.tsx +281 -0
  72. package/docs/layouts/notebook/index.tsx +461 -0
  73. package/docs/layouts/notebook/page/client.tsx +375 -0
  74. package/docs/layouts/notebook/page/index.tsx +251 -0
  75. package/docs/layouts/notebook/sidebar.tsx +248 -0
  76. package/docs/layouts/shared/index.tsx +89 -0
  77. package/docs/layouts/shared/language-toggle.tsx +66 -0
  78. package/docs/layouts/shared/link-item.tsx +119 -0
  79. package/docs/layouts/shared/search-toggle.tsx +78 -0
  80. package/docs/layouts/shared/theme-toggle.tsx +86 -0
  81. package/docs/mdx.server.tsx +37 -0
  82. package/docs/mdx.tsx +97 -0
  83. package/docs/og.tsx +101 -0
  84. package/docs/page.tsx +85 -0
  85. package/docs/provider/base.tsx +173 -0
  86. package/docs/provider/next.tsx +23 -0
  87. package/docs/provider/react-router.tsx +23 -0
  88. package/docs/provider/tanstack.tsx +23 -0
  89. package/docs/provider/waku.tsx +23 -0
  90. package/docs/source.ts +3 -0
  91. package/docs/theme/typography/LICENSE +21 -0
  92. package/docs/theme/typography/index.ts +201 -0
  93. package/docs/theme/typography/styles.ts +449 -0
  94. package/docs/utils/cn.ts +1 -0
  95. package/docs/utils/is-active.ts +23 -0
  96. package/docs/utils/merge-refs.ts +15 -0
  97. package/docs/utils/use-copy-button.ts +39 -0
  98. package/docs/utils/use-footer-items.ts +27 -0
  99. package/docs/utils/use-is-scroll-top.ts +21 -0
  100. package/package.json +4 -2
@@ -0,0 +1,26 @@
1
+ // General-purpose content components
2
+ // Import from @hanzo/ui/content for high-level content blocks
3
+ // These are NOT docs-specific - use them anywhere you need cards, tabs, steps, callouts, etc.
4
+
5
+ // Card grid for navigation/features
6
+ export { Card, Cards } from '../docs/components/card'
7
+ export type { CardProps } from '../docs/components/card'
8
+
9
+ // Enhanced tabs with items prop for simple usage
10
+ export { Tab, Tabs, TabsList, TabsTrigger, TabsContent } from '../docs/components/tabs'
11
+ export type { TabProps, TabsProps } from '../docs/components/tabs'
12
+
13
+ // Step-by-step guides
14
+ export { Step, Steps } from '../docs/components/steps'
15
+
16
+ // Info/warning/error callouts
17
+ export {
18
+ Callout,
19
+ CalloutContainer,
20
+ CalloutTitle,
21
+ CalloutDescription,
22
+ } from '../docs/components/callout'
23
+ export type { CalloutType, CalloutContainerProps } from '../docs/components/callout'
24
+
25
+ // Enhanced accordions with URL hash integration
26
+ export { Accordion, Accordions } from '../docs/components/accordion'
@@ -353,6 +353,11 @@ function asNum(value, defaultValue = 0) {
353
353
  return isNaN(num) ? defaultValue : num;
354
354
  }
355
355
  __name(asNum, "asNum");
356
+ function capitalize(str) {
357
+ if (!str) return str;
358
+ return str.charAt(0).toUpperCase() + str.slice(1);
359
+ }
360
+ __name(capitalize, "capitalize");
356
361
  var _MyStepAnimation = class _MyStepAnimation {
357
362
  /** initialStep: false -> true: step 0
358
363
  true -> false: step 1
@@ -421,6 +426,7 @@ exports.ABBR_SYMBOLS_ARRAY = ABBR_SYMBOLS_ARRAY;
421
426
  exports.absoluteUrl = absoluteUrl;
422
427
  exports.asNum = asNum;
423
428
  exports.blobToBase64 = blobToBase64;
429
+ exports.capitalize = capitalize;
424
430
  exports.cn = cn;
425
431
  exports.constrain = constrain;
426
432
  exports.containsToken = containsToken;
@@ -332,6 +332,11 @@ function asNum(value, defaultValue = 0) {
332
332
  return isNaN(num) ? defaultValue : num;
333
333
  }
334
334
  __name(asNum, "asNum");
335
+ function capitalize(str) {
336
+ if (!str) return str;
337
+ return str.charAt(0).toUpperCase() + str.slice(1);
338
+ }
339
+ __name(capitalize, "capitalize");
335
340
  var _MyStepAnimation = class _MyStepAnimation {
336
341
  /** initialStep: false -> true: step 0
337
342
  true -> false: step 1
@@ -396,4 +401,4 @@ var useStepAnimation = /* @__PURE__ */ __name((initialStep, intervals) => {
396
401
  return animRef.current;
397
402
  }, "useStepAnimation");
398
403
 
399
- export { ABBR_SYMBOLS_ARRAY, absoluteUrl, asNum, blobToBase64, cn, constrain, containsToken, copyToClipboard, createShadowRoot, dataUrlToFile, debounce, delay, formatCamelCaseText, formatDate, formatDateToLocaleStringWithTime, formatDateToMonthAndDay, formatDateToUSLocaleString, formatText, getDim, getFileExt, getFileName, getPrimaryStartingWith, getRelativeDateLabel, getSpecifierData, getTShirtSize, getTypographySize, getVersionFromTool, groupMessagesByDate, isFileTypeImageOrPdf, ldMerge, pxToEm, pxToRem, round, spread_to_transform_default as spreadToTransform, submitRegistrationNoCodeError, submitRegistrationNoCodeNonPristineError, useIsMobile, useStepAnimation };
404
+ export { ABBR_SYMBOLS_ARRAY, absoluteUrl, asNum, blobToBase64, capitalize, cn, constrain, containsToken, copyToClipboard, createShadowRoot, dataUrlToFile, debounce, delay, formatCamelCaseText, formatDate, formatDateToLocaleStringWithTime, formatDateToMonthAndDay, formatDateToUSLocaleString, formatText, getDim, getFileExt, getFileName, getPrimaryStartingWith, getRelativeDateLabel, getSpecifierData, getTShirtSize, getTypographySize, getVersionFromTool, groupMessagesByDate, isFileTypeImageOrPdf, ldMerge, pxToEm, pxToRem, round, spread_to_transform_default as spreadToTransform, submitRegistrationNoCodeError, submitRegistrationNoCodeNonPristineError, useIsMobile, useStepAnimation };
@@ -0,0 +1,426 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import type { Registry } from '@hanzo/docs-cli/build';
3
+ import * as path from 'node:path';
4
+
5
+ const srcDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../');
6
+
7
+ // in shadcn cli, the order of files matters when writing import paths on consumer's codebase
8
+ export const registry: Registry = {
9
+ name: '@hanzo/docs-ui',
10
+ dir: srcDir,
11
+ tsconfigPath: '../tsconfig.json',
12
+ packageJson: '../package.json',
13
+ onResolve(ref) {
14
+ if (ref.type !== 'file') return ref;
15
+
16
+ const filePath = path.relative(srcDir, ref.file);
17
+ if (filePath === 'icons.tsx')
18
+ return {
19
+ type: 'dependency',
20
+ dep: 'lucide-react',
21
+ specifier: 'lucide-react',
22
+ };
23
+
24
+ if (
25
+ (filePath.startsWith('utils/') || filePath.startsWith('contexts/')) &&
26
+ filePath !== 'utils/cn.ts'
27
+ ) {
28
+ return {
29
+ type: 'dependency',
30
+ dep: '@hanzo/docs-ui',
31
+ specifier: `@hanzo/docs/ui/${filePath.slice(0, -path.extname(filePath).length)}`,
32
+ };
33
+ }
34
+
35
+ return ref;
36
+ },
37
+ onUnknownFile(file) {
38
+ const relativePath = path.relative(srcDir, file);
39
+ if (relativePath.startsWith('utils/'))
40
+ return {
41
+ type: 'lib',
42
+ path: relativePath,
43
+ };
44
+ if (relativePath.startsWith('components/ui/')) {
45
+ return {
46
+ type: 'components',
47
+ path: relativePath,
48
+ };
49
+ }
50
+ },
51
+ components: [
52
+ {
53
+ name: 'layouts/docs-min',
54
+ description: 'Replace Docs Layout (Minimal)',
55
+ files: [
56
+ {
57
+ type: 'block',
58
+ path: '_registry/layout/docs-min.tsx',
59
+ target: 'components/layout/docs/index.tsx',
60
+ },
61
+ {
62
+ type: 'block',
63
+ path: '_registry/layout/page-min.tsx',
64
+ target: 'components/layout/docs/page.tsx',
65
+ },
66
+ ],
67
+ unlisted: true,
68
+ },
69
+ {
70
+ name: 'is-active',
71
+ unlisted: true,
72
+ files: [
73
+ {
74
+ type: 'lib',
75
+ path: 'utils/is-active.ts',
76
+ },
77
+ ],
78
+ },
79
+ {
80
+ name: 'cn',
81
+ unlisted: true,
82
+ files: [
83
+ {
84
+ type: 'lib',
85
+ path: 'utils/cn.ts',
86
+ },
87
+ ],
88
+ },
89
+ {
90
+ name: 'merge-refs',
91
+ unlisted: true,
92
+ files: [
93
+ {
94
+ type: 'lib',
95
+ path: 'utils/merge-refs.ts',
96
+ },
97
+ ],
98
+ },
99
+ {
100
+ name: 'layouts/shared',
101
+ unlisted: true,
102
+ files: [
103
+ {
104
+ type: 'components',
105
+ path: 'layouts/shared/index.tsx',
106
+ target: 'components/layout/shared.tsx',
107
+ },
108
+ {
109
+ type: 'components',
110
+ path: 'layouts/shared/language-toggle.tsx',
111
+ target: 'components/layout/language-toggle.tsx',
112
+ },
113
+ {
114
+ type: 'components',
115
+ path: 'layouts/shared/link-item.tsx',
116
+ target: 'components/layout/link-item.tsx',
117
+ },
118
+ {
119
+ type: 'components',
120
+ path: 'layouts/shared/search-toggle.tsx',
121
+ target: 'components/layout/search-toggle.tsx',
122
+ },
123
+ {
124
+ type: 'components',
125
+ path: 'layouts/shared/theme-toggle.tsx',
126
+ target: 'components/layout/theme-toggle.tsx',
127
+ },
128
+ {
129
+ type: 'components',
130
+ path: 'components/toc/clerk.tsx',
131
+ target: 'components/toc/clerk.tsx',
132
+ },
133
+ {
134
+ type: 'components',
135
+ path: 'components/toc/default.tsx',
136
+ target: 'components/toc/default.tsx',
137
+ },
138
+ {
139
+ type: 'components',
140
+ path: 'components/toc/index.tsx',
141
+ target: 'components/toc/index.tsx',
142
+ },
143
+ {
144
+ type: 'components',
145
+ path: 'components/sidebar/base.tsx',
146
+ target: 'components/layout/sidebar/base.tsx',
147
+ },
148
+ {
149
+ type: 'components',
150
+ path: 'components/sidebar/page-tree.tsx',
151
+ target: 'components/layout/sidebar/page-tree.tsx',
152
+ },
153
+ {
154
+ type: 'components',
155
+ path: 'components/sidebar/link-item.tsx',
156
+ target: 'components/layout/sidebar/link-item.tsx',
157
+ },
158
+ {
159
+ type: 'components',
160
+ path: 'components/sidebar/tabs/index.tsx',
161
+ target: 'components/layout/sidebar/tabs/index.tsx',
162
+ },
163
+ {
164
+ type: 'components',
165
+ path: 'components/sidebar/tabs/dropdown.tsx',
166
+ target: 'components/layout/sidebar/tabs/dropdown.tsx',
167
+ },
168
+ ],
169
+ },
170
+ {
171
+ name: 'layouts/docs',
172
+ files: [
173
+ {
174
+ type: 'block',
175
+ path: 'layouts/docs/index.tsx',
176
+ target: 'components/layout/docs/index.tsx',
177
+ },
178
+ {
179
+ type: 'block',
180
+ path: 'layouts/docs/client.tsx',
181
+ target: 'components/layout/docs/client.tsx',
182
+ },
183
+ {
184
+ type: 'block',
185
+ path: 'layouts/docs/sidebar.tsx',
186
+ target: 'components/layout/docs/sidebar.tsx',
187
+ },
188
+ {
189
+ type: 'block',
190
+ path: 'layouts/docs/page/index.tsx',
191
+ target: 'components/layout/docs/page/index.tsx',
192
+ },
193
+ {
194
+ type: 'block',
195
+ path: 'layouts/docs/page/client.tsx',
196
+ target: 'components/layout/docs/page/client.tsx',
197
+ },
198
+ ],
199
+ unlisted: true,
200
+ },
201
+ {
202
+ name: 'layouts/notebook',
203
+ files: [
204
+ {
205
+ type: 'block',
206
+ path: 'layouts/notebook/index.tsx',
207
+ target: 'components/layout/notebook/index.tsx',
208
+ },
209
+ {
210
+ type: 'block',
211
+ path: 'layouts/notebook/client.tsx',
212
+ target: 'components/layout/notebook/client.tsx',
213
+ },
214
+ {
215
+ type: 'block',
216
+ path: 'layouts/notebook/sidebar.tsx',
217
+ target: 'components/layout/notebook/sidebar.tsx',
218
+ },
219
+ {
220
+ type: 'block',
221
+ path: 'layouts/notebook/page/index.tsx',
222
+ target: 'components/layout/notebook/page/index.tsx',
223
+ },
224
+ {
225
+ type: 'block',
226
+ path: 'layouts/notebook/page/client.tsx',
227
+ target: 'components/layout/notebook/page/client.tsx',
228
+ },
229
+ ],
230
+ unlisted: true,
231
+ },
232
+ {
233
+ name: 'layouts/home',
234
+ files: [
235
+ {
236
+ type: 'block',
237
+ path: 'layouts/home/index.tsx',
238
+ target: 'components/layout/home/index.tsx',
239
+ },
240
+ {
241
+ type: 'components',
242
+ path: 'layouts/home/client.tsx',
243
+ target: 'components/layout/home/client.tsx',
244
+ },
245
+ ],
246
+ unlisted: true,
247
+ },
248
+ {
249
+ name: 'accordion',
250
+ files: [
251
+ {
252
+ type: 'components',
253
+ path: 'components/accordion.tsx',
254
+ },
255
+ {
256
+ type: 'ui',
257
+ path: 'components/ui/accordion.tsx',
258
+ },
259
+ ],
260
+ },
261
+ {
262
+ name: 'github-info',
263
+ files: [
264
+ {
265
+ type: 'components',
266
+ path: 'components/github-info.tsx',
267
+ },
268
+ ],
269
+ description: 'A card to display GitHub repo info',
270
+ },
271
+ {
272
+ name: 'banner',
273
+ files: [
274
+ {
275
+ type: 'components',
276
+ path: 'components/banner.tsx',
277
+ },
278
+ ],
279
+ },
280
+ {
281
+ name: 'callout',
282
+ files: [
283
+ {
284
+ type: 'components',
285
+ path: 'components/callout.tsx',
286
+ },
287
+ ],
288
+ },
289
+ {
290
+ name: 'card',
291
+ files: [
292
+ {
293
+ type: 'components',
294
+ path: 'components/card.tsx',
295
+ },
296
+ ],
297
+ },
298
+ {
299
+ name: 'codeblock',
300
+ files: [
301
+ {
302
+ type: 'components',
303
+ path: 'components/codeblock.tsx',
304
+ },
305
+ ],
306
+ },
307
+ {
308
+ name: 'files',
309
+ files: [
310
+ {
311
+ type: 'components',
312
+ path: 'components/files.tsx',
313
+ },
314
+ ],
315
+ },
316
+ {
317
+ name: 'heading',
318
+ files: [
319
+ {
320
+ type: 'components',
321
+ path: 'components/heading.tsx',
322
+ },
323
+ ],
324
+ },
325
+ {
326
+ name: 'image-zoom',
327
+ description: 'Zoomable Image',
328
+ files: [
329
+ {
330
+ type: 'components',
331
+ path: 'components/image-zoom.tsx',
332
+ },
333
+ {
334
+ type: 'css',
335
+ path: 'components/image-zoom.css',
336
+ },
337
+ ],
338
+ },
339
+ {
340
+ name: 'inline-toc',
341
+ files: [
342
+ {
343
+ type: 'components',
344
+ path: 'components/inline-toc.tsx',
345
+ },
346
+ ],
347
+ },
348
+ {
349
+ name: 'steps',
350
+ files: [
351
+ {
352
+ type: 'components',
353
+ path: 'components/steps.tsx',
354
+ },
355
+ ],
356
+ },
357
+ {
358
+ name: 'tabs',
359
+ files: [
360
+ {
361
+ type: 'components',
362
+ path: 'components/tabs.tsx',
363
+ },
364
+ {
365
+ type: 'ui',
366
+ path: 'components/ui/tabs.tsx',
367
+ },
368
+ ],
369
+ },
370
+ {
371
+ name: 'type-table',
372
+ files: [
373
+ {
374
+ type: 'components',
375
+ path: 'components/type-table.tsx',
376
+ },
377
+ ],
378
+ },
379
+ {
380
+ name: 'button',
381
+ unlisted: true,
382
+ files: [
383
+ {
384
+ type: 'ui',
385
+ path: 'components/ui/button.tsx',
386
+ },
387
+ ],
388
+ },
389
+ {
390
+ name: 'popover',
391
+ unlisted: true,
392
+ files: [
393
+ {
394
+ type: 'ui',
395
+ path: 'components/ui/popover.tsx',
396
+ },
397
+ ],
398
+ },
399
+ {
400
+ name: 'scroll-area',
401
+ unlisted: true,
402
+ files: [
403
+ {
404
+ type: 'ui',
405
+ path: 'components/ui/scroll-area.tsx',
406
+ },
407
+ ],
408
+ },
409
+ {
410
+ name: 'collapsible',
411
+ unlisted: true,
412
+ files: [
413
+ {
414
+ type: 'ui',
415
+ path: 'components/ui/collapsible.tsx',
416
+ },
417
+ ],
418
+ },
419
+ ],
420
+ dependencies: {
421
+ '@hanzo/docs-core': null,
422
+ '@hanzo/docs-ui': null,
423
+ '@icons': null,
424
+ react: null,
425
+ },
426
+ };
@@ -0,0 +1,197 @@
1
+ 'use client';
2
+ import type * as PageTree from '@hanzo/docs-core/page-tree';
3
+ import {
4
+ type ComponentProps,
5
+ createContext,
6
+ type ReactNode,
7
+ use,
8
+ useMemo,
9
+ useState,
10
+ } from 'react';
11
+ import { cn } from '@/utils/cn';
12
+ import { TreeContextProvider, useTreeContext } from '@hanzo/docs-ui/contexts/tree';
13
+ import Link from '@hanzo/docs-core/link';
14
+ import { useSearchContext } from '@hanzo/docs-ui/contexts/search';
15
+ import { cva } from 'class-variance-authority';
16
+ import { usePathname } from '@hanzo/docs-core/framework';
17
+
18
+ interface SidebarContext {
19
+ open: boolean;
20
+ setOpen: React.Dispatch<React.SetStateAction<boolean>>;
21
+ }
22
+
23
+ const SidebarContext = createContext<SidebarContext | null>(null);
24
+
25
+ export interface DocsLayoutProps {
26
+ tree: PageTree.Root;
27
+ children: ReactNode;
28
+ }
29
+
30
+ export function DocsLayout({ tree, children }: DocsLayoutProps) {
31
+ return (
32
+ <TreeContextProvider tree={tree}>
33
+ <SidebarProvider>
34
+ <header className="sticky top-0 bg-fd-background h-14 z-20">
35
+ <nav className="flex flex-row items-center gap-2 size-full px-4">
36
+ <Link href="/" className="font-medium mr-auto">
37
+ My Docs
38
+ </Link>
39
+
40
+ <SearchToggle />
41
+ <NavbarSidebarTrigger className="md:hidden" />
42
+ </nav>
43
+ </header>
44
+ <main
45
+ id="nd-docs-layout"
46
+ className="flex flex-1 flex-row [--fd-nav-height:56px]"
47
+ >
48
+ <Sidebar />
49
+ {children}
50
+ </main>
51
+ </SidebarProvider>
52
+ </TreeContextProvider>
53
+ );
54
+ }
55
+
56
+ function SidebarProvider({ children }: { children: ReactNode }) {
57
+ const [open, setOpen] = useState(false);
58
+
59
+ return (
60
+ <SidebarContext
61
+ value={useMemo(
62
+ () => ({
63
+ open,
64
+ setOpen,
65
+ }),
66
+ [open],
67
+ )}
68
+ >
69
+ {children}
70
+ </SidebarContext>
71
+ );
72
+ }
73
+
74
+ function SearchToggle(props: ComponentProps<'button'>) {
75
+ const { enabled, setOpenSearch } = useSearchContext();
76
+ if (!enabled) return;
77
+
78
+ return (
79
+ <button
80
+ {...props}
81
+ className={cn('text-sm', props.className)}
82
+ onClick={() => setOpenSearch(true)}
83
+ >
84
+ Search
85
+ </button>
86
+ );
87
+ }
88
+
89
+ function NavbarSidebarTrigger(props: ComponentProps<'button'>) {
90
+ const { open, setOpen } = use(SidebarContext)!;
91
+
92
+ return (
93
+ <button
94
+ {...props}
95
+ className={cn('text-sm', props.className)}
96
+ onClick={() => setOpen(!open)}
97
+ >
98
+ Sidebar
99
+ </button>
100
+ );
101
+ }
102
+
103
+ function Sidebar() {
104
+ const { root } = useTreeContext();
105
+ const { open } = use(SidebarContext)!;
106
+
107
+ const children = useMemo(() => {
108
+ function renderItems(items: PageTree.Node[]) {
109
+ return items.map((item) => (
110
+ <SidebarItem key={item.$id} item={item}>
111
+ {item.type === 'folder' ? renderItems(item.children) : null}
112
+ </SidebarItem>
113
+ ));
114
+ }
115
+
116
+ return renderItems(root.children);
117
+ }, [root]);
118
+
119
+ return (
120
+ <aside
121
+ className={cn(
122
+ 'fixed flex flex-col shrink-0 p-4 top-14 z-20 text-sm overflow-auto md:sticky md:h-[calc(100dvh-56px)] md:w-[300px]',
123
+ 'max-md:inset-x-0 max-md:bottom-0 max-md:bg-fd-background',
124
+ !open && 'max-md:invisible',
125
+ )}
126
+ >
127
+ {children}
128
+ </aside>
129
+ );
130
+ }
131
+
132
+ const linkVariants = cva(
133
+ 'flex items-center gap-2 w-full py-1.5 rounded-lg text-fd-foreground/80 [&_svg]:size-4',
134
+ {
135
+ variants: {
136
+ active: {
137
+ true: 'text-fd-primary font-medium',
138
+ false: 'hover:text-fd-accent-foreground',
139
+ },
140
+ },
141
+ },
142
+ );
143
+
144
+ function SidebarItem({
145
+ item,
146
+ children,
147
+ }: {
148
+ item: PageTree.Node;
149
+ children: ReactNode;
150
+ }) {
151
+ const pathname = usePathname();
152
+
153
+ if (item.type === 'page') {
154
+ return (
155
+ <Link
156
+ href={item.url}
157
+ className={linkVariants({
158
+ active: pathname === item.url,
159
+ })}
160
+ >
161
+ {item.icon}
162
+ {item.name}
163
+ </Link>
164
+ );
165
+ }
166
+
167
+ if (item.type === 'separator') {
168
+ return (
169
+ <p className="text-fd-muted-foreground mt-6 mb-2 first:mt-0">
170
+ {item.icon}
171
+ {item.name}
172
+ </p>
173
+ );
174
+ }
175
+
176
+ return (
177
+ <div>
178
+ {item.index ? (
179
+ <Link
180
+ className={linkVariants({
181
+ active: pathname === item.index.url,
182
+ })}
183
+ href={item.index.url}
184
+ >
185
+ {item.index.icon}
186
+ {item.index.name}
187
+ </Link>
188
+ ) : (
189
+ <p className={cn(linkVariants(), 'text-start')}>
190
+ {item.icon}
191
+ {item.name}
192
+ </p>
193
+ )}
194
+ <div className="pl-4 border-l flex flex-col">{children}</div>
195
+ </div>
196
+ );
197
+ }