tembro 2.0.6 → 2.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 (341) hide show
  1. package/dist/components/calendar/calendar.cjs +1 -1
  2. package/dist/components/calendar/calendar.d.ts +2 -1
  3. package/dist/components/calendar/calendar.js +126 -122
  4. package/dist/components/calendar/date-range-picker.cjs +1 -1
  5. package/dist/components/calendar/date-range-picker.d.ts +2 -1
  6. package/dist/components/calendar/date-range-picker.js +39 -39
  7. package/dist/components/charts/charts.cjs +1 -1
  8. package/dist/components/charts/charts.d.ts +13 -4
  9. package/dist/components/charts/charts.js +76 -46
  10. package/dist/components/charts/progress-ring.cjs +1 -1
  11. package/dist/components/charts/progress-ring.d.ts +3 -1
  12. package/dist/components/charts/progress-ring.js +11 -10
  13. package/dist/components/data-table/data-table-column-visibility-menu.cjs +1 -1
  14. package/dist/components/data-table/data-table-column-visibility-menu.d.ts +4 -1
  15. package/dist/components/data-table/data-table-column-visibility-menu.js +36 -8
  16. package/dist/components/data-table/data-table-toolbar.cjs +1 -1
  17. package/dist/components/data-table/data-table-toolbar.js +12 -13
  18. package/dist/components/display/carousel.cjs +1 -1
  19. package/dist/components/display/carousel.d.ts +9 -1
  20. package/dist/components/display/carousel.js +123 -81
  21. package/dist/components/display/code-block.cjs +2 -2
  22. package/dist/components/display/code-block.d.ts +2 -1
  23. package/dist/components/display/code-block.js +17 -12
  24. package/dist/components/display/delta-badge.cjs +1 -1
  25. package/dist/components/display/delta-badge.d.ts +2 -1
  26. package/dist/components/display/delta-badge.js +11 -7
  27. package/dist/components/display/kanban.cjs +1 -1
  28. package/dist/components/display/kanban.d.ts +4 -1
  29. package/dist/components/display/kanban.js +60 -33
  30. package/dist/components/display/metric-grid.cjs +1 -1
  31. package/dist/components/display/metric-grid.d.ts +1 -0
  32. package/dist/components/display/metric-grid.js +1 -1
  33. package/dist/components/display/qr-code.cjs +1 -1
  34. package/dist/components/display/qr-code.d.ts +2 -1
  35. package/dist/components/display/qr-code.js +19 -16
  36. package/dist/components/display/tag.cjs +1 -1
  37. package/dist/components/display/tag.d.ts +2 -1
  38. package/dist/components/display/tag.js +28 -22
  39. package/dist/components/display/tree-view.cjs +1 -1
  40. package/dist/components/display/tree-view.d.ts +9 -1
  41. package/dist/components/display/tree-view.js +104 -40
  42. package/dist/components/display/trend-card.cjs +1 -1
  43. package/dist/components/display/trend-card.js +27 -41
  44. package/dist/components/feedback/alert.cjs +1 -1
  45. package/dist/components/feedback/alert.d.ts +2 -1
  46. package/dist/components/feedback/alert.js +6 -6
  47. package/dist/components/feedback/loading-state.cjs +1 -1
  48. package/dist/components/feedback/loading-state.d.ts +3 -1
  49. package/dist/components/feedback/loading-state.js +23 -10
  50. package/dist/components/feedback/page-state.cjs +1 -1
  51. package/dist/components/feedback/page-state.d.ts +2 -1
  52. package/dist/components/feedback/page-state.js +7 -4
  53. package/dist/components/form/form-field-shell.cjs +1 -1
  54. package/dist/components/form/form-field-shell.d.ts +4 -2
  55. package/dist/components/form/form-field-shell.js +57 -41
  56. package/dist/components/form/form-input.cjs +1 -1
  57. package/dist/components/form/form-input.js +2 -0
  58. package/dist/components/form/form-select.cjs +1 -1
  59. package/dist/components/form/form-select.js +2 -0
  60. package/dist/components/form/form-switch.cjs +1 -1
  61. package/dist/components/form/form-switch.d.ts +1 -1
  62. package/dist/components/form/form-switch.js +58 -54
  63. package/dist/components/form/form-textarea.cjs +1 -1
  64. package/dist/components/form/form-textarea.d.ts +1 -1
  65. package/dist/components/form/form-textarea.js +29 -27
  66. package/dist/components/layout/app-sidebar.cjs +1 -1
  67. package/dist/components/layout/app-sidebar.d.ts +1 -0
  68. package/dist/components/layout/app-sidebar.js +36 -36
  69. package/dist/components/layout/breadcrumbs.cjs +1 -1
  70. package/dist/components/layout/breadcrumbs.d.ts +2 -1
  71. package/dist/components/layout/breadcrumbs.js +7 -7
  72. package/dist/components/modern/calendar-scheduler.cjs +1 -1
  73. package/dist/components/modern/calendar-scheduler.d.ts +5 -1
  74. package/dist/components/modern/calendar-scheduler.js +47 -28
  75. package/dist/components/modern/dual-list-picker.cjs +1 -1
  76. package/dist/components/modern/dual-list-picker.d.ts +5 -1
  77. package/dist/components/modern/dual-list-picker.js +81 -23
  78. package/dist/components/modern/rich-text-editor.cjs +1 -1
  79. package/dist/components/modern/rich-text-editor.d.ts +3 -1
  80. package/dist/components/modern/rich-text-editor.js +12 -9
  81. package/dist/components/modern/tour.cjs +1 -1
  82. package/dist/components/modern/tour.d.ts +5 -1
  83. package/dist/components/modern/tour.js +57 -35
  84. package/dist/components/navigation/command-bar.cjs +1 -1
  85. package/dist/components/navigation/command-bar.d.ts +3 -1
  86. package/dist/components/navigation/command-bar.js +10 -7
  87. package/dist/components/navigation/demo.cjs +1 -1
  88. package/dist/components/navigation/demo.js +0 -1
  89. package/dist/components/navigation/nav-tabs.cjs +1 -1
  90. package/dist/components/navigation/nav-tabs.js +39 -27
  91. package/dist/components/navigation/page-tabs.cjs +1 -1
  92. package/dist/components/navigation/page-tabs.js +41 -30
  93. package/dist/components/navigation/pagination.cjs +1 -1
  94. package/dist/components/navigation/pagination.d.ts +7 -1
  95. package/dist/components/navigation/pagination.js +48 -25
  96. package/dist/components/notifications/notification-center.cjs +1 -1
  97. package/dist/components/notifications/notification-center.d.ts +3 -1
  98. package/dist/components/notifications/notification-center.js +56 -38
  99. package/dist/components/notifications/toast.cjs +1 -1
  100. package/dist/components/notifications/toast.d.ts +1 -0
  101. package/dist/components/notifications/toast.js +4 -0
  102. package/dist/components/overlay/alert-dialog.cjs +1 -1
  103. package/dist/components/overlay/alert-dialog.js +1 -1
  104. package/dist/components/overlay/confirm-dialog.cjs +1 -1
  105. package/dist/components/overlay/confirm-dialog.d.ts +4 -2
  106. package/dist/components/overlay/confirm-dialog.js +31 -22
  107. package/dist/components/overlay/drawer.cjs +1 -1
  108. package/dist/components/overlay/drawer.d.ts +3 -1
  109. package/dist/components/overlay/drawer.js +24 -16
  110. package/dist/components/ui/accordion/index.d.ts +4 -0
  111. package/dist/components/ui/badge/index.cjs +1 -1
  112. package/dist/components/ui/badge/index.d.ts +1 -1
  113. package/dist/components/ui/badge/index.js +2 -1
  114. package/dist/components/ui/button/index.cjs +1 -1
  115. package/dist/components/ui/button/index.d.ts +2 -1
  116. package/dist/components/ui/button/index.js +13 -10
  117. package/dist/components/ui/dialog/index.cjs +1 -1
  118. package/dist/components/ui/dialog/index.js +2 -1
  119. package/dist/components/ui/hover-card.cjs +1 -1
  120. package/dist/components/ui/hover-card.d.ts +11 -3
  121. package/dist/components/ui/hover-card.js +29 -24
  122. package/dist/components/ui/input/index.d.ts +1 -1
  123. package/dist/components/ui/popover/index.cjs +1 -1
  124. package/dist/components/ui/popover/index.d.ts +2 -2
  125. package/dist/components/ui/popover/index.js +6 -5
  126. package/dist/components/ui/radio-group/index.cjs +1 -1
  127. package/dist/components/ui/radio-group/index.d.ts +5 -1
  128. package/dist/components/ui/radio-group/index.js +50 -16
  129. package/dist/components/ui/right-click-menu/index.cjs +1 -1
  130. package/dist/components/ui/right-click-menu/index.d.ts +5 -1
  131. package/dist/components/ui/right-click-menu/index.js +77 -23
  132. package/dist/components/ui/segmented-control/index.cjs +1 -1
  133. package/dist/components/ui/segmented-control/index.d.ts +2 -1
  134. package/dist/components/ui/segmented-control/index.js +23 -11
  135. package/dist/components/ui/select/index.cjs +1 -1
  136. package/dist/components/ui/select/index.d.ts +8 -1
  137. package/dist/components/ui/select/index.js +40 -35
  138. package/dist/components/ui/switch/index.cjs +1 -1
  139. package/dist/components/ui/switch/index.d.ts +18 -0
  140. package/dist/components/ui/switch/index.js +47 -17
  141. package/dist/components/ui/tabs/index.cjs +1 -1
  142. package/dist/components/ui/tabs/index.d.ts +14 -4
  143. package/dist/components/ui/tabs/index.js +11 -8
  144. package/dist/components/ui/textarea/index.cjs +1 -1
  145. package/dist/components/ui/textarea/index.d.ts +10 -2
  146. package/dist/components/ui/textarea/index.js +37 -8
  147. package/dist/components/ui/textarea.cjs +1 -0
  148. package/dist/components/ui/textarea.d.ts +1 -0
  149. package/dist/components/ui/textarea.js +2 -0
  150. package/dist/components/ui/tooltip/index.cjs +1 -1
  151. package/dist/components/ui/tooltip/index.d.ts +11 -3
  152. package/dist/components/ui/tooltip/index.js +41 -20
  153. package/dist/components/upload/file-upload.cjs +1 -1
  154. package/dist/components/upload/file-upload.d.ts +8 -1
  155. package/dist/components/upload/file-upload.js +132 -100
  156. package/dist/components/upload/image-upload.cjs +1 -1
  157. package/dist/components/upload/image-upload.d.ts +5 -1
  158. package/dist/components/upload/image-upload.js +34 -9
  159. package/dist/index.cjs +1 -1
  160. package/dist/index.js +31 -31
  161. package/dist/public-component-surface.cjs +1 -1
  162. package/dist/public-component-surface.js +15 -0
  163. package/dist/showcase/component-api-schema.cjs +1 -1
  164. package/dist/showcase/component-api-schema.js +187 -10
  165. package/dist/showcase/fallback.cjs +3 -3
  166. package/dist/showcase/fallback.js +221 -243
  167. package/dist/showcase/premium/alert/showcase.cjs +1 -1
  168. package/dist/showcase/premium/alert/showcase.js +1 -0
  169. package/dist/showcase/premium/app-sidebar/showcase.cjs +1 -1
  170. package/dist/showcase/premium/app-sidebar/showcase.js +1 -0
  171. package/dist/showcase/premium/badge/showcase.cjs +1 -1
  172. package/dist/showcase/premium/badge/showcase.js +7 -0
  173. package/dist/showcase/premium/badge/types.d.ts +1 -1
  174. package/dist/showcase/premium/breadcrumbs/showcase.cjs +1 -1
  175. package/dist/showcase/premium/breadcrumbs/showcase.js +12 -1
  176. package/dist/showcase/premium/button/mock.cjs +1 -4
  177. package/dist/showcase/premium/button/mock.js +1 -1
  178. package/dist/showcase/premium/button/showcase.cjs +1 -1
  179. package/dist/showcase/premium/button/showcase.js +2 -2
  180. package/dist/showcase/premium/card/showcase.cjs +1 -1
  181. package/dist/showcase/premium/card/showcase.js +96 -47
  182. package/dist/showcase/premium/carousel/showcase.cjs +1 -1
  183. package/dist/showcase/premium/carousel/showcase.js +76 -49
  184. package/dist/showcase/premium/command-bar/showcase.cjs +1 -1
  185. package/dist/showcase/premium/command-bar/showcase.js +2 -0
  186. package/dist/showcase/premium/data-table/mock.cjs +12 -11
  187. package/dist/showcase/premium/data-table/mock.js +1 -1
  188. package/dist/showcase/premium/file-upload/showcase.cjs +1 -1
  189. package/dist/showcase/premium/file-upload/showcase.js +25 -4
  190. package/dist/showcase/premium/image-upload/showcase.cjs +1 -1
  191. package/dist/showcase/premium/image-upload/showcase.js +4 -1
  192. package/dist/showcase/premium/index.cjs +1 -1
  193. package/dist/showcase/premium/index.js +396 -402
  194. package/dist/showcase/premium/loading-state/showcase.cjs +1 -1
  195. package/dist/showcase/premium/loading-state/showcase.js +3 -1
  196. package/dist/showcase/premium/metric-grid/showcase.cjs +1 -1
  197. package/dist/showcase/premium/metric-grid/showcase.js +96 -28
  198. package/dist/showcase/premium/notification-center/showcase.cjs +1 -1
  199. package/dist/showcase/premium/notification-center/showcase.js +7 -3
  200. package/dist/showcase/premium/page-state/showcase.cjs +1 -1
  201. package/dist/showcase/premium/page-state/showcase.js +5 -1
  202. package/dist/showcase/premium/pagination/showcase.cjs +1 -1
  203. package/dist/showcase/premium/pagination/showcase.js +15 -2
  204. package/dist/showcase/premium/popover/showcase.cjs +1 -1
  205. package/dist/showcase/premium/popover/showcase.js +85 -50
  206. package/dist/showcase/premium/radio-group/showcase.cjs +1 -1
  207. package/dist/showcase/premium/radio-group/showcase.js +27 -4
  208. package/dist/showcase/premium/right-click-menu/showcase.cjs +1 -1
  209. package/dist/showcase/premium/right-click-menu/showcase.js +56 -43
  210. package/dist/showcase/premium/segmented-control/showcase.cjs +1 -1
  211. package/dist/showcase/premium/segmented-control/showcase.js +1 -0
  212. package/dist/showcase/premium/select/data.cjs +1 -1
  213. package/dist/showcase/premium/select/data.js +69 -34
  214. package/dist/showcase/premium/select/mock.cjs +19 -6
  215. package/dist/showcase/premium/select/mock.js +3 -2
  216. package/dist/showcase/premium/select/showcase.cjs +1 -1
  217. package/dist/showcase/premium/select/showcase.js +43 -18
  218. package/dist/showcase/premium/select/types.d.ts +3 -0
  219. package/dist/showcase/premium/statistic/showcase.cjs +1 -1
  220. package/dist/showcase/premium/statistic/showcase.js +57 -25
  221. package/dist/showcase/premium/switch/showcase.cjs +1 -1
  222. package/dist/showcase/premium/switch/showcase.js +25 -14
  223. package/dist/showcase/premium/tabs/showcase.cjs +1 -1
  224. package/dist/showcase/premium/tabs/showcase.js +171 -48
  225. package/dist/showcase/premium/textarea/mock.cjs +12 -4
  226. package/dist/showcase/premium/textarea/mock.js +1 -1
  227. package/dist/showcase/premium/textarea/showcase.cjs +1 -1
  228. package/dist/showcase/premium/textarea/showcase.js +8 -3
  229. package/dist/showcase/premium/toast/showcase.cjs +1 -1
  230. package/dist/showcase/premium/toast/showcase.js +32 -6
  231. package/dist/showcase/premium/types.d.ts +1 -1
  232. package/dist/showcase/registry-quality.cjs +1 -1
  233. package/dist/showcase/registry-quality.js +1 -0
  234. package/dist/showcase/render-registry-preview.cjs +1 -1
  235. package/dist/showcase/render-registry-preview.js +57 -79
  236. package/dist/showcase/site-data.cjs +1 -1
  237. package/dist/showcase/site-data.js +60 -21
  238. package/dist/showcase/tembro-registry.json.cjs +1 -1
  239. package/dist/showcase/tembro-registry.json.js +34 -20
  240. package/dist/showcase/types.d.ts +1 -1
  241. package/package.json +1 -1
  242. package/packages/cli/dist/index.cjs +31 -31
  243. package/packages/cli/vendor/src/components/calendar/calendar.tsx +59 -50
  244. package/packages/cli/vendor/src/components/calendar/date-range-picker.tsx +7 -4
  245. package/packages/cli/vendor/src/components/charts/charts.tsx +109 -66
  246. package/packages/cli/vendor/src/components/charts/progress-ring.tsx +37 -26
  247. package/packages/cli/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +41 -22
  248. package/packages/cli/vendor/src/components/data-table/data-table-toolbar.tsx +8 -6
  249. package/packages/cli/vendor/src/components/display/carousel.tsx +84 -3
  250. package/packages/cli/vendor/src/components/display/code-block.tsx +22 -2
  251. package/packages/cli/vendor/src/components/display/delta-badge.tsx +11 -11
  252. package/packages/cli/vendor/src/components/display/kanban.tsx +103 -57
  253. package/packages/cli/vendor/src/components/display/metric-grid.tsx +16 -13
  254. package/packages/cli/vendor/src/components/display/qr-code.tsx +17 -15
  255. package/packages/cli/vendor/src/components/display/tag.tsx +39 -21
  256. package/packages/cli/vendor/src/components/display/tree-view.tsx +221 -84
  257. package/packages/cli/vendor/src/components/display/trend-card.tsx +6 -40
  258. package/packages/cli/vendor/src/components/feedback/alert.tsx +3 -1
  259. package/packages/cli/vendor/src/components/feedback/loading-state.tsx +33 -17
  260. package/packages/cli/vendor/src/components/feedback/page-state.tsx +8 -7
  261. package/packages/cli/vendor/src/components/form/form-field-shell.tsx +37 -8
  262. package/packages/cli/vendor/src/components/form/form-input.tsx +2 -0
  263. package/packages/cli/vendor/src/components/form/form-select.tsx +2 -0
  264. package/packages/cli/vendor/src/components/form/form-switch.tsx +9 -3
  265. package/packages/cli/vendor/src/components/form/form-textarea.tsx +6 -2
  266. package/packages/cli/vendor/src/components/layout/app-sidebar.tsx +3 -1
  267. package/packages/cli/vendor/src/components/layout/breadcrumbs.tsx +3 -1
  268. package/packages/cli/vendor/src/components/modern/calendar-scheduler.tsx +28 -13
  269. package/packages/cli/vendor/src/components/modern/dual-list-picker.tsx +103 -35
  270. package/packages/cli/vendor/src/components/modern/rich-text-editor.tsx +25 -22
  271. package/packages/cli/vendor/src/components/modern/tour.tsx +47 -29
  272. package/packages/cli/vendor/src/components/navigation/command-bar.tsx +6 -0
  273. package/packages/cli/vendor/src/components/navigation/demo.tsx +0 -1
  274. package/packages/cli/vendor/src/components/navigation/nav-tabs.tsx +38 -29
  275. package/packages/cli/vendor/src/components/navigation/page-tabs.tsx +35 -23
  276. package/packages/cli/vendor/src/components/navigation/pagination.tsx +65 -31
  277. package/packages/cli/vendor/src/components/notifications/notification-center.tsx +64 -39
  278. package/packages/cli/vendor/src/components/notifications/toast.tsx +23 -21
  279. package/packages/cli/vendor/src/components/overlay/alert-dialog.tsx +1 -1
  280. package/packages/cli/vendor/src/components/overlay/confirm-dialog.tsx +45 -22
  281. package/packages/cli/vendor/src/components/overlay/drawer.tsx +18 -5
  282. package/packages/cli/vendor/src/components/ui/accordion/index.tsx +4 -0
  283. package/packages/cli/vendor/src/components/ui/badge/index.tsx +2 -1
  284. package/packages/cli/vendor/src/components/ui/button/index.tsx +11 -5
  285. package/packages/cli/vendor/src/components/ui/dialog/index.tsx +2 -1
  286. package/packages/cli/vendor/src/components/ui/hover-card.tsx +54 -28
  287. package/packages/cli/vendor/src/components/ui/popover/index.tsx +10 -8
  288. package/packages/cli/vendor/src/components/ui/radio-group/index.tsx +55 -6
  289. package/packages/cli/vendor/src/components/ui/right-click-menu/index.tsx +120 -9
  290. package/packages/cli/vendor/src/components/ui/segmented-control/index.tsx +29 -3
  291. package/packages/cli/vendor/src/components/ui/select/index.tsx +64 -31
  292. package/packages/cli/vendor/src/components/ui/switch/index.tsx +71 -6
  293. package/packages/cli/vendor/src/components/ui/tabs/index.tsx +22 -6
  294. package/packages/cli/vendor/src/components/ui/textarea/index.tsx +105 -15
  295. package/packages/cli/vendor/src/components/ui/textarea.tsx +2 -0
  296. package/packages/cli/vendor/src/components/ui/tooltip/index.tsx +67 -22
  297. package/packages/cli/vendor/src/components/upload/file-upload.tsx +120 -57
  298. package/packages/cli/vendor/src/components/upload/image-upload.tsx +71 -24
  299. package/packages/cli/vendor/src/public-component-surface.ts +3 -0
  300. package/packages/cli/vendor/src/showcase/component-api-schema.ts +79 -41
  301. package/packages/cli/vendor/src/showcase/fallback.tsx +4 -19
  302. package/packages/cli/vendor/src/showcase/premium/alert/showcase.tsx +1 -0
  303. package/packages/cli/vendor/src/showcase/premium/app-sidebar/showcase.tsx +1 -0
  304. package/packages/cli/vendor/src/showcase/premium/badge/showcase.tsx +4 -2
  305. package/packages/cli/vendor/src/showcase/premium/badge/types.ts +1 -1
  306. package/packages/cli/vendor/src/showcase/premium/breadcrumbs/showcase.tsx +3 -1
  307. package/packages/cli/vendor/src/showcase/premium/button/mock.ts +2 -5
  308. package/packages/cli/vendor/src/showcase/premium/button/showcase.tsx +2 -2
  309. package/packages/cli/vendor/src/showcase/premium/card/showcase.tsx +26 -1
  310. package/packages/cli/vendor/src/showcase/premium/carousel/showcase.tsx +19 -3
  311. package/packages/cli/vendor/src/showcase/premium/command-bar/showcase.tsx +1 -1
  312. package/packages/cli/vendor/src/showcase/premium/data-table/mock.ts +26 -25
  313. package/packages/cli/vendor/src/showcase/premium/file-upload/showcase.tsx +21 -1
  314. package/packages/cli/vendor/src/showcase/premium/image-upload/showcase.tsx +4 -1
  315. package/packages/cli/vendor/src/showcase/premium/index.ts +0 -2
  316. package/packages/cli/vendor/src/showcase/premium/loading-state/showcase.tsx +1 -1
  317. package/packages/cli/vendor/src/showcase/premium/metric-grid/showcase.tsx +36 -4
  318. package/packages/cli/vendor/src/showcase/premium/notification-center/showcase.tsx +4 -3
  319. package/packages/cli/vendor/src/showcase/premium/page-state/showcase.tsx +1 -0
  320. package/packages/cli/vendor/src/showcase/premium/pagination/showcase.tsx +13 -1
  321. package/packages/cli/vendor/src/showcase/premium/popover/showcase.tsx +27 -11
  322. package/packages/cli/vendor/src/showcase/premium/radio-group/showcase.tsx +13 -1
  323. package/packages/cli/vendor/src/showcase/premium/right-click-menu/showcase.tsx +8 -5
  324. package/packages/cli/vendor/src/showcase/premium/segmented-control/showcase.tsx +1 -0
  325. package/packages/cli/vendor/src/showcase/premium/select/data.ts +12 -3
  326. package/packages/cli/vendor/src/showcase/premium/select/mock.ts +4 -4
  327. package/packages/cli/vendor/src/showcase/premium/select/showcase.tsx +22 -1
  328. package/packages/cli/vendor/src/showcase/premium/select/types.ts +3 -0
  329. package/packages/cli/vendor/src/showcase/premium/statistic/showcase.tsx +10 -1
  330. package/packages/cli/vendor/src/showcase/premium/switch/showcase.tsx +15 -6
  331. package/packages/cli/vendor/src/showcase/premium/tabs/showcase.tsx +48 -1
  332. package/packages/cli/vendor/src/showcase/premium/textarea/mock.ts +12 -4
  333. package/packages/cli/vendor/src/showcase/premium/textarea/showcase.tsx +5 -0
  334. package/packages/cli/vendor/src/showcase/premium/toast/showcase.tsx +17 -1
  335. package/packages/cli/vendor/src/showcase/premium/types.ts +1 -1
  336. package/packages/cli/vendor/src/showcase/registry-quality.ts +3 -2
  337. package/packages/cli/vendor/src/showcase/render-registry-preview.tsx +0 -15
  338. package/packages/cli/vendor/src/showcase/site-data.tsx +21 -15
  339. package/packages/cli/vendor/src/showcase/tembro-registry.json +42 -20
  340. package/packages/cli/vendor/src/showcase/types.ts +1 -1
  341. package/registry.json +18 -6
