@lexibase/rdx-ui 1.0.0-beta.1 → 1.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 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`: Radix dropdowns, buttons,
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,90 @@ 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` and the RDX theme.
47
+ `components.json` selects shadcn/ui's `base-nova` style.
48
+ Use `render` for interactive composition and `ButtonLink` for navigation:
49
+
50
+ ```tsx
51
+ import { Button } from '@lexibase/rdx-ui/components/button'
52
+ import { ButtonLink } from '@lexibase/rdx-ui/components/button-link'
53
+ import {
54
+ DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,
55
+ DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem,
56
+ DropdownMenuLinkItem,
57
+ } from '@lexibase/rdx-ui/components/dropdown-menu'
58
+ import Link from 'next/link'
59
+
60
+ <ButtonLink href="/docs" variant="outline">Documentation</ButtonLink>
61
+
62
+ <DropdownMenu>
63
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
64
+ Actions
65
+ </DropdownMenuTrigger>
66
+ <DropdownMenuContent align="start">
67
+ <DropdownMenuGroup>
68
+ <DropdownMenuLabel>Project</DropdownMenuLabel>
69
+ <DropdownMenuItem onClick={() => console.log('Action')}>Run action</DropdownMenuItem>
70
+ <DropdownMenuLinkItem closeOnClick render={<Link href="/docs" />}>
71
+ Documentation
72
+ </DropdownMenuLinkItem>
73
+ </DropdownMenuGroup>
74
+ </DropdownMenuContent>
75
+ </DropdownMenu>
76
+ ```
77
+
78
+ `ButtonLink` renders a Next.js link with button styling, not `role="button"`.
79
+ `Button` renders a native button; use `type="submit"` explicitly for form submission.
80
+ Menu actions use `onClick`. Labels must be inside `DropdownMenuGroup`.
81
+ Checkbox values are boolean; checkbox/radio items remain open unless
82
+ `closeOnClick` is enabled.
83
+ Navigation items use `DropdownMenuLinkItem` (with `closeOnClick` when desired).
84
+ Components accept Base UI state-aware `className` callbacks and `render` props.
85
+ For custom CSS, use Base UI's
86
+ `data-open`, `data-closed`, `data-highlighted`, `data-popup-open`,
87
+ `--available-height` and `--transform-origin`. `Separator` remains decorative by
88
+ default; set `decorative={false}` for an accessible separator role.
89
+
90
+ See [Base UI Menu](https://base-ui.com/react/components/menu) and
91
+ [shadcn Base UI](https://ui.shadcn.com/docs/components/base/button).
92
+
93
+ ## Documentation tabs
94
+
95
+ `Tabs`, `TabsList`, `TabsTrigger` and `TabsContent` wrap Base UI Tabs and are
96
+ automatically registered in MDX. Import them from
97
+ `@lexibase/rdx-ui/components/tabs` in React files. Use an explicit `defaultValue`,
98
+ matching string values and an `aria-label` on each list. Arrow keys move focus;
99
+ Enter or Space activates a tab. The list scrolls on small screens. See
100
+ the repository's `MDX.md` and `docs/examples/tabs.mdx` for complete examples.
101
+
102
+ ## Documentation accordion
103
+
104
+ `Accordion`, `AccordionItem`, `AccordionTrigger` and `AccordionContent` wrap
105
+ Base UI Accordion and are registered in MDX. Import them from
106
+ `@lexibase/rdx-ui/components/accordion` in React files. One item opens at a time
107
+ by default; `multiple` allows independent expansion. Triggers use native buttons
108
+ inside `h3` headings with normal Tab navigation and Enter/Space activation.
109
+ 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.1",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
+ "engines": {
6
+ "node": "^22.18.0 || ^24.11.0"
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,46 +22,48 @@
18
22
  "directory": "packages/rdx-ui"
19
23
  },
20
24
  "dependencies": {
21
- "@tailwindcss/postcss": "^4.1.13",
22
- "postcss": "^8.5.6",
23
- "tailwindcss": "^4.1.13",
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.542.0",
30
+ "lucide-react": "^0.577.0",
30
31
  "next-themes": "^0.4.6",
31
- "tailwind-merge": "^3.3.1",
32
- "tw-animate-css": "^1.3.8"
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"
33
37
  },
34
38
  "peerDependencies": {
35
- "next": "^15.5.0",
36
- "react": "^19.1.0",
37
- "react-dom": "^19.1.0"
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.1.13",
44
+ "@tailwindcss/postcss": "^4.3.3",
41
45
  "@types/node": "^22.15.3",
42
- "@types/react": "19.1.0",
43
- "@types/react-dom": "19.1.1",
44
- "eslint": "^9.34.0",
45
- "postcss": "^8.5.6",
46
- "next": "^15.5.0",
47
- "react": "19.1.0",
48
- "react-dom": "19.1.0",
49
- "tailwindcss": "^4.1.13",
50
- "typescript": "5.9.2",
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": "./src/styles/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
- "./hooks/*": "./src/hooks/*.ts",
60
- "./contenxts/*": "./src/contexts/*",
61
67
  "./providers/*": "./src/providers/*.tsx"
62
68
  },
63
69
  "scripts": {
@@ -0,0 +1,2 @@
1
+ // Keep Tailwind's native dependencies in the PostCSS worker, not the app bundle.
2
+ module.exports = require('@tailwindcss/postcss')
@@ -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
- [require.resolve('@tailwindcss/postcss')]: {},
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 * as React from 'react'
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
- }: React.ComponentProps<'button'> &
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
- <Comp
54
+ <ButtonPrimitive
63
55
  data-slot="button"
64
- className={cn(buttonVariants({ variant, size, active, 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 }