@puzzle-ui/ui 0.4.0 → 0.5.1
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/README.md +61 -77
- package/dist/components/actions/button/button.css +5 -2
- package/dist/components/actions/button-group/button-group.css +16 -17
- package/dist/components/data-display/badge/badge.css +15 -2
- package/dist/components/data-display/badge/index.cjs +13 -2
- package/dist/components/data-display/badge/index.cjs.map +1 -1
- package/dist/components/data-display/badge/index.d.cts +7 -2
- package/dist/components/data-display/badge/index.d.ts +7 -2
- package/dist/components/data-display/badge/index.js +13 -2
- package/dist/components/data-display/badge/index.js.map +1 -1
- package/dist/components/data-display/rating/index.cjs +84 -0
- package/dist/components/data-display/rating/index.cjs.map +1 -0
- package/dist/components/data-display/rating/index.d.cts +3 -0
- package/dist/components/data-display/rating/index.d.ts +3 -0
- package/dist/components/data-display/rating/index.js +84 -0
- package/dist/components/data-display/rating/index.js.map +1 -0
- package/dist/components/data-display/rating/rating.css +72 -0
- package/dist/components/data-display/rating/rating.types.d.cts +15 -0
- package/dist/components/data-display/rating/rating.types.d.ts +15 -0
- package/dist/components/data-display/rating/rating.utils.cjs +27 -0
- package/dist/components/data-display/rating/rating.utils.cjs.map +1 -0
- package/dist/components/data-display/rating/rating.utils.js +25 -0
- package/dist/components/data-display/rating/rating.utils.js.map +1 -0
- package/dist/components/forms/checkbox/checkbox.css +2 -10
- package/dist/components/forms/input-group/input-group.d.cts +4 -2
- package/dist/components/forms/input-group/input-group.d.ts +4 -2
- package/dist/components/forms/radio-group/index.d.cts +2 -1
- package/dist/components/forms/radio-group/index.d.ts +2 -1
- package/dist/components/forms/stepper/index.d.cts +4 -2
- package/dist/components/forms/stepper/index.d.ts +4 -2
- package/dist/components/forms/time-picker/index.cjs +108 -0
- package/dist/components/forms/time-picker/index.cjs.map +1 -0
- package/dist/components/forms/time-picker/index.d.cts +3 -0
- package/dist/components/forms/time-picker/index.d.ts +3 -0
- package/dist/components/forms/time-picker/index.js +108 -0
- package/dist/components/forms/time-picker/index.js.map +1 -0
- package/dist/components/forms/time-picker/time-picker.css +166 -0
- package/dist/components/forms/time-picker/time-picker.types.d.cts +34 -0
- package/dist/components/forms/time-picker/time-picker.types.d.ts +34 -0
- package/dist/components/forms/time-picker/time-picker.utils.cjs +25 -0
- package/dist/components/forms/time-picker/time-picker.utils.cjs.map +1 -0
- package/dist/components/forms/time-picker/time-picker.utils.js +24 -0
- package/dist/components/forms/time-picker/time-picker.utils.js.map +1 -0
- package/dist/components/forms/token-input/index.cjs +189 -0
- package/dist/components/forms/token-input/index.cjs.map +1 -0
- package/dist/components/forms/token-input/index.d.cts +3 -0
- package/dist/components/forms/token-input/index.d.ts +3 -0
- package/dist/components/forms/token-input/index.js +189 -0
- package/dist/components/forms/token-input/index.js.map +1 -0
- package/dist/components/forms/token-input/token-input.css +145 -0
- package/dist/components/forms/token-input/token-input.types.d.cts +48 -0
- package/dist/components/forms/token-input/token-input.types.d.ts +48 -0
- package/dist/components/forms/token-input/token-input.utils.cjs +30 -0
- package/dist/components/forms/token-input/token-input.utils.cjs.map +1 -0
- package/dist/components/forms/token-input/token-input.utils.js +24 -0
- package/dist/components/forms/token-input/token-input.utils.js.map +1 -0
- package/dist/components/layout/aspect-ratio/aspect-ratio.css +14 -0
- package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs +11 -0
- package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs.map +1 -0
- package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js +11 -0
- package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js.map +1 -0
- package/dist/components/layout/aspect-ratio/index.cjs +16 -0
- package/dist/components/layout/aspect-ratio/index.cjs.map +1 -0
- package/dist/components/layout/aspect-ratio/index.d.cts +11 -0
- package/dist/components/layout/aspect-ratio/index.d.ts +11 -0
- package/dist/components/layout/aspect-ratio/index.js +16 -0
- package/dist/components/layout/aspect-ratio/index.js.map +1 -0
- package/dist/components/layout/container/container.css +1 -0
- package/dist/components/layout/page-layout/page-layout.css +1 -0
- package/dist/components/layout/scroll-area/index.cjs +27 -0
- package/dist/components/layout/scroll-area/index.cjs.map +1 -0
- package/dist/components/layout/scroll-area/index.d.cts +15 -0
- package/dist/components/layout/scroll-area/index.d.ts +15 -0
- package/dist/components/layout/scroll-area/index.js +27 -0
- package/dist/components/layout/scroll-area/index.js.map +1 -0
- package/dist/components/layout/scroll-area/scroll-area.css +25 -0
- package/dist/components/layout/scroll-area/scroll-area.utils.cjs +8 -0
- package/dist/components/layout/scroll-area/scroll-area.utils.cjs.map +1 -0
- package/dist/components/layout/scroll-area/scroll-area.utils.d.cts +2 -0
- package/dist/components/layout/scroll-area/scroll-area.utils.d.ts +2 -0
- package/dist/components/layout/scroll-area/scroll-area.utils.js +8 -0
- package/dist/components/layout/scroll-area/scroll-area.utils.js.map +1 -0
- package/dist/components/layout/section/index.cjs +10 -4
- package/dist/components/layout/section/index.cjs.map +1 -1
- package/dist/components/layout/section/index.d.cts +1 -1
- package/dist/components/layout/section/index.d.ts +1 -1
- package/dist/components/layout/section/index.js +11 -5
- package/dist/components/layout/section/index.js.map +1 -1
- package/dist/components/layout/section/section.css +9 -3
- package/dist/components/layout/section/types.d.cts +5 -5
- package/dist/components/layout/section/types.d.ts +5 -5
- package/dist/components/layout/separator/index.cjs +18 -0
- package/dist/components/layout/separator/index.cjs.map +1 -0
- package/dist/components/layout/separator/index.d.cts +17 -0
- package/dist/components/layout/separator/index.d.ts +17 -0
- package/dist/components/layout/separator/index.js +18 -0
- package/dist/components/layout/separator/index.js.map +1 -0
- package/dist/components/layout/separator/separator.css +15 -0
- package/dist/components/layout/separator/separator.utils.cjs +11 -0
- package/dist/components/layout/separator/separator.utils.cjs.map +1 -0
- package/dist/components/layout/separator/separator.utils.d.cts +2 -0
- package/dist/components/layout/separator/separator.utils.d.ts +2 -0
- package/dist/components/layout/separator/separator.utils.js +11 -0
- package/dist/components/layout/separator/separator.utils.js.map +1 -0
- package/dist/components/layout/stack/index.cjs +18 -2
- package/dist/components/layout/stack/index.cjs.map +1 -1
- package/dist/components/layout/stack/index.d.cts +8 -6
- package/dist/components/layout/stack/index.d.ts +8 -6
- package/dist/components/layout/stack/index.js +18 -2
- package/dist/components/layout/stack/index.js.map +1 -1
- package/dist/components/layout/stack/stack.css +118 -138
- package/dist/components/navigation/carousel/index.cjs +4 -1
- package/dist/components/navigation/carousel/index.cjs.map +1 -1
- package/dist/components/navigation/carousel/index.js +4 -1
- package/dist/components/navigation/carousel/index.js.map +1 -1
- package/dist/components/navigation/tabs/index.d.cts +3 -2
- package/dist/components/navigation/tabs/index.d.ts +3 -2
- package/dist/components/navigation/tree-view/index.cjs +1 -1
- package/dist/components/navigation/tree-view/index.cjs.map +1 -1
- package/dist/components/navigation/tree-view/index.d.cts +5 -1
- package/dist/components/navigation/tree-view/index.d.ts +5 -1
- package/dist/components/navigation/tree-view/index.js +1 -1
- package/dist/components/navigation/tree-view/index.js.map +1 -1
- package/dist/components/navigation/tree-view/tree-view.css +5 -3
- package/dist/components/overlays/context-menu/context-menu.css +151 -0
- package/dist/components/overlays/context-menu/context-menu.types.d.cts +69 -0
- package/dist/components/overlays/context-menu/context-menu.types.d.ts +69 -0
- package/dist/components/overlays/context-menu/context-menu.utils.cjs +46 -0
- package/dist/components/overlays/context-menu/context-menu.utils.cjs.map +1 -0
- package/dist/components/overlays/context-menu/context-menu.utils.js +40 -0
- package/dist/components/overlays/context-menu/context-menu.utils.js.map +1 -0
- package/dist/components/overlays/context-menu/index.cjs +830 -0
- package/dist/components/overlays/context-menu/index.cjs.map +1 -0
- package/dist/components/overlays/context-menu/index.d.cts +42 -0
- package/dist/components/overlays/context-menu/index.d.ts +42 -0
- package/dist/components/overlays/context-menu/index.js +818 -0
- package/dist/components/overlays/context-menu/index.js.map +1 -0
- package/dist/components/typography/heading.cjs +6 -2
- package/dist/components/typography/heading.cjs.map +1 -1
- package/dist/components/typography/heading.d.cts +3 -2
- package/dist/components/typography/heading.d.ts +3 -2
- package/dist/components/typography/heading.js +6 -2
- package/dist/components/typography/heading.js.map +1 -1
- package/dist/components/typography/text.cjs +6 -2
- package/dist/components/typography/text.cjs.map +1 -1
- package/dist/components/typography/text.d.cts +3 -2
- package/dist/components/typography/text.d.ts +3 -2
- package/dist/components/typography/text.js +6 -2
- package/dist/components/typography/text.js.map +1 -1
- package/dist/components/utilities/preview/index.cjs +88 -20
- package/dist/components/utilities/preview/index.cjs.map +1 -1
- package/dist/components/utilities/preview/index.d.cts +6 -2
- package/dist/components/utilities/preview/index.d.ts +6 -2
- package/dist/components/utilities/preview/index.js +88 -20
- package/dist/components/utilities/preview/index.js.map +1 -1
- package/dist/components/utilities/preview/preview.css +26 -11
- package/dist/components/utilities/transition/index.d.cts +1 -1
- package/dist/components/utilities/transition/index.d.ts +1 -1
- package/dist/index.cjs +123 -97
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +8 -1
- package/package.json +11 -6
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { getNextActiveIndex, getPreviousActiveIndex, insertTokenCharacters, isTokenComplete, removeTokenCharacter, replaceTokenCharacter, sanitizeToken } from "./token-input.utils.js";
|
|
3
|
+
import "./token-input.css";
|
|
4
|
+
import { cn } from "@puzzle-ui/core";
|
|
5
|
+
import { useCallback, useId, useRef, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
// src/components/forms/token-input/index.tsx
|
|
8
|
+
const TokenInput = ({ length = 6, value, defaultValue = "", onValueChange, onComplete, mode = "numeric", mask = false, disabled = false, readOnly = false, required = false, size = "md", label, id, name, form, inputMode, autoComplete = "one-time-code", className, style, ref, onFocus, onBlur, onKeyDown, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, ...rootProps }) => {
|
|
9
|
+
if (!Number.isInteger(length) || length < 1 || length > 32) throw new Error("TokenInput length must be an integer between 1 and 32.");
|
|
10
|
+
const generatedId = useId();
|
|
11
|
+
const inputId = id ?? `${generatedId}-input`;
|
|
12
|
+
const labelId = `${inputId}-label`;
|
|
13
|
+
const isControlled = value !== void 0;
|
|
14
|
+
const [internalValue, setInternalValue] = useState(() => sanitizeToken(defaultValue, length, mode));
|
|
15
|
+
const currentValue = sanitizeToken(isControlled ? value : internalValue, length, mode);
|
|
16
|
+
const [activeIndex, setActiveIndex] = useState(() => getNextActiveIndex(currentValue, length));
|
|
17
|
+
const inputRefs = useRef([]);
|
|
18
|
+
const completedValueRef = useRef(null);
|
|
19
|
+
const resolvedActiveIndex = Math.min(activeIndex, length - 1);
|
|
20
|
+
const isInvalid = ariaInvalid === true || ariaInvalid === "true";
|
|
21
|
+
const setFirstInputRef = useCallback((node) => {
|
|
22
|
+
inputRefs.current[0] = node;
|
|
23
|
+
if (typeof ref === "function") return ref(node);
|
|
24
|
+
if (ref) ref.current = node;
|
|
25
|
+
}, [ref]);
|
|
26
|
+
const focusInput = useCallback((index) => {
|
|
27
|
+
const nextIndex = Math.max(0, Math.min(index, length - 1));
|
|
28
|
+
setActiveIndex(nextIndex);
|
|
29
|
+
const input = inputRefs.current[nextIndex];
|
|
30
|
+
input?.focus();
|
|
31
|
+
input?.select();
|
|
32
|
+
}, [length]);
|
|
33
|
+
const commitValue = useCallback((nextValue) => {
|
|
34
|
+
const normalized = sanitizeToken(nextValue, length, mode);
|
|
35
|
+
if (normalized === currentValue) return;
|
|
36
|
+
const wasComplete = isTokenComplete(currentValue, length);
|
|
37
|
+
const isComplete = isTokenComplete(normalized, length);
|
|
38
|
+
if (!isControlled) setInternalValue(normalized);
|
|
39
|
+
onValueChange?.(normalized);
|
|
40
|
+
if (!wasComplete && isComplete) {
|
|
41
|
+
if (completedValueRef.current !== normalized) onComplete?.(normalized);
|
|
42
|
+
completedValueRef.current = normalized;
|
|
43
|
+
} else if (!isComplete) completedValueRef.current = null;
|
|
44
|
+
}, [
|
|
45
|
+
currentValue,
|
|
46
|
+
isControlled,
|
|
47
|
+
length,
|
|
48
|
+
mode,
|
|
49
|
+
onComplete,
|
|
50
|
+
onValueChange
|
|
51
|
+
]);
|
|
52
|
+
const handleChange = (index, event) => {
|
|
53
|
+
if (disabled || readOnly) return;
|
|
54
|
+
const enteredValue = sanitizeToken(event.currentTarget.value, length, mode);
|
|
55
|
+
const nextValue = enteredValue.length > 1 ? insertTokenCharacters(currentValue, index, enteredValue, length, mode) : replaceTokenCharacter(currentValue, index, enteredValue, length, mode);
|
|
56
|
+
commitValue(nextValue);
|
|
57
|
+
if (enteredValue.length > 0) focusInput(Math.min(index + enteredValue.length, length - 1));
|
|
58
|
+
else focusInput(index);
|
|
59
|
+
};
|
|
60
|
+
const handlePaste = (index, event) => {
|
|
61
|
+
if (disabled || readOnly) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
const pastedValue = sanitizeToken(event.clipboardData.getData("text"), length, mode);
|
|
67
|
+
if (!pastedValue) return;
|
|
68
|
+
const nextValue = insertTokenCharacters(currentValue, index, pastedValue, length, mode);
|
|
69
|
+
commitValue(nextValue);
|
|
70
|
+
focusInput(Math.min(index + pastedValue.length, length - 1));
|
|
71
|
+
};
|
|
72
|
+
const handleKeyDown = (index, event) => {
|
|
73
|
+
onKeyDown?.(event);
|
|
74
|
+
if (event.defaultPrevented || disabled) return;
|
|
75
|
+
switch (event.key) {
|
|
76
|
+
case "ArrowLeft":
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
focusInput(index - 1);
|
|
79
|
+
break;
|
|
80
|
+
case "ArrowRight":
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
focusInput(index + 1);
|
|
83
|
+
break;
|
|
84
|
+
case "Home":
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
focusInput(0);
|
|
87
|
+
break;
|
|
88
|
+
case "End":
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
focusInput(length - 1);
|
|
91
|
+
break;
|
|
92
|
+
case "Backspace":
|
|
93
|
+
if (readOnly) return;
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
if (currentValue[index]) {
|
|
96
|
+
commitValue(removeTokenCharacter(currentValue, index));
|
|
97
|
+
focusInput(index);
|
|
98
|
+
} else if (index > 0) {
|
|
99
|
+
const previousIndex = getPreviousActiveIndex(index);
|
|
100
|
+
commitValue(removeTokenCharacter(currentValue, previousIndex));
|
|
101
|
+
focusInput(previousIndex);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case "Delete":
|
|
105
|
+
if (readOnly) return;
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
if (currentValue[index]) commitValue(removeTokenCharacter(currentValue, index));
|
|
108
|
+
focusInput(index);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const handleFocus = (event) => {
|
|
112
|
+
const index = Number(event.currentTarget.dataset.tokenIndex);
|
|
113
|
+
setActiveIndex(index);
|
|
114
|
+
event.currentTarget.select();
|
|
115
|
+
onFocus?.(event);
|
|
116
|
+
};
|
|
117
|
+
const handleBlur = (event) => {
|
|
118
|
+
onBlur?.(event);
|
|
119
|
+
};
|
|
120
|
+
const resolvedLabelledBy = ariaLabelledBy ?? (label != null ? labelId : void 0);
|
|
121
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
122
|
+
...rootProps,
|
|
123
|
+
className: cn("puzzle-token-input", className),
|
|
124
|
+
style,
|
|
125
|
+
role: "group",
|
|
126
|
+
"aria-label": ariaLabel,
|
|
127
|
+
"aria-labelledby": resolvedLabelledBy,
|
|
128
|
+
"aria-describedby": ariaDescribedBy,
|
|
129
|
+
"aria-invalid": ariaInvalid,
|
|
130
|
+
"aria-required": required || void 0,
|
|
131
|
+
"aria-disabled": disabled || void 0,
|
|
132
|
+
"data-size": size,
|
|
133
|
+
"data-disabled": disabled || void 0,
|
|
134
|
+
"data-readonly": readOnly || void 0,
|
|
135
|
+
"data-invalid": isInvalid || void 0,
|
|
136
|
+
children: [
|
|
137
|
+
label != null && /* @__PURE__ */ jsx("label", {
|
|
138
|
+
id: labelId,
|
|
139
|
+
htmlFor: inputId,
|
|
140
|
+
className: "puzzle-token-input__label",
|
|
141
|
+
children: label
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ jsx("div", {
|
|
144
|
+
className: "puzzle-token-input__slots",
|
|
145
|
+
children: Array.from({ length }, (_, index) => {
|
|
146
|
+
const character = currentValue[index] ?? "";
|
|
147
|
+
const slotLabel = ariaLabel ? `${ariaLabel}, character ${index + 1} of ${length}` : index === 0 && (id != null || label != null) ? void 0 : `Character ${index + 1} of ${length}`;
|
|
148
|
+
return /* @__PURE__ */ jsx("input", {
|
|
149
|
+
ref: index === 0 ? setFirstInputRef : (node) => {
|
|
150
|
+
inputRefs.current[index] = node;
|
|
151
|
+
},
|
|
152
|
+
id: index === 0 ? inputId : void 0,
|
|
153
|
+
"data-token-index": index,
|
|
154
|
+
className: "puzzle-token-input__slot",
|
|
155
|
+
type: mask ? "password" : "text",
|
|
156
|
+
inputMode: inputMode ?? (mode === "numeric" ? "numeric" : "text"),
|
|
157
|
+
autoComplete: index === 0 ? autoComplete : "off",
|
|
158
|
+
autoCapitalize: "off",
|
|
159
|
+
spellCheck: false,
|
|
160
|
+
maxLength: length,
|
|
161
|
+
value: character,
|
|
162
|
+
disabled,
|
|
163
|
+
readOnly,
|
|
164
|
+
required,
|
|
165
|
+
tabIndex: index === resolvedActiveIndex ? 0 : -1,
|
|
166
|
+
"aria-label": slotLabel,
|
|
167
|
+
"aria-describedby": ariaDescribedBy,
|
|
168
|
+
"aria-invalid": ariaInvalid,
|
|
169
|
+
onChange: (event) => handleChange(index, event),
|
|
170
|
+
onPaste: (event) => handlePaste(index, event),
|
|
171
|
+
onKeyDown: (event) => handleKeyDown(index, event),
|
|
172
|
+
onFocus: handleFocus,
|
|
173
|
+
onBlur: handleBlur
|
|
174
|
+
}, index);
|
|
175
|
+
})
|
|
176
|
+
}),
|
|
177
|
+
name && /* @__PURE__ */ jsx("input", {
|
|
178
|
+
type: "hidden",
|
|
179
|
+
name,
|
|
180
|
+
form,
|
|
181
|
+
value: currentValue,
|
|
182
|
+
disabled
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
export { TokenInput };
|
|
188
|
+
|
|
189
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/forms/token-input/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useId,\n useRef,\n useState,\n type ChangeEvent,\n type ClipboardEvent,\n type FocusEvent,\n type KeyboardEvent,\n} from \"react\";\n\nimport { cn } from \"@puzzle-ui/core\";\n\nimport {\n getNextActiveIndex,\n getPreviousActiveIndex,\n insertTokenCharacters,\n isTokenComplete,\n removeTokenCharacter,\n replaceTokenCharacter,\n sanitizeToken,\n} from \"./token-input.utils\";\n\nimport type { TokenInputProps } from \"./token-input.types\";\n\nimport \"./token-input.css\";\n\nconst TokenInput = ({\n length = 6,\n value,\n defaultValue = \"\",\n onValueChange,\n onComplete,\n mode = \"numeric\",\n mask = false,\n disabled = false,\n readOnly = false,\n required = false,\n size = \"md\",\n label,\n id,\n name,\n form,\n inputMode,\n autoComplete = \"one-time-code\",\n className,\n style,\n ref,\n onFocus,\n onBlur,\n onKeyDown,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n ...rootProps\n}: TokenInputProps) => {\n if (!Number.isInteger(length) || length < 1 || length > 32) {\n throw new Error(\"TokenInput length must be an integer between 1 and 32.\");\n }\n\n const generatedId = useId();\n\n // The ID belongs to the first native input.\n // This allows FormField's associated label to work.\n const inputId = id ?? `${generatedId}-input`;\n const labelId = `${inputId}-label`;\n\n const isControlled = value !== undefined;\n\n const [internalValue, setInternalValue] = useState(() =>\n sanitizeToken(defaultValue, length, mode),\n );\n\n const currentValue = sanitizeToken(\n isControlled ? value : internalValue,\n length,\n mode,\n );\n\n const [activeIndex, setActiveIndex] = useState(() =>\n getNextActiveIndex(currentValue, length),\n );\n\n const inputRefs = useRef<Array<HTMLInputElement | null>>([]);\n const completedValueRef = useRef<string | null>(null);\n\n const resolvedActiveIndex = Math.min(activeIndex, length - 1);\n\n const isInvalid = ariaInvalid === true || ariaInvalid === \"true\";\n\n const setFirstInputRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRefs.current[0] = node;\n\n if (typeof ref === \"function\") {\n return ref(node);\n }\n\n if (ref) {\n ref.current = node;\n }\n },\n [ref],\n );\n\n const focusInput = useCallback(\n (index: number) => {\n const nextIndex = Math.max(0, Math.min(index, length - 1));\n\n setActiveIndex(nextIndex);\n\n const input = inputRefs.current[nextIndex];\n\n input?.focus();\n input?.select();\n },\n [length],\n );\n\n const commitValue = useCallback(\n (nextValue: string) => {\n const normalized = sanitizeToken(nextValue, length, mode);\n\n if (normalized === currentValue) {\n return;\n }\n\n const wasComplete = isTokenComplete(currentValue, length);\n\n const isComplete = isTokenComplete(normalized, length);\n\n if (!isControlled) {\n setInternalValue(normalized);\n }\n\n onValueChange?.(normalized);\n\n if (!wasComplete && isComplete) {\n if (completedValueRef.current !== normalized) {\n onComplete?.(normalized);\n }\n\n completedValueRef.current = normalized;\n } else if (!isComplete) {\n completedValueRef.current = null;\n }\n },\n [currentValue, isControlled, length, mode, onComplete, onValueChange],\n );\n\n const handleChange = (\n index: number,\n event: ChangeEvent<HTMLInputElement>,\n ) => {\n if (disabled || readOnly) {\n return;\n }\n\n const enteredValue = sanitizeToken(event.currentTarget.value, length, mode);\n\n const nextValue =\n enteredValue.length > 1\n ? insertTokenCharacters(currentValue, index, enteredValue, length, mode)\n : replaceTokenCharacter(\n currentValue,\n index,\n enteredValue,\n length,\n mode,\n );\n\n commitValue(nextValue);\n\n if (enteredValue.length > 0) {\n focusInput(Math.min(index + enteredValue.length, length - 1));\n } else {\n focusInput(index);\n }\n };\n\n const handlePaste = (\n index: number,\n event: ClipboardEvent<HTMLInputElement>,\n ) => {\n if (disabled || readOnly) {\n event.preventDefault();\n return;\n }\n\n event.preventDefault();\n\n const pastedValue = sanitizeToken(\n event.clipboardData.getData(\"text\"),\n length,\n mode,\n );\n\n if (!pastedValue) {\n return;\n }\n\n const nextValue = insertTokenCharacters(\n currentValue,\n index,\n pastedValue,\n length,\n mode,\n );\n\n commitValue(nextValue);\n\n focusInput(Math.min(index + pastedValue.length, length - 1));\n };\n\n const handleKeyDown = (\n index: number,\n event: KeyboardEvent<HTMLInputElement>,\n ) => {\n onKeyDown?.(event);\n\n if (event.defaultPrevented || disabled) {\n return;\n }\n\n switch (event.key) {\n case \"ArrowLeft\": {\n event.preventDefault();\n focusInput(index - 1);\n break;\n }\n\n case \"ArrowRight\": {\n event.preventDefault();\n focusInput(index + 1);\n break;\n }\n\n case \"Home\": {\n event.preventDefault();\n focusInput(0);\n break;\n }\n\n case \"End\": {\n event.preventDefault();\n focusInput(length - 1);\n break;\n }\n\n case \"Backspace\": {\n if (readOnly) {\n return;\n }\n\n event.preventDefault();\n\n if (currentValue[index]) {\n commitValue(removeTokenCharacter(currentValue, index));\n\n focusInput(index);\n } else if (index > 0) {\n const previousIndex = getPreviousActiveIndex(index);\n\n commitValue(removeTokenCharacter(currentValue, previousIndex));\n\n focusInput(previousIndex);\n }\n\n break;\n }\n\n case \"Delete\": {\n if (readOnly) {\n return;\n }\n\n event.preventDefault();\n\n if (currentValue[index]) {\n commitValue(removeTokenCharacter(currentValue, index));\n }\n\n focusInput(index);\n break;\n }\n\n default:\n break;\n }\n };\n\n const handleFocus = (event: FocusEvent<HTMLInputElement>) => {\n const index = Number(event.currentTarget.dataset.tokenIndex);\n\n setActiveIndex(index);\n\n // Selecting the current character makes replacement intuitive.\n event.currentTarget.select();\n\n onFocus?.(event);\n };\n\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n };\n\n const resolvedLabelledBy =\n ariaLabelledBy ?? (label != null ? labelId : undefined);\n\n return (\n <div\n {...rootProps}\n className={cn(\"puzzle-token-input\", className)}\n style={style}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={resolvedLabelledBy}\n aria-describedby={ariaDescribedBy}\n aria-invalid={ariaInvalid}\n aria-required={required || undefined}\n aria-disabled={disabled || undefined}\n data-size={size}\n data-disabled={disabled || undefined}\n data-readonly={readOnly || undefined}\n data-invalid={isInvalid || undefined}\n >\n {label != null && (\n <label\n id={labelId}\n htmlFor={inputId}\n className=\"puzzle-token-input__label\"\n >\n {label}\n </label>\n )}\n\n <div className=\"puzzle-token-input__slots\">\n {Array.from({ length }, (_, index) => {\n const character = currentValue[index] ?? \"\";\n\n const slotLabel = ariaLabel\n ? `${ariaLabel}, character ${index + 1} of ${length}`\n : index === 0 && (id != null || label != null)\n ? undefined\n : `Character ${index + 1} of ${length}`;\n\n return (\n <input\n key={index}\n ref={\n index === 0\n ? setFirstInputRef\n : (node) => {\n inputRefs.current[index] = node;\n }\n }\n id={index === 0 ? inputId : undefined}\n data-token-index={index}\n className=\"puzzle-token-input__slot\"\n type={mask ? \"password\" : \"text\"}\n inputMode={inputMode ?? (mode === \"numeric\" ? \"numeric\" : \"text\")}\n autoComplete={index === 0 ? autoComplete : \"off\"}\n autoCapitalize=\"off\"\n spellCheck={false}\n maxLength={length}\n value={character}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n tabIndex={index === resolvedActiveIndex ? 0 : -1}\n aria-label={slotLabel}\n aria-describedby={ariaDescribedBy}\n aria-invalid={ariaInvalid}\n onChange={(event) => handleChange(index, event)}\n onPaste={(event) => handlePaste(index, event)}\n onKeyDown={(event) => handleKeyDown(index, event)}\n onFocus={handleFocus}\n onBlur={handleBlur}\n />\n );\n })}\n </div>\n\n {name && (\n <input\n type=\"hidden\"\n name={name}\n form={form}\n value={currentValue}\n disabled={disabled}\n />\n )}\n </div>\n );\n};\n\nexport { TokenInput };\n\nexport type {\n TokenInputProps,\n TokenInputMode,\n TokenInputSize,\n} from \"./token-input.types\";\n"],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
.puzzle-token-input {
|
|
2
|
+
align-items: flex-start;
|
|
3
|
+
gap: var(--puzzle-space-2, .5rem);
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
width: fit-content;
|
|
6
|
+
min-width: 0;
|
|
7
|
+
max-width: 100%;
|
|
8
|
+
display: flex;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.puzzle-token-input__label {
|
|
12
|
+
color: var(--puzzle-color-text);
|
|
13
|
+
font-size: var(--puzzle-font-size-sm, .875rem);
|
|
14
|
+
font-weight: var(--puzzle-font-weight-medium, 500);
|
|
15
|
+
line-height: var(--puzzle-font-line-height-normal, 1.5);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.puzzle-token-input__slots {
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: var(--puzzle-space-2, .5rem);
|
|
21
|
+
direction: ltr;
|
|
22
|
+
flex-wrap: nowrap;
|
|
23
|
+
max-width: 100%;
|
|
24
|
+
display: flex;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.puzzle-token-input__slot {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
border: var(--puzzle-border-width, 1px) solid var(--puzzle-color-border);
|
|
30
|
+
border-radius: var(--puzzle-radius-md, .5rem);
|
|
31
|
+
background: var(--puzzle-color-surface);
|
|
32
|
+
width: 2.5rem;
|
|
33
|
+
min-width: 0;
|
|
34
|
+
height: 2.75rem;
|
|
35
|
+
color: var(--puzzle-color-text);
|
|
36
|
+
font: inherit;
|
|
37
|
+
font-size: 1.125rem;
|
|
38
|
+
font-weight: var(--puzzle-font-weight-medium, 500);
|
|
39
|
+
text-align: center;
|
|
40
|
+
font-variant-numeric: tabular-nums;
|
|
41
|
+
caret-color: var(--puzzle-color-ring);
|
|
42
|
+
touch-action: manipulation;
|
|
43
|
+
-webkit-tap-highlight-color: transparent;
|
|
44
|
+
transition: border-color var(--puzzle-duration-fast, .15s) var(--puzzle-easing-standard, ease),
|
|
45
|
+
box-shadow var(--puzzle-duration-fast, .15s) var(--puzzle-easing-standard, ease),
|
|
46
|
+
background-color var(--puzzle-duration-fast, .15s) var(--puzzle-easing-standard, ease);
|
|
47
|
+
outline: none;
|
|
48
|
+
flex: none;
|
|
49
|
+
padding: 0;
|
|
50
|
+
line-height: 1;
|
|
51
|
+
display: block;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.puzzle-token-input__slot:hover:not(:disabled) {
|
|
55
|
+
border-color: var(--puzzle-color-text-muted);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.puzzle-token-input__slot:focus {
|
|
59
|
+
border-color: var(--puzzle-color-ring);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.puzzle-token-input__slot:focus-visible {
|
|
63
|
+
outline: 2px solid var(--puzzle-color-ring);
|
|
64
|
+
outline-offset: 2px;
|
|
65
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--puzzle-color-ring), transparent 82%);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.puzzle-token-input[data-invalid="true"] .puzzle-token-input__slot {
|
|
69
|
+
border-color: var(--puzzle-color-danger);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.puzzle-token-input[data-invalid="true"] .puzzle-token-input__slot:focus-visible {
|
|
73
|
+
outline-color: var(--puzzle-color-danger);
|
|
74
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--puzzle-color-danger), transparent 82%);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.puzzle-token-input[data-disabled="true"] {
|
|
78
|
+
cursor: not-allowed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.puzzle-token-input[data-disabled="true"] .puzzle-token-input__slot {
|
|
82
|
+
opacity: .6;
|
|
83
|
+
cursor: not-allowed;
|
|
84
|
+
background: var(--puzzle-color-surface-sunken);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.puzzle-token-input[data-readonly="true"] .puzzle-token-input__slot {
|
|
88
|
+
cursor: default;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.puzzle-token-input[data-size="sm"] .puzzle-token-input__slot {
|
|
92
|
+
width: 2rem;
|
|
93
|
+
height: 2.25rem;
|
|
94
|
+
font-size: var(--puzzle-font-size-sm, .875rem);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.puzzle-token-input[data-size="md"] .puzzle-token-input__slot {
|
|
98
|
+
width: 2.5rem;
|
|
99
|
+
height: 2.75rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.puzzle-token-input[data-size="lg"] .puzzle-token-input__slot {
|
|
103
|
+
width: 3rem;
|
|
104
|
+
height: 3.25rem;
|
|
105
|
+
font-size: 1.375rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (width <= 360px) {
|
|
109
|
+
.puzzle-token-input__slots {
|
|
110
|
+
gap: var(--puzzle-space-1, .25rem);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.puzzle-token-input[data-size="md"] .puzzle-token-input__slot {
|
|
114
|
+
width: 2.25rem;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@media (forced-colors: active) {
|
|
119
|
+
.puzzle-token-input__slot {
|
|
120
|
+
color: fieldtext;
|
|
121
|
+
forced-color-adjust: auto;
|
|
122
|
+
background: field;
|
|
123
|
+
border-color: fieldtext;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.puzzle-token-input__slot:focus-visible {
|
|
127
|
+
outline-offset: 2px;
|
|
128
|
+
outline: 2px solid highlight;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.puzzle-token-input__slot:disabled {
|
|
132
|
+
color: graytext;
|
|
133
|
+
border-color: graytext;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.puzzle-token-input[data-invalid="true"] .puzzle-token-input__slot {
|
|
137
|
+
border-color: mark;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@media (prefers-reduced-motion: reduce) {
|
|
142
|
+
.puzzle-token-input__slot {
|
|
143
|
+
transition: none;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, FocusEventHandler, InputHTMLAttributes, KeyboardEventHandler, ReactNode, Ref } from "react";
|
|
2
|
+
type TokenInputMode = "numeric" | "alphanumeric";
|
|
3
|
+
type TokenInputSize = "sm" | "md" | "lg";
|
|
4
|
+
type TokenInputProps = Omit<ComponentPropsWithoutRef<"div">, "children" | "id" | "onChange" | "onFocus" | "onBlur" | "onKeyDown"> & {
|
|
5
|
+
/** Number of characters in the token. Defaults to 6. */
|
|
6
|
+
length?: number;
|
|
7
|
+
/** Controlled token value. */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Initial value for uncontrolled usage. */
|
|
10
|
+
defaultValue?: string;
|
|
11
|
+
/** Called whenever the token value changes. */
|
|
12
|
+
onValueChange?: (value: string) => void;
|
|
13
|
+
/** Called when the token transitions from incomplete to complete. */
|
|
14
|
+
onComplete?: (value: string) => void;
|
|
15
|
+
/** Allowed characters. Defaults to numeric. */
|
|
16
|
+
mode?: TokenInputMode;
|
|
17
|
+
/** Masks the entered characters. */
|
|
18
|
+
mask?: boolean;
|
|
19
|
+
/** Disables all inputs. */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Makes all inputs read-only. */
|
|
22
|
+
readOnly?: boolean;
|
|
23
|
+
/** Marks every character input as required. */
|
|
24
|
+
required?: boolean;
|
|
25
|
+
/** Visual size of the individual character slots. */
|
|
26
|
+
size?: TokenInputSize;
|
|
27
|
+
/** Visible label associated with the first input. */
|
|
28
|
+
label?: ReactNode;
|
|
29
|
+
/** ID applied to the first native input. */
|
|
30
|
+
id?: string;
|
|
31
|
+
/** Name used for native form submission. */
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Associates the hidden form input with a form. */
|
|
34
|
+
form?: string;
|
|
35
|
+
/** Input mode for the native character inputs. */
|
|
36
|
+
inputMode?: InputHTMLAttributes<HTMLInputElement>["inputMode"];
|
|
37
|
+
/** Autocomplete mode for the first character input. */
|
|
38
|
+
autoComplete?: InputHTMLAttributes<HTMLInputElement>["autoComplete"];
|
|
39
|
+
/** Ref forwarded to the first native input. */
|
|
40
|
+
ref?: Ref<HTMLInputElement>;
|
|
41
|
+
/** Called when an individual character input receives focus. */
|
|
42
|
+
onFocus?: FocusEventHandler<HTMLInputElement>;
|
|
43
|
+
/** Called when an individual character input loses focus. */
|
|
44
|
+
onBlur?: FocusEventHandler<HTMLInputElement>;
|
|
45
|
+
/** Called when an individual character input receives a key event. */
|
|
46
|
+
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
|
|
47
|
+
};
|
|
48
|
+
export type { TokenInputMode, TokenInputProps, TokenInputSize };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, FocusEventHandler, InputHTMLAttributes, KeyboardEventHandler, ReactNode, Ref } from "react";
|
|
2
|
+
type TokenInputMode = "numeric" | "alphanumeric";
|
|
3
|
+
type TokenInputSize = "sm" | "md" | "lg";
|
|
4
|
+
type TokenInputProps = Omit<ComponentPropsWithoutRef<"div">, "children" | "id" | "onChange" | "onFocus" | "onBlur" | "onKeyDown"> & {
|
|
5
|
+
/** Number of characters in the token. Defaults to 6. */
|
|
6
|
+
length?: number;
|
|
7
|
+
/** Controlled token value. */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Initial value for uncontrolled usage. */
|
|
10
|
+
defaultValue?: string;
|
|
11
|
+
/** Called whenever the token value changes. */
|
|
12
|
+
onValueChange?: (value: string) => void;
|
|
13
|
+
/** Called when the token transitions from incomplete to complete. */
|
|
14
|
+
onComplete?: (value: string) => void;
|
|
15
|
+
/** Allowed characters. Defaults to numeric. */
|
|
16
|
+
mode?: TokenInputMode;
|
|
17
|
+
/** Masks the entered characters. */
|
|
18
|
+
mask?: boolean;
|
|
19
|
+
/** Disables all inputs. */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Makes all inputs read-only. */
|
|
22
|
+
readOnly?: boolean;
|
|
23
|
+
/** Marks every character input as required. */
|
|
24
|
+
required?: boolean;
|
|
25
|
+
/** Visual size of the individual character slots. */
|
|
26
|
+
size?: TokenInputSize;
|
|
27
|
+
/** Visible label associated with the first input. */
|
|
28
|
+
label?: ReactNode;
|
|
29
|
+
/** ID applied to the first native input. */
|
|
30
|
+
id?: string;
|
|
31
|
+
/** Name used for native form submission. */
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Associates the hidden form input with a form. */
|
|
34
|
+
form?: string;
|
|
35
|
+
/** Input mode for the native character inputs. */
|
|
36
|
+
inputMode?: InputHTMLAttributes<HTMLInputElement>["inputMode"];
|
|
37
|
+
/** Autocomplete mode for the first character input. */
|
|
38
|
+
autoComplete?: InputHTMLAttributes<HTMLInputElement>["autoComplete"];
|
|
39
|
+
/** Ref forwarded to the first native input. */
|
|
40
|
+
ref?: Ref<HTMLInputElement>;
|
|
41
|
+
/** Called when an individual character input receives focus. */
|
|
42
|
+
onFocus?: FocusEventHandler<HTMLInputElement>;
|
|
43
|
+
/** Called when an individual character input loses focus. */
|
|
44
|
+
onBlur?: FocusEventHandler<HTMLInputElement>;
|
|
45
|
+
/** Called when an individual character input receives a key event. */
|
|
46
|
+
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
|
|
47
|
+
};
|
|
48
|
+
export type { TokenInputMode, TokenInputProps, TokenInputSize };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/components/forms/token-input/token-input.utils.ts
|
|
2
|
+
const sanitizeToken = (value, length, mode = "numeric") => {
|
|
3
|
+
return (mode === "numeric" ? value.replace(/[^0-9]/g, "") : value.replace(/[^a-zA-Z0-9]/g, "")).slice(0, Math.max(0, length));
|
|
4
|
+
};
|
|
5
|
+
const getNextActiveIndex = (value, length) => Math.min(value.length, length - 1);
|
|
6
|
+
const getPreviousActiveIndex = (index) => Math.max(0, index - 1);
|
|
7
|
+
const isTokenComplete = (value, length) => value.length === length;
|
|
8
|
+
const removeTokenCharacter = (value, index) => value.slice(0, index) + value.slice(index + 1);
|
|
9
|
+
const replaceTokenCharacter = (value, index, character, length, mode) => {
|
|
10
|
+
const normalized = sanitizeToken(character, length, mode);
|
|
11
|
+
if (!normalized) return removeTokenCharacter(value, index);
|
|
12
|
+
return sanitizeToken(value.slice(0, index) + normalized[0] + value.slice(index + 1), length, mode);
|
|
13
|
+
};
|
|
14
|
+
const insertTokenCharacters = (value, index, insertedValue, length, mode) => {
|
|
15
|
+
const normalized = sanitizeToken(insertedValue, length, mode);
|
|
16
|
+
if (!normalized) return value;
|
|
17
|
+
if (normalized.length >= length) return normalized.slice(0, length);
|
|
18
|
+
if (normalized.length === 1) return replaceTokenCharacter(value, index, normalized, length, mode);
|
|
19
|
+
const start = Math.max(0, Math.min(index, length));
|
|
20
|
+
return sanitizeToken(value.slice(0, start) + normalized + value.slice(start + normalized.length), length, mode);
|
|
21
|
+
};
|
|
22
|
+
exports.getNextActiveIndex = getNextActiveIndex;
|
|
23
|
+
exports.getPreviousActiveIndex = getPreviousActiveIndex;
|
|
24
|
+
exports.insertTokenCharacters = insertTokenCharacters;
|
|
25
|
+
exports.isTokenComplete = isTokenComplete;
|
|
26
|
+
exports.removeTokenCharacter = removeTokenCharacter;
|
|
27
|
+
exports.replaceTokenCharacter = replaceTokenCharacter;
|
|
28
|
+
exports.sanitizeToken = sanitizeToken;
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=token-input.utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-input.utils.cjs","names":[],"sources":["../../../../src/components/forms/token-input/token-input.utils.ts"],"sourcesContent":["import type { TokenInputMode } from \"./token-input.types\";\n\nconst sanitizeToken = (\n value: string,\n length: number,\n mode: TokenInputMode = \"numeric\",\n): string => {\n const normalized =\n mode === \"numeric\"\n ? value.replace(/[^0-9]/g, \"\")\n : value.replace(/[^a-zA-Z0-9]/g, \"\");\n\n return normalized.slice(0, Math.max(0, length));\n};\n\nconst getNextActiveIndex = (value: string, length: number): number =>\n Math.min(value.length, length - 1);\n\nconst getPreviousActiveIndex = (index: number): number =>\n Math.max(0, index - 1);\n\nconst isTokenComplete = (value: string, length: number): boolean =>\n value.length === length;\n\nconst removeTokenCharacter = (value: string, index: number): string =>\n value.slice(0, index) + value.slice(index + 1);\n\nconst replaceTokenCharacter = (\n value: string,\n index: number,\n character: string,\n length: number,\n mode: TokenInputMode,\n): string => {\n const normalized = sanitizeToken(character, length, mode);\n\n if (!normalized) {\n return removeTokenCharacter(value, index);\n }\n\n return sanitizeToken(\n value.slice(0, index) + normalized[0] + value.slice(index + 1),\n length,\n mode,\n );\n};\n\nconst insertTokenCharacters = (\n value: string,\n index: number,\n insertedValue: string,\n length: number,\n mode: TokenInputMode,\n): string => {\n const normalized = sanitizeToken(insertedValue, length, mode);\n\n if (!normalized) {\n return value;\n }\n\n // A complete token always replaces the existing token.\n if (normalized.length >= length) {\n return normalized.slice(0, length);\n }\n\n // A single character replaces the current position.\n if (normalized.length === 1) {\n return replaceTokenCharacter(value, index, normalized, length, mode);\n }\n\n // Multiple characters are inserted at the current position.\n const start = Math.max(0, Math.min(index, length));\n\n return sanitizeToken(\n value.slice(0, start) + normalized + value.slice(start + normalized.length),\n length,\n mode,\n );\n};\n\nexport {\n sanitizeToken,\n getNextActiveIndex,\n getPreviousActiveIndex,\n isTokenComplete,\n removeTokenCharacter,\n replaceTokenCharacter,\n insertTokenCharacters,\n};\n"],"mappings":";AAEA,MAAM,iBACJ,OACA,QACA,OAAuB,cACZ;CAMX,QAJE,SAAS,YACL,MAAM,QAAQ,WAAW,EAAE,IAC3B,MAAM,QAAQ,iBAAiB,EAAE,EAAA,CAErB,MAAM,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC;AAChD;AAEA,MAAM,sBAAsB,OAAe,WACzC,KAAK,IAAI,MAAM,QAAQ,SAAS,CAAC;AAEnC,MAAM,0BAA0B,UAC9B,KAAK,IAAI,GAAG,QAAQ,CAAC;AAEvB,MAAM,mBAAmB,OAAe,WACtC,MAAM,WAAW;AAEnB,MAAM,wBAAwB,OAAe,UAC3C,MAAM,MAAM,GAAG,KAAK,IAAI,MAAM,MAAM,QAAQ,CAAC;AAE/C,MAAM,yBACJ,OACA,OACA,WACA,QACA,SACW;CACX,MAAM,aAAa,cAAc,WAAW,QAAQ,IAAI;CAExD,IAAI,CAAC,YACH,OAAO,qBAAqB,OAAO,KAAK;CAG1C,OAAO,cACL,MAAM,MAAM,GAAG,KAAK,IAAI,WAAW,KAAK,MAAM,MAAM,QAAQ,CAAC,GAC7D,QACA,IACF;AACF;AAEA,MAAM,yBACJ,OACA,OACA,eACA,QACA,SACW;CACX,MAAM,aAAa,cAAc,eAAe,QAAQ,IAAI;CAE5D,IAAI,CAAC,YACH,OAAO;CAIT,IAAI,WAAW,UAAU,QACvB,OAAO,WAAW,MAAM,GAAG,MAAM;CAInC,IAAI,WAAW,WAAW,GACxB,OAAO,sBAAsB,OAAO,OAAO,YAAY,QAAQ,IAAI;CAIrE,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM,CAAC;CAEjD,OAAO,cACL,MAAM,MAAM,GAAG,KAAK,IAAI,aAAa,MAAM,MAAM,QAAQ,WAAW,MAAM,GAC1E,QACA,IACF;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/components/forms/token-input/token-input.utils.ts
|
|
2
|
+
const sanitizeToken = (value, length, mode = "numeric") => {
|
|
3
|
+
return (mode === "numeric" ? value.replace(/[^0-9]/g, "") : value.replace(/[^a-zA-Z0-9]/g, "")).slice(0, Math.max(0, length));
|
|
4
|
+
};
|
|
5
|
+
const getNextActiveIndex = (value, length) => Math.min(value.length, length - 1);
|
|
6
|
+
const getPreviousActiveIndex = (index) => Math.max(0, index - 1);
|
|
7
|
+
const isTokenComplete = (value, length) => value.length === length;
|
|
8
|
+
const removeTokenCharacter = (value, index) => value.slice(0, index) + value.slice(index + 1);
|
|
9
|
+
const replaceTokenCharacter = (value, index, character, length, mode) => {
|
|
10
|
+
const normalized = sanitizeToken(character, length, mode);
|
|
11
|
+
if (!normalized) return removeTokenCharacter(value, index);
|
|
12
|
+
return sanitizeToken(value.slice(0, index) + normalized[0] + value.slice(index + 1), length, mode);
|
|
13
|
+
};
|
|
14
|
+
const insertTokenCharacters = (value, index, insertedValue, length, mode) => {
|
|
15
|
+
const normalized = sanitizeToken(insertedValue, length, mode);
|
|
16
|
+
if (!normalized) return value;
|
|
17
|
+
if (normalized.length >= length) return normalized.slice(0, length);
|
|
18
|
+
if (normalized.length === 1) return replaceTokenCharacter(value, index, normalized, length, mode);
|
|
19
|
+
const start = Math.max(0, Math.min(index, length));
|
|
20
|
+
return sanitizeToken(value.slice(0, start) + normalized + value.slice(start + normalized.length), length, mode);
|
|
21
|
+
};
|
|
22
|
+
export { getNextActiveIndex, getPreviousActiveIndex, insertTokenCharacters, isTokenComplete, removeTokenCharacter, replaceTokenCharacter, sanitizeToken };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=token-input.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-input.utils.js","names":[],"sources":["../../../../src/components/forms/token-input/token-input.utils.ts"],"sourcesContent":["import type { TokenInputMode } from \"./token-input.types\";\n\nconst sanitizeToken = (\n value: string,\n length: number,\n mode: TokenInputMode = \"numeric\",\n): string => {\n const normalized =\n mode === \"numeric\"\n ? value.replace(/[^0-9]/g, \"\")\n : value.replace(/[^a-zA-Z0-9]/g, \"\");\n\n return normalized.slice(0, Math.max(0, length));\n};\n\nconst getNextActiveIndex = (value: string, length: number): number =>\n Math.min(value.length, length - 1);\n\nconst getPreviousActiveIndex = (index: number): number =>\n Math.max(0, index - 1);\n\nconst isTokenComplete = (value: string, length: number): boolean =>\n value.length === length;\n\nconst removeTokenCharacter = (value: string, index: number): string =>\n value.slice(0, index) + value.slice(index + 1);\n\nconst replaceTokenCharacter = (\n value: string,\n index: number,\n character: string,\n length: number,\n mode: TokenInputMode,\n): string => {\n const normalized = sanitizeToken(character, length, mode);\n\n if (!normalized) {\n return removeTokenCharacter(value, index);\n }\n\n return sanitizeToken(\n value.slice(0, index) + normalized[0] + value.slice(index + 1),\n length,\n mode,\n );\n};\n\nconst insertTokenCharacters = (\n value: string,\n index: number,\n insertedValue: string,\n length: number,\n mode: TokenInputMode,\n): string => {\n const normalized = sanitizeToken(insertedValue, length, mode);\n\n if (!normalized) {\n return value;\n }\n\n // A complete token always replaces the existing token.\n if (normalized.length >= length) {\n return normalized.slice(0, length);\n }\n\n // A single character replaces the current position.\n if (normalized.length === 1) {\n return replaceTokenCharacter(value, index, normalized, length, mode);\n }\n\n // Multiple characters are inserted at the current position.\n const start = Math.max(0, Math.min(index, length));\n\n return sanitizeToken(\n value.slice(0, start) + normalized + value.slice(start + normalized.length),\n length,\n mode,\n );\n};\n\nexport {\n sanitizeToken,\n getNextActiveIndex,\n getPreviousActiveIndex,\n isTokenComplete,\n removeTokenCharacter,\n replaceTokenCharacter,\n insertTokenCharacters,\n};\n"],"mappings":";AAEA,MAAM,iBACJ,OACA,QACA,OAAuB,cACZ;CAMX,QAJE,SAAS,YACL,MAAM,QAAQ,WAAW,EAAE,IAC3B,MAAM,QAAQ,iBAAiB,EAAE,EAAA,CAErB,MAAM,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC;AAChD;AAEA,MAAM,sBAAsB,OAAe,WACzC,KAAK,IAAI,MAAM,QAAQ,SAAS,CAAC;AAEnC,MAAM,0BAA0B,UAC9B,KAAK,IAAI,GAAG,QAAQ,CAAC;AAEvB,MAAM,mBAAmB,OAAe,WACtC,MAAM,WAAW;AAEnB,MAAM,wBAAwB,OAAe,UAC3C,MAAM,MAAM,GAAG,KAAK,IAAI,MAAM,MAAM,QAAQ,CAAC;AAE/C,MAAM,yBACJ,OACA,OACA,WACA,QACA,SACW;CACX,MAAM,aAAa,cAAc,WAAW,QAAQ,IAAI;CAExD,IAAI,CAAC,YACH,OAAO,qBAAqB,OAAO,KAAK;CAG1C,OAAO,cACL,MAAM,MAAM,GAAG,KAAK,IAAI,WAAW,KAAK,MAAM,MAAM,QAAQ,CAAC,GAC7D,QACA,IACF;AACF;AAEA,MAAM,yBACJ,OACA,OACA,eACA,QACA,SACW;CACX,MAAM,aAAa,cAAc,eAAe,QAAQ,IAAI;CAE5D,IAAI,CAAC,YACH,OAAO;CAIT,IAAI,WAAW,UAAU,QACvB,OAAO,WAAW,MAAM,GAAG,MAAM;CAInC,IAAI,WAAW,WAAW,GACxB,OAAO,sBAAsB,OAAO,OAAO,YAAY,QAAQ,IAAI;CAIrE,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM,CAAC;CAEjD,OAAO,cACL,MAAM,MAAM,GAAG,KAAK,IAAI,aAAa,MAAM,MAAM,QAAQ,WAAW,MAAM,GAC1E,QACA,IACF;AACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/components/layout/aspect-ratio/aspect-ratio.utils.ts
|
|
2
|
+
const getAspectRatioStyle = (ratio, style) => {
|
|
3
|
+
if (!Number.isFinite(ratio) || ratio <= 0) throw new RangeError("AspectRatio: ratio must be a positive finite number.");
|
|
4
|
+
return {
|
|
5
|
+
...style,
|
|
6
|
+
aspectRatio: ratio
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
exports.getAspectRatioStyle = getAspectRatioStyle;
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=aspect-ratio.utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aspect-ratio.utils.cjs","names":[],"sources":["../../../../src/components/layout/aspect-ratio/aspect-ratio.utils.ts"],"sourcesContent":["import type { CSSProperties } from \"react\";\n\nconst getAspectRatioStyle = (\n ratio: number,\n style?: CSSProperties,\n): CSSProperties => {\n if (!Number.isFinite(ratio) || ratio <= 0) {\n throw new RangeError(\n \"AspectRatio: ratio must be a positive finite number.\",\n );\n }\n\n return {\n ...style,\n aspectRatio: ratio,\n };\n};\n\nexport { getAspectRatioStyle };\n"],"mappings":";AAEA,MAAM,uBACJ,OACA,UACkB;CAClB,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GACtC,MAAM,IAAI,WACR,sDACF;CAGF,OAAO;EACL,GAAG;EACH,aAAa;CACf;AACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/components/layout/aspect-ratio/aspect-ratio.utils.ts
|
|
2
|
+
const getAspectRatioStyle = (ratio, style) => {
|
|
3
|
+
if (!Number.isFinite(ratio) || ratio <= 0) throw new RangeError("AspectRatio: ratio must be a positive finite number.");
|
|
4
|
+
return {
|
|
5
|
+
...style,
|
|
6
|
+
aspectRatio: ratio
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export { getAspectRatioStyle };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=aspect-ratio.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aspect-ratio.utils.js","names":[],"sources":["../../../../src/components/layout/aspect-ratio/aspect-ratio.utils.ts"],"sourcesContent":["import type { CSSProperties } from \"react\";\n\nconst getAspectRatioStyle = (\n ratio: number,\n style?: CSSProperties,\n): CSSProperties => {\n if (!Number.isFinite(ratio) || ratio <= 0) {\n throw new RangeError(\n \"AspectRatio: ratio must be a positive finite number.\",\n );\n }\n\n return {\n ...style,\n aspectRatio: ratio,\n };\n};\n\nexport { getAspectRatioStyle };\n"],"mappings":";AAEA,MAAM,uBACJ,OACA,UACkB;CAClB,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GACtC,MAAM,IAAI,WACR,sDACF;CAGF,OAAO;EACL,GAAG;EACH,aAAa;CACf;AACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const require_aspect_ratio_utils = require("./aspect-ratio.utils.cjs");
|
|
2
|
+
;/* empty css */
|
|
3
|
+
let _puzzle_ui_core = require("@puzzle-ui/core");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
// src/components/layout/aspect-ratio/index.tsx
|
|
6
|
+
const AspectRatio = ({ as, ratio = 1, className, style, children, ...rest }) => {
|
|
7
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(as ?? "div", {
|
|
8
|
+
className: (0, _puzzle_ui_core.cn)("puzzle-aspect-ratio", className),
|
|
9
|
+
style: require_aspect_ratio_utils.getAspectRatioStyle(ratio, style),
|
|
10
|
+
...rest,
|
|
11
|
+
children
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
exports.AspectRatio = AspectRatio;
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["cn","getAspectRatioStyle"],"sources":["../../../../src/components/layout/aspect-ratio/index.tsx"],"sourcesContent":["import type {\n ComponentPropsWithRef,\n CSSProperties,\n ElementType,\n ReactNode,\n} from \"react\";\nimport { cn } from \"@puzzle-ui/core\";\nimport { getAspectRatioStyle } from \"./aspect-ratio.utils\";\nimport \"./aspect-ratio.css\";\n\ntype AspectRatioOwnProps<C extends ElementType> = {\n as?: C;\n ratio?: number;\n className?: string;\n style?: CSSProperties;\n children?: ReactNode;\n};\n\ntype AspectRatioProps<C extends ElementType = \"div\"> = AspectRatioOwnProps<C> &\n Omit<ComponentPropsWithRef<C>, keyof AspectRatioOwnProps<C> | \"as\">;\n\nconst AspectRatio = <C extends ElementType = \"div\">({\n as,\n ratio = 1,\n className,\n style,\n children,\n ...rest\n}: AspectRatioProps<C>) => {\n const Component = (as ?? \"div\") as ElementType;\n\n return (\n <Component\n className={cn(\"puzzle-aspect-ratio\", className)}\n style={getAspectRatioStyle(ratio, style)}\n {...rest}\n >\n {children}\n </Component>\n );\n};\n\nexport { AspectRatio, type AspectRatioProps };\n"],"mappings":";;;;;AAqBA,MAAM,eAA8C,EAClD,IACA,QAAQ,GACR,WACA,OACA,UACA,GAAG,WACsB;CAGzB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAHiB,MAAM,OAGvB;EACE,YAAA,GAAWA,gBAAAA,GAAAA,CAAG,uBAAuB,SAAS;EAC9C,OAAOC,2BAAAA,oBAAoB,OAAO,KAAK;EACvC,GAAI;EAEH;CACQ,CAAA;AAEf"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CSSProperties, ComponentPropsWithRef, ElementType, ReactNode } from "react";
|
|
2
|
+
type AspectRatioOwnProps<C extends ElementType> = {
|
|
3
|
+
as?: C;
|
|
4
|
+
ratio?: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
style?: CSSProperties;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
type AspectRatioProps<C extends ElementType = "div"> = AspectRatioOwnProps<C> & Omit<ComponentPropsWithRef<C>, keyof AspectRatioOwnProps<C> | "as">;
|
|
10
|
+
declare const AspectRatio: <C extends ElementType = "div">({ as, ratio, className, style, children, ...rest }: AspectRatioProps<C>) => import("react").JSX.Element;
|
|
11
|
+
export { AspectRatio, type AspectRatioProps };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CSSProperties, ComponentPropsWithRef, ElementType, ReactNode } from "react";
|
|
2
|
+
type AspectRatioOwnProps<C extends ElementType> = {
|
|
3
|
+
as?: C;
|
|
4
|
+
ratio?: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
style?: CSSProperties;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
type AspectRatioProps<C extends ElementType = "div"> = AspectRatioOwnProps<C> & Omit<ComponentPropsWithRef<C>, keyof AspectRatioOwnProps<C> | "as">;
|
|
10
|
+
declare const AspectRatio: <C extends ElementType = "div">({ as, ratio, className, style, children, ...rest }: AspectRatioProps<C>) => import("react").JSX.Element;
|
|
11
|
+
export { AspectRatio, type AspectRatioProps };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getAspectRatioStyle } from "./aspect-ratio.utils.js";
|
|
2
|
+
import "./aspect-ratio.css";
|
|
3
|
+
import { cn } from "@puzzle-ui/core";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
// src/components/layout/aspect-ratio/index.tsx
|
|
6
|
+
const AspectRatio = ({ as, ratio = 1, className, style, children, ...rest }) => {
|
|
7
|
+
return /* @__PURE__ */ jsx(as ?? "div", {
|
|
8
|
+
className: cn("puzzle-aspect-ratio", className),
|
|
9
|
+
style: getAspectRatioStyle(ratio, style),
|
|
10
|
+
...rest,
|
|
11
|
+
children
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export { AspectRatio };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=index.js.map
|