@refraktor/core 0.0.3 → 0.0.4
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/.turbo/turbo-build.log +1 -1
- package/build/components/button/button.styles.js +5 -5
- package/build/components/chip/chip-group/chip-group.d.ts +4 -0
- package/build/components/chip/chip-group/chip-group.d.ts.map +1 -0
- package/build/components/chip/chip-group/chip-group.js +43 -0
- package/build/components/chip/chip-group/index.d.ts +2 -0
- package/build/components/chip/chip-group/index.d.ts.map +1 -0
- package/build/components/chip/chip-group/index.js +1 -0
- package/build/components/chip/chip.context.d.ts +15 -0
- package/build/components/chip/chip.context.d.ts.map +1 -0
- package/build/components/chip/chip.context.js +2 -0
- package/build/components/chip/chip.d.ts +4 -0
- package/build/components/chip/chip.d.ts.map +1 -0
- package/build/components/chip/chip.js +85 -0
- package/build/components/chip/chip.styles.d.ts +5 -0
- package/build/components/chip/chip.styles.d.ts.map +1 -0
- package/build/components/chip/chip.styles.js +19 -0
- package/build/components/chip/chip.test.d.ts +2 -0
- package/build/components/chip/chip.test.d.ts.map +1 -0
- package/build/components/chip/chip.test.js +107 -0
- package/build/components/chip/chip.types.d.ts +95 -0
- package/build/components/chip/chip.types.d.ts.map +1 -0
- package/build/components/chip/chip.types.js +1 -0
- package/build/components/chip/index.d.ts +4 -0
- package/build/components/chip/index.d.ts.map +1 -0
- package/build/components/chip/index.js +2 -0
- package/build/components/combobox/combobox-dropdown/combobox-dropdown.d.ts +4 -0
- package/build/components/combobox/combobox-dropdown/combobox-dropdown.d.ts.map +1 -0
- package/build/components/combobox/combobox-dropdown/combobox-dropdown.js +21 -0
- package/build/components/combobox/combobox-dropdown/index.d.ts +2 -0
- package/build/components/combobox/combobox-dropdown/index.d.ts.map +1 -0
- package/build/components/combobox/combobox-dropdown/index.js +1 -0
- package/build/components/combobox/combobox-group/combobox-group.d.ts +4 -0
- package/build/components/combobox/combobox-group/combobox-group.d.ts.map +1 -0
- package/build/components/combobox/combobox-group/combobox-group.js +15 -0
- package/build/components/combobox/combobox-group/index.d.ts +2 -0
- package/build/components/combobox/combobox-group/index.d.ts.map +1 -0
- package/build/components/combobox/combobox-group/index.js +1 -0
- package/build/components/combobox/combobox-input/combobox-input.d.ts +4 -0
- package/build/components/combobox/combobox-input/combobox-input.d.ts.map +1 -0
- package/build/components/combobox/combobox-input/combobox-input.js +101 -0
- package/build/components/combobox/combobox-input/index.d.ts +2 -0
- package/build/components/combobox/combobox-input/index.d.ts.map +1 -0
- package/build/components/combobox/combobox-input/index.js +1 -0
- package/build/components/combobox/combobox-option/combobox-option.d.ts +4 -0
- package/build/components/combobox/combobox-option/combobox-option.d.ts.map +1 -0
- package/build/components/combobox/combobox-option/combobox-option.js +86 -0
- package/build/components/combobox/combobox-option/index.d.ts +2 -0
- package/build/components/combobox/combobox-option/index.d.ts.map +1 -0
- package/build/components/combobox/combobox-option/index.js +1 -0
- package/build/components/combobox/combobox-root/combobox-root.d.ts +4 -0
- package/build/components/combobox/combobox-root/combobox-root.d.ts.map +1 -0
- package/build/components/combobox/combobox-root/combobox-root.js +282 -0
- package/build/components/combobox/combobox-root/index.d.ts +2 -0
- package/build/components/combobox/combobox-root/index.d.ts.map +1 -0
- package/build/components/combobox/combobox-root/index.js +1 -0
- package/build/components/combobox/combobox.context.d.ts +73 -0
- package/build/components/combobox/combobox.context.d.ts.map +1 -0
- package/build/components/combobox/combobox.context.js +50 -0
- package/build/components/combobox/combobox.d.ts +4 -0
- package/build/components/combobox/combobox.d.ts.map +1 -0
- package/build/components/combobox/combobox.js +31 -0
- package/build/components/combobox/combobox.test.d.ts +2 -0
- package/build/components/combobox/combobox.test.d.ts.map +1 -0
- package/build/components/combobox/combobox.test.js +104 -0
- package/build/components/combobox/combobox.types.d.ts +205 -0
- package/build/components/combobox/combobox.types.d.ts.map +1 -0
- package/build/components/combobox/combobox.types.js +1 -0
- package/build/components/combobox/index.d.ts +8 -0
- package/build/components/combobox/index.d.ts.map +1 -0
- package/build/components/combobox/index.js +6 -0
- package/build/components/combobox/use-combobox.d.ts +32 -0
- package/build/components/combobox/use-combobox.d.ts.map +1 -0
- package/build/components/combobox/use-combobox.js +80 -0
- package/build/components/drawer/drawer-body/drawer-body.d.ts +4 -0
- package/build/components/drawer/drawer-body/drawer-body.d.ts.map +1 -0
- package/build/components/drawer/drawer-body/drawer-body.js +11 -0
- package/build/components/drawer/drawer-body/index.d.ts +2 -0
- package/build/components/drawer/drawer-body/index.d.ts.map +1 -0
- package/build/components/drawer/drawer-body/index.js +1 -0
- package/build/components/drawer/drawer-content/drawer-content.d.ts.map +1 -1
- package/build/components/drawer/drawer-content/drawer-content.js +9 -6
- package/build/components/drawer/drawer-overlay/drawer-overlay.d.ts.map +1 -1
- package/build/components/drawer/drawer-overlay/drawer-overlay.js +4 -4
- package/build/components/drawer/drawer-root/drawer-root.d.ts.map +1 -1
- package/build/components/drawer/drawer-root/drawer-root.js +8 -9
- package/build/components/drawer/drawer.context.d.ts +2 -1
- package/build/components/drawer/drawer.context.d.ts.map +1 -1
- package/build/components/drawer/drawer.d.ts.map +1 -1
- package/build/components/drawer/drawer.js +5 -3
- package/build/components/drawer/drawer.test.js +55 -16
- package/build/components/drawer/drawer.types.d.ts +29 -3
- package/build/components/drawer/drawer.types.d.ts.map +1 -1
- package/build/components/drawer/index.d.ts +2 -1
- package/build/components/drawer/index.d.ts.map +1 -1
- package/build/components/drawer/index.js +1 -0
- package/build/components/drawer/use-drawer.d.ts +8 -1
- package/build/components/drawer/use-drawer.d.ts.map +1 -1
- package/build/components/drawer/use-drawer.js +25 -38
- package/build/components/file-input/file-input.d.ts +4 -0
- package/build/components/file-input/file-input.d.ts.map +1 -0
- package/build/components/file-input/file-input.js +88 -0
- package/build/components/file-input/file-input.test.d.ts +2 -0
- package/build/components/file-input/file-input.test.d.ts.map +1 -0
- package/build/components/file-input/file-input.test.js +74 -0
- package/build/components/file-input/file-input.types.d.ts +46 -0
- package/build/components/file-input/file-input.types.d.ts.map +1 -0
- package/build/components/file-input/file-input.types.js +1 -0
- package/build/components/file-input/file-input.utils.d.ts +11 -0
- package/build/components/file-input/file-input.utils.d.ts.map +1 -0
- package/build/components/file-input/file-input.utils.js +67 -0
- package/build/components/file-input/file-input.utils.test.d.ts +2 -0
- package/build/components/file-input/file-input.utils.test.d.ts.map +1 -0
- package/build/components/file-input/file-input.utils.test.js +27 -0
- package/build/components/file-input/index.d.ts +3 -0
- package/build/components/file-input/index.d.ts.map +1 -0
- package/build/components/file-input/index.js +2 -0
- package/build/components/for/for.d.ts +8 -0
- package/build/components/for/for.d.ts.map +1 -0
- package/build/components/for/for.js +32 -0
- package/build/components/for/for.test.d.ts +2 -0
- package/build/components/for/for.test.d.ts.map +1 -0
- package/build/components/for/for.test.js +31 -0
- package/build/components/for/for.types.d.ts +33 -0
- package/build/components/for/for.types.d.ts.map +1 -0
- package/build/components/for/for.types.js +1 -0
- package/build/components/for/index.d.ts +3 -0
- package/build/components/for/index.d.ts.map +1 -0
- package/build/components/for/index.js +1 -0
- package/build/components/index.d.ts +6 -0
- package/build/components/index.d.ts.map +1 -1
- package/build/components/index.js +6 -0
- package/build/components/modal/index.d.ts +2 -1
- package/build/components/modal/index.d.ts.map +1 -1
- package/build/components/modal/index.js +1 -0
- package/build/components/modal/modal-body/index.d.ts +2 -0
- package/build/components/modal/modal-body/index.d.ts.map +1 -0
- package/build/components/modal/modal-body/index.js +1 -0
- package/build/components/modal/modal-body/modal-body.d.ts +4 -0
- package/build/components/modal/modal-body/modal-body.d.ts.map +1 -0
- package/build/components/modal/modal-body/modal-body.js +11 -0
- package/build/components/modal/modal-content/modal-content.d.ts.map +1 -1
- package/build/components/modal/modal-content/modal-content.js +13 -5
- package/build/components/modal/modal-header/modal-header.js +2 -2
- package/build/components/modal/modal-overlay/modal-overlay.d.ts.map +1 -1
- package/build/components/modal/modal-overlay/modal-overlay.js +4 -4
- package/build/components/modal/modal-root/modal-root.d.ts.map +1 -1
- package/build/components/modal/modal-root/modal-root.js +12 -9
- package/build/components/modal/modal.context.d.ts +5 -2
- package/build/components/modal/modal.context.d.ts.map +1 -1
- package/build/components/modal/modal.d.ts.map +1 -1
- package/build/components/modal/modal.js +5 -3
- package/build/components/modal/modal.test.js +78 -13
- package/build/components/modal/modal.types.d.ts +34 -5
- package/build/components/modal/modal.types.d.ts.map +1 -1
- package/build/components/modal/use-modal.d.ts +8 -1
- package/build/components/modal/use-modal.d.ts.map +1 -1
- package/build/components/modal/use-modal.js +25 -38
- package/build/components/password-input/index.d.ts +3 -0
- package/build/components/password-input/index.d.ts.map +1 -0
- package/build/components/password-input/index.js +2 -0
- package/build/components/password-input/password-input.d.ts +4 -0
- package/build/components/password-input/password-input.d.ts.map +1 -0
- package/build/components/password-input/password-input.js +32 -0
- package/build/components/password-input/password-input.test.d.ts +2 -0
- package/build/components/password-input/password-input.test.d.ts.map +1 -0
- package/build/components/password-input/password-input.test.js +47 -0
- package/build/components/password-input/password-input.types.d.ts +24 -0
- package/build/components/password-input/password-input.types.d.ts.map +1 -0
- package/build/components/password-input/password-input.types.js +1 -0
- package/build/components/pin-input/index.d.ts +3 -0
- package/build/components/pin-input/index.d.ts.map +1 -0
- package/build/components/pin-input/index.js +2 -0
- package/build/components/pin-input/pin-input.d.ts +4 -0
- package/build/components/pin-input/pin-input.d.ts.map +1 -0
- package/build/components/pin-input/pin-input.js +245 -0
- package/build/components/pin-input/pin-input.test.d.ts +2 -0
- package/build/components/pin-input/pin-input.test.d.ts.map +1 -0
- package/build/components/pin-input/pin-input.test.js +87 -0
- package/build/components/pin-input/pin-input.types.d.ts +44 -0
- package/build/components/pin-input/pin-input.types.d.ts.map +1 -0
- package/build/components/pin-input/pin-input.types.js +1 -0
- package/build/components/scroll-area/index.d.ts +3 -0
- package/build/components/scroll-area/index.d.ts.map +1 -0
- package/build/components/scroll-area/index.js +1 -0
- package/build/components/scroll-area/scroll-area.d.ts +4 -0
- package/build/components/scroll-area/scroll-area.d.ts.map +1 -0
- package/build/components/scroll-area/scroll-area.js +30 -0
- package/build/components/scroll-area/scroll-area.test.d.ts +2 -0
- package/build/components/scroll-area/scroll-area.test.d.ts.map +1 -0
- package/build/components/scroll-area/scroll-area.test.js +39 -0
- package/build/components/scroll-area/scroll-area.types.d.ts +25 -0
- package/build/components/scroll-area/scroll-area.types.d.ts.map +1 -0
- package/build/components/scroll-area/scroll-area.types.js +1 -0
- package/build/components/segmented-control/segmented-control.d.ts.map +1 -1
- package/build/components/segmented-control/segmented-control.js +3 -3
- package/build/components/segmented-control/segmented-control.styles.js +13 -13
- package/build/components/segmented-control/segmented-control.test.js +11 -0
- package/build/components/segmented-control/segmented-control.types.d.ts +2 -0
- package/build/components/segmented-control/segmented-control.types.d.ts.map +1 -1
- package/build/icons/eye-off.d.ts +4 -0
- package/build/icons/eye-off.d.ts.map +1 -0
- package/build/icons/eye-off.js +5 -0
- package/build/icons/eye.d.ts +4 -0
- package/build/icons/eye.d.ts.map +1 -0
- package/build/icons/eye.js +5 -0
- package/build/icons/index.d.ts +2 -0
- package/build/icons/index.d.ts.map +1 -1
- package/build/icons/index.js +2 -0
- package/build/style.css +1 -1
- package/package.json +2 -2
- package/src/components/button/button.styles.ts +5 -5
- package/src/components/chip/chip-group/chip-group.tsx +107 -0
- package/src/components/chip/chip-group/index.ts +1 -0
- package/src/components/chip/chip.context.ts +15 -0
- package/src/components/chip/chip.styles.ts +36 -0
- package/src/components/chip/chip.test.tsx +197 -0
- package/src/components/chip/chip.tsx +208 -0
- package/src/components/chip/chip.types.ts +134 -0
- package/src/components/chip/index.ts +10 -0
- package/src/components/drawer/drawer-body/drawer-body.tsx +29 -0
- package/src/components/drawer/drawer-body/index.ts +1 -0
- package/src/components/drawer/drawer-content/drawer-content.tsx +63 -26
- package/src/components/drawer/drawer-overlay/drawer-overlay.tsx +6 -5
- package/src/components/drawer/drawer-root/drawer-root.tsx +17 -18
- package/src/components/drawer/drawer.context.ts +2 -1
- package/src/components/drawer/drawer.test.tsx +144 -36
- package/src/components/drawer/drawer.tsx +31 -3
- package/src/components/drawer/drawer.types.ts +37 -3
- package/src/components/drawer/index.ts +2 -0
- package/src/components/drawer/use-drawer.ts +44 -51
- package/src/components/file-input/file-input.test.tsx +134 -0
- package/src/components/file-input/file-input.tsx +224 -0
- package/src/components/file-input/file-input.types.ts +78 -0
- package/src/components/file-input/file-input.utils.test.ts +36 -0
- package/src/components/file-input/file-input.utils.ts +130 -0
- package/src/components/file-input/index.ts +2 -0
- package/src/components/for/for.test.tsx +66 -0
- package/src/components/for/for.tsx +53 -0
- package/src/components/for/for.types.ts +40 -0
- package/src/components/for/index.ts +2 -0
- package/src/components/index.ts +6 -0
- package/src/components/menu/menu-dropdown/menu-dropdown.tsx +220 -220
- package/src/components/menu/menu-sub-dropdown/menu-sub-dropdown.tsx +221 -221
- package/src/components/modal/index.ts +4 -1
- package/src/components/modal/modal-body/index.ts +1 -0
- package/src/components/modal/modal-body/modal-body.tsx +29 -0
- package/src/components/modal/modal-content/modal-content.tsx +71 -24
- package/src/components/modal/modal-header/modal-header.tsx +2 -2
- package/src/components/modal/modal-overlay/modal-overlay.tsx +46 -45
- package/src/components/modal/modal-root/modal-root.tsx +22 -17
- package/src/components/modal/modal.context.ts +5 -2
- package/src/components/modal/modal.test.tsx +234 -64
- package/src/components/modal/modal.tsx +36 -4
- package/src/components/modal/modal.types.ts +49 -8
- package/src/components/modal/use-modal.ts +44 -51
- package/src/components/password-input/index.ts +2 -0
- package/src/components/password-input/password-input.test.tsx +72 -0
- package/src/components/password-input/password-input.tsx +85 -0
- package/src/components/password-input/password-input.types.ts +30 -0
- package/src/components/pin-input/index.ts +2 -0
- package/src/components/pin-input/pin-input.test.tsx +149 -0
- package/src/components/pin-input/pin-input.tsx +473 -0
- package/src/components/pin-input/pin-input.types.ts +78 -0
- package/src/components/scroll-area/index.ts +6 -0
- package/src/components/scroll-area/scroll-area.test.tsx +72 -0
- package/src/components/scroll-area/scroll-area.tsx +70 -0
- package/src/components/scroll-area/scroll-area.types.ts +37 -0
- package/src/components/segmented-control/segmented-control.styles.ts +13 -13
- package/src/components/segmented-control/segmented-control.test.tsx +18 -0
- package/src/components/segmented-control/segmented-control.tsx +11 -1
- package/src/components/segmented-control/segmented-control.types.ts +3 -0
- package/src/components/select/select-dropdown/select-dropdown.tsx +299 -299
- package/src/components/select/select-root/select-root.tsx +333 -333
- package/src/components/select/select-trigger/select-trigger.tsx +123 -123
- package/src/components/select/select.context.ts +140 -140
- package/src/components/select/select.test.tsx +190 -190
- package/src/components/select/select.types.ts +272 -272
- package/src/components/select/use-select.ts +170 -170
- package/src/icons/eye-off.tsx +30 -0
- package/src/icons/eye.tsx +24 -0
- package/src/icons/index.ts +2 -0
- package/src/style.css +14 -8
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -10,19 +10,23 @@ import { ModalRoot } from "./modal-root";
|
|
|
10
10
|
import { ModalOverlay } from "./modal-overlay";
|
|
11
11
|
import { ModalContent } from "./modal-content";
|
|
12
12
|
import { ModalHeader } from "./modal-header";
|
|
13
|
+
import { ModalBody } from "./modal-body";
|
|
13
14
|
import { ModalClose } from "./modal-close";
|
|
14
15
|
|
|
16
|
+
export type ModalSize = "xs" | "sm" | "md" | "lg" | "xl" | "full";
|
|
17
|
+
|
|
15
18
|
export type ModalClassNames = {
|
|
16
19
|
root?: string;
|
|
17
20
|
overlay?: string;
|
|
18
21
|
content?: string;
|
|
19
22
|
header?: string;
|
|
23
|
+
body?: string;
|
|
20
24
|
close?: string;
|
|
21
25
|
};
|
|
22
26
|
|
|
23
|
-
export interface
|
|
27
|
+
export interface ModalRootProps extends ComponentPropsWithoutRef<"div"> {
|
|
24
28
|
/** Children containing modal subcomponents */
|
|
25
|
-
children
|
|
29
|
+
children?: ReactNode;
|
|
26
30
|
|
|
27
31
|
/** State of the modal (controlled) */
|
|
28
32
|
opened?: boolean;
|
|
@@ -48,6 +52,18 @@ export interface ModalProps extends ComponentPropsWithoutRef<"div"> {
|
|
|
48
52
|
/** Radius for modal content @default `md` */
|
|
49
53
|
radius?: RefraktorRadius;
|
|
50
54
|
|
|
55
|
+
/** Modal content width @default `md` */
|
|
56
|
+
size?: ModalSize;
|
|
57
|
+
|
|
58
|
+
/** Whether to center modal vertically @default `true` */
|
|
59
|
+
centered?: boolean;
|
|
60
|
+
|
|
61
|
+
/** Whether to trap focus within the modal @default `true` */
|
|
62
|
+
trapFocus?: boolean;
|
|
63
|
+
|
|
64
|
+
/** Whether to return focus to trigger after close @default `true` */
|
|
65
|
+
returnFocus?: boolean;
|
|
66
|
+
|
|
51
67
|
/** Transition props for overlay/content, uses Transition internally */
|
|
52
68
|
transitionProps?: Omit<TransitionProps, "children" | "mounted">;
|
|
53
69
|
|
|
@@ -58,7 +74,19 @@ export interface ModalProps extends ComponentPropsWithoutRef<"div"> {
|
|
|
58
74
|
classNames?: ModalClassNames;
|
|
59
75
|
}
|
|
60
76
|
|
|
61
|
-
export
|
|
77
|
+
export interface ModalProps extends Omit<ModalRootProps, "title"> {
|
|
78
|
+
/** Title text rendered in the header */
|
|
79
|
+
title?: ReactNode;
|
|
80
|
+
|
|
81
|
+
/** Whether to render the overlay @default `true` */
|
|
82
|
+
withOverlay?: boolean;
|
|
83
|
+
|
|
84
|
+
/** Whether to show the close button in the header @default `true` */
|
|
85
|
+
withCloseButton?: boolean;
|
|
86
|
+
|
|
87
|
+
/** Props passed to the Overlay subcomponent */
|
|
88
|
+
overlayProps?: ModalOverlayProps;
|
|
89
|
+
}
|
|
62
90
|
|
|
63
91
|
export interface ModalOverlayProps extends ComponentPropsWithoutRef<"div"> {
|
|
64
92
|
/** Whether clicking the overlay closes modal @default `true` */
|
|
@@ -86,9 +114,6 @@ export interface ModalHeaderProps extends ComponentPropsWithoutRef<"div"> {
|
|
|
86
114
|
/** Header content */
|
|
87
115
|
children?: ReactNode;
|
|
88
116
|
|
|
89
|
-
/** Shorthand header text */
|
|
90
|
-
text?: ReactNode;
|
|
91
|
-
|
|
92
117
|
/** Whether to show close button inside header @default `true` */
|
|
93
118
|
withClose?: boolean;
|
|
94
119
|
|
|
@@ -96,8 +121,18 @@ export interface ModalHeaderProps extends ComponentPropsWithoutRef<"div"> {
|
|
|
96
121
|
className?: string;
|
|
97
122
|
}
|
|
98
123
|
|
|
99
|
-
export interface
|
|
100
|
-
|
|
124
|
+
export interface ModalBodyProps extends ComponentPropsWithoutRef<"div"> {
|
|
125
|
+
/** Body content */
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
|
|
128
|
+
/** Used for editing root class name */
|
|
129
|
+
className?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface ModalCloseProps extends Omit<
|
|
133
|
+
ComponentPropsWithoutRef<"button">,
|
|
134
|
+
"onClick"
|
|
135
|
+
> {
|
|
101
136
|
/** Optional close content (defaults to `x`) */
|
|
102
137
|
children?: ReactNode;
|
|
103
138
|
|
|
@@ -118,6 +153,7 @@ export interface ModalFactoryPayload extends FactoryPayload {
|
|
|
118
153
|
Overlay: typeof ModalOverlay;
|
|
119
154
|
Content: typeof ModalContent;
|
|
120
155
|
Header: typeof ModalHeader;
|
|
156
|
+
Body: typeof ModalBody;
|
|
121
157
|
Close: typeof ModalClose;
|
|
122
158
|
};
|
|
123
159
|
}
|
|
@@ -142,6 +178,11 @@ export interface ModalHeaderFactoryPayload extends FactoryPayload {
|
|
|
142
178
|
ref: HTMLDivElement;
|
|
143
179
|
}
|
|
144
180
|
|
|
181
|
+
export interface ModalBodyFactoryPayload extends FactoryPayload {
|
|
182
|
+
props: ModalBodyProps;
|
|
183
|
+
ref: HTMLDivElement;
|
|
184
|
+
}
|
|
185
|
+
|
|
145
186
|
export interface ModalCloseFactoryPayload extends FactoryPayload {
|
|
146
187
|
props: ModalCloseProps;
|
|
147
188
|
ref: HTMLButtonElement;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { useUncontrolled } from "@refraktor/utils";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
FloatingContext,
|
|
4
|
+
useDismiss,
|
|
5
|
+
useFloating,
|
|
6
|
+
useInteractions,
|
|
7
|
+
useRole
|
|
8
|
+
} from "@floating-ui/react";
|
|
9
|
+
import { useCallback } from "react";
|
|
3
10
|
|
|
4
11
|
interface UseModalProps {
|
|
5
12
|
opened?: boolean;
|
|
@@ -7,7 +14,6 @@ interface UseModalProps {
|
|
|
7
14
|
onOpenedChange?: (opened: boolean) => void;
|
|
8
15
|
closeOnClickOutside?: boolean;
|
|
9
16
|
closeOnEscape?: boolean;
|
|
10
|
-
contentRef: React.MutableRefObject<HTMLElement | null>;
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export interface UseModalReturn {
|
|
@@ -15,6 +21,15 @@ export interface UseModalReturn {
|
|
|
15
21
|
open: () => void;
|
|
16
22
|
close: () => void;
|
|
17
23
|
toggle: () => void;
|
|
24
|
+
context: FloatingContext;
|
|
25
|
+
refs: {
|
|
26
|
+
setReference: (node: HTMLElement | null) => void;
|
|
27
|
+
setFloating: (node: HTMLElement | null) => void;
|
|
28
|
+
floating: React.MutableRefObject<HTMLElement | null>;
|
|
29
|
+
};
|
|
30
|
+
getFloatingProps: (
|
|
31
|
+
userProps?: React.HTMLAttributes<HTMLElement>
|
|
32
|
+
) => Record<string, unknown>;
|
|
18
33
|
}
|
|
19
34
|
|
|
20
35
|
export function useModal(options: UseModalProps): UseModalReturn {
|
|
@@ -23,8 +38,7 @@ export function useModal(options: UseModalProps): UseModalReturn {
|
|
|
23
38
|
defaultOpened,
|
|
24
39
|
onOpenedChange,
|
|
25
40
|
closeOnClickOutside = true,
|
|
26
|
-
closeOnEscape = true
|
|
27
|
-
contentRef
|
|
41
|
+
closeOnEscape = true
|
|
28
42
|
} = options;
|
|
29
43
|
|
|
30
44
|
const [isOpen, setIsOpen] = useUncontrolled({
|
|
@@ -34,6 +48,23 @@ export function useModal(options: UseModalProps): UseModalReturn {
|
|
|
34
48
|
onChange: onOpenedChange
|
|
35
49
|
});
|
|
36
50
|
|
|
51
|
+
const floating = useFloating({
|
|
52
|
+
open: isOpen,
|
|
53
|
+
onOpenChange: setIsOpen
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const dismiss = useDismiss(floating.context, {
|
|
57
|
+
outsidePress: closeOnClickOutside,
|
|
58
|
+
outsidePressEvent: "mousedown",
|
|
59
|
+
escapeKey: closeOnEscape
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const role = useRole(floating.context, {
|
|
63
|
+
role: "dialog"
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const { getFloatingProps } = useInteractions([dismiss, role]);
|
|
67
|
+
|
|
37
68
|
const open = useCallback(() => {
|
|
38
69
|
setIsOpen(true);
|
|
39
70
|
}, [setIsOpen]);
|
|
@@ -46,56 +77,18 @@ export function useModal(options: UseModalProps): UseModalReturn {
|
|
|
46
77
|
setIsOpen(!isOpen);
|
|
47
78
|
}, [isOpen, setIsOpen]);
|
|
48
79
|
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (!isOpen || !closeOnEscape) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
55
|
-
if (event.key === "Escape") {
|
|
56
|
-
setIsOpen(false);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
61
|
-
|
|
62
|
-
return () => {
|
|
63
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
64
|
-
};
|
|
65
|
-
}, [closeOnEscape, isOpen, setIsOpen]);
|
|
66
|
-
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
if (!isOpen || !closeOnClickOutside) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const handlePointerDown = (event: MouseEvent | TouchEvent) => {
|
|
73
|
-
const target = event.target;
|
|
74
|
-
|
|
75
|
-
if (!(target instanceof Node)) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (contentRef.current?.contains(target)) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
setIsOpen(false);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
document.addEventListener("mousedown", handlePointerDown);
|
|
87
|
-
document.addEventListener("touchstart", handlePointerDown);
|
|
88
|
-
|
|
89
|
-
return () => {
|
|
90
|
-
document.removeEventListener("mousedown", handlePointerDown);
|
|
91
|
-
document.removeEventListener("touchstart", handlePointerDown);
|
|
92
|
-
};
|
|
93
|
-
}, [closeOnClickOutside, contentRef, isOpen, setIsOpen]);
|
|
94
|
-
|
|
95
80
|
return {
|
|
96
81
|
opened: isOpen,
|
|
97
82
|
open,
|
|
98
83
|
close,
|
|
99
|
-
toggle
|
|
84
|
+
toggle,
|
|
85
|
+
context: floating.context,
|
|
86
|
+
refs: {
|
|
87
|
+
setReference: floating.refs.setReference,
|
|
88
|
+
setFloating: floating.refs.setFloating,
|
|
89
|
+
floating: floating.refs
|
|
90
|
+
.floating as React.MutableRefObject<HTMLElement | null>
|
|
91
|
+
},
|
|
92
|
+
getFloatingProps
|
|
100
93
|
};
|
|
101
94
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render, screen, userEvent } from "../../vitest";
|
|
3
|
+
import PasswordInput from "./password-input";
|
|
4
|
+
|
|
5
|
+
describe("@refraktor/core/PasswordInput", () => {
|
|
6
|
+
it("supports input wrapper props", async () => {
|
|
7
|
+
await render(
|
|
8
|
+
<PasswordInput
|
|
9
|
+
label="Password"
|
|
10
|
+
description="Use at least 8 characters"
|
|
11
|
+
error="Password is required"
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const input = screen.getByLabelText("Password");
|
|
16
|
+
|
|
17
|
+
expect(input).toHaveAttribute("type", "password");
|
|
18
|
+
expect(input).toHaveAttribute("aria-invalid", "true");
|
|
19
|
+
expect(screen.getByText("Use at least 8 characters")).toBeInTheDocument();
|
|
20
|
+
expect(screen.getByText("Password is required")).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("toggles password visibility", async () => {
|
|
24
|
+
const user = userEvent.setup();
|
|
25
|
+
|
|
26
|
+
await render(<PasswordInput label="Password" />);
|
|
27
|
+
|
|
28
|
+
const input = screen.getByLabelText("Password");
|
|
29
|
+
const showButton = screen.getByRole("button", {
|
|
30
|
+
name: "Show password"
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(input).toHaveAttribute("type", "password");
|
|
34
|
+
await user.click(showButton);
|
|
35
|
+
expect(input).toHaveAttribute("type", "text");
|
|
36
|
+
|
|
37
|
+
const hideButton = screen.getByRole("button", {
|
|
38
|
+
name: "Hide password"
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await user.click(hideButton);
|
|
42
|
+
expect(input).toHaveAttribute("type", "password");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("respects disabled state", async () => {
|
|
46
|
+
const user = userEvent.setup();
|
|
47
|
+
|
|
48
|
+
await render(<PasswordInput label="Password" disabled />);
|
|
49
|
+
|
|
50
|
+
const input = screen.getByLabelText("Password");
|
|
51
|
+
const toggle = screen.getByRole("button", {
|
|
52
|
+
name: "Show password"
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(toggle).toBeDisabled();
|
|
56
|
+
await user.click(toggle);
|
|
57
|
+
expect(input).toHaveAttribute("type", "password");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("allows disabling visibility toggle", async () => {
|
|
61
|
+
await render(
|
|
62
|
+
<PasswordInput label="Password" withVisibilityToggle={false} />
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const input = screen.getByLabelText("Password");
|
|
66
|
+
|
|
67
|
+
expect(input).toHaveAttribute("type", "password");
|
|
68
|
+
expect(
|
|
69
|
+
screen.queryByRole("button", { name: "Show password" })
|
|
70
|
+
).not.toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useUncontrolled } from "@refraktor/utils";
|
|
2
|
+
import { EyeIcon, EyeOffIcon } from "../../icons";
|
|
3
|
+
import { useTheme } from "../../theme";
|
|
4
|
+
import { createComponentConfig, factory, useProps } from "../../utils";
|
|
5
|
+
import { Input } from "../input";
|
|
6
|
+
import {
|
|
7
|
+
PasswordInputFactoryPayload,
|
|
8
|
+
PasswordInputProps
|
|
9
|
+
} from "./password-input.types";
|
|
10
|
+
|
|
11
|
+
const defaultProps = {
|
|
12
|
+
withVisibilityToggle: true,
|
|
13
|
+
showPasswordLabel: "Show password",
|
|
14
|
+
hidePasswordLabel: "Hide password"
|
|
15
|
+
} satisfies Partial<PasswordInputProps>;
|
|
16
|
+
|
|
17
|
+
const PasswordInput = factory<PasswordInputFactoryPayload>((_props, ref) => {
|
|
18
|
+
const { cx } = useTheme();
|
|
19
|
+
const {
|
|
20
|
+
visible,
|
|
21
|
+
defaultVisible,
|
|
22
|
+
onVisibilityChange,
|
|
23
|
+
withVisibilityToggle,
|
|
24
|
+
showPasswordLabel,
|
|
25
|
+
hidePasswordLabel,
|
|
26
|
+
rightSection,
|
|
27
|
+
disabled,
|
|
28
|
+
...props
|
|
29
|
+
} = useProps("PasswordInput", defaultProps, _props);
|
|
30
|
+
|
|
31
|
+
const [isVisible, setVisible] = useUncontrolled({
|
|
32
|
+
value: visible,
|
|
33
|
+
defaultValue: defaultVisible,
|
|
34
|
+
finalValue: false,
|
|
35
|
+
onChange: onVisibilityChange
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const Icon = isVisible ? EyeOffIcon : EyeIcon;
|
|
39
|
+
const toggleLabel = isVisible ? hidePasswordLabel : showPasswordLabel;
|
|
40
|
+
|
|
41
|
+
const visibilityToggle = (
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
aria-label={toggleLabel}
|
|
46
|
+
aria-pressed={isVisible}
|
|
47
|
+
className={cx(
|
|
48
|
+
"inline-flex cursor-pointer items-center justify-center border-0 bg-transparent p-0 text-[var(--refraktor-text-secondary)] transition-colors hover:text-[var(--refraktor-text)]",
|
|
49
|
+
disabled && "cursor-not-allowed opacity-50"
|
|
50
|
+
)}
|
|
51
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
52
|
+
onClick={() => setVisible(!isVisible)}
|
|
53
|
+
>
|
|
54
|
+
<Icon size={16} />
|
|
55
|
+
</button>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
let resolvedRightSection = rightSection;
|
|
59
|
+
|
|
60
|
+
if (withVisibilityToggle) {
|
|
61
|
+
resolvedRightSection = rightSection ? (
|
|
62
|
+
<span className="inline-flex items-center gap-1">
|
|
63
|
+
{rightSection}
|
|
64
|
+
{visibilityToggle}
|
|
65
|
+
</span>
|
|
66
|
+
) : (
|
|
67
|
+
visibilityToggle
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Input
|
|
73
|
+
{...props}
|
|
74
|
+
ref={ref}
|
|
75
|
+
type={isVisible ? "text" : "password"}
|
|
76
|
+
disabled={disabled}
|
|
77
|
+
rightSection={resolvedRightSection}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
PasswordInput.displayName = "@refraktor/core/PasswordInput";
|
|
83
|
+
PasswordInput.configure = createComponentConfig<PasswordInputProps>();
|
|
84
|
+
|
|
85
|
+
export default PasswordInput;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createComponentConfig, FactoryPayload } from "../../utils";
|
|
2
|
+
import { InputProps } from "../input";
|
|
3
|
+
|
|
4
|
+
export interface PasswordInputProps extends Omit<InputProps, "type"> {
|
|
5
|
+
/** Whether to render the visibility toggle icon @default `true` */
|
|
6
|
+
withVisibilityToggle?: boolean;
|
|
7
|
+
|
|
8
|
+
/** Controls visibility state */
|
|
9
|
+
visible?: boolean;
|
|
10
|
+
|
|
11
|
+
/** Initial visibility state */
|
|
12
|
+
defaultVisible?: boolean;
|
|
13
|
+
|
|
14
|
+
/** Callback called when visibility changes */
|
|
15
|
+
onVisibilityChange?: (visible: boolean) => void;
|
|
16
|
+
|
|
17
|
+
/** Accessible label for showing the password @default `Show password` */
|
|
18
|
+
showPasswordLabel?: string;
|
|
19
|
+
|
|
20
|
+
/** Accessible label for hiding the password @default `Hide password` */
|
|
21
|
+
hidePasswordLabel?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PasswordInputFactoryPayload extends FactoryPayload {
|
|
25
|
+
props: PasswordInputProps;
|
|
26
|
+
ref: HTMLInputElement;
|
|
27
|
+
compound: {
|
|
28
|
+
configure: ReturnType<typeof createComponentConfig<PasswordInputProps>>;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { render, screen, userEvent } from "../../vitest";
|
|
4
|
+
import PinInput from "./pin-input";
|
|
5
|
+
|
|
6
|
+
const getCells = (length: number) =>
|
|
7
|
+
Array.from({ length }, (_, index) =>
|
|
8
|
+
screen.getByLabelText(`Character ${index + 1} of ${length}`)
|
|
9
|
+
) as HTMLInputElement[];
|
|
10
|
+
|
|
11
|
+
describe("@refraktor/core/PinInput", () => {
|
|
12
|
+
it("supports input wrapper props", async () => {
|
|
13
|
+
await render(
|
|
14
|
+
<PinInput
|
|
15
|
+
label="Verification code"
|
|
16
|
+
description="Enter the code from your authenticator app"
|
|
17
|
+
error="Code is required"
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const firstCell = screen.getByLabelText("Verification code");
|
|
22
|
+
|
|
23
|
+
expect(firstCell).toHaveAttribute("aria-invalid", "true");
|
|
24
|
+
expect(
|
|
25
|
+
screen.getByText("Enter the code from your authenticator app")
|
|
26
|
+
).toBeInTheDocument();
|
|
27
|
+
expect(screen.getByText("Code is required")).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("auto-advances focus and fires onComplete", async () => {
|
|
31
|
+
const user = userEvent.setup();
|
|
32
|
+
const onComplete = vi.fn();
|
|
33
|
+
|
|
34
|
+
await render(<PinInput length={4} onComplete={onComplete} />);
|
|
35
|
+
|
|
36
|
+
const cells = getCells(4);
|
|
37
|
+
|
|
38
|
+
await user.type(cells[0], "1");
|
|
39
|
+
expect(cells[1]).toHaveFocus();
|
|
40
|
+
|
|
41
|
+
await user.type(cells[1], "2");
|
|
42
|
+
await user.type(cells[2], "3");
|
|
43
|
+
await user.type(cells[3], "4");
|
|
44
|
+
|
|
45
|
+
expect(onComplete).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(onComplete).toHaveBeenCalledWith("1234");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("handles backspace navigation", async () => {
|
|
50
|
+
const user = userEvent.setup();
|
|
51
|
+
|
|
52
|
+
await render(<PinInput length={4} />);
|
|
53
|
+
|
|
54
|
+
const cells = getCells(4);
|
|
55
|
+
|
|
56
|
+
await user.type(cells[0], "1");
|
|
57
|
+
await user.type(cells[1], "2");
|
|
58
|
+
expect(cells[2]).toHaveFocus();
|
|
59
|
+
|
|
60
|
+
await user.keyboard("{Backspace}");
|
|
61
|
+
|
|
62
|
+
expect(cells[1]).toHaveFocus();
|
|
63
|
+
expect(cells[1]).toHaveValue("");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("handles paste across cells", async () => {
|
|
67
|
+
const user = userEvent.setup();
|
|
68
|
+
|
|
69
|
+
await render(<PinInput length={6} characterSet="alphanumeric" />);
|
|
70
|
+
|
|
71
|
+
const cells = getCells(6);
|
|
72
|
+
|
|
73
|
+
cells[0].focus();
|
|
74
|
+
await user.paste("A1B2C3");
|
|
75
|
+
|
|
76
|
+
expect(cells.map((cell) => cell.value)).toEqual([
|
|
77
|
+
"A",
|
|
78
|
+
"1",
|
|
79
|
+
"B",
|
|
80
|
+
"2",
|
|
81
|
+
"C",
|
|
82
|
+
"3"
|
|
83
|
+
]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("supports alphanumeric and custom filtering props", async () => {
|
|
87
|
+
const user = userEvent.setup();
|
|
88
|
+
|
|
89
|
+
await render(
|
|
90
|
+
<PinInput
|
|
91
|
+
length={4}
|
|
92
|
+
characterPattern={/[A-F0-9]/}
|
|
93
|
+
transform="uppercase"
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const cells = getCells(4);
|
|
98
|
+
|
|
99
|
+
await user.type(cells[0], "a");
|
|
100
|
+
await user.type(cells[1], "g");
|
|
101
|
+
await user.type(cells[1], "9");
|
|
102
|
+
|
|
103
|
+
expect(cells[0]).toHaveValue("A");
|
|
104
|
+
expect(cells[1]).toHaveValue("9");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("supports controlled usage", async () => {
|
|
108
|
+
const user = userEvent.setup();
|
|
109
|
+
|
|
110
|
+
function Demo() {
|
|
111
|
+
const [value, setValue] = useState("");
|
|
112
|
+
return <PinInput length={4} value={value} onChange={setValue} />;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await render(<Demo />);
|
|
116
|
+
|
|
117
|
+
const cells = getCells(4);
|
|
118
|
+
await user.type(cells[0], "1");
|
|
119
|
+
await user.type(cells[1], "2");
|
|
120
|
+
|
|
121
|
+
expect(cells[0]).toHaveValue("1");
|
|
122
|
+
expect(cells[1]).toHaveValue("2");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("supports masking and hidden form value", async () => {
|
|
126
|
+
await render(
|
|
127
|
+
<PinInput
|
|
128
|
+
length={4}
|
|
129
|
+
defaultValue="A1B2"
|
|
130
|
+
characterSet="alphanumeric"
|
|
131
|
+
mask
|
|
132
|
+
name="otp"
|
|
133
|
+
/>
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const cells = getCells(4);
|
|
137
|
+
|
|
138
|
+
for (const cell of cells) {
|
|
139
|
+
expect(cell).toHaveAttribute("type", "password");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const hidden = document.querySelector(
|
|
143
|
+
'input[type="hidden"][name="otp"]'
|
|
144
|
+
) as HTMLInputElement | null;
|
|
145
|
+
|
|
146
|
+
expect(hidden).not.toBeNull();
|
|
147
|
+
expect(hidden?.value).toBe("A1B2");
|
|
148
|
+
});
|
|
149
|
+
});
|