@hypoth-ui/cli 0.0.1 → 0.1.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 (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,138 @@
1
+ "use client";
2
+
3
+ import {
4
+ type HTMLAttributes,
5
+ type ReactNode,
6
+ createElement,
7
+ forwardRef,
8
+ useEffect,
9
+ useRef,
10
+ } from "react";
11
+ import "@hypoth-ui/wc";
12
+
13
+ export type TreeSelectionMode = "single" | "multiple" | "none";
14
+ export type TreeSize = "default" | "compact";
15
+
16
+ export interface TreeRootProps extends HTMLAttributes<HTMLElement> {
17
+ /**
18
+ * Selection mode.
19
+ * @default "single"
20
+ */
21
+ selectionMode?: TreeSelectionMode;
22
+
23
+ /**
24
+ * Size variant.
25
+ * @default "default"
26
+ */
27
+ size?: TreeSize;
28
+
29
+ /**
30
+ * Show connecting lines.
31
+ * @default false
32
+ */
33
+ lines?: boolean;
34
+
35
+ /**
36
+ * Accessible label.
37
+ * @default "Tree"
38
+ */
39
+ label?: string;
40
+
41
+ /**
42
+ * Whether the tree is in a loading state.
43
+ * When true, sets aria-busy and disables keyboard navigation.
44
+ * @default false
45
+ */
46
+ loading?: boolean;
47
+
48
+ /**
49
+ * Text to display/announce during loading.
50
+ * @default "Loading..."
51
+ */
52
+ loadingText?: string;
53
+
54
+ /**
55
+ * Node IDs that are currently loading children.
56
+ * Allows for node-level loading indicators.
57
+ */
58
+ loadingNodes?: string[];
59
+
60
+ /**
61
+ * Callback when selection changes.
62
+ */
63
+ onSelectionChange?: (selectedItems: string[]) => void;
64
+
65
+ /**
66
+ * Tree content (TreeItem elements).
67
+ */
68
+ children?: ReactNode;
69
+ }
70
+
71
+ /**
72
+ * Tree root component for hierarchical data display.
73
+ */
74
+ export const TreeRoot = forwardRef<HTMLElement, TreeRootProps>(function TreeRoot(
75
+ {
76
+ selectionMode = "single",
77
+ size = "default",
78
+ lines = false,
79
+ label = "Tree",
80
+ loading = false,
81
+ loadingText = "Loading...",
82
+ loadingNodes,
83
+ onSelectionChange,
84
+ children,
85
+ className,
86
+ ...props
87
+ },
88
+ forwardedRef
89
+ ) {
90
+ const internalRef = useRef<HTMLElement>(null);
91
+
92
+ // Sync forwarded ref
93
+ useEffect(() => {
94
+ if (typeof forwardedRef === "function") {
95
+ forwardedRef(internalRef.current);
96
+ } else if (forwardedRef) {
97
+ (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = internalRef.current;
98
+ }
99
+ }, [forwardedRef]);
100
+
101
+ // Set up event listeners
102
+ useEffect(() => {
103
+ const element = internalRef.current;
104
+ if (!element) return;
105
+
106
+ const handleSelectionChange = (e: Event) => {
107
+ const event = e as CustomEvent<{ selectedItems: string[] }>;
108
+ onSelectionChange?.(event.detail.selectedItems);
109
+ };
110
+
111
+ element.addEventListener("ds:selection-change", handleSelectionChange);
112
+ return () => element.removeEventListener("ds:selection-change", handleSelectionChange);
113
+ }, [onSelectionChange]);
114
+
115
+ // Sync loadingNodes as a property (cannot be set via attribute)
116
+ useEffect(() => {
117
+ const element = internalRef.current as HTMLElement & { loadingNodes?: Set<string> | string[] } | null;
118
+ if (element && loadingNodes !== undefined) {
119
+ element.loadingNodes = loadingNodes;
120
+ }
121
+ }, [loadingNodes]);
122
+
123
+ return createElement(
124
+ "ds-tree",
125
+ {
126
+ ref: internalRef,
127
+ "selection-mode": selectionMode,
128
+ size,
129
+ lines: lines || undefined,
130
+ label,
131
+ loading: loading || undefined,
132
+ "loading-text": loadingText,
133
+ class: className,
134
+ ...props,
135
+ },
136
+ children
137
+ );
138
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tree component exports
3
+ */
4
+ export { DsTree, type TreeSelectionMode, type TreeSize } from "./tree.js";
5
+ export { DsTreeItem } from "./tree-item.js";
6
+ export {
7
+ calculateTreeItemPosition,
8
+ updateTreeItemAriaAttributes,
9
+ updateAllTreeItemAriaAttributes,
10
+ type TreeItemPosition,
11
+ } from "./tree-utils.js";
@@ -0,0 +1,273 @@
1
+ import { type TemplateResult, html, nothing } from "lit";
2
+ import { property, state } from "lit/decorators.js";
3
+ import { classMap } from "lit/directives/class-map.js";
4
+ import { DSElement } from "../../base/ds-element.js";
5
+ import { emitEvent } from "../../events/emit.js";
6
+ import { define } from "../../registry/define.js";
7
+ import type { DsTree } from "./tree.js";
8
+ import { calculateTreeItemPosition } from "./tree-utils.js";
9
+
10
+ // Chevron icon
11
+ const chevronIcon = html`
12
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
13
+ <path d="M9 18l6-6-6-6" />
14
+ </svg>
15
+ `;
16
+
17
+ /**
18
+ * Tree item component for hierarchical nodes.
19
+ *
20
+ * @element ds-tree-item
21
+ *
22
+ * @slot - Item content (label)
23
+ * @slot icon - Optional leading icon
24
+ * @slot children - Nested TreeItem elements
25
+ *
26
+ * @fires ds-expand - When expand state changes
27
+ * @fires ds-select - When item is selected
28
+ * @fires ds-activate - When item is activated (Enter/double-click)
29
+ */
30
+ export class DsTreeItem extends DSElement {
31
+ static override styles = [];
32
+
33
+ /**
34
+ * Unique item ID.
35
+ */
36
+ @property({ type: String, attribute: "item-id" })
37
+ itemId = "";
38
+
39
+ /**
40
+ * Whether item is expanded.
41
+ */
42
+ @property({ type: Boolean, reflect: true })
43
+ expanded = false;
44
+
45
+ /**
46
+ * Whether item is selected.
47
+ */
48
+ @property({ type: Boolean, reflect: true })
49
+ selected = false;
50
+
51
+ /**
52
+ * Whether item is disabled.
53
+ */
54
+ @property({ type: Boolean, reflect: true })
55
+ disabled = false;
56
+
57
+ /**
58
+ * Whether this item is loading (e.g., fetching children).
59
+ * Can be set directly or inherited from tree's loadingNodes.
60
+ */
61
+ @property({ type: Boolean, reflect: true })
62
+ loading = false;
63
+
64
+ @state()
65
+ private hasChildren = false;
66
+
67
+ @state()
68
+ private _level = 1;
69
+
70
+ @state()
71
+ private _setSize = 1;
72
+
73
+ @state()
74
+ private _posInSet = 1;
75
+
76
+ private get treeRoot(): DsTree | null {
77
+ return this.closest("ds-tree") as DsTree | null;
78
+ }
79
+
80
+ /**
81
+ * Returns true if this item is loading (either directly or via tree's loadingNodes).
82
+ */
83
+ private get isLoading(): boolean {
84
+ if (this.loading) return true;
85
+ const tree = this.treeRoot;
86
+ if (tree && this.itemId) {
87
+ return tree.isNodeLoading(this.itemId);
88
+ }
89
+ return false;
90
+ }
91
+
92
+ /**
93
+ * Returns true if the tree root is in a loading state.
94
+ */
95
+ private get isTreeLoading(): boolean {
96
+ return this.treeRoot?.loading ?? false;
97
+ }
98
+
99
+ override connectedCallback(): void {
100
+ super.connectedCallback();
101
+ this.updateHasChildren();
102
+ // Calculate position after DOM is ready
103
+ requestAnimationFrame(() => {
104
+ this.updateAriaPosition();
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Update ARIA position attributes for APG compliance.
110
+ * Provides screen readers with context like "Item 3 of 5, level 2".
111
+ */
112
+ private updateAriaPosition(): void {
113
+ const position = calculateTreeItemPosition(this as unknown as HTMLElement);
114
+ this._level = position.level;
115
+ this._setSize = position.setSize;
116
+ this._posInSet = position.posInSet;
117
+ }
118
+
119
+ private updateHasChildren(): void {
120
+ const childrenSlot = this.querySelector("[slot=children]");
121
+ this.hasChildren = childrenSlot !== null && childrenSlot.querySelector("ds-tree-item") !== null;
122
+ }
123
+
124
+ private handleExpandClick(event: Event): void {
125
+ event.stopPropagation();
126
+ if (this.disabled || this.isTreeLoading || this.isLoading || !this.hasChildren) return;
127
+
128
+ this.expanded = !this.expanded;
129
+ emitEvent(this, "expand", {
130
+ detail: {
131
+ itemId: this.itemId,
132
+ expanded: this.expanded,
133
+ },
134
+ });
135
+ }
136
+
137
+ private handleContentClick(): void {
138
+ if (this.disabled || this.isTreeLoading) return;
139
+
140
+ const tree = this.treeRoot;
141
+ if (tree) {
142
+ tree.handleItemSelect(this.itemId);
143
+ this.selected = tree.isItemSelected(this.itemId);
144
+ }
145
+
146
+ emitEvent(this, "select", {
147
+ detail: {
148
+ itemId: this.itemId,
149
+ selected: this.selected,
150
+ },
151
+ });
152
+ }
153
+
154
+ private handleKeyDown(event: KeyboardEvent): void {
155
+ if (this.disabled || this.isTreeLoading) return;
156
+
157
+ switch (event.key) {
158
+ case "ArrowRight":
159
+ if (this.hasChildren && !this.expanded) {
160
+ event.preventDefault();
161
+ this.expanded = true;
162
+ emitEvent(this, "expand", {
163
+ detail: {
164
+ itemId: this.itemId,
165
+ expanded: true,
166
+ },
167
+ });
168
+ }
169
+ break;
170
+
171
+ case "ArrowLeft":
172
+ if (this.hasChildren && this.expanded) {
173
+ event.preventDefault();
174
+ this.expanded = false;
175
+ emitEvent(this, "expand", {
176
+ detail: {
177
+ itemId: this.itemId,
178
+ expanded: false,
179
+ },
180
+ });
181
+ }
182
+ break;
183
+
184
+ case "Enter":
185
+ event.preventDefault();
186
+ emitEvent(this, "activate", { detail: { itemId: this.itemId } });
187
+ break;
188
+
189
+ case " ":
190
+ event.preventDefault();
191
+ this.handleContentClick();
192
+ break;
193
+ }
194
+ }
195
+
196
+ override render(): TemplateResult {
197
+ const classes = {
198
+ "ds-tree-item": true,
199
+ };
200
+
201
+ // Check for children via slot
202
+ this.updateHasChildren();
203
+
204
+ const itemLoading = this.isLoading;
205
+
206
+ return html`
207
+ <li
208
+ class=${classMap(classes)}
209
+ role="treeitem"
210
+ aria-expanded=${this.hasChildren ? (this.expanded ? "true" : "false") : nothing}
211
+ aria-selected=${this.selected ? "true" : "false"}
212
+ aria-busy=${itemLoading ? "true" : nothing}
213
+ aria-level=${this._level}
214
+ aria-setsize=${this._setSize}
215
+ aria-posinset=${this._posInSet}
216
+ ?data-expanded=${this.expanded}
217
+ ?data-selected=${this.selected}
218
+ ?data-disabled=${this.disabled}
219
+ ?data-loading=${itemLoading}
220
+ >
221
+ <div
222
+ class="ds-tree-item__content"
223
+ tabindex=${this.disabled ? -1 : 0}
224
+ @click=${this.handleContentClick}
225
+ @keydown=${this.handleKeyDown}
226
+ >
227
+ ${
228
+ this.hasChildren
229
+ ? html`
230
+ <button
231
+ type="button"
232
+ class="ds-tree-item__expand"
233
+ aria-label=${this.expanded ? "Collapse" : "Expand"}
234
+ tabindex="-1"
235
+ @click=${this.handleExpandClick}
236
+ >
237
+ ${chevronIcon}
238
+ </button>
239
+ `
240
+ : html`<span class="ds-tree-item__spacer"></span>`
241
+ }
242
+ <slot name="icon"></slot>
243
+ <span class="ds-tree-item__label">
244
+ <slot></slot>
245
+ </span>
246
+ </div>
247
+ ${
248
+ this.hasChildren
249
+ ? html`
250
+ <ul
251
+ class="ds-tree-item__children ds-tree"
252
+ role="group"
253
+ ?hidden=${!this.expanded}
254
+ >
255
+ <slot name="children" @slotchange=${this.updateHasChildren}></slot>
256
+ </ul>
257
+ `
258
+ : nothing
259
+ }
260
+ </li>
261
+ `;
262
+ }
263
+ }
264
+
265
+ // Register the component
266
+ define("ds-tree-item", DsTreeItem);
267
+
268
+ // TypeScript declaration for HTML
269
+ declare global {
270
+ interface HTMLElementTagNameMap {
271
+ "ds-tree-item": DsTreeItem;
272
+ }
273
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Tree structure utilities for APG-compliant ARIA attributes.
3
+ *
4
+ * Calculates aria-level, aria-setsize, and aria-posinset for tree items
5
+ * to provide proper screen reader context (e.g., "Item 3 of 5, level 2").
6
+ */
7
+
8
+ export interface TreeItemPosition {
9
+ /** Nesting depth (1-based, root items are level 1) */
10
+ level: number;
11
+ /** Number of siblings at this level */
12
+ setSize: number;
13
+ /** Position among siblings (1-based) */
14
+ posInSet: number;
15
+ }
16
+
17
+ /**
18
+ * Calculate position attributes for a tree item element.
19
+ *
20
+ * @param element - The ds-tree-item element
21
+ * @returns Position info for aria-level, aria-setsize, aria-posinset
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const position = calculateTreeItemPosition(treeItemElement);
26
+ * // { level: 2, setSize: 3, posInSet: 1 }
27
+ * ```
28
+ */
29
+ export function calculateTreeItemPosition(element: HTMLElement): TreeItemPosition {
30
+ // Calculate level by counting ancestor tree items
31
+ let level = 1;
32
+ let parent: HTMLElement | null = element.parentElement;
33
+
34
+ while (parent) {
35
+ // Check if parent is a tree item (indicates nested level)
36
+ if (parent.tagName?.toLowerCase() === "ds-tree-item") {
37
+ level++;
38
+ }
39
+ // Stop at tree root
40
+ if (parent.tagName?.toLowerCase() === "ds-tree") {
41
+ break;
42
+ }
43
+ parent = parent.parentElement;
44
+ }
45
+
46
+ // Find siblings at the same level
47
+ const siblings = getSiblingTreeItems(element);
48
+ const setSize = siblings.length;
49
+ const posInSet = siblings.indexOf(element) + 1;
50
+
51
+ return { level, setSize, posInSet };
52
+ }
53
+
54
+ /**
55
+ * Get sibling tree items at the same level.
56
+ *
57
+ * @param element - The ds-tree-item element
58
+ * @returns Array of sibling tree item elements
59
+ */
60
+ function getSiblingTreeItems(element: HTMLElement): HTMLElement[] {
61
+ // Find the container (either tree root or children slot)
62
+ let container: HTMLElement | null = element.parentElement;
63
+
64
+ // If parent is a slot, get the slot's parent
65
+ if (container?.tagName?.toLowerCase() === "slot") {
66
+ container = container.parentElement;
67
+ }
68
+
69
+ // If parent is a ul with role="group", get its parent (the tree-item)
70
+ // and then find the children slot
71
+ if (container?.getAttribute("role") === "group") {
72
+ container = container.parentElement;
73
+ }
74
+
75
+ if (!container) {
76
+ return [element];
77
+ }
78
+
79
+ // Query for direct tree-item children
80
+ // This handles both root level (ds-tree > ds-tree-item)
81
+ // and nested level (ds-tree-item [slot=children] > ds-tree-item)
82
+ const items: HTMLElement[] = [];
83
+
84
+ // Check direct children and slotted children
85
+ const checkChildren = (parent: HTMLElement) => {
86
+ for (const child of parent.children) {
87
+ if (child.tagName?.toLowerCase() === "ds-tree-item") {
88
+ items.push(child as HTMLElement);
89
+ } else if (child.tagName?.toLowerCase() === "slot") {
90
+ // Get assigned elements from slot
91
+ const slot = child as HTMLSlotElement;
92
+ for (const assigned of slot.assignedElements()) {
93
+ if (assigned.tagName?.toLowerCase() === "ds-tree-item") {
94
+ items.push(assigned as HTMLElement);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ };
100
+
101
+ // For root tree, check its direct children
102
+ if (container.tagName?.toLowerCase() === "ds-tree") {
103
+ checkChildren(container);
104
+ } else if (container.tagName?.toLowerCase() === "ds-tree-item") {
105
+ // For nested items, find the children slot container
106
+ const childrenSlot = container.querySelector('[slot="children"]');
107
+ if (childrenSlot) {
108
+ for (const child of childrenSlot.children) {
109
+ if (child.tagName?.toLowerCase() === "ds-tree-item") {
110
+ items.push(child as HTMLElement);
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ // If no items found via DOM traversal, fall back to the element itself
117
+ return items.length > 0 ? items : [element];
118
+ }
119
+
120
+ /**
121
+ * Update ARIA position attributes on a tree item element.
122
+ *
123
+ * @param element - The ds-tree-item element to update
124
+ */
125
+ export function updateTreeItemAriaAttributes(element: HTMLElement): void {
126
+ const position = calculateTreeItemPosition(element);
127
+
128
+ element.setAttribute("aria-level", String(position.level));
129
+ element.setAttribute("aria-setsize", String(position.setSize));
130
+ element.setAttribute("aria-posinset", String(position.posInSet));
131
+ }
132
+
133
+ /**
134
+ * Update ARIA position attributes on all tree items within a tree.
135
+ *
136
+ * @param treeRoot - The ds-tree root element
137
+ */
138
+ export function updateAllTreeItemAriaAttributes(treeRoot: HTMLElement): void {
139
+ const items = treeRoot.querySelectorAll("ds-tree-item");
140
+ for (const item of items) {
141
+ updateTreeItemAriaAttributes(item as unknown as HTMLElement);
142
+ }
143
+ }
@@ -0,0 +1,139 @@
1
+ import { type TemplateResult, html, nothing } from "lit";
2
+ import { property, state } from "lit/decorators.js";
3
+ import { classMap } from "lit/directives/class-map.js";
4
+ import { DSElement } from "../../base/ds-element.js";
5
+ import { emitEvent } from "../../events/emit.js";
6
+ import { define } from "../../registry/define.js";
7
+
8
+ export type TreeSelectionMode = "single" | "multiple" | "none";
9
+ export type TreeSize = "default" | "compact";
10
+
11
+ /**
12
+ * Tree root component for hierarchical data display.
13
+ *
14
+ * @element ds-tree
15
+ *
16
+ * @slot - TreeItem elements
17
+ *
18
+ * @fires ds-selection-change - When selection changes
19
+ *
20
+ * @cssprop --ds-tree-indent - Indentation for nested items
21
+ */
22
+ export class DsTree extends DSElement {
23
+ static override styles = [];
24
+
25
+ /**
26
+ * Selection mode.
27
+ */
28
+ @property({ type: String, attribute: "selection-mode", reflect: true })
29
+ selectionMode: TreeSelectionMode = "single";
30
+
31
+ /**
32
+ * Size variant.
33
+ */
34
+ @property({ type: String, reflect: true })
35
+ size: TreeSize = "default";
36
+
37
+ /**
38
+ * Show connecting lines.
39
+ */
40
+ @property({ type: Boolean, reflect: true })
41
+ lines = false;
42
+
43
+ /**
44
+ * Accessible label.
45
+ */
46
+ @property({ type: String })
47
+ label = "Tree";
48
+
49
+ /**
50
+ * Whether the tree is in a loading state.
51
+ * When true, sets aria-busy and disables keyboard navigation.
52
+ */
53
+ @property({ type: Boolean, reflect: true })
54
+ loading = false;
55
+
56
+ /**
57
+ * Text to display/announce during loading.
58
+ */
59
+ @property({ type: String, attribute: "loading-text" })
60
+ loadingText = "Loading...";
61
+
62
+ /**
63
+ * Node IDs that are currently loading children.
64
+ * Allows for node-level loading indicators.
65
+ */
66
+ @property({ attribute: false })
67
+ loadingNodes: Set<string> | string[] = new Set();
68
+
69
+ @state()
70
+ private selectedItems: Set<string> = new Set();
71
+
72
+ /**
73
+ * Checks if a specific node is currently loading.
74
+ */
75
+ isNodeLoading(nodeId: string): boolean {
76
+ if (this.loadingNodes instanceof Set) {
77
+ return this.loadingNodes.has(nodeId);
78
+ }
79
+ return this.loadingNodes.includes(nodeId);
80
+ }
81
+
82
+ handleItemSelect(itemId: string): void {
83
+ // Disable selection during loading
84
+ if (this.loading) return;
85
+ if (this.selectionMode === "none") return;
86
+
87
+ if (this.selectionMode === "single") {
88
+ this.selectedItems.clear();
89
+ this.selectedItems.add(itemId);
90
+ } else {
91
+ if (this.selectedItems.has(itemId)) {
92
+ this.selectedItems.delete(itemId);
93
+ } else {
94
+ this.selectedItems.add(itemId);
95
+ }
96
+ }
97
+
98
+ emitEvent(this, "selection-change", {
99
+ detail: { selectedItems: Array.from(this.selectedItems) },
100
+ });
101
+
102
+ this.requestUpdate();
103
+ }
104
+
105
+ isItemSelected(itemId: string): boolean {
106
+ return this.selectedItems.has(itemId);
107
+ }
108
+
109
+ override render(): TemplateResult {
110
+ const classes = {
111
+ "ds-tree": true,
112
+ };
113
+
114
+ return html`
115
+ <ul
116
+ class=${classMap(classes)}
117
+ role="tree"
118
+ aria-label=${this.label}
119
+ aria-multiselectable=${this.selectionMode === "multiple" ? "true" : nothing}
120
+ aria-busy=${this.loading ? "true" : nothing}
121
+ data-size=${this.size !== "default" ? this.size : nothing}
122
+ ?data-lines=${this.lines}
123
+ ?data-loading=${this.loading}
124
+ >
125
+ <slot></slot>
126
+ </ul>
127
+ `;
128
+ }
129
+ }
130
+
131
+ // Register the component
132
+ define("ds-tree", DsTree);
133
+
134
+ // TypeScript declaration for HTML
135
+ declare global {
136
+ interface HTMLElementTagNameMap {
137
+ "ds-tree": DsTree;
138
+ }
139
+ }