@hypoth-ui/cli 0.0.1 → 0.1.0

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 (375) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -115
  3. package/dist/{add-PDBC4JTE.js → add-V5PW73GC.js} +29 -17
  4. package/dist/{chunk-5LTQ2XVL.js → chunk-27CLUUVC.js} +0 -2
  5. package/dist/{chunk-YPKFYE45.js → chunk-NWIRSZUQ.js} +6 -13
  6. package/dist/{chunk-GJ6JOQ3Q.js → chunk-PBK72SJJ.js} +1 -1
  7. package/dist/{diff-BQEXG7HU.js → diff-776UATCA.js} +2 -2
  8. package/dist/index.js +5 -5
  9. package/dist/{init-7AZXYAPJ.js → init-GDU2PW7K.js} +10 -13
  10. package/dist/{list-X6ZLM2NQ.js → list-XDP5I537.js} +3 -3
  11. package/package.json +16 -12
  12. package/registry/components.json +1820 -206
  13. package/templates/accordion/index.tsx +266 -0
  14. package/templates/accordion/wc/accordion-content.ts +113 -0
  15. package/templates/accordion/wc/accordion-item.ts +111 -0
  16. package/templates/accordion/wc/accordion-trigger.ts +105 -0
  17. package/templates/accordion/wc/accordion.ts +213 -0
  18. package/templates/accordion/wc/index.ts +12 -0
  19. package/templates/alert/index.tsx +177 -0
  20. package/templates/alert/wc/alert.ts +167 -0
  21. package/templates/alert/wc/index.ts +1 -0
  22. package/templates/alert-dialog/index.tsx +360 -0
  23. package/templates/alert-dialog/wc/alert-dialog-action.ts +43 -0
  24. package/templates/alert-dialog/wc/alert-dialog-cancel.ts +43 -0
  25. package/templates/alert-dialog/wc/alert-dialog-content.ts +42 -0
  26. package/templates/alert-dialog/wc/alert-dialog-description.ts +34 -0
  27. package/templates/alert-dialog/wc/alert-dialog-footer.ts +25 -0
  28. package/templates/alert-dialog/wc/alert-dialog-header.ts +25 -0
  29. package/templates/alert-dialog/wc/alert-dialog-title.ts +34 -0
  30. package/templates/alert-dialog/wc/alert-dialog-trigger.ts +46 -0
  31. package/templates/alert-dialog/wc/alert-dialog.ts +302 -0
  32. package/templates/alert-dialog/wc/index.ts +13 -0
  33. package/templates/aspect-ratio/index.tsx +50 -0
  34. package/templates/aspect-ratio/wc/aspect-ratio.ts +78 -0
  35. package/templates/aspect-ratio/wc/index.ts +5 -0
  36. package/templates/avatar/avatar-group.tsx +88 -0
  37. package/templates/avatar/avatar.tsx +124 -0
  38. package/templates/avatar/index.tsx +33 -0
  39. package/templates/avatar/wc/avatar-group.ts +112 -0
  40. package/templates/avatar/wc/avatar.ts +184 -0
  41. package/templates/avatar/wc/index.ts +5 -0
  42. package/templates/badge/index.tsx +140 -0
  43. package/templates/badge/wc/badge.ts +119 -0
  44. package/templates/badge/wc/index.ts +9 -0
  45. package/templates/breadcrumb/index.tsx +157 -0
  46. package/templates/breadcrumb/wc/breadcrumb-item.ts +30 -0
  47. package/templates/breadcrumb/wc/breadcrumb-link.ts +70 -0
  48. package/templates/breadcrumb/wc/breadcrumb-list.ts +30 -0
  49. package/templates/breadcrumb/wc/breadcrumb-page.ts +32 -0
  50. package/templates/breadcrumb/wc/breadcrumb-separator.ts +31 -0
  51. package/templates/breadcrumb/wc/breadcrumb.ts +55 -0
  52. package/templates/breadcrumb/wc/index.ts +10 -0
  53. package/templates/button/button.tsx +119 -0
  54. package/templates/button/index.ts +1 -0
  55. package/templates/button/wc/button.ts +169 -0
  56. package/templates/calendar/index.tsx +149 -0
  57. package/templates/calendar/wc/calendar.ts +316 -0
  58. package/templates/calendar/wc/index.ts +4 -0
  59. package/templates/card/index.tsx +108 -0
  60. package/templates/card/wc/card-content.ts +25 -0
  61. package/templates/card/wc/card-footer.ts +25 -0
  62. package/templates/card/wc/card-header.ts +25 -0
  63. package/templates/card/wc/card.ts +43 -0
  64. package/templates/card/wc/index.ts +8 -0
  65. package/templates/checkbox/checkbox.tsx +85 -0
  66. package/templates/checkbox/wc/checkbox.ts +247 -0
  67. package/templates/collapsible/index.tsx +172 -0
  68. package/templates/collapsible/wc/collapsible-content.ts +97 -0
  69. package/templates/collapsible/wc/collapsible-trigger.ts +39 -0
  70. package/templates/collapsible/wc/collapsible.ts +143 -0
  71. package/templates/collapsible/wc/index.ts +7 -0
  72. package/templates/combobox/combobox-content.tsx +141 -0
  73. package/templates/combobox/combobox-context.ts +36 -0
  74. package/templates/combobox/combobox-empty.tsx +38 -0
  75. package/templates/combobox/combobox-input.tsx +159 -0
  76. package/templates/combobox/combobox-loading.tsx +38 -0
  77. package/templates/combobox/combobox-option.tsx +99 -0
  78. package/templates/combobox/combobox-root.tsx +207 -0
  79. package/templates/combobox/combobox-tag.tsx +62 -0
  80. package/templates/combobox/index.ts +62 -0
  81. package/templates/combobox/wc/combobox-content.ts +97 -0
  82. package/templates/combobox/wc/combobox-input.ts +134 -0
  83. package/templates/combobox/wc/combobox-option.ts +111 -0
  84. package/templates/combobox/wc/combobox-tag.ts +103 -0
  85. package/templates/combobox/wc/combobox.ts +981 -0
  86. package/templates/combobox/wc/index.ts +5 -0
  87. package/templates/command/index.tsx +279 -0
  88. package/templates/command/wc/command-empty.ts +24 -0
  89. package/templates/command/wc/command-group.ts +60 -0
  90. package/templates/command/wc/command-input.ts +136 -0
  91. package/templates/command/wc/command-item.ts +78 -0
  92. package/templates/command/wc/command-list.ts +103 -0
  93. package/templates/command/wc/command-loading.ts +24 -0
  94. package/templates/command/wc/command-separator.ts +23 -0
  95. package/templates/command/wc/command.ts +176 -0
  96. package/templates/context-menu/index.tsx +262 -0
  97. package/templates/context-menu/wc/context-menu-content.ts +41 -0
  98. package/templates/context-menu/wc/context-menu-item.ts +83 -0
  99. package/templates/context-menu/wc/context-menu-label.ts +30 -0
  100. package/templates/context-menu/wc/context-menu-separator.ts +28 -0
  101. package/templates/context-menu/wc/context-menu.ts +324 -0
  102. package/templates/context-menu/wc/index.ts +9 -0
  103. package/templates/data-table/index.tsx +263 -0
  104. package/templates/data-table/wc/data-table.ts +405 -0
  105. package/templates/data-table/wc/index.ts +10 -0
  106. package/templates/date-picker/date-picker-calendar.tsx +352 -0
  107. package/templates/date-picker/date-picker-content.tsx +121 -0
  108. package/templates/date-picker/date-picker-context.ts +46 -0
  109. package/templates/date-picker/date-picker-root.tsx +201 -0
  110. package/templates/date-picker/date-picker-trigger.tsx +95 -0
  111. package/templates/date-picker/index.ts +44 -0
  112. package/templates/date-picker/wc/date-picker-calendar.ts +457 -0
  113. package/templates/date-picker/wc/date-picker.ts +592 -0
  114. package/templates/date-picker/wc/date-utils.ts +467 -0
  115. package/templates/date-picker/wc/index.ts +3 -0
  116. package/templates/dialog/dialog-close.tsx +57 -0
  117. package/templates/dialog/dialog-content.tsx +106 -0
  118. package/templates/dialog/dialog-context.ts +24 -0
  119. package/templates/dialog/dialog-description.tsx +51 -0
  120. package/templates/dialog/dialog-root.tsx +104 -0
  121. package/templates/dialog/dialog-title.tsx +38 -0
  122. package/templates/dialog/dialog-trigger.tsx +94 -0
  123. package/templates/dialog/index.ts +52 -0
  124. package/templates/dialog/wc/dialog-content.ts +59 -0
  125. package/templates/dialog/wc/dialog-description.ts +58 -0
  126. package/templates/dialog/wc/dialog-title.ts +56 -0
  127. package/templates/dialog/wc/dialog.ts +411 -0
  128. package/templates/drawer/index.tsx +263 -0
  129. package/templates/drawer/wc/drawer-content.ts +150 -0
  130. package/templates/drawer/wc/drawer-description.ts +34 -0
  131. package/templates/drawer/wc/drawer-footer.ts +25 -0
  132. package/templates/drawer/wc/drawer-header.ts +25 -0
  133. package/templates/drawer/wc/drawer-title.ts +34 -0
  134. package/templates/drawer/wc/drawer.ts +348 -0
  135. package/templates/drawer/wc/index.ts +10 -0
  136. package/templates/dropdown-menu/index.tsx +454 -0
  137. package/templates/dropdown-menu/wc/dropdown-menu-checkbox-item.ts +93 -0
  138. package/templates/dropdown-menu/wc/dropdown-menu-content.ts +43 -0
  139. package/templates/dropdown-menu/wc/dropdown-menu-item.ts +85 -0
  140. package/templates/dropdown-menu/wc/dropdown-menu-label.ts +31 -0
  141. package/templates/dropdown-menu/wc/dropdown-menu-radio-group.ts +80 -0
  142. package/templates/dropdown-menu/wc/dropdown-menu-radio-item.ts +101 -0
  143. package/templates/dropdown-menu/wc/dropdown-menu-separator.ts +28 -0
  144. package/templates/dropdown-menu/wc/dropdown-menu.ts +358 -0
  145. package/templates/dropdown-menu/wc/index.ts +12 -0
  146. package/templates/field/field-description.tsx +39 -0
  147. package/templates/field/field-error.tsx +37 -0
  148. package/templates/field/field.tsx +46 -0
  149. package/templates/field/index.ts +4 -0
  150. package/templates/field/label.tsx +40 -0
  151. package/templates/field/wc/field-description.ts +42 -0
  152. package/templates/field/wc/field-error.ts +46 -0
  153. package/templates/field/wc/field.ts +210 -0
  154. package/templates/field/wc/label.ts +54 -0
  155. package/templates/file-upload/file-upload-context.ts +26 -0
  156. package/templates/file-upload/file-upload-dropzone.tsx +111 -0
  157. package/templates/file-upload/file-upload-input.tsx +86 -0
  158. package/templates/file-upload/file-upload-item.tsx +105 -0
  159. package/templates/file-upload/file-upload-root.tsx +115 -0
  160. package/templates/file-upload/index.ts +50 -0
  161. package/templates/file-upload/wc/file-upload.ts +380 -0
  162. package/templates/file-upload/wc/index.ts +1 -0
  163. package/templates/hover-card/index.tsx +203 -0
  164. package/templates/hover-card/wc/hover-card-content.ts +50 -0
  165. package/templates/hover-card/wc/hover-card.ts +382 -0
  166. package/templates/hover-card/wc/index.ts +6 -0
  167. package/templates/icon/icon.tsx +76 -0
  168. package/templates/icon/wc/icon-adapter.ts +108 -0
  169. package/templates/icon/wc/icon.ts +161 -0
  170. package/templates/input/input.tsx +130 -0
  171. package/templates/input/wc/input.ts +216 -0
  172. package/templates/layout/app-shell.tsx +177 -0
  173. package/templates/layout/box.tsx +53 -0
  174. package/templates/layout/center.tsx +42 -0
  175. package/templates/layout/container.tsx +43 -0
  176. package/templates/layout/flow.tsx +83 -0
  177. package/templates/layout/grid.tsx +79 -0
  178. package/templates/layout/index.ts +33 -0
  179. package/templates/layout/inline.tsx +16 -0
  180. package/templates/layout/page.tsx +43 -0
  181. package/templates/layout/section.tsx +39 -0
  182. package/templates/layout/spacer.tsx +30 -0
  183. package/templates/layout/split.tsx +47 -0
  184. package/templates/layout/stack.tsx +16 -0
  185. package/templates/layout/wc/app-shell.ts +58 -0
  186. package/templates/layout/wc/box.ts +117 -0
  187. package/templates/layout/wc/center.ts +78 -0
  188. package/templates/layout/wc/container.ts +77 -0
  189. package/templates/layout/wc/flow.ts +149 -0
  190. package/templates/layout/wc/footer.ts +57 -0
  191. package/templates/layout/wc/grid.ts +142 -0
  192. package/templates/layout/wc/header.ts +57 -0
  193. package/templates/layout/wc/index.ts +41 -0
  194. package/templates/layout/wc/main.ts +46 -0
  195. package/templates/layout/wc/page.ts +81 -0
  196. package/templates/layout/wc/section.ts +65 -0
  197. package/templates/layout/wc/spacer.ts +77 -0
  198. package/templates/layout/wc/split.ts +94 -0
  199. package/templates/layout/wc/wrap.ts +93 -0
  200. package/templates/layout/wrap.tsx +46 -0
  201. package/templates/link/link.tsx +109 -0
  202. package/templates/link/wc/link.ts +124 -0
  203. package/templates/list/index.tsx +55 -0
  204. package/templates/list/list-item.tsx +117 -0
  205. package/templates/list/list.tsx +115 -0
  206. package/templates/list/wc/index.ts +5 -0
  207. package/templates/list/wc/list-item.ts +127 -0
  208. package/templates/list/wc/list.ts +114 -0
  209. package/templates/menu/index.ts +49 -0
  210. package/templates/menu/menu-content.tsx +109 -0
  211. package/templates/menu/menu-context.ts +17 -0
  212. package/templates/menu/menu-item.tsx +108 -0
  213. package/templates/menu/menu-label.tsx +32 -0
  214. package/templates/menu/menu-root.tsx +108 -0
  215. package/templates/menu/menu-separator.tsx +24 -0
  216. package/templates/menu/menu-trigger.tsx +104 -0
  217. package/templates/menu/wc/menu-content.ts +67 -0
  218. package/templates/menu/wc/menu-item.ts +109 -0
  219. package/templates/menu/wc/menu.ts +449 -0
  220. package/templates/navigation-menu/index.tsx +328 -0
  221. package/templates/navigation-menu/wc/index.ts +12 -0
  222. package/templates/navigation-menu/wc/navigation-menu-content.ts +30 -0
  223. package/templates/navigation-menu/wc/navigation-menu-indicator.ts +30 -0
  224. package/templates/navigation-menu/wc/navigation-menu-item.ts +60 -0
  225. package/templates/navigation-menu/wc/navigation-menu-link.ts +97 -0
  226. package/templates/navigation-menu/wc/navigation-menu-list.ts +30 -0
  227. package/templates/navigation-menu/wc/navigation-menu-trigger.ts +110 -0
  228. package/templates/navigation-menu/wc/navigation-menu-viewport.ts +85 -0
  229. package/templates/navigation-menu/wc/navigation-menu.ts +272 -0
  230. package/templates/number-input/index.ts +46 -0
  231. package/templates/number-input/number-input-context.ts +38 -0
  232. package/templates/number-input/number-input-decrement.tsx +53 -0
  233. package/templates/number-input/number-input-field.tsx +93 -0
  234. package/templates/number-input/number-input-increment.tsx +53 -0
  235. package/templates/number-input/number-input-root.tsx +137 -0
  236. package/templates/number-input/wc/index.ts +1 -0
  237. package/templates/number-input/wc/number-input.ts +283 -0
  238. package/templates/pagination/index.tsx +198 -0
  239. package/templates/pagination/wc/index.ts +11 -0
  240. package/templates/pagination/wc/pagination-content.ts +30 -0
  241. package/templates/pagination/wc/pagination-ellipsis.ts +28 -0
  242. package/templates/pagination/wc/pagination-item.ts +30 -0
  243. package/templates/pagination/wc/pagination-link.ts +76 -0
  244. package/templates/pagination/wc/pagination-next.ts +69 -0
  245. package/templates/pagination/wc/pagination-previous.ts +69 -0
  246. package/templates/pagination/wc/pagination.ts +156 -0
  247. package/templates/pin-input/index.ts +39 -0
  248. package/templates/pin-input/pin-input-context.ts +30 -0
  249. package/templates/pin-input/pin-input-field.tsx +186 -0
  250. package/templates/pin-input/pin-input-root.tsx +120 -0
  251. package/templates/pin-input/wc/index.ts +1 -0
  252. package/templates/pin-input/wc/pin-input.ts +259 -0
  253. package/templates/popover/popover.tsx +121 -0
  254. package/templates/popover/wc/popover-content.ts +66 -0
  255. package/templates/popover/wc/popover.ts +343 -0
  256. package/templates/progress/index.tsx +117 -0
  257. package/templates/progress/wc/index.ts +4 -0
  258. package/templates/progress/wc/progress.ts +174 -0
  259. package/templates/radio/radio.tsx +43 -0
  260. package/templates/radio/wc/radio-group.ts +261 -0
  261. package/templates/radio/wc/radio.ts +145 -0
  262. package/templates/scroll-area/index.tsx +144 -0
  263. package/templates/scroll-area/wc/index.ts +8 -0
  264. package/templates/scroll-area/wc/scroll-area-scrollbar.ts +143 -0
  265. package/templates/scroll-area/wc/scroll-area-thumb.ts +225 -0
  266. package/templates/scroll-area/wc/scroll-area-viewport.ts +120 -0
  267. package/templates/scroll-area/wc/scroll-area.ts +63 -0
  268. package/templates/select/index.ts +57 -0
  269. package/templates/select/select-content.tsx +243 -0
  270. package/templates/select/select-context.ts +30 -0
  271. package/templates/select/select-group.tsx +53 -0
  272. package/templates/select/select-label.tsx +34 -0
  273. package/templates/select/select-option.tsx +97 -0
  274. package/templates/select/select-root.tsx +153 -0
  275. package/templates/select/select-separator.tsx +27 -0
  276. package/templates/select/select-trigger.tsx +112 -0
  277. package/templates/select/select-value.tsx +48 -0
  278. package/templates/select/wc/index.ts +6 -0
  279. package/templates/select/wc/select-content.ts +89 -0
  280. package/templates/select/wc/select-group.ts +82 -0
  281. package/templates/select/wc/select-label.ts +49 -0
  282. package/templates/select/wc/select-option.ts +111 -0
  283. package/templates/select/wc/select-trigger.ts +101 -0
  284. package/templates/select/wc/select.ts +840 -0
  285. package/templates/separator/index.tsx +49 -0
  286. package/templates/separator/wc/index.ts +5 -0
  287. package/templates/separator/wc/separator.ts +60 -0
  288. package/templates/sheet/index.tsx +291 -0
  289. package/templates/sheet/wc/index.ts +12 -0
  290. package/templates/sheet/wc/sheet-close.ts +43 -0
  291. package/templates/sheet/wc/sheet-content.ts +47 -0
  292. package/templates/sheet/wc/sheet-description.ts +34 -0
  293. package/templates/sheet/wc/sheet-footer.ts +25 -0
  294. package/templates/sheet/wc/sheet-header.ts +25 -0
  295. package/templates/sheet/wc/sheet-overlay.ts +23 -0
  296. package/templates/sheet/wc/sheet-title.ts +34 -0
  297. package/templates/sheet/wc/sheet.ts +336 -0
  298. package/templates/skeleton/index.tsx +131 -0
  299. package/templates/skeleton/wc/index.ts +10 -0
  300. package/templates/skeleton/wc/skeleton.ts +107 -0
  301. package/templates/slider/index.ts +41 -0
  302. package/templates/slider/slider-context.ts +36 -0
  303. package/templates/slider/slider-range.tsx +59 -0
  304. package/templates/slider/slider-root.tsx +166 -0
  305. package/templates/slider/slider-thumb.tsx +213 -0
  306. package/templates/slider/slider-track.tsx +113 -0
  307. package/templates/slider/wc/index.ts +1 -0
  308. package/templates/slider/wc/slider.ts +465 -0
  309. package/templates/spinner/spinner.tsx +64 -0
  310. package/templates/spinner/wc/spinner.ts +70 -0
  311. package/templates/stepper/index.tsx +230 -0
  312. package/templates/stepper/wc/index.ts +12 -0
  313. package/templates/stepper/wc/stepper-content.ts +30 -0
  314. package/templates/stepper/wc/stepper-description.ts +25 -0
  315. package/templates/stepper/wc/stepper-indicator.ts +30 -0
  316. package/templates/stepper/wc/stepper-item.ts +55 -0
  317. package/templates/stepper/wc/stepper-separator.ts +29 -0
  318. package/templates/stepper/wc/stepper-title.ts +25 -0
  319. package/templates/stepper/wc/stepper-trigger.ts +67 -0
  320. package/templates/stepper/wc/stepper.ts +164 -0
  321. package/templates/switch/switch.tsx +90 -0
  322. package/templates/switch/wc/switch.ts +228 -0
  323. package/templates/table/body.tsx +21 -0
  324. package/templates/table/cell.tsx +44 -0
  325. package/templates/table/head.tsx +112 -0
  326. package/templates/table/header.tsx +21 -0
  327. package/templates/table/index.tsx +93 -0
  328. package/templates/table/root.tsx +82 -0
  329. package/templates/table/row.tsx +36 -0
  330. package/templates/table/wc/index.ts +9 -0
  331. package/templates/table/wc/table-body.ts +32 -0
  332. package/templates/table/wc/table-cell.ts +58 -0
  333. package/templates/table/wc/table-head.ts +129 -0
  334. package/templates/table/wc/table-header.ts +32 -0
  335. package/templates/table/wc/table-row.ts +50 -0
  336. package/templates/table/wc/table.ts +93 -0
  337. package/templates/tabs/index.tsx +222 -0
  338. package/templates/tabs/wc/index.ts +8 -0
  339. package/templates/tabs/wc/tabs-content.ts +82 -0
  340. package/templates/tabs/wc/tabs-list.ts +56 -0
  341. package/templates/tabs/wc/tabs-trigger.ts +136 -0
  342. package/templates/tabs/wc/tabs.ts +202 -0
  343. package/templates/tag/index.tsx +186 -0
  344. package/templates/tag/wc/index.ts +4 -0
  345. package/templates/tag/wc/tag.ts +166 -0
  346. package/templates/text/text.tsx +100 -0
  347. package/templates/text/wc/text.ts +94 -0
  348. package/templates/textarea/textarea.tsx +134 -0
  349. package/templates/textarea/wc/textarea.ts +280 -0
  350. package/templates/time-picker/index.ts +42 -0
  351. package/templates/time-picker/time-picker-context.ts +28 -0
  352. package/templates/time-picker/time-picker-root.tsx +113 -0
  353. package/templates/time-picker/time-picker-segment.tsx +91 -0
  354. package/templates/time-picker/wc/index.ts +1 -0
  355. package/templates/time-picker/wc/time-picker.ts +221 -0
  356. package/templates/toast/index.tsx +71 -0
  357. package/templates/toast/provider.tsx +228 -0
  358. package/templates/toast/toast.tsx +142 -0
  359. package/templates/toast/use-toast.ts +89 -0
  360. package/templates/toast/wc/index.ts +15 -0
  361. package/templates/toast/wc/toast-controller.ts +282 -0
  362. package/templates/toast/wc/toast-provider.ts +161 -0
  363. package/templates/toast/wc/toast.ts +165 -0
  364. package/templates/tooltip/tooltip.tsx +62 -0
  365. package/templates/tooltip/wc/tooltip-content.ts +64 -0
  366. package/templates/tooltip/wc/tooltip.ts +289 -0
  367. package/templates/tree/index.tsx +60 -0
  368. package/templates/tree/tree-item.tsx +131 -0
  369. package/templates/tree/tree.tsx +138 -0
  370. package/templates/tree/wc/index.ts +11 -0
  371. package/templates/tree/wc/tree-item.ts +273 -0
  372. package/templates/tree/wc/tree-utils.ts +143 -0
  373. package/templates/tree/wc/tree.ts +139 -0
  374. package/templates/visually-hidden/visually-hidden.tsx +45 -0
  375. package/templates/visually-hidden/wc/visually-hidden.ts +64 -0
