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.
Files changed (250) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/dist/cache-7G6D532T.mjs +1 -0
  4. package/dist/chunk-A4HQPEPU.mjs +1 -0
  5. package/dist/chunk-BA5NH5HU.mjs +1 -0
  6. package/dist/chunk-BQCD3DWG.mjs +1 -0
  7. package/dist/chunk-H63UMKYF.mjs +1 -0
  8. package/dist/chunk-IWHRQHS7.mjs +1 -0
  9. package/dist/chunk-JZXLCA2E.mjs +1 -0
  10. package/dist/chunk-MFU7Q6WF.mjs +1 -0
  11. package/dist/chunk-QYPNX5UN.mjs +1 -0
  12. package/dist/chunk-XEAPSFMB.mjs +1 -0
  13. package/dist/client/components/mdx/index.d.mts +209 -0
  14. package/dist/client/components/mdx/index.d.ts +209 -0
  15. package/dist/client/components/mdx/index.js +1 -0
  16. package/dist/client/components/mdx/index.mjs +1 -0
  17. package/dist/client/hooks/index.d.mts +133 -0
  18. package/dist/client/hooks/index.d.ts +133 -0
  19. package/dist/client/hooks/index.js +1 -0
  20. package/dist/client/hooks/index.mjs +1 -0
  21. package/dist/client/index.d.mts +138 -298
  22. package/dist/client/index.d.ts +138 -298
  23. package/dist/client/index.js +1 -3630
  24. package/dist/client/index.mjs +1 -697
  25. package/dist/client/ssr.d.mts +7 -3
  26. package/dist/client/ssr.d.ts +7 -3
  27. package/dist/client/ssr.js +1 -2928
  28. package/dist/client/ssr.mjs +1 -33
  29. package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
  30. package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
  31. package/dist/node/index.d.mts +2 -4
  32. package/dist/node/index.d.ts +2 -4
  33. package/dist/node/index.js +31 -1161
  34. package/dist/node/index.mjs +31 -736
  35. package/dist/search-dialog-EB3N4TYM.mjs +1 -0
  36. package/dist/types-BuZWFT7r.d.ts +159 -0
  37. package/dist/types-CvT-SGbK.d.mts +159 -0
  38. package/dist/use-routes-5bAtAAYX.d.mts +30 -0
  39. package/dist/use-routes-BefRXY3v.d.ts +30 -0
  40. package/package.json +34 -12
  41. package/src/client/app/config-context.tsx +18 -0
  42. package/src/client/app/docs-layout.tsx +14 -0
  43. package/src/client/app/index.tsx +137 -262
  44. package/src/client/app/mdx-component.tsx +52 -0
  45. package/src/client/app/mdx-components-context.tsx +23 -0
  46. package/src/client/app/mdx-page.tsx +20 -0
  47. package/src/client/app/preload.tsx +38 -30
  48. package/src/client/app/router.tsx +30 -0
  49. package/src/client/app/scroll-handler.tsx +40 -0
  50. package/src/client/app/theme-context.tsx +75 -0
  51. package/src/client/components/default-layout.tsx +80 -0
  52. package/src/client/components/docs-layout.tsx +105 -0
  53. package/src/client/components/icons-dev.tsx +74 -0
  54. package/src/client/components/mdx/admonition.tsx +107 -0
  55. package/src/client/components/mdx/badge.tsx +41 -0
  56. package/src/client/components/mdx/button.tsx +35 -0
  57. package/src/client/components/mdx/card.tsx +124 -0
  58. package/src/client/components/mdx/code-block.tsx +119 -0
  59. package/src/client/components/mdx/component-preview.tsx +47 -0
  60. package/src/client/components/mdx/component-props.tsx +83 -0
  61. package/src/client/components/mdx/field.tsx +66 -0
  62. package/src/client/components/mdx/file-tree.tsx +287 -0
  63. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  64. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  65. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  66. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  67. package/src/client/components/mdx/image.tsx +23 -0
  68. package/src/client/components/mdx/index.ts +53 -0
  69. package/src/client/components/mdx/link.tsx +38 -0
  70. package/src/client/components/mdx/list.tsx +192 -0
  71. package/src/client/components/mdx/table.tsx +156 -0
  72. package/src/client/components/mdx/tabs.tsx +135 -0
  73. package/src/client/components/mdx/video.tsx +68 -0
  74. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  75. package/src/client/components/primitives/button-group.tsx +54 -0
  76. package/src/client/components/primitives/button.tsx +145 -0
  77. package/src/client/components/primitives/helpers/observer.ts +120 -0
  78. package/src/client/components/primitives/index.ts +17 -0
  79. package/src/client/components/primitives/link.tsx +122 -0
  80. package/src/client/components/primitives/menu.tsx +159 -0
  81. package/src/client/components/primitives/navbar.tsx +359 -0
  82. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  83. package/src/client/components/primitives/on-this-page.tsx +461 -0
  84. package/src/client/components/primitives/page-nav.tsx +87 -0
  85. package/src/client/components/primitives/popover.tsx +47 -0
  86. package/src/client/components/primitives/search-dialog.tsx +183 -0
  87. package/src/client/components/primitives/sidebar.tsx +154 -0
  88. package/src/client/components/primitives/tabs.tsx +90 -0
  89. package/src/client/components/primitives/tooltip.tsx +83 -0
  90. package/src/client/components/primitives/types.ts +11 -0
  91. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  92. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  93. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  94. package/src/client/components/ui-base/github-stars.tsx +27 -0
  95. package/src/client/components/ui-base/head.tsx +69 -0
  96. package/src/client/components/ui-base/loading.tsx +87 -0
  97. package/src/client/components/ui-base/navbar.tsx +138 -0
  98. package/src/client/components/ui-base/not-found.tsx +24 -0
  99. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  100. package/src/client/components/ui-base/page-nav.tsx +39 -0
  101. package/src/client/components/ui-base/powered-by.tsx +19 -0
  102. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  103. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  104. package/src/client/components/ui-base/sidebar.tsx +104 -0
  105. package/src/client/components/ui-base/tabs.tsx +65 -0
  106. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  107. package/src/client/hooks/index.ts +12 -0
  108. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  109. package/src/client/hooks/use-i18n.ts +84 -0
  110. package/src/client/hooks/use-localized-to.ts +95 -0
  111. package/src/client/hooks/use-location.ts +5 -0
  112. package/src/client/hooks/use-navbar.ts +60 -0
  113. package/src/client/hooks/use-onthispage.ts +23 -0
  114. package/src/client/hooks/use-page-nav.ts +22 -0
  115. package/src/client/hooks/use-routes.ts +72 -0
  116. package/src/client/hooks/use-search.ts +71 -0
  117. package/src/client/hooks/use-sidebar.ts +49 -0
  118. package/src/client/hooks/use-tabs.ts +43 -0
  119. package/src/client/hooks/use-version.ts +78 -0
  120. package/src/client/index.ts +55 -17
  121. package/src/client/integrations/codesandbox.ts +179 -0
  122. package/src/client/ssr.tsx +27 -16
  123. package/src/client/theme/neutral.css +360 -0
  124. package/src/client/types.ts +131 -27
  125. package/src/client/utils/cn.ts +6 -0
  126. package/src/client/utils/copy-clipboard.ts +22 -0
  127. package/src/client/utils/get-base-file-path.ts +21 -0
  128. package/src/client/utils/github.ts +121 -0
  129. package/src/client/utils/use-on-change.ts +15 -0
  130. package/src/client/virtual.d.ts +24 -0
  131. package/src/node/cache.ts +156 -156
  132. package/src/node/config.ts +159 -103
  133. package/src/node/index.ts +13 -13
  134. package/src/node/mdx.ts +213 -61
  135. package/src/node/plugin/entry.ts +29 -18
  136. package/src/node/plugin/html.ts +11 -11
  137. package/src/node/plugin/index.ts +161 -84
  138. package/src/node/plugin/types.ts +2 -4
  139. package/src/node/routes/cache.ts +6 -6
  140. package/src/node/routes/index.ts +206 -113
  141. package/src/node/routes/parser.ts +102 -82
  142. package/src/node/routes/sorter.ts +15 -15
  143. package/src/node/routes/types.ts +24 -24
  144. package/src/node/ssg/index.ts +73 -47
  145. package/src/node/ssg/meta.ts +4 -4
  146. package/src/node/ssg/options.ts +5 -5
  147. package/src/node/ssg/sitemap.ts +14 -14
  148. package/src/node/utils.ts +54 -31
  149. package/tsconfig.json +25 -20
  150. package/tsup.config.ts +23 -14
  151. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  152. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  153. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  154. package/dist/Video-KNTY5BNO.mjs +0 -6
  155. package/dist/cache-KNL5B4EE.mjs +0 -12
  156. package/dist/chunk-7SFUJWTB.mjs +0 -211
  157. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  158. package/dist/chunk-FMTOYQLO.mjs +0 -37
  159. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  160. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  161. package/dist/client/index.css +0 -2847
  162. package/dist/client/ssr.css +0 -2847
  163. package/dist/types-Dj-bfnC3.d.mts +0 -74
  164. package/dist/types-Dj-bfnC3.d.ts +0 -74
  165. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  166. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  167. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  168. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  169. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  170. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  171. package/src/client/theme/components/Playground/index.ts +0 -1
  172. package/src/client/theme/components/Playground/playground.css +0 -238
  173. package/src/client/theme/components/Video/Video.tsx +0 -84
  174. package/src/client/theme/components/Video/index.ts +0 -1
  175. package/src/client/theme/components/Video/video.css +0 -41
  176. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  177. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  178. package/src/client/theme/components/mdx/Button.tsx +0 -50
  179. package/src/client/theme/components/mdx/Card.tsx +0 -80
  180. package/src/client/theme/components/mdx/Field.tsx +0 -60
  181. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  182. package/src/client/theme/components/mdx/List.tsx +0 -57
  183. package/src/client/theme/components/mdx/Table.tsx +0 -151
  184. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  185. package/src/client/theme/components/mdx/index.ts +0 -27
  186. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  187. package/src/client/theme/icons/bun.tsx +0 -62
  188. package/src/client/theme/icons/deno.tsx +0 -20
  189. package/src/client/theme/icons/discord.tsx +0 -12
  190. package/src/client/theme/icons/github.tsx +0 -15
  191. package/src/client/theme/icons/npm.tsx +0 -13
  192. package/src/client/theme/icons/pnpm.tsx +0 -72
  193. package/src/client/theme/icons/twitter.tsx +0 -12
  194. package/src/client/theme/styles/markdown.css +0 -394
  195. package/src/client/theme/styles/variables.css +0 -175
  196. package/src/client/theme/styles.css +0 -39
  197. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  198. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  199. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  200. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  201. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  202. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  203. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  204. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  205. package/src/client/theme/ui/Footer/footer.css +0 -32
  206. package/src/client/theme/ui/Head/Head.tsx +0 -69
  207. package/src/client/theme/ui/Head/index.ts +0 -1
  208. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  209. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  210. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  211. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  212. package/src/client/theme/ui/Layout/base.css +0 -106
  213. package/src/client/theme/ui/Layout/index.ts +0 -2
  214. package/src/client/theme/ui/Layout/pagination.css +0 -72
  215. package/src/client/theme/ui/Layout/responsive.css +0 -47
  216. package/src/client/theme/ui/Link/Link.tsx +0 -392
  217. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  218. package/src/client/theme/ui/Link/index.ts +0 -2
  219. package/src/client/theme/ui/Link/link-preview.css +0 -48
  220. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  221. package/src/client/theme/ui/Loading/index.ts +0 -1
  222. package/src/client/theme/ui/Loading/loading.css +0 -30
  223. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  224. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  225. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  226. package/src/client/theme/ui/Navbar/index.ts +0 -2
  227. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  228. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  229. package/src/client/theme/ui/NotFound/index.ts +0 -1
  230. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  231. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  232. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  233. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  234. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  235. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  236. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  237. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  238. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  239. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  240. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  241. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  242. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  243. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  244. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  245. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  246. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  247. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  248. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  249. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  250. package/src/client/utils.ts +0 -49
