boltdocs 2.7.10 → 2.7.11
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/dist/client/index.cjs +1929 -1
- package/dist/client/index.js +1880 -1
- package/dist/client/mdx.cjs +7 -1
- package/dist/client/mdx.js +7 -1
- package/dist/client/primitives.cjs +60 -1
- package/dist/client/primitives.js +20 -1
- package/dist/docs-layout-BXHV0xw_.cjs +1431 -0
- package/dist/docs-layout-DwFndmj5.js +1231 -0
- package/dist/icons-dev-3cZMyt8r.cjs +1204 -0
- package/dist/icons-dev-Df8OQ481.js +839 -0
- package/dist/image-DtrI2cw3.cjs +268 -0
- package/dist/image-jxPb-2iV.js +214 -0
- package/dist/mdx-BdWkJTeB.cjs +523 -0
- package/dist/mdx-UTTLFWJq.js +494 -0
- package/dist/node/cli-entry.cjs +1 -1
- package/dist/node/cli-entry.mjs +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.mjs +1 -1
- package/dist/{node-DtEDyN1u.cjs → node-BSM4qcDK.cjs} +1 -1
- package/dist/{node-_1jhMGYx.mjs → node-BspZN3R2.mjs} +1 -1
- package/dist/{package-DrwtlXfk.cjs → package-DIIrjuWI.cjs} +1 -1
- package/dist/{package--0Yf0t1N.mjs → package-K0zsjGIz.mjs} +1 -1
- package/dist/{search-dialog-ByvGScjt.js → search-dialog-BHuIiUC6.js} +3 -1
- package/dist/search-dialog-BNF10tDl.js +375 -0
- package/dist/search-dialog-BwkDuI9R.cjs +220 -0
- package/dist/search-dialog-C7xuvyNk.cjs +386 -0
- package/dist/search-dialog-CIQg6k8c.cjs +8 -0
- package/dist/search-dialog-D-DDN7zJ.js +208 -0
- package/package.json +3 -4
- package/dist/docs-layout-KoWNZc8_.js +0 -6
- package/dist/docs-layout-x2yKt2cL.cjs +0 -6
- package/dist/icons-dev-B_RZIyxu.js +0 -6
- package/dist/icons-dev-BlV3wWFT.cjs +0 -6
- package/dist/image-BHhTvQzr.cjs +0 -6
- package/dist/image-CqKzYD8f.js +0 -6
- package/dist/mdx-DudBEac0.js +0 -7
- package/dist/mdx-r4cDQxWu.cjs +0 -7
- package/dist/search-dialog-B584t9ZF.js +0 -6
- package/dist/search-dialog-BvBopRsZ.cjs +0 -6
- package/dist/search-dialog-Cyko6TJm.cjs +0 -6
- package/dist/search-dialog-D6BNohIJ.js +0 -6
- package/dist/search-dialog-DuYTIefy.cjs +0 -6
- package/src/client/app/config-context.tsx +0 -51
- package/src/client/app/doc-page.tsx +0 -38
- package/src/client/app/docs-layout.tsx +0 -28
- package/src/client/app/head.tsx +0 -122
- package/src/client/app/helmet-compat.tsx +0 -36
- package/src/client/app/mdx-component.tsx +0 -8
- package/src/client/app/mdx-components-context.tsx +0 -72
- package/src/client/app/routes-context.tsx +0 -34
- package/src/client/app/scroll-handler.tsx +0 -74
- package/src/client/app/theme-context.tsx +0 -103
- package/src/client/app/ui-context.tsx +0 -42
- package/src/client/components/docs-layout-default.tsx +0 -85
- package/src/client/components/icons-dev.tsx +0 -282
- package/src/client/components/mdx/callout.tsx +0 -97
- package/src/client/components/mdx/card.tsx +0 -99
- package/src/client/components/mdx/cards.tsx +0 -27
- package/src/client/components/mdx/code-block.tsx +0 -184
- package/src/client/components/mdx/field.tsx +0 -33
- package/src/client/components/mdx/image.tsx +0 -44
- package/src/client/components/mdx/index.ts +0 -19
- package/src/client/components/mdx/table.tsx +0 -54
- package/src/client/components/mdx/typographics.tsx +0 -120
- package/src/client/components/mdx/use-code-block.ts +0 -34
- package/src/client/components/primitives/breadcrumbs.tsx +0 -54
- package/src/client/components/primitives/button-group.tsx +0 -54
- package/src/client/components/primitives/button.tsx +0 -6
- package/src/client/components/primitives/code-block.tsx +0 -120
- package/src/client/components/primitives/docs-layout.tsx +0 -125
- package/src/client/components/primitives/error-boundary.tsx +0 -107
- package/src/client/components/primitives/heading.tsx +0 -128
- package/src/client/components/primitives/helpers/observer.ts +0 -141
- package/src/client/components/primitives/image.tsx +0 -26
- package/src/client/components/primitives/link.tsx +0 -102
- package/src/client/components/primitives/menu.tsx +0 -137
- package/src/client/components/primitives/navbar.tsx +0 -466
- package/src/client/components/primitives/on-this-page.tsx +0 -430
- package/src/client/components/primitives/page-nav.tsx +0 -51
- package/src/client/components/primitives/popover.tsx +0 -28
- package/src/client/components/primitives/search-dialog.tsx +0 -193
- package/src/client/components/primitives/sidebar.tsx +0 -423
- package/src/client/components/primitives/skeleton.tsx +0 -26
- package/src/client/components/primitives/tabs.tsx +0 -70
- package/src/client/components/primitives/tooltip.tsx +0 -81
- package/src/client/components/primitives/types.ts +0 -11
- package/src/client/components/ui-base/banner.tsx +0 -66
- package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
- package/src/client/components/ui-base/copy-markdown.tsx +0 -107
- package/src/client/components/ui-base/error-boundary.tsx +0 -15
- package/src/client/components/ui-base/github-stars.tsx +0 -29
- package/src/client/components/ui-base/icons.tsx +0 -240
- package/src/client/components/ui-base/index.ts +0 -16
- package/src/client/components/ui-base/last-updated.tsx +0 -27
- package/src/client/components/ui-base/navbar.tsx +0 -266
- package/src/client/components/ui-base/not-found.tsx +0 -26
- package/src/client/components/ui-base/on-this-page.tsx +0 -57
- package/src/client/components/ui-base/page-nav.tsx +0 -50
- package/src/client/components/ui-base/search-dialog.tsx +0 -163
- package/src/client/components/ui-base/search-highlight.tsx +0 -10
- package/src/client/components/ui-base/sidebar.tsx +0 -92
- package/src/client/components/ui-base/tabs.tsx +0 -83
- package/src/client/components/ui-base/theme-toggle.tsx +0 -130
- package/src/client/components/ui-base/version-i18n.tsx +0 -80
- package/src/client/hooks/index.ts +0 -13
- package/src/client/hooks/use-analytics.ts +0 -272
- package/src/client/hooks/use-breadcrumbs.ts +0 -22
- package/src/client/hooks/use-i18n.ts +0 -182
- package/src/client/hooks/use-localized-to.ts +0 -113
- package/src/client/hooks/use-location.ts +0 -5
- package/src/client/hooks/use-navbar.ts +0 -130
- package/src/client/hooks/use-page-nav.ts +0 -46
- package/src/client/hooks/use-routes.ts +0 -108
- package/src/client/hooks/use-search-highlight.ts +0 -185
- package/src/client/hooks/use-search.ts +0 -118
- package/src/client/hooks/use-sidebar.ts +0 -205
- package/src/client/hooks/use-tabs.ts +0 -46
- package/src/client/hooks/use-version.ts +0 -111
- package/src/client/index.ts +0 -31
- package/src/client/mdx.ts +0 -2
- package/src/client/primitives.ts +0 -19
- package/src/client/ssg/boltdocs-shell.tsx +0 -148
- package/src/client/ssg/create-routes.tsx +0 -473
- package/src/client/ssg/index.ts +0 -4
- package/src/client/ssg/mdx-page.tsx +0 -38
- package/src/client/store/boltdocs-context.tsx +0 -137
- package/src/client/theme/neutral.css +0 -141
- package/src/client/theme/reset.css +0 -189
- package/src/client/types.ts +0 -116
- package/src/client/utils/cn.ts +0 -6
- package/src/client/utils/copy-clipboard.ts +0 -22
- package/src/client/utils/get-base-file-path.ts +0 -21
- package/src/client/utils/github.ts +0 -121
- package/src/client/utils/i18n.ts +0 -23
- package/src/client/utils/path.ts +0 -9
- package/src/client/utils/react-to-text.ts +0 -34
- package/src/client/virtual.d.ts +0 -24
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react'
|
|
2
|
-
import { useTabs as useTabsHook } from '../../hooks/use-tabs'
|
|
3
|
-
import { Tabs as T } from '../primitives/tabs'
|
|
4
|
-
import { Link } from '../primitives/link'
|
|
5
|
-
import type { BoltdocsTab, ComponentRoute } from '../../types'
|
|
6
|
-
import * as DefaultIcons from './icons'
|
|
7
|
-
import virtualIcons from 'virtual:boltdocs-icons'
|
|
8
|
-
import { getTranslated } from '../../utils/i18n'
|
|
9
|
-
import { useRoutes } from '../../hooks/use-routes'
|
|
10
|
-
|
|
11
|
-
export function Tabs({
|
|
12
|
-
tabs,
|
|
13
|
-
routes,
|
|
14
|
-
}: {
|
|
15
|
-
tabs: BoltdocsTab[]
|
|
16
|
-
routes: ComponentRoute[]
|
|
17
|
-
}) {
|
|
18
|
-
const { currentLocale } = useRoutes()
|
|
19
|
-
const { indicatorStyle, tabRefs, activeIndex } = useTabsHook(tabs, routes)
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
const activeTab = tabRefs.current[activeIndex]
|
|
23
|
-
if (activeTab) {
|
|
24
|
-
activeTab.scrollIntoView({
|
|
25
|
-
behavior: 'smooth',
|
|
26
|
-
block: 'nearest',
|
|
27
|
-
inline: 'center',
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
}, [activeIndex])
|
|
31
|
-
|
|
32
|
-
const renderTabIcon = (iconName?: string) => {
|
|
33
|
-
if (!iconName) return null
|
|
34
|
-
if (iconName.trim().startsWith('<svg')) {
|
|
35
|
-
return (
|
|
36
|
-
<span
|
|
37
|
-
className="h-4 w-4"
|
|
38
|
-
dangerouslySetInnerHTML={{ __html: iconName }}
|
|
39
|
-
/>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
const icons = { ...DefaultIcons, ...virtualIcons } as Record<string, any>
|
|
43
|
-
const TabIcon = icons[iconName] || icons[iconName + 'Icon']
|
|
44
|
-
if (TabIcon) {
|
|
45
|
-
return <TabIcon size={16} />
|
|
46
|
-
}
|
|
47
|
-
return <img src={iconName} alt="" className="h-4 w-4 object-contain" />
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<div className="mx-auto max-w-(--breakpoint-3xl) px-4 md:px-6 select-none">
|
|
52
|
-
<T.List className="border-none py-0 scrollbar-hide relative flex flex-row items-center">
|
|
53
|
-
{tabs.map((tab, index) => {
|
|
54
|
-
const isActive = index === activeIndex
|
|
55
|
-
const firstRoute = routes.find(
|
|
56
|
-
(r) => r.tab && r.tab.toLowerCase() === tab.id.toLowerCase(),
|
|
57
|
-
)
|
|
58
|
-
const linkTo = firstRoute ? firstRoute.path : '#'
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<Link
|
|
62
|
-
key={tab.id}
|
|
63
|
-
href={linkTo}
|
|
64
|
-
ref={(el: HTMLAnchorElement | null) => {
|
|
65
|
-
tabRefs.current[index] = el
|
|
66
|
-
}}
|
|
67
|
-
className={`relative flex items-center gap-2 px-4 py-3.5 text-sm font-semibold transition-colors duration-300 outline-none whitespace-nowrap ${
|
|
68
|
-
isActive ? 'text-primary-500' : 'text-muted hover:text-body'
|
|
69
|
-
}`}
|
|
70
|
-
>
|
|
71
|
-
{renderTabIcon(tab.icon)}
|
|
72
|
-
<span>{getTranslated(tab.text, currentLocale)}</span>
|
|
73
|
-
</Link>
|
|
74
|
-
)
|
|
75
|
-
})}
|
|
76
|
-
<T.Indicator
|
|
77
|
-
style={indicatorStyle}
|
|
78
|
-
className="h-0.5 bg-primary-500 rounded-full transition-all duration-300"
|
|
79
|
-
/>
|
|
80
|
-
</T.List>
|
|
81
|
-
</div>
|
|
82
|
-
)
|
|
83
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
import { Sun, Moon, Monitor } from './icons'
|
|
3
|
-
import { useTheme } from '../../app/theme-context'
|
|
4
|
-
import { Button } from 'react-aria-components'
|
|
5
|
-
import { Menu } from '../primitives/menu'
|
|
6
|
-
import { cn } from '../../utils/cn'
|
|
7
|
-
|
|
8
|
-
export function ThemeToggle() {
|
|
9
|
-
const { theme, setTheme } = useTheme()
|
|
10
|
-
const [mounted, setMounted] = useState(false)
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
setMounted(true)
|
|
14
|
-
}, [])
|
|
15
|
-
|
|
16
|
-
if (!mounted) {
|
|
17
|
-
return <div className="h-9 w-9" />
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const Icon = theme === 'system' ? Monitor : theme === 'dark' ? Moon : Sun
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<Menu.Trigger placement="bottom right">
|
|
24
|
-
<Button
|
|
25
|
-
className="flex h-9 w-9 items-center justify-center rounded-xl text-muted transition-colors hover:bg-surface hover:text-body outline-none border-none bg-transparent cursor-pointer"
|
|
26
|
-
aria-label="Selection theme"
|
|
27
|
-
>
|
|
28
|
-
<Icon size={20} className="animate-in fade-in zoom-in duration-300" />
|
|
29
|
-
</Button>
|
|
30
|
-
<Menu.Root
|
|
31
|
-
selectionMode="single"
|
|
32
|
-
selectedKeys={[theme]}
|
|
33
|
-
onSelectionChange={(keys) => {
|
|
34
|
-
const newTheme = Array.from(keys)[0] as 'light' | 'dark' | 'system'
|
|
35
|
-
setTheme(newTheme)
|
|
36
|
-
}}
|
|
37
|
-
className="w-36 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100"
|
|
38
|
-
>
|
|
39
|
-
<Menu.Item
|
|
40
|
-
id="light"
|
|
41
|
-
className="group flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5"
|
|
42
|
-
>
|
|
43
|
-
<Sun
|
|
44
|
-
className="group-hover:text-primary-500 dark:group-hover:text-primary-200"
|
|
45
|
-
size={16}
|
|
46
|
-
/>
|
|
47
|
-
<span className="ml-2">Light</span>
|
|
48
|
-
</Menu.Item>
|
|
49
|
-
<Menu.Item
|
|
50
|
-
id="dark"
|
|
51
|
-
className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5"
|
|
52
|
-
>
|
|
53
|
-
<Moon
|
|
54
|
-
className="group-hover:text-primary-500 dark:group-hover:text-primary-200"
|
|
55
|
-
size={16}
|
|
56
|
-
/>
|
|
57
|
-
<span className="ml-2">Dark</span>
|
|
58
|
-
</Menu.Item>
|
|
59
|
-
<Menu.Item
|
|
60
|
-
id="system"
|
|
61
|
-
className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5"
|
|
62
|
-
>
|
|
63
|
-
<Monitor
|
|
64
|
-
className="group-hover:text-primary-500 dark:group-hover:text-primary-200"
|
|
65
|
-
size={16}
|
|
66
|
-
/>
|
|
67
|
-
<span className="ml-2">System</span>
|
|
68
|
-
</Menu.Item>
|
|
69
|
-
</Menu.Root>
|
|
70
|
-
</Menu.Trigger>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function ThemeSwitcher({ className }: { className?: string }) {
|
|
75
|
-
const { theme, setTheme } = useTheme()
|
|
76
|
-
const [mounted, setMounted] = useState(false)
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
setMounted(true)
|
|
80
|
-
}, [])
|
|
81
|
-
|
|
82
|
-
if (!mounted) {
|
|
83
|
-
return (
|
|
84
|
-
<div
|
|
85
|
-
className={cn(
|
|
86
|
-
'h-10 w-full bg-surface rounded-xl animate-pulse',
|
|
87
|
-
className,
|
|
88
|
-
)}
|
|
89
|
-
/>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const isDark = theme === 'dark'
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<div
|
|
97
|
-
className={cn(
|
|
98
|
-
'flex p-1 bg-surface border border-subtle rounded-xl relative w-full h-11',
|
|
99
|
-
className,
|
|
100
|
-
)}
|
|
101
|
-
>
|
|
102
|
-
<div
|
|
103
|
-
className={cn(
|
|
104
|
-
'absolute inset-y-1 w-[calc(50%-4px)] bg-main border border-subtle rounded-lg transition-all duration-300 ease-out shadow-xs',
|
|
105
|
-
isDark ? 'translate-x-full' : 'translate-x-0',
|
|
106
|
-
)}
|
|
107
|
-
/>
|
|
108
|
-
<button
|
|
109
|
-
onClick={() => setTheme('light')}
|
|
110
|
-
className={cn(
|
|
111
|
-
'flex-1 flex items-center justify-center rounded-lg z-10 transition-colors outline-none cursor-pointer border-none bg-transparent',
|
|
112
|
-
!isDark ? 'text-body font-semibold' : 'text-muted hover:text-body',
|
|
113
|
-
)}
|
|
114
|
-
aria-label="Light mode"
|
|
115
|
-
>
|
|
116
|
-
<Sun size={18} />
|
|
117
|
-
</button>
|
|
118
|
-
<button
|
|
119
|
-
onClick={() => setTheme('dark')}
|
|
120
|
-
className={cn(
|
|
121
|
-
'flex-1 flex items-center justify-center rounded-lg z-10 transition-colors outline-none cursor-pointer border-none bg-transparent',
|
|
122
|
-
isDark ? 'text-body font-semibold' : 'text-muted hover:text-body',
|
|
123
|
-
)}
|
|
124
|
-
aria-label="Dark mode"
|
|
125
|
-
>
|
|
126
|
-
<Moon size={18} />
|
|
127
|
-
</button>
|
|
128
|
-
</div>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { useVersion } from '../../hooks/use-version'
|
|
2
|
-
import { useI18n } from '../../hooks/use-i18n'
|
|
3
|
-
import { Menu } from '../primitives/menu'
|
|
4
|
-
import { Button } from '../primitives/button'
|
|
5
|
-
import { ChevronDown, Languages } from './icons'
|
|
6
|
-
import { cn } from '../../utils/cn'
|
|
7
|
-
|
|
8
|
-
export function VersionSelector({ className }: { className?: string }) {
|
|
9
|
-
const { currentVersionLabel, availableVersions, handleVersionChange } =
|
|
10
|
-
useVersion()
|
|
11
|
-
|
|
12
|
-
if (availableVersions.length === 0) return null
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<Menu.Trigger>
|
|
16
|
-
<Button
|
|
17
|
-
className={cn(
|
|
18
|
-
'flex h-9 items-center justify-between gap-2 border border-subtle bg-surface px-4 py-1.5 rounded-xl text-xs font-semibold text-body hover:bg-primary-50/20 hover:border-primary-500/50 transition-all duration-300 outline-none select-none cursor-pointer',
|
|
19
|
-
className,
|
|
20
|
-
)}
|
|
21
|
-
>
|
|
22
|
-
<span className="font-semibold text-[0.8125rem]">
|
|
23
|
-
{currentVersionLabel}
|
|
24
|
-
</span>
|
|
25
|
-
<ChevronDown className="w-3.5 h-3.5 text-muted/60" />
|
|
26
|
-
</Button>
|
|
27
|
-
<Menu.Root className="w-40 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100">
|
|
28
|
-
<Menu.Section items={availableVersions}>
|
|
29
|
-
{(version) => (
|
|
30
|
-
<Menu.Item
|
|
31
|
-
key={`${version.value ?? ''}`}
|
|
32
|
-
onPress={() => handleVersionChange(version.value)}
|
|
33
|
-
className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body hover:bg-primary-50/50 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5"
|
|
34
|
-
>
|
|
35
|
-
{version.label as string}
|
|
36
|
-
</Menu.Item>
|
|
37
|
-
)}
|
|
38
|
-
</Menu.Section>
|
|
39
|
-
</Menu.Root>
|
|
40
|
-
</Menu.Trigger>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function I18nSelector({ className }: { className?: string }) {
|
|
45
|
-
const { currentLocale, availableLocales, handleLocaleChange } = useI18n()
|
|
46
|
-
|
|
47
|
-
if (availableLocales.length === 0) return null
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<Menu.Trigger>
|
|
51
|
-
<Button
|
|
52
|
-
className={cn(
|
|
53
|
-
'flex h-9 items-center justify-between gap-2 border border-subtle bg-surface px-4 py-1.5 rounded-xl text-xs font-semibold text-body hover:bg-primary-50/20 hover:border-primary-500/50 transition-all duration-300 outline-none select-none cursor-pointer',
|
|
54
|
-
className,
|
|
55
|
-
)}
|
|
56
|
-
>
|
|
57
|
-
<div className="flex items-center gap-1.5">
|
|
58
|
-
<Languages className="w-3.5 h-3.5 text-primary-500" />
|
|
59
|
-
<span className="font-bold text-[0.75rem] uppercase opacity-90">
|
|
60
|
-
{currentLocale || 'en'}
|
|
61
|
-
</span>
|
|
62
|
-
</div>
|
|
63
|
-
<ChevronDown className="w-3.5 h-3.5 text-muted/60" />
|
|
64
|
-
</Button>
|
|
65
|
-
<Menu.Root className="w-40 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100">
|
|
66
|
-
<Menu.Section items={availableLocales}>
|
|
67
|
-
{(locale) => (
|
|
68
|
-
<Menu.Item
|
|
69
|
-
key={`${locale.value ?? ''}`}
|
|
70
|
-
onPress={() => handleLocaleChange(locale.value)}
|
|
71
|
-
className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5"
|
|
72
|
-
>
|
|
73
|
-
<span>{locale.label as string}</span>
|
|
74
|
-
</Menu.Item>
|
|
75
|
-
)}
|
|
76
|
-
</Menu.Section>
|
|
77
|
-
</Menu.Root>
|
|
78
|
-
</Menu.Trigger>
|
|
79
|
-
)
|
|
80
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export { useNavbar } from './use-navbar'
|
|
2
|
-
export { useSidebar } from './use-sidebar'
|
|
3
|
-
export { useSearch } from './use-search'
|
|
4
|
-
export { useTabs } from './use-tabs'
|
|
5
|
-
export { useVersion } from './use-version'
|
|
6
|
-
export { useI18n } from './use-i18n'
|
|
7
|
-
export { usePageNav } from './use-page-nav'
|
|
8
|
-
export { useBreadcrumbs } from './use-breadcrumbs'
|
|
9
|
-
export { useRoutes } from './use-routes'
|
|
10
|
-
export { useLocalizedTo } from './use-localized-to'
|
|
11
|
-
export { useLocation } from './use-location'
|
|
12
|
-
export { useSearchHighlight } from './use-search-highlight'
|
|
13
|
-
export { useAnalytics, useTrackPageView, useTrackEvent } from './use-analytics'
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
2
|
-
import { useLocation } from './use-location'
|
|
3
|
-
import type { BoltdocsIntegrationsConfig } from '../../shared/types'
|
|
4
|
-
|
|
5
|
-
declare global {
|
|
6
|
-
interface Window {
|
|
7
|
-
gtag?: (...args: unknown[]) => void
|
|
8
|
-
dataLayer?: unknown[]
|
|
9
|
-
gtag_report_conversion?: (url?: string) => boolean
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface AnalyticsEvent {
|
|
14
|
-
action: string
|
|
15
|
-
category?: string
|
|
16
|
-
label?: string
|
|
17
|
-
value?: number
|
|
18
|
-
params?: Record<string, unknown>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface AnalyticsInstance {
|
|
22
|
-
trackPageView: (path: string, title?: string) => void
|
|
23
|
-
trackEvent: (event: AnalyticsEvent) => void
|
|
24
|
-
trackSearch: (query: string, resultsCount?: number) => void
|
|
25
|
-
trackDownload: (file: string, type?: string) => void
|
|
26
|
-
trackExternalLink: (url: string) => void
|
|
27
|
-
isEnabled: boolean
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function createAnalyticsInstance(
|
|
31
|
-
config?: BoltdocsIntegrationsConfig,
|
|
32
|
-
): AnalyticsInstance {
|
|
33
|
-
if (typeof window === 'undefined') {
|
|
34
|
-
return createDisabledAnalytics()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const isGtagAvailable = typeof window.gtag === 'function'
|
|
38
|
-
|
|
39
|
-
if (isGtagAvailable) {
|
|
40
|
-
return createGtagAnalytics(config)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (window.dataLayer) {
|
|
44
|
-
return createDataLayerAnalytics(config)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return createDisabledAnalytics()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function createGtagAnalytics(
|
|
51
|
-
config?: BoltdocsIntegrationsConfig,
|
|
52
|
-
): AnalyticsInstance {
|
|
53
|
-
return {
|
|
54
|
-
trackPageView: (path: string, title?: string) => {
|
|
55
|
-
window.gtag?.('event', 'page_view', {
|
|
56
|
-
page_path: path,
|
|
57
|
-
page_title: title || document.title,
|
|
58
|
-
send_to: config?.ga4?.measurementId,
|
|
59
|
-
})
|
|
60
|
-
},
|
|
61
|
-
trackEvent: ({ action, category, label, value, params }) => {
|
|
62
|
-
window.gtag?.('event', action, {
|
|
63
|
-
event_category: category,
|
|
64
|
-
event_label: label,
|
|
65
|
-
value,
|
|
66
|
-
send_to: config?.ga4?.measurementId,
|
|
67
|
-
...params,
|
|
68
|
-
})
|
|
69
|
-
},
|
|
70
|
-
trackSearch: (query: string, resultsCount?: number) => {
|
|
71
|
-
window.gtag?.('event', 'search', {
|
|
72
|
-
search_term: query,
|
|
73
|
-
results_count: resultsCount,
|
|
74
|
-
send_to: config?.ga4?.measurementId,
|
|
75
|
-
})
|
|
76
|
-
},
|
|
77
|
-
trackDownload: (file: string, type?: string) => {
|
|
78
|
-
window.gtag?.('event', 'file_download', {
|
|
79
|
-
file_name: file,
|
|
80
|
-
file_type: type || file.split('.').pop(),
|
|
81
|
-
send_to: config?.ga4?.measurementId,
|
|
82
|
-
})
|
|
83
|
-
},
|
|
84
|
-
trackExternalLink: (url: string) => {
|
|
85
|
-
window.gtag?.('event', 'external_link', {
|
|
86
|
-
link_url: url,
|
|
87
|
-
send_to: config?.ga4?.measurementId,
|
|
88
|
-
})
|
|
89
|
-
},
|
|
90
|
-
isEnabled: true,
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function createDataLayerAnalytics(
|
|
95
|
-
config?: BoltdocsIntegrationsConfig,
|
|
96
|
-
): AnalyticsInstance {
|
|
97
|
-
return {
|
|
98
|
-
trackPageView: (path: string, title?: string) => {
|
|
99
|
-
window.dataLayer?.push({
|
|
100
|
-
event: 'page_view',
|
|
101
|
-
page_path: path,
|
|
102
|
-
page_title: title || document.title,
|
|
103
|
-
send_to: config?.gtm?.tagId,
|
|
104
|
-
})
|
|
105
|
-
},
|
|
106
|
-
trackEvent: ({ action, category, label, value, params }) => {
|
|
107
|
-
window.dataLayer?.push({
|
|
108
|
-
event: action,
|
|
109
|
-
event_category: category,
|
|
110
|
-
event_label: label,
|
|
111
|
-
value,
|
|
112
|
-
send_to: config?.gtm?.tagId,
|
|
113
|
-
...params,
|
|
114
|
-
})
|
|
115
|
-
},
|
|
116
|
-
trackSearch: (query: string, resultsCount?: number) => {
|
|
117
|
-
window.dataLayer?.push({
|
|
118
|
-
event: 'search',
|
|
119
|
-
search_term: query,
|
|
120
|
-
results_count: resultsCount,
|
|
121
|
-
send_to: config?.gtm?.tagId,
|
|
122
|
-
})
|
|
123
|
-
},
|
|
124
|
-
trackDownload: (file: string, type?: string) => {
|
|
125
|
-
window.dataLayer?.push({
|
|
126
|
-
event: 'file_download',
|
|
127
|
-
file_name: file,
|
|
128
|
-
file_type: type || file.split('.').pop(),
|
|
129
|
-
send_to: config?.gtm?.tagId,
|
|
130
|
-
})
|
|
131
|
-
},
|
|
132
|
-
trackExternalLink: (url: string) => {
|
|
133
|
-
window.dataLayer?.push({
|
|
134
|
-
event: 'external_link',
|
|
135
|
-
link_url: url,
|
|
136
|
-
send_to: config?.gtm?.tagId,
|
|
137
|
-
})
|
|
138
|
-
},
|
|
139
|
-
isEnabled: true,
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function createDisabledAnalytics(): AnalyticsInstance {
|
|
144
|
-
return {
|
|
145
|
-
trackPageView: () => {},
|
|
146
|
-
trackEvent: () => {},
|
|
147
|
-
trackSearch: () => {},
|
|
148
|
-
trackDownload: () => {},
|
|
149
|
-
trackExternalLink: () => {},
|
|
150
|
-
isEnabled: false,
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface UseAnalyticsOptions {
|
|
155
|
-
config?: BoltdocsIntegrationsConfig
|
|
156
|
-
autoTrackPageViews?: boolean
|
|
157
|
-
autoTrackDownloads?: boolean
|
|
158
|
-
autoTrackExternalLinks?: boolean
|
|
159
|
-
excludePatterns?: RegExp[]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const CONFIG_INSTANCE_SYMBOL = Symbol.for('__BDOCS_CONFIG_INSTANCE__')
|
|
163
|
-
|
|
164
|
-
export function useAnalytics(options: UseAnalyticsOptions = {}) {
|
|
165
|
-
const {
|
|
166
|
-
config: optionsConfig,
|
|
167
|
-
autoTrackPageViews = true,
|
|
168
|
-
autoTrackDownloads = true,
|
|
169
|
-
autoTrackExternalLinks = true,
|
|
170
|
-
excludePatterns = [],
|
|
171
|
-
} = options
|
|
172
|
-
|
|
173
|
-
const globalConfig =
|
|
174
|
-
typeof globalThis !== 'undefined'
|
|
175
|
-
? ((globalThis as any)[CONFIG_INSTANCE_SYMBOL] as
|
|
176
|
-
| { integrations?: BoltdocsIntegrationsConfig }
|
|
177
|
-
| undefined)
|
|
178
|
-
: undefined
|
|
179
|
-
const config = optionsConfig ?? globalConfig?.integrations
|
|
180
|
-
|
|
181
|
-
const analytics = useMemo(() => createAnalyticsInstance(config), [config])
|
|
182
|
-
|
|
183
|
-
const previousPath = useRef<string>('')
|
|
184
|
-
const location = useLocation()
|
|
185
|
-
|
|
186
|
-
useEffect(() => {
|
|
187
|
-
if (!autoTrackPageViews || !analytics.isEnabled) return
|
|
188
|
-
|
|
189
|
-
const path = location.pathname + location.search
|
|
190
|
-
|
|
191
|
-
if (path !== previousPath.current) {
|
|
192
|
-
previousPath.current = path
|
|
193
|
-
analytics.trackPageView(path, document.title)
|
|
194
|
-
}
|
|
195
|
-
}, [location.pathname, autoTrackPageViews, analytics])
|
|
196
|
-
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
if (!autoTrackDownloads || !analytics.isEnabled) return
|
|
199
|
-
|
|
200
|
-
const handleClick = (event: MouseEvent) => {
|
|
201
|
-
const target = (event.target as Element)?.closest('a')
|
|
202
|
-
if (!target) return
|
|
203
|
-
|
|
204
|
-
const href = target.getAttribute('href')
|
|
205
|
-
if (!href) return
|
|
206
|
-
|
|
207
|
-
if (excludePatterns.some((pattern) => pattern.test(href))) return
|
|
208
|
-
|
|
209
|
-
const isDownload =
|
|
210
|
-
target.hasAttribute('download') ||
|
|
211
|
-
/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar|7z|tar|gz|mp3|mp4|avi|mov|png|jpg|jpeg|gif|svg|webp)$/i.test(
|
|
212
|
-
href,
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
if (isDownload) {
|
|
216
|
-
const fileName = href.split('/').pop() || href
|
|
217
|
-
analytics.trackDownload(fileName, fileName.split('.').pop())
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
document.addEventListener('click', handleClick)
|
|
222
|
-
return () => document.removeEventListener('click', handleClick)
|
|
223
|
-
}, [autoTrackDownloads, autoTrackExternalLinks, analytics, excludePatterns])
|
|
224
|
-
|
|
225
|
-
useEffect(() => {
|
|
226
|
-
if (!autoTrackExternalLinks || !analytics.isEnabled) return
|
|
227
|
-
|
|
228
|
-
const handleClick = (event: MouseEvent) => {
|
|
229
|
-
const target = (event.target as Element)?.closest('a')
|
|
230
|
-
if (!target) return
|
|
231
|
-
|
|
232
|
-
const href = target.getAttribute('href')
|
|
233
|
-
if (!href) return
|
|
234
|
-
|
|
235
|
-
if (excludePatterns.some((pattern) => pattern.test(href))) return
|
|
236
|
-
|
|
237
|
-
const isExternal =
|
|
238
|
-
href.startsWith('http://') ||
|
|
239
|
-
href.startsWith('https://') ||
|
|
240
|
-
href.startsWith('//')
|
|
241
|
-
|
|
242
|
-
if (isExternal && !href.includes(window.location.hostname)) {
|
|
243
|
-
analytics.trackExternalLink(href)
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
document.addEventListener('click', handleClick)
|
|
248
|
-
return () => document.removeEventListener('click', handleClick)
|
|
249
|
-
}, [autoTrackExternalLinks, analytics, excludePatterns])
|
|
250
|
-
|
|
251
|
-
return analytics
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export function useTrackPageView() {
|
|
255
|
-
const analytics = useMemo(() => createAnalyticsInstance(), [])
|
|
256
|
-
return useCallback(
|
|
257
|
-
(path: string, title?: string) => {
|
|
258
|
-
analytics.trackPageView(path, title)
|
|
259
|
-
},
|
|
260
|
-
[analytics],
|
|
261
|
-
)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export function useTrackEvent() {
|
|
265
|
-
const analytics = useMemo(() => createAnalyticsInstance(), [])
|
|
266
|
-
return useCallback(
|
|
267
|
-
(event: AnalyticsEvent) => {
|
|
268
|
-
analytics.trackEvent(event)
|
|
269
|
-
},
|
|
270
|
-
[analytics],
|
|
271
|
-
)
|
|
272
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { useRoutes } from './use-routes'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Hook to generate breadcrumbs based on the current active route.
|
|
5
|
-
*/
|
|
6
|
-
export function useBreadcrumbs() {
|
|
7
|
-
const { currentRoute: activeRoute } = useRoutes()
|
|
8
|
-
|
|
9
|
-
const crumbs: Array<{ label: string; href?: string }> = []
|
|
10
|
-
|
|
11
|
-
if (activeRoute) {
|
|
12
|
-
if (activeRoute.groupTitle) {
|
|
13
|
-
crumbs.push({ label: activeRoute.groupTitle })
|
|
14
|
-
}
|
|
15
|
-
crumbs.push({ label: activeRoute.title, href: activeRoute.path })
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
crumbs,
|
|
20
|
-
activeRoute,
|
|
21
|
-
}
|
|
22
|
-
}
|