cortena-ui 0.1.0 → 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.
Files changed (68) hide show
  1. package/dist/index.d.ts +2004 -4
  2. package/dist/index.js +5949 -2
  3. package/dist/index.js.map +1 -1
  4. package/package.json +41 -5
  5. package/src/components/accordion.tsx +86 -0
  6. package/src/components/alert.tsx +81 -0
  7. package/src/components/avatar.tsx +53 -0
  8. package/src/components/badge.tsx +51 -0
  9. package/src/components/calendar.tsx +193 -0
  10. package/src/components/card.tsx +85 -0
  11. package/src/components/chart/chart.tsx +193 -0
  12. package/src/components/chart/container.tsx +324 -0
  13. package/src/components/chart/data.ts +101 -0
  14. package/src/components/chart/index.tsx +84 -0
  15. package/src/components/chart/nivo-charts.tsx +227 -0
  16. package/src/components/chart/nivo-theme.ts +136 -0
  17. package/src/components/chart/recharts-charts.tsx +232 -0
  18. package/src/components/chart/tokens.ts +210 -0
  19. package/src/components/chart/types.ts +142 -0
  20. package/src/components/checkbox.tsx +49 -0
  21. package/src/components/chip.tsx +49 -0
  22. package/src/components/combobox.tsx +405 -0
  23. package/src/components/command.tsx +225 -0
  24. package/src/components/data-table/data-table.tsx +731 -0
  25. package/src/components/data-table/export.ts +121 -0
  26. package/src/components/data-table/features.ts +123 -0
  27. package/src/components/data-table/index.tsx +45 -0
  28. package/src/components/data-table/parts.tsx +568 -0
  29. package/src/components/data-table/system-columns.tsx +91 -0
  30. package/src/components/data-table/types.ts +182 -0
  31. package/src/components/data-table/use-data-table.ts +224 -0
  32. package/src/components/data-table/use-server-source.ts +232 -0
  33. package/src/components/date-field.tsx +327 -0
  34. package/src/components/date-picker.tsx +230 -0
  35. package/src/components/dialog.tsx +146 -0
  36. package/src/components/dropdown-menu.tsx +314 -0
  37. package/src/components/dropzone.tsx +260 -0
  38. package/src/components/empty-state.tsx +91 -0
  39. package/src/components/error-banner.tsx +65 -0
  40. package/src/components/field.tsx +250 -0
  41. package/src/components/form.tsx +212 -0
  42. package/src/components/input.tsx +32 -0
  43. package/src/components/kbd.tsx +26 -0
  44. package/src/components/label.tsx +26 -0
  45. package/src/components/loading-skeleton.tsx +153 -0
  46. package/src/components/markdown.tsx +377 -0
  47. package/src/components/page-header.tsx +99 -0
  48. package/src/components/popover.tsx +133 -0
  49. package/src/components/progress.tsx +83 -0
  50. package/src/components/radio-group.tsx +48 -0
  51. package/src/components/scroll-area.tsx +74 -0
  52. package/src/components/section-card.tsx +75 -0
  53. package/src/components/segmented.tsx +81 -0
  54. package/src/components/select.tsx +222 -0
  55. package/src/components/separator.tsx +41 -0
  56. package/src/components/sheet.tsx +161 -0
  57. package/src/components/skeleton.tsx +22 -0
  58. package/src/components/sortable-list.tsx +275 -0
  59. package/src/components/spinner.tsx +32 -0
  60. package/src/components/status-dot.tsx +78 -0
  61. package/src/components/switch.tsx +48 -0
  62. package/src/components/tabs.tsx +109 -0
  63. package/src/components/textarea.tsx +75 -0
  64. package/src/components/toast.tsx +150 -0
  65. package/src/components/toolbar.tsx +63 -0
  66. package/src/components/tooltip.tsx +103 -0
  67. package/src/hooks/use-cortena-theme.ts +268 -0
  68. package/src/index.ts +91 -3
