@open-mercato/ui 0.6.4-develop.4178.1.aad9ddaa95 → 0.6.4-develop.4210.1.d412061cfe

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/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +14 -0
  3. package/dist/backend/DataTable.js +28 -62
  4. package/dist/backend/DataTable.js.map +2 -2
  5. package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
  6. package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
  7. package/dist/primitives/activity-feed.js +196 -0
  8. package/dist/primitives/activity-feed.js.map +7 -0
  9. package/dist/primitives/avatar.js +140 -14
  10. package/dist/primitives/avatar.js.map +3 -3
  11. package/dist/primitives/badge.js +94 -5
  12. package/dist/primitives/badge.js.map +2 -2
  13. package/dist/primitives/button-group.js +52 -0
  14. package/dist/primitives/button-group.js.map +7 -0
  15. package/dist/primitives/color-picker.js +520 -0
  16. package/dist/primitives/color-picker.js.map +7 -0
  17. package/dist/primitives/command-menu.js +355 -0
  18. package/dist/primitives/command-menu.js.map +7 -0
  19. package/dist/primitives/dialog.js +210 -35
  20. package/dist/primitives/dialog.js.map +2 -2
  21. package/dist/primitives/drawer.js +219 -0
  22. package/dist/primitives/drawer.js.map +7 -0
  23. package/dist/primitives/notification-feed.js +213 -0
  24. package/dist/primitives/notification-feed.js.map +7 -0
  25. package/dist/primitives/notification.js +23 -2
  26. package/dist/primitives/notification.js.map +2 -2
  27. package/dist/primitives/pagination.js +245 -0
  28. package/dist/primitives/pagination.js.map +7 -0
  29. package/dist/primitives/progress.js +178 -5
  30. package/dist/primitives/progress.js.map +2 -2
  31. package/dist/primitives/rating.js +174 -0
  32. package/dist/primitives/rating.js.map +7 -0
  33. package/dist/primitives/scroll-area.js +82 -0
  34. package/dist/primitives/scroll-area.js.map +7 -0
  35. package/dist/primitives/segmented-control.js +101 -0
  36. package/dist/primitives/segmented-control.js.map +7 -0
  37. package/dist/primitives/separator.js +103 -4
  38. package/dist/primitives/separator.js.map +2 -2
  39. package/dist/primitives/slider.js +115 -0
  40. package/dist/primitives/slider.js.map +7 -0
  41. package/dist/primitives/step-indicator.js +187 -0
  42. package/dist/primitives/step-indicator.js.map +7 -0
  43. package/dist/primitives/table.js +102 -10
  44. package/dist/primitives/table.js.map +2 -2
  45. package/dist/primitives/tabs.js +122 -14
  46. package/dist/primitives/tabs.js.map +2 -2
  47. package/dist/primitives/time-picker.js +9 -11
  48. package/dist/primitives/time-picker.js.map +2 -2
  49. package/package.json +8 -4
  50. package/src/backend/DataTable.tsx +43 -60
  51. package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
  52. package/src/primitives/__tests__/activity-feed.test.tsx +216 -0
  53. package/src/primitives/__tests__/avatar.test.tsx +131 -0
  54. package/src/primitives/__tests__/badge.test.tsx +189 -0
  55. package/src/primitives/__tests__/button-group.test.tsx +117 -0
  56. package/src/primitives/__tests__/color-picker.test.tsx +589 -0
  57. package/src/primitives/__tests__/command-menu.test.tsx +231 -0
  58. package/src/primitives/__tests__/dialog.test.tsx +290 -0
  59. package/src/primitives/__tests__/drawer.test.tsx +264 -0
  60. package/src/primitives/__tests__/notification-feed.test.tsx +303 -0
  61. package/src/primitives/__tests__/notification.test.tsx +102 -2
  62. package/src/primitives/__tests__/pagination.test.tsx +268 -0
  63. package/src/primitives/__tests__/progress.test.tsx +222 -0
  64. package/src/primitives/__tests__/rating.test.tsx +164 -0
  65. package/src/primitives/__tests__/scroll-area.test.tsx +109 -0
  66. package/src/primitives/__tests__/segmented-control.test.tsx +129 -0
  67. package/src/primitives/__tests__/separator.test.tsx +143 -0
  68. package/src/primitives/__tests__/slider.test.tsx +86 -0
  69. package/src/primitives/__tests__/step-indicator.test.tsx +205 -0
  70. package/src/primitives/__tests__/table.test.tsx +199 -0
  71. package/src/primitives/__tests__/tabs.test.tsx +139 -0
  72. package/src/primitives/activity-feed.tsx +326 -0
  73. package/src/primitives/avatar.tsx +225 -22
  74. package/src/primitives/badge.tsx +141 -6
  75. package/src/primitives/button-group.tsx +100 -0
  76. package/src/primitives/color-picker.tsx +693 -0
  77. package/src/primitives/command-menu.tsx +503 -0
  78. package/src/primitives/dialog.tsx +315 -51
  79. package/src/primitives/drawer.tsx +338 -0
  80. package/src/primitives/notification-feed.tsx +358 -0
  81. package/src/primitives/notification.tsx +42 -2
  82. package/src/primitives/pagination.tsx +376 -0
  83. package/src/primitives/progress.tsx +267 -6
  84. package/src/primitives/rating.tsx +245 -0
  85. package/src/primitives/scroll-area.tsx +169 -0
  86. package/src/primitives/segmented-control.tsx +162 -0
  87. package/src/primitives/separator.tsx +163 -3
  88. package/src/primitives/slider.tsx +160 -0
  89. package/src/primitives/step-indicator.tsx +287 -0
  90. package/src/primitives/table.tsx +142 -11
  91. package/src/primitives/tabs.tsx +174 -12
  92. package/src/primitives/time-picker.tsx +9 -11
