boltdocs 1.10.2 → 1.11.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/package.json +29 -7
- package/src/client/app/config-context.tsx +18 -0
- package/src/client/app/docs-layout.tsx +14 -0
- package/src/client/app/index.tsx +132 -260
- package/src/client/app/mdx-component.tsx +52 -0
- package/src/client/app/mdx-components-context.tsx +23 -0
- package/src/client/app/mdx-page.tsx +20 -0
- package/src/client/app/preload.tsx +38 -30
- package/src/client/app/router.tsx +30 -0
- package/src/client/app/scroll-handler.tsx +40 -0
- package/src/client/app/theme-context.tsx +75 -0
- package/src/client/components/default-layout.tsx +80 -0
- package/src/client/components/docs-layout.tsx +105 -0
- package/src/client/components/icons-dev.tsx +74 -0
- package/src/client/components/mdx/admonition.tsx +107 -0
- package/src/client/components/mdx/badge.tsx +41 -0
- package/src/client/components/mdx/button.tsx +35 -0
- package/src/client/components/mdx/card.tsx +124 -0
- package/src/client/components/mdx/code-block.tsx +119 -0
- package/src/client/components/mdx/component-preview.tsx +47 -0
- package/src/client/components/mdx/component-props.tsx +83 -0
- package/src/client/components/mdx/field.tsx +66 -0
- package/src/client/components/mdx/file-tree.tsx +287 -0
- package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
- package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
- package/src/client/components/mdx/hooks/useTable.ts +74 -0
- package/src/client/components/mdx/hooks/useTabs.ts +68 -0
- package/src/client/components/mdx/image.tsx +23 -0
- package/src/client/components/mdx/index.ts +53 -0
- package/src/client/components/mdx/link.tsx +38 -0
- package/src/client/components/mdx/list.tsx +192 -0
- package/src/client/components/mdx/table.tsx +156 -0
- package/src/client/components/mdx/tabs.tsx +135 -0
- package/src/client/components/mdx/video.tsx +68 -0
- package/src/client/components/primitives/breadcrumbs.tsx +79 -0
- package/src/client/components/primitives/button-group.tsx +54 -0
- package/src/client/components/primitives/button.tsx +145 -0
- package/src/client/components/primitives/helpers/observer.ts +120 -0
- package/src/client/components/primitives/index.ts +17 -0
- package/src/client/components/primitives/link.tsx +122 -0
- package/src/client/components/primitives/menu.tsx +159 -0
- package/src/client/components/primitives/navbar.tsx +359 -0
- package/src/client/components/primitives/navigation-menu.tsx +116 -0
- package/src/client/components/primitives/on-this-page.tsx +461 -0
- package/src/client/components/primitives/page-nav.tsx +87 -0
- package/src/client/components/primitives/popover.tsx +47 -0
- package/src/client/components/primitives/search-dialog.tsx +183 -0
- package/src/client/components/primitives/sidebar.tsx +154 -0
- package/src/client/components/primitives/tabs.tsx +90 -0
- package/src/client/components/primitives/tooltip.tsx +83 -0
- package/src/client/components/primitives/types.ts +11 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
- package/src/client/components/ui-base/copy-markdown.tsx +112 -0
- package/src/client/components/ui-base/error-boundary.tsx +52 -0
- package/src/client/components/ui-base/github-stars.tsx +27 -0
- package/src/client/components/ui-base/head.tsx +69 -0
- package/src/client/components/ui-base/loading.tsx +87 -0
- package/src/client/components/ui-base/navbar.tsx +138 -0
- package/src/client/components/ui-base/not-found.tsx +24 -0
- package/src/client/components/ui-base/on-this-page.tsx +152 -0
- package/src/client/components/ui-base/page-nav.tsx +39 -0
- package/src/client/components/ui-base/powered-by.tsx +19 -0
- package/src/client/components/ui-base/progress-bar.tsx +67 -0
- package/src/client/components/ui-base/search-dialog.tsx +82 -0
- package/src/client/components/ui-base/sidebar.tsx +104 -0
- package/src/client/components/ui-base/tabs.tsx +65 -0
- package/src/client/components/ui-base/theme-toggle.tsx +32 -0
- package/src/client/hooks/index.ts +12 -0
- package/src/client/hooks/use-breadcrumbs.ts +22 -0
- package/src/client/hooks/use-i18n.ts +84 -0
- package/src/client/hooks/use-localized-to.ts +95 -0
- package/src/client/hooks/use-location.ts +5 -0
- package/src/client/hooks/use-navbar.ts +60 -0
- package/src/client/hooks/use-onthispage.ts +23 -0
- package/src/client/hooks/use-page-nav.ts +22 -0
- package/src/client/hooks/use-routes.ts +72 -0
- package/src/client/hooks/use-search.ts +71 -0
- package/src/client/hooks/use-sidebar.ts +49 -0
- package/src/client/hooks/use-tabs.ts +43 -0
- package/src/client/hooks/use-version.ts +78 -0
- package/src/client/index.ts +55 -17
- package/src/client/integrations/codesandbox.ts +179 -0
- package/src/client/ssr.tsx +27 -16
- package/src/client/theme/neutral.css +360 -0
- package/src/client/types.ts +131 -27
- package/src/client/utils/cn.ts +6 -0
- package/src/client/utils/copy-clipboard.ts +22 -0
- package/src/client/utils/get-base-file-path.ts +21 -0
- package/src/client/utils/github.ts +121 -0
- package/src/client/utils/use-on-change.ts +15 -0
- package/src/client/virtual.d.ts +24 -0
- package/src/node/cache.ts +156 -156
- package/src/node/config.ts +159 -103
- package/src/node/index.ts +13 -13
- package/src/node/mdx.ts +213 -61
- package/src/node/plugin/entry.ts +29 -18
- package/src/node/plugin/html.ts +11 -11
- package/src/node/plugin/index.ts +161 -83
- package/src/node/plugin/types.ts +2 -4
- package/src/node/routes/cache.ts +6 -6
- package/src/node/routes/index.ts +206 -113
- package/src/node/routes/parser.ts +106 -81
- package/src/node/routes/sorter.ts +15 -15
- package/src/node/routes/types.ts +24 -24
- package/src/node/ssg/index.ts +46 -46
- package/src/node/ssg/meta.ts +4 -4
- package/src/node/ssg/options.ts +5 -5
- package/src/node/ssg/sitemap.ts +14 -14
- package/src/node/utils.ts +31 -31
- package/tsconfig.json +25 -20
- package/tsup.config.ts +23 -14
- package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
- package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
- package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
- package/dist/Video-KNTY5BNO.mjs +0 -6
- package/dist/cache-KNL5B4EE.mjs +0 -12
- package/dist/chunk-7SFUJWTB.mjs +0 -211
- package/dist/chunk-FFBNU6IJ.mjs +0 -386
- package/dist/chunk-FMTOYQLO.mjs +0 -37
- package/dist/chunk-TKLQWU7H.mjs +0 -1920
- package/dist/chunk-Z7JHYNAS.mjs +0 -57
- package/dist/client/index.css +0 -2847
- package/dist/client/index.d.mts +0 -372
- package/dist/client/index.d.ts +0 -372
- package/dist/client/index.js +0 -3630
- package/dist/client/index.mjs +0 -697
- package/dist/client/ssr.css +0 -2847
- package/dist/client/ssr.d.mts +0 -27
- package/dist/client/ssr.d.ts +0 -27
- package/dist/client/ssr.js +0 -2928
- package/dist/client/ssr.mjs +0 -33
- package/dist/config-BsFQ-ErD.d.mts +0 -159
- package/dist/config-BsFQ-ErD.d.ts +0 -159
- package/dist/node/index.d.mts +0 -91
- package/dist/node/index.d.ts +0 -91
- package/dist/node/index.js +0 -1187
- package/dist/node/index.mjs +0 -762
- package/dist/types-Dj-bfnC3.d.mts +0 -74
- package/dist/types-Dj-bfnC3.d.ts +0 -74
- package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
- package/src/client/theme/components/CodeBlock/index.ts +0 -1
- package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
- package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
- package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
- package/src/client/theme/components/Playground/Playground.tsx +0 -180
- package/src/client/theme/components/Playground/index.ts +0 -1
- package/src/client/theme/components/Playground/playground.css +0 -238
- package/src/client/theme/components/Video/Video.tsx +0 -84
- package/src/client/theme/components/Video/index.ts +0 -1
- package/src/client/theme/components/Video/video.css +0 -41
- package/src/client/theme/components/mdx/Admonition.tsx +0 -80
- package/src/client/theme/components/mdx/Badge.tsx +0 -31
- package/src/client/theme/components/mdx/Button.tsx +0 -50
- package/src/client/theme/components/mdx/Card.tsx +0 -80
- package/src/client/theme/components/mdx/Field.tsx +0 -60
- package/src/client/theme/components/mdx/FileTree.tsx +0 -229
- package/src/client/theme/components/mdx/List.tsx +0 -57
- package/src/client/theme/components/mdx/Table.tsx +0 -151
- package/src/client/theme/components/mdx/Tabs.tsx +0 -123
- package/src/client/theme/components/mdx/index.ts +0 -27
- package/src/client/theme/components/mdx/mdx-components.css +0 -764
- package/src/client/theme/icons/bun.tsx +0 -62
- package/src/client/theme/icons/deno.tsx +0 -20
- package/src/client/theme/icons/discord.tsx +0 -12
- package/src/client/theme/icons/github.tsx +0 -15
- package/src/client/theme/icons/npm.tsx +0 -13
- package/src/client/theme/icons/pnpm.tsx +0 -72
- package/src/client/theme/icons/twitter.tsx +0 -12
- package/src/client/theme/styles/markdown.css +0 -394
- package/src/client/theme/styles/variables.css +0 -175
- package/src/client/theme/styles.css +0 -39
- package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
- package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
- package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
- package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
- package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
- package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
- package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
- package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
- package/src/client/theme/ui/Footer/footer.css +0 -32
- package/src/client/theme/ui/Head/Head.tsx +0 -69
- package/src/client/theme/ui/Head/index.ts +0 -1
- package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
- package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
- package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
- package/src/client/theme/ui/Layout/Layout.tsx +0 -203
- package/src/client/theme/ui/Layout/base.css +0 -106
- package/src/client/theme/ui/Layout/index.ts +0 -2
- package/src/client/theme/ui/Layout/pagination.css +0 -72
- package/src/client/theme/ui/Layout/responsive.css +0 -47
- package/src/client/theme/ui/Link/Link.tsx +0 -392
- package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
- package/src/client/theme/ui/Link/index.ts +0 -2
- package/src/client/theme/ui/Link/link-preview.css +0 -48
- package/src/client/theme/ui/Loading/Loading.tsx +0 -10
- package/src/client/theme/ui/Loading/index.ts +0 -1
- package/src/client/theme/ui/Loading/loading.css +0 -30
- package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
- package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
- package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
- package/src/client/theme/ui/Navbar/index.ts +0 -2
- package/src/client/theme/ui/Navbar/navbar.css +0 -347
- package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
- package/src/client/theme/ui/NotFound/index.ts +0 -1
- package/src/client/theme/ui/NotFound/not-found.css +0 -64
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
- package/src/client/theme/ui/OnThisPage/index.ts +0 -1
- package/src/client/theme/ui/OnThisPage/toc.css +0 -152
- package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
- package/src/client/theme/ui/PoweredBy/index.ts +0 -1
- package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
- package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
- package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
- package/src/client/theme/ui/ProgressBar/index.ts +0 -1
- package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
- package/src/client/theme/ui/SearchDialog/index.ts +0 -1
- package/src/client/theme/ui/SearchDialog/search.css +0 -152
- package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
- package/src/client/theme/ui/Sidebar/index.ts +0 -1
- package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
- package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
- package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
- package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
- package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
- package/src/client/utils.ts +0 -49
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as RAC from 'react-aria-components'
|
|
2
|
+
import { Search, Hash, FileText, CornerDownLeft } from 'lucide-react'
|
|
3
|
+
import { cn } from '@client/utils/cn'
|
|
4
|
+
import type { ComponentBase } from './types'
|
|
5
|
+
|
|
6
|
+
export interface SearchDialogProps extends ComponentBase {
|
|
7
|
+
isOpen?: boolean
|
|
8
|
+
onOpenChange?: (isOpen: boolean) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SearchDialogItemProps
|
|
12
|
+
extends Omit<RAC.ListBoxItemProps, 'children'> {
|
|
13
|
+
className?: string
|
|
14
|
+
children: React.ReactNode
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SearchDialogItemIconProps {
|
|
18
|
+
isHeading?: boolean
|
|
19
|
+
className?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const SearchDialogRoot = ({
|
|
23
|
+
children,
|
|
24
|
+
isOpen,
|
|
25
|
+
onOpenChange,
|
|
26
|
+
className,
|
|
27
|
+
}: SearchDialogProps) => {
|
|
28
|
+
return (
|
|
29
|
+
<RAC.ModalOverlay
|
|
30
|
+
isOpen={isOpen}
|
|
31
|
+
onOpenChange={onOpenChange}
|
|
32
|
+
isDismissable
|
|
33
|
+
className={cn(
|
|
34
|
+
'fixed inset-0 z-100 bg-black/40 backdrop-blur-sm px-4 py-4 sm:py-20',
|
|
35
|
+
'entering:animate-in entering:fade-in exiting:animate-out exiting:fade-out',
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
<RAC.Modal
|
|
39
|
+
className={cn(
|
|
40
|
+
'mx-auto w-full max-w-2xl overflow-hidden rounded-xl border border-border-subtle bg-bg-surface shadow-2xl ring-1 ring-black/5 outline-none',
|
|
41
|
+
'entering:animate-in entering:fade-in entering:zoom-in-95 exiting:animate-out exiting:fade-out exiting:zoom-out-95',
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
<RAC.Dialog className="flex flex-col max-h-[70vh] focus:outline-none">
|
|
46
|
+
{children as any}
|
|
47
|
+
</RAC.Dialog>
|
|
48
|
+
</RAC.Modal>
|
|
49
|
+
</RAC.ModalOverlay>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const SearchDialogAutocomplete = ({
|
|
54
|
+
children,
|
|
55
|
+
className,
|
|
56
|
+
...props
|
|
57
|
+
}: RAC.AutocompleteProps<object> & { className?: string }) => {
|
|
58
|
+
return (
|
|
59
|
+
<div className={className}>
|
|
60
|
+
<RAC.Autocomplete {...props} className="flex flex-col min-h-0">
|
|
61
|
+
{children}
|
|
62
|
+
</RAC.Autocomplete>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const SearchDialogInput = ({
|
|
68
|
+
className,
|
|
69
|
+
...props
|
|
70
|
+
}: RAC.InputProps & { className?: string }) => {
|
|
71
|
+
return (
|
|
72
|
+
<RAC.SearchField
|
|
73
|
+
className="flex items-center gap-3 border-b border-border-subtle px-4 py-4"
|
|
74
|
+
autoFocus
|
|
75
|
+
>
|
|
76
|
+
<Search className="h-5 w-5 text-text-muted" />
|
|
77
|
+
<RAC.Input
|
|
78
|
+
{...props}
|
|
79
|
+
className={cn(
|
|
80
|
+
'w-full bg-transparent text-lg text-text-main placeholder-text-muted outline-none',
|
|
81
|
+
className,
|
|
82
|
+
)}
|
|
83
|
+
placeholder="Search documentation..."
|
|
84
|
+
/>
|
|
85
|
+
<div className="flex items-center gap-1.5 rounded-md border border-border-subtle bg-bg-main px-1.5 py-1 text-[10px] font-medium text-text-muted">
|
|
86
|
+
<kbd className="font-sans">ESC</kbd>
|
|
87
|
+
</div>
|
|
88
|
+
</RAC.SearchField>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const SearchDialogList = ({
|
|
93
|
+
children,
|
|
94
|
+
className,
|
|
95
|
+
...props
|
|
96
|
+
}: RAC.ListBoxProps<object> & { className?: string }) => {
|
|
97
|
+
return (
|
|
98
|
+
<RAC.ListBox
|
|
99
|
+
{...props}
|
|
100
|
+
className={cn('flex-1 overflow-y-auto p-2 outline-none', className)}
|
|
101
|
+
>
|
|
102
|
+
{children as any}
|
|
103
|
+
</RAC.ListBox>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const SearchDialogItemRoot = ({
|
|
108
|
+
children,
|
|
109
|
+
className,
|
|
110
|
+
...props
|
|
111
|
+
}: SearchDialogItemProps) => {
|
|
112
|
+
return (
|
|
113
|
+
<RAC.ListBoxItem
|
|
114
|
+
{...props}
|
|
115
|
+
className={cn(
|
|
116
|
+
'group flex items-center gap-3 rounded-lg p-3 text-left outline-none cursor-pointer transition-colors',
|
|
117
|
+
'text-text-muted hover:bg-bg-main hover:text-text-main focus:bg-primary-500 focus:text-white selected:bg-primary-500 selected:text-white',
|
|
118
|
+
className,
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{(itemProps) => (
|
|
122
|
+
<>
|
|
123
|
+
{children}
|
|
124
|
+
{(itemProps.isFocused || itemProps.isSelected) && (
|
|
125
|
+
<div className="ml-auto opacity-50 flex items-center gap-1">
|
|
126
|
+
<span className="text-[10px]">Select</span>
|
|
127
|
+
<CornerDownLeft size={10} />
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
130
|
+
</>
|
|
131
|
+
)}
|
|
132
|
+
</RAC.ListBoxItem>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const SearchDialogItemIcon = ({
|
|
137
|
+
isHeading,
|
|
138
|
+
className,
|
|
139
|
+
}: SearchDialogItemIconProps) => {
|
|
140
|
+
return (
|
|
141
|
+
<div className={cn('shrink-0', className)}>
|
|
142
|
+
{isHeading ? <Hash size={18} /> : <FileText size={18} />}
|
|
143
|
+
</div>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const SearchDialogItemTitle = ({
|
|
148
|
+
children,
|
|
149
|
+
className,
|
|
150
|
+
}: ComponentBase) => {
|
|
151
|
+
return (
|
|
152
|
+
<span
|
|
153
|
+
className={cn('block font-medium truncate flex-1 text-sm', className)}
|
|
154
|
+
>
|
|
155
|
+
{children}
|
|
156
|
+
</span>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const SearchDialogItemBio = ({ children, className }: ComponentBase) => {
|
|
161
|
+
return (
|
|
162
|
+
<span
|
|
163
|
+
className={cn(
|
|
164
|
+
'ml-2 text-xs opacity-70 truncate hidden sm:inline group-focus:opacity-100',
|
|
165
|
+
className,
|
|
166
|
+
)}
|
|
167
|
+
>
|
|
168
|
+
{children}
|
|
169
|
+
</span>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export default {
|
|
174
|
+
Root: SearchDialogRoot,
|
|
175
|
+
Autocomplete: SearchDialogAutocomplete,
|
|
176
|
+
Input: SearchDialogInput,
|
|
177
|
+
List: SearchDialogList,
|
|
178
|
+
Item: Object.assign(SearchDialogItemRoot, {
|
|
179
|
+
Icon: SearchDialogItemIcon,
|
|
180
|
+
Title: SearchDialogItemTitle,
|
|
181
|
+
Bio: SearchDialogItemBio,
|
|
182
|
+
}),
|
|
183
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Link } from './link'
|
|
2
|
+
import * as RAC from 'react-aria-components'
|
|
3
|
+
import { ChevronRight } from 'lucide-react'
|
|
4
|
+
import { cn } from '@client/utils/cn'
|
|
5
|
+
import type { ComponentBase } from './types'
|
|
6
|
+
import type { ComponentRoute } from '@client/types'
|
|
7
|
+
|
|
8
|
+
export interface SidebarGroupProps extends ComponentBase {
|
|
9
|
+
title?: string
|
|
10
|
+
icon?: React.ElementType
|
|
11
|
+
isOpen?: boolean
|
|
12
|
+
onToggle?: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SidebarLinkProps extends ComponentBase {
|
|
16
|
+
label: string
|
|
17
|
+
href: string
|
|
18
|
+
active?: boolean
|
|
19
|
+
icon?: React.ElementType
|
|
20
|
+
badge?: ComponentRoute['badge']
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const Badge = ({ badge }: { badge: ComponentRoute['badge'] }) => {
|
|
24
|
+
const colors = {
|
|
25
|
+
new: 'bg-primary-500/20 text-primary-500',
|
|
26
|
+
updated: 'bg-gray-500/20 text-gray-500',
|
|
27
|
+
deprecated: 'bg-red-500/20 text-red-500',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Expire Badge
|
|
31
|
+
if (typeof badge === 'object' && badge?.expires) {
|
|
32
|
+
const expireDate = new Date(badge.expires)
|
|
33
|
+
const today = new Date()
|
|
34
|
+
const diffTime = expireDate.getTime() - today.getTime()
|
|
35
|
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
|
36
|
+
|
|
37
|
+
if (diffDays === 0) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const text = typeof badge === 'string' ? badge : badge?.text
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<span
|
|
46
|
+
className={cn(
|
|
47
|
+
'ml-auto flex h-4.5 items-center rounded-full text-[9px] font-medium px-1.5 py-0.5 text-center whitespace-nowrap',
|
|
48
|
+
colors[text as keyof typeof colors] || colors.new,
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
{text}
|
|
52
|
+
</span>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const SidebarRoot = ({ children, className }: ComponentBase) => {
|
|
57
|
+
return (
|
|
58
|
+
<aside
|
|
59
|
+
className={cn(
|
|
60
|
+
'boltdocs-sidebar sticky top-navbar hidden lg:flex flex-col shrink-0',
|
|
61
|
+
'w-sidebar h-full',
|
|
62
|
+
'overflow-y-auto border-r border-border-subtle bg-bg-main',
|
|
63
|
+
'py-6 px-4',
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
<nav className="flex-1 space-y-6">{children}</nav>
|
|
68
|
+
</aside>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const SidebarGroup = ({
|
|
73
|
+
children,
|
|
74
|
+
title,
|
|
75
|
+
icon: Icon,
|
|
76
|
+
isOpen = true,
|
|
77
|
+
onToggle,
|
|
78
|
+
className,
|
|
79
|
+
}: SidebarGroupProps) => {
|
|
80
|
+
return (
|
|
81
|
+
<div className={cn('space-y-1', className)}>
|
|
82
|
+
{title && (
|
|
83
|
+
<RAC.Button
|
|
84
|
+
onPress={onToggle}
|
|
85
|
+
className={cn(
|
|
86
|
+
'flex w-full items-center justify-between px-2 py-1.5 text-xs font-bold uppercase tracking-wider outline-none cursor-pointer',
|
|
87
|
+
'text-text-muted hover:text-text-main transition-colors',
|
|
88
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-md',
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
<div className="flex items-center gap-2">
|
|
92
|
+
{Icon && <Icon size={14} />}
|
|
93
|
+
{title}
|
|
94
|
+
</div>
|
|
95
|
+
<ChevronRight
|
|
96
|
+
size={14}
|
|
97
|
+
className={cn(
|
|
98
|
+
'transition-transform duration-200',
|
|
99
|
+
isOpen && 'rotate-90',
|
|
100
|
+
)}
|
|
101
|
+
/>
|
|
102
|
+
</RAC.Button>
|
|
103
|
+
)}
|
|
104
|
+
{isOpen && <div className="space-y-0.5">{children}</div>}
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const SidebarGroupItem = ({ children, className }: ComponentBase) => {
|
|
110
|
+
return <div className={cn(className)}>{children}</div>
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const SidebarLink = ({
|
|
114
|
+
label,
|
|
115
|
+
href,
|
|
116
|
+
active,
|
|
117
|
+
icon: Icon,
|
|
118
|
+
badge,
|
|
119
|
+
className,
|
|
120
|
+
}: SidebarLinkProps) => {
|
|
121
|
+
return (
|
|
122
|
+
<Link
|
|
123
|
+
href={href}
|
|
124
|
+
className={cn(
|
|
125
|
+
'group flex items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm outline-none transition-colors',
|
|
126
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
127
|
+
active
|
|
128
|
+
? 'bg-primary-500/10 text-primary-500 font-medium'
|
|
129
|
+
: 'text-text-muted hover:bg-bg-surface hover:text-text-main',
|
|
130
|
+
className,
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
{Icon && (
|
|
134
|
+
<Icon
|
|
135
|
+
size={16}
|
|
136
|
+
className={cn(
|
|
137
|
+
active
|
|
138
|
+
? 'text-primary-500'
|
|
139
|
+
: 'text-text-muted group-hover:text-text-main',
|
|
140
|
+
)}
|
|
141
|
+
/>
|
|
142
|
+
)}
|
|
143
|
+
<span className="truncate">{label}</span>
|
|
144
|
+
{badge && <Badge badge={badge} />}
|
|
145
|
+
</Link>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default {
|
|
150
|
+
SidebarRoot,
|
|
151
|
+
SidebarGroup,
|
|
152
|
+
SidebarGroupItem,
|
|
153
|
+
SidebarLink,
|
|
154
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { cn } from '@client/utils/cn'
|
|
2
|
+
import type { ComponentBase } from './types'
|
|
3
|
+
|
|
4
|
+
export interface TabsItemProps extends ComponentBase {
|
|
5
|
+
id: string
|
|
6
|
+
selected?: boolean
|
|
7
|
+
onClick?: () => void
|
|
8
|
+
onKeyDown?: (event: React.KeyboardEvent) => void
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TabsIndicatorProps extends ComponentBase {
|
|
13
|
+
style?: React.CSSProperties
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const TabsRoot = ({
|
|
17
|
+
children,
|
|
18
|
+
className = '',
|
|
19
|
+
...props
|
|
20
|
+
}: ComponentBase) => {
|
|
21
|
+
return (
|
|
22
|
+
<div className={cn('w-full', className)} {...props}>
|
|
23
|
+
{children}
|
|
24
|
+
</div>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const TabsList = ({ children, className = '' }: ComponentBase) => {
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
role="tablist"
|
|
32
|
+
className={cn(
|
|
33
|
+
'relative flex flex-row items-center border-b border-border-subtle',
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const TabsItem = ({
|
|
43
|
+
children,
|
|
44
|
+
id,
|
|
45
|
+
selected,
|
|
46
|
+
className = '',
|
|
47
|
+
...props
|
|
48
|
+
}: TabsItemProps) => {
|
|
49
|
+
return (
|
|
50
|
+
<button
|
|
51
|
+
role="tab"
|
|
52
|
+
aria-selected={selected}
|
|
53
|
+
className={cn(
|
|
54
|
+
'flex items-center gap-2 px-4 py-2 text-sm font-medium transition-colors outline-none cursor-pointer bg-transparent border-none',
|
|
55
|
+
selected ? 'text-primary-500' : 'text-text-muted hover:text-text-main',
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
</button>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const TabsContent = ({ children, className = '' }: ComponentBase) => {
|
|
66
|
+
return <div className={cn('p-4 outline-none', className)}>{children}</div>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const TabsIndicator = ({
|
|
70
|
+
className = '',
|
|
71
|
+
style,
|
|
72
|
+
}: TabsIndicatorProps) => {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
className={cn(
|
|
76
|
+
'absolute bottom-0 h-0.5 bg-primary-500 transition-all duration-300',
|
|
77
|
+
className,
|
|
78
|
+
)}
|
|
79
|
+
style={style}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default {
|
|
85
|
+
TabsRoot,
|
|
86
|
+
TabsList,
|
|
87
|
+
TabsItem,
|
|
88
|
+
TabsContent,
|
|
89
|
+
TabsIndicator,
|
|
90
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import * as RAC from 'react-aria-components'
|
|
3
|
+
import { cn } from '@client/utils/cn'
|
|
4
|
+
|
|
5
|
+
export interface TooltipProps extends Omit<RAC.TooltipProps, 'children'> {
|
|
6
|
+
/** The content to show inside the tooltip */
|
|
7
|
+
content: ReactNode
|
|
8
|
+
/** The trigger element (usually a button or link) */
|
|
9
|
+
children: React.ReactElement
|
|
10
|
+
/** Delay in milliseconds before showing the tooltip */
|
|
11
|
+
delay?: number
|
|
12
|
+
/** Delay in milliseconds before hiding the tooltip */
|
|
13
|
+
closeDelay?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Fixed type for TooltipContentProps to match RAC's internal expectations
|
|
17
|
+
export interface TooltipContentProps extends RAC.TooltipProps {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Modern, accessible Tooltip component built with React Aria Components.
|
|
21
|
+
* Featuring glassmorphism, animations, and smart positioning.
|
|
22
|
+
*/
|
|
23
|
+
const TooltipContent = ({
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
...props
|
|
27
|
+
}: TooltipContentProps) => {
|
|
28
|
+
return (
|
|
29
|
+
<RAC.Tooltip
|
|
30
|
+
{...props}
|
|
31
|
+
offset={8}
|
|
32
|
+
className={(values) =>
|
|
33
|
+
cn(
|
|
34
|
+
'group z-50 overflow-visible rounded-md bg-bg-surface/90 px-2.5 py-1.5 text-xs font-medium text-text-main shadow-lg backdrop-blur-md ring-1 ring-border-subtle outline-hidden select-none',
|
|
35
|
+
'data-entering:animate-in data-entering:fade-in data-entering:zoom-in-95 data-entering:duration-100',
|
|
36
|
+
'data-exiting:animate-out data-exiting:fade-out data-exiting:zoom-out-95 data-exiting:duration-75',
|
|
37
|
+
'data-[placement=top]:slide-in-from-bottom-1',
|
|
38
|
+
'data-[placement=bottom]:slide-in-from-top-1',
|
|
39
|
+
'data-[placement=left]:slide-in-from-right-1',
|
|
40
|
+
'data-[placement=right]:slide-in-from-left-1',
|
|
41
|
+
typeof className === 'function' ? className(values) : className,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
{(values) => (
|
|
46
|
+
<>
|
|
47
|
+
<RAC.OverlayArrow>
|
|
48
|
+
<svg
|
|
49
|
+
width={8}
|
|
50
|
+
height={8}
|
|
51
|
+
viewBox="0 0 8 8"
|
|
52
|
+
className="fill-bg-surface/90 stroke-border-subtle group-data-[placement=bottom]:rotate-180 group-data-[placement=left]:-rotate-90 group-data-[placement=right]:rotate-90"
|
|
53
|
+
>
|
|
54
|
+
<title>Arrow</title>
|
|
55
|
+
<path d="M0 0 L4 4 L8 0" />
|
|
56
|
+
</svg>
|
|
57
|
+
</RAC.OverlayArrow>
|
|
58
|
+
{typeof children === 'function' ? children(values) : children}
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
</RAC.Tooltip>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const Tooltip = ({
|
|
66
|
+
content,
|
|
67
|
+
children,
|
|
68
|
+
delay = 500,
|
|
69
|
+
closeDelay = 0,
|
|
70
|
+
...props
|
|
71
|
+
}: TooltipProps) => {
|
|
72
|
+
return (
|
|
73
|
+
<RAC.TooltipTrigger delay={delay} closeDelay={closeDelay}>
|
|
74
|
+
{children}
|
|
75
|
+
<TooltipContent {...props}>{content}</TooltipContent>
|
|
76
|
+
</RAC.TooltipTrigger>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
Tooltip,
|
|
82
|
+
TooltipContent,
|
|
83
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export type ComponentBase = {
|
|
4
|
+
className?: string
|
|
5
|
+
children?: ReactNode
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper to type compound components (e.g. Navbar with Navbar.Link)
|
|
10
|
+
*/
|
|
11
|
+
export type CompoundComponent<P, S> = React.FC<P> & S
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useBreadcrumbs } from '@hooks/use-breadcrumbs'
|
|
2
|
+
import { Home } from 'lucide-react'
|
|
3
|
+
import {
|
|
4
|
+
BreadcrumbsItem,
|
|
5
|
+
BreadcrumbsLink,
|
|
6
|
+
BreadcrumbsRoot,
|
|
7
|
+
BreadcrumbsSeparator,
|
|
8
|
+
} from '@components/primitives/breadcrumbs'
|
|
9
|
+
import { cn } from '@client/utils/cn'
|
|
10
|
+
import { useConfig } from '@client/app/config-context'
|
|
11
|
+
|
|
12
|
+
export function Breadcrumbs() {
|
|
13
|
+
const { crumbs, activeRoute } = useBreadcrumbs()
|
|
14
|
+
const config = useConfig()
|
|
15
|
+
|
|
16
|
+
if (crumbs.length === 0) return null
|
|
17
|
+
|
|
18
|
+
if (!config.themeConfig?.breadcrumbs) return null
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<BreadcrumbsRoot>
|
|
22
|
+
<BreadcrumbsItem>
|
|
23
|
+
<BreadcrumbsLink href="/">
|
|
24
|
+
<Home size={14} />
|
|
25
|
+
</BreadcrumbsLink>
|
|
26
|
+
</BreadcrumbsItem>
|
|
27
|
+
{crumbs.map((crumb, i) => (
|
|
28
|
+
<BreadcrumbsItem key={`crumb-${crumb.href}-${crumb.label}-${i}`}>
|
|
29
|
+
<BreadcrumbsSeparator />
|
|
30
|
+
<BreadcrumbsLink
|
|
31
|
+
href={crumb.href}
|
|
32
|
+
className={cn({
|
|
33
|
+
'font-medium text-text-main': crumb.href === activeRoute?.path,
|
|
34
|
+
})}
|
|
35
|
+
>
|
|
36
|
+
{crumb.label}
|
|
37
|
+
</BreadcrumbsLink>
|
|
38
|
+
</BreadcrumbsItem>
|
|
39
|
+
))}
|
|
40
|
+
</BreadcrumbsRoot>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { Copy, Check, ExternalLink, ChevronDown } from 'lucide-react'
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
ButtonGroup,
|
|
6
|
+
Menu,
|
|
7
|
+
MenuItem,
|
|
8
|
+
MenuTrigger,
|
|
9
|
+
cn,
|
|
10
|
+
} from '@client/components/primitives'
|
|
11
|
+
|
|
12
|
+
import type { ComponentRoute } from '@client/types'
|
|
13
|
+
|
|
14
|
+
export interface CopyMarkdownProps {
|
|
15
|
+
content?: string
|
|
16
|
+
mdxRaw?: string
|
|
17
|
+
route?: ComponentRoute
|
|
18
|
+
config?: boolean | { text?: string; icon?: string }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const useCopyMarkdown = (content: string) => {
|
|
22
|
+
const [copied, setCopied] = useState(false)
|
|
23
|
+
|
|
24
|
+
const handleCopy = () => {
|
|
25
|
+
navigator.clipboard.writeText(content)
|
|
26
|
+
setCopied(true)
|
|
27
|
+
setTimeout(() => setCopied(false), 2000)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const handleOpenRaw = () => {
|
|
31
|
+
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' })
|
|
32
|
+
const url = URL.createObjectURL(blob)
|
|
33
|
+
window.open(url, '_blank')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
copied,
|
|
38
|
+
handleCopy,
|
|
39
|
+
handleOpenRaw,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function CopyMarkdown({ content, mdxRaw, config }: CopyMarkdownProps) {
|
|
44
|
+
const displayContent = mdxRaw || content || ''
|
|
45
|
+
const { copied, handleCopy, handleOpenRaw } = useCopyMarkdown(displayContent)
|
|
46
|
+
|
|
47
|
+
const isEnabled = config !== false
|
|
48
|
+
const buttonText =
|
|
49
|
+
typeof config === 'object'
|
|
50
|
+
? config.text || 'Copy Markdown'
|
|
51
|
+
: 'Copy Markdown'
|
|
52
|
+
|
|
53
|
+
if (!isEnabled || !displayContent) return null
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="relative inline-flex z-100 flex-shrink-0 w-max translate-y-0 active:translate-y-px transition-transform duration-200">
|
|
57
|
+
<ButtonGroup className="rounded-xl border border-border-subtle bg-bg-surface/40 backdrop-blur-md transition-all duration-300 hover:border-primary-500/50 hover:shadow-lg hover:shadow-primary-500/5 group overflow-hidden">
|
|
58
|
+
<Button
|
|
59
|
+
variant="ghost"
|
|
60
|
+
onPress={handleCopy}
|
|
61
|
+
icon={copied ? <Check size={16} /> : <Copy size={16} />}
|
|
62
|
+
iconPosition="left"
|
|
63
|
+
className={cn(
|
|
64
|
+
'px-5 py-2 bg-transparent text-[0.8125rem] font-semibold h-9 border-none shrink-0',
|
|
65
|
+
'text-text-main transition-all duration-300 hover:bg-primary-500/5',
|
|
66
|
+
copied && 'text-emerald-500 hover:bg-emerald-500/5',
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
{copied ? 'Copied!' : buttonText}
|
|
70
|
+
</Button>
|
|
71
|
+
|
|
72
|
+
<MenuTrigger placement="bottom end">
|
|
73
|
+
<Button
|
|
74
|
+
variant="ghost"
|
|
75
|
+
isIconOnly
|
|
76
|
+
icon={<ChevronDown size={14} />}
|
|
77
|
+
className={cn(
|
|
78
|
+
'px-3.5 h-9 border-l border-border-subtle/50 text-text-muted rounded-none bg-transparent shrink-0',
|
|
79
|
+
'transition-all duration-300 hover:bg-primary-500/5 hover:text-primary-500',
|
|
80
|
+
)}
|
|
81
|
+
/>
|
|
82
|
+
<Menu className="w-52">
|
|
83
|
+
<MenuItem
|
|
84
|
+
onAction={handleCopy}
|
|
85
|
+
className="flex flex-row items-start gap-2.5 group"
|
|
86
|
+
>
|
|
87
|
+
<Copy
|
|
88
|
+
size={16}
|
|
89
|
+
className="w-4 h-4 shrink-0 mt-0.5 transition-transform duration-200 group-hover:-translate-y-0.5 text-text-muted group-hover:text-primary-500"
|
|
90
|
+
/>
|
|
91
|
+
<span className="font-medium text-[0.8125rem]">
|
|
92
|
+
Copy Markdown
|
|
93
|
+
</span>
|
|
94
|
+
</MenuItem>
|
|
95
|
+
<MenuItem
|
|
96
|
+
onAction={handleOpenRaw}
|
|
97
|
+
className="flex flex-row items-start gap-2.5 group"
|
|
98
|
+
>
|
|
99
|
+
<ExternalLink
|
|
100
|
+
size={16}
|
|
101
|
+
className="w-4 h-4 shrink-0 mt-0.5 transition-transform duration-200 group-hover:-translate-y-0.5 text-text-muted group-hover:text-primary-500"
|
|
102
|
+
/>
|
|
103
|
+
<span className="font-medium text-[0.8125rem]">
|
|
104
|
+
View as Markdown
|
|
105
|
+
</span>
|
|
106
|
+
</MenuItem>
|
|
107
|
+
</Menu>
|
|
108
|
+
</MenuTrigger>
|
|
109
|
+
</ButtonGroup>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|