@zentauri-ui/zentauri-components 1.7.1 → 1.7.3

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 (92) hide show
  1. package/README.md +9 -5
  2. package/cli/registry.json +2 -0
  3. package/dist/chunk-BC6M42HQ.mjs +251 -0
  4. package/dist/chunk-BC6M42HQ.mjs.map +1 -0
  5. package/dist/chunk-KEKMMNL5.mjs +600 -0
  6. package/dist/chunk-KEKMMNL5.mjs.map +1 -0
  7. package/dist/chunk-NZDHSIIC.js +616 -0
  8. package/dist/chunk-NZDHSIIC.js.map +1 -0
  9. package/dist/chunk-QSPXPU72.js +259 -0
  10. package/dist/chunk-QSPXPU72.js.map +1 -0
  11. package/dist/design-system/command.d.ts +41 -0
  12. package/dist/design-system/command.d.ts.map +1 -0
  13. package/dist/design-system/index.d.ts +2 -0
  14. package/dist/design-system/index.d.ts.map +1 -1
  15. package/dist/design-system/popover.d.ts +40 -0
  16. package/dist/design-system/popover.d.ts.map +1 -0
  17. package/dist/ui/command/animated/animations.d.ts +3 -0
  18. package/dist/ui/command/animated/animations.d.ts.map +1 -0
  19. package/dist/ui/command/animated/command-content-animated.d.ts +6 -0
  20. package/dist/ui/command/animated/command-content-animated.d.ts.map +1 -0
  21. package/dist/ui/command/animated/index.d.ts +4 -0
  22. package/dist/ui/command/animated/index.d.ts.map +1 -0
  23. package/dist/ui/command/animated/types.d.ts +9 -0
  24. package/dist/ui/command/animated/types.d.ts.map +1 -0
  25. package/dist/ui/command/animated.js +92 -0
  26. package/dist/ui/command/animated.js.map +1 -0
  27. package/dist/ui/command/animated.mjs +89 -0
  28. package/dist/ui/command/animated.mjs.map +1 -0
  29. package/dist/ui/command/command-base.d.ts +53 -0
  30. package/dist/ui/command/command-base.d.ts.map +1 -0
  31. package/dist/ui/command/command.d.ts +6 -0
  32. package/dist/ui/command/command.d.ts.map +1 -0
  33. package/dist/ui/command/index.d.ts +5 -0
  34. package/dist/ui/command/index.d.ts.map +1 -0
  35. package/dist/ui/command/types.d.ts +111 -0
  36. package/dist/ui/command/types.d.ts.map +1 -0
  37. package/dist/ui/command/variants.d.ts +15 -0
  38. package/dist/ui/command/variants.d.ts.map +1 -0
  39. package/dist/ui/command.js +69 -0
  40. package/dist/ui/command.js.map +1 -0
  41. package/dist/ui/command.mjs +16 -0
  42. package/dist/ui/command.mjs.map +1 -0
  43. package/dist/ui/popover/animated/animations.d.ts +3 -0
  44. package/dist/ui/popover/animated/animations.d.ts.map +1 -0
  45. package/dist/ui/popover/animated/index.d.ts +4 -0
  46. package/dist/ui/popover/animated/index.d.ts.map +1 -0
  47. package/dist/ui/popover/animated/popover-content-animated.d.ts +3 -0
  48. package/dist/ui/popover/animated/popover-content-animated.d.ts.map +1 -0
  49. package/dist/ui/popover/animated/types.d.ts +9 -0
  50. package/dist/ui/popover/animated/types.d.ts.map +1 -0
  51. package/dist/ui/popover/animated.js +67 -0
  52. package/dist/ui/popover/animated.js.map +1 -0
  53. package/dist/ui/popover/animated.mjs +64 -0
  54. package/dist/ui/popover/animated.mjs.map +1 -0
  55. package/dist/ui/popover/index.d.ts +4 -0
  56. package/dist/ui/popover/index.d.ts.map +1 -0
  57. package/dist/ui/popover/popover-base.d.ts +8 -0
  58. package/dist/ui/popover/popover-base.d.ts.map +1 -0
  59. package/dist/ui/popover/popover.d.ts +2 -0
  60. package/dist/ui/popover/popover.d.ts.map +1 -0
  61. package/dist/ui/popover/types.d.ts +34 -0
  62. package/dist/ui/popover/types.d.ts.map +1 -0
  63. package/dist/ui/popover/variants.d.ts +6 -0
  64. package/dist/ui/popover/variants.d.ts.map +1 -0
  65. package/dist/ui/popover.js +34 -0
  66. package/dist/ui/popover.js.map +1 -0
  67. package/dist/ui/popover.mjs +5 -0
  68. package/dist/ui/popover.mjs.map +1 -0
  69. package/package.json +1 -1
  70. package/src/design-system/command.ts +80 -0
  71. package/src/design-system/index.ts +2 -0
  72. package/src/design-system/popover.ts +66 -0
  73. package/src/ui/command/animated/animations.ts +29 -0
  74. package/src/ui/command/animated/command-content-animated.tsx +58 -0
  75. package/src/ui/command/animated/index.ts +10 -0
  76. package/src/ui/command/animated/types.ts +23 -0
  77. package/src/ui/command/command-base.tsx +660 -0
  78. package/src/ui/command/command.test.tsx +130 -0
  79. package/src/ui/command/command.tsx +8 -0
  80. package/src/ui/command/index.ts +34 -0
  81. package/src/ui/command/types.ts +129 -0
  82. package/src/ui/command/variants.ts +41 -0
  83. package/src/ui/popover/animated/animations.ts +15 -0
  84. package/src/ui/popover/animated/index.ts +10 -0
  85. package/src/ui/popover/animated/popover-content-animated.tsx +54 -0
  86. package/src/ui/popover/animated/types.ts +18 -0
  87. package/src/ui/popover/index.ts +18 -0
  88. package/src/ui/popover/popover-base.tsx +261 -0
  89. package/src/ui/popover/popover.test.tsx +84 -0
  90. package/src/ui/popover/popover.tsx +8 -0
  91. package/src/ui/popover/types.ts +38 -0
  92. package/src/ui/popover/variants.ts +21 -0
