boltdocs 1.10.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/dist/cache-7G6D532T.mjs +1 -0
- package/dist/chunk-A4HQPEPU.mjs +1 -0
- package/dist/chunk-BA5NH5HU.mjs +1 -0
- package/dist/chunk-BQCD3DWG.mjs +1 -0
- package/dist/chunk-H63UMKYF.mjs +1 -0
- package/dist/chunk-IWHRQHS7.mjs +1 -0
- package/dist/chunk-JZXLCA2E.mjs +1 -0
- package/dist/chunk-MFU7Q6WF.mjs +1 -0
- package/dist/chunk-QYPNX5UN.mjs +1 -0
- package/dist/chunk-XEAPSFMB.mjs +1 -0
- package/dist/client/components/mdx/index.d.mts +209 -0
- package/dist/client/components/mdx/index.d.ts +209 -0
- package/dist/client/components/mdx/index.js +1 -0
- package/dist/client/components/mdx/index.mjs +1 -0
- package/dist/client/hooks/index.d.mts +133 -0
- package/dist/client/hooks/index.d.ts +133 -0
- package/dist/client/hooks/index.js +1 -0
- package/dist/client/hooks/index.mjs +1 -0
- package/dist/client/index.d.mts +138 -298
- package/dist/client/index.d.ts +138 -298
- package/dist/client/index.js +1 -3630
- package/dist/client/index.mjs +1 -697
- package/dist/client/ssr.d.mts +7 -3
- package/dist/client/ssr.d.ts +7 -3
- package/dist/client/ssr.js +1 -2928
- package/dist/client/ssr.mjs +1 -33
- package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
- package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
- package/dist/node/index.d.mts +2 -4
- package/dist/node/index.d.ts +2 -4
- package/dist/node/index.js +31 -1161
- package/dist/node/index.mjs +31 -736
- package/dist/search-dialog-EB3N4TYM.mjs +1 -0
- package/dist/types-BuZWFT7r.d.ts +159 -0
- package/dist/types-CvT-SGbK.d.mts +159 -0
- package/dist/use-routes-5bAtAAYX.d.mts +30 -0
- package/dist/use-routes-BefRXY3v.d.ts +30 -0
- package/package.json +34 -12
- package/src/client/app/config-context.tsx +18 -0
- package/src/client/app/docs-layout.tsx +14 -0
- package/src/client/app/index.tsx +137 -262
- package/src/client/app/mdx-component.tsx +52 -0
- package/src/client/app/mdx-components-context.tsx +23 -0
- package/src/client/app/mdx-page.tsx +20 -0
- package/src/client/app/preload.tsx +38 -30
- package/src/client/app/router.tsx +30 -0
- package/src/client/app/scroll-handler.tsx +40 -0
- package/src/client/app/theme-context.tsx +75 -0
- package/src/client/components/default-layout.tsx +80 -0
- package/src/client/components/docs-layout.tsx +105 -0
- package/src/client/components/icons-dev.tsx +74 -0
- package/src/client/components/mdx/admonition.tsx +107 -0
- package/src/client/components/mdx/badge.tsx +41 -0
- package/src/client/components/mdx/button.tsx +35 -0
- package/src/client/components/mdx/card.tsx +124 -0
- package/src/client/components/mdx/code-block.tsx +119 -0
- package/src/client/components/mdx/component-preview.tsx +47 -0
- package/src/client/components/mdx/component-props.tsx +83 -0
- package/src/client/components/mdx/field.tsx +66 -0
- package/src/client/components/mdx/file-tree.tsx +287 -0
- package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
- package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
- package/src/client/components/mdx/hooks/useTable.ts +74 -0
- package/src/client/components/mdx/hooks/useTabs.ts +68 -0
- package/src/client/components/mdx/image.tsx +23 -0
- package/src/client/components/mdx/index.ts +53 -0
- package/src/client/components/mdx/link.tsx +38 -0
- package/src/client/components/mdx/list.tsx +192 -0
- package/src/client/components/mdx/table.tsx +156 -0
- package/src/client/components/mdx/tabs.tsx +135 -0
- package/src/client/components/mdx/video.tsx +68 -0
- package/src/client/components/primitives/breadcrumbs.tsx +79 -0
- package/src/client/components/primitives/button-group.tsx +54 -0
- package/src/client/components/primitives/button.tsx +145 -0
- package/src/client/components/primitives/helpers/observer.ts +120 -0
- package/src/client/components/primitives/index.ts +17 -0
- package/src/client/components/primitives/link.tsx +122 -0
- package/src/client/components/primitives/menu.tsx +159 -0
- package/src/client/components/primitives/navbar.tsx +359 -0
- package/src/client/components/primitives/navigation-menu.tsx +116 -0
- package/src/client/components/primitives/on-this-page.tsx +461 -0
- package/src/client/components/primitives/page-nav.tsx +87 -0
- package/src/client/components/primitives/popover.tsx +47 -0
- package/src/client/components/primitives/search-dialog.tsx +183 -0
- package/src/client/components/primitives/sidebar.tsx +154 -0
- package/src/client/components/primitives/tabs.tsx +90 -0
- package/src/client/components/primitives/tooltip.tsx +83 -0
- package/src/client/components/primitives/types.ts +11 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
- package/src/client/components/ui-base/copy-markdown.tsx +112 -0
- package/src/client/components/ui-base/error-boundary.tsx +52 -0
- package/src/client/components/ui-base/github-stars.tsx +27 -0
- package/src/client/components/ui-base/head.tsx +69 -0
- package/src/client/components/ui-base/loading.tsx +87 -0
- package/src/client/components/ui-base/navbar.tsx +138 -0
- package/src/client/components/ui-base/not-found.tsx +24 -0
- package/src/client/components/ui-base/on-this-page.tsx +152 -0
- package/src/client/components/ui-base/page-nav.tsx +39 -0
- package/src/client/components/ui-base/powered-by.tsx +19 -0
- package/src/client/components/ui-base/progress-bar.tsx +67 -0
- package/src/client/components/ui-base/search-dialog.tsx +82 -0
- package/src/client/components/ui-base/sidebar.tsx +104 -0
- package/src/client/components/ui-base/tabs.tsx +65 -0
- package/src/client/components/ui-base/theme-toggle.tsx +32 -0
- package/src/client/hooks/index.ts +12 -0
- package/src/client/hooks/use-breadcrumbs.ts +22 -0
- package/src/client/hooks/use-i18n.ts +84 -0
- package/src/client/hooks/use-localized-to.ts +95 -0
- package/src/client/hooks/use-location.ts +5 -0
- package/src/client/hooks/use-navbar.ts +60 -0
- package/src/client/hooks/use-onthispage.ts +23 -0
- package/src/client/hooks/use-page-nav.ts +22 -0
- package/src/client/hooks/use-routes.ts +72 -0
- package/src/client/hooks/use-search.ts +71 -0
- package/src/client/hooks/use-sidebar.ts +49 -0
- package/src/client/hooks/use-tabs.ts +43 -0
- package/src/client/hooks/use-version.ts +78 -0
- package/src/client/index.ts +55 -17
- package/src/client/integrations/codesandbox.ts +179 -0
- package/src/client/ssr.tsx +27 -16
- package/src/client/theme/neutral.css +360 -0
- package/src/client/types.ts +131 -27
- package/src/client/utils/cn.ts +6 -0
- package/src/client/utils/copy-clipboard.ts +22 -0
- package/src/client/utils/get-base-file-path.ts +21 -0
- package/src/client/utils/github.ts +121 -0
- package/src/client/utils/use-on-change.ts +15 -0
- package/src/client/virtual.d.ts +24 -0
- package/src/node/cache.ts +156 -156
- package/src/node/config.ts +159 -103
- package/src/node/index.ts +13 -13
- package/src/node/mdx.ts +213 -61
- package/src/node/plugin/entry.ts +29 -18
- package/src/node/plugin/html.ts +11 -11
- package/src/node/plugin/index.ts +161 -84
- package/src/node/plugin/types.ts +2 -4
- package/src/node/routes/cache.ts +6 -6
- package/src/node/routes/index.ts +206 -113
- package/src/node/routes/parser.ts +102 -82
- package/src/node/routes/sorter.ts +15 -15
- package/src/node/routes/types.ts +24 -24
- package/src/node/ssg/index.ts +73 -47
- package/src/node/ssg/meta.ts +4 -4
- package/src/node/ssg/options.ts +5 -5
- package/src/node/ssg/sitemap.ts +14 -14
- package/src/node/utils.ts +54 -31
- package/tsconfig.json +25 -20
- package/tsup.config.ts +23 -14
- package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
- package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
- package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
- package/dist/Video-KNTY5BNO.mjs +0 -6
- package/dist/cache-KNL5B4EE.mjs +0 -12
- package/dist/chunk-7SFUJWTB.mjs +0 -211
- package/dist/chunk-FFBNU6IJ.mjs +0 -386
- package/dist/chunk-FMTOYQLO.mjs +0 -37
- package/dist/chunk-TKLQWU7H.mjs +0 -1920
- package/dist/chunk-Z7JHYNAS.mjs +0 -57
- package/dist/client/index.css +0 -2847
- package/dist/client/ssr.css +0 -2847
- package/dist/types-Dj-bfnC3.d.mts +0 -74
- package/dist/types-Dj-bfnC3.d.ts +0 -74
- package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
- package/src/client/theme/components/CodeBlock/index.ts +0 -1
- package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
- package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
- package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
- package/src/client/theme/components/Playground/Playground.tsx +0 -180
- package/src/client/theme/components/Playground/index.ts +0 -1
- package/src/client/theme/components/Playground/playground.css +0 -238
- package/src/client/theme/components/Video/Video.tsx +0 -84
- package/src/client/theme/components/Video/index.ts +0 -1
- package/src/client/theme/components/Video/video.css +0 -41
- package/src/client/theme/components/mdx/Admonition.tsx +0 -80
- package/src/client/theme/components/mdx/Badge.tsx +0 -31
- package/src/client/theme/components/mdx/Button.tsx +0 -50
- package/src/client/theme/components/mdx/Card.tsx +0 -80
- package/src/client/theme/components/mdx/Field.tsx +0 -60
- package/src/client/theme/components/mdx/FileTree.tsx +0 -229
- package/src/client/theme/components/mdx/List.tsx +0 -57
- package/src/client/theme/components/mdx/Table.tsx +0 -151
- package/src/client/theme/components/mdx/Tabs.tsx +0 -123
- package/src/client/theme/components/mdx/index.ts +0 -27
- package/src/client/theme/components/mdx/mdx-components.css +0 -764
- package/src/client/theme/icons/bun.tsx +0 -62
- package/src/client/theme/icons/deno.tsx +0 -20
- package/src/client/theme/icons/discord.tsx +0 -12
- package/src/client/theme/icons/github.tsx +0 -15
- package/src/client/theme/icons/npm.tsx +0 -13
- package/src/client/theme/icons/pnpm.tsx +0 -72
- package/src/client/theme/icons/twitter.tsx +0 -12
- package/src/client/theme/styles/markdown.css +0 -394
- package/src/client/theme/styles/variables.css +0 -175
- package/src/client/theme/styles.css +0 -39
- package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
- package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
- package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
- package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
- package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
- package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
- package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
- package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
- package/src/client/theme/ui/Footer/footer.css +0 -32
- package/src/client/theme/ui/Head/Head.tsx +0 -69
- package/src/client/theme/ui/Head/index.ts +0 -1
- package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
- package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
- package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
- package/src/client/theme/ui/Layout/Layout.tsx +0 -203
- package/src/client/theme/ui/Layout/base.css +0 -106
- package/src/client/theme/ui/Layout/index.ts +0 -2
- package/src/client/theme/ui/Layout/pagination.css +0 -72
- package/src/client/theme/ui/Layout/responsive.css +0 -47
- package/src/client/theme/ui/Link/Link.tsx +0 -392
- package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
- package/src/client/theme/ui/Link/index.ts +0 -2
- package/src/client/theme/ui/Link/link-preview.css +0 -48
- package/src/client/theme/ui/Loading/Loading.tsx +0 -10
- package/src/client/theme/ui/Loading/index.ts +0 -1
- package/src/client/theme/ui/Loading/loading.css +0 -30
- package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
- package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
- package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
- package/src/client/theme/ui/Navbar/index.ts +0 -2
- package/src/client/theme/ui/Navbar/navbar.css +0 -347
- package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
- package/src/client/theme/ui/NotFound/index.ts +0 -1
- package/src/client/theme/ui/NotFound/not-found.css +0 -64
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
- package/src/client/theme/ui/OnThisPage/index.ts +0 -1
- package/src/client/theme/ui/OnThisPage/toc.css +0 -152
- package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
- package/src/client/theme/ui/PoweredBy/index.ts +0 -1
- package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
- package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
- package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
- package/src/client/theme/ui/ProgressBar/index.ts +0 -1
- package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
- package/src/client/theme/ui/SearchDialog/index.ts +0 -1
- package/src/client/theme/ui/SearchDialog/search.css +0 -152
- package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
- package/src/client/theme/ui/Sidebar/index.ts +0 -1
- package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
- package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
- package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
- package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
- package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
- package/src/client/utils.ts +0 -49
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useEffect, useCallback } from 'react'
|
|
2
|
+
import { useSearch } from '@hooks/use-search'
|
|
3
|
+
import {
|
|
4
|
+
SearchDialogAutocomplete,
|
|
5
|
+
SearchDialogInput,
|
|
6
|
+
SearchDialogItemBio,
|
|
7
|
+
SearchDialogItemIcon,
|
|
8
|
+
SearchDialogItemRoot,
|
|
9
|
+
SearchDialogItemTitle,
|
|
10
|
+
SearchDialogList,
|
|
11
|
+
SearchDialogRoot,
|
|
12
|
+
} from '@components/primitives/search-dialog'
|
|
13
|
+
import Navbar from '@components/primitives/navbar'
|
|
14
|
+
import { useNavigate } from 'react-router-dom'
|
|
15
|
+
|
|
16
|
+
export function SearchDialog({ routes }: { routes: any[] }) {
|
|
17
|
+
const { isOpen, setIsOpen, query, setQuery, list } = useSearch(routes)
|
|
18
|
+
const navigate = useNavigate()
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
22
|
+
const isMac = /Mac/.test(navigator.userAgent)
|
|
23
|
+
const isMeta = isMac ? e.metaKey : e.ctrlKey
|
|
24
|
+
|
|
25
|
+
if (isMeta && (e.key === 'k' || e.key === 'j')) {
|
|
26
|
+
e.preventDefault()
|
|
27
|
+
setIsOpen((prev) => !prev)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
window.addEventListener('keydown', handleKeyDown)
|
|
31
|
+
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
32
|
+
}, [setIsOpen])
|
|
33
|
+
|
|
34
|
+
const handleSelect = useCallback(
|
|
35
|
+
(key: React.Key) => {
|
|
36
|
+
const path = String(key)
|
|
37
|
+
setIsOpen(false)
|
|
38
|
+
|
|
39
|
+
if (path.includes('#')) {
|
|
40
|
+
const [p, id] = path.split('#')
|
|
41
|
+
navigate(p)
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
const el = document.getElementById(id)
|
|
44
|
+
if (el) el.scrollIntoView({ behavior: 'smooth' })
|
|
45
|
+
}, 100)
|
|
46
|
+
} else {
|
|
47
|
+
navigate(path)
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
[navigate, setIsOpen],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
<Navbar.SearchTrigger onPress={() => setIsOpen(true)} />
|
|
56
|
+
|
|
57
|
+
<SearchDialogRoot isOpen={isOpen} onOpenChange={setIsOpen}>
|
|
58
|
+
<SearchDialogAutocomplete onSelectionChange={handleSelect}>
|
|
59
|
+
<SearchDialogInput
|
|
60
|
+
value={query}
|
|
61
|
+
onChange={(e: any) => setQuery(e.target.value)}
|
|
62
|
+
/>
|
|
63
|
+
<SearchDialogList items={list}>
|
|
64
|
+
{(item: any) => (
|
|
65
|
+
<SearchDialogItemRoot
|
|
66
|
+
key={item.id}
|
|
67
|
+
onPress={() => handleSelect(item.id)}
|
|
68
|
+
textValue={item.title}
|
|
69
|
+
>
|
|
70
|
+
<SearchDialogItemIcon isHeading={item.isHeading} />
|
|
71
|
+
<div className="flex flex-col justify-center gap-0.5">
|
|
72
|
+
<SearchDialogItemTitle>{item.title}</SearchDialogItemTitle>
|
|
73
|
+
<SearchDialogItemBio>{item.groupTitle}</SearchDialogItemBio>
|
|
74
|
+
</div>
|
|
75
|
+
</SearchDialogItemRoot>
|
|
76
|
+
)}
|
|
77
|
+
</SearchDialogList>
|
|
78
|
+
</SearchDialogAutocomplete>
|
|
79
|
+
</SearchDialogRoot>
|
|
80
|
+
</>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react'
|
|
2
|
+
import { useSidebar } from '@hooks/use-sidebar'
|
|
3
|
+
import SidebarPrimitive from '@components/primitives/sidebar'
|
|
4
|
+
import { PoweredBy } from './powered-by'
|
|
5
|
+
import * as LucideIcons from 'lucide-react'
|
|
6
|
+
import type { ComponentRoute } from '@client/types'
|
|
7
|
+
import type { BoltdocsConfig } from '@node/config'
|
|
8
|
+
|
|
9
|
+
function getIcon(iconName?: string): React.ElementType | undefined {
|
|
10
|
+
if (!iconName) return undefined
|
|
11
|
+
const IconComponent = (LucideIcons as Record<string, any>)[iconName]
|
|
12
|
+
return IconComponent || undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function CollapsibleSidebarGroup({
|
|
16
|
+
group,
|
|
17
|
+
activePath,
|
|
18
|
+
getIcon,
|
|
19
|
+
}: {
|
|
20
|
+
group: {
|
|
21
|
+
slug: string
|
|
22
|
+
title: string
|
|
23
|
+
routes: ComponentRoute[]
|
|
24
|
+
icon?: string
|
|
25
|
+
}
|
|
26
|
+
activePath: string
|
|
27
|
+
getIcon: (iconName?: string) => React.ElementType | undefined
|
|
28
|
+
}) {
|
|
29
|
+
const hasActiveRoute = useMemo(
|
|
30
|
+
() => group.routes.some((r) => r.path === activePath),
|
|
31
|
+
[group.routes, activePath],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const [isOpen, setIsOpen] = useState(true)
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (hasActiveRoute) {
|
|
38
|
+
setIsOpen(true)
|
|
39
|
+
}
|
|
40
|
+
}, [hasActiveRoute])
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<SidebarPrimitive.SidebarGroup
|
|
44
|
+
title={group.title}
|
|
45
|
+
isOpen={isOpen}
|
|
46
|
+
onToggle={() => setIsOpen(!isOpen)}
|
|
47
|
+
>
|
|
48
|
+
{group.routes.map((route: ComponentRoute) => (
|
|
49
|
+
<SidebarPrimitive.SidebarLink
|
|
50
|
+
key={route.path}
|
|
51
|
+
label={route.title}
|
|
52
|
+
href={route.path}
|
|
53
|
+
active={activePath === route.path}
|
|
54
|
+
icon={getIcon(route.icon)}
|
|
55
|
+
badge={route.badge}
|
|
56
|
+
/>
|
|
57
|
+
))}
|
|
58
|
+
</SidebarPrimitive.SidebarGroup>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function Sidebar({
|
|
63
|
+
routes,
|
|
64
|
+
config,
|
|
65
|
+
}: {
|
|
66
|
+
routes: ComponentRoute[]
|
|
67
|
+
config: BoltdocsConfig
|
|
68
|
+
}) {
|
|
69
|
+
const { groups, ungrouped, activePath } = useSidebar(routes)
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<SidebarPrimitive.SidebarRoot>
|
|
73
|
+
{ungrouped.length > 0 && (
|
|
74
|
+
<SidebarPrimitive.SidebarGroup className="mb-6">
|
|
75
|
+
{ungrouped.map((route) => (
|
|
76
|
+
<SidebarPrimitive.SidebarLink
|
|
77
|
+
key={route.path}
|
|
78
|
+
label={route.title}
|
|
79
|
+
href={route.path}
|
|
80
|
+
active={activePath === route.path}
|
|
81
|
+
icon={getIcon(route.icon)}
|
|
82
|
+
badge={route.badge}
|
|
83
|
+
/>
|
|
84
|
+
))}
|
|
85
|
+
</SidebarPrimitive.SidebarGroup>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
{groups.map((group) => (
|
|
89
|
+
<CollapsibleSidebarGroup
|
|
90
|
+
key={group.slug}
|
|
91
|
+
group={group}
|
|
92
|
+
activePath={activePath}
|
|
93
|
+
getIcon={getIcon}
|
|
94
|
+
/>
|
|
95
|
+
))}
|
|
96
|
+
|
|
97
|
+
{config.themeConfig?.poweredBy && (
|
|
98
|
+
<div className="mt-auto pt-8">
|
|
99
|
+
<PoweredBy />
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
</SidebarPrimitive.SidebarRoot>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useTabs as useTabsHook } from '@hooks/use-tabs'
|
|
2
|
+
import T from '@components/primitives/tabs'
|
|
3
|
+
import { Link } from '@components/primitives/link'
|
|
4
|
+
import type { BoltdocsTab, ComponentRoute } from '@client/types'
|
|
5
|
+
import * as Icons from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
export function Tabs({
|
|
8
|
+
tabs,
|
|
9
|
+
routes,
|
|
10
|
+
}: {
|
|
11
|
+
tabs: BoltdocsTab[]
|
|
12
|
+
routes: ComponentRoute[]
|
|
13
|
+
}) {
|
|
14
|
+
const { indicatorStyle, tabRefs, activeIndex } = useTabsHook(tabs, routes)
|
|
15
|
+
|
|
16
|
+
const renderTabIcon = (iconName?: string) => {
|
|
17
|
+
if (!iconName) return null
|
|
18
|
+
if (iconName.trim().startsWith('<svg')) {
|
|
19
|
+
return (
|
|
20
|
+
<span
|
|
21
|
+
className="h-4 w-4"
|
|
22
|
+
dangerouslySetInnerHTML={{ __html: iconName }}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
const LucideIcon = (Icons as Record<string, any>)[iconName]
|
|
27
|
+
if (LucideIcon) {
|
|
28
|
+
return <LucideIcon size={16} />
|
|
29
|
+
}
|
|
30
|
+
return <img src={iconName} alt="" className="h-4 w-4 object-contain" />
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="mx-auto max-w-(--breakpoint-3xl) px-4 md:px-6">
|
|
35
|
+
<T.TabsList className="border-none py-0">
|
|
36
|
+
{tabs.map((tab, index) => {
|
|
37
|
+
const isActive = index === activeIndex
|
|
38
|
+
const firstRoute = routes.find(
|
|
39
|
+
(r) => r.tab && r.tab.toLowerCase() === tab.id.toLowerCase(),
|
|
40
|
+
)
|
|
41
|
+
const linkTo = firstRoute ? firstRoute.path : '#'
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Link
|
|
45
|
+
key={tab.id}
|
|
46
|
+
href={linkTo}
|
|
47
|
+
ref={(el: HTMLAnchorElement | null) => {
|
|
48
|
+
tabRefs.current[index] = el
|
|
49
|
+
}}
|
|
50
|
+
className={`relative flex items-center gap-2 px-4 py-3 text-sm font-medium transition-colors outline-none ${
|
|
51
|
+
isActive
|
|
52
|
+
? 'text-primary-500'
|
|
53
|
+
: 'text-text-muted hover:text-text-main'
|
|
54
|
+
}`}
|
|
55
|
+
>
|
|
56
|
+
{renderTabIcon(tab.icon)}
|
|
57
|
+
<span>{tab.text}</span>
|
|
58
|
+
</Link>
|
|
59
|
+
)
|
|
60
|
+
})}
|
|
61
|
+
<T.TabsIndicator style={indicatorStyle} />
|
|
62
|
+
</T.TabsList>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { Sun, Moon } from 'lucide-react'
|
|
3
|
+
import { useTheme } from '@client/app/theme-context'
|
|
4
|
+
import { ToggleButton } from 'react-aria-components'
|
|
5
|
+
|
|
6
|
+
export function ThemeToggle() {
|
|
7
|
+
const { theme, toggleTheme } = useTheme()
|
|
8
|
+
const [mounted, setMounted] = useState(false)
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setMounted(true)
|
|
12
|
+
}, [])
|
|
13
|
+
|
|
14
|
+
if (!mounted) {
|
|
15
|
+
return <div className="h-9 w-9" />
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ToggleButton
|
|
20
|
+
onChange={toggleTheme}
|
|
21
|
+
className="flex h-9 w-9 items-center justify-center rounded-md text-text-muted transition-colors hover:bg-bg-surface hover:text-text-main"
|
|
22
|
+
aria-label="Toggle theme"
|
|
23
|
+
isSelected={theme === 'dark'}
|
|
24
|
+
>
|
|
25
|
+
{theme === 'dark' ? (
|
|
26
|
+
<Sun size={20} className="animate-in fade-in zoom-in duration-300" />
|
|
27
|
+
) : (
|
|
28
|
+
<Moon size={20} className="animate-in fade-in zoom-in duration-300" />
|
|
29
|
+
)}
|
|
30
|
+
</ToggleButton>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './use-navbar'
|
|
2
|
+
export * from './use-sidebar'
|
|
3
|
+
export * from './use-search'
|
|
4
|
+
export * from './use-onthispage'
|
|
5
|
+
export * from './use-tabs'
|
|
6
|
+
export * from './use-version'
|
|
7
|
+
export * from './use-i18n'
|
|
8
|
+
export * from './use-page-nav'
|
|
9
|
+
export * from './use-breadcrumbs'
|
|
10
|
+
export * from './use-routes'
|
|
11
|
+
export * from './use-localized-to'
|
|
12
|
+
export * from './use-location'
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useNavigate } from 'react-router-dom'
|
|
2
|
+
import { getBaseFilePath } from '@client/utils/get-base-file-path'
|
|
3
|
+
import { useRoutes } from './use-routes'
|
|
4
|
+
|
|
5
|
+
export interface LocaleOption {
|
|
6
|
+
key: string
|
|
7
|
+
label: string
|
|
8
|
+
value: string
|
|
9
|
+
isCurrent: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface UseI18nReturn {
|
|
13
|
+
currentLocale: string | undefined
|
|
14
|
+
currentLocaleLabel: string | undefined
|
|
15
|
+
availableLocales: LocaleOption[]
|
|
16
|
+
handleLocaleChange: (locale: string) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook to manage and switch between different locales (languages) of the documentation.
|
|
21
|
+
*/
|
|
22
|
+
export function useI18n(): UseI18nReturn {
|
|
23
|
+
const navigate = useNavigate()
|
|
24
|
+
const routeContext = useRoutes()
|
|
25
|
+
const { allRoutes, currentRoute, currentLocale, config } = routeContext
|
|
26
|
+
const i18n = config.i18n
|
|
27
|
+
|
|
28
|
+
const handleLocaleChange = (locale: string) => {
|
|
29
|
+
if (!i18n || locale === currentLocale) return
|
|
30
|
+
|
|
31
|
+
let targetPath = '/'
|
|
32
|
+
|
|
33
|
+
if (currentRoute) {
|
|
34
|
+
const baseFile = getBaseFilePath(
|
|
35
|
+
currentRoute.filePath,
|
|
36
|
+
currentRoute.version,
|
|
37
|
+
currentRoute.locale,
|
|
38
|
+
)
|
|
39
|
+
const targetRoute = allRoutes.find(
|
|
40
|
+
(r) =>
|
|
41
|
+
getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
|
|
42
|
+
(r.locale || i18n.defaultLocale) === locale &&
|
|
43
|
+
r.version === currentRoute.version,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if (targetRoute) {
|
|
47
|
+
targetPath = targetRoute.path
|
|
48
|
+
} else {
|
|
49
|
+
const defaultIndexRoute = allRoutes.find(
|
|
50
|
+
(r) =>
|
|
51
|
+
getBaseFilePath(r.filePath, r.version, r.locale) === 'index.md' &&
|
|
52
|
+
(r.locale || i18n.defaultLocale) === locale &&
|
|
53
|
+
r.version === currentRoute.version,
|
|
54
|
+
)
|
|
55
|
+
targetPath = defaultIndexRoute
|
|
56
|
+
? defaultIndexRoute.path
|
|
57
|
+
: locale === i18n.defaultLocale
|
|
58
|
+
? currentRoute.version
|
|
59
|
+
? `/${currentRoute.version}`
|
|
60
|
+
: '/'
|
|
61
|
+
: currentRoute.version
|
|
62
|
+
? `/${currentRoute.version}/${locale}`
|
|
63
|
+
: `/${locale}`
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
targetPath = locale === i18n.defaultLocale ? '/' : `/${locale}`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
navigate(targetPath)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const availableLocales = routeContext.availableLocales.map((l) => ({
|
|
73
|
+
...l,
|
|
74
|
+
label: l.label as string,
|
|
75
|
+
value: l.key,
|
|
76
|
+
}))
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
currentLocale,
|
|
80
|
+
currentLocaleLabel: routeContext.currentLocaleLabel,
|
|
81
|
+
availableLocales,
|
|
82
|
+
handleLocaleChange,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useLocation } from 'react-router-dom'
|
|
2
|
+
import { useConfig } from '@client/app/config-context'
|
|
3
|
+
import type { LinkProps as RouterLinkProps } from 'react-router-dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to automatically localize a path based on the current version and locale context.
|
|
7
|
+
* It ensures that navigation within the /docs path preserves the active version and language.
|
|
8
|
+
*/
|
|
9
|
+
export function useLocalizedTo(to: RouterLinkProps['to']) {
|
|
10
|
+
const location = useLocation()
|
|
11
|
+
const config = useConfig()
|
|
12
|
+
|
|
13
|
+
if (!config || typeof to !== 'string') return to
|
|
14
|
+
if (!config.i18n && !config.versions) return to
|
|
15
|
+
|
|
16
|
+
const basePath = '/docs'
|
|
17
|
+
if (!to.startsWith(basePath)) return to
|
|
18
|
+
|
|
19
|
+
// 1. Detect current context from location
|
|
20
|
+
const curSub = location.pathname.substring(basePath.length)
|
|
21
|
+
const curParts = curSub.split('/').filter(Boolean)
|
|
22
|
+
|
|
23
|
+
let currentVersion = config.versions?.defaultVersion
|
|
24
|
+
let currentLocale = config.i18n?.defaultLocale
|
|
25
|
+
|
|
26
|
+
let cIdx = 0
|
|
27
|
+
if (
|
|
28
|
+
config.versions &&
|
|
29
|
+
curParts.length > cIdx &&
|
|
30
|
+
config.versions.versions[curParts[cIdx]]
|
|
31
|
+
) {
|
|
32
|
+
currentVersion = curParts[cIdx]
|
|
33
|
+
cIdx++
|
|
34
|
+
}
|
|
35
|
+
if (
|
|
36
|
+
config.i18n &&
|
|
37
|
+
curParts.length > cIdx &&
|
|
38
|
+
config.i18n.locales[curParts[cIdx]]
|
|
39
|
+
) {
|
|
40
|
+
currentLocale = curParts[cIdx]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 2. Parse the target `to` path
|
|
44
|
+
const toSub = to.substring(basePath.length)
|
|
45
|
+
const toParts = toSub.split('/').filter(Boolean)
|
|
46
|
+
|
|
47
|
+
let tIdx = 0
|
|
48
|
+
let hasVersion = false
|
|
49
|
+
let hasLocale = false
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
config.versions &&
|
|
53
|
+
toParts.length > tIdx &&
|
|
54
|
+
config.versions.versions[toParts[tIdx]]
|
|
55
|
+
) {
|
|
56
|
+
hasVersion = true
|
|
57
|
+
tIdx++
|
|
58
|
+
}
|
|
59
|
+
if (
|
|
60
|
+
config.i18n &&
|
|
61
|
+
toParts.length > tIdx &&
|
|
62
|
+
config.i18n.locales[toParts[tIdx]]
|
|
63
|
+
) {
|
|
64
|
+
hasLocale = true
|
|
65
|
+
tIdx++
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Extract just the actual route parts
|
|
69
|
+
const routeParts = toParts.slice(tIdx)
|
|
70
|
+
|
|
71
|
+
// Reconstruct path
|
|
72
|
+
const finalParts = []
|
|
73
|
+
if (config.versions) {
|
|
74
|
+
if (hasVersion) {
|
|
75
|
+
finalParts.push(toParts[0])
|
|
76
|
+
} else if (currentVersion) {
|
|
77
|
+
finalParts.push(currentVersion)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (config.i18n) {
|
|
81
|
+
if (hasLocale) {
|
|
82
|
+
finalParts.push(toParts[hasVersion ? 1 : 0])
|
|
83
|
+
} else if (currentLocale) {
|
|
84
|
+
finalParts.push(currentLocale)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
finalParts.push(...routeParts)
|
|
89
|
+
|
|
90
|
+
let finalPath = `${basePath}/${finalParts.join('/')}`
|
|
91
|
+
if (finalPath.endsWith('/')) {
|
|
92
|
+
finalPath = finalPath.slice(0, -1)
|
|
93
|
+
}
|
|
94
|
+
return finalPath === basePath ? basePath : finalPath
|
|
95
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useLocation } from 'react-router-dom'
|
|
2
|
+
import { useConfig } from '@client/app/config-context'
|
|
3
|
+
import { useTheme } from '@client/app/theme-context'
|
|
4
|
+
import type { NavbarLink } from '@client/types'
|
|
5
|
+
|
|
6
|
+
export function useNavbar() {
|
|
7
|
+
const config = useConfig()
|
|
8
|
+
const { theme } = useTheme()
|
|
9
|
+
const location = useLocation()
|
|
10
|
+
|
|
11
|
+
const themeConfig = config.themeConfig || {}
|
|
12
|
+
const title = themeConfig.title || 'Boltdocs'
|
|
13
|
+
const rawLinks = themeConfig.navbar || []
|
|
14
|
+
const socialLinks = themeConfig.socialLinks || []
|
|
15
|
+
const githubRepo = themeConfig.githubRepo
|
|
16
|
+
|
|
17
|
+
// Transform links to the new NavbarLink structure
|
|
18
|
+
const links: NavbarLink[] = rawLinks.map((item: any) => {
|
|
19
|
+
const href = item.href || item.to || item.link || ''
|
|
20
|
+
return {
|
|
21
|
+
label: item.label || item.text || '',
|
|
22
|
+
href,
|
|
23
|
+
active: location.pathname === href,
|
|
24
|
+
to:
|
|
25
|
+
href.startsWith('http') || href.startsWith('//')
|
|
26
|
+
? 'external'
|
|
27
|
+
: undefined,
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const logo = themeConfig.logo
|
|
32
|
+
const logoSrc = !logo
|
|
33
|
+
? null
|
|
34
|
+
: typeof logo === 'string'
|
|
35
|
+
? logo
|
|
36
|
+
: theme === 'dark'
|
|
37
|
+
? (logo as any).dark
|
|
38
|
+
: (logo as any).light
|
|
39
|
+
|
|
40
|
+
const logoProps = {
|
|
41
|
+
alt:
|
|
42
|
+
(logo && typeof logo === 'object' ? (logo as any).alt : undefined) ||
|
|
43
|
+
title,
|
|
44
|
+
width: logo && typeof logo === 'object' ? (logo as any).width : undefined,
|
|
45
|
+
height: logo && typeof logo === 'object' ? (logo as any).height : undefined,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const github = githubRepo ? `https://github.com/${githubRepo}` : null
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
links,
|
|
52
|
+
title,
|
|
53
|
+
logo: logoSrc,
|
|
54
|
+
logoProps,
|
|
55
|
+
github,
|
|
56
|
+
social: socialLinks,
|
|
57
|
+
config,
|
|
58
|
+
theme,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
interface Heading {
|
|
4
|
+
id: string
|
|
5
|
+
text: string
|
|
6
|
+
level: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to manage and provide current page headings for the OnThisPage component.
|
|
11
|
+
*/
|
|
12
|
+
export function useOnThisPage(headings: Heading[] = []) {
|
|
13
|
+
const [activeId, setActiveId] = useState<string | null>(null)
|
|
14
|
+
|
|
15
|
+
// We keep the signature the same for backward compatibility,
|
|
16
|
+
// but the activeId tracking is now handled by AnchorProvider in the primitives.
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
headings,
|
|
20
|
+
activeId,
|
|
21
|
+
setActiveId,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useRoutes } from './use-routes'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook to manage the previous and next button functionality for documentation pages.
|
|
5
|
+
*/
|
|
6
|
+
export function usePageNav() {
|
|
7
|
+
const { routes } = useRoutes()
|
|
8
|
+
const currentPath = window.location.pathname
|
|
9
|
+
|
|
10
|
+
const currentIndex = routes.findIndex((r) => r.path === currentPath)
|
|
11
|
+
const currentRoute = routes[currentIndex]
|
|
12
|
+
|
|
13
|
+
const prevPage = currentIndex > 0 ? routes[currentIndex - 1] : null
|
|
14
|
+
const nextPage =
|
|
15
|
+
currentIndex < routes.length - 1 ? routes[currentIndex + 1] : null
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
prevPage,
|
|
19
|
+
nextPage,
|
|
20
|
+
currentRoute,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useLocation } from 'react-router-dom'
|
|
2
|
+
import { useConfig } from '@client/app/config-context'
|
|
3
|
+
import { usePreload } from '@client/app/preload'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to access the framework's routing state.
|
|
7
|
+
* Returns both the complete set of routes and a filtered list based on the current
|
|
8
|
+
* version and locale.
|
|
9
|
+
*/
|
|
10
|
+
export function useRoutes() {
|
|
11
|
+
const { routes: allRoutes } = usePreload()
|
|
12
|
+
const config = useConfig()
|
|
13
|
+
const location = useLocation()
|
|
14
|
+
|
|
15
|
+
// Find the current route exactly matching the pathname
|
|
16
|
+
const currentRoute = allRoutes.find((r) => r.path === location.pathname)
|
|
17
|
+
|
|
18
|
+
// Derive current locale and version from the route or defaults
|
|
19
|
+
const currentLocale = config.i18n
|
|
20
|
+
? currentRoute?.locale || config.i18n.defaultLocale
|
|
21
|
+
: undefined
|
|
22
|
+
|
|
23
|
+
const currentVersion = config.versions
|
|
24
|
+
? currentRoute?.version || config.versions.defaultVersion
|
|
25
|
+
: undefined
|
|
26
|
+
|
|
27
|
+
// Filter routes to those matching the current version and locale
|
|
28
|
+
const routes = allRoutes.filter((r) => {
|
|
29
|
+
const localeMatch = config.i18n
|
|
30
|
+
? (r.locale || config.i18n.defaultLocale) === currentLocale
|
|
31
|
+
: true
|
|
32
|
+
const versionMatch = config.versions
|
|
33
|
+
? (r.version || config.versions.defaultVersion) === currentVersion
|
|
34
|
+
: true
|
|
35
|
+
return localeMatch && versionMatch
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Labels and lists for UI convenience
|
|
39
|
+
const currentLocaleLabel =
|
|
40
|
+
config.i18n?.locales[currentLocale as string] || currentLocale
|
|
41
|
+
const currentVersionLabel =
|
|
42
|
+
config.versions?.versions[currentVersion as string] || currentVersion
|
|
43
|
+
|
|
44
|
+
const availableLocales = config.i18n
|
|
45
|
+
? Object.entries(config.i18n.locales).map(([key, label]) => ({
|
|
46
|
+
key,
|
|
47
|
+
label,
|
|
48
|
+
isCurrent: key === currentLocale,
|
|
49
|
+
}))
|
|
50
|
+
: []
|
|
51
|
+
|
|
52
|
+
const availableVersions = config.versions
|
|
53
|
+
? Object.entries(config.versions.versions).map(([key, label]) => ({
|
|
54
|
+
key,
|
|
55
|
+
label,
|
|
56
|
+
isCurrent: key === currentVersion,
|
|
57
|
+
}))
|
|
58
|
+
: []
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
routes,
|
|
62
|
+
allRoutes,
|
|
63
|
+
currentRoute,
|
|
64
|
+
currentLocale,
|
|
65
|
+
currentLocaleLabel,
|
|
66
|
+
availableLocales,
|
|
67
|
+
currentVersion,
|
|
68
|
+
currentVersionLabel,
|
|
69
|
+
availableVersions,
|
|
70
|
+
config,
|
|
71
|
+
}
|
|
72
|
+
}
|