@@ -2,19 +2,109 @@ import * as React from "react"
2
2
 
3
3
  import { cn } from "@/lib/utils"
4
4
 
5
- export type TextareaProps = React.ComponentProps<"textarea">
6
-
7
- function Textarea({ className, ...props }: TextareaProps) {
8
- return (
9
- <textarea
10
- data-slot="textarea"
11
- className={cn(
12
- "flex field-sizing-content min-h-28 w-full rounded-[var(--aui-control-radius,var(--radius-lg))] border border-[color:var(--aui-control-border-strong,var(--input))] bg-[color:var(--aui-control-surface,var(--background))] px-4 py-3.5 text-sm font-medium text-foreground shadow-[var(--aui-shadow-xs,0_1px_2px_rgba(15,23,42,0.04))] transition-[background-color,border-color,box-shadow,color] outline-none placeholder:text-muted-foreground/78 hover:border-[color:var(--aui-control-hover-border,var(--ring))] hover:bg-[color:var(--aui-control-surface-hover,var(--background))] focus-visible:border-[color:var(--ring)] focus-visible:ring-0 focus-visible:shadow-[var(--aui-shadow-xs,0_1px_2px_rgba(15,23,42,0.04)),0_0_0_1px_var(--aui-focus-ring,var(--ring)),0_0_0_5px_var(--aui-focus-ring-soft,transparent)] disabled:cursor-not-allowed disabled:border-[color:color-mix(in_oklch,var(--border),transparent_18%)] disabled:bg-[color:var(--aui-control-surface-disabled,var(--muted))] disabled:text-muted-foreground disabled:shadow-none disabled:opacity-100 aria-invalid:border-destructive aria-invalid:shadow-[0_0_0_1px_color-mix(in_oklch,var(--destructive),transparent_32%)] read-only:bg-[color:var(--aui-control-surface-readonly,var(--muted))]",
13
- className
14
- )}
15
- {...props}
16
- />
17
- )
18
- }
19
-
5
+ export type TextareaProps = React.ComponentProps<"textarea"> & {
6
+ onValueChange?: (value: string) => void
7
+ helperText?: React.ReactNode
8
+ errorText?: React.ReactNode
9
+ showCharacterCount?: boolean
10
+ countFormatter?: (currentLength: number, maxLength?: number) => React.ReactNode
11
+ wrapperClassName?: string
12
+ textareaClassName?: string
13
+ }
14
+
15
+ function getTextareaLength(value: TextareaProps["value"] | TextareaProps["defaultValue"]) {
16
+ if (typeof value === "string") return value.length
17
+ if (typeof value === "number") return String(value).length
18
+ if (Array.isArray(value)) return value.join("").length
19
+ return 0
20
+ }
21
+
22
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
23
+ (
24
+ {
25
+ className,
26
+ value,
27
+ defaultValue,
28
+ onChange,
29
+ onValueChange,
30
+ helperText,
31
+ errorText,
32
+ showCharacterCount = false,
33
+ countFormatter,
34
+ wrapperClassName,
35
+ textareaClassName,
36
+ maxLength,
37
+ "aria-invalid": ariaInvalid,
38
+ ...props
39
+ },
40
+ ref
41
+ ) => {
42
+ const isControlled = value !== undefined
43
+ const [uncontrolledLength, setUncontrolledLength] = React.useState(() => getTextareaLength(defaultValue))
44
+ const currentLength = isControlled ? getTextareaLength(value) : uncontrolledLength
45
+ const helperMessage = errorText ?? helperText
46
+ const invalid = Boolean(errorText) || ariaInvalid === true || ariaInvalid === "true"
47
+
48
+ const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> = (event) => {
49
+ if (!isControlled) {
50
+ setUncontrolledLength(event.target.value.length)
51
+ }
52
+ onChange?.(event)
53
+ onValueChange?.(event.target.value)
54
+ }
55
+
56
+ const textarea = (
57
+ <textarea
58
+ data-slot="textarea"
59
+ value={value}
60
+ defaultValue={isControlled ? undefined : defaultValue}
61
+ maxLength={maxLength}
62
+ aria-invalid={invalid || undefined}
63
+ className={cn(
64
+ "flex field-sizing-content min-h-28 w-full rounded-[var(--aui-control-radius,var(--radius-lg))] border border-[color:var(--aui-control-border-strong,var(--input))] bg-[color:var(--aui-control-surface,var(--background))] px-4 py-3.5 text-sm font-medium text-foreground shadow-[var(--aui-shadow-xs,0_1px_2px_rgba(15,23,42,0.04))] transition-[background-color,border-color,box-shadow,color] outline-none placeholder:text-muted-foreground/78 hover:border-[color:var(--aui-control-hover-border,var(--ring))] hover:bg-[color:var(--aui-control-surface-hover,var(--background))] focus-visible:border-[color:var(--ring)] focus-visible:ring-0 focus-visible:shadow-[var(--aui-shadow-xs,0_1px_2px_rgba(15,23,42,0.04)),0_0_0_1px_var(--aui-focus-ring,var(--ring)),0_0_0_5px_var(--aui-focus-ring-soft,transparent)] disabled:cursor-not-allowed disabled:border-[color:color-mix(in_oklch,var(--border),transparent_18%)] disabled:bg-[color:var(--aui-control-surface-disabled,var(--muted))] disabled:text-muted-foreground disabled:shadow-none disabled:opacity-100 aria-invalid:border-destructive aria-invalid:shadow-[0_0_0_1px_color-mix(in_oklch,var(--destructive),transparent_32%)] read-only:bg-[color:var(--aui-control-surface-readonly,var(--muted))]",
65
+ textareaClassName,
66
+ className
67
+ )}
68
+ onChange={handleChange}
69
+ ref={ref}
70
+ {...props}
71
+ />
72
+ )
73
+
74
+ if (!helperMessage && !showCharacterCount) {
75
+ return textarea
76
+ }
77
+
78
+ return (
79
+ <div data-slot="textarea-field" className={cn("grid gap-1.5", wrapperClassName)}>
80
+ {textarea}
81
+ <div data-slot="textarea-meta" className="flex items-start justify-between gap-3 px-1">
82
+ <div
83
+ data-slot="textarea-helper"
84
+ className={cn(
85
+ "min-w-0 text-xs leading-5",
86
+ errorText ? "text-destructive" : "text-muted-foreground"
87
+ )}
88
+ >
89
+ {helperMessage}
90
+ </div>
91
+ {showCharacterCount ? (
92
+ <div
93
+ data-slot="textarea-count"
94
+ className={cn(
95
+ "shrink-0 text-[11px] font-medium tabular-nums",
96
+ errorText ? "text-destructive" : "text-muted-foreground"
97
+ )}
98
+ >
99
+ {countFormatter?.(currentLength, maxLength) ??
100
+ (typeof maxLength === "number" ? `${currentLength}/${maxLength}` : currentLength)}
101
+ </div>
102
+ ) : null}
103
+ </div>
104
+ </div>
105
+ )
106
+ }
107
+ )
108
+ Textarea.displayName = "Textarea"
109
+
20
110
  export { Textarea }
