@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,84 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Settings, UserCheck } from "lucide-react";
3
+ import { Timeline } from ".";
4
+
5
+ const meta: Meta<typeof Timeline> = {
6
+ title: "Data display/Timeline",
7
+ component: Timeline,
8
+ };
9
+
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof Timeline>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ items: [
17
+ {
18
+ content: "First",
19
+ color: "green",
20
+ },
21
+ {
22
+ content: "Second",
23
+ icon: <UserCheck />,
24
+ status: "done",
25
+ },
26
+ {
27
+ content: "Third",
28
+ icon: <Settings />,
29
+ status: "pending",
30
+ },
31
+ ],
32
+ },
33
+ };
34
+
35
+ export const OnlyRight: Story = {
36
+ args: {
37
+ items: [
38
+ {
39
+ content: (
40
+ <div>
41
+ <time className="text-gray-400 text-sm">12/12/2022</time>
42
+ <p>Argentina Campeón Mundial</p>
43
+ <p>El dibu héroe de un país</p>
44
+ <p>Messi mejor jugador del mundo</p>
45
+ <p>Mejor país del mundo</p>
46
+ </div>
47
+ ),
48
+ status: "done",
49
+ },
50
+ {
51
+ content: (
52
+ <div>
53
+ <time className="text-gray-400 text-sm">10/12/2023</time>
54
+ <p>Milei electo</p>
55
+ </div>
56
+ ),
57
+ status: "done",
58
+ },
59
+ {
60
+ content: (
61
+ <div>
62
+ <time className="text-gray-400 text-sm">14/10/2024</time>
63
+ <p>Año de recuperación del país</p>
64
+ </div>
65
+ ),
66
+ status: "done",
67
+ },
68
+ {
69
+ content: (
70
+ <div>
71
+ <time className="text-gray-400 text-sm">1/1/2025</time>
72
+ <p>Nuevo año</p>
73
+ </div>
74
+ ),
75
+ status: "pending",
76
+ },
77
+ ],
78
+ },
79
+ render: (args) => (
80
+ <div className="w-[400px]">
81
+ <Timeline {...args} />
82
+ </div>
83
+ ),
84
+ };
@@ -0,0 +1,147 @@
1
+ import { HTMLProps, ReactNode } from "react";
2
+ import { cn } from "../../lib";
3
+ import { TimelineStatus } from "./timeline-status";
4
+
5
+ interface ItemTimeline {
6
+ content?: ReactNode;
7
+ icon?: ReactNode;
8
+ status?: "done" | "pending" | "error";
9
+ color?: string;
10
+ }
11
+
12
+ interface TimelineProps {
13
+ items: ItemTimeline[];
14
+ itemsProps?: HTMLProps<HTMLDivElement>;
15
+ containerProps?: HTMLProps<HTMLUListElement>;
16
+ classNameContainer?: string;
17
+ classNameItem?: string;
18
+ classNameItemDot?: string;
19
+ classNameItemContainerDot?: string;
20
+ classNameItemContent?: string;
21
+ }
22
+ export const Timeline = ({
23
+ items,
24
+ classNameContainer,
25
+ containerProps,
26
+ ...itemsProps
27
+ }: TimelineProps) => {
28
+ return (
29
+ <TimelineRoot className={classNameContainer} {...containerProps}>
30
+ {items.map((item: ItemTimeline, i) => (
31
+ <TimelineListItem
32
+ key={i}
33
+ icon={item.icon}
34
+ color={item.color}
35
+ status={item.status}
36
+ {...itemsProps}
37
+ >
38
+ {item.content}
39
+ </TimelineListItem>
40
+ ))}
41
+ </TimelineRoot>
42
+ );
43
+ };
44
+
45
+ export const TimelineRoot = (props: HTMLProps<HTMLUListElement>) => {
46
+ return (
47
+ <ul
48
+ data-slot="timeline"
49
+ {...props}
50
+ className={cn(
51
+ "relative flex justify-center flex-col border-input",
52
+ props.className,
53
+ )}
54
+ />
55
+ );
56
+ };
57
+
58
+ interface TimelineListItemProps extends HTMLProps<HTMLLIElement> {
59
+ icon?: ReactNode;
60
+ status?: "done" | "pending" | "error";
61
+ color?: string;
62
+ itemsProps?: HTMLProps<HTMLDivElement>;
63
+ classNameContainer?: string;
64
+ classNameItemDot?: string;
65
+ classNameItemContainerDot?: string;
66
+ classNameItemContent?: string;
67
+ }
68
+ export const TimelineListItem = ({
69
+ icon,
70
+ status,
71
+ color,
72
+ classNameItemDot,
73
+ classNameItemContainerDot,
74
+ classNameItemContent,
75
+ itemsProps,
76
+ children,
77
+ ...props
78
+ }: TimelineListItemProps) => {
79
+ return (
80
+ <li
81
+ data-slot="timeline-item"
82
+ className={cn(
83
+ "rounded-lg w-full px-2 gap-1 grid hover:bg-muted transition-colors ease-in grid-cols-[minmax(auto,20px)_1fr]",
84
+ props.className,
85
+ )}
86
+ {...props}
87
+ >
88
+ <TimelineItemDot
89
+ icon={icon}
90
+ status={status}
91
+ color={color}
92
+ classNameDot={classNameItemDot}
93
+ classNameContainerDot={classNameItemContainerDot}
94
+ />
95
+ <TimelineItemContent className={classNameItemContent} {...itemsProps}>
96
+ {children}
97
+ </TimelineItemContent>
98
+ </li>
99
+ );
100
+ };
101
+
102
+ interface TimelineItemDotProps {
103
+ icon?: ReactNode;
104
+ status?: "done" | "pending" | "error";
105
+ color?: string;
106
+ classNameDot?: string;
107
+ classNameContainerDot?: string;
108
+ }
109
+ export const TimelineItemDot = ({
110
+ icon,
111
+ status = "pending",
112
+ color,
113
+ classNameDot,
114
+ classNameContainerDot,
115
+ }: TimelineItemDotProps) => {
116
+ return (
117
+ <div
118
+ data-slot="timeline-item-dot"
119
+ className={cn(
120
+ "relative pb-6 last:after:hidden after:absolute after:top-0 after:bottom-0 after:left-1/2 after:w-px after:-translate-x-1/2 after:bg-gray-200",
121
+ classNameContainerDot,
122
+ )}
123
+ >
124
+ <div
125
+ data-slot="timeline-item-dot-icon"
126
+ className={cn(
127
+ "relative z-10 flex text-white items-center justify-center rounded-full left-1/2 -translate-x-1/2",
128
+ icon ? "aspect-square p-1.5 min-h-7 min-w-7 top-2" : "size-2 top-4",
129
+ classNameDot,
130
+ )}
131
+ style={{ backgroundColor: color || TimelineStatus?.[status] }}
132
+ >
133
+ {icon}
134
+ </div>
135
+ </div>
136
+ );
137
+ };
138
+
139
+ export const TimelineItemContent = (props: HTMLProps<HTMLDivElement>) => {
140
+ return (
141
+ <div
142
+ data-slot="timeline-item-content"
143
+ {...props}
144
+ className={cn("grow px-2 pb-4 pt-2", props.className)}
145
+ />
146
+ );
147
+ };
@@ -0,0 +1 @@
1
+ export * from "./toast";
@@ -0,0 +1,392 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { LibraryProvider } from "../../providers";
3
+ import { Button } from "../button";
4
+ import {
5
+ createToastManager,
6
+ ToastProvider,
7
+ toast,
8
+ useToastManager,
9
+ } from "./toast";
10
+
11
+ /**
12
+ * Imperative toast notification system built on `@base-ui/react/toast`.
13
+ * Toasts stack in the bottom-right corner with enter/exit animations,
14
+ * swipe-to-dismiss, and expand-on-hover.
15
+ *
16
+ * ## Usage
17
+ *
18
+ * Call `toast` or `toast.*` from anywhere — components, services, stores, event handlers.
19
+ * A `string` renders as the prominent title; an object accepts:
20
+ *
21
+ * | Prop | Type | Description |
22
+ * |------|------|-------------|
23
+ * | `variant` | `"default" \| "success" \| "error" \| "warning" \| "info"` | Color and icon |
24
+ * | `title` | `string` | Primary text (`font-medium`) |
25
+ * | `description` | `string \| ReactNode` | Secondary text (muted) |
26
+ * | `content` | `ReactNode` | Replaces the entire toast layout |
27
+ * | `actions` | `ButtonProps[]` | Action buttons below the message |
28
+ * | `id` | `string` | Stable ID — prevents duplicates, triggers bump animation |
29
+ * | `timeout` | `number \| null` | Duration in ms. `null` = persistent |
30
+ *
31
+ * ```ts
32
+ * toast("Notification"); // default — no variant or color
33
+ * toast.success("Changes saved");
34
+ * toast.error("Something went wrong");
35
+ * toast.warning({ title: "Warning", description: "File exceeds maximum size" });
36
+ * toast.info("Update available");
37
+ *
38
+ * toast.promise(saveData(), {
39
+ * loading: { description: "Saving..." },
40
+ * success: () => ({ variant: "success", description: "Saved" }),
41
+ * error: (e) => ({ variant: "error", description: e.message }),
42
+ * });
43
+ * ```
44
+ *
45
+ * ## Setup
46
+ *
47
+ * **Requires `<LibraryProvider>` at the app root.** It includes `ToastProvider` automatically — no
48
+ * extra wrapping needed:
49
+ *
50
+ * ```tsx
51
+ * // app.tsx
52
+ * import { LibraryProvider } from "@boxcustodia/library";
53
+ *
54
+ * export function App() {
55
+ * return (
56
+ * <LibraryProvider>
57
+ * <Router />
58
+ * </LibraryProvider>
59
+ * );
60
+ * }
61
+ * ```
62
+ */
63
+ const meta: Meta<typeof ToastProvider> = {
64
+ title: "Components/Toast",
65
+ component: ToastProvider,
66
+ };
67
+
68
+ export default meta;
69
+ type Story = StoryObj<typeof meta>;
70
+
71
+ // ─── Basic ────────────────────────────────────────────────────────────────────
72
+
73
+ export const Default: Story = {
74
+ render: () => (
75
+ <Button
76
+ variant="outline"
77
+ onClick={() => toast("Notification without variant.")}
78
+ >
79
+ Default
80
+ </Button>
81
+ ),
82
+ };
83
+
84
+ export const TitleOnly: Story = {
85
+ render: () => (
86
+ <Button variant="outline" onClick={() => toast({ title: "Title only" })}>
87
+ Title only
88
+ </Button>
89
+ ),
90
+ };
91
+
92
+ export const DescriptionOnly: Story = {
93
+ render: () => (
94
+ <Button
95
+ variant="outline"
96
+ onClick={() => toast({ description: "Description only, no title." })}
97
+ >
98
+ Description only
99
+ </Button>
100
+ ),
101
+ };
102
+
103
+ // ─── Variants ─────────────────────────────────────────────────────────────────
104
+
105
+ export const Success: Story = {
106
+ render: () => (
107
+ <Button
108
+ variant="outline"
109
+ onClick={() => toast.success("Changes saved successfully.")}
110
+ >
111
+ Success
112
+ </Button>
113
+ ),
114
+ };
115
+
116
+ export const Error: Story = {
117
+ render: () => (
118
+ <Button
119
+ variant="outline"
120
+ onClick={() => toast.error("Could not complete the operation.")}
121
+ >
122
+ Error
123
+ </Button>
124
+ ),
125
+ };
126
+
127
+ export const Warning: Story = {
128
+ render: () => (
129
+ <Button
130
+ variant="outline"
131
+ onClick={() =>
132
+ toast.warning({
133
+ title: "Warning",
134
+ description: "File exceeds the maximum allowed size.",
135
+ })
136
+ }
137
+ >
138
+ Warning
139
+ </Button>
140
+ ),
141
+ };
142
+
143
+ export const Info: Story = {
144
+ render: () => (
145
+ <Button
146
+ variant="outline"
147
+ onClick={() =>
148
+ toast.info({
149
+ title: "Update available",
150
+ description: "v2.4.0 is ready to install.",
151
+ })
152
+ }
153
+ >
154
+ Info
155
+ </Button>
156
+ ),
157
+ };
158
+
159
+ // ─── Composition ──────────────────────────────────────────────────────────────
160
+
161
+ /**
162
+ * `content` replaces the entire toast layout — useful for custom layouts,
163
+ * image previews, or any arbitrary JSX inside the toast container.
164
+ */
165
+ export const CustomContent: Story = {
166
+ render: () => (
167
+ <Button
168
+ variant="outline"
169
+ onClick={() =>
170
+ toast.info({
171
+ content: (
172
+ <div className="flex flex-col gap-1 pr-6">
173
+ <span className="text-[0.975rem] font-medium leading-5">
174
+ Custom layout
175
+ </span>
176
+ <span className="text-[0.925rem] leading-5 text-muted-foreground">
177
+ Any JSX can go inside the toast.
178
+ </span>
179
+ <code className="mt-1 rounded bg-muted px-1.5 py-0.5 text-xs">
180
+ content: ReactNode
181
+ </code>
182
+ </div>
183
+ ),
184
+ })
185
+ }
186
+ >
187
+ Custom content
188
+ </Button>
189
+ ),
190
+ };
191
+
192
+ /**
193
+ * `actions` renders buttons below the message. Accepts the same API as `<Button>`.
194
+ * Useful for inline confirmations without opening a dialog.
195
+ *
196
+ * To dismiss the toast from an action, capture the ID returned by `add()` and call `remove(id)`:
197
+ *
198
+ * ```tsx
199
+ * const { add, close } = useToastManager();
200
+ *
201
+ * let id: string;
202
+ * id = add({
203
+ * variant: "warning",
204
+ * title: "Unsaved changes",
205
+ * actions: [
206
+ * { children: "Discard", onClick: () => close(id) },
207
+ * { children: "Save", onClick: () => { save(); close(id); } },
208
+ * ],
209
+ * });
210
+ * ```
211
+ */
212
+ export const WithActions: Story = {
213
+ render: () => {
214
+ const { add, close } = useToastManager();
215
+ return (
216
+ <Button
217
+ variant="outline"
218
+ onClick={() => {
219
+ let id: string;
220
+ id = add({
221
+ variant: "warning",
222
+ title: "Unsaved changes",
223
+ description: "They will be lost if you leave this page.",
224
+ actions: [
225
+ {
226
+ children: "Discard",
227
+ variant: "outline",
228
+ size: "sm",
229
+ onClick: () => close(id),
230
+ },
231
+ {
232
+ children: "Save",
233
+ variant: "default",
234
+ size: "sm",
235
+ onClick: () => close(id),
236
+ },
237
+ ],
238
+ });
239
+ }}
240
+ >
241
+ With actions
242
+ </Button>
243
+ );
244
+ },
245
+ };
246
+
247
+ /**
248
+ * `toast.promise()` handles all three promise states automatically:
249
+ * shows a loading toast while pending, then transitions to success or error on settle.
250
+ */
251
+ const Deferred = globalThis.Promise;
252
+ const NativeError = globalThis.Error;
253
+
254
+ const mockPurchaseSuccess = () =>
255
+ new Deferred<{ message: string }>((resolve) =>
256
+ setTimeout(
257
+ () =>
258
+ resolve({
259
+ message:
260
+ "Order #4821 confirmed. A summary has been sent to your email.",
261
+ }),
262
+ 2000,
263
+ ),
264
+ );
265
+
266
+ const mockPurchaseError = () =>
267
+ new Deferred<{ message: string }>((_, reject) =>
268
+ setTimeout(
269
+ () => reject(new NativeError("Insufficient funds on the card.")),
270
+ 2000,
271
+ ),
272
+ );
273
+
274
+ export const PromiseToast: Story = {
275
+ render: () => (
276
+ <div className="flex gap-2">
277
+ <Button
278
+ variant="outline"
279
+ onClick={() =>
280
+ toast.promise(mockPurchaseSuccess(), {
281
+ loading: { description: "Processing payment..." },
282
+ success: (data: any) => ({
283
+ variant: "success",
284
+ title: "Purchase complete",
285
+ description: data.message,
286
+ }),
287
+ error: (err: Error) => ({
288
+ variant: "error",
289
+ title: "Payment failed",
290
+ description: err.message,
291
+ }),
292
+ })
293
+ }
294
+ >
295
+ Confirm purchase
296
+ </Button>
297
+ <Button
298
+ variant="outline"
299
+ onClick={() =>
300
+ toast.promise(mockPurchaseError(), {
301
+ loading: { description: "Processing payment..." },
302
+ success: (data: any) => ({
303
+ variant: "success",
304
+ title: "Purchase complete",
305
+ description: data.message,
306
+ }),
307
+ error: (err: Error) => ({
308
+ variant: "error",
309
+ title: "Payment failed",
310
+ description: err.message,
311
+ }),
312
+ })
313
+ }
314
+ >
315
+ Pay (insufficient funds)
316
+ </Button>
317
+ </div>
318
+ ),
319
+ };
320
+
321
+ // ─── Advanced ─────────────────────────────────────────────────────────────────
322
+
323
+ /**
324
+ * `useToastManager()` exposes the full manager inside a component.
325
+ * Useful when you need access to `update()`, `toasts[]`, or `promise()` with local state.
326
+ * For most cases, the `toast.*` singleton is enough.
327
+ */
328
+ export const WithHook: Story = {
329
+ render: () => {
330
+ const { add, toasts } = useToastManager();
331
+ return (
332
+ <div className="flex items-center gap-3">
333
+ <Button
334
+ variant="outline"
335
+ onClick={() =>
336
+ add({
337
+ variant: "info",
338
+ title: "From the hook",
339
+ description:
340
+ "useToastManager() gives full access to the manager.",
341
+ })
342
+ }
343
+ >
344
+ Add toast
345
+ </Button>
346
+ <span className="text-sm text-muted-foreground">
347
+ {toasts.length} active
348
+ </span>
349
+ </div>
350
+ );
351
+ },
352
+ };
353
+
354
+ /**
355
+ * `createToastManager()` creates an isolated manager instance — useful in tests
356
+ * or microfrontends that need a separate toast stack from the global singleton.
357
+ *
358
+ * ```tsx
359
+ * const myManager = createToastManager();
360
+ *
361
+ * <ToastProvider toastManager={myManager}>
362
+ * <App />
363
+ * </ToastProvider>
364
+ *
365
+ * myManager.add({ variant: "success", title: "Isolated instance" });
366
+ * ```
367
+ */
368
+ const isolatedManager = createToastManager();
369
+
370
+ export const IsolatedManager: Story = {
371
+ decorators: [
372
+ (Story) => (
373
+ <LibraryProvider toastManager={isolatedManager}>
374
+ <Story />
375
+ </LibraryProvider>
376
+ ),
377
+ ],
378
+ render: () => (
379
+ <Button
380
+ variant="outline"
381
+ onClick={() =>
382
+ isolatedManager.add({
383
+ variant: "info",
384
+ title: "Isolated manager",
385
+ description: "Independent instance from the global singleton.",
386
+ })
387
+ }
388
+ >
389
+ Trigger isolated
390
+ </Button>
391
+ ),
392
+ };
@@ -0,0 +1,50 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { describe, expect, it } from "vitest";
4
+ import { ToastProvider, useToastManager } from "../toast";
5
+
6
+ function ToastTrigger({
7
+ variant,
8
+ title,
9
+ description,
10
+ }: {
11
+ variant?: string;
12
+ title?: string;
13
+ description?: string;
14
+ }) {
15
+ const manager = useToastManager();
16
+ return (
17
+ <button
18
+ onClick={() =>
19
+ manager.add({ variant: variant as any, title, description })
20
+ }
21
+ >
22
+ Show toast
23
+ </button>
24
+ );
25
+ }
26
+
27
+ function renderWithProvider(ui: React.ReactNode) {
28
+ return render(<ToastProvider>{ui}</ToastProvider>);
29
+ }
30
+
31
+ describe("Toast component", () => {
32
+ it("should render description", async () => {
33
+ renderWithProvider(<ToastTrigger description="test" />);
34
+ await userEvent.click(screen.getByText("Show toast"));
35
+ expect(await screen.findByText("test")).toBeInTheDocument();
36
+ });
37
+
38
+ it("should render title", async () => {
39
+ renderWithProvider(<ToastTrigger title="title" description="test" />);
40
+ await userEvent.click(screen.getByText("Show toast"));
41
+ expect(await screen.findByText("title")).toBeInTheDocument();
42
+ });
43
+
44
+ it("should render with success variant ring", async () => {
45
+ renderWithProvider(<ToastTrigger variant="success" description="test" />);
46
+ await userEvent.click(screen.getByText("Show toast"));
47
+ const toast = await screen.findByRole("status");
48
+ expect(toast.className).toContain("ring-success");
49
+ });
50
+ });