@ui-sdk/web 0.0.0-stage → 0.1.0-beta.1

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 (467) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +10 -2
  3. package/browser/web/accordion.js +1 -0
  4. package/browser/web/alert-dialog.js +1 -0
  5. package/browser/web/autocomplete.js +1 -0
  6. package/browser/web/avatar.js +1 -0
  7. package/browser/web/button.js +1 -0
  8. package/browser/web/calendar.js +1 -0
  9. package/browser/web/carousel.js +1 -0
  10. package/browser/web/checkbox.js +1 -0
  11. package/browser/web/chunks/0eRGibL6.js +1 -0
  12. package/browser/web/chunks/2bIEUO6_.js +1 -0
  13. package/browser/web/chunks/5A9w2ULz.js +1 -0
  14. package/browser/web/chunks/5RSyydjV.js +1 -0
  15. package/browser/web/chunks/9pWTy2wl.js +1 -0
  16. package/browser/web/chunks/B0f2j6zz.js +1 -0
  17. package/browser/web/chunks/B1rux6br.js +1 -0
  18. package/browser/web/chunks/B3-APaNf.js +1 -0
  19. package/browser/web/chunks/B7EgSgjQ.js +1 -0
  20. package/browser/web/chunks/B8QcOzJU.js +1 -0
  21. package/browser/web/chunks/BFueEkwm.js +3 -0
  22. package/browser/web/chunks/BJj3oS29.js +1 -0
  23. package/browser/web/chunks/BNSvTCpr.js +1 -0
  24. package/browser/web/chunks/BPD0kAZB.js +1 -0
  25. package/browser/web/chunks/BQeduZqT.js +1 -0
  26. package/browser/web/chunks/B_CVFyiz.js +1 -0
  27. package/browser/web/chunks/B_ns1YHO.js +1 -0
  28. package/browser/web/chunks/BcwkVTsR.js +1 -0
  29. package/browser/web/chunks/BeaQ6TYM.js +1 -0
  30. package/browser/web/chunks/BgBB013u.js +1 -0
  31. package/browser/web/chunks/BgsKhHDm.js +1 -0
  32. package/browser/web/chunks/BjMcbuhi.js +9 -0
  33. package/browser/web/chunks/Bjiy_F2N.js +1 -0
  34. package/browser/web/chunks/BqScCLwv.js +1 -0
  35. package/browser/web/chunks/BqX3Utqx.js +1 -0
  36. package/browser/web/chunks/BtVkQf3z.js +1 -0
  37. package/browser/web/chunks/By30h-7B.js +1 -0
  38. package/browser/web/chunks/C1zqYpy2.js +1 -0
  39. package/browser/web/chunks/C436juFf.js +1 -0
  40. package/browser/web/chunks/C4_bULs1.js +1 -0
  41. package/browser/web/chunks/C4omqJEz.js +1 -0
  42. package/browser/web/chunks/C7tt0T3n.js +1 -0
  43. package/browser/web/chunks/C8w7s5dg.js +1 -0
  44. package/browser/web/chunks/CElY4erO.js +1 -0
  45. package/browser/web/chunks/CFODo-m2.js +1 -0
  46. package/browser/web/chunks/CL4wjam9.js +1 -0
  47. package/browser/web/chunks/CMg-8UVf.js +1 -0
  48. package/browser/web/chunks/COWKfakj.js +1 -0
  49. package/browser/web/chunks/CQBU0qZ4.js +1 -0
  50. package/browser/web/chunks/CV6uABgc.js +1 -0
  51. package/browser/web/chunks/C_SWFpWQ.js +1 -0
  52. package/browser/web/chunks/C_Uanbrl.js +1 -0
  53. package/browser/web/chunks/Ce_CSanC.js +1 -0
  54. package/browser/web/chunks/Cg-ZmRhS.js +1 -0
  55. package/browser/web/chunks/Cgf2wPI_.js +1 -0
  56. package/browser/web/chunks/Cuep_3Vf.js +1 -0
  57. package/browser/web/chunks/D02PaHn6.js +1 -0
  58. package/browser/web/chunks/D5LiaI5R.js +1 -0
  59. package/browser/web/chunks/D9UgVYvF.js +1 -0
  60. package/browser/web/chunks/DC08tnkE.js +2 -0
  61. package/browser/web/chunks/DG8YGSvu.js +1 -0
  62. package/browser/web/chunks/DIQd0FYD.js +1 -0
  63. package/browser/web/chunks/DMLqacE8.js +1 -0
  64. package/browser/web/chunks/DSUI3yI-.js +1 -0
  65. package/browser/web/chunks/DT3Hb76X.js +1 -0
  66. package/browser/web/chunks/DY75g3-W.js +1 -0
  67. package/browser/web/chunks/DZpNQKZ9.js +1 -0
  68. package/browser/web/chunks/Db-W1wrC.js +1 -0
  69. package/browser/web/chunks/Dc1xK3Ii.js +1 -0
  70. package/browser/web/chunks/DdF6awFs.js +1 -0
  71. package/browser/web/chunks/DdaCLml8.js +1 -0
  72. package/browser/web/chunks/Dh8MLjQO.js +1 -0
  73. package/browser/web/chunks/DiuslJRy.js +1 -0
  74. package/browser/web/chunks/DjRcw3Y0.js +1 -0
  75. package/browser/web/chunks/Djdv-16J.js +1 -0
  76. package/browser/web/chunks/DnFXe_lF.js +1 -0
  77. package/browser/web/chunks/DneGv1oC.js +1 -0
  78. package/browser/web/chunks/DsVxP-p-.js +1 -0
  79. package/browser/web/chunks/DwDilCo2.js +1 -0
  80. package/browser/web/chunks/DwV48AXv.js +1 -0
  81. package/browser/web/chunks/F37aF4-L.js +1 -0
  82. package/browser/web/chunks/LyB6XIbN.js +1 -0
  83. package/browser/web/chunks/NKnbYGjC.js +1 -0
  84. package/browser/web/chunks/Vk-rVi16.js +1 -0
  85. package/browser/web/chunks/XsM0J1Ex.js +1 -0
  86. package/browser/web/chunks/asSN9iEp.js +1 -0
  87. package/browser/web/chunks/c-AjHfdF.js +1 -0
  88. package/browser/web/chunks/dR-12TPY.js +1 -0
  89. package/browser/web/chunks/hR4xM0EC.js +1 -0
  90. package/browser/web/chunks/hqOaBxAg.js +1 -0
  91. package/browser/web/chunks/kMKqqeG4.js +1 -0
  92. package/browser/web/chunks/kXboBcfi.js +1 -0
  93. package/browser/web/chunks/l4aQIg22.js +1 -0
  94. package/browser/web/chunks/tJC0JSU4.js +1 -0
  95. package/browser/web/chunks/wHdS_QYM.js +1 -0
  96. package/browser/web/chunks/yv8dTvNI.js +1 -0
  97. package/browser/web/chunks/yw9M3nfi.js +1 -0
  98. package/browser/web/collapsible.js +1 -0
  99. package/browser/web/color-picker.js +1 -0
  100. package/browser/web/combobox.js +2 -0
  101. package/browser/web/command.js +1 -0
  102. package/browser/web/core.js +5 -0
  103. package/browser/web/date-input.js +1 -0
  104. package/browser/web/date-picker.js +1 -0
  105. package/browser/web/dialog.js +1 -0
  106. package/browser/web/drawer.js +1 -0
  107. package/browser/web/field.js +1 -0
  108. package/browser/web/file-input.js +1 -0
  109. package/browser/web/hover-card.js +1 -0
  110. package/browser/web/image-cropper.js +1 -0
  111. package/browser/web/input.js +1 -0
  112. package/browser/web/label.js +1 -0
  113. package/browser/web/listbox.js +1 -0
  114. package/browser/web/menu.js +1 -0
  115. package/browser/web/menubar.js +1 -0
  116. package/browser/web/meter.js +1 -0
  117. package/browser/web/navigation-menu.js +1 -0
  118. package/browser/web/number-input.js +1 -0
  119. package/browser/web/otp-input.js +2 -0
  120. package/browser/web/overlays.js +1 -0
  121. package/browser/web/pagination.js +1 -0
  122. package/browser/web/password-input.js +1 -0
  123. package/browser/web/popover.js +1 -0
  124. package/browser/web/preferences.js +1 -0
  125. package/browser/web/progress.js +1 -0
  126. package/browser/web/radio.js +1 -0
  127. package/browser/web/rating.js +1 -0
  128. package/browser/web/resizable.js +1 -0
  129. package/browser/web/scroll-area.js +1 -0
  130. package/browser/web/segmented-control.js +1 -0
  131. package/browser/web/select.js +1 -0
  132. package/browser/web/separator.js +1 -0
  133. package/browser/web/slider.js +1 -0
  134. package/browser/web/switch.js +1 -0
  135. package/browser/web/tabs.js +1 -0
  136. package/browser/web/textarea.js +1 -0
  137. package/browser/web/time-input.js +1 -0
  138. package/browser/web/timeline.js +1 -0
  139. package/browser/web/toast.js +1 -0
  140. package/browser/web/toggle.js +1 -0
  141. package/browser/web/token-input.js +8 -0
  142. package/browser/web/toolbar.js +1 -0
  143. package/browser/web/tooltip.js +1 -0
  144. package/browser/web/tree.js +1 -0
  145. package/browser/web/trigger-menu.js +4 -0
  146. package/browser/web/waterfall.js +1 -0
  147. package/browser/web.css +3378 -0
  148. package/browser/web.js +3 -0
  149. package/custom-elements.json +32315 -0
  150. package/dist/accordion.d.ts +2 -0
  151. package/dist/accordion.js +57 -0
  152. package/dist/action.d.ts +16 -0
  153. package/dist/action.js +39 -0
  154. package/dist/agent.d.ts +2 -0
  155. package/dist/agent.js +7 -0
  156. package/dist/autocomplete.d.ts +2 -0
  157. package/dist/autocomplete.js +120 -0
  158. package/dist/autoload.d.ts +18 -0
  159. package/dist/autoload.js +63 -0
  160. package/dist/avatar.d.ts +2 -0
  161. package/dist/avatar.js +27 -0
  162. package/dist/browser-core.d.ts +2 -0
  163. package/dist/browser-core.js +8 -0
  164. package/dist/button.d.ts +2 -0
  165. package/dist/button.js +35 -0
  166. package/dist/calendar.d.ts +23 -0
  167. package/dist/calendar.js +429 -0
  168. package/dist/carousel.d.ts +2 -0
  169. package/dist/carousel.js +54 -0
  170. package/dist/checkbox.d.ts +4 -0
  171. package/dist/checkbox.js +75 -0
  172. package/dist/collapsible.d.ts +2 -0
  173. package/dist/collapsible.js +28 -0
  174. package/dist/color-picker.d.ts +2 -0
  175. package/dist/color-picker.js +64 -0
  176. package/dist/combobox.d.ts +2 -0
  177. package/dist/combobox.js +334 -0
  178. package/dist/command.d.ts +2 -0
  179. package/dist/command.js +122 -0
  180. package/dist/date-input.d.ts +3 -0
  181. package/dist/date-input.js +87 -0
  182. package/dist/date-picker.d.ts +2 -0
  183. package/dist/date-picker.js +174 -0
  184. package/dist/default-text.d.ts +3 -0
  185. package/dist/default-text.js +24 -0
  186. package/dist/define-element.d.ts +404 -0
  187. package/dist/define-element.js +1489 -0
  188. package/dist/dialog.d.ts +3 -0
  189. package/dist/dialog.js +94 -0
  190. package/dist/drawer.d.ts +2 -0
  191. package/dist/drawer.js +96 -0
  192. package/dist/entries/accordion.d.ts +3 -0
  193. package/dist/entries/accordion.js +7 -0
  194. package/dist/entries/alert-dialog.d.ts +3 -0
  195. package/dist/entries/alert-dialog.js +13 -0
  196. package/dist/entries/autocomplete.d.ts +3 -0
  197. package/dist/entries/autocomplete.js +7 -0
  198. package/dist/entries/avatar.d.ts +2 -0
  199. package/dist/entries/avatar.js +3 -0
  200. package/dist/entries/button.d.ts +2 -0
  201. package/dist/entries/button.js +3 -0
  202. package/dist/entries/calendar.d.ts +3 -0
  203. package/dist/entries/calendar.js +7 -0
  204. package/dist/entries/carousel.d.ts +3 -0
  205. package/dist/entries/carousel.js +7 -0
  206. package/dist/entries/checkbox.d.ts +3 -0
  207. package/dist/entries/checkbox.js +15 -0
  208. package/dist/entries/collapsible.d.ts +3 -0
  209. package/dist/entries/collapsible.js +7 -0
  210. package/dist/entries/color-picker.d.ts +3 -0
  211. package/dist/entries/color-picker.js +7 -0
  212. package/dist/entries/combobox.d.ts +3 -0
  213. package/dist/entries/combobox.js +7 -0
  214. package/dist/entries/command.d.ts +3 -0
  215. package/dist/entries/command.js +7 -0
  216. package/dist/entries/date-input.d.ts +3 -0
  217. package/dist/entries/date-input.js +7 -0
  218. package/dist/entries/date-picker.d.ts +3 -0
  219. package/dist/entries/date-picker.js +7 -0
  220. package/dist/entries/dialog.d.ts +3 -0
  221. package/dist/entries/dialog.js +7 -0
  222. package/dist/entries/drawer.d.ts +3 -0
  223. package/dist/entries/drawer.js +8 -0
  224. package/dist/entries/field.d.ts +2 -0
  225. package/dist/entries/field.js +3 -0
  226. package/dist/entries/file-input.d.ts +3 -0
  227. package/dist/entries/file-input.js +13 -0
  228. package/dist/entries/hover-card.d.ts +3 -0
  229. package/dist/entries/hover-card.js +7 -0
  230. package/dist/entries/image-cropper.d.ts +3 -0
  231. package/dist/entries/image-cropper.js +7 -0
  232. package/dist/entries/input.d.ts +3 -0
  233. package/dist/entries/input.js +8 -0
  234. package/dist/entries/label.d.ts +2 -0
  235. package/dist/entries/label.js +3 -0
  236. package/dist/entries/listbox.d.ts +3 -0
  237. package/dist/entries/listbox.js +7 -0
  238. package/dist/entries/menu.d.ts +3 -0
  239. package/dist/entries/menu.js +7 -0
  240. package/dist/entries/menubar.d.ts +3 -0
  241. package/dist/entries/menubar.js +7 -0
  242. package/dist/entries/meter.d.ts +2 -0
  243. package/dist/entries/meter.js +3 -0
  244. package/dist/entries/navigation-menu.d.ts +3 -0
  245. package/dist/entries/navigation-menu.js +7 -0
  246. package/dist/entries/number-input.d.ts +3 -0
  247. package/dist/entries/number-input.js +7 -0
  248. package/dist/entries/otp-input.d.ts +3 -0
  249. package/dist/entries/otp-input.js +7 -0
  250. package/dist/entries/overlays.d.ts +2 -0
  251. package/dist/entries/overlays.js +3 -0
  252. package/dist/entries/pagination.d.ts +3 -0
  253. package/dist/entries/pagination.js +11 -0
  254. package/dist/entries/password-input.d.ts +3 -0
  255. package/dist/entries/password-input.js +7 -0
  256. package/dist/entries/popover.d.ts +3 -0
  257. package/dist/entries/popover.js +7 -0
  258. package/dist/entries/preferences.d.ts +3 -0
  259. package/dist/entries/preferences.js +13 -0
  260. package/dist/entries/progress.d.ts +2 -0
  261. package/dist/entries/progress.js +3 -0
  262. package/dist/entries/radio.d.ts +3 -0
  263. package/dist/entries/radio.js +7 -0
  264. package/dist/entries/rating.d.ts +3 -0
  265. package/dist/entries/rating.js +8 -0
  266. package/dist/entries/resizable.d.ts +3 -0
  267. package/dist/entries/resizable.js +11 -0
  268. package/dist/entries/scroll-area.d.ts +3 -0
  269. package/dist/entries/scroll-area.js +7 -0
  270. package/dist/entries/segmented-control.d.ts +3 -0
  271. package/dist/entries/segmented-control.js +7 -0
  272. package/dist/entries/select.d.ts +3 -0
  273. package/dist/entries/select.js +7 -0
  274. package/dist/entries/separator.d.ts +2 -0
  275. package/dist/entries/separator.js +3 -0
  276. package/dist/entries/slider.d.ts +3 -0
  277. package/dist/entries/slider.js +7 -0
  278. package/dist/entries/switch.d.ts +3 -0
  279. package/dist/entries/switch.js +7 -0
  280. package/dist/entries/tabs.d.ts +3 -0
  281. package/dist/entries/tabs.js +7 -0
  282. package/dist/entries/textarea.d.ts +3 -0
  283. package/dist/entries/textarea.js +7 -0
  284. package/dist/entries/time-input.d.ts +3 -0
  285. package/dist/entries/time-input.js +7 -0
  286. package/dist/entries/timeline.d.ts +3 -0
  287. package/dist/entries/timeline.js +7 -0
  288. package/dist/entries/toast.d.ts +2 -0
  289. package/dist/entries/toast.js +3 -0
  290. package/dist/entries/toggle.d.ts +3 -0
  291. package/dist/entries/toggle.js +12 -0
  292. package/dist/entries/token-input.d.ts +3 -0
  293. package/dist/entries/token-input.js +17 -0
  294. package/dist/entries/toolbar.d.ts +3 -0
  295. package/dist/entries/toolbar.js +7 -0
  296. package/dist/entries/tooltip.d.ts +3 -0
  297. package/dist/entries/tooltip.js +7 -0
  298. package/dist/entries/tree.d.ts +3 -0
  299. package/dist/entries/tree.js +17 -0
  300. package/dist/entries/trigger-menu.d.ts +3 -0
  301. package/dist/entries/trigger-menu.js +11 -0
  302. package/dist/entries/waterfall.d.ts +3 -0
  303. package/dist/entries/waterfall.js +11 -0
  304. package/dist/events.d.ts +1658 -0
  305. package/dist/events.js +0 -0
  306. package/dist/field.d.ts +2 -0
  307. package/dist/field.js +101 -0
  308. package/dist/file-input.d.ts +2 -0
  309. package/dist/file-input.js +140 -0
  310. package/dist/forward-attributes.d.ts +2 -0
  311. package/dist/forward-attributes.js +22 -0
  312. package/dist/history.d.ts +2 -0
  313. package/dist/history.js +11 -0
  314. package/dist/hover-card.d.ts +2 -0
  315. package/dist/hover-card.js +31 -0
  316. package/dist/image-cropper.d.ts +2 -0
  317. package/dist/image-cropper.js +66 -0
  318. package/dist/index.d.ts +66 -0
  319. package/dist/index.js +269 -0
  320. package/dist/input.d.ts +3 -0
  321. package/dist/input.js +69 -0
  322. package/dist/label.d.ts +2 -0
  323. package/dist/label.js +11 -0
  324. package/dist/list.d.ts +40 -0
  325. package/dist/list.js +192 -0
  326. package/dist/listbox.d.ts +2 -0
  327. package/dist/listbox.js +84 -0
  328. package/dist/menu.d.ts +4 -0
  329. package/dist/menu.js +295 -0
  330. package/dist/menubar.d.ts +4 -0
  331. package/dist/menubar.js +30 -0
  332. package/dist/meter.d.ts +2 -0
  333. package/dist/meter.js +22 -0
  334. package/dist/navigation-menu.d.ts +2 -0
  335. package/dist/navigation-menu.js +114 -0
  336. package/dist/number-input.d.ts +2 -0
  337. package/dist/number-input.js +73 -0
  338. package/dist/observe.d.ts +2 -0
  339. package/dist/observe.js +7 -0
  340. package/dist/otp-input.d.ts +2 -0
  341. package/dist/otp-input.js +72 -0
  342. package/dist/overlays.d.ts +13 -0
  343. package/dist/overlays.js +156 -0
  344. package/dist/pagination.d.ts +2 -0
  345. package/dist/pagination.js +127 -0
  346. package/dist/password-input.d.ts +2 -0
  347. package/dist/password-input.js +57 -0
  348. package/dist/popover.d.ts +2 -0
  349. package/dist/popover.js +64 -0
  350. package/dist/popup.d.ts +40 -0
  351. package/dist/popup.js +112 -0
  352. package/dist/preferences.d.ts +2 -0
  353. package/dist/preferences.js +56 -0
  354. package/dist/progress.d.ts +2 -0
  355. package/dist/progress.js +22 -0
  356. package/dist/radio.d.ts +2 -0
  357. package/dist/radio.js +33 -0
  358. package/dist/rating.d.ts +2 -0
  359. package/dist/rating.js +64 -0
  360. package/dist/resizable.d.ts +2 -0
  361. package/dist/resizable.js +42 -0
  362. package/dist/scroll-area.d.ts +2 -0
  363. package/dist/scroll-area.js +45 -0
  364. package/dist/segmented-control.d.ts +2 -0
  365. package/dist/segmented-control.js +28 -0
  366. package/dist/select.d.ts +2 -0
  367. package/dist/select.js +239 -0
  368. package/dist/separator.d.ts +2 -0
  369. package/dist/separator.js +11 -0
  370. package/dist/slider.d.ts +2 -0
  371. package/dist/slider.js +77 -0
  372. package/dist/tabs.d.ts +2 -0
  373. package/dist/tabs.js +37 -0
  374. package/dist/timeline.d.ts +2 -0
  375. package/dist/timeline.js +27 -0
  376. package/dist/toast.d.ts +20 -0
  377. package/dist/toast.js +177 -0
  378. package/dist/toggle.d.ts +3 -0
  379. package/dist/toggle.js +64 -0
  380. package/dist/token-input.d.ts +2 -0
  381. package/dist/token-input.js +103 -0
  382. package/dist/toolbar.d.ts +2 -0
  383. package/dist/toolbar.js +36 -0
  384. package/dist/tooltip.d.ts +2 -0
  385. package/dist/tooltip.js +35 -0
  386. package/dist/tree.d.ts +2 -0
  387. package/dist/tree.js +147 -0
  388. package/dist/trigger-menu.d.ts +2 -0
  389. package/dist/trigger-menu.js +133 -0
  390. package/dist/waterfall.d.ts +2 -0
  391. package/dist/waterfall.js +140 -0
  392. package/docs/README.md +89 -0
  393. package/docs/agents.md +1523 -0
  394. package/docs/components/accordion.md +1401 -0
  395. package/docs/components/alert-dialog.md +788 -0
  396. package/docs/components/autocomplete.md +1692 -0
  397. package/docs/components/avatar.md +261 -0
  398. package/docs/components/button.md +441 -0
  399. package/docs/components/calendar.md +1536 -0
  400. package/docs/components/carousel.md +1055 -0
  401. package/docs/components/checkbox.md +1045 -0
  402. package/docs/components/collapsible.md +535 -0
  403. package/docs/components/color-picker.md +651 -0
  404. package/docs/components/combobox.md +3274 -0
  405. package/docs/components/command.md +1431 -0
  406. package/docs/components/date-input.md +563 -0
  407. package/docs/components/date-picker.md +1136 -0
  408. package/docs/components/dialog.md +1360 -0
  409. package/docs/components/drawer.md +1201 -0
  410. package/docs/components/field.md +519 -0
  411. package/docs/components/file-input.md +617 -0
  412. package/docs/components/hover-card.md +486 -0
  413. package/docs/components/image-cropper.md +489 -0
  414. package/docs/components/input.md +327 -0
  415. package/docs/components/label.md +208 -0
  416. package/docs/components/listbox.md +1015 -0
  417. package/docs/components/menu.md +1212 -0
  418. package/docs/components/menubar.md +460 -0
  419. package/docs/components/meter.md +298 -0
  420. package/docs/components/navigation-menu.md +1573 -0
  421. package/docs/components/number-input.md +1087 -0
  422. package/docs/components/otp-input.md +558 -0
  423. package/docs/components/pagination.md +639 -0
  424. package/docs/components/password-input.md +649 -0
  425. package/docs/components/popover.md +1433 -0
  426. package/docs/components/preferences.md +243 -0
  427. package/docs/components/progress.md +406 -0
  428. package/docs/components/radio.md +855 -0
  429. package/docs/components/rating.md +431 -0
  430. package/docs/components/resizable.md +866 -0
  431. package/docs/components/scroll-area.md +898 -0
  432. package/docs/components/segmented-control.md +421 -0
  433. package/docs/components/select.md +1957 -0
  434. package/docs/components/separator.md +238 -0
  435. package/docs/components/slider.md +1018 -0
  436. package/docs/components/switch.md +583 -0
  437. package/docs/components/tabs.md +948 -0
  438. package/docs/components/textarea.md +331 -0
  439. package/docs/components/time-input.md +235 -0
  440. package/docs/components/timeline.md +622 -0
  441. package/docs/components/toast.md +1223 -0
  442. package/docs/components/toggle.md +522 -0
  443. package/docs/components/token-input.md +854 -0
  444. package/docs/components/toolbar.md +815 -0
  445. package/docs/components/tooltip.md +860 -0
  446. package/docs/components/tree.md +1243 -0
  447. package/docs/components/trigger-menu.md +1110 -0
  448. package/docs/components/waterfall.md +709 -0
  449. package/docs/controllers.md +66 -0
  450. package/docs/core.md +8027 -0
  451. package/docs/custom-parts.md +91 -0
  452. package/docs/first-component.md +88 -0
  453. package/docs/forms.md +125 -0
  454. package/docs/installation.md +141 -0
  455. package/docs/introduction.md +32 -0
  456. package/docs/lists-and-menus.md +166 -0
  457. package/docs/llms.txt +1566 -0
  458. package/docs/parts-and-composition.md +57 -0
  459. package/docs/popups.md +92 -0
  460. package/docs/rendering.md +71 -0
  461. package/docs/server-rendering.md +118 -0
  462. package/docs/state-and-events.md +134 -0
  463. package/docs/styling-and-animation.md +104 -0
  464. package/docs/utilities/async-options.md +332 -0
  465. package/docs/utilities/shortcuts.md +184 -0
  466. package/html-custom-data.json +14792 -0
  467. package/package.json +302 -3
