boltdocs 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/boltdocs.js +0 -1
- package/dist/cache-CQKlT4fI.mjs +6 -0
- package/dist/cache-DorPMFgW.cjs +6 -0
- package/dist/cards-BLoSiRuL.d.ts +30 -0
- package/dist/cards-CQn9mXZS.d.cts +30 -0
- package/dist/chunk-Ds5LZdWN.cjs +6 -0
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.d.cts +173 -1328
- package/dist/client/index.d.ts +172 -1327
- package/dist/client/index.js +1 -1
- package/dist/{package-c99Cs7mD.cjs → client/mdx.cjs} +1 -1
- package/dist/client/mdx.d.cts +128 -0
- package/dist/client/mdx.d.ts +129 -0
- package/dist/client/mdx.js +6 -0
- package/dist/client/primitives.cjs +6 -0
- package/dist/client/primitives.d.cts +818 -0
- package/dist/client/primitives.d.ts +818 -0
- package/dist/client/primitives.js +6 -0
- package/dist/client/theme/neutral.css +74 -361
- package/dist/client/theme/reset.css +189 -0
- package/dist/docs-layout-BlDhcQRv.cjs +6 -0
- package/dist/docs-layout-BvAOWEJw.js +6 -0
- package/dist/doctor-BQiQhCTl.cjs +6 -0
- package/dist/doctor-COpf35L2.cjs +20 -0
- package/dist/doctor-Dh1XP7Pz.mjs +20 -0
- package/dist/generator-DGW6pkCC.cjs +22 -0
- package/dist/generator-Dv3wEmhZ.mjs +22 -0
- package/dist/icons-dev-CrQLjoQp.js +6 -0
- package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
- package/dist/image-BkIfa9oo.js +6 -0
- package/dist/image-DIGjCPe6.cjs +6 -0
- package/dist/mdx-K0WYBAJ3.js +7 -0
- package/dist/mdx-hpErbRUe.cjs +7 -0
- package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
- package/dist/meta-loader-9IpAHWDS.mjs +6 -0
- package/dist/node/cli-entry.cjs +1 -2
- package/dist/node/cli-entry.mjs +1 -2
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +66 -13
- package/dist/node/index.d.mts +66 -14
- package/dist/node/index.mjs +1 -1
- package/dist/node/routes/worker.cjs +6 -0
- package/dist/node/routes/worker.d.cts +2 -0
- package/dist/node/routes/worker.d.mts +2 -0
- package/dist/node/routes/worker.mjs +6 -0
- package/dist/node-C2nWXElP.mjs +112 -0
- package/dist/node-CinkUtxV.cjs +112 -0
- package/dist/package-BMYLDBBP.cjs +6 -0
- package/dist/{package-DukYeKmD.mjs → package-HegMOTL_.mjs} +1 -1
- package/dist/parser-Bh11BsdA.cjs +6 -0
- package/dist/parser-D8eQvE7N.mjs +6 -0
- package/dist/parser-DYRzXWmA.cjs +6 -0
- package/dist/routes-CHf76Ye4.cjs +6 -0
- package/dist/routes-CMUZGI6T.mjs +6 -0
- package/dist/routes-Co1mRM58.cjs +6 -0
- package/dist/search-dialog-BACuzoVX.cjs +6 -0
- package/dist/search-dialog-BKagVT17.js +6 -0
- package/dist/search-dialog-C8w12eUx.js +6 -0
- package/dist/search-dialog-CGyrozZE.cjs +6 -0
- package/dist/search-dialog-D26rUnJ_.cjs +6 -0
- package/dist/sidebar-DKvg6KOc.d.cts +491 -0
- package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
- package/dist/utils-BxNAXhZZ.mjs +7 -0
- package/dist/utils-Clzu7jvb.cjs +7 -0
- package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
- package/dist/worker-pool-BwU8ckrg.cjs +6 -0
- package/package.json +27 -8
- package/src/client/app/doc-page.tsx +9 -5
- package/src/client/app/docs-layout.tsx +17 -3
- package/src/client/app/head.tsx +122 -0
- package/src/client/app/helmet-compat.tsx +36 -0
- package/src/client/app/mdx-component.tsx +5 -52
- package/src/client/app/mdx-components-context.tsx +32 -8
- package/src/client/app/routes-context.tsx +2 -2
- package/src/client/app/scroll-handler.tsx +1 -1
- package/src/client/app/theme-context.tsx +5 -5
- package/src/client/app/ui-context.tsx +42 -0
- package/src/client/components/docs-layout-default.tsx +85 -0
- package/src/client/components/icons-dev.tsx +38 -15
- package/src/client/components/mdx/callout.tsx +97 -0
- package/src/client/components/mdx/card.tsx +73 -98
- package/src/client/components/mdx/cards.tsx +27 -0
- package/src/client/components/mdx/code-block.tsx +37 -17
- package/src/client/components/mdx/field.tsx +24 -56
- package/src/client/components/mdx/image.tsx +36 -15
- package/src/client/components/mdx/index.ts +19 -53
- package/src/client/components/mdx/table.tsx +46 -148
- package/src/client/components/mdx/typographics.tsx +120 -0
- package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
- package/src/client/components/primitives/breadcrumbs.tsx +5 -24
- package/src/client/components/primitives/button.tsx +3 -142
- package/src/client/components/primitives/code-block.tsx +104 -97
- package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
- package/src/client/components/primitives/error-boundary.tsx +107 -0
- package/src/client/components/primitives/heading.tsx +128 -0
- package/src/client/components/primitives/helpers/observer.ts +62 -32
- package/src/client/components/primitives/image.tsx +26 -0
- package/src/client/components/primitives/link.tsx +50 -52
- package/src/client/components/primitives/menu.tsx +25 -49
- package/src/client/components/primitives/navbar.tsx +234 -59
- package/src/client/components/primitives/on-this-page.tsx +169 -40
- package/src/client/components/primitives/page-nav.tsx +11 -39
- package/src/client/components/primitives/popover.tsx +12 -30
- package/src/client/components/primitives/search-dialog.tsx +77 -71
- package/src/client/components/primitives/sidebar.tsx +312 -119
- package/src/client/components/primitives/skeleton.tsx +1 -1
- package/src/client/components/primitives/tabs.tsx +5 -16
- package/src/client/components/primitives/tooltip.tsx +1 -1
- package/src/client/components/ui-base/banner.tsx +66 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
- package/src/client/components/ui-base/copy-markdown.tsx +43 -35
- package/src/client/components/ui-base/error-boundary.tsx +9 -46
- package/src/client/components/ui-base/github-stars.tsx +5 -3
- package/src/client/components/ui-base/index.ts +3 -3
- package/src/client/components/ui-base/last-updated.tsx +27 -0
- package/src/client/components/ui-base/navbar.tsx +183 -89
- package/src/client/components/ui-base/not-found.tsx +11 -9
- package/src/client/components/ui-base/on-this-page.tsx +8 -104
- package/src/client/components/ui-base/page-nav.tsx +23 -9
- package/src/client/components/ui-base/search-dialog.tsx +111 -36
- package/src/client/components/ui-base/search-highlight.tsx +10 -0
- package/src/client/components/ui-base/sidebar.tsx +77 -154
- package/src/client/components/ui-base/tabs.tsx +20 -7
- package/src/client/components/ui-base/theme-toggle.tsx +88 -10
- package/src/client/components/ui-base/version-i18n.tsx +80 -0
- package/src/client/hooks/index.ts +2 -1
- package/src/client/hooks/use-analytics.ts +272 -0
- package/src/client/hooks/use-i18n.ts +120 -53
- package/src/client/hooks/use-localized-to.ts +70 -30
- package/src/client/hooks/use-navbar.ts +69 -39
- package/src/client/hooks/use-page-nav.ts +28 -25
- package/src/client/hooks/use-routes.ts +64 -81
- package/src/client/hooks/use-search-highlight.ts +185 -0
- package/src/client/hooks/use-search.ts +12 -3
- package/src/client/hooks/use-sidebar.ts +183 -77
- package/src/client/hooks/use-tabs.ts +3 -4
- package/src/client/hooks/use-version.ts +46 -18
- package/src/client/index.ts +13 -86
- package/src/client/mdx.ts +2 -0
- package/src/client/primitives.ts +19 -0
- package/src/client/ssg/boltdocs-shell.tsx +78 -57
- package/src/client/ssg/create-routes.tsx +290 -50
- package/src/client/ssg/mdx-page.tsx +2 -1
- package/src/client/store/boltdocs-context.tsx +83 -12
- package/src/client/theme/neutral.css +74 -361
- package/src/client/theme/reset.css +189 -0
- package/src/client/types.ts +10 -2
- package/src/client/utils/path.ts +9 -0
- package/src/client/utils/react-to-text.ts +24 -24
- package/src/client/virtual.d.ts +1 -1
- package/src/shared/types.ts +97 -21
- package/dist/node-CWN8U_p8.mjs +0 -88
- package/dist/node-D5iosYXv.cjs +0 -88
- package/dist/search-dialog-3lvKsbVG.js +0 -6
- package/dist/search-dialog-DMK5OpgH.cjs +0 -6
- package/dist/use-search-C9bxCqfF.js +0 -6
- package/dist/use-search-DcfZSunO.cjs +0 -6
- package/src/client/components/mdx/admonition.tsx +0 -91
- package/src/client/components/mdx/badge.tsx +0 -41
- package/src/client/components/mdx/button.tsx +0 -35
- package/src/client/components/mdx/component-preview.tsx +0 -37
- package/src/client/components/mdx/component-props.tsx +0 -83
- package/src/client/components/mdx/file-tree.tsx +0 -325
- package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
- package/src/client/components/mdx/hooks/useTable.ts +0 -74
- package/src/client/components/mdx/hooks/useTabs.ts +0 -68
- package/src/client/components/mdx/link.tsx +0 -38
- package/src/client/components/mdx/list.tsx +0 -192
- package/src/client/components/mdx/tabs.tsx +0 -135
- package/src/client/components/mdx/video.tsx +0 -68
- package/src/client/components/primitives/index.ts +0 -19
- package/src/client/components/primitives/navigation-menu.tsx +0 -114
- package/src/client/components/ui-base/head.tsx +0 -76
- package/src/client/components/ui-base/loading.tsx +0 -57
- package/src/client/components/ui-base/powered-by.tsx +0 -25
- package/src/client/hooks/use-onthispage.ts +0 -23
- package/src/client/utils/use-on-change.ts +0 -15
|
@@ -3,20 +3,22 @@ import { Link } from '../primitives/link'
|
|
|
3
3
|
|
|
4
4
|
export function NotFound() {
|
|
5
5
|
return (
|
|
6
|
-
<div className="flex items-center justify-center min-h-[
|
|
7
|
-
<div className="space-y-
|
|
8
|
-
<span className="text-
|
|
6
|
+
<div className="flex items-center justify-center min-h-[65vh] text-center px-4">
|
|
7
|
+
<div className="space-y-6 max-w-md mx-auto p-8 border border-subtle bg-surface rounded-2xl shadow-xs">
|
|
8
|
+
<span className="block text-7xl font-extrabold tracking-tight text-primary-500">
|
|
9
9
|
404
|
|
10
10
|
</span>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
<div className="space-y-2">
|
|
12
|
+
<h1 className="text-xl font-bold text-body">Page Not Found</h1>
|
|
13
|
+
<p className="text-sm text-muted leading-relaxed">
|
|
14
|
+
The page you're looking for doesn't exist or has been moved.
|
|
15
|
+
</p>
|
|
16
|
+
</div>
|
|
15
17
|
<Link
|
|
16
18
|
href="/"
|
|
17
|
-
className="inline-flex items-center gap-2 rounded-
|
|
19
|
+
className="inline-flex items-center gap-2 rounded-xl border border-subtle bg-main px-6 py-2.5 text-xs font-semibold text-body hover:bg-primary-50/50 hover:border-primary-500/50 transition-all duration-300 outline-none select-none"
|
|
18
20
|
>
|
|
19
|
-
<ArrowLeft size={
|
|
21
|
+
<ArrowLeft size={14} /> Go to Home
|
|
20
22
|
</Link>
|
|
21
23
|
</div>
|
|
22
24
|
</div>
|
|
@@ -1,123 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
OnThisPage as OTP,
|
|
3
|
-
AnchorProvider,
|
|
4
|
-
ScrollProvider,
|
|
5
|
-
useActiveAnchor,
|
|
6
|
-
} from '../primitives/on-this-page'
|
|
7
|
-
import { useRef, useEffect, useState, useCallback, useMemo } from 'react'
|
|
8
|
-
import { useOnThisPage } from '../../hooks/use-onthispage'
|
|
1
|
+
import { OnThisPage as OTP } from '../primitives/on-this-page'
|
|
9
2
|
import type { OnThisPageProps } from '../../types'
|
|
10
3
|
import { Pencil, CircleHelp, TextAlignStart } from 'lucide-react'
|
|
11
4
|
|
|
12
5
|
export function OnThisPage({
|
|
13
|
-
headings
|
|
6
|
+
headings = [],
|
|
14
7
|
editLink,
|
|
15
8
|
communityHelp,
|
|
16
9
|
filePath,
|
|
17
10
|
}: OnThisPageProps) {
|
|
18
|
-
const { headings } = useOnThisPage(rawHeadings)
|
|
19
|
-
|
|
20
|
-
const toc = useMemo(
|
|
21
|
-
() =>
|
|
22
|
-
headings.map((h) => ({ title: h.text, url: `#${h.id}`, depth: h.level })),
|
|
23
|
-
[headings],
|
|
24
|
-
)
|
|
25
|
-
|
|
26
11
|
if (headings.length === 0) return null
|
|
27
12
|
|
|
28
|
-
return (
|
|
29
|
-
<AnchorProvider toc={toc}>
|
|
30
|
-
<OnThisPageInner
|
|
31
|
-
headings={headings}
|
|
32
|
-
editLink={editLink}
|
|
33
|
-
communityHelp={communityHelp}
|
|
34
|
-
filePath={filePath}
|
|
35
|
-
/>
|
|
36
|
-
</AnchorProvider>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function OnThisPageInner({
|
|
41
|
-
headings,
|
|
42
|
-
editLink,
|
|
43
|
-
communityHelp,
|
|
44
|
-
filePath,
|
|
45
|
-
}: OnThisPageProps & {
|
|
46
|
-
headings: { level: number; text: string; id: string }[]
|
|
47
|
-
}) {
|
|
48
|
-
const activeId = useActiveAnchor()
|
|
49
|
-
const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({
|
|
50
|
-
opacity: 0,
|
|
51
|
-
})
|
|
52
|
-
const listRef = useRef<HTMLUListElement>(null)
|
|
53
|
-
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
if (!activeId || !listRef.current) return
|
|
57
|
-
|
|
58
|
-
const activeLink = listRef.current.querySelector(
|
|
59
|
-
`a[href="#${activeId}"]`,
|
|
60
|
-
) as HTMLElement
|
|
61
|
-
|
|
62
|
-
if (activeLink) {
|
|
63
|
-
setIndicatorStyle({
|
|
64
|
-
transform: `translateY(${activeLink.offsetTop}px)`,
|
|
65
|
-
height: `${activeLink.offsetHeight}px`,
|
|
66
|
-
opacity: 1,
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
}, [activeId])
|
|
70
|
-
|
|
71
|
-
const handleClick = useCallback(
|
|
72
|
-
(e: React.MouseEvent<HTMLAnchorElement>, id: string) => {
|
|
73
|
-
e.preventDefault()
|
|
74
|
-
const el = document.getElementById(id)
|
|
75
|
-
|
|
76
|
-
if (el) {
|
|
77
|
-
el.scrollIntoView({ behavior: 'smooth' })
|
|
78
|
-
window.history.pushState(null, '', `#${id}`)
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
[],
|
|
82
|
-
)
|
|
83
|
-
|
|
84
13
|
return (
|
|
85
14
|
<OTP.Root>
|
|
86
15
|
<OTP.Header className="flex flex-row gap-x-2">
|
|
87
16
|
<TextAlignStart size={16} />
|
|
88
17
|
On this page
|
|
89
18
|
</OTP.Header>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
className="max-h-[450px] boltdocs-otp-scroll-area"
|
|
93
|
-
ref={scrollContainerRef}
|
|
94
|
-
>
|
|
95
|
-
<OTP.Indicator style={indicatorStyle} />
|
|
96
|
-
<ul
|
|
97
|
-
className="relative space-y-2 border-l border-border-subtle"
|
|
98
|
-
ref={listRef}
|
|
99
|
-
>
|
|
100
|
-
{headings.map((h) => (
|
|
101
|
-
<OTP.Item key={h.id} level={h.level}>
|
|
102
|
-
<OTP.Link
|
|
103
|
-
href={`#${h.id}`}
|
|
104
|
-
active={activeId === h.id}
|
|
105
|
-
onClick={(e) => handleClick(e, h.id)}
|
|
106
|
-
className="pl-4"
|
|
107
|
-
>
|
|
108
|
-
{h.text}
|
|
109
|
-
</OTP.Link>
|
|
110
|
-
</OTP.Item>
|
|
111
|
-
))}
|
|
112
|
-
</ul>
|
|
113
|
-
</OTP.Content>
|
|
114
|
-
</ScrollProvider>
|
|
19
|
+
|
|
20
|
+
<OTP.Tree headings={headings} />
|
|
115
21
|
|
|
116
22
|
{(editLink || communityHelp) && (
|
|
117
|
-
<div className="mt-8 pt-8 border-t border-
|
|
118
|
-
<p className="text-xs font-bold
|
|
119
|
-
Need help?
|
|
120
|
-
</p>
|
|
23
|
+
<div className="mt-8 pt-8 border-t border-subtle space-y-4">
|
|
24
|
+
<p className="text-xs font-bold text-body">Need help?</p>
|
|
121
25
|
<ul className="space-y-3">
|
|
122
26
|
{editLink && filePath && (
|
|
123
27
|
<li>
|
|
@@ -125,7 +29,7 @@ function OnThisPageInner({
|
|
|
125
29
|
href={editLink.replace(':path', filePath)}
|
|
126
30
|
target="_blank"
|
|
127
31
|
rel="noopener noreferrer"
|
|
128
|
-
className="flex items-center gap-2 text-sm text-
|
|
32
|
+
className="flex items-center gap-2 text-sm text-muted hover:text-body transition-colors"
|
|
129
33
|
>
|
|
130
34
|
<Pencil size={16} />
|
|
131
35
|
Edit this page
|
|
@@ -138,7 +42,7 @@ function OnThisPageInner({
|
|
|
138
42
|
href={communityHelp}
|
|
139
43
|
target="_blank"
|
|
140
44
|
rel="noopener noreferrer"
|
|
141
|
-
className="flex items-center gap-2 text-sm text-
|
|
45
|
+
className="flex items-center gap-2 text-sm text-muted hover:text-body transition-colors"
|
|
142
46
|
>
|
|
143
47
|
<CircleHelp size={16} />
|
|
144
48
|
Community help
|
|
@@ -3,7 +3,7 @@ import { PageNav as PageNavPrimitive } from '../primitives/page-nav'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Component to display the previous and next page navigation buttons.
|
|
6
|
-
* Enhanced with subtle entrance animations
|
|
6
|
+
* Enhanced with subtle entrance animations, modern card layout, and hover highlights.
|
|
7
7
|
*/
|
|
8
8
|
export function PageNav() {
|
|
9
9
|
const { prevPage, nextPage } = usePageNav()
|
|
@@ -11,11 +11,17 @@ export function PageNav() {
|
|
|
11
11
|
if (!prevPage && !nextPage) return null
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
|
-
<PageNavPrimitive.Root className="animate-in fade-in slide-in-from-bottom-4 duration-700">
|
|
14
|
+
<PageNavPrimitive.Root className="pt-8 border-t border-subtle grid sm:grid-cols-2 gap-4 animate-in fade-in slide-in-from-bottom-4 duration-700 select-none">
|
|
15
15
|
{prevPage ? (
|
|
16
|
-
<PageNavPrimitive.Link
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
<PageNavPrimitive.Link
|
|
17
|
+
to={prevPage.path}
|
|
18
|
+
direction="prev"
|
|
19
|
+
className="group border border-subtle bg-surface p-5 rounded-2xl transition-all duration-300 hover:border-primary-500/50 hover:bg-primary-50/20"
|
|
20
|
+
>
|
|
21
|
+
<PageNavPrimitive.Title className="text-xs font-bold uppercase tracking-wider text-muted/60 mb-1">
|
|
22
|
+
Previous
|
|
23
|
+
</PageNavPrimitive.Title>
|
|
24
|
+
<PageNavPrimitive.Description className="text-sm sm:text-base font-bold text-body group-hover:text-primary-500 transition-colors">
|
|
19
25
|
{prevPage.title}
|
|
20
26
|
</PageNavPrimitive.Description>
|
|
21
27
|
</PageNavPrimitive.Link>
|
|
@@ -23,13 +29,21 @@ export function PageNav() {
|
|
|
23
29
|
<div />
|
|
24
30
|
)}
|
|
25
31
|
|
|
26
|
-
{nextPage
|
|
27
|
-
<PageNavPrimitive.Link
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
{nextPage ? (
|
|
33
|
+
<PageNavPrimitive.Link
|
|
34
|
+
to={nextPage.path}
|
|
35
|
+
direction="next"
|
|
36
|
+
className="group border border-subtle bg-surface p-5 rounded-2xl transition-all duration-300 hover:border-primary-500/50 hover:bg-primary-50/20"
|
|
37
|
+
>
|
|
38
|
+
<PageNavPrimitive.Title className="text-xs font-bold uppercase tracking-wider text-muted/60 mb-1">
|
|
39
|
+
Next
|
|
40
|
+
</PageNavPrimitive.Title>
|
|
41
|
+
<PageNavPrimitive.Description className="text-sm sm:text-base font-bold text-body group-hover:text-primary-500 transition-colors">
|
|
30
42
|
{nextPage.title}
|
|
31
43
|
</PageNavPrimitive.Description>
|
|
32
44
|
</PageNavPrimitive.Link>
|
|
45
|
+
) : (
|
|
46
|
+
<div />
|
|
33
47
|
)}
|
|
34
48
|
</PageNavPrimitive.Root>
|
|
35
49
|
)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { useEffect, useCallback } from 'react'
|
|
2
|
+
import { Search } from 'lucide-react'
|
|
2
3
|
import { useSearch } from '../../hooks/use-search'
|
|
3
4
|
import { SearchDialog as SearchDialogPrimitive } from '../primitives/search-dialog'
|
|
4
5
|
import Navbar from '../primitives/navbar'
|
|
5
6
|
import { useNavigate } from 'react-router-dom'
|
|
6
7
|
import type { ComponentRoute } from '../../types'
|
|
8
|
+
|
|
7
9
|
interface SearchResult {
|
|
8
10
|
id: string
|
|
9
11
|
title: string
|
|
@@ -13,6 +15,29 @@ interface SearchResult {
|
|
|
13
15
|
isHeading?: boolean
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
function Highlight({ text, query }: { text: string; query: string }) {
|
|
19
|
+
if (!query || !text) return <>{text}</>
|
|
20
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
21
|
+
const regex = new RegExp(`(${escapedQuery})`, 'gi')
|
|
22
|
+
const parts = text.split(regex)
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
{parts.map((part, i) =>
|
|
26
|
+
regex.test(part) ? (
|
|
27
|
+
<mark
|
|
28
|
+
key={i}
|
|
29
|
+
className="bg-primary-500/20 text-primary-600 dark:text-primary-400 font-bold px-0.5 rounded-sm"
|
|
30
|
+
>
|
|
31
|
+
{part}
|
|
32
|
+
</mark>
|
|
33
|
+
) : (
|
|
34
|
+
part
|
|
35
|
+
),
|
|
36
|
+
)}
|
|
37
|
+
</>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
16
41
|
export function SearchDialog({ routes }: { routes: ComponentRoute[] }) {
|
|
17
42
|
const { isOpen, setIsOpen, query, setQuery, list } = useSearch(routes)
|
|
18
43
|
const navigate = useNavigate()
|
|
@@ -36,53 +61,103 @@ export function SearchDialog({ routes }: { routes: ComponentRoute[] }) {
|
|
|
36
61
|
const path = String(key)
|
|
37
62
|
setIsOpen(false)
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
const [baseUrl, hash] = path.split('#')
|
|
65
|
+
const search = query ? `?hl=${encodeURIComponent(query)}` : ''
|
|
66
|
+
const finalPath = `${baseUrl}${search}${hash ? `#${hash}` : ''}`
|
|
67
|
+
|
|
68
|
+
navigate(finalPath)
|
|
69
|
+
|
|
70
|
+
if (hash) {
|
|
42
71
|
setTimeout(() => {
|
|
43
|
-
const el = document.getElementById(
|
|
72
|
+
const el = document.getElementById(hash)
|
|
44
73
|
if (el) el.scrollIntoView({ behavior: 'smooth' })
|
|
45
74
|
}, 100)
|
|
46
|
-
} else {
|
|
47
|
-
navigate(path)
|
|
48
75
|
}
|
|
49
76
|
},
|
|
50
|
-
[navigate, setIsOpen],
|
|
77
|
+
[navigate, setIsOpen, query],
|
|
51
78
|
)
|
|
52
79
|
|
|
53
80
|
return (
|
|
54
81
|
<>
|
|
55
|
-
<Navbar.SearchTrigger
|
|
82
|
+
<Navbar.SearchTrigger.Desktop
|
|
83
|
+
onPress={() => setIsOpen(true)}
|
|
84
|
+
className="rounded-xl border border-subtle bg-surface text-muted transition-all duration-200 hover:border-primary-500/50 hover:text-body hover:bg-soft/50 hover:shadow-sm active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-primary-500/30"
|
|
85
|
+
>
|
|
86
|
+
<div className="flex items-center gap-2">
|
|
87
|
+
<Search size={16} />
|
|
88
|
+
<span className="hidden sm:inline-block">Search docs...</span>
|
|
89
|
+
</div>
|
|
90
|
+
<Navbar.SearchTrigger.Kbd className="[&_kbd]:bg-main [&_kbd]:border [&_kbd]:border-subtle [&_kbd]:rounded [&_kbd]:px-1.5 [&_kbd]:h-5 [&_kbd]:w-5" />
|
|
91
|
+
</Navbar.SearchTrigger.Desktop>
|
|
56
92
|
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
93
|
+
<Navbar.SearchTrigger.Mobile
|
|
94
|
+
onPress={() => setIsOpen(true)}
|
|
95
|
+
className="rounded-xl text-muted transition-all duration-200 hover:text-body active:scale-95 focus-visible:ring-2 focus-visible:ring-primary-500/30"
|
|
96
|
+
>
|
|
97
|
+
<Search size={20} />
|
|
98
|
+
</Navbar.SearchTrigger.Mobile>
|
|
99
|
+
|
|
100
|
+
<SearchDialogPrimitive.Overlay
|
|
101
|
+
isOpen={isOpen}
|
|
102
|
+
isDismissable
|
|
103
|
+
onOpenChange={() => setIsOpen(false)}
|
|
104
|
+
className="fixed inset-0 z-100 flex items-center justify-center p-4 bg-black/40 backdrop-blur-xs animate-fade-in"
|
|
105
|
+
>
|
|
106
|
+
<SearchDialogPrimitive.Content className="w-full max-w-lg bg-main border border-subtle shadow-md rounded-2xl overflow-hidden p-6">
|
|
107
|
+
<SearchDialogPrimitive.Dialog
|
|
108
|
+
aria-label="Search documentation"
|
|
109
|
+
className="flex flex-col min-h-0 h-[450px]"
|
|
110
|
+
>
|
|
111
|
+
<SearchDialogPrimitive.Autocomplete
|
|
112
|
+
onSelectionChange={handleSelect}
|
|
113
|
+
className="flex flex-col min-h-0"
|
|
114
|
+
>
|
|
115
|
+
<SearchDialogPrimitive.Input
|
|
116
|
+
value={query}
|
|
117
|
+
onChange={setQuery}
|
|
118
|
+
className="flex items-center gap-2 border border-subtle bg-surface px-4 py-2.5 rounded-xl focus-within:border-primary-500 mb-4"
|
|
71
119
|
>
|
|
72
|
-
<SearchDialogPrimitive.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<SearchDialogPrimitive.
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
120
|
+
<SearchDialogPrimitive.Input.SearchInput
|
|
121
|
+
placeholder="Search documentation..."
|
|
122
|
+
className="w-full bg-transparent outline-none text-body text-sm"
|
|
123
|
+
/>
|
|
124
|
+
{query && (
|
|
125
|
+
<SearchDialogPrimitive.Input.Button
|
|
126
|
+
onPress={() => setQuery('')}
|
|
127
|
+
className="text-muted hover:text-body text-xs cursor-pointer select-none"
|
|
128
|
+
>
|
|
129
|
+
✕
|
|
130
|
+
</SearchDialogPrimitive.Input.Button>
|
|
131
|
+
)}
|
|
132
|
+
</SearchDialogPrimitive.Input>
|
|
133
|
+
|
|
134
|
+
<SearchDialogPrimitive.List items={list as SearchResult[]}>
|
|
135
|
+
{(item: SearchResult) => (
|
|
136
|
+
<SearchDialogPrimitive.Item
|
|
137
|
+
key={item.id}
|
|
138
|
+
onPress={() => handleSelect(item.id)}
|
|
139
|
+
textValue={item.title}
|
|
140
|
+
className="flex items-center gap-3 px-4 py-2 rounded-xl group dark:hover:bg-primary-300/40 hover:bg-primary-200/50 transition-colors duration-100"
|
|
141
|
+
>
|
|
142
|
+
<SearchDialogPrimitive.Item.Icon
|
|
143
|
+
isHeading={item.isHeading}
|
|
144
|
+
className="text-muted group-hover:text-primary-500 group-focus:text-primary-500"
|
|
145
|
+
/>
|
|
146
|
+
<div className="flex flex-col justify-center min-w-0">
|
|
147
|
+
<SearchDialogPrimitive.Item.Title className="text-sm font-medium text-body truncate dark:group-hover:text-primary-100">
|
|
148
|
+
<Highlight text={item.title} query={query} />
|
|
149
|
+
</SearchDialogPrimitive.Item.Title>
|
|
150
|
+
<SearchDialogPrimitive.Item.Bio className="text-xs text-muted truncate">
|
|
151
|
+
<Highlight text={item.bio} query={query} />
|
|
152
|
+
</SearchDialogPrimitive.Item.Bio>
|
|
153
|
+
</div>
|
|
154
|
+
</SearchDialogPrimitive.Item>
|
|
155
|
+
)}
|
|
156
|
+
</SearchDialogPrimitive.List>
|
|
157
|
+
</SearchDialogPrimitive.Autocomplete>
|
|
158
|
+
</SearchDialogPrimitive.Dialog>
|
|
159
|
+
</SearchDialogPrimitive.Content>
|
|
160
|
+
</SearchDialogPrimitive.Overlay>
|
|
86
161
|
</>
|
|
87
162
|
)
|
|
88
163
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useSearchHighlight } from '../../hooks/use-search-highlight'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Component that enables search term highlighting on the page.
|
|
5
|
+
* It doesn't render anything visible.
|
|
6
|
+
*/
|
|
7
|
+
export function SearchHighlight() {
|
|
8
|
+
useSearchHighlight('.boltdocs-page')
|
|
9
|
+
return null
|
|
10
|
+
}
|