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

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 (349) hide show
  1. package/dist/index.css +1 -1
  2. package/package.json +4 -3
  3. package/src/__doc__/Changelog.mdx +6 -0
  4. package/src/__doc__/Components.mdx +73 -0
  5. package/src/__doc__/Examples.tsx +69 -0
  6. package/src/__doc__/Icons.mdx +41 -0
  7. package/src/__doc__/Intro.mdx +138 -0
  8. package/src/__doc__/MCP.mdx +71 -0
  9. package/src/__doc__/Migration.mdx +475 -0
  10. package/src/__doc__/Theme.mdx +132 -0
  11. package/src/__doc__/Types.mdx +252 -0
  12. package/src/components/alert/alert.stories.tsx +142 -0
  13. package/src/components/alert/alert.tsx +109 -0
  14. package/src/components/alert/index.ts +7 -0
  15. package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
  16. package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
  17. package/src/components/alert-dialog/alert-dialog.tsx +265 -0
  18. package/src/components/alert-dialog/index.ts +1 -0
  19. package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
  20. package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
  21. package/src/components/auto-complete/auto-complete.tsx +82 -0
  22. package/src/components/auto-complete/index.ts +2 -0
  23. package/src/components/avatar/avatar.stories.tsx +84 -0
  24. package/src/components/avatar/avatar.test.tsx +61 -0
  25. package/src/components/avatar/avatar.tsx +104 -0
  26. package/src/components/avatar/index.ts +1 -0
  27. package/src/components/background-image/background-image.stories.tsx +21 -0
  28. package/src/components/background-image/background-image.test.tsx +29 -0
  29. package/src/components/background-image/background-image.tsx +23 -0
  30. package/src/components/background-image/index.ts +1 -0
  31. package/src/components/button/button.stories.tsx +396 -0
  32. package/src/components/button/button.test.tsx +58 -0
  33. package/src/components/button/button.tsx +31 -0
  34. package/src/components/button/button.variants.ts +44 -0
  35. package/src/components/button/components/base-button.tsx +86 -0
  36. package/src/components/button/components/loader-overlay.tsx +21 -0
  37. package/src/components/button/components/loading-icon.tsx +47 -0
  38. package/src/components/button/index.ts +3 -0
  39. package/src/components/calendar/calendar.model.ts +86 -0
  40. package/src/components/calendar/calendar.stories.tsx +155 -0
  41. package/src/components/calendar/calendar.test.tsx +12 -0
  42. package/src/components/calendar/calendar.tsx +185 -0
  43. package/src/components/calendar/components/calendar-navigation.tsx +141 -0
  44. package/src/components/calendar/components/day.tsx +61 -0
  45. package/src/components/calendar/components/decade-view.tsx +45 -0
  46. package/src/components/calendar/components/index.ts +6 -0
  47. package/src/components/calendar/components/month-view.tsx +58 -0
  48. package/src/components/calendar/components/week-days.tsx +27 -0
  49. package/src/components/calendar/components/year-view.tsx +29 -0
  50. package/src/components/calendar/hooks/index.ts +4 -0
  51. package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
  52. package/src/components/calendar/hooks/use-calendar.ts +90 -0
  53. package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
  54. package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
  55. package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
  56. package/src/components/calendar/index.ts +1 -0
  57. package/src/components/calendar/utils/typeguards.ts +7 -0
  58. package/src/components/card/card.stories.tsx +116 -0
  59. package/src/components/card/card.tsx +74 -0
  60. package/src/components/card/index.ts +1 -0
  61. package/src/components/center/center.stories.tsx +81 -0
  62. package/src/components/center/center.tsx +24 -0
  63. package/src/components/center/index.ts +1 -0
  64. package/src/components/checkbox/checkbox.stories.tsx +307 -0
  65. package/src/components/checkbox/checkbox.tsx +273 -0
  66. package/src/components/checkbox/index.ts +1 -0
  67. package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
  68. package/src/components/checkbox-group/checkbox-group.tsx +16 -0
  69. package/src/components/checkbox-group/index.ts +1 -0
  70. package/src/components/combobox/combobox.stories.tsx +339 -0
  71. package/src/components/combobox/combobox.tsx +892 -0
  72. package/src/components/combobox/index.ts +1 -0
  73. package/src/components/date-picker/date-input.stories.tsx +158 -0
  74. package/src/components/date-picker/date-input.tsx +163 -0
  75. package/src/components/date-picker/date-picker.model.ts +90 -0
  76. package/src/components/date-picker/date-picker.stories.tsx +200 -0
  77. package/src/components/date-picker/date-picker.test.tsx +23 -0
  78. package/src/components/date-picker/date-picker.tsx +298 -0
  79. package/src/components/date-picker/date-picker.utils.ts +260 -0
  80. package/src/components/date-picker/index.ts +3 -0
  81. package/src/components/date-picker/use-date-input-popover.ts +48 -0
  82. package/src/components/date-picker/use-date-input.ts +125 -0
  83. package/src/components/dialog/dialog.stories.tsx +171 -0
  84. package/src/components/dialog/dialog.test.tsx +68 -0
  85. package/src/components/dialog/dialog.tsx +277 -0
  86. package/src/components/dialog/index.ts +1 -0
  87. package/src/components/divider/divider.stories.tsx +70 -0
  88. package/src/components/divider/divider.test.tsx +22 -0
  89. package/src/components/divider/divider.tsx +23 -0
  90. package/src/components/divider/index.ts +1 -0
  91. package/src/components/dropzone/dropzone.stories.tsx +210 -0
  92. package/src/components/dropzone/dropzone.tsx +154 -0
  93. package/src/components/dropzone/file-types.ts +64 -0
  94. package/src/components/dropzone/index.ts +3 -0
  95. package/src/components/dropzone/upload-primitives.tsx +310 -0
  96. package/src/components/dropzone/use-dropzone.ts +122 -0
  97. package/src/components/empty-state/empty-state.stories.tsx +56 -0
  98. package/src/components/empty-state/empty-state.tsx +39 -0
  99. package/src/components/empty-state/index.ts +1 -0
  100. package/src/components/field/field.stories.tsx +223 -0
  101. package/src/components/field/field.tsx +229 -0
  102. package/src/components/field/index.ts +1 -0
  103. package/src/components/form/form.stories.tsx +594 -0
  104. package/src/components/form/form.tsx +30 -0
  105. package/src/components/form/index.ts +1 -0
  106. package/src/components/heading/heading.stories.tsx +74 -0
  107. package/src/components/heading/heading.tsx +28 -0
  108. package/src/components/heading/heading.variants.ts +27 -0
  109. package/src/components/heading/index.ts +1 -0
  110. package/src/components/index.ts +46 -0
  111. package/src/components/input/index.ts +1 -0
  112. package/src/components/input/input.stories.tsx +104 -0
  113. package/src/components/input/input.tsx +75 -0
  114. package/src/components/kbd/index.ts +1 -0
  115. package/src/components/kbd/kbd.stories.tsx +40 -0
  116. package/src/components/kbd/kbd.tsx +31 -0
  117. package/src/components/kbd/kbd.variants.ts +26 -0
  118. package/src/components/label/index.ts +1 -0
  119. package/src/components/label/label.stories.tsx +68 -0
  120. package/src/components/label/label.test.tsx +61 -0
  121. package/src/components/label/label.tsx +62 -0
  122. package/src/components/loader/index.ts +1 -0
  123. package/src/components/loader/loader.stories.tsx +60 -0
  124. package/src/components/loader/loader.test.tsx +26 -0
  125. package/src/components/loader/loader.tsx +60 -0
  126. package/src/components/menu/index.ts +2 -0
  127. package/src/components/menu/menu-primitives.tsx +248 -0
  128. package/src/components/menu/menu.stories.tsx +203 -0
  129. package/src/components/menu/menu.tsx +100 -0
  130. package/src/components/menu/util/render-menu-item.tsx +54 -0
  131. package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
  132. package/src/components/multi-select/index.ts +1 -0
  133. package/src/components/multi-select/multi-select.stories.tsx +294 -0
  134. package/src/components/multi-select/multi-select.tsx +300 -0
  135. package/src/components/multi-select/multi-select.variants.ts +22 -0
  136. package/src/components/number-input/index.ts +1 -0
  137. package/src/components/number-input/number-input.stories.tsx +209 -0
  138. package/src/components/number-input/number-input.test.tsx +87 -0
  139. package/src/components/number-input/number-input.tsx +230 -0
  140. package/src/components/pagination/components/pagination-option.tsx +27 -0
  141. package/src/components/pagination/index.ts +1 -0
  142. package/src/components/pagination/pagination.stories.tsx +80 -0
  143. package/src/components/pagination/pagination.test.tsx +76 -0
  144. package/src/components/pagination/pagination.tsx +102 -0
  145. package/src/components/password/index.ts +1 -0
  146. package/src/components/password/password.stories.tsx +104 -0
  147. package/src/components/password/password.tsx +71 -0
  148. package/src/components/popover/index.ts +1 -0
  149. package/src/components/popover/popover.stories.tsx +213 -0
  150. package/src/components/popover/popover.tsx +203 -0
  151. package/src/components/progress/index.ts +1 -0
  152. package/src/components/progress/progress.stories.tsx +124 -0
  153. package/src/components/progress/progress.test.tsx +25 -0
  154. package/src/components/progress/progress.tsx +124 -0
  155. package/src/components/scroll-area/index.ts +1 -0
  156. package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
  157. package/src/components/scroll-area/scroll-area.tsx +64 -0
  158. package/src/components/select/index.ts +1 -0
  159. package/src/components/select/select.stories.tsx +253 -0
  160. package/src/components/select/select.tsx +430 -0
  161. package/src/components/show/index.ts +1 -0
  162. package/src/components/show/show.stories.tsx +197 -0
  163. package/src/components/show/show.test.tsx +41 -0
  164. package/src/components/show/show.tsx +16 -0
  165. package/src/components/skeleton/index.ts +1 -0
  166. package/src/components/skeleton/skeleton.stories.tsx +36 -0
  167. package/src/components/skeleton/skeleton.test.tsx +14 -0
  168. package/src/components/skeleton/skeleton.tsx +15 -0
  169. package/src/components/stack/index.ts +1 -0
  170. package/src/components/stack/stack.stories.tsx +194 -0
  171. package/src/components/stack/stack.tsx +52 -0
  172. package/src/components/stepper/Stepper.tsx +190 -0
  173. package/src/components/stepper/context/stepper-context.tsx +11 -0
  174. package/src/components/stepper/index.ts +1 -0
  175. package/src/components/stepper/stepper.stories.tsx +130 -0
  176. package/src/components/stepper/stepper.test.tsx +91 -0
  177. package/src/components/switch/index.ts +1 -0
  178. package/src/components/switch/switch.stories.tsx +122 -0
  179. package/src/components/switch/switch.test.tsx +30 -0
  180. package/src/components/switch/switch.tsx +86 -0
  181. package/src/components/table/index.ts +3 -0
  182. package/src/components/table/table-primitives.tsx +122 -0
  183. package/src/components/table/table.model.ts +20 -0
  184. package/src/components/table/table.stories.tsx +169 -0
  185. package/src/components/table/table.test.tsx +91 -0
  186. package/src/components/table/table.tsx +109 -0
  187. package/src/components/table-pagination/index.ts +2 -0
  188. package/src/components/table-pagination/table-pagination.model.ts +2 -0
  189. package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
  190. package/src/components/table-pagination/table-pagination.test.tsx +32 -0
  191. package/src/components/table-pagination/table-pagination.tsx +108 -0
  192. package/src/components/tabs/context/tabs-context.tsx +14 -0
  193. package/src/components/tabs/index.ts +1 -0
  194. package/src/components/tabs/tabs.stories.tsx +182 -0
  195. package/src/components/tabs/tabs.test.tsx +61 -0
  196. package/src/components/tabs/tabs.tsx +175 -0
  197. package/src/components/tag/index.ts +2 -0
  198. package/src/components/tag/tag.stories.tsx +170 -0
  199. package/src/components/tag/tag.test.tsx +18 -0
  200. package/src/components/tag/tag.tsx +99 -0
  201. package/src/components/tag/tag.variants.ts +31 -0
  202. package/src/components/textarea/index.ts +1 -0
  203. package/src/components/textarea/textarea.stories.tsx +73 -0
  204. package/src/components/textarea/textarea.tsx +105 -0
  205. package/src/components/timeline/index.ts +1 -0
  206. package/src/components/timeline/timeline-status.ts +5 -0
  207. package/src/components/timeline/timeline.stories.tsx +84 -0
  208. package/src/components/timeline/timeline.tsx +147 -0
  209. package/src/components/toast/index.ts +1 -0
  210. package/src/components/toast/toast.stories.tsx +392 -0
  211. package/src/components/toast/toast.test.tsx +50 -0
  212. package/src/components/toast/toast.tsx +411 -0
  213. package/src/components/tooltip/index.ts +1 -0
  214. package/src/components/tooltip/tooltip.stories.tsx +226 -0
  215. package/src/components/tooltip/tooltip.test.tsx +46 -0
  216. package/src/components/tooltip/tooltip.tsx +171 -0
  217. package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
  218. package/src/components/tree/index.ts +2 -0
  219. package/src/components/tree/tree-primitives.tsx +126 -0
  220. package/src/components/tree/tree.stories.tsx +468 -0
  221. package/src/components/tree/tree.tsx +42 -0
  222. package/src/hooks/index.ts +26 -0
  223. package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
  224. package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
  225. package/src/hooks/useArray/index.ts +1 -0
  226. package/src/hooks/useArray/useArray.ts +76 -0
  227. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
  228. package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
  229. package/src/hooks/useAsync/index.ts +1 -0
  230. package/src/hooks/useAsync/useAsync.ts +58 -0
  231. package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
  232. package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
  233. package/src/hooks/useClickOutside/index.ts +1 -0
  234. package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
  235. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
  236. package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
  237. package/src/hooks/useClipboard/index.ts +1 -0
  238. package/src/hooks/useClipboard/useClipboard.tsx +28 -0
  239. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
  240. package/src/hooks/useDebounceCallback/index.ts +1 -0
  241. package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
  242. package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
  243. package/src/hooks/useDebounceValue/index.ts +1 -0
  244. package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
  245. package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
  246. package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
  247. package/src/hooks/useDisclosure/index.ts +1 -0
  248. package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
  249. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
  250. package/src/hooks/useDocumentTitle/index.ts +1 -0
  251. package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
  252. package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
  253. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
  254. package/src/hooks/useEventListener/index.ts +1 -0
  255. package/src/hooks/useEventListener/useEventListener.ts +25 -0
  256. package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
  257. package/src/hooks/useFocusTrap/index.ts +1 -0
  258. package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
  259. package/src/hooks/useFocusTrap/tabbable.ts +70 -0
  260. package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
  261. package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
  262. package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
  263. package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
  264. package/src/hooks/useHotkey/__utils__/index.ts +3 -0
  265. package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
  266. package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
  267. package/src/hooks/useHotkey/index.ts +1 -0
  268. package/src/hooks/useHotkey/useHotkey.ts +34 -0
  269. package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
  270. package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
  271. package/src/hooks/useHover/index.ts +1 -0
  272. package/src/hooks/useHover/useHover.tsx +40 -0
  273. package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
  274. package/src/hooks/useIsVisible/index.ts +1 -0
  275. package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
  276. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
  277. package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
  278. package/src/hooks/useLocalStorage/index.ts +1 -0
  279. package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
  280. package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
  281. package/src/hooks/useMediaQuery/index.ts +1 -0
  282. package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
  283. package/src/hooks/useMemoizedFn/index.ts +1 -0
  284. package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
  285. package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
  286. package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
  287. package/src/hooks/useMutation/index.ts +1 -0
  288. package/src/hooks/useMutation/useMutation.tsx +60 -0
  289. package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
  290. package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
  291. package/src/hooks/useObject/index.ts +1 -0
  292. package/src/hooks/useObject/useObject.tsx +48 -0
  293. package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
  294. package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
  295. package/src/hooks/usePagination/index.ts +2 -0
  296. package/src/hooks/usePagination/usePagination.tsx +74 -0
  297. package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
  298. package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
  299. package/src/hooks/usePortal/index.ts +1 -0
  300. package/src/hooks/usePortal/usePortal.ts +40 -0
  301. package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
  302. package/src/hooks/usePreventCloseWindow/index.ts +1 -0
  303. package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
  304. package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
  305. package/src/hooks/useRangePagination/index.ts +2 -0
  306. package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
  307. package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
  308. package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
  309. package/src/hooks/useSelection/index.ts +1 -0
  310. package/src/hooks/useSelection/useSelection.ts +121 -0
  311. package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
  312. package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
  313. package/src/hooks/useStep/index.ts +1 -0
  314. package/src/hooks/useStep/useStep.ts +57 -0
  315. package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
  316. package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
  317. package/src/hooks/useToggle/index.ts +1 -0
  318. package/src/hooks/useToggle/useToggle.ts +16 -0
  319. package/src/index.ts +6 -0
  320. package/src/lib/cn.ts +8 -0
  321. package/src/lib/index.ts +1 -0
  322. package/src/models/Generic.model.ts +67 -0
  323. package/src/models/index.ts +1 -0
  324. package/src/providers/index.ts +2 -0
  325. package/src/providers/library-provider.tsx +44 -0
  326. package/src/providers/theme/ThemeProvider.tsx +25 -0
  327. package/src/providers/theme/index.ts +3 -0
  328. package/src/providers/theme/types.ts +11 -0
  329. package/src/providers/theme/useThemeProps.ts +25 -0
  330. package/src/stores/theme.store.ts +31 -0
  331. package/src/styles/components.css +4 -0
  332. package/src/styles/index.css +2 -0
  333. package/src/styles/library.css +2 -0
  334. package/src/styles/theme.css +232 -0
  335. package/src/utils/dates/parseDateRange.utility.ts +39 -0
  336. package/src/utils/form.tsx +91 -0
  337. package/src/utils/functions/createSafeContext.ts +17 -0
  338. package/src/utils/functions/ensureReactElement.tsx +30 -0
  339. package/src/utils/functions/getFormData.ts +19 -0
  340. package/src/utils/functions/index.ts +4 -0
  341. package/src/utils/functions/mergeRefs.ts +18 -0
  342. package/src/utils/index.ts +3 -0
  343. package/src/utils/strings/extractInitials.utility.ts +10 -0
  344. package/src/utils/strings/index.ts +1 -0
  345. package/src/utils/tests/click.ts +3 -0
  346. package/src/utils/tests/index.ts +2 -0
  347. package/src/utils/tests/keyboard.ts +21 -0
  348. package/src/utils/tests/type.ts +6 -0
  349. package/dist/components.css +0 -2
