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,40 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react'
|
|
2
|
+
import { useLocation } from 'react-router-dom'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handles scroll restoration and hash scrolling on navigation.
|
|
6
|
+
* It ensures the page scrolls to top on pathname changes,
|
|
7
|
+
* or specifically to an anchor element if a hash is present.
|
|
8
|
+
*/
|
|
9
|
+
export function ScrollHandler() {
|
|
10
|
+
const { pathname, hash } = useLocation()
|
|
11
|
+
|
|
12
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: pathname is used as a trigger for scroll-to-top on navigation
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
const container = document.querySelector('.boltdocs-content')
|
|
15
|
+
if (!container) return
|
|
16
|
+
|
|
17
|
+
if (hash) {
|
|
18
|
+
const id = hash.replace('#', '')
|
|
19
|
+
const element = document.getElementById(id)
|
|
20
|
+
if (element) {
|
|
21
|
+
const offset = 80
|
|
22
|
+
const containerRect = container.getBoundingClientRect().top
|
|
23
|
+
const elementRect = element.getBoundingClientRect().top
|
|
24
|
+
const elementPosition = elementRect - containerRect
|
|
25
|
+
const offsetPosition = elementPosition - offset + container.scrollTop
|
|
26
|
+
|
|
27
|
+
container.scrollTo({
|
|
28
|
+
top: offsetPosition,
|
|
29
|
+
behavior: 'smooth',
|
|
30
|
+
})
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Scroll to top on navigation when no hash is specified
|
|
36
|
+
container.scrollTo(0, 0)
|
|
37
|
+
}, [pathname, hash])
|
|
38
|
+
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createContext, use, useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
type Theme = 'light' | 'dark'
|
|
4
|
+
|
|
5
|
+
interface ThemeContextType {
|
|
6
|
+
theme: Theme
|
|
7
|
+
toggleTheme: () => void
|
|
8
|
+
setTheme: (theme: Theme) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
|
|
12
|
+
|
|
13
|
+
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
14
|
+
const [theme, setThemeState] = useState<Theme>('dark')
|
|
15
|
+
const [mounted, setMounted] = useState(false)
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setMounted(true)
|
|
19
|
+
const stored = localStorage.getItem('boltdocs-theme')
|
|
20
|
+
if (stored === 'light' || stored === 'dark') {
|
|
21
|
+
setThemeState(stored as Theme)
|
|
22
|
+
} else {
|
|
23
|
+
const prefersDark = window.matchMedia(
|
|
24
|
+
'(prefers-color-scheme: dark)',
|
|
25
|
+
).matches
|
|
26
|
+
setThemeState(prefersDark ? 'dark' : 'light')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
30
|
+
const handleChange = (e: MediaQueryListEvent) => {
|
|
31
|
+
if (!localStorage.getItem('boltdocs-theme')) {
|
|
32
|
+
setThemeState(e.matches ? 'dark' : 'light')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
mediaQuery.addEventListener('change', handleChange)
|
|
36
|
+
return () => mediaQuery.removeEventListener('change', handleChange)
|
|
37
|
+
}, [])
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!mounted) return
|
|
41
|
+
const root = document.documentElement
|
|
42
|
+
if (theme === 'light') {
|
|
43
|
+
root.classList.add('theme-light')
|
|
44
|
+
root.dataset.theme = 'light'
|
|
45
|
+
} else {
|
|
46
|
+
root.classList.remove('theme-light')
|
|
47
|
+
root.dataset.theme = 'dark'
|
|
48
|
+
}
|
|
49
|
+
}, [theme, mounted])
|
|
50
|
+
|
|
51
|
+
const toggleTheme = () => {
|
|
52
|
+
const newTheme = theme === 'dark' ? 'light' : 'dark'
|
|
53
|
+
setThemeState(newTheme)
|
|
54
|
+
localStorage.setItem('boltdocs-theme', newTheme)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const setTheme = (newTheme: Theme) => {
|
|
58
|
+
setThemeState(newTheme)
|
|
59
|
+
localStorage.setItem('boltdocs-theme', newTheme)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<ThemeContext.Provider value={{ theme, toggleTheme, setTheme }}>
|
|
64
|
+
{children}
|
|
65
|
+
</ThemeContext.Provider>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function useTheme() {
|
|
70
|
+
const context = use(ThemeContext)
|
|
71
|
+
if (context === undefined) {
|
|
72
|
+
throw new Error('useTheme must be used within a ThemeProvider')
|
|
73
|
+
}
|
|
74
|
+
return context
|
|
75
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { DocsLayout } from '@components/docs-layout'
|
|
2
|
+
import { Navbar } from '@components/ui-base/navbar'
|
|
3
|
+
import { Sidebar } from '@components/ui-base/sidebar'
|
|
4
|
+
import { OnThisPage } from '@components/ui-base/on-this-page'
|
|
5
|
+
import { Head } from '@components/ui-base/head'
|
|
6
|
+
import { Breadcrumbs } from '@components/ui-base/breadcrumbs'
|
|
7
|
+
import { PageNav } from '@components/ui-base/page-nav'
|
|
8
|
+
import { ProgressBar } from '@components/ui-base/progress-bar'
|
|
9
|
+
import { ErrorBoundary } from '@components/ui-base/error-boundary'
|
|
10
|
+
import { CopyMarkdown } from '@components/ui-base/copy-markdown'
|
|
11
|
+
import { useRoutes } from '@client/hooks/use-routes'
|
|
12
|
+
import { useConfig } from '@client/app/config-context'
|
|
13
|
+
import { useMdxComponents } from '@client/app/mdx-components-context'
|
|
14
|
+
|
|
15
|
+
import { useLocation } from 'react-router-dom'
|
|
16
|
+
|
|
17
|
+
export interface LayoutProps {
|
|
18
|
+
children: React.ReactNode
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The built-in default layout for Boltdocs.
|
|
23
|
+
* Users who create their own `layout.tsx` can import the same building blocks
|
|
24
|
+
* and rearrange, wrap, or replace any section.
|
|
25
|
+
*/
|
|
26
|
+
export function DefaultLayout({ children }: LayoutProps) {
|
|
27
|
+
const { routes: filteredRoutes, allRoutes, currentRoute } = useRoutes()
|
|
28
|
+
const { pathname } = useLocation()
|
|
29
|
+
const config = useConfig()
|
|
30
|
+
const mdxComponents = useMdxComponents()
|
|
31
|
+
const CopyMarkdownComp = (mdxComponents.CopyMarkdown as any) || CopyMarkdown
|
|
32
|
+
|
|
33
|
+
const isHome = pathname === '/' || pathname === ''
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<DocsLayout>
|
|
37
|
+
<ProgressBar />
|
|
38
|
+
<Head
|
|
39
|
+
siteTitle={config.themeConfig?.title || 'Boltdocs'}
|
|
40
|
+
siteDescription={config.themeConfig?.description || ''}
|
|
41
|
+
routes={allRoutes}
|
|
42
|
+
/>
|
|
43
|
+
<Navbar />
|
|
44
|
+
|
|
45
|
+
<DocsLayout.Body>
|
|
46
|
+
{!isHome && <Sidebar routes={filteredRoutes} config={config} />}
|
|
47
|
+
|
|
48
|
+
<DocsLayout.Content>
|
|
49
|
+
{!isHome && (
|
|
50
|
+
<DocsLayout.ContentHeader>
|
|
51
|
+
<Breadcrumbs />
|
|
52
|
+
<CopyMarkdownComp
|
|
53
|
+
mdxRaw={currentRoute?._rawContent}
|
|
54
|
+
route={currentRoute}
|
|
55
|
+
config={config.themeConfig?.copyMarkdown}
|
|
56
|
+
/>
|
|
57
|
+
</DocsLayout.ContentHeader>
|
|
58
|
+
)}
|
|
59
|
+
|
|
60
|
+
<ErrorBoundary>{children}</ErrorBoundary>
|
|
61
|
+
|
|
62
|
+
{!isHome && (
|
|
63
|
+
<DocsLayout.ContentFooter>
|
|
64
|
+
<PageNav />
|
|
65
|
+
</DocsLayout.ContentFooter>
|
|
66
|
+
)}
|
|
67
|
+
</DocsLayout.Content>
|
|
68
|
+
|
|
69
|
+
{!isHome && (
|
|
70
|
+
<OnThisPage
|
|
71
|
+
headings={currentRoute?.headings}
|
|
72
|
+
editLink={config.themeConfig?.editLink}
|
|
73
|
+
communityHelp={config.themeConfig?.communityHelp}
|
|
74
|
+
filePath={currentRoute?.filePath}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</DocsLayout.Body>
|
|
78
|
+
</DocsLayout>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
import { cn } from '@client/utils/cn'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props shared by all layout slot components.
|
|
6
|
+
*/
|
|
7
|
+
interface SlotProps {
|
|
8
|
+
children?: React.ReactNode
|
|
9
|
+
className?: string
|
|
10
|
+
style?: React.CSSProperties
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Root layout shell. Renders a full-height flex column.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <DocsLayout>
|
|
19
|
+
* <Navbar />
|
|
20
|
+
* <DocsLayout.Body>...</DocsLayout.Body>
|
|
21
|
+
* </DocsLayout>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function DocsLayoutRoot({ children, className, style }: SlotProps) {
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={cn(
|
|
28
|
+
'h-screen flex flex-col overflow-hidden bg-bg-main text-text-main',
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
style={style}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Horizontal flex container for sidebar + content + toc.
|
|
40
|
+
*/
|
|
41
|
+
function Body({ children, className, style }: SlotProps) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
className={cn(
|
|
45
|
+
'mx-auto flex flex-1 w-full max-w-(--breakpoint-3xl) bg-bg-main overflow-hidden',
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
style={style}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Main scrollable content area.
|
|
57
|
+
*/
|
|
58
|
+
function Content({ children, className, style }: SlotProps) {
|
|
59
|
+
return (
|
|
60
|
+
<main
|
|
61
|
+
className={cn(
|
|
62
|
+
'boltdocs-content flex-1 min-w-0 overflow-y-auto',
|
|
63
|
+
className,
|
|
64
|
+
)}
|
|
65
|
+
style={style}
|
|
66
|
+
>
|
|
67
|
+
<div className="boltdocs-page mx-auto max-w-content-max pt-4 pb-20 px-4 sm:px-8">
|
|
68
|
+
{children}
|
|
69
|
+
</div>
|
|
70
|
+
</main>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Content header row (breadcrumbs + copy markdown).
|
|
76
|
+
*/
|
|
77
|
+
function ContentHeader({ children, className, style }: SlotProps) {
|
|
78
|
+
return (
|
|
79
|
+
<div
|
|
80
|
+
className={cn('flex items-center justify-between mb-10', className)}
|
|
81
|
+
style={style}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Footer area inside the content section (page nav).
|
|
90
|
+
*/
|
|
91
|
+
function ContentFooter({ children, className, style }: SlotProps) {
|
|
92
|
+
return (
|
|
93
|
+
<div className={cn('mt-20', className)} style={style}>
|
|
94
|
+
{children}
|
|
95
|
+
</div>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Attach sub-components to the root
|
|
100
|
+
export const DocsLayout = Object.assign(DocsLayoutRoot, {
|
|
101
|
+
Body,
|
|
102
|
+
Content,
|
|
103
|
+
ContentHeader,
|
|
104
|
+
ContentFooter,
|
|
105
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { SVGProps } from 'react'
|
|
2
|
+
|
|
3
|
+
type WrapperProps = SVGProps<SVGSVGElement> & {
|
|
4
|
+
size?: number
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function wrapperProps(props: WrapperProps) {
|
|
8
|
+
const { size = 20, ...rest } = props
|
|
9
|
+
return {
|
|
10
|
+
...rest,
|
|
11
|
+
width: size,
|
|
12
|
+
height: size,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const Github = (props: WrapperProps) => (
|
|
17
|
+
<svg
|
|
18
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
19
|
+
viewBox="0 0 24 24"
|
|
20
|
+
fill="currentColor"
|
|
21
|
+
{...wrapperProps(props)}
|
|
22
|
+
>
|
|
23
|
+
<title>{'GitHub'}</title>
|
|
24
|
+
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
|
25
|
+
</svg>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export const Discord = (props: WrapperProps) => (
|
|
29
|
+
<svg
|
|
30
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
31
|
+
viewBox="0 0 24 24"
|
|
32
|
+
fill="currentColor"
|
|
33
|
+
{...wrapperProps(props)}
|
|
34
|
+
>
|
|
35
|
+
<title>{'Discord'}</title>
|
|
36
|
+
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418Z" />
|
|
37
|
+
</svg>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
export const XSocial = (props: WrapperProps) => (
|
|
41
|
+
<svg
|
|
42
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
43
|
+
viewBox="0 0 24 24"
|
|
44
|
+
fill="currentColor"
|
|
45
|
+
{...wrapperProps(props)}
|
|
46
|
+
>
|
|
47
|
+
<title>{'X'}</title>
|
|
48
|
+
<path d="M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z" />
|
|
49
|
+
</svg>
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
export const CodeSandbox = (props: WrapperProps) => (
|
|
53
|
+
<svg
|
|
54
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
55
|
+
viewBox="0 0 24 24"
|
|
56
|
+
fill="currentColor"
|
|
57
|
+
{...wrapperProps(props)}
|
|
58
|
+
>
|
|
59
|
+
<title>{'CodeSandbox'}</title>
|
|
60
|
+
<path d="M0 24h24V0H0v2.455h21.546v19.09H2.454V0H0Z" />
|
|
61
|
+
</svg>
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
export const Bluesky = (props: WrapperProps) => (
|
|
65
|
+
<svg
|
|
66
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
67
|
+
viewBox="0 0 24 24"
|
|
68
|
+
fill="currentColor"
|
|
69
|
+
{...wrapperProps(props)}
|
|
70
|
+
>
|
|
71
|
+
<title>{'Bluesky'}</title>
|
|
72
|
+
<path d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026" />
|
|
73
|
+
</svg>
|
|
74
|
+
)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Info,
|
|
3
|
+
Lightbulb,
|
|
4
|
+
AlertTriangle,
|
|
5
|
+
ShieldAlert,
|
|
6
|
+
Bookmark,
|
|
7
|
+
Zap,
|
|
8
|
+
Flame,
|
|
9
|
+
} from 'lucide-react'
|
|
10
|
+
import { cn } from '@client/utils/cn'
|
|
11
|
+
import { cva } from 'class-variance-authority'
|
|
12
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
13
|
+
|
|
14
|
+
const ICON_MAP: Record<string, React.ReactNode> = {
|
|
15
|
+
note: <Bookmark size={18} />,
|
|
16
|
+
tip: <Lightbulb size={18} />,
|
|
17
|
+
info: <Info size={18} />,
|
|
18
|
+
warning: <AlertTriangle size={18} />,
|
|
19
|
+
danger: <ShieldAlert size={18} />,
|
|
20
|
+
important: <Flame size={18} />,
|
|
21
|
+
caution: <Zap size={18} />,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const LABEL_MAP: Record<string, string> = {
|
|
25
|
+
note: 'Note',
|
|
26
|
+
tip: 'Tip',
|
|
27
|
+
info: 'Info',
|
|
28
|
+
warning: 'Warning',
|
|
29
|
+
danger: 'Danger',
|
|
30
|
+
important: 'Important',
|
|
31
|
+
caution: 'Caution',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const admonitionVariants = cva('py-4 px-4 rounded-lg', {
|
|
35
|
+
variants: {
|
|
36
|
+
type: {
|
|
37
|
+
note: 'border-primary-400 bg-primary-500/5 text-primary-400',
|
|
38
|
+
tip: 'border-emerald-500 bg-emerald-500/5 text-emerald-500',
|
|
39
|
+
info: 'border-sky-500 bg-sky-500/5 text-sky-500',
|
|
40
|
+
warning: 'border-amber-500 bg-amber-500/5 text-amber-500',
|
|
41
|
+
danger: 'border-red-500 bg-red-500/5 text-red-500',
|
|
42
|
+
important: 'border-orange-500 bg-orange-500/5 text-orange-500',
|
|
43
|
+
caution: 'border-yellow-500 bg-yellow-500/5 text-yellow-500',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
type: 'note',
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
type AdmonitionVariants = VariantProps<typeof admonitionVariants>
|
|
51
|
+
|
|
52
|
+
export interface AdmonitionProps
|
|
53
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
54
|
+
AdmonitionVariants {
|
|
55
|
+
title?: string
|
|
56
|
+
children: React.ReactNode
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function Admonition({
|
|
60
|
+
type = 'note',
|
|
61
|
+
title,
|
|
62
|
+
children,
|
|
63
|
+
className = '',
|
|
64
|
+
...rest
|
|
65
|
+
}: AdmonitionProps) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
className={cn(admonitionVariants({ type }), className)}
|
|
69
|
+
role={type === 'warning' || type === 'danger' ? 'alert' : 'note'}
|
|
70
|
+
{...rest}
|
|
71
|
+
>
|
|
72
|
+
<div className="flex items-center flex-row gap-2 mb-2">
|
|
73
|
+
<span className={cn('shrink-0', admonitionVariants({ type }))}>
|
|
74
|
+
{ICON_MAP[type as keyof typeof ICON_MAP]}
|
|
75
|
+
</span>
|
|
76
|
+
<span className="text-sm font-bold tracking-tight text-text-main">
|
|
77
|
+
{title || LABEL_MAP[type as keyof typeof LABEL_MAP]}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="text-sm text-text-muted leading-relaxed [&>p]:m-0 [&>p]:mb-2 [&>p:last-child]:mb-0">
|
|
81
|
+
{children}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const Note = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
88
|
+
<Admonition type="note" {...props} />
|
|
89
|
+
)
|
|
90
|
+
export const Tip = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
91
|
+
<Admonition type="tip" {...props} />
|
|
92
|
+
)
|
|
93
|
+
export const Warning = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
94
|
+
<Admonition type="warning" {...props} />
|
|
95
|
+
)
|
|
96
|
+
export const Danger = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
97
|
+
<Admonition type="danger" {...props} />
|
|
98
|
+
)
|
|
99
|
+
export const InfoBox = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
100
|
+
<Admonition type="info" {...props} />
|
|
101
|
+
)
|
|
102
|
+
export const Important = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
103
|
+
<Admonition type="important" {...props} />
|
|
104
|
+
)
|
|
105
|
+
export const Caution = (props: Omit<AdmonitionProps, 'type'>) => (
|
|
106
|
+
<Admonition type="caution" {...props} />
|
|
107
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cn } from '@client/utils/cn'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
4
|
+
|
|
5
|
+
const badgeVariants = cva(
|
|
6
|
+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold tracking-tight',
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: 'bg-bg-surface text-text-muted border-border-subtle',
|
|
11
|
+
primary: 'bg-primary-500/15 text-primary-400 border-primary-500/20',
|
|
12
|
+
success: 'bg-emerald-500/15 text-emerald-400 border-emerald-500/20',
|
|
13
|
+
warning: 'bg-amber-500/15 text-amber-400 border-amber-500/20',
|
|
14
|
+
danger: 'bg-red-500/15 text-red-400 border-red-500/20',
|
|
15
|
+
info: 'bg-sky-500/15 text-sky-400 border-sky-500/20',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
variant: 'default',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export interface BadgeProps
|
|
25
|
+
extends React.HTMLAttributes<HTMLSpanElement>,
|
|
26
|
+
VariantProps<typeof badgeVariants> {
|
|
27
|
+
children: React.ReactNode
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function Badge({
|
|
31
|
+
variant = 'default',
|
|
32
|
+
children,
|
|
33
|
+
className = '',
|
|
34
|
+
...rest
|
|
35
|
+
}: BadgeProps) {
|
|
36
|
+
return (
|
|
37
|
+
<span className={cn(badgeVariants({ variant }), className)} {...rest}>
|
|
38
|
+
{children}
|
|
39
|
+
</span>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button as ButtonPrimitive,
|
|
3
|
+
buttonVariants,
|
|
4
|
+
type ButtonProps,
|
|
5
|
+
} from '@components/primitives/button'
|
|
6
|
+
import { cn } from '@client/utils/cn'
|
|
7
|
+
|
|
8
|
+
export type { ButtonProps } from '@components/primitives/button'
|
|
9
|
+
|
|
10
|
+
export const Button = ({
|
|
11
|
+
className,
|
|
12
|
+
variant,
|
|
13
|
+
size,
|
|
14
|
+
rounded,
|
|
15
|
+
iconSize,
|
|
16
|
+
disabled,
|
|
17
|
+
...props
|
|
18
|
+
}: ButtonProps) => {
|
|
19
|
+
return (
|
|
20
|
+
<ButtonPrimitive
|
|
21
|
+
className={cn(
|
|
22
|
+
'group',
|
|
23
|
+
buttonVariants({
|
|
24
|
+
variant,
|
|
25
|
+
size,
|
|
26
|
+
rounded,
|
|
27
|
+
iconSize,
|
|
28
|
+
disabled,
|
|
29
|
+
className,
|
|
30
|
+
}),
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { MouseEvent as ReactMouseEvent } from 'react'
|
|
2
|
+
import { useCallback, useRef } from 'react'
|
|
3
|
+
import * as RAC from 'react-aria-components'
|
|
4
|
+
import { cn } from '@client/utils/cn'
|
|
5
|
+
import { cva } from 'class-variance-authority'
|
|
6
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
7
|
+
|
|
8
|
+
const cardsVariants = cva('grid gap-4 my-6', {
|
|
9
|
+
variants: {
|
|
10
|
+
cols: {
|
|
11
|
+
1: 'grid-cols-1',
|
|
12
|
+
2: 'grid-cols-1 sm:grid-cols-2',
|
|
13
|
+
3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
|
|
14
|
+
4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
cols: 3,
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
type CardsVariants = VariantProps<typeof cardsVariants>
|
|
22
|
+
|
|
23
|
+
export interface CardsProps
|
|
24
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
25
|
+
CardsVariants {}
|
|
26
|
+
|
|
27
|
+
export function Cards({
|
|
28
|
+
cols = 3,
|
|
29
|
+
children,
|
|
30
|
+
className = '',
|
|
31
|
+
...rest
|
|
32
|
+
}: CardsProps) {
|
|
33
|
+
return (
|
|
34
|
+
<div className={cn(cardsVariants({ cols }), className)} {...rest}>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
41
|
+
title?: string
|
|
42
|
+
icon?: React.ReactNode
|
|
43
|
+
href?: string
|
|
44
|
+
children?: React.ReactNode
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function Card({
|
|
48
|
+
title,
|
|
49
|
+
icon,
|
|
50
|
+
href,
|
|
51
|
+
children,
|
|
52
|
+
className = '',
|
|
53
|
+
...rest
|
|
54
|
+
}: CardProps) {
|
|
55
|
+
const cardRef = useRef<HTMLDivElement>(null)
|
|
56
|
+
const linkRef = useRef<HTMLAnchorElement>(null)
|
|
57
|
+
|
|
58
|
+
const handleMouseMove = useCallback((e: ReactMouseEvent<HTMLDivElement>) => {
|
|
59
|
+
const el = cardRef.current || linkRef.current
|
|
60
|
+
if (!el) return
|
|
61
|
+
const { left, top } = el.getBoundingClientRect()
|
|
62
|
+
el.style.setProperty('--x', `${e.clientX - left}px`)
|
|
63
|
+
el.style.setProperty('--y', `${e.clientY - top}px`)
|
|
64
|
+
}, [])
|
|
65
|
+
|
|
66
|
+
const inner = (
|
|
67
|
+
<>
|
|
68
|
+
<div
|
|
69
|
+
className="pointer-events-none absolute -inset-px rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
|
|
70
|
+
style={{
|
|
71
|
+
background:
|
|
72
|
+
'radial-gradient(400px circle at var(--x) var(--y), color-mix(in oklch, var(--color-primary-500), transparent 90%), transparent 80%)',
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
{icon && (
|
|
76
|
+
<div className="mb-3 flex h-10 w-10 items-center justify-center rounded-lg bg-primary-500/10 text-primary-400 text-lg transition-transform duration-300 group-hover:scale-105 group-hover:-rotate-3">
|
|
77
|
+
{icon}
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
<div className="space-y-1.5">
|
|
81
|
+
{title && <h3 className="text-sm font-bold text-text-main">{title}</h3>}
|
|
82
|
+
{children && (
|
|
83
|
+
<div className="text-sm text-text-muted leading-relaxed">
|
|
84
|
+
{children}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
</>
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
const cardClasses = cn(
|
|
92
|
+
'group relative block rounded-xl border border-border-subtle bg-bg-surface p-5 outline-none overflow-hidden',
|
|
93
|
+
'transition-all duration-200 hover:border-primary-500/40 hover:shadow-lg hover:shadow-primary-500/5',
|
|
94
|
+
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
95
|
+
className,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if (href) {
|
|
99
|
+
return (
|
|
100
|
+
<RAC.Link
|
|
101
|
+
ref={linkRef}
|
|
102
|
+
href={href}
|
|
103
|
+
className={cn(cardClasses, 'no-underline cursor-pointer')}
|
|
104
|
+
onMouseMove={handleMouseMove}
|
|
105
|
+
{...(rest as any)}
|
|
106
|
+
>
|
|
107
|
+
{inner}
|
|
108
|
+
</RAC.Link>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: spotlight effect is decorative
|
|
114
|
+
<div
|
|
115
|
+
ref={cardRef}
|
|
116
|
+
role="presentation"
|
|
117
|
+
className={cardClasses}
|
|
118
|
+
onMouseMove={handleMouseMove}
|
|
119
|
+
{...rest}
|
|
120
|
+
>
|
|
121
|
+
{inner}
|
|
122
|
+
</div>
|
|
123
|
+
)
|
|
124
|
+
}
|