@adamosuiteservices/ui 2.11.17 → 2.11.18

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 (66) hide show
  1. package/dist/accordion-rounded.cjs +1 -1
  2. package/dist/accordion-rounded.js +1 -1
  3. package/dist/badge.cjs +3 -3
  4. package/dist/badge.js +2 -2
  5. package/dist/breadcrumb.cjs +5 -5
  6. package/dist/breadcrumb.js +16 -16
  7. package/dist/components/ui/date-picker-selector/date-picker-selector.d.ts +2 -2
  8. package/dist/components/ui/sheet/sheet.d.ts +2 -1
  9. package/dist/date-picker-selector.cjs +1 -1
  10. package/dist/date-picker-selector.js +29 -30
  11. package/dist/dialog.cjs +1 -1
  12. package/dist/dialog.js +1 -1
  13. package/dist/select.cjs +2 -2
  14. package/dist/select.js +2 -2
  15. package/dist/{sheet-DVT_djHX.cjs → sheet-CvcCaGSl.cjs} +11 -11
  16. package/dist/{sheet-CPf9Guon.js → sheet-IRIc3TJ1.js} +33 -19
  17. package/dist/sheet.cjs +1 -1
  18. package/dist/sheet.js +7 -6
  19. package/dist/sidebar.cjs +1 -1
  20. package/dist/sidebar.js +1 -1
  21. package/dist/styles.css +1 -1
  22. package/dist/tabs-underline.cjs +3 -3
  23. package/dist/tabs-underline.js +13 -13
  24. package/dist/tabs.cjs +5 -4
  25. package/dist/tabs.js +3 -2
  26. package/dist/themes.css +1 -1
  27. package/docs/AI-GUIDE.md +321 -321
  28. package/docs/components/layout/sidebar.md +399 -399
  29. package/docs/components/layout/toaster.md +436 -436
  30. package/docs/components/ui/accordion-rounded.md +584 -584
  31. package/docs/components/ui/accordion.md +269 -269
  32. package/docs/components/ui/badge.md +2 -1
  33. package/docs/components/ui/button-group.md +984 -984
  34. package/docs/components/ui/button.md +1137 -1137
  35. package/docs/components/ui/calendar.md +1159 -1159
  36. package/docs/components/ui/card.md +1455 -1455
  37. package/docs/components/ui/checkbox.md +292 -292
  38. package/docs/components/ui/collapsible.md +323 -323
  39. package/docs/components/ui/command.md +454 -454
  40. package/docs/components/ui/context-menu.md +540 -540
  41. package/docs/components/ui/date-picker-selector.md +0 -2
  42. package/docs/components/ui/dialog.md +628 -628
  43. package/docs/components/ui/dropdown-menu.md +709 -709
  44. package/docs/components/ui/field.md +706 -706
  45. package/docs/components/ui/hover-card.md +446 -446
  46. package/docs/components/ui/input.md +362 -362
  47. package/docs/components/ui/kbd.md +434 -434
  48. package/docs/components/ui/label.md +359 -359
  49. package/docs/components/ui/pagination.md +650 -650
  50. package/docs/components/ui/popover.md +536 -536
  51. package/docs/components/ui/progress.md +182 -182
  52. package/docs/components/ui/radio-group.md +311 -311
  53. package/docs/components/ui/select.md +352 -352
  54. package/docs/components/ui/separator.md +214 -214
  55. package/docs/components/ui/sheet.md +174 -142
  56. package/docs/components/ui/skeleton.md +140 -140
  57. package/docs/components/ui/slider.md +341 -341
  58. package/docs/components/ui/spinner.md +170 -170
  59. package/docs/components/ui/switch.md +408 -408
  60. package/docs/components/ui/tabs-underline.md +106 -106
  61. package/docs/components/ui/tabs.md +122 -122
  62. package/docs/components/ui/textarea.md +243 -243
  63. package/docs/components/ui/toggle.md +237 -237
  64. package/docs/components/ui/tooltip.md +317 -317
  65. package/docs/components/ui/typography.md +280 -280
  66. 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/