@@ -0,0 +1,2 @@
1
+ export * from "./textarea/index"
2
+
@@ -1,37 +1,82 @@
1
1
  import * as React from "react"
2
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
2
3
 
3
4
  import { cn } from "@/lib/utils"
4
5
 
5
- export type TooltipProps = Omit<React.ComponentProps<"span">, "content"> & {
6
+ export type TooltipProps = Omit<TooltipPrimitive.Root.Props, "children"> &
7
+ Omit<React.ComponentProps<"span">, "content" | "children"> & {
6
8
  content: React.ReactNode
7
- side?: "top" | "bottom" | "left" | "right"
9
+ children: React.ReactNode
10
+ side?: TooltipPrimitive.Positioner.Props["side"]
11
+ align?: TooltipPrimitive.Positioner.Props["align"]
12
+ sideOffset?: TooltipPrimitive.Positioner.Props["sideOffset"]
13
+ alignOffset?: TooltipPrimitive.Positioner.Props["alignOffset"]
14
+ collisionPadding?: TooltipPrimitive.Positioner.Props["collisionPadding"]
15
+ delay?: TooltipPrimitive.Trigger.Props["delay"]
16
+ closeDelay?: TooltipPrimitive.Trigger.Props["closeDelay"]
8
17
  disabled?: boolean
9
18
  }
10
19
 
11
- const sideClassName = {
12
- top: "bottom-full left-1/2 mb-2 -translate-x-1/2",
13
- bottom: "left-1/2 top-full mt-2 -translate-x-1/2",
14
- left: "right-full top-1/2 mr-2 -translate-y-1/2",
15
- right: "left-full top-1/2 ml-2 -translate-y-1/2",
20
+ function getPlainText(node: React.ReactNode): string {
21
+ if (typeof node === "string" || typeof node === "number") return String(node)
22
+ if (Array.isArray(node)) return node.map(getPlainText).join("")
23
+ if (React.isValidElement<{ children?: React.ReactNode }>(node)) return getPlainText(node.props.children)
24
+ return ""
16
25
  }
17
26
 
18
- function Tooltip({ content, side = "top", disabled = false, className, children, ...props }: TooltipProps) {
27
+ function Tooltip({
28
+ content,
29
+ side = "top",
30
+ align = "center",
31
+ sideOffset = 8,
32
+ alignOffset = 0,
33
+ collisionPadding = 8,
34
+ delay = 500,
35
+ closeDelay = 0,
36
+ disabled = false,
37
+ className,
38
+ children,
39
+ ...props
40
+ }: TooltipProps) {
41
+ const trigger = React.isValidElement(children) ? (
42
+ children
43
+ ) : (
44
+ <span className="inline-flex">{children}</span>
45
+ )
46
+ const contentText = getPlainText(content)
47
+ const shouldRenderFallbackLabel =
48
+ !disabled && contentText.length > 0 && !getPlainText(children).includes(contentText)
49
+
19
50
  return (
20
- <span data-slot="tooltip" className={cn("group/tooltip relative inline-flex", className)} {...props}>
21
- {children}
22
- {!disabled && (
23
- <span
24
- data-slot="tooltip-content"
25
- role="tooltip"
26
- className={cn(
27
- "pointer-events-none absolute z-50 hidden max-w-64 whitespace-nowrap rounded-xl border border-border/75 bg-popover/98 px-2.5 py-1.5 text-[11px] font-medium text-popover-foreground shadow-[0_16px_42px_color-mix(in_oklch,var(--foreground),transparent_86%)] backdrop-blur group-hover/tooltip:block group-focus-within/tooltip:block",
28
- sideClassName[side]
29
- )}
51
+ <TooltipPrimitive.Root disabled={disabled}>
52
+ <TooltipPrimitive.Trigger
53
+ data-slot="tooltip-trigger"
54
+ delay={delay}
55
+ closeDelay={closeDelay}
56
+ disabled={disabled}
57
+ render={<span data-slot="tooltip" className={cn("inline-flex", className)} {...props} />}
58
+ >
59
+ {trigger}
60
+ {shouldRenderFallbackLabel ? <span className="sr-only">{content}</span> : null}
61
+ </TooltipPrimitive.Trigger>
62
+ <TooltipPrimitive.Portal>
63
+ <TooltipPrimitive.Positioner
64
+ className="isolate z-50"
65
+ side={side}
66
+ align={align}
67
+ sideOffset={sideOffset}
68
+ alignOffset={alignOffset}
69
+ collisionPadding={collisionPadding}
30
70
  >
31
- {content}
32
- </span>
33
- )}
34
- </span>
71
+ <TooltipPrimitive.Popup
72
+ data-slot="tooltip-content"
73
+ className="z-50 max-w-64 origin-(--transform-origin) rounded-[var(--radius-lg)] border border-border/75 bg-popover/98 px-2.5 py-1.5 text-[11px] font-medium leading-4 text-popover-foreground shadow-[0_16px_42px_color-mix(in_oklch,var(--foreground),transparent_86%)] outline-hidden backdrop-blur duration-100 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95"
74
+ >
75
+ {content}
76
+ </TooltipPrimitive.Popup>
77
+ </TooltipPrimitive.Positioner>
78
+ </TooltipPrimitive.Portal>
79
+ </TooltipPrimitive.Root>
35
80
  )
36
81
  }
37
82
 
@@ -4,7 +4,8 @@ import { FileIcon, UploadCloudIcon, XIcon } from "lucide-react"
4
4
  import { Button } from "@/components/ui/button"
5
5
  import { cn, stopInteractivePropagation } from "@/lib/utils"
6
6
 
7
- export type FileUploadRejectReason = "max-files" | "max-size" | "type"
7
+ export type FileUploadRejectReason = "max-files" | "max-size" | "type"
8
+ export type FileUploadItemStatus = "idle" | "uploading" | "success" | "error"
8
9
 
9
10
  export type FileUploadRejectedFile = {
10
11
  file: File
@@ -24,13 +25,15 @@ export type FileUploadRejectionMessages = Partial<
24
25
  Record<FileUploadRejectReason, string | ((context: FileUploadRejectionMessageContext) => string)>
25
26
  >
26
27
 
27
- export type FileUploadRenderFileState = {
28
- file: File
29
- index: number
30
- progress?: number
31
- remove: () => void
32
- removeLabel?: string
33
- }
28
+ export type FileUploadRenderFileState = {
29
+ file: File
30
+ index: number
31
+ progress?: number
32
+ status: FileUploadItemStatus
33
+ remove: () => void
34
+ retry?: () => void
35
+ removeLabel?: string
36
+ }
34
37
 
35
38
  export type FileUploadRenderRejectedFileState = {
36
39
  rejectedFile: FileUploadRejectedFile
@@ -70,13 +73,17 @@ export type FileUploadProps = NativeFileInputProps & {
70
73
  rejectionMessages?: FileUploadRejectionMessages
71
74
  maxFiles?: number
72
75
  maxSize?: number
73
- appendFiles?: boolean
74
- showFileList?: boolean
75
- showClearButton?: boolean
76
- loading?: boolean
77
- progress?: number | Record<string, number>
78
- renderFile?: (state: FileUploadRenderFileState) => React.ReactNode
79
- renderRejectedFile?: (state: FileUploadRenderRejectedFileState) => React.ReactNode
76
+ appendFiles?: boolean
77
+ showFileList?: boolean
78
+ showClearButton?: boolean
79
+ loading?: boolean
80
+ progress?: number | Record<string, number>
81
+ status?: FileUploadItemStatus | Record<string, FileUploadItemStatus>
82
+ validateFile?: (file: File) => string | null | Promise<string | null>
83
+ onRetryFile?: (file: File, index: number) => void
84
+ fileTriggerLabel?: string
85
+ renderFile?: (state: FileUploadRenderFileState) => React.ReactNode
86
+ renderRejectedFile?: (state: FileUploadRenderRejectedFileState) => React.ReactNode
80
87
  renderActions?: (state: { openFileDialog: () => void; clearFiles: () => void; files: File[] }) => React.ReactNode
81
88
  className?: string
82
89
  inputClassName?: string
@@ -122,10 +129,15 @@ function getFileKey(file: File) {
122
129
  return `${file.name}-${file.size}-${file.lastModified}`
123
130
  }
124
131
 
125
- function getProgressForFile(progress: FileUploadProps["progress"], file: File) {
126
- if (typeof progress === "number") return progress
127
- return progress?.[getFileKey(file)] ?? progress?.[file.name]
128
- }
132
+ function getProgressForFile(progress: FileUploadProps["progress"], file: File) {
133
+ if (typeof progress === "number") return progress
134
+ return progress?.[getFileKey(file)] ?? progress?.[file.name]
135
+ }
136
+
137
+ function getStatusForFile(status: FileUploadProps["status"], file: File): FileUploadItemStatus {
138
+ if (typeof status === "string") return status
139
+ return status?.[getFileKey(file)] ?? status?.[file.name] ?? "idle"
140
+ }
129
141
 
130
142
  function resolveRejectionMessage(
131
143
  reason: FileUploadRejectReason,
@@ -148,29 +160,31 @@ function resolveRejectionMessage(
148
160
  }
149
161
  }
150
162
 
151
- function validateIncomingFiles({
163
+ function validateIncomingFiles({
152
164
  currentFiles,
153
165
  incomingFiles,
154
166
  accept,
155
167
  maxFiles,
156
- maxSize,
157
- appendFiles,
158
- rejectionMessages,
159
- }: {
168
+ maxSize,
169
+ appendFiles,
170
+ rejectionMessages,
171
+ validateFile,
172
+ }: {
160
173
  currentFiles: File[]
161
174
  incomingFiles: File[]
162
175
  accept?: string
163
176
  maxFiles?: number
164
177
  maxSize?: number
165
- appendFiles: boolean
166
- rejectionMessages?: FileUploadRejectionMessages
167
- }) {
168
- const accepted: File[] = []
169
- const rejected: FileUploadRejectedFile[] = []
178
+ appendFiles: boolean
179
+ rejectionMessages?: FileUploadRejectionMessages
180
+ validateFile?: FileUploadProps["validateFile"]
181
+ }) {
182
+ const accepted: File[] = []
183
+ const rejected: FileUploadRejectedFile[] = []
170
184
  const nextBase = appendFiles ? currentFiles : []
171
185
  const effectiveMaxFiles = maxFiles ?? (appendFiles ? undefined : incomingFiles.length)
172
186
 
173
- for (const file of incomingFiles) {
187
+ for (const file of incomingFiles) {
174
188
  if (effectiveMaxFiles !== undefined && nextBase.length + accepted.length >= effectiveMaxFiles) {
175
189
  rejected.push({
176
190
  file,
@@ -189,17 +203,27 @@ function validateIncomingFiles({
189
203
  continue
190
204
  }
191
205
 
192
- if (!fileMatchesAccept(file, accept)) {
206
+ if (!fileMatchesAccept(file, accept)) {
193
207
  rejected.push({
194
208
  file,
195
209
  reason: "type",
196
210
  message: resolveRejectionMessage("type", { file, maxFiles: effectiveMaxFiles, maxSize, accept }, rejectionMessages),
197
211
  })
198
- continue
199
- }
200
-
201
- accepted.push(file)
202
- }
212
+ continue
213
+ }
214
+
215
+ const customMessage = validateFile?.(file)
216
+ if (typeof customMessage === "string" && customMessage) {
217
+ rejected.push({
218
+ file,
219
+ reason: "type",
220
+ message: customMessage,
221
+ })
222
+ continue
223
+ }
224
+
225
+ accepted.push(file)
226
+ }
203
227
 
204
228
  return {
205
229
  accepted,
@@ -208,14 +232,25 @@ function validateIncomingFiles({
208
232
  }
209
233
  }
210
234
 
211
- function defaultRenderFile({ file, progress, remove, removeLabel = "Remove file" }: FileUploadRenderFileState) {
235
+ function defaultRenderFile({ file, progress, status, remove, retry, removeLabel = "Remove file" }: FileUploadRenderFileState) {
212
236
  return (
213
237
  <div className="flex min-w-0 items-center gap-3">
214
238
  <span className="flex size-9 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background/90 shadow-sm">
215
239
  <FileIcon className="size-4 text-muted-foreground" />
216
240
  </span>
217
241
  <div className="min-w-0 flex-1">
218
- <div className="truncate text-sm font-medium text-foreground">{file.name}</div>
242
+ <div className="flex flex-wrap items-center gap-2">
243
+ <div className="truncate text-sm font-medium text-foreground">{file.name}</div>
244
+ <span className={cn(
245
+ "rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.16em]",
246
+ status === "uploading" && "bg-primary/10 text-primary",
247
+ status === "success" && "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
248
+ status === "error" && "bg-destructive/10 text-destructive",
249
+ status === "idle" && "bg-muted text-muted-foreground"
250
+ )}>
251
+ {status}
252
+ </span>
253
+ </div>
219
254
  <div className="text-xs text-muted-foreground">{formatBytes(file.size)}</div>
220
255
  {typeof progress === "number" && (
221
256
  <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-muted/80">
@@ -226,6 +261,11 @@ function defaultRenderFile({ file, progress, remove, removeLabel = "Remove file"
226
261
  </div>
227
262
  )}
228
263
  </div>
264
+ {status === "error" && retry ? (
265
+ <Button type="button" variant="outline" size="sm" className="rounded-full" onClick={retry}>
266
+ Retry
267
+ </Button>
268
+ ) : null}
229
269
  <Button
230
270
  type="button"
231
271
  variant="ghost"
@@ -245,7 +285,7 @@ function defaultRenderFile({ file, progress, remove, removeLabel = "Remove file"
245
285
  )
246
286
  }
247
287
 
248
- function FileUpload({
288
+ function FileUpload({
249
289
  files = [],
250
290
  onFilesChange,
251
291
  rejectedFiles,
@@ -264,9 +304,13 @@ function FileUpload({
264
304
  appendFiles = true,
265
305
  showFileList = true,
266
306
  showClearButton = true,
267
- loading = false,
268
- progress,
269
- renderFile,
307
+ loading = false,
308
+ progress,
309
+ status,
310
+ validateFile,
311
+ onRetryFile,
312
+ fileTriggerLabel = "Open file picker",
313
+ renderFile,
270
314
  renderRejectedFile,
271
315
  renderActions,
272
316
  className,
@@ -316,13 +360,22 @@ function FileUpload({
316
360
  }, [onFilesChange, setRejectedFiles])
317
361
 
318
362
  const processFiles = React.useCallback(
319
- (incomingFiles: File[]) => {
320
- const result = validateIncomingFiles({ currentFiles: files, incomingFiles, accept, maxFiles, maxSize, appendFiles, rejectionMessages })
321
- onFilesChange?.(result.nextFiles)
322
- setRejectedFiles(result.rejected)
323
- },
324
- [accept, appendFiles, files, maxFiles, maxSize, onFilesChange, rejectionMessages, setRejectedFiles]
325
- )
363
+ (incomingFiles: File[]) => {
364
+ const result = validateIncomingFiles({
365
+ currentFiles: files,
366
+ incomingFiles,
367
+ accept,
368
+ maxFiles,
369
+ maxSize,
370
+ appendFiles,
371
+ rejectionMessages,
372
+ validateFile,
373
+ })
374
+ onFilesChange?.(result.nextFiles)
375
+ setRejectedFiles(result.rejected)
376
+ },
377
+ [accept, appendFiles, files, maxFiles, maxSize, onFilesChange, rejectionMessages, setRejectedFiles, validateFile]
378
+ )
326
379
 
327
380
  const removeFile = React.useCallback(
328
381
  (fileIndex: number) => {
@@ -333,7 +386,7 @@ function FileUpload({
333
386
 
334
387
  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
335
388
  if (isDisabled) return
336
- processFiles(Array.from(event.target.files ?? []))
389
+ processFiles(Array.from(event.target.files ?? []))
337
390
  event.target.value = ""
338
391
  }
339
392
 
@@ -371,8 +424,8 @@ function FileUpload({
371
424
  event.preventDefault()
372
425
  dragDepthRef.current = 0
373
426
  setIsDragging(false)
374
- processFiles(Array.from(event.dataTransfer.files ?? []))
375
- }
427
+ processFiles(Array.from(event.dataTransfer.files ?? []))
428
+ }
376
429
 
377
430
  return (
378
431
  <div data-slot="file-upload" className={cn("grid gap-3", className)}>
@@ -391,9 +444,9 @@ function FileUpload({
391
444
  data-slot="file-upload-dropzone"
392
445
  data-dragging={isDragging || undefined}
393
446
  data-disabled={isDisabled || undefined}
394
- role="button"
395
- aria-disabled={isDisabled || undefined}
396
- aria-label={dropzoneAriaLabel}
447
+ role="button"
448
+ aria-disabled={isDisabled || undefined}
449
+ aria-label={dropzoneAriaLabel ?? fileTriggerLabel}
397
450
  tabIndex={isDisabled ? -1 : 0}
398
451
  className={cn(
399
452
  "grid cursor-pointer gap-4 rounded-[var(--radius-2xl)] border border-dashed border-border/80 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),white_12%),var(--card))] p-6 text-center shadow-sm ring-1 ring-foreground/5 outline-none transition-[background-color,border-color,box-shadow,transform] hover:-translate-y-px hover:border-primary/35 hover:bg-muted/25 hover:shadow-[0_14px_36px_rgba(15,23,42,0.08)] focus-visible:ring-2 focus-visible:ring-ring data-[dragging=true]:border-primary data-[dragging=true]:bg-primary/6 data-[dragging=true]:shadow-[0_18px_50px_color-mix(in_oklch,var(--primary),transparent_84%)] data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-60",
@@ -432,6 +485,7 @@ function FileUpload({
432
485
  variant="outline"
433
486
  size="sm"
434
487
  disabled={isDisabled}
488
+ aria-label={typeof buttonLabel === "string" ? buttonLabel : fileTriggerLabel}
435
489
  onClick={(event) => {
436
490
  stopInteractivePropagation(event)
437
491
  openFileDialog()
@@ -463,13 +517,22 @@ function FileUpload({
463
517
 
464
518
  {showFileList && files.length > 0 && (
465
519
  <div data-slot="file-upload-list" className={cn("grid gap-2", fileListClassName)}>
466
- {files.map((file, index) => {
467
- const state = { file, index, progress: getProgressForFile(progress, file), remove: () => removeFile(index), removeLabel }
520
+ {files.map((file, index) => {
521
+ const state = {
522
+ file,
523
+ index,
524
+ progress: getProgressForFile(progress, file),
525
+ status: getStatusForFile(status, file),
526
+ remove: () => removeFile(index),
527
+ retry: onRetryFile ? () => onRetryFile(file, index) : undefined,
528
+ removeLabel,
529
+ }
468
530
 
469
531
  return (
470
532
  <div
471
533
  key={`${file.name}-${file.lastModified}-${index}`}
472
534
  data-slot="file-upload-item"
535
+ data-status={state.status}
473
536
  className={cn("rounded-[min(var(--radius-xl),18px)] border border-border/80 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),white_10%),var(--card))] p-3.5 shadow-sm ring-1 ring-foreground/5", fileItemClassName)}
474
537
  >
475
538
  {renderFile?.(state) ?? defaultRenderFile(state)}
@@ -11,13 +11,33 @@ import {
11
11
  import { Button } from "@/components/ui/button"
12
12
  import { cn, stopInteractivePropagation } from "@/lib/utils"
13
13
 
14
- export type ImageUploadProps = Omit<FileUploadProps, "accept" | "renderFile"> & {
15
- accept?: string
16
- preview?: boolean
17
- previewClassName?: string
18
- imageClassName?: string
19
- renderImageFile?: (state: FileUploadRenderFileState & { previewUrl?: string }) => React.ReactNode
20
- }
14
+ export type ImageUploadProps = Omit<FileUploadProps, "accept" | "renderFile"> & {
15
+ accept?: string
16
+ preview?: boolean
17
+ previewClassName?: string
18
+ imageClassName?: string
19
+ minImageWidth?: number
20
+ minImageHeight?: number
21
+ aspectRatio?: number
22
+ aspectRatioTolerance?: number
23
+ renderImageFile?: (state: FileUploadRenderFileState & { previewUrl?: string }) => React.ReactNode
24
+ }
25
+
26
+ function readImageMeta(file: File) {
27
+ return new Promise<{ width: number; height: number }>((resolve, reject) => {
28
+ const url = URL.createObjectURL(file)
29
+ const image = new Image()
30
+ image.onload = () => {
31
+ resolve({ width: image.width, height: image.height })
32
+ URL.revokeObjectURL(url)
33
+ }
34
+ image.onerror = () => {
35
+ URL.revokeObjectURL(url)
36
+ reject(new Error("Image metadata could not be read"))
37
+ }
38
+ image.src = url
39
+ })
40
+ }
21
41
 
22
42
  function useImagePreviewUrls(files: File[], enabled = true) {
23
43
  const [urls, setUrls] = React.useState<Record<string, string>>({})
@@ -104,22 +124,49 @@ function ImageUpload({
104
124
  buttonLabel = "Choose image",
105
125
  dropzoneLabel = "Drop images here or click to upload",
106
126
  preview = true,
107
- previewClassName,
108
- imageClassName,
109
- files = [],
110
- renderImageFile,
111
- ...props
112
- }: ImageUploadProps) {
113
- const previewUrls = useImagePreviewUrls(files, preview)
114
-
115
- return (
116
- <FileUpload
117
- accept={accept}
118
- buttonLabel={buttonLabel}
119
- dropzoneLabel={dropzoneLabel}
120
- files={files}
121
- renderFile={(state) => {
122
- const previewUrl = previewUrls[getFileKey(state.file)]
127
+ previewClassName,
128
+ imageClassName,
129
+ files = [],
130
+ minImageWidth,
131
+ minImageHeight,
132
+ aspectRatio,
133
+ aspectRatioTolerance = 0.04,
134
+ renderImageFile,
135
+ ...props
136
+ }: ImageUploadProps) {
137
+ const previewUrls = useImagePreviewUrls(files, preview)
138
+
139
+ return (
140
+ <FileUpload
141
+ accept={accept}
142
+ buttonLabel={buttonLabel}
143
+ dropzoneLabel={dropzoneLabel}
144
+ files={files}
145
+ validateFile={async (file) => {
146
+ if (!file.type.startsWith("image/")) return "Only image files are allowed."
147
+ if (!minImageWidth && !minImageHeight && !aspectRatio) return null
148
+
149
+ const { width, height } = await readImageMeta(file)
150
+
151
+ if (minImageWidth && width < minImageWidth) {
152
+ return `Image width must be at least ${minImageWidth}px.`
153
+ }
154
+
155
+ if (minImageHeight && height < minImageHeight) {
156
+ return `Image height must be at least ${minImageHeight}px.`
157
+ }
158
+
159
+ if (aspectRatio) {
160
+ const ratio = width / height
161
+ if (Math.abs(ratio - aspectRatio) > aspectRatioTolerance) {
162
+ return `Image ratio must be close to ${aspectRatio.toFixed(2)}:1.`
163
+ }
164
+ }
165
+
166
+ return null
167
+ }}
168
+ renderFile={(state) => {
169
+ const previewUrl = previewUrls[getFileKey(state.file)]
123
170
 
124
171
  return renderImageFile?.({ ...state, previewUrl }) ??
125
172
  defaultRenderImageFile({ ...state, previewUrl, previewClassName, imageClassName })
@@ -129,4 +176,4 @@ function ImageUpload({
129
176
  )
130
177
  }
131
178
 
132
- export { ImageUpload, useImagePreviewUrls }
179
+ export { ImageUpload, useImagePreviewUrls }
@@ -14,6 +14,9 @@ export const documentedPublicComponentSurfaces: readonly PublicComponentSurfaceE
14
14
  { slug: "overlay", registryName: "dialog", surface: "documented" },
15
15
  { slug: "form-field", registryName: "form-field-shell", surface: "documented" },
16
16
  { slug: "data-table", registryName: "data-table", surface: "documented" },
17
+ { slug: "calendar", registryName: "calendar", surface: "documented" },
18
+ { slug: "date-picker", registryName: "date-picker", surface: "documented" },
19
+ { slug: "date-range-picker", registryName: "date-range-picker", surface: "documented" },
17
20
  { slug: "alert", registryName: "alert", surface: "documented" },
18
21
  { slug: "bar-chart", registryName: "charts", surface: "documented" },
19
22
  { slug: "sidebar", registryName: "sidebar", surface: "documented" },