@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,411 @@
1
+ import {
2
+ type DialogBehavior,
3
+ type Presence,
4
+ createDialogBehavior,
5
+ createPresence,
6
+ prefersReducedMotion,
7
+ } from "@hypoth-ui/primitives-dom";
8
+ import { html } from "lit";
9
+ import { property, state } from "lit/decorators.js";
10
+ import { DSElement } from "../../base/ds-element.js";
11
+ import { StandardEvents, emitEvent } from "../../events/emit.js";
12
+ import { define } from "../../registry/define.js";
13
+ import { devWarn, hasRequiredChild, Warnings } from "../../utils/dev-warnings.js";
14
+
15
+ // Import child components to ensure they're registered
16
+ import type { DsDialogContent } from "./dialog-content.js";
17
+ import "./dialog-content.js";
18
+ import "./dialog-title.js";
19
+ import "./dialog-description.js";
20
+
21
+ export type DialogRole = "dialog" | "alertdialog";
22
+
23
+ /**
24
+ * Modal dialog component with accessibility and focus management.
25
+ *
26
+ * Provides a modal overlay with backdrop, focus trap, and dismissal handling.
27
+ * Follows WAI-ARIA Dialog (Modal) pattern.
28
+ *
29
+ * @element ds-dialog
30
+ *
31
+ * @slot trigger - Button or element that opens the dialog
32
+ * @slot - Dialog content (ds-dialog-content)
33
+ *
34
+ * @fires ds:open-change - Fired when open state changes (detail: { open, reason })
35
+ *
36
+ * @example
37
+ * ```html
38
+ * <ds-dialog>
39
+ * <button slot="trigger">Open Dialog</button>
40
+ * <ds-dialog-content>
41
+ * <ds-dialog-title>Confirm Action</ds-dialog-title>
42
+ * <ds-dialog-description>Are you sure?</ds-dialog-description>
43
+ * <button>Yes</button>
44
+ * <button>No</button>
45
+ * </ds-dialog-content>
46
+ * </ds-dialog>
47
+ * ```
48
+ */
49
+ export class DsDialog extends DSElement {
50
+ /** Whether the dialog is open */
51
+ @property({ type: Boolean, reflect: true })
52
+ open = false;
53
+
54
+ /** Whether Escape key closes the dialog */
55
+ @property({ type: Boolean, attribute: "close-on-escape" })
56
+ closeOnEscape = true;
57
+
58
+ /** Whether clicking the backdrop closes the dialog */
59
+ @property({ type: Boolean, attribute: "close-on-backdrop" })
60
+ closeOnBackdrop = true;
61
+
62
+ /** Whether to animate open/close transitions */
63
+ @property({ type: Boolean })
64
+ animated = true;
65
+
66
+ /** Dialog role - stored internally, read from attribute */
67
+ @state()
68
+ private dialogRole: DialogRole = "dialog";
69
+
70
+ /** Whether the dialog is closing (for animation) */
71
+ @state()
72
+ private isClosing = false;
73
+
74
+ private dialogBehavior: DialogBehavior | null = null;
75
+ private presence: Presence | null = null;
76
+ private attributeObserver: MutationObserver | null = null;
77
+ private backdropElement: HTMLElement | null = null;
78
+
79
+ override connectedCallback(): void {
80
+ super.connectedCallback();
81
+
82
+ // Listen for trigger clicks via event delegation
83
+ this.addEventListener("click", this.handleTriggerClick);
84
+
85
+ // Watch for role attribute changes (don't reflect back, just read)
86
+ this.attributeObserver = new MutationObserver(() => {
87
+ const roleAttr = this.getAttribute("role");
88
+ if (roleAttr === "alertdialog" || roleAttr === "dialog") {
89
+ this.dialogRole = roleAttr;
90
+ }
91
+ // Remove the role from ds-dialog itself (it shouldn't have it)
92
+ if (roleAttr) {
93
+ this.removeAttribute("role");
94
+ }
95
+ });
96
+ this.attributeObserver.observe(this, {
97
+ attributes: true,
98
+ attributeFilter: ["role"],
99
+ });
100
+
101
+ // Read initial role attribute
102
+ const initialRole = this.getAttribute("role");
103
+ if (initialRole === "alertdialog" || initialRole === "dialog") {
104
+ this.dialogRole = initialRole;
105
+ this.removeAttribute("role");
106
+ }
107
+
108
+ // Initialize dialog behavior
109
+ this.initDialogBehavior();
110
+ }
111
+
112
+ override disconnectedCallback(): void {
113
+ super.disconnectedCallback();
114
+ this.removeEventListener("click", this.handleTriggerClick);
115
+ this.attributeObserver?.disconnect();
116
+ this.attributeObserver = null;
117
+ this.cleanup();
118
+ }
119
+
120
+ /**
121
+ * Initializes the dialog behavior primitive.
122
+ */
123
+ private initDialogBehavior(): void {
124
+ this.dialogBehavior = createDialogBehavior({
125
+ defaultOpen: this.open,
126
+ role: this.dialogRole,
127
+ closeOnEscape: this.closeOnEscape,
128
+ closeOnOutsideClick: this.closeOnBackdrop,
129
+ onOpenChange: (open) => {
130
+ // Sync state when behavior changes (e.g., from escape or outside click)
131
+ if (!open && this.open) {
132
+ this.handleBehaviorClose();
133
+ }
134
+ },
135
+ });
136
+
137
+ // Set trigger element if one exists
138
+ const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
139
+ if (trigger) {
140
+ this.dialogBehavior.setTriggerElement(trigger);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Handles close triggered by the behavior (escape/outside click).
146
+ */
147
+ private handleBehaviorClose(reason: "escape" | "outside-click" = "escape"): void {
148
+ // Emit cancelable open-change event before closing
149
+ const openChangeEvent = emitEvent(this, StandardEvents.OPEN_CHANGE, {
150
+ detail: { open: false, reason },
151
+ cancelable: true,
152
+ });
153
+
154
+ if (openChangeEvent.defaultPrevented) {
155
+ // Re-open the behavior since we're preventing close
156
+ this.dialogBehavior?.open();
157
+ return;
158
+ }
159
+
160
+ const content = this.querySelector("ds-dialog-content") as DsDialogContent | null;
161
+
162
+ // If animated, use presence for exit animation
163
+ if (this.animated && content && !prefersReducedMotion()) {
164
+ this.isClosing = true;
165
+ this._closeReason = reason;
166
+
167
+ // Create presence for exit animation
168
+ this.presence = createPresence({
169
+ onExitComplete: () => {
170
+ this.completeClose();
171
+ },
172
+ });
173
+ this.presence.hide(content);
174
+ } else {
175
+ // No animation - close immediately
176
+ this.open = false;
177
+ this.isClosing = false;
178
+ }
179
+ }
180
+
181
+ /** Tracks the reason for closing (for animation completion) */
182
+ private _closeReason: "escape" | "outside-click" | "trigger" | "programmatic" = "programmatic";
183
+
184
+ /**
185
+ * Opens the dialog.
186
+ */
187
+ public show(): void {
188
+ if (this.open) return;
189
+
190
+ // Store trigger element for focus return
191
+ const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
192
+ if (trigger) {
193
+ this.dialogBehavior?.setTriggerElement(trigger);
194
+ } else if (document.activeElement instanceof HTMLElement) {
195
+ this.dialogBehavior?.setTriggerElement(document.activeElement);
196
+ }
197
+
198
+ this.open = true;
199
+ this.dialogBehavior?.open();
200
+ emitEvent(this, StandardEvents.OPEN_CHANGE, {
201
+ detail: { open: true, reason: "trigger" },
202
+ });
203
+ }
204
+
205
+ /**
206
+ * Closes the dialog.
207
+ * @param reason - The reason for closing (default: "programmatic")
208
+ */
209
+ public close(reason: "escape" | "outside-click" | "trigger" | "programmatic" = "programmatic"): void {
210
+ if (!this.open) return;
211
+
212
+ // Emit cancelable open-change event before closing
213
+ const openChangeEvent = emitEvent(this, StandardEvents.OPEN_CHANGE, {
214
+ detail: { open: false, reason },
215
+ cancelable: true,
216
+ });
217
+
218
+ if (openChangeEvent.defaultPrevented) {
219
+ return;
220
+ }
221
+
222
+ const content = this.querySelector("ds-dialog-content") as DsDialogContent | null;
223
+
224
+ // Close the behavior (this deactivates focus trap and dismiss layer)
225
+ this.dialogBehavior?.close();
226
+
227
+ // If animated, use presence for exit animation
228
+ if (this.animated && content && !prefersReducedMotion()) {
229
+ this.isClosing = true;
230
+ this._closeReason = reason;
231
+
232
+ // Create presence for exit animation
233
+ this.presence = createPresence({
234
+ onExitComplete: () => {
235
+ this.completeClose();
236
+ },
237
+ });
238
+ this.presence.hide(content);
239
+ } else {
240
+ // No animation - close immediately
241
+ this.open = false;
242
+ this.isClosing = false;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Completes the close after exit animation.
248
+ * Note: The open-change event was already emitted before animation started.
249
+ */
250
+ private completeClose(): void {
251
+ this.presence?.destroy();
252
+ this.presence = null;
253
+ this.open = false;
254
+ this.isClosing = false;
255
+ // Event was already emitted before animation started
256
+ }
257
+
258
+ private handleTriggerClick = (event: Event): void => {
259
+ const target = event.target as HTMLElement;
260
+ const trigger = target.closest('[slot="trigger"]');
261
+
262
+ if (trigger && this.contains(trigger)) {
263
+ // Store trigger before opening
264
+ this.dialogBehavior?.setTriggerElement(trigger as HTMLElement);
265
+ this.show();
266
+ }
267
+ };
268
+
269
+ private handleBackdropClick = (event: MouseEvent): void => {
270
+ // Only close if clicking directly on backdrop, not on dialog content
271
+ const content = this.querySelector("ds-dialog-content");
272
+ const target = event.target as Node;
273
+
274
+ // If click is inside content, don't close
275
+ if (content?.contains(target)) {
276
+ return;
277
+ }
278
+
279
+ // Click was on backdrop (outside content)
280
+ if (this.closeOnBackdrop) {
281
+ this.close("outside-click");
282
+ }
283
+ };
284
+
285
+ private cleanup(): void {
286
+ this.dialogBehavior?.destroy();
287
+ this.dialogBehavior = null;
288
+ this.presence?.destroy();
289
+ this.presence = null;
290
+ }
291
+
292
+ override async updated(changedProperties: Map<string, unknown>): Promise<void> {
293
+ super.updated(changedProperties);
294
+
295
+ if (changedProperties.has("open")) {
296
+ const content = this.querySelector("ds-dialog-content") as DsDialogContent | null;
297
+
298
+ if (this.open) {
299
+ // Wait for the next microtask to ensure DOM is committed
300
+ await this.updateComplete;
301
+
302
+ // Set data-state to open for entry animation
303
+ if (content) {
304
+ content.dataState = "open";
305
+ }
306
+
307
+ // Set content element on behavior (activates focus trap and dismiss layer)
308
+ this.dialogBehavior?.setContentElement(content);
309
+ this.updateContentAccessibility();
310
+
311
+ // Attach backdrop click handler
312
+ this.backdropElement = this.querySelector(".ds-dialog__backdrop");
313
+ this.backdropElement?.addEventListener("click", this.handleBackdropClick);
314
+ } else {
315
+ // Clean up backdrop listener
316
+ this.backdropElement?.removeEventListener("click", this.handleBackdropClick);
317
+ this.backdropElement = null;
318
+
319
+ // Clear content element on behavior
320
+ this.dialogBehavior?.setContentElement(null);
321
+ }
322
+ }
323
+
324
+ // Update role on content when dialogRole changes
325
+ if (changedProperties.has("dialogRole") && this.open) {
326
+ this.updateContentAccessibility();
327
+ }
328
+
329
+ // Sync behavior options when they change
330
+ if (changedProperties.has("closeOnEscape") || changedProperties.has("closeOnBackdrop")) {
331
+ // Re-create behavior with new options
332
+ const wasOpen = this.dialogBehavior?.state.open;
333
+ this.dialogBehavior?.destroy();
334
+ this.initDialogBehavior();
335
+ if (wasOpen) {
336
+ this.dialogBehavior?.open();
337
+ const content = this.querySelector("ds-dialog-content") as HTMLElement | null;
338
+ this.dialogBehavior?.setContentElement(content);
339
+ }
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Updates accessibility attributes on dialog content.
345
+ */
346
+ private updateContentAccessibility(): void {
347
+ const content = this.querySelector("ds-dialog-content");
348
+ if (!content || !this.dialogBehavior) return;
349
+
350
+ // Get props from behavior
351
+ const contentProps = this.dialogBehavior.getContentProps();
352
+
353
+ // Apply content props
354
+ content.setAttribute("role", this.dialogRole);
355
+ content.setAttribute("aria-modal", contentProps["aria-modal"]);
356
+
357
+ // Dev warning: Check for required dialog title
358
+ if (!hasRequiredChild(this, "ds-dialog-title") && !this.getAttribute("aria-label")) {
359
+ devWarn(Warnings.dialogMissingTitle("ds-dialog"));
360
+ }
361
+
362
+ // Connect title via aria-labelledby
363
+ const title = this.querySelector("ds-dialog-title");
364
+ if (title) {
365
+ const titleProps = this.dialogBehavior.getTitleProps();
366
+ if (!title.id) {
367
+ title.id = titleProps.id;
368
+ }
369
+ content.setAttribute("aria-labelledby", title.id);
370
+ }
371
+
372
+ // Connect description via aria-describedby
373
+ const description = this.querySelector("ds-dialog-description");
374
+ if (description) {
375
+ const descProps = this.dialogBehavior.getDescriptionProps();
376
+ if (!description.id) {
377
+ description.id = descProps.id;
378
+ }
379
+ content.setAttribute("aria-describedby", description.id);
380
+ this.dialogBehavior.setHasDescription(true);
381
+ } else {
382
+ // Dev warning: Title without description
383
+ if (title) {
384
+ devWarn(Warnings.dialogMissingDescription("ds-dialog"));
385
+ }
386
+ this.dialogBehavior.setHasDescription(false);
387
+ }
388
+ }
389
+
390
+ override render() {
391
+ // Always render backdrop, hide when not open
392
+ return html`
393
+ <slot name="trigger"></slot>
394
+ <div
395
+ class="ds-dialog__backdrop"
396
+ ?hidden=${!this.open}
397
+ ?data-closing=${this.isClosing}
398
+ >
399
+ <slot></slot>
400
+ </div>
401
+ `;
402
+ }
403
+ }
404
+
405
+ define("ds-dialog", DsDialog);
406
+
407
+ declare global {
408
+ interface HTMLElementTagNameMap {
409
+ "ds-dialog": DsDialog;
410
+ }
411
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Drawer compound component exports.
3
+ *
4
+ * Drawer is a mobile-optimized slide-in panel with swipe gesture support.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { Drawer } from "@/components/ui";
9
+ *
10
+ * <Drawer.Root>
11
+ * <Drawer.Trigger>
12
+ * <button>Open Menu</button>
13
+ * </Drawer.Trigger>
14
+ * <Drawer.Content>
15
+ * <Drawer.Header>
16
+ * <Drawer.Title>Navigation</Drawer.Title>
17
+ * </Drawer.Header>
18
+ * <nav>Menu items...</nav>
19
+ * </Drawer.Content>
20
+ * </Drawer.Root>
21
+ * ```
22
+ */
23
+
24
+ import {
25
+ type HTMLAttributes,
26
+ type ReactNode,
27
+ createElement,
28
+ forwardRef,
29
+ useCallback,
30
+ useEffect,
31
+ useRef,
32
+ useState,
33
+ } from "react";
34
+
35
+ // ============================================================================
36
+ // Types
37
+ // ============================================================================
38
+
39
+ export type DrawerSide = "top" | "right" | "bottom" | "left";
40
+
41
+ export interface DrawerRootProps extends HTMLAttributes<HTMLElement> {
42
+ /** Content */
43
+ children?: ReactNode;
44
+ /** Controlled open state */
45
+ open?: boolean;
46
+ /** Default open state (uncontrolled) */
47
+ defaultOpen?: boolean;
48
+ /** Called when open state changes */
49
+ onOpenChange?: (open: boolean) => void;
50
+ /** Side of the screen the drawer appears from */
51
+ side?: DrawerSide;
52
+ /** Whether swipe-to-dismiss is enabled */
53
+ swipeDismiss?: boolean;
54
+ /** Whether Escape key closes the drawer */
55
+ closeOnEscape?: boolean;
56
+ /** Whether clicking the overlay closes the drawer */
57
+ closeOnOverlay?: boolean;
58
+ /** Whether to animate open/close transitions */
59
+ animated?: boolean;
60
+ }
61
+
62
+ export interface DrawerTriggerProps extends HTMLAttributes<HTMLElement> {
63
+ /** Trigger content (typically a button) */
64
+ children?: ReactNode;
65
+ }
66
+
67
+ export interface DrawerContentProps extends HTMLAttributes<HTMLElement> {
68
+ /** Content */
69
+ children?: ReactNode;
70
+ }
71
+
72
+ export interface DrawerHeaderProps extends HTMLAttributes<HTMLElement> {
73
+ /** Header content */
74
+ children?: ReactNode;
75
+ }
76
+
77
+ export interface DrawerFooterProps extends HTMLAttributes<HTMLElement> {
78
+ /** Footer content */
79
+ children?: ReactNode;
80
+ }
81
+
82
+ export interface DrawerTitleProps extends HTMLAttributes<HTMLElement> {
83
+ /** Title content */
84
+ children?: ReactNode;
85
+ }
86
+
87
+ export interface DrawerDescriptionProps extends HTMLAttributes<HTMLElement> {
88
+ /** Description content */
89
+ children?: ReactNode;
90
+ }
91
+
92
+ // ============================================================================
93
+ // Components
94
+ // ============================================================================
95
+
96
+ /**
97
+ * Drawer root component.
98
+ */
99
+ const DrawerRoot = forwardRef<HTMLElement, DrawerRootProps>(function DrawerRoot(
100
+ {
101
+ children,
102
+ className,
103
+ open: controlledOpen,
104
+ defaultOpen = false,
105
+ onOpenChange,
106
+ side = "bottom",
107
+ swipeDismiss = true,
108
+ closeOnEscape = true,
109
+ closeOnOverlay = true,
110
+ animated = true,
111
+ ...props
112
+ },
113
+ ref
114
+ ) {
115
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
116
+ const isControlled = controlledOpen !== undefined;
117
+ const open = isControlled ? controlledOpen : internalOpen;
118
+ const elementRef = useRef<HTMLElement>(null);
119
+
120
+ // Combine refs
121
+ const combinedRef = (node: HTMLElement | null) => {
122
+ (elementRef as React.MutableRefObject<HTMLElement | null>).current = node;
123
+ if (typeof ref === "function") {
124
+ ref(node);
125
+ } else if (ref) {
126
+ (ref as React.MutableRefObject<HTMLElement | null>).current = node;
127
+ }
128
+ };
129
+
130
+ const handleOpenChange = useCallback(
131
+ (event: Event) => {
132
+ const customEvent = event as CustomEvent;
133
+ const isOpen = customEvent.type === "ds:open";
134
+
135
+ if (!isControlled) {
136
+ setInternalOpen(isOpen);
137
+ }
138
+ onOpenChange?.(isOpen);
139
+ },
140
+ [isControlled, onOpenChange]
141
+ );
142
+
143
+ // Attach event listeners
144
+ useEffect(() => {
145
+ const element = elementRef.current;
146
+ if (!element) return;
147
+
148
+ element.addEventListener("ds:open", handleOpenChange);
149
+ element.addEventListener("ds:close", handleOpenChange);
150
+
151
+ return () => {
152
+ element.removeEventListener("ds:open", handleOpenChange);
153
+ element.removeEventListener("ds:close", handleOpenChange);
154
+ };
155
+ }, [handleOpenChange]);
156
+
157
+ return createElement(
158
+ "ds-drawer",
159
+ {
160
+ ref: combinedRef,
161
+ class: className,
162
+ open: open || undefined,
163
+ side,
164
+ "swipe-dismiss": swipeDismiss,
165
+ "close-on-escape": closeOnEscape,
166
+ "close-on-overlay": closeOnOverlay,
167
+ animated: animated || undefined,
168
+ ...props,
169
+ },
170
+ children
171
+ );
172
+ });
173
+ DrawerRoot.displayName = "Drawer.Root";
174
+
175
+ /**
176
+ * Drawer trigger component.
177
+ */
178
+ const DrawerTrigger = forwardRef<HTMLElement, DrawerTriggerProps>(function DrawerTrigger(
179
+ { children, className, ...props },
180
+ ref
181
+ ) {
182
+ return createElement("div", { ref, className, slot: "trigger", ...props }, children);
183
+ });
184
+ DrawerTrigger.displayName = "Drawer.Trigger";
185
+
186
+ /**
187
+ * Drawer content component.
188
+ */
189
+ const DrawerContent = forwardRef<HTMLElement, DrawerContentProps>(function DrawerContent(
190
+ { children, className, ...props },
191
+ ref
192
+ ) {
193
+ return createElement("ds-drawer-content", { ref, class: className, ...props }, children);
194
+ });
195
+ DrawerContent.displayName = "Drawer.Content";
196
+
197
+ /**
198
+ * Drawer header component.
199
+ */
200
+ const DrawerHeader = forwardRef<HTMLElement, DrawerHeaderProps>(function DrawerHeader(
201
+ { children, className, ...props },
202
+ ref
203
+ ) {
204
+ return createElement("ds-drawer-header", { ref, class: className, ...props }, children);
205
+ });
206
+ DrawerHeader.displayName = "Drawer.Header";
207
+
208
+ /**
209
+ * Drawer footer component.
210
+ */
211
+ const DrawerFooter = forwardRef<HTMLElement, DrawerFooterProps>(function DrawerFooter(
212
+ { children, className, ...props },
213
+ ref
214
+ ) {
215
+ return createElement("ds-drawer-footer", { ref, class: className, ...props }, children);
216
+ });
217
+ DrawerFooter.displayName = "Drawer.Footer";
218
+
219
+ /**
220
+ * Drawer title component.
221
+ */
222
+ const DrawerTitle = forwardRef<HTMLElement, DrawerTitleProps>(function DrawerTitle(
223
+ { children, className, ...props },
224
+ ref
225
+ ) {
226
+ return createElement("ds-drawer-title", { ref, class: className, ...props }, children);
227
+ });
228
+ DrawerTitle.displayName = "Drawer.Title";
229
+
230
+ /**
231
+ * Drawer description component.
232
+ */
233
+ const DrawerDescription = forwardRef<HTMLElement, DrawerDescriptionProps>(
234
+ function DrawerDescription({ children, className, ...props }, ref) {
235
+ return createElement("ds-drawer-description", { ref, class: className, ...props }, children);
236
+ }
237
+ );
238
+ DrawerDescription.displayName = "Drawer.Description";
239
+
240
+ // ============================================================================
241
+ // Compound Component
242
+ // ============================================================================
243
+
244
+ export const Drawer = {
245
+ Root: DrawerRoot,
246
+ Trigger: DrawerTrigger,
247
+ Content: DrawerContent,
248
+ Header: DrawerHeader,
249
+ Footer: DrawerFooter,
250
+ Title: DrawerTitle,
251
+ Description: DrawerDescription,
252
+ };
253
+
254
+ // Also export individual components
255
+ export {
256
+ DrawerRoot,
257
+ DrawerTrigger,
258
+ DrawerContent,
259
+ DrawerHeader,
260
+ DrawerFooter,
261
+ DrawerTitle,
262
+ DrawerDescription,
263
+ };