@@ -0,0 +1,70 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { CatIcon, Github, PencilIcon } from "lucide-react";
3
+ import { Button, Divider } from "../../components";
4
+
5
+ const meta: Meta<typeof Divider> = {
6
+ title: "others/Divider",
7
+ component: Divider,
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Horizontal: Story = {
14
+ args: {
15
+ orientation: "horizontal",
16
+ },
17
+ decorators: [
18
+ (Story) => (
19
+ <div className="w-20 m-auto rounded flex flex-col items-center gap-2">
20
+ <Button size="icon">
21
+ <PencilIcon />
22
+ </Button>
23
+ <Story />
24
+ <Button size="icon">
25
+ <CatIcon />
26
+ </Button>
27
+ <Story />
28
+ <Button size="icon">
29
+ <Github />
30
+ </Button>
31
+ </div>
32
+ ),
33
+ ],
34
+ };
35
+
36
+ /**
37
+ * Para poder desplegar el componente `Divider` de manera vertical, se debe utilizar el atributo `orientation` con el valor `vertical`
38
+ * y setear una altura en el contenedor
39
+ *
40
+ * ```tsx
41
+ * <div className="h-16"> // <- Altura fija
42
+ * <PencilIcon />
43
+ * <Divider orientation="vertical" /> // <- vertical
44
+ * <CatIcon />
45
+ * </div>
46
+ * ```
47
+ */
48
+
49
+ export const Vertical: Story = {
50
+ args: {
51
+ orientation: "vertical",
52
+ },
53
+ decorators: [
54
+ (Story) => (
55
+ <div className="h-16 w-36 m-auto rounded flex items-center gap-2">
56
+ <Button size="icon">
57
+ <PencilIcon />
58
+ </Button>
59
+ <Story />
60
+ <Button size="icon">
61
+ <CatIcon />
62
+ </Button>
63
+ <Story />
64
+ <Button size="icon">
65
+ <Github />
66
+ </Button>
67
+ </div>
68
+ ),
69
+ ],
70
+ };
@@ -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 { HTMLProps } from "react";
2
+ import { cn } from "../../lib";
3
+
4
+ type Props = {
5
+ orientation?: "horizontal" | "vertical";
6
+ } & HTMLProps<HTMLHRElement>;
7
+
8
+ export const Divider = (props: Props) => {
9
+ const { className, orientation = "horizontal" } = props;
10
+
11
+ return (
12
+ <hr
13
+ {...props}
14
+ aria-orientation={orientation}
15
+ className={cn(
16
+ "shrink-0 bg-input",
17
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
18
+ className,
19
+ )}
20
+ data-slot="divider"
21
+ />
22
+ );
23
+ };
@@ -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";