@lexibase/rdx-ui 3.0.0 → 3.1.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/THIRD_PARTY_NOTICES.md +3 -2
- package/package.json +5 -3
- package/src/components/docs-page-transition.tsx +178 -0
- package/src/components/docs-sidebar.tsx +103 -30
- package/src/components/docs-toc.tsx +6 -6
- package/src/components/mdx-code-block-view.tsx +133 -7
- package/src/components/mdx-code-block.tsx +83 -30
- package/src/components/mdx-components.tsx +1 -4
- package/src/components/mdx-folder-tree.tsx +58 -0
- package/src/components/mdx-mermaid.tsx +252 -0
- package/src/components/sheet.tsx +146 -0
- package/src/components/tabs.tsx +5 -1
- package/src/lib/i18n.ts +20 -0
- package/src/lib/mermaid.ts +114 -0
- package/src/styles/globals.css +90 -14
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
import { getMessages } from '../lib/i18n'
|
|
10
10
|
import { CodeBlockLine } from './code-block'
|
|
11
11
|
import { MdxCodeBlockView } from './mdx-code-block-view'
|
|
12
|
+
import { MdxFolderTree } from './mdx-folder-tree'
|
|
13
|
+
import { MdxMermaid } from './mdx-mermaid'
|
|
12
14
|
|
|
13
15
|
function textOf(children: ReactNode): string {
|
|
14
16
|
return Children.toArray(children)
|
|
@@ -47,8 +49,16 @@ export function MdxCodeBlock({
|
|
|
47
49
|
lang,
|
|
48
50
|
children,
|
|
49
51
|
'data-rdx-code-mode': mode,
|
|
52
|
+
'data-rdx-code-title': title,
|
|
53
|
+
'data-rdx-code-line-numbers': numbered,
|
|
54
|
+
'data-rdx-code-start-line': firstLine,
|
|
50
55
|
...props
|
|
51
|
-
}: ComponentProps<'pre'> & {
|
|
56
|
+
}: ComponentProps<'pre'> & {
|
|
57
|
+
'data-rdx-code-mode'?: 'folder' | 'diff' | 'mermaid'
|
|
58
|
+
'data-rdx-code-title'?: string
|
|
59
|
+
'data-rdx-code-line-numbers'?: string
|
|
60
|
+
'data-rdx-code-start-line'?: string
|
|
61
|
+
}) {
|
|
52
62
|
const messages = getMessages(lang)
|
|
53
63
|
const code = Children.toArray(children).find((child) => isValidElement(child))
|
|
54
64
|
const codeProps = isValidElement<{
|
|
@@ -59,42 +69,85 @@ export function MdxCodeBlock({
|
|
|
59
69
|
: undefined
|
|
60
70
|
const language = codeProps?.className?.match(/(?:^|\s)language-([^\s]+)/)?.[1]
|
|
61
71
|
const value = textOf(codeProps?.children ?? children)
|
|
72
|
+
if (mode === 'mermaid' || language?.toLowerCase() === 'mermaid') {
|
|
73
|
+
return (
|
|
74
|
+
<MdxMermaid
|
|
75
|
+
value={value}
|
|
76
|
+
title={title}
|
|
77
|
+
lineNumbers={numbered === 'true'}
|
|
78
|
+
startLine={firstLine === undefined ? undefined : Number(firstLine)}
|
|
79
|
+
preProps={props}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
62
83
|
const label =
|
|
63
|
-
|
|
84
|
+
title ||
|
|
85
|
+
(mode === 'folder' ? messages.folderTree : (language ?? messages.code))
|
|
86
|
+
const lineNumbers = numbered === 'true'
|
|
87
|
+
const raw = codeProps?.children ?? children
|
|
88
|
+
const nodes = Children.toArray(raw)
|
|
89
|
+
type LineProps = {
|
|
90
|
+
children?: ReactNode
|
|
91
|
+
className?: string
|
|
92
|
+
'data-rdx-line-variant'?: string
|
|
93
|
+
}
|
|
94
|
+
const lines = nodes.filter(
|
|
95
|
+
(node) =>
|
|
96
|
+
isValidElement<LineProps>(node) &&
|
|
97
|
+
(node.props['data-rdx-line-variant'] ||
|
|
98
|
+
node.props.className?.split(/\s+/).includes('line'))
|
|
99
|
+
)
|
|
100
|
+
const lastLine = lines.at(-1)
|
|
64
101
|
const content =
|
|
65
|
-
mode === '
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
mode === 'folder' ? (
|
|
103
|
+
<MdxFolderTree value={value} />
|
|
104
|
+
) : (mode === 'diff' || lineNumbers) && typeof raw !== 'string' ? (
|
|
105
|
+
nodes.map((node) => {
|
|
106
|
+
if (lineNumbers && node === '\n') return null
|
|
107
|
+
if (!isValidElement<LineProps>(node)) return node
|
|
108
|
+
const variant = node.props['data-rdx-line-variant']
|
|
109
|
+
if (
|
|
110
|
+
!variant &&
|
|
111
|
+
!(lineNumbers && node.props.className?.split(/\s+/).includes('line'))
|
|
112
|
+
)
|
|
113
|
+
return node
|
|
114
|
+
// A final newline terminates the last source line; intentional blank
|
|
115
|
+
// lines before it still receive the official component's own number.
|
|
116
|
+
if (
|
|
117
|
+
lineNumbers &&
|
|
118
|
+
lines.length > 1 &&
|
|
119
|
+
node === lastLine &&
|
|
120
|
+
value.endsWith('\n') &&
|
|
121
|
+
textOf(node.props.children) === ''
|
|
122
|
+
)
|
|
123
|
+
return null
|
|
124
|
+
return (
|
|
125
|
+
<CodeBlockLine
|
|
126
|
+
key={node.key}
|
|
127
|
+
variant={
|
|
128
|
+
variant === 'added' || variant === 'removed'
|
|
129
|
+
? variant
|
|
130
|
+
: variant === 'hunk'
|
|
131
|
+
? 'highlighted'
|
|
132
|
+
: 'default'
|
|
133
|
+
}
|
|
134
|
+
>
|
|
135
|
+
{variant === 'added' || variant === 'removed'
|
|
136
|
+
? withoutMarker(node.props.children)
|
|
137
|
+
: node.props.children}
|
|
138
|
+
</CodeBlockLine>
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
) : (
|
|
142
|
+
raw
|
|
143
|
+
)
|
|
93
144
|
return (
|
|
94
145
|
<MdxCodeBlockView
|
|
95
146
|
label={label}
|
|
96
147
|
mode={mode}
|
|
97
148
|
value={value}
|
|
149
|
+
lineNumbers={lineNumbers}
|
|
150
|
+
startLine={firstLine === undefined ? undefined : Number(firstLine)}
|
|
98
151
|
preProps={{
|
|
99
152
|
...props,
|
|
100
153
|
className: [props.className, language && `language-${language}`]
|
|
@@ -105,10 +105,7 @@ export const mdxComponents = {
|
|
|
105
105
|
/>
|
|
106
106
|
),
|
|
107
107
|
a: ({ className, children, ...props }: ComponentProps<'a'>) => (
|
|
108
|
-
<a
|
|
109
|
-
className={cn('underline text-chart-1 text-blue-500', className)}
|
|
110
|
-
{...props}
|
|
111
|
-
>
|
|
108
|
+
<a className={cn('underline text-rdx-highlighting', className)} {...props}>
|
|
112
109
|
{linkedImages(children)}
|
|
113
110
|
</a>
|
|
114
111
|
),
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
File,
|
|
3
|
+
FileCode2,
|
|
4
|
+
FileImage,
|
|
5
|
+
FileJson,
|
|
6
|
+
FileText,
|
|
7
|
+
Folder,
|
|
8
|
+
} from 'lucide-react'
|
|
9
|
+
|
|
10
|
+
import { CodeBlockLine } from './code-block'
|
|
11
|
+
|
|
12
|
+
function entryIcon(name: string) {
|
|
13
|
+
if (name.endsWith('/')) return { Icon: Folder, kind: 'folder' }
|
|
14
|
+
const extension = name.includes('.')
|
|
15
|
+
? name.split('.').at(-1)?.toLowerCase()
|
|
16
|
+
: undefined
|
|
17
|
+
if (extension === 'json' || extension === 'jsonc')
|
|
18
|
+
return { Icon: FileJson, kind: 'json' }
|
|
19
|
+
if (extension && /^(?:png|jpe?g|gif|webp|svg|avif|ico|bmp)$/.test(extension))
|
|
20
|
+
return { Icon: FileImage, kind: 'image' }
|
|
21
|
+
if (extension && /^(?:mdx?|txt|log|rst)$/.test(extension))
|
|
22
|
+
return { Icon: FileText, kind: 'text' }
|
|
23
|
+
if (
|
|
24
|
+
extension &&
|
|
25
|
+
/^(?:[cm]?[jt]sx?|html|css|scss|vue|svelte|py|rb|go|rs|java|c|cpp|h|cs|php|sql|sh|bash|ya?ml|toml)$/.test(
|
|
26
|
+
extension
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
return { Icon: FileCode2, kind: 'code' }
|
|
30
|
+
return { Icon: File, kind: 'file' }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Decorate generated text with icons inside the official shadcn line component. */
|
|
34
|
+
export function MdxFolderTree({ value }: { value: string }) {
|
|
35
|
+
const trailingNewline = value.endsWith('\n')
|
|
36
|
+
const lines = value.replace(/\n$/, '').split('\n')
|
|
37
|
+
return lines.map((line, index) => {
|
|
38
|
+
const match = line.match(/^([ │\t]*(?:├──|└──)\s*)(.*)$/)
|
|
39
|
+
const prefix = match?.[1] ?? line.match(/^[ │\t]*/)?.[0] ?? ''
|
|
40
|
+
const name = match?.[2] ?? line.slice(prefix.length)
|
|
41
|
+
const { Icon, kind } = entryIcon(name)
|
|
42
|
+
return (
|
|
43
|
+
<CodeBlockLine key={index} data-rdx-folder-kind={name ? kind : undefined}>
|
|
44
|
+
{prefix && <span aria-hidden="true">{prefix}</span>}
|
|
45
|
+
{name && (
|
|
46
|
+
<>
|
|
47
|
+
<Icon
|
|
48
|
+
aria-hidden="true"
|
|
49
|
+
className="mr-1.5 inline-block size-3.5 align-[-2px] text-muted-foreground"
|
|
50
|
+
/>
|
|
51
|
+
{name}
|
|
52
|
+
</>
|
|
53
|
+
)}
|
|
54
|
+
{index < lines.length - 1 || trailingNewline ? '\n' : null}
|
|
55
|
+
</CodeBlockLine>
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
type ComponentProps,
|
|
9
|
+
} from 'react'
|
|
10
|
+
import { useTheme } from 'next-themes'
|
|
11
|
+
import {
|
|
12
|
+
CodeXml,
|
|
13
|
+
Eye,
|
|
14
|
+
Maximize2,
|
|
15
|
+
RotateCw,
|
|
16
|
+
Workflow,
|
|
17
|
+
WrapText,
|
|
18
|
+
} from 'lucide-react'
|
|
19
|
+
|
|
20
|
+
import { renderMermaid } from '../lib/mermaid'
|
|
21
|
+
import { useMessages } from '../providers/locale-provider'
|
|
22
|
+
import { Button } from './button'
|
|
23
|
+
import {
|
|
24
|
+
CodeBlock,
|
|
25
|
+
CodeBlockActions,
|
|
26
|
+
CodeBlockContent,
|
|
27
|
+
CodeBlockCopyButton,
|
|
28
|
+
CodeBlockHeader,
|
|
29
|
+
CodeBlockTitle,
|
|
30
|
+
} from './code-block'
|
|
31
|
+
import {
|
|
32
|
+
Dialog,
|
|
33
|
+
DialogContent,
|
|
34
|
+
DialogDescription,
|
|
35
|
+
DialogTitle,
|
|
36
|
+
DialogTrigger,
|
|
37
|
+
} from './dialog'
|
|
38
|
+
|
|
39
|
+
/** A Mermaid fence adapts the existing shadcn Code Block and Dialog. */
|
|
40
|
+
export function MdxMermaid({
|
|
41
|
+
value,
|
|
42
|
+
title,
|
|
43
|
+
lineNumbers,
|
|
44
|
+
startLine,
|
|
45
|
+
preProps,
|
|
46
|
+
}: {
|
|
47
|
+
value: string
|
|
48
|
+
title?: string
|
|
49
|
+
lineNumbers?: boolean
|
|
50
|
+
startLine?: number
|
|
51
|
+
preProps: ComponentProps<'pre'>
|
|
52
|
+
}) {
|
|
53
|
+
const messages = useMessages()
|
|
54
|
+
const { resolvedTheme } = useTheme()
|
|
55
|
+
const block = useRef<HTMLDivElement>(null)
|
|
56
|
+
const [visible, setVisible] = useState(false)
|
|
57
|
+
const [sourceView, setSourceView] = useState(false)
|
|
58
|
+
const [wrap, setWrap] = useState(false)
|
|
59
|
+
const [attempt, setAttempt] = useState(0)
|
|
60
|
+
const [result, setResult] = useState<{
|
|
61
|
+
source: string
|
|
62
|
+
theme: string
|
|
63
|
+
svg?: string
|
|
64
|
+
error?: boolean
|
|
65
|
+
}>()
|
|
66
|
+
const current =
|
|
67
|
+
result?.source === value && result.theme === resolvedTheme
|
|
68
|
+
? result
|
|
69
|
+
: undefined
|
|
70
|
+
const svg = current?.svg
|
|
71
|
+
const error = current?.error
|
|
72
|
+
const image = svg
|
|
73
|
+
? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
|
|
74
|
+
: undefined
|
|
75
|
+
const description = useMemo(() => {
|
|
76
|
+
if (!svg) return title || messages.diagram
|
|
77
|
+
const document = new DOMParser().parseFromString(svg, 'image/svg+xml')
|
|
78
|
+
return [
|
|
79
|
+
title || document.querySelector('title')?.textContent || messages.diagram,
|
|
80
|
+
document.querySelector('desc')?.textContent,
|
|
81
|
+
]
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join('. ')
|
|
84
|
+
}, [svg, title, messages.diagram])
|
|
85
|
+
const label = title || 'Mermaid'
|
|
86
|
+
const showSource = sourceView || !svg
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const element = block.current
|
|
90
|
+
if (!element) return
|
|
91
|
+
const observer = new IntersectionObserver(
|
|
92
|
+
(entries) => {
|
|
93
|
+
if (entries.some((entry) => entry.isIntersecting)) {
|
|
94
|
+
setVisible(true)
|
|
95
|
+
observer.disconnect()
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{ rootMargin: '200px' }
|
|
99
|
+
)
|
|
100
|
+
observer.observe(element)
|
|
101
|
+
return () => observer.disconnect()
|
|
102
|
+
}, [])
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!visible || !resolvedTheme) return
|
|
106
|
+
let active = true
|
|
107
|
+
const theme = resolvedTheme
|
|
108
|
+
void renderMermaid(value, theme === 'dark', () => active).then(
|
|
109
|
+
(svg) => {
|
|
110
|
+
if (active && svg) setResult({ source: value, theme, svg })
|
|
111
|
+
},
|
|
112
|
+
() => {
|
|
113
|
+
if (active) setResult({ source: value, theme, error: true })
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
return () => {
|
|
117
|
+
active = false
|
|
118
|
+
}
|
|
119
|
+
}, [value, resolvedTheme, visible, attempt])
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<CodeBlock
|
|
123
|
+
ref={block}
|
|
124
|
+
size="sm"
|
|
125
|
+
data-rdx-code-block
|
|
126
|
+
data-rdx-code-mode="mermaid"
|
|
127
|
+
data-rdx-code-value={value}
|
|
128
|
+
className="my-4 max-w-full"
|
|
129
|
+
>
|
|
130
|
+
<CodeBlockHeader>
|
|
131
|
+
<CodeBlockTitle title={label}>
|
|
132
|
+
<Workflow aria-hidden="true" /> {label}
|
|
133
|
+
</CodeBlockTitle>
|
|
134
|
+
<CodeBlockActions>
|
|
135
|
+
{svg && (
|
|
136
|
+
<Button
|
|
137
|
+
variant="ghost"
|
|
138
|
+
size="icon-xs"
|
|
139
|
+
aria-label={
|
|
140
|
+
sourceView ? messages.showDiagram : messages.showDiagramSource
|
|
141
|
+
}
|
|
142
|
+
title={
|
|
143
|
+
sourceView ? messages.showDiagram : messages.showDiagramSource
|
|
144
|
+
}
|
|
145
|
+
aria-pressed={sourceView}
|
|
146
|
+
onClick={() => setSourceView(!sourceView)}
|
|
147
|
+
>
|
|
148
|
+
{sourceView ? (
|
|
149
|
+
<Eye aria-hidden="true" />
|
|
150
|
+
) : (
|
|
151
|
+
<CodeXml aria-hidden="true" />
|
|
152
|
+
)}
|
|
153
|
+
</Button>
|
|
154
|
+
)}
|
|
155
|
+
{image && (
|
|
156
|
+
<Dialog>
|
|
157
|
+
<DialogTrigger
|
|
158
|
+
render={<Button variant="ghost" size="icon-xs" />}
|
|
159
|
+
aria-label={messages.enlargeDiagram}
|
|
160
|
+
title={messages.enlargeDiagram}
|
|
161
|
+
>
|
|
162
|
+
<Maximize2 aria-hidden="true" />
|
|
163
|
+
</DialogTrigger>
|
|
164
|
+
<DialogContent
|
|
165
|
+
data-rdx-mermaid-dialog
|
|
166
|
+
className="flex max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] max-w-6xl flex-col gap-2 sm:max-w-6xl"
|
|
167
|
+
>
|
|
168
|
+
<DialogTitle className="pr-8">
|
|
169
|
+
{title || messages.diagram}
|
|
170
|
+
</DialogTitle>
|
|
171
|
+
<DialogDescription className="sr-only">
|
|
172
|
+
{messages.imagePreviewDescription}
|
|
173
|
+
</DialogDescription>
|
|
174
|
+
<div
|
|
175
|
+
role="region"
|
|
176
|
+
aria-label={messages.diagram}
|
|
177
|
+
tabIndex={0}
|
|
178
|
+
className="min-h-0 overflow-auto rounded-md p-2 outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
179
|
+
>
|
|
180
|
+
<img
|
|
181
|
+
src={image}
|
|
182
|
+
alt={description}
|
|
183
|
+
className="mx-auto h-auto max-w-none"
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
</DialogContent>
|
|
187
|
+
</Dialog>
|
|
188
|
+
)}
|
|
189
|
+
{error && (
|
|
190
|
+
<Button
|
|
191
|
+
variant="ghost"
|
|
192
|
+
size="icon-xs"
|
|
193
|
+
aria-label={messages.tryAgain}
|
|
194
|
+
title={messages.tryAgain}
|
|
195
|
+
onClick={() => setAttempt(attempt + 1)}
|
|
196
|
+
>
|
|
197
|
+
<RotateCw aria-hidden="true" />
|
|
198
|
+
</Button>
|
|
199
|
+
)}
|
|
200
|
+
{showSource && (
|
|
201
|
+
<Button
|
|
202
|
+
variant="ghost"
|
|
203
|
+
size="icon-xs"
|
|
204
|
+
className="md:hidden"
|
|
205
|
+
aria-label={messages.wrapCode}
|
|
206
|
+
title={messages.wrapCode}
|
|
207
|
+
aria-pressed={wrap}
|
|
208
|
+
onClick={() => setWrap(!wrap)}
|
|
209
|
+
>
|
|
210
|
+
<WrapText aria-hidden="true" />
|
|
211
|
+
</Button>
|
|
212
|
+
)}
|
|
213
|
+
<CodeBlockCopyButton value={value} />
|
|
214
|
+
</CodeBlockActions>
|
|
215
|
+
</CodeBlockHeader>
|
|
216
|
+
{error && (
|
|
217
|
+
<p role="status" className="px-3 pt-2 text-xs text-muted-foreground">
|
|
218
|
+
{messages.diagramError}
|
|
219
|
+
</p>
|
|
220
|
+
)}
|
|
221
|
+
{!current && visible && (
|
|
222
|
+
<p role="status" className="px-3 pt-2 text-xs text-muted-foreground">
|
|
223
|
+
{messages.diagramLoading}
|
|
224
|
+
</p>
|
|
225
|
+
)}
|
|
226
|
+
{showSource ? (
|
|
227
|
+
<CodeBlockContent
|
|
228
|
+
{...preProps}
|
|
229
|
+
wrap={wrap}
|
|
230
|
+
lineNumbers={lineNumbers}
|
|
231
|
+
startLine={startLine}
|
|
232
|
+
>
|
|
233
|
+
{value}
|
|
234
|
+
</CodeBlockContent>
|
|
235
|
+
) : (
|
|
236
|
+
<div
|
|
237
|
+
role="region"
|
|
238
|
+
aria-label={title || messages.diagram}
|
|
239
|
+
tabIndex={0}
|
|
240
|
+
data-rdx-mermaid-preview
|
|
241
|
+
className="min-w-0 overflow-auto p-4 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
|
|
242
|
+
>
|
|
243
|
+
<img
|
|
244
|
+
src={image}
|
|
245
|
+
alt={description}
|
|
246
|
+
className="mx-auto h-auto max-w-full"
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
</CodeBlock>
|
|
251
|
+
)
|
|
252
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Dialog as SheetPrimitive } from '@base-ui/react/dialog'
|
|
5
|
+
import { cn, mergeStateClassName } from '../lib/utils'
|
|
6
|
+
import { useMessages } from '../providers/locale-provider'
|
|
7
|
+
|
|
8
|
+
import { Button } from './button'
|
|
9
|
+
import { XIcon } from 'lucide-react'
|
|
10
|
+
|
|
11
|
+
function Sheet({ ...props }: SheetPrimitive.Root.Props) {
|
|
12
|
+
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {
|
|
16
|
+
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function SheetClose({ ...props }: SheetPrimitive.Close.Props) {
|
|
20
|
+
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {
|
|
24
|
+
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
|
|
28
|
+
return (
|
|
29
|
+
<SheetPrimitive.Backdrop
|
|
30
|
+
data-slot="sheet-overlay"
|
|
31
|
+
className={mergeStateClassName(
|
|
32
|
+
'fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs motion-reduce:transition-none',
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function SheetContent({
|
|
41
|
+
className,
|
|
42
|
+
children,
|
|
43
|
+
side = 'right',
|
|
44
|
+
showCloseButton = true,
|
|
45
|
+
closeLabel,
|
|
46
|
+
portalProps,
|
|
47
|
+
...props
|
|
48
|
+
}: SheetPrimitive.Popup.Props & {
|
|
49
|
+
side?: 'top' | 'right' | 'bottom' | 'left'
|
|
50
|
+
showCloseButton?: boolean
|
|
51
|
+
closeLabel?: string
|
|
52
|
+
portalProps?: SheetPrimitive.Portal.Props
|
|
53
|
+
}) {
|
|
54
|
+
const messages = useMessages()
|
|
55
|
+
return (
|
|
56
|
+
<SheetPortal {...portalProps}>
|
|
57
|
+
<SheetOverlay />
|
|
58
|
+
<SheetPrimitive.Popup
|
|
59
|
+
data-slot="sheet-content"
|
|
60
|
+
data-side={side}
|
|
61
|
+
className={mergeStateClassName(
|
|
62
|
+
'fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm motion-reduce:transition-none',
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
{showCloseButton && (
|
|
69
|
+
<SheetPrimitive.Close
|
|
70
|
+
data-slot="sheet-close"
|
|
71
|
+
render={
|
|
72
|
+
<Button
|
|
73
|
+
variant="ghost"
|
|
74
|
+
className="absolute top-3 right-3"
|
|
75
|
+
size="icon-sm"
|
|
76
|
+
/>
|
|
77
|
+
}
|
|
78
|
+
>
|
|
79
|
+
<XIcon aria-hidden="true" />
|
|
80
|
+
<span className="sr-only">{closeLabel ?? messages.close}</span>
|
|
81
|
+
</SheetPrimitive.Close>
|
|
82
|
+
)}
|
|
83
|
+
</SheetPrimitive.Popup>
|
|
84
|
+
</SheetPortal>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
data-slot="sheet-header"
|
|
92
|
+
className={cn('flex flex-col gap-0.5 p-4', className)}
|
|
93
|
+
{...props}
|
|
94
|
+
/>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
data-slot="sheet-footer"
|
|
102
|
+
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
|
|
109
|
+
return (
|
|
110
|
+
<SheetPrimitive.Title
|
|
111
|
+
data-slot="sheet-title"
|
|
112
|
+
className={mergeStateClassName(
|
|
113
|
+
'cn-font-heading text-base font-medium text-foreground',
|
|
114
|
+
className
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function SheetDescription({
|
|
122
|
+
className,
|
|
123
|
+
...props
|
|
124
|
+
}: SheetPrimitive.Description.Props) {
|
|
125
|
+
return (
|
|
126
|
+
<SheetPrimitive.Description
|
|
127
|
+
data-slot="sheet-description"
|
|
128
|
+
className={mergeStateClassName(
|
|
129
|
+
'text-sm text-muted-foreground',
|
|
130
|
+
className
|
|
131
|
+
)}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
Sheet,
|
|
139
|
+
SheetTrigger,
|
|
140
|
+
SheetClose,
|
|
141
|
+
SheetContent,
|
|
142
|
+
SheetHeader,
|
|
143
|
+
SheetFooter,
|
|
144
|
+
SheetTitle,
|
|
145
|
+
SheetDescription,
|
|
146
|
+
}
|
package/src/components/tabs.tsx
CHANGED
|
@@ -144,6 +144,7 @@ function Tabs({ className, ...props }: TabsPrimitive.Root.Props) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
function TabsList({ className, ...props }: TabsPrimitive.List.Props) {
|
|
147
|
+
const messages = useMessages()
|
|
147
148
|
return (
|
|
148
149
|
<div
|
|
149
150
|
data-slot="tabs-header"
|
|
@@ -151,6 +152,9 @@ function TabsList({ className, ...props }: TabsPrimitive.List.Props) {
|
|
|
151
152
|
>
|
|
152
153
|
<TabsPrimitive.List
|
|
153
154
|
data-slot="tabs-list"
|
|
155
|
+
aria-label={
|
|
156
|
+
props['aria-labelledby'] ? undefined : messages.documentationTabs
|
|
157
|
+
}
|
|
154
158
|
className={mergeStateClassName(
|
|
155
159
|
'flex min-w-0 flex-1 gap-0.5 overflow-x-auto p-1.5 data-[orientation=vertical]:flex-col',
|
|
156
160
|
className
|
|
@@ -167,7 +171,7 @@ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
|
|
167
171
|
<TabsPrimitive.Tab
|
|
168
172
|
data-slot="tabs-trigger"
|
|
169
173
|
className={mergeStateClassName(
|
|
170
|
-
'relative shrink-0 rounded-md border border-transparent px-2.5 py-1 text-sm font-medium whitespace-nowrap text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-active:border-border data-active:bg-background data-active:text-
|
|
174
|
+
'relative shrink-0 rounded-md border border-transparent px-2.5 py-1 text-sm font-medium whitespace-nowrap text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-active:border-border data-active:bg-background data-active:text-rdx-selected data-active:shadow-xs data-disabled:cursor-not-allowed data-disabled:opacity-50 data-disabled:hover:bg-transparent data-disabled:hover:text-muted-foreground motion-reduce:transition-none',
|
|
171
175
|
className
|
|
172
176
|
)}
|
|
173
177
|
{...props}
|
package/src/lib/i18n.ts
CHANGED
|
@@ -32,15 +32,25 @@ const en = {
|
|
|
32
32
|
'Press Escape or use the close button to return to the document.',
|
|
33
33
|
copyCode: 'Copy code',
|
|
34
34
|
wrapCode: 'Wrap lines',
|
|
35
|
+
showMore: 'Show more',
|
|
36
|
+
showLess: 'Show less',
|
|
35
37
|
codeCopied: 'Code copied to clipboard.',
|
|
36
38
|
copyCodeError: 'Could not copy. Select the code and copy it manually.',
|
|
37
39
|
copyRetry: 'Could not copy. Try again or copy manually.',
|
|
38
40
|
copied: 'Copied!',
|
|
39
41
|
copyTab: 'Copy active tab',
|
|
42
|
+
documentationTabs: 'Documentation tabs',
|
|
40
43
|
tabCopied: 'Tab content copied to clipboard.',
|
|
41
44
|
copyTabError: 'Could not copy. Select the tab content and copy it manually.',
|
|
42
45
|
code: 'Code',
|
|
43
46
|
folderTree: 'Folder',
|
|
47
|
+
diagram: 'Diagram',
|
|
48
|
+
showDiagram: 'Show diagram',
|
|
49
|
+
showDiagramSource: 'Show diagram source',
|
|
50
|
+
enlargeDiagram: 'Enlarge diagram',
|
|
51
|
+
diagramLoading: 'Rendering diagram…',
|
|
52
|
+
diagramError:
|
|
53
|
+
'Could not render this diagram. Check the Mermaid source below.',
|
|
44
54
|
table: 'Table',
|
|
45
55
|
current: 'Current',
|
|
46
56
|
version: 'Version {version}',
|
|
@@ -101,17 +111,27 @@ const ptBR = {
|
|
|
101
111
|
'Pressione Escape ou use o botão de fechar para voltar ao documento.',
|
|
102
112
|
copyCode: 'Copiar código',
|
|
103
113
|
wrapCode: 'Quebrar linhas',
|
|
114
|
+
showMore: 'Mostrar mais',
|
|
115
|
+
showLess: 'Mostrar menos',
|
|
104
116
|
codeCopied: 'Código copiado para a área de transferência.',
|
|
105
117
|
copyCodeError:
|
|
106
118
|
'Não foi possível copiar. Selecione o código e copie manualmente.',
|
|
107
119
|
copyRetry: 'Não foi possível copiar. Tente novamente ou copie manualmente.',
|
|
108
120
|
copied: 'Copiado!',
|
|
109
121
|
copyTab: 'Copiar aba ativa',
|
|
122
|
+
documentationTabs: 'Abas da documentação',
|
|
110
123
|
tabCopied: 'Conteúdo da aba copiado para a área de transferência.',
|
|
111
124
|
copyTabError:
|
|
112
125
|
'Não foi possível copiar. Selecione o conteúdo da aba e copie manualmente.',
|
|
113
126
|
code: 'Código',
|
|
114
127
|
folderTree: 'Pastas',
|
|
128
|
+
diagram: 'Diagrama',
|
|
129
|
+
showDiagram: 'Mostrar diagrama',
|
|
130
|
+
showDiagramSource: 'Mostrar código do diagrama',
|
|
131
|
+
enlargeDiagram: 'Ampliar diagrama',
|
|
132
|
+
diagramLoading: 'Renderizando diagrama…',
|
|
133
|
+
diagramError:
|
|
134
|
+
'Não foi possível renderizar o diagrama. Confira o código Mermaid abaixo.',
|
|
115
135
|
table: 'Tabela',
|
|
116
136
|
current: 'Atual',
|
|
117
137
|
version: 'Versão {version}',
|