@boxcustodia/library 2.0.0-alpha.10 → 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 (352) hide show
  1. package/dist/index.cjs.js +70 -70
  2. package/dist/index.css +2 -0
  3. package/dist/index.d.ts +420 -272
  4. package/dist/index.es.js +34448 -27816
  5. package/dist/theme.css +1 -1
  6. package/package.json +11 -6
  7. package/src/__doc__/Changelog.mdx +6 -0
  8. package/src/__doc__/Components.mdx +73 -0
  9. package/src/__doc__/Examples.tsx +69 -0
  10. package/src/__doc__/Icons.mdx +41 -0
  11. package/src/__doc__/Intro.mdx +138 -0
  12. package/src/__doc__/MCP.mdx +71 -0
  13. package/src/__doc__/Migration.mdx +475 -0
  14. package/src/__doc__/Theme.mdx +132 -0
  15. package/src/__doc__/Types.mdx +252 -0
  16. package/src/components/alert/alert.stories.tsx +142 -0
  17. package/src/components/alert/alert.tsx +109 -0
  18. package/src/components/alert/index.ts +7 -0
  19. package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
  20. package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
  21. package/src/components/alert-dialog/alert-dialog.tsx +265 -0
  22. package/src/components/alert-dialog/index.ts +1 -0
  23. package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
  24. package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
  25. package/src/components/auto-complete/auto-complete.tsx +82 -0
  26. package/src/components/auto-complete/index.ts +2 -0
  27. package/src/components/avatar/avatar.stories.tsx +84 -0
  28. package/src/components/avatar/avatar.test.tsx +61 -0
  29. package/src/components/avatar/avatar.tsx +104 -0
  30. package/src/components/avatar/index.ts +1 -0
  31. package/src/components/background-image/background-image.stories.tsx +21 -0
  32. package/src/components/background-image/background-image.test.tsx +29 -0
  33. package/src/components/background-image/background-image.tsx +23 -0
  34. package/src/components/background-image/index.ts +1 -0
  35. package/src/components/button/button.stories.tsx +396 -0
  36. package/src/components/button/button.test.tsx +58 -0
  37. package/src/components/button/button.tsx +31 -0
  38. package/src/components/button/button.variants.ts +44 -0
  39. package/src/components/button/components/base-button.tsx +86 -0
  40. package/src/components/button/components/loader-overlay.tsx +21 -0
  41. package/src/components/button/components/loading-icon.tsx +47 -0
  42. package/src/components/button/index.ts +3 -0
  43. package/src/components/calendar/calendar.model.ts +86 -0
  44. package/src/components/calendar/calendar.stories.tsx +155 -0
  45. package/src/components/calendar/calendar.test.tsx +12 -0
  46. package/src/components/calendar/calendar.tsx +185 -0
  47. package/src/components/calendar/components/calendar-navigation.tsx +141 -0
  48. package/src/components/calendar/components/day.tsx +61 -0
  49. package/src/components/calendar/components/decade-view.tsx +45 -0
  50. package/src/components/calendar/components/index.ts +6 -0
  51. package/src/components/calendar/components/month-view.tsx +58 -0
  52. package/src/components/calendar/components/week-days.tsx +27 -0
  53. package/src/components/calendar/components/year-view.tsx +29 -0
  54. package/src/components/calendar/hooks/index.ts +4 -0
  55. package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
  56. package/src/components/calendar/hooks/use-calendar.ts +90 -0
  57. package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
  58. package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
  59. package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
  60. package/src/components/calendar/index.ts +1 -0
  61. package/src/components/calendar/utils/typeguards.ts +7 -0
  62. package/src/components/card/card.stories.tsx +116 -0
  63. package/src/components/card/card.tsx +74 -0
  64. package/src/components/card/index.ts +1 -0
  65. package/src/components/center/center.stories.tsx +81 -0
  66. package/src/components/center/center.tsx +24 -0
  67. package/src/components/center/index.ts +1 -0
  68. package/src/components/checkbox/checkbox.stories.tsx +307 -0
  69. package/src/components/checkbox/checkbox.tsx +273 -0
  70. package/src/components/checkbox/index.ts +1 -0
  71. package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
  72. package/src/components/checkbox-group/checkbox-group.tsx +16 -0
  73. package/src/components/checkbox-group/index.ts +1 -0
  74. package/src/components/combobox/combobox.stories.tsx +339 -0
  75. package/src/components/combobox/combobox.tsx +892 -0
  76. package/src/components/combobox/index.ts +1 -0
  77. package/src/components/date-picker/date-input.stories.tsx +158 -0
  78. package/src/components/date-picker/date-input.tsx +163 -0
  79. package/src/components/date-picker/date-picker.model.ts +90 -0
  80. package/src/components/date-picker/date-picker.stories.tsx +200 -0
  81. package/src/components/date-picker/date-picker.test.tsx +23 -0
  82. package/src/components/date-picker/date-picker.tsx +298 -0
  83. package/src/components/date-picker/date-picker.utils.ts +260 -0
  84. package/src/components/date-picker/index.ts +3 -0
  85. package/src/components/date-picker/use-date-input-popover.ts +48 -0
  86. package/src/components/date-picker/use-date-input.ts +125 -0
  87. package/src/components/dialog/dialog.stories.tsx +171 -0
  88. package/src/components/dialog/dialog.test.tsx +68 -0
  89. package/src/components/dialog/dialog.tsx +277 -0
  90. package/src/components/dialog/index.ts +1 -0
  91. package/src/components/divider/divider.stories.tsx +70 -0
  92. package/src/components/divider/divider.test.tsx +22 -0
  93. package/src/components/divider/divider.tsx +23 -0
  94. package/src/components/divider/index.ts +1 -0
  95. package/src/components/dropzone/dropzone.stories.tsx +210 -0
  96. package/src/components/dropzone/dropzone.tsx +154 -0
  97. package/src/components/dropzone/file-types.ts +64 -0
  98. package/src/components/dropzone/index.ts +3 -0
  99. package/src/components/dropzone/upload-primitives.tsx +310 -0
  100. package/src/components/dropzone/use-dropzone.ts +122 -0
  101. package/src/components/empty-state/empty-state.stories.tsx +56 -0
  102. package/src/components/empty-state/empty-state.tsx +39 -0
  103. package/src/components/empty-state/index.ts +1 -0
  104. package/src/components/field/field.stories.tsx +223 -0
  105. package/src/components/field/field.tsx +229 -0
  106. package/src/components/field/index.ts +1 -0
  107. package/src/components/form/form.stories.tsx +594 -0
  108. package/src/components/form/form.tsx +30 -0
  109. package/src/components/form/index.ts +1 -0
  110. package/src/components/heading/heading.stories.tsx +74 -0
  111. package/src/components/heading/heading.tsx +28 -0
  112. package/src/components/heading/heading.variants.ts +27 -0
  113. package/src/components/heading/index.ts +1 -0
  114. package/src/components/index.ts +46 -0
  115. package/src/components/input/index.ts +1 -0
  116. package/src/components/input/input.stories.tsx +104 -0
  117. package/src/components/input/input.tsx +75 -0
  118. package/src/components/kbd/index.ts +1 -0
  119. package/src/components/kbd/kbd.stories.tsx +40 -0
  120. package/src/components/kbd/kbd.tsx +31 -0
  121. package/src/components/kbd/kbd.variants.ts +26 -0
  122. package/src/components/label/index.ts +1 -0
  123. package/src/components/label/label.stories.tsx +68 -0
  124. package/src/components/label/label.test.tsx +61 -0
  125. package/src/components/label/label.tsx +62 -0
  126. package/src/components/loader/index.ts +1 -0
  127. package/src/components/loader/loader.stories.tsx +60 -0
  128. package/src/components/loader/loader.test.tsx +26 -0
  129. package/src/components/loader/loader.tsx +60 -0
  130. package/src/components/menu/index.ts +2 -0
  131. package/src/components/menu/menu-primitives.tsx +248 -0
  132. package/src/components/menu/menu.stories.tsx +203 -0
  133. package/src/components/menu/menu.tsx +100 -0
  134. package/src/components/menu/util/render-menu-item.tsx +54 -0
  135. package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
  136. package/src/components/multi-select/index.ts +1 -0
  137. package/src/components/multi-select/multi-select.stories.tsx +294 -0
  138. package/src/components/multi-select/multi-select.tsx +300 -0
  139. package/src/components/multi-select/multi-select.variants.ts +22 -0
  140. package/src/components/number-input/index.ts +1 -0
  141. package/src/components/number-input/number-input.stories.tsx +209 -0
  142. package/src/components/number-input/number-input.test.tsx +87 -0
  143. package/src/components/number-input/number-input.tsx +230 -0
  144. package/src/components/pagination/components/pagination-option.tsx +27 -0
  145. package/src/components/pagination/index.ts +1 -0
  146. package/src/components/pagination/pagination.stories.tsx +80 -0
  147. package/src/components/pagination/pagination.test.tsx +76 -0
  148. package/src/components/pagination/pagination.tsx +102 -0
  149. package/src/components/password/index.ts +1 -0
  150. package/src/components/password/password.stories.tsx +104 -0
  151. package/src/components/password/password.tsx +71 -0
  152. package/src/components/popover/index.ts +1 -0
  153. package/src/components/popover/popover.stories.tsx +213 -0
  154. package/src/components/popover/popover.tsx +203 -0
  155. package/src/components/progress/index.ts +1 -0
  156. package/src/components/progress/progress.stories.tsx +124 -0
  157. package/src/components/progress/progress.test.tsx +25 -0
  158. package/src/components/progress/progress.tsx +124 -0
  159. package/src/components/scroll-area/index.ts +1 -0
  160. package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
  161. package/src/components/scroll-area/scroll-area.tsx +64 -0
  162. package/src/components/select/index.ts +1 -0
  163. package/src/components/select/select.stories.tsx +253 -0
  164. package/src/components/select/select.tsx +430 -0
  165. package/src/components/show/index.ts +1 -0
  166. package/src/components/show/show.stories.tsx +197 -0
  167. package/src/components/show/show.test.tsx +41 -0
  168. package/src/components/show/show.tsx +16 -0
  169. package/src/components/skeleton/index.ts +1 -0
  170. package/src/components/skeleton/skeleton.stories.tsx +36 -0
  171. package/src/components/skeleton/skeleton.test.tsx +14 -0
  172. package/src/components/skeleton/skeleton.tsx +15 -0
  173. package/src/components/stack/index.ts +1 -0
  174. package/src/components/stack/stack.stories.tsx +194 -0
  175. package/src/components/stack/stack.tsx +52 -0
  176. package/src/components/stepper/Stepper.tsx +190 -0
  177. package/src/components/stepper/context/stepper-context.tsx +11 -0
  178. package/src/components/stepper/index.ts +1 -0
  179. package/src/components/stepper/stepper.stories.tsx +130 -0
  180. package/src/components/stepper/stepper.test.tsx +91 -0
  181. package/src/components/switch/index.ts +1 -0
  182. package/src/components/switch/switch.stories.tsx +122 -0
  183. package/src/components/switch/switch.test.tsx +30 -0
  184. package/src/components/switch/switch.tsx +86 -0
  185. package/src/components/table/index.ts +3 -0
  186. package/src/components/table/table-primitives.tsx +122 -0
  187. package/src/components/table/table.model.ts +20 -0
  188. package/src/components/table/table.stories.tsx +169 -0
  189. package/src/components/table/table.test.tsx +91 -0
  190. package/src/components/table/table.tsx +109 -0
  191. package/src/components/table-pagination/index.ts +2 -0
  192. package/src/components/table-pagination/table-pagination.model.ts +2 -0
  193. package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
  194. package/src/components/table-pagination/table-pagination.test.tsx +32 -0
  195. package/src/components/table-pagination/table-pagination.tsx +108 -0
  196. package/src/components/tabs/context/tabs-context.tsx +14 -0
  197. package/src/components/tabs/index.ts +1 -0
  198. package/src/components/tabs/tabs.stories.tsx +182 -0
  199. package/src/components/tabs/tabs.test.tsx +61 -0
  200. package/src/components/tabs/tabs.tsx +175 -0
  201. package/src/components/tag/index.ts +2 -0
  202. package/src/components/tag/tag.stories.tsx +170 -0
  203. package/src/components/tag/tag.test.tsx +18 -0
  204. package/src/components/tag/tag.tsx +99 -0
  205. package/src/components/tag/tag.variants.ts +31 -0
  206. package/src/components/textarea/index.ts +1 -0
  207. package/src/components/textarea/textarea.stories.tsx +73 -0
  208. package/src/components/textarea/textarea.tsx +105 -0
  209. package/src/components/timeline/index.ts +1 -0
  210. package/src/components/timeline/timeline-status.ts +5 -0
  211. package/src/components/timeline/timeline.stories.tsx +84 -0
  212. package/src/components/timeline/timeline.tsx +147 -0
  213. package/src/components/toast/index.ts +1 -0
  214. package/src/components/toast/toast.stories.tsx +392 -0
  215. package/src/components/toast/toast.test.tsx +50 -0
  216. package/src/components/toast/toast.tsx +411 -0
  217. package/src/components/tooltip/index.ts +1 -0
  218. package/src/components/tooltip/tooltip.stories.tsx +226 -0
  219. package/src/components/tooltip/tooltip.test.tsx +46 -0
  220. package/src/components/tooltip/tooltip.tsx +171 -0
  221. package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
  222. package/src/components/tree/index.ts +2 -0
  223. package/src/components/tree/tree-primitives.tsx +126 -0
  224. package/src/components/tree/tree.stories.tsx +468 -0
  225. package/src/components/tree/tree.tsx +42 -0
  226. package/src/hooks/index.ts +26 -0
  227. package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
  228. package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
  229. package/src/hooks/useArray/index.ts +1 -0
  230. package/src/hooks/useArray/useArray.ts +76 -0
  231. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
  232. package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
  233. package/src/hooks/useAsync/index.ts +1 -0
  234. package/src/hooks/useAsync/useAsync.ts +58 -0
  235. package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
  236. package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
  237. package/src/hooks/useClickOutside/index.ts +1 -0
  238. package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
  239. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
  240. package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
  241. package/src/hooks/useClipboard/index.ts +1 -0
  242. package/src/hooks/useClipboard/useClipboard.tsx +28 -0
  243. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
  244. package/src/hooks/useDebounceCallback/index.ts +1 -0
  245. package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
  246. package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
  247. package/src/hooks/useDebounceValue/index.ts +1 -0
  248. package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
  249. package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
  250. package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
  251. package/src/hooks/useDisclosure/index.ts +1 -0
  252. package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
  253. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
  254. package/src/hooks/useDocumentTitle/index.ts +1 -0
  255. package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
  256. package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
  257. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
  258. package/src/hooks/useEventListener/index.ts +1 -0
  259. package/src/hooks/useEventListener/useEventListener.ts +25 -0
  260. package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
  261. package/src/hooks/useFocusTrap/index.ts +1 -0
  262. package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
  263. package/src/hooks/useFocusTrap/tabbable.ts +70 -0
  264. package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
  265. package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
  266. package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
  267. package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
  268. package/src/hooks/useHotkey/__utils__/index.ts +3 -0
  269. package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
  270. package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
  271. package/src/hooks/useHotkey/index.ts +1 -0
  272. package/src/hooks/useHotkey/useHotkey.ts +34 -0
  273. package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
  274. package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
  275. package/src/hooks/useHover/index.ts +1 -0
  276. package/src/hooks/useHover/useHover.tsx +40 -0
  277. package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
  278. package/src/hooks/useIsVisible/index.ts +1 -0
  279. package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
  280. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
  281. package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
  282. package/src/hooks/useLocalStorage/index.ts +1 -0
  283. package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
  284. package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
  285. package/src/hooks/useMediaQuery/index.ts +1 -0
  286. package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
  287. package/src/hooks/useMemoizedFn/index.ts +1 -0
  288. package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
  289. package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
  290. package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
  291. package/src/hooks/useMutation/index.ts +1 -0
  292. package/src/hooks/useMutation/useMutation.tsx +60 -0
  293. package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
  294. package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
  295. package/src/hooks/useObject/index.ts +1 -0
  296. package/src/hooks/useObject/useObject.tsx +48 -0
  297. package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
  298. package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
  299. package/src/hooks/usePagination/index.ts +2 -0
  300. package/src/hooks/usePagination/usePagination.tsx +74 -0
  301. package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
  302. package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
  303. package/src/hooks/usePortal/index.ts +1 -0
  304. package/src/hooks/usePortal/usePortal.ts +40 -0
  305. package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
  306. package/src/hooks/usePreventCloseWindow/index.ts +1 -0
  307. package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
  308. package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
  309. package/src/hooks/useRangePagination/index.ts +2 -0
  310. package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
  311. package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
  312. package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
  313. package/src/hooks/useSelection/index.ts +1 -0
  314. package/src/hooks/useSelection/useSelection.ts +121 -0
  315. package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
  316. package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
  317. package/src/hooks/useStep/index.ts +1 -0
  318. package/src/hooks/useStep/useStep.ts +57 -0
  319. package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
  320. package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
  321. package/src/hooks/useToggle/index.ts +1 -0
  322. package/src/hooks/useToggle/useToggle.ts +16 -0
  323. package/src/index.ts +6 -0
  324. package/src/lib/cn.ts +8 -0
  325. package/src/lib/index.ts +1 -0
  326. package/src/models/Generic.model.ts +67 -0
  327. package/src/models/index.ts +1 -0
  328. package/src/providers/index.ts +2 -0
  329. package/src/providers/library-provider.tsx +44 -0
  330. package/src/providers/theme/ThemeProvider.tsx +25 -0
  331. package/src/providers/theme/index.ts +3 -0
  332. package/src/providers/theme/types.ts +11 -0
  333. package/src/providers/theme/useThemeProps.ts +25 -0
  334. package/src/stores/theme.store.ts +31 -0
  335. package/src/styles/components.css +4 -0
  336. package/src/styles/index.css +2 -0
  337. package/src/styles/library.css +2 -0
  338. package/src/styles/theme.css +232 -0
  339. package/src/utils/dates/parseDateRange.utility.ts +39 -0
  340. package/src/utils/form.tsx +91 -0
  341. package/src/utils/functions/createSafeContext.ts +17 -0
  342. package/src/utils/functions/ensureReactElement.tsx +30 -0
  343. package/src/utils/functions/getFormData.ts +19 -0
  344. package/src/utils/functions/index.ts +4 -0
  345. package/src/utils/functions/mergeRefs.ts +18 -0
  346. package/src/utils/index.ts +3 -0
  347. package/src/utils/strings/extractInitials.utility.ts +10 -0
  348. package/src/utils/strings/index.ts +1 -0
  349. package/src/utils/tests/click.ts +3 -0
  350. package/src/utils/tests/index.ts +2 -0
  351. package/src/utils/tests/keyboard.ts +21 -0
  352. package/src/utils/tests/type.ts +6 -0
