@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,76 @@
1
+ import { render, screen, waitFor } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { Pagination } from "../../components";
4
+ import { DOTS } from "../../hooks";
5
+ import { click } from "../../utils";
6
+
7
+ describe("Pagination component", () => {
8
+ it("should render correctly", () => {
9
+ render(<Pagination pageSize={10} totalItems={100} />);
10
+ });
11
+
12
+ it("should navigate to page", async () => {
13
+ render(<Pagination pageSize={10} totalItems={100} />);
14
+
15
+ const page = screen.getByText(/Ir a la página 3/i).parentElement!;
16
+ click(page);
17
+
18
+ waitFor(() => {
19
+ expect(page).toHaveAttribute("active", "true");
20
+ });
21
+ });
22
+
23
+ it("should navigate to first page", async () => {
24
+ render(<Pagination pageSize={10} totalItems={100} />);
25
+
26
+ const page = screen.getByText(/Ir a primera página/i);
27
+ click(page);
28
+
29
+ waitFor(() => {
30
+ expect(page).toHaveAttribute("active", "true");
31
+ });
32
+ });
33
+
34
+ it("should navigate to previous page", async () => {
35
+ render(<Pagination pageSize={10} totalItems={100} />);
36
+
37
+ const page = screen.getByText(/Ir a página anterior/i);
38
+ click(page);
39
+
40
+ waitFor(() => {
41
+ expect(page).toHaveAttribute("active", "true");
42
+ });
43
+ });
44
+
45
+ it("should navigate to next page", async () => {
46
+ render(<Pagination pageSize={10} totalItems={100} />);
47
+
48
+ const page = screen.getByText(/Ir a próxima página/i);
49
+ click(page);
50
+
51
+ waitFor(() => {
52
+ expect(page).toHaveAttribute("active", "true");
53
+ });
54
+ });
55
+
56
+ it("should navigate to last page", async () => {
57
+ render(<Pagination pageSize={10} totalItems={100} />);
58
+
59
+ const page = screen.getByText(/Ir a última página/i);
60
+ click(page);
61
+
62
+ waitFor(() => {
63
+ expect(page).toHaveAttribute("active", "true");
64
+ });
65
+ });
66
+
67
+ it("should render dots", async () => {
68
+ render(<Pagination pageSize={10} totalItems={100} />);
69
+ const dots = screen.getByText(DOTS);
70
+
71
+ waitFor(() => {
72
+ expect(dots).toBeInTheDocument();
73
+ expect(dots).toHaveAttribute("dots", "true");
74
+ });
75
+ });
76
+ });
@@ -0,0 +1,102 @@
1
+ import {
2
+ ChevronLeft,
3
+ ChevronRight,
4
+ ChevronsLeft,
5
+ ChevronsRight,
6
+ } from "lucide-react";
7
+ import { HTMLProps } from "react";
8
+ import { DOTS, useRangePagination, useRangePaginationProps } from "../../hooks";
9
+ import { cn } from "../../lib";
10
+ import {
11
+ PaginationOption as Option,
12
+ PaginationOptionProps,
13
+ } from "./components/pagination-option";
14
+
15
+ interface Props extends useRangePaginationProps {
16
+ optionProps?: PaginationOptionProps;
17
+ className?: string;
18
+ containerProps?: HTMLProps<HTMLDivElement>;
19
+ }
20
+
21
+ export const Pagination = ({
22
+ optionProps,
23
+ className,
24
+ containerProps,
25
+ ...rangeProps
26
+ }: Props) => {
27
+ const {
28
+ paginationRange,
29
+ currentPage,
30
+ goTo,
31
+ maxPage,
32
+ next,
33
+ prev,
34
+ isLastPage,
35
+ isFirstPage,
36
+ } = useRangePagination(rangeProps);
37
+
38
+ return (
39
+ <div
40
+ {...containerProps}
41
+ className={cn(
42
+ "flex items-end select-none h-10",
43
+ containerProps?.className,
44
+ className,
45
+ )}
46
+ data-slot="pagination"
47
+ >
48
+ <Option
49
+ {...optionProps}
50
+ disabled={isFirstPage}
51
+ onClick={() => goTo(1)}
52
+ className={cn("rounded-l-md", optionProps?.className)}
53
+ >
54
+ <ChevronsLeft data-slot="pagination-option-icon" />
55
+ <span className="sr-only">Ir a primera página</span>
56
+ </Option>
57
+
58
+ <Option {...optionProps} onClick={prev} disabled={isFirstPage}>
59
+ <ChevronLeft data-slot="pagination-option-icon" />
60
+ <span className="sr-only">Ir a página anterior</span>
61
+ </Option>
62
+
63
+ {paginationRange.map((pageNumber) => (
64
+ <>
65
+ {pageNumber === DOTS ? (
66
+ <Option
67
+ key={pageNumber}
68
+ className={cn("pointer-events-none", optionProps?.className)}
69
+ >
70
+ {DOTS}
71
+ </Option>
72
+ ) : (
73
+ <Option
74
+ {...optionProps}
75
+ isActive={pageNumber === currentPage}
76
+ onClick={() => goTo(pageNumber as number)}
77
+ key={pageNumber}
78
+ >
79
+ {pageNumber}
80
+ <span className="sr-only">Ir a la página {pageNumber}</span>
81
+ </Option>
82
+ )}
83
+ </>
84
+ ))}
85
+
86
+ <Option {...optionProps} onClick={next} disabled={isLastPage}>
87
+ <ChevronRight data-slot="pagination-option-icon" />
88
+ <span className="sr-only">Ir a próxima página</span>
89
+ </Option>
90
+
91
+ <Option
92
+ {...optionProps}
93
+ disabled={isLastPage}
94
+ onClick={() => goTo(maxPage)}
95
+ className={cn("rounded-r-md", optionProps?.className)}
96
+ >
97
+ <ChevronsRight data-slot="pagination-option-icon" />
98
+ <span className="sr-only">Ir a última página</span>
99
+ </Option>
100
+ </div>
101
+ );
102
+ };
@@ -0,0 +1 @@
1
+ export * from "./password";
@@ -0,0 +1,104 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Lock, Unlock } from "lucide-react";
3
+ import { useState } from "react";
4
+ import { createToastManager, ToastProvider } from "../toast";
5
+ import { Password } from "./password";
6
+
7
+ const toastManager = createToastManager();
8
+
9
+ /**
10
+ * Password input with a visibility toggle button. `aria-invalid` drives error
11
+ * styling — no Field wrapper needed.
12
+ *
13
+ * The toggle button is always rendered; `toggleable={false}` disables it
14
+ * without removing it from the DOM, keeping the layout consistent.
15
+ *
16
+ * `onShow` fires when the password becomes visible; `onHide` fires when
17
+ * it's hidden again — both after the internal state has changed.
18
+ */
19
+ const meta: Meta<typeof Password> = {
20
+ title: "Components/Password",
21
+ component: Password,
22
+ parameters: { layout: "centered" },
23
+ args: {
24
+ placeholder: "Enter your password",
25
+ },
26
+ argTypes: {
27
+ showIcon: { control: false },
28
+ hideIcon: { control: false },
29
+ onShow: { control: false },
30
+ onHide: { control: false },
31
+ onChange: { control: false },
32
+ },
33
+ decorators: [
34
+ (Story) => (
35
+ <ToastProvider toastManager={toastManager}>
36
+ <Story />
37
+ </ToastProvider>
38
+ ),
39
+ ],
40
+ };
41
+
42
+ export default meta;
43
+ type Story = StoryObj<typeof Password>;
44
+
45
+ export const Default: Story = {};
46
+
47
+ export const Disabled: Story = {
48
+ args: {
49
+ disabled: true,
50
+ defaultValue: "my-secret-password",
51
+ },
52
+ };
53
+
54
+ /**
55
+ * `aria-invalid="true"` applies error border and ring — no Field wrapper needed.
56
+ */
57
+ export const Invalid: Story = {
58
+ args: { "aria-invalid": "true" },
59
+ };
60
+
61
+ /**
62
+ * `toggleable={false}` disables the button without removing it from the DOM.
63
+ * Use this when you want to prevent the user from revealing the password
64
+ * while keeping the visual layout consistent.
65
+ */
66
+ export const NonToggleable: Story = {
67
+ args: {
68
+ toggleable: false,
69
+ defaultValue: "my-secret-password",
70
+ },
71
+ };
72
+
73
+ /**
74
+ * `onChange` receives the string value as first argument, so no
75
+ * `event.target.value` needed.
76
+ */
77
+ export const Controlled: Story = {
78
+ render: () => {
79
+ const [value, setValue] = useState("");
80
+ return (
81
+ <Password
82
+ placeholder="Enter your password"
83
+ value={value}
84
+ onChange={(val) => setValue(val)}
85
+ />
86
+ );
87
+ },
88
+ };
89
+
90
+ export const CustomIcons: Story = {
91
+ args: {
92
+ showIcon: <Lock className="h-4 w-4" />,
93
+ hideIcon: <Unlock className="h-4 w-4" />,
94
+ },
95
+ };
96
+
97
+ export const WithCallbacks: Story = {
98
+ args: {
99
+ onShow: () =>
100
+ toastManager.add({ variant: "success", description: "Password visible" }),
101
+ onHide: () =>
102
+ toastManager.add({ variant: "success", description: "Password hidden" }),
103
+ },
104
+ };
@@ -0,0 +1,71 @@
1
+ import { Eye, EyeOff } from "lucide-react";
2
+ import { type ChangeEvent, type ReactNode } from "react";
3
+ import { useToggle } from "../../hooks";
4
+ import { cn } from "../../lib";
5
+ import { InputPrimitive, type InputProps } from "../input/input";
6
+
7
+ export type PasswordProps = Omit<InputProps, "unstyled" | "nativeInput"> & {
8
+ showIcon?: ReactNode;
9
+ hideIcon?: ReactNode;
10
+ onShow?: () => void;
11
+ onHide?: () => void;
12
+ toggleable?: boolean;
13
+ };
14
+
15
+ export function Password({
16
+ className,
17
+ showIcon = <Eye className="h-4 w-4" />,
18
+ hideIcon = <EyeOff className="h-4 w-4" />,
19
+ onShow,
20
+ onHide,
21
+ toggleable = true,
22
+ onChange,
23
+ ...rest
24
+ }: PasswordProps) {
25
+ const [showPassword, toggleShowPassword] = useToggle(false);
26
+
27
+ const handleToggle = () => {
28
+ toggleShowPassword();
29
+ showPassword ? onHide?.() : onShow?.();
30
+ };
31
+
32
+ const handleChange = onChange
33
+ ? (event: ChangeEvent<HTMLInputElement>) =>
34
+ onChange(event.target.value, event)
35
+ : undefined;
36
+
37
+ return (
38
+ <div
39
+ className={cn(
40
+ "relative inline-flex w-full rounded-md border border-input bg-background",
41
+ "text-sm transition-shadow",
42
+ "has-focus-visible:border-ring",
43
+ "has-aria-invalid:border-error has-focus-visible:has-aria-invalid:ring-error/20",
44
+ "has-disabled:cursor-not-allowed has-disabled:opacity-50",
45
+ className,
46
+ )}
47
+ data-slot="password"
48
+ >
49
+ <InputPrimitive
50
+ {...rest}
51
+ onChange={handleChange}
52
+ type={showPassword ? "text" : "password"}
53
+ className="w-full min-w-0 rounded-[inherit] bg-transparent py-2 pl-3 pr-10 text-sm outline-none placeholder:text-muted-foreground [&::-ms-clear]:hidden [&::-ms-reveal]:hidden"
54
+ />
55
+ <button
56
+ data-slot="password-toggle"
57
+ type="button"
58
+ onClick={handleToggle}
59
+ disabled={!toggleable}
60
+ className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
61
+ >
62
+ {showPassword ? hideIcon : showIcon}
63
+ </button>
64
+ </div>
65
+ );
66
+ }
67
+
68
+ // ── Backward-compat alias ─────────────────────────────────────────────────────
69
+
70
+ export { Password as PasswordRoot };
71
+ export type { PasswordProps as PasswordRootProps };
@@ -0,0 +1 @@
1
+ export * from "./popover";
@@ -0,0 +1,213 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { XIcon } from "lucide-react";
3
+ import { useState } from "storybook/preview-api";
4
+ import { Button, Input, Label } from "../../components";
5
+ import {
6
+ Popover,
7
+ PopoverClose,
8
+ PopoverDescription,
9
+ PopoverPopup,
10
+ PopoverRoot,
11
+ PopoverTitle,
12
+ PopoverTrigger,
13
+ } from ".";
14
+
15
+ /**
16
+ * Floating panel anchored to a trigger element. Built on
17
+ * [Base UI Popover](https://base-ui.com/react/components/popover.md).
18
+ *
19
+ * Provides two APIs: `Popover` (composite, single-component) and primitives
20
+ * (`PopoverRoot` + `PopoverTrigger` + `PopoverPopup`) for full layout control.
21
+ *
22
+ * `trigger` accepts a `ReactElement` passed as the `render` prop of `PopoverTrigger` — no
23
+ * wrapper is added. Use `popupClassName` to style the popup without going through `popupProps`.
24
+ * `title` and `description` render `PopoverTitle` / `PopoverDescription` with automatic ARIA
25
+ * wiring. Supports controlled state via `open` / `onOpenChange` and imperative control via
26
+ * `PopoverCreateHandle`.
27
+ */
28
+ const meta: Meta<typeof Popover> = {
29
+ title: "Components/Popover",
30
+ component: Popover,
31
+ };
32
+
33
+ export default meta;
34
+ type Story = StoryObj<typeof Popover>;
35
+
36
+ export const Default: Story = {
37
+ render: (args) => (
38
+ <Popover {...args} trigger={<Button variant="outline">Open</Button>}>
39
+ Lorem Ipsum es simplemente el texto de relleno de las imprentas y archivos
40
+ de texto. Lorem Ipsum ha sido el texto de relleno estándar de las
41
+ industrias desde el año 1500.
42
+ </Popover>
43
+ ),
44
+ };
45
+
46
+ /**
47
+ * `tooltipStyle` renders the popup compact — smaller padding, `rounded-md`, and `w-fit`.
48
+ * Intended for brief contextual hints that don't need a full panel.
49
+ */
50
+ export const TooltipStyle: Story = {
51
+ render: () => (
52
+ <Popover trigger={<Button variant="outline">Hint</Button>} tooltipStyle>
53
+ Contextual tooltip content
54
+ </Popover>
55
+ ),
56
+ };
57
+
58
+ /**
59
+ * `side` and `align` control popup placement relative to the trigger.
60
+ * `sideOffset` and `alignOffset` fine-tune the gap and shift in pixels.
61
+ */
62
+ export const Positioning: Story = {
63
+ render: () => (
64
+ <div className="flex gap-4">
65
+ {(["top", "bottom", "left", "right"] as const).map((side) => (
66
+ <Popover
67
+ key={side}
68
+ side={side}
69
+ trigger={<Button variant="outline">{side}</Button>}
70
+ >
71
+ Anchored to {side}
72
+ </Popover>
73
+ ))}
74
+ </div>
75
+ ),
76
+ };
77
+
78
+ export const WithTitleAndDescription: Story = {
79
+ render: () => (
80
+ <Popover
81
+ trigger={<Button variant="outline">Update Profile</Button>}
82
+ title="Update Profile"
83
+ description="Edit your profile details below."
84
+ >
85
+ <div className="space-y-3">
86
+ <div className="grid grid-cols-3 items-center gap-4">
87
+ <Label htmlFor="firstName">First Name</Label>
88
+ <Input
89
+ id="firstName"
90
+ defaultValue="John"
91
+ className="col-span-2 h-8"
92
+ />
93
+ </div>
94
+ <div className="grid grid-cols-3 items-center gap-4">
95
+ <Label htmlFor="lastName">Last Name</Label>
96
+ <Input id="lastName" defaultValue="Doe" className="col-span-2 h-8" />
97
+ </div>
98
+ </div>
99
+ </Popover>
100
+ ),
101
+ };
102
+
103
+ /**
104
+ * `open` and `onOpenChange` enable external state control. Use when another element
105
+ * drives the popover's visibility without the trigger being part of the popover itself.
106
+ */
107
+ export const Controlled: Story = {
108
+ render: function Render(args) {
109
+ const [open, setOpen] = useState(false);
110
+ return (
111
+ <Popover
112
+ {...args}
113
+ open={open}
114
+ onOpenChange={(value: boolean) => setOpen(value)}
115
+ trigger={<Button variant="outline">Open</Button>}
116
+ >
117
+ This popover is controlled externally via <code>open</code> /{" "}
118
+ <code>onOpenChange</code>.
119
+ </Popover>
120
+ );
121
+ },
122
+ args: {
123
+ open: false,
124
+ },
125
+ };
126
+
127
+ /**
128
+ * `matchTriggerWidth` makes the popup as wide as its trigger using Base UI's
129
+ * `--anchor-width` CSS variable on the positioner. The trigger width drives the
130
+ * popup width — widening or narrowing the trigger changes the popup accordingly.
131
+ *
132
+ * Useful for dropdowns or comboboxes where content should visually align with the
133
+ * field it belongs to. Incompatible with `tooltipStyle` (which forces `w-fit`).
134
+ */
135
+ export const MatchTriggerWidth: Story = {
136
+ render: () => (
137
+ <div className="flex flex-col items-start gap-6">
138
+ <Popover
139
+ matchTriggerWidth
140
+ trigger={
141
+ <Button variant="outline" className="w-40">
142
+ Narrow
143
+ </Button>
144
+ }
145
+ >
146
+ This popup matches the trigger width.
147
+ </Popover>
148
+ <Popover
149
+ matchTriggerWidth
150
+ trigger={
151
+ <Button variant="outline" className="w-80">
152
+ Wide
153
+ </Button>
154
+ }
155
+ >
156
+ This popup also matches its trigger width.
157
+ </Popover>
158
+ </div>
159
+ ),
160
+ };
161
+
162
+ /**
163
+ * Compose primitives directly for full layout and slot control.
164
+ *
165
+ * `PopoverTrigger` and `PopoverClose` accept a `render` prop to replace the default `<button>`.
166
+ * `PopoverTitle` and `PopoverDescription` wire `aria-labelledby` / `aria-describedby` on the
167
+ * popup automatically.
168
+ *
169
+ * ```tsx
170
+ * <PopoverRoot>
171
+ * <PopoverTrigger render={<Button variant="outline" />}>Open</PopoverTrigger>
172
+ * <PopoverPopup className="w-80">
173
+ * <PopoverClose
174
+ * aria-label="Close"
175
+ * className="absolute end-2 top-2"
176
+ * render={<Button size="icon" variant="ghost"><XIcon /></Button>}
177
+ * />
178
+ * <PopoverTitle>Title</PopoverTitle>
179
+ * <PopoverDescription>Description</PopoverDescription>
180
+ * <PopoverClose render={<Button variant="outline" />}>Dismiss</PopoverClose>
181
+ * </PopoverPopup>
182
+ * </PopoverRoot>
183
+ * ```
184
+ */
185
+ export const Primitive: Story = {
186
+ render: () => (
187
+ <PopoverRoot>
188
+ <PopoverTrigger render={<Button variant="outline" />}>
189
+ Open Popover
190
+ </PopoverTrigger>
191
+ <PopoverPopup className="w-80">
192
+ <PopoverClose
193
+ aria-label="Close"
194
+ className="absolute end-2 top-2"
195
+ render={
196
+ <Button size="icon" variant="ghost">
197
+ <XIcon />
198
+ </Button>
199
+ }
200
+ />
201
+ <div className="mb-2">
202
+ <PopoverTitle className="text-base">Notifications</PopoverTitle>
203
+ <PopoverDescription>
204
+ You are all caught up. Good job!
205
+ </PopoverDescription>
206
+ </div>
207
+ <PopoverClose render={<Button variant="outline" />}>
208
+ Dismiss
209
+ </PopoverClose>
210
+ </PopoverPopup>
211
+ </PopoverRoot>
212
+ ),
213
+ };