boltdocs 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/bin/boltdocs.js +0 -1
  2. package/dist/cache-CQKlT4fI.mjs +6 -0
  3. package/dist/cache-DorPMFgW.cjs +6 -0
  4. package/dist/cards-BLoSiRuL.d.ts +30 -0
  5. package/dist/cards-CQn9mXZS.d.cts +30 -0
  6. package/dist/chunk-Ds5LZdWN.cjs +6 -0
  7. package/dist/client/index.cjs +1 -1
  8. package/dist/client/index.d.cts +167 -1338
  9. package/dist/client/index.d.ts +166 -1337
  10. package/dist/client/index.js +1 -1
  11. package/dist/{package-CFP44vfn.cjs → client/mdx.cjs} +1 -1
  12. package/dist/client/mdx.d.cts +128 -0
  13. package/dist/client/mdx.d.ts +129 -0
  14. package/dist/client/mdx.js +6 -0
  15. package/dist/client/primitives.cjs +6 -0
  16. package/dist/client/primitives.d.cts +818 -0
  17. package/dist/client/primitives.d.ts +818 -0
  18. package/dist/client/primitives.js +6 -0
  19. package/dist/client/theme/neutral.css +74 -361
  20. package/dist/client/theme/reset.css +189 -0
  21. package/dist/docs-layout-BlDhcQRv.cjs +6 -0
  22. package/dist/docs-layout-BvAOWEJw.js +6 -0
  23. package/dist/doctor-BQiQhCTl.cjs +6 -0
  24. package/dist/doctor-COpf35L2.cjs +20 -0
  25. package/dist/doctor-Dh1XP7Pz.mjs +20 -0
  26. package/dist/generator-DGW6pkCC.cjs +22 -0
  27. package/dist/generator-Dv3wEmhZ.mjs +22 -0
  28. package/dist/icons-dev-CrQLjoQp.js +6 -0
  29. package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
  30. package/dist/image-BkIfa9oo.js +6 -0
  31. package/dist/image-DIGjCPe6.cjs +6 -0
  32. package/dist/mdx-K0WYBAJ3.js +7 -0
  33. package/dist/mdx-hpErbRUe.cjs +7 -0
  34. package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
  35. package/dist/meta-loader-9IpAHWDS.mjs +6 -0
  36. package/dist/node/cli-entry.cjs +1 -2
  37. package/dist/node/cli-entry.mjs +1 -2
  38. package/dist/node/index.cjs +1 -1
  39. package/dist/node/index.d.cts +55 -11
  40. package/dist/node/index.d.mts +55 -12
  41. package/dist/node/index.mjs +1 -1
  42. package/dist/node/routes/worker.cjs +6 -0
  43. package/dist/node/routes/worker.d.cts +2 -0
  44. package/dist/node/routes/worker.d.mts +2 -0
  45. package/dist/node/routes/worker.mjs +6 -0
  46. package/dist/node-C2nWXElP.mjs +112 -0
  47. package/dist/node-CinkUtxV.cjs +112 -0
  48. package/dist/package-BMYLDBBP.cjs +6 -0
  49. package/dist/{package-Bqbn1AYK.mjs → package-HegMOTL_.mjs} +1 -1
  50. package/dist/parser-Bh11BsdA.cjs +6 -0
  51. package/dist/parser-D8eQvE7N.mjs +6 -0
  52. package/dist/parser-DYRzXWmA.cjs +6 -0
  53. package/dist/routes-CHf76Ye4.cjs +6 -0
  54. package/dist/routes-CMUZGI6T.mjs +6 -0
  55. package/dist/routes-Co1mRM58.cjs +6 -0
  56. package/dist/search-dialog-BACuzoVX.cjs +6 -0
  57. package/dist/search-dialog-BKagVT17.js +6 -0
  58. package/dist/search-dialog-C8w12eUx.js +6 -0
  59. package/dist/search-dialog-CGyrozZE.cjs +6 -0
  60. package/dist/search-dialog-D26rUnJ_.cjs +6 -0
  61. package/dist/sidebar-DKvg6KOc.d.cts +491 -0
  62. package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
  63. package/dist/utils-BxNAXhZZ.mjs +7 -0
  64. package/dist/utils-Clzu7jvb.cjs +7 -0
  65. package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
  66. package/dist/worker-pool-BwU8ckrg.cjs +6 -0
  67. package/package.json +27 -8
  68. package/src/client/app/doc-page.tsx +9 -5
  69. package/src/client/app/docs-layout.tsx +17 -3
  70. package/src/client/app/head.tsx +122 -0
  71. package/src/client/app/helmet-compat.tsx +36 -0
  72. package/src/client/app/mdx-component.tsx +5 -52
  73. package/src/client/app/mdx-components-context.tsx +32 -8
  74. package/src/client/app/routes-context.tsx +2 -2
  75. package/src/client/app/scroll-handler.tsx +1 -1
  76. package/src/client/app/theme-context.tsx +5 -5
  77. package/src/client/app/ui-context.tsx +42 -0
  78. package/src/client/components/docs-layout-default.tsx +85 -0
  79. package/src/client/components/icons-dev.tsx +38 -15
  80. package/src/client/components/mdx/callout.tsx +97 -0
  81. package/src/client/components/mdx/card.tsx +73 -98
  82. package/src/client/components/mdx/cards.tsx +27 -0
  83. package/src/client/components/mdx/code-block.tsx +37 -17
  84. package/src/client/components/mdx/field.tsx +24 -56
  85. package/src/client/components/mdx/image.tsx +36 -15
  86. package/src/client/components/mdx/index.ts +19 -53
  87. package/src/client/components/mdx/table.tsx +46 -148
  88. package/src/client/components/mdx/typographics.tsx +120 -0
  89. package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
  90. package/src/client/components/primitives/breadcrumbs.tsx +5 -24
  91. package/src/client/components/primitives/button.tsx +3 -142
  92. package/src/client/components/primitives/code-block.tsx +104 -97
  93. package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
  94. package/src/client/components/primitives/error-boundary.tsx +107 -0
  95. package/src/client/components/primitives/heading.tsx +128 -0
  96. package/src/client/components/primitives/helpers/observer.ts +62 -32
  97. package/src/client/components/primitives/image.tsx +26 -0
  98. package/src/client/components/primitives/link.tsx +50 -52
  99. package/src/client/components/primitives/menu.tsx +25 -49
  100. package/src/client/components/primitives/navbar.tsx +234 -59
  101. package/src/client/components/primitives/on-this-page.tsx +169 -40
  102. package/src/client/components/primitives/page-nav.tsx +11 -39
  103. package/src/client/components/primitives/popover.tsx +12 -30
  104. package/src/client/components/primitives/search-dialog.tsx +77 -71
  105. package/src/client/components/primitives/sidebar.tsx +312 -119
  106. package/src/client/components/primitives/skeleton.tsx +1 -1
  107. package/src/client/components/primitives/tabs.tsx +5 -16
  108. package/src/client/components/primitives/tooltip.tsx +1 -1
  109. package/src/client/components/ui-base/banner.tsx +66 -0
  110. package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
  111. package/src/client/components/ui-base/copy-markdown.tsx +43 -35
  112. package/src/client/components/ui-base/error-boundary.tsx +9 -46
  113. package/src/client/components/ui-base/github-stars.tsx +5 -3
  114. package/src/client/components/ui-base/index.ts +3 -3
  115. package/src/client/components/ui-base/last-updated.tsx +27 -0
  116. package/src/client/components/ui-base/navbar.tsx +183 -89
  117. package/src/client/components/ui-base/not-found.tsx +11 -9
  118. package/src/client/components/ui-base/on-this-page.tsx +8 -104
  119. package/src/client/components/ui-base/page-nav.tsx +23 -9
  120. package/src/client/components/ui-base/search-dialog.tsx +111 -36
  121. package/src/client/components/ui-base/search-highlight.tsx +10 -0
  122. package/src/client/components/ui-base/sidebar.tsx +77 -154
  123. package/src/client/components/ui-base/tabs.tsx +20 -7
  124. package/src/client/components/ui-base/theme-toggle.tsx +88 -10
  125. package/src/client/components/ui-base/version-i18n.tsx +80 -0
  126. package/src/client/hooks/index.ts +2 -1
  127. package/src/client/hooks/use-analytics.ts +272 -0
  128. package/src/client/hooks/use-i18n.ts +116 -50
  129. package/src/client/hooks/use-localized-to.ts +70 -27
  130. package/src/client/hooks/use-navbar.ts +69 -39
  131. package/src/client/hooks/use-page-nav.ts +28 -25
  132. package/src/client/hooks/use-routes.ts +63 -80
  133. package/src/client/hooks/use-search-highlight.ts +185 -0
  134. package/src/client/hooks/use-search.ts +12 -3
  135. package/src/client/hooks/use-sidebar.ts +183 -80
  136. package/src/client/hooks/use-tabs.ts +3 -4
  137. package/src/client/hooks/use-version.ts +44 -29
  138. package/src/client/index.ts +13 -87
  139. package/src/client/mdx.ts +2 -0
  140. package/src/client/primitives.ts +19 -0
  141. package/src/client/ssg/boltdocs-shell.tsx +68 -79
  142. package/src/client/ssg/create-routes.tsx +268 -72
  143. package/src/client/ssg/mdx-page.tsx +2 -1
  144. package/src/client/store/boltdocs-context.tsx +72 -20
  145. package/src/client/theme/neutral.css +74 -361
  146. package/src/client/theme/reset.css +189 -0
  147. package/src/client/types.ts +10 -2
  148. package/src/client/utils/path.ts +9 -0
  149. package/src/client/utils/react-to-text.ts +24 -24
  150. package/src/client/virtual.d.ts +1 -1
  151. package/src/shared/types.ts +82 -22
  152. package/dist/node-Bogvkxao.mjs +0 -101
  153. package/dist/node-CXaog6St.cjs +0 -101
  154. package/dist/search-dialog-CV3eJzMm.cjs +0 -6
  155. package/dist/search-dialog-DNTomKgu.js +0 -6
  156. package/dist/use-search-CS3gH19M.js +0 -6
  157. package/dist/use-search-DBpJZQuw.cjs +0 -6
  158. package/src/client/components/mdx/admonition.tsx +0 -91
  159. package/src/client/components/mdx/badge.tsx +0 -41
  160. package/src/client/components/mdx/button.tsx +0 -35
  161. package/src/client/components/mdx/component-preview.tsx +0 -37
  162. package/src/client/components/mdx/component-props.tsx +0 -83
  163. package/src/client/components/mdx/file-tree.tsx +0 -325
  164. package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
  165. package/src/client/components/mdx/hooks/useTable.ts +0 -74
  166. package/src/client/components/mdx/hooks/useTabs.ts +0 -68
  167. package/src/client/components/mdx/link.tsx +0 -38
  168. package/src/client/components/mdx/list.tsx +0 -192
  169. package/src/client/components/mdx/tabs.tsx +0 -135
  170. package/src/client/components/mdx/video.tsx +0 -68
  171. package/src/client/components/primitives/index.ts +0 -19
  172. package/src/client/components/primitives/navigation-menu.tsx +0 -114
  173. package/src/client/components/ui-base/head.tsx +0 -83
  174. package/src/client/components/ui-base/loading.tsx +0 -57
  175. package/src/client/components/ui-base/powered-by.tsx +0 -25
  176. package/src/client/hooks/use-onthispage.ts +0 -23
  177. package/src/client/utils/use-on-change.ts +0 -15
