boltdocs 2.7.9 → 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.
Files changed (174) hide show
  1. package/dist/{cache-DorPMFgW.cjs → cache-Ba-DZQNH.cjs} +1 -1
  2. package/dist/{cache-CQKlT4fI.mjs → cache-BuMZ58L5.mjs} +1 -1
  3. package/dist/chunk-CU-zTemE.cjs +6 -0
  4. package/dist/client/index.cjs +1929 -1
  5. package/dist/client/index.js +1880 -1
  6. package/dist/client/mdx.cjs +7 -1
  7. package/dist/client/mdx.js +7 -1
  8. package/dist/client/primitives.cjs +60 -1
  9. package/dist/client/primitives.js +20 -1
  10. package/dist/docs-layout-BXHV0xw_.cjs +1431 -0
  11. package/dist/docs-layout-DwFndmj5.js +1231 -0
  12. package/dist/doctor-Be7Ly1oM.mjs +21 -0
  13. package/dist/{doctor-D4_Y7M4p.cjs → doctor-CrytFkqW.cjs} +1 -1
  14. package/dist/doctor-jMxWZyLJ.cjs +21 -0
  15. package/dist/generator-CHqxiQhF.cjs +21 -0
  16. package/dist/generator-ClVanhvi.mjs +21 -0
  17. package/dist/icons-dev-3cZMyt8r.cjs +1204 -0
  18. package/dist/icons-dev-Df8OQ481.js +839 -0
  19. package/dist/image-DtrI2cw3.cjs +268 -0
  20. package/dist/image-jxPb-2iV.js +214 -0
  21. package/dist/mdx-BdWkJTeB.cjs +523 -0
  22. package/dist/mdx-UTTLFWJq.js +494 -0
  23. package/dist/meta-loader-CWg2gnbY.mjs +6 -0
  24. package/dist/meta-loader-Cv9O0Pzl.cjs +6 -0
  25. package/dist/node/cli-entry.cjs +1 -1
  26. package/dist/node/cli-entry.mjs +1 -1
  27. package/dist/node/index.cjs +1 -1
  28. package/dist/node/index.mjs +1 -1
  29. package/dist/node/routes/worker.cjs +1 -1
  30. package/dist/node/routes/worker.mjs +1 -1
  31. package/dist/node-BSM4qcDK.cjs +111 -0
  32. package/dist/node-BspZN3R2.mjs +111 -0
  33. package/dist/{package-VfQM94VL.cjs → package-DIIrjuWI.cjs} +1 -1
  34. package/dist/{package-B4MD00N3.mjs → package-K0zsjGIz.mjs} +1 -1
  35. package/dist/{parser-Bh11BsdA.cjs → parser-Aq8LoH-0.cjs} +1 -1
  36. package/dist/{parser-DYRzXWmA.cjs → parser-CdNbqN5y.cjs} +1 -1
  37. package/dist/parser-nE792MLO.mjs +6 -0
  38. package/dist/rolldown-runtime-fkIsjY3S.mjs +6 -0
  39. package/dist/{routes-Co1mRM58.cjs → routes-2k3tbUmC.cjs} +1 -1
  40. package/dist/routes-CpxZIsMM.mjs +6 -0
  41. package/dist/{routes-CHf76Ye4.cjs → routes-DP1vmWRj.cjs} +1 -1
  42. package/dist/search-dialog-BHuIiUC6.js +8 -0
  43. package/dist/search-dialog-BNF10tDl.js +375 -0
  44. package/dist/search-dialog-BwkDuI9R.cjs +220 -0
  45. package/dist/search-dialog-C7xuvyNk.cjs +386 -0
  46. package/dist/search-dialog-CIQg6k8c.cjs +8 -0
  47. package/dist/search-dialog-D-DDN7zJ.js +208 -0
  48. package/dist/utils-CG65J0Sc.mjs +7 -0
  49. package/dist/utils-CKunkU96.cjs +7 -0
  50. package/dist/{worker-pool-BwU8ckrg.cjs → worker-pool-Crbqgw5R.cjs} +1 -1
  51. package/package.json +5 -5
  52. package/dist/chunk-Ds5LZdWN.cjs +0 -6
  53. package/dist/docs-layout-KoWNZc8_.js +0 -6
  54. package/dist/docs-layout-x2yKt2cL.cjs +0 -6
  55. package/dist/doctor-BD1BSB03.mjs +0 -23
  56. package/dist/doctor-BHc9ua6r.cjs +0 -23
  57. package/dist/generator-DGW6pkCC.cjs +0 -22
  58. package/dist/generator-Dv3wEmhZ.mjs +0 -22
  59. package/dist/icons-dev-B_RZIyxu.js +0 -6
  60. package/dist/icons-dev-BlV3wWFT.cjs +0 -6
  61. package/dist/image-BHhTvQzr.cjs +0 -6
  62. package/dist/image-CqKzYD8f.js +0 -6
  63. package/dist/mdx-DudBEac0.js +0 -7
  64. package/dist/mdx-r4cDQxWu.cjs +0 -7
  65. package/dist/meta-loader-0gJ4PtBC.cjs +0 -6
  66. package/dist/meta-loader-9IpAHWDS.mjs +0 -6
  67. package/dist/node-DBaH7kat.mjs +0 -111
  68. package/dist/node-t5C3Q85p.cjs +0 -111
  69. package/dist/parser-9cVdK7w9.mjs +0 -6
  70. package/dist/routes-DwrMa5-z.mjs +0 -6
  71. package/dist/search-dialog-B584t9ZF.js +0 -6
  72. package/dist/search-dialog-BvBopRsZ.cjs +0 -6
  73. package/dist/search-dialog-ByvGScjt.js +0 -6
  74. package/dist/search-dialog-Cyko6TJm.cjs +0 -6
  75. package/dist/search-dialog-D6BNohIJ.js +0 -6
  76. package/dist/search-dialog-DuYTIefy.cjs +0 -6
  77. package/dist/utils-BxNAXhZZ.mjs +0 -7
  78. package/dist/utils-Clzu7jvb.cjs +0 -7
  79. package/src/client/app/config-context.tsx +0 -51
  80. package/src/client/app/doc-page.tsx +0 -38
  81. package/src/client/app/docs-layout.tsx +0 -28
  82. package/src/client/app/head.tsx +0 -122
  83. package/src/client/app/helmet-compat.tsx +0 -36
  84. package/src/client/app/mdx-component.tsx +0 -8
  85. package/src/client/app/mdx-components-context.tsx +0 -72
  86. package/src/client/app/routes-context.tsx +0 -34
  87. package/src/client/app/scroll-handler.tsx +0 -74
  88. package/src/client/app/theme-context.tsx +0 -103
  89. package/src/client/app/ui-context.tsx +0 -42
  90. package/src/client/components/docs-layout-default.tsx +0 -85
  91. package/src/client/components/icons-dev.tsx +0 -282
  92. package/src/client/components/mdx/callout.tsx +0 -97
  93. package/src/client/components/mdx/card.tsx +0 -99
  94. package/src/client/components/mdx/cards.tsx +0 -27
  95. package/src/client/components/mdx/code-block.tsx +0 -184
  96. package/src/client/components/mdx/field.tsx +0 -33
  97. package/src/client/components/mdx/image.tsx +0 -44
  98. package/src/client/components/mdx/index.ts +0 -19
  99. package/src/client/components/mdx/table.tsx +0 -54
  100. package/src/client/components/mdx/typographics.tsx +0 -120
  101. package/src/client/components/mdx/use-code-block.ts +0 -34
  102. package/src/client/components/primitives/breadcrumbs.tsx +0 -54
  103. package/src/client/components/primitives/button-group.tsx +0 -54
  104. package/src/client/components/primitives/button.tsx +0 -6
  105. package/src/client/components/primitives/code-block.tsx +0 -120
  106. package/src/client/components/primitives/docs-layout.tsx +0 -125
  107. package/src/client/components/primitives/error-boundary.tsx +0 -107
  108. package/src/client/components/primitives/heading.tsx +0 -128
  109. package/src/client/components/primitives/helpers/observer.ts +0 -141
  110. package/src/client/components/primitives/image.tsx +0 -26
  111. package/src/client/components/primitives/link.tsx +0 -102
  112. package/src/client/components/primitives/menu.tsx +0 -137
  113. package/src/client/components/primitives/navbar.tsx +0 -466
  114. package/src/client/components/primitives/on-this-page.tsx +0 -430
  115. package/src/client/components/primitives/page-nav.tsx +0 -51
  116. package/src/client/components/primitives/popover.tsx +0 -28
  117. package/src/client/components/primitives/search-dialog.tsx +0 -193
  118. package/src/client/components/primitives/sidebar.tsx +0 -423
  119. package/src/client/components/primitives/skeleton.tsx +0 -26
  120. package/src/client/components/primitives/tabs.tsx +0 -70
  121. package/src/client/components/primitives/tooltip.tsx +0 -81
  122. package/src/client/components/primitives/types.ts +0 -11
  123. package/src/client/components/ui-base/banner.tsx +0 -66
  124. package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
  125. package/src/client/components/ui-base/copy-markdown.tsx +0 -107
  126. package/src/client/components/ui-base/error-boundary.tsx +0 -15
  127. package/src/client/components/ui-base/github-stars.tsx +0 -29
  128. package/src/client/components/ui-base/icons.tsx +0 -240
  129. package/src/client/components/ui-base/index.ts +0 -16
  130. package/src/client/components/ui-base/last-updated.tsx +0 -27
  131. package/src/client/components/ui-base/navbar.tsx +0 -266
  132. package/src/client/components/ui-base/not-found.tsx +0 -26
  133. package/src/client/components/ui-base/on-this-page.tsx +0 -57
  134. package/src/client/components/ui-base/page-nav.tsx +0 -50
  135. package/src/client/components/ui-base/search-dialog.tsx +0 -163
  136. package/src/client/components/ui-base/search-highlight.tsx +0 -10
  137. package/src/client/components/ui-base/sidebar.tsx +0 -92
  138. package/src/client/components/ui-base/tabs.tsx +0 -83
  139. package/src/client/components/ui-base/theme-toggle.tsx +0 -130
  140. package/src/client/components/ui-base/version-i18n.tsx +0 -80
  141. package/src/client/hooks/index.ts +0 -13
  142. package/src/client/hooks/use-analytics.ts +0 -272
  143. package/src/client/hooks/use-breadcrumbs.ts +0 -22
  144. package/src/client/hooks/use-i18n.ts +0 -182
  145. package/src/client/hooks/use-localized-to.ts +0 -113
  146. package/src/client/hooks/use-location.ts +0 -5
  147. package/src/client/hooks/use-navbar.ts +0 -130
  148. package/src/client/hooks/use-page-nav.ts +0 -46
  149. package/src/client/hooks/use-routes.ts +0 -108
  150. package/src/client/hooks/use-search-highlight.ts +0 -185
  151. package/src/client/hooks/use-search.ts +0 -118
  152. package/src/client/hooks/use-sidebar.ts +0 -205
  153. package/src/client/hooks/use-tabs.ts +0 -46
  154. package/src/client/hooks/use-version.ts +0 -111
  155. package/src/client/index.ts +0 -31
  156. package/src/client/mdx.ts +0 -2
  157. package/src/client/primitives.ts +0 -19
  158. package/src/client/ssg/boltdocs-shell.tsx +0 -148
  159. package/src/client/ssg/create-routes.tsx +0 -473
  160. package/src/client/ssg/index.ts +0 -4
  161. package/src/client/ssg/mdx-page.tsx +0 -38
  162. package/src/client/store/boltdocs-context.tsx +0 -137
  163. package/src/client/theme/neutral.css +0 -141
  164. package/src/client/theme/reset.css +0 -189
  165. package/src/client/types.ts +0 -116
  166. package/src/client/utils/cn.ts +0 -6
  167. package/src/client/utils/copy-clipboard.ts +0 -22
  168. package/src/client/utils/get-base-file-path.ts +0 -21
  169. package/src/client/utils/github.ts +0 -121
  170. package/src/client/utils/i18n.ts +0 -23
  171. package/src/client/utils/path.ts +0 -9
  172. package/src/client/utils/react-to-text.ts +0 -34
  173. package/src/client/virtual.d.ts +0 -24
  174. /package/dist/{worker-pool-Bd8Y9KDv.mjs → worker-pool-CGn7DrLb.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
- }