@rovula/ui 0.1.7 → 0.1.9
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/cjs/bundle.css +273 -126
- package/dist/cjs/bundle.js +1545 -1545
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/AlertDialog/AlertDialog.stories.d.ts +3 -0
- package/dist/cjs/types/components/Dialog/Dialog.d.ts +7 -1
- package/dist/cjs/types/components/Dialog/Dialog.stories.d.ts +3 -0
- package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +2 -0
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
- package/dist/cjs/types/components/Form/Field.d.ts +26 -0
- package/dist/cjs/types/components/Form/FieldMessage.d.ts +7 -0
- package/dist/cjs/types/components/Form/Form.d.ts +49 -11
- package/dist/cjs/types/components/Form/Form.stories.d.ts +23 -0
- package/dist/cjs/types/components/Form/ValidationHintList.d.ts +20 -0
- package/dist/cjs/types/components/Form/ValidationHintList.stories.d.ts +9 -0
- package/dist/cjs/types/components/Form/index.d.ts +10 -0
- package/dist/cjs/types/components/Form/useOptionBridge.d.ts +17 -0
- package/dist/cjs/types/components/OtpInput/OtpInput.d.ts +17 -0
- package/dist/cjs/types/components/OtpInput/OtpInput.stories.d.ts +15 -0
- package/dist/cjs/types/components/OtpInput/OtpInputGroup.d.ts +25 -0
- package/dist/cjs/types/components/OtpInput/index.d.ts +5 -0
- package/dist/cjs/types/components/TextInput/TextInput.styles.d.ts +3 -0
- package/dist/cjs/types/index.d.ts +5 -0
- package/dist/cjs/types/theme/ThemeColorCoverageRuntime.stories.d.ts +10 -0
- package/dist/cjs/types/utils/colors.d.ts +84 -0
- package/dist/components/ActionButton/ActionButton.stories.js +2 -2
- package/dist/components/ActionButton/ActionButton.styles.js +1 -1
- package/dist/components/AlertDialog/AlertDialog.js +6 -6
- package/dist/components/AlertDialog/AlertDialog.stories.js +3 -0
- package/dist/components/Avatar/Avatar.stories.js +1 -1
- package/dist/components/Avatar/Avatar.styles.js +1 -1
- package/dist/components/Avatar/AvatarBase.js +1 -1
- package/dist/components/Avatar/AvatarGroup.stories.js +1 -1
- package/dist/components/Button/Buttons.stories.js +2 -2
- package/dist/components/Calendar/Calendar.js +1 -1
- package/dist/components/Checkbox/Checkbox.js +1 -1
- package/dist/components/Checkbox/Checkbox.stories.js +17 -7
- package/dist/components/Collapsible/Collapsible.styles.js +1 -1
- package/dist/components/DataTable/DataTable.js +2 -2
- package/dist/components/Dialog/Dialog.js +12 -7
- package/dist/components/Dialog/Dialog.stories.js +90 -2
- package/dist/components/Dropdown/Dropdown.js +2 -2
- package/dist/components/DropdownMenu/DropdownMenu.js +3 -3
- package/dist/components/FocusedScrollView/FocusedScrollView.stories.js +6 -6
- package/dist/components/Form/Field.js +60 -0
- package/dist/components/Form/FieldMessage.js +24 -0
- package/dist/components/Form/Form.js +73 -41
- package/dist/components/Form/Form.stories.js +221 -0
- package/dist/components/Form/ValidationHintList.js +30 -0
- package/dist/components/Form/ValidationHintList.stories.js +50 -0
- package/dist/components/Form/index.js +5 -0
- package/dist/components/Form/useOptionBridge.js +27 -0
- package/dist/components/InputFilter/InputFilter.js +5 -4
- package/dist/components/InputFilter/InputFilter.stories.js +1 -1
- package/dist/components/InputFilter/InputFilter.styles.js +14 -1
- package/dist/components/Label/Label.styles.js +1 -1
- package/dist/components/Menu/Menu.js +2 -2
- package/dist/components/NumberInput/NumberInput.stories.js +1 -1
- package/dist/components/OtpInput/OtpInput.js +118 -0
- package/dist/components/OtpInput/OtpInput.stories.js +60 -0
- package/dist/components/OtpInput/OtpInputGroup.js +23 -0
- package/dist/components/OtpInput/index.js +3 -0
- package/dist/components/PasswordInput/PasswordInput.stories.js +1 -1
- package/dist/components/Popover/Popover.js +1 -1
- package/dist/components/RadioGroup/RadioGroup.js +1 -1
- package/dist/components/RadioGroup/RadioGroup.stories.js +2 -2
- package/dist/components/Search/Search.js +13 -1
- package/dist/components/Search/Search.stories.js +1 -1
- package/dist/components/Slider/Slider.js +1 -1
- package/dist/components/Slider/Slider.stories.js +5 -5
- package/dist/components/Switch/Switch.stories.js +2 -2
- package/dist/components/Table/Table.js +5 -5
- package/dist/components/Tabs/Tabs.js +12 -9
- package/dist/components/Tabs/Tabs.stories.js +1 -1
- package/dist/components/Text/Text.js +1 -1
- package/dist/components/Text/Text.stories.js +1 -1
- package/dist/components/TextArea/TextArea.stories.js +1 -1
- package/dist/components/TextArea/TextArea.styles.js +3 -3
- package/dist/components/TextInput/TextInput.js +3 -2
- package/dist/components/TextInput/TextInput.stories.js +3 -3
- package/dist/components/TextInput/TextInput.styles.js +41 -19
- package/dist/components/Toast/Toast.js +4 -2
- package/dist/components/Toast/Toast.stories.js +1 -1
- package/dist/components/Toast/Toast.styles.js +4 -4
- package/dist/components/Toast/Toaster.js +2 -2
- package/dist/components/Tree/Tree.stories.js +1 -1
- package/dist/components/Tree/TreeItem.js +1 -1
- package/dist/esm/bundle.css +273 -126
- package/dist/esm/bundle.js +1545 -1545
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/AlertDialog/AlertDialog.stories.d.ts +3 -0
- package/dist/esm/types/components/Dialog/Dialog.d.ts +7 -1
- package/dist/esm/types/components/Dialog/Dialog.stories.d.ts +3 -0
- package/dist/esm/types/components/Dropdown/Dropdown.d.ts +2 -0
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
- package/dist/esm/types/components/Form/Field.d.ts +26 -0
- package/dist/esm/types/components/Form/FieldMessage.d.ts +7 -0
- package/dist/esm/types/components/Form/Form.d.ts +49 -11
- package/dist/esm/types/components/Form/Form.stories.d.ts +23 -0
- package/dist/esm/types/components/Form/ValidationHintList.d.ts +20 -0
- package/dist/esm/types/components/Form/ValidationHintList.stories.d.ts +9 -0
- package/dist/esm/types/components/Form/index.d.ts +10 -0
- package/dist/esm/types/components/Form/useOptionBridge.d.ts +17 -0
- package/dist/esm/types/components/OtpInput/OtpInput.d.ts +17 -0
- package/dist/esm/types/components/OtpInput/OtpInput.stories.d.ts +15 -0
- package/dist/esm/types/components/OtpInput/OtpInputGroup.d.ts +25 -0
- package/dist/esm/types/components/OtpInput/index.d.ts +5 -0
- package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +3 -0
- package/dist/esm/types/index.d.ts +5 -0
- package/dist/esm/types/theme/ThemeColorCoverageRuntime.stories.d.ts +10 -0
- package/dist/esm/types/utils/colors.d.ts +84 -0
- package/dist/index.d.ts +248 -2
- package/dist/index.js +3 -0
- package/dist/src/theme/global.css +340 -151
- package/dist/theme/ThemeColorCoverageRuntime.stories.js +91 -0
- package/dist/utils/colors.js +92 -0
- package/package.json +4 -2
- package/src/components/ActionButton/ActionButton.stories.tsx +6 -6
- package/src/components/ActionButton/ActionButton.styles.ts +1 -1
- package/src/components/AlertDialog/AlertDialog.stories.tsx +22 -0
- package/src/components/AlertDialog/AlertDialog.tsx +6 -6
- package/src/components/Avatar/Avatar.stories.tsx +1 -1
- package/src/components/Avatar/Avatar.styles.ts +1 -1
- package/src/components/Avatar/AvatarBase.tsx +1 -1
- package/src/components/Avatar/AvatarGroup.stories.tsx +1 -1
- package/src/components/Button/Buttons.stories.tsx +10 -10
- package/src/components/Calendar/Calendar.tsx +3 -3
- package/src/components/Checkbox/Checkbox.stories.tsx +35 -12
- package/src/components/Checkbox/Checkbox.tsx +7 -5
- package/src/components/Collapsible/Collapsible.styles.ts +1 -1
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/Dialog/Dialog.stories.tsx +173 -0
- package/src/components/Dialog/Dialog.tsx +32 -15
- package/src/components/Dropdown/Dropdown.styles.ts +1 -1
- package/src/components/Dropdown/Dropdown.tsx +16 -14
- package/src/components/DropdownMenu/DropdownMenu.tsx +3 -3
- package/src/components/FocusedScrollView/FocusedScrollView.stories.tsx +10 -10
- package/src/components/Form/Field.tsx +160 -0
- package/src/components/Form/FieldMessage.tsx +38 -0
- package/src/components/Form/Form.docs.mdx +67 -0
- package/src/components/Form/Form.stories.tsx +490 -0
- package/src/components/Form/Form.tsx +185 -87
- package/src/components/Form/README.md +284 -0
- package/src/components/Form/ValidationHintList.stories.tsx +118 -0
- package/src/components/Form/ValidationHintList.tsx +95 -0
- package/src/components/Form/index.ts +28 -0
- package/src/components/Form/useOptionBridge.ts +55 -0
- package/src/components/InputFilter/InputFilter.stories.tsx +1 -1
- package/src/components/InputFilter/InputFilter.styles.ts +14 -1
- package/src/components/InputFilter/InputFilter.tsx +33 -28
- package/src/components/Label/Label.styles.ts +2 -2
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/Menu/Menu.tsx +12 -12
- package/src/components/NumberInput/NumberInput.stories.tsx +1 -1
- package/src/components/OtpInput/OtpInput.stories.tsx +168 -0
- package/src/components/OtpInput/OtpInput.tsx +223 -0
- package/src/components/OtpInput/OtpInputGroup.tsx +74 -0
- package/src/components/OtpInput/index.ts +5 -0
- package/src/components/PasswordInput/PasswordInput.stories.tsx +1 -1
- package/src/components/Popover/Popover.tsx +1 -1
- package/src/components/RadioGroup/RadioGroup.stories.tsx +4 -4
- package/src/components/RadioGroup/RadioGroup.tsx +2 -1
- package/src/components/Search/Search.stories.tsx +1 -1
- package/src/components/Search/Search.tsx +6 -2
- package/src/components/Slider/Slider.stories.tsx +7 -7
- package/src/components/Slider/Slider.tsx +1 -1
- package/src/components/Switch/Switch.stories.tsx +4 -4
- package/src/components/Table/Table.tsx +5 -5
- package/src/components/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +29 -18
- package/src/components/Text/Text.stories.tsx +1 -1
- package/src/components/Text/Text.tsx +1 -1
- package/src/components/TextArea/TextArea.stories.tsx +1 -1
- package/src/components/TextArea/TextArea.styles.ts +3 -3
- package/src/components/TextInput/TextInput.stories.tsx +7 -7
- package/src/components/TextInput/TextInput.styles.ts +42 -19
- package/src/components/TextInput/TextInput.tsx +3 -1
- package/src/components/Toast/Toast.stories.tsx +1 -1
- package/src/components/Toast/Toast.styles.tsx +7 -7
- package/src/components/Toast/Toast.tsx +5 -4
- package/src/components/Toast/Toaster.tsx +17 -20
- package/src/components/Tree/Tree.stories.tsx +1 -1
- package/src/components/Tree/TreeItem.tsx +1 -1
- package/src/index.ts +5 -0
- package/src/theme/ThemeColorCoverageRuntime.stories.tsx +236 -0
- package/src/theme/direct-token-migration-plan.md +121 -0
- package/src/theme/figma-mcp-check-report.md +225 -0
- package/src/theme/figma-mcp-component-checklist.json +1250 -0
- package/src/theme/presets/colors.js +155 -44
- package/src/theme/themes/xspector/components/loading.css +2 -2
- package/src/theme/tokens/color.css +3 -3
- package/src/theme/tokens/components/action-button.css +1 -1
- package/src/theme/tokens/components/dropdown-menu.css +3 -3
- package/src/theme/tokens/components/loading.css +2 -2
- package/src/theme/tokens/components/switch.css +1 -1
- package/src/theme/utils.js +164 -25
- package/src/utils/colors.ts +92 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
KeyboardEvent,
|
|
3
|
+
ClipboardEvent,
|
|
4
|
+
forwardRef,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
} from "react";
|
|
9
|
+
import { cn } from "@/utils/cn";
|
|
10
|
+
|
|
11
|
+
export type OtpInputProps = {
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: (value: string) => void;
|
|
14
|
+
onBlur?: () => void;
|
|
15
|
+
onComplete?: (value: string) => void;
|
|
16
|
+
length?: number;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
invalid?: boolean;
|
|
19
|
+
autoFocus?: boolean;
|
|
20
|
+
inputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
21
|
+
charPattern?: RegExp;
|
|
22
|
+
className?: string;
|
|
23
|
+
inputClassName?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const sanitizeChars = (
|
|
27
|
+
raw: string,
|
|
28
|
+
charPattern: RegExp,
|
|
29
|
+
maxLength: number,
|
|
30
|
+
): string[] => {
|
|
31
|
+
const chars = Array.from(raw).filter((char) => charPattern.test(char));
|
|
32
|
+
return chars.slice(0, maxLength);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const OtpInput = forwardRef<HTMLInputElement, OtpInputProps>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
value,
|
|
39
|
+
onChange,
|
|
40
|
+
onBlur,
|
|
41
|
+
onComplete,
|
|
42
|
+
length = 6,
|
|
43
|
+
disabled = false,
|
|
44
|
+
invalid = false,
|
|
45
|
+
autoFocus = false,
|
|
46
|
+
inputMode = "numeric",
|
|
47
|
+
charPattern = /^\d$/,
|
|
48
|
+
className,
|
|
49
|
+
inputClassName,
|
|
50
|
+
},
|
|
51
|
+
ref,
|
|
52
|
+
) => {
|
|
53
|
+
const inputRefs = useRef<Array<HTMLInputElement | null>>([]);
|
|
54
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
55
|
+
|
|
56
|
+
const slots = useMemo(() => {
|
|
57
|
+
const normalizedValue = value || "";
|
|
58
|
+
return Array.from({ length }, (_, index) => normalizedValue[index] || "");
|
|
59
|
+
}, [length, value]);
|
|
60
|
+
|
|
61
|
+
useImperativeHandle(
|
|
62
|
+
ref,
|
|
63
|
+
() => inputRefs.current[0] as HTMLInputElement,
|
|
64
|
+
[],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const setCode = (nextSlots: string[]) => {
|
|
68
|
+
const nextValue = nextSlots.join("");
|
|
69
|
+
onChange(nextValue);
|
|
70
|
+
|
|
71
|
+
if (onComplete && nextSlots.every((slot) => slot !== "")) {
|
|
72
|
+
onComplete(nextValue);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const focusInput = (index: number) => {
|
|
77
|
+
const clamped = Math.max(0, Math.min(length - 1, index));
|
|
78
|
+
inputRefs.current[clamped]?.focus();
|
|
79
|
+
inputRefs.current[clamped]?.select();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleInputChange = (index: number, rawValue: string) => {
|
|
83
|
+
if (disabled) return;
|
|
84
|
+
|
|
85
|
+
if (!rawValue) {
|
|
86
|
+
const nextSlots = [...slots];
|
|
87
|
+
nextSlots[index] = "";
|
|
88
|
+
setCode(nextSlots);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const incomingChars = sanitizeChars(rawValue, charPattern, length);
|
|
93
|
+
if (!incomingChars.length) return;
|
|
94
|
+
|
|
95
|
+
const nextSlots = [...slots];
|
|
96
|
+
let cursor = index;
|
|
97
|
+
|
|
98
|
+
incomingChars.forEach((char) => {
|
|
99
|
+
if (cursor < length) {
|
|
100
|
+
nextSlots[cursor] = char;
|
|
101
|
+
cursor += 1;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
setCode(nextSlots);
|
|
106
|
+
|
|
107
|
+
if (cursor < length) {
|
|
108
|
+
focusInput(cursor);
|
|
109
|
+
} else {
|
|
110
|
+
focusInput(length - 1);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handlePaste = (
|
|
115
|
+
index: number,
|
|
116
|
+
event: ClipboardEvent<HTMLInputElement>,
|
|
117
|
+
) => {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
if (disabled) return;
|
|
120
|
+
|
|
121
|
+
const pasted = event.clipboardData.getData("text");
|
|
122
|
+
const incomingChars = sanitizeChars(pasted, charPattern, length - index);
|
|
123
|
+
if (!incomingChars.length) return;
|
|
124
|
+
|
|
125
|
+
const nextSlots = [...slots];
|
|
126
|
+
incomingChars.forEach((char, offset) => {
|
|
127
|
+
nextSlots[index + offset] = char;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
setCode(nextSlots);
|
|
131
|
+
const nextFocusIndex = Math.min(index + incomingChars.length, length - 1);
|
|
132
|
+
focusInput(nextFocusIndex);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const handleKeyDown = (
|
|
136
|
+
index: number,
|
|
137
|
+
event: KeyboardEvent<HTMLInputElement>,
|
|
138
|
+
) => {
|
|
139
|
+
if (disabled) return;
|
|
140
|
+
|
|
141
|
+
if (event.key === "ArrowLeft") {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
focusInput(index - 1);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (event.key === "ArrowRight") {
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
focusInput(index + 1);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (event.key !== "Backspace") return;
|
|
154
|
+
|
|
155
|
+
if (slots[index]) {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
const nextSlots = [...slots];
|
|
158
|
+
nextSlots[index] = "";
|
|
159
|
+
setCode(nextSlots);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (index > 0) {
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
const nextSlots = [...slots];
|
|
166
|
+
nextSlots[index - 1] = "";
|
|
167
|
+
setCode(nextSlots);
|
|
168
|
+
focusInput(index - 1);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div
|
|
174
|
+
className={cn("flex items-center gap-3", className)}
|
|
175
|
+
ref={containerRef}
|
|
176
|
+
>
|
|
177
|
+
{slots.map((slot, index) => (
|
|
178
|
+
<input
|
|
179
|
+
key={index}
|
|
180
|
+
ref={(element) => {
|
|
181
|
+
inputRefs.current[index] = element;
|
|
182
|
+
}}
|
|
183
|
+
type="text"
|
|
184
|
+
inputMode={inputMode}
|
|
185
|
+
autoComplete={index === 0 ? "one-time-code" : "off"}
|
|
186
|
+
value={slot}
|
|
187
|
+
maxLength={1}
|
|
188
|
+
disabled={disabled}
|
|
189
|
+
autoFocus={autoFocus && index === 0}
|
|
190
|
+
aria-invalid={invalid || undefined}
|
|
191
|
+
className={cn(
|
|
192
|
+
"h-14 w-[46px] rounded-[8px] text-input-filled-text border bg-transparent text-center text-2xl font-semibold outline-none transition-all duration-200",
|
|
193
|
+
"border-input-default-stroke focus:border-input-active-stroke",
|
|
194
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
195
|
+
invalid && "border-input-error focus:border-input-error",
|
|
196
|
+
inputClassName,
|
|
197
|
+
)}
|
|
198
|
+
onFocus={(event) => {
|
|
199
|
+
event.target.select();
|
|
200
|
+
}}
|
|
201
|
+
onBlur={(event) => {
|
|
202
|
+
const nextFocused = event.relatedTarget as Node | null;
|
|
203
|
+
const stillInside =
|
|
204
|
+
nextFocused && containerRef.current?.contains(nextFocused);
|
|
205
|
+
|
|
206
|
+
// Notify RHF only when focus leaves the whole OTP control.
|
|
207
|
+
if (!stillInside) {
|
|
208
|
+
onBlur?.();
|
|
209
|
+
}
|
|
210
|
+
}}
|
|
211
|
+
onChange={(event) => handleInputChange(index, event.target.value)}
|
|
212
|
+
onPaste={(event) => handlePaste(index, event)}
|
|
213
|
+
onKeyDown={(event) => handleKeyDown(index, event)}
|
|
214
|
+
/>
|
|
215
|
+
))}
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
OtpInput.displayName = "OtpInput";
|
|
222
|
+
|
|
223
|
+
export default OtpInput;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/utils/cn";
|
|
3
|
+
import { OtpInput, OtpInputProps } from "./OtpInput";
|
|
4
|
+
|
|
5
|
+
export type OtpInputGroupProps = OtpInputProps & {
|
|
6
|
+
id?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
helperText?: string;
|
|
10
|
+
error?: boolean;
|
|
11
|
+
errorMessage?: string;
|
|
12
|
+
keepFooterSpace?: boolean;
|
|
13
|
+
labelClassName?: string;
|
|
14
|
+
messageClassName?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const OtpInputGroup = React.forwardRef<HTMLInputElement, OtpInputGroupProps>(
|
|
18
|
+
(
|
|
19
|
+
{
|
|
20
|
+
id,
|
|
21
|
+
label,
|
|
22
|
+
required = false,
|
|
23
|
+
helperText,
|
|
24
|
+
error = false,
|
|
25
|
+
errorMessage,
|
|
26
|
+
keepFooterSpace = true,
|
|
27
|
+
labelClassName,
|
|
28
|
+
messageClassName,
|
|
29
|
+
className,
|
|
30
|
+
...otpProps
|
|
31
|
+
},
|
|
32
|
+
ref
|
|
33
|
+
) => {
|
|
34
|
+
const isInvalid = Boolean(error || errorMessage);
|
|
35
|
+
const message = isInvalid ? errorMessage : helperText;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className={cn("inline-flex w-full flex-col gap-2", className)}>
|
|
39
|
+
{label && (
|
|
40
|
+
<label
|
|
41
|
+
htmlFor={id}
|
|
42
|
+
className={cn(
|
|
43
|
+
"typography-overline text-input-default-text",
|
|
44
|
+
isInvalid && "text-input-error",
|
|
45
|
+
labelClassName
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
{label}
|
|
49
|
+
{required && <span className="ml-1 text-input-error">*</span>}
|
|
50
|
+
</label>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
<OtpInput {...otpProps} ref={ref} invalid={isInvalid} />
|
|
54
|
+
|
|
55
|
+
{(message || keepFooterSpace) && (
|
|
56
|
+
<div
|
|
57
|
+
className={cn(
|
|
58
|
+
"typography-small2 min-h-[18px]",
|
|
59
|
+
isInvalid ? "text-input-error" : "text-text-g-contrast-medium",
|
|
60
|
+
messageClassName
|
|
61
|
+
)}
|
|
62
|
+
role={isInvalid ? "alert" : undefined}
|
|
63
|
+
>
|
|
64
|
+
{message || " "}
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
OtpInputGroup.displayName = "OtpInputGroup";
|
|
73
|
+
|
|
74
|
+
export default OtpInputGroup;
|
|
@@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef<
|
|
|
19
19
|
align={align}
|
|
20
20
|
sideOffset={sideOffset}
|
|
21
21
|
className={cn(
|
|
22
|
-
"z-50 min-w-72 rounded-md border bg-
|
|
22
|
+
"z-50 min-w-72 rounded-md border bg-bg-bg1 border-none overflow-hidden p-0 text-common-black shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
23
23
|
className
|
|
24
24
|
)}
|
|
25
25
|
{...props}
|
|
@@ -11,7 +11,7 @@ const meta = {
|
|
|
11
11
|
},
|
|
12
12
|
decorators: [
|
|
13
13
|
(Story) => (
|
|
14
|
-
<div className="p-5 flex w-full bg-
|
|
14
|
+
<div className="p-5 flex w-full bg-bg-bg2">
|
|
15
15
|
<Story />
|
|
16
16
|
</div>
|
|
17
17
|
),
|
|
@@ -121,11 +121,11 @@ const radioPreviewRows: RadioPreviewRow[] = [
|
|
|
121
121
|
|
|
122
122
|
export const FigmaPreview = {
|
|
123
123
|
render: () => (
|
|
124
|
-
<div className="bg-
|
|
125
|
-
<div className="mx-auto flex w-full max-w-[360px] flex-col gap-4 rounded-lg border border-
|
|
124
|
+
<div className="bg-bg-bg2 p-8 min-h-screen">
|
|
125
|
+
<div className="mx-auto flex w-full max-w-[360px] flex-col gap-4 rounded-lg border border-bg-stroke1 bg-bg-bg1 p-6">
|
|
126
126
|
{radioPreviewRows.map((row) => (
|
|
127
127
|
<div key={row.label} className="grid grid-cols-[160px_16px] items-center gap-4">
|
|
128
|
-
<span className="text-sm font-medium text-text-
|
|
128
|
+
<span className="text-sm font-medium text-text-contrast-max">{row.label}</span>
|
|
129
129
|
<RadioGroup defaultValue={row.checked ? "option" : undefined} disabled={row.disabled}>
|
|
130
130
|
<RadioGroupItem value="option" className={row.className} />
|
|
131
131
|
</RadioGroup>
|
|
@@ -28,7 +28,8 @@ const RadioGroupItem = React.forwardRef<
|
|
|
28
28
|
"hover:border-function-default-hover",
|
|
29
29
|
"focus:outline-none",
|
|
30
30
|
// Disabled state styles
|
|
31
|
-
"data-[disabled]:!border-state-disable-
|
|
31
|
+
"data-[disabled]:!border-state-disable-outline data-[disabled]:!fill-state-disable-outline data-[disabled]:!cursor-not-allowed data-[disabled]:!pointer-events-none data-[disabled]:!text-state-disable-outline",
|
|
32
|
+
"[&[data-disabled][data-state=checked]]:!border-state-disable-solid [&[data-disabled][data-state=checked]]:!text-state-disable-solid",
|
|
32
33
|
// Checked state styles
|
|
33
34
|
"data-[state=checked]:border-function-active-solid data-[state=checked]:text-function-active-solid",
|
|
34
35
|
"hover:data-[state=checked]:border-function-active-hover hover:data-[state=checked]:text-function-active-hover",
|
|
@@ -26,10 +26,13 @@ export type SearchProps = {
|
|
|
26
26
|
} & Omit<InputProps, "value" | "onSelect">;
|
|
27
27
|
|
|
28
28
|
const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
|
|
29
|
+
const { label = "Search", required = false, ...restProps } = props;
|
|
30
|
+
|
|
29
31
|
return (
|
|
30
32
|
<Dropdown
|
|
31
|
-
label=
|
|
32
|
-
{
|
|
33
|
+
label={label}
|
|
34
|
+
required={required}
|
|
35
|
+
{...restProps}
|
|
33
36
|
ref={ref}
|
|
34
37
|
keepCloseIconOnValue
|
|
35
38
|
hasClearIcon
|
|
@@ -37,6 +40,7 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
|
|
|
37
40
|
endIcon={null}
|
|
38
41
|
filterMode
|
|
39
42
|
isFloatingLabel={false}
|
|
43
|
+
segmentedInput={false}
|
|
40
44
|
/>
|
|
41
45
|
);
|
|
42
46
|
});
|
|
@@ -13,7 +13,7 @@ const meta = {
|
|
|
13
13
|
},
|
|
14
14
|
decorators: [
|
|
15
15
|
(Story) => (
|
|
16
|
-
<div className="p-5 flex w-full bg-
|
|
16
|
+
<div className="p-5 flex w-full bg-bg-bg1">
|
|
17
17
|
<Story />
|
|
18
18
|
</div>
|
|
19
19
|
),
|
|
@@ -54,15 +54,15 @@ export const GradientColor: StoryObj = {
|
|
|
54
54
|
const [darkRed, setDarkRed] = useState([50]);
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
|
-
<div className="w-[400px] p-6 space-y-8 rounded-md shadow-lg bg-
|
|
57
|
+
<div className="w-[400px] p-6 space-y-8 rounded-md shadow-lg bg-bg-bg1">
|
|
58
58
|
{/* Exposure (Black to White) */}
|
|
59
59
|
<Slider
|
|
60
60
|
value={exposure}
|
|
61
61
|
onValueChange={setExposure}
|
|
62
62
|
className="bg-grey-transparent-24"
|
|
63
|
-
trackClassName="h-1 rounded-full bg-grey-transparent-24 bg-gradient-to-r from-black to-white"
|
|
63
|
+
trackClassName="h-1 rounded-full bg-grey-transparent-24 bg-gradient-to-r from-common-black to-common-white"
|
|
64
64
|
rangeClassName="bg-unset"
|
|
65
|
-
thumbClassName="bg-white border border-white shadow"
|
|
65
|
+
thumbClassName="bg-common-white border border-common-white shadow"
|
|
66
66
|
thumbStyle={{
|
|
67
67
|
filter: "drop-shadow(0px 6px 12px rgba(0, 0, 0, 0.12))",
|
|
68
68
|
}}
|
|
@@ -78,7 +78,7 @@ export const GradientColor: StoryObj = {
|
|
|
78
78
|
className="bg-grey-transparent-24"
|
|
79
79
|
trackClassName="h-1 rounded-full bg-grey-transparent-24"
|
|
80
80
|
rangeClassName="bg-unset"
|
|
81
|
-
thumbClassName="bg-white border border-white shadow"
|
|
81
|
+
thumbClassName="bg-common-white border border-common-white shadow"
|
|
82
82
|
thumbStyle={{
|
|
83
83
|
filter: "drop-shadow(0px 6px 12px rgba(0, 0, 0, 0.12))",
|
|
84
84
|
}}
|
|
@@ -98,7 +98,7 @@ export const GradientColor: StoryObj = {
|
|
|
98
98
|
"linear-gradient(90deg, #F00 0%, #FF8502 11.54%, #F2FF05 23.08%, #06F90F 35.58%, #00E6FF 48.56%, #0404FF 61.54%, #AB02FF 74.52%, #FF069E 87.5%, #FF0408 100%)",
|
|
99
99
|
}}
|
|
100
100
|
rangeClassName="bg-unset"
|
|
101
|
-
thumbClassName="bg-white border border-white shadow"
|
|
101
|
+
thumbClassName="bg-common-white border border-common-white shadow"
|
|
102
102
|
thumbStyle={{
|
|
103
103
|
filter: "drop-shadow(0px 6px 12px rgba(0, 0, 0, 0.12))",
|
|
104
104
|
}}
|
|
@@ -114,7 +114,7 @@ export const GradientColor: StoryObj = {
|
|
|
114
114
|
className="bg-grey-transparent-24"
|
|
115
115
|
trackClassName="h-1 rounded-full bg-grey-transparent-24 bg-gradient-to-r from-[#848484] to-[#A00202]"
|
|
116
116
|
rangeClassName="bg-unset"
|
|
117
|
-
thumbClassName="bg-white border border-white shadow"
|
|
117
|
+
thumbClassName="bg-common-white border border-common-white shadow"
|
|
118
118
|
thumbStyle={{
|
|
119
119
|
filter: "drop-shadow(0px 6px 12px rgba(0, 0, 0, 0.12))",
|
|
120
120
|
}}
|
|
@@ -53,7 +53,7 @@ const Slider = React.forwardRef<
|
|
|
53
53
|
</SliderPrimitive.Track>
|
|
54
54
|
<SliderPrimitive.Thumb
|
|
55
55
|
className={cn(
|
|
56
|
-
"block h-5 w-5 cursor-pointer rounded-full border-[3px] border-primary bg-
|
|
56
|
+
"block h-5 w-5 cursor-pointer rounded-full border-[3px] border-primary bg-bg-bg1 outline-none ring-0 transition-colors disabled:pointer-events-none disabled:opacity-50",
|
|
57
57
|
thumbClassName
|
|
58
58
|
)}
|
|
59
59
|
style={thumbStyle}
|
|
@@ -13,7 +13,7 @@ const meta = {
|
|
|
13
13
|
},
|
|
14
14
|
decorators: [
|
|
15
15
|
(Story) => (
|
|
16
|
-
<div className="p-5 flex w-full bg-
|
|
16
|
+
<div className="p-5 flex w-full bg-bg-bg2">
|
|
17
17
|
<Story />
|
|
18
18
|
</div>
|
|
19
19
|
),
|
|
@@ -58,11 +58,11 @@ const switchPreviewRows: SwitchPreviewRow[] = [
|
|
|
58
58
|
|
|
59
59
|
export const FigmaPreview = {
|
|
60
60
|
render: () => (
|
|
61
|
-
<div className="bg-
|
|
62
|
-
<div className="mx-auto flex w-full max-w-[300px] flex-col gap-4 rounded-lg border border-
|
|
61
|
+
<div className="bg-bg-bg2 p-8 min-h-screen">
|
|
62
|
+
<div className="mx-auto flex w-full max-w-[300px] flex-col gap-4 rounded-lg border border-bg-stroke1 bg-bg-bg1 p-6">
|
|
63
63
|
{switchPreviewRows.map((row) => (
|
|
64
64
|
<div key={row.label} className="grid grid-cols-[1fr_40px] items-center gap-6">
|
|
65
|
-
<span className="text-sm font-medium text-text-
|
|
65
|
+
<span className="text-sm font-medium text-text-contrast-max">{row.label}</span>
|
|
66
66
|
<Switch checked={row.checked} disabled={row.disabled} forceHover={row.forceHover} />
|
|
67
67
|
</div>
|
|
68
68
|
))}
|
|
@@ -54,7 +54,7 @@ const TableFooter = React.forwardRef<
|
|
|
54
54
|
<tfoot
|
|
55
55
|
ref={ref}
|
|
56
56
|
className={cn(
|
|
57
|
-
"border-t bg-
|
|
57
|
+
"border-t bg-transparent-grey2-8 font-medium [&>tr]:last:border-b-0",
|
|
58
58
|
className
|
|
59
59
|
)}
|
|
60
60
|
{...props}
|
|
@@ -69,7 +69,7 @@ const TableRow = React.forwardRef<
|
|
|
69
69
|
<tr
|
|
70
70
|
ref={ref}
|
|
71
71
|
className={cn(
|
|
72
|
-
"border-b transition-colors hover:bg-
|
|
72
|
+
"border-b transition-colors hover:bg-transparent-grey2-8 data-[state=selected]:bg-grey-20",
|
|
73
73
|
className
|
|
74
74
|
)}
|
|
75
75
|
{...props}
|
|
@@ -84,7 +84,7 @@ const TableHead = React.forwardRef<
|
|
|
84
84
|
<th
|
|
85
85
|
ref={ref}
|
|
86
86
|
className={cn(
|
|
87
|
-
" h-12 py-3 px-6 text-left align-middle typography-body2 text-text-
|
|
87
|
+
" h-12 py-3 px-6 text-left align-middle typography-body2 text-text-g-contrast-low [&:has([role=checkbox])]:pr-4 [&:has([role=checkbox])]:w-4",
|
|
88
88
|
className
|
|
89
89
|
)}
|
|
90
90
|
{...props}
|
|
@@ -99,7 +99,7 @@ const TableCell = React.forwardRef<
|
|
|
99
99
|
<td
|
|
100
100
|
ref={ref}
|
|
101
101
|
className={cn(
|
|
102
|
-
" py-3 px-6 text-left align-middle typography-body3 text-text-
|
|
102
|
+
" py-3 px-6 text-left align-middle typography-body3 text-text-g-contrast-low [&:has([role=checkbox])]:pr-4 [&:has([role=checkbox])]:w-4",
|
|
103
103
|
className
|
|
104
104
|
)}
|
|
105
105
|
{...props}
|
|
@@ -113,7 +113,7 @@ const TableCaption = React.forwardRef<
|
|
|
113
113
|
>(({ className, ...props }, ref) => (
|
|
114
114
|
<caption
|
|
115
115
|
ref={ref}
|
|
116
|
-
className={cn("mt-4 text-sm text-
|
|
116
|
+
className={cn("mt-4 text-sm text-text-g-contrast-medium", className)}
|
|
117
117
|
{...props}
|
|
118
118
|
/>
|
|
119
119
|
));
|
|
@@ -54,7 +54,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
54
54
|
tabBarSize = 38,
|
|
55
55
|
enableBorderLine = true,
|
|
56
56
|
enableAddTabButton = false,
|
|
57
|
-
keepIconSpace =
|
|
57
|
+
keepIconSpace = false,
|
|
58
58
|
disabled = false,
|
|
59
59
|
keepMounted = false,
|
|
60
60
|
tabMode = "start",
|
|
@@ -75,6 +75,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
75
75
|
onTabChange,
|
|
76
76
|
}) => {
|
|
77
77
|
const [activeTab, setActiveTab] = useState(initialTab);
|
|
78
|
+
const [hoveredTab, setHoveredTab] = useState<number | null>(null);
|
|
78
79
|
const [sliderStyle, setSliderStyle] = useState({
|
|
79
80
|
width: "0px",
|
|
80
81
|
transform: "translateX(0px)",
|
|
@@ -147,7 +148,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
147
148
|
"border-state-disable-outline": disabled,
|
|
148
149
|
"flex-1": tabMode === "justify",
|
|
149
150
|
},
|
|
150
|
-
tabBarContainerClassName
|
|
151
|
+
tabBarContainerClassName,
|
|
151
152
|
)}
|
|
152
153
|
>
|
|
153
154
|
{leftAction}
|
|
@@ -157,7 +158,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
157
158
|
{
|
|
158
159
|
"flex-1": tabMode === "justify",
|
|
159
160
|
},
|
|
160
|
-
tabBarWrapperClassName
|
|
161
|
+
tabBarWrapperClassName,
|
|
161
162
|
)}
|
|
162
163
|
>
|
|
163
164
|
<div
|
|
@@ -167,7 +168,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
167
168
|
{
|
|
168
169
|
"gap-0": tabMode === "justify",
|
|
169
170
|
},
|
|
170
|
-
tabBarClassName
|
|
171
|
+
tabBarClassName,
|
|
171
172
|
)}
|
|
172
173
|
style={{
|
|
173
174
|
justifyContent: tabMode === "justify" ? "stretch" : tabMode,
|
|
@@ -184,13 +185,12 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
184
185
|
disabled={disabled || tab.disabled}
|
|
185
186
|
id={`tab-${index}`}
|
|
186
187
|
className={cn(
|
|
187
|
-
"flex justify-center flex-row items-center py-3 cursor-pointer transition-colors duration-300 box-border gap-1 flex-shrink-0
|
|
188
|
+
"group relative flex justify-center flex-row items-center py-3 cursor-pointer transition-colors duration-300 box-border gap-1 flex-shrink-0 typography-subtitle6",
|
|
188
189
|
{
|
|
189
|
-
"text-
|
|
190
|
-
"text-text-
|
|
191
|
-
index !== activeTab,
|
|
190
|
+
"text-text-contrast-max": index === activeTab,
|
|
191
|
+
"text-text-g-contrast-medium": index !== activeTab,
|
|
192
192
|
// -- disabled
|
|
193
|
-
"text-state-disable-
|
|
193
|
+
"text-state-disable-outline pointer-events-none":
|
|
194
194
|
disabled || tab.disabled,
|
|
195
195
|
[tab.disableClassName ?? tabButtonDisableClassName ?? ""]:
|
|
196
196
|
disabled || tab.disabled,
|
|
@@ -207,33 +207,44 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
207
207
|
[tab.activeClassName ?? tabButtonActiveClassName ?? ""]:
|
|
208
208
|
index === activeTab,
|
|
209
209
|
"flex-1": tabMode === "justify",
|
|
210
|
-
}
|
|
210
|
+
},
|
|
211
211
|
)}
|
|
212
212
|
onClick={() => {
|
|
213
213
|
setActiveTab(index);
|
|
214
214
|
onTabChange?.(index);
|
|
215
215
|
}}
|
|
216
|
+
onMouseEnter={() => setHoveredTab(index)}
|
|
217
|
+
onMouseLeave={() => setHoveredTab(null)}
|
|
216
218
|
>
|
|
217
219
|
{(keepIconSpace || tab.startTabContent) && (
|
|
218
|
-
<div className="h-full w-3 flex items-center justify-center">
|
|
219
|
-
{tab.isLoading ?
|
|
220
|
+
<div className="h-full w-3 flex items-center justify-center [&_svg]:size-3 [&_svg]:stroke-current [&_svg]:text-current">
|
|
221
|
+
{tab.isLoading ? (
|
|
222
|
+
<Loading size={12} />
|
|
223
|
+
) : (
|
|
224
|
+
tab.startTabContent
|
|
225
|
+
)}
|
|
220
226
|
</div>
|
|
221
227
|
)}
|
|
222
228
|
{tab.label}
|
|
223
229
|
{(keepIconSpace || tab.endTabContent) && (
|
|
224
|
-
<div className="h-full w-3 flex items-center justify-center">
|
|
230
|
+
<div className="h-full w-3 flex items-center justify-center [&_svg]:size-3 [&_svg]:stroke-current [&_svg]:text-current">
|
|
225
231
|
{tab.endTabContent}
|
|
226
232
|
</div>
|
|
227
233
|
)}
|
|
234
|
+
{!disabled && !tab.disabled && index !== activeTab && (
|
|
235
|
+
<div className="absolute bottom-0 left-0 right-0 h-[2px] rounded-[1px] bg-function-default-hover opacity-0 transition-opacity duration-150 group-hover:opacity-100" />
|
|
236
|
+
)}
|
|
228
237
|
</button>
|
|
229
238
|
))}
|
|
230
239
|
<div
|
|
231
240
|
className={cn(
|
|
232
|
-
|
|
241
|
+
"absolute left-0 bottom-0 h-[2px] rounded-[1px] bg-function-active-solid transition-all duration-300 ease-in-out",
|
|
233
242
|
{
|
|
234
|
-
|
|
243
|
+
hidden: disabled || tabs[activeTab]?.disabled,
|
|
244
|
+
"bg-function-active-hover":
|
|
245
|
+
hoveredTab === activeTab && !disabled,
|
|
235
246
|
},
|
|
236
|
-
borderSliderClassName
|
|
247
|
+
borderSliderClassName,
|
|
237
248
|
)}
|
|
238
249
|
style={sliderStyle}
|
|
239
250
|
/>
|
|
@@ -243,7 +254,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
243
254
|
<div
|
|
244
255
|
className={cn(
|
|
245
256
|
"sticky right-0 flex content-center items-center mx-4",
|
|
246
|
-
addTabButtonWrapperClassName
|
|
257
|
+
addTabButtonWrapperClassName,
|
|
247
258
|
)}
|
|
248
259
|
>
|
|
249
260
|
<ActionButton
|
|
@@ -260,7 +271,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
260
271
|
</div>
|
|
261
272
|
|
|
262
273
|
<div
|
|
263
|
-
className={cn("mt-4 text-
|
|
274
|
+
className={cn("mt-4 text-text-g-contrast-high", tabContentClassName)}
|
|
264
275
|
role="tabpanel"
|
|
265
276
|
id={`tab-content-${activeTab}`}
|
|
266
277
|
aria-labelledby={`tab-${activeTab}`}
|