@@ -0,0 +1,660 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useId,
9
+ useMemo,
10
+ useRef,
11
+ useState,
12
+ type ReactNode,
13
+ type RefObject,
14
+ } from "react";
15
+ import { createPortal } from "react-dom";
16
+
17
+ import { cn } from "../../lib/utils";
18
+ import { useFocusManagement } from "../../hooks/useFocusManagement";
19
+
20
+ import type {
21
+ CommandContentProps,
22
+ CommandCtx,
23
+ CommandGroupProps,
24
+ CommandInputProps,
25
+ CommandItemProps,
26
+ CommandListProps,
27
+ CommandProps,
28
+ CommandSectionProps,
29
+ CommandTriggerProps,
30
+ ItemMeta,
31
+ RegisteredItem,
32
+ CommandContentVariantProps,
33
+ CommandContentLayerProps,
34
+ CommandContentOverlayRenderProps,
35
+ CommandContentPanelRenderProps
36
+ } from "./types";
37
+ import {
38
+ commandContentVariants,
39
+ commandEmptyVariants,
40
+ commandFooterVariants,
41
+ commandGroupHeadingVariants,
42
+ commandInputRowVariants,
43
+ commandInputVariants,
44
+ commandItemVariants,
45
+ commandListVariants,
46
+ commandOverlayVariants,
47
+ commandSeparatorVariants,
48
+ commandTriggerVariants
49
+ } from "./variants";
50
+
51
+ const CommandContext = createContext<CommandCtx | null>(null);
52
+
53
+ export function useCommandContext(component: string): CommandCtx {
54
+ const ctx = useContext(CommandContext);
55
+ if (!ctx) {
56
+ throw new Error(`${component} must be used within <Command>`);
57
+ }
58
+ return ctx;
59
+ }
60
+
61
+ function itemMatches(
62
+ value: string,
63
+ meta: ItemMeta,
64
+ normalized: string,
65
+ ): boolean {
66
+ if (!normalized) {
67
+ return true;
68
+ }
69
+ if (value.toLowerCase().includes(normalized)) {
70
+ return true;
71
+ }
72
+ if (meta.searchText?.toLowerCase().includes(normalized)) {
73
+ return true;
74
+ }
75
+ return Boolean(
76
+ meta.keywords?.some((keyword) =>
77
+ keyword.toLowerCase().includes(normalized),
78
+ ),
79
+ );
80
+ }
81
+
82
+ function isEditableEventTarget(target: EventTarget | null): boolean {
83
+ if (!(target instanceof HTMLElement)) {
84
+ return false;
85
+ }
86
+ if (target.isContentEditable) {
87
+ return true;
88
+ }
89
+ return Boolean(
90
+ target.closest("input, textarea, select, [contenteditable=true]"),
91
+ );
92
+ }
93
+
94
+ function useCommandPortalTarget(): HTMLElement | null {
95
+ const [isMounted, setIsMounted] = useState(false);
96
+
97
+ useEffect(() => {
98
+ setIsMounted(true);
99
+ }, []);
100
+
101
+ if (!isMounted) {
102
+ return null;
103
+ }
104
+
105
+ return document.body;
106
+ }
107
+
108
+ export function CommandPortal({ children }: { children: ReactNode }) {
109
+ const portalTarget = useCommandPortalTarget();
110
+
111
+ if (!portalTarget) {
112
+ return null;
113
+ }
114
+
115
+ return createPortal(children, portalTarget);
116
+ }
117
+
118
+ export function CommandPortalFrame({ children }: { children: ReactNode }) {
119
+ return (
120
+ <div className="fixed inset-0 z-9999" data-slot="command-portal">
121
+ {children}
122
+ </div>
123
+ );
124
+ }
125
+
126
+ export function CommandContentLayer({
127
+ className,
128
+ size,
129
+ appearance,
130
+ children,
131
+ ref,
132
+ id,
133
+ style,
134
+ componentName,
135
+ renderPresence,
136
+ renderOverlay,
137
+ renderPanel,
138
+ }: CommandContentLayerProps) {
139
+ const { open, setOpen, labelId, contentRef, triggerRef } =
140
+ useCommandContext(componentName);
141
+
142
+ useFocusManagement({
143
+ open,
144
+ setOpen,
145
+ contentRef,
146
+ triggerRef,
147
+ });
148
+
149
+ const overlayProps: CommandContentOverlayRenderProps = {
150
+ role: "presentation",
151
+ "data-slot": "command-overlay",
152
+ className: commandOverlayVariants(),
153
+ onClick: () => setOpen(false),
154
+ };
155
+
156
+ const panelProps: CommandContentPanelRenderProps = {
157
+ ref: (node) => {
158
+ contentRef.current = node;
159
+ if (typeof ref === "function") {
160
+ ref(node);
161
+ } else if (ref) {
162
+ (ref as RefObject<HTMLDivElement | null>).current = node;
163
+ }
164
+ },
165
+ role: "dialog",
166
+ "aria-modal": true,
167
+ "aria-labelledby": labelId,
168
+ "data-slot": "command-content",
169
+ tabIndex: -1,
170
+ className: cn(commandContentVariants({ size, appearance }), className),
171
+ id,
172
+ style,
173
+ children,
174
+ };
175
+
176
+ const content = open ? (
177
+ <CommandPortalFrame>
178
+ {renderOverlay ? renderOverlay(overlayProps) : <div {...overlayProps} />}
179
+ {renderPanel ? renderPanel(panelProps) : <div {...panelProps} />}
180
+ </CommandPortalFrame>
181
+ ) : null;
182
+
183
+ return (
184
+ <CommandPortal>
185
+ {renderPresence ? renderPresence(content) : content}
186
+ </CommandPortal>
187
+ );
188
+ }
189
+
190
+ export function Command({
191
+ open,
192
+ defaultOpen = false,
193
+ onOpenChange,
194
+ hotkey,
195
+ label = "Command menu",
196
+ children,
197
+ }: CommandProps) {
198
+ const isControlled = open !== undefined;
199
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
200
+ const resolvedOpen = isControlled ? Boolean(open) : uncontrolledOpen;
201
+
202
+ const setOpen = useCallback(
203
+ (next: boolean) => {
204
+ if (!isControlled) {
205
+ setUncontrolledOpen(next);
206
+ }
207
+ onOpenChange?.(next);
208
+ },
209
+ [isControlled, onOpenChange],
210
+ );
211
+
212
+ const labelId = useId();
213
+ const listId = useId();
214
+ const contentRef = useRef<HTMLDivElement | null>(null);
215
+ const triggerRef = useRef<HTMLElement | null>(null);
216
+ const inputRef = useRef<HTMLInputElement | null>(null);
217
+
218
+ const [query, setQuery] = useState("");
219
+ const [activeValue, setActiveValue] = useState<string | null>(null);
220
+ const itemsRef = useRef<RegisteredItem[]>([]);
221
+ const [registryVersion, setRegistryVersion] = useState(0);
222
+
223
+ const registerItem = useCallback((item: RegisteredItem) => {
224
+ itemsRef.current = [...itemsRef.current, item];
225
+ setRegistryVersion((version) => version + 1);
226
+ return () => {
227
+ itemsRef.current = itemsRef.current.filter((entry) => entry !== item);
228
+ setRegistryVersion((version) => version + 1);
229
+ };
230
+ }, []);
231
+
232
+ const invalidateRegistry = useCallback(() => {
233
+ setRegistryVersion((version) => version + 1);
234
+ }, []);
235
+
236
+ const visibleValues = useMemo(() => {
237
+ void registryVersion;
238
+ const normalized = query.trim().toLowerCase();
239
+ return itemsRef.current
240
+ .filter((item) => {
241
+ const meta = item.metaRef.current;
242
+ if (meta.disabled) {
243
+ return false;
244
+ }
245
+ return itemMatches(item.value, meta, normalized);
246
+ })
247
+ .map((item) => item.value);
248
+ }, [query, registryVersion]);
249
+
250
+ const visibleSet = useMemo(() => new Set(visibleValues), [visibleValues]);
251
+ const isVisible = useCallback(
252
+ (value: string) => visibleSet.has(value),
253
+ [visibleSet],
254
+ );
255
+
256
+ const selectValue = useCallback((value: string) => {
257
+ const entry = itemsRef.current.find((item) => item.value === value);
258
+ if (!entry || entry.metaRef.current.disabled) {
259
+ return false;
260
+ }
261
+ entry?.metaRef.current.onSelect?.(value);
262
+ return true;
263
+ }, []);
264
+
265
+ // Reset transient state when the palette closes; keep active in sync with results.
266
+ useEffect(() => {
267
+ if (!resolvedOpen) {
268
+ setQuery("");
269
+ setActiveValue(null);
270
+ }
271
+ }, [resolvedOpen]);
272
+
273
+ useEffect(() => {
274
+ if (!resolvedOpen) {
275
+ return;
276
+ }
277
+ if (activeValue && visibleSet.has(activeValue)) {
278
+ return;
279
+ }
280
+ setActiveValue(visibleValues[0] ?? null);
281
+ }, [resolvedOpen, activeValue, visibleSet, visibleValues]);
282
+
283
+ // Global hotkey: meta/ctrl + key toggles the palette.
284
+ useEffect(() => {
285
+ if (!hotkey) {
286
+ return;
287
+ }
288
+ const key = (typeof hotkey === "string" ? hotkey : "k").toLowerCase();
289
+ const handler = (event: KeyboardEvent) => {
290
+ if (isEditableEventTarget(event.target)) {
291
+ return;
292
+ }
293
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === key) {
294
+ event.preventDefault();
295
+ setOpen(!resolvedOpen);
296
+ }
297
+ };
298
+ window.addEventListener("keydown", handler);
299
+ return () => window.removeEventListener("keydown", handler);
300
+ }, [hotkey, resolvedOpen, setOpen]);
301
+
302
+ const ctx = useMemo<CommandCtx>(
303
+ () => ({
304
+ open: resolvedOpen,
305
+ setOpen,
306
+ labelId,
307
+ listId,
308
+ query,
309
+ setQuery,
310
+ activeValue,
311
+ setActiveValue,
312
+ visibleValues,
313
+ isVisible,
314
+ registerItem,
315
+ invalidateRegistry,
316
+ selectValue,
317
+ contentRef,
318
+ triggerRef,
319
+ inputRef,
320
+ }),
321
+ [
322
+ resolvedOpen,
323
+ setOpen,
324
+ labelId,
325
+ listId,
326
+ query,
327
+ activeValue,
328
+ visibleValues,
329
+ isVisible,
330
+ registerItem,
331
+ invalidateRegistry,
332
+ selectValue,
333
+ ],
334
+ );
335
+
336
+ return (
337
+ <CommandContext.Provider value={ctx}>
338
+ <span hidden id={labelId}>
339
+ {label}
340
+ </span>
341
+ {children}
342
+ </CommandContext.Provider>
343
+ );
344
+ }
345
+
346
+ Command.displayName = "Command";
347
+
348
+ export function CommandTrigger({
349
+ className,
350
+ children,
351
+ onClick,
352
+ ref: refProp,
353
+ }: CommandTriggerProps) {
354
+ const { setOpen, triggerRef } = useCommandContext("CommandTrigger");
355
+ return (
356
+ <button
357
+ ref={(node) => {
358
+ triggerRef.current = node;
359
+ if (typeof refProp === "function") {
360
+ refProp(node);
361
+ } else if (refProp) {
362
+ (refProp as RefObject<HTMLButtonElement | null>).current = node;
363
+ }
364
+ }}
365
+ type="button"
366
+ data-slot="command-trigger"
367
+ className={cn(commandTriggerVariants(), className)}
368
+ onClick={(event) => {
369
+ onClick?.(event);
370
+ if (!event.defaultPrevented) {
371
+ setOpen(true);
372
+ }
373
+ }}
374
+ >
375
+ {children}
376
+ </button>
377
+ );
378
+ }
379
+
380
+ CommandTrigger.displayName = "CommandTrigger";
381
+
382
+ export function CommandContent({
383
+ className,
384
+ size,
385
+ appearance,
386
+ children,
387
+ ref,
388
+ id,
389
+ style,
390
+ }: CommandContentProps) {
391
+ return (
392
+ <CommandContentLayer
393
+ appearance={appearance}
394
+ className={className}
395
+ componentName="CommandContent"
396
+ id={id}
397
+ ref={ref}
398
+ size={size}
399
+ style={style}
400
+ >
401
+ {children}
402
+ </CommandContentLayer>
403
+ );
404
+ }
405
+
406
+ CommandContent.displayName = "CommandContent";
407
+
408
+ export function CommandInput({
409
+ className,
410
+ placeholder,
411
+ ref,
412
+ }: CommandInputProps) {
413
+ const {
414
+ query,
415
+ setQuery,
416
+ visibleValues,
417
+ activeValue,
418
+ setActiveValue,
419
+ selectValue,
420
+ setOpen,
421
+ inputRef,
422
+ listId,
423
+ } = useCommandContext("CommandInput");
424
+
425
+ const moveActive = (direction: 1 | -1) => {
426
+ if (visibleValues.length === 0) {
427
+ return;
428
+ }
429
+ const currentIndex = activeValue ? visibleValues.indexOf(activeValue) : -1;
430
+ const nextIndex =
431
+ currentIndex === -1
432
+ ? direction === 1
433
+ ? 0
434
+ : visibleValues.length - 1
435
+ : (currentIndex + direction + visibleValues.length) %
436
+ visibleValues.length;
437
+ setActiveValue(visibleValues[nextIndex] ?? null);
438
+ };
439
+
440
+ return (
441
+ <div className={commandInputRowVariants()} data-slot="command-input-row">
442
+ <svg
443
+ aria-hidden
444
+ viewBox="0 0 24 24"
445
+ className="size-4 shrink-0 opacity-60"
446
+ fill="none"
447
+ stroke="currentColor"
448
+ strokeWidth="2"
449
+ strokeLinecap="round"
450
+ strokeLinejoin="round"
451
+ >
452
+ <circle cx="11" cy="11" r="8" />
453
+ <path d="m21 21-4.3-4.3" />
454
+ </svg>
455
+ <input
456
+ ref={(node) => {
457
+ inputRef.current = node;
458
+ if (typeof ref === "function") {
459
+ ref(node);
460
+ } else if (ref) {
461
+ (ref as RefObject<HTMLInputElement | null>).current = node;
462
+ }
463
+ }}
464
+ type="text"
465
+ autoFocus
466
+ role="combobox"
467
+ aria-expanded
468
+ aria-controls={listId}
469
+ aria-autocomplete="list"
470
+ data-slot="command-input"
471
+ className={cn(commandInputVariants(), className)}
472
+ placeholder={placeholder}
473
+ value={query}
474
+ onChange={(event) => setQuery(event.target.value)}
475
+ onKeyDown={(event) => {
476
+ if (event.key === "ArrowDown") {
477
+ event.preventDefault();
478
+ moveActive(1);
479
+ } else if (event.key === "ArrowUp") {
480
+ event.preventDefault();
481
+ moveActive(-1);
482
+ } else if (event.key === "Enter") {
483
+ if (activeValue && visibleValues.includes(activeValue)) {
484
+ event.preventDefault();
485
+ if (selectValue(activeValue)) {
486
+ setOpen(false);
487
+ }
488
+ }
489
+ }
490
+ }}
491
+ />
492
+ </div>
493
+ );
494
+ }
495
+
496
+ CommandInput.displayName = "CommandInput";
497
+
498
+ export function CommandList({ className, children }: CommandListProps) {
499
+ const { listId } = useCommandContext("CommandList");
500
+
501
+ return (
502
+ <div
503
+ id={listId}
504
+ role="listbox"
505
+ aria-label="Commands"
506
+ data-slot="command-list"
507
+ className={cn(commandListVariants(), className)}
508
+ >
509
+ {children}
510
+ </div>
511
+ );
512
+ }
513
+
514
+ CommandList.displayName = "CommandList";
515
+
516
+ export function CommandGroup({
517
+ className,
518
+ heading,
519
+ children,
520
+ }: CommandGroupProps) {
521
+ return (
522
+ <div
523
+ role="group"
524
+ data-slot="command-group"
525
+ className={cn(
526
+ "[&:not(:has([data-slot=command-item]:not([hidden])))]:hidden",
527
+ className,
528
+ )}
529
+ >
530
+ {heading ? (
531
+ <div className={commandGroupHeadingVariants()} aria-hidden>
532
+ {heading}
533
+ </div>
534
+ ) : null}
535
+ {children}
536
+ </div>
537
+ );
538
+ }
539
+
540
+ CommandGroup.displayName = "CommandGroup";
541
+
542
+ export function CommandItem({
543
+ className,
544
+ value,
545
+ keywords,
546
+ disabled,
547
+ onSelect,
548
+ children,
549
+ }: CommandItemProps) {
550
+ const {
551
+ registerItem,
552
+ invalidateRegistry,
553
+ isVisible,
554
+ activeValue,
555
+ setActiveValue,
556
+ selectValue,
557
+ setOpen,
558
+ } = useCommandContext("CommandItem");
559
+
560
+ const itemRef = useRef<HTMLDivElement | null>(null);
561
+ const keywordSignature = keywords?.join("\u0000") ?? "";
562
+ const metaRef = useRef<ItemMeta>({ keywords, disabled, onSelect });
563
+ metaRef.current = {
564
+ keywords,
565
+ disabled,
566
+ onSelect,
567
+ searchText: metaRef.current.searchText,
568
+ };
569
+
570
+ useEffect(() => {
571
+ const unregister = registerItem({ value, metaRef });
572
+ return unregister;
573
+ }, [registerItem, value]);
574
+
575
+ useEffect(() => {
576
+ metaRef.current = {
577
+ keywords: metaRef.current.keywords,
578
+ disabled,
579
+ onSelect: metaRef.current.onSelect,
580
+ searchText: itemRef.current?.textContent ?? undefined,
581
+ };
582
+ invalidateRegistry();
583
+ }, [children, disabled, invalidateRegistry, keywordSignature]);
584
+
585
+ const visible = isVisible(value);
586
+ const active = activeValue === value;
587
+
588
+ return (
589
+ <div
590
+ ref={itemRef}
591
+ role="option"
592
+ aria-selected={active}
593
+ aria-disabled={disabled || undefined}
594
+ data-slot="command-item"
595
+ data-value={value}
596
+ data-active={active || undefined}
597
+ hidden={!visible || undefined}
598
+ className={cn(commandItemVariants(), className)}
599
+ onMouseEnter={() => {
600
+ if (!disabled) {
601
+ setActiveValue(value);
602
+ }
603
+ }}
604
+ onClick={() => {
605
+ if (disabled) {
606
+ return;
607
+ }
608
+ if (selectValue(value)) {
609
+ setOpen(false);
610
+ }
611
+ }}
612
+ >
613
+ {children}
614
+ </div>
615
+ );
616
+ }
617
+
618
+ CommandItem.displayName = "CommandItem";
619
+
620
+ export function CommandSeparator({ className }: { className?: string }) {
621
+ return (
622
+ <div
623
+ role="separator"
624
+ data-slot="command-separator"
625
+ className={cn(commandSeparatorVariants(), className)}
626
+ />
627
+ );
628
+ }
629
+
630
+ CommandSeparator.displayName = "CommandSeparator";
631
+
632
+ export function CommandEmpty({ className, children }: CommandSectionProps) {
633
+ const { visibleValues } = useCommandContext("CommandEmpty");
634
+ if (visibleValues.length > 0) {
635
+ return null;
636
+ }
637
+ return (
638
+ <div
639
+ data-slot="command-empty"
640
+ className={cn(commandEmptyVariants(), className)}
641
+ >
642
+ {children}
643
+ </div>
644
+ );
645
+ }
646
+
647
+ CommandEmpty.displayName = "CommandEmpty";
648
+
649
+ export function CommandFooter({ className, children }: CommandSectionProps) {
650
+ return (
651
+ <div
652
+ data-slot="command-footer"
653
+ className={cn(commandFooterVariants(), className)}
654
+ >
655
+ {children}
656
+ </div>
657
+ );
658
+ }
659
+
660
+ CommandFooter.displayName = "CommandFooter";