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.
Files changed (250) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/dist/cache-7G6D532T.mjs +1 -0
  4. package/dist/chunk-A4HQPEPU.mjs +1 -0
  5. package/dist/chunk-BA5NH5HU.mjs +1 -0
  6. package/dist/chunk-BQCD3DWG.mjs +1 -0
  7. package/dist/chunk-H63UMKYF.mjs +1 -0
  8. package/dist/chunk-IWHRQHS7.mjs +1 -0
  9. package/dist/chunk-JZXLCA2E.mjs +1 -0
  10. package/dist/chunk-MFU7Q6WF.mjs +1 -0
  11. package/dist/chunk-QYPNX5UN.mjs +1 -0
  12. package/dist/chunk-XEAPSFMB.mjs +1 -0
  13. package/dist/client/components/mdx/index.d.mts +209 -0
  14. package/dist/client/components/mdx/index.d.ts +209 -0
  15. package/dist/client/components/mdx/index.js +1 -0
  16. package/dist/client/components/mdx/index.mjs +1 -0
  17. package/dist/client/hooks/index.d.mts +133 -0
  18. package/dist/client/hooks/index.d.ts +133 -0
  19. package/dist/client/hooks/index.js +1 -0
  20. package/dist/client/hooks/index.mjs +1 -0
  21. package/dist/client/index.d.mts +138 -298
  22. package/dist/client/index.d.ts +138 -298
  23. package/dist/client/index.js +1 -3630
  24. package/dist/client/index.mjs +1 -697
  25. package/dist/client/ssr.d.mts +7 -3
  26. package/dist/client/ssr.d.ts +7 -3
  27. package/dist/client/ssr.js +1 -2928
  28. package/dist/client/ssr.mjs +1 -33
  29. package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
  30. package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
  31. package/dist/node/index.d.mts +2 -4
  32. package/dist/node/index.d.ts +2 -4
  33. package/dist/node/index.js +31 -1161
  34. package/dist/node/index.mjs +31 -736
  35. package/dist/search-dialog-EB3N4TYM.mjs +1 -0
  36. package/dist/types-BuZWFT7r.d.ts +159 -0
  37. package/dist/types-CvT-SGbK.d.mts +159 -0
  38. package/dist/use-routes-5bAtAAYX.d.mts +30 -0
  39. package/dist/use-routes-BefRXY3v.d.ts +30 -0
  40. package/package.json +34 -12
  41. package/src/client/app/config-context.tsx +18 -0
  42. package/src/client/app/docs-layout.tsx +14 -0
  43. package/src/client/app/index.tsx +137 -262
  44. package/src/client/app/mdx-component.tsx +52 -0
  45. package/src/client/app/mdx-components-context.tsx +23 -0
  46. package/src/client/app/mdx-page.tsx +20 -0
  47. package/src/client/app/preload.tsx +38 -30
  48. package/src/client/app/router.tsx +30 -0
  49. package/src/client/app/scroll-handler.tsx +40 -0
  50. package/src/client/app/theme-context.tsx +75 -0
  51. package/src/client/components/default-layout.tsx +80 -0
  52. package/src/client/components/docs-layout.tsx +105 -0
  53. package/src/client/components/icons-dev.tsx +74 -0
  54. package/src/client/components/mdx/admonition.tsx +107 -0
  55. package/src/client/components/mdx/badge.tsx +41 -0
  56. package/src/client/components/mdx/button.tsx +35 -0
  57. package/src/client/components/mdx/card.tsx +124 -0
  58. package/src/client/components/mdx/code-block.tsx +119 -0
  59. package/src/client/components/mdx/component-preview.tsx +47 -0
  60. package/src/client/components/mdx/component-props.tsx +83 -0
  61. package/src/client/components/mdx/field.tsx +66 -0
  62. package/src/client/components/mdx/file-tree.tsx +287 -0
  63. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  64. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  65. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  66. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  67. package/src/client/components/mdx/image.tsx +23 -0
  68. package/src/client/components/mdx/index.ts +53 -0
  69. package/src/client/components/mdx/link.tsx +38 -0
  70. package/src/client/components/mdx/list.tsx +192 -0
  71. package/src/client/components/mdx/table.tsx +156 -0
  72. package/src/client/components/mdx/tabs.tsx +135 -0
  73. package/src/client/components/mdx/video.tsx +68 -0
  74. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  75. package/src/client/components/primitives/button-group.tsx +54 -0
  76. package/src/client/components/primitives/button.tsx +145 -0
  77. package/src/client/components/primitives/helpers/observer.ts +120 -0
  78. package/src/client/components/primitives/index.ts +17 -0
  79. package/src/client/components/primitives/link.tsx +122 -0
  80. package/src/client/components/primitives/menu.tsx +159 -0
  81. package/src/client/components/primitives/navbar.tsx +359 -0
  82. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  83. package/src/client/components/primitives/on-this-page.tsx +461 -0
  84. package/src/client/components/primitives/page-nav.tsx +87 -0
  85. package/src/client/components/primitives/popover.tsx +47 -0
  86. package/src/client/components/primitives/search-dialog.tsx +183 -0
  87. package/src/client/components/primitives/sidebar.tsx +154 -0
  88. package/src/client/components/primitives/tabs.tsx +90 -0
  89. package/src/client/components/primitives/tooltip.tsx +83 -0
  90. package/src/client/components/primitives/types.ts +11 -0
  91. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  92. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  93. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  94. package/src/client/components/ui-base/github-stars.tsx +27 -0
  95. package/src/client/components/ui-base/head.tsx +69 -0
  96. package/src/client/components/ui-base/loading.tsx +87 -0
  97. package/src/client/components/ui-base/navbar.tsx +138 -0
  98. package/src/client/components/ui-base/not-found.tsx +24 -0
  99. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  100. package/src/client/components/ui-base/page-nav.tsx +39 -0
  101. package/src/client/components/ui-base/powered-by.tsx +19 -0
  102. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  103. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  104. package/src/client/components/ui-base/sidebar.tsx +104 -0
  105. package/src/client/components/ui-base/tabs.tsx +65 -0
  106. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  107. package/src/client/hooks/index.ts +12 -0
  108. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  109. package/src/client/hooks/use-i18n.ts +84 -0
  110. package/src/client/hooks/use-localized-to.ts +95 -0
  111. package/src/client/hooks/use-location.ts +5 -0
  112. package/src/client/hooks/use-navbar.ts +60 -0
  113. package/src/client/hooks/use-onthispage.ts +23 -0
  114. package/src/client/hooks/use-page-nav.ts +22 -0
  115. package/src/client/hooks/use-routes.ts +72 -0
  116. package/src/client/hooks/use-search.ts +71 -0
  117. package/src/client/hooks/use-sidebar.ts +49 -0
  118. package/src/client/hooks/use-tabs.ts +43 -0
  119. package/src/client/hooks/use-version.ts +78 -0
  120. package/src/client/index.ts +55 -17
  121. package/src/client/integrations/codesandbox.ts +179 -0
  122. package/src/client/ssr.tsx +27 -16
  123. package/src/client/theme/neutral.css +360 -0
  124. package/src/client/types.ts +131 -27
  125. package/src/client/utils/cn.ts +6 -0
  126. package/src/client/utils/copy-clipboard.ts +22 -0
  127. package/src/client/utils/get-base-file-path.ts +21 -0
  128. package/src/client/utils/github.ts +121 -0
  129. package/src/client/utils/use-on-change.ts +15 -0
  130. package/src/client/virtual.d.ts +24 -0
  131. package/src/node/cache.ts +156 -156
  132. package/src/node/config.ts +159 -103
  133. package/src/node/index.ts +13 -13
  134. package/src/node/mdx.ts +213 -61
  135. package/src/node/plugin/entry.ts +29 -18
  136. package/src/node/plugin/html.ts +11 -11
  137. package/src/node/plugin/index.ts +161 -84
  138. package/src/node/plugin/types.ts +2 -4
  139. package/src/node/routes/cache.ts +6 -6
  140. package/src/node/routes/index.ts +206 -113
  141. package/src/node/routes/parser.ts +102 -82
  142. package/src/node/routes/sorter.ts +15 -15
  143. package/src/node/routes/types.ts +24 -24
  144. package/src/node/ssg/index.ts +73 -47
  145. package/src/node/ssg/meta.ts +4 -4
  146. package/src/node/ssg/options.ts +5 -5
  147. package/src/node/ssg/sitemap.ts +14 -14
  148. package/src/node/utils.ts +54 -31
  149. package/tsconfig.json +25 -20
  150. package/tsup.config.ts +23 -14
  151. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  152. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  153. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  154. package/dist/Video-KNTY5BNO.mjs +0 -6
  155. package/dist/cache-KNL5B4EE.mjs +0 -12
  156. package/dist/chunk-7SFUJWTB.mjs +0 -211
  157. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  158. package/dist/chunk-FMTOYQLO.mjs +0 -37
  159. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  160. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  161. package/dist/client/index.css +0 -2847
  162. package/dist/client/ssr.css +0 -2847
  163. package/dist/types-Dj-bfnC3.d.mts +0 -74
  164. package/dist/types-Dj-bfnC3.d.ts +0 -74
  165. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  166. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  167. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  168. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  169. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  170. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  171. package/src/client/theme/components/Playground/index.ts +0 -1
  172. package/src/client/theme/components/Playground/playground.css +0 -238
  173. package/src/client/theme/components/Video/Video.tsx +0 -84
  174. package/src/client/theme/components/Video/index.ts +0 -1
  175. package/src/client/theme/components/Video/video.css +0 -41
  176. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  177. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  178. package/src/client/theme/components/mdx/Button.tsx +0 -50
  179. package/src/client/theme/components/mdx/Card.tsx +0 -80
  180. package/src/client/theme/components/mdx/Field.tsx +0 -60
  181. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  182. package/src/client/theme/components/mdx/List.tsx +0 -57
  183. package/src/client/theme/components/mdx/Table.tsx +0 -151
  184. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  185. package/src/client/theme/components/mdx/index.ts +0 -27
  186. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  187. package/src/client/theme/icons/bun.tsx +0 -62
  188. package/src/client/theme/icons/deno.tsx +0 -20
  189. package/src/client/theme/icons/discord.tsx +0 -12
  190. package/src/client/theme/icons/github.tsx +0 -15
  191. package/src/client/theme/icons/npm.tsx +0 -13
  192. package/src/client/theme/icons/pnpm.tsx +0 -72
  193. package/src/client/theme/icons/twitter.tsx +0 -12
  194. package/src/client/theme/styles/markdown.css +0 -394
  195. package/src/client/theme/styles/variables.css +0 -175
  196. package/src/client/theme/styles.css +0 -39
  197. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  198. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  199. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  200. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  201. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  202. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  203. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  204. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  205. package/src/client/theme/ui/Footer/footer.css +0 -32
  206. package/src/client/theme/ui/Head/Head.tsx +0 -69
  207. package/src/client/theme/ui/Head/index.ts +0 -1
  208. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  209. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  210. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  211. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  212. package/src/client/theme/ui/Layout/base.css +0 -106
  213. package/src/client/theme/ui/Layout/index.ts +0 -2
  214. package/src/client/theme/ui/Layout/pagination.css +0 -72
  215. package/src/client/theme/ui/Layout/responsive.css +0 -47
  216. package/src/client/theme/ui/Link/Link.tsx +0 -392
  217. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  218. package/src/client/theme/ui/Link/index.ts +0 -2
  219. package/src/client/theme/ui/Link/link-preview.css +0 -48
  220. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  221. package/src/client/theme/ui/Loading/index.ts +0 -1
  222. package/src/client/theme/ui/Loading/loading.css +0 -30
  223. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  224. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  225. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  226. package/src/client/theme/ui/Navbar/index.ts +0 -2
  227. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  228. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  229. package/src/client/theme/ui/NotFound/index.ts +0 -1
  230. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  231. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  232. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  233. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  234. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  235. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  236. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  237. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  238. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  239. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  240. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  241. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  242. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  243. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  244. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  245. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  246. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  247. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  248. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  249. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  250. 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
+ }