@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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +14 -0
- package/dist/backend/DataTable.js +28 -62
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
- package/dist/primitives/activity-feed.js +196 -0
- package/dist/primitives/activity-feed.js.map +7 -0
- package/dist/primitives/avatar.js +140 -14
- package/dist/primitives/avatar.js.map +3 -3
- package/dist/primitives/badge.js +94 -5
- package/dist/primitives/badge.js.map +2 -2
- package/dist/primitives/button-group.js +52 -0
- package/dist/primitives/button-group.js.map +7 -0
- package/dist/primitives/color-picker.js +520 -0
- package/dist/primitives/color-picker.js.map +7 -0
- package/dist/primitives/command-menu.js +355 -0
- package/dist/primitives/command-menu.js.map +7 -0
- package/dist/primitives/dialog.js +210 -35
- package/dist/primitives/dialog.js.map +2 -2
- package/dist/primitives/drawer.js +219 -0
- package/dist/primitives/drawer.js.map +7 -0
- package/dist/primitives/notification-feed.js +213 -0
- package/dist/primitives/notification-feed.js.map +7 -0
- package/dist/primitives/notification.js +23 -2
- package/dist/primitives/notification.js.map +2 -2
- package/dist/primitives/pagination.js +245 -0
- package/dist/primitives/pagination.js.map +7 -0
- package/dist/primitives/progress.js +178 -5
- package/dist/primitives/progress.js.map +2 -2
- package/dist/primitives/rating.js +174 -0
- package/dist/primitives/rating.js.map +7 -0
- package/dist/primitives/scroll-area.js +82 -0
- package/dist/primitives/scroll-area.js.map +7 -0
- package/dist/primitives/segmented-control.js +101 -0
- package/dist/primitives/segmented-control.js.map +7 -0
- package/dist/primitives/separator.js +103 -4
- package/dist/primitives/separator.js.map +2 -2
- package/dist/primitives/slider.js +115 -0
- package/dist/primitives/slider.js.map +7 -0
- package/dist/primitives/step-indicator.js +187 -0
- package/dist/primitives/step-indicator.js.map +7 -0
- package/dist/primitives/table.js +102 -10
- package/dist/primitives/table.js.map +2 -2
- package/dist/primitives/tabs.js +122 -14
- package/dist/primitives/tabs.js.map +2 -2
- package/dist/primitives/time-picker.js +9 -11
- package/dist/primitives/time-picker.js.map +2 -2
- package/package.json +8 -4
- package/src/backend/DataTable.tsx +43 -60
- package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
- package/src/primitives/__tests__/activity-feed.test.tsx +216 -0
- package/src/primitives/__tests__/avatar.test.tsx +131 -0
- package/src/primitives/__tests__/badge.test.tsx +189 -0
- package/src/primitives/__tests__/button-group.test.tsx +117 -0
- package/src/primitives/__tests__/color-picker.test.tsx +589 -0
- package/src/primitives/__tests__/command-menu.test.tsx +231 -0
- package/src/primitives/__tests__/dialog.test.tsx +290 -0
- package/src/primitives/__tests__/drawer.test.tsx +264 -0
- package/src/primitives/__tests__/notification-feed.test.tsx +303 -0
- package/src/primitives/__tests__/notification.test.tsx +102 -2
- package/src/primitives/__tests__/pagination.test.tsx +268 -0
- package/src/primitives/__tests__/progress.test.tsx +222 -0
- package/src/primitives/__tests__/rating.test.tsx +164 -0
- package/src/primitives/__tests__/scroll-area.test.tsx +109 -0
- package/src/primitives/__tests__/segmented-control.test.tsx +129 -0
- package/src/primitives/__tests__/separator.test.tsx +143 -0
- package/src/primitives/__tests__/slider.test.tsx +86 -0
- package/src/primitives/__tests__/step-indicator.test.tsx +205 -0
- package/src/primitives/__tests__/table.test.tsx +199 -0
- package/src/primitives/__tests__/tabs.test.tsx +139 -0
- package/src/primitives/activity-feed.tsx +326 -0
- package/src/primitives/avatar.tsx +225 -22
- package/src/primitives/badge.tsx +141 -6
- package/src/primitives/button-group.tsx +100 -0
- package/src/primitives/color-picker.tsx +693 -0
- package/src/primitives/command-menu.tsx +503 -0
- package/src/primitives/dialog.tsx +315 -51
- package/src/primitives/drawer.tsx +338 -0
- package/src/primitives/notification-feed.tsx +358 -0
- package/src/primitives/notification.tsx +42 -2
- package/src/primitives/pagination.tsx +376 -0
- package/src/primitives/progress.tsx +267 -6
- package/src/primitives/rating.tsx +245 -0
- package/src/primitives/scroll-area.tsx +169 -0
- package/src/primitives/segmented-control.tsx +162 -0
- package/src/primitives/separator.tsx +163 -3
- package/src/primitives/slider.tsx +160 -0
- package/src/primitives/step-indicator.tsx +287 -0
- package/src/primitives/table.tsx +142 -11
- package/src/primitives/tabs.tsx +174 -12
- 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
|
+
}
|