@lexibase/rdx-ui 1.1.0 → 3.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/README.md +4 -5
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/package.json +10 -6
- package/src/components/button.tsx +22 -8
- package/src/components/code-block.tsx +274 -43
- package/src/components/copy-code-button.tsx +1 -76
- package/src/components/dialog.tsx +124 -34
- package/src/components/docs-search-dialog.tsx +29 -14
- package/src/components/docs-search.tsx +9 -5
- package/src/components/docs-sidebar.tsx +9 -7
- package/src/components/docs-toc.tsx +6 -3
- package/src/components/footer-credit.tsx +8 -4
- package/src/components/home.tsx +3 -3
- package/src/components/kbd.tsx +29 -0
- package/src/components/loading-button.tsx +5 -1
- package/src/components/mdx-code-block-view.tsx +77 -0
- package/src/components/mdx-code-block.tsx +108 -0
- package/src/components/mdx-components.tsx +105 -47
- package/src/components/mdx-image.tsx +73 -0
- package/src/components/mode-toggle.tsx +5 -2
- package/src/components/navbar.tsx +13 -9
- package/src/components/not-found.tsx +36 -0
- package/src/components/table.tsx +122 -0
- package/src/components/tabs.tsx +17 -9
- package/src/components/version-dropdown.tsx +9 -2
- package/src/components/version-notice.tsx +7 -5
- package/src/hooks/use-highlighted-code.ts +75 -0
- package/src/lib/highlighter.tsx +124 -0
- package/src/lib/i18n.ts +156 -0
- package/src/providers/locale-provider.tsx +29 -0
- package/src/styles/globals.css +1 -0
- package/src/styles/shadcn.css +650 -0
- package/src/components/button-link.tsx +0 -27
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
cloneElement,
|
|
4
|
+
isValidElement,
|
|
5
|
+
type ComponentProps,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from 'react'
|
|
8
|
+
|
|
9
|
+
import { getMessages } from '../lib/i18n'
|
|
10
|
+
import { CodeBlockLine } from './code-block'
|
|
11
|
+
import { MdxCodeBlockView } from './mdx-code-block-view'
|
|
12
|
+
|
|
13
|
+
function textOf(children: ReactNode): string {
|
|
14
|
+
return Children.toArray(children)
|
|
15
|
+
.map((child) =>
|
|
16
|
+
typeof child === 'string' || typeof child === 'number'
|
|
17
|
+
? String(child)
|
|
18
|
+
: isValidElement<{ children?: ReactNode }>(child)
|
|
19
|
+
? textOf(child.props.children)
|
|
20
|
+
: ''
|
|
21
|
+
)
|
|
22
|
+
.join('')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Shadcn draws diff markers itself. Strip the original marker from highlighted
|
|
26
|
+
// tokens while keeping the original patch as the clipboard value.
|
|
27
|
+
function withoutMarker(children: ReactNode): ReactNode {
|
|
28
|
+
let remaining = 1
|
|
29
|
+
function strip(nodes: ReactNode): ReactNode {
|
|
30
|
+
return Children.map(nodes, (node) => {
|
|
31
|
+
if (!remaining) return node
|
|
32
|
+
if (typeof node === 'string') {
|
|
33
|
+
const value = node.slice(remaining)
|
|
34
|
+
remaining = Math.max(0, remaining - node.length)
|
|
35
|
+
return value
|
|
36
|
+
}
|
|
37
|
+
return isValidElement<{ children?: ReactNode }>(node)
|
|
38
|
+
? cloneElement(node, {}, strip(node.props.children))
|
|
39
|
+
: node
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
return strip(children)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Translate Markdown fences to the complete shadcn Code Block composition. */
|
|
46
|
+
export function MdxCodeBlock({
|
|
47
|
+
lang,
|
|
48
|
+
children,
|
|
49
|
+
'data-rdx-code-mode': mode,
|
|
50
|
+
...props
|
|
51
|
+
}: ComponentProps<'pre'> & { 'data-rdx-code-mode'?: 'folder' | 'diff' }) {
|
|
52
|
+
const messages = getMessages(lang)
|
|
53
|
+
const code = Children.toArray(children).find((child) => isValidElement(child))
|
|
54
|
+
const codeProps = isValidElement<{
|
|
55
|
+
className?: string
|
|
56
|
+
children?: ReactNode
|
|
57
|
+
}>(code)
|
|
58
|
+
? code.props
|
|
59
|
+
: undefined
|
|
60
|
+
const language = codeProps?.className?.match(/(?:^|\s)language-([^\s]+)/)?.[1]
|
|
61
|
+
const value = textOf(codeProps?.children ?? children)
|
|
62
|
+
const label =
|
|
63
|
+
mode === 'folder' ? messages.folderTree : (language ?? messages.code)
|
|
64
|
+
const content =
|
|
65
|
+
mode === 'diff'
|
|
66
|
+
? Children.map(codeProps?.children, (node) => {
|
|
67
|
+
if (
|
|
68
|
+
!isValidElement<{
|
|
69
|
+
children?: ReactNode
|
|
70
|
+
'data-rdx-line-variant'?: string
|
|
71
|
+
}>(node)
|
|
72
|
+
)
|
|
73
|
+
return node
|
|
74
|
+
const variant = node.props['data-rdx-line-variant']
|
|
75
|
+
if (!variant) return node
|
|
76
|
+
return (
|
|
77
|
+
<CodeBlockLine
|
|
78
|
+
variant={
|
|
79
|
+
variant === 'added' || variant === 'removed'
|
|
80
|
+
? variant
|
|
81
|
+
: variant === 'hunk'
|
|
82
|
+
? 'highlighted'
|
|
83
|
+
: 'default'
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
{variant === 'added' || variant === 'removed'
|
|
87
|
+
? withoutMarker(node.props.children)
|
|
88
|
+
: node.props.children}
|
|
89
|
+
</CodeBlockLine>
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
: (codeProps?.children ?? children)
|
|
93
|
+
return (
|
|
94
|
+
<MdxCodeBlockView
|
|
95
|
+
label={label}
|
|
96
|
+
mode={mode}
|
|
97
|
+
value={value}
|
|
98
|
+
preProps={{
|
|
99
|
+
...props,
|
|
100
|
+
className: [props.className, language && `language-${language}`]
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.join(' '),
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{content}
|
|
106
|
+
</MdxCodeBlockView>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getMessages } from '../lib/i18n'
|
|
2
|
+
import {
|
|
3
|
+
Children,
|
|
4
|
+
cloneElement,
|
|
5
|
+
isValidElement,
|
|
6
|
+
type ComponentProps,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from 'react'
|
|
2
9
|
|
|
3
10
|
import { cn } from '../lib/utils'
|
|
11
|
+
import { MdxImage } from './mdx-image'
|
|
4
12
|
import {
|
|
5
13
|
Accordion,
|
|
6
14
|
AccordionContent,
|
|
@@ -9,10 +17,59 @@ import {
|
|
|
9
17
|
} from './accordion'
|
|
10
18
|
import { Blockquote } from './blockquote'
|
|
11
19
|
import { Callout } from './callout'
|
|
12
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
CodeBlock,
|
|
22
|
+
CodeBlockActions,
|
|
23
|
+
CodeBlockContent,
|
|
24
|
+
CodeBlockCopyButton,
|
|
25
|
+
CodeBlockFooter,
|
|
26
|
+
CodeBlockHeader,
|
|
27
|
+
CodeBlockLine,
|
|
28
|
+
CodeBlockTitle,
|
|
29
|
+
} from './code-block'
|
|
30
|
+
import { MdxCodeBlock } from './mdx-code-block'
|
|
31
|
+
import { Kbd, KbdGroup } from './kbd'
|
|
32
|
+
import {
|
|
33
|
+
Table,
|
|
34
|
+
TableBody,
|
|
35
|
+
TableCaption,
|
|
36
|
+
TableCell,
|
|
37
|
+
TableFooter,
|
|
38
|
+
TableHead,
|
|
39
|
+
TableHeader,
|
|
40
|
+
TableRow,
|
|
41
|
+
} from './table'
|
|
13
42
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from './tabs'
|
|
14
43
|
import { Separator } from '@lexibase/rdx-ui/components/separator'
|
|
15
44
|
|
|
45
|
+
function linkedImages(children: ReactNode): ReactNode {
|
|
46
|
+
return Children.map(children, (child) => {
|
|
47
|
+
if (!isValidElement<{ children?: ReactNode; zoom?: boolean }>(child))
|
|
48
|
+
return child
|
|
49
|
+
if (child.type === MdxImage) return cloneElement(child, { zoom: false })
|
|
50
|
+
return child.props.children
|
|
51
|
+
? cloneElement(child, {}, linkedImages(child.props.children))
|
|
52
|
+
: child
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function MdxTable({ lang, className, ...props }: ComponentProps<'table'>) {
|
|
57
|
+
return (
|
|
58
|
+
<Table
|
|
59
|
+
containerProps={{
|
|
60
|
+
'data-slot': 'mdx-table-scroll',
|
|
61
|
+
role: 'region',
|
|
62
|
+
'aria-label': getMessages(lang).table,
|
|
63
|
+
tabIndex: 0,
|
|
64
|
+
className:
|
|
65
|
+
'my-6 min-w-0 max-w-full focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',
|
|
66
|
+
}}
|
|
67
|
+
className={className}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
16
73
|
export const mdxComponents = {
|
|
17
74
|
h1: ({ className, ...props }: ComponentProps<'h1'>) => (
|
|
18
75
|
<h1 className={cn('text-4xl font-bold my-6', className)} {...props} />
|
|
@@ -47,14 +104,16 @@ export const mdxComponents = {
|
|
|
47
104
|
{...props}
|
|
48
105
|
/>
|
|
49
106
|
),
|
|
50
|
-
a: ({ className, ...props }: ComponentProps<'a'>) => (
|
|
107
|
+
a: ({ className, children, ...props }: ComponentProps<'a'>) => (
|
|
51
108
|
<a
|
|
52
109
|
className={cn('underline text-chart-1 text-blue-500', className)}
|
|
53
110
|
{...props}
|
|
54
|
-
|
|
111
|
+
>
|
|
112
|
+
{linkedImages(children)}
|
|
113
|
+
</a>
|
|
55
114
|
),
|
|
56
115
|
blockquote: Blockquote,
|
|
57
|
-
pre:
|
|
116
|
+
pre: MdxCodeBlock,
|
|
58
117
|
code: ({ className, ...props }: ComponentProps<'code'>) => (
|
|
59
118
|
<code
|
|
60
119
|
className={cn(
|
|
@@ -77,49 +136,35 @@ export const mdxComponents = {
|
|
|
77
136
|
hr: ({ className, ...props }: ComponentProps<'hr'>) => (
|
|
78
137
|
<hr className={cn('my-8 border-border', className)} {...props} />
|
|
79
138
|
),
|
|
80
|
-
img:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
table: ({ className, ...props }: ComponentProps<'table'>) => (
|
|
88
|
-
<div
|
|
89
|
-
data-slot="mdx-table-scroll"
|
|
90
|
-
role="region"
|
|
91
|
-
aria-label="Table"
|
|
92
|
-
tabIndex={0}
|
|
93
|
-
className="my-6 min-w-0 max-w-full overflow-x-auto rounded-lg border border-border bg-background focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
94
|
-
>
|
|
95
|
-
<table
|
|
96
|
-
className={cn(
|
|
97
|
-
'w-full border-separate border-spacing-0 text-sm [&_tbody>tr]:transition-colors motion-reduce:[&_tbody>tr]:transition-none [&_tbody>tr:nth-child(even)]:bg-muted/20 [&_tbody>tr:hover]:bg-muted/40 [&_tbody>tr:last-child>td]:border-b-0',
|
|
98
|
-
className
|
|
99
|
-
)}
|
|
100
|
-
{...props}
|
|
101
|
-
/>
|
|
102
|
-
</div>
|
|
103
|
-
),
|
|
139
|
+
img: MdxImage,
|
|
140
|
+
table: MdxTable,
|
|
141
|
+
thead: TableHeader,
|
|
142
|
+
tbody: TableBody,
|
|
143
|
+
tfoot: TableFooter,
|
|
144
|
+
tr: TableRow,
|
|
145
|
+
caption: TableCaption,
|
|
104
146
|
th: ({ className, scope = 'col', ...props }: ComponentProps<'th'>) => (
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
147
|
+
<TableHead scope={scope} className={className} {...props} />
|
|
148
|
+
),
|
|
149
|
+
td: TableCell,
|
|
150
|
+
CodeBlock,
|
|
151
|
+
CodeBlockActions,
|
|
152
|
+
CodeBlockContent,
|
|
153
|
+
CodeBlockCopyButton,
|
|
154
|
+
CodeBlockFooter,
|
|
155
|
+
CodeBlockHeader,
|
|
156
|
+
CodeBlockLine,
|
|
157
|
+
CodeBlockTitle,
|
|
158
|
+
Kbd,
|
|
159
|
+
KbdGroup,
|
|
160
|
+
Table,
|
|
161
|
+
TableHeader,
|
|
162
|
+
TableBody,
|
|
163
|
+
TableFooter,
|
|
164
|
+
TableRow,
|
|
165
|
+
TableHead,
|
|
166
|
+
TableCell,
|
|
167
|
+
TableCaption,
|
|
123
168
|
Callout,
|
|
124
169
|
Accordion,
|
|
125
170
|
AccordionItem,
|
|
@@ -140,3 +185,16 @@ export const mdxComponents = {
|
|
|
140
185
|
/>
|
|
141
186
|
),
|
|
142
187
|
}
|
|
188
|
+
|
|
189
|
+
/** Bind server-rendered MDX labels to the same locale as interactive components. */
|
|
190
|
+
export function getMdxComponents(lang?: string) {
|
|
191
|
+
return {
|
|
192
|
+
...mdxComponents,
|
|
193
|
+
pre: (props: ComponentProps<'pre'>) => (
|
|
194
|
+
<MdxCodeBlock {...props} lang={lang} />
|
|
195
|
+
),
|
|
196
|
+
table: (props: ComponentProps<'table'>) => (
|
|
197
|
+
<MdxTable {...props} lang={lang} />
|
|
198
|
+
),
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from 'react'
|
|
4
|
+
|
|
5
|
+
import { formatMessage } from '../lib/i18n'
|
|
6
|
+
import { cn } from '../lib/utils'
|
|
7
|
+
import { useMessages } from '../providers/locale-provider'
|
|
8
|
+
import { Button } from './button'
|
|
9
|
+
import {
|
|
10
|
+
Dialog,
|
|
11
|
+
DialogContent,
|
|
12
|
+
DialogDescription,
|
|
13
|
+
DialogTitle,
|
|
14
|
+
DialogTrigger,
|
|
15
|
+
} from './dialog'
|
|
16
|
+
|
|
17
|
+
/** Markdown images keep their normal appearance and use shadcn Dialog to expand. */
|
|
18
|
+
export function MdxImage({
|
|
19
|
+
className,
|
|
20
|
+
alt = '',
|
|
21
|
+
zoom = true,
|
|
22
|
+
...props
|
|
23
|
+
}: ComponentProps<'img'> & { zoom?: boolean }) {
|
|
24
|
+
const messages = useMessages()
|
|
25
|
+
const image = (
|
|
26
|
+
<img
|
|
27
|
+
{...props}
|
|
28
|
+
alt={alt}
|
|
29
|
+
className={cn('max-w-full h-auto rounded-md my-4', className)}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
// Decorative images and images inside links retain their original semantics.
|
|
33
|
+
if (!zoom || !alt.trim() || !props.src) return image
|
|
34
|
+
return (
|
|
35
|
+
<Dialog>
|
|
36
|
+
<DialogTrigger
|
|
37
|
+
data-rdx-image-trigger
|
|
38
|
+
aria-label={formatMessage(messages.enlargeImage, { alt })}
|
|
39
|
+
title={formatMessage(messages.enlargeImage, { alt })}
|
|
40
|
+
render={
|
|
41
|
+
<Button
|
|
42
|
+
variant="ghost"
|
|
43
|
+
className="h-auto max-w-full cursor-zoom-in p-0 align-middle hover:bg-transparent dark:hover:bg-transparent"
|
|
44
|
+
/>
|
|
45
|
+
}
|
|
46
|
+
>
|
|
47
|
+
{image}
|
|
48
|
+
</DialogTrigger>
|
|
49
|
+
<DialogContent
|
|
50
|
+
data-rdx-image-dialog
|
|
51
|
+
closeLabel={messages.closeImage}
|
|
52
|
+
overlayClassName="bg-black/70"
|
|
53
|
+
className="flex max-h-[calc(100dvh-2rem)] w-max max-w-[calc(100vw-2rem)] flex-col gap-0 p-2 sm:max-w-[calc(100vw-4rem)] [&>[data-slot=dialog-close]]:bg-popover/90"
|
|
54
|
+
>
|
|
55
|
+
<DialogTitle className="sr-only">{alt}</DialogTitle>
|
|
56
|
+
<DialogDescription className="sr-only">
|
|
57
|
+
{messages.imagePreviewDescription}
|
|
58
|
+
</DialogDescription>
|
|
59
|
+
<img
|
|
60
|
+
src={props.src}
|
|
61
|
+
srcSet={props.srcSet}
|
|
62
|
+
sizes={props.srcSet ? 'calc(100vw - 4rem)' : undefined}
|
|
63
|
+
crossOrigin={props.crossOrigin}
|
|
64
|
+
referrerPolicy={props.referrerPolicy}
|
|
65
|
+
alt={alt}
|
|
66
|
+
loading="eager"
|
|
67
|
+
decoding="async"
|
|
68
|
+
className="h-auto max-h-[calc(100dvh-4rem)] w-auto max-w-full self-center rounded-md object-contain"
|
|
69
|
+
/>
|
|
70
|
+
</DialogContent>
|
|
71
|
+
</Dialog>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { useMessages } from '../providers/locale-provider'
|
|
4
|
+
|
|
3
5
|
import { SunMoon } from 'lucide-react'
|
|
4
6
|
import { useTheme } from 'next-themes'
|
|
5
7
|
import { Button } from '@lexibase/rdx-ui/components/button'
|
|
6
8
|
|
|
7
9
|
export function ModeToggle() {
|
|
10
|
+
const messages = useMessages()
|
|
8
11
|
const { theme, setTheme } = useTheme()
|
|
9
12
|
|
|
10
13
|
const toggleTheme = () => {
|
|
@@ -17,10 +20,10 @@ export function ModeToggle() {
|
|
|
17
20
|
size="icon"
|
|
18
21
|
variant="ghost"
|
|
19
22
|
onClick={toggleTheme}
|
|
20
|
-
aria-label=
|
|
23
|
+
aria-label={messages.modeToggle}
|
|
21
24
|
>
|
|
22
25
|
<SunMoon className="h-[1.2rem] w-[1.2rem]" />
|
|
23
|
-
<span className="sr-only">
|
|
26
|
+
<span className="sr-only">{messages.modeToggle}</span>
|
|
24
27
|
</Button>
|
|
25
28
|
)
|
|
26
29
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { getMessages } from '../lib/i18n'
|
|
1
2
|
import type { ReactNode } from 'react'
|
|
2
3
|
import { Github } from 'lucide-react'
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import { Button } from './button'
|
|
5
6
|
import { Logo } from './logo'
|
|
6
7
|
import { Separator } from './separator'
|
|
7
8
|
import { ModeToggle } from './mode-toggle'
|
|
8
9
|
|
|
9
10
|
/** Shared composition extracted from apps/web/components/layout/Navbar. */
|
|
10
11
|
export function Navbar({
|
|
12
|
+
lang,
|
|
11
13
|
title = '',
|
|
12
14
|
siteTitle = 'RDX',
|
|
13
15
|
logo = <Logo className="size-5" />,
|
|
@@ -19,6 +21,7 @@ export function Navbar({
|
|
|
19
21
|
github,
|
|
20
22
|
showToggleMode = false,
|
|
21
23
|
}: {
|
|
24
|
+
lang?: string
|
|
22
25
|
title?: string
|
|
23
26
|
siteTitle?: string
|
|
24
27
|
logo?: ReactNode
|
|
@@ -30,14 +33,15 @@ export function Navbar({
|
|
|
30
33
|
github?: { visible: boolean; url: string }
|
|
31
34
|
showToggleMode?: boolean
|
|
32
35
|
}) {
|
|
36
|
+
const messages = getMessages(lang)
|
|
33
37
|
return (
|
|
34
38
|
<nav
|
|
35
39
|
data-rdx-navbar
|
|
36
|
-
aria-label=
|
|
40
|
+
aria-label={messages.mainNavigation}
|
|
37
41
|
className="w-full flex flex-wrap shrink-0 items-center justify-between px-6 py-2 border-b border-border gap-y-2"
|
|
38
42
|
>
|
|
39
43
|
<div className="flex items-center gap-1">
|
|
40
|
-
<
|
|
44
|
+
<Button
|
|
41
45
|
variant="ghost"
|
|
42
46
|
size={title === '' ? 'icon' : 'default'}
|
|
43
47
|
aria-label={siteTitle}
|
|
@@ -46,20 +50,20 @@ export function Navbar({
|
|
|
46
50
|
>
|
|
47
51
|
{logo}
|
|
48
52
|
{title !== '' && <span>{title}</span>}
|
|
49
|
-
</
|
|
53
|
+
</Button>
|
|
50
54
|
</div>
|
|
51
55
|
<div className="flex items-center gap-x-1 md:gap-x-2 h-4">
|
|
52
56
|
{search}
|
|
53
57
|
{versionMenu}
|
|
54
58
|
{docsLink ?? (
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
</
|
|
59
|
+
<Button variant="link" size="sm" href={docsHref}>
|
|
60
|
+
{messages.docs}
|
|
61
|
+
</Button>
|
|
58
62
|
)}
|
|
59
63
|
{github?.visible && github.url && (
|
|
60
64
|
<>
|
|
61
65
|
<Separator orientation="vertical" />
|
|
62
|
-
<
|
|
66
|
+
<Button
|
|
63
67
|
variant="ghost"
|
|
64
68
|
size="icon"
|
|
65
69
|
title="GitHub"
|
|
@@ -71,7 +75,7 @@ export function Navbar({
|
|
|
71
75
|
>
|
|
72
76
|
<Github className="h-[1.2rem] w-[1.2rem]" />
|
|
73
77
|
<span className="sr-only">GitHub</span>
|
|
74
|
-
</
|
|
78
|
+
</Button>
|
|
75
79
|
</>
|
|
76
80
|
)}
|
|
77
81
|
{showToggleMode && (
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getMessages } from '../lib/i18n'
|
|
2
|
+
import { Button } from './button'
|
|
3
|
+
|
|
4
|
+
/** The development website's 404 layout, shared with generated projects. */
|
|
5
|
+
export function NotFound({
|
|
6
|
+
href = '/docs',
|
|
7
|
+
lang,
|
|
8
|
+
}: {
|
|
9
|
+
href?: string
|
|
10
|
+
lang?: string
|
|
11
|
+
}) {
|
|
12
|
+
const messages = getMessages(lang)
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<main
|
|
16
|
+
id="rdx-main"
|
|
17
|
+
data-rdx-not-found
|
|
18
|
+
tabIndex={-1}
|
|
19
|
+
className="bg-transparent text-primary h-full min-h-0 w-full flex flex-1 items-center justify-center cursor-default p-7 outline-none"
|
|
20
|
+
>
|
|
21
|
+
<div className="w-full md:w-1/2">
|
|
22
|
+
<h1 className="text-9xl font-bold font-mono text-center">404</h1>
|
|
23
|
+
<article className="flex flex-col gap-y-5">
|
|
24
|
+
<p className="text-3xl font-bold font-mono text-center">
|
|
25
|
+
{messages.notFound}
|
|
26
|
+
</p>
|
|
27
|
+
<p className="text-sm">{messages.notFoundDescription}</p>
|
|
28
|
+
<p className="text-sm">{messages.brokenLink}</p>
|
|
29
|
+
<Button variant="primary" href={href}>
|
|
30
|
+
{messages.backToDocs}
|
|
31
|
+
</Button>
|
|
32
|
+
</article>
|
|
33
|
+
</div>
|
|
34
|
+
</main>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from 'react'
|
|
4
|
+
|
|
5
|
+
import { cn } from '../lib/utils'
|
|
6
|
+
|
|
7
|
+
// shadcn/ui base-nova Table; see THIRD_PARTY_NOTICES.md for the upstream MIT license.
|
|
8
|
+
type TableProps = ComponentProps<'table'> & {
|
|
9
|
+
/** Configure the scroll container for documentation layout and keyboard access. */
|
|
10
|
+
containerProps?: ComponentProps<'div'> & { 'data-slot'?: string }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function Table({ className, containerProps, ...props }: TableProps) {
|
|
14
|
+
const { className: containerClassName, ...containerAttributes } =
|
|
15
|
+
containerProps ?? {}
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
data-slot="table-container"
|
|
19
|
+
{...containerAttributes}
|
|
20
|
+
className={cn('relative w-full overflow-x-auto', containerClassName)}
|
|
21
|
+
>
|
|
22
|
+
<table
|
|
23
|
+
data-slot="table"
|
|
24
|
+
className={cn('w-full caption-bottom text-sm', className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function TableHeader({ className, ...props }: ComponentProps<'thead'>) {
|
|
32
|
+
return (
|
|
33
|
+
<thead
|
|
34
|
+
data-slot="table-header"
|
|
35
|
+
className={cn('[&_tr]:border-b', className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function TableBody({ className, ...props }: ComponentProps<'tbody'>) {
|
|
42
|
+
return (
|
|
43
|
+
<tbody
|
|
44
|
+
data-slot="table-body"
|
|
45
|
+
className={cn('[&_tr:last-child]:border-0', className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function TableFooter({ className, ...props }: ComponentProps<'tfoot'>) {
|
|
52
|
+
return (
|
|
53
|
+
<tfoot
|
|
54
|
+
data-slot="table-footer"
|
|
55
|
+
className={cn(
|
|
56
|
+
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function TableRow({ className, ...props }: ComponentProps<'tr'>) {
|
|
65
|
+
return (
|
|
66
|
+
<tr
|
|
67
|
+
data-slot="table-row"
|
|
68
|
+
className={cn(
|
|
69
|
+
'border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted',
|
|
70
|
+
className
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function TableHead({ className, ...props }: ComponentProps<'th'>) {
|
|
78
|
+
return (
|
|
79
|
+
<th
|
|
80
|
+
data-slot="table-head"
|
|
81
|
+
className={cn(
|
|
82
|
+
'h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0',
|
|
83
|
+
className
|
|
84
|
+
)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function TableCell({ className, ...props }: ComponentProps<'td'>) {
|
|
91
|
+
return (
|
|
92
|
+
<td
|
|
93
|
+
data-slot="table-cell"
|
|
94
|
+
className={cn(
|
|
95
|
+
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0',
|
|
96
|
+
className
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function TableCaption({ className, ...props }: ComponentProps<'caption'>) {
|
|
104
|
+
return (
|
|
105
|
+
<caption
|
|
106
|
+
data-slot="table-caption"
|
|
107
|
+
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
Table,
|
|
115
|
+
TableHeader,
|
|
116
|
+
TableBody,
|
|
117
|
+
TableFooter,
|
|
118
|
+
TableHead,
|
|
119
|
+
TableRow,
|
|
120
|
+
TableCell,
|
|
121
|
+
TableCaption,
|
|
122
|
+
}
|