@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,105 @@
1
+ /**
2
+ * FileUpload Item component - individual file display.
3
+ */
4
+
5
+ import { type FileInfo, formatBytes } from "@hypoth-ui/primitives-dom";
6
+ import { type HTMLAttributes, type ReactNode, forwardRef, useCallback } from "react";
7
+ import { useFileUploadContext } from "./file-upload-context.js";
8
+
9
+ export interface FileUploadItemProps extends Omit<HTMLAttributes<HTMLLIElement>, "children"> {
10
+ /** The file info to display */
11
+ file: FileInfo;
12
+ /** Custom render function for file content */
13
+ children?: ReactNode | ((file: FileInfo) => ReactNode);
14
+ }
15
+
16
+ /**
17
+ * Individual file item display with progress and remove functionality.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * {files.map(file => (
22
+ * <FileUpload.Item key={file.id} file={file} />
23
+ * ))}
24
+ *
25
+ * // With custom content
26
+ * <FileUpload.Item file={file}>
27
+ * {(f) => <span>{f.name} - {formatBytes(f.size)}</span>}
28
+ * </FileUpload.Item>
29
+ * ```
30
+ */
31
+ export const FileUploadItem = forwardRef<HTMLLIElement, FileUploadItemProps>(
32
+ ({ file, children, className, ...restProps }, ref) => {
33
+ const { behavior, disabled } = useFileUploadContext("FileUpload.Item");
34
+
35
+ const handleRemove = useCallback(() => {
36
+ behavior.removeFile(file.id);
37
+ }, [behavior, file.id]);
38
+
39
+ const renderContent = () => {
40
+ if (typeof children === "function") {
41
+ return children(file);
42
+ }
43
+
44
+ if (children) {
45
+ return children;
46
+ }
47
+
48
+ // Default rendering
49
+ return (
50
+ <>
51
+ {file.preview && (
52
+ <img
53
+ src={file.preview}
54
+ alt={file.name}
55
+ style={{ width: 40, height: 40, objectFit: "cover", borderRadius: 4 }}
56
+ />
57
+ )}
58
+ <div style={{ flex: 1, minWidth: 0 }}>
59
+ <div style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
60
+ {file.name}
61
+ </div>
62
+ <div style={{ fontSize: "0.875em", opacity: 0.7 }}>
63
+ {formatBytes(file.size)}
64
+ {file.status === "error" && file.error && (
65
+ <span style={{ color: "var(--ds-color-error, red)", marginLeft: 8 }}>
66
+ {file.error}
67
+ </span>
68
+ )}
69
+ </div>
70
+ {file.status === "uploading" && (
71
+ <progress value={file.progress} max={100} style={{ width: "100%" }} />
72
+ )}
73
+ </div>
74
+ {!disabled && (
75
+ <button
76
+ type="button"
77
+ aria-label={`Remove ${file.name}`}
78
+ onClick={handleRemove}
79
+ style={{
80
+ background: "none",
81
+ border: "none",
82
+ cursor: "pointer",
83
+ padding: 4,
84
+ opacity: 0.7,
85
+ }}
86
+ >
87
+ ×
88
+ </button>
89
+ )}
90
+ </>
91
+ );
92
+ };
93
+
94
+ return (
95
+ <li ref={ref} className={className} data-status={file.status} {...restProps}>
96
+ {renderContent()}
97
+ </li>
98
+ );
99
+ }
100
+ );
101
+
102
+ FileUploadItem.displayName = "FileUpload.Item";
103
+
104
+ // Re-export formatBytes for convenience
105
+ export { formatBytes };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * FileUpload Root component - provides context to all FileUpload compound components.
3
+ */
4
+
5
+ import { type FileInfo, type FileUploadError, createFileUploadBehavior } from "@hypoth-ui/primitives-dom";
6
+ import { type ReactNode, useCallback, useMemo, useRef, useState } from "react";
7
+ import { FileUploadProvider } from "./file-upload-context.js";
8
+
9
+ export interface FileUploadRootProps {
10
+ /** FileUpload content */
11
+ children?: ReactNode;
12
+ /** Accepted file types (MIME types or extensions) */
13
+ accept?: string;
14
+ /** Maximum number of files */
15
+ maxFiles?: number;
16
+ /** Maximum file size in bytes */
17
+ maxSize?: number;
18
+ /** Minimum file size in bytes */
19
+ minSize?: number;
20
+ /** Allow multiple files */
21
+ multiple?: boolean;
22
+ /** Disabled state */
23
+ disabled?: boolean;
24
+ /** Called when files are added */
25
+ onFilesAdd?: (files: FileInfo[]) => void;
26
+ /** Called when a file is removed */
27
+ onFileRemove?: (file: FileInfo) => void;
28
+ /** Called when files change */
29
+ onFilesChange?: (files: FileInfo[]) => void;
30
+ /** Called on validation error */
31
+ onError?: (error: FileUploadError) => void;
32
+ }
33
+
34
+ /**
35
+ * Root component for FileUpload compound pattern.
36
+ * Provides context to Dropzone, Input, FileList, and Item components.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * <FileUpload.Root accept="image/*" maxSize={5242880} onFilesChange={(files) => console.log(files)}>
41
+ * <FileUpload.Dropzone>
42
+ * <p>Drop files here or click to upload</p>
43
+ * </FileUpload.Dropzone>
44
+ * <FileUpload.FileList />
45
+ * </FileUpload.Root>
46
+ * ```
47
+ */
48
+ export function FileUploadRoot({
49
+ children,
50
+ accept = "",
51
+ maxFiles = Number.POSITIVE_INFINITY,
52
+ maxSize,
53
+ minSize,
54
+ multiple = false,
55
+ disabled = false,
56
+ onFilesAdd,
57
+ onFileRemove,
58
+ onFilesChange,
59
+ onError,
60
+ }: FileUploadRootProps) {
61
+ const [files, setFiles] = useState<FileInfo[]>([]);
62
+ const [isDragging, setIsDragging] = useState(false);
63
+ const inputRef = useRef<HTMLInputElement>(null);
64
+
65
+ // Create behavior instance
66
+ // biome-ignore lint/correctness/useExhaustiveDependencies: behavior is created once
67
+ const behavior = useMemo(
68
+ () =>
69
+ createFileUploadBehavior({
70
+ accept,
71
+ maxFiles,
72
+ maxSize,
73
+ minSize,
74
+ multiple,
75
+ disabled,
76
+ onFilesAdd: (files) => {
77
+ onFilesAdd?.(files);
78
+ },
79
+ onFileRemove: (file) => {
80
+ onFileRemove?.(file);
81
+ },
82
+ onFilesChange: (files) => {
83
+ setFiles(files);
84
+ onFilesChange?.(files);
85
+ },
86
+ onError: (error) => {
87
+ onError?.(error);
88
+ },
89
+ }),
90
+ []
91
+ );
92
+
93
+ const openFileDialog = useCallback(() => {
94
+ if (!disabled) {
95
+ inputRef.current?.click();
96
+ }
97
+ }, [disabled]);
98
+
99
+ const contextValue = useMemo(
100
+ () => ({
101
+ behavior,
102
+ files,
103
+ isDragging,
104
+ setIsDragging,
105
+ inputRef,
106
+ openFileDialog,
107
+ disabled,
108
+ }),
109
+ [behavior, files, isDragging, openFileDialog, disabled]
110
+ );
111
+
112
+ return <FileUploadProvider value={contextValue}>{children}</FileUploadProvider>;
113
+ }
114
+
115
+ FileUploadRoot.displayName = "FileUpload.Root";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * FileUpload compound component for file selection and uploads.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * // Basic file upload
7
+ * <FileUpload.Root accept="image/*" maxSize={5242880} onFilesChange={(files) => console.log(files)}>
8
+ * <FileUpload.Input />
9
+ * <FileUpload.Dropzone className="dropzone">
10
+ * <p>Drop files here or click to upload</p>
11
+ * </FileUpload.Dropzone>
12
+ * {files.map(file => (
13
+ * <FileUpload.Item key={file.id} file={file} />
14
+ * ))}
15
+ * </FileUpload.Root>
16
+ *
17
+ * // Multiple files with custom styling
18
+ * <FileUpload.Root multiple maxFiles={5}>
19
+ * <FileUpload.Input />
20
+ * <FileUpload.Dropzone>
21
+ * <UploadIcon />
22
+ * <p>Drag and drop files here</p>
23
+ * </FileUpload.Dropzone>
24
+ * </FileUpload.Root>
25
+ * ```
26
+ */
27
+
28
+ export { FileUploadRoot, type FileUploadRootProps } from "./file-upload-root.js";
29
+ export { FileUploadDropzone, type FileUploadDropzoneProps } from "./file-upload-dropzone.js";
30
+ export { FileUploadInput, type FileUploadInputProps } from "./file-upload-input.js";
31
+ export { FileUploadItem, type FileUploadItemProps, formatBytes } from "./file-upload-item.js";
32
+ export {
33
+ useFileUploadContext,
34
+ type FileUploadContextValue,
35
+ } from "./file-upload-context.js";
36
+
37
+ // Re-export types from primitives
38
+ export type { FileInfo, FileUploadError } from "@hypoth-ui/primitives-dom";
39
+
40
+ export const FileUpload = {
41
+ Root: FileUploadRoot,
42
+ Dropzone: FileUploadDropzone,
43
+ Input: FileUploadInput,
44
+ Item: FileUploadItem,
45
+ } as const;
46
+
47
+ import { FileUploadDropzone } from "./file-upload-dropzone.js";
48
+ import { FileUploadInput } from "./file-upload-input.js";
49
+ import { FileUploadItem } from "./file-upload-item.js";
50
+ import { FileUploadRoot } from "./file-upload-root.js";
@@ -0,0 +1,380 @@
1
+ /**
2
+ * FileUpload component for file selection with drag-and-drop support.
3
+ *
4
+ * @element ds-file-upload
5
+ * @fires ds:change - Fired when files change with { files }
6
+ * @fires ds:error - Fired on validation error
7
+ *
8
+ * @slot - Custom dropzone content
9
+ * @slot file-list - Custom file list rendering
10
+ *
11
+ * @example
12
+ * ```html
13
+ * <!-- Basic file upload -->
14
+ * <ds-file-upload accept="image/*" max-size="5242880"></ds-file-upload>
15
+ *
16
+ * <!-- Multiple files -->
17
+ * <ds-file-upload multiple max-files="5"></ds-file-upload>
18
+ *
19
+ * <!-- Custom content -->
20
+ * <ds-file-upload>
21
+ * <span slot="dropzone">Drop your files here</span>
22
+ * </ds-file-upload>
23
+ * ```
24
+ */
25
+
26
+ import {
27
+ type FileInfo,
28
+ type FileUploadBehavior,
29
+ createFileUploadBehavior,
30
+ formatBytes,
31
+ } from "@hypoth-ui/primitives-dom";
32
+ import { html, nothing } from "lit";
33
+ import { property, state } from "lit/decorators.js";
34
+ import { DSElement } from "../../base/ds-element.js";
35
+ import { StandardEvents, emitEvent } from "../../events/emit.js";
36
+ import { define } from "../../registry/define.js";
37
+
38
+ export class DsFileUpload extends DSElement {
39
+ /** Accepted file types (MIME types or extensions) */
40
+ @property({ type: String, reflect: true })
41
+ accept = "";
42
+
43
+ /** Maximum number of files */
44
+ @property({ type: Number, reflect: true, attribute: "max-files" })
45
+ maxFiles = Number.POSITIVE_INFINITY;
46
+
47
+ /** Maximum file size in bytes */
48
+ @property({ type: Number, reflect: true, attribute: "max-size" })
49
+ maxSize: number | undefined = undefined;
50
+
51
+ /** Minimum file size in bytes */
52
+ @property({ type: Number, reflect: true, attribute: "min-size" })
53
+ minSize: number | undefined = undefined;
54
+
55
+ /** Allow multiple files */
56
+ @property({ type: Boolean, reflect: true })
57
+ multiple = false;
58
+
59
+ /** Disabled state */
60
+ @property({ type: Boolean, reflect: true })
61
+ disabled = false;
62
+
63
+ /** Show file list */
64
+ @property({ type: Boolean, attribute: "show-file-list" })
65
+ showFileList = true;
66
+
67
+ /** Show remove buttons in file list */
68
+ @property({ type: Boolean, attribute: "show-remove" })
69
+ showRemove = true;
70
+
71
+ /** ARIA label */
72
+ @property({ type: String, attribute: "aria-label" })
73
+ override ariaLabel: string | null = null;
74
+
75
+ @state()
76
+ private behavior: FileUploadBehavior | null = null;
77
+
78
+ @state()
79
+ private files: FileInfo[] = [];
80
+
81
+ @state()
82
+ private isDragging = false;
83
+
84
+ @state()
85
+ private announcement = "";
86
+
87
+ private inputRef: HTMLInputElement | null = null;
88
+ private announcementTimeout: number | null = null;
89
+
90
+ override connectedCallback(): void {
91
+ super.connectedCallback();
92
+ this.initBehavior();
93
+ }
94
+
95
+ override disconnectedCallback(): void {
96
+ super.disconnectedCallback();
97
+ this.behavior?.destroy();
98
+ this.behavior = null;
99
+ if (this.announcementTimeout) {
100
+ clearTimeout(this.announcementTimeout);
101
+ }
102
+ }
103
+
104
+ private announce(message: string): void {
105
+ // Clear previous announcement timeout
106
+ if (this.announcementTimeout) {
107
+ clearTimeout(this.announcementTimeout);
108
+ }
109
+
110
+ // Set the announcement
111
+ this.announcement = message;
112
+
113
+ // Clear the announcement after a delay to allow repeat announcements
114
+ this.announcementTimeout = window.setTimeout(() => {
115
+ this.announcement = "";
116
+ }, 1000);
117
+ }
118
+
119
+ override updated(changedProperties: Map<string, unknown>): void {
120
+ super.updated(changedProperties);
121
+
122
+ if (
123
+ changedProperties.has("accept") ||
124
+ changedProperties.has("maxFiles") ||
125
+ changedProperties.has("maxSize") ||
126
+ changedProperties.has("minSize") ||
127
+ changedProperties.has("multiple") ||
128
+ changedProperties.has("disabled")
129
+ ) {
130
+ this.initBehavior();
131
+ }
132
+ }
133
+
134
+ private initBehavior(): void {
135
+ // Preserve existing files
136
+ const existingFiles = this.files;
137
+ this.behavior?.destroy();
138
+
139
+ this.behavior = createFileUploadBehavior({
140
+ accept: this.accept,
141
+ maxFiles: this.maxFiles,
142
+ maxSize: this.maxSize,
143
+ minSize: this.minSize,
144
+ multiple: this.multiple,
145
+ disabled: this.disabled,
146
+ onFilesChange: (files) => {
147
+ const previousCount = this.files.length;
148
+ const newCount = files.length;
149
+ this.files = files;
150
+
151
+ // Announce file changes for screen readers
152
+ if (newCount > previousCount) {
153
+ const added = newCount - previousCount;
154
+ const lastFile = files[files.length - 1];
155
+ this.announce(
156
+ added === 1 && lastFile ? `${lastFile.name} added` : `${added} files added`
157
+ );
158
+ }
159
+
160
+ emitEvent(this, StandardEvents.CHANGE, { detail: { files } });
161
+ },
162
+ onError: (error) => {
163
+ emitEvent(this, "ds:error", { detail: error });
164
+ },
165
+ });
166
+
167
+ // Restore files if re-initializing
168
+ if (existingFiles.length > 0) {
169
+ // Files are managed by behavior, need to re-add them
170
+ }
171
+ }
172
+
173
+ /** Public method to get current files */
174
+ getFiles(): FileInfo[] {
175
+ return this.files;
176
+ }
177
+
178
+ /** Public method to clear all files */
179
+ clearFiles(): void {
180
+ this.behavior?.clearFiles();
181
+ }
182
+
183
+ /** Public method to remove a file */
184
+ removeFile(id: string): void {
185
+ this.behavior?.removeFile(id);
186
+ }
187
+
188
+ private handleClick(): void {
189
+ if (this.disabled) return;
190
+ this.inputRef?.click();
191
+ }
192
+
193
+ private handleInputChange(event: Event): void {
194
+ const input = event.target as HTMLInputElement;
195
+ if (input.files && input.files.length > 0) {
196
+ this.behavior?.addFiles(input.files);
197
+ // Reset input so the same file can be selected again
198
+ input.value = "";
199
+ }
200
+ }
201
+
202
+ private handleKeyDown(event: KeyboardEvent): void {
203
+ if (this.behavior?.handleKeyDown(event)) {
204
+ this.inputRef?.click();
205
+ }
206
+ }
207
+
208
+ private handleDragEnter(event: DragEvent): void {
209
+ this.behavior?.handleDragEnter(event);
210
+ this.isDragging = this.behavior?.state.isDragging ?? false;
211
+ }
212
+
213
+ private handleDragLeave(event: DragEvent): void {
214
+ this.behavior?.handleDragLeave(event);
215
+ this.isDragging = this.behavior?.state.isDragging ?? false;
216
+ }
217
+
218
+ private handleDragOver(event: DragEvent): void {
219
+ this.behavior?.handleDragOver(event);
220
+ }
221
+
222
+ private handleDrop(event: DragEvent): void {
223
+ this.behavior?.handleDrop(event);
224
+ this.isDragging = false;
225
+ }
226
+
227
+ private handleRemoveFile(id: string): void {
228
+ const file = this.files.find((f) => f.id === id);
229
+ if (file) {
230
+ this.announce(`${file.name} removed`);
231
+ }
232
+ this.behavior?.removeFile(id);
233
+ }
234
+
235
+ private renderFileItem(file: FileInfo) {
236
+ return html`
237
+ <div
238
+ class="ds-file-upload__file"
239
+ data-status=${file.status}
240
+ >
241
+ ${
242
+ file.preview
243
+ ? html`<img
244
+ class="ds-file-upload__preview"
245
+ src=${file.preview}
246
+ alt=${file.name}
247
+ />`
248
+ : html`<span class="ds-file-upload__file-icon" aria-hidden="true">📄</span>`
249
+ }
250
+
251
+ <div class="ds-file-upload__file-info">
252
+ <span class="ds-file-upload__file-name">${file.name}</span>
253
+ <span class="ds-file-upload__file-size">${formatBytes(file.size)}</span>
254
+ ${
255
+ file.status === "error"
256
+ ? html`<span class="ds-file-upload__file-error">${file.error}</span>`
257
+ : nothing
258
+ }
259
+ ${
260
+ file.status === "uploading"
261
+ ? html`<progress
262
+ class="ds-file-upload__progress"
263
+ value=${file.progress}
264
+ max="100"
265
+ ></progress>`
266
+ : nothing
267
+ }
268
+ </div>
269
+
270
+ ${
271
+ this.showRemove && !this.disabled
272
+ ? html`
273
+ <button
274
+ type="button"
275
+ class="ds-file-upload__remove"
276
+ aria-label="Remove ${file.name}"
277
+ @click=${() => this.handleRemoveFile(file.id)}
278
+ >
279
+ ×
280
+ </button>
281
+ `
282
+ : nothing
283
+ }
284
+ </div>
285
+ `;
286
+ }
287
+
288
+ override render() {
289
+ if (!this.behavior) return nothing;
290
+
291
+ const dropzoneProps = this.behavior.getDropzoneProps();
292
+ const inputProps = this.behavior.getInputProps();
293
+
294
+ return html`
295
+ <div class="ds-file-upload" data-disabled=${this.disabled || nothing}>
296
+ <div
297
+ class="ds-file-upload__dropzone"
298
+ role=${dropzoneProps.role}
299
+ tabindex=${dropzoneProps.tabIndex}
300
+ aria-label=${this.ariaLabel || dropzoneProps["aria-label"]}
301
+ aria-disabled=${dropzoneProps["aria-disabled"] ?? nothing}
302
+ data-dragging=${this.isDragging || nothing}
303
+ @click=${this.handleClick}
304
+ @keydown=${this.handleKeyDown}
305
+ @dragenter=${this.handleDragEnter}
306
+ @dragleave=${this.handleDragLeave}
307
+ @dragover=${this.handleDragOver}
308
+ @drop=${this.handleDrop}
309
+ >
310
+ <slot>
311
+ <span class="ds-file-upload__icon" aria-hidden="true">📁</span>
312
+ <span class="ds-file-upload__text">
313
+ ${
314
+ this.isDragging
315
+ ? "Drop files here"
316
+ : this.multiple
317
+ ? "Drop files here or click to upload"
318
+ : "Drop file here or click to upload"
319
+ }
320
+ </span>
321
+ ${
322
+ this.accept
323
+ ? html`<span class="ds-file-upload__hint">Accepted: ${this.accept}</span>`
324
+ : nothing
325
+ }
326
+ ${
327
+ this.maxSize
328
+ ? html`<span class="ds-file-upload__hint">Max size: ${formatBytes(this.maxSize)}</span>`
329
+ : nothing
330
+ }
331
+ </slot>
332
+
333
+ <input
334
+ class="ds-file-upload__input"
335
+ type=${inputProps.type}
336
+ accept=${inputProps.accept ?? nothing}
337
+ ?multiple=${inputProps.multiple}
338
+ ?disabled=${inputProps.disabled}
339
+ aria-hidden=${inputProps["aria-hidden"]}
340
+ tabindex=${inputProps.tabIndex}
341
+ @change=${this.handleInputChange}
342
+ .ref=${(el: HTMLInputElement | null) => {
343
+ this.inputRef = el;
344
+ }}
345
+ />
346
+ </div>
347
+
348
+ ${
349
+ this.showFileList && this.files.length > 0
350
+ ? html`
351
+ <div class="ds-file-upload__list" role="list" aria-label="Selected files">
352
+ <slot name="file-list">
353
+ ${this.files.map((file) => this.renderFileItem(file))}
354
+ </slot>
355
+ </div>
356
+ `
357
+ : nothing
358
+ }
359
+
360
+ <!-- ARIA live region for announcements -->
361
+ <div
362
+ role="status"
363
+ aria-live="polite"
364
+ aria-atomic="true"
365
+ class="ds-file-upload__announcer"
366
+ >
367
+ ${this.announcement}
368
+ </div>
369
+ </div>
370
+ `;
371
+ }
372
+ }
373
+
374
+ define("ds-file-upload", DsFileUpload);
375
+
376
+ declare global {
377
+ interface HTMLElementTagNameMap {
378
+ "ds-file-upload": DsFileUpload;
379
+ }
380
+ }
@@ -0,0 +1 @@
1
+ export { DsFileUpload } from "./file-upload.js";