@@ -0,0 +1,1212 @@
1
+ # Menu
2
+
3
+ Choose actions and settings from a button or a context menu.
4
+
5
+ Start with a trigger and a few actions. Add groups, checkboxes, radio choices, or submenus as the task grows. The menu handles focus, arrow keys, typeahead, and dismissal.
6
+
7
+ <ComponentExample
8
+ name="menu/basic"
9
+ inspect
10
+ caption="Press Open Menu and choose an item, with the pointer or the arrow keys and Enter. The menu closes and the console logs your choice."
11
+ />
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @ui-sdk/web @ui-sdk/core
17
+ ```
18
+
19
+ ## Composition
20
+
21
+ Use the following composition to build a `ui-menu`:
22
+
23
+ ```text
24
+ ui-menu
25
+ ├── ui-menu-trigger
26
+ │ └── button
27
+ └── ui-menu-content
28
+ ├── ui-menu-item
29
+ └── ui-menu-item
30
+ ```
31
+
32
+ Give items a `value` and handle the menu’s `action` event to run the selected command. A Group and GroupLabel can organize related actions; a Separator divides them without becoming a focus stop.
33
+
34
+ Pass a promise to `event.detail.waitUntil()` in the menu’s `action` listener or an item’s `select` listener to keep the item pending. It gets `data-pending`, `aria-busy` and `aria-disabled`, the menu stays open, and choosing it again does nothing until the promise settles. The menu closes once it resolves. A rejection keeps it open and puts the reason in the item’s `error` state until the next try. After a second, screen readers hear the `pending-announcement` attribute, “Loading” by default.
35
+
36
+ ## Usage
37
+
38
+ Import the parts and put them together:
39
+
40
+ ```html
41
+ <script type="module">
42
+ import "@ui-sdk/web/menu"
43
+ </script>
44
+
45
+ <ui-menu>
46
+ <ui-menu-trigger>
47
+ <button>Actions</button>
48
+ </ui-menu-trigger>
49
+ <ui-menu-content>
50
+ <ui-menu-item value="rename">Rename</ui-menu-item>
51
+ <ui-menu-item value="duplicate">Duplicate</ui-menu-item>
52
+ </ui-menu-content>
53
+ </ui-menu>
54
+ ```
55
+
56
+ ## Submenus
57
+
58
+ Nest a Sub, SubTrigger, and SubContent to add another level. Hover or press the arrow key toward a submenu to open it. The menu lets a pointer travel diagonally into the submenu without accidentally choosing an item it passes over. Escape closes only the submenu and returns focus to its trigger, as the menu pattern asks.
59
+
60
+ ```ts title="submenu.ts"
61
+ import "@ui-sdk/web/menu"
62
+ import { ChevronRight, createElement } from "lucide"
63
+ import { log } from "./log"
64
+
65
+ function mount(root: HTMLElement) {
66
+ root.innerHTML = `
67
+ <ui-menu>
68
+ <ui-menu-trigger>
69
+ <button>Edit</button>
70
+ </ui-menu-trigger>
71
+ <ui-menu-content>
72
+ <ui-menu-item value="undo" shortcut="mod+z">Undo</ui-menu-item>
73
+ <ui-menu-item value="redo" shortcut="mod+shift+z">Redo</ui-menu-item>
74
+ <ui-menu-separator></ui-menu-separator>
75
+ <ui-menu-sub>
76
+ <ui-menu-sub-trigger>
77
+ Find
78
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
79
+ </ui-menu-sub-trigger>
80
+ <ui-menu-sub-content>
81
+ <ui-menu-item value="find" shortcut="mod+f">Find…</ui-menu-item>
82
+ <ui-menu-item value="find-next" shortcut="mod+g">Find next</ui-menu-item>
83
+ <ui-menu-item value="find-previous" shortcut="mod+shift+g">Find previous</ui-menu-item>
84
+ </ui-menu-sub-content>
85
+ </ui-menu-sub>
86
+ <ui-menu-sub>
87
+ <ui-menu-sub-trigger>
88
+ Transform
89
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
90
+ </ui-menu-sub-trigger>
91
+ <ui-menu-sub-content>
92
+ <ui-menu-sub>
93
+ <ui-menu-sub-trigger>
94
+ Case
95
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
96
+ </ui-menu-sub-trigger>
97
+ <ui-menu-sub-content>
98
+ <ui-menu-item value="uppercase">Uppercase</ui-menu-item>
99
+ <ui-menu-item value="lowercase">Lowercase</ui-menu-item>
100
+ </ui-menu-sub-content>
101
+ </ui-menu-sub>
102
+ <ui-menu-item value="trim">Trim whitespace</ui-menu-item>
103
+ </ui-menu-sub-content>
104
+ </ui-menu-sub>
105
+ </ui-menu-content>
106
+ </ui-menu>
107
+ `
108
+ root
109
+ .querySelector("ui-menu")!
110
+ .addEventListener("action", (event) =>
111
+ log(root, `Chose ${event.detail.value}`)
112
+ )
113
+ }
114
+
115
+ export { mount }
116
+ ```
117
+
118
+ Open Edit, then Find or Transform. Use the arrows to enter and leave each level; choosing an action closes the menu tree.
119
+
120
+ ## Shortcuts
121
+
122
+ An item’s `shortcut` displays a platform-specific hint and sets `aria-keyshortcuts`. `mod+s` reads as Command+S on Apple platforms and Control+S elsewhere.
123
+
124
+ In markup, this is a hint: your app owns the key handler. [Data menus](#menus-as-data) also register their shortcuts while closed. Letters and digits match physical keys, including combinations such as Option+N.
125
+
126
+ ```ts title="shortcuts.ts"
127
+ import "@ui-sdk/web/menu"
128
+ import { log } from "./log"
129
+
130
+ function mount(root: HTMLElement) {
131
+ root.innerHTML = `
132
+ <ui-menu>
133
+ <ui-menu-trigger>
134
+ <button>File</button>
135
+ </ui-menu-trigger>
136
+ <ui-menu-content>
137
+ <ui-menu-item value="new-tab" shortcut="mod+t">New tab</ui-menu-item>
138
+ <ui-menu-item value="new-window" shortcut="mod+n">New window</ui-menu-item>
139
+ <ui-menu-item value="open" shortcut="mod+o">Open…</ui-menu-item>
140
+ <ui-menu-separator></ui-menu-separator>
141
+ <ui-menu-item value="save" shortcut="mod+s">Save</ui-menu-item>
142
+ <ui-menu-item value="save-as" shortcut="mod+shift+s">Save as…</ui-menu-item>
143
+ <ui-menu-separator></ui-menu-separator>
144
+ <ui-menu-item value="print" shortcut="mod+p">Print…</ui-menu-item>
145
+ </ui-menu-content>
146
+ </ui-menu>
147
+ `
148
+ root
149
+ .querySelector("ui-menu")!
150
+ .addEventListener("action", (event) =>
151
+ log(root, `Chose ${event.detail.value}`)
152
+ )
153
+ }
154
+
155
+ export { mount }
156
+ ```
157
+
158
+ Open File to see shortcut hints for your platform. Choose an item to report its action; this markup example does not register global shortcuts.
159
+
160
+ ## Icons
161
+
162
+ Put decorative icons in ItemIcon so screen readers and typeahead ignore them. `mirror` flips a directional icon in right-to-left text. An icon-only item still needs an `aria-label`.
163
+
164
+ `data-has-icon` on items and `data-has-icons` on the menu let text line up when only some items have icons.
165
+
166
+ ```ts title="icons.ts"
167
+ import "@ui-sdk/web/menu"
168
+ import { Code, createElement, Download, Link, Mail, Reply, Share } from "lucide"
169
+ import { log } from "./log"
170
+
171
+ function mount(root: HTMLElement) {
172
+ root.innerHTML = `
173
+ <ui-menu>
174
+ <ui-menu-trigger>
175
+ <button>
176
+ ${createElement(Share, { "aria-hidden": "true" }).outerHTML}
177
+ Share
178
+ </button>
179
+ </ui-menu-trigger>
180
+ <ui-menu-content>
181
+ <ui-menu-item value="copy-link">
182
+ <ui-menu-item-icon>
183
+ ${createElement(Link, { "aria-hidden": "true" }).outerHTML}
184
+ </ui-menu-item-icon>
185
+ Copy link
186
+ </ui-menu-item>
187
+ <ui-menu-item value="email">
188
+ <ui-menu-item-icon>
189
+ ${createElement(Mail, { "aria-hidden": "true" }).outerHTML}
190
+ </ui-menu-item-icon>
191
+ Email
192
+ </ui-menu-item>
193
+ <ui-menu-item value="reply">
194
+ <ui-menu-item-icon mirror>
195
+ ${createElement(Reply, { "aria-hidden": "true" }).outerHTML}
196
+ </ui-menu-item-icon>
197
+ Reply
198
+ </ui-menu-item>
199
+ <ui-menu-separator></ui-menu-separator>
200
+ <ui-menu-item value="download">
201
+ <ui-menu-item-icon>
202
+ ${createElement(Download, { "aria-hidden": "true" }).outerHTML}
203
+ </ui-menu-item-icon>
204
+ Download
205
+ </ui-menu-item>
206
+ <ui-menu-item value="embed">
207
+ <ui-menu-item-icon>
208
+ ${createElement(Code, { "aria-hidden": "true" }).outerHTML}
209
+ </ui-menu-item-icon>
210
+ Embed
211
+ </ui-menu-item>
212
+ </ui-menu-content>
213
+ </ui-menu>
214
+ `
215
+ root
216
+ .querySelector("ui-menu")!
217
+ .addEventListener("action", (event) =>
218
+ log(root, `Chose ${event.detail.value}`)
219
+ )
220
+ }
221
+
222
+ export { mount }
223
+ ```
224
+
225
+ Open Share and type an item’s name. The icons aren’t part of the item’s accessible name or the text typeahead matches.
226
+
227
+ ## Checkboxes
228
+
229
+ Checkbox items toggle independently and stay open by default. Listen for `checkedchange`; set `checked` to control the item, or use `default-checked` for its initial state. An ItemIndicator renders the check; `keep-mounted` preserves its space while unchecked. Style `data-checked` and `data-unchecked` for each state.
230
+
231
+ ```ts title="checkboxes.ts"
232
+ import "@ui-sdk/web/menu"
233
+ import { Check, createElement } from "lucide"
234
+ import { log } from "./log"
235
+
236
+ function mount(root: HTMLElement) {
237
+ root.innerHTML = `
238
+ <ui-menu>
239
+ <ui-menu-trigger>
240
+ <button>View</button>
241
+ </ui-menu-trigger>
242
+ <ui-menu-content>
243
+ <ui-menu-group>
244
+ <ui-menu-group-label>Appearance</ui-menu-group-label>
245
+ <ui-menu-checkbox-item value="status-bar" default-checked>
246
+ <ui-menu-item-indicator keep-mounted>
247
+ ${createElement(Check, { "aria-hidden": "true" }).outerHTML}
248
+ </ui-menu-item-indicator>
249
+ Status bar
250
+ </ui-menu-checkbox-item>
251
+ <ui-menu-checkbox-item value="minimap">
252
+ <ui-menu-item-indicator keep-mounted>
253
+ ${createElement(Check, { "aria-hidden": "true" }).outerHTML}
254
+ </ui-menu-item-indicator>
255
+ Minimap
256
+ </ui-menu-checkbox-item>
257
+ </ui-menu-group>
258
+ <ui-menu-separator></ui-menu-separator>
259
+ <ui-menu-checkbox-item value="line-numbers" default-checked>
260
+ <ui-menu-item-indicator keep-mounted>
261
+ ${createElement(Check, { "aria-hidden": "true" }).outerHTML}
262
+ </ui-menu-item-indicator>
263
+ Line numbers
264
+ </ui-menu-checkbox-item>
265
+ <ui-menu-checkbox-item value="word-wrap">
266
+ <ui-menu-item-indicator keep-mounted>
267
+ ${createElement(Check, { "aria-hidden": "true" }).outerHTML}
268
+ </ui-menu-item-indicator>
269
+ Word wrap
270
+ </ui-menu-checkbox-item>
271
+ </ui-menu-content>
272
+ </ui-menu>
273
+ `
274
+ for (const item of root.querySelectorAll("ui-menu-checkbox-item")) {
275
+ item.addEventListener("checkedchange", (event) => {
276
+ log(root, `${item.value} ${event.detail.value ? "on" : "off"}`)
277
+ })
278
+ }
279
+ }
280
+
281
+ export { mount }
282
+ ```
283
+
284
+ Open View and toggle Status bar or Minimap. The menu stays open so you can change several settings, and the console logs each one.
285
+
286
+ ## Radio Group
287
+
288
+ Radio items choose one value in their group and stay open by default. Listen for `valuechange`; set `value` to control the group, or use `default-value` for its initial state. Give the group a `name` when it should identify itself in the menu’s value-change callback.
289
+
290
+ ```ts title="radio-group.ts"
291
+ import "@ui-sdk/web/menu"
292
+ import { log } from "./log"
293
+
294
+ function mount(root: HTMLElement) {
295
+ root.innerHTML = `
296
+ <ui-menu>
297
+ <ui-menu-trigger>
298
+ <button>Panel</button>
299
+ </ui-menu-trigger>
300
+ <ui-menu-content>
301
+ <ui-menu-radio-group name="position" default-value="bottom">
302
+ <ui-menu-group-label>Position</ui-menu-group-label>
303
+ ${["Top", "Bottom", "Right"]
304
+ .map(
305
+ (option) => `
306
+ <ui-menu-radio-item value="${option.toLowerCase()}">
307
+ <ui-menu-item-indicator></ui-menu-item-indicator>
308
+ ${option}
309
+ </ui-menu-radio-item>
310
+ `
311
+ )
312
+ .join("")}
313
+ </ui-menu-radio-group>
314
+ <ui-menu-separator></ui-menu-separator>
315
+ <ui-menu-radio-group default-value="comfortable">
316
+ <ui-menu-group-label>Density</ui-menu-group-label>
317
+ ${["Compact", "Comfortable"]
318
+ .map(
319
+ (option) => `
320
+ <ui-menu-radio-item value="${option.toLowerCase()}">
321
+ <ui-menu-item-indicator></ui-menu-item-indicator>
322
+ ${option}
323
+ </ui-menu-radio-item>
324
+ `
325
+ )
326
+ .join("")}
327
+ </ui-menu-radio-group>
328
+ </ui-menu-content>
329
+ </ui-menu>
330
+ `
331
+ const [position, density] = root.querySelectorAll("ui-menu-radio-group")
332
+ position.addEventListener("valuechange", (event) =>
333
+ log(root, `Panel at the ${event.detail.value}`)
334
+ )
335
+ density.addEventListener("valuechange", (event) =>
336
+ log(root, `${event.detail.value} density`)
337
+ )
338
+ }
339
+
340
+ export { mount }
341
+ ```
342
+
343
+ Open Panel and choose a position. Only one item in each group is checked, the menu stays open, and the console logs the change.
344
+
345
+ ## Context Menu
346
+
347
+ A ContextTrigger opens at a right-click or long press. Give the area a tab stop so Shift+F10 and the context-menu key can open it from the keyboard. Right-clicking elsewhere in the same area repositions an open menu.
348
+
349
+ Set `disabled` to leave that area’s native browser menu available. Context triggers can pass a payload too.
350
+
351
+ ```ts title="context-menu.ts"
352
+ import "@ui-sdk/web/menu"
353
+ import { log } from "./log"
354
+
355
+ function mount(root: HTMLElement) {
356
+ root.innerHTML = `
357
+ <ui-menu>
358
+ <ui-menu-context-trigger tabindex="0">Right-click or long-press here</ui-menu-context-trigger>
359
+ <ui-menu-content>
360
+ <ui-menu-item value="back">Back</ui-menu-item>
361
+ <ui-menu-item value="forward" disabled>Forward</ui-menu-item>
362
+ <ui-menu-item value="reload">Reload</ui-menu-item>
363
+ <ui-menu-separator></ui-menu-separator>
364
+ <ui-menu-item value="save">Save page as…</ui-menu-item>
365
+ <ui-menu-item value="print">Print…</ui-menu-item>
366
+ </ui-menu-content>
367
+ </ui-menu>
368
+ `
369
+ root
370
+ .querySelector("ui-menu")!
371
+ .addEventListener("action", (event) =>
372
+ log(root, `Chose ${event.detail.value}`)
373
+ )
374
+ }
375
+
376
+ export { mount }
377
+ ```
378
+
379
+ Right-click or long-press the area, or focus it and press Shift+F10. Choose an action and the console logs it.
380
+
381
+ ## Placement
382
+
383
+ `side` and `align` choose the starting position. The menu flips or shifts when it does not fit; `data-side` reports where it ended up. Logical alignment and submenu keys follow the surrounding text direction. See the shared [popup guide](../popups.md) for the positioning contract.
384
+
385
+ ```html
386
+ <ui-menu-content side="right" align="start"></ui-menu-content>
387
+ ```
388
+
389
+ ## Offsets and Collision Padding
390
+
391
+ `side-offset` sets the gap, `align-offset` shifts along the anchor, and `collision-padding` leaves room around the viewport. Use `--anchor-width` to match the trigger's width and `--available-height` to scroll a long list.
392
+
393
+ ```html
394
+ <ui-menu-content
395
+ side-offset="8"
396
+ align-offset="-4"
397
+ collision-padding="24"
398
+ ></ui-menu-content>
399
+ ```
400
+
401
+ ## Modal
402
+
403
+ The default `modal="pointer"` consumes an outside press while closing the menu. `true` also locks scrolling and makes the page inert. `false` lets the outside press reach its target after closing.
404
+
405
+ ```html
406
+ <ui-menu modal="false">
407
+ <ui-menu-trigger>
408
+ <button>Actions</button>
409
+ </ui-menu-trigger>
410
+ <ui-menu-content></ui-menu-content>
411
+ </ui-menu>
412
+ ```
413
+
414
+ ## Submenu Delay
415
+
416
+ `delay` is the hover wait in milliseconds, 100 by default. It applies to the submenus directly inside that menu. Set it on a Sub to change the delay for the next level.
417
+
418
+ ```html
419
+ <ui-menu delay="300">
420
+ <ui-menu-trigger>
421
+ <button>File</button>
422
+ </ui-menu-trigger>
423
+ <ui-menu-content>
424
+ <ui-menu-sub delay="0">
425
+ <ui-menu-sub-trigger>Open recent</ui-menu-sub-trigger>
426
+ <ui-menu-sub-content></ui-menu-sub-content>
427
+ </ui-menu-sub>
428
+ </ui-menu-content>
429
+ </ui-menu>
430
+ ```
431
+
432
+ ## Keyboard Navigation
433
+
434
+ Arrow keys wrap by default; `loop` false stops at the ends. Typing finds an item by name. If an item has extra descriptive text, set `label` to the text typeahead should match. `label` does not replace its accessible name; use `aria-label` for that.
435
+
436
+ ```html
437
+ <ui-menu loop="false">
438
+ <ui-menu-trigger>
439
+ <button>Status</button>
440
+ </ui-menu-trigger>
441
+ <ui-menu-content>
442
+ <ui-menu-item value="busy" label="Do not disturb">
443
+ Do not disturb
444
+ <span>Notifications are muted</span>
445
+ </ui-menu-item>
446
+ </ui-menu-content>
447
+ </ui-menu>
448
+ ```
449
+
450
+ ## Focus After Closing
451
+
452
+ When the menu closes with focus inside it, focus returns to the trigger. If an action removes the trigger or moves people on to something else, set `final-focus` on the content to a selector, or to `"none"` to leave focus where the action put it. See [popup focus](../popups.md#focus) for the shared focus rules.
453
+
454
+ ```html
455
+ <ui-menu-content final-focus="#file-list"></ui-menu-content>
456
+ ```
457
+
458
+ ## Disabled Items
459
+
460
+ Disabled items remain focusable so people can discover them, but cannot run or open a submenu. A disabled Trigger is a native disabled button and cannot receive focus. Add `focusable-when-disabled` to keep it in the tab order, so it can host a tooltip that says why; presses still do nothing.
461
+
462
+ ```ts title="disabled.ts"
463
+ import "@ui-sdk/web/menu"
464
+ import { Check, ChevronRight, createElement } from "lucide"
465
+ import { log } from "./log"
466
+
467
+ function mount(root: HTMLElement) {
468
+ root.innerHTML = `
469
+ <div data-slot="row">
470
+ <ui-menu>
471
+ <ui-menu-trigger>
472
+ <button>Document</button>
473
+ </ui-menu-trigger>
474
+ <ui-menu-content>
475
+ <ui-menu-item value="undo" shortcut="mod+z" disabled>
476
+ Undo
477
+ </ui-menu-item>
478
+ <ui-menu-item value="redo" shortcut="mod+shift+z">
479
+ Redo
480
+ </ui-menu-item>
481
+ <ui-menu-separator></ui-menu-separator>
482
+ <ui-menu-checkbox-item value="autosave" default-checked disabled>
483
+ <ui-menu-item-indicator>${createElement(Check, { "aria-hidden": "true" }).outerHTML}</ui-menu-item-indicator>
484
+ Autosave
485
+ </ui-menu-checkbox-item>
486
+ <ui-menu-separator></ui-menu-separator>
487
+ <ui-menu-radio-group default-value="pdf">
488
+ <ui-menu-group-label>Export as</ui-menu-group-label>
489
+ <ui-menu-radio-item value="pdf">
490
+ <ui-menu-item-indicator>${createElement(Check, { "aria-hidden": "true" }).outerHTML}</ui-menu-item-indicator>
491
+ PDF
492
+ </ui-menu-radio-item>
493
+ <ui-menu-radio-item value="docx" disabled>
494
+ <ui-menu-item-indicator>${createElement(Check, { "aria-hidden": "true" }).outerHTML}</ui-menu-item-indicator>
495
+ Word
496
+ </ui-menu-radio-item>
497
+ <ui-menu-radio-item value="markdown">
498
+ <ui-menu-item-indicator>${createElement(Check, { "aria-hidden": "true" }).outerHTML}</ui-menu-item-indicator>
499
+ Markdown
500
+ </ui-menu-radio-item>
501
+ </ui-menu-radio-group>
502
+ <ui-menu-separator></ui-menu-separator>
503
+ <ui-menu-sub>
504
+ <ui-menu-sub-trigger disabled>
505
+ Move to
506
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
507
+ </ui-menu-sub-trigger>
508
+ <ui-menu-sub-content>
509
+ <ui-menu-item>Archive</ui-menu-item>
510
+ </ui-menu-sub-content>
511
+ </ui-menu-sub>
512
+ </ui-menu-content>
513
+ </ui-menu>
514
+ <ui-menu>
515
+ <ui-menu-trigger disabled>
516
+ <button>Locked</button>
517
+ </ui-menu-trigger>
518
+ <ui-menu-content>
519
+ <ui-menu-item>Unlock</ui-menu-item>
520
+ </ui-menu-content>
521
+ </ui-menu>
522
+ </div>
523
+ `
524
+ root
525
+ .querySelector("ui-menu")!
526
+ .addEventListener("action", (event) =>
527
+ log(root, `Chose ${event.detail.value}`)
528
+ )
529
+ }
530
+
531
+ export { mount }
532
+ ```
533
+
534
+ Open Document and move through its items with the arrow keys. Disabled actions remain discoverable but cannot run; Locked cannot open a menu.
535
+
536
+ ## Keeping the Menu Open
537
+
538
+ Plain items close when selected; checkbox and radio items stay open. `close-on-select` changes either default. A selection handler can also prevent the default event to keep it open for one action.
539
+
540
+ ```html
541
+ <ui-menu-item id="zoom-in" close-on-select="false">Zoom in</ui-menu-item>
542
+ <ui-menu-checkbox-item value="fit" close-on-select
543
+ >Fit to window</ui-menu-checkbox-item
544
+ >
545
+
546
+ <script type="module">
547
+ document
548
+ .querySelector("#zoom-in")
549
+ .addEventListener("select", () => setZoom(zoom + 10))
550
+ </script>
551
+ ```
552
+
553
+ ## Container
554
+
555
+ Menus render in place and use the top layer. `container` moves the popup to another element; submenus remain inside their parent popup. Set `container` to that element’s selector, as the [popup guide](../popups.md) explains.
556
+
557
+ ```html
558
+ <ui-menu container="#panel"></ui-menu>
559
+ ```
560
+
561
+ ## Animation
562
+
563
+ The menu stays mounted through an exit animation. `data-starting-style`, `data-ending-style`, `data-side`, and `--transform-origin` describe the transition. `keep-mounted` leaves the closed content hidden afterward; `openchangecomplete` reports when it finishes.
564
+
565
+ ```ts title="animation.ts"
566
+ import "@ui-sdk/web/menu"
567
+ import { ChevronRight, createElement } from "lucide"
568
+ import { log } from "./log"
569
+
570
+ function mount(root: HTMLElement) {
571
+ root.innerHTML = `
572
+ <ui-menu>
573
+ <ui-menu-trigger>
574
+ <button>Animated</button>
575
+ </ui-menu-trigger>
576
+ <ui-menu-content keep-mounted>
577
+ <ui-menu-item>Back</ui-menu-item>
578
+ <ui-menu-item>Forward</ui-menu-item>
579
+ <ui-menu-item>Reload</ui-menu-item>
580
+ <ui-menu-separator></ui-menu-separator>
581
+ <ui-menu-sub>
582
+ <ui-menu-sub-trigger>
583
+ More tools
584
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
585
+ </ui-menu-sub-trigger>
586
+ <ui-menu-sub-content>
587
+ <ui-menu-item>Save page as…</ui-menu-item>
588
+ <ui-menu-item>Developer tools</ui-menu-item>
589
+ </ui-menu-sub-content>
590
+ </ui-menu-sub>
591
+ </ui-menu-content>
592
+ </ui-menu>
593
+ `
594
+ const menu = root.querySelector("ui-menu")!
595
+ menu.addEventListener("openchange", (event) =>
596
+ log(root, event.detail.value ? "Opening" : "Closing")
597
+ )
598
+ menu.addEventListener("openchangecomplete", (event) =>
599
+ log(root, event.detail.value ? "Finished opening" : "Finished closing")
600
+ )
601
+ }
602
+
603
+ export { mount }
604
+ ```
605
+
606
+ Open Animated, then its More tools submenu, and close them. The console logs Opening or Closing, then when the animation finishes.
607
+
608
+ ## Presentation
609
+
610
+ `presentation="auto"` uses a sheet on small touch screens and a popover elsewhere; `"sheet"` always requests a sheet. The sheet keeps menu roles and keys. Submenus replace the current level, and Escape or the arrow toward the parent goes back. Context menus and menus in a menubar remain popovers.
611
+
612
+ Use `data-presentation` or the content’s `presentation` state for the two layouts. Leave a sheet’s translation to the SDK. `--sheet-backdrop` sets its dimming color, and the sheet is modal unless `modal` is false. The [popup guide](../popups.md) covers shared presentation behavior.
613
+
614
+ ```ts title="presentation.ts"
615
+ import "@ui-sdk/web/menu"
616
+ import {
617
+ ChevronRight,
618
+ createElement,
619
+ Download,
620
+ Link,
621
+ Share,
622
+ Trash,
623
+ } from "lucide"
624
+ import { log } from "./log"
625
+
626
+ function mount(root: HTMLElement) {
627
+ root.innerHTML = `
628
+ <ui-menu presentation="sheet">
629
+ <ui-menu-trigger>
630
+ <button>Options</button>
631
+ </ui-menu-trigger>
632
+ <ui-menu-content>
633
+ <ui-menu-item value="share">
634
+ ${createElement(Share, { "aria-hidden": "true" }).outerHTML}
635
+ Share
636
+ </ui-menu-item>
637
+ <ui-menu-item value="copy-link">
638
+ ${createElement(Link, { "aria-hidden": "true" }).outerHTML}
639
+ Copy link
640
+ </ui-menu-item>
641
+ <ui-menu-sub>
642
+ <ui-menu-sub-trigger>
643
+ ${createElement(Download, { "aria-hidden": "true" }).outerHTML}
644
+ Move to
645
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
646
+ </ui-menu-sub-trigger>
647
+ <ui-menu-sub-content>
648
+ <ui-menu-item value="inbox">Inbox</ui-menu-item>
649
+ <ui-menu-item value="archive">Archive</ui-menu-item>
650
+ </ui-menu-sub-content>
651
+ </ui-menu-sub>
652
+ <ui-menu-separator></ui-menu-separator>
653
+ <ui-menu-item value="delete" destructive>
654
+ ${createElement(Trash, { "aria-hidden": "true" }).outerHTML}
655
+ Delete
656
+ </ui-menu-item>
657
+ </ui-menu-content>
658
+ </ui-menu>
659
+ `
660
+ root
661
+ .querySelector("ui-menu")!
662
+ .addEventListener("action", (event) =>
663
+ log(root, `Chose ${event.detail.value}`)
664
+ )
665
+ }
666
+
667
+ export { mount }
668
+ ```
669
+
670
+ Open Options as a sheet, enter Move to, then go back or choose a destination. You can drag downward to dismiss the sheet.
671
+
672
+ ## Controlled
673
+
674
+ Read or set the element’s `open` property, and listen for `openchange`. A change carries a reason, and `event.preventDefault()` can refuse it. A controller can open the menu from another control; see [state and events](../state-and-events.md) and [controllers](../controllers.md) for that shared pattern.
675
+
676
+ ```ts
677
+ const menu = document.querySelector("ui-menu")!
678
+
679
+ menu.open = true
680
+ menu.addEventListener("openchange", (event) => {
681
+ console.log(event.detail.value, event.detail.reason)
682
+ })
683
+ ```
684
+
685
+ ## Triggers Outside the Menu
686
+
687
+ Give the menu an `id` and connect external triggers with `for`. Assign their `payload` properties and read the active row through `api.getPayload()`. Only the active trigger is expanded and receives focus back. Code can also open with a `payload` and `anchor`; the Promise resolves when the menu closes.
688
+
689
+ ```ts title="payload.ts"
690
+ import type { UiMenuElement } from "@ui-sdk/web/menu"
691
+ import "@ui-sdk/web/menu"
692
+ import { createElement, Ellipsis } from "lucide"
693
+ import { log } from "./log"
694
+
695
+ type File = { name: string; size: string }
696
+
697
+ const files: File[] = [
698
+ { name: "Roadmap.pdf", size: "2.4 MB" },
699
+ { name: "Budget.xlsx", size: "860 KB" },
700
+ { name: "Team photo.jpg", size: "5.1 MB" },
701
+ ]
702
+
703
+ let count = 0
704
+
705
+ function mount(root: HTMLElement) {
706
+ const id = `file-actions-${++count}`
707
+ root.innerHTML = `
708
+ <div data-slot="stack">
709
+ <div data-slot="controls">
710
+ <button>Open ${files[0].name} Actions</button>
711
+ </div>
712
+ <ul data-slot="list"></ul>
713
+ <ui-menu id="${id}">
714
+ <ui-menu-content align="end">
715
+ <ui-menu-group>
716
+ <ui-menu-group-label></ui-menu-group-label>
717
+ <ui-menu-item value="open">Open</ui-menu-item>
718
+ <ui-menu-item value="rename">Rename</ui-menu-item>
719
+ <ui-menu-item value="duplicate">Duplicate</ui-menu-item>
720
+ </ui-menu-group>
721
+ <ui-menu-separator></ui-menu-separator>
722
+ <ui-menu-item value="delete">Delete</ui-menu-item>
723
+ </ui-menu-content>
724
+ </ui-menu>
725
+ </div>
726
+ `
727
+ const menu = root.querySelector<UiMenuElement<File>>("ui-menu")!
728
+ const label = root.querySelector("ui-menu-group-label")!
729
+
730
+ for (const file of files) {
731
+ const row = document.createElement("li")
732
+ row.dataset.slot = "row"
733
+ row.innerHTML = `${file.name}<span data-slot="hint">${file.size}</span>`
734
+ const trigger = Object.assign(document.createElement("ui-menu-trigger"), {
735
+ payload: file,
736
+ })
737
+ trigger.setAttribute("for", id)
738
+ trigger.innerHTML = `
739
+ <button data-variant="ghost" data-size="sm" aria-label="Actions for ${file.name}">
740
+ ${createElement(Ellipsis, { "aria-hidden": "true" }).outerHTML}
741
+ </button>
742
+ `
743
+ row.append(trigger)
744
+ root.querySelector("ul")!.append(row)
745
+ }
746
+
747
+ menu.addEventListener("payloadchange", (event) => {
748
+ const file = event.detail.value
749
+ label.textContent = file?.name ?? ""
750
+ })
751
+ menu.addEventListener("action", (event) => {
752
+ const { value } = event.detail
753
+ log(root, `${value} ${menu.api.getPayload()?.name}`)
754
+ })
755
+
756
+ const opener = root.querySelector<HTMLButtonElement>(
757
+ '[data-slot="controls"] button'
758
+ )!
759
+ opener.addEventListener("click", async () => {
760
+ const result = await menu.api.open({ payload: files[0], anchor: opener })
761
+ log(root, `Closed with ${result.reason}`)
762
+ })
763
+ }
764
+
765
+ export { mount }
766
+ ```
767
+
768
+ Open the actions for different files. One menu shows the active row; opening from code uses the same payload and reports the closing reason.
769
+
770
+ ## Menus as Data
771
+
772
+ `items` accepts `MenuData`: actions, groups, checkboxes, radio groups, and submenus. A string is shorthand for an item whose label and value match. Data menus register their shortcuts even while closed; set `item-shortcuts` to `false` to turn that off.
773
+
774
+ Listen on each part, or capture its `checkedchange` and `valuechange` events on the menu with `{ capture: true }`. Read the new state from `detail.value` and identify the item’s `value` or group’s `name` on the event target. Either change can be canceled. You can also supply a `<template>` of your own parts, which the menu fills from `items` while it keeps the shortcuts registered. The [lists and menus guide](../lists-and-menus.md) covers the data shape.
775
+
776
+ ```html
777
+ <ui-menu>
778
+ <ui-menu-trigger>
779
+ <button>File</button>
780
+ </ui-menu-trigger>
781
+ </ui-menu>
782
+
783
+ <script type="module">
784
+ const fileMenu = [
785
+ { value: "open", label: "Open…", shortcut: "mod+o" },
786
+ { type: "separator" },
787
+ {
788
+ type: "checkbox",
789
+ value: "toolbar",
790
+ label: "Toolbar",
791
+ defaultChecked: true,
792
+ },
793
+ {
794
+ type: "radio-group",
795
+ name: "sort",
796
+ label: "Sort by",
797
+ defaultValue: "name",
798
+ items: [
799
+ { value: "name", label: "Name" },
800
+ { value: "date", label: "Date modified" },
801
+ ],
802
+ },
803
+ { type: "submenu", label: "Share", items: ["Copy link", "Email"] },
804
+ ]
805
+
806
+ const menu = document.querySelector("ui-menu")
807
+ menu.items = fileMenu
808
+ menu.addEventListener("action", (event) => run(event.detail.value))
809
+ </script>
810
+ ```
811
+
812
+ ## Links and Custom Rendering
813
+
814
+ Put a native link inside `<ui-menu-item as-child>`, as the shared [composition API](../parts-and-composition.md) describes. Enter follows the link like a click, preserving its target and modifier-key behavior.
815
+
816
+ ```html
817
+ <ui-menu-item as-child>
818
+ <a href="#composition">Composition</a>
819
+ </ui-menu-item>
820
+ ```
821
+
822
+ ## Custom Rendering
823
+
824
+ See [Links and Custom Rendering](#links-and-custom-rendering) for a native link. The shared [composition guide](../parts-and-composition.md) covers element replacement and state-based rendering.
825
+
826
+ ## Styling
827
+
828
+ Use `data-highlighted` for the active item, `data-disabled` for unavailable actions, and `data-checked` for selected settings. Popup sizing and motion hooks are shared with the [styling guide](../styling-and-animation.md).
829
+
830
+ Each part has a `data-slot`, such as `menu-item`, and its state attributes are listed under [Data Attributes](#data-attributes).
831
+
832
+ ### `<ui-menu-content>`
833
+ - `--anchor-width`: Width of the anchor, usually the trigger. Use it to match widths.
834
+ - `--anchor-height`: Height of the anchor.
835
+ - `--available-width`: Space beside the popup, up to the viewport edge. Not inherited either.
836
+ - `--available-height`: Space between the anchor and the viewport edge. Use it for max-height. Not inherited, so a child that needs it copies it on the popup, like `[--list-height:var(--available-height)]`, and reads `max-h-(--list-height)`. The first popup registers both names for the whole page with `CSS.registerProperty`, so they don't inherit anywhere. A page that needs them to inherit calls it first, with `inherits: true`. An `@property` rule can't, since a registration in script wins.
837
+ - `--transform-origin`: The point on the anchor the popup opens from. Use it to scale from the right place.
838
+ - `--arrow-offset`: Where the anchor's center falls along the popup's edge, kept `arrowPadding` from the corners. Position a ::before arrow with it.
839
+ - `--drawer-progress`: While it presents as a sheet: how far open the sheet is, from 0 closed to 1 at its largest snap point. Never past 1.
840
+ - `--drawer-dim`: While it presents as a sheet: how dim the page behind is, from 0 to 1. The backdrop's opacity follows it.
841
+ - `--drawer-expand`: While it presents as a sheet: 0 at the second-largest snap point and below, 1 at the largest. With one snap point, the whole travel.
842
+ - `--drawer-stack`: While it presents as a sheet: how many sheets opened from inside this one are open above it, rising smoothly as each one opens. Push it back with it.
843
+ - `--sheet-backdrop`: Set it yourself: the color the page dims to while it presents as a sheet. Black at 0.2 by default.
844
+
845
+ ### `<ui-menu-viewport>`
846
+ - `--keyboard-inset`: While it presents as a sheet: how many pixels of the page the on-screen keyboard covers, 0 without one.
847
+ - `--visual-viewport-width`: While it presents as a sheet: the width of the part of the page you can see.
848
+ - `--visual-viewport-height`: While it presents as a sheet: the height of the part of the page you can see, above the keyboard.
849
+ - `--visual-viewport-top`: While it presents as a sheet: how far the part of the page you can see is scrolled from the top of the layout, as when the keyboard pushes it up.
850
+
851
+ ## Data Attributes
852
+
853
+ `<ui-menu-radio-group>`, `<ui-menu-group>`, `<ui-menu-group-label>`, `<ui-menu-separator>` and `<ui-menu-shortcut>` have no state attributes.
854
+
855
+ ### `<ui-menu-trigger>`
856
+ `[data-slot="menu-trigger"]`
857
+
858
+ - `data-closed`: Present on a trigger while the menu isn't open from it.
859
+ - `data-open`: Present on the trigger that opened the menu, while it's open.
860
+
861
+ ### `<ui-menu-context-trigger>`
862
+ `[data-slot="menu-context-trigger"]`
863
+
864
+ - `data-closed`: Present on an area while the menu isn't open from it.
865
+ - `data-disabled`: Present when the item can't be chosen.
866
+
867
+ ### `<ui-menu-content>`
868
+ `[data-slot="menu-content"]`
869
+
870
+ - `data-closed`: Present while the menu is closed, including while it animates out.
871
+ - `data-presentation` (`popover`, `sheet`): How the menu presents this time it's open. Submenus present like their root.
872
+ - `data-nested`: Present on submenus.
873
+ - `data-open`: Present while the menu is open. A submenu trigger has it while its submenu is open.
874
+ - `data-starting-style`: Present for the first frame. Style the state you animate from.
875
+ - `data-ending-style`: Present while the menu animates out. Style the state you animate to.
876
+ - `data-has-icons`: Present while any item inside holds an icon part, so items without one can line up.
877
+
878
+ ### `<ui-menu-item>`
879
+ `[data-slot="menu-item"]`
880
+
881
+ - `data-disabled`: Present when the item can't be chosen.
882
+ - `data-has-icon`: Present on an item while it holds an icon part.
883
+
884
+ ### `<ui-menu-checkbox-item>`
885
+ `[data-slot="menu-checkbox-item"]`
886
+
887
+ - `data-checked`: Present on checked checkbox items, and on the radio item that's its group's value.
888
+ - `data-unchecked`: Present on items that aren't checked.
889
+
890
+ ### `<ui-menu-item-indicator>`
891
+ `[data-slot="menu-item-indicator"]`
892
+
893
+ - `data-checked`: Present while its item is checked.
894
+ - `data-unchecked`: Present while its item isn't checked, with `keepMounted`.
895
+
896
+ ### `<ui-menu-radio-item>`
897
+ `[data-slot="menu-radio-item"]`
898
+
899
+ - `data-checked`: Present on checked checkbox items, and on the radio item that's its group's value.
900
+ - `data-unchecked`: Present on items that aren't checked.
901
+
902
+ ### `<ui-menu-item-icon>`
903
+ `[data-slot="menu-item-icon"]`
904
+
905
+ - `data-mirror`: Present on an icon that flips in right-to-left text, with `mirror`.
906
+
907
+ ## API Reference
908
+
909
+ Props are attributes in kebab case, like `default-checked` and `side-offset`. Attributes can change at any time, including the options on `<ui-menu>`: `"false"` is false, and numbers are numbers. Callbacks are DOM events.
910
+
911
+ ### Menu
912
+
913
+ - `container?` (`DocumentFragment | Element | null`): Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
914
+ - `open?` (`boolean`): Whether the menu is open. Pass it to control the menu.
915
+ - `defaultOpen?` (`boolean`): Whether the menu starts open, when uncontrolled.
916
+ - `openchange event`: Runs when the menu wants to open or close. `details.reason` says why, and `details.cancel()` stops it.
917
+ - `openchangecomplete event`: Runs once the menu has finished opening or closing, after any animation.
918
+ - `payloadchange event`: Runs when the payload the content shows changes: a trigger or context area with another payload opens the menu, or takes the open menu over, which fires no `openchange`, and so does a controller's `open({ payload })` on the open menu. `details.cancel()` keeps the previous payload.
919
+ - `action event`: Runs for every plain item, including items in submenus, with the item's value. Return a Promise to mark the item pending until it settles: it gets `data-pending`, `aria-busy` and `aria-disabled`, the menu stays open, and choosing it again waits. It closes once the Promise resolves, and a rejection keeps it open with the reason in the item's `error` state until the next try.
920
+ - `pendingAnnouncement?` (`string`): What screen readers hear once an item has been pending for a second, so a slow action doesn't pass in silence. Set it in the page's language. An empty string says nothing.
921
+ - `checkedchange event`: Runs when any checkbox item in the menu or its submenus toggles, with the item's value in `details.value`. Made for data menus.
922
+ - `valuechange event`: Runs when any radio group in the menu or its submenus changes, with the group in `details.group`. Made for data menus.
923
+ - `items?` (`MenuData`): The menu as data. Renders the content for you, unless you place a content part of your own, and runs the items' shortcuts.
924
+ - `itemShortcuts?` (`boolean`): Whether a data menu runs its items' shortcuts while closed.
925
+ - `loop?` (`boolean`): Whether arrow keys wrap from the last item to the first.
926
+ - `delay?` (`number`): Milliseconds the pointer rests on a submenu trigger before its submenu opens.
927
+ - `presentation?` (`PresentationSetting<"popover">`): How the menu presents. `"sheet"` slides it up from the bottom of the screen, where it can be swiped away and submenus open in its place, and `"auto"` does that on small touch screens. Decided each time it opens; the server renders `"popover"` for `"auto"`. A context menu and a menu in a menubar are always popovers.
928
+ - `controller?` (`PopupController<any, MenuOpenChangeReason, MenuOpenOptions, string, MenuPayloadChangeReason>`): Opens, closes and inspects the menu from anywhere, and connects triggers outside it. Create one with `createMenuController()`.
929
+ - `modal?` (`"pointer" | boolean`): `"pointer"`: an outside press only closes the menu. `true`: also locks scroll, traps focus and makes the rest of the page inert. `false`: outside presses reach what they hit.
930
+ - `dismissible?` (`boolean`): Whether Escape, an outside press, focus or the window leaving, Android's back gesture and a sheet's swipe can close it. Triggers, Close parts and the controller always can. Pass `false` while it holds unsaved work, and ask in `dismissattempt`.
931
+ - `dismissattempt event`: Runs when `dismissible` refused a dismissal made on purpose: Escape, an outside press, back or a swipe. Focus or the window leaving is refused without it. Ask "Discard changes?" here.
932
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
933
+
934
+ ### MenuTrigger
935
+
936
+ A trigger outside the menu names it with `for="menu-id"`, and takes `payload` as a property.
937
+
938
+ - `disabled?` (`boolean`): Stops the trigger from opening the menu. A native button can't be focused or pressed, unless `focusableWhenDisabled` is set.
939
+ - `focusableWhenDisabled?` (`boolean`): Keeps a disabled trigger in the tab order with `aria-disabled` instead of the native attribute, so it can host a tooltip that says why. Presses still do nothing.
940
+ - `payload?` (`unknown`): Data for the content to render when this trigger opens the menu, like the row it belongs to.
941
+ - `shortcut?` (`string`): A keyboard shortcut that opens the menu from anywhere, like `"mod+j"`. `mod` is Command on Apple platforms and Control elsewhere. Sets `aria-keyshortcuts`.
942
+ - `controller?` (`PopupController<any, MenuOpenChangeReason, MenuOpenOptions, string, MenuPayloadChangeReason>`): Connects a trigger outside the menu, like one per table row, to the menu with this controller.
943
+
944
+ ### MenuContextTrigger
945
+
946
+ - `disabled?` (`boolean`): Stops a right-click, a long press or Shift+F10 from opening the menu here. The browser's own menu shows instead.
947
+ - `payload?` (`unknown`): Data for the content to render when this area opens the menu, like the row it belongs to.
948
+ - `controller?` (`PopupController<any, MenuOpenChangeReason, MenuOpenOptions, string, MenuPayloadChangeReason>`): Connects an area outside the menu to the menu with this controller.
949
+
950
+ ### MenuContent
951
+
952
+ - `side?` (`Side`): Which side of the anchor the popup opens on. Flips to the other side when there isn't room. Which side of the trigger the menu opens on. Submenus open to the side, mirrored in right-to-left layouts.
953
+ - `align?` (`Align`): How the popup lines up with the anchor along that side. `start` and `end` follow the reading direction. How the menu lines up with the trigger along that side.
954
+ - `sideOffset?` (`number`): Gap between the anchor and the popup, in pixels. Gap between the trigger and the menu, in pixels.
955
+ - `alignOffset?` (`number`): Shift along the anchor, in pixels. Shift along the trigger, in pixels. Submenus default to lining up their first item with the trigger.
956
+ - `collisionPadding?` (`number`): Space to keep from the viewport edges, in pixels.
957
+ - `arrowPadding?` (`number`): Space to keep between the arrow and the popup's corners, in pixels.
958
+ - `anchor?` (`string | Anchor | null`): Positions the popup against this instead of its trigger: an element, a CSS selector, a point like `{ x, y }`, or anything with `getBoundingClientRect()`. It's read on every check, so it can move. A controller's `open({ anchor })` wins for that open.
959
+ - `container?` (`DocumentFragment | Element | null`): Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
960
+ - `finalFocus?` (`PopupFocusTarget`): Where focus goes when the menu closes while focus is inside it. `"trigger"` returns it to the trigger and `"none"` leaves it. A CSS selector, an element or a function picks a specific element.
961
+ - `keepMounted?` (`boolean`): Whether the menu stays in the page, hidden, while closed. For animation libraries that drive the exit from state.
962
+
963
+ ### MenuItem
964
+
965
+ - `value?` (`string`): Passed to `action` when the item runs.
966
+ - `label?` (`string`): The text typeahead matches, when the content isn't plain text. It isn't the accessible name: use `aria-label` for that.
967
+ - `disabled?` (`boolean`): Keeps the item focusable but stops it from running, as the menu pattern asks. Sets `data-disabled` and `aria-disabled`.
968
+ - `destructive?` (`boolean`): Styles the item as dangerous, like Delete. Sets `data-destructive`.
969
+ - `shortcut?` (`string`): A keyboard shortcut like `"mod+n"`. Shows `⌘N` on Apple platforms and `Ctrl+N` elsewhere, and sets `aria-keyshortcuts`.
970
+ - `closeOnSelect?` (`boolean`): Whether running the item closes the menu.
971
+ - `select event`: Runs when the item is selected, by pointer or keyboard, with the event that selected it, or a new `select` Event when no DOM event did. Call `event.preventDefault()` to keep the menu open and skip `action`. Return a Promise to mark the item pending until it settles, like `action`.
972
+
973
+ ### MenuCheckboxItem
974
+
975
+ - `value` (`string`): Identifies the item, in the menu's `checkedchange`.
976
+ - `label?` (`string`): The text typeahead matches, when the content isn't plain text. It isn't the accessible name: use `aria-label` for that.
977
+ - `disabled?` (`boolean`): Keeps the item focusable but stops it from toggling, as the menu pattern asks.
978
+ - `checked?` (`boolean`): Whether the item is checked. Pass it to control the item.
979
+ - `defaultChecked?` (`boolean`): Whether the item starts checked, when uncontrolled.
980
+ - `checkedchange event`: Runs when the item wants to toggle. `details.reason` says why, and `details.cancel()` stops it.
981
+ - `closeOnSelect?` (`boolean`): Whether toggling closes the menu.
982
+
983
+ ### MenuRadioGroup
984
+
985
+ - `value?` (`T | null`): The selected value. Pass it to control the group. `null` selects nothing.
986
+ - `defaultValue?` (`T | null`): The value selected at first, when uncontrolled.
987
+ - `valuechange event`: Runs when the selection wants to change. `details.reason` says why, and `details.cancel()` stops it.
988
+ - `name?` (`string`): Identifies the group in the menu's `valuechange`. Defaults to a generated id.
989
+
990
+ ### MenuRadioItem
991
+
992
+ - `value` (`string`): Selected in the group when the item is chosen.
993
+ - `label?` (`string`): The text typeahead matches, when the content isn't plain text. It isn't the accessible name: use `aria-label` for that.
994
+ - `disabled?` (`boolean`): Keeps the item focusable but stops it from being chosen, as the menu pattern asks.
995
+ - `closeOnSelect?` (`boolean`): Whether choosing the item closes the menu.
996
+
997
+ ### MenuItemIndicator
998
+
999
+ - `keepMounted?` (`boolean`): Whether the indicator stays in the page while its item is unchecked, so it can be styled or animated in both states.
1000
+
1001
+ ### MenuItemIcon
1002
+
1003
+ - `mirror?` (`boolean`): Flips the icon horizontally in right-to-left text, for icons that point a way, like arrows and chevrons. Icons that picture a thing, like a clock or a check, stay as they are.
1004
+
1005
+ ### MenuSub
1006
+
1007
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
1008
+ - `open?` (`boolean`): Whether the menu is open. Pass it to control the menu.
1009
+ - `defaultOpen?` (`boolean`): Whether the menu starts open, when uncontrolled.
1010
+ - `openchange event`: Runs when the menu wants to open or close. `details.reason` says why, and `details.cancel()` stops it.
1011
+ - `openchangecomplete event`: Runs once the menu has finished opening or closing, after any animation.
1012
+ - `loop?` (`boolean`): Whether arrow keys wrap from the last item to the first.
1013
+ - `delay?` (`number`): Milliseconds the pointer rests on a submenu trigger before its submenu opens.
1014
+
1015
+ ### MenuSubTrigger
1016
+
1017
+ - `disabled?` (`boolean`): Keeps the submenu trigger focusable but stops it from opening its submenu, as the menu pattern asks. Sets `data-disabled` and `aria-disabled`.
1018
+
1019
+ ### MenuSubContent
1020
+
1021
+ It takes the same attributes as `ui-menu-content`.
1022
+
1023
+ ### MenuGroup, MenuGroupLabel and MenuSeparator
1024
+
1025
+ These take no attributes of their own.
1026
+
1027
+ ### State
1028
+
1029
+ The same state shows as data attributes on each element.
1030
+
1031
+ `MenuTrigger`, `MenuContextTrigger` and `MenuSubTrigger`:
1032
+
1033
+ - `open` (`boolean`): Whether the menu is open from this trigger.
1034
+ - `disabled` (`boolean`): Whether the trigger's `disabled` prop is set, or the menubar it's in is disabled.
1035
+
1036
+ `MenuContent`:
1037
+
1038
+ - `open` (`boolean`): Whether the menu is open. False while it animates out.
1039
+ - `nested` (`boolean`): Whether this is a submenu.
1040
+ - `presentation` (`Presentation`): How the menu is shown.
1041
+ - `payload` (`Payload | undefined`): The payload of the last open, from the trigger that opened it or a controller, typed as the controller's `Payload`.
1042
+
1043
+ `MenuItem`:
1044
+
1045
+ - `highlighted` (`boolean`): Whether the item has keyboard focus or is under the pointer.
1046
+ - `disabled` (`boolean`): Whether the item can't be chosen.
1047
+ - `pending` (`boolean`): Whether its `onSelect` or `onAction` Promise is running.
1048
+ - `error` (`unknown`): Why its last action failed, until it runs again.
1049
+
1050
+ `MenuCheckboxItem` and `MenuRadioItem`:
1051
+
1052
+ - `highlighted` (`boolean`): Whether the item has keyboard focus or is under the pointer.
1053
+ - `disabled` (`boolean`): Whether the item can't be chosen.
1054
+ - `checked` (`boolean`): Whether the item is on: its `checked` prop when you pass one, otherwise `defaultChecked` until a press changes it.
1055
+
1056
+ `MenuRadioGroup`:
1057
+
1058
+ - `value` (`string | null`): The selected value, or null when nothing is selected.
1059
+
1060
+ `MenuItemIndicator`:
1061
+
1062
+ - `checked` (`boolean`): Whether its checkbox item is on, or its radio item is its group's value. While false, the indicator isn't shown unless `keepMounted` is set.
1063
+ - `disabled` (`boolean`): Whether the item can't be chosen.
1064
+
1065
+ `MenuItemIcon`:
1066
+
1067
+ - `mirror` (`boolean`): Whether it flips in right-to-left text.
1068
+
1069
+ ### Reasons
1070
+
1071
+ `event.detail.reason` says why the menu or a submenu opened or closed.
1072
+
1073
+ - `trigger-press` (`MouseEvent | KeyboardEvent | TouchEvent`): Click, tap, Enter or Space on the part that opens, closes or toggles. Arrow keys that open a menu or submenu count too. A tap on a combobox's input that opens a sheet reports its `touchend`.
1074
+ - `trigger-hover` (`PointerEvent`): The pointer rested on a trigger, or left it, with hover opening.
1075
+ - `item-press` (`MouseEvent | KeyboardEvent`): A member of a collection was chosen: a menu item, a tab, a radio or a checkbox item.
1076
+ - `outside-press` (`MouseEvent`): A press outside the layer.
1077
+ - `escape-key` (`KeyboardEvent`): Escape, or a submenu's close key (ArrowLeft in left-to-right layouts).
1078
+ - `close-press` (`MouseEvent | KeyboardEvent | SubmitEvent`): A Close part was pressed, or a form with `method="dialog"` was submitted.
1079
+ - `focus-out` (`KeyboardEvent | FocusEvent`): Focus left the layer, or Tab moved it out.
1080
+ - `window-blur` (`FocusEvent`): The window lost focus.
1081
+ - `submenu-switch` (`KeyboardEvent | PointerEvent`): Another submenu in the same menu, or another menu in the same menubar, took over.
1082
+ - `parent-close` (`none`): The parent layer closed.
1083
+ - `trigger-release` (`PointerEvent`): A press-and-hold open was released outside the popup.
1084
+ - `back` (`Event`): Going back: the Android back button or gesture, or Backspace in an empty command input, which returns to the previous page.
1085
+ - `shortcut` (`KeyboardEvent`): A keyboard shortcut, like a trigger's `shortcut`.
1086
+ - `swipe` (`TouchEvent | PointerEvent`): A swipe gesture: a finger, mouse or pen dragged it.
1087
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
1088
+
1089
+ For a payload change, it says what opened the menu or took it over.
1090
+
1091
+ - `trigger-press` (`MouseEvent | KeyboardEvent | TouchEvent`): Click, tap, Enter or Space on the part that opens, closes or toggles. Arrow keys that open a menu or submenu count too. A tap on a combobox's input that opens a sheet reports its `touchend`.
1092
+ - `trigger-hover` (`PointerEvent`): The pointer rested on a trigger, or left it, with hover opening.
1093
+ - `shortcut` (`KeyboardEvent`): A keyboard shortcut, like a trigger's `shortcut`.
1094
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
1095
+
1096
+ With `dismissible` false, the refused dismissals `dismissattempt` reports:
1097
+
1098
+ - `outside-press` (`MouseEvent`): A press outside the layer.
1099
+ - `escape-key` (`KeyboardEvent`): Escape, or a submenu's close key (ArrowLeft in left-to-right layouts).
1100
+ - `back` (`Event`): Going back: the Android back button or gesture, or Backspace in an empty command input, which returns to the previous page.
1101
+ - `swipe` (`TouchEvent | PointerEvent`): A swipe gesture: a finger, mouse or pen dragged it.
1102
+
1103
+ Checkbox items report why they toggled, in `checkedchange`:
1104
+
1105
+ - `item-press` (`MouseEvent | KeyboardEvent`): A member of a collection was chosen: a menu item, a tab, a radio or a checkbox item.
1106
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
1107
+
1108
+ Radio groups report why their value changed, in `valuechange`:
1109
+
1110
+ - `item-press` (`MouseEvent | KeyboardEvent`): A member of a collection was chosen: a menu item, a tab, a radio or a checkbox item.
1111
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
1112
+
1113
+ ### Controller
1114
+
1115
+ The element's `api` property has the same methods. The `open` property reads and sets whether the menu is
1116
+ open.
1117
+
1118
+ Every write method takes an optional `{ reason, event }`, passed to `openchange`. `open()` also takes `focus`: `"first"`, `"last"`, `"popup"` or `"none"`.
1119
+
1120
+ - `isOpen()` (`() => boolean`): Whether it's open. False as soon as it starts closing, while it still animates out.
1121
+ - `setOpen(open, options?)` (`(open: boolean, options?: PopupChangeOptions<MenuOpenChangeReason> | undefined) => boolean`): Opens or closes it. Returns false when the change was canceled or it was already open or closed.
1122
+ - `open(options?)` (`(options?: (PopupChangeOptions<MenuPayloadChangeReason> & { payload?: Payload | undefined; anchor?: Anchor | undefined; signal?: AbortSignal | undefined; } & MenuOpenOptions) | undefined) => Promise<...>`): Opens it, and resolves once it closes, with why, how and any return value. Resolves exactly once and never rejects: on the next close from any source, when `signal` aborts, or with reason `"unmount"` when the component leaves the page.
1123
+ - `close(options?)` (`(options?: (PopupChangeOptions<MenuOpenChangeReason> & { returnValue?: Result | undefined; }) | undefined) => boolean`): Closes it. `returnValue` is passed to the promise `open()` returned. Returns false when the close was canceled or it was already closed.
1124
+ - `toggle(options?)` (`(options?: PopupChangeOptions<MenuOpenChangeReason> | undefined) => boolean`): Opens it when it's closed, and closes it when it's open. Returns false when the change was canceled.
1125
+ - `getPayload()` (`() => Payload | undefined`): The payload of the last open, from a controller or the trigger that opened it.
1126
+ - `focus()` (`() => void`): Moves focus to the trigger that opened it last, or the first trigger.
1127
+
1128
+ ### Events
1129
+
1130
+ Events fire on their own element and don't bubble, with the new state in `event.detail.value` and the `reason` and `event` beside it. Calling `preventDefault()` cancels the change.
1131
+
1132
+ | Member | On | Description |
1133
+ | -------------------------- | ----------------------- | --------------------------------------------------------------------------------------- |
1134
+ | `open` property, attribute | `ui-menu` | Whether the menu is open. The attribute sets where it starts, and is live |
1135
+ | `api` property | `ui-menu` | The [controller](#controller) |
1136
+ | `items` property | `ui-menu` | A data menu, rendered from its `<template>` |
1137
+ | `openchange` event | `ui-menu` | `detail: { value: open, reason, event, source }` |
1138
+ | `openchangecomplete` event | `ui-menu` | `detail: { value: open }`, after the animation |
1139
+ | `payloadchange` event | `ui-menu` | `detail: { value: payload, reason, event, source }`, when the content's payload changes |
1140
+ | `action` event | `ui-menu` | `detail: { value }` for every plain item |
1141
+ | `dismissattempt` event | `ui-menu` | `detail: { reason, event }`, when a dismissal was refused |
1142
+ | `select` event | `ui-menu-item` | Before the menu closes. `preventDefault()` keeps it open |
1143
+ | `checkedchange` event | `ui-menu-checkbox-item` | `detail: { value: checked, reason, event, source }`. Listen on the item |
1144
+ | `valuechange` event | `ui-menu-radio-group` | `detail: { value, reason, event, source }`. Listen on the group |
1145
+ | `payload` property | `ui-menu-trigger` | Data for the content, read when the trigger connects |
1146
+ | `for` attribute | `ui-menu-trigger` | The `id` of a menu the trigger opens from outside it |
1147
+
1148
+ ## Accessibility
1149
+
1150
+ The menu follows the [WAI-ARIA menu button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/).
1151
+
1152
+ ### Tested Behavior
1153
+
1154
+ Every line below is an automated test.
1155
+
1156
+ **Trigger**
1157
+
1158
+ - Advertises its menu and reflects the open state.
1159
+ - Click opens the menu and moves focus into it.
1160
+ - Click on the open trigger closes the menu.
1161
+ - Enter opens the menu and focuses the first item.
1162
+ - Space opens the menu and focuses the first item.
1163
+ - ArrowDown opens the menu and focuses the first item.
1164
+ - ArrowUp opens the menu and focuses the last item.
1165
+ - A click on a trigger in a shadow root opens the menu, and it stays open. Vue and web components only.
1166
+
1167
+ **Keyboard navigation**
1168
+
1169
+ - ArrowDown and ArrowUp move between items, skipping separators and labels.
1170
+ - Disabled items are focusable.
1171
+ - Focus wraps at both ends.
1172
+ - Home and End move to the first and last items.
1173
+ - Typing a character moves to the next item that starts with it.
1174
+ - Typing several characters quickly matches the whole prefix.
1175
+
1176
+ **Activation**
1177
+
1178
+ - Enter on an item runs it, closes the menu and returns focus.
1179
+ - Clicking an item runs it and closes the menu.
1180
+ - Disabled items do nothing.
1181
+ - Checkbox items toggle and keep the menu open.
1182
+ - Radio items select one value and keep the menu open.
1183
+ - Hovering an item highlights it.
1184
+
1185
+ **Dismissal**
1186
+
1187
+ - Escape closes the menu and returns focus to the trigger.
1188
+ - Clicking outside closes the menu.
1189
+ - Escape while an input method is composing text leaves the menu open.
1190
+ - Pressing a scrollbar outside the menu leaves it open.
1191
+ - Tab closes the menu.
1192
+
1193
+ **Submenu**
1194
+
1195
+ - The submenu trigger advertises its menu.
1196
+ - ArrowRight opens the submenu and focuses its first item.
1197
+ - Enter opens the submenu and focuses its first item.
1198
+ - Space opens the submenu and focuses its first item.
1199
+ - ArrowLeft closes the submenu and returns focus to its trigger.
1200
+ - Nested submenus open and run their items.
1201
+ - Submenu keyboard navigation stays inside the submenu.
1202
+ - Keys pressed in quick succession are never lost.
1203
+ - Clicking outside closes every menu.
1204
+
1205
+ Runs in React, Vue and web components.
1206
+
1207
+ Screen readers hear the same thing in each tested framework, recorded by a virtual screen reader:
1208
+
1209
+ - Announcements while using the menu with a keyboard.
1210
+ - Item shortcuts are announced.
1211
+
1212
+ Runs in React, Vue and web components.