boltdocs 2.7.10 → 2.8.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 (193) hide show
  1. package/README.md +2 -2
  2. package/dist/banner-3N4Jd_L9.d.ts +100 -0
  3. package/dist/banner-MynZD_Ox.d.cts +100 -0
  4. package/dist/cache-BMUyNiiA.mjs +6 -0
  5. package/dist/cache-CKm45d2w.cjs +6 -0
  6. package/dist/client/index.cjs +2268 -1
  7. package/dist/client/index.d.cts +86 -110
  8. package/dist/client/index.d.ts +87 -111
  9. package/dist/client/index.js +2214 -1
  10. package/dist/client/mdx.cjs +12 -1
  11. package/dist/client/mdx.d.cts +39 -93
  12. package/dist/client/mdx.d.ts +38 -93
  13. package/dist/client/mdx.js +7 -1
  14. package/dist/client/primitives.cjs +60 -1
  15. package/dist/client/primitives.d.cts +411 -347
  16. package/dist/client/primitives.d.ts +411 -347
  17. package/dist/client/primitives.js +20 -1
  18. package/dist/docs-layout-CwCq42Zt.cjs +1348 -0
  19. package/dist/docs-layout-Dn6S5g59.js +1167 -0
  20. package/dist/doctor-BArviV8X.cjs +28 -0
  21. package/dist/doctor-CgLA7_Uv.mjs +28 -0
  22. package/dist/{doctor-CrytFkqW.cjs → doctor-DyNUVe96.cjs} +1 -1
  23. package/dist/{routes-DP1vmWRj.cjs → doctor-aN_leTbh.mjs} +1 -1
  24. package/dist/{generator-ClVanhvi.mjs → generator-BHCrLU6h.mjs} +2 -2
  25. package/dist/{generator-CHqxiQhF.cjs → generator-CC2yHzhZ.cjs} +2 -2
  26. package/dist/icons-dev-DvJ-hh9x.cjs +1209 -0
  27. package/dist/icons-dev-Oju24Wjp.js +845 -0
  28. package/dist/image-Ch4-GxdO.cjs +268 -0
  29. package/dist/image-Do8V9PCW.js +214 -0
  30. package/dist/mdx-D3A2_l7P.js +520 -0
  31. package/dist/mdx-PLhhPJRS.cjs +531 -0
  32. package/dist/node/cli-entry.cjs +3 -1
  33. package/dist/node/cli-entry.mjs +3 -1
  34. package/dist/node/index.cjs +1 -1
  35. package/dist/node/index.d.cts +258 -152
  36. package/dist/node/index.d.mts +258 -150
  37. package/dist/node/index.mjs +1 -1
  38. package/dist/node/routes/worker.cjs +1 -1
  39. package/dist/node/routes/worker.mjs +1 -1
  40. package/dist/node-BmlP0eBP.cjs +159 -0
  41. package/dist/node-Y8_4ayje.mjs +159 -0
  42. package/dist/package-2nFy_NsW.cjs +6 -0
  43. package/dist/{package--0Yf0t1N.mjs → package-DAbtltXX.mjs} +1 -1
  44. package/dist/parser-B7-6PyQz.cjs +6 -0
  45. package/dist/{parser-Aq8LoH-0.cjs → parser-BzB-zCkF.cjs} +1 -1
  46. package/dist/parser-WGZdWs0X.mjs +6 -0
  47. package/dist/routes-BDDSxAl0.mjs +6 -0
  48. package/dist/routes-DJNJ-rTt.cjs +6 -0
  49. package/dist/routes-DiYC4nD2.cjs +6 -0
  50. package/dist/routes-_Bb2f4eI.mjs +6 -0
  51. package/dist/search-dialog-BXVoecTx.cjs +483 -0
  52. package/dist/search-dialog-BYhOov4S.cjs +331 -0
  53. package/dist/search-dialog-C09riYmx.js +313 -0
  54. package/dist/search-dialog-CUeAfy-8.cjs +8 -0
  55. package/dist/search-dialog-D8gLkhUV.js +453 -0
  56. package/dist/search-dialog-DHc_8FFX.js +8 -0
  57. package/dist/{sidebar-CcBkrm06.d.cts → sidebar-DNq4_ZAa.d.ts} +118 -52
  58. package/dist/{sidebar-CyZS9YOm.d.ts → sidebar-Dlkgbxs6.d.cts} +118 -52
  59. package/dist/utils-BYITg7T5.mjs +7 -0
  60. package/dist/utils-Cjmx1hhk.cjs +7 -0
  61. package/dist/worker-pool-CtqklOXq.cjs +6 -0
  62. package/dist/worker-pool-k0DY6k8T.mjs +6 -0
  63. package/package.json +5 -6
  64. package/src/shared/config-utils.ts +4 -0
  65. package/src/shared/types.ts +52 -6
  66. package/dist/cache-Ba-DZQNH.cjs +0 -6
  67. package/dist/cache-BuMZ58L5.mjs +0 -6
  68. package/dist/cards-BakZPTz9.d.ts +0 -30
  69. package/dist/cards-CQn9mXZS.d.cts +0 -30
  70. package/dist/docs-layout-KoWNZc8_.js +0 -6
  71. package/dist/docs-layout-x2yKt2cL.cjs +0 -6
  72. package/dist/doctor-Be7Ly1oM.mjs +0 -21
  73. package/dist/doctor-jMxWZyLJ.cjs +0 -21
  74. package/dist/icons-dev-B_RZIyxu.js +0 -6
  75. package/dist/icons-dev-BlV3wWFT.cjs +0 -6
  76. package/dist/image-BHhTvQzr.cjs +0 -6
  77. package/dist/image-CqKzYD8f.js +0 -6
  78. package/dist/mdx-DudBEac0.js +0 -7
  79. package/dist/mdx-r4cDQxWu.cjs +0 -7
  80. package/dist/node-DtEDyN1u.cjs +0 -111
  81. package/dist/node-_1jhMGYx.mjs +0 -111
  82. package/dist/package-DrwtlXfk.cjs +0 -6
  83. package/dist/parser-CdNbqN5y.cjs +0 -6
  84. package/dist/parser-nE792MLO.mjs +0 -6
  85. package/dist/rolldown-runtime-fkIsjY3S.mjs +0 -6
  86. package/dist/routes-2k3tbUmC.cjs +0 -6
  87. package/dist/routes-CpxZIsMM.mjs +0 -6
  88. package/dist/search-dialog-B584t9ZF.js +0 -6
  89. package/dist/search-dialog-BvBopRsZ.cjs +0 -6
  90. package/dist/search-dialog-ByvGScjt.js +0 -6
  91. package/dist/search-dialog-Cyko6TJm.cjs +0 -6
  92. package/dist/search-dialog-D6BNohIJ.js +0 -6
  93. package/dist/search-dialog-DuYTIefy.cjs +0 -6
  94. package/dist/utils-CG65J0Sc.mjs +0 -7
  95. package/dist/utils-CKunkU96.cjs +0 -7
  96. package/dist/worker-pool-CGn7DrLb.mjs +0 -6
  97. package/dist/worker-pool-Crbqgw5R.cjs +0 -6
  98. package/src/client/app/config-context.tsx +0 -51
  99. package/src/client/app/doc-page.tsx +0 -38
  100. package/src/client/app/docs-layout.tsx +0 -28
  101. package/src/client/app/head.tsx +0 -122
  102. package/src/client/app/helmet-compat.tsx +0 -36
  103. package/src/client/app/mdx-component.tsx +0 -8
  104. package/src/client/app/mdx-components-context.tsx +0 -72
  105. package/src/client/app/routes-context.tsx +0 -34
  106. package/src/client/app/scroll-handler.tsx +0 -74
  107. package/src/client/app/theme-context.tsx +0 -103
  108. package/src/client/app/ui-context.tsx +0 -42
  109. package/src/client/components/docs-layout-default.tsx +0 -85
  110. package/src/client/components/icons-dev.tsx +0 -282
  111. package/src/client/components/mdx/callout.tsx +0 -97
  112. package/src/client/components/mdx/card.tsx +0 -99
  113. package/src/client/components/mdx/cards.tsx +0 -27
  114. package/src/client/components/mdx/code-block.tsx +0 -184
  115. package/src/client/components/mdx/field.tsx +0 -33
  116. package/src/client/components/mdx/image.tsx +0 -44
  117. package/src/client/components/mdx/index.ts +0 -19
  118. package/src/client/components/mdx/table.tsx +0 -54
  119. package/src/client/components/mdx/typographics.tsx +0 -120
  120. package/src/client/components/mdx/use-code-block.ts +0 -34
  121. package/src/client/components/primitives/breadcrumbs.tsx +0 -54
  122. package/src/client/components/primitives/button-group.tsx +0 -54
  123. package/src/client/components/primitives/button.tsx +0 -6
  124. package/src/client/components/primitives/code-block.tsx +0 -120
  125. package/src/client/components/primitives/docs-layout.tsx +0 -125
  126. package/src/client/components/primitives/error-boundary.tsx +0 -107
  127. package/src/client/components/primitives/heading.tsx +0 -128
  128. package/src/client/components/primitives/helpers/observer.ts +0 -141
  129. package/src/client/components/primitives/image.tsx +0 -26
  130. package/src/client/components/primitives/link.tsx +0 -102
  131. package/src/client/components/primitives/menu.tsx +0 -137
  132. package/src/client/components/primitives/navbar.tsx +0 -466
  133. package/src/client/components/primitives/on-this-page.tsx +0 -430
  134. package/src/client/components/primitives/page-nav.tsx +0 -51
  135. package/src/client/components/primitives/popover.tsx +0 -28
  136. package/src/client/components/primitives/search-dialog.tsx +0 -193
  137. package/src/client/components/primitives/sidebar.tsx +0 -423
  138. package/src/client/components/primitives/skeleton.tsx +0 -26
  139. package/src/client/components/primitives/tabs.tsx +0 -70
  140. package/src/client/components/primitives/tooltip.tsx +0 -81
  141. package/src/client/components/primitives/types.ts +0 -11
  142. package/src/client/components/ui-base/banner.tsx +0 -66
  143. package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
  144. package/src/client/components/ui-base/copy-markdown.tsx +0 -107
  145. package/src/client/components/ui-base/error-boundary.tsx +0 -15
  146. package/src/client/components/ui-base/github-stars.tsx +0 -29
  147. package/src/client/components/ui-base/icons.tsx +0 -240
  148. package/src/client/components/ui-base/index.ts +0 -16
  149. package/src/client/components/ui-base/last-updated.tsx +0 -27
  150. package/src/client/components/ui-base/navbar.tsx +0 -266
  151. package/src/client/components/ui-base/not-found.tsx +0 -26
  152. package/src/client/components/ui-base/on-this-page.tsx +0 -57
  153. package/src/client/components/ui-base/page-nav.tsx +0 -50
  154. package/src/client/components/ui-base/search-dialog.tsx +0 -163
  155. package/src/client/components/ui-base/search-highlight.tsx +0 -10
  156. package/src/client/components/ui-base/sidebar.tsx +0 -92
  157. package/src/client/components/ui-base/tabs.tsx +0 -83
  158. package/src/client/components/ui-base/theme-toggle.tsx +0 -130
  159. package/src/client/components/ui-base/version-i18n.tsx +0 -80
  160. package/src/client/hooks/index.ts +0 -13
  161. package/src/client/hooks/use-analytics.ts +0 -272
  162. package/src/client/hooks/use-breadcrumbs.ts +0 -22
  163. package/src/client/hooks/use-i18n.ts +0 -182
  164. package/src/client/hooks/use-localized-to.ts +0 -113
  165. package/src/client/hooks/use-location.ts +0 -5
  166. package/src/client/hooks/use-navbar.ts +0 -130
  167. package/src/client/hooks/use-page-nav.ts +0 -46
  168. package/src/client/hooks/use-routes.ts +0 -108
  169. package/src/client/hooks/use-search-highlight.ts +0 -185
  170. package/src/client/hooks/use-search.ts +0 -118
  171. package/src/client/hooks/use-sidebar.ts +0 -205
  172. package/src/client/hooks/use-tabs.ts +0 -46
  173. package/src/client/hooks/use-version.ts +0 -111
  174. package/src/client/index.ts +0 -31
  175. package/src/client/mdx.ts +0 -2
  176. package/src/client/primitives.ts +0 -19
  177. package/src/client/ssg/boltdocs-shell.tsx +0 -148
  178. package/src/client/ssg/create-routes.tsx +0 -473
  179. package/src/client/ssg/index.ts +0 -4
  180. package/src/client/ssg/mdx-page.tsx +0 -38
  181. package/src/client/store/boltdocs-context.tsx +0 -137
  182. package/src/client/theme/neutral.css +0 -141
  183. package/src/client/theme/reset.css +0 -189
  184. package/src/client/types.ts +0 -116
  185. package/src/client/utils/cn.ts +0 -6
  186. package/src/client/utils/copy-clipboard.ts +0 -22
  187. package/src/client/utils/get-base-file-path.ts +0 -21
  188. package/src/client/utils/github.ts +0 -121
  189. package/src/client/utils/i18n.ts +0 -23
  190. package/src/client/utils/path.ts +0 -9
  191. package/src/client/utils/react-to-text.ts +0 -34
  192. package/src/client/virtual.d.ts +0 -24
  193. /package/dist/{meta-loader-CWg2gnbY.mjs → meta-loader-DzwDFtdT.mjs} +0 -0
@@ -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
- }