@adamosuiteservices/ui 1.7.12 → 1.8.13

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