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,182 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
|
-
import { useNavigate } from 'react-router-dom'
|
|
3
|
-
import { getBaseFilePath } from '../utils/get-base-file-path'
|
|
4
|
-
import { useRoutes } from './use-routes'
|
|
5
|
-
import { useConfig } from '../app/config-context'
|
|
6
|
-
import { useBoltdocsContext } from '../store/boltdocs-context'
|
|
7
|
-
import type { BoltdocsLocale } from '../../shared/types'
|
|
8
|
-
|
|
9
|
-
export interface LocaleOption {
|
|
10
|
-
key: BoltdocsLocale
|
|
11
|
-
label: string
|
|
12
|
-
value: string
|
|
13
|
-
isCurrent: boolean
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface UseI18nReturn {
|
|
17
|
-
currentLocale: BoltdocsLocale | undefined
|
|
18
|
-
currentLocaleLabel: string | undefined
|
|
19
|
-
availableLocales: LocaleOption[]
|
|
20
|
-
handleLocaleChange: (locale: BoltdocsLocale) => void
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Hook to manage and switch between different locales (languages) of the documentation.
|
|
25
|
-
*/
|
|
26
|
-
export function useI18n(): UseI18nReturn {
|
|
27
|
-
const navigate = useNavigate()
|
|
28
|
-
const config = useConfig()
|
|
29
|
-
const { allRoutes, currentRoute, currentLocale, currentVersion } = useRoutes()
|
|
30
|
-
const i18n = config.i18n
|
|
31
|
-
const { setLocale } = useBoltdocsContext()
|
|
32
|
-
|
|
33
|
-
const handleLocaleChange = (locale: string) => {
|
|
34
|
-
if (!i18n || locale === currentLocale) return
|
|
35
|
-
|
|
36
|
-
// Update store
|
|
37
|
-
setLocale(locale)
|
|
38
|
-
|
|
39
|
-
const base = config.base || '/'
|
|
40
|
-
const safeBase = base === '/' ? '' : base.replace(/\/$/, '')
|
|
41
|
-
const isDocRoute = !!currentRoute?.filePath
|
|
42
|
-
|
|
43
|
-
let targetPath = ''
|
|
44
|
-
|
|
45
|
-
if (currentRoute) {
|
|
46
|
-
// Case A: We are on a known route. Determine if it is doc or external.
|
|
47
|
-
if (isDocRoute) {
|
|
48
|
-
// Documentation Context logic
|
|
49
|
-
const baseFile = getBaseFilePath(
|
|
50
|
-
currentRoute.filePath,
|
|
51
|
-
currentRoute.version,
|
|
52
|
-
currentRoute.locale,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
const targetRoute = allRoutes.find(
|
|
56
|
-
(r) =>
|
|
57
|
-
getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
|
|
58
|
-
(r.locale || i18n.defaultLocale) === locale &&
|
|
59
|
-
r.version === currentRoute.version,
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
if (targetRoute) {
|
|
63
|
-
targetPath = targetRoute.path
|
|
64
|
-
} else {
|
|
65
|
-
// Recovery: Find target index, or hardcode reconstruct using version space
|
|
66
|
-
const defaultIndexRoute = allRoutes.find(
|
|
67
|
-
(r) =>
|
|
68
|
-
getBaseFilePath(r.filePath, r.version, r.locale) === 'index.md' &&
|
|
69
|
-
(r.locale || i18n.defaultLocale) === locale &&
|
|
70
|
-
r.version === currentRoute.version,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
if (defaultIndexRoute) {
|
|
74
|
-
targetPath = defaultIndexRoute.path
|
|
75
|
-
} else {
|
|
76
|
-
// Hardcoded absolute construction preserving existing version structure
|
|
77
|
-
const vPath = currentRoute.version ? `/${currentRoute.version}` : ''
|
|
78
|
-
const lPath = locale === i18n.defaultLocale ? '' : `/${locale}`
|
|
79
|
-
targetPath = `${safeBase}${vPath}${lPath}` || '/'
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
// External Context Logic: simply rewrite the current absolute path
|
|
84
|
-
// Extract pure relative component by stripping existing locale prefix
|
|
85
|
-
let rawExternal = currentRoute.path
|
|
86
|
-
|
|
87
|
-
// Strip existing locale if any
|
|
88
|
-
const parts = rawExternal.split('/').filter(Boolean)
|
|
89
|
-
if (
|
|
90
|
-
parts.length > 0 &&
|
|
91
|
-
(Array.isArray(i18n.locales)
|
|
92
|
-
? i18n.locales.includes(parts[0])
|
|
93
|
-
: !!i18n.locales[parts[0]])
|
|
94
|
-
) {
|
|
95
|
-
// Already prefixed external route like /es/about -> become /about
|
|
96
|
-
parts.shift()
|
|
97
|
-
rawExternal = '/' + parts.join('/')
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Re-apply new locale
|
|
101
|
-
if (locale === i18n.defaultLocale) {
|
|
102
|
-
targetPath = rawExternal === '' ? '/' : rawExternal
|
|
103
|
-
} else {
|
|
104
|
-
const cleanExt = rawExternal.startsWith('/')
|
|
105
|
-
? rawExternal
|
|
106
|
-
: `/${rawExternal}`
|
|
107
|
-
targetPath = `/${locale}${cleanExt === '/' ? '' : cleanExt}`
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
// Case B: Fallback for Unknown / 404 page
|
|
112
|
-
// Try to find first available page that matches the intended combo
|
|
113
|
-
const targetRoute = allRoutes.find(
|
|
114
|
-
(r) =>
|
|
115
|
-
(r.locale || i18n.defaultLocale) === locale &&
|
|
116
|
-
(r.version || config.versions?.defaultVersion) ===
|
|
117
|
-
(currentVersion || config.versions?.defaultVersion),
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
if (targetRoute) {
|
|
121
|
-
targetPath = targetRoute.path
|
|
122
|
-
} else {
|
|
123
|
-
const vPath =
|
|
124
|
-
currentVersion && currentVersion !== config.versions?.defaultVersion
|
|
125
|
-
? `/${currentVersion}`
|
|
126
|
-
: ''
|
|
127
|
-
targetPath =
|
|
128
|
-
locale === i18n.defaultLocale
|
|
129
|
-
? `${safeBase}${vPath}`
|
|
130
|
-
: `${safeBase}${vPath}/${locale}`
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Final safety check: cleanup double slashes and empty targets
|
|
135
|
-
if (!targetPath || targetPath === '') targetPath = '/'
|
|
136
|
-
targetPath = targetPath.replace(/\/+/g, '/')
|
|
137
|
-
|
|
138
|
-
navigate(targetPath)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const locales = i18n?.locales
|
|
142
|
-
const defaultLabel = locales
|
|
143
|
-
? Array.isArray(locales)
|
|
144
|
-
? currentLocale
|
|
145
|
-
: (locales as Record<string, string>)[currentLocale as string]
|
|
146
|
-
: undefined
|
|
147
|
-
|
|
148
|
-
const currentLocaleConfig = i18n?.localeConfigs?.[currentLocale as string]
|
|
149
|
-
const currentLocaleLabel =
|
|
150
|
-
currentLocaleConfig?.label || defaultLabel || currentLocale
|
|
151
|
-
|
|
152
|
-
const availableLocales = useMemo(() => {
|
|
153
|
-
return i18n
|
|
154
|
-
? Array.isArray(i18n.locales)
|
|
155
|
-
? i18n.locales.map((key) => {
|
|
156
|
-
const localeConfig = i18n?.localeConfigs?.[key]
|
|
157
|
-
return {
|
|
158
|
-
key: key as BoltdocsLocale,
|
|
159
|
-
label: localeConfig?.label || key,
|
|
160
|
-
value: key,
|
|
161
|
-
isCurrent: key === currentLocale,
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
: Object.entries(i18n.locales).map(([key, label]) => {
|
|
165
|
-
const localeConfig = i18n?.localeConfigs?.[key]
|
|
166
|
-
return {
|
|
167
|
-
key: key as BoltdocsLocale,
|
|
168
|
-
label: localeConfig?.label || label,
|
|
169
|
-
value: key,
|
|
170
|
-
isCurrent: key === currentLocale,
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
: []
|
|
174
|
-
}, [i18n, currentLocale])
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
currentLocale,
|
|
178
|
-
currentLocaleLabel,
|
|
179
|
-
availableLocales,
|
|
180
|
-
handleLocaleChange,
|
|
181
|
-
}
|
|
182
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { useConfig } from '../app/config-context'
|
|
2
|
-
import type { LinkProps as RouterLinkProps } from 'react-router-dom'
|
|
3
|
-
import { useRoutes } from './use-routes'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Hook to automatically localize a path based on the current version and locale context.
|
|
7
|
-
* It ensures that navigation preserves the active version and language across the entire site.
|
|
8
|
-
*/
|
|
9
|
-
export function useLocalizedTo(to: string): string
|
|
10
|
-
export function useLocalizedTo(to: RouterLinkProps['to']): RouterLinkProps['to']
|
|
11
|
-
export function useLocalizedTo(
|
|
12
|
-
to: RouterLinkProps['to'],
|
|
13
|
-
): RouterLinkProps['to'] {
|
|
14
|
-
const config = useConfig()
|
|
15
|
-
const {
|
|
16
|
-
currentLocale: activeLocale,
|
|
17
|
-
currentVersion: activeVersion,
|
|
18
|
-
allRoutes,
|
|
19
|
-
} = useRoutes()
|
|
20
|
-
|
|
21
|
-
if (!config || typeof to !== 'string') return to
|
|
22
|
-
|
|
23
|
-
// External, absolute, or anchor links don't need localization prefixing
|
|
24
|
-
if (
|
|
25
|
-
to.startsWith('http') ||
|
|
26
|
-
to.startsWith('//') ||
|
|
27
|
-
to.startsWith('#') ||
|
|
28
|
-
to.startsWith('site:')
|
|
29
|
-
) {
|
|
30
|
-
return to.replace('site:', '')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// 0. Identify if the incoming path is explicitly registered as a known route
|
|
34
|
-
const [pathOnly, hashAndQuery] = to.split(/([?#].*)/s)
|
|
35
|
-
const normalizedTo =
|
|
36
|
-
pathOnly.endsWith('/') && pathOnly.length > 1
|
|
37
|
-
? pathOnly.slice(0, -1)
|
|
38
|
-
: pathOnly
|
|
39
|
-
|
|
40
|
-
const isKnownRoute = allRoutes?.some((r) => {
|
|
41
|
-
const rp =
|
|
42
|
-
r.path.endsWith('/') && r.path.length > 1 ? r.path.slice(0, -1) : r.path
|
|
43
|
-
return rp === (normalizedTo || '/')
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const i18n = config.i18n
|
|
47
|
-
const versions = config.versions
|
|
48
|
-
const base = (config.base || '/docs').replace(/\/$/, '')
|
|
49
|
-
const baseSegment = base.startsWith('/') ? base.substring(1) : base
|
|
50
|
-
|
|
51
|
-
const rawParts = pathOnly.split('/').filter(Boolean)
|
|
52
|
-
|
|
53
|
-
// Classify: it's a Doc Path if it explicitly contains base segment,
|
|
54
|
-
// OR if it's an 'unknown' path (backward compatible fallback assumes unknown = doc).
|
|
55
|
-
const hasExplicitBase =
|
|
56
|
-
baseSegment && rawParts.length > 0 && rawParts[0] === baseSegment
|
|
57
|
-
const isDocsPath = hasExplicitBase || (!isKnownRoute && rawParts.length > 0)
|
|
58
|
-
|
|
59
|
-
// 3. Clean the 'to' path of ANY existing prefixes to avoid stacking
|
|
60
|
-
const parts = [...rawParts]
|
|
61
|
-
let pIdx = 0
|
|
62
|
-
|
|
63
|
-
// Strip base segment if present at start
|
|
64
|
-
if (baseSegment && parts[pIdx] === baseSegment) pIdx++
|
|
65
|
-
|
|
66
|
-
// Strip versions if present
|
|
67
|
-
if (versions && parts.length > pIdx) {
|
|
68
|
-
const vMatch = versions.versions.find((v) => v.path === parts[pIdx])
|
|
69
|
-
if (vMatch) pIdx++
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Strip locales if present
|
|
73
|
-
const isLocale =
|
|
74
|
-
i18n &&
|
|
75
|
-
parts.length > pIdx &&
|
|
76
|
-
(Array.isArray(i18n.locales)
|
|
77
|
-
? i18n.locales.includes(parts[pIdx])
|
|
78
|
-
: parts[pIdx] in i18n.locales)
|
|
79
|
-
if (isLocale) pIdx++
|
|
80
|
-
|
|
81
|
-
// The actual relative route remaining
|
|
82
|
-
const routeContent = parts.slice(pIdx)
|
|
83
|
-
|
|
84
|
-
// 4. Reconstruct dynamically based on context
|
|
85
|
-
const resultParts: string[] = []
|
|
86
|
-
|
|
87
|
-
if (isDocsPath) {
|
|
88
|
-
// Reconstruct DOCS path: /base/version/locale/content
|
|
89
|
-
if (baseSegment) resultParts.push(baseSegment)
|
|
90
|
-
if (versions && activeVersion) resultParts.push(activeVersion)
|
|
91
|
-
if (i18n && activeLocale) resultParts.push(activeLocale)
|
|
92
|
-
} else {
|
|
93
|
-
// Reconstruct EXTERNAL path: /locale/content
|
|
94
|
-
if (i18n && activeLocale) resultParts.push(activeLocale)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
resultParts.push(...routeContent)
|
|
98
|
-
|
|
99
|
-
let finalPath = `/${resultParts.join('/')}`
|
|
100
|
-
|
|
101
|
-
// Preserve trailing slash if present in input and output isn't just root
|
|
102
|
-
if (
|
|
103
|
-
pathOnly.endsWith('/') &&
|
|
104
|
-
pathOnly.length > 1 &&
|
|
105
|
-
!finalPath.endsWith('/')
|
|
106
|
-
) {
|
|
107
|
-
finalPath += '/'
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Restore original query/hash
|
|
111
|
-
const result = (finalPath || '/') + (hashAndQuery || '')
|
|
112
|
-
return result
|
|
113
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
|
-
import { useLocation } from 'react-router-dom'
|
|
3
|
-
import { useConfig } from '../app/config-context'
|
|
4
|
-
import { useTheme } from '../app/theme-context'
|
|
5
|
-
import type { NavbarLink } from '../types'
|
|
6
|
-
import { getTranslated } from '../utils/i18n'
|
|
7
|
-
import { useRoutes } from './use-routes'
|
|
8
|
-
|
|
9
|
-
export function useNavbar() {
|
|
10
|
-
const config = useConfig()
|
|
11
|
-
const { theme, resolvedTheme } = useTheme()
|
|
12
|
-
const location = useLocation()
|
|
13
|
-
const { currentLocale } = useRoutes()
|
|
14
|
-
|
|
15
|
-
const themeConfig = config.theme || {}
|
|
16
|
-
const title = getTranslated(themeConfig.title, currentLocale) || 'Boltdocs'
|
|
17
|
-
const rawLinks = themeConfig.navbar || []
|
|
18
|
-
const socialLinks = themeConfig.socialLinks || []
|
|
19
|
-
const githubRepo = themeConfig.githubRepo
|
|
20
|
-
|
|
21
|
-
// Transform links to the new NavbarLink structure
|
|
22
|
-
const links: NavbarLink[] = useMemo(() => {
|
|
23
|
-
return rawLinks.map((item: any) => {
|
|
24
|
-
const href = (item.href || item.to || item.link || '') as string
|
|
25
|
-
|
|
26
|
-
// Robust active state calculation
|
|
27
|
-
const getIsActive = (h: string) => {
|
|
28
|
-
const activePath = location.pathname
|
|
29
|
-
if (activePath === h) return true
|
|
30
|
-
if (!h || h === '/') return activePath === '/'
|
|
31
|
-
|
|
32
|
-
const cleanPathParts = (p: string) => {
|
|
33
|
-
const parts = p.split('/').filter(Boolean)
|
|
34
|
-
let i = 0
|
|
35
|
-
// Skip locale
|
|
36
|
-
if (
|
|
37
|
-
config.i18n?.locales &&
|
|
38
|
-
parts[i] &&
|
|
39
|
-
config.i18n.locales[parts[i]]
|
|
40
|
-
) {
|
|
41
|
-
i++
|
|
42
|
-
}
|
|
43
|
-
// Skip version
|
|
44
|
-
if (config.versions?.versions && parts[i]) {
|
|
45
|
-
if (config.versions.versions.some((v) => v.path === parts[i])) {
|
|
46
|
-
i++
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return parts.slice(i)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const hParts = cleanPathParts(h)
|
|
53
|
-
const pParts = cleanPathParts(activePath)
|
|
54
|
-
|
|
55
|
-
if (hParts.length === 0) return pParts.length === 0
|
|
56
|
-
|
|
57
|
-
// Must match at least as many parts as the candidate link
|
|
58
|
-
if (pParts.length < hParts.length) return false
|
|
59
|
-
|
|
60
|
-
// Every part of hParts must match pParts at the same position
|
|
61
|
-
return hParts.every((part, i) => pParts[i] === part)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Process nested items recursively
|
|
65
|
-
const processItems = (items?: any[]): NavbarLink[] => {
|
|
66
|
-
if (!items || items.length === 0) return undefined as any
|
|
67
|
-
return items.map((subItem: any) => {
|
|
68
|
-
const subHref = (subItem.href ||
|
|
69
|
-
subItem.to ||
|
|
70
|
-
subItem.link ||
|
|
71
|
-
'') as string
|
|
72
|
-
return {
|
|
73
|
-
label: getTranslated(subItem.label || subItem.text, currentLocale),
|
|
74
|
-
href: subHref,
|
|
75
|
-
active: getIsActive(subHref),
|
|
76
|
-
to:
|
|
77
|
-
subHref.startsWith('http') || subHref.startsWith('//')
|
|
78
|
-
? 'external'
|
|
79
|
-
: undefined,
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const linkItems = processItems(item.items)
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
label: getTranslated(item.label || item.text, currentLocale),
|
|
88
|
-
href,
|
|
89
|
-
active: getIsActive(href),
|
|
90
|
-
to:
|
|
91
|
-
href.startsWith('http') || href.startsWith('//')
|
|
92
|
-
? 'external'
|
|
93
|
-
: undefined,
|
|
94
|
-
items: linkItems,
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
}, [rawLinks, location.pathname, currentLocale, config])
|
|
98
|
-
|
|
99
|
-
const logo = themeConfig.logo
|
|
100
|
-
// Use resolvedTheme so 'system' correctly maps to 'dark' or 'light'
|
|
101
|
-
// based on the OS preference, instead of always falling back to 'light'.
|
|
102
|
-
const logoSrc = !logo
|
|
103
|
-
? null
|
|
104
|
-
: typeof logo === 'string'
|
|
105
|
-
? logo
|
|
106
|
-
: resolvedTheme === 'dark'
|
|
107
|
-
? (logo as any).dark
|
|
108
|
-
: (logo as any).light
|
|
109
|
-
|
|
110
|
-
const logoProps = {
|
|
111
|
-
alt:
|
|
112
|
-
(logo && typeof logo === 'object' ? (logo as any).alt : undefined) ||
|
|
113
|
-
title,
|
|
114
|
-
width: logo && typeof logo === 'object' ? (logo as any).width : undefined,
|
|
115
|
-
height: logo && typeof logo === 'object' ? (logo as any).height : undefined,
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const github = githubRepo ? `https://github.com/${githubRepo}` : null
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
links,
|
|
122
|
-
title,
|
|
123
|
-
logo: logoSrc,
|
|
124
|
-
logoProps,
|
|
125
|
-
github,
|
|
126
|
-
social: socialLinks,
|
|
127
|
-
config,
|
|
128
|
-
theme,
|
|
129
|
-
}
|
|
130
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
|
-
import { useLocation } from 'react-router-dom'
|
|
3
|
-
import { useRoutes } from './use-routes'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Hook to manage the previous and next button functionality for documentation pages.
|
|
7
|
-
* Intelligent: respects current locale, version, and tab to keep navigation logical.
|
|
8
|
-
*/
|
|
9
|
-
export function usePageNav() {
|
|
10
|
-
const { routes, currentRoute } = useRoutes()
|
|
11
|
-
const location = useLocation()
|
|
12
|
-
|
|
13
|
-
return useMemo(() => {
|
|
14
|
-
if (!currentRoute) {
|
|
15
|
-
return {
|
|
16
|
-
prevPage: null,
|
|
17
|
-
nextPage: null,
|
|
18
|
-
currentRoute: null,
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const activeTabId = currentRoute.tab?.toLowerCase()
|
|
23
|
-
|
|
24
|
-
// Subset of routes that match the current context (locale and version are already filtered by useRoutes)
|
|
25
|
-
// We further filter by tab to keep the user in the same logical section
|
|
26
|
-
const contextRoutes = activeTabId
|
|
27
|
-
? routes.filter((r) => r.tab?.toLowerCase() === activeTabId)
|
|
28
|
-
: routes.filter((r) => !r.tab)
|
|
29
|
-
|
|
30
|
-
const currentIndex = contextRoutes.findIndex(
|
|
31
|
-
(r) => r.path === location.pathname,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
const prevPage = currentIndex > 0 ? contextRoutes[currentIndex - 1] : null
|
|
35
|
-
const nextPage =
|
|
36
|
-
currentIndex !== -1 && currentIndex < contextRoutes.length - 1
|
|
37
|
-
? contextRoutes[currentIndex + 1]
|
|
38
|
-
: null
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
prevPage,
|
|
42
|
-
nextPage,
|
|
43
|
-
currentRoute,
|
|
44
|
-
}
|
|
45
|
-
}, [routes, currentRoute, location.pathname])
|
|
46
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
|
-
import { useLocation } from 'react-router-dom'
|
|
3
|
-
import { useConfig } from '../app/config-context'
|
|
4
|
-
import { useRoutesContext } from '../app/routes-context'
|
|
5
|
-
import { useBoltdocsContext } from '../store/boltdocs-context'
|
|
6
|
-
import { normalizePath } from '../utils/path'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Hook to access the framework's routing state.
|
|
10
|
-
* Returns both the complete set of routes and a filtered list based on the current
|
|
11
|
-
* version and locale.
|
|
12
|
-
*/
|
|
13
|
-
export function useRoutes() {
|
|
14
|
-
const { routes: allRoutes } = useRoutesContext()
|
|
15
|
-
const config = useConfig()
|
|
16
|
-
const location = useLocation()
|
|
17
|
-
|
|
18
|
-
// Use Zustand store for active state
|
|
19
|
-
const {
|
|
20
|
-
hasHydrated,
|
|
21
|
-
currentLocale: currentLocaleStore,
|
|
22
|
-
currentVersion: currentVersionStore,
|
|
23
|
-
} = useBoltdocsContext()
|
|
24
|
-
|
|
25
|
-
const currentPath = normalizePath(location.pathname)
|
|
26
|
-
|
|
27
|
-
// Find the current active route matching the pathname
|
|
28
|
-
const currentRoute = allRoutes?.find?.(
|
|
29
|
-
(r) => normalizePath(r.path) === currentPath,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
// 2. STRICT SOURCE OF TRUTH:
|
|
33
|
-
// Derive the active states exclusively from the hydrated Context Store.
|
|
34
|
-
// This ensures that user preference (LocalStorage) takes precedence over ambiguous URL fallbacks.
|
|
35
|
-
const currentLocale = config.i18n
|
|
36
|
-
? currentLocaleStore || config.i18n.defaultLocale
|
|
37
|
-
: undefined
|
|
38
|
-
|
|
39
|
-
const currentVersion = config.versions
|
|
40
|
-
? currentVersionStore || config.versions.defaultVersion
|
|
41
|
-
: undefined
|
|
42
|
-
|
|
43
|
-
// Filter routes to those matching the current version and locale
|
|
44
|
-
const routes = useMemo(() => {
|
|
45
|
-
if (!allRoutes) return []
|
|
46
|
-
|
|
47
|
-
// Pre-calculate alternate presence using a Map of maps or a composite key
|
|
48
|
-
// Key: filePath | (locale || defaultLocale) | (version || defaultVersion)
|
|
49
|
-
const alternateCounts = new Map<string, number>()
|
|
50
|
-
const defaultLocale = config.i18n?.defaultLocale || ''
|
|
51
|
-
const defaultVersion = config.versions?.defaultVersion || ''
|
|
52
|
-
|
|
53
|
-
for (const r of allRoutes) {
|
|
54
|
-
const locale = r.locale || defaultLocale
|
|
55
|
-
const version = r.version || defaultVersion
|
|
56
|
-
const key = `${r.filePath}::${locale}::${version}`
|
|
57
|
-
alternateCounts.set(key, (alternateCounts.get(key) || 0) + 1)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return allRoutes.filter((r) => {
|
|
61
|
-
const localeMatch = config.i18n
|
|
62
|
-
? (r.locale || config.i18n.defaultLocale) === currentLocale
|
|
63
|
-
: true
|
|
64
|
-
const versionMatch = config.versions
|
|
65
|
-
? (r.version || config.versions.defaultVersion) === currentVersion
|
|
66
|
-
: true
|
|
67
|
-
|
|
68
|
-
if (!(localeMatch && versionMatch)) return false
|
|
69
|
-
|
|
70
|
-
// Resolve duplicate paths (aliases) like /docs vs /docs/en
|
|
71
|
-
// 3. Resolve duplicate route aliases (e.g., /docs/page vs /docs/latest/page or /docs/es/page)
|
|
72
|
-
// If duplicates exist, we only show the style (prefixed or unprefixed) that matches the user's current page style.
|
|
73
|
-
const isCurrentLocalePrefixed = !!currentRoute?.locale
|
|
74
|
-
const isCurrentVersionPrefixed = !!currentRoute?.version
|
|
75
|
-
|
|
76
|
-
const isRouteLocalePrefixed = !!r.locale
|
|
77
|
-
const isRouteVersionPrefixed = !!r.version
|
|
78
|
-
|
|
79
|
-
const locale = r.locale || defaultLocale
|
|
80
|
-
const version = r.version || defaultVersion
|
|
81
|
-
const key = `${r.filePath}::${locale}::${version}`
|
|
82
|
-
const hasAlternate = (alternateCounts.get(key) || 0) > 1
|
|
83
|
-
|
|
84
|
-
if (hasAlternate) {
|
|
85
|
-
// Style mismatch checks
|
|
86
|
-
const localeMismatch =
|
|
87
|
-
config.i18n && isCurrentLocalePrefixed !== isRouteLocalePrefixed
|
|
88
|
-
const versionMismatch =
|
|
89
|
-
config.versions && isCurrentVersionPrefixed !== isRouteVersionPrefixed
|
|
90
|
-
|
|
91
|
-
if (localeMismatch || versionMismatch) {
|
|
92
|
-
return false
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return true
|
|
97
|
-
})
|
|
98
|
-
}, [allRoutes, config, currentLocale, currentVersion, currentRoute])
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
routes,
|
|
102
|
-
allRoutes,
|
|
103
|
-
currentRoute,
|
|
104
|
-
currentLocale: currentLocale as import('../../shared/types').BoltdocsLocale,
|
|
105
|
-
currentVersion:
|
|
106
|
-
currentVersion as import('../../shared/types').BoltdocsVersion,
|
|
107
|
-
}
|
|
108
|
-
}
|