@@ -11,7 +11,6 @@ import {
11
11
  } from 'react'
12
12
  import scrollIntoView from 'scroll-into-view-if-needed'
13
13
  import { cn } from '../../utils/cn'
14
- import { useOnChange } from '../../utils/use-on-change'
15
14
  import type { ComponentBase } from './types'
16
15
  import { getItemId, Observer } from './helpers/observer'
17
16
 
@@ -41,6 +40,10 @@ export interface AnchorProviderProps {
41
40
  * @defaultValue false
42
41
  */
43
42
  single?: boolean
43
+ /**
44
+ * Custom IntersectionObserver options
45
+ */
46
+ observerOptions?: IntersectionObserverInit
44
47
  children?: ReactNode
45
48
  }
46
49
 
@@ -123,6 +126,7 @@ export function ScrollProvider({
123
126
  export function AnchorProvider({
124
127
  toc,
125
128
  single = false,
129
+ observerOptions,
126
130
  children,
127
131
  }: AnchorProviderProps) {
128
132
  const observer = useMemo(() => new Observer(), [])
@@ -136,10 +140,16 @@ export function AnchorProvider({
136
140
  useEffect(() => {
137
141
  // We use a rootMargin that acts as an activation "line" near the top.
138
142
  // headings are "intersecting" (active=true) when they are BELOW this line.
139
- observer.watch({
140
- rootMargin: '-100px 0% 0% 0%',
143
+ // Default to a more permissive margin for detecting visible headings
144
+ const defaultOptions = {
145
+ rootMargin: '-80px 0% -60% 0%',
141
146
  threshold: 0,
142
- })
147
+ }
148
+ const options = observerOptions
149
+ ? { ...defaultOptions, ...observerOptions }
150
+ : defaultOptions
151
+
152
+ observer.watch(options)
143
153
  observer.onChange = () => setItems([...observer.items])
144
154
 
145
155
  return () => {
@@ -156,7 +166,7 @@ export const OnThisPage = ({ children, className }: ComponentBase) => {
156
166
  className={cn(
157
167
  'sticky top-navbar hidden xl:flex flex-col shrink-0',
158
168
  'w-toc',
159
- 'py-8 pl-6 pr-4',
169
+ 'py-4 pl-6 pr-4',
160
170
  className,
161
171
  )}
162
172
  >
@@ -168,10 +178,7 @@ export const OnThisPage = ({ children, className }: ComponentBase) => {
168
178
  const OnThisPageHeader = ({ children, className, ...props }: ComponentBase) => {
169
179
  return (
170
180
  <div
171
- className={cn(
172
- 'mb-4 text-xs font-bold text-text-main',
173
- className,
174
- )}
181
+ className={cn('mb-4 text-xs font-bold text-body', className)}
175
182
  {...props}
176
183
  >
177
184
  {children}
@@ -192,7 +199,16 @@ const OnThisPageContent = ({
192
199
  return (
193
200
  <div
194
201
  ref={internalRef}
195
- className={cn('relative overflow-y-auto boltdocs-otp-content', className)}
202
+ className={cn(
203
+ 'relative overflow-y-auto boltdocs-otp-content pb-12',
204
+ 'max-h-[70%]',
205
+ className,
206
+ )}
207
+ style={{
208
+ maskImage: 'linear-gradient(to bottom, black 90%, transparent 100%)',
209
+ WebkitMaskImage:
210
+ 'linear-gradient(to bottom, black 90%, transparent 100%)',
211
+ }}
196
212
  {...props}
197
213
  >
198
214
  {children}
@@ -206,7 +222,7 @@ const OnThisPageList = ({ children, className }: ComponentBase) => {
206
222
  return (
207
223
  <ul
208
224
  className={cn(
209
- 'relative space-y-1 text-sm border-l border-border-subtle',
225
+ 'relative space-y-0.5 text-sm border-l border-subtle',
210
226
  className,
211
227
  )}
212
228
  >
@@ -234,41 +250,49 @@ const OnThisPageLink = ({
234
250
  const containerRef = use(ScrollContext)
235
251
  const id = href ? getItemId(href) : null
236
252
  const anchorRef = useRef<HTMLAnchorElement>(null)
237
- const [internalActive, setInternalActive] = useState(active)
238
-
239
- useOnChange(
240
- id && items ? items.find((i) => i.id === id)?.active : null,
241
- (newActive) => {
242
- if (newActive !== null && newActive !== internalActive) {
243
- setInternalActive(!!newActive)
244
-
245
- if (newActive && anchorRef.current && containerRef?.current) {
246
- scrollIntoView(anchorRef.current, {
247
- behavior: 'smooth',
248
- block: 'center',
249
- inline: 'center',
250
- scrollMode: 'if-needed',
251
- boundary: containerRef.current,
252
- })
253
- }
254
- }
255
- },
256
- )
257
253
 
258
- // Also sync with external active prop if provided
254
+ const computedActive =
255
+ active !== undefined
256
+ ? active
257
+ : id && items
258
+ ? !!items.find((i) => i.id === id)?.active
259
+ : false
260
+
259
261
  useEffect(() => {
260
- if (active !== undefined) setInternalActive(active)
261
- }, [active])
262
+ if (computedActive && anchorRef.current && containerRef?.current) {
263
+ scrollIntoView(anchorRef.current, {
264
+ behavior: 'smooth',
265
+ block: 'center',
266
+ inline: 'center',
267
+ scrollMode: 'if-needed',
268
+ boundary: containerRef.current,
269
+ })
270
+ }
271
+ }, [computedActive, containerRef])
272
+
273
+ const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
274
+ if (onClick) {
275
+ onClick(e)
276
+ } else if (href && href.startsWith('#')) {
277
+ e.preventDefault()
278
+ const elementId = href.slice(1)
279
+ const el = document.getElementById(elementId)
280
+ if (el) {
281
+ el.scrollIntoView({ behavior: 'smooth' })
282
+ window.history.pushState(null, '', href)
283
+ }
284
+ }
285
+ }
262
286
 
263
287
  return (
264
288
  <a
265
289
  ref={anchorRef}
266
290
  href={href}
267
- onClick={onClick}
268
- data-active={internalActive}
291
+ onClick={handleClick}
292
+ data-active={computedActive}
269
293
  className={cn(
270
- 'block py-1 pl-4 text-[13px] outline-none transition-colors hover:text-text-main',
271
- internalActive ? 'text-primary-500 font-medium' : 'text-text-muted',
294
+ 'block py-0.5 pl-4 text-[13px] outline-none transition-colors',
295
+ computedActive ? 'text-primary-500' : 'text-muted hover:text-body',
272
296
  className,
273
297
  )}
274
298
  >
@@ -281,17 +305,118 @@ const OnThisPageIndicator = ({
281
305
  style,
282
306
  className,
283
307
  }: OnThisPageIndicatorProps) => {
308
+ const containerRef = useRef<HTMLDivElement>(null)
309
+ const [internalStyle, setInternalStyle] = useState<React.CSSProperties>({
310
+ opacity: 0,
311
+ ...style,
312
+ })
313
+
314
+ const items = useItems()
315
+
316
+ useEffect(() => {
317
+ const parent = containerRef.current?.parentElement
318
+ if (!parent) return
319
+
320
+ const activeLinks = parent.querySelectorAll('a[data-active="true"]')
321
+
322
+ if (activeLinks.length > 0) {
323
+ const firstActiveLink = activeLinks[0] as HTMLElement
324
+ const lastActiveLink = activeLinks[activeLinks.length - 1] as HTMLElement
325
+
326
+ const firstRect = firstActiveLink.getBoundingClientRect()
327
+ const lastRect = lastActiveLink.getBoundingClientRect()
328
+ const parentRect = parent.getBoundingClientRect()
329
+
330
+ const offsetTop = firstRect.top - parentRect.top
331
+ const height = lastRect.bottom - firstRect.top
332
+
333
+ setInternalStyle({
334
+ transform: `translateY(${offsetTop}px)`,
335
+ height: `${height}px`,
336
+ opacity: 1,
337
+ ...style,
338
+ })
339
+ } else {
340
+ setInternalStyle({
341
+ opacity: 0,
342
+ ...style,
343
+ })
344
+ }
345
+ }, [items, style])
346
+
284
347
  return (
285
348
  <div
349
+ ref={containerRef}
286
350
  className={cn(
287
- 'absolute -left-px w-0.5 rounded-full bg-primary-500 transition-all duration-300',
351
+ 'absolute -left-px w-0.5 rounded-full bg-primary-500',
288
352
  className,
289
353
  )}
290
- style={style}
354
+ style={{
355
+ transition:
356
+ 'transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1), height 180ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 150ms',
357
+ ...internalStyle,
358
+ }}
291
359
  />
292
360
  )
293
361
  }
294
362
 
363
+ /**
364
+ * High-level automated list of toc items
365
+ */
366
+ export function OnThisPageItems({
367
+ headings = [],
368
+ className,
369
+ }: {
370
+ headings: { level: number; text: string; id: string }[]
371
+ } & ComponentBase) {
372
+ const activeIds = useActiveAnchors()
373
+
374
+ if (headings.length === 0) return null
375
+
376
+ return (
377
+ <OnThisPageList className={className}>
378
+ <OnThisPageIndicator />
379
+ {headings.map((h) => (
380
+ <OnThisPageItem key={h.id} level={h.level}>
381
+ <OnThisPageLink href={`#${h.id}`} active={activeIds.includes(h.id)}>
382
+ {h.text}
383
+ </OnThisPageLink>
384
+ </OnThisPageItem>
385
+ ))}
386
+ </OnThisPageList>
387
+ )
388
+ }
389
+
390
+ /**
391
+ * High-level automated Table of Contents tree
392
+ */
393
+ export function OnThisPageTree({
394
+ headings = [],
395
+ className,
396
+ }: {
397
+ headings: { level: number; text: string; id: string }[]
398
+ } & ComponentBase) {
399
+ const toc = useMemo(
400
+ () =>
401
+ headings.map((h) => ({ title: h.text, url: `#${h.id}`, depth: h.level })),
402
+ [headings],
403
+ )
404
+
405
+ const scrollContainerRef = useRef<HTMLDivElement>(null)
406
+
407
+ if (headings.length === 0) return null
408
+
409
+ return (
410
+ <AnchorProvider toc={toc} single={false}>
411
+ <ScrollProvider containerRef={scrollContainerRef}>
412
+ <OnThisPageContent ref={scrollContainerRef}>
413
+ <OnThisPageItems headings={headings} className={className} />
414
+ </OnThisPageContent>
415
+ </ScrollProvider>
416
+ </AnchorProvider>
417
+ )
418
+ }
419
+
295
420
  OnThisPage.Root = OnThisPage
296
421
  OnThisPage.Header = OnThisPageHeader
297
422
  OnThisPage.Content = OnThisPageContent
@@ -299,3 +424,7 @@ OnThisPage.List = OnThisPageList
299
424
  OnThisPage.Item = OnThisPageItem
300
425
  OnThisPage.Link = OnThisPageLink
301
426
  OnThisPage.Indicator = OnThisPageIndicator
427
+ OnThisPage.Items = OnThisPageItems
428
+ OnThisPage.Tree = OnThisPageTree
429
+
430
+ export default OnThisPage
@@ -1,4 +1,4 @@
1
- import * as RAC from 'react-aria-components'
1
+ import { Link } from './link'
2
2
  import { ChevronLeft, ChevronRight } from 'lucide-react'
3
3
  import { cn } from '../../utils/cn'
4
4
  import type { ComponentBase } from './types'
@@ -10,62 +10,34 @@ export interface PageNavProps extends ComponentBase {
10
10
 
11
11
  export const PageNav = ({ children, className }: ComponentBase) => {
12
12
  return (
13
- <nav
14
- className={cn(
15
- 'grid grid-cols-1 sm:grid-cols-2 gap-4 mt-12 pt-8 border-t border-border-subtle',
16
- className,
17
- )}
18
- >
19
- {children}
20
- </nav>
13
+ <nav className={cn('grid sm:grid-cols-2 gap-4', className)}>{children}</nav>
21
14
  )
22
15
  }
23
16
 
24
17
  const PageNavLink = ({ children, to, direction, className }: PageNavProps) => {
25
18
  const isNext = direction === 'next'
26
19
  return (
27
- <RAC.Link
20
+ <Link
28
21
  href={to}
29
22
  className={cn(
30
- 'flex group items-center p-4 rounded-xl border border-border-subtle bg-bg-surface outline-none',
31
- 'transition-all hover:bg-bg-main hover:border-primary-500 hover:shadow-lg',
32
- 'focus-visible:ring-2 focus-visible:ring-primary-500/30',
33
- isNext ? 'text-right justify-end' : 'text-left justify-start',
23
+ 'flex items-center outline-none no-underline',
24
+ isNext ? 'justify-end' : 'justify-start',
34
25
  className,
35
26
  )}
36
27
  >
37
- {!isNext && (
38
- <ChevronLeft className="mr-3 h-5 w-5 text-text-muted group-hover:text-primary-500 transition-transform group-hover:-translate-x-1" />
39
- )}
40
- <div className="flex flex-col gap-1 flex-1">{children}</div>
41
- {isNext && (
42
- <ChevronRight className="ml-3 h-5 w-5 text-text-muted group-hover:text-primary-500 transition-transform group-hover:translate-x-1" />
43
- )}
44
- </RAC.Link>
28
+ {!isNext && <ChevronLeft className="shrink-0" />}
29
+ <div className="flex flex-col flex-1">{children}</div>
30
+ {isNext && <ChevronRight className="shrink-0" />}
31
+ </Link>
45
32
  )
46
33
  }
47
34
 
48
35
  const PageNavTitle = ({ children, className }: ComponentBase) => {
49
- return (
50
- <span
51
- className={cn(
52
- 'text-xs font-medium uppercase text-text-muted',
53
- className,
54
- )}
55
- >
56
- {children}
57
- </span>
58
- )
36
+ return <span className={cn(className)}>{children}</span>
59
37
  }
60
38
 
61
39
  const PageNavDescription = ({ children, className }: ComponentBase) => {
62
- return (
63
- <span
64
- className={cn('text-base font-bold text-text-main truncate', className)}
65
- >
66
- {children}
67
- </span>
68
- )
40
+ return <span className={cn('truncate', className)}>{children}</span>
69
41
  }
70
42
 
71
43
  const PageNavIcon = ({ children }: ComponentBase) => {
@@ -1,46 +1,28 @@
1
- 'use client'
2
-
3
- import * as RAC from 'react-aria-components'
1
+ import {
2
+ Popover as RACPopover,
3
+ type PopoverProps as RACPopoverProps,
4
+ } from 'react-aria-components'
4
5
  import { cn } from '../../utils/cn'
5
6
 
6
- export interface PopoverProps extends Omit<RAC.PopoverProps, 'children'> {
7
+ export interface PopoverProps extends Omit<RACPopoverProps, 'children'> {
7
8
  children: React.ReactNode
8
9
  className?: string
9
- showArrow?: boolean
10
10
  }
11
11
 
12
12
  /**
13
13
  * A reusable Popover primitive with premium glassmorphism styling and smooth animations.
14
14
  */
15
- export const Popover = ({
16
- children,
17
- className,
18
- showArrow,
19
- ...props
20
- }: PopoverProps) => {
15
+ export const Popover = ({ children, className, ...props }: PopoverProps) => {
21
16
  return (
22
- <RAC.Popover
17
+ <RACPopover
23
18
  offset={8}
24
- {...props}
25
- className={RAC.composeRenderProps(className, (className) =>
26
- cn(
27
- 'z-50 overflow-auto rounded-xl border border-border-subtle bg-bg-surface/80 shadow-xl backdrop-blur-md outline-none transition-none',
28
- className,
29
- ),
19
+ className={cn(
20
+ 'z-50 overflow-auto outline-none transition-none',
21
+ className,
30
22
  )}
23
+ {...props}
31
24
  >
32
- {showArrow && (
33
- <RAC.OverlayArrow className="group">
34
- <svg
35
- viewBox="0 0 12 12"
36
- className="block h-3 w-3 fill-bg-surface/80 stroke-border-subtle group-placement-bottom:rotate-180 group-placement-left:-rotate-90 group-placement-right:rotate-90"
37
- aria-hidden="true"
38
- >
39
- <path d="M0 0 L6 6 L12 0" />
40
- </svg>
41
- </RAC.OverlayArrow>
42
- )}
43
25
  {children as any}
44
- </RAC.Popover>
26
+ </RACPopover>
45
27
  )
46
28
  }
@@ -1,13 +1,10 @@
1
+ 'use client'
2
+
1
3
  import * as RAC from 'react-aria-components'
2
- import { Search, Hash, FileText, CornerDownLeft } from 'lucide-react'
4
+ import { Hash, FileText, CornerDownLeft } from 'lucide-react'
3
5
  import { cn } from '../../utils/cn'
4
6
  import type { ComponentBase } from './types'
5
7
 
6
- export interface SearchDialogProps extends ComponentBase {
7
- isOpen?: boolean
8
- onOpenChange?: (isOpen: boolean) => void
9
- }
10
-
11
8
  export interface SearchDialogItemProps
12
9
  extends Omit<RAC.ListBoxItemProps, 'children'> {
13
10
  className?: string
@@ -19,37 +16,66 @@ export interface SearchDialogItemIconProps {
19
16
  className?: string
20
17
  }
21
18
 
19
+ /**
20
+ * Pure, unstyled SearchDialog Overlay (maps to RAC.ModalOverlay)
21
+ */
22
22
  export const SearchDialog = ({
23
- children,
24
- isOpen,
25
- onOpenChange,
26
23
  className,
27
- }: SearchDialogProps) => {
24
+ ...props
25
+ }: RAC.ModalOverlayProps) => {
28
26
  return (
29
27
  <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>
28
+ className={cn('fixed inset-0 z-100', className)}
29
+ {...props}
30
+ />
50
31
  )
51
32
  }
52
33
 
34
+ /**
35
+ * Pure, unstyled SearchDialog Content (maps to RAC.Modal)
36
+ */
37
+ const SearchDialogContent = ({
38
+ className,
39
+ ...props
40
+ }: RAC.ModalOverlayProps) => <RAC.Modal className={cn(className)} {...props} />
41
+
42
+ /**
43
+ * Pure, unstyled SearchDialog Dialog (maps to RAC.Dialog)
44
+ */
45
+ const SearchDialogDialog = ({ className, ...props }: RAC.DialogProps) => (
46
+ <RAC.Dialog
47
+ className={cn('flex flex-col focus:outline-none', className)}
48
+ {...props}
49
+ />
50
+ )
51
+
52
+ /**
53
+ * Pure, unstyled SearchDialog Input Field (maps to RAC.SearchField)
54
+ */
55
+ const SearchDialogField = ({ className, ...props }: RAC.SearchFieldProps) => (
56
+ <RAC.SearchField className={cn('flex items-center', className)} {...props} />
57
+ )
58
+
59
+ /**
60
+ * Pure, unstyled SearchInput (maps to RAC.Input)
61
+ */
62
+ const SearchDialogSearchInput = ({ className, ...props }: RAC.InputProps) => (
63
+ <RAC.Input
64
+ className={cn('w-full bg-transparent outline-none border-none', className)}
65
+ {...props}
66
+ />
67
+ )
68
+
69
+ /**
70
+ * Pure, unstyled Clear Button (maps to RAC.Button with slot="clear")
71
+ */
72
+ const SearchDialogClearButton = ({ className, ...props }: RAC.ButtonProps) => (
73
+ <RAC.Button slot="clear" className={cn(className)} {...props} />
74
+ )
75
+
76
+ /**
77
+ * Pure, unstyled Autocomplete container (maps to RAC.Autocomplete)
78
+ */
53
79
  const SearchDialogAutocomplete = <T extends object>({
54
80
  children,
55
81
  className,
@@ -61,7 +87,7 @@ const SearchDialogAutocomplete = <T extends object>({
61
87
  }) => {
62
88
  const Autocomplete = RAC.Autocomplete as any
63
89
  return (
64
- <div className={className}>
90
+ <div className={cn('flex-1 min-h-0', className)}>
65
91
  <Autocomplete
66
92
  {...props}
67
93
  onSelectionChange={onSelectionChange}
@@ -73,31 +99,9 @@ const SearchDialogAutocomplete = <T extends object>({
73
99
  )
74
100
  }
75
101
 
76
- const SearchDialogInput = ({
77
- className,
78
- ...props
79
- }: RAC.InputProps & { className?: string }) => {
80
- return (
81
- <RAC.SearchField
82
- className="flex items-center gap-3 border-b border-border-subtle px-4 py-4"
83
- autoFocus
84
- >
85
- <Search className="h-5 w-5 text-text-muted" />
86
- <RAC.Input
87
- {...props}
88
- className={cn(
89
- 'w-full bg-transparent text-lg text-text-main placeholder-text-muted outline-none',
90
- className,
91
- )}
92
- placeholder="Search documentation..."
93
- />
94
- <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">
95
- <kbd className="font-sans">ESC</kbd>
96
- </div>
97
- </RAC.SearchField>
98
- )
99
- }
100
-
102
+ /**
103
+ * Pure, unstyled List Box (maps to RAC.ListBox)
104
+ */
101
105
  const SearchDialogList = <T extends object>({
102
106
  children,
103
107
  className,
@@ -106,13 +110,16 @@ const SearchDialogList = <T extends object>({
106
110
  return (
107
111
  <RAC.ListBox
108
112
  {...props}
109
- className={cn('flex-1 overflow-y-auto p-2 outline-none', className)}
113
+ className={cn('flex-1 overflow-y-auto outline-none min-h-0', className)}
110
114
  >
111
115
  {children as any}
112
116
  </RAC.ListBox>
113
117
  )
114
118
  }
115
119
 
120
+ /**
121
+ * Pure, unstyled List Box Item (maps to RAC.ListBoxItem)
122
+ */
116
123
  const SearchDialogItemRoot = ({
117
124
  children,
118
125
  className,
@@ -122,8 +129,7 @@ const SearchDialogItemRoot = ({
122
129
  <RAC.ListBoxItem
123
130
  {...props}
124
131
  className={cn(
125
- 'group flex items-center gap-3 rounded-lg p-3 text-left outline-none cursor-pointer transition-colors',
126
- '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',
132
+ 'group flex items-center outline-none cursor-pointer',
127
133
  className,
128
134
  )}
129
135
  >
@@ -155,31 +161,31 @@ const SearchDialogItemIcon = ({
155
161
 
156
162
  const SearchDialogItemTitle = ({ children, className }: ComponentBase) => {
157
163
  return (
158
- <span
159
- className={cn('block font-medium truncate flex-1 text-sm', className)}
160
- >
161
- {children}
162
- </span>
164
+ <span className={cn('block truncate flex-1', className)}>{children}</span>
163
165
  )
164
166
  }
165
167
 
166
168
  const SearchDialogItemBio = ({ children, className }: ComponentBase) => {
167
169
  return (
168
- <span
169
- className={cn(
170
- 'ml-2 text-xs opacity-70 truncate hidden sm:inline group-focus:opacity-100',
171
- className,
172
- )}
173
- >
170
+ <span className={cn('ml-2 truncate hidden sm:inline', className)}>
174
171
  {children}
175
172
  </span>
176
173
  )
177
174
  }
178
175
 
176
+ // Compound API wiring
179
177
  SearchDialog.Root = SearchDialog
178
+ SearchDialog.Overlay = SearchDialog
179
+ SearchDialog.Content = SearchDialogContent
180
+ SearchDialog.Dialog = SearchDialogDialog
180
181
  SearchDialog.Autocomplete = SearchDialogAutocomplete
181
- SearchDialog.Input = SearchDialogInput
182
182
  SearchDialog.List = SearchDialogList
183
+
184
+ SearchDialog.Input = Object.assign(SearchDialogField, {
185
+ SearchInput: SearchDialogSearchInput,
186
+ Button: SearchDialogClearButton,
187
+ })
188
+
183
189
  SearchDialog.Item = Object.assign(SearchDialogItemRoot, {
184
190
  Icon: SearchDialogItemIcon,
185
191
  Title: SearchDialogItemTitle,