@@ -0,0 +1,405 @@
1
+ "use client";
2
+
3
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";
4
+ import { Check, ChevronDown, X } from "lucide-react";
5
+ import { createContext, useContext, type ReactNode } from "react";
6
+ import { cn } from "@/lib/cn";
7
+ import { inputClassName } from "@/components/input";
8
+
9
+ /**
10
+ * Combobox: a filterable text input with a listbox popup, on Base UI.
11
+ *
12
+ * Give the Root `items` (an array, or `[{ value, label }]`) and render the
13
+ * list with `<ComboboxList>{(item) => <ComboboxItem value={item} />}</ComboboxList>`;
14
+ * Base UI filters `items` against the input as the user types. Without
15
+ * `items` nothing is filtered and `ComboboxEmpty` never shows.
16
+ *
17
+ * - `ComboboxInput` is our `Input` with a trailing open/close trigger and,
18
+ * with `clearable`, an X button that clears the value.
19
+ * - `ComboboxContent` composes Portal + Positioner + Popup + List so
20
+ * consumers write one element; positioning props go to the Positioner.
21
+ * - Multiple selection: set `multiple` on the Root and wrap `ComboboxValue`
22
+ * (rendering a `ComboboxChip` per value) plus `ComboboxInput` in
23
+ * `ComboboxChips`. Inside `ComboboxChips` the input drops its own frame;
24
+ * the chips container carries the field look and anchors the popup.
25
+ * - `useComboboxFilter` is Base UI's `useFilter` for external filtering via
26
+ * `filteredItems`.
27
+ *
28
+ * `onValueChange` receives `(value, eventDetails)`; `onInputValueChange`
29
+ * reports the typed text.
30
+ */
31
+ function Combobox<Value, Multiple extends boolean | undefined = false, Item = Value>(
32
+ props: ComboboxPrimitive.Root.Props<Value, Multiple, Item>,
33
+ ) {
34
+ return <ComboboxPrimitive.Root {...props} />;
35
+ }
36
+
37
+ const InsideChipsContext = createContext(false);
38
+
39
+ const controlButtonClassName = cn(
40
+ "flex size-6 shrink-0 items-center justify-center rounded-[var(--ds-radius-sm)]",
41
+ "text-[color:var(--ds-muted-foreground)] outline-none",
42
+ "transition-[background-color,color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]",
43
+ "hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]",
44
+ "focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40",
45
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
46
+ "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
47
+ );
48
+
49
+ function ComboboxTrigger({ className, children, ...props }: ComboboxPrimitive.Trigger.Props) {
50
+ return (
51
+ <ComboboxPrimitive.Trigger
52
+ data-slot="combobox-trigger"
53
+ aria-label="Open"
54
+ className={cn(controlButtonClassName, className)}
55
+ {...props}
56
+ >
57
+ {children ?? (
58
+ <ComboboxPrimitive.Icon
59
+ data-slot="combobox-icon"
60
+ className="flex transition-transform duration-[var(--ds-duration-fast)] data-[popup-open]:rotate-180"
61
+ >
62
+ <ChevronDown />
63
+ </ComboboxPrimitive.Icon>
64
+ )}
65
+ </ComboboxPrimitive.Trigger>
66
+ );
67
+ }
68
+
69
+ function ComboboxClear({ className, children, ...props }: ComboboxPrimitive.Clear.Props) {
70
+ return (
71
+ <ComboboxPrimitive.Clear
72
+ data-slot="combobox-clear"
73
+ aria-label="Clear"
74
+ className={cn(controlButtonClassName, className)}
75
+ {...props}
76
+ >
77
+ {children ?? <X />}
78
+ </ComboboxPrimitive.Clear>
79
+ );
80
+ }
81
+
82
+ export interface ComboboxInputProps extends ComboboxPrimitive.Input.Props {
83
+ /** Show an X button that clears the selection while there is one. */
84
+ clearable?: boolean;
85
+ /** Class for the wrapper that holds the input and its buttons. */
86
+ wrapperClassName?: string;
87
+ }
88
+
89
+ function ComboboxInput({ className, clearable = false, wrapperClassName, ...props }: ComboboxInputProps) {
90
+ const insideChips = useContext(InsideChipsContext);
91
+ if (insideChips) {
92
+ return (
93
+ <ComboboxPrimitive.Input
94
+ data-slot="combobox-input"
95
+ className={cn(
96
+ "h-7 min-w-16 flex-1 bg-transparent outline-none",
97
+ "text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]",
98
+ "placeholder:text-[color:var(--ds-text-tertiary)]",
99
+ "disabled:cursor-not-allowed",
100
+ className,
101
+ )}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+ return (
107
+ <ComboboxPrimitive.InputGroup
108
+ data-slot="combobox-input-group"
109
+ className={cn("relative flex w-full items-center", wrapperClassName)}
110
+ >
111
+ <ComboboxPrimitive.Input
112
+ data-slot="combobox-input"
113
+ className={cn(inputClassName, clearable ? "pr-16" : "pr-9", className)}
114
+ {...props}
115
+ />
116
+ <div className="absolute inset-y-0 right-1.5 flex items-center gap-0.5">
117
+ {clearable ? <ComboboxClear /> : null}
118
+ <ComboboxTrigger />
119
+ </div>
120
+ </ComboboxPrimitive.InputGroup>
121
+ );
122
+ }
123
+
124
+ function ComboboxValue(props: ComboboxPrimitive.Value.Props) {
125
+ return <ComboboxPrimitive.Value {...props} />;
126
+ }
127
+
128
+ export interface ComboboxChipsProps extends ComboboxPrimitive.Chips.Props {
129
+ /** Class for the inner chip row; `className` styles the field frame. */
130
+ chipsClassName?: string;
131
+ }
132
+
133
+ function ComboboxChips({ className, chipsClassName, children, ...props }: ComboboxChipsProps) {
134
+ return (
135
+ <InsideChipsContext.Provider value>
136
+ <ComboboxPrimitive.InputGroup
137
+ data-slot="combobox-input-group"
138
+ className={cn(
139
+ "relative flex w-full min-w-0 items-start gap-1 rounded-[var(--ds-radius-md)] border border-[var(--ds-border)] bg-[var(--ds-muted)] py-1 pl-1.5 pr-1.5",
140
+ "transition-[border-color,box-shadow] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]",
141
+ "hover:border-[var(--ds-border-strong)]",
142
+ "focus-within:border-[var(--ds-ring)] focus-within:ring-[3px] focus-within:ring-[var(--ds-ring)]/40",
143
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
144
+ "data-[invalid]:border-[var(--ds-destructive)] data-[invalid]:ring-[3px] data-[invalid]:ring-[var(--ds-destructive)]/20",
145
+ className,
146
+ )}
147
+ >
148
+ <ComboboxPrimitive.Chips
149
+ data-slot="combobox-chips"
150
+ className={cn("flex min-h-7 min-w-0 flex-1 flex-wrap items-center gap-1 px-1", chipsClassName)}
151
+ {...props}
152
+ >
153
+ {children}
154
+ </ComboboxPrimitive.Chips>
155
+ <ComboboxTrigger className="mt-0.5" />
156
+ </ComboboxPrimitive.InputGroup>
157
+ </InsideChipsContext.Provider>
158
+ );
159
+ }
160
+
161
+ export interface ComboboxChipProps extends ComboboxPrimitive.Chip.Props {
162
+ /** Hide the remove button. */
163
+ removable?: boolean;
164
+ }
165
+
166
+ function ComboboxChip({ className, children, removable = true, ...props }: ComboboxChipProps) {
167
+ return (
168
+ <ComboboxPrimitive.Chip
169
+ data-slot="combobox-chip"
170
+ className={cn(
171
+ "inline-flex h-6 max-w-full cursor-default items-center gap-1 whitespace-nowrap border pl-2.5 outline-none",
172
+ removable ? "pr-1" : "pr-2.5",
173
+ "rounded-[var(--ds-radius-full)] border-[var(--ds-border-subtle)] bg-[var(--ds-hover)]",
174
+ "text-[length:var(--ds-text-caption)] font-medium text-[color:var(--ds-muted-foreground)]",
175
+ "focus-visible:border-[var(--ds-ring)] focus-visible:bg-[var(--ds-primary-soft)] focus-visible:text-[color:var(--ds-primary)]",
176
+ "data-[disabled]:opacity-50",
177
+ className,
178
+ )}
179
+ {...props}
180
+ >
181
+ <span className="truncate">{children}</span>
182
+ {removable ? (
183
+ <ComboboxPrimitive.ChipRemove
184
+ data-slot="combobox-chip-remove"
185
+ aria-label="Remove"
186
+ className={cn(
187
+ "flex size-4 shrink-0 items-center justify-center rounded-[var(--ds-radius-full)] outline-none",
188
+ "transition-[background-color,color] duration-[var(--ds-duration-fast)]",
189
+ "hover:bg-[var(--ds-border)] hover:text-[color:var(--ds-foreground)]",
190
+ "[&_svg]:size-3",
191
+ )}
192
+ >
193
+ <X />
194
+ </ComboboxPrimitive.ChipRemove>
195
+ ) : null}
196
+ </ComboboxPrimitive.Chip>
197
+ );
198
+ }
199
+
200
+ type PositionerProps = Pick<
201
+ ComboboxPrimitive.Positioner.Props,
202
+ | "side"
203
+ | "align"
204
+ | "sideOffset"
205
+ | "alignOffset"
206
+ | "anchor"
207
+ | "collisionBoundary"
208
+ | "collisionPadding"
209
+ | "collisionAvoidance"
210
+ | "sticky"
211
+ | "positionMethod"
212
+ >;
213
+
214
+ export interface ComboboxContentProps
215
+ extends Omit<ComboboxPrimitive.Popup.Props, "children">,
216
+ PositionerProps {
217
+ /** Items, or a render function called per filtered item (needs `items` on the Root). */
218
+ children?: ComboboxPrimitive.List.Props["children"];
219
+ /**
220
+ * Shown (via `ComboboxEmpty`, above the list) when nothing matches the query.
221
+ * Needs `items` on the Root.
222
+ */
223
+ empty?: ReactNode;
224
+ /** Class for the inner List element. */
225
+ listClassName?: string;
226
+ /** Keep the popup mounted in the DOM while hidden. */
227
+ keepMounted?: boolean;
228
+ /** Where to portal the popup; defaults to `<body>`. */
229
+ container?: ComboboxPrimitive.Portal.Props["container"];
230
+ }
231
+
232
+ function ComboboxContent({
233
+ className,
234
+ children,
235
+ empty,
236
+ listClassName,
237
+ side = "bottom",
238
+ align = "start",
239
+ sideOffset = 4,
240
+ alignOffset,
241
+ anchor,
242
+ collisionBoundary,
243
+ collisionPadding,
244
+ collisionAvoidance,
245
+ sticky,
246
+ positionMethod,
247
+ keepMounted,
248
+ container,
249
+ ...props
250
+ }: ComboboxContentProps) {
251
+ return (
252
+ <ComboboxPrimitive.Portal keepMounted={keepMounted} container={container}>
253
+ <ComboboxPrimitive.Positioner
254
+ data-slot="combobox-positioner"
255
+ side={side}
256
+ align={align}
257
+ sideOffset={sideOffset}
258
+ alignOffset={alignOffset}
259
+ anchor={anchor}
260
+ collisionBoundary={collisionBoundary}
261
+ collisionPadding={collisionPadding}
262
+ collisionAvoidance={collisionAvoidance}
263
+ sticky={sticky}
264
+ positionMethod={positionMethod}
265
+ className="isolate z-50 outline-none"
266
+ >
267
+ <ComboboxPrimitive.Popup
268
+ data-slot="combobox-content"
269
+ className={cn(
270
+ "flex max-h-[min(var(--available-height,none),24rem)] w-[var(--anchor-width,auto)] min-w-[var(--anchor-width,auto)] flex-col overflow-hidden",
271
+ "rounded-[var(--ds-radius-lg)] border border-[var(--ds-border)]",
272
+ "bg-[var(--ds-popover)] text-[color:var(--ds-popover-foreground)] shadow-[var(--ds-shadow-lg)]",
273
+ "outline-none",
274
+ "open:animate-in open:fade-in-0 open:zoom-in-95",
275
+ "closed:animate-out closed:fade-out-0 closed:zoom-out-95",
276
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
277
+ "duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]",
278
+ className,
279
+ )}
280
+ {...props}
281
+ >
282
+ {empty !== undefined ? <ComboboxEmpty>{empty}</ComboboxEmpty> : null}
283
+ <ComboboxPrimitive.List
284
+ data-slot="combobox-list"
285
+ className={cn("flex flex-col overflow-y-auto overflow-x-hidden p-1 outline-none", listClassName)}
286
+ >
287
+ {children}
288
+ </ComboboxPrimitive.List>
289
+ </ComboboxPrimitive.Popup>
290
+ </ComboboxPrimitive.Positioner>
291
+ </ComboboxPrimitive.Portal>
292
+ );
293
+ }
294
+
295
+ function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) {
296
+ return (
297
+ <ComboboxPrimitive.Item
298
+ data-slot="combobox-item"
299
+ className={cn(
300
+ "relative flex w-full cursor-default select-none items-center gap-2 py-1.5 pr-8 pl-2",
301
+ "rounded-[var(--ds-radius-sm)] text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-foreground)] outline-none",
302
+ "data-[highlighted]:bg-[var(--ds-hover)] data-[highlighted]:text-[color:var(--ds-foreground)]",
303
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
304
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
305
+ className,
306
+ )}
307
+ {...props}
308
+ >
309
+ <span data-slot="combobox-item-text" className="flex-1 truncate">
310
+ {children}
311
+ </span>
312
+ <ComboboxPrimitive.ItemIndicator
313
+ data-slot="combobox-item-indicator"
314
+ className="absolute right-2 flex size-4 items-center justify-center text-[color:var(--ds-primary)]"
315
+ >
316
+ <Check className="size-4" />
317
+ </ComboboxPrimitive.ItemIndicator>
318
+ </ComboboxPrimitive.Item>
319
+ );
320
+ }
321
+
322
+ /** Renders per filtered item; use inside a `ComboboxGroup` given `items`, or for a flat list pass a function as `ComboboxContent` children instead. */
323
+ function ComboboxCollection(props: ComboboxPrimitive.Collection.Props) {
324
+ return <ComboboxPrimitive.Collection {...props} />;
325
+ }
326
+
327
+ function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
328
+ return (
329
+ <ComboboxPrimitive.Empty
330
+ data-slot="combobox-empty"
331
+ className={cn(
332
+ "px-2 py-1.5 text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-muted-foreground)] empty:m-0 empty:p-0",
333
+ className,
334
+ )}
335
+ {...props}
336
+ />
337
+ );
338
+ }
339
+
340
+ function ComboboxStatus({ className, ...props }: ComboboxPrimitive.Status.Props) {
341
+ return (
342
+ <ComboboxPrimitive.Status
343
+ data-slot="combobox-status"
344
+ className={cn(
345
+ "px-2 py-1.5 text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-muted-foreground)] empty:m-0 empty:p-0",
346
+ className,
347
+ )}
348
+ {...props}
349
+ />
350
+ );
351
+ }
352
+
353
+ function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
354
+ return (
355
+ <ComboboxPrimitive.Group
356
+ data-slot="combobox-group"
357
+ className={cn("flex flex-col", className)}
358
+ {...props}
359
+ />
360
+ );
361
+ }
362
+
363
+ function ComboboxGroupLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) {
364
+ return (
365
+ <ComboboxPrimitive.GroupLabel
366
+ data-slot="combobox-group-label"
367
+ className={cn(
368
+ "px-2 py-1.5 text-[length:var(--ds-text-caption)] font-medium text-[color:var(--ds-muted-foreground)]",
369
+ className,
370
+ )}
371
+ {...props}
372
+ />
373
+ );
374
+ }
375
+
376
+ function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) {
377
+ return (
378
+ <ComboboxPrimitive.Separator
379
+ data-slot="combobox-separator"
380
+ className={cn("-mx-1 my-1 h-px bg-[var(--ds-border)]", className)}
381
+ {...props}
382
+ />
383
+ );
384
+ }
385
+
386
+ const useComboboxFilter = ComboboxPrimitive.useFilter;
387
+
388
+ export {
389
+ Combobox,
390
+ ComboboxInput,
391
+ ComboboxTrigger,
392
+ ComboboxClear,
393
+ ComboboxValue,
394
+ ComboboxContent,
395
+ ComboboxItem,
396
+ ComboboxCollection,
397
+ ComboboxEmpty,
398
+ ComboboxStatus,
399
+ ComboboxGroup,
400
+ ComboboxGroupLabel,
401
+ ComboboxSeparator,
402
+ ComboboxChips,
403
+ ComboboxChip,
404
+ useComboboxFilter,
405
+ };
@@ -0,0 +1,225 @@
1
+ "use client";
2
+
3
+ import { Command as CommandPrimitive } from "cmdk";
4
+ import { Search } from "lucide-react";
5
+ import type * as React from "react";
6
+ import { cn } from "@/lib/cn";
7
+ import {
8
+ Dialog,
9
+ DialogContent,
10
+ type DialogContentProps,
11
+ DialogDescription,
12
+ DialogTitle,
13
+ } from "@/components/dialog";
14
+
15
+ /**
16
+ * Command palette on cmdk, styled like DropdownMenu.
17
+ *
18
+ * cmdk filters and ranks items itself from each item's text (or `value` /
19
+ * `keywords`); `CommandEmpty` renders when nothing matches. The selected
20
+ * (keyboard-highlighted) item carries `data-selected="true"`; `onSelect` on
21
+ * `CommandItem` fires on click and on Enter.
22
+ *
23
+ * `CommandDialog` puts a palette inside our Dialog with no header padding;
24
+ * it needs a `title` (visually hidden) so the dialog is labelled. Wire
25
+ * `open` / `onOpenChange` to a global shortcut (⌘K) for the usual pattern.
26
+ */
27
+ function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
28
+ return (
29
+ <CommandPrimitive
30
+ data-slot="command"
31
+ className={cn(
32
+ "flex h-full w-full flex-col overflow-hidden outline-none",
33
+ "rounded-[var(--ds-radius-lg)] bg-[var(--ds-popover)] text-[color:var(--ds-popover-foreground)]",
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ export interface CommandDialogProps extends React.ComponentProps<typeof Dialog> {
42
+ /** Accessible name for the dialog; visually hidden. */
43
+ title?: string;
44
+ /** Accessible description for the dialog; visually hidden. */
45
+ description?: string;
46
+ /** Class for the dialog popup. */
47
+ className?: string;
48
+ /** Class for the inner Command root. */
49
+ commandClassName?: string;
50
+ /** Where to portal the dialog; defaults to `<body>`. */
51
+ container?: DialogContentProps["container"];
52
+ /** Keep the dialog mounted in the DOM while hidden. */
53
+ keepMounted?: boolean;
54
+ /** Props for the inner cmdk root (`filter`, `shouldFilter`, `loop`, `value`, ...). */
55
+ commandProps?: Omit<React.ComponentProps<typeof CommandPrimitive>, "children" | "className">;
56
+ children?: React.ReactNode;
57
+ }
58
+
59
+ function CommandDialog({
60
+ title = "Command palette",
61
+ description = "Search for a command to run",
62
+ className,
63
+ commandClassName,
64
+ container,
65
+ keepMounted,
66
+ commandProps,
67
+ children,
68
+ ...props
69
+ }: CommandDialogProps) {
70
+ return (
71
+ <Dialog {...props}>
72
+ <DialogContent
73
+ data-slot="command-dialog"
74
+ container={container}
75
+ keepMounted={keepMounted}
76
+ className={cn("max-w-xl overflow-hidden p-0", className)}
77
+ >
78
+ <DialogTitle className="sr-only">{title}</DialogTitle>
79
+ <DialogDescription className="sr-only">{description}</DialogDescription>
80
+ <Command
81
+ className={cn(
82
+ "rounded-none",
83
+ "[&_[data-slot=command-input-wrapper]]:h-12 [&_[data-slot=command-input-wrapper]_svg]:size-5",
84
+ "[&_[data-slot=command-input]]:h-12 [&_[data-slot=command-input]]:text-[length:var(--ds-text-body-sm)]",
85
+ "[&_[data-slot=command-list]]:max-h-80",
86
+ "[&_[data-slot=command-item]]:py-2 [&_[data-slot=command-item]_svg]:size-5",
87
+ commandClassName,
88
+ )}
89
+ {...commandProps}
90
+ >
91
+ {children}
92
+ </Command>
93
+ </DialogContent>
94
+ </Dialog>
95
+ );
96
+ }
97
+
98
+ function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
99
+ return (
100
+ <div
101
+ data-slot="command-input-wrapper"
102
+ className="flex h-10 items-center gap-2 border-b border-[var(--ds-border)] px-3"
103
+ >
104
+ <Search className="size-4 shrink-0 text-[color:var(--ds-muted-foreground)]" />
105
+ <CommandPrimitive.Input
106
+ data-slot="command-input"
107
+ className={cn(
108
+ "flex h-10 w-full min-w-0 bg-transparent py-2 outline-none",
109
+ "text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-foreground)]",
110
+ "placeholder:text-[color:var(--ds-text-tertiary)]",
111
+ "disabled:cursor-not-allowed disabled:opacity-50",
112
+ className,
113
+ )}
114
+ {...props}
115
+ />
116
+ </div>
117
+ );
118
+ }
119
+
120
+ function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
121
+ return (
122
+ <CommandPrimitive.List
123
+ data-slot="command-list"
124
+ className={cn(
125
+ "max-h-72 scroll-py-1 overflow-y-auto overflow-x-hidden p-1 outline-none",
126
+ className,
127
+ )}
128
+ {...props}
129
+ />
130
+ );
131
+ }
132
+
133
+ function CommandEmpty({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
134
+ return (
135
+ <CommandPrimitive.Empty
136
+ data-slot="command-empty"
137
+ className={cn(
138
+ "py-6 text-center text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-muted-foreground)]",
139
+ className,
140
+ )}
141
+ {...props}
142
+ />
143
+ );
144
+ }
145
+
146
+ function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
147
+ return (
148
+ <CommandPrimitive.Group
149
+ data-slot="command-group"
150
+ className={cn(
151
+ "overflow-hidden text-[color:var(--ds-foreground)]",
152
+ "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5",
153
+ "[&_[cmdk-group-heading]]:text-[length:var(--ds-text-caption)] [&_[cmdk-group-heading]]:font-medium",
154
+ "[&_[cmdk-group-heading]]:text-[color:var(--ds-muted-foreground)]",
155
+ className,
156
+ )}
157
+ {...props}
158
+ />
159
+ );
160
+ }
161
+
162
+ function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
163
+ return (
164
+ <CommandPrimitive.Separator
165
+ data-slot="command-separator"
166
+ className={cn("-mx-1 my-1 h-px bg-[var(--ds-border-subtle)]", className)}
167
+ {...props}
168
+ />
169
+ );
170
+ }
171
+
172
+ export interface CommandItemProps extends React.ComponentProps<typeof CommandPrimitive.Item> {
173
+ variant?: "default" | "destructive";
174
+ }
175
+
176
+ function CommandItem({ className, variant = "default", ...props }: CommandItemProps) {
177
+ return (
178
+ <CommandPrimitive.Item
179
+ data-slot="command-item"
180
+ data-variant={variant}
181
+ className={cn(
182
+ "relative flex cursor-default select-none items-center gap-2 px-2 py-1.5 outline-none",
183
+ "rounded-[var(--ds-radius-sm)] text-[length:var(--ds-text-body-sm)] text-[color:var(--ds-foreground)]",
184
+ "transition-colors duration-[var(--ds-duration-fast)]",
185
+ "data-[selected=true]:bg-[var(--ds-hover)] data-[selected=true]:text-[color:var(--ds-foreground)]",
186
+ "data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
187
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
188
+ variant === "destructive" &&
189
+ "text-[color:var(--ds-destructive)] data-[selected=true]:bg-[var(--ds-destructive-soft)] data-[selected=true]:text-[color:var(--ds-destructive)]",
190
+ className,
191
+ )}
192
+ {...props}
193
+ />
194
+ );
195
+ }
196
+
197
+ function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
198
+ return (
199
+ <span
200
+ data-slot="command-shortcut"
201
+ className={cn(
202
+ "ml-auto text-[length:var(--ds-text-caption)] tracking-widest text-[color:var(--ds-muted-foreground)]",
203
+ className,
204
+ )}
205
+ {...props}
206
+ />
207
+ );
208
+ }
209
+
210
+ function CommandLoading(props: React.ComponentProps<typeof CommandPrimitive.Loading>) {
211
+ return <CommandPrimitive.Loading data-slot="command-loading" {...props} />;
212
+ }
213
+
214
+ export {
215
+ Command,
216
+ CommandDialog,
217
+ CommandInput,
218
+ CommandList,
219
+ CommandEmpty,
220
+ CommandGroup,
221
+ CommandItem,
222
+ CommandSeparator,
223
+ CommandShortcut,
224
+ CommandLoading,
225
+ };