@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.
- package/dist/index.cjs.js +70 -70
- package/dist/index.css +2 -0
- package/dist/index.d.ts +420 -272
- package/dist/index.es.js +34448 -27816
- package/dist/theme.css +1 -1
- package/package.json +11 -6
- package/src/__doc__/Changelog.mdx +6 -0
- package/src/__doc__/Components.mdx +73 -0
- package/src/__doc__/Examples.tsx +69 -0
- package/src/__doc__/Icons.mdx +41 -0
- package/src/__doc__/Intro.mdx +138 -0
- package/src/__doc__/MCP.mdx +71 -0
- package/src/__doc__/Migration.mdx +475 -0
- package/src/__doc__/Theme.mdx +132 -0
- package/src/__doc__/Types.mdx +252 -0
- package/src/components/alert/alert.stories.tsx +142 -0
- package/src/components/alert/alert.tsx +109 -0
- package/src/components/alert/index.ts +7 -0
- package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
- package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
- package/src/components/alert-dialog/alert-dialog.tsx +265 -0
- package/src/components/alert-dialog/index.ts +1 -0
- package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
- package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
- package/src/components/auto-complete/auto-complete.tsx +82 -0
- package/src/components/auto-complete/index.ts +2 -0
- package/src/components/avatar/avatar.stories.tsx +84 -0
- package/src/components/avatar/avatar.test.tsx +61 -0
- package/src/components/avatar/avatar.tsx +104 -0
- package/src/components/avatar/index.ts +1 -0
- package/src/components/background-image/background-image.stories.tsx +21 -0
- package/src/components/background-image/background-image.test.tsx +29 -0
- package/src/components/background-image/background-image.tsx +23 -0
- package/src/components/background-image/index.ts +1 -0
- package/src/components/button/button.stories.tsx +396 -0
- package/src/components/button/button.test.tsx +58 -0
- package/src/components/button/button.tsx +31 -0
- package/src/components/button/button.variants.ts +44 -0
- package/src/components/button/components/base-button.tsx +86 -0
- package/src/components/button/components/loader-overlay.tsx +21 -0
- package/src/components/button/components/loading-icon.tsx +47 -0
- package/src/components/button/index.ts +3 -0
- package/src/components/calendar/calendar.model.ts +86 -0
- package/src/components/calendar/calendar.stories.tsx +155 -0
- package/src/components/calendar/calendar.test.tsx +12 -0
- package/src/components/calendar/calendar.tsx +185 -0
- package/src/components/calendar/components/calendar-navigation.tsx +141 -0
- package/src/components/calendar/components/day.tsx +61 -0
- package/src/components/calendar/components/decade-view.tsx +45 -0
- package/src/components/calendar/components/index.ts +6 -0
- package/src/components/calendar/components/month-view.tsx +58 -0
- package/src/components/calendar/components/week-days.tsx +27 -0
- package/src/components/calendar/components/year-view.tsx +29 -0
- package/src/components/calendar/hooks/index.ts +4 -0
- package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
- package/src/components/calendar/hooks/use-calendar.ts +90 -0
- package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
- package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
- package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
- package/src/components/calendar/index.ts +1 -0
- package/src/components/calendar/utils/typeguards.ts +7 -0
- package/src/components/card/card.stories.tsx +116 -0
- package/src/components/card/card.tsx +74 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/center/center.stories.tsx +81 -0
- package/src/components/center/center.tsx +24 -0
- package/src/components/center/index.ts +1 -0
- package/src/components/checkbox/checkbox.stories.tsx +307 -0
- package/src/components/checkbox/checkbox.tsx +273 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
- package/src/components/checkbox-group/checkbox-group.tsx +16 -0
- package/src/components/checkbox-group/index.ts +1 -0
- package/src/components/combobox/combobox.stories.tsx +339 -0
- package/src/components/combobox/combobox.tsx +892 -0
- package/src/components/combobox/index.ts +1 -0
- package/src/components/date-picker/date-input.stories.tsx +158 -0
- package/src/components/date-picker/date-input.tsx +163 -0
- package/src/components/date-picker/date-picker.model.ts +90 -0
- package/src/components/date-picker/date-picker.stories.tsx +200 -0
- package/src/components/date-picker/date-picker.test.tsx +23 -0
- package/src/components/date-picker/date-picker.tsx +298 -0
- package/src/components/date-picker/date-picker.utils.ts +260 -0
- package/src/components/date-picker/index.ts +3 -0
- package/src/components/date-picker/use-date-input-popover.ts +48 -0
- package/src/components/date-picker/use-date-input.ts +125 -0
- package/src/components/dialog/dialog.stories.tsx +171 -0
- package/src/components/dialog/dialog.test.tsx +68 -0
- package/src/components/dialog/dialog.tsx +277 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/divider/divider.stories.tsx +70 -0
- package/src/components/divider/divider.test.tsx +22 -0
- package/src/components/divider/divider.tsx +23 -0
- package/src/components/divider/index.ts +1 -0
- package/src/components/dropzone/dropzone.stories.tsx +210 -0
- package/src/components/dropzone/dropzone.tsx +154 -0
- package/src/components/dropzone/file-types.ts +64 -0
- package/src/components/dropzone/index.ts +3 -0
- package/src/components/dropzone/upload-primitives.tsx +310 -0
- package/src/components/dropzone/use-dropzone.ts +122 -0
- package/src/components/empty-state/empty-state.stories.tsx +56 -0
- package/src/components/empty-state/empty-state.tsx +39 -0
- package/src/components/empty-state/index.ts +1 -0
- package/src/components/field/field.stories.tsx +223 -0
- package/src/components/field/field.tsx +229 -0
- package/src/components/field/index.ts +1 -0
- package/src/components/form/form.stories.tsx +594 -0
- package/src/components/form/form.tsx +30 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/heading/heading.stories.tsx +74 -0
- package/src/components/heading/heading.tsx +28 -0
- package/src/components/heading/heading.variants.ts +27 -0
- package/src/components/heading/index.ts +1 -0
- package/src/components/index.ts +46 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.stories.tsx +104 -0
- package/src/components/input/input.tsx +75 -0
- package/src/components/kbd/index.ts +1 -0
- package/src/components/kbd/kbd.stories.tsx +40 -0
- package/src/components/kbd/kbd.tsx +31 -0
- package/src/components/kbd/kbd.variants.ts +26 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.stories.tsx +68 -0
- package/src/components/label/label.test.tsx +61 -0
- package/src/components/label/label.tsx +62 -0
- package/src/components/loader/index.ts +1 -0
- package/src/components/loader/loader.stories.tsx +60 -0
- package/src/components/loader/loader.test.tsx +26 -0
- package/src/components/loader/loader.tsx +60 -0
- package/src/components/menu/index.ts +2 -0
- package/src/components/menu/menu-primitives.tsx +248 -0
- package/src/components/menu/menu.stories.tsx +203 -0
- package/src/components/menu/menu.tsx +100 -0
- package/src/components/menu/util/render-menu-item.tsx +54 -0
- package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
- package/src/components/multi-select/index.ts +1 -0
- package/src/components/multi-select/multi-select.stories.tsx +294 -0
- package/src/components/multi-select/multi-select.tsx +300 -0
- package/src/components/multi-select/multi-select.variants.ts +22 -0
- package/src/components/number-input/index.ts +1 -0
- package/src/components/number-input/number-input.stories.tsx +209 -0
- package/src/components/number-input/number-input.test.tsx +87 -0
- package/src/components/number-input/number-input.tsx +230 -0
- package/src/components/pagination/components/pagination-option.tsx +27 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.stories.tsx +80 -0
- package/src/components/pagination/pagination.test.tsx +76 -0
- package/src/components/pagination/pagination.tsx +102 -0
- package/src/components/password/index.ts +1 -0
- package/src/components/password/password.stories.tsx +104 -0
- package/src/components/password/password.tsx +71 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.stories.tsx +213 -0
- package/src/components/popover/popover.tsx +203 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/progress/progress.stories.tsx +124 -0
- package/src/components/progress/progress.test.tsx +25 -0
- package/src/components/progress/progress.tsx +124 -0
- package/src/components/scroll-area/index.ts +1 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
- package/src/components/scroll-area/scroll-area.tsx +64 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/select.stories.tsx +253 -0
- package/src/components/select/select.tsx +430 -0
- package/src/components/show/index.ts +1 -0
- package/src/components/show/show.stories.tsx +197 -0
- package/src/components/show/show.test.tsx +41 -0
- package/src/components/show/show.tsx +16 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.stories.tsx +36 -0
- package/src/components/skeleton/skeleton.test.tsx +14 -0
- package/src/components/skeleton/skeleton.tsx +15 -0
- package/src/components/stack/index.ts +1 -0
- package/src/components/stack/stack.stories.tsx +194 -0
- package/src/components/stack/stack.tsx +52 -0
- package/src/components/stepper/Stepper.tsx +190 -0
- package/src/components/stepper/context/stepper-context.tsx +11 -0
- package/src/components/stepper/index.ts +1 -0
- package/src/components/stepper/stepper.stories.tsx +130 -0
- package/src/components/stepper/stepper.test.tsx +91 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.stories.tsx +122 -0
- package/src/components/switch/switch.test.tsx +30 -0
- package/src/components/switch/switch.tsx +86 -0
- package/src/components/table/index.ts +3 -0
- package/src/components/table/table-primitives.tsx +122 -0
- package/src/components/table/table.model.ts +20 -0
- package/src/components/table/table.stories.tsx +169 -0
- package/src/components/table/table.test.tsx +91 -0
- package/src/components/table/table.tsx +109 -0
- package/src/components/table-pagination/index.ts +2 -0
- package/src/components/table-pagination/table-pagination.model.ts +2 -0
- package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
- package/src/components/table-pagination/table-pagination.test.tsx +32 -0
- package/src/components/table-pagination/table-pagination.tsx +108 -0
- package/src/components/tabs/context/tabs-context.tsx +14 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs.stories.tsx +182 -0
- package/src/components/tabs/tabs.test.tsx +61 -0
- package/src/components/tabs/tabs.tsx +175 -0
- package/src/components/tag/index.ts +2 -0
- package/src/components/tag/tag.stories.tsx +170 -0
- package/src/components/tag/tag.test.tsx +18 -0
- package/src/components/tag/tag.tsx +99 -0
- package/src/components/tag/tag.variants.ts +31 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/textarea.stories.tsx +73 -0
- package/src/components/textarea/textarea.tsx +105 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/timeline/timeline-status.ts +5 -0
- package/src/components/timeline/timeline.stories.tsx +84 -0
- package/src/components/timeline/timeline.tsx +147 -0
- package/src/components/toast/index.ts +1 -0
- package/src/components/toast/toast.stories.tsx +392 -0
- package/src/components/toast/toast.test.tsx +50 -0
- package/src/components/toast/toast.tsx +411 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.stories.tsx +226 -0
- package/src/components/tooltip/tooltip.test.tsx +46 -0
- package/src/components/tooltip/tooltip.tsx +171 -0
- package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
- package/src/components/tree/index.ts +2 -0
- package/src/components/tree/tree-primitives.tsx +126 -0
- package/src/components/tree/tree.stories.tsx +468 -0
- package/src/components/tree/tree.tsx +42 -0
- package/src/hooks/index.ts +26 -0
- package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
- package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
- package/src/hooks/useArray/index.ts +1 -0
- package/src/hooks/useArray/useArray.ts +76 -0
- package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
- package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
- package/src/hooks/useAsync/index.ts +1 -0
- package/src/hooks/useAsync/useAsync.ts +58 -0
- package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
- package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
- package/src/hooks/useClickOutside/index.ts +1 -0
- package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
- package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
- package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
- package/src/hooks/useClipboard/index.ts +1 -0
- package/src/hooks/useClipboard/useClipboard.tsx +28 -0
- package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
- package/src/hooks/useDebounceCallback/index.ts +1 -0
- package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
- package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
- package/src/hooks/useDebounceValue/index.ts +1 -0
- package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
- package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
- package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
- package/src/hooks/useDisclosure/index.ts +1 -0
- package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
- package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
- package/src/hooks/useDocumentTitle/index.ts +1 -0
- package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
- package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
- package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
- package/src/hooks/useEventListener/index.ts +1 -0
- package/src/hooks/useEventListener/useEventListener.ts +25 -0
- package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
- package/src/hooks/useFocusTrap/index.ts +1 -0
- package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
- package/src/hooks/useFocusTrap/tabbable.ts +70 -0
- package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
- package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
- package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
- package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
- package/src/hooks/useHotkey/__utils__/index.ts +3 -0
- package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
- package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
- package/src/hooks/useHotkey/index.ts +1 -0
- package/src/hooks/useHotkey/useHotkey.ts +34 -0
- package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
- package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
- package/src/hooks/useHover/index.ts +1 -0
- package/src/hooks/useHover/useHover.tsx +40 -0
- package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
- package/src/hooks/useIsVisible/index.ts +1 -0
- package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
- package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
- package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
- package/src/hooks/useLocalStorage/index.ts +1 -0
- package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
- package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
- package/src/hooks/useMediaQuery/index.ts +1 -0
- package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
- package/src/hooks/useMemoizedFn/index.ts +1 -0
- package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
- package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
- package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
- package/src/hooks/useMutation/index.ts +1 -0
- package/src/hooks/useMutation/useMutation.tsx +60 -0
- package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
- package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
- package/src/hooks/useObject/index.ts +1 -0
- package/src/hooks/useObject/useObject.tsx +48 -0
- package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
- package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
- package/src/hooks/usePagination/index.ts +2 -0
- package/src/hooks/usePagination/usePagination.tsx +74 -0
- package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
- package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
- package/src/hooks/usePortal/index.ts +1 -0
- package/src/hooks/usePortal/usePortal.ts +40 -0
- package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
- package/src/hooks/usePreventCloseWindow/index.ts +1 -0
- package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
- package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
- package/src/hooks/useRangePagination/index.ts +2 -0
- package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
- package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
- package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
- package/src/hooks/useSelection/index.ts +1 -0
- package/src/hooks/useSelection/useSelection.ts +121 -0
- package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
- package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
- package/src/hooks/useStep/index.ts +1 -0
- package/src/hooks/useStep/useStep.ts +57 -0
- package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
- package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
- package/src/hooks/useToggle/index.ts +1 -0
- package/src/hooks/useToggle/useToggle.ts +16 -0
- package/src/index.ts +6 -0
- package/src/lib/cn.ts +8 -0
- package/src/lib/index.ts +1 -0
- package/src/models/Generic.model.ts +67 -0
- package/src/models/index.ts +1 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/library-provider.tsx +44 -0
- package/src/providers/theme/ThemeProvider.tsx +25 -0
- package/src/providers/theme/index.ts +3 -0
- package/src/providers/theme/types.ts +11 -0
- package/src/providers/theme/useThemeProps.ts +25 -0
- package/src/stores/theme.store.ts +31 -0
- package/src/styles/components.css +4 -0
- package/src/styles/index.css +2 -0
- package/src/styles/library.css +2 -0
- package/src/styles/theme.css +232 -0
- package/src/utils/dates/parseDateRange.utility.ts +39 -0
- package/src/utils/form.tsx +91 -0
- package/src/utils/functions/createSafeContext.ts +17 -0
- package/src/utils/functions/ensureReactElement.tsx +30 -0
- package/src/utils/functions/getFormData.ts +19 -0
- package/src/utils/functions/index.ts +4 -0
- package/src/utils/functions/mergeRefs.ts +18 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/strings/extractInitials.utility.ts +10 -0
- package/src/utils/strings/index.ts +1 -0
- package/src/utils/tests/click.ts +3 -0
- package/src/utils/tests/index.ts +2 -0
- package/src/utils/tests/keyboard.ts +21 -0
- 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,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
|
+
};
|