@@ -0,0 +1,63 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import useRangePagination, { DOTS } from "../useRangePagination";
4
+
5
+ describe("usePagination hook", () => {
6
+ it("should be defined", () => {
7
+ expect(useRangePagination).toBeDefined();
8
+ });
9
+
10
+ it("shouldn't show dots if there is not enough items", () => {
11
+ const { result } = renderHook(() =>
12
+ useRangePagination({
13
+ totalItems: 5,
14
+ pageSize: 1,
15
+ }),
16
+ );
17
+
18
+ expect(result.current.paginationRange).toEqual([1, 2, 3, 4, 5]);
19
+ });
20
+
21
+ it("should show right dots", () => {
22
+ const { result } = renderHook(() =>
23
+ useRangePagination({
24
+ totalItems: 10,
25
+ pageSize: 1,
26
+ }),
27
+ );
28
+
29
+ expect(result.current.paginationRange).toEqual([1, 2, 3, 4, 5, DOTS, 10]);
30
+ });
31
+
32
+ it("should show left dots", () => {
33
+ const { result } = renderHook(() =>
34
+ useRangePagination({
35
+ totalItems: 10,
36
+ pageSize: 1,
37
+ initialCurrentPage: 8,
38
+ }),
39
+ );
40
+
41
+ expect(result.current.paginationRange).toEqual([1, DOTS, 6, 7, 8, 9, 10]);
42
+ });
43
+
44
+ it("should show dots both sides", () => {
45
+ const { result } = renderHook(() =>
46
+ useRangePagination({
47
+ totalItems: 10,
48
+ pageSize: 1,
49
+ initialCurrentPage: 5,
50
+ }),
51
+ );
52
+
53
+ expect(result.current.paginationRange).toEqual([
54
+ 1,
55
+ DOTS,
56
+ 4,
57
+ 5,
58
+ 6,
59
+ DOTS,
60
+ 10,
61
+ ]);
62
+ });
63
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./useRangePagination";
2
+ export { default as useRangePagination } from "./useRangePagination";
@@ -0,0 +1,72 @@
1
+ import { range } from "lodash";
2
+ import { useMemo } from "react";
3
+ import { usePagination, type usePaginationProps } from "../../hooks";
4
+
5
+ export const DOTS = "...";
6
+
7
+ export type useRangePaginationProps = {
8
+ /**
9
+ * Cantidad de elementos visibles al lado de la pagina actual
10
+ */
11
+ siblingCount?: number;
12
+ } & usePaginationProps;
13
+
14
+ const useRangePagination = ({
15
+ siblingCount = 1,
16
+ ...paginationProps
17
+ }: useRangePaginationProps) => {
18
+ const pagination = usePagination(paginationProps);
19
+ const { currentPage, maxPage } = pagination;
20
+ const { totalItems, pageSize } = paginationProps;
21
+
22
+ const generateRange = () => {
23
+ const DOTS_THRESHOLD = 2;
24
+ const EXTRA_ITEMS = 3;
25
+ const ITEMS_UNTIL_DOTS = DOTS_THRESHOLD + EXTRA_ITEMS * siblingCount;
26
+ const FIRST_PAGE = 1;
27
+
28
+ // No hay suficientes elementos para agregar DOTS
29
+ if (ITEMS_UNTIL_DOTS >= maxPage) {
30
+ return range(FIRST_PAGE, maxPage + 1);
31
+ }
32
+
33
+ // índice de elemento de izquierda y derecha
34
+ const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
35
+ const rightSiblingIndex = Math.min(currentPage + siblingCount, maxPage);
36
+
37
+ const shouldShowLeftDots = leftSiblingIndex > DOTS_THRESHOLD;
38
+ const shouldShowRightDots = rightSiblingIndex < maxPage - DOTS_THRESHOLD;
39
+
40
+ // Debe mostrar DOTS de ambos lados
41
+ if (shouldShowLeftDots && shouldShowRightDots) {
42
+ const middleElements = range(leftSiblingIndex, rightSiblingIndex + 1);
43
+ return [FIRST_PAGE, DOTS, ...middleElements, DOTS, maxPage];
44
+ }
45
+
46
+ // Debe mostrar DOTS a la derecha
47
+ if (shouldShowRightDots) {
48
+ const leftElements = range(FIRST_PAGE, ITEMS_UNTIL_DOTS + 1);
49
+ return [...leftElements, DOTS, maxPage];
50
+ }
51
+
52
+ // Debe mostrar DOTS a la izquierda
53
+ if (shouldShowLeftDots) {
54
+ const rightElements = range(maxPage - ITEMS_UNTIL_DOTS + 1, maxPage + 1);
55
+ return [FIRST_PAGE, DOTS, ...rightElements];
56
+ }
57
+ };
58
+
59
+ const paginationRange = useMemo(generateRange, [
60
+ totalItems,
61
+ pageSize,
62
+ currentPage,
63
+ siblingCount,
64
+ ]);
65
+
66
+ return {
67
+ ...pagination,
68
+ paginationRange: paginationRange ?? [],
69
+ };
70
+ };
71
+
72
+ export default useRangePagination;
@@ -0,0 +1,140 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Trash } from "lucide-react";
3
+ import { useMemo, useState } from "react";
4
+ import { Button, Checkbox } from "../../../components";
5
+ import { cn } from "../../../lib";
6
+ import useSelection from "../useSelection";
7
+
8
+ /**
9
+ * Gestión de la selección de elementos en una lista.
10
+ * Optimizado para un rendimiento eficiente, utiliza un conjunto (Set) interno para almacenar los elementos seleccionados, permitiendo consultas rápidas para verificar si un elemento está seleccionado.
11
+ * Proporciona funciones simples para seleccionar, deseleccionar, alternar la selección de un elemento, seleccionar todos los elementos, limpiar la selección y obtener el estado actual de la selección
12
+ */
13
+ const meta: Meta = {
14
+ title: "hooks/useSelection",
15
+ tags: ["autodocs"],
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof meta>;
20
+
21
+ export const Default: Story = {
22
+ render: () => {
23
+ const items = useMemo(() => ["🍆", "🍅", "🥑", "🥬"], []);
24
+ const {
25
+ selected,
26
+ toggle,
27
+ toggleAll,
28
+ clear,
29
+ isSelected,
30
+ isAllSelected,
31
+ isNoneSelected,
32
+ setSelected,
33
+ } = useSelection(items);
34
+
35
+ return (
36
+ <div className="space-y-4">
37
+ <div className="flex gap-2">
38
+ {items.map((item) => (
39
+ <div
40
+ key={item}
41
+ onClick={() => toggle(item)}
42
+ className={cn(
43
+ "rounded transition-colors w-32 h-32 shadow text-3xl grid place-items-center cursor-pointer",
44
+ { "bg-accent": isSelected(item) },
45
+ )}
46
+ >
47
+ {item}
48
+ </div>
49
+ ))}
50
+ </div>
51
+
52
+ <div className="flex gap-4 my-2">
53
+ <Button onClick={clear}>Limpiar</Button>
54
+ <Button onClick={toggleAll}>Toggle all</Button>
55
+ <Button onClick={() => setSelected(["🍅"])}>set 🍅</Button>
56
+ </div>
57
+
58
+ <pre className="p-4 text-white rounded-md bg-slate-950">
59
+ <code className="block">
60
+ Selected:{" "}
61
+ <span className="text-blue-300">
62
+ {JSON.stringify(selected, null, 2)}
63
+ </span>
64
+ </code>
65
+ <code className="block">
66
+ isAllSelected:{" "}
67
+ <span className={isAllSelected ? "text-green-500" : "text-red-500"}>
68
+ {JSON.stringify(isAllSelected)}
69
+ </span>
70
+ </code>
71
+ <code className="block">
72
+ isNoneSelected:{" "}
73
+ <span
74
+ className={isNoneSelected ? "text-green-500" : "text-red-500"}
75
+ >
76
+ {JSON.stringify(isNoneSelected)}
77
+ </span>
78
+ </code>
79
+ </pre>
80
+ </div>
81
+ );
82
+ },
83
+ };
84
+
85
+ export const DynamicArray: Story = {
86
+ name: "Array dinamico",
87
+ render: () => {
88
+ // state o useMemo
89
+ const [items, setItems] = useState([
90
+ { id: 1, name: "Item 1" },
91
+ { id: 2, name: "Item 2" },
92
+ { id: 3, name: "Item 3" },
93
+ ]);
94
+
95
+ const { selected, toggle, isSelected, unselect } = useSelection(items);
96
+
97
+ const addNewItem = () => {
98
+ const id = new Date().getTime();
99
+ setItems((prevItems) => [...prevItems, { id, name: `Item ${id}` }]);
100
+ };
101
+
102
+ const removeItem = (item: { id: number; name: string }) => {
103
+ setItems((prevItems) => prevItems.filter((i) => i.id !== item.id));
104
+ unselect(item);
105
+ };
106
+
107
+ return (
108
+ <div>
109
+ {items.map((item) => (
110
+ <div key={item.id} className="flex items-center gap-2">
111
+ <Checkbox
112
+ name={item.id.toString()}
113
+ checked={isSelected(item)}
114
+ onCheckedChange={() => toggle(item)}
115
+ />
116
+ <span className="text-sm">{item.name}</span>
117
+ <Button
118
+ size="icon"
119
+ variant="ghost"
120
+ onClick={() => removeItem(item)}
121
+ >
122
+ <Trash />
123
+ </Button>
124
+ </div>
125
+ ))}
126
+ <Button className="my-2" onClick={addNewItem}>
127
+ Añadir nuevo elemento
128
+ </Button>
129
+ <pre className="p-4 text-white rounded-md bg-slate-950">
130
+ <code className="block">
131
+ Selected:{" "}
132
+ <span className="text-blue-300">
133
+ {JSON.stringify(selected, null, 2)}
134
+ </span>
135
+ </code>
136
+ </pre>
137
+ </div>
138
+ );
139
+ },
140
+ };
@@ -0,0 +1,57 @@
1
+ import { act, renderHook, waitFor } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import useSelection from "../useSelection";
4
+
5
+ describe("useSelection hook", () => {
6
+ it("should be defined", () => {
7
+ const items = [1, 2, 3];
8
+ const { result } = renderHook(() => useSelection(items));
9
+ const { isAllSelected, isNoneSelected } = result.current;
10
+
11
+ expect(isAllSelected).toBe(false);
12
+ expect(isNoneSelected).toBe(true);
13
+ });
14
+
15
+ it("should select all", () => {
16
+ const items = [1, 2, 3];
17
+ const { result } = renderHook(() => useSelection(items));
18
+ const { toggleAll } = result.current;
19
+
20
+ act(() => toggleAll());
21
+ expect(result.current.isAllSelected).toBe(true);
22
+ expect(result.current.isNoneSelected).toBe(false);
23
+ });
24
+
25
+ it("should select item", () => {
26
+ const items = [1, 2, 3];
27
+ const { result } = renderHook(() => useSelection(items));
28
+ const { select } = result.current;
29
+
30
+ act(() => select(2));
31
+ expect(result.current.isSelected(2)).toBe(true);
32
+ });
33
+
34
+ it("should unselect item", () => {
35
+ const items = [1, 2, 3];
36
+ const { result } = renderHook(() => useSelection(items));
37
+ const { select } = result.current;
38
+
39
+ act(() => select(2));
40
+ expect(result.current.isSelected(2)).toBe(true);
41
+
42
+ act(() => select(2));
43
+ waitFor(() => {
44
+ expect(result.current.isSelected(2)).toBe(false);
45
+ });
46
+ });
47
+
48
+ it("should clear selections", () => {
49
+ const items = [1, 2, 3];
50
+ const { result } = renderHook(() => useSelection(items));
51
+ const { clear } = result.current;
52
+
53
+ act(() => clear());
54
+ expect(result.current.isAllSelected).toBe(false);
55
+ expect(result.current.isNoneSelected).toBe(true);
56
+ });
57
+ });
@@ -0,0 +1 @@
1
+ export { default as useSelection } from "./useSelection";
@@ -0,0 +1,121 @@
1
+ import { useCallback, useState } from "react";
2
+
3
+ type Return<T> = {
4
+ /**
5
+ * Indica si todos los elementos están seleccionados.
6
+ */
7
+ isAllSelected: boolean;
8
+
9
+ /**
10
+ * Indica si algunos elementos están seleccionados.
11
+ */
12
+ isSomeSelected: boolean;
13
+
14
+ /**
15
+ * Indica si ningun elemento está seleccionado.
16
+ */
17
+ isNoneSelected: boolean;
18
+
19
+ /**
20
+ * Función que devuelve true si el elemento dado está seleccionado, false de lo contrario.
21
+ * @param item El elemento a verificar.
22
+ * @returns true si el elemento está seleccionado, false de lo contrario.
23
+ */
24
+ isSelected: (item: T) => boolean;
25
+
26
+ /**
27
+ * Array de elementos seleccionados.
28
+ */
29
+ selected: T[];
30
+
31
+ /**
32
+ * Establece los elementos seleccionados.
33
+ * @param items Los elementos seleccionados.
34
+ */
35
+ setSelected: (items: T[]) => void;
36
+
37
+ /**
38
+ * Selecciona un elemento.
39
+ * @param item El elemento a seleccionar.
40
+ */
41
+ select: (item: T) => void;
42
+
43
+ /**
44
+ * Deselecciona un elemento.
45
+ * @param item El elemento a deseleccionar.
46
+ */
47
+ unselect: (item: T) => void;
48
+
49
+ /**
50
+ * Alterna la selección de un elemento (si está seleccionado, lo deselecciona y viceversa).
51
+ * @param item El elemento cuya selección se alternará.
52
+ */
53
+ toggle: (item: T) => void;
54
+
55
+ /**
56
+ * Alterna la selección de todos los elementos (si todos están seleccionados, los deselecciona; de lo contrario, los selecciona).
57
+ */
58
+ toggleAll: () => void;
59
+
60
+ /**
61
+ * Deselecciona todos los elementos.
62
+ */
63
+ clear: () => void;
64
+ };
65
+ function useSelection<T>(items: T[]): Return<T> {
66
+ const [selected, setSelected] = useState(new Set<T>());
67
+
68
+ const isSelected = useCallback((item: T) => selected.has(item), [selected]);
69
+
70
+ const select = useCallback((item: T) => {
71
+ setSelected((prevSelected) => new Set([...prevSelected, item]));
72
+ }, []);
73
+
74
+ const unselect = useCallback((item: T) => {
75
+ setSelected((prevSelected) => {
76
+ const newSelected = new Set(prevSelected);
77
+ newSelected.delete(item);
78
+ return newSelected;
79
+ });
80
+ }, []);
81
+
82
+ const toggle = useCallback((item: T) => {
83
+ setSelected((prevSelected) => {
84
+ const newSelected = new Set(prevSelected);
85
+ if (newSelected.has(item)) {
86
+ newSelected.delete(item);
87
+ } else {
88
+ newSelected.add(item);
89
+ }
90
+ return newSelected;
91
+ });
92
+ }, []);
93
+
94
+ const setItems = useCallback((items: T[]) => {
95
+ setSelected(new Set(items));
96
+ }, []);
97
+
98
+ const toggleAll = useCallback(() => {
99
+ setSelected((prevSelected) =>
100
+ prevSelected.size === items.length ? new Set() : new Set(items),
101
+ );
102
+ }, [items]);
103
+
104
+ const clear = useCallback(() => setSelected(new Set()), []);
105
+
106
+ return {
107
+ isAllSelected: selected.size === items.length,
108
+ isSomeSelected: selected.size > 0 && selected.size < items.length,
109
+ isNoneSelected: selected.size === 0,
110
+ isSelected,
111
+ selected: [...selected],
112
+ setSelected: setItems,
113
+ select,
114
+ unselect,
115
+ toggle,
116
+ toggleAll,
117
+ clear,
118
+ };
119
+ }
120
+
121
+ export default useSelection;
@@ -0,0 +1,98 @@
1
+ import { Meta } from "@storybook/react-vite";
2
+ import { Button } from "../../../components";
3
+ import { cn } from "../../../lib";
4
+ import useStep from "../useStep";
5
+
6
+ /**
7
+ * Hook que recibe un array de elementos `T` y facita el manejo de vistas/formularios con múltiples pasos
8
+ */
9
+ const meta: Meta = {
10
+ title: "hooks/useStep",
11
+ tags: ["autodocs"],
12
+ };
13
+
14
+ export default meta;
15
+
16
+ export const Default = {
17
+ render: () => {
18
+ const data = [
19
+ {
20
+ id: 1,
21
+ content: "🚌",
22
+ },
23
+ {
24
+ id: 2,
25
+ content: "🚗",
26
+ },
27
+ {
28
+ id: 3,
29
+ content: "🚕",
30
+ },
31
+ ];
32
+ const {
33
+ steps,
34
+ back,
35
+ next,
36
+ goTo,
37
+ step,
38
+ isLastStep,
39
+ isFirstStep,
40
+ currentStepIndex,
41
+ } = useStep(data);
42
+
43
+ return (
44
+ <div>
45
+ <div className="flex gap-x-12">
46
+ {data.map((transporte) => (
47
+ <div
48
+ key={transporte.id}
49
+ className={cn(
50
+ "bg-popover grow rounded text-center shadow p-14 text-3xl",
51
+ step.id === transporte.id && "bg-accent",
52
+ )}
53
+ >
54
+ {transporte.content}
55
+ </div>
56
+ ))}
57
+ </div>
58
+
59
+ <div className="my-2 flex flex-wrap gap-2">
60
+ <Button onClick={back} disabled={isFirstStep}>
61
+ Back
62
+ </Button>
63
+ <Button onClick={next} disabled={isLastStep}>
64
+ Next
65
+ </Button>
66
+ <Button onClick={() => goTo(0)}>Go to first step</Button>
67
+ <Button onClick={() => goTo(1)}>Go to second step</Button>
68
+ <Button onClick={() => goTo(2)}>Go to third step</Button>
69
+ </div>
70
+
71
+ <pre className="mt-2 rounded-md bg-slate-950 p-4 text-white">
72
+ <code className="block">
73
+ Steps: <span>{JSON.stringify(steps, null, 2)}</span>
74
+ </code>
75
+ <code className="block">Current step index: {currentStepIndex}</code>
76
+ <code className="block">
77
+ Current step:{" "}
78
+ <span className="text-blue-300">
79
+ {JSON.stringify(step, null, 2)}
80
+ </span>
81
+ </code>
82
+ <code className="block">
83
+ isFirstStep:{" "}
84
+ <span className={isFirstStep ? "text-green-500" : "text-red-500"}>
85
+ {JSON.stringify(isFirstStep)}
86
+ </span>
87
+ </code>
88
+ <code className="block">
89
+ isLastStep:{" "}
90
+ <span className={isLastStep ? "text-green-500" : "text-red-500"}>
91
+ {JSON.stringify(isLastStep)}
92
+ </span>
93
+ </code>
94
+ </pre>
95
+ </div>
96
+ );
97
+ },
98
+ };
@@ -0,0 +1,51 @@
1
+ import { act, renderHook, waitFor } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { useStep } from "../../../hooks";
4
+
5
+ describe("useStep hook", () => {
6
+ it("should be defined", () => {
7
+ expect(useStep).toBeDefined();
8
+ });
9
+
10
+ it("should return the current step", () => {
11
+ const steps = ["step1", "step2", "step3"];
12
+ const { result } = renderHook(() => useStep(steps));
13
+
14
+ expect(result.current.currentStepIndex).toBe(0);
15
+ expect(result.current.isFirstStep).toBe(true);
16
+ });
17
+
18
+ it("should go to the next and prev step", () => {
19
+ const steps = ["step1", "step2", "step3"];
20
+ const { result } = renderHook(() => useStep(steps));
21
+
22
+ // go next
23
+ act(() => result.current.next());
24
+
25
+ waitFor(() => {
26
+ expect(result.current.currentStepIndex).toBe(1);
27
+ expect(result.current.isFirstStep).toBe(false);
28
+ });
29
+
30
+ // go back
31
+ act(() => result.current.back());
32
+
33
+ waitFor(() => {
34
+ expect(result.current.currentStepIndex).toBe(0);
35
+ expect(result.current.isFirstStep).toBe(true);
36
+ });
37
+ });
38
+
39
+ it("should go to specified step", () => {
40
+ const steps = ["step1", "step2", "step3"];
41
+ const { result } = renderHook(() => useStep(steps));
42
+
43
+ act(() => result.current.goTo(2));
44
+
45
+ waitFor(() => {
46
+ expect(result.current.currentStepIndex).toBe(2);
47
+ expect(result.current.isFirstStep).toBe(false);
48
+ expect(result.current.isLastStep).toBe(true);
49
+ });
50
+ });
51
+ });
@@ -0,0 +1 @@
1
+ export { default as useStep } from "./useStep";
@@ -0,0 +1,57 @@
1
+ import { findIndex, ListIterateeCustom } from "lodash";
2
+ import { ReactNode, useState } from "react";
3
+
4
+ interface Return<T = ReactNode> {
5
+ currentStepIndex: number;
6
+ step: T;
7
+ steps: T[];
8
+ isFirstStep: boolean;
9
+ isLastStep: boolean;
10
+ goTo: (i: number) => void;
11
+ next: () => void;
12
+ back: () => void;
13
+ findAndGo: (callback: ListIterateeCustom<T, boolean>) => void;
14
+ }
15
+
16
+ function useStep<T = ReactNode>(steps: T[], amountSteps?: number): Return<T> {
17
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
18
+ const length = amountSteps ?? steps.length;
19
+
20
+ const next = (): void => {
21
+ setCurrentStepIndex((i: number) => {
22
+ if (i >= length - 1) return i;
23
+ return i + 1;
24
+ });
25
+ };
26
+
27
+ const back = (): void => {
28
+ setCurrentStepIndex((i: number) => {
29
+ if (i <= 0) return i;
30
+ return i - 1;
31
+ });
32
+ };
33
+
34
+ const goTo = (i: number): void => {
35
+ setCurrentStepIndex(i);
36
+ };
37
+
38
+ const findAndGo = (t: ListIterateeCustom<T, boolean>): void => {
39
+ const stepIndex = findIndex(steps, t);
40
+ if (stepIndex === -1) return;
41
+ goTo(stepIndex);
42
+ };
43
+
44
+ return {
45
+ currentStepIndex,
46
+ step: steps[currentStepIndex],
47
+ steps,
48
+ isFirstStep: currentStepIndex === 0,
49
+ isLastStep: currentStepIndex === length - 1,
50
+ goTo,
51
+ next,
52
+ back,
53
+ findAndGo,
54
+ };
55
+ }
56
+
57
+ export default useStep;
@@ -0,0 +1,25 @@
1
+ import { Meta } from "@storybook/react-vite";
2
+ import { Button, Checkbox } from "../../../components";
3
+ import useToggle from "../useToggle";
4
+
5
+ /**
6
+ * Hook que facilita el manejo de un estado booleano. Provee métodos para abrir, cerrar y toggle el estado
7
+ */
8
+ const meta: Meta = {
9
+ title: "hooks/useToggle",
10
+ };
11
+
12
+ export default meta;
13
+
14
+ export const Default = {
15
+ render: () => {
16
+ const [isOpen, toggle] = useToggle();
17
+
18
+ return (
19
+ <div className="space-y-2">
20
+ <Checkbox name="open" checked={isOpen} />
21
+ <Button onClick={() => toggle()}>Toggle</Button>
22
+ </div>
23
+ );
24
+ },
25
+ };