@lexibase/rdx-ui 1.0.0-beta.1 → 1.0.0-beta.2
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 +91 -1
- package/package.json +32 -24
- package/postcss-plugin.cjs +2 -0
- package/postcss.config.mjs +1 -5
- package/src/components/accordion.tsx +91 -0
- package/src/components/blockquote.tsx +33 -0
- package/src/components/button-link.tsx +27 -0
- package/src/components/button.tsx +9 -12
- package/src/components/callout.tsx +96 -0
- package/src/components/code-block.tsx +55 -0
- package/src/components/command.tsx +90 -0
- package/src/components/copy-code-button.tsx +78 -0
- package/src/components/dialog.tsx +76 -0
- package/src/components/docs-search-dialog.tsx +184 -0
- package/src/components/docs-search.tsx +80 -0
- package/src/components/docs-sidebar.tsx +13 -20
- package/src/components/docs-toc.tsx +231 -0
- package/src/components/dropdown-menu.tsx +88 -39
- package/src/components/home.tsx +5 -6
- package/src/components/mdx-components.tsx +118 -27
- package/src/components/navbar.tsx +21 -27
- package/src/components/popover.tsx +68 -0
- package/src/components/separator.tsx +11 -9
- package/src/components/tabs.tsx +183 -0
- package/src/components/version-dropdown.tsx +34 -30
- package/src/components/version-notice.tsx +39 -0
- package/src/lib/docs-search.ts +68 -0
- package/src/lib/utils.ts +10 -0
- package/src/styles/globals.css +34 -0
- package/src/styles/globals.d.css.ts +2 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# RDX UI
|
|
2
2
|
|
|
3
3
|
The shared theme used by the RDX development website and generated documentation
|
|
4
|
-
sites. The source of truth is `packages/rdx-ui/src`:
|
|
4
|
+
sites. The source of truth is `packages/rdx-ui/src`: Base UI dropdowns, buttons,
|
|
5
5
|
logo, theme provider, navbar, sidebar, landing page and MDX components.
|
|
6
6
|
|
|
7
7
|
Published as `@lexibase/rdx-ui`. React/Next.js source is intentionally shipped
|
|
@@ -20,3 +20,93 @@ export default function MyHome() {
|
|
|
20
20
|
Configure project-owned components via `homepage.component` and `navbar.logo`.
|
|
21
21
|
Do not edit generated `.rdx/` files. Installing the versioning plugin does not
|
|
22
22
|
change this theme: it contributes data to the shared version dropdown.
|
|
23
|
+
|
|
24
|
+
## Documentation components
|
|
25
|
+
|
|
26
|
+
`Callout` is registered automatically in MDX, with optional `title` and variants
|
|
27
|
+
`note` (default), `tip`, `success`, `warning`, `danger`:
|
|
28
|
+
|
|
29
|
+
```mdx
|
|
30
|
+
<Callout variant="warning" title="Before updating">
|
|
31
|
+
|
|
32
|
+
Read **release notes** before deploying.
|
|
33
|
+
|
|
34
|
+
</Callout>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
In React, import it from `@lexibase/rdx-ui/components/callout`. It renders a static
|
|
38
|
+
note, not an automatic live alert. The Markdown map also styles all six heading
|
|
39
|
+
levels, responsive images, GFM tables and keyboard-scrollable code blocks.
|
|
40
|
+
Fenced code blocks include a language label and copy button automatically;
|
|
41
|
+
inline code remains unchanged. Copy failures leave the code selectable and show
|
|
42
|
+
manual-copy guidance instead of a false success message.
|
|
43
|
+
|
|
44
|
+
## Base UI composition
|
|
45
|
+
|
|
46
|
+
The shared components use `@base-ui/react`. Existing RDX colors, variants,
|
|
47
|
+
dimensions and layout are preserved; `components.json` selects `base-nova` for
|
|
48
|
+
future shadcn additions, without regenerating the existing theme.
|
|
49
|
+
|
|
50
|
+
This beta changes the composition API: `asChild` is no longer supported.
|
|
51
|
+
Use `render` for interactive composition and `ButtonLink` for navigation:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { Button } from '@lexibase/rdx-ui/components/button'
|
|
55
|
+
import { ButtonLink } from '@lexibase/rdx-ui/components/button-link'
|
|
56
|
+
import {
|
|
57
|
+
DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,
|
|
58
|
+
DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem,
|
|
59
|
+
DropdownMenuLinkItem,
|
|
60
|
+
} from '@lexibase/rdx-ui/components/dropdown-menu'
|
|
61
|
+
import Link from 'next/link'
|
|
62
|
+
|
|
63
|
+
<ButtonLink href="/docs" variant="outline">Documentation</ButtonLink>
|
|
64
|
+
|
|
65
|
+
<DropdownMenu>
|
|
66
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
67
|
+
Actions
|
|
68
|
+
</DropdownMenuTrigger>
|
|
69
|
+
<DropdownMenuContent align="start">
|
|
70
|
+
<DropdownMenuGroup>
|
|
71
|
+
<DropdownMenuLabel>Project</DropdownMenuLabel>
|
|
72
|
+
<DropdownMenuItem onClick={() => console.log('Action')}>Run action</DropdownMenuItem>
|
|
73
|
+
<DropdownMenuLinkItem closeOnClick render={<Link href="/docs" />}>
|
|
74
|
+
Documentation
|
|
75
|
+
</DropdownMenuLinkItem>
|
|
76
|
+
</DropdownMenuGroup>
|
|
77
|
+
</DropdownMenuContent>
|
|
78
|
+
</DropdownMenu>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`ButtonLink` renders a Next.js link with button styling, not `role="button"`.
|
|
82
|
+
`Button` renders a native button; use `type="submit"` explicitly for form submission.
|
|
83
|
+
Menu actions use `onClick` instead of Radix's `onSelect`. Labels must be inside
|
|
84
|
+
`DropdownMenuGroup`. Checkbox values are boolean, not the Radix `indeterminate`
|
|
85
|
+
string; checkbox/radio items remain open unless `closeOnClick` is enabled.
|
|
86
|
+
Navigation items use `DropdownMenuLinkItem` (with `closeOnClick` when desired).
|
|
87
|
+
Components accept Base UI state-aware `className` callbacks and `render` props.
|
|
88
|
+
For custom CSS, replace Radix state selectors/variables with Base UI's
|
|
89
|
+
`data-open`, `data-closed`, `data-highlighted`, `data-popup-open`,
|
|
90
|
+
`--available-height` and `--transform-origin`. `Separator` remains decorative by
|
|
91
|
+
default; set `decorative={false}` for an accessible separator role.
|
|
92
|
+
|
|
93
|
+
See [Base UI Menu](https://base-ui.com/react/components/menu) and
|
|
94
|
+
[shadcn Base UI](https://ui.shadcn.com/docs/components/base/button).
|
|
95
|
+
|
|
96
|
+
## Documentation tabs
|
|
97
|
+
|
|
98
|
+
`Tabs`, `TabsList`, `TabsTrigger` and `TabsContent` wrap Base UI Tabs and are
|
|
99
|
+
automatically registered in MDX. Import them from
|
|
100
|
+
`@lexibase/rdx-ui/components/tabs` in React files. Use an explicit `defaultValue`,
|
|
101
|
+
matching string values and an `aria-label` on each list. Arrow keys move focus;
|
|
102
|
+
Enter or Space activates a tab. The list scrolls on small screens. See
|
|
103
|
+
the repository's `MDX.md` and `docs/examples/tabs.mdx` for complete examples.
|
|
104
|
+
|
|
105
|
+
## Documentation accordion
|
|
106
|
+
|
|
107
|
+
`Accordion`, `AccordionItem`, `AccordionTrigger` and `AccordionContent` wrap
|
|
108
|
+
Base UI Accordion and are registered in MDX. Import them from
|
|
109
|
+
`@lexibase/rdx-ui/components/accordion` in React files. One item opens at a time
|
|
110
|
+
by default; `multiple` allows independent expansion. Triggers use native buttons
|
|
111
|
+
inside `h3` headings with normal Tab navigation and Enter/Space activation.
|
|
112
|
+
See `MDX.md` and `docs/examples/accordion.mdx` for examples and React options.
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexibase/rdx-ui",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=20.9"
|
|
7
|
+
},
|
|
5
8
|
"description": "The shared RDX documentation theme and UI components.",
|
|
6
9
|
"license": "MIT",
|
|
7
10
|
"files": [
|
|
8
11
|
"src",
|
|
9
12
|
"postcss.config.mjs",
|
|
13
|
+
"postcss-plugin.cjs",
|
|
10
14
|
"README.md"
|
|
11
15
|
],
|
|
12
16
|
"publishConfig": {
|
|
@@ -18,42 +22,46 @@
|
|
|
18
22
|
"directory": "packages/rdx-ui"
|
|
19
23
|
},
|
|
20
24
|
"dependencies": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"tailwindcss": "^4.
|
|
24
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
25
|
-
"@radix-ui/react-separator": "^1.1.7",
|
|
26
|
-
"@radix-ui/react-slot": "^1.2.3",
|
|
25
|
+
"cmdk": "^1.1.1",
|
|
26
|
+
"@base-ui/react": "^1.9.0",
|
|
27
|
+
"@tailwindcss/postcss": "^4.3.3",
|
|
27
28
|
"class-variance-authority": "^0.7.1",
|
|
28
29
|
"clsx": "^2.1.1",
|
|
29
|
-
"lucide-react": "^0.
|
|
30
|
+
"lucide-react": "^0.577.0",
|
|
30
31
|
"next-themes": "^0.4.6",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
32
|
+
"postcss": "^8.5.31",
|
|
33
|
+
"tailwind-merge": "^3.7.0",
|
|
34
|
+
"tailwindcss": "^4.3.3",
|
|
35
|
+
"tw-animate-css": "^1.4.0",
|
|
36
|
+
"@lexibase/rdx-types": "1.0.0-beta.5"
|
|
33
37
|
},
|
|
34
38
|
"peerDependencies": {
|
|
35
|
-
"next": "^
|
|
36
|
-
"react": "^19.1
|
|
37
|
-
"react-dom": "^19.1
|
|
39
|
+
"next": "^16.4.2",
|
|
40
|
+
"react": "^19.3.1",
|
|
41
|
+
"react-dom": "^19.3.1"
|
|
38
42
|
},
|
|
39
43
|
"devDependencies": {
|
|
40
|
-
"@tailwindcss/postcss": "^4.
|
|
44
|
+
"@tailwindcss/postcss": "^4.3.3",
|
|
41
45
|
"@types/node": "^22.15.3",
|
|
42
|
-
"@types/react": "19.
|
|
43
|
-
"@types/react-dom": "19.
|
|
44
|
-
"eslint": "^9.
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"react": "19.1
|
|
48
|
-
"react-dom": "19.1
|
|
49
|
-
"tailwindcss": "^4.
|
|
50
|
-
"typescript": "
|
|
46
|
+
"@types/react": "19.3.0",
|
|
47
|
+
"@types/react-dom": "19.3.0",
|
|
48
|
+
"eslint": "^9.39.5",
|
|
49
|
+
"next": "^16.4.2",
|
|
50
|
+
"postcss": "^8.5.31",
|
|
51
|
+
"react": "19.3.1",
|
|
52
|
+
"react-dom": "19.3.1",
|
|
53
|
+
"tailwindcss": "^4.3.3",
|
|
54
|
+
"typescript": "6.0.3",
|
|
51
55
|
"@rdx/typescript-config": "1.0.0",
|
|
52
56
|
"@rdx/eslint-config": "1.0.0"
|
|
53
57
|
},
|
|
54
58
|
"exports": {
|
|
55
|
-
"./globals.css":
|
|
59
|
+
"./globals.css": {
|
|
60
|
+
"types": "./src/styles/globals.d.css.ts",
|
|
61
|
+
"default": "./src/styles/globals.css"
|
|
62
|
+
},
|
|
56
63
|
"./postcss.config": "./postcss.config.mjs",
|
|
64
|
+
"./postcss-plugin": "./postcss-plugin.cjs",
|
|
57
65
|
"./lib/*": "./src/lib/*.ts",
|
|
58
66
|
"./components/*": "./src/components/*.tsx",
|
|
59
67
|
"./hooks/*": "./src/hooks/*.ts",
|
package/postcss.config.mjs
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { createRequire } from 'node:module'
|
|
2
|
-
|
|
3
|
-
const require = createRequire(import.meta.url)
|
|
4
|
-
|
|
5
1
|
/** @type {import('postcss-load-config').Config} */
|
|
6
2
|
const config = {
|
|
7
3
|
plugins: {
|
|
8
|
-
|
|
4
|
+
'@lexibase/rdx-ui/postcss-plugin': {},
|
|
9
5
|
},
|
|
10
6
|
}
|
|
11
7
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion'
|
|
4
|
+
import { ChevronDown, ChevronUp } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
import { mergeStateClassName } from '../lib/utils'
|
|
7
|
+
|
|
8
|
+
function Accordion({
|
|
9
|
+
className,
|
|
10
|
+
multiple = false,
|
|
11
|
+
...props
|
|
12
|
+
}: AccordionPrimitive.Root.Props<string>) {
|
|
13
|
+
return (
|
|
14
|
+
<AccordionPrimitive.Root
|
|
15
|
+
data-slot="accordion"
|
|
16
|
+
multiple={multiple}
|
|
17
|
+
className={mergeStateClassName(
|
|
18
|
+
'my-4 flex w-full min-w-0 flex-col',
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
|
|
27
|
+
return (
|
|
28
|
+
<AccordionPrimitive.Item
|
|
29
|
+
data-slot="accordion-item"
|
|
30
|
+
className={mergeStateClassName(
|
|
31
|
+
'min-w-0 border-b border-border last:border-b-0',
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function AccordionTrigger({
|
|
40
|
+
className,
|
|
41
|
+
children,
|
|
42
|
+
...props
|
|
43
|
+
}: AccordionPrimitive.Trigger.Props) {
|
|
44
|
+
return (
|
|
45
|
+
<AccordionPrimitive.Header className="m-0 flex text-sm font-medium">
|
|
46
|
+
<AccordionPrimitive.Trigger
|
|
47
|
+
data-slot="accordion-trigger"
|
|
48
|
+
className={mergeStateClassName(
|
|
49
|
+
'group/accordion-trigger relative flex flex-1 min-w-0 items-start justify-between gap-3 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium text-foreground transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 data-disabled:pointer-events-none data-disabled:opacity-50 motion-reduce:transition-none',
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<span className="min-w-0 [overflow-wrap:anywhere]">{children}</span>
|
|
55
|
+
<ChevronDown
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
data-slot="accordion-trigger-icon"
|
|
58
|
+
className="pointer-events-none ml-auto size-4 shrink-0 text-muted-foreground group-aria-expanded/accordion-trigger:hidden"
|
|
59
|
+
/>
|
|
60
|
+
<ChevronUp
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
data-slot="accordion-trigger-icon"
|
|
63
|
+
className="pointer-events-none ml-auto hidden size-4 shrink-0 text-muted-foreground group-aria-expanded/accordion-trigger:inline"
|
|
64
|
+
/>
|
|
65
|
+
</AccordionPrimitive.Trigger>
|
|
66
|
+
</AccordionPrimitive.Header>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function AccordionContent({
|
|
71
|
+
className,
|
|
72
|
+
children,
|
|
73
|
+
...props
|
|
74
|
+
}: AccordionPrimitive.Panel.Props) {
|
|
75
|
+
return (
|
|
76
|
+
<AccordionPrimitive.Panel
|
|
77
|
+
data-slot="accordion-content"
|
|
78
|
+
className={mergeStateClassName(
|
|
79
|
+
'min-w-0 overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up motion-reduce:data-open:animate-none motion-reduce:data-closed:animate-none',
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
>
|
|
84
|
+
<div className="pt-0 pb-2.5 leading-6 text-foreground/85 [overflow-wrap:anywhere] [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground">
|
|
85
|
+
{children}
|
|
86
|
+
</div>
|
|
87
|
+
</AccordionPrimitive.Panel>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
import { Quote } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
import { cn } from '../lib/utils'
|
|
5
|
+
|
|
6
|
+
/** A quotation keeps native semantics, without the heading or tone of a Callout. */
|
|
7
|
+
export function Blockquote({
|
|
8
|
+
children,
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: ComponentProps<'blockquote'>) {
|
|
12
|
+
return (
|
|
13
|
+
<blockquote
|
|
14
|
+
data-rdx-blockquote
|
|
15
|
+
className={cn(
|
|
16
|
+
'my-6 flex min-w-0 gap-3 rounded-lg border border-border border-l-2 border-l-ring/50 bg-muted/25 p-4 text-foreground/85 sm:p-5',
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<Quote
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
className="mt-1 size-4 shrink-0 text-muted-foreground/70"
|
|
24
|
+
/>
|
|
25
|
+
<div
|
|
26
|
+
data-slot="blockquote-content"
|
|
27
|
+
className="min-w-0 flex-1 leading-7 [overflow-wrap:anywhere] [&>:first-child]:mt-0 [&>:last-child]:mb-0"
|
|
28
|
+
>
|
|
29
|
+
{children}
|
|
30
|
+
</div>
|
|
31
|
+
</blockquote>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
4
|
+
import type { ComponentProps } from 'react'
|
|
5
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
6
|
+
|
|
7
|
+
import { buttonVariants } from './button'
|
|
8
|
+
import { cn } from '../lib/utils'
|
|
9
|
+
|
|
10
|
+
/** Navigation keeps native anchor semantics, independent of button behavior. */
|
|
11
|
+
function ButtonLink({
|
|
12
|
+
className,
|
|
13
|
+
variant,
|
|
14
|
+
size,
|
|
15
|
+
active,
|
|
16
|
+
...props
|
|
17
|
+
}: ComponentProps<typeof Link> & VariantProps<typeof buttonVariants>) {
|
|
18
|
+
return (
|
|
19
|
+
<Link
|
|
20
|
+
data-slot="button"
|
|
21
|
+
className={cn(buttonVariants({ variant, size, active }), className)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { ButtonLink }
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { Slot } from '@radix-ui/react-slot'
|
|
3
|
+
import { Button as ButtonPrimitive } from '@base-ui/react/button'
|
|
5
4
|
import { cva, type VariantProps } from 'class-variance-authority'
|
|
6
5
|
|
|
7
6
|
import { cn } from '@lexibase/rdx-ui/lib/utils'
|
|
@@ -49,19 +48,17 @@ function Button({
|
|
|
49
48
|
variant,
|
|
50
49
|
size,
|
|
51
50
|
active,
|
|
52
|
-
asChild = false,
|
|
53
51
|
...props
|
|
54
|
-
}:
|
|
55
|
-
VariantProps<typeof buttonVariants> & {
|
|
56
|
-
asChild?: boolean
|
|
57
|
-
active?: boolean
|
|
58
|
-
}) {
|
|
59
|
-
const Comp = asChild ? Slot : 'button'
|
|
60
|
-
|
|
52
|
+
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
|
|
61
53
|
return (
|
|
62
|
-
<
|
|
54
|
+
<ButtonPrimitive
|
|
63
55
|
data-slot="button"
|
|
64
|
-
className={
|
|
56
|
+
className={(state) =>
|
|
57
|
+
cn(
|
|
58
|
+
buttonVariants({ variant, size, active }),
|
|
59
|
+
typeof className === 'function' ? className(state) : className
|
|
60
|
+
)
|
|
61
|
+
}
|
|
65
62
|
{...props}
|
|
66
63
|
/>
|
|
67
64
|
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
CircleAlert,
|
|
4
|
+
CircleCheck,
|
|
5
|
+
Info,
|
|
6
|
+
Lightbulb,
|
|
7
|
+
TriangleAlert,
|
|
8
|
+
} from 'lucide-react'
|
|
9
|
+
|
|
10
|
+
import { cn } from '../lib/utils'
|
|
11
|
+
|
|
12
|
+
const variants = {
|
|
13
|
+
note: {
|
|
14
|
+
icon: Info,
|
|
15
|
+
label: 'Note',
|
|
16
|
+
classes: 'border-border bg-muted/40',
|
|
17
|
+
iconClass: 'text-muted-foreground',
|
|
18
|
+
},
|
|
19
|
+
tip: {
|
|
20
|
+
icon: Lightbulb,
|
|
21
|
+
label: 'Tip',
|
|
22
|
+
classes: 'border-blue-500/30 bg-blue-500/5',
|
|
23
|
+
iconClass: 'text-blue-700 dark:text-blue-400',
|
|
24
|
+
},
|
|
25
|
+
success: {
|
|
26
|
+
icon: CircleCheck,
|
|
27
|
+
label: 'Success',
|
|
28
|
+
classes: 'border-emerald-500/30 bg-emerald-500/5',
|
|
29
|
+
iconClass: 'text-emerald-700 dark:text-emerald-400',
|
|
30
|
+
},
|
|
31
|
+
warning: {
|
|
32
|
+
icon: TriangleAlert,
|
|
33
|
+
label: 'Warning',
|
|
34
|
+
classes: 'border-amber-500/30 bg-amber-500/5',
|
|
35
|
+
iconClass: 'text-amber-700 dark:text-amber-400',
|
|
36
|
+
},
|
|
37
|
+
danger: {
|
|
38
|
+
icon: CircleAlert,
|
|
39
|
+
label: 'Danger',
|
|
40
|
+
classes: 'border-red-500/30 bg-red-500/5',
|
|
41
|
+
iconClass: 'text-red-700 dark:text-red-400',
|
|
42
|
+
},
|
|
43
|
+
} as const
|
|
44
|
+
|
|
45
|
+
export type CalloutVariant = keyof typeof variants
|
|
46
|
+
export type CalloutProps = Omit<ComponentProps<'aside'>, 'title'> & {
|
|
47
|
+
variant?: CalloutVariant
|
|
48
|
+
title?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Static documentation content, deliberately not a live alert region. */
|
|
52
|
+
export function Callout({
|
|
53
|
+
variant = 'note',
|
|
54
|
+
title,
|
|
55
|
+
className,
|
|
56
|
+
children,
|
|
57
|
+
...props
|
|
58
|
+
}: CalloutProps) {
|
|
59
|
+
if (!Object.hasOwn(variants, variant)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`[RDX] Invalid Callout variant "${variant}". Use note, tip, success, warning or danger.`
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
const { icon: Icon, label, classes, iconClass } = variants[variant]
|
|
65
|
+
return (
|
|
66
|
+
<aside
|
|
67
|
+
role="note"
|
|
68
|
+
aria-label={title || label}
|
|
69
|
+
data-rdx-callout={variant}
|
|
70
|
+
className={cn(
|
|
71
|
+
'my-6 flex min-w-0 gap-3 rounded-md border p-4 text-sm text-foreground',
|
|
72
|
+
classes,
|
|
73
|
+
className
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
<Icon
|
|
78
|
+
aria-hidden="true"
|
|
79
|
+
className={cn('mt-0.5 size-4 shrink-0', iconClass)}
|
|
80
|
+
/>
|
|
81
|
+
<div className="min-w-0 flex-1">
|
|
82
|
+
{title && (
|
|
83
|
+
<p data-slot="callout-title" className="mb-2 font-semibold leading-5">
|
|
84
|
+
{title}
|
|
85
|
+
</p>
|
|
86
|
+
)}
|
|
87
|
+
<div
|
|
88
|
+
data-slot="callout-content"
|
|
89
|
+
className="leading-6 break-words [&>:first-child]:mt-0 [&>:last-child]:mb-0"
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</aside>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Children, isValidElement, type ComponentProps } from 'react'
|
|
2
|
+
import { CodeXml } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
import { cn } from '../lib/utils'
|
|
5
|
+
import { CopyCodeButton } from './copy-code-button'
|
|
6
|
+
|
|
7
|
+
/** Keep the code server-rendered; only clipboard interaction needs JavaScript. */
|
|
8
|
+
export function CodeBlock({
|
|
9
|
+
children,
|
|
10
|
+
className,
|
|
11
|
+
...props
|
|
12
|
+
}: ComponentProps<'pre'>) {
|
|
13
|
+
const code = Children.toArray(children).find(
|
|
14
|
+
(child) =>
|
|
15
|
+
isValidElement<{ className?: string }>(child) &&
|
|
16
|
+
child.props.className?.includes('language-')
|
|
17
|
+
)
|
|
18
|
+
const language = isValidElement<{ className?: string }>(code)
|
|
19
|
+
? code.props.className?.match(/(?:^|\s)language-([^\s]+)/)?.[1]
|
|
20
|
+
: undefined
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
data-rdx-code-block
|
|
24
|
+
className="my-4 min-w-0 max-w-full overflow-hidden rounded-lg border border-border bg-muted/30 text-foreground"
|
|
25
|
+
>
|
|
26
|
+
<div className="flex flex-wrap items-center justify-between gap-x-3 gap-y-1.5 border-b border-border/70 bg-muted/30 px-3 py-1.5">
|
|
27
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
28
|
+
<span
|
|
29
|
+
aria-hidden="true"
|
|
30
|
+
className="flex size-6 shrink-0 items-center justify-center rounded-md border border-blue-500/20 bg-blue-500/10 text-blue-700 dark:text-blue-400"
|
|
31
|
+
>
|
|
32
|
+
<CodeXml className="size-3.5" />
|
|
33
|
+
</span>
|
|
34
|
+
<span
|
|
35
|
+
className="min-w-0 truncate font-mono text-xs font-medium text-muted-foreground"
|
|
36
|
+
title={language}
|
|
37
|
+
>
|
|
38
|
+
{language ?? 'Code'}
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
<CopyCodeButton />
|
|
42
|
+
</div>
|
|
43
|
+
<pre
|
|
44
|
+
tabIndex={0}
|
|
45
|
+
className={cn(
|
|
46
|
+
'm-0 max-w-full overflow-x-auto p-3 font-mono text-[13px] leading-6 text-foreground/90 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring [&>code]:bg-transparent [&>code]:rounded-none [&>code]:p-0 [&>code]:[font-family:inherit] [&>code]:text-inherit',
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</pre>
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// shadcn/ui Base Nova Command (cmdk), composed with the Base UI Dialog.
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { Command as CommandPrimitive } from 'cmdk'
|
|
6
|
+
import { Search } from 'lucide-react'
|
|
7
|
+
import { cn } from '../lib/utils'
|
|
8
|
+
|
|
9
|
+
function Command({
|
|
10
|
+
className,
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof CommandPrimitive>) {
|
|
13
|
+
return (
|
|
14
|
+
<CommandPrimitive
|
|
15
|
+
data-slot="command"
|
|
16
|
+
className={cn(
|
|
17
|
+
'flex size-full flex-col overflow-hidden rounded-xl bg-popover p-1 text-popover-foreground',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
function CommandInput({
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
data-slot="command-input-wrapper"
|
|
31
|
+
className="flex items-center gap-2 border-b px-2 py-2 pr-9"
|
|
32
|
+
>
|
|
33
|
+
<Search className="size-4 shrink-0 text-muted-foreground" />
|
|
34
|
+
<CommandPrimitive.Input
|
|
35
|
+
data-slot="command-input"
|
|
36
|
+
className={cn(
|
|
37
|
+
'h-8 min-w-0 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground disabled:opacity-50',
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
function CommandList({
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<typeof CommandPrimitive.List>) {
|
|
49
|
+
return (
|
|
50
|
+
<CommandPrimitive.List
|
|
51
|
+
data-slot="command-list"
|
|
52
|
+
className={cn(
|
|
53
|
+
'max-h-[min(50dvh,24rem)] scroll-py-1 overflow-x-hidden overflow-y-auto outline-none',
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
function CommandEmpty({
|
|
61
|
+
className,
|
|
62
|
+
...props
|
|
63
|
+
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
|
64
|
+
return (
|
|
65
|
+
<CommandPrimitive.Empty
|
|
66
|
+
data-slot="command-empty"
|
|
67
|
+
className={cn(
|
|
68
|
+
'px-4 py-6 text-center text-sm text-muted-foreground',
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
function CommandItem({
|
|
76
|
+
className,
|
|
77
|
+
...props
|
|
78
|
+
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
|
|
79
|
+
return (
|
|
80
|
+
<CommandPrimitive.Item
|
|
81
|
+
data-slot="command-item"
|
|
82
|
+
className={cn(
|
|
83
|
+
'relative flex cursor-default items-center gap-2 rounded-lg px-2 py-2 text-sm outline-none select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-muted data-[selected=true]:text-foreground',
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
export { Command, CommandInput, CommandList, CommandEmpty, CommandItem }
|