@@ -0,0 +1,503 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
5
+ import { Command as CommandPrimitive } from 'cmdk'
6
+ import { ChevronRight, Search, X } from 'lucide-react'
7
+
8
+ import { cn } from '@open-mercato/shared/lib/utils'
9
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
10
+ import { Kbd } from './kbd'
11
+
12
+ /**
13
+ * Command palette primitive — Cmd+K spotlight-style launcher backed by
14
+ * the `cmdk` library (auto-filters items as the user types) and hosted
15
+ * inside a Radix Dialog overlay so it inherits the modal focus contract
16
+ * (ESC closes, outside-click closes, focus trap, ARIA role="dialog").
17
+ *
18
+ * Compound API:
19
+ * <CommandMenu> — root dialog (controlled via `open` / `onOpenChange`,
20
+ * or hands off via `defaultOpen`). Wraps `cmdk` + Radix
21
+ * Dialog.
22
+ * <CommandMenuTrigger> — anchor / trigger button (Radix Dialog.Trigger).
23
+ * <CommandMenuContent> — the floating panel rendered through Portal:
24
+ * centered overlay with rounded card, max-width,
25
+ * shadow, animations.
26
+ * <CommandMenuInput> — leading magnifier + input + trailing `⌘K` kbd
27
+ * + auto X to clear when there's a value.
28
+ * <CommandMenuList> — scrollable container for groups/items.
29
+ * <CommandMenuEmpty> — fallback when no items match the search query.
30
+ * <CommandMenuGroup> — labelled section with optional `actionLabel`
31
+ * / `onAction` "see all"-style affordance.
32
+ * <CommandMenuItem> — selectable row. Supports `leading` (avatar /
33
+ * icon / flag), label, optional `description`,
34
+ * optional `shortcut` (Kbd), and the auto
35
+ * chevron on hover/selection.
36
+ * <CommandMenuSeparator> — visual divider between groups.
37
+ * <CommandMenuFooter> — bottom bar with shortcut hints + help link.
38
+ *
39
+ * Figma source: DS Open Mercato `Command Menu` page (4152:24764) —
40
+ * Search Input [1.1] (4187:559), Items [1.1] (4171:15653), Footer [1.1]
41
+ * (4172:16590).
42
+ *
43
+ * ```tsx
44
+ * const [open, setOpen] = React.useState(false)
45
+ *
46
+ * React.useEffect(() => {
47
+ * const onKey = (e: KeyboardEvent) => {
48
+ * if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
49
+ * e.preventDefault()
50
+ * setOpen((o) => !o)
51
+ * }
52
+ * }
53
+ * window.addEventListener('keydown', onKey)
54
+ * return () => window.removeEventListener('keydown', onKey)
55
+ * }, [])
56
+ *
57
+ * <CommandMenu open={open} onOpenChange={setOpen}>
58
+ * <CommandMenuContent>
59
+ * <CommandMenuInput placeholder="Search HR tools or press..." />
60
+ * <CommandMenuList>
61
+ * <CommandMenuEmpty>No results.</CommandMenuEmpty>
62
+ * <CommandMenuGroup heading="Tools & Apps">
63
+ * <CommandMenuItem leading={<Logo />} onSelect={...}>Monday.com</CommandMenuItem>
64
+ * <CommandMenuItem leading={<Logo />} onSelect={...}>Loom</CommandMenuItem>
65
+ * </CommandMenuGroup>
66
+ * <CommandMenuSeparator />
67
+ * <CommandMenuGroup heading="Employees">
68
+ * <CommandMenuItem leading={<Avatar />} description="Engineer">James Brown</CommandMenuItem>
69
+ * </CommandMenuGroup>
70
+ * </CommandMenuList>
71
+ * <CommandMenuFooter />
72
+ * </CommandMenuContent>
73
+ * </CommandMenu>
74
+ * ```
75
+ */
76
+
77
+ type CommandMenuRootProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root>
78
+
79
+ const CommandMenu = (props: CommandMenuRootProps) => <DialogPrimitive.Root {...props} />
80
+ CommandMenu.displayName = 'CommandMenu'
81
+
82
+ const CommandMenuTrigger = DialogPrimitive.Trigger
83
+ const CommandMenuPortal = DialogPrimitive.Portal
84
+ const CommandMenuClose = DialogPrimitive.Close
85
+
86
+ const CommandMenuOverlay = React.forwardRef<
87
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
88
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
89
+ >(({ className, ...props }, ref) => (
90
+ <DialogPrimitive.Overlay
91
+ ref={ref}
92
+ data-slot="command-menu-overlay"
93
+ className={cn(
94
+ 'fixed inset-0 z-overlay bg-foreground/40 backdrop-blur-sm',
95
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
96
+ 'data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0',
97
+ className,
98
+ )}
99
+ {...props}
100
+ />
101
+ ))
102
+ CommandMenuOverlay.displayName = 'CommandMenuOverlay'
103
+
104
+ export type CommandMenuContentProps = React.ComponentPropsWithoutRef<
105
+ typeof DialogPrimitive.Content
106
+ > & {
107
+ /** Accessible dialog title — required by Radix; auto visually hidden. */
108
+ title?: string
109
+ /** Optional max-width override (default `max-w-xl`, ~640px). */
110
+ contentClassName?: string
111
+ /** Pass to underlying `cmdk` root (`loop`, `shouldFilter`, etc.). */
112
+ commandProps?: Omit<React.ComponentPropsWithoutRef<typeof CommandPrimitive>, 'children' | 'className'>
113
+ }
114
+
115
+ const CommandMenuContent = React.forwardRef<
116
+ React.ElementRef<typeof DialogPrimitive.Content>,
117
+ CommandMenuContentProps
118
+ >(({ className, contentClassName, title, commandProps, children, ...props }, ref) => {
119
+ const t = useT()
120
+ const resolvedTitle = title ?? t('ui.commandMenu.title.srOnly', 'Command menu')
121
+ return (
122
+ <CommandMenuPortal>
123
+ <CommandMenuOverlay />
124
+ <DialogPrimitive.Content
125
+ ref={ref}
126
+ data-slot="command-menu-content"
127
+ className={cn(
128
+ // top-1/4 places content roughly 25% from viewport top (Tailwind fraction scale);
129
+ // w-[calc(100%-2rem)] kept — no DS token expresses "viewport minus 2rem gutters".
130
+ 'fixed left-1/2 top-1/4 z-popover w-[calc(100%-2rem)] -translate-x-1/2 outline-none',
131
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
132
+ 'data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0',
133
+ 'data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95',
134
+ 'data-[state=open]:duration-150 data-[state=closed]:duration-100',
135
+ className,
136
+ )}
137
+ {...props}
138
+ >
139
+ <DialogPrimitive.Title className="sr-only">{resolvedTitle}</DialogPrimitive.Title>
140
+ <CommandPrimitive
141
+ data-slot="command-menu-root"
142
+ className={cn(
143
+ 'mx-auto flex w-full max-w-xl flex-col overflow-hidden rounded-lg border border-input bg-background shadow-lg',
144
+ contentClassName,
145
+ )}
146
+ {...commandProps}
147
+ >
148
+ {children}
149
+ </CommandPrimitive>
150
+ </DialogPrimitive.Content>
151
+ </CommandMenuPortal>
152
+ )
153
+ })
154
+ CommandMenuContent.displayName = 'CommandMenuContent'
155
+
156
+ export type CommandMenuInputProps = React.ComponentPropsWithoutRef<
157
+ typeof CommandPrimitive.Input
158
+ > & {
159
+ /** Show the trailing `⌘K` hint. Default `true`. */
160
+ showShortcut?: boolean
161
+ /** Custom shortcut label. Default `⌘K`. */
162
+ shortcutLabel?: string
163
+ /** Show the trailing × clear button when the input has a value. Default `true`. */
164
+ showClear?: boolean
165
+ /** Accessible label for the clear button. Default `'Clear search'`. */
166
+ clearAriaLabel?: string
167
+ /** Container className override (border / padding / row). */
168
+ wrapperClassName?: string
169
+ }
170
+
171
+ const CommandMenuInput = React.forwardRef<
172
+ React.ElementRef<typeof CommandPrimitive.Input>,
173
+ CommandMenuInputProps
174
+ >(
175
+ (
176
+ {
177
+ className,
178
+ wrapperClassName,
179
+ showShortcut = true,
180
+ shortcutLabel = '⌘K',
181
+ showClear = true,
182
+ clearAriaLabel = 'Clear search',
183
+ value,
184
+ defaultValue,
185
+ onValueChange,
186
+ ...props
187
+ },
188
+ ref,
189
+ ) => {
190
+ const isControlled = value !== undefined
191
+ const [internal, setInternal] = React.useState<string>(
192
+ typeof defaultValue === 'string' ? defaultValue : '',
193
+ )
194
+ const currentValue = isControlled ? (value as string) : internal
195
+
196
+ const handleChange = React.useCallback(
197
+ (next: string) => {
198
+ if (!isControlled) setInternal(next)
199
+ onValueChange?.(next)
200
+ },
201
+ [isControlled, onValueChange],
202
+ )
203
+
204
+ return (
205
+ <div
206
+ data-slot="command-menu-input-wrapper"
207
+ className={cn(
208
+ 'flex h-12 items-center gap-2 border-b border-input px-4',
209
+ wrapperClassName,
210
+ )}
211
+ >
212
+ <Search
213
+ aria-hidden="true"
214
+ className="size-4 shrink-0 text-muted-foreground"
215
+ data-slot="command-menu-input-icon"
216
+ />
217
+ <CommandPrimitive.Input
218
+ ref={ref}
219
+ data-slot="command-menu-input"
220
+ value={currentValue}
221
+ onValueChange={handleChange}
222
+ className={cn(
223
+ 'flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none',
224
+ className,
225
+ )}
226
+ {...props}
227
+ />
228
+ {showClear && currentValue.length > 0 ? (
229
+ <button
230
+ type="button"
231
+ data-slot="command-menu-input-clear"
232
+ aria-label={clearAriaLabel}
233
+ onClick={() => handleChange('')}
234
+ className={cn(
235
+ 'inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none transition-colors',
236
+ 'hover:bg-muted/40 hover:text-foreground',
237
+ 'focus-visible:shadow-focus',
238
+ )}
239
+ >
240
+ <X aria-hidden="true" className="size-3.5" />
241
+ </button>
242
+ ) : showShortcut ? (
243
+ <Kbd
244
+ data-slot="command-menu-input-shortcut"
245
+ className="shrink-0"
246
+ >
247
+ {shortcutLabel}
248
+ </Kbd>
249
+ ) : null}
250
+ </div>
251
+ )
252
+ },
253
+ )
254
+ CommandMenuInput.displayName = 'CommandMenuInput'
255
+
256
+ const CommandMenuList = React.forwardRef<
257
+ React.ElementRef<typeof CommandPrimitive.List>,
258
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
259
+ >(({ className, ...props }, ref) => (
260
+ <CommandPrimitive.List
261
+ ref={ref}
262
+ data-slot="command-menu-list"
263
+ className={cn(
264
+ 'max-h-[420px] overflow-y-auto overflow-x-hidden p-2',
265
+ className,
266
+ )}
267
+ {...props}
268
+ />
269
+ ))
270
+ CommandMenuList.displayName = 'CommandMenuList'
271
+
272
+ const CommandMenuEmpty = React.forwardRef<
273
+ React.ElementRef<typeof CommandPrimitive.Empty>,
274
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
275
+ >(({ className, ...props }, ref) => (
276
+ <CommandPrimitive.Empty
277
+ ref={ref}
278
+ data-slot="command-menu-empty"
279
+ className={cn(
280
+ 'py-8 text-center text-sm text-muted-foreground',
281
+ className,
282
+ )}
283
+ {...props}
284
+ />
285
+ ))
286
+ CommandMenuEmpty.displayName = 'CommandMenuEmpty'
287
+
288
+ export type CommandMenuGroupProps = React.ComponentPropsWithoutRef<
289
+ typeof CommandPrimitive.Group
290
+ > & {
291
+ /** Optional trailing "see all"-style affordance shown next to the heading. */
292
+ actionLabel?: string
293
+ /** Click handler for the trailing action. Renders an arrow icon when set. */
294
+ onAction?: () => void
295
+ /** Accessible label for the trailing action button. Default uses `actionLabel`. */
296
+ actionAriaLabel?: string
297
+ }
298
+
299
+ const CommandMenuGroup = React.forwardRef<
300
+ React.ElementRef<typeof CommandPrimitive.Group>,
301
+ CommandMenuGroupProps
302
+ >(
303
+ (
304
+ { className, heading, actionLabel, onAction, actionAriaLabel, children, ...props },
305
+ ref,
306
+ ) => {
307
+ const hasAction = onAction !== undefined
308
+ return (
309
+ <CommandPrimitive.Group
310
+ ref={ref}
311
+ data-slot="command-menu-group"
312
+ heading={heading}
313
+ className={cn(
314
+ 'overflow-hidden text-foreground',
315
+ '[&_[cmdk-group-heading]]:flex [&_[cmdk-group-heading]]:items-center [&_[cmdk-group-heading]]:justify-between',
316
+ '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5',
317
+ '[&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
318
+ className,
319
+ )}
320
+ {...props}
321
+ >
322
+ {hasAction ? (
323
+ <div
324
+ data-slot="command-menu-group-action"
325
+ className="-mt-7 mb-1 flex justify-end px-2"
326
+ >
327
+ <button
328
+ type="button"
329
+ onClick={onAction}
330
+ aria-label={actionAriaLabel ?? actionLabel ?? 'See all'}
331
+ className={cn(
332
+ 'inline-flex items-center gap-1 rounded-md text-xs font-medium text-muted-foreground outline-none transition-colors',
333
+ 'hover:text-foreground focus-visible:shadow-focus',
334
+ )}
335
+ >
336
+ {actionLabel}
337
+ <ChevronRight aria-hidden="true" className="size-3" />
338
+ </button>
339
+ </div>
340
+ ) : null}
341
+ {children}
342
+ </CommandPrimitive.Group>
343
+ )
344
+ },
345
+ )
346
+ CommandMenuGroup.displayName = 'CommandMenuGroup'
347
+
348
+ export type CommandMenuItemProps = React.ComponentPropsWithoutRef<
349
+ typeof CommandPrimitive.Item
350
+ > & {
351
+ /** Optional leading slot — avatar, icon, flag, brand mark, etc. */
352
+ leading?: React.ReactNode
353
+ /** Optional secondary line beneath the label. */
354
+ description?: React.ReactNode
355
+ /** Optional trailing keyboard hint. */
356
+ shortcut?: React.ReactNode
357
+ /** Hide the auto chevron-right trailing affordance. Default `false`. */
358
+ hideChevron?: boolean
359
+ }
360
+
361
+ const CommandMenuItem = React.forwardRef<
362
+ React.ElementRef<typeof CommandPrimitive.Item>,
363
+ CommandMenuItemProps
364
+ >(
365
+ (
366
+ {
367
+ className,
368
+ children,
369
+ leading,
370
+ description,
371
+ shortcut,
372
+ hideChevron = false,
373
+ ...props
374
+ },
375
+ ref,
376
+ ) => (
377
+ <CommandPrimitive.Item
378
+ ref={ref}
379
+ data-slot="command-menu-item"
380
+ className={cn(
381
+ 'group relative flex w-full cursor-pointer select-none items-center gap-3 rounded-md px-2 py-2 text-sm text-foreground outline-none',
382
+ 'data-[selected=true]:bg-muted/40',
383
+ 'data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',
384
+ className,
385
+ )}
386
+ {...props}
387
+ >
388
+ {leading ? (
389
+ <span
390
+ data-slot="command-menu-item-leading"
391
+ aria-hidden="true"
392
+ className="flex size-6 shrink-0 items-center justify-center"
393
+ >
394
+ {leading}
395
+ </span>
396
+ ) : null}
397
+ <div
398
+ data-slot="command-menu-item-text"
399
+ className="min-w-0 flex-1"
400
+ >
401
+ <div className="truncate text-sm font-medium text-foreground">
402
+ {children}
403
+ </div>
404
+ {description ? (
405
+ <div
406
+ data-slot="command-menu-item-description"
407
+ className="truncate text-xs text-muted-foreground"
408
+ >
409
+ {description}
410
+ </div>
411
+ ) : null}
412
+ </div>
413
+ {shortcut ? (
414
+ <span
415
+ data-slot="command-menu-item-shortcut"
416
+ className="ml-auto shrink-0"
417
+ >
418
+ {shortcut}
419
+ </span>
420
+ ) : !hideChevron ? (
421
+ <ChevronRight
422
+ aria-hidden="true"
423
+ data-slot="command-menu-item-chevron"
424
+ className="ml-auto size-4 shrink-0 text-muted-foreground opacity-0 transition-opacity group-data-[selected=true]:opacity-100"
425
+ />
426
+ ) : null}
427
+ </CommandPrimitive.Item>
428
+ ),
429
+ )
430
+ CommandMenuItem.displayName = 'CommandMenuItem'
431
+
432
+ const CommandMenuSeparator = React.forwardRef<
433
+ React.ElementRef<typeof CommandPrimitive.Separator>,
434
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
435
+ >(({ className, ...props }, ref) => (
436
+ <CommandPrimitive.Separator
437
+ ref={ref}
438
+ data-slot="command-menu-separator"
439
+ className={cn('-mx-2 my-2 h-px bg-input', className)}
440
+ {...props}
441
+ />
442
+ ))
443
+ CommandMenuSeparator.displayName = 'CommandMenuSeparator'
444
+
445
+ export type CommandMenuFooterProps = React.HTMLAttributes<HTMLDivElement> & {
446
+ /** Override the left-side shortcut hints (defaults to ↑/↓ Navigate, ↵ Select). */
447
+ hints?: React.ReactNode
448
+ /** Optional right-side help affordance ("Any problem? Contact"). */
449
+ helpSlot?: React.ReactNode
450
+ }
451
+
452
+ const CommandMenuFooter = React.forwardRef<HTMLDivElement, CommandMenuFooterProps>(
453
+ ({ className, hints, helpSlot, ...props }, ref) => {
454
+ const t = useT()
455
+ return (
456
+ <div
457
+ ref={ref}
458
+ data-slot="command-menu-footer"
459
+ className={cn(
460
+ 'flex items-center justify-between gap-3 border-t border-input px-3 py-2 text-xs text-muted-foreground',
461
+ className,
462
+ )}
463
+ {...props}
464
+ >
465
+ <div data-slot="command-menu-footer-hints" className="flex items-center gap-3">
466
+ {hints ?? (
467
+ <>
468
+ <span className="inline-flex items-center gap-1">
469
+ <Kbd>{'↑'}</Kbd>
470
+ <Kbd>{'↓'}</Kbd>
471
+ <span>{t('ui.commandMenu.footer.navigate', 'Navigate')}</span>
472
+ </span>
473
+ <span className="inline-flex items-center gap-1">
474
+ <Kbd>{'↵'}</Kbd>
475
+ <span>{t('ui.commandMenu.footer.select', 'Select')}</span>
476
+ </span>
477
+ </>
478
+ )}
479
+ </div>
480
+ {helpSlot ? (
481
+ <div data-slot="command-menu-footer-help">{helpSlot}</div>
482
+ ) : null}
483
+ </div>
484
+ )
485
+ },
486
+ )
487
+ CommandMenuFooter.displayName = 'CommandMenuFooter'
488
+
489
+ export {
490
+ CommandMenu,
491
+ CommandMenuTrigger,
492
+ CommandMenuPortal,
493
+ CommandMenuClose,
494
+ CommandMenuOverlay,
495
+ CommandMenuContent,
496
+ CommandMenuInput,
497
+ CommandMenuList,
498
+ CommandMenuEmpty,
499
+ CommandMenuGroup,
500
+ CommandMenuItem,
501
+ CommandMenuSeparator,
502
+ CommandMenuFooter,
503
+ }