@@ -0,0 +1,67 @@
1
+ import { html } from "lit";
2
+ import { property } from "lit/decorators.js";
3
+ import { DSElement } from "../../base/ds-element.js";
4
+ import { define } from "../../registry/define.js";
5
+
6
+ export type MenuContentState = "open" | "closed";
7
+
8
+ /**
9
+ * Menu content container with role="menu".
10
+ *
11
+ * @element ds-menu-content
12
+ *
13
+ * @slot - Menu items (ds-menu-item elements)
14
+ *
15
+ * @attr {string} data-state - Animation state ("open" or "closed")
16
+ *
17
+ * @example
18
+ * ```html
19
+ * <ds-menu>
20
+ * <button slot="trigger">Actions</button>
21
+ * <ds-menu-content>
22
+ * <ds-menu-item>Edit</ds-menu-item>
23
+ * <ds-menu-item>Delete</ds-menu-item>
24
+ * </ds-menu-content>
25
+ * </ds-menu>
26
+ * ```
27
+ */
28
+ export class DsMenuContent extends DSElement {
29
+ /** Unique ID for ARIA association */
30
+ @property({ type: String, reflect: true })
31
+ override id = "";
32
+
33
+ /** Animation state (open or closed) - set by parent ds-menu */
34
+ @property({ type: String, reflect: true, attribute: "data-state" })
35
+ dataState: MenuContentState = "closed";
36
+
37
+ override connectedCallback(): void {
38
+ super.connectedCallback();
39
+
40
+ // Generate ID if not set
41
+ if (!this.id) {
42
+ this.id = `menu-content-${crypto.randomUUID().slice(0, 8)}`;
43
+ }
44
+
45
+ // Set ARIA role for menu
46
+ this.setAttribute("role", "menu");
47
+
48
+ // Hidden by default (parent menu controls visibility)
49
+ this.setAttribute("hidden", "");
50
+ }
51
+
52
+ override render() {
53
+ return html`
54
+ <div class="ds-menu-content" part="container">
55
+ <slot></slot>
56
+ </div>
57
+ `;
58
+ }
59
+ }
60
+
61
+ define("ds-menu-content", DsMenuContent);
62
+
63
+ declare global {
64
+ interface HTMLElementTagNameMap {
65
+ "ds-menu-content": DsMenuContent;
66
+ }
67
+ }
@@ -0,0 +1,109 @@
1
+ import { html } from "lit";
2
+ import { property } from "lit/decorators.js";
3
+ import { DSElement } from "../../base/ds-element.js";
4
+ import { emitEvent } from "../../events/emit.js";
5
+ import { define } from "../../registry/define.js";
6
+
7
+ /**
8
+ * Menu item with role="menuitem".
9
+ *
10
+ * @element ds-menu-item
11
+ *
12
+ * @slot - Item content (text, icon, etc.)
13
+ *
14
+ * @fires ds:select - Fired when item is selected via click, Enter, or Space
15
+ *
16
+ * @example
17
+ * ```html
18
+ * <ds-menu-item value="edit">Edit</ds-menu-item>
19
+ * <ds-menu-item value="delete" disabled>Delete</ds-menu-item>
20
+ * ```
21
+ */
22
+ export class DsMenuItem extends DSElement {
23
+ /** Whether the item is disabled */
24
+ @property({ type: Boolean, reflect: true })
25
+ disabled = false;
26
+
27
+ /** Value associated with this item (used in ds:select event) */
28
+ @property({ type: String })
29
+ value = "";
30
+
31
+ override connectedCallback(): void {
32
+ super.connectedCallback();
33
+
34
+ // Set ARIA role
35
+ this.setAttribute("role", "menuitem");
36
+
37
+ // Set tabIndex for roving focus (will be managed by parent)
38
+ this.tabIndex = -1;
39
+
40
+ // Update aria-disabled
41
+ this.updateAriaDisabled();
42
+
43
+ // Listen for activation
44
+ this.addEventListener("click", this.handleClick);
45
+ this.addEventListener("keydown", this.handleKeyDown);
46
+ }
47
+
48
+ override disconnectedCallback(): void {
49
+ super.disconnectedCallback();
50
+ this.removeEventListener("click", this.handleClick);
51
+ this.removeEventListener("keydown", this.handleKeyDown);
52
+ }
53
+
54
+ private updateAriaDisabled(): void {
55
+ if (this.disabled) {
56
+ this.setAttribute("aria-disabled", "true");
57
+ } else {
58
+ this.removeAttribute("aria-disabled");
59
+ }
60
+ }
61
+
62
+ private handleClick = (): void => {
63
+ this.select();
64
+ };
65
+
66
+ private handleKeyDown = (event: KeyboardEvent): void => {
67
+ if (event.key === "Enter" || event.key === " ") {
68
+ event.preventDefault();
69
+ this.select();
70
+ }
71
+ };
72
+
73
+ /**
74
+ * Triggers selection of this item.
75
+ * Emits ds:select event and notifies parent menu to close.
76
+ */
77
+ public select(): void {
78
+ if (this.disabled) return;
79
+
80
+ // Get value from attribute or text content
81
+ const value = this.value || this.textContent?.trim() || "";
82
+
83
+ emitEvent(this, "select", { detail: { value } });
84
+ }
85
+
86
+ override updated(changedProperties: Map<string, unknown>): void {
87
+ super.updated(changedProperties);
88
+
89
+ if (changedProperties.has("disabled")) {
90
+ this.updateAriaDisabled();
91
+ }
92
+ }
93
+
94
+ override render() {
95
+ return html`
96
+ <div class="ds-menu-item" part="container">
97
+ <slot></slot>
98
+ </div>
99
+ `;
100
+ }
101
+ }
102
+
103
+ define("ds-menu-item", DsMenuItem);
104
+
105
+ declare global {
106
+ interface HTMLElementTagNameMap {
107
+ "ds-menu-item": DsMenuItem;
108
+ }
109
+ }
@@ -0,0 +1,449 @@
1
+ import {
2
+ type AnchorPosition,
3
+ type DismissableLayer,
4
+ type Placement,
5
+ type Presence,
6
+ type RovingFocus,
7
+ type TypeAhead,
8
+ createAnchorPosition,
9
+ createDismissableLayer,
10
+ createPresence,
11
+ createRovingFocus,
12
+ createTypeAhead,
13
+ prefersReducedMotion,
14
+ } from "@hypoth-ui/primitives-dom";
15
+ import { html } from "lit";
16
+ import { property } from "lit/decorators.js";
17
+ import { DSElement } from "../../base/ds-element.js";
18
+ import { StandardEvents, emitEvent } from "../../events/emit.js";
19
+ import { define } from "../../registry/define.js";
20
+
21
+ // Import child components to ensure they're registered
22
+ import type { DsMenuContent } from "./menu-content.js";
23
+ import "./menu-content.js";
24
+ import "./menu-item.js";
25
+
26
+ /**
27
+ * Menu component with roving focus and type-ahead.
28
+ *
29
+ * Implements WAI-ARIA Menu Button pattern with:
30
+ * - Arrow key navigation between items
31
+ * - Type-ahead search to jump to items
32
+ * - Enter/Space/Click to select items
33
+ * - Escape to close
34
+ *
35
+ * @element ds-menu
36
+ *
37
+ * @slot trigger - Button that opens the menu
38
+ * @slot - Menu content (ds-menu-content with ds-menu-item children)
39
+ *
40
+ * @fires ds:open - Fired when menu opens
41
+ * @fires ds:close - Fired when menu closes
42
+ * @fires ds:select - Fired when a menu item is selected (bubbles from ds-menu-item)
43
+ *
44
+ * @example
45
+ * ```html
46
+ * <ds-menu>
47
+ * <button slot="trigger">Actions</button>
48
+ * <ds-menu-content>
49
+ * <ds-menu-item value="edit">Edit</ds-menu-item>
50
+ * <ds-menu-item value="delete">Delete</ds-menu-item>
51
+ * </ds-menu-content>
52
+ * </ds-menu>
53
+ * ```
54
+ */
55
+ export class DsMenu extends DSElement {
56
+ /** Whether the menu is open */
57
+ @property({ type: Boolean, reflect: true })
58
+ open = false;
59
+
60
+ /** Placement relative to trigger */
61
+ @property({ type: String, reflect: true })
62
+ placement: Placement = "bottom-start";
63
+
64
+ /** Offset distance from trigger in pixels */
65
+ @property({ type: Number })
66
+ offset = 8;
67
+
68
+ /** Whether to flip placement when near viewport edge */
69
+ @property({ type: Boolean })
70
+ flip = true;
71
+
72
+ /** Whether to animate open/close transitions */
73
+ @property({ type: Boolean })
74
+ animated = true;
75
+
76
+ private anchorPosition: AnchorPosition | null = null;
77
+ private dismissLayer: DismissableLayer | null = null;
78
+ private presence: Presence | null = null;
79
+ private rovingFocus: RovingFocus | null = null;
80
+ private typeAhead: TypeAhead | null = null;
81
+ private triggerElement: HTMLElement | null = null;
82
+ private resizeObserver: ResizeObserver | null = null;
83
+ private scrollHandler: (() => void) | null = null;
84
+ private focusFirstOnOpen: "first" | "last" | null = null;
85
+
86
+ override connectedCallback(): void {
87
+ super.connectedCallback();
88
+
89
+ // Listen for trigger interactions
90
+ this.addEventListener("click", this.handleTriggerClick);
91
+ this.addEventListener("keydown", this.handleTriggerKeyDown);
92
+
93
+ // Listen for item selection (bubbled from ds-menu-item)
94
+ this.addEventListener("ds:select", this.handleItemSelect);
95
+
96
+ // Setup after first render
97
+ this.updateComplete.then(() => {
98
+ this.setupTriggerAccessibility();
99
+ });
100
+ }
101
+
102
+ override disconnectedCallback(): void {
103
+ super.disconnectedCallback();
104
+ this.removeEventListener("click", this.handleTriggerClick);
105
+ this.removeEventListener("keydown", this.handleTriggerKeyDown);
106
+ this.removeEventListener("ds:select", this.handleItemSelect);
107
+ this.cleanup();
108
+ }
109
+
110
+ /**
111
+ * Opens the menu.
112
+ */
113
+ public show(): void {
114
+ if (this.open) return;
115
+
116
+ this.open = true;
117
+ emitEvent(this, StandardEvents.OPEN);
118
+ }
119
+
120
+ /**
121
+ * Closes the menu.
122
+ */
123
+ public close(): void {
124
+ if (!this.open) return;
125
+
126
+ const content = this.querySelector("ds-menu-content") as DsMenuContent | null;
127
+
128
+ // If animated, use presence for exit animation
129
+ if (this.animated && content && !prefersReducedMotion()) {
130
+ // Cleanup dismiss layer so it doesn't re-trigger
131
+ this.dismissLayer?.deactivate();
132
+ this.dismissLayer = null;
133
+
134
+ // Create presence for exit animation
135
+ this.presence = createPresence({
136
+ onExitComplete: () => {
137
+ this.completeClose();
138
+ },
139
+ });
140
+ this.presence.hide(content);
141
+ } else {
142
+ // No animation - close immediately
143
+ this.cleanup();
144
+ this.open = false;
145
+ emitEvent(this, StandardEvents.CLOSE);
146
+
147
+ // Return focus to trigger
148
+ this.triggerElement?.focus();
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Completes the close after exit animation.
154
+ */
155
+ private completeClose(): void {
156
+ this.cleanup();
157
+ this.open = false;
158
+ emitEvent(this, StandardEvents.CLOSE);
159
+
160
+ // Return focus to trigger
161
+ this.triggerElement?.focus();
162
+ }
163
+
164
+ /**
165
+ * Toggles the menu open/closed state.
166
+ */
167
+ public toggle(): void {
168
+ if (this.open) {
169
+ this.close();
170
+ } else {
171
+ this.show();
172
+ }
173
+ }
174
+
175
+ private handleTriggerClick = (event: Event): void => {
176
+ const target = event.target as HTMLElement;
177
+ const trigger = target.closest('[slot="trigger"]');
178
+
179
+ if (trigger && this.contains(trigger)) {
180
+ event.preventDefault();
181
+ this.triggerElement = trigger as HTMLElement;
182
+ this.focusFirstOnOpen = "first";
183
+ this.toggle();
184
+ }
185
+ };
186
+
187
+ private handleTriggerKeyDown = (event: KeyboardEvent): void => {
188
+ const target = event.target as HTMLElement;
189
+ const trigger = target.closest('[slot="trigger"]');
190
+
191
+ if (!trigger || !this.contains(trigger)) return;
192
+
193
+ this.triggerElement = trigger as HTMLElement;
194
+
195
+ switch (event.key) {
196
+ case "Enter":
197
+ case " ":
198
+ event.preventDefault();
199
+ this.focusFirstOnOpen = "first";
200
+ this.toggle();
201
+ break;
202
+ case "ArrowDown":
203
+ event.preventDefault();
204
+ this.focusFirstOnOpen = "first";
205
+ if (!this.open) this.show();
206
+ break;
207
+ case "ArrowUp":
208
+ event.preventDefault();
209
+ this.focusFirstOnOpen = "last";
210
+ if (!this.open) this.show();
211
+ break;
212
+ }
213
+ };
214
+
215
+ private handleItemSelect = (_event: Event): void => {
216
+ // Item was selected, close the menu
217
+ // Don't stop propagation - let the event bubble to consumers
218
+ this.close();
219
+ };
220
+
221
+ private handleDismiss = (): void => {
222
+ this.close();
223
+ };
224
+
225
+ private setupTriggerAccessibility(): void {
226
+ const trigger = this.querySelector('[slot="trigger"]');
227
+ const content = this.querySelector("ds-menu-content");
228
+
229
+ if (trigger && content) {
230
+ // Store reference
231
+ this.triggerElement = trigger as HTMLElement;
232
+
233
+ // Set ARIA attributes on trigger
234
+ trigger.setAttribute("aria-haspopup", "menu");
235
+ trigger.setAttribute("aria-expanded", String(this.open));
236
+ trigger.setAttribute("aria-controls", content.id);
237
+ }
238
+ }
239
+
240
+ private updateTriggerAria(): void {
241
+ const trigger = this.querySelector('[slot="trigger"]');
242
+ if (trigger) {
243
+ trigger.setAttribute("aria-expanded", String(this.open));
244
+ }
245
+ }
246
+
247
+ private setupPositioning(): void {
248
+ const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
249
+ const content = this.querySelector("ds-menu-content") as HTMLElement | null;
250
+
251
+ if (!trigger || !content) return;
252
+
253
+ // Setup anchor positioning
254
+ this.anchorPosition = createAnchorPosition({
255
+ anchor: trigger,
256
+ floating: content,
257
+ placement: this.placement,
258
+ offset: this.offset,
259
+ flip: this.flip,
260
+ onPositionChange: (pos) => {
261
+ content.setAttribute("data-placement", pos.placement);
262
+ },
263
+ });
264
+
265
+ // Setup resize observer for repositioning
266
+ this.resizeObserver = new ResizeObserver(() => {
267
+ this.anchorPosition?.update();
268
+ });
269
+ this.resizeObserver.observe(trigger);
270
+ this.resizeObserver.observe(content);
271
+
272
+ // Setup scroll handler for repositioning
273
+ this.scrollHandler = () => {
274
+ this.anchorPosition?.update();
275
+ };
276
+ window.addEventListener("scroll", this.scrollHandler, { passive: true });
277
+ window.addEventListener("resize", this.scrollHandler, { passive: true });
278
+ }
279
+
280
+ private setupDismissLayer(): void {
281
+ const content = this.querySelector("ds-menu-content") as HTMLElement | null;
282
+ const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
283
+
284
+ if (!content) return;
285
+
286
+ this.dismissLayer = createDismissableLayer({
287
+ container: content,
288
+ excludeElements: trigger ? [trigger] : [],
289
+ onDismiss: this.handleDismiss,
290
+ closeOnEscape: true,
291
+ closeOnOutsideClick: true,
292
+ });
293
+ this.dismissLayer.activate();
294
+ }
295
+
296
+ private setupRovingFocus(): void {
297
+ const content = this.querySelector("ds-menu-content") as HTMLElement | null;
298
+
299
+ if (!content) return;
300
+
301
+ this.rovingFocus = createRovingFocus({
302
+ container: content,
303
+ selector: "ds-menu-item:not([disabled])",
304
+ direction: "vertical",
305
+ loop: true,
306
+ skipDisabled: true,
307
+ });
308
+ }
309
+
310
+ private setupTypeAhead(): void {
311
+ const content = this.querySelector("ds-menu-content") as HTMLElement | null;
312
+
313
+ if (!content) return;
314
+
315
+ this.typeAhead = createTypeAhead({
316
+ items: () =>
317
+ Array.from(content.querySelectorAll<HTMLElement>("ds-menu-item:not([disabled])")),
318
+ getText: (item) => item.textContent?.trim() || "",
319
+ onMatch: (_item, index) => {
320
+ this.rovingFocus?.setFocusedIndex(index);
321
+ },
322
+ });
323
+
324
+ // Wire type-ahead to keydown events
325
+ content.addEventListener("keydown", this.handleTypeAheadKeyDown);
326
+ }
327
+
328
+ private handleTypeAheadKeyDown = (event: KeyboardEvent): void => {
329
+ this.typeAhead?.handleKeyDown(event);
330
+ };
331
+
332
+ private focusInitialItem(): void {
333
+ const content = this.querySelector("ds-menu-content");
334
+ if (!content) return;
335
+
336
+ const items = content.querySelectorAll<HTMLElement>("ds-menu-item:not([disabled])");
337
+ if (items.length === 0) return;
338
+
339
+ if (this.focusFirstOnOpen === "last") {
340
+ // Focus last item (ArrowUp on trigger)
341
+ const lastIndex = items.length - 1;
342
+ this.rovingFocus?.setFocusedIndex(lastIndex);
343
+ } else {
344
+ // Focus first item (default)
345
+ this.rovingFocus?.setFocusedIndex(0);
346
+ }
347
+
348
+ this.focusFirstOnOpen = null;
349
+ }
350
+
351
+ private cleanup(): void {
352
+ const content = this.querySelector("ds-menu-content") as HTMLElement | null;
353
+
354
+ // Cleanup type-ahead listener
355
+ if (content) {
356
+ content.removeEventListener("keydown", this.handleTypeAheadKeyDown);
357
+ }
358
+
359
+ // Cleanup anchor positioning
360
+ this.anchorPosition?.destroy();
361
+ this.anchorPosition = null;
362
+
363
+ // Cleanup dismiss layer
364
+ this.dismissLayer?.deactivate();
365
+ this.dismissLayer = null;
366
+
367
+ // Cleanup presence
368
+ this.presence?.destroy();
369
+ this.presence = null;
370
+
371
+ // Cleanup roving focus
372
+ this.rovingFocus?.destroy();
373
+ this.rovingFocus = null;
374
+
375
+ // Cleanup type-ahead
376
+ this.typeAhead?.reset();
377
+ this.typeAhead = null;
378
+
379
+ // Cleanup resize observer
380
+ this.resizeObserver?.disconnect();
381
+ this.resizeObserver = null;
382
+
383
+ // Cleanup scroll handler
384
+ if (this.scrollHandler) {
385
+ window.removeEventListener("scroll", this.scrollHandler);
386
+ window.removeEventListener("resize", this.scrollHandler);
387
+ this.scrollHandler = null;
388
+ }
389
+ }
390
+
391
+ override async updated(changedProperties: Map<string, unknown>): Promise<void> {
392
+ super.updated(changedProperties);
393
+
394
+ if (changedProperties.has("open")) {
395
+ this.updateTriggerAria();
396
+
397
+ const content = this.querySelector("ds-menu-content") as DsMenuContent | null;
398
+
399
+ if (this.open) {
400
+ // Show content
401
+ content?.removeAttribute("hidden");
402
+
403
+ // Set data-state to open for entry animation
404
+ if (content) {
405
+ content.dataState = "open";
406
+ }
407
+
408
+ // Wait for DOM update
409
+ await this.updateComplete;
410
+
411
+ // Setup all behaviors
412
+ this.setupPositioning();
413
+ this.setupDismissLayer();
414
+ this.setupRovingFocus();
415
+ this.setupTypeAhead();
416
+
417
+ // Focus initial item
418
+ this.focusInitialItem();
419
+ } else {
420
+ // Hide content
421
+ content?.setAttribute("hidden", "");
422
+ }
423
+ }
424
+
425
+ // Update positioning if placement or offset changes while open
426
+ if (this.open && (changedProperties.has("placement") || changedProperties.has("offset"))) {
427
+ this.cleanup();
428
+ this.setupPositioning();
429
+ this.setupDismissLayer();
430
+ this.setupRovingFocus();
431
+ this.setupTypeAhead();
432
+ }
433
+ }
434
+
435
+ override render() {
436
+ return html`
437
+ <slot name="trigger"></slot>
438
+ <slot></slot>
439
+ `;
440
+ }
441
+ }
442
+
443
+ define("ds-menu", DsMenu);
444
+
445
+ declare global {
446
+ interface HTMLElementTagNameMap {
447
+ "ds-menu": DsMenu;
448
+ }
449
+ }