@@ -0,0 +1,74 @@
1
+ import { useState, useMemo } from 'react'
2
+
3
+ interface SortConfig {
4
+ key: number
5
+ direction: 'asc' | 'desc'
6
+ }
7
+
8
+ interface UseTableProps {
9
+ data?: (string | React.ReactNode)[][]
10
+ sortable?: boolean
11
+ paginated?: boolean
12
+ pageSize?: number
13
+ }
14
+
15
+ export function useTable({
16
+ data,
17
+ sortable = false,
18
+ paginated = false,
19
+ pageSize = 10,
20
+ }: UseTableProps) {
21
+ const [sortConfig, setSortConfig] = useState<SortConfig | null>(null)
22
+ const [currentPage, setCurrentPage] = useState(1)
23
+
24
+ const processedData = useMemo(() => {
25
+ if (!data) return []
26
+ const items = [...data]
27
+
28
+ if (sortable && sortConfig !== null) {
29
+ items.sort((a, b) => {
30
+ const aVal = a[sortConfig.key]
31
+ const bVal = b[sortConfig.key]
32
+
33
+ const aStr = typeof aVal === 'string' ? aVal : ''
34
+ const bStr = typeof bVal === 'string' ? bVal : ''
35
+
36
+ if (aStr < bStr) return sortConfig.direction === 'asc' ? -1 : 1
37
+ if (aStr > bStr) return sortConfig.direction === 'asc' ? 1 : -1
38
+ return 0
39
+ })
40
+ }
41
+
42
+ return items
43
+ }, [data, sortConfig, sortable])
44
+
45
+ const totalPages = Math.ceil(processedData.length / pageSize)
46
+
47
+ const paginatedData = useMemo(() => {
48
+ if (!paginated) return processedData
49
+ const start = (currentPage - 1) * pageSize
50
+ return processedData.slice(start, start + pageSize)
51
+ }, [processedData, paginated, currentPage, pageSize])
52
+
53
+ const requestSort = (index: number) => {
54
+ if (!sortable) return
55
+ let direction: 'asc' | 'desc' = 'asc'
56
+ if (
57
+ sortConfig &&
58
+ sortConfig.key === index &&
59
+ sortConfig.direction === 'asc'
60
+ ) {
61
+ direction = 'desc'
62
+ }
63
+ setSortConfig({ key: index, direction })
64
+ }
65
+
66
+ return {
67
+ sortConfig,
68
+ currentPage,
69
+ setCurrentPage,
70
+ totalPages,
71
+ paginatedData,
72
+ requestSort,
73
+ }
74
+ }
@@ -0,0 +1,68 @@
1
+ import {
2
+ useState,
3
+ useRef,
4
+ useEffect,
5
+ useCallback,
6
+ type ReactElement,
7
+ type KeyboardEvent,
8
+ } from 'react'
9
+
10
+ interface UseTabsProps {
11
+ initialIndex?: number
12
+ tabs: ReactElement<any>[]
13
+ }
14
+
15
+ export function useTabs({ initialIndex = 0, tabs }: UseTabsProps) {
16
+ const defaultActive = tabs[initialIndex]?.props.disabled
17
+ ? tabs.findIndex((t) => !t.props.disabled)
18
+ : initialIndex
19
+
20
+ const [active, setActive] = useState(defaultActive === -1 ? 0 : defaultActive)
21
+ const tabRefs = useRef<(HTMLButtonElement | null)[]>([])
22
+ const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({
23
+ opacity: 0,
24
+ transform: 'translateX(0)',
25
+ width: 0,
26
+ })
27
+
28
+ // biome-ignore lint/correctness/useExhaustiveDependencies: updates when content changes
29
+ useEffect(() => {
30
+ const activeTab = tabRefs.current[active]
31
+ if (activeTab) {
32
+ setIndicatorStyle({
33
+ opacity: 1,
34
+ width: activeTab.offsetWidth,
35
+ transform: `translateX(${activeTab.offsetLeft}px)`,
36
+ })
37
+ }
38
+ }, [active, tabs])
39
+
40
+ const handleKeyDown = useCallback(
41
+ (e: KeyboardEvent<HTMLDivElement>) => {
42
+ let direction = 0
43
+ if (e.key === 'ArrowRight') direction = 1
44
+ else if (e.key === 'ArrowLeft') direction = -1
45
+
46
+ if (direction !== 0) {
47
+ let nextIndex = (active + direction + tabs.length) % tabs.length
48
+ while (tabs[nextIndex].props.disabled && nextIndex !== active) {
49
+ nextIndex = (nextIndex + direction + tabs.length) % tabs.length
50
+ }
51
+
52
+ if (nextIndex !== active && !tabs[nextIndex].props.disabled) {
53
+ setActive(nextIndex)
54
+ tabRefs.current[nextIndex]?.focus()
55
+ }
56
+ }
57
+ },
58
+ [active, tabs],
59
+ )
60
+
61
+ return {
62
+ active,
63
+ setActive,
64
+ tabRefs,
65
+ indicatorStyle,
66
+ handleKeyDown,
67
+ }
68
+ }
@@ -0,0 +1,23 @@
1
+ import type { ImgHTMLAttributes } from 'react'
2
+ import { useTheme } from '@client/app/theme-context'
3
+
4
+ export interface ImageProps extends ImgHTMLAttributes<HTMLImageElement> {
5
+ src: string
6
+ darkSrc?: string
7
+ theme?: 'light' | 'dark'
8
+ }
9
+
10
+ /**
11
+ * A themed Image component for Boltdocs.
12
+ * It supports rendering based on the current active theme.
13
+ */
14
+ export function Image({ src, alt, theme: imageTheme, ...props }: ImageProps) {
15
+ const { theme: currentTheme } = useTheme()
16
+
17
+ // If a specific theme is required for this image, only render if it matches
18
+ if (imageTheme && imageTheme !== currentTheme) {
19
+ return null
20
+ }
21
+
22
+ return <img src={src} alt={alt || ''} {...props} />
23
+ }
@@ -0,0 +1,53 @@
1
+ export { Button } from './button'
2
+ export type { ButtonProps } from './button'
3
+
4
+ export { CodeBlock } from './code-block'
5
+
6
+ export { Tabs, Tab } from './tabs'
7
+ export type { TabsProps, TabProps } from './tabs'
8
+
9
+ export { Video } from './video'
10
+
11
+ export { Badge } from './badge'
12
+ export type { BadgeProps } from './badge'
13
+
14
+ export { Card, Cards } from './card'
15
+ export type { CardProps, CardsProps } from './card'
16
+
17
+ export {
18
+ Admonition,
19
+ Note,
20
+ Tip,
21
+ Warning,
22
+ Danger,
23
+ InfoBox,
24
+ Important,
25
+ Caution,
26
+ } from './admonition'
27
+ export type { AdmonitionProps } from './admonition'
28
+
29
+ export { List } from './list'
30
+ export type { ListProps } from './list'
31
+
32
+ export { FileTree } from './file-tree'
33
+ export type { FileTreeProps } from './file-tree'
34
+
35
+ export { Table } from './table'
36
+ export type { TableProps } from './table'
37
+
38
+ export { Field } from './field'
39
+ export type { FieldProps } from './field'
40
+
41
+ export { Link } from './link'
42
+ export type { LinkProps } from './link'
43
+
44
+ export { Image } from './image'
45
+ export type { ImageProps } from './image'
46
+
47
+ export { ComponentProps } from './component-props'
48
+ export type { ComponentPropsProps } from './component-props'
49
+
50
+ export { ComponentPreview } from './component-preview'
51
+ export type { ComponentPreviewProps } from './component-preview'
52
+ export { CopyMarkdown } from '../ui-base/copy-markdown'
53
+ export type { CopyMarkdownProps } from '../ui-base/copy-markdown'
@@ -0,0 +1,38 @@
1
+ import {
2
+ Link as LinkPrimitive,
3
+ type LinkProps as LinkPrimitiveProps,
4
+ } from '@components/primitives/link'
5
+ import { cn } from '@client/utils/cn'
6
+
7
+ export type LinkProps = LinkPrimitiveProps & {
8
+ to: string
9
+ children?: React.ReactNode
10
+ }
11
+
12
+ /**
13
+ * A premium Link component for Boltdocs that handles internal and external routing.
14
+ */
15
+ export function Link({ to, children, className = '', ...props }: LinkProps) {
16
+ const isExternal =
17
+ to &&
18
+ (to.startsWith('http://') ||
19
+ to.startsWith('https://') ||
20
+ to.startsWith('//'))
21
+
22
+ const combinedClassName = cn(
23
+ 'text-blue-600 hover:text-blue-800 hover:underline cursor-pointer',
24
+ className,
25
+ )
26
+
27
+ return (
28
+ <LinkPrimitive
29
+ href={to}
30
+ className={combinedClassName}
31
+ target={isExternal ? '_blank' : undefined}
32
+ rel={isExternal ? 'noopener noreferrer' : undefined}
33
+ {...props}
34
+ >
35
+ {children}
36
+ </LinkPrimitive>
37
+ )
38
+ }
@@ -0,0 +1,192 @@
1
+ import {
2
+ Children,
3
+ isValidElement,
4
+ type ReactNode,
5
+ type ReactElement,
6
+ type ComponentPropsWithoutRef,
7
+ } from 'react'
8
+ import { Check, ChevronRight, Circle } from 'lucide-react'
9
+ import { cn } from '@client/utils/cn'
10
+ import { cva, type VariantProps } from 'class-variance-authority'
11
+
12
+ const listVariants = cva('my-6 transition-all duration-200', {
13
+ variants: {
14
+ variant: {
15
+ default: 'list-disc pl-5 text-text-muted marker:text-primary-500/50',
16
+ number:
17
+ 'list-decimal pl-5 text-text-muted marker:text-primary-500/50 marker:font-bold',
18
+ checked: 'list-none p-0',
19
+ arrow: 'list-none p-0',
20
+ bubble: 'list-none p-0',
21
+ },
22
+ cols: {
23
+ 1: 'grid-cols-1',
24
+ 2: 'grid-cols-1 sm:grid-cols-2',
25
+ 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
26
+ 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
27
+ },
28
+ isGrid: {
29
+ true: 'grid gap-x-8 gap-y-3',
30
+ false: 'space-y-2',
31
+ },
32
+ dense: {
33
+ true: 'space-y-1',
34
+ false: 'space-y-2',
35
+ },
36
+ },
37
+ compoundVariants: [
38
+ {
39
+ variant: 'default',
40
+ dense: true,
41
+ className: 'space-y-0.5',
42
+ },
43
+ ],
44
+ defaultVariants: {
45
+ variant: 'default',
46
+ cols: 1,
47
+ isGrid: false,
48
+ dense: false,
49
+ },
50
+ })
51
+
52
+ const itemVariants = cva(
53
+ 'group flex items-start gap-3 text-sm leading-relaxed transition-all duration-200',
54
+ {
55
+ variants: {
56
+ variant: {
57
+ default: '',
58
+ number: '',
59
+ checked: 'hover:translate-x-0.5',
60
+ arrow: 'hover:translate-x-0.5',
61
+ bubble: 'hover:translate-x-0.5',
62
+ },
63
+ dense: {
64
+ true: 'py-0',
65
+ false: 'py-0.5',
66
+ },
67
+ },
68
+ defaultVariants: {
69
+ variant: 'default',
70
+ dense: false,
71
+ },
72
+ },
73
+ )
74
+
75
+ const iconContainerVariants = cva(
76
+ 'mt-1 shrink-0 flex items-center justify-center transition-transform group-hover:scale-110',
77
+ {
78
+ variants: {
79
+ variant: {
80
+ bubble:
81
+ 'h-5 w-5 rounded-full bg-primary-500/10 text-primary-500 text-[10px] font-bold',
82
+ default: '',
83
+ },
84
+ },
85
+ defaultVariants: {
86
+ variant: 'default',
87
+ },
88
+ },
89
+ )
90
+
91
+ type ListVariantProps = VariantProps<typeof listVariants>
92
+
93
+ export interface ListProps
94
+ extends ComponentPropsWithoutRef<'ul'>,
95
+ Omit<ListVariantProps, 'variant'> {
96
+ variant?: 'checked' | 'arrow' | 'default' | 'bubble' | 'number'
97
+ children: ReactNode
98
+ }
99
+
100
+ interface ListItemProps extends VariantProps<typeof itemVariants> {
101
+ icon?: ReactNode
102
+ children: ReactNode
103
+ }
104
+
105
+ function ListItem({ icon, children, variant, dense }: ListItemProps) {
106
+ return (
107
+ <li className={cn(itemVariants({ variant, dense }))}>
108
+ {icon && (
109
+ <span
110
+ className={cn(
111
+ iconContainerVariants({
112
+ variant: variant === 'bubble' ? 'bubble' : 'default',
113
+ }),
114
+ )}
115
+ >
116
+ {icon}
117
+ </span>
118
+ )}
119
+ <div className="flex-1 text-text-muted group-hover:text-text-main transition-colors">
120
+ {children}
121
+ </div>
122
+ </li>
123
+ )
124
+ }
125
+
126
+ const ICON_MAP: Record<string, (cls?: string) => ReactNode> = {
127
+ checked: (cls) => (
128
+ <Check size={14} className={cn('text-emerald-500 shrink-0', cls)} />
129
+ ),
130
+ arrow: (cls) => (
131
+ <ChevronRight size={14} className={cn('text-primary-400 shrink-0', cls)} />
132
+ ),
133
+ bubble: (cls) => (
134
+ <Circle
135
+ size={6}
136
+ fill="currentColor"
137
+ className={cn('text-primary-500 shrink-0', cls)}
138
+ />
139
+ ),
140
+ default: () => null,
141
+ number: () => null,
142
+ }
143
+
144
+ export function List({
145
+ variant = 'default',
146
+ cols = 1,
147
+ dense = false,
148
+ children,
149
+ className,
150
+ ...props
151
+ }: ListProps) {
152
+ const isGrid = cols !== undefined && Number(cols) > 1
153
+ const renderIcon = ICON_MAP[variant]
154
+ const containerClasses = listVariants({
155
+ variant,
156
+ cols,
157
+ dense,
158
+ isGrid,
159
+ className,
160
+ })
161
+
162
+ const Component = variant === 'number' ? 'ol' : 'ul'
163
+
164
+ // Handling raw MDX siblings (nested logic)
165
+ if (variant === 'default' || variant === 'number') {
166
+ return (
167
+ <Component className={containerClasses} {...props}>
168
+ {children}
169
+ </Component>
170
+ )
171
+ }
172
+
173
+ return (
174
+ <ul className={containerClasses} {...props}>
175
+ {Children.map(children, (child) => {
176
+ if (!isValidElement(child)) return child
177
+
178
+ const element = child as ReactElement<{ children?: ReactNode }>
179
+ const content =
180
+ element.type === 'li'
181
+ ? element.props.children
182
+ : element.props.children || child
183
+
184
+ return (
185
+ <ListItem icon={renderIcon()} variant={variant} dense={dense}>
186
+ {content}
187
+ </ListItem>
188
+ )
189
+ })}
190
+ </ul>
191
+ )
192
+ }
@@ -0,0 +1,156 @@
1
+ import * as RAC from 'react-aria-components'
2
+ import { useTable } from './hooks/useTable'
3
+ import {
4
+ ChevronUp,
5
+ ChevronDown,
6
+ ChevronLeft,
7
+ ChevronRight,
8
+ ChevronsLeft,
9
+ ChevronsRight,
10
+ } from 'lucide-react'
11
+ import { cn } from '@client/utils/cn'
12
+
13
+ export interface TableProps {
14
+ headers?: string[]
15
+ data?: (string | React.ReactNode)[][]
16
+ children?: React.ReactNode
17
+ className?: string
18
+ sortable?: boolean
19
+ paginated?: boolean
20
+ pageSize?: number
21
+ }
22
+
23
+ export function Table({
24
+ headers,
25
+ data,
26
+ children,
27
+ className = '',
28
+ sortable = false,
29
+ paginated = false,
30
+ pageSize = 10,
31
+ }: TableProps) {
32
+ const {
33
+ sortConfig,
34
+ currentPage,
35
+ setCurrentPage,
36
+ totalPages,
37
+ paginatedData,
38
+ requestSort,
39
+ } = useTable({
40
+ data,
41
+ sortable,
42
+ paginated,
43
+ pageSize,
44
+ })
45
+
46
+ const renderSortIcon = (index: number) => {
47
+ if (!sortable) return null
48
+ if (sortConfig?.key !== index)
49
+ return <ChevronDown size={14} className="ml-1 opacity-30" />
50
+ return sortConfig.direction === 'asc' ? (
51
+ <ChevronUp size={14} className="ml-1 text-primary-400" />
52
+ ) : (
53
+ <ChevronDown size={14} className="ml-1 text-primary-400" />
54
+ )
55
+ }
56
+
57
+ const tableContent = children ? (
58
+ children
59
+ ) : (
60
+ <>
61
+ {headers && (
62
+ <thead>
63
+ <tr>
64
+ {headers.map((header, i) => (
65
+ <th
66
+ key={i}
67
+ onClick={() => requestSort(i)}
68
+ className={cn(
69
+ 'text-left px-3 py-2.5 border-b-2 border-border-subtle text-text-main font-semibold text-sm',
70
+ sortable &&
71
+ 'cursor-pointer select-none hover:text-primary-400 transition-colors',
72
+ )}
73
+ >
74
+ <div className="flex items-center">
75
+ {header}
76
+ {renderSortIcon(i)}
77
+ </div>
78
+ </th>
79
+ ))}
80
+ </tr>
81
+ </thead>
82
+ )}
83
+ {paginatedData && (
84
+ <tbody>
85
+ {paginatedData.map((row, i) => (
86
+ <tr key={i} className="transition-colors hover:bg-bg-surface">
87
+ {row.map((cell, j) => (
88
+ <td
89
+ key={j}
90
+ className="px-3 py-2 border-b border-border-subtle text-sm text-text-muted"
91
+ >
92
+ {cell}
93
+ </td>
94
+ ))}
95
+ </tr>
96
+ ))}
97
+ </tbody>
98
+ )}
99
+ </>
100
+ )
101
+
102
+ return (
103
+ <div
104
+ className={cn(
105
+ 'my-6 rounded-lg border border-border-subtle overflow-hidden',
106
+ className,
107
+ )}
108
+ >
109
+ <div className="overflow-x-auto">
110
+ <table className="w-full border-collapse text-sm">{tableContent}</table>
111
+ </div>
112
+
113
+ {paginated && totalPages > 1 && (
114
+ <div className="flex items-center justify-between border-t border-border-subtle px-4 py-3">
115
+ <span className="text-xs text-text-muted">
116
+ Page {currentPage} of {totalPages}
117
+ </span>
118
+ <div className="flex items-center gap-1">
119
+ <RAC.Button
120
+ onPress={() => setCurrentPage(1)}
121
+ isDisabled={currentPage === 1}
122
+ className="grid place-items-center h-7 w-7 rounded-md text-text-muted outline-none transition-colors hover:bg-bg-surface disabled:opacity-30 disabled:pointer-events-none cursor-pointer"
123
+ >
124
+ <ChevronsLeft size={16} />
125
+ </RAC.Button>
126
+ <RAC.Button
127
+ onPress={() =>
128
+ setCurrentPage((prev: number) => Math.max(prev - 1, 1))
129
+ }
130
+ isDisabled={currentPage === 1}
131
+ className="grid place-items-center h-7 w-7 rounded-md text-text-muted outline-none transition-colors hover:bg-bg-surface disabled:opacity-30 disabled:pointer-events-none cursor-pointer"
132
+ >
133
+ <ChevronLeft size={16} />
134
+ </RAC.Button>
135
+ <RAC.Button
136
+ onPress={() =>
137
+ setCurrentPage((prev: number) => Math.min(prev + 1, totalPages))
138
+ }
139
+ isDisabled={currentPage === totalPages}
140
+ className="grid place-items-center h-7 w-7 rounded-md text-text-muted outline-none transition-colors hover:bg-bg-surface disabled:opacity-30 disabled:pointer-events-none cursor-pointer"
141
+ >
142
+ <ChevronRight size={16} />
143
+ </RAC.Button>
144
+ <RAC.Button
145
+ onPress={() => setCurrentPage(totalPages)}
146
+ isDisabled={currentPage === totalPages}
147
+ className="grid place-items-center h-7 w-7 rounded-md text-text-muted outline-none transition-colors hover:bg-bg-surface disabled:opacity-30 disabled:pointer-events-none cursor-pointer"
148
+ >
149
+ <ChevronsRight size={16} />
150
+ </RAC.Button>
151
+ </div>
152
+ </div>
153
+ )}
154
+ </div>
155
+ )
156
+ }