boltdocs 1.10.2 → 2.0.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/dist/cache-7G6D532T.mjs +1 -0
- package/dist/chunk-A4HQPEPU.mjs +1 -0
- package/dist/chunk-BA5NH5HU.mjs +1 -0
- package/dist/chunk-BQCD3DWG.mjs +1 -0
- package/dist/chunk-H63UMKYF.mjs +1 -0
- package/dist/chunk-IWHRQHS7.mjs +1 -0
- package/dist/chunk-JZXLCA2E.mjs +1 -0
- package/dist/chunk-MFU7Q6WF.mjs +1 -0
- package/dist/chunk-QYPNX5UN.mjs +1 -0
- package/dist/chunk-XEAPSFMB.mjs +1 -0
- package/dist/client/components/mdx/index.d.mts +209 -0
- package/dist/client/components/mdx/index.d.ts +209 -0
- package/dist/client/components/mdx/index.js +1 -0
- package/dist/client/components/mdx/index.mjs +1 -0
- package/dist/client/hooks/index.d.mts +133 -0
- package/dist/client/hooks/index.d.ts +133 -0
- package/dist/client/hooks/index.js +1 -0
- package/dist/client/hooks/index.mjs +1 -0
- package/dist/client/index.d.mts +138 -298
- package/dist/client/index.d.ts +138 -298
- package/dist/client/index.js +1 -3630
- package/dist/client/index.mjs +1 -697
- package/dist/client/ssr.d.mts +7 -3
- package/dist/client/ssr.d.ts +7 -3
- package/dist/client/ssr.js +1 -2928
- package/dist/client/ssr.mjs +1 -33
- package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
- package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
- package/dist/node/index.d.mts +2 -4
- package/dist/node/index.d.ts +2 -4
- package/dist/node/index.js +31 -1161
- package/dist/node/index.mjs +31 -736
- package/dist/search-dialog-EB3N4TYM.mjs +1 -0
- package/dist/types-BuZWFT7r.d.ts +159 -0
- package/dist/types-CvT-SGbK.d.mts +159 -0
- package/dist/use-routes-5bAtAAYX.d.mts +30 -0
- package/dist/use-routes-BefRXY3v.d.ts +30 -0
- package/package.json +34 -12
- package/src/client/app/config-context.tsx +18 -0
- package/src/client/app/docs-layout.tsx +14 -0
- package/src/client/app/index.tsx +137 -262
- 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 -84
- 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 +102 -82
- package/src/node/routes/sorter.ts +15 -15
- package/src/node/routes/types.ts +24 -24
- package/src/node/ssg/index.ts +73 -47
- 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 +54 -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/ssr.css +0 -2847
- 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,122 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Link as RACLink,
|
|
4
|
+
type LinkProps as RACLinkProps,
|
|
5
|
+
} from 'react-aria-components'
|
|
6
|
+
import { useLocation } from 'react-router-dom'
|
|
7
|
+
import { useLocalizedTo } from '@hooks/use-localized-to'
|
|
8
|
+
import { usePreload } from '@client/app/preload'
|
|
9
|
+
import { cn } from '@client/utils/cn'
|
|
10
|
+
|
|
11
|
+
export interface LinkProps extends RACLinkProps {
|
|
12
|
+
/** Should prefetch the page on hover? Default 'hover' */
|
|
13
|
+
prefetch?: 'hover' | 'none'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A primitive Link component that wraps React Aria Components' Link
|
|
18
|
+
* and adds framework-specific logic for path localization and preloading.
|
|
19
|
+
*
|
|
20
|
+
* It uses the global navigation configuration from BoltdocsRouterProvider
|
|
21
|
+
* to handle seamless client-side transitions.
|
|
22
|
+
*/
|
|
23
|
+
export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
24
|
+
(props, ref) => {
|
|
25
|
+
const { href, prefetch = 'hover', onMouseEnter, onFocus, ...rest } = props
|
|
26
|
+
|
|
27
|
+
const localizedHref = useLocalizedTo(href ?? '')
|
|
28
|
+
const { preload } = usePreload()
|
|
29
|
+
|
|
30
|
+
const handleMouseEnter = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
31
|
+
onMouseEnter?.(e)
|
|
32
|
+
if (
|
|
33
|
+
prefetch === 'hover' &&
|
|
34
|
+
typeof localizedHref === 'string' &&
|
|
35
|
+
localizedHref.startsWith('/')
|
|
36
|
+
) {
|
|
37
|
+
preload(localizedHref)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const handleFocus = (e: React.FocusEvent) => {
|
|
42
|
+
onFocus?.(e as any)
|
|
43
|
+
if (
|
|
44
|
+
prefetch === 'hover' &&
|
|
45
|
+
typeof localizedHref === 'string' &&
|
|
46
|
+
localizedHref.startsWith('/')
|
|
47
|
+
) {
|
|
48
|
+
preload(localizedHref)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<RACLink
|
|
54
|
+
{...rest}
|
|
55
|
+
ref={ref}
|
|
56
|
+
href={localizedHref as string}
|
|
57
|
+
onMouseEnter={handleMouseEnter}
|
|
58
|
+
onFocus={handleFocus as any}
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
Link.displayName = 'Link'
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Props for the NavLink component, extending standard Link props.
|
|
67
|
+
*/
|
|
68
|
+
export interface NavLinkProps
|
|
69
|
+
extends Omit<LinkProps, 'className' | 'children'> {
|
|
70
|
+
/**
|
|
71
|
+
* When true, the active state will only be applied if the paths match exactly.
|
|
72
|
+
* Default is false.
|
|
73
|
+
*/
|
|
74
|
+
end?: boolean
|
|
75
|
+
/**
|
|
76
|
+
* Provides access to the active state for conditional children rendering.
|
|
77
|
+
*/
|
|
78
|
+
children?:
|
|
79
|
+
| React.ReactNode
|
|
80
|
+
| ((props: { isActive: boolean }) => React.ReactNode)
|
|
81
|
+
/**
|
|
82
|
+
* Provides access to the active state for conditional styling.
|
|
83
|
+
*/
|
|
84
|
+
className?: string | ((props: { isActive: boolean }) => string)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A primitive NavLink component that provides active state detection.
|
|
89
|
+
*
|
|
90
|
+
* It combines the Link primitive with path matching logic to determine
|
|
91
|
+
* if the link is currently active based on the browser's location.
|
|
92
|
+
*/
|
|
93
|
+
export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
94
|
+
(props, ref) => {
|
|
95
|
+
const { href, end = false, className, children, ...rest } = props
|
|
96
|
+
const location = useLocation()
|
|
97
|
+
const localizedHref = useLocalizedTo(href ?? '')
|
|
98
|
+
|
|
99
|
+
const isActive = end
|
|
100
|
+
? location.pathname === localizedHref
|
|
101
|
+
: location.pathname.startsWith(localizedHref as string)
|
|
102
|
+
|
|
103
|
+
const resolvedClassName =
|
|
104
|
+
typeof className === 'function'
|
|
105
|
+
? className({ isActive })
|
|
106
|
+
: cn(className, isActive && 'active')
|
|
107
|
+
const resolvedChildren =
|
|
108
|
+
typeof children === 'function' ? children({ isActive }) : children
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<Link
|
|
112
|
+
{...rest}
|
|
113
|
+
ref={ref}
|
|
114
|
+
href={href}
|
|
115
|
+
className={resolvedClassName as any}
|
|
116
|
+
>
|
|
117
|
+
{resolvedChildren as any}
|
|
118
|
+
</Link>
|
|
119
|
+
)
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
NavLink.displayName = 'NavLink'
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Check, ChevronRight, Dot } from 'lucide-react'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import * as RAC from 'react-aria-components'
|
|
6
|
+
import { Popover, type PopoverProps } from './popover'
|
|
7
|
+
import { cn } from '@client/utils/cn'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* MenuTrigger wraps a trigger (usually a Button) and a Menu.
|
|
11
|
+
*/
|
|
12
|
+
export interface MenuTriggerProps extends RAC.MenuTriggerProps {
|
|
13
|
+
placement?: PopoverProps['placement']
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function MenuTrigger(props: MenuTriggerProps) {
|
|
17
|
+
const [trigger, menu] = (
|
|
18
|
+
React.Children.toArray(props.children) as React.ReactElement[]
|
|
19
|
+
).slice(0, 2)
|
|
20
|
+
return (
|
|
21
|
+
<RAC.MenuTrigger {...props}>
|
|
22
|
+
{trigger as any}
|
|
23
|
+
<Popover placement={props.placement} className="min-w-[200px]">
|
|
24
|
+
{menu as any}
|
|
25
|
+
</Popover>
|
|
26
|
+
</RAC.MenuTrigger>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* SubmenuTrigger for nested menus.
|
|
32
|
+
*/
|
|
33
|
+
export function SubmenuTrigger(props: RAC.SubmenuTriggerProps) {
|
|
34
|
+
const [trigger, menu] = (
|
|
35
|
+
React.Children.toArray(props.children) as React.ReactElement[]
|
|
36
|
+
).slice(0, 2)
|
|
37
|
+
return (
|
|
38
|
+
<RAC.SubmenuTrigger {...props}>
|
|
39
|
+
{trigger as any}
|
|
40
|
+
<Popover offset={-4} crossOffset={-4}>
|
|
41
|
+
{menu as any}
|
|
42
|
+
</Popover>
|
|
43
|
+
</RAC.SubmenuTrigger>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The Menu container.
|
|
49
|
+
*/
|
|
50
|
+
export function Menu<T extends object>(props: RAC.MenuProps<T>) {
|
|
51
|
+
return (
|
|
52
|
+
<RAC.Menu
|
|
53
|
+
{...props}
|
|
54
|
+
className={RAC.composeRenderProps(props.className, (className) =>
|
|
55
|
+
cn('p-1.5 outline-none max-h-[inherit] overflow-auto', className),
|
|
56
|
+
)}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* MenuItem with support for selection states and submenus.
|
|
63
|
+
*/
|
|
64
|
+
export function MenuItem(props: RAC.MenuItemProps) {
|
|
65
|
+
const textValue =
|
|
66
|
+
props.textValue ||
|
|
67
|
+
(typeof props.children === 'string' ? props.children : undefined)
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<RAC.MenuItem
|
|
71
|
+
{...props}
|
|
72
|
+
textValue={textValue}
|
|
73
|
+
className={RAC.composeRenderProps(
|
|
74
|
+
props.className,
|
|
75
|
+
(className, { isFocused, isPressed, isDisabled }) =>
|
|
76
|
+
cn(
|
|
77
|
+
'group relative flex flex-row items-center gap-2.5 px-3 py-1.5 rounded-lg outline-none cursor-default transition-all duration-200',
|
|
78
|
+
'text-text-main text-[0.8125rem]',
|
|
79
|
+
isFocused && 'bg-primary-500/10 text-primary-600 shadow-sm',
|
|
80
|
+
isPressed && 'scale-[0.98] bg-primary-500/15',
|
|
81
|
+
isDisabled && 'opacity-40 grayscale pointer-events-none',
|
|
82
|
+
className,
|
|
83
|
+
),
|
|
84
|
+
)}
|
|
85
|
+
>
|
|
86
|
+
{RAC.composeRenderProps(
|
|
87
|
+
props.children,
|
|
88
|
+
(children, { selectionMode, isSelected, hasSubmenu }) => (
|
|
89
|
+
<>
|
|
90
|
+
{selectionMode !== 'none' && (
|
|
91
|
+
<span className="flex items-center w-4 h-4 shrink-0 justify-center">
|
|
92
|
+
{isSelected && selectionMode === 'multiple' && (
|
|
93
|
+
<Check className="w-3.5 h-3.5 stroke-[2.5px] text-primary-500 animate-in zoom-in-50 duration-200" />
|
|
94
|
+
)}
|
|
95
|
+
{isSelected && selectionMode === 'single' && (
|
|
96
|
+
<Dot className="w-6 h-6 text-primary-500 animate-in zoom-in-50 duration-200" />
|
|
97
|
+
)}
|
|
98
|
+
</span>
|
|
99
|
+
)}
|
|
100
|
+
<div className="flex-1 flex flex-row items-center gap-2.5 truncate font-medium">
|
|
101
|
+
{children}
|
|
102
|
+
</div>
|
|
103
|
+
{hasSubmenu && (
|
|
104
|
+
<ChevronRight className="w-4 h-4 ml-auto text-text-muted group-focused:text-primary-500/70 transition-colors" />
|
|
105
|
+
)}
|
|
106
|
+
</>
|
|
107
|
+
),
|
|
108
|
+
)}
|
|
109
|
+
</RAC.MenuItem>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* MenuSection for grouping items with an optional header.
|
|
115
|
+
*/
|
|
116
|
+
export interface MenuSectionProps<T> extends RAC.MenuSectionProps<T> {
|
|
117
|
+
title?: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function MenuSection<T extends object>({
|
|
121
|
+
title,
|
|
122
|
+
...props
|
|
123
|
+
}: MenuSectionProps<T>) {
|
|
124
|
+
return (
|
|
125
|
+
<RAC.MenuSection
|
|
126
|
+
{...props}
|
|
127
|
+
className={cn('flex flex-col gap-0.5', props.className)}
|
|
128
|
+
>
|
|
129
|
+
{title && (
|
|
130
|
+
<RAC.Header className="px-3 py-2 text-[10px] font-bold uppercase tracking-[0.075em] text-text-muted/50 select-none">
|
|
131
|
+
{title}
|
|
132
|
+
</RAC.Header>
|
|
133
|
+
)}
|
|
134
|
+
<RAC.Collection items={props.items}>{props.children}</RAC.Collection>
|
|
135
|
+
</RAC.MenuSection>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* MenuSeparator for visual division.
|
|
141
|
+
*/
|
|
142
|
+
export function MenuSeparator(props: RAC.SeparatorProps) {
|
|
143
|
+
return (
|
|
144
|
+
<RAC.Separator
|
|
145
|
+
{...props}
|
|
146
|
+
className="mx-2 my-1.5 border-t border-border-subtle/50"
|
|
147
|
+
/>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Default export for convenience
|
|
152
|
+
export default {
|
|
153
|
+
Root: Menu,
|
|
154
|
+
Item: MenuItem,
|
|
155
|
+
Trigger: MenuTrigger,
|
|
156
|
+
SubTrigger: SubmenuTrigger,
|
|
157
|
+
Section: MenuSection,
|
|
158
|
+
Separator: MenuSeparator,
|
|
159
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { type ReactNode, useState, useEffect } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Separator,
|
|
5
|
+
ToggleButton,
|
|
6
|
+
Link,
|
|
7
|
+
Menu,
|
|
8
|
+
MenuItem,
|
|
9
|
+
MenuTrigger,
|
|
10
|
+
cn,
|
|
11
|
+
} from './index'
|
|
12
|
+
import { Button as ButtonRAC } from 'react-aria-components'
|
|
13
|
+
import { Search, Sun, Moon, ExternalLink, ChevronDown } from 'lucide-react'
|
|
14
|
+
import * as IconsSocials from '@components/icons-dev'
|
|
15
|
+
import type { ComponentBase } from './types'
|
|
16
|
+
import type { BoltdocsSocialLink } from '@node/config'
|
|
17
|
+
|
|
18
|
+
export interface NavbarLinkProps extends Omit<ComponentBase, 'children'> {
|
|
19
|
+
label: ReactNode
|
|
20
|
+
href: string
|
|
21
|
+
active?: boolean
|
|
22
|
+
to?: 'internal' | 'external'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface NavbarLogoProps extends Omit<ComponentBase, 'children'> {
|
|
26
|
+
src: string
|
|
27
|
+
alt: string
|
|
28
|
+
width?: number
|
|
29
|
+
height?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface NavbarSearchTriggerProps extends ComponentBase {
|
|
33
|
+
onPress: () => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface NavbarThemeProps {
|
|
37
|
+
className?: string
|
|
38
|
+
theme: 'dark' | 'light'
|
|
39
|
+
onThemeChange: (isSelected: boolean) => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface NavbarMenuProps extends ComponentBase {
|
|
43
|
+
label: ReactNode
|
|
44
|
+
icon?: ReactNode
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface NavbarVersionProps extends ComponentBase {
|
|
48
|
+
current: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface NavbarItemProps extends Omit<ComponentBase, 'children'> {
|
|
52
|
+
label: string
|
|
53
|
+
onPress?: () => void
|
|
54
|
+
isCurrent?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface NavbarSocialsProps extends ComponentBase {
|
|
58
|
+
icon: string
|
|
59
|
+
link: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const NavbarRoot = ({
|
|
63
|
+
children,
|
|
64
|
+
className,
|
|
65
|
+
...props
|
|
66
|
+
}: ComponentBase) => {
|
|
67
|
+
return (
|
|
68
|
+
<header
|
|
69
|
+
className={cn(
|
|
70
|
+
'boltdocs-navbar sticky top-0 z-50 w-full border-b border-border-subtle bg-bg-main/80 backdrop-blur-md',
|
|
71
|
+
className,
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</header>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const NavbarContent = ({ children, className }: ComponentBase) => {
|
|
81
|
+
return (
|
|
82
|
+
<div
|
|
83
|
+
className={cn(
|
|
84
|
+
'mx-auto flex lg:h-navbar max-w-(--breakpoint-3xl) items-center justify-between px-4 md:px-6',
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
{children}
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const NavbarLeft = ({ children, className }: ComponentBase) => {
|
|
94
|
+
return (
|
|
95
|
+
<div className={cn('flex items-center gap-4', className)}>{children}</div>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const NavbarRight = ({ children, className }: ComponentBase) => {
|
|
100
|
+
return (
|
|
101
|
+
<div className={cn('flex items-center gap-2 md:gap-4', className)}>
|
|
102
|
+
{children}
|
|
103
|
+
</div>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const NavbarCenter = ({ children, className }: ComponentBase) => {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
className={cn(
|
|
111
|
+
'hidden lg:flex flex-1 justify-center items-center gap-4 px-4',
|
|
112
|
+
className,
|
|
113
|
+
)}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const NavbarLogo = ({
|
|
121
|
+
src,
|
|
122
|
+
alt,
|
|
123
|
+
width = 24,
|
|
124
|
+
height = 24,
|
|
125
|
+
className,
|
|
126
|
+
}: NavbarLogoProps) => {
|
|
127
|
+
return (
|
|
128
|
+
<Link
|
|
129
|
+
href="/"
|
|
130
|
+
className={cn('flex items-center gap-2 shrink-0 outline-none', className)}
|
|
131
|
+
>
|
|
132
|
+
{src ? (
|
|
133
|
+
<img
|
|
134
|
+
src={src}
|
|
135
|
+
alt={alt}
|
|
136
|
+
width={width}
|
|
137
|
+
height={height}
|
|
138
|
+
className="h-6 w-6 object-contain"
|
|
139
|
+
/>
|
|
140
|
+
) : null}
|
|
141
|
+
</Link>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const NavbarTitle = ({ children, className }: ComponentBase) => {
|
|
146
|
+
return (
|
|
147
|
+
<span
|
|
148
|
+
className={cn(
|
|
149
|
+
'text-lg font-bold tracking-tight hidden sm:inline-block',
|
|
150
|
+
className,
|
|
151
|
+
)}
|
|
152
|
+
>
|
|
153
|
+
{children}
|
|
154
|
+
</span>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const NavbarLinks = ({ children, className }: ComponentBase) => {
|
|
159
|
+
return (
|
|
160
|
+
<nav
|
|
161
|
+
className={cn(
|
|
162
|
+
'hidden md:flex items-center gap-6 text-sm font-medium',
|
|
163
|
+
className,
|
|
164
|
+
)}
|
|
165
|
+
>
|
|
166
|
+
{children}
|
|
167
|
+
</nav>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const NavbarLink = ({
|
|
172
|
+
label,
|
|
173
|
+
href,
|
|
174
|
+
active,
|
|
175
|
+
to,
|
|
176
|
+
className,
|
|
177
|
+
}: NavbarLinkProps) => {
|
|
178
|
+
return (
|
|
179
|
+
<Link
|
|
180
|
+
href={href}
|
|
181
|
+
target={to === 'external' ? '_blank' : undefined}
|
|
182
|
+
className={cn(
|
|
183
|
+
'transition-colors outline-none hover:text-text-main focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm',
|
|
184
|
+
active ? 'text-primary-500' : 'text-text-muted',
|
|
185
|
+
className,
|
|
186
|
+
)}
|
|
187
|
+
>
|
|
188
|
+
{label as any}
|
|
189
|
+
{to === 'external' && (
|
|
190
|
+
<span className="ml-1 inline-block">
|
|
191
|
+
<ExternalLink size={12} />
|
|
192
|
+
</span>
|
|
193
|
+
)}
|
|
194
|
+
</Link>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export const NavbarSearchTrigger = ({
|
|
199
|
+
className,
|
|
200
|
+
onPress,
|
|
201
|
+
}: NavbarSearchTriggerProps) => {
|
|
202
|
+
const [mounted, setMounted] = useState(false)
|
|
203
|
+
const isMac = mounted && /Mac|iPod|iPhone|iPad/.test(navigator.platform)
|
|
204
|
+
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
setMounted(true)
|
|
207
|
+
}, [])
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<ButtonRAC
|
|
211
|
+
onPress={onPress}
|
|
212
|
+
className={cn(
|
|
213
|
+
'flex items-center gap-2 rounded-full border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-muted outline-none cursor-pointer',
|
|
214
|
+
'transition-colors hover:border-border-strong hover:text-text-main',
|
|
215
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
216
|
+
'w-full max-w-[320px] justify-between',
|
|
217
|
+
className,
|
|
218
|
+
)}
|
|
219
|
+
>
|
|
220
|
+
<div className="flex items-center gap-2">
|
|
221
|
+
<Search size={16} />
|
|
222
|
+
<span className="hidden sm:inline-block">Search docs...</span>
|
|
223
|
+
</div>
|
|
224
|
+
<div className="hidden sm:flex items-center gap-1 pointer-events-none select-none">
|
|
225
|
+
<kbd className="flex h-5 items-center justify-center rounded border border-border-subtle bg-bg-main px-1.5 font-mono text-[10px] font-medium">
|
|
226
|
+
{isMac ? '⌘' : 'Ctrl'}
|
|
227
|
+
</kbd>
|
|
228
|
+
<kbd className="flex h-5 w-5 items-center justify-center rounded border border-border-subtle bg-bg-main font-mono text-[10px] font-medium">
|
|
229
|
+
K
|
|
230
|
+
</kbd>
|
|
231
|
+
</div>
|
|
232
|
+
</ButtonRAC>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export const NavbarTheme = ({
|
|
237
|
+
className,
|
|
238
|
+
theme,
|
|
239
|
+
onThemeChange,
|
|
240
|
+
}: NavbarThemeProps) => {
|
|
241
|
+
return (
|
|
242
|
+
<ToggleButton
|
|
243
|
+
isSelected={theme === 'dark'}
|
|
244
|
+
onChange={onThemeChange}
|
|
245
|
+
className={cn(
|
|
246
|
+
'rounded-md p-2 text-text-muted outline-none cursor-pointer transition-colors',
|
|
247
|
+
'hover:bg-bg-surface hover:text-text-main',
|
|
248
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
249
|
+
className,
|
|
250
|
+
)}
|
|
251
|
+
aria-label="Toggle theme"
|
|
252
|
+
>
|
|
253
|
+
{theme === 'dark' ? <Sun size={20} /> : <Moon size={20} />}
|
|
254
|
+
</ToggleButton>
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export const NavbarMenu = ({
|
|
259
|
+
label,
|
|
260
|
+
children,
|
|
261
|
+
className,
|
|
262
|
+
icon,
|
|
263
|
+
}: NavbarMenuProps) => {
|
|
264
|
+
return (
|
|
265
|
+
<MenuTrigger placement="bottom end">
|
|
266
|
+
<Button
|
|
267
|
+
variant="ghost"
|
|
268
|
+
className={cn(
|
|
269
|
+
'flex items-center gap-1.5 rounded-md px-3 py-1.5 text-text-muted outline-none cursor-pointer transition-colors',
|
|
270
|
+
'hover:bg-bg-surface hover:text-text-main',
|
|
271
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
272
|
+
className,
|
|
273
|
+
)}
|
|
274
|
+
>
|
|
275
|
+
{icon && <span className="flex items-center shrink-0">{icon}</span>}
|
|
276
|
+
<span className="text-[13px] font-bold uppercase tracking-wide">
|
|
277
|
+
{label}
|
|
278
|
+
</span>
|
|
279
|
+
<ChevronDown size={14} className="ml-0.5 opacity-50" />
|
|
280
|
+
</Button>
|
|
281
|
+
<Menu className="min-w-[180px]">{children as any}</Menu>
|
|
282
|
+
</MenuTrigger>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export const NavbarItem = ({
|
|
287
|
+
label,
|
|
288
|
+
className,
|
|
289
|
+
onPress,
|
|
290
|
+
isCurrent,
|
|
291
|
+
}: NavbarItemProps) => {
|
|
292
|
+
return (
|
|
293
|
+
<MenuItem
|
|
294
|
+
onAction={onPress}
|
|
295
|
+
className={cn(
|
|
296
|
+
isCurrent &&
|
|
297
|
+
'bg-primary-500 text-white font-medium hover:bg-primary-600 focus:bg-primary-600 focus:text-white',
|
|
298
|
+
className,
|
|
299
|
+
)}
|
|
300
|
+
>
|
|
301
|
+
{label}
|
|
302
|
+
</MenuItem>
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export const Icon = ({ name }: { name: BoltdocsSocialLink['icon'] }) => {
|
|
307
|
+
if (name === 'github') return <IconsSocials.Github />
|
|
308
|
+
if (name === 'discord') return <IconsSocials.Discord />
|
|
309
|
+
if (name === 'x') return <IconsSocials.XSocial />
|
|
310
|
+
if (name === 'bluesky') return <IconsSocials.Bluesky />
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export const NavbarSocials = ({
|
|
314
|
+
icon,
|
|
315
|
+
link,
|
|
316
|
+
className,
|
|
317
|
+
}: NavbarSocialsProps) => {
|
|
318
|
+
return (
|
|
319
|
+
<Link
|
|
320
|
+
href={link}
|
|
321
|
+
target="_blank"
|
|
322
|
+
rel="noopener noreferrer"
|
|
323
|
+
className={cn(
|
|
324
|
+
'rounded-md p-2 text-text-muted outline-none transition-colors',
|
|
325
|
+
'hover:bg-bg-surface hover:text-text-main',
|
|
326
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
327
|
+
className,
|
|
328
|
+
)}
|
|
329
|
+
>
|
|
330
|
+
<Icon name={icon} />
|
|
331
|
+
</Link>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export const NavbarSplit = ({ className }: ComponentBase) => {
|
|
336
|
+
return (
|
|
337
|
+
<Separator
|
|
338
|
+
orientation="vertical"
|
|
339
|
+
className={cn('h-6 w-px bg-border-subtle mx-1', className)}
|
|
340
|
+
/>
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export default {
|
|
345
|
+
NavbarRoot,
|
|
346
|
+
NavbarLeft,
|
|
347
|
+
NavbarRight,
|
|
348
|
+
NavbarCenter,
|
|
349
|
+
NavbarLogo,
|
|
350
|
+
Title: NavbarTitle,
|
|
351
|
+
Links: NavbarLinks,
|
|
352
|
+
Link: NavbarLink,
|
|
353
|
+
SearchTrigger: NavbarSearchTrigger,
|
|
354
|
+
Theme: NavbarTheme,
|
|
355
|
+
Item: NavbarItem,
|
|
356
|
+
Socials: NavbarSocials,
|
|
357
|
+
Split: NavbarSplit,
|
|
358
|
+
Content: NavbarContent,
|
|
359
|
+
}
|