boltdocs 2.6.1 → 2.7.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/bin/boltdocs.js +0 -1
- package/dist/cache-CQKlT4fI.mjs +6 -0
- package/dist/cache-DorPMFgW.cjs +6 -0
- package/dist/cards-BLoSiRuL.d.ts +30 -0
- package/dist/cards-CQn9mXZS.d.cts +30 -0
- package/dist/chunk-Ds5LZdWN.cjs +6 -0
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.d.cts +173 -1328
- package/dist/client/index.d.ts +172 -1327
- package/dist/client/index.js +1 -1
- package/dist/{package-c99Cs7mD.cjs → client/mdx.cjs} +1 -1
- package/dist/client/mdx.d.cts +128 -0
- package/dist/client/mdx.d.ts +129 -0
- package/dist/client/mdx.js +6 -0
- package/dist/client/primitives.cjs +6 -0
- package/dist/client/primitives.d.cts +818 -0
- package/dist/client/primitives.d.ts +818 -0
- package/dist/client/primitives.js +6 -0
- package/dist/client/theme/neutral.css +74 -361
- package/dist/client/theme/reset.css +189 -0
- package/dist/docs-layout-BlDhcQRv.cjs +6 -0
- package/dist/docs-layout-BvAOWEJw.js +6 -0
- package/dist/doctor-BQiQhCTl.cjs +6 -0
- package/dist/doctor-COpf35L2.cjs +20 -0
- package/dist/doctor-Dh1XP7Pz.mjs +20 -0
- package/dist/generator-DGW6pkCC.cjs +22 -0
- package/dist/generator-Dv3wEmhZ.mjs +22 -0
- package/dist/icons-dev-CrQLjoQp.js +6 -0
- package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
- package/dist/image-BkIfa9oo.js +6 -0
- package/dist/image-DIGjCPe6.cjs +6 -0
- package/dist/mdx-K0WYBAJ3.js +7 -0
- package/dist/mdx-hpErbRUe.cjs +7 -0
- package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
- package/dist/meta-loader-9IpAHWDS.mjs +6 -0
- package/dist/node/cli-entry.cjs +1 -2
- package/dist/node/cli-entry.mjs +1 -2
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +66 -13
- package/dist/node/index.d.mts +66 -14
- package/dist/node/index.mjs +1 -1
- package/dist/node/routes/worker.cjs +6 -0
- package/dist/node/routes/worker.d.cts +2 -0
- package/dist/node/routes/worker.d.mts +2 -0
- package/dist/node/routes/worker.mjs +6 -0
- package/dist/node-C2nWXElP.mjs +112 -0
- package/dist/node-CinkUtxV.cjs +112 -0
- package/dist/package-BMYLDBBP.cjs +6 -0
- package/dist/{package-DukYeKmD.mjs → package-HegMOTL_.mjs} +1 -1
- package/dist/parser-Bh11BsdA.cjs +6 -0
- package/dist/parser-D8eQvE7N.mjs +6 -0
- package/dist/parser-DYRzXWmA.cjs +6 -0
- package/dist/routes-CHf76Ye4.cjs +6 -0
- package/dist/routes-CMUZGI6T.mjs +6 -0
- package/dist/routes-Co1mRM58.cjs +6 -0
- package/dist/search-dialog-BACuzoVX.cjs +6 -0
- package/dist/search-dialog-BKagVT17.js +6 -0
- package/dist/search-dialog-C8w12eUx.js +6 -0
- package/dist/search-dialog-CGyrozZE.cjs +6 -0
- package/dist/search-dialog-D26rUnJ_.cjs +6 -0
- package/dist/sidebar-DKvg6KOc.d.cts +491 -0
- package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
- package/dist/utils-BxNAXhZZ.mjs +7 -0
- package/dist/utils-Clzu7jvb.cjs +7 -0
- package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
- package/dist/worker-pool-BwU8ckrg.cjs +6 -0
- package/package.json +27 -8
- package/src/client/app/doc-page.tsx +9 -5
- package/src/client/app/docs-layout.tsx +17 -3
- package/src/client/app/head.tsx +122 -0
- package/src/client/app/helmet-compat.tsx +36 -0
- package/src/client/app/mdx-component.tsx +5 -52
- package/src/client/app/mdx-components-context.tsx +32 -8
- package/src/client/app/routes-context.tsx +2 -2
- package/src/client/app/scroll-handler.tsx +1 -1
- package/src/client/app/theme-context.tsx +5 -5
- package/src/client/app/ui-context.tsx +42 -0
- package/src/client/components/docs-layout-default.tsx +85 -0
- package/src/client/components/icons-dev.tsx +38 -15
- package/src/client/components/mdx/callout.tsx +97 -0
- package/src/client/components/mdx/card.tsx +73 -98
- package/src/client/components/mdx/cards.tsx +27 -0
- package/src/client/components/mdx/code-block.tsx +37 -17
- package/src/client/components/mdx/field.tsx +24 -56
- package/src/client/components/mdx/image.tsx +36 -15
- package/src/client/components/mdx/index.ts +19 -53
- package/src/client/components/mdx/table.tsx +46 -148
- package/src/client/components/mdx/typographics.tsx +120 -0
- package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
- package/src/client/components/primitives/breadcrumbs.tsx +5 -24
- package/src/client/components/primitives/button.tsx +3 -142
- package/src/client/components/primitives/code-block.tsx +104 -97
- package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
- package/src/client/components/primitives/error-boundary.tsx +107 -0
- package/src/client/components/primitives/heading.tsx +128 -0
- package/src/client/components/primitives/helpers/observer.ts +62 -32
- package/src/client/components/primitives/image.tsx +26 -0
- package/src/client/components/primitives/link.tsx +50 -52
- package/src/client/components/primitives/menu.tsx +25 -49
- package/src/client/components/primitives/navbar.tsx +234 -59
- package/src/client/components/primitives/on-this-page.tsx +169 -40
- package/src/client/components/primitives/page-nav.tsx +11 -39
- package/src/client/components/primitives/popover.tsx +12 -30
- package/src/client/components/primitives/search-dialog.tsx +77 -71
- package/src/client/components/primitives/sidebar.tsx +312 -119
- package/src/client/components/primitives/skeleton.tsx +1 -1
- package/src/client/components/primitives/tabs.tsx +5 -16
- package/src/client/components/primitives/tooltip.tsx +1 -1
- package/src/client/components/ui-base/banner.tsx +66 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
- package/src/client/components/ui-base/copy-markdown.tsx +43 -35
- package/src/client/components/ui-base/error-boundary.tsx +9 -46
- package/src/client/components/ui-base/github-stars.tsx +5 -3
- package/src/client/components/ui-base/index.ts +3 -3
- package/src/client/components/ui-base/last-updated.tsx +27 -0
- package/src/client/components/ui-base/navbar.tsx +183 -89
- package/src/client/components/ui-base/not-found.tsx +11 -9
- package/src/client/components/ui-base/on-this-page.tsx +8 -104
- package/src/client/components/ui-base/page-nav.tsx +23 -9
- package/src/client/components/ui-base/search-dialog.tsx +111 -36
- package/src/client/components/ui-base/search-highlight.tsx +10 -0
- package/src/client/components/ui-base/sidebar.tsx +77 -154
- package/src/client/components/ui-base/tabs.tsx +20 -7
- package/src/client/components/ui-base/theme-toggle.tsx +88 -10
- package/src/client/components/ui-base/version-i18n.tsx +80 -0
- package/src/client/hooks/index.ts +2 -1
- package/src/client/hooks/use-analytics.ts +272 -0
- package/src/client/hooks/use-i18n.ts +120 -53
- package/src/client/hooks/use-localized-to.ts +70 -30
- package/src/client/hooks/use-navbar.ts +69 -39
- package/src/client/hooks/use-page-nav.ts +28 -25
- package/src/client/hooks/use-routes.ts +64 -81
- package/src/client/hooks/use-search-highlight.ts +185 -0
- package/src/client/hooks/use-search.ts +12 -3
- package/src/client/hooks/use-sidebar.ts +183 -77
- package/src/client/hooks/use-tabs.ts +3 -4
- package/src/client/hooks/use-version.ts +46 -18
- package/src/client/index.ts +13 -86
- package/src/client/mdx.ts +2 -0
- package/src/client/primitives.ts +19 -0
- package/src/client/ssg/boltdocs-shell.tsx +78 -57
- package/src/client/ssg/create-routes.tsx +290 -50
- package/src/client/ssg/mdx-page.tsx +2 -1
- package/src/client/store/boltdocs-context.tsx +83 -12
- package/src/client/theme/neutral.css +74 -361
- package/src/client/theme/reset.css +189 -0
- package/src/client/types.ts +10 -2
- package/src/client/utils/path.ts +9 -0
- package/src/client/utils/react-to-text.ts +24 -24
- package/src/client/virtual.d.ts +1 -1
- package/src/shared/types.ts +97 -21
- package/dist/node-CWN8U_p8.mjs +0 -88
- package/dist/node-D5iosYXv.cjs +0 -88
- package/dist/search-dialog-3lvKsbVG.js +0 -6
- package/dist/search-dialog-DMK5OpgH.cjs +0 -6
- package/dist/use-search-C9bxCqfF.js +0 -6
- package/dist/use-search-DcfZSunO.cjs +0 -6
- package/src/client/components/mdx/admonition.tsx +0 -91
- package/src/client/components/mdx/badge.tsx +0 -41
- package/src/client/components/mdx/button.tsx +0 -35
- package/src/client/components/mdx/component-preview.tsx +0 -37
- package/src/client/components/mdx/component-props.tsx +0 -83
- package/src/client/components/mdx/file-tree.tsx +0 -325
- package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
- package/src/client/components/mdx/hooks/useTable.ts +0 -74
- package/src/client/components/mdx/hooks/useTabs.ts +0 -68
- package/src/client/components/mdx/link.tsx +0 -38
- package/src/client/components/mdx/list.tsx +0 -192
- package/src/client/components/mdx/tabs.tsx +0 -135
- package/src/client/components/mdx/video.tsx +0 -68
- package/src/client/components/primitives/index.ts +0 -19
- package/src/client/components/primitives/navigation-menu.tsx +0 -114
- package/src/client/components/ui-base/head.tsx +0 -76
- package/src/client/components/ui-base/loading.tsx +0 -57
- package/src/client/components/ui-base/powered-by.tsx +0 -25
- package/src/client/hooks/use-onthispage.ts +0 -23
- package/src/client/utils/use-on-change.ts +0 -15
|
@@ -1,169 +1,92 @@
|
|
|
1
|
-
import { useState, useEffect, useMemo } from 'react'
|
|
2
|
-
import { useSidebar } from '../../hooks/use-sidebar'
|
|
3
1
|
import { Sidebar as SidebarPrimitive } from '../primitives/sidebar'
|
|
4
|
-
import { PoweredBy } from './powered-by'
|
|
5
2
|
import * as LucideIcons from 'lucide-react'
|
|
6
|
-
import virtualIcons from 'virtual:boltdocs-icons'
|
|
7
3
|
import type { ComponentRoute } from '../../types'
|
|
8
4
|
import type { BoltdocsConfig } from '../../../shared/types'
|
|
5
|
+
import { VersionSelector, I18nSelector } from './version-i18n'
|
|
6
|
+
import { ThemeSwitcher } from './theme-toggle'
|
|
7
|
+
import { useNavbar } from '../../hooks/use-navbar'
|
|
8
|
+
import { useUI } from '../../app/ui-context'
|
|
9
|
+
import { Button } from '../primitives/button'
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
string,
|
|
14
|
-
React.ElementType
|
|
15
|
-
>
|
|
16
|
-
const IconComponent = icons[iconName] || icons[iconName + 'Icon']
|
|
17
|
-
return IconComponent || undefined
|
|
11
|
+
interface SidebarProps {
|
|
12
|
+
routes: ComponentRoute[]
|
|
13
|
+
config: BoltdocsConfig
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
getIcon,
|
|
24
|
-
}: {
|
|
25
|
-
route: ComponentRoute
|
|
26
|
-
activePath: string
|
|
27
|
-
getIcon: (iconName?: string) => React.ElementType | undefined
|
|
28
|
-
}) {
|
|
29
|
-
const isCurrent =
|
|
30
|
-
activePath ===
|
|
31
|
-
(route.path.endsWith('/') ? route.path.slice(0, -1) : route.path)
|
|
32
|
-
|
|
33
|
-
const hasActiveSubRoute = useMemo(
|
|
34
|
-
() => route.subRoutes?.some((r) => r.path === activePath),
|
|
35
|
-
[route.subRoutes, activePath],
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
const [isOpen, setIsOpen] = useState(hasActiveSubRoute || isCurrent)
|
|
16
|
+
function SidebarMain({ routes, config }: SidebarProps) {
|
|
17
|
+
const { logo, title, logoProps } = useNavbar()
|
|
18
|
+
const { closeSidebar } = useUI()
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
const SidebarLogo = logo ? (
|
|
21
|
+
<img
|
|
22
|
+
src={logo}
|
|
23
|
+
alt={logoProps?.alt || title}
|
|
24
|
+
width={24}
|
|
25
|
+
height={24}
|
|
26
|
+
className="rounded-xl"
|
|
27
|
+
/>
|
|
28
|
+
) : null
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
<SidebarPrimitive.SubGroup
|
|
48
|
-
label={route.title}
|
|
49
|
-
href={route.path}
|
|
50
|
-
active={isCurrent}
|
|
51
|
-
icon={getIcon(route.icon)}
|
|
52
|
-
badge={route.badge}
|
|
53
|
-
isOpen={isOpen}
|
|
54
|
-
onToggle={() => setIsOpen(!isOpen)}
|
|
55
|
-
>
|
|
56
|
-
{route.subRoutes?.map((subRoute: ComponentRoute) => {
|
|
57
|
-
const isSubCurrent =
|
|
58
|
-
activePath ===
|
|
59
|
-
(subRoute.path.endsWith('/') ? subRoute.path.slice(0, -1) : subRoute.path)
|
|
60
|
-
return (
|
|
61
|
-
<SidebarPrimitive.Link
|
|
62
|
-
key={subRoute.path}
|
|
63
|
-
label={subRoute.title}
|
|
64
|
-
href={subRoute.path}
|
|
65
|
-
active={isSubCurrent}
|
|
66
|
-
icon={getIcon(subRoute.icon)}
|
|
67
|
-
badge={subRoute.badge}
|
|
68
|
-
/>
|
|
69
|
-
)
|
|
70
|
-
})}
|
|
71
|
-
</SidebarPrimitive.SubGroup>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
30
|
+
const hasUtilities = config.versions || config.i18n
|
|
74
31
|
|
|
75
|
-
function SidebarGroupSection({
|
|
76
|
-
group,
|
|
77
|
-
activePath,
|
|
78
|
-
getIcon,
|
|
79
|
-
}: {
|
|
80
|
-
group: {
|
|
81
|
-
slug: string
|
|
82
|
-
title: string
|
|
83
|
-
routes: ComponentRoute[]
|
|
84
|
-
icon?: string
|
|
85
|
-
}
|
|
86
|
-
activePath: string
|
|
87
|
-
getIcon: (iconName?: string) => React.ElementType | undefined
|
|
88
|
-
}) {
|
|
89
32
|
return (
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
activePath={activePath}
|
|
98
|
-
getIcon={getIcon}
|
|
99
|
-
/>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
33
|
+
<>
|
|
34
|
+
{/* Desktop Version */}
|
|
35
|
+
<SidebarPrimitive.Root>
|
|
36
|
+
<SidebarPrimitive.Content>
|
|
37
|
+
<SidebarPrimitive.Items routes={routes} />
|
|
38
|
+
</SidebarPrimitive.Content>
|
|
39
|
+
</SidebarPrimitive.Root>
|
|
102
40
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
41
|
+
{/* Mobile Version */}
|
|
42
|
+
<SidebarPrimitive.Mobile>
|
|
43
|
+
<SidebarPrimitive.Header>
|
|
44
|
+
<div className="flex items-center gap-3">
|
|
45
|
+
{SidebarLogo}
|
|
46
|
+
<span className="font-bold text-lg tracking-tight text-body truncate max-w-[120px]">
|
|
47
|
+
{title}
|
|
48
|
+
</span>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="flex items-center gap-2">
|
|
51
|
+
<ThemeSwitcher className="w-24 h-9 rounded-xl" />
|
|
52
|
+
<Button
|
|
53
|
+
onPress={closeSidebar}
|
|
54
|
+
className="h-9 w-9 flex items-center justify-center bg-transparent border-none outline-none select-none cursor-pointer rounded-xl hover:bg-primary-50/50 text-muted hover:text-body transition-colors"
|
|
55
|
+
aria-label="Close sidebar"
|
|
56
|
+
>
|
|
57
|
+
<LucideIcons.X size={20} />
|
|
58
|
+
</Button>
|
|
59
|
+
</div>
|
|
60
|
+
</SidebarPrimitive.Header>
|
|
61
|
+
<SidebarPrimitive.Content>
|
|
62
|
+
{hasUtilities && (
|
|
63
|
+
<div className="flex flex-col gap-4 mb-10">
|
|
64
|
+
<div className="flex gap-3">
|
|
65
|
+
{config.versions && (
|
|
66
|
+
<VersionSelector className="flex-1 justify-between h-10 bg-surface border-subtle rounded-xl" />
|
|
67
|
+
)}
|
|
68
|
+
{config.i18n && (
|
|
69
|
+
<I18nSelector className="flex-1 justify-between h-10 bg-surface border-subtle rounded-xl" />
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
<div className="mt-2 border-b border-subtle" />
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
<SidebarPrimitive.Items routes={routes} />
|
|
76
|
+
</SidebarPrimitive.Content>
|
|
77
|
+
</SidebarPrimitive.Mobile>
|
|
78
|
+
</>
|
|
118
79
|
)
|
|
119
80
|
}
|
|
120
81
|
|
|
121
|
-
export
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<SidebarPrimitive.Root>
|
|
133
|
-
{ungrouped.length > 0 && (
|
|
134
|
-
<SidebarPrimitive.Group className="mb-6">
|
|
135
|
-
{ungrouped.map((route) => {
|
|
136
|
-
const isCurrent =
|
|
137
|
-
activePath ===
|
|
138
|
-
(route.path.endsWith('/') ? route.path.slice(0, -1) : route.path)
|
|
139
|
-
return (
|
|
140
|
-
<SidebarPrimitive.Link
|
|
141
|
-
key={route.path}
|
|
142
|
-
label={route.title}
|
|
143
|
-
href={route.path}
|
|
144
|
-
active={isCurrent}
|
|
145
|
-
icon={getIcon(route.icon)}
|
|
146
|
-
badge={route.badge}
|
|
147
|
-
/>
|
|
148
|
-
)
|
|
149
|
-
})}
|
|
150
|
-
</SidebarPrimitive.Group>
|
|
151
|
-
)}
|
|
152
|
-
|
|
153
|
-
{groups.map((group) => (
|
|
154
|
-
<SidebarGroupSection
|
|
155
|
-
key={group.slug}
|
|
156
|
-
group={group}
|
|
157
|
-
activePath={activePath}
|
|
158
|
-
getIcon={getIcon}
|
|
159
|
-
/>
|
|
160
|
-
))}
|
|
161
|
-
|
|
162
|
-
{themeConfig?.poweredBy && (
|
|
163
|
-
<div className="mt-auto pt-8">
|
|
164
|
-
<PoweredBy />
|
|
165
|
-
</div>
|
|
166
|
-
)}
|
|
167
|
-
</SidebarPrimitive.Root>
|
|
168
|
-
)
|
|
169
|
-
}
|
|
82
|
+
export const Sidebar = Object.assign(SidebarMain, {
|
|
83
|
+
Root: SidebarPrimitive.Root,
|
|
84
|
+
Mobile: SidebarPrimitive.Mobile,
|
|
85
|
+
Header: SidebarPrimitive.Header,
|
|
86
|
+
Content: SidebarPrimitive.Content,
|
|
87
|
+
Group: SidebarPrimitive.Group,
|
|
88
|
+
Link: SidebarPrimitive.Link,
|
|
89
|
+
SubGroup: SidebarPrimitive.SubGroup,
|
|
90
|
+
Item: SidebarPrimitive.Item,
|
|
91
|
+
Items: SidebarPrimitive.Items,
|
|
92
|
+
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
1
2
|
import { useTabs as useTabsHook } from '../../hooks/use-tabs'
|
|
2
3
|
import { Tabs as T } from '../primitives/tabs'
|
|
3
4
|
import { Link } from '../primitives/link'
|
|
@@ -16,6 +17,17 @@ export function Tabs({
|
|
|
16
17
|
const { currentLocale } = useRoutes()
|
|
17
18
|
const { indicatorStyle, tabRefs, activeIndex } = useTabsHook(tabs, routes)
|
|
18
19
|
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const activeTab = tabRefs.current[activeIndex]
|
|
22
|
+
if (activeTab) {
|
|
23
|
+
activeTab.scrollIntoView({
|
|
24
|
+
behavior: 'smooth',
|
|
25
|
+
block: 'nearest',
|
|
26
|
+
inline: 'center',
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}, [activeIndex])
|
|
30
|
+
|
|
19
31
|
const renderTabIcon = (iconName?: string) => {
|
|
20
32
|
if (!iconName) return null
|
|
21
33
|
if (iconName.trim().startsWith('<svg')) {
|
|
@@ -34,8 +46,8 @@ export function Tabs({
|
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
return (
|
|
37
|
-
<div className="mx-auto max-w-(--breakpoint-3xl) px-4 md:px-6">
|
|
38
|
-
<T.List className="border-none py-0">
|
|
49
|
+
<div className="mx-auto max-w-(--breakpoint-3xl) px-4 md:px-6 select-none">
|
|
50
|
+
<T.List className="border-none py-0 scrollbar-hide relative flex flex-row items-center">
|
|
39
51
|
{tabs.map((tab, index) => {
|
|
40
52
|
const isActive = index === activeIndex
|
|
41
53
|
const firstRoute = routes.find(
|
|
@@ -50,10 +62,8 @@ export function Tabs({
|
|
|
50
62
|
ref={(el: HTMLAnchorElement | null) => {
|
|
51
63
|
tabRefs.current[index] = el
|
|
52
64
|
}}
|
|
53
|
-
className={`relative flex items-center gap-2 px-4 py-3 text-sm font-
|
|
54
|
-
isActive
|
|
55
|
-
? 'text-primary-500'
|
|
56
|
-
: 'text-text-muted hover:text-text-main'
|
|
65
|
+
className={`relative flex items-center gap-2 px-4 py-3.5 text-sm font-semibold transition-colors duration-300 outline-none whitespace-nowrap ${
|
|
66
|
+
isActive ? 'text-primary-500' : 'text-muted hover:text-body'
|
|
57
67
|
}`}
|
|
58
68
|
>
|
|
59
69
|
{renderTabIcon(tab.icon)}
|
|
@@ -61,7 +71,10 @@ export function Tabs({
|
|
|
61
71
|
</Link>
|
|
62
72
|
)
|
|
63
73
|
})}
|
|
64
|
-
<T.Indicator
|
|
74
|
+
<T.Indicator
|
|
75
|
+
style={indicatorStyle}
|
|
76
|
+
className="h-0.5 bg-primary-500 rounded-full transition-all duration-300"
|
|
77
|
+
/>
|
|
65
78
|
</T.List>
|
|
66
79
|
</div>
|
|
67
80
|
)
|
|
@@ -3,6 +3,7 @@ import { Sun, Moon, Monitor } from 'lucide-react'
|
|
|
3
3
|
import { useTheme } from '../../app/theme-context'
|
|
4
4
|
import { Button } from 'react-aria-components'
|
|
5
5
|
import { Menu } from '../primitives/menu'
|
|
6
|
+
import { cn } from '../../utils/cn'
|
|
6
7
|
|
|
7
8
|
export function ThemeToggle() {
|
|
8
9
|
const { theme, setTheme } = useTheme()
|
|
@@ -21,7 +22,7 @@ export function ThemeToggle() {
|
|
|
21
22
|
return (
|
|
22
23
|
<Menu.Trigger placement="bottom right">
|
|
23
24
|
<Button
|
|
24
|
-
className="flex h-9 w-9 items-center justify-center rounded-
|
|
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"
|
|
25
26
|
aria-label="Selection theme"
|
|
26
27
|
>
|
|
27
28
|
<Icon size={20} className="animate-in fade-in zoom-in duration-300" />
|
|
@@ -33,20 +34,97 @@ export function ThemeToggle() {
|
|
|
33
34
|
const newTheme = Array.from(keys)[0] as 'light' | 'dark' | 'system'
|
|
34
35
|
setTheme(newTheme)
|
|
35
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"
|
|
36
38
|
>
|
|
37
|
-
<Menu.Item
|
|
38
|
-
|
|
39
|
-
|
|
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>
|
|
40
48
|
</Menu.Item>
|
|
41
|
-
<Menu.Item
|
|
42
|
-
|
|
43
|
-
|
|
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>
|
|
44
58
|
</Menu.Item>
|
|
45
|
-
<Menu.Item
|
|
46
|
-
|
|
47
|
-
|
|
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>
|
|
48
68
|
</Menu.Item>
|
|
49
69
|
</Menu.Root>
|
|
50
70
|
</Menu.Trigger>
|
|
51
71
|
)
|
|
52
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
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
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 'lucide-react'
|
|
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,7 +1,6 @@
|
|
|
1
1
|
export { useNavbar } from './use-navbar'
|
|
2
2
|
export { useSidebar } from './use-sidebar'
|
|
3
3
|
export { useSearch } from './use-search'
|
|
4
|
-
export { useOnThisPage } from './use-onthispage'
|
|
5
4
|
export { useTabs } from './use-tabs'
|
|
6
5
|
export { useVersion } from './use-version'
|
|
7
6
|
export { useI18n } from './use-i18n'
|
|
@@ -10,3 +9,5 @@ export { useBreadcrumbs } from './use-breadcrumbs'
|
|
|
10
9
|
export { useRoutes } from './use-routes'
|
|
11
10
|
export { useLocalizedTo } from './use-localized-to'
|
|
12
11
|
export { useLocation } from './use-location'
|
|
12
|
+
export { useSearchHighlight } from './use-search-highlight'
|
|
13
|
+
export { useAnalytics, useTrackPageView, useTrackEvent } from './use-analytics'
|