@boxcustodia/library 2.0.0-alpha.11 → 2.0.0-alpha.13

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 (352) hide show
  1. package/dist/index.cjs.js +38 -38
  2. package/dist/index.css +1 -1
  3. package/dist/index.d.ts +28 -29
  4. package/dist/index.es.js +6804 -6792
  5. package/package.json +4 -3
  6. package/src/__doc__/Changelog.mdx +6 -0
  7. package/src/__doc__/Components.mdx +73 -0
  8. package/src/__doc__/Examples.tsx +69 -0
  9. package/src/__doc__/Icons.mdx +41 -0
  10. package/src/__doc__/Intro.mdx +138 -0
  11. package/src/__doc__/MCP.mdx +71 -0
  12. package/src/__doc__/Migration.mdx +451 -0
  13. package/src/__doc__/Theme.mdx +132 -0
  14. package/src/__doc__/Types.mdx +252 -0
  15. package/src/components/alert/alert.stories.tsx +142 -0
  16. package/src/components/alert/alert.tsx +109 -0
  17. package/src/components/alert/index.ts +7 -0
  18. package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
  19. package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
  20. package/src/components/alert-dialog/alert-dialog.tsx +265 -0
  21. package/src/components/alert-dialog/index.ts +1 -0
  22. package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
  23. package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
  24. package/src/components/auto-complete/auto-complete.tsx +82 -0
  25. package/src/components/auto-complete/index.ts +2 -0
  26. package/src/components/avatar/avatar.stories.tsx +84 -0
  27. package/src/components/avatar/avatar.test.tsx +61 -0
  28. package/src/components/avatar/avatar.tsx +104 -0
  29. package/src/components/avatar/index.ts +1 -0
  30. package/src/components/background-image/background-image.stories.tsx +21 -0
  31. package/src/components/background-image/background-image.test.tsx +29 -0
  32. package/src/components/background-image/background-image.tsx +23 -0
  33. package/src/components/background-image/index.ts +1 -0
  34. package/src/components/button/button.stories.tsx +396 -0
  35. package/src/components/button/button.test.tsx +58 -0
  36. package/src/components/button/button.tsx +31 -0
  37. package/src/components/button/button.variants.ts +44 -0
  38. package/src/components/button/components/base-button.tsx +86 -0
  39. package/src/components/button/components/loader-overlay.tsx +21 -0
  40. package/src/components/button/components/loading-icon.tsx +47 -0
  41. package/src/components/button/index.ts +3 -0
  42. package/src/components/calendar/calendar.model.ts +86 -0
  43. package/src/components/calendar/calendar.stories.tsx +155 -0
  44. package/src/components/calendar/calendar.test.tsx +12 -0
  45. package/src/components/calendar/calendar.tsx +185 -0
  46. package/src/components/calendar/components/calendar-navigation.tsx +141 -0
  47. package/src/components/calendar/components/day.tsx +61 -0
  48. package/src/components/calendar/components/decade-view.tsx +45 -0
  49. package/src/components/calendar/components/index.ts +6 -0
  50. package/src/components/calendar/components/month-view.tsx +58 -0
  51. package/src/components/calendar/components/week-days.tsx +27 -0
  52. package/src/components/calendar/components/year-view.tsx +29 -0
  53. package/src/components/calendar/hooks/index.ts +4 -0
  54. package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
  55. package/src/components/calendar/hooks/use-calendar.ts +90 -0
  56. package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
  57. package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
  58. package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
  59. package/src/components/calendar/index.ts +1 -0
  60. package/src/components/calendar/utils/typeguards.ts +7 -0
  61. package/src/components/card/card.stories.tsx +116 -0
  62. package/src/components/card/card.tsx +74 -0
  63. package/src/components/card/index.ts +1 -0
  64. package/src/components/center/center.stories.tsx +81 -0
  65. package/src/components/center/center.tsx +24 -0
  66. package/src/components/center/index.ts +1 -0
  67. package/src/components/checkbox/checkbox.stories.tsx +307 -0
  68. package/src/components/checkbox/checkbox.tsx +273 -0
  69. package/src/components/checkbox/index.ts +1 -0
  70. package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
  71. package/src/components/checkbox-group/checkbox-group.tsx +16 -0
  72. package/src/components/checkbox-group/index.ts +1 -0
  73. package/src/components/combobox/combobox.stories.tsx +339 -0
  74. package/src/components/combobox/combobox.tsx +898 -0
  75. package/src/components/combobox/index.ts +1 -0
  76. package/src/components/date-picker/date-input.stories.tsx +158 -0
  77. package/src/components/date-picker/date-input.tsx +163 -0
  78. package/src/components/date-picker/date-picker.model.ts +90 -0
  79. package/src/components/date-picker/date-picker.stories.tsx +200 -0
  80. package/src/components/date-picker/date-picker.test.tsx +23 -0
  81. package/src/components/date-picker/date-picker.tsx +298 -0
  82. package/src/components/date-picker/date-picker.utils.ts +260 -0
  83. package/src/components/date-picker/index.ts +3 -0
  84. package/src/components/date-picker/use-date-input-popover.ts +48 -0
  85. package/src/components/date-picker/use-date-input.ts +125 -0
  86. package/src/components/dialog/dialog.stories.tsx +171 -0
  87. package/src/components/dialog/dialog.test.tsx +68 -0
  88. package/src/components/dialog/dialog.tsx +277 -0
  89. package/src/components/dialog/index.ts +1 -0
  90. package/src/components/divider/divider.stories.tsx +139 -0
  91. package/src/components/divider/divider.test.tsx +22 -0
  92. package/src/components/divider/divider.tsx +23 -0
  93. package/src/components/divider/index.ts +1 -0
  94. package/src/components/dropzone/dropzone.stories.tsx +210 -0
  95. package/src/components/dropzone/dropzone.tsx +154 -0
  96. package/src/components/dropzone/file-types.ts +64 -0
  97. package/src/components/dropzone/index.ts +3 -0
  98. package/src/components/dropzone/upload-primitives.tsx +310 -0
  99. package/src/components/dropzone/use-dropzone.ts +122 -0
  100. package/src/components/empty-state/empty-state.stories.tsx +56 -0
  101. package/src/components/empty-state/empty-state.tsx +39 -0
  102. package/src/components/empty-state/index.ts +1 -0
  103. package/src/components/field/field.stories.tsx +223 -0
  104. package/src/components/field/field.tsx +229 -0
  105. package/src/components/field/index.ts +1 -0
  106. package/src/components/form/form.stories.tsx +594 -0
  107. package/src/components/form/form.tsx +30 -0
  108. package/src/components/form/index.ts +1 -0
  109. package/src/components/heading/heading.stories.tsx +74 -0
  110. package/src/components/heading/heading.tsx +28 -0
  111. package/src/components/heading/heading.variants.ts +27 -0
  112. package/src/components/heading/index.ts +1 -0
  113. package/src/components/index.ts +46 -0
  114. package/src/components/input/index.ts +1 -0
  115. package/src/components/input/input.stories.tsx +104 -0
  116. package/src/components/input/input.tsx +75 -0
  117. package/src/components/kbd/index.ts +1 -0
  118. package/src/components/kbd/kbd.stories.tsx +40 -0
  119. package/src/components/kbd/kbd.tsx +31 -0
  120. package/src/components/kbd/kbd.variants.ts +26 -0
  121. package/src/components/label/index.ts +1 -0
  122. package/src/components/label/label.stories.tsx +68 -0
  123. package/src/components/label/label.test.tsx +61 -0
  124. package/src/components/label/label.tsx +62 -0
  125. package/src/components/loader/index.ts +1 -0
  126. package/src/components/loader/loader.stories.tsx +60 -0
  127. package/src/components/loader/loader.test.tsx +26 -0
  128. package/src/components/loader/loader.tsx +60 -0
  129. package/src/components/menu/index.ts +2 -0
  130. package/src/components/menu/menu-primitives.tsx +248 -0
  131. package/src/components/menu/menu.stories.tsx +203 -0
  132. package/src/components/menu/menu.tsx +100 -0
  133. package/src/components/menu/util/render-menu-item.tsx +54 -0
  134. package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
  135. package/src/components/multi-select/index.ts +1 -0
  136. package/src/components/multi-select/multi-select.stories.tsx +294 -0
  137. package/src/components/multi-select/multi-select.tsx +300 -0
  138. package/src/components/multi-select/multi-select.variants.ts +22 -0
  139. package/src/components/number-input/index.ts +1 -0
  140. package/src/components/number-input/number-input.stories.tsx +209 -0
  141. package/src/components/number-input/number-input.test.tsx +87 -0
  142. package/src/components/number-input/number-input.tsx +232 -0
  143. package/src/components/pagination/components/pagination-option.tsx +27 -0
  144. package/src/components/pagination/index.ts +1 -0
  145. package/src/components/pagination/pagination.stories.tsx +80 -0
  146. package/src/components/pagination/pagination.test.tsx +76 -0
  147. package/src/components/pagination/pagination.tsx +102 -0
  148. package/src/components/password/index.ts +1 -0
  149. package/src/components/password/password.stories.tsx +104 -0
  150. package/src/components/password/password.tsx +75 -0
  151. package/src/components/popover/index.ts +1 -0
  152. package/src/components/popover/popover.stories.tsx +213 -0
  153. package/src/components/popover/popover.tsx +203 -0
  154. package/src/components/progress/index.ts +1 -0
  155. package/src/components/progress/progress.stories.tsx +124 -0
  156. package/src/components/progress/progress.test.tsx +25 -0
  157. package/src/components/progress/progress.tsx +124 -0
  158. package/src/components/scroll-area/index.ts +1 -0
  159. package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
  160. package/src/components/scroll-area/scroll-area.tsx +64 -0
  161. package/src/components/select/index.ts +1 -0
  162. package/src/components/select/select.stories.tsx +253 -0
  163. package/src/components/select/select.tsx +430 -0
  164. package/src/components/show/index.ts +1 -0
  165. package/src/components/show/show.stories.tsx +197 -0
  166. package/src/components/show/show.test.tsx +41 -0
  167. package/src/components/show/show.tsx +16 -0
  168. package/src/components/skeleton/index.ts +1 -0
  169. package/src/components/skeleton/skeleton.stories.tsx +36 -0
  170. package/src/components/skeleton/skeleton.test.tsx +14 -0
  171. package/src/components/skeleton/skeleton.tsx +15 -0
  172. package/src/components/stack/index.ts +1 -0
  173. package/src/components/stack/stack.stories.tsx +194 -0
  174. package/src/components/stack/stack.tsx +52 -0
  175. package/src/components/stepper/Stepper.tsx +190 -0
  176. package/src/components/stepper/context/stepper-context.tsx +11 -0
  177. package/src/components/stepper/index.ts +1 -0
  178. package/src/components/stepper/stepper.stories.tsx +130 -0
  179. package/src/components/stepper/stepper.test.tsx +91 -0
  180. package/src/components/switch/index.ts +1 -0
  181. package/src/components/switch/switch.stories.tsx +122 -0
  182. package/src/components/switch/switch.test.tsx +30 -0
  183. package/src/components/switch/switch.tsx +86 -0
  184. package/src/components/table/index.ts +3 -0
  185. package/src/components/table/table-primitives.tsx +122 -0
  186. package/src/components/table/table.model.ts +20 -0
  187. package/src/components/table/table.stories.tsx +169 -0
  188. package/src/components/table/table.test.tsx +91 -0
  189. package/src/components/table/table.tsx +109 -0
  190. package/src/components/table-pagination/index.ts +2 -0
  191. package/src/components/table-pagination/table-pagination.model.ts +2 -0
  192. package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
  193. package/src/components/table-pagination/table-pagination.test.tsx +32 -0
  194. package/src/components/table-pagination/table-pagination.tsx +108 -0
  195. package/src/components/tabs/context/tabs-context.tsx +14 -0
  196. package/src/components/tabs/index.ts +1 -0
  197. package/src/components/tabs/tabs.stories.tsx +182 -0
  198. package/src/components/tabs/tabs.test.tsx +61 -0
  199. package/src/components/tabs/tabs.tsx +175 -0
  200. package/src/components/tag/index.ts +2 -0
  201. package/src/components/tag/tag.stories.tsx +170 -0
  202. package/src/components/tag/tag.test.tsx +18 -0
  203. package/src/components/tag/tag.tsx +99 -0
  204. package/src/components/tag/tag.variants.ts +31 -0
  205. package/src/components/textarea/index.ts +1 -0
  206. package/src/components/textarea/textarea.stories.tsx +73 -0
  207. package/src/components/textarea/textarea.tsx +105 -0
  208. package/src/components/timeline/index.ts +1 -0
  209. package/src/components/timeline/timeline-status.ts +5 -0
  210. package/src/components/timeline/timeline.stories.tsx +84 -0
  211. package/src/components/timeline/timeline.tsx +147 -0
  212. package/src/components/toast/index.ts +1 -0
  213. package/src/components/toast/toast.stories.tsx +392 -0
  214. package/src/components/toast/toast.test.tsx +50 -0
  215. package/src/components/toast/toast.tsx +411 -0
  216. package/src/components/tooltip/index.ts +1 -0
  217. package/src/components/tooltip/tooltip.stories.tsx +226 -0
  218. package/src/components/tooltip/tooltip.test.tsx +46 -0
  219. package/src/components/tooltip/tooltip.tsx +171 -0
  220. package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
  221. package/src/components/tree/index.ts +2 -0
  222. package/src/components/tree/tree-primitives.tsx +126 -0
  223. package/src/components/tree/tree.stories.tsx +468 -0
  224. package/src/components/tree/tree.tsx +42 -0
  225. package/src/hooks/index.ts +26 -0
  226. package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
  227. package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
  228. package/src/hooks/useArray/index.ts +1 -0
  229. package/src/hooks/useArray/useArray.ts +76 -0
  230. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
  231. package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
  232. package/src/hooks/useAsync/index.ts +1 -0
  233. package/src/hooks/useAsync/useAsync.ts +58 -0
  234. package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
  235. package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
  236. package/src/hooks/useClickOutside/index.ts +1 -0
  237. package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
  238. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
  239. package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
  240. package/src/hooks/useClipboard/index.ts +1 -0
  241. package/src/hooks/useClipboard/useClipboard.tsx +28 -0
  242. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
  243. package/src/hooks/useDebounceCallback/index.ts +1 -0
  244. package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
  245. package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
  246. package/src/hooks/useDebounceValue/index.ts +1 -0
  247. package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
  248. package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
  249. package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
  250. package/src/hooks/useDisclosure/index.ts +1 -0
  251. package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
  252. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
  253. package/src/hooks/useDocumentTitle/index.ts +1 -0
  254. package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
  255. package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
  256. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
  257. package/src/hooks/useEventListener/index.ts +1 -0
  258. package/src/hooks/useEventListener/useEventListener.ts +25 -0
  259. package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
  260. package/src/hooks/useFocusTrap/index.ts +1 -0
  261. package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
  262. package/src/hooks/useFocusTrap/tabbable.ts +70 -0
  263. package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
  264. package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
  265. package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
  266. package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
  267. package/src/hooks/useHotkey/__utils__/index.ts +3 -0
  268. package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
  269. package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
  270. package/src/hooks/useHotkey/index.ts +1 -0
  271. package/src/hooks/useHotkey/useHotkey.ts +34 -0
  272. package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
  273. package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
  274. package/src/hooks/useHover/index.ts +1 -0
  275. package/src/hooks/useHover/useHover.tsx +40 -0
  276. package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
  277. package/src/hooks/useIsVisible/index.ts +1 -0
  278. package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
  279. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
  280. package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
  281. package/src/hooks/useLocalStorage/index.ts +1 -0
  282. package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
  283. package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
  284. package/src/hooks/useMediaQuery/index.ts +1 -0
  285. package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
  286. package/src/hooks/useMemoizedFn/index.ts +1 -0
  287. package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
  288. package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
  289. package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
  290. package/src/hooks/useMutation/index.ts +1 -0
  291. package/src/hooks/useMutation/useMutation.tsx +60 -0
  292. package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
  293. package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
  294. package/src/hooks/useObject/index.ts +1 -0
  295. package/src/hooks/useObject/useObject.tsx +48 -0
  296. package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
  297. package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
  298. package/src/hooks/usePagination/index.ts +2 -0
  299. package/src/hooks/usePagination/usePagination.tsx +74 -0
  300. package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
  301. package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
  302. package/src/hooks/usePortal/index.ts +1 -0
  303. package/src/hooks/usePortal/usePortal.ts +40 -0
  304. package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
  305. package/src/hooks/usePreventCloseWindow/index.ts +1 -0
  306. package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
  307. package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
  308. package/src/hooks/useRangePagination/index.ts +2 -0
  309. package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
  310. package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
  311. package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
  312. package/src/hooks/useSelection/index.ts +1 -0
  313. package/src/hooks/useSelection/useSelection.ts +121 -0
  314. package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
  315. package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
  316. package/src/hooks/useStep/index.ts +1 -0
  317. package/src/hooks/useStep/useStep.ts +57 -0
  318. package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
  319. package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
  320. package/src/hooks/useToggle/index.ts +1 -0
  321. package/src/hooks/useToggle/useToggle.ts +16 -0
  322. package/src/index.ts +6 -0
  323. package/src/lib/cn.ts +8 -0
  324. package/src/lib/index.ts +1 -0
  325. package/src/models/Generic.model.ts +67 -0
  326. package/src/models/index.ts +1 -0
  327. package/src/providers/index.ts +2 -0
  328. package/src/providers/library-provider.tsx +44 -0
  329. package/src/providers/theme/ThemeProvider.tsx +25 -0
  330. package/src/providers/theme/index.ts +3 -0
  331. package/src/providers/theme/types.ts +11 -0
  332. package/src/providers/theme/useThemeProps.ts +25 -0
  333. package/src/stores/theme.store.ts +31 -0
  334. package/src/styles/components.css +4 -0
  335. package/src/styles/index.css +2 -0
  336. package/src/styles/library.css +2 -0
  337. package/src/styles/theme.css +232 -0
  338. package/src/utils/dates/parseDateRange.utility.ts +39 -0
  339. package/src/utils/form.tsx +91 -0
  340. package/src/utils/functions/createSafeContext.ts +17 -0
  341. package/src/utils/functions/ensureReactElement.tsx +30 -0
  342. package/src/utils/functions/getFormData.ts +19 -0
  343. package/src/utils/functions/index.ts +4 -0
  344. package/src/utils/functions/mergeRefs.ts +18 -0
  345. package/src/utils/index.ts +3 -0
  346. package/src/utils/strings/extractInitials.utility.ts +10 -0
  347. package/src/utils/strings/index.ts +1 -0
  348. package/src/utils/tests/click.ts +3 -0
  349. package/src/utils/tests/index.ts +2 -0
  350. package/src/utils/tests/keyboard.ts +21 -0
  351. package/src/utils/tests/type.ts +6 -0
  352. package/dist/components.css +0 -2
