boltdocs 2.6.2 → 2.7.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 (177) hide show
  1. package/bin/boltdocs.js +0 -1
  2. package/dist/cache-CQKlT4fI.mjs +6 -0
  3. package/dist/cache-DorPMFgW.cjs +6 -0
  4. package/dist/cards-BLoSiRuL.d.ts +30 -0
  5. package/dist/cards-CQn9mXZS.d.cts +30 -0
  6. package/dist/chunk-Ds5LZdWN.cjs +6 -0
  7. package/dist/client/index.cjs +1 -1
  8. package/dist/client/index.d.cts +167 -1338
  9. package/dist/client/index.d.ts +166 -1337
  10. package/dist/client/index.js +1 -1
  11. package/dist/{package-CFP44vfn.cjs → client/mdx.cjs} +1 -1
  12. package/dist/client/mdx.d.cts +128 -0
  13. package/dist/client/mdx.d.ts +129 -0
  14. package/dist/client/mdx.js +6 -0
  15. package/dist/client/primitives.cjs +6 -0
  16. package/dist/client/primitives.d.cts +818 -0
  17. package/dist/client/primitives.d.ts +818 -0
  18. package/dist/client/primitives.js +6 -0
  19. package/dist/client/theme/neutral.css +74 -361
  20. package/dist/client/theme/reset.css +189 -0
  21. package/dist/docs-layout-BlDhcQRv.cjs +6 -0
  22. package/dist/docs-layout-BvAOWEJw.js +6 -0
  23. package/dist/doctor-BQiQhCTl.cjs +6 -0
  24. package/dist/doctor-COpf35L2.cjs +20 -0
  25. package/dist/doctor-Dh1XP7Pz.mjs +20 -0
  26. package/dist/generator-DGW6pkCC.cjs +22 -0
  27. package/dist/generator-Dv3wEmhZ.mjs +22 -0
  28. package/dist/icons-dev-CrQLjoQp.js +6 -0
  29. package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
  30. package/dist/image-BkIfa9oo.js +6 -0
  31. package/dist/image-DIGjCPe6.cjs +6 -0
  32. package/dist/mdx-K0WYBAJ3.js +7 -0
  33. package/dist/mdx-hpErbRUe.cjs +7 -0
  34. package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
  35. package/dist/meta-loader-9IpAHWDS.mjs +6 -0
  36. package/dist/node/cli-entry.cjs +1 -2
  37. package/dist/node/cli-entry.mjs +1 -2
  38. package/dist/node/index.cjs +1 -1
  39. package/dist/node/index.d.cts +55 -11
  40. package/dist/node/index.d.mts +55 -12
  41. package/dist/node/index.mjs +1 -1
  42. package/dist/node/routes/worker.cjs +6 -0
  43. package/dist/node/routes/worker.d.cts +2 -0
  44. package/dist/node/routes/worker.d.mts +2 -0
  45. package/dist/node/routes/worker.mjs +6 -0
  46. package/dist/node-C2nWXElP.mjs +112 -0
  47. package/dist/node-CinkUtxV.cjs +112 -0
  48. package/dist/package-BMYLDBBP.cjs +6 -0
  49. package/dist/{package-Bqbn1AYK.mjs → package-HegMOTL_.mjs} +1 -1
  50. package/dist/parser-Bh11BsdA.cjs +6 -0
  51. package/dist/parser-D8eQvE7N.mjs +6 -0
  52. package/dist/parser-DYRzXWmA.cjs +6 -0
  53. package/dist/routes-CHf76Ye4.cjs +6 -0
  54. package/dist/routes-CMUZGI6T.mjs +6 -0
  55. package/dist/routes-Co1mRM58.cjs +6 -0
  56. package/dist/search-dialog-BACuzoVX.cjs +6 -0
  57. package/dist/search-dialog-BKagVT17.js +6 -0
  58. package/dist/search-dialog-C8w12eUx.js +6 -0
  59. package/dist/search-dialog-CGyrozZE.cjs +6 -0
  60. package/dist/search-dialog-D26rUnJ_.cjs +6 -0
  61. package/dist/sidebar-DKvg6KOc.d.cts +491 -0
  62. package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
  63. package/dist/utils-BxNAXhZZ.mjs +7 -0
  64. package/dist/utils-Clzu7jvb.cjs +7 -0
  65. package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
  66. package/dist/worker-pool-BwU8ckrg.cjs +6 -0
  67. package/package.json +27 -8
  68. package/src/client/app/doc-page.tsx +9 -5
  69. package/src/client/app/docs-layout.tsx +17 -3
  70. package/src/client/app/head.tsx +122 -0
  71. package/src/client/app/helmet-compat.tsx +36 -0
  72. package/src/client/app/mdx-component.tsx +5 -52
  73. package/src/client/app/mdx-components-context.tsx +32 -8
  74. package/src/client/app/routes-context.tsx +2 -2
  75. package/src/client/app/scroll-handler.tsx +1 -1
  76. package/src/client/app/theme-context.tsx +5 -5
  77. package/src/client/app/ui-context.tsx +42 -0
  78. package/src/client/components/docs-layout-default.tsx +85 -0
  79. package/src/client/components/icons-dev.tsx +38 -15
  80. package/src/client/components/mdx/callout.tsx +97 -0
  81. package/src/client/components/mdx/card.tsx +73 -98
  82. package/src/client/components/mdx/cards.tsx +27 -0
  83. package/src/client/components/mdx/code-block.tsx +37 -17
  84. package/src/client/components/mdx/field.tsx +24 -56
  85. package/src/client/components/mdx/image.tsx +36 -15
  86. package/src/client/components/mdx/index.ts +19 -53
  87. package/src/client/components/mdx/table.tsx +46 -148
  88. package/src/client/components/mdx/typographics.tsx +120 -0
  89. package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
  90. package/src/client/components/primitives/breadcrumbs.tsx +5 -24
  91. package/src/client/components/primitives/button.tsx +3 -142
  92. package/src/client/components/primitives/code-block.tsx +104 -97
  93. package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
  94. package/src/client/components/primitives/error-boundary.tsx +107 -0
  95. package/src/client/components/primitives/heading.tsx +128 -0
  96. package/src/client/components/primitives/helpers/observer.ts +62 -32
  97. package/src/client/components/primitives/image.tsx +26 -0
  98. package/src/client/components/primitives/link.tsx +50 -52
  99. package/src/client/components/primitives/menu.tsx +25 -49
  100. package/src/client/components/primitives/navbar.tsx +234 -59
  101. package/src/client/components/primitives/on-this-page.tsx +169 -40
  102. package/src/client/components/primitives/page-nav.tsx +11 -39
  103. package/src/client/components/primitives/popover.tsx +12 -30
  104. package/src/client/components/primitives/search-dialog.tsx +77 -71
  105. package/src/client/components/primitives/sidebar.tsx +312 -119
  106. package/src/client/components/primitives/skeleton.tsx +1 -1
  107. package/src/client/components/primitives/tabs.tsx +5 -16
  108. package/src/client/components/primitives/tooltip.tsx +1 -1
  109. package/src/client/components/ui-base/banner.tsx +66 -0
  110. package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
  111. package/src/client/components/ui-base/copy-markdown.tsx +43 -35
  112. package/src/client/components/ui-base/error-boundary.tsx +9 -46
  113. package/src/client/components/ui-base/github-stars.tsx +5 -3
  114. package/src/client/components/ui-base/index.ts +3 -3
  115. package/src/client/components/ui-base/last-updated.tsx +27 -0
  116. package/src/client/components/ui-base/navbar.tsx +183 -89
  117. package/src/client/components/ui-base/not-found.tsx +11 -9
  118. package/src/client/components/ui-base/on-this-page.tsx +8 -104
  119. package/src/client/components/ui-base/page-nav.tsx +23 -9
  120. package/src/client/components/ui-base/search-dialog.tsx +111 -36
  121. package/src/client/components/ui-base/search-highlight.tsx +10 -0
  122. package/src/client/components/ui-base/sidebar.tsx +77 -154
  123. package/src/client/components/ui-base/tabs.tsx +20 -7
  124. package/src/client/components/ui-base/theme-toggle.tsx +88 -10
  125. package/src/client/components/ui-base/version-i18n.tsx +80 -0
  126. package/src/client/hooks/index.ts +2 -1
  127. package/src/client/hooks/use-analytics.ts +272 -0
  128. package/src/client/hooks/use-i18n.ts +116 -50
  129. package/src/client/hooks/use-localized-to.ts +70 -27
  130. package/src/client/hooks/use-navbar.ts +69 -39
  131. package/src/client/hooks/use-page-nav.ts +28 -25
  132. package/src/client/hooks/use-routes.ts +63 -80
  133. package/src/client/hooks/use-search-highlight.ts +185 -0
  134. package/src/client/hooks/use-search.ts +12 -3
  135. package/src/client/hooks/use-sidebar.ts +183 -80
  136. package/src/client/hooks/use-tabs.ts +3 -4
  137. package/src/client/hooks/use-version.ts +44 -29
  138. package/src/client/index.ts +13 -87
  139. package/src/client/mdx.ts +2 -0
  140. package/src/client/primitives.ts +19 -0
  141. package/src/client/ssg/boltdocs-shell.tsx +68 -79
  142. package/src/client/ssg/create-routes.tsx +268 -72
  143. package/src/client/ssg/mdx-page.tsx +2 -1
  144. package/src/client/store/boltdocs-context.tsx +72 -20
  145. package/src/client/theme/neutral.css +74 -361
  146. package/src/client/theme/reset.css +189 -0
  147. package/src/client/types.ts +10 -2
  148. package/src/client/utils/path.ts +9 -0
  149. package/src/client/utils/react-to-text.ts +24 -24
  150. package/src/client/virtual.d.ts +1 -1
  151. package/src/shared/types.ts +82 -22
  152. package/dist/node-Bogvkxao.mjs +0 -101
  153. package/dist/node-CXaog6St.cjs +0 -101
  154. package/dist/search-dialog-CV3eJzMm.cjs +0 -6
  155. package/dist/search-dialog-DNTomKgu.js +0 -6
  156. package/dist/use-search-CS3gH19M.js +0 -6
  157. package/dist/use-search-DBpJZQuw.cjs +0 -6
  158. package/src/client/components/mdx/admonition.tsx +0 -91
  159. package/src/client/components/mdx/badge.tsx +0 -41
  160. package/src/client/components/mdx/button.tsx +0 -35
  161. package/src/client/components/mdx/component-preview.tsx +0 -37
  162. package/src/client/components/mdx/component-props.tsx +0 -83
  163. package/src/client/components/mdx/file-tree.tsx +0 -325
  164. package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
  165. package/src/client/components/mdx/hooks/useTable.ts +0 -74
  166. package/src/client/components/mdx/hooks/useTabs.ts +0 -68
  167. package/src/client/components/mdx/link.tsx +0 -38
  168. package/src/client/components/mdx/list.tsx +0 -192
  169. package/src/client/components/mdx/tabs.tsx +0 -135
  170. package/src/client/components/mdx/video.tsx +0 -68
  171. package/src/client/components/primitives/index.ts +0 -19
  172. package/src/client/components/primitives/navigation-menu.tsx +0 -114
  173. package/src/client/components/ui-base/head.tsx +0 -83
  174. package/src/client/components/ui-base/loading.tsx +0 -57
  175. package/src/client/components/ui-base/powered-by.tsx +0 -25
  176. package/src/client/hooks/use-onthispage.ts +0 -23
  177. package/src/client/utils/use-on-change.ts +0 -15
