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.
- package/bin/boltdocs.js +0 -1
- package/dist/cache-CQKlT4fI.mjs +6 -0
- package/dist/cache-DorPMFgW.cjs +6 -0
- package/dist/cards-BLoSiRuL.d.ts +30 -0
- package/dist/cards-CQn9mXZS.d.cts +30 -0
- package/dist/chunk-Ds5LZdWN.cjs +6 -0
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.d.cts +167 -1338
- package/dist/client/index.d.ts +166 -1337
- package/dist/client/index.js +1 -1
- package/dist/{package-CFP44vfn.cjs → client/mdx.cjs} +1 -1
- package/dist/client/mdx.d.cts +128 -0
- package/dist/client/mdx.d.ts +129 -0
- package/dist/client/mdx.js +6 -0
- package/dist/client/primitives.cjs +6 -0
- package/dist/client/primitives.d.cts +818 -0
- package/dist/client/primitives.d.ts +818 -0
- package/dist/client/primitives.js +6 -0
- package/dist/client/theme/neutral.css +74 -361
- package/dist/client/theme/reset.css +189 -0
- package/dist/docs-layout-BlDhcQRv.cjs +6 -0
- package/dist/docs-layout-BvAOWEJw.js +6 -0
- package/dist/doctor-BQiQhCTl.cjs +6 -0
- package/dist/doctor-COpf35L2.cjs +20 -0
- package/dist/doctor-Dh1XP7Pz.mjs +20 -0
- package/dist/generator-DGW6pkCC.cjs +22 -0
- package/dist/generator-Dv3wEmhZ.mjs +22 -0
- package/dist/icons-dev-CrQLjoQp.js +6 -0
- package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
- package/dist/image-BkIfa9oo.js +6 -0
- package/dist/image-DIGjCPe6.cjs +6 -0
- package/dist/mdx-K0WYBAJ3.js +7 -0
- package/dist/mdx-hpErbRUe.cjs +7 -0
- package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
- package/dist/meta-loader-9IpAHWDS.mjs +6 -0
- package/dist/node/cli-entry.cjs +1 -2
- package/dist/node/cli-entry.mjs +1 -2
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +55 -11
- package/dist/node/index.d.mts +55 -12
- package/dist/node/index.mjs +1 -1
- package/dist/node/routes/worker.cjs +6 -0
- package/dist/node/routes/worker.d.cts +2 -0
- package/dist/node/routes/worker.d.mts +2 -0
- package/dist/node/routes/worker.mjs +6 -0
- package/dist/node-C2nWXElP.mjs +112 -0
- package/dist/node-CinkUtxV.cjs +112 -0
- package/dist/package-BMYLDBBP.cjs +6 -0
- package/dist/{package-Bqbn1AYK.mjs → package-HegMOTL_.mjs} +1 -1
- package/dist/parser-Bh11BsdA.cjs +6 -0
- package/dist/parser-D8eQvE7N.mjs +6 -0
- package/dist/parser-DYRzXWmA.cjs +6 -0
- package/dist/routes-CHf76Ye4.cjs +6 -0
- package/dist/routes-CMUZGI6T.mjs +6 -0
- package/dist/routes-Co1mRM58.cjs +6 -0
- package/dist/search-dialog-BACuzoVX.cjs +6 -0
- package/dist/search-dialog-BKagVT17.js +6 -0
- package/dist/search-dialog-C8w12eUx.js +6 -0
- package/dist/search-dialog-CGyrozZE.cjs +6 -0
- package/dist/search-dialog-D26rUnJ_.cjs +6 -0
- package/dist/sidebar-DKvg6KOc.d.cts +491 -0
- package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
- package/dist/utils-BxNAXhZZ.mjs +7 -0
- package/dist/utils-Clzu7jvb.cjs +7 -0
- package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
- package/dist/worker-pool-BwU8ckrg.cjs +6 -0
- package/package.json +27 -8
- package/src/client/app/doc-page.tsx +9 -5
- package/src/client/app/docs-layout.tsx +17 -3
- package/src/client/app/head.tsx +122 -0
- package/src/client/app/helmet-compat.tsx +36 -0
- package/src/client/app/mdx-component.tsx +5 -52
- package/src/client/app/mdx-components-context.tsx +32 -8
- package/src/client/app/routes-context.tsx +2 -2
- package/src/client/app/scroll-handler.tsx +1 -1
- package/src/client/app/theme-context.tsx +5 -5
- package/src/client/app/ui-context.tsx +42 -0
- package/src/client/components/docs-layout-default.tsx +85 -0
- package/src/client/components/icons-dev.tsx +38 -15
- package/src/client/components/mdx/callout.tsx +97 -0
- package/src/client/components/mdx/card.tsx +73 -98
- package/src/client/components/mdx/cards.tsx +27 -0
- package/src/client/components/mdx/code-block.tsx +37 -17
- package/src/client/components/mdx/field.tsx +24 -56
- package/src/client/components/mdx/image.tsx +36 -15
- package/src/client/components/mdx/index.ts +19 -53
- package/src/client/components/mdx/table.tsx +46 -148
- package/src/client/components/mdx/typographics.tsx +120 -0
- package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
- package/src/client/components/primitives/breadcrumbs.tsx +5 -24
- package/src/client/components/primitives/button.tsx +3 -142
- package/src/client/components/primitives/code-block.tsx +104 -97
- package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
- package/src/client/components/primitives/error-boundary.tsx +107 -0
- package/src/client/components/primitives/heading.tsx +128 -0
- package/src/client/components/primitives/helpers/observer.ts +62 -32
- package/src/client/components/primitives/image.tsx +26 -0
- package/src/client/components/primitives/link.tsx +50 -52
- package/src/client/components/primitives/menu.tsx +25 -49
- package/src/client/components/primitives/navbar.tsx +234 -59
- package/src/client/components/primitives/on-this-page.tsx +169 -40
- package/src/client/components/primitives/page-nav.tsx +11 -39
- package/src/client/components/primitives/popover.tsx +12 -30
- package/src/client/components/primitives/search-dialog.tsx +77 -71
- package/src/client/components/primitives/sidebar.tsx +312 -119
- package/src/client/components/primitives/skeleton.tsx +1 -1
- package/src/client/components/primitives/tabs.tsx +5 -16
- package/src/client/components/primitives/tooltip.tsx +1 -1
- package/src/client/components/ui-base/banner.tsx +66 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
- package/src/client/components/ui-base/copy-markdown.tsx +43 -35
- package/src/client/components/ui-base/error-boundary.tsx +9 -46
- package/src/client/components/ui-base/github-stars.tsx +5 -3
- package/src/client/components/ui-base/index.ts +3 -3
- package/src/client/components/ui-base/last-updated.tsx +27 -0
- package/src/client/components/ui-base/navbar.tsx +183 -89
- package/src/client/components/ui-base/not-found.tsx +11 -9
- package/src/client/components/ui-base/on-this-page.tsx +8 -104
- package/src/client/components/ui-base/page-nav.tsx +23 -9
- package/src/client/components/ui-base/search-dialog.tsx +111 -36
- package/src/client/components/ui-base/search-highlight.tsx +10 -0
- package/src/client/components/ui-base/sidebar.tsx +77 -154
- package/src/client/components/ui-base/tabs.tsx +20 -7
- package/src/client/components/ui-base/theme-toggle.tsx +88 -10
- package/src/client/components/ui-base/version-i18n.tsx +80 -0
- package/src/client/hooks/index.ts +2 -1
- package/src/client/hooks/use-analytics.ts +272 -0
- package/src/client/hooks/use-i18n.ts +116 -50
- package/src/client/hooks/use-localized-to.ts +70 -27
- package/src/client/hooks/use-navbar.ts +69 -39
- package/src/client/hooks/use-page-nav.ts +28 -25
- package/src/client/hooks/use-routes.ts +63 -80
- package/src/client/hooks/use-search-highlight.ts +185 -0
- package/src/client/hooks/use-search.ts +12 -3
- package/src/client/hooks/use-sidebar.ts +183 -80
- package/src/client/hooks/use-tabs.ts +3 -4
- package/src/client/hooks/use-version.ts +44 -29
- package/src/client/index.ts +13 -87
- package/src/client/mdx.ts +2 -0
- package/src/client/primitives.ts +19 -0
- package/src/client/ssg/boltdocs-shell.tsx +68 -79
- package/src/client/ssg/create-routes.tsx +268 -72
- package/src/client/ssg/mdx-page.tsx +2 -1
- package/src/client/store/boltdocs-context.tsx +72 -20
- package/src/client/theme/neutral.css +74 -361
- package/src/client/theme/reset.css +189 -0
- package/src/client/types.ts +10 -2
- package/src/client/utils/path.ts +9 -0
- package/src/client/utils/react-to-text.ts +24 -24
- package/src/client/virtual.d.ts +1 -1
- package/src/shared/types.ts +82 -22
- package/dist/node-Bogvkxao.mjs +0 -101
- package/dist/node-CXaog6St.cjs +0 -101
- package/dist/search-dialog-CV3eJzMm.cjs +0 -6
- package/dist/search-dialog-DNTomKgu.js +0 -6
- package/dist/use-search-CS3gH19M.js +0 -6
- package/dist/use-search-DBpJZQuw.cjs +0 -6
- package/src/client/components/mdx/admonition.tsx +0 -91
- package/src/client/components/mdx/badge.tsx +0 -41
- package/src/client/components/mdx/button.tsx +0 -35
- package/src/client/components/mdx/component-preview.tsx +0 -37
- package/src/client/components/mdx/component-props.tsx +0 -83
- package/src/client/components/mdx/file-tree.tsx +0 -325
- package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
- package/src/client/components/mdx/hooks/useTable.ts +0 -74
- package/src/client/components/mdx/hooks/useTabs.ts +0 -68
- package/src/client/components/mdx/link.tsx +0 -38
- package/src/client/components/mdx/list.tsx +0 -192
- package/src/client/components/mdx/tabs.tsx +0 -135
- package/src/client/components/mdx/video.tsx +0 -68
- package/src/client/components/primitives/index.ts +0 -19
- package/src/client/components/primitives/navigation-menu.tsx +0 -114
- package/src/client/components/ui-base/head.tsx +0 -83
- package/src/client/components/ui-base/loading.tsx +0 -57
- package/src/client/components/ui-base/powered-by.tsx +0 -25
- package/src/client/hooks/use-onthispage.ts +0 -23
- 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
|
-
}
|