@adamosuiteservices/ui 2.11.15 → 2.11.17
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/dist/colors.css +1 -1
- package/dist/styles.css +1 -1
- package/dist/themes.css +1 -1
- package/docs/AI-GUIDE.md +321 -321
- package/docs/components/layout/sidebar.md +399 -399
- package/docs/components/layout/toaster.md +436 -436
- package/docs/components/ui/accordion-rounded.md +584 -584
- package/docs/components/ui/accordion.md +269 -269
- package/docs/components/ui/button-group.md +984 -984
- package/docs/components/ui/button.md +1137 -1137
- package/docs/components/ui/calendar.md +1159 -1159
- package/docs/components/ui/card.md +1455 -1455
- package/docs/components/ui/checkbox.md +292 -292
- package/docs/components/ui/collapsible.md +323 -323
- package/docs/components/ui/command.md +454 -454
- package/docs/components/ui/context-menu.md +540 -540
- package/docs/components/ui/dialog.md +628 -628
- package/docs/components/ui/dropdown-menu.md +709 -709
- package/docs/components/ui/field.md +706 -706
- package/docs/components/ui/hover-card.md +446 -446
- package/docs/components/ui/input.md +362 -362
- package/docs/components/ui/kbd.md +434 -434
- package/docs/components/ui/label.md +359 -359
- package/docs/components/ui/pagination.md +650 -650
- package/docs/components/ui/popover.md +536 -536
- package/docs/components/ui/progress.md +182 -182
- package/docs/components/ui/radio-group.md +311 -311
- package/docs/components/ui/select.md +352 -352
- package/docs/components/ui/separator.md +214 -214
- package/docs/components/ui/sheet.md +142 -142
- package/docs/components/ui/skeleton.md +140 -140
- package/docs/components/ui/slider.md +341 -341
- package/docs/components/ui/spinner.md +170 -170
- package/docs/components/ui/switch.md +408 -408
- package/docs/components/ui/tabs-underline.md +106 -106
- package/docs/components/ui/tabs.md +122 -122
- package/docs/components/ui/textarea.md +243 -243
- package/docs/components/ui/toggle.md +237 -237
- package/docs/components/ui/tooltip.md +317 -317
- package/docs/components/ui/typography.md +280 -280
- package/package.json +1 -1
|
@@ -1,709 +1,709 @@
|
|
|
1
|
-
# Dropdown Menu
|
|
2
|
-
|
|
3
|
-
Menú desplegable activado por click, basado en Radix UI. Soporta submenús, checkboxes, radio groups, separadores, y shortcuts visuales. Similar a ContextMenu pero activado con click en lugar de click derecho.
|
|
4
|
-
|
|
5
|
-
## Importación
|
|
6
|
-
|
|
7
|
-
```tsx
|
|
8
|
-
import {
|
|
9
|
-
DropdownMenu,
|
|
10
|
-
DropdownMenuTrigger,
|
|
11
|
-
DropdownMenuContent,
|
|
12
|
-
DropdownMenuItem,
|
|
13
|
-
DropdownMenuCheckboxItem,
|
|
14
|
-
DropdownMenuRadioGroup,
|
|
15
|
-
DropdownMenuRadioItem,
|
|
16
|
-
DropdownMenuLabel,
|
|
17
|
-
DropdownMenuSeparator,
|
|
18
|
-
DropdownMenuShortcut,
|
|
19
|
-
DropdownMenuSub,
|
|
20
|
-
DropdownMenuSubContent,
|
|
21
|
-
DropdownMenuSubTrigger,
|
|
22
|
-
DropdownMenuGroup,
|
|
23
|
-
DropdownMenuPortal,
|
|
24
|
-
} from "@adamosuiteservices/ui/dropdown-menu";
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Anatomía
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
<DropdownMenu>
|
|
31
|
-
<DropdownMenuTrigger asChild>
|
|
32
|
-
<Button>Open Menu</Button>
|
|
33
|
-
</DropdownMenuTrigger>
|
|
34
|
-
<DropdownMenuContent>
|
|
35
|
-
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
36
|
-
<DropdownMenuSeparator />
|
|
37
|
-
<DropdownMenuItem>
|
|
38
|
-
Profile
|
|
39
|
-
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
40
|
-
</DropdownMenuItem>
|
|
41
|
-
<DropdownMenuCheckboxItem checked={showPanel}>
|
|
42
|
-
Show Panel
|
|
43
|
-
</DropdownMenuCheckboxItem>
|
|
44
|
-
<DropdownMenuSeparator />
|
|
45
|
-
<DropdownMenuSub>
|
|
46
|
-
<DropdownMenuSubTrigger>More Options</DropdownMenuSubTrigger>
|
|
47
|
-
<DropdownMenuSubContent>
|
|
48
|
-
<DropdownMenuItem>Export</DropdownMenuItem>
|
|
49
|
-
</DropdownMenuSubContent>
|
|
50
|
-
</DropdownMenuSub>
|
|
51
|
-
</DropdownMenuContent>
|
|
52
|
-
</DropdownMenu>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**Componentes**: 14 (Menu, Trigger, Content, Item, CheckboxItem, RadioGroup, RadioItem, Label, Separator, Shortcut, Sub, SubTrigger, SubContent, Group, Portal)
|
|
56
|
-
|
|
57
|
-
## Props Principales
|
|
58
|
-
|
|
59
|
-
### DropdownMenu (Root)
|
|
60
|
-
|
|
61
|
-
| Prop | Tipo | Default | Descripción |
|
|
62
|
-
| -------------- | ------------------------- | ------- | -------------------------------------------------- |
|
|
63
|
-
| `open` | `boolean` | - | Estado controlado del menú |
|
|
64
|
-
| `onOpenChange` | `(open: boolean) => void` | - | Callback cuando cambia estado |
|
|
65
|
-
| `defaultOpen` | `boolean` | `false` | Estado inicial no controlado |
|
|
66
|
-
| `modal` | `boolean` | `true` | Comportamiento modal (bloquea interacción externa) |
|
|
67
|
-
|
|
68
|
-
### DropdownMenuTrigger
|
|
69
|
-
|
|
70
|
-
| Prop | Tipo | Descripción |
|
|
71
|
-
| ----------- | --------- | -------------------------------------------------- |
|
|
72
|
-
| `asChild` | `boolean` | Pasa props al child en lugar de renderizar wrapper |
|
|
73
|
-
| `disabled` | `boolean` | Desactiva el trigger |
|
|
74
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
75
|
-
|
|
76
|
-
### DropdownMenuContent
|
|
77
|
-
|
|
78
|
-
| Prop | Tipo | Default | Descripción |
|
|
79
|
-
| ---------------------- | ---------------------------------------- | ---------- | --------------------------------------------- |
|
|
80
|
-
| `align` | `"start" \| "center" \| "end"` | `"center"` | Alineamiento horizontal respecto al trigger |
|
|
81
|
-
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` | Lado donde aparece el menú |
|
|
82
|
-
| `sideOffset` | `number` | `4` | Distancia desde trigger (px) |
|
|
83
|
-
| `alignOffset` | `number` | `0` | Offset del alineamiento |
|
|
84
|
-
| `className` | `string` | - | Clases CSS adicionales |
|
|
85
|
-
| `loop` | `boolean` | `false` | Navegación circular con arrow keys |
|
|
86
|
-
| `onCloseAutoFocus` | `(event) => void` | - | Callback al cerrar (antes de restaurar focus) |
|
|
87
|
-
| `onEscapeKeyDown` | `(event) => void` | - | Callback al presionar Escape |
|
|
88
|
-
| `onPointerDownOutside` | `(event) => void` | - | Callback al hacer clic fuera |
|
|
89
|
-
|
|
90
|
-
**Estilos default**: `min-w-[8rem]`, `max-h-(--radix-dropdown-menu-content-available-height)`, scroll automático, `z-50`
|
|
91
|
-
|
|
92
|
-
### DropdownMenuItem
|
|
93
|
-
|
|
94
|
-
| Prop | Tipo | Default | Descripción |
|
|
95
|
-
| ----------- | ---------------------------- | ----------- | --------------------------------------- |
|
|
96
|
-
| `onSelect` | `(event) => void` | - | Callback al seleccionar (click o Enter) |
|
|
97
|
-
| `disabled` | `boolean` | `false` | Desactiva el item |
|
|
98
|
-
| `inset` | `boolean` | `false` | Agrega padding izquierdo (`pl-8`) |
|
|
99
|
-
| `variant` | `"default" \| "destructive"` | `"default"` | Estilo del item |
|
|
100
|
-
| `className` | `string` | - | Clases CSS adicionales |
|
|
101
|
-
|
|
102
|
-
**Variantes**:
|
|
103
|
-
|
|
104
|
-
- `default`: `focus:bg-accent`, `focus:text-accent-foreground`
|
|
105
|
-
- `destructive`: `text-destructive`, `focus:bg-destructive/10`, `focus:text-destructive`
|
|
106
|
-
|
|
107
|
-
### DropdownMenuCheckboxItem
|
|
108
|
-
|
|
109
|
-
| Prop | Tipo | Descripción |
|
|
110
|
-
| ----------------- | ---------------------------- | -------------------------- |
|
|
111
|
-
| `checked` | `boolean \| "indeterminate"` | Estado del checkbox |
|
|
112
|
-
| `onCheckedChange` | `(checked: boolean) => void` | Callback al cambiar estado |
|
|
113
|
-
| `disabled` | `boolean` | Desactiva el item |
|
|
114
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
115
|
-
|
|
116
|
-
**Indicador**: CheckIcon automático cuando `checked={true}`, posicionado con `pl-8`
|
|
117
|
-
|
|
118
|
-
### DropdownMenuRadioGroup
|
|
119
|
-
|
|
120
|
-
| Prop | Tipo | Descripción |
|
|
121
|
-
| --------------- | ------------------------- | ------------------------- |
|
|
122
|
-
| `value` | `string` | Valor seleccionado |
|
|
123
|
-
| `onValueChange` | `(value: string) => void` | Callback al cambiar valor |
|
|
124
|
-
|
|
125
|
-
### DropdownMenuRadioItem
|
|
126
|
-
|
|
127
|
-
| Prop | Tipo | Descripción |
|
|
128
|
-
| ----------- | --------- | ---------------------- |
|
|
129
|
-
| `value` | `string` | Valor único del item |
|
|
130
|
-
| `disabled` | `boolean` | Desactiva el item |
|
|
131
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
132
|
-
|
|
133
|
-
**Indicador**: CircleIcon automático cuando seleccionado, posicionado con `pl-8`
|
|
134
|
-
|
|
135
|
-
### DropdownMenuSub
|
|
136
|
-
|
|
137
|
-
| Prop | Tipo | Descripción |
|
|
138
|
-
| -------------- | ------------------------- | ----------------------------- |
|
|
139
|
-
| `open` | `boolean` | Estado controlado del submenú |
|
|
140
|
-
| `onOpenChange` | `(open: boolean) => void` | Callback al cambiar estado |
|
|
141
|
-
|
|
142
|
-
### DropdownMenuSubTrigger
|
|
143
|
-
|
|
144
|
-
| Prop | Tipo | Descripción |
|
|
145
|
-
| ----------- | --------- | -------------------------- |
|
|
146
|
-
| `disabled` | `boolean` | Desactiva el trigger |
|
|
147
|
-
| `inset` | `boolean` | Padding izquierdo (`pl-8`) |
|
|
148
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
149
|
-
|
|
150
|
-
**Indicador**: ChevronRightIcon automático con `ml-auto`
|
|
151
|
-
|
|
152
|
-
### DropdownMenuLabel
|
|
153
|
-
|
|
154
|
-
| Prop | Tipo | Descripción |
|
|
155
|
-
| ----------- | --------- | -------------------------- |
|
|
156
|
-
| `inset` | `boolean` | Padding izquierdo (`pl-8`) |
|
|
157
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
158
|
-
|
|
159
|
-
**Estilos**: `font-medium`, `text-sm`
|
|
160
|
-
|
|
161
|
-
### DropdownMenuShortcut
|
|
162
|
-
|
|
163
|
-
| Prop | Tipo | Descripción |
|
|
164
|
-
| ----------- | -------- | ---------------------- |
|
|
165
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
166
|
-
|
|
167
|
-
**Estilos**: `text-muted-foreground`, `text-xs`, `tracking-widest`, `ml-auto`
|
|
168
|
-
|
|
169
|
-
## Patrones de Uso
|
|
170
|
-
|
|
171
|
-
### Básico
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
<DropdownMenu>
|
|
175
|
-
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
|
|
176
|
-
<DropdownMenuContent>
|
|
177
|
-
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
178
|
-
<DropdownMenuSeparator />
|
|
179
|
-
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
180
|
-
<DropdownMenuItem>Billing</DropdownMenuItem>
|
|
181
|
-
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
182
|
-
</DropdownMenuContent>
|
|
183
|
-
</DropdownMenu>
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Con Trigger Custom (asChild)
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
<DropdownMenu>
|
|
190
|
-
<DropdownMenuTrigger asChild>
|
|
191
|
-
<Button variant="outline">Open Menu</Button>
|
|
192
|
-
</DropdownMenuTrigger>
|
|
193
|
-
<DropdownMenuContent>
|
|
194
|
-
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
195
|
-
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
196
|
-
</DropdownMenuContent>
|
|
197
|
-
</DropdownMenu>
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Con Iconos y Shortcuts
|
|
201
|
-
|
|
202
|
-
```tsx
|
|
203
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
204
|
-
|
|
205
|
-
<DropdownMenu>
|
|
206
|
-
<DropdownMenuTrigger asChild>
|
|
207
|
-
<Button variant="outline">Open</Button>
|
|
208
|
-
</DropdownMenuTrigger>
|
|
209
|
-
<DropdownMenuContent className="w-56" align="start">
|
|
210
|
-
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
211
|
-
<DropdownMenuSeparator />
|
|
212
|
-
<DropdownMenuItem>
|
|
213
|
-
<Icon symbol="person" />
|
|
214
|
-
Profile
|
|
215
|
-
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
216
|
-
</DropdownMenuItem>
|
|
217
|
-
<DropdownMenuItem>
|
|
218
|
-
<Icon symbol="credit_card" />
|
|
219
|
-
Billing
|
|
220
|
-
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
221
|
-
</DropdownMenuItem>
|
|
222
|
-
<DropdownMenuItem>
|
|
223
|
-
<Icon symbol="settings" />
|
|
224
|
-
Settings
|
|
225
|
-
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
226
|
-
</DropdownMenuItem>
|
|
227
|
-
<DropdownMenuSeparator />
|
|
228
|
-
<DropdownMenuItem>
|
|
229
|
-
<Icon symbol="logout" />
|
|
230
|
-
Log out
|
|
231
|
-
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
|
232
|
-
</DropdownMenuItem>
|
|
233
|
-
</DropdownMenuContent>
|
|
234
|
-
</DropdownMenu>;
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Nota**: SVGs automáticamente `size-4`, `text-muted-foreground` (excepto en variant destructive)
|
|
238
|
-
|
|
239
|
-
### Con Checkboxes
|
|
240
|
-
|
|
241
|
-
```tsx
|
|
242
|
-
import { useState } from "react";
|
|
243
|
-
|
|
244
|
-
function ViewOptions() {
|
|
245
|
-
const [showStatusBar, setShowStatusBar] = useState(true);
|
|
246
|
-
const [showActivityBar, setShowActivityBar] = useState(false);
|
|
247
|
-
const [showPanel, setShowPanel] = useState(false);
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
<DropdownMenu>
|
|
251
|
-
<DropdownMenuTrigger asChild>
|
|
252
|
-
<Button variant="outline">View Options</Button>
|
|
253
|
-
</DropdownMenuTrigger>
|
|
254
|
-
<DropdownMenuContent className="w-56">
|
|
255
|
-
<DropdownMenuLabel>Appearance</DropdownMenuLabel>
|
|
256
|
-
<DropdownMenuSeparator />
|
|
257
|
-
<DropdownMenuCheckboxItem
|
|
258
|
-
checked={showStatusBar}
|
|
259
|
-
onCheckedChange={setShowStatusBar}
|
|
260
|
-
>
|
|
261
|
-
Status Bar
|
|
262
|
-
</DropdownMenuCheckboxItem>
|
|
263
|
-
<DropdownMenuCheckboxItem
|
|
264
|
-
checked={showActivityBar}
|
|
265
|
-
onCheckedChange={setShowActivityBar}
|
|
266
|
-
>
|
|
267
|
-
Activity Bar
|
|
268
|
-
</DropdownMenuCheckboxItem>
|
|
269
|
-
<DropdownMenuCheckboxItem
|
|
270
|
-
checked={showPanel}
|
|
271
|
-
onCheckedChange={setShowPanel}
|
|
272
|
-
>
|
|
273
|
-
Panel
|
|
274
|
-
</DropdownMenuCheckboxItem>
|
|
275
|
-
</DropdownMenuContent>
|
|
276
|
-
</DropdownMenu>
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Con Radio Group
|
|
282
|
-
|
|
283
|
-
```tsx
|
|
284
|
-
import { useState } from "react";
|
|
285
|
-
|
|
286
|
-
function Settings() {
|
|
287
|
-
const [position, setPosition] = useState("bottom");
|
|
288
|
-
|
|
289
|
-
return (
|
|
290
|
-
<DropdownMenu>
|
|
291
|
-
<DropdownMenuTrigger asChild>
|
|
292
|
-
<Button variant="outline">Panel Position</Button>
|
|
293
|
-
</DropdownMenuTrigger>
|
|
294
|
-
<DropdownMenuContent className="w-56">
|
|
295
|
-
<DropdownMenuLabel>Panel Position</DropdownMenuLabel>
|
|
296
|
-
<DropdownMenuSeparator />
|
|
297
|
-
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
|
|
298
|
-
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
|
299
|
-
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
|
300
|
-
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
|
301
|
-
</DropdownMenuRadioGroup>
|
|
302
|
-
</DropdownMenuContent>
|
|
303
|
-
</DropdownMenu>
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Con Submenú
|
|
309
|
-
|
|
310
|
-
```tsx
|
|
311
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
312
|
-
|
|
313
|
-
<DropdownMenu>
|
|
314
|
-
<DropdownMenuTrigger asChild>
|
|
315
|
-
<Button variant="outline">Options</Button>
|
|
316
|
-
</DropdownMenuTrigger>
|
|
317
|
-
<DropdownMenuContent>
|
|
318
|
-
<DropdownMenuItem>
|
|
319
|
-
<Icon symbol="group" />
|
|
320
|
-
Team
|
|
321
|
-
</DropdownMenuItem>
|
|
322
|
-
<DropdownMenuSub>
|
|
323
|
-
<DropdownMenuSubTrigger>
|
|
324
|
-
<Icon symbol="person" />
|
|
325
|
-
Invite users
|
|
326
|
-
</DropdownMenuSubTrigger>
|
|
327
|
-
<DropdownMenuPortal>
|
|
328
|
-
<DropdownMenuSubContent>
|
|
329
|
-
<DropdownMenuItem>
|
|
330
|
-
<Icon symbol="mail" />
|
|
331
|
-
Email
|
|
332
|
-
</DropdownMenuItem>
|
|
333
|
-
<DropdownMenuItem>
|
|
334
|
-
<Icon symbol="chat_bubble" />
|
|
335
|
-
Message
|
|
336
|
-
</DropdownMenuItem>
|
|
337
|
-
<DropdownMenuSeparator />
|
|
338
|
-
<DropdownMenuItem>
|
|
339
|
-
<Icon symbol="add" />
|
|
340
|
-
More...
|
|
341
|
-
</DropdownMenuItem>
|
|
342
|
-
</DropdownMenuSubContent>
|
|
343
|
-
</DropdownMenuPortal>
|
|
344
|
-
</DropdownMenuSub>
|
|
345
|
-
</DropdownMenuContent>
|
|
346
|
-
</DropdownMenu>;
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### User Menu (Avatar)
|
|
350
|
-
|
|
351
|
-
```tsx
|
|
352
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
353
|
-
|
|
354
|
-
<DropdownMenu>
|
|
355
|
-
<DropdownMenuTrigger asChild>
|
|
356
|
-
<Button variant="ghost" className="h-8 w-8 rounded-full">
|
|
357
|
-
<Icon symbol="person" />
|
|
358
|
-
</Button>
|
|
359
|
-
</DropdownMenuTrigger>
|
|
360
|
-
<DropdownMenuContent className="w-56" align="end" forceMount>
|
|
361
|
-
<DropdownMenuLabel className="font-normal">
|
|
362
|
-
<div className="flex flex-col space-y-1">
|
|
363
|
-
<p className="text-sm font-medium leading-none">John Doe</p>
|
|
364
|
-
<p className="text-xs leading-none text-muted-foreground">
|
|
365
|
-
john.doe@example.com
|
|
366
|
-
</p>
|
|
367
|
-
</div>
|
|
368
|
-
</DropdownMenuLabel>
|
|
369
|
-
<DropdownMenuSeparator />
|
|
370
|
-
<DropdownMenuItem>
|
|
371
|
-
<Icon symbol="person" />
|
|
372
|
-
Profile
|
|
373
|
-
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
374
|
-
</DropdownMenuItem>
|
|
375
|
-
<DropdownMenuItem>
|
|
376
|
-
<Icon symbol="credit_card" />
|
|
377
|
-
Billing
|
|
378
|
-
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
379
|
-
</DropdownMenuItem>
|
|
380
|
-
<DropdownMenuItem>
|
|
381
|
-
<Icon symbol="settings" />
|
|
382
|
-
Settings
|
|
383
|
-
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
384
|
-
</DropdownMenuItem>
|
|
385
|
-
<DropdownMenuSeparator />
|
|
386
|
-
<DropdownMenuItem>
|
|
387
|
-
<Icon symbol="logout" />
|
|
388
|
-
Log out
|
|
389
|
-
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
|
390
|
-
</DropdownMenuItem>
|
|
391
|
-
</DropdownMenuContent>
|
|
392
|
-
</DropdownMenu>;
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
### File Actions (More Menu)
|
|
396
|
-
|
|
397
|
-
```tsx
|
|
398
|
-
import {
|
|
399
|
-
MoreHorizontalIcon,
|
|
400
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
401
|
-
|
|
402
|
-
<DropdownMenu>
|
|
403
|
-
<DropdownMenuTrigger asChild>
|
|
404
|
-
<Button variant="ghost" size="sm">
|
|
405
|
-
<Icon symbol="more_horiz" />
|
|
406
|
-
</Button>
|
|
407
|
-
</DropdownMenuTrigger>
|
|
408
|
-
<DropdownMenuContent align="end">
|
|
409
|
-
<DropdownMenuItem>
|
|
410
|
-
<Icon symbol="open_in_new" />
|
|
411
|
-
Open
|
|
412
|
-
</DropdownMenuItem>
|
|
413
|
-
<DropdownMenuItem>
|
|
414
|
-
<Icon symbol="content_copy" />
|
|
415
|
-
Copy Link
|
|
416
|
-
</DropdownMenuItem>
|
|
417
|
-
<DropdownMenuItem>
|
|
418
|
-
<Icon symbol="share" />
|
|
419
|
-
Share
|
|
420
|
-
</DropdownMenuItem>
|
|
421
|
-
<DropdownMenuSeparator />
|
|
422
|
-
<DropdownMenuItem>
|
|
423
|
-
<Icon symbol="download" />
|
|
424
|
-
Download
|
|
425
|
-
</DropdownMenuItem>
|
|
426
|
-
<DropdownMenuItem>
|
|
427
|
-
<Icon symbol="edit" />
|
|
428
|
-
Rename
|
|
429
|
-
</DropdownMenuItem>
|
|
430
|
-
<DropdownMenuSeparator />
|
|
431
|
-
<DropdownMenuItem variant="destructive">
|
|
432
|
-
<Icon symbol="delete" />
|
|
433
|
-
Delete
|
|
434
|
-
</DropdownMenuItem>
|
|
435
|
-
</DropdownMenuContent>
|
|
436
|
-
</DropdownMenu>;
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
### Simple Actions con Chevron
|
|
440
|
-
|
|
441
|
-
```tsx
|
|
442
|
-
import {
|
|
443
|
-
ChevronDownIcon,
|
|
444
|
-
EditIcon,
|
|
445
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
446
|
-
|
|
447
|
-
<DropdownMenu>
|
|
448
|
-
<DropdownMenuTrigger asChild>
|
|
449
|
-
<Button variant="outline">
|
|
450
|
-
Actions
|
|
451
|
-
<Icon symbol="expand_more" />
|
|
452
|
-
</Button>
|
|
453
|
-
</DropdownMenuTrigger>
|
|
454
|
-
<DropdownMenuContent>
|
|
455
|
-
<DropdownMenuItem>
|
|
456
|
-
<Icon symbol="edit" />
|
|
457
|
-
Edit
|
|
458
|
-
</DropdownMenuItem>
|
|
459
|
-
<DropdownMenuItem>
|
|
460
|
-
<Icon symbol="content_copy" />
|
|
461
|
-
Duplicate
|
|
462
|
-
</DropdownMenuItem>
|
|
463
|
-
<DropdownMenuItem>
|
|
464
|
-
<Icon symbol="share" />
|
|
465
|
-
Share
|
|
466
|
-
</DropdownMenuItem>
|
|
467
|
-
<DropdownMenuSeparator />
|
|
468
|
-
<DropdownMenuItem variant="destructive">
|
|
469
|
-
<Icon symbol="delete" />
|
|
470
|
-
Delete
|
|
471
|
-
</DropdownMenuItem>
|
|
472
|
-
</DropdownMenuContent>
|
|
473
|
-
</DropdownMenu>;
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
### Project Menu (Nested)
|
|
477
|
-
|
|
478
|
-
```tsx
|
|
479
|
-
import {
|
|
480
|
-
FolderIcon,
|
|
481
|
-
EditIcon,
|
|
482
|
-
ShareIcon,
|
|
483
|
-
CopyIcon,
|
|
484
|
-
UsersIcon,
|
|
485
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
486
|
-
|
|
487
|
-
<DropdownMenu>
|
|
488
|
-
<DropdownMenuTrigger asChild>
|
|
489
|
-
<Button variant="outline">
|
|
490
|
-
<Icon symbol="folder" />
|
|
491
|
-
My Project
|
|
492
|
-
<Icon symbol="expand_more" />
|
|
493
|
-
</Button>
|
|
494
|
-
</DropdownMenuTrigger>
|
|
495
|
-
<DropdownMenuContent className="w-56">
|
|
496
|
-
<DropdownMenuLabel>Project Actions</DropdownMenuLabel>
|
|
497
|
-
<DropdownMenuSeparator />
|
|
498
|
-
<DropdownMenuItem>
|
|
499
|
-
<Icon symbol="edit" />
|
|
500
|
-
Edit Project
|
|
501
|
-
</DropdownMenuItem>
|
|
502
|
-
<DropdownMenuItem>
|
|
503
|
-
<Icon symbol="share" />
|
|
504
|
-
Share Project
|
|
505
|
-
</DropdownMenuItem>
|
|
506
|
-
<DropdownMenuItem>
|
|
507
|
-
<Icon symbol="content_copy" />
|
|
508
|
-
Duplicate Project
|
|
509
|
-
</DropdownMenuItem>
|
|
510
|
-
<DropdownMenuSeparator />
|
|
511
|
-
<DropdownMenuSub>
|
|
512
|
-
<DropdownMenuSubTrigger>
|
|
513
|
-
<Icon symbol="group" />
|
|
514
|
-
Manage Access
|
|
515
|
-
</DropdownMenuSubTrigger>
|
|
516
|
-
<DropdownMenuPortal>
|
|
517
|
-
<DropdownMenuSubContent>
|
|
518
|
-
<DropdownMenuItem>
|
|
519
|
-
<Icon symbol="person_add" />
|
|
520
|
-
Add Member
|
|
521
|
-
</DropdownMenuItem>
|
|
522
|
-
<DropdownMenuItem>
|
|
523
|
-
<Icon symbol="admin_panel_settings" />
|
|
524
|
-
Permissions
|
|
525
|
-
</DropdownMenuItem>
|
|
526
|
-
<DropdownMenuSeparator />
|
|
527
|
-
<DropdownMenuItem>
|
|
528
|
-
<Icon symbol="group" />
|
|
529
|
-
View Members
|
|
530
|
-
</DropdownMenuItem>
|
|
531
|
-
</DropdownMenuSubContent>
|
|
532
|
-
</DropdownMenuPortal>
|
|
533
|
-
</DropdownMenuSub>
|
|
534
|
-
<DropdownMenuSeparator />
|
|
535
|
-
<DropdownMenuItem>
|
|
536
|
-
<Icon symbol="download" />
|
|
537
|
-
Export
|
|
538
|
-
</DropdownMenuItem>
|
|
539
|
-
<DropdownMenuItem>
|
|
540
|
-
<Icon symbol="settings" />
|
|
541
|
-
Settings
|
|
542
|
-
</DropdownMenuItem>
|
|
543
|
-
<DropdownMenuSeparator />
|
|
544
|
-
<DropdownMenuItem variant="destructive">
|
|
545
|
-
<Icon symbol="delete" />
|
|
546
|
-
Delete Project
|
|
547
|
-
</DropdownMenuItem>
|
|
548
|
-
</DropdownMenuContent>
|
|
549
|
-
</DropdownMenu>;
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
### Items Deshabilitados
|
|
553
|
-
|
|
554
|
-
```tsx
|
|
555
|
-
<DropdownMenu>
|
|
556
|
-
<DropdownMenuTrigger asChild>
|
|
557
|
-
<Button variant="outline">Options</Button>
|
|
558
|
-
</DropdownMenuTrigger>
|
|
559
|
-
<DropdownMenuContent>
|
|
560
|
-
<DropdownMenuItem>Available Action</DropdownMenuItem>
|
|
561
|
-
<DropdownMenuItem disabled>Premium Feature</DropdownMenuItem>
|
|
562
|
-
<DropdownMenuItem>Another Action</DropdownMenuItem>
|
|
563
|
-
<DropdownMenuSeparator />
|
|
564
|
-
<DropdownMenuItem disabled>Coming Soon</DropdownMenuItem>
|
|
565
|
-
<DropdownMenuItem variant="destructive" disabled>
|
|
566
|
-
No Permission
|
|
567
|
-
</DropdownMenuItem>
|
|
568
|
-
</DropdownMenuContent>
|
|
569
|
-
</DropdownMenu>
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
**Estilos disabled**: `opacity-50`, `pointer-events-none`, `data-[disabled=true]`
|
|
573
|
-
|
|
574
|
-
### Controlado
|
|
575
|
-
|
|
576
|
-
```tsx
|
|
577
|
-
import { useState } from "react";
|
|
578
|
-
|
|
579
|
-
function App() {
|
|
580
|
-
const [open, setOpen] = useState(false);
|
|
581
|
-
|
|
582
|
-
return (
|
|
583
|
-
<DropdownMenu open={open} onOpenChange={setOpen}>
|
|
584
|
-
<DropdownMenuTrigger asChild>
|
|
585
|
-
<Button>Menu</Button>
|
|
586
|
-
</DropdownMenuTrigger>
|
|
587
|
-
<DropdownMenuContent>
|
|
588
|
-
<DropdownMenuItem onClick={() => setOpen(false)}>
|
|
589
|
-
Action (closes menu)
|
|
590
|
-
</DropdownMenuItem>
|
|
591
|
-
</DropdownMenuContent>
|
|
592
|
-
</DropdownMenu>
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
## Casos de Uso Comunes
|
|
598
|
-
|
|
599
|
-
**User menus**: Avatar menu con profile, settings, logout
|
|
600
|
-
**Action menus**: File actions, row actions, more options
|
|
601
|
-
**Navigation**: Nested navigation menus
|
|
602
|
-
**Filters/Options**: View settings, panel positions
|
|
603
|
-
**Context actions**: Edit, share, delete operations
|
|
604
|
-
**Bulk actions**: Select multiple items, apply actions
|
|
605
|
-
|
|
606
|
-
## Estados y Data Attributes
|
|
607
|
-
|
|
608
|
-
### DropdownMenuItem States
|
|
609
|
-
|
|
610
|
-
- **Focus**: `data-[highlighted]` → `bg-accent`, `text-accent-foreground`
|
|
611
|
-
- **Disabled**: `data-[disabled=true]` → `opacity-50`, `pointer-events-none`
|
|
612
|
-
- **Destructive**: `data-[variant=destructive]` → `text-destructive`
|
|
613
|
-
|
|
614
|
-
### DropdownMenuSubTrigger States
|
|
615
|
-
|
|
616
|
-
- **Open**: `data-[state=open]` → `bg-accent`, `text-accent-foreground`
|
|
617
|
-
|
|
618
|
-
### Content Animations
|
|
619
|
-
|
|
620
|
-
- **Opening**: `data-[state=open]` → `animate-in`, `fade-in-0`, `zoom-in-95`
|
|
621
|
-
- **Closing**: `data-[state=closed]` → `animate-out`, `fade-out-0`, `zoom-out-95`
|
|
622
|
-
- **Slide in**: `data-[side=top|right|bottom|left]` → direccional
|
|
623
|
-
|
|
624
|
-
## Navegación por Teclado
|
|
625
|
-
|
|
626
|
-
- ✅ **Arrow Up/Down**: Navega entre items
|
|
627
|
-
- ✅ **Arrow Right**: Abre submenú (si disponible)
|
|
628
|
-
- ✅ **Arrow Left**: Cierra submenú
|
|
629
|
-
- ✅ **Enter/Space**: Activa item enfocado
|
|
630
|
-
- ✅ **Escape**: Cierra menú
|
|
631
|
-
- ✅ **Tab**: Mueve focus fuera del menú
|
|
632
|
-
- ✅ **Home/End**: Primer/último item (si `loop={true}`)
|
|
633
|
-
- ✅ **Type ahead**: Busca item por primera letra
|
|
634
|
-
|
|
635
|
-
## Accesibilidad
|
|
636
|
-
|
|
637
|
-
- ✅ **ARIA**: `role="menu"` en content, `role="menuitem"` en items
|
|
638
|
-
- ✅ **Keyboard navigation**: Navegación completa por teclado
|
|
639
|
-
- ✅ **Focus management**: Focus visible y restaurado correctamente
|
|
640
|
-
- ✅ **Screen readers**: Anuncia items, estado de checkboxes/radios, disabled
|
|
641
|
-
- ✅ **Disabled items**: `aria-disabled="true"` en items deshabilitados
|
|
642
|
-
- ✅ **Submenus**: `aria-haspopup="menu"` en SubTriggers
|
|
643
|
-
- ✅ **Checked state**: `aria-checked` en CheckboxItem y RadioItem
|
|
644
|
-
|
|
645
|
-
## Notas de Implementación
|
|
646
|
-
|
|
647
|
-
- **Basado en Radix UI**: `@radix-ui/react-dropdown-menu`
|
|
648
|
-
- **Click to open**: Se activa con click (diferente de ContextMenu que usa right-click)
|
|
649
|
-
- **Auto positioning**: Portal con posicionamiento automático
|
|
650
|
-
- **Collision detection**: Evita salirse del viewport
|
|
651
|
-
- **Auto-close**: Cierra al seleccionar item (excepto checkboxes/radios)
|
|
652
|
-
- **Focus trap**: Mientras está abierto, focus queda dentro del menú
|
|
653
|
-
- **Portal rendering**: Content se renderiza en `document.body` por defecto
|
|
654
|
-
- **SVG auto-sizing**: Iconos automáticamente `size-4` y color muted
|
|
655
|
-
- **Inset support**: `inset` prop agrega `pl-8` para alinear con items con iconos
|
|
656
|
-
- **ChevronRightIcon**: Incluido automáticamente en SubTriggers
|
|
657
|
-
- **CheckIcon/CircleIcon**: Incluidos automáticamente en CheckboxItem/RadioItem
|
|
658
|
-
- **Variant destructive**: SVGs ignoran color muted y usan `text-destructive`
|
|
659
|
-
- **Default sideOffset**: 4px de distancia desde trigger
|
|
660
|
-
|
|
661
|
-
## Posicionamiento
|
|
662
|
-
|
|
663
|
-
Control completo del posicionamiento del menú:
|
|
664
|
-
|
|
665
|
-
```tsx
|
|
666
|
-
<DropdownMenuContent
|
|
667
|
-
align="end" // "start" | "center" | "end"
|
|
668
|
-
side="bottom" // "top" | "right" | "bottom" | "left"
|
|
669
|
-
sideOffset={8} // distancia desde trigger
|
|
670
|
-
alignOffset={-4} // offset horizontal
|
|
671
|
-
className="w-64" // ancho custom
|
|
672
|
-
>
|
|
673
|
-
{/* ... */}
|
|
674
|
-
</DropdownMenuContent>
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
**Ejemplos comunes**:
|
|
678
|
-
|
|
679
|
-
- User menu (top-right avatar): `align="end"`
|
|
680
|
-
- Actions menu (more button): `align="end"`
|
|
681
|
-
- Navigation menu: `align="start"`
|
|
682
|
-
|
|
683
|
-
## Troubleshooting
|
|
684
|
-
|
|
685
|
-
**Menú no aparece**: Verifica que DropdownMenuTrigger tenga `asChild` si usas Button custom
|
|
686
|
-
**Items no responden a click**: Usa `onSelect` en lugar de `onClick`
|
|
687
|
-
**Iconos mal alineados**: Usa `inset` en items sin iconos cuando otros sí tienen
|
|
688
|
-
**Menú no cierra al seleccionar**: CheckboxItems y RadioItems no cierran automáticamente (comportamiento esperado)
|
|
689
|
-
**Submenú no abre**: Verifica que estés usando `DropdownMenuSub` + `DropdownMenuSubTrigger` + `DropdownMenuSubContent`
|
|
690
|
-
**Shortcuts no ejecutan**: `DropdownMenuShortcut` es solo visual, implementa lógica en `onSelect`
|
|
691
|
-
**Focus se pierde al cerrar**: Usa `onCloseAutoFocus` para control custom del focus
|
|
692
|
-
**Posición incorrecta**: Verifica `align` y `side` props, ajusta con `sideOffset` y `alignOffset`
|
|
693
|
-
**Trigger no muestra estado**: Radix no cambia apariencia del trigger automáticamente, usa estado controlado
|
|
694
|
-
|
|
695
|
-
## Diferencias con ContextMenu
|
|
696
|
-
|
|
697
|
-
| Aspecto | DropdownMenu | ContextMenu |
|
|
698
|
-
| -------------------- | --------------------------- | --------------------- |
|
|
699
|
-
| **Activación** | Click izquierdo | Click derecho |
|
|
700
|
-
| **Trigger** | Botón/elemento visible | Área target |
|
|
701
|
-
| **Uso común** | Menús de acción, navegación | Acciones contextuales |
|
|
702
|
-
| **Indicador visual** | Botón con chevron | Sin indicador |
|
|
703
|
-
| **Posicionamiento** | Relativo al trigger | Posición del cursor |
|
|
704
|
-
|
|
705
|
-
## Referencias
|
|
706
|
-
|
|
707
|
-
- **Radix UI Dropdown Menu**: https://www.radix-ui.com/primitives/docs/components/dropdown-menu
|
|
708
|
-
- **shadcn/ui Dropdown Menu**: https://ui.shadcn.com/docs/components/dropdown-menu
|
|
709
|
-
- **ARIA Menu Pattern**: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
|
|
1
|
+
# Dropdown Menu
|
|
2
|
+
|
|
3
|
+
Menú desplegable activado por click, basado en Radix UI. Soporta submenús, checkboxes, radio groups, separadores, y shortcuts visuales. Similar a ContextMenu pero activado con click en lugar de click derecho.
|
|
4
|
+
|
|
5
|
+
## Importación
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
DropdownMenuContent,
|
|
12
|
+
DropdownMenuItem,
|
|
13
|
+
DropdownMenuCheckboxItem,
|
|
14
|
+
DropdownMenuRadioGroup,
|
|
15
|
+
DropdownMenuRadioItem,
|
|
16
|
+
DropdownMenuLabel,
|
|
17
|
+
DropdownMenuSeparator,
|
|
18
|
+
DropdownMenuShortcut,
|
|
19
|
+
DropdownMenuSub,
|
|
20
|
+
DropdownMenuSubContent,
|
|
21
|
+
DropdownMenuSubTrigger,
|
|
22
|
+
DropdownMenuGroup,
|
|
23
|
+
DropdownMenuPortal,
|
|
24
|
+
} from "@adamosuiteservices/ui/dropdown-menu";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Anatomía
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<DropdownMenu>
|
|
31
|
+
<DropdownMenuTrigger asChild>
|
|
32
|
+
<Button>Open Menu</Button>
|
|
33
|
+
</DropdownMenuTrigger>
|
|
34
|
+
<DropdownMenuContent>
|
|
35
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
36
|
+
<DropdownMenuSeparator />
|
|
37
|
+
<DropdownMenuItem>
|
|
38
|
+
Profile
|
|
39
|
+
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
40
|
+
</DropdownMenuItem>
|
|
41
|
+
<DropdownMenuCheckboxItem checked={showPanel}>
|
|
42
|
+
Show Panel
|
|
43
|
+
</DropdownMenuCheckboxItem>
|
|
44
|
+
<DropdownMenuSeparator />
|
|
45
|
+
<DropdownMenuSub>
|
|
46
|
+
<DropdownMenuSubTrigger>More Options</DropdownMenuSubTrigger>
|
|
47
|
+
<DropdownMenuSubContent>
|
|
48
|
+
<DropdownMenuItem>Export</DropdownMenuItem>
|
|
49
|
+
</DropdownMenuSubContent>
|
|
50
|
+
</DropdownMenuSub>
|
|
51
|
+
</DropdownMenuContent>
|
|
52
|
+
</DropdownMenu>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Componentes**: 14 (Menu, Trigger, Content, Item, CheckboxItem, RadioGroup, RadioItem, Label, Separator, Shortcut, Sub, SubTrigger, SubContent, Group, Portal)
|
|
56
|
+
|
|
57
|
+
## Props Principales
|
|
58
|
+
|
|
59
|
+
### DropdownMenu (Root)
|
|
60
|
+
|
|
61
|
+
| Prop | Tipo | Default | Descripción |
|
|
62
|
+
| -------------- | ------------------------- | ------- | -------------------------------------------------- |
|
|
63
|
+
| `open` | `boolean` | - | Estado controlado del menú |
|
|
64
|
+
| `onOpenChange` | `(open: boolean) => void` | - | Callback cuando cambia estado |
|
|
65
|
+
| `defaultOpen` | `boolean` | `false` | Estado inicial no controlado |
|
|
66
|
+
| `modal` | `boolean` | `true` | Comportamiento modal (bloquea interacción externa) |
|
|
67
|
+
|
|
68
|
+
### DropdownMenuTrigger
|
|
69
|
+
|
|
70
|
+
| Prop | Tipo | Descripción |
|
|
71
|
+
| ----------- | --------- | -------------------------------------------------- |
|
|
72
|
+
| `asChild` | `boolean` | Pasa props al child en lugar de renderizar wrapper |
|
|
73
|
+
| `disabled` | `boolean` | Desactiva el trigger |
|
|
74
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
75
|
+
|
|
76
|
+
### DropdownMenuContent
|
|
77
|
+
|
|
78
|
+
| Prop | Tipo | Default | Descripción |
|
|
79
|
+
| ---------------------- | ---------------------------------------- | ---------- | --------------------------------------------- |
|
|
80
|
+
| `align` | `"start" \| "center" \| "end"` | `"center"` | Alineamiento horizontal respecto al trigger |
|
|
81
|
+
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` | Lado donde aparece el menú |
|
|
82
|
+
| `sideOffset` | `number` | `4` | Distancia desde trigger (px) |
|
|
83
|
+
| `alignOffset` | `number` | `0` | Offset del alineamiento |
|
|
84
|
+
| `className` | `string` | - | Clases CSS adicionales |
|
|
85
|
+
| `loop` | `boolean` | `false` | Navegación circular con arrow keys |
|
|
86
|
+
| `onCloseAutoFocus` | `(event) => void` | - | Callback al cerrar (antes de restaurar focus) |
|
|
87
|
+
| `onEscapeKeyDown` | `(event) => void` | - | Callback al presionar Escape |
|
|
88
|
+
| `onPointerDownOutside` | `(event) => void` | - | Callback al hacer clic fuera |
|
|
89
|
+
|
|
90
|
+
**Estilos default**: `min-w-[8rem]`, `max-h-(--radix-dropdown-menu-content-available-height)`, scroll automático, `z-50`
|
|
91
|
+
|
|
92
|
+
### DropdownMenuItem
|
|
93
|
+
|
|
94
|
+
| Prop | Tipo | Default | Descripción |
|
|
95
|
+
| ----------- | ---------------------------- | ----------- | --------------------------------------- |
|
|
96
|
+
| `onSelect` | `(event) => void` | - | Callback al seleccionar (click o Enter) |
|
|
97
|
+
| `disabled` | `boolean` | `false` | Desactiva el item |
|
|
98
|
+
| `inset` | `boolean` | `false` | Agrega padding izquierdo (`pl-8`) |
|
|
99
|
+
| `variant` | `"default" \| "destructive"` | `"default"` | Estilo del item |
|
|
100
|
+
| `className` | `string` | - | Clases CSS adicionales |
|
|
101
|
+
|
|
102
|
+
**Variantes**:
|
|
103
|
+
|
|
104
|
+
- `default`: `focus:bg-accent`, `focus:text-accent-foreground`
|
|
105
|
+
- `destructive`: `text-destructive`, `focus:bg-destructive/10`, `focus:text-destructive`
|
|
106
|
+
|
|
107
|
+
### DropdownMenuCheckboxItem
|
|
108
|
+
|
|
109
|
+
| Prop | Tipo | Descripción |
|
|
110
|
+
| ----------------- | ---------------------------- | -------------------------- |
|
|
111
|
+
| `checked` | `boolean \| "indeterminate"` | Estado del checkbox |
|
|
112
|
+
| `onCheckedChange` | `(checked: boolean) => void` | Callback al cambiar estado |
|
|
113
|
+
| `disabled` | `boolean` | Desactiva el item |
|
|
114
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
115
|
+
|
|
116
|
+
**Indicador**: CheckIcon automático cuando `checked={true}`, posicionado con `pl-8`
|
|
117
|
+
|
|
118
|
+
### DropdownMenuRadioGroup
|
|
119
|
+
|
|
120
|
+
| Prop | Tipo | Descripción |
|
|
121
|
+
| --------------- | ------------------------- | ------------------------- |
|
|
122
|
+
| `value` | `string` | Valor seleccionado |
|
|
123
|
+
| `onValueChange` | `(value: string) => void` | Callback al cambiar valor |
|
|
124
|
+
|
|
125
|
+
### DropdownMenuRadioItem
|
|
126
|
+
|
|
127
|
+
| Prop | Tipo | Descripción |
|
|
128
|
+
| ----------- | --------- | ---------------------- |
|
|
129
|
+
| `value` | `string` | Valor único del item |
|
|
130
|
+
| `disabled` | `boolean` | Desactiva el item |
|
|
131
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
132
|
+
|
|
133
|
+
**Indicador**: CircleIcon automático cuando seleccionado, posicionado con `pl-8`
|
|
134
|
+
|
|
135
|
+
### DropdownMenuSub
|
|
136
|
+
|
|
137
|
+
| Prop | Tipo | Descripción |
|
|
138
|
+
| -------------- | ------------------------- | ----------------------------- |
|
|
139
|
+
| `open` | `boolean` | Estado controlado del submenú |
|
|
140
|
+
| `onOpenChange` | `(open: boolean) => void` | Callback al cambiar estado |
|
|
141
|
+
|
|
142
|
+
### DropdownMenuSubTrigger
|
|
143
|
+
|
|
144
|
+
| Prop | Tipo | Descripción |
|
|
145
|
+
| ----------- | --------- | -------------------------- |
|
|
146
|
+
| `disabled` | `boolean` | Desactiva el trigger |
|
|
147
|
+
| `inset` | `boolean` | Padding izquierdo (`pl-8`) |
|
|
148
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
149
|
+
|
|
150
|
+
**Indicador**: ChevronRightIcon automático con `ml-auto`
|
|
151
|
+
|
|
152
|
+
### DropdownMenuLabel
|
|
153
|
+
|
|
154
|
+
| Prop | Tipo | Descripción |
|
|
155
|
+
| ----------- | --------- | -------------------------- |
|
|
156
|
+
| `inset` | `boolean` | Padding izquierdo (`pl-8`) |
|
|
157
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
158
|
+
|
|
159
|
+
**Estilos**: `font-medium`, `text-sm`
|
|
160
|
+
|
|
161
|
+
### DropdownMenuShortcut
|
|
162
|
+
|
|
163
|
+
| Prop | Tipo | Descripción |
|
|
164
|
+
| ----------- | -------- | ---------------------- |
|
|
165
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
166
|
+
|
|
167
|
+
**Estilos**: `text-muted-foreground`, `text-xs`, `tracking-widest`, `ml-auto`
|
|
168
|
+
|
|
169
|
+
## Patrones de Uso
|
|
170
|
+
|
|
171
|
+
### Básico
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
<DropdownMenu>
|
|
175
|
+
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
|
|
176
|
+
<DropdownMenuContent>
|
|
177
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
178
|
+
<DropdownMenuSeparator />
|
|
179
|
+
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
180
|
+
<DropdownMenuItem>Billing</DropdownMenuItem>
|
|
181
|
+
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
182
|
+
</DropdownMenuContent>
|
|
183
|
+
</DropdownMenu>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Con Trigger Custom (asChild)
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
<DropdownMenu>
|
|
190
|
+
<DropdownMenuTrigger asChild>
|
|
191
|
+
<Button variant="outline">Open Menu</Button>
|
|
192
|
+
</DropdownMenuTrigger>
|
|
193
|
+
<DropdownMenuContent>
|
|
194
|
+
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
195
|
+
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
196
|
+
</DropdownMenuContent>
|
|
197
|
+
</DropdownMenu>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Con Iconos y Shortcuts
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
204
|
+
|
|
205
|
+
<DropdownMenu>
|
|
206
|
+
<DropdownMenuTrigger asChild>
|
|
207
|
+
<Button variant="outline">Open</Button>
|
|
208
|
+
</DropdownMenuTrigger>
|
|
209
|
+
<DropdownMenuContent className="w-56" align="start">
|
|
210
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
211
|
+
<DropdownMenuSeparator />
|
|
212
|
+
<DropdownMenuItem>
|
|
213
|
+
<Icon symbol="person" />
|
|
214
|
+
Profile
|
|
215
|
+
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
216
|
+
</DropdownMenuItem>
|
|
217
|
+
<DropdownMenuItem>
|
|
218
|
+
<Icon symbol="credit_card" />
|
|
219
|
+
Billing
|
|
220
|
+
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
221
|
+
</DropdownMenuItem>
|
|
222
|
+
<DropdownMenuItem>
|
|
223
|
+
<Icon symbol="settings" />
|
|
224
|
+
Settings
|
|
225
|
+
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
226
|
+
</DropdownMenuItem>
|
|
227
|
+
<DropdownMenuSeparator />
|
|
228
|
+
<DropdownMenuItem>
|
|
229
|
+
<Icon symbol="logout" />
|
|
230
|
+
Log out
|
|
231
|
+
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
|
232
|
+
</DropdownMenuItem>
|
|
233
|
+
</DropdownMenuContent>
|
|
234
|
+
</DropdownMenu>;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Nota**: SVGs automáticamente `size-4`, `text-muted-foreground` (excepto en variant destructive)
|
|
238
|
+
|
|
239
|
+
### Con Checkboxes
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
import { useState } from "react";
|
|
243
|
+
|
|
244
|
+
function ViewOptions() {
|
|
245
|
+
const [showStatusBar, setShowStatusBar] = useState(true);
|
|
246
|
+
const [showActivityBar, setShowActivityBar] = useState(false);
|
|
247
|
+
const [showPanel, setShowPanel] = useState(false);
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<DropdownMenu>
|
|
251
|
+
<DropdownMenuTrigger asChild>
|
|
252
|
+
<Button variant="outline">View Options</Button>
|
|
253
|
+
</DropdownMenuTrigger>
|
|
254
|
+
<DropdownMenuContent className="w-56">
|
|
255
|
+
<DropdownMenuLabel>Appearance</DropdownMenuLabel>
|
|
256
|
+
<DropdownMenuSeparator />
|
|
257
|
+
<DropdownMenuCheckboxItem
|
|
258
|
+
checked={showStatusBar}
|
|
259
|
+
onCheckedChange={setShowStatusBar}
|
|
260
|
+
>
|
|
261
|
+
Status Bar
|
|
262
|
+
</DropdownMenuCheckboxItem>
|
|
263
|
+
<DropdownMenuCheckboxItem
|
|
264
|
+
checked={showActivityBar}
|
|
265
|
+
onCheckedChange={setShowActivityBar}
|
|
266
|
+
>
|
|
267
|
+
Activity Bar
|
|
268
|
+
</DropdownMenuCheckboxItem>
|
|
269
|
+
<DropdownMenuCheckboxItem
|
|
270
|
+
checked={showPanel}
|
|
271
|
+
onCheckedChange={setShowPanel}
|
|
272
|
+
>
|
|
273
|
+
Panel
|
|
274
|
+
</DropdownMenuCheckboxItem>
|
|
275
|
+
</DropdownMenuContent>
|
|
276
|
+
</DropdownMenu>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Con Radio Group
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
import { useState } from "react";
|
|
285
|
+
|
|
286
|
+
function Settings() {
|
|
287
|
+
const [position, setPosition] = useState("bottom");
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<DropdownMenu>
|
|
291
|
+
<DropdownMenuTrigger asChild>
|
|
292
|
+
<Button variant="outline">Panel Position</Button>
|
|
293
|
+
</DropdownMenuTrigger>
|
|
294
|
+
<DropdownMenuContent className="w-56">
|
|
295
|
+
<DropdownMenuLabel>Panel Position</DropdownMenuLabel>
|
|
296
|
+
<DropdownMenuSeparator />
|
|
297
|
+
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
|
|
298
|
+
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
|
299
|
+
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
|
300
|
+
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
|
301
|
+
</DropdownMenuRadioGroup>
|
|
302
|
+
</DropdownMenuContent>
|
|
303
|
+
</DropdownMenu>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Con Submenú
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
312
|
+
|
|
313
|
+
<DropdownMenu>
|
|
314
|
+
<DropdownMenuTrigger asChild>
|
|
315
|
+
<Button variant="outline">Options</Button>
|
|
316
|
+
</DropdownMenuTrigger>
|
|
317
|
+
<DropdownMenuContent>
|
|
318
|
+
<DropdownMenuItem>
|
|
319
|
+
<Icon symbol="group" />
|
|
320
|
+
Team
|
|
321
|
+
</DropdownMenuItem>
|
|
322
|
+
<DropdownMenuSub>
|
|
323
|
+
<DropdownMenuSubTrigger>
|
|
324
|
+
<Icon symbol="person" />
|
|
325
|
+
Invite users
|
|
326
|
+
</DropdownMenuSubTrigger>
|
|
327
|
+
<DropdownMenuPortal>
|
|
328
|
+
<DropdownMenuSubContent>
|
|
329
|
+
<DropdownMenuItem>
|
|
330
|
+
<Icon symbol="mail" />
|
|
331
|
+
Email
|
|
332
|
+
</DropdownMenuItem>
|
|
333
|
+
<DropdownMenuItem>
|
|
334
|
+
<Icon symbol="chat_bubble" />
|
|
335
|
+
Message
|
|
336
|
+
</DropdownMenuItem>
|
|
337
|
+
<DropdownMenuSeparator />
|
|
338
|
+
<DropdownMenuItem>
|
|
339
|
+
<Icon symbol="add" />
|
|
340
|
+
More...
|
|
341
|
+
</DropdownMenuItem>
|
|
342
|
+
</DropdownMenuSubContent>
|
|
343
|
+
</DropdownMenuPortal>
|
|
344
|
+
</DropdownMenuSub>
|
|
345
|
+
</DropdownMenuContent>
|
|
346
|
+
</DropdownMenu>;
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### User Menu (Avatar)
|
|
350
|
+
|
|
351
|
+
```tsx
|
|
352
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
353
|
+
|
|
354
|
+
<DropdownMenu>
|
|
355
|
+
<DropdownMenuTrigger asChild>
|
|
356
|
+
<Button variant="ghost" className="h-8 w-8 rounded-full">
|
|
357
|
+
<Icon symbol="person" />
|
|
358
|
+
</Button>
|
|
359
|
+
</DropdownMenuTrigger>
|
|
360
|
+
<DropdownMenuContent className="w-56" align="end" forceMount>
|
|
361
|
+
<DropdownMenuLabel className="font-normal">
|
|
362
|
+
<div className="flex flex-col space-y-1">
|
|
363
|
+
<p className="text-sm font-medium leading-none">John Doe</p>
|
|
364
|
+
<p className="text-xs leading-none text-muted-foreground">
|
|
365
|
+
john.doe@example.com
|
|
366
|
+
</p>
|
|
367
|
+
</div>
|
|
368
|
+
</DropdownMenuLabel>
|
|
369
|
+
<DropdownMenuSeparator />
|
|
370
|
+
<DropdownMenuItem>
|
|
371
|
+
<Icon symbol="person" />
|
|
372
|
+
Profile
|
|
373
|
+
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
374
|
+
</DropdownMenuItem>
|
|
375
|
+
<DropdownMenuItem>
|
|
376
|
+
<Icon symbol="credit_card" />
|
|
377
|
+
Billing
|
|
378
|
+
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
379
|
+
</DropdownMenuItem>
|
|
380
|
+
<DropdownMenuItem>
|
|
381
|
+
<Icon symbol="settings" />
|
|
382
|
+
Settings
|
|
383
|
+
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
384
|
+
</DropdownMenuItem>
|
|
385
|
+
<DropdownMenuSeparator />
|
|
386
|
+
<DropdownMenuItem>
|
|
387
|
+
<Icon symbol="logout" />
|
|
388
|
+
Log out
|
|
389
|
+
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
|
390
|
+
</DropdownMenuItem>
|
|
391
|
+
</DropdownMenuContent>
|
|
392
|
+
</DropdownMenu>;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### File Actions (More Menu)
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
import {
|
|
399
|
+
MoreHorizontalIcon,
|
|
400
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
401
|
+
|
|
402
|
+
<DropdownMenu>
|
|
403
|
+
<DropdownMenuTrigger asChild>
|
|
404
|
+
<Button variant="ghost" size="sm">
|
|
405
|
+
<Icon symbol="more_horiz" />
|
|
406
|
+
</Button>
|
|
407
|
+
</DropdownMenuTrigger>
|
|
408
|
+
<DropdownMenuContent align="end">
|
|
409
|
+
<DropdownMenuItem>
|
|
410
|
+
<Icon symbol="open_in_new" />
|
|
411
|
+
Open
|
|
412
|
+
</DropdownMenuItem>
|
|
413
|
+
<DropdownMenuItem>
|
|
414
|
+
<Icon symbol="content_copy" />
|
|
415
|
+
Copy Link
|
|
416
|
+
</DropdownMenuItem>
|
|
417
|
+
<DropdownMenuItem>
|
|
418
|
+
<Icon symbol="share" />
|
|
419
|
+
Share
|
|
420
|
+
</DropdownMenuItem>
|
|
421
|
+
<DropdownMenuSeparator />
|
|
422
|
+
<DropdownMenuItem>
|
|
423
|
+
<Icon symbol="download" />
|
|
424
|
+
Download
|
|
425
|
+
</DropdownMenuItem>
|
|
426
|
+
<DropdownMenuItem>
|
|
427
|
+
<Icon symbol="edit" />
|
|
428
|
+
Rename
|
|
429
|
+
</DropdownMenuItem>
|
|
430
|
+
<DropdownMenuSeparator />
|
|
431
|
+
<DropdownMenuItem variant="destructive">
|
|
432
|
+
<Icon symbol="delete" />
|
|
433
|
+
Delete
|
|
434
|
+
</DropdownMenuItem>
|
|
435
|
+
</DropdownMenuContent>
|
|
436
|
+
</DropdownMenu>;
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Simple Actions con Chevron
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
import {
|
|
443
|
+
ChevronDownIcon,
|
|
444
|
+
EditIcon,
|
|
445
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
446
|
+
|
|
447
|
+
<DropdownMenu>
|
|
448
|
+
<DropdownMenuTrigger asChild>
|
|
449
|
+
<Button variant="outline">
|
|
450
|
+
Actions
|
|
451
|
+
<Icon symbol="expand_more" />
|
|
452
|
+
</Button>
|
|
453
|
+
</DropdownMenuTrigger>
|
|
454
|
+
<DropdownMenuContent>
|
|
455
|
+
<DropdownMenuItem>
|
|
456
|
+
<Icon symbol="edit" />
|
|
457
|
+
Edit
|
|
458
|
+
</DropdownMenuItem>
|
|
459
|
+
<DropdownMenuItem>
|
|
460
|
+
<Icon symbol="content_copy" />
|
|
461
|
+
Duplicate
|
|
462
|
+
</DropdownMenuItem>
|
|
463
|
+
<DropdownMenuItem>
|
|
464
|
+
<Icon symbol="share" />
|
|
465
|
+
Share
|
|
466
|
+
</DropdownMenuItem>
|
|
467
|
+
<DropdownMenuSeparator />
|
|
468
|
+
<DropdownMenuItem variant="destructive">
|
|
469
|
+
<Icon symbol="delete" />
|
|
470
|
+
Delete
|
|
471
|
+
</DropdownMenuItem>
|
|
472
|
+
</DropdownMenuContent>
|
|
473
|
+
</DropdownMenu>;
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Project Menu (Nested)
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
import {
|
|
480
|
+
FolderIcon,
|
|
481
|
+
EditIcon,
|
|
482
|
+
ShareIcon,
|
|
483
|
+
CopyIcon,
|
|
484
|
+
UsersIcon,
|
|
485
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
486
|
+
|
|
487
|
+
<DropdownMenu>
|
|
488
|
+
<DropdownMenuTrigger asChild>
|
|
489
|
+
<Button variant="outline">
|
|
490
|
+
<Icon symbol="folder" />
|
|
491
|
+
My Project
|
|
492
|
+
<Icon symbol="expand_more" />
|
|
493
|
+
</Button>
|
|
494
|
+
</DropdownMenuTrigger>
|
|
495
|
+
<DropdownMenuContent className="w-56">
|
|
496
|
+
<DropdownMenuLabel>Project Actions</DropdownMenuLabel>
|
|
497
|
+
<DropdownMenuSeparator />
|
|
498
|
+
<DropdownMenuItem>
|
|
499
|
+
<Icon symbol="edit" />
|
|
500
|
+
Edit Project
|
|
501
|
+
</DropdownMenuItem>
|
|
502
|
+
<DropdownMenuItem>
|
|
503
|
+
<Icon symbol="share" />
|
|
504
|
+
Share Project
|
|
505
|
+
</DropdownMenuItem>
|
|
506
|
+
<DropdownMenuItem>
|
|
507
|
+
<Icon symbol="content_copy" />
|
|
508
|
+
Duplicate Project
|
|
509
|
+
</DropdownMenuItem>
|
|
510
|
+
<DropdownMenuSeparator />
|
|
511
|
+
<DropdownMenuSub>
|
|
512
|
+
<DropdownMenuSubTrigger>
|
|
513
|
+
<Icon symbol="group" />
|
|
514
|
+
Manage Access
|
|
515
|
+
</DropdownMenuSubTrigger>
|
|
516
|
+
<DropdownMenuPortal>
|
|
517
|
+
<DropdownMenuSubContent>
|
|
518
|
+
<DropdownMenuItem>
|
|
519
|
+
<Icon symbol="person_add" />
|
|
520
|
+
Add Member
|
|
521
|
+
</DropdownMenuItem>
|
|
522
|
+
<DropdownMenuItem>
|
|
523
|
+
<Icon symbol="admin_panel_settings" />
|
|
524
|
+
Permissions
|
|
525
|
+
</DropdownMenuItem>
|
|
526
|
+
<DropdownMenuSeparator />
|
|
527
|
+
<DropdownMenuItem>
|
|
528
|
+
<Icon symbol="group" />
|
|
529
|
+
View Members
|
|
530
|
+
</DropdownMenuItem>
|
|
531
|
+
</DropdownMenuSubContent>
|
|
532
|
+
</DropdownMenuPortal>
|
|
533
|
+
</DropdownMenuSub>
|
|
534
|
+
<DropdownMenuSeparator />
|
|
535
|
+
<DropdownMenuItem>
|
|
536
|
+
<Icon symbol="download" />
|
|
537
|
+
Export
|
|
538
|
+
</DropdownMenuItem>
|
|
539
|
+
<DropdownMenuItem>
|
|
540
|
+
<Icon symbol="settings" />
|
|
541
|
+
Settings
|
|
542
|
+
</DropdownMenuItem>
|
|
543
|
+
<DropdownMenuSeparator />
|
|
544
|
+
<DropdownMenuItem variant="destructive">
|
|
545
|
+
<Icon symbol="delete" />
|
|
546
|
+
Delete Project
|
|
547
|
+
</DropdownMenuItem>
|
|
548
|
+
</DropdownMenuContent>
|
|
549
|
+
</DropdownMenu>;
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Items Deshabilitados
|
|
553
|
+
|
|
554
|
+
```tsx
|
|
555
|
+
<DropdownMenu>
|
|
556
|
+
<DropdownMenuTrigger asChild>
|
|
557
|
+
<Button variant="outline">Options</Button>
|
|
558
|
+
</DropdownMenuTrigger>
|
|
559
|
+
<DropdownMenuContent>
|
|
560
|
+
<DropdownMenuItem>Available Action</DropdownMenuItem>
|
|
561
|
+
<DropdownMenuItem disabled>Premium Feature</DropdownMenuItem>
|
|
562
|
+
<DropdownMenuItem>Another Action</DropdownMenuItem>
|
|
563
|
+
<DropdownMenuSeparator />
|
|
564
|
+
<DropdownMenuItem disabled>Coming Soon</DropdownMenuItem>
|
|
565
|
+
<DropdownMenuItem variant="destructive" disabled>
|
|
566
|
+
No Permission
|
|
567
|
+
</DropdownMenuItem>
|
|
568
|
+
</DropdownMenuContent>
|
|
569
|
+
</DropdownMenu>
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Estilos disabled**: `opacity-50`, `pointer-events-none`, `data-[disabled=true]`
|
|
573
|
+
|
|
574
|
+
### Controlado
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
import { useState } from "react";
|
|
578
|
+
|
|
579
|
+
function App() {
|
|
580
|
+
const [open, setOpen] = useState(false);
|
|
581
|
+
|
|
582
|
+
return (
|
|
583
|
+
<DropdownMenu open={open} onOpenChange={setOpen}>
|
|
584
|
+
<DropdownMenuTrigger asChild>
|
|
585
|
+
<Button>Menu</Button>
|
|
586
|
+
</DropdownMenuTrigger>
|
|
587
|
+
<DropdownMenuContent>
|
|
588
|
+
<DropdownMenuItem onClick={() => setOpen(false)}>
|
|
589
|
+
Action (closes menu)
|
|
590
|
+
</DropdownMenuItem>
|
|
591
|
+
</DropdownMenuContent>
|
|
592
|
+
</DropdownMenu>
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
## Casos de Uso Comunes
|
|
598
|
+
|
|
599
|
+
**User menus**: Avatar menu con profile, settings, logout
|
|
600
|
+
**Action menus**: File actions, row actions, more options
|
|
601
|
+
**Navigation**: Nested navigation menus
|
|
602
|
+
**Filters/Options**: View settings, panel positions
|
|
603
|
+
**Context actions**: Edit, share, delete operations
|
|
604
|
+
**Bulk actions**: Select multiple items, apply actions
|
|
605
|
+
|
|
606
|
+
## Estados y Data Attributes
|
|
607
|
+
|
|
608
|
+
### DropdownMenuItem States
|
|
609
|
+
|
|
610
|
+
- **Focus**: `data-[highlighted]` → `bg-accent`, `text-accent-foreground`
|
|
611
|
+
- **Disabled**: `data-[disabled=true]` → `opacity-50`, `pointer-events-none`
|
|
612
|
+
- **Destructive**: `data-[variant=destructive]` → `text-destructive`
|
|
613
|
+
|
|
614
|
+
### DropdownMenuSubTrigger States
|
|
615
|
+
|
|
616
|
+
- **Open**: `data-[state=open]` → `bg-accent`, `text-accent-foreground`
|
|
617
|
+
|
|
618
|
+
### Content Animations
|
|
619
|
+
|
|
620
|
+
- **Opening**: `data-[state=open]` → `animate-in`, `fade-in-0`, `zoom-in-95`
|
|
621
|
+
- **Closing**: `data-[state=closed]` → `animate-out`, `fade-out-0`, `zoom-out-95`
|
|
622
|
+
- **Slide in**: `data-[side=top|right|bottom|left]` → direccional
|
|
623
|
+
|
|
624
|
+
## Navegación por Teclado
|
|
625
|
+
|
|
626
|
+
- ✅ **Arrow Up/Down**: Navega entre items
|
|
627
|
+
- ✅ **Arrow Right**: Abre submenú (si disponible)
|
|
628
|
+
- ✅ **Arrow Left**: Cierra submenú
|
|
629
|
+
- ✅ **Enter/Space**: Activa item enfocado
|
|
630
|
+
- ✅ **Escape**: Cierra menú
|
|
631
|
+
- ✅ **Tab**: Mueve focus fuera del menú
|
|
632
|
+
- ✅ **Home/End**: Primer/último item (si `loop={true}`)
|
|
633
|
+
- ✅ **Type ahead**: Busca item por primera letra
|
|
634
|
+
|
|
635
|
+
## Accesibilidad
|
|
636
|
+
|
|
637
|
+
- ✅ **ARIA**: `role="menu"` en content, `role="menuitem"` en items
|
|
638
|
+
- ✅ **Keyboard navigation**: Navegación completa por teclado
|
|
639
|
+
- ✅ **Focus management**: Focus visible y restaurado correctamente
|
|
640
|
+
- ✅ **Screen readers**: Anuncia items, estado de checkboxes/radios, disabled
|
|
641
|
+
- ✅ **Disabled items**: `aria-disabled="true"` en items deshabilitados
|
|
642
|
+
- ✅ **Submenus**: `aria-haspopup="menu"` en SubTriggers
|
|
643
|
+
- ✅ **Checked state**: `aria-checked` en CheckboxItem y RadioItem
|
|
644
|
+
|
|
645
|
+
## Notas de Implementación
|
|
646
|
+
|
|
647
|
+
- **Basado en Radix UI**: `@radix-ui/react-dropdown-menu`
|
|
648
|
+
- **Click to open**: Se activa con click (diferente de ContextMenu que usa right-click)
|
|
649
|
+
- **Auto positioning**: Portal con posicionamiento automático
|
|
650
|
+
- **Collision detection**: Evita salirse del viewport
|
|
651
|
+
- **Auto-close**: Cierra al seleccionar item (excepto checkboxes/radios)
|
|
652
|
+
- **Focus trap**: Mientras está abierto, focus queda dentro del menú
|
|
653
|
+
- **Portal rendering**: Content se renderiza en `document.body` por defecto
|
|
654
|
+
- **SVG auto-sizing**: Iconos automáticamente `size-4` y color muted
|
|
655
|
+
- **Inset support**: `inset` prop agrega `pl-8` para alinear con items con iconos
|
|
656
|
+
- **ChevronRightIcon**: Incluido automáticamente en SubTriggers
|
|
657
|
+
- **CheckIcon/CircleIcon**: Incluidos automáticamente en CheckboxItem/RadioItem
|
|
658
|
+
- **Variant destructive**: SVGs ignoran color muted y usan `text-destructive`
|
|
659
|
+
- **Default sideOffset**: 4px de distancia desde trigger
|
|
660
|
+
|
|
661
|
+
## Posicionamiento
|
|
662
|
+
|
|
663
|
+
Control completo del posicionamiento del menú:
|
|
664
|
+
|
|
665
|
+
```tsx
|
|
666
|
+
<DropdownMenuContent
|
|
667
|
+
align="end" // "start" | "center" | "end"
|
|
668
|
+
side="bottom" // "top" | "right" | "bottom" | "left"
|
|
669
|
+
sideOffset={8} // distancia desde trigger
|
|
670
|
+
alignOffset={-4} // offset horizontal
|
|
671
|
+
className="w-64" // ancho custom
|
|
672
|
+
>
|
|
673
|
+
{/* ... */}
|
|
674
|
+
</DropdownMenuContent>
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
**Ejemplos comunes**:
|
|
678
|
+
|
|
679
|
+
- User menu (top-right avatar): `align="end"`
|
|
680
|
+
- Actions menu (more button): `align="end"`
|
|
681
|
+
- Navigation menu: `align="start"`
|
|
682
|
+
|
|
683
|
+
## Troubleshooting
|
|
684
|
+
|
|
685
|
+
**Menú no aparece**: Verifica que DropdownMenuTrigger tenga `asChild` si usas Button custom
|
|
686
|
+
**Items no responden a click**: Usa `onSelect` en lugar de `onClick`
|
|
687
|
+
**Iconos mal alineados**: Usa `inset` en items sin iconos cuando otros sí tienen
|
|
688
|
+
**Menú no cierra al seleccionar**: CheckboxItems y RadioItems no cierran automáticamente (comportamiento esperado)
|
|
689
|
+
**Submenú no abre**: Verifica que estés usando `DropdownMenuSub` + `DropdownMenuSubTrigger` + `DropdownMenuSubContent`
|
|
690
|
+
**Shortcuts no ejecutan**: `DropdownMenuShortcut` es solo visual, implementa lógica en `onSelect`
|
|
691
|
+
**Focus se pierde al cerrar**: Usa `onCloseAutoFocus` para control custom del focus
|
|
692
|
+
**Posición incorrecta**: Verifica `align` y `side` props, ajusta con `sideOffset` y `alignOffset`
|
|
693
|
+
**Trigger no muestra estado**: Radix no cambia apariencia del trigger automáticamente, usa estado controlado
|
|
694
|
+
|
|
695
|
+
## Diferencias con ContextMenu
|
|
696
|
+
|
|
697
|
+
| Aspecto | DropdownMenu | ContextMenu |
|
|
698
|
+
| -------------------- | --------------------------- | --------------------- |
|
|
699
|
+
| **Activación** | Click izquierdo | Click derecho |
|
|
700
|
+
| **Trigger** | Botón/elemento visible | Área target |
|
|
701
|
+
| **Uso común** | Menús de acción, navegación | Acciones contextuales |
|
|
702
|
+
| **Indicador visual** | Botón con chevron | Sin indicador |
|
|
703
|
+
| **Posicionamiento** | Relativo al trigger | Posición del cursor |
|
|
704
|
+
|
|
705
|
+
## Referencias
|
|
706
|
+
|
|
707
|
+
- **Radix UI Dropdown Menu**: https://www.radix-ui.com/primitives/docs/components/dropdown-menu
|
|
708
|
+
- **shadcn/ui Dropdown Menu**: https://ui.shadcn.com/docs/components/dropdown-menu
|
|
709
|
+
- **ARIA Menu Pattern**: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
|