@@ -1,83 +0,0 @@
1
- import { cn } from '../../utils/cn'
2
-
3
- export interface PropItem {
4
- name: string
5
- type: string
6
- defaultValue?: string
7
- required?: boolean
8
- description: React.ReactNode
9
- }
10
-
11
- export interface ComponentPropsProps {
12
- title?: string
13
- props: PropItem[]
14
- className?: string
15
- }
16
-
17
- export function ComponentProps({
18
- title,
19
- props,
20
- className = '',
21
- }: ComponentPropsProps) {
22
- return (
23
- <div className={cn('my-6', className)}>
24
- {title && (
25
- <h3 className="text-base font-bold text-text-main mb-3">{title}</h3>
26
- )}
27
- <div className="overflow-x-auto rounded-lg border border-border-subtle">
28
- <table className="w-full border-collapse text-sm">
29
- <thead>
30
- <tr>
31
- <th className="text-left px-4 py-3 border-b-2 border-border-subtle text-xs font-bold uppercase tracking-wider text-text-muted">
32
- Property
33
- </th>
34
- <th className="text-left px-4 py-3 border-b-2 border-border-subtle text-xs font-bold uppercase tracking-wider text-text-muted">
35
- Type
36
- </th>
37
- <th className="text-left px-4 py-3 border-b-2 border-border-subtle text-xs font-bold uppercase tracking-wider text-text-muted">
38
- Default
39
- </th>
40
- <th className="text-left px-4 py-3 border-b-2 border-border-subtle text-xs font-bold uppercase tracking-wider text-text-muted">
41
- Description
42
- </th>
43
- </tr>
44
- </thead>
45
- <tbody>
46
- {props.map((prop, index) => (
47
- <tr
48
- key={`${prop.name}-${index}`}
49
- className="transition-colors hover:bg-bg-surface"
50
- >
51
- <td className="px-4 py-2.5 border-b border-border-subtle">
52
- <code className="rounded bg-bg-surface px-1.5 py-0.5 font-mono text-xs font-bold text-primary-400">
53
- {prop.name}
54
- </code>
55
- {prop.required && (
56
- <span className="ml-1 text-red-400 font-bold">*</span>
57
- )}
58
- </td>
59
- <td className="px-4 py-2.5 border-b border-border-subtle">
60
- <code className="rounded bg-bg-muted px-1.5 py-0.5 font-mono text-xs text-text-muted">
61
- {prop.type}
62
- </code>
63
- </td>
64
- <td className="px-4 py-2.5 border-b border-border-subtle">
65
- {prop.defaultValue ? (
66
- <code className="rounded bg-bg-muted px-1.5 py-0.5 font-mono text-xs text-primary-400">
67
- {prop.defaultValue}
68
- </code>
69
- ) : (
70
- <span className="text-text-dim">—</span>
71
- )}
72
- </td>
73
- <td className="px-4 py-2.5 border-b border-border-subtle text-text-muted">
74
- {prop.description}
75
- </td>
76
- </tr>
77
- ))}
78
- </tbody>
79
- </table>
80
- </div>
81
- </div>
82
- )
83
- }
@@ -1,325 +0,0 @@
1
- import { Children, isValidElement, useMemo } from 'react'
2
- import * as RAC from 'react-aria-components'
3
- import {
4
- Folder,
5
- FileText,
6
- File,
7
- FileCode,
8
- FileImage,
9
- ChevronRight,
10
- } from 'lucide-react'
11
- import { cn } from '../../utils/cn'
12
-
13
- import {
14
- TypeScript,
15
- JavaScript,
16
- React as ReactIcon,
17
- Json,
18
- Css,
19
- BracketsOrange,
20
- Markdown,
21
- Shell,
22
- Yaml,
23
- } from '../icons-dev'
24
-
25
- // --- Constants & Types ---
26
-
27
- const ICON_SIZE = 16
28
- const STROKE_WIDTH = 2
29
-
30
- const FILE_EXTENSION_MAP: Record<
31
- string,
32
- React.ComponentType<{ size?: number }>
33
- > = {
34
- ts: TypeScript,
35
- tsx: ReactIcon,
36
- js: JavaScript,
37
- jsx: ReactIcon,
38
- json: Json,
39
- css: Css,
40
- html: BracketsOrange,
41
- md: Markdown,
42
- mdx: Markdown,
43
- bash: Shell,
44
- sh: Shell,
45
- yaml: Yaml,
46
- yml: Yaml,
47
- }
48
-
49
- const FILE_REGEXES = {
50
- CODE: /\.(ts|tsx|js|jsx|json|mjs|cjs|astro|vue|svelte)$/i,
51
- TEXT: /\.(md|mdx|txt)$/i,
52
- IMAGE: /\.(png|jpg|jpeg|svg|gif)$/i,
53
- } as const
54
-
55
- export interface FileTreeProps {
56
- children: React.ReactNode
57
- }
58
-
59
- interface TreeItemData {
60
- id: string
61
- name: string
62
- comment?: string
63
- isFolder: boolean
64
- children?: TreeItemData[]
65
- }
66
-
67
- // --- Helpers ---
68
-
69
- function getTextContent(node: React.ReactNode): string {
70
- if (typeof node === 'string') return node
71
- if (typeof node === 'number') return node.toString()
72
- if (Array.isArray(node)) return node.map(getTextContent).join('')
73
- if (
74
- isValidElement(node) &&
75
- node.props &&
76
- typeof node.props === 'object' &&
77
- 'children' in node.props
78
- ) {
79
- return getTextContent(
80
- (node.props as { children?: React.ReactNode }).children,
81
- )
82
- }
83
- return ''
84
- }
85
-
86
- function getFileIcon(filename: string, isFolder: boolean) {
87
- const name = filename.toLowerCase()
88
- const iconClass = 'shrink-0 transition-colors duration-200'
89
-
90
- if (isFolder) {
91
- return (
92
- <Folder
93
- size={ICON_SIZE}
94
- strokeWidth={STROKE_WIDTH}
95
- className={cn(iconClass, 'text-primary-400')}
96
- fill="currentColor"
97
- fillOpacity={0.15}
98
- />
99
- )
100
- }
101
-
102
- // Check for specialized language icons
103
- const extension = name.split('.').pop() || ''
104
- const LangIcon = FILE_EXTENSION_MAP[extension]
105
- if (LangIcon) {
106
- return <LangIcon size={ICON_SIZE} />
107
- }
108
-
109
- const fileIconClass = cn(
110
- iconClass,
111
- 'text-text-dim group-hover:text-text-main',
112
- )
113
-
114
- if (FILE_REGEXES.CODE.test(name))
115
- return (
116
- <FileCode
117
- size={ICON_SIZE}
118
- strokeWidth={STROKE_WIDTH}
119
- className={fileIconClass}
120
- />
121
- )
122
- if (FILE_REGEXES.TEXT.test(name))
123
- return (
124
- <FileText
125
- size={ICON_SIZE}
126
- strokeWidth={STROKE_WIDTH}
127
- className={fileIconClass}
128
- />
129
- )
130
- if (FILE_REGEXES.IMAGE.test(name))
131
- return (
132
- <FileImage
133
- size={ICON_SIZE}
134
- strokeWidth={STROKE_WIDTH}
135
- className={fileIconClass}
136
- />
137
- )
138
-
139
- return (
140
- <File
141
- size={ICON_SIZE}
142
- strokeWidth={STROKE_WIDTH}
143
- className={fileIconClass}
144
- />
145
- )
146
- }
147
-
148
- function isListElement(
149
- node: unknown,
150
- tag: 'ul' | 'li',
151
- ): node is React.ReactElement<{ children?: React.ReactNode }> {
152
- if (!isValidElement(node)) return false
153
-
154
- const type = node.type
155
- if (typeof type === 'string') return type === tag
156
- if (typeof type === 'function') {
157
- return type.name === tag || type.name?.toLowerCase() === tag
158
- }
159
-
160
- const props = node.props as any
161
- return props?.originalType === tag || props?.mdxType === tag
162
- }
163
-
164
- function parseLabel(rawLabel: string): { name: string; comment?: string } {
165
- const commentMatch = rawLabel.match(/\s+(\/\/|#)\s+(.*)$/)
166
- if (commentMatch) {
167
- return {
168
- name: rawLabel.slice(0, commentMatch.index).trim(),
169
- comment: commentMatch[2],
170
- }
171
- }
172
- return { name: rawLabel.trim() }
173
- }
174
-
175
- function parseMdxToData(
176
- node: React.ReactNode,
177
- path: string = 'root',
178
- ): TreeItemData[] {
179
- if (!isValidElement(node)) return []
180
-
181
- const items: TreeItemData[] = []
182
-
183
- if (isListElement(node, 'ul')) {
184
- Children.forEach(
185
- (node.props as { children?: React.ReactNode }).children,
186
- (child, index) => {
187
- items.push(...parseMdxToData(child, `${path}-${index}`))
188
- },
189
- )
190
- return items
191
- }
192
-
193
- if (isListElement(node, 'li')) {
194
- const children = Children.toArray(
195
- (node.props as { children?: React.ReactNode }).children,
196
- )
197
- const nestedListIndex = children.findIndex((child) =>
198
- isListElement(child, 'ul'),
199
- )
200
-
201
- const hasNested = nestedListIndex !== -1
202
- const labelNodes = hasNested ? children.slice(0, nestedListIndex) : children
203
- const nestedNodes = hasNested ? children.slice(nestedListIndex) : []
204
-
205
- const rawLabelContent = getTextContent(labelNodes)
206
- const { name, comment } = parseLabel(rawLabelContent)
207
-
208
- const isExplicitDir = name.endsWith('/')
209
- const labelText = isExplicitDir ? name.slice(0, -1) : name
210
- const isFolder = hasNested || isExplicitDir
211
-
212
- items.push({
213
- id: `${path}-${labelText}`,
214
- name: labelText,
215
- comment,
216
- isFolder,
217
- children: hasNested
218
- ? parseMdxToData(nestedNodes[0], `${path}-${labelText}`)
219
- : undefined,
220
- })
221
- return items
222
- }
223
-
224
- if (
225
- node.props &&
226
- typeof node.props === 'object' &&
227
- 'children' in node.props
228
- ) {
229
- Children.forEach(
230
- (node.props as { children?: React.ReactNode }).children,
231
- (child, index) => {
232
- items.push(...parseMdxToData(child, `${path}-${index}`))
233
- },
234
- )
235
- }
236
-
237
- return items
238
- }
239
-
240
- // --- Sub-Components ---
241
-
242
- function FileTreeNode({ item }: { item: TreeItemData }) {
243
- return (
244
- <RAC.TreeItem
245
- id={item.id}
246
- textValue={item.name}
247
- className="outline-none group focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-md"
248
- >
249
- <RAC.TreeItemContent>
250
- {({ isExpanded, hasChildItems }) => (
251
- <div className="flex items-center gap-2 py-1 px-1.5 rounded-md transition-colors hover:bg-primary-500/5 cursor-pointer">
252
- <div
253
- style={{ width: `calc((var(--tree-item-level) - 1) * 1rem)` }}
254
- className="shrink-0"
255
- />
256
- {hasChildItems ? (
257
- <RAC.Button
258
- slot="chevron"
259
- className="outline-none text-text-dim hover:text-primary-400 p-0.5 rounded transition-colors"
260
- >
261
- <ChevronRight
262
- size={14}
263
- strokeWidth={3}
264
- className={cn(
265
- 'transition-transform duration-200',
266
- isExpanded && 'rotate-90',
267
- )}
268
- />
269
- </RAC.Button>
270
- ) : (
271
- <div className="w-[18px]" />
272
- )}
273
-
274
- {getFileIcon(item.name, item.isFolder)}
275
-
276
- <span
277
- className={cn(
278
- 'text-sm transition-colors truncate select-none',
279
- item.isFolder
280
- ? 'font-semibold text-text-main'
281
- : 'text-text-muted group-hover:text-text-main',
282
- )}
283
- >
284
- {item.name}
285
- </span>
286
-
287
- {item.comment && (
288
- <span className="ml-2 text-xs italic text-text-dim opacity-70 group-hover:opacity-100 transition-opacity whitespace-nowrap overflow-hidden text-ellipsis font-sans">
289
- {'//'} {item.comment}
290
- </span>
291
- )}
292
- </div>
293
- )}
294
- </RAC.TreeItemContent>
295
-
296
- {item.children && (
297
- <RAC.Collection items={item.children}>
298
- {(child) => <FileTreeNode item={child} />}
299
- </RAC.Collection>
300
- )}
301
- </RAC.TreeItem>
302
- )
303
- }
304
-
305
- // --- Main Component ---
306
-
307
- export function FileTree({ children }: FileTreeProps) {
308
- const items = useMemo(() => parseMdxToData(children), [children])
309
-
310
- return (
311
- <div className="my-8">
312
- <RAC.Tree
313
- items={items}
314
- aria-label="File Tree"
315
- className={cn(
316
- 'rounded-xl border border-border-subtle bg-bg-surface/50 p-4 font-mono text-sm shadow-sm backdrop-blur-sm outline-none',
317
- 'max-h-[500px] overflow-y-auto scrollbar-thin scrollbar-thumb-border-subtle',
318
- 'focus-visible:ring-2 focus-visible:ring-primary-500/20',
319
- )}
320
- >
321
- {(item) => <FileTreeNode item={item} />}
322
- </RAC.Tree>
323
- </div>
324
- )
325
- }
@@ -1,16 +0,0 @@
1
- import { useMemo } from 'react'
2
- import type { ComponentPreviewProps } from '../component-preview'
3
-
4
- export function useComponentPreview(props: ComponentPreviewProps) {
5
- const { code: propsCode, children, preview } = props
6
- const initialCode = useMemo(() => {
7
- const base = propsCode ?? (typeof children === 'string' ? children : '')
8
- return base.trim()
9
- }, [propsCode, children])
10
-
11
- const previewElement = useMemo(() => {
12
- return preview ?? (typeof children !== 'string' ? children : null)
13
- }, [preview, children])
14
-
15
- return { initialCode, previewElement }
16
- }
@@ -1,74 +0,0 @@
1
- import { useState, useMemo } from 'react'
2
-
3
- interface SortConfig {
4
- key: number
5
- direction: 'asc' | 'desc'
6
- }
7
-
8
- interface UseTableProps {
9
- data?: (string | React.ReactNode)[][]
10
- sortable?: boolean
11
- paginated?: boolean
12
- pageSize?: number
13
- }
14
-
15
- export function useTable({
16
- data,
17
- sortable = false,
18
- paginated = false,
19
- pageSize = 10,
20
- }: UseTableProps) {
21
- const [sortConfig, setSortConfig] = useState<SortConfig | null>(null)
22
- const [currentPage, setCurrentPage] = useState(1)
23
-
24
- const processedData = useMemo(() => {
25
- if (!data) return []
26
- const items = [...data]
27
-
28
- if (sortable && sortConfig !== null) {
29
- items.sort((a, b) => {
30
- const aVal = a[sortConfig.key]
31
- const bVal = b[sortConfig.key]
32
-
33
- const aStr = typeof aVal === 'string' ? aVal : ''
34
- const bStr = typeof bVal === 'string' ? bVal : ''
35
-
36
- if (aStr < bStr) return sortConfig.direction === 'asc' ? -1 : 1
37
- if (aStr > bStr) return sortConfig.direction === 'asc' ? 1 : -1
38
- return 0
39
- })
40
- }
41
-
42
- return items
43
- }, [data, sortConfig, sortable])
44
-
45
- const totalPages = Math.ceil(processedData.length / pageSize)
46
-
47
- const paginatedData = useMemo(() => {
48
- if (!paginated) return processedData
49
- const start = (currentPage - 1) * pageSize
50
- return processedData.slice(start, start + pageSize)
51
- }, [processedData, paginated, currentPage, pageSize])
52
-
53
- const requestSort = (index: number) => {
54
- if (!sortable) return
55
- let direction: 'asc' | 'desc' = 'asc'
56
- if (
57
- sortConfig &&
58
- sortConfig.key === index &&
59
- sortConfig.direction === 'asc'
60
- ) {
61
- direction = 'desc'
62
- }
63
- setSortConfig({ key: index, direction })
64
- }
65
-
66
- return {
67
- sortConfig,
68
- currentPage,
69
- setCurrentPage,
70
- totalPages,
71
- paginatedData,
72
- requestSort,
73
- }
74
- }
@@ -1,68 +0,0 @@
1
- import {
2
- useState,
3
- useRef,
4
- useEffect,
5
- useCallback,
6
- type ReactElement,
7
- type KeyboardEvent,
8
- } from 'react'
9
-
10
- interface UseTabsProps {
11
- initialIndex?: number
12
- tabs: ReactElement<any>[]
13
- }
14
-
15
- export function useTabs({ initialIndex = 0, tabs }: UseTabsProps) {
16
- const defaultActive = tabs[initialIndex]?.props.disabled
17
- ? tabs.findIndex((t) => !t.props.disabled)
18
- : initialIndex
19
-
20
- const [active, setActive] = useState(defaultActive === -1 ? 0 : defaultActive)
21
- const tabRefs = useRef<(HTMLButtonElement | null)[]>([])
22
- const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({
23
- opacity: 0,
24
- transform: 'translateX(0)',
25
- width: 0,
26
- })
27
-
28
- // biome-ignore lint/correctness/useExhaustiveDependencies: updates when content changes
29
- useEffect(() => {
30
- const activeTab = tabRefs.current[active]
31
- if (activeTab) {
32
- setIndicatorStyle({
33
- opacity: 1,
34
- width: activeTab.offsetWidth,
35
- transform: `translateX(${activeTab.offsetLeft}px)`,
36
- })
37
- }
38
- }, [active, tabs])
39
-
40
- const handleKeyDown = useCallback(
41
- (e: KeyboardEvent<HTMLDivElement>) => {
42
- let direction = 0
43
- if (e.key === 'ArrowRight') direction = 1
44
- else if (e.key === 'ArrowLeft') direction = -1
45
-
46
- if (direction !== 0) {
47
- let nextIndex = (active + direction + tabs.length) % tabs.length
48
- while (tabs[nextIndex].props.disabled && nextIndex !== active) {
49
- nextIndex = (nextIndex + direction + tabs.length) % tabs.length
50
- }
51
-
52
- if (nextIndex !== active && !tabs[nextIndex].props.disabled) {
53
- setActive(nextIndex)
54
- tabRefs.current[nextIndex]?.focus()
55
- }
56
- }
57
- },
58
- [active, tabs],
59
- )
60
-
61
- return {
62
- active,
63
- setActive,
64
- tabRefs,
65
- indicatorStyle,
66
- handleKeyDown,
67
- }
68
- }
@@ -1,38 +0,0 @@
1
- import {
2
- Link as LinkPrimitive,
3
- type LinkProps as LinkPrimitiveProps,
4
- } from '../primitives/link'
5
- import { cn } from '../../utils/cn'
6
-
7
- export type LinkProps = LinkPrimitiveProps & {
8
- to: string
9
- children?: React.ReactNode
10
- }
11
-
12
- /**
13
- * A premium Link component for Boltdocs that handles internal and external routing.
14
- */
15
- export function Link({ to, children, className = '', ...props }: LinkProps) {
16
- const isExternal =
17
- to &&
18
- (to.startsWith('http://') ||
19
- to.startsWith('https://') ||
20
- to.startsWith('//'))
21
-
22
- const combinedClassName = cn(
23
- 'text-blue-600 hover:text-blue-800 hover:underline cursor-pointer',
24
- className,
25
- )
26
-
27
- return (
28
- <LinkPrimitive
29
- href={to}
30
- className={combinedClassName}
31
- target={isExternal ? '_blank' : undefined}
32
- rel={isExternal ? 'noopener noreferrer' : undefined}
33
- {...props}
34
- >
35
- {children}
36
- </LinkPrimitive>
37
- )
38
- }