@@ -0,0 +1,139 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Divider } from "../../components";
3
+
4
+ /**
5
+ * Visual separator between sections or inline elements.
6
+ *
7
+ * Built on the Base UI `Separator` primitive. Supports `orientation` (`horizontal` | `vertical`)
8
+ * and a `render` prop for polymorphic composition. The DOM element receives `data-orientation`
9
+ * which can be used as a CSS selector for conditional styles.
10
+ */
11
+ const meta: Meta<typeof Divider> = {
12
+ title: "Components/Divider",
13
+ component: Divider,
14
+ args: {
15
+ orientation: "horizontal",
16
+ },
17
+ parameters: {
18
+ layout: "centered",
19
+ },
20
+ };
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof Divider>;
24
+
25
+ export const Default: Story = {
26
+ render: () => (
27
+ <div className="w-72 rounded-lg border p-4 text-sm">
28
+ <div className="flex items-center justify-between">
29
+ <span className="font-medium">Deployment #847</span>
30
+ <span className="text-xs text-green-500">● Live</span>
31
+ </div>
32
+ <Divider className="my-3" />
33
+ <div className="space-y-1.5 text-muted-foreground">
34
+ <div className="flex justify-between">
35
+ <span>Triggered by</span>
36
+ <span className="text-foreground">fbaigorria</span>
37
+ </div>
38
+ <div className="flex justify-between">
39
+ <span>Branch</span>
40
+ <span className="text-foreground">develop</span>
41
+ </div>
42
+ <div className="flex justify-between">
43
+ <span>Duration</span>
44
+ <span className="text-foreground">1m 43s</span>
45
+ </div>
46
+ </div>
47
+ <Divider className="my-3" />
48
+ <div className="flex items-center justify-center gap-4 text-muted-foreground">
49
+ <span>2 warnings</span>
50
+ <Divider orientation="vertical" className="h-4" />
51
+ <span>0 errors</span>
52
+ <Divider orientation="vertical" className="h-4" />
53
+ <span>127 tests</span>
54
+ </div>
55
+ </div>
56
+ ),
57
+ };
58
+
59
+ /**
60
+ * Default orientation. Renders a full-width 1px horizontal line via `data-[orientation=horizontal]`.
61
+ * Commonly used to separate named sections inside a panel or card.
62
+ */
63
+ export const Horizontal: Story = {
64
+ render: () => (
65
+ <div className="w-72 rounded-lg border p-4 space-y-4">
66
+ <div>
67
+ <p className="text-sm font-medium">Profile</p>
68
+ <p className="text-xs text-muted-foreground">Franco Baigorria</p>
69
+ </div>
70
+ <Divider />
71
+ <div>
72
+ <p className="text-sm font-medium">Team</p>
73
+ <p className="text-xs text-muted-foreground">Engineering</p>
74
+ </div>
75
+ <Divider />
76
+ <div>
77
+ <p className="text-sm font-medium">Role</p>
78
+ <p className="text-xs text-muted-foreground">Frontend Architect</p>
79
+ </div>
80
+ </div>
81
+ ),
82
+ };
83
+
84
+ /**
85
+ * In vertical orientation the component uses `self-stretch` to fill the full height of
86
+ * the flex container. Wrapping in a `div` with `flex` and a defined height is the expected pattern.
87
+ */
88
+ export const Vertical: Story = {
89
+ args: {
90
+ orientation: "vertical",
91
+ },
92
+ render: (args) => (
93
+ <div className="flex h-10 items-center gap-4">
94
+ <span className="text-sm">Section A</span>
95
+ <Divider {...args} />
96
+ <span className="text-sm">Section B</span>
97
+ <Divider {...args} />
98
+ <span className="text-sm">Section C</span>
99
+ </div>
100
+ ),
101
+ };
102
+
103
+ /**
104
+ * The `render` prop replaces the rendered DOM element. Useful for swapping semantics
105
+ * to `<hr>` or any other element without losing component styles.
106
+ *
107
+ * ```tsx
108
+ * <Divider render={<hr />} />
109
+ * ```
110
+ */
111
+ export const Render: Story = {
112
+ render: (args) => (
113
+ <div className="w-64">
114
+ <Divider {...args} render={<hr />} />
115
+ </div>
116
+ ),
117
+ parameters: {
118
+ docs: {
119
+ source: {
120
+ code: `<Divider render={<hr />} />`,
121
+ },
122
+ },
123
+ },
124
+ };
125
+
126
+ /**
127
+ * `className` accepts Tailwind classes to override color, thickness, or spacing.
128
+ * `data-[orientation=horizontal]` and `data-[orientation=vertical]` are available
129
+ * as CSS selectors for advanced conditional styles.
130
+ */
131
+ export const Custom: Story = {
132
+ render: () => (
133
+ <div className="space-y-4 w-64">
134
+ <Divider className="bg-primary" />
135
+ <Divider className="bg-error" />
136
+ <Divider className="border-t border-dashed border-input bg-transparent" />
137
+ </div>
138
+ ),
139
+ };
@@ -0,0 +1,22 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { Divider } from "../../components";
4
+
5
+ describe("Divider component", () => {
6
+ const testId = "divider";
7
+ it("should render correctly", () => {
8
+ render(<Divider data-testid={testId} />);
9
+ const divider = screen.getByTestId(testId);
10
+
11
+ expect(divider).toBeInTheDocument();
12
+ expect(divider).toHaveAttribute("aria-orientation", "horizontal");
13
+ });
14
+
15
+ it("should accept vertical orientation", () => {
16
+ render(<Divider orientation="vertical" data-testid={testId} />);
17
+ const divider = screen.getByTestId(testId);
18
+
19
+ expect(divider).toBeInTheDocument();
20
+ expect(divider).toHaveAttribute("aria-orientation", "vertical");
21
+ });
22
+ });
@@ -0,0 +1,23 @@
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
2
+ import type React from "react";
3
+ import { cn } from "tailwind-variants";
4
+
5
+ export function Divider({
6
+ className,
7
+ orientation = "horizontal",
8
+ ...props
9
+ }: SeparatorPrimitive.Props): React.ReactElement {
10
+ return (
11
+ <SeparatorPrimitive
12
+ className={cn(
13
+ "shrink-0 bg-input data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:not-[[class^='h-']]:not-[[class*='_h-']]:self-stretch",
14
+ className,
15
+ )}
16
+ data-slot="separator"
17
+ orientation={orientation}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ export { SeparatorPrimitive };
@@ -0,0 +1 @@
1
+ export * from "./divider";
@@ -0,0 +1,210 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Camera, File, FileArchive, ImageIcon, Trash } from "lucide-react";
3
+ import React from "react";
4
+ import { Button } from "../../components/button";
5
+ import { Dropzone } from ".";
6
+ import { FileType, FileTypeGroups, FileTypeValue } from "./file-types";
7
+ import {
8
+ UploadAcceptedFiles,
9
+ UploadContent,
10
+ UploadRejectedFiles,
11
+ UploadRoot,
12
+ UploadTrigger,
13
+ } from "./upload-primitives";
14
+ import { FileError } from "./use-dropzone";
15
+
16
+ const meta: Meta<typeof Dropzone> = {
17
+ title: "data entry/Dropzone",
18
+ component: Dropzone,
19
+ subcomponents: {
20
+ UploadRoot: UploadRoot as React.ComponentType<unknown>,
21
+ UploadTrigger: UploadTrigger as React.ComponentType<unknown>,
22
+ UploadContent: UploadContent as React.ComponentType<unknown>,
23
+ UploadAcceptedFiles: UploadAcceptedFiles as React.ComponentType<unknown>,
24
+ UploadRejectedFiles: UploadRejectedFiles as React.ComponentType<unknown>,
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+ type Story = StoryObj<typeof Dropzone>;
30
+
31
+ export const Default: Story = {
32
+ args: {
33
+ allowedExtensions: [...FileTypeGroups.IMAGES, FileType.PDF],
34
+ },
35
+ };
36
+
37
+ export const WithCustomIcon: Story = {
38
+ args: {
39
+ allowedExtensions: FileTypeGroups.IMAGES,
40
+ icon: ImageIcon,
41
+ iconSize: 48,
42
+ },
43
+ };
44
+
45
+ export const FileUploader: Story = {
46
+ args: {
47
+ allowedExtensions: [FileType.PDF, FileType.ZIP],
48
+ icon: FileArchive,
49
+ iconSize: 48,
50
+ },
51
+ };
52
+
53
+ export const MultipleFiles: Story = {
54
+ args: {
55
+ multiple: true,
56
+ maxFiles: 3,
57
+ allowedExtensions: [...FileTypeGroups.IMAGES, FileType.PDF],
58
+ },
59
+ };
60
+
61
+ /**
62
+ * El callback `onError` te permite manejar errores cuando se rechazan archivos durante la validación.
63
+ *
64
+ * ### Interfaz FileError
65
+ * ```typescript
66
+ * type FileError = {
67
+ * file: File; // El archivo que causó el error
68
+ * errorMessage: string; // Mensaje de error genérico
69
+ * errorCode: FileErrorCode; // Código para identificar el tipo de error
70
+ * };
71
+ * ```
72
+ *
73
+ * ### Códigos de Error Disponibles
74
+ * - `'INVALID_EXTENSION'`: El archivo no tiene una extensión permitida
75
+ * - `'FILE_TOO_LARGE'`: El archivo excede el tamaño máximo permitido
76
+ * - `'MAX_FILES_EXCEEDED'`: Se ha alcanzado el límite máximo de archivos
77
+ *
78
+ * ### Ejemplo de Uso
79
+ * ```typescript
80
+ * onError: (fileErrors) => {
81
+ * fileErrors.forEach(({ file, errorCode }) => {
82
+ * switch (errorCode) {
83
+ * case 'INVALID_EXTENSION':
84
+ * toast.error(`${file.name} no es un tipo válido`);
85
+ * break;
86
+ * case 'FILE_TOO_LARGE':
87
+ * toast.error(`${file.name} es muy grande`);
88
+ * break;
89
+ * case 'MAX_FILES_EXCEEDED':
90
+ * toast.error('Demasiados archivos');
91
+ * break;
92
+ * }
93
+ * });
94
+ * };
95
+ * ```
96
+ *
97
+ * ### Notas Importantes
98
+ * - El `onError` se ejecuta automáticamente cada vez que `validateFiles` encuentra errores
99
+ * - Puedes acceder al archivo original para mostrar información personalizada
100
+ * - Los mensajes de error son genéricos, puedes crear los tuyos propios basándote en el `errorCode`
101
+ */
102
+ export const WithErrorHandling: Story = {
103
+ args: {
104
+ multiple: true,
105
+ maxFiles: 2,
106
+ maxFileSize: 2 * 1024 * 1024, // 2MB
107
+ allowedExtensions: [...FileTypeGroups.IMAGES, FileType.PDF],
108
+ onError: (fileErrors: FileError[]) => {
109
+ console.log("Errores de archivos:", fileErrors);
110
+ alert(
111
+ fileErrors
112
+ .map(
113
+ ({ file, errorMessage, errorCode }) =>
114
+ `${file.name}: ${errorMessage} (${errorCode})`,
115
+ )
116
+ .join("\n"),
117
+ );
118
+ },
119
+ },
120
+ };
121
+
122
+ export const CustomFilePreview: Story = {
123
+ args: {
124
+ multiple: true,
125
+ allowedExtensions: [...FileTypeGroups.IMAGES, FileType.PDF],
126
+ showRejected: true,
127
+ renderAcceptedFile: (file, removeItem) => (
128
+ <div className="flex items-center gap-2 p-2 bg-muted rounded-lg">
129
+ {FileTypeGroups.IMAGES.includes(file.type as FileTypeValue) ? (
130
+ <img
131
+ src={URL.createObjectURL(file)}
132
+ alt={file.name}
133
+ className="h-12 w-12 rounded object-cover"
134
+ />
135
+ ) : (
136
+ <File className="h-12 w-12" />
137
+ )}
138
+ <div className="flex flex-col">
139
+ <span className="font-medium">{file.name}</span>
140
+ <span className="text-sm text-muted-foreground">
141
+ {Math.round(file.size / 1024)}KB
142
+ </span>
143
+ </div>
144
+ <Button
145
+ variant="ghost"
146
+ size="icon"
147
+ className="ml-auto"
148
+ onClick={removeItem}
149
+ >
150
+ <Trash className="h-4 w-4" />
151
+ </Button>
152
+ </div>
153
+ ),
154
+ renderRejectedFile: (file, removeItem) => (
155
+ <div className="flex items-center gap-2 p-2 bg-destructive/10 rounded-lg">
156
+ <File className="h-12 w-12" />
157
+ <div className="flex flex-col">
158
+ <span className="font-medium">{file.name}</span>
159
+ <span className="text-sm text-muted-foreground">
160
+ Archivo rechazado
161
+ </span>
162
+ </div>
163
+ <Button
164
+ variant="ghost"
165
+ size="icon"
166
+ className="ml-auto"
167
+ onClick={removeItem}
168
+ >
169
+ <Trash className="h-4 w-4" />
170
+ </Button>
171
+ </div>
172
+ ),
173
+ },
174
+ };
175
+
176
+ /**
177
+ * El componente Dropzone está compuesto por los componentes primitivos UploadRoot, UploadTrigger, UploadContent, UploadAcceptedFiles y UploadRejectedFiles.
178
+ *
179
+ * Este ejemplo muestra como armar el componente Dropzone usando los componentes primitivos.
180
+ *
181
+ * ##### Estructura
182
+ * ```tsx
183
+ * <UploadRoot>
184
+ * <UploadTrigger>
185
+ * ...
186
+ * </UploadTrigger>
187
+ * <UploadContent>
188
+ * <UploadAcceptedFiles />
189
+ * <UploadRejectedFiles />
190
+ * </UploadContent>
191
+ * </UploadRoot>
192
+ * ```
193
+ */
194
+ export const Primitives: Story = {
195
+ render: () => {
196
+ return (
197
+ <UploadRoot>
198
+ <UploadTrigger asChild>
199
+ <Button>
200
+ <Camera /> Upload
201
+ </Button>
202
+ </UploadTrigger>
203
+ <UploadContent>
204
+ <UploadAcceptedFiles />
205
+ <UploadRejectedFiles />
206
+ </UploadContent>
207
+ </UploadRoot>
208
+ );
209
+ },
210
+ };
@@ -0,0 +1,154 @@
1
+ import { LucideIcon, UploadCloud } from "lucide-react";
2
+ import { ComponentPropsWithoutRef, useMemo } from "react";
3
+ import { cn } from "../../lib";
4
+ import { FileTypeValue } from "./file-types";
5
+ import {
6
+ UploadAcceptedFiles,
7
+ UploadContent,
8
+ UploadRejectedFiles,
9
+ UploadRoot,
10
+ UploadTrigger,
11
+ } from "./upload-primitives";
12
+ import { FileError } from "./use-dropzone";
13
+
14
+ export type DropzoneProps = {
15
+ /**
16
+ * Función que se ejecuta cuando se agregan archivos
17
+ */
18
+ onFileAdd?: (files: File[]) => void;
19
+ /**
20
+ * Función que se ejecuta cuando se elimina un archivo
21
+ */
22
+ onFileRemove?: (index: number, type: "accepted" | "rejected") => void;
23
+ /**
24
+ * Evento que captura cambios en lista de archivos
25
+ * @default () => {}
26
+ */
27
+ onChangeFiles?: (files: File[]) => void;
28
+ /**
29
+ * Lista de archivos
30
+ * @default []
31
+ */
32
+ files?: File[];
33
+ /**
34
+ * Número máximo de archivos permitidos
35
+ * @default 1
36
+ */
37
+ maxFiles?: number;
38
+ /**
39
+ * Lista de extensiones permitidas (e.j. ["image/jpeg", "image/png"])
40
+ * @default []
41
+ */
42
+ allowedExtensions?: FileTypeValue[];
43
+ /**
44
+ * Tamaño máximo de archivo en bytes
45
+ * @default 5MB
46
+ */
47
+ maxFileSize?: number;
48
+ /**
49
+ * Permite seleccionar múltiples archivos
50
+ */
51
+ multiple?: boolean;
52
+ /**
53
+ * Función para renderizar cada archivo aceptado
54
+ */
55
+ renderAcceptedFile?: (file: File, removeItem: () => void) => React.ReactNode;
56
+ /**
57
+ * Función para renderizar cada archivo rechazado
58
+ */
59
+ renderRejectedFile?: (file: File, removeItem: () => void) => React.ReactNode;
60
+ /**
61
+ * Mostrar archivos rechazados
62
+ * @default false
63
+ */
64
+ showRejected?: boolean;
65
+ /**
66
+ * Clase CSS para el contenedor
67
+ */
68
+ className?: string;
69
+ /**
70
+ * Icono personalizado para el dropzone
71
+ * @default UploadCloud
72
+ */
73
+ icon?: LucideIcon;
74
+ /**
75
+ * Tamaño del icono
76
+ * @default 40
77
+ */
78
+ iconSize?: number;
79
+ /**
80
+ * Función que se ejecuta cuando se rechazan archivos
81
+ */
82
+ onError?: (fileErrors: FileError[]) => void;
83
+ } & Omit<ComponentPropsWithoutRef<"div">, "onDrop" | "onError">;
84
+
85
+ export const Dropzone = ({
86
+ onFileAdd,
87
+ onFileRemove,
88
+ onChangeFiles = () => {},
89
+ files,
90
+ maxFiles = 1,
91
+ allowedExtensions = [],
92
+ maxFileSize = 5 * 1024 * 1024,
93
+ multiple,
94
+ renderAcceptedFile,
95
+ renderRejectedFile,
96
+ showRejected = false,
97
+ className,
98
+ icon: Icon = UploadCloud,
99
+ iconSize = 40,
100
+ onError,
101
+ ...props
102
+ }: DropzoneProps) => {
103
+ const allowedLabels = useMemo(() => {
104
+ return allowedExtensions.map((ext) => ext.split("/")?.[1]).join(", ");
105
+ }, [allowedExtensions]);
106
+
107
+ return (
108
+ <UploadRoot
109
+ onFileAdd={onFileAdd}
110
+ onFileRemove={onFileRemove}
111
+ files={files}
112
+ onChangeFiles={onChangeFiles}
113
+ maxFiles={maxFiles}
114
+ allowedExtensions={allowedExtensions}
115
+ maxFileSize={maxFileSize}
116
+ multiple={multiple ? true : undefined}
117
+ className={cn("w-full", className)}
118
+ onError={onError}
119
+ {...props}
120
+ >
121
+ <UploadTrigger asChild>
122
+ <div
123
+ className={cn(
124
+ "w-full min-h-[150px] border-2 border-dashed rounded-lg border-muted p-4",
125
+ "hover:bg-muted/50 data-[drag-active=true]:border-primary data-[drag-active=true]:bg-primary/10",
126
+ "transition-colors flex flex-col items-center justify-center gap-4",
127
+ "pointer-events-auto",
128
+ )}
129
+ >
130
+ <Icon
131
+ className="text-muted-foreground pointer-events-none"
132
+ style={{ width: iconSize, height: iconSize }}
133
+ />
134
+ <div className="text-center text-muted-foreground pointer-events-none">
135
+ <p>Arrastra y suelta archivos aquí o haz clic para seleccionar</p>
136
+ {allowedExtensions.length > 0 && (
137
+ <p className="text-sm">Tipos permitidos: {allowedLabels}</p>
138
+ )}
139
+ <p className="text-sm">
140
+ Tamaño máximo: {Math.floor(maxFileSize / 1024 / 1024)}MB
141
+ </p>
142
+ </div>
143
+ </div>
144
+ </UploadTrigger>
145
+
146
+ <UploadContent>
147
+ <UploadAcceptedFiles renderFile={renderAcceptedFile} />
148
+ {showRejected && (
149
+ <UploadRejectedFiles renderFile={renderRejectedFile} />
150
+ )}
151
+ </UploadContent>
152
+ </UploadRoot>
153
+ );
154
+ };
@@ -0,0 +1,64 @@
1
+ export const FileType = {
2
+ // Imágenes
3
+ JPEG: "image/jpeg",
4
+ JPG: "image/jpg",
5
+ PNG: "image/png",
6
+ GIF: "image/gif",
7
+ WEBP: "image/webp",
8
+ SVG: "image/svg+xml",
9
+
10
+ // Documentos
11
+ PDF: "application/pdf",
12
+ DOC: "application/msword",
13
+ DOCX: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
14
+ XLS: "application/vnd.ms-excel",
15
+ XLSX: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
16
+ PPT: "application/vnd.ms-powerpoint",
17
+ PPTX: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
18
+
19
+ // Archivos de texto
20
+ TXT: "text/plain",
21
+ CSV: "text/csv",
22
+
23
+ // Archivos comprimidos
24
+ ZIP: "application/zip",
25
+ RAR: "application/x-rar-compressed",
26
+
27
+ // Audio
28
+ MP3: "audio/mpeg",
29
+ WAV: "audio/wav",
30
+
31
+ // Video
32
+ MP4: "video/mp4",
33
+ WEBM: "video/webm",
34
+
35
+ // Otros
36
+ JSON: "application/json",
37
+ XML: "application/xml",
38
+ } as const;
39
+
40
+ export type FileTypeValue = (typeof FileType)[keyof typeof FileType];
41
+
42
+ // Grupos comunes de tipos de archivo
43
+ export const FileTypeGroups: Record<string, FileTypeValue[]> = {
44
+ IMAGES: [
45
+ FileType.JPEG,
46
+ FileType.JPG,
47
+ FileType.PNG,
48
+ FileType.GIF,
49
+ FileType.WEBP,
50
+ FileType.SVG,
51
+ ],
52
+ DOCUMENTS: [
53
+ FileType.PDF,
54
+ FileType.DOC,
55
+ FileType.DOCX,
56
+ FileType.XLS,
57
+ FileType.XLSX,
58
+ FileType.PPT,
59
+ FileType.PPTX,
60
+ ],
61
+ COMPRESSED: [FileType.ZIP, FileType.RAR],
62
+ AUDIO: [FileType.MP3, FileType.WAV],
63
+ VIDEO: [FileType.MP4, FileType.WEBM],
64
+ } as const;
@@ -0,0 +1,3 @@
1
+ export * from "./dropzone";
2
+ export * from "./file-types";
3
+ export * from "./upload-primitives";