awesome-auth-input 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,455 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const reactDom = require("react-dom");
6
+ const AuthCodeField = react.forwardRef(
7
+ function AuthCodeField2({
8
+ children,
9
+ name,
10
+ validation = { type: "numeric" },
11
+ autoSubmit = true,
12
+ onComplete,
13
+ value: userValue,
14
+ onValueChange: userOnValueChange,
15
+ defaultValue,
16
+ type = "text",
17
+ disabled = false,
18
+ ...divProps
19
+ }, forwardedRef) {
20
+ const inputElements = react.useRef(/* @__PURE__ */ new Map());
21
+ const [value, setValue] = useControllableState({
22
+ value: userValue !== void 0 ? Array.from(userValue) : void 0,
23
+ defaultValue: defaultValue ? Array.from(defaultValue) : [],
24
+ onValueChange: userOnValueChange ? (chars) => userOnValueChange(chars.join("")) : void 0
25
+ });
26
+ const validationConfig = validation.type == "custom" ? validation : defaultPatternInputMap[validation.type];
27
+ const [focusedIndex, setFocusedIndex] = react.useState(0);
28
+ const [inputCount, setInputCount] = react.useState(0);
29
+ const registerInputRef = react.useCallback((ref) => {
30
+ if (ref) {
31
+ if (!inputElements.current.has(ref)) {
32
+ inputElements.current.set(ref, true);
33
+ setInputCount(inputElements.current.size);
34
+ }
35
+ }
36
+ }, []);
37
+ const validateChar = react.useCallback(
38
+ (char) => {
39
+ if (!char) return true;
40
+ const regex = validationConfig.regex;
41
+ return regex.test(char);
42
+ },
43
+ [validationConfig]
44
+ );
45
+ const validateString = react.useCallback(
46
+ (str) => {
47
+ return Array.from(str).filter((char) => validateChar(char)).join("");
48
+ },
49
+ [validateChar]
50
+ );
51
+ const hiddenInputRef = react.useRef(null);
52
+ const dispatch = (action) => {
53
+ var _a, _b;
54
+ const inputs = Array.from(inputElements.current.keys());
55
+ switch (action.type) {
56
+ case "TYPE_CHAR": {
57
+ const { char, index: targetIndex } = action;
58
+ if (!validateChar(char)) return;
59
+ const hasExistingValue = !!value[targetIndex];
60
+ const firstEmptyIndex = Array.from({
61
+ length: inputs.length
62
+ }).findIndex((_, i) => !value[i]);
63
+ const insertionIndex = hasExistingValue || firstEmptyIndex === -1 ? targetIndex : firstEmptyIndex;
64
+ const newValue = Array.from({ length: inputs.length }, (_, i) => {
65
+ if (i === insertionIndex) return char;
66
+ return value[i] ?? "";
67
+ });
68
+ reactDom.flushSync(() => setValue(newValue));
69
+ if (insertionIndex < inputs.length - 1) {
70
+ requestAnimationFrame(
71
+ () => focusInput(inputs.at(insertionIndex + 1))
72
+ );
73
+ } else {
74
+ requestAnimationFrame(() => focusInput(inputs.at(insertionIndex)));
75
+ }
76
+ return;
77
+ }
78
+ case "REMOVE_CHAR": {
79
+ const { index } = action;
80
+ const totalInputLength = inputs.length;
81
+ const leftPortion = value.slice(0, index);
82
+ const rightPortion = value.slice(index + 1);
83
+ const compacted = leftPortion.concat(rightPortion);
84
+ const emptySlots = Array(
85
+ Math.max(0, totalInputLength - compacted.length)
86
+ ).fill("");
87
+ reactDom.flushSync(() => setValue(compacted.concat(emptySlots)));
88
+ if (index != 0) {
89
+ focusInput(inputs.at(index - 1));
90
+ } else {
91
+ focusInput(inputs.at(0));
92
+ }
93
+ return;
94
+ }
95
+ case "CLEAR_CHAR": {
96
+ const { index } = action;
97
+ const newValue = value.slice();
98
+ newValue[index] = "";
99
+ reactDom.flushSync(() => setValue(newValue));
100
+ return;
101
+ }
102
+ case "SUBMIT_CODE": {
103
+ const currentValue = value.join("");
104
+ onComplete == null ? void 0 : onComplete(currentValue);
105
+ (_b = (_a = hiddenInputRef.current) == null ? void 0 : _a.form) == null ? void 0 : _b.requestSubmit();
106
+ break;
107
+ }
108
+ case "PASTE": {
109
+ const { value: value2 } = action;
110
+ const totalInputLength = inputs.length;
111
+ const validatedValue = validateString(value2);
112
+ if (validatedValue.length === 0) return;
113
+ const pastedCharacters = Array.from(value2).slice(0, inputs.length);
114
+ const emptySlots = Array(
115
+ Math.max(0, totalInputLength - value2.length)
116
+ ).fill("");
117
+ reactDom.flushSync(() => setValue([...pastedCharacters, ...emptySlots]));
118
+ if (pastedCharacters.length === totalInputLength) {
119
+ focusInput(inputs.at(-1));
120
+ } else {
121
+ focusInput(inputs.at(value2.length));
122
+ }
123
+ return;
124
+ }
125
+ case "NAVIGATE_PREVIOUS": {
126
+ const { index } = action;
127
+ if (index > 0) {
128
+ focusInput(inputs.at(index - 1));
129
+ }
130
+ return;
131
+ }
132
+ case "NAVIGATE_NEXT": {
133
+ const { index } = action;
134
+ if (value[index] && index < inputs.length - 1) {
135
+ focusInput(inputs.at(index + 1));
136
+ return;
137
+ }
138
+ const firstEmpty = Array.from({ length: inputs.length }).findIndex(
139
+ (_, i) => !value[i]
140
+ );
141
+ focusInput(inputs.at(firstEmpty));
142
+ return;
143
+ }
144
+ case "CLEAR_ALL": {
145
+ reactDom.flushSync(() => setValue([]));
146
+ focusInput(inputs.at(0));
147
+ return;
148
+ }
149
+ }
150
+ };
151
+ react.useEffect(() => {
152
+ var _a, _b;
153
+ const concatenatedValue = value.join("");
154
+ const isCodeFullyEntered = concatenatedValue.length === inputCount && value.every((v) => v !== "") && inputCount > 0;
155
+ if (!isCodeFullyEntered) return;
156
+ onComplete == null ? void 0 : onComplete(concatenatedValue);
157
+ if (autoSubmit) {
158
+ (_b = (_a = hiddenInputRef.current) == null ? void 0 : _a.form) == null ? void 0 : _b.requestSubmit();
159
+ }
160
+ }, [value, inputCount, onComplete, autoSubmit]);
161
+ const contextValue = react.useMemo(
162
+ () => ({
163
+ dispatch,
164
+ value,
165
+ registerInputRef,
166
+ name,
167
+ hiddenInputRef,
168
+ validation: validationConfig,
169
+ type,
170
+ focusedIndex,
171
+ setFocusedIndex,
172
+ disabled,
173
+ inputCount,
174
+ inputElements: inputElements.current
175
+ }),
176
+ [value, focusedIndex, disabled, name, type, inputCount]
177
+ );
178
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthCodeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
179
+ "div",
180
+ {
181
+ ref: forwardedRef,
182
+ onPaste: (event) => {
183
+ event.preventDefault();
184
+ const clipboardData = event.clipboardData;
185
+ const pastedText = clipboardData.getData("text/plain");
186
+ dispatch({ type: "PASTE", value: pastedText });
187
+ },
188
+ ...divProps,
189
+ children: [
190
+ /* @__PURE__ */ jsxRuntime.jsx(AuthCodeHiddenInput, {}),
191
+ children
192
+ ]
193
+ }
194
+ ) });
195
+ }
196
+ );
197
+ const AuthCodeHiddenInput = react.forwardRef(function AuthCodeHiddenInput2(props, ref) {
198
+ const context = useAuthCodeContext();
199
+ const { name, value, hiddenInputRef } = context;
200
+ const memoizedRefs = react.useCallback(mergeRefs(ref, hiddenInputRef), [
201
+ ref,
202
+ hiddenInputRef
203
+ ]);
204
+ return /* @__PURE__ */ jsxRuntime.jsx(
205
+ "input",
206
+ {
207
+ ref: memoizedRefs,
208
+ name,
209
+ value: value.join(""),
210
+ autoCapitalize: "off",
211
+ autoCorrect: "off",
212
+ autoSave: "off",
213
+ inputMode: "numeric",
214
+ autoComplete: "one-time-code",
215
+ required: true,
216
+ ...props,
217
+ type: "hidden"
218
+ }
219
+ );
220
+ });
221
+ function AuthCodeInput({ index, ...props }) {
222
+ const context = useAuthCodeContext();
223
+ const {
224
+ dispatch,
225
+ registerInputRef,
226
+ validation,
227
+ focusedIndex,
228
+ setFocusedIndex,
229
+ disabled,
230
+ inputCount
231
+ } = context;
232
+ const currentCharacter = context.value[index] || "";
233
+ const firstEmptyIndex = Array.from({ length: inputCount }).findIndex(
234
+ (_, i) => !context.value[i]
235
+ );
236
+ const nextEmptyOrLastIndex = firstEmptyIndex === -1 ? inputCount - 1 : firstEmptyIndex;
237
+ const mergedInputRef = react.useCallback(mergeRefs(registerInputRef), [
238
+ registerInputRef
239
+ ]);
240
+ return /* @__PURE__ */ jsxRuntime.jsx(
241
+ "input",
242
+ {
243
+ type: context.type,
244
+ tabIndex: index === focusedIndex ? 0 : -1,
245
+ ref: mergedInputRef,
246
+ inputMode: validation.inputMode,
247
+ pattern: validation.pattern,
248
+ "aria-label": `One Time Password Character ${index + 1} out of ${inputCount}`,
249
+ disabled,
250
+ autoComplete: "off",
251
+ autoCorrect: "off",
252
+ autoCapitalize: "off",
253
+ "data-1p-ignore": true,
254
+ "data-lpignore": "true",
255
+ "data-form-type": "other",
256
+ "data-bwignore": "true",
257
+ ...props,
258
+ onPointerDown: (event) => {
259
+ event.preventDefault();
260
+ const focusTargetIndex = Math.min(index, nextEmptyOrLastIndex);
261
+ const focusTargetElement = Array.from(context.inputElements.keys())[focusTargetIndex];
262
+ focusInput(focusTargetElement);
263
+ },
264
+ onFocus: (event) => {
265
+ event.target.select();
266
+ setFocusedIndex(index);
267
+ },
268
+ onInput: (event) => {
269
+ const newInputValue = event.currentTarget.value;
270
+ const browserInputType = event.nativeEvent.inputType;
271
+ const isPasteOperation = browserInputType === "insertFromPaste" || browserInputType === "insertFromDrop";
272
+ const isLongInput = newInputValue.length > 2;
273
+ if (isPasteOperation || isLongInput) {
274
+ event.preventDefault();
275
+ dispatch({ type: "PASTE", value: newInputValue });
276
+ }
277
+ },
278
+ onChange: (event) => {
279
+ const newInputValue = event.target.value;
280
+ if (event.target.validity.patternMismatch) return;
281
+ dispatch({ type: "TYPE_CHAR", char: newInputValue, index });
282
+ },
283
+ onKeyDown: mergeEventHandlers(props.onKeyDown, (event) => {
284
+ if (event.metaKey && event.key == "c") return;
285
+ if (isUndoShortcut(event) || isCopyShortcut(event) || isRedoShortcut(event)) {
286
+ event.preventDefault();
287
+ return;
288
+ }
289
+ if ((event.metaKey || event.ctrlKey) && event.key === "Backspace") {
290
+ dispatch({ type: "CLEAR_ALL" });
291
+ return;
292
+ }
293
+ switch (event.key) {
294
+ case "Delete":
295
+ case "Backspace": {
296
+ event.preventDefault();
297
+ dispatch({ type: "REMOVE_CHAR", index });
298
+ return;
299
+ }
300
+ case "Enter": {
301
+ dispatch({ type: "SUBMIT_CODE" });
302
+ return;
303
+ }
304
+ case "ArrowLeft": {
305
+ event.preventDefault();
306
+ dispatch({ type: "NAVIGATE_PREVIOUS", index });
307
+ return;
308
+ }
309
+ case "ArrowRight": {
310
+ event.preventDefault();
311
+ dispatch({ type: "NAVIGATE_NEXT", index });
312
+ return;
313
+ }
314
+ // Prevents up and down movements from unselecting
315
+ case "ArrowUp":
316
+ case "ArrowDown": {
317
+ event.preventDefault();
318
+ return;
319
+ }
320
+ default:
321
+ if (event.key === currentCharacter) {
322
+ dispatch({ type: "TYPE_CHAR", char: currentCharacter, index });
323
+ return;
324
+ }
325
+ if (event.metaKey || event.ctrlKey) {
326
+ return;
327
+ }
328
+ const hasValue = !!event.currentTarget.value;
329
+ const isFullySelected = event.currentTarget.selectionStart === 0 && event.currentTarget.selectionEnd != null && event.currentTarget.selectionEnd > 0;
330
+ if (hasValue && isFullySelected) {
331
+ if (event.key.length === 1) {
332
+ const isValid = validation.regex.test(event.key);
333
+ if (!isValid) {
334
+ event.preventDefault();
335
+ return;
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }),
341
+ value: currentCharacter
342
+ }
343
+ );
344
+ }
345
+ const AuthCodeContext = react.createContext(null);
346
+ function useAuthCodeContext() {
347
+ const value = react.useContext(AuthCodeContext);
348
+ if (!value) {
349
+ throw new Error(
350
+ "\n[awesome-auth-input] <AuthCode.Input> must be used within <AuthCode.Group>.\n\nMake sure all <AuthCode.Input> components are children of <AuthCode.Group>:\n\nExample:\n <AuthCode.Group>\n <AuthCode.Input index={0} />\n <AuthCode.Input index={1} />\n </AuthCode.Group>\n"
351
+ );
352
+ }
353
+ return value;
354
+ }
355
+ function mergeEventHandlers(currentHandler, nextHandler) {
356
+ return (event) => {
357
+ currentHandler == null ? void 0 : currentHandler(event);
358
+ if (!event.defaultPrevented) {
359
+ nextHandler == null ? void 0 : nextHandler(event);
360
+ }
361
+ };
362
+ }
363
+ function isUndoShortcut(event) {
364
+ return (event.metaKey || event.ctrlKey) && event.key === "z";
365
+ }
366
+ function isRedoShortcut(event) {
367
+ return (event.metaKey || event.ctrlKey) && (event.key === "y" || event.shiftKey && event.key === "z");
368
+ }
369
+ function isCopyShortcut(event) {
370
+ return (event.metaKey || event.ctrlKey) && event.key === "c";
371
+ }
372
+ function mergeRefs(...refs) {
373
+ return function(node) {
374
+ refs.forEach((ref) => {
375
+ if (typeof ref === "function") {
376
+ ref(node);
377
+ } else if (ref != null) {
378
+ ref.current = node;
379
+ }
380
+ });
381
+ };
382
+ }
383
+ const focusInput = (element) => {
384
+ if (!element) return;
385
+ if (element.disabled) return;
386
+ if (element.ownerDocument.activeElement === element) {
387
+ element.select();
388
+ } else {
389
+ element.focus();
390
+ }
391
+ };
392
+ function useControllableState({
393
+ defaultValue,
394
+ onValueChange,
395
+ value
396
+ }) {
397
+ const [internalState, setInternalState] = react.useState(defaultValue);
398
+ const isControlled = value !== void 0;
399
+ const currentValue = isControlled ? value : internalState;
400
+ const setState = react.useCallback(
401
+ (newValue) => {
402
+ if (!isControlled) {
403
+ setInternalState(newValue);
404
+ }
405
+ onValueChange == null ? void 0 : onValueChange(newValue);
406
+ },
407
+ [isControlled, onValueChange]
408
+ );
409
+ return [currentValue, setState];
410
+ }
411
+ const numericRegex = /^\d$/;
412
+ const alphaNumericRegex = /^[a-zA-Z\d]$/;
413
+ const alphaRegex = /^[a-zA-Z]$/;
414
+ const defaultPatternInputMap = {
415
+ alpha: {
416
+ type: "alpha",
417
+ regex: alphaRegex,
418
+ pattern: "[a-zA-Z]{1}",
419
+ inputMode: "text"
420
+ },
421
+ alphanumeric: {
422
+ type: "alphanumeric",
423
+ regex: alphaNumericRegex,
424
+ pattern: "[a-zA-Z0-9]{1}",
425
+ inputMode: "text"
426
+ },
427
+ numeric: {
428
+ type: "numeric",
429
+ regex: numericRegex,
430
+ pattern: "[0-9]{1}",
431
+ inputMode: "numeric"
432
+ }
433
+ };
434
+ const AuthCode = {
435
+ /**
436
+ * The root component that manages state and validation for auth code inputs.
437
+ * All `<AuthCode.Input>` components must be children of this component.
438
+ */
439
+ Group: AuthCodeField,
440
+ /**
441
+ * Individual input field for a single character of the auth code.
442
+ * Must be used within `<AuthCode.Group>`.
443
+ */
444
+ Input: AuthCodeInput,
445
+ /**
446
+ * Hidden input that contains the full auth code value for form submission.
447
+ * Automatically rendered by Group, but can be customized if needed.
448
+ */
449
+ HiddenInput: AuthCodeHiddenInput
450
+ };
451
+ exports.AuthCode = AuthCode;
452
+ exports.AuthCodeGroup = AuthCodeField;
453
+ exports.AuthCodeHiddenInput = AuthCodeHiddenInput;
454
+ exports.AuthCodeInput = AuthCodeInput;
455
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/AuthCode/OneTimePassword.tsx","../src/index.ts"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useRef,\n useState,\n type ReactNode,\n type Ref,\n useMemo,\n useEffect,\n forwardRef,\n} from \"react\";\nimport { flushSync } from \"react-dom\";\n\ninterface AuthCodeCustomField {\n /**\n * The child components, typically `<AuthCode.Input>` components.\n *\n * All `<AuthCode.Input>` components must be direct or nested children of `<AuthCode.Group>`.\n */\n children: ReactNode;\n /**\n * A string specifying a name for the input control. This name is submitted\n * along with the control's value when the form data is submitted.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name\n */\n name?: string;\n /**\n * Specifies what type of validation the user agent has to provide for each\n * character input. Allows \"numeric\", \"alpha\", \"alphanumeric\", or a custom\n * validation pattern with regex.\n *\n * @defaultValue `{ type: \"numeric\" }`\n *\n * @example\n * ```tsx\n * // Numeric only (default)\n * <AuthCode.Group validation={{ type: \"numeric\" }} />\n *\n * // Alphabetic only\n * <AuthCode.Group validation={{ type: \"alpha\" }} />\n *\n * // Custom validation\n * <AuthCode.Group\n * validation={{\n * type: \"custom\",\n * regex: /^[A-Z]$/,\n * pattern: \"[A-Z]{1}\",\n * inputMode: \"text\"\n * }}\n * />\n * ```\n */\n validation?: ValidationPattern;\n /**\n * Whether or not the component should attempt to automatically submit when\n * all fields are filled. If the field is associated with an HTML `form`\n * element, the form's `requestSubmit` method will be called.\n *\n * @defaultValue `true`\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit\n */\n autoSubmit?: boolean;\n /**\n * When the `autoSubmit` prop is set to `true`, this callback will be fired\n * before attempting to submit the associated form. It will be called whether\n * or not a form is located, or if submission is not allowed.\n *\n * The callback receives the complete auth code value as a string.\n *\n * @example\n * ```tsx\n * <AuthCode.Group\n * onComplete={(value) => {\n * console.log('Code entered:', value);\n * verifyCode(value);\n * }}\n * />\n * ```\n */\n onComplete?: (value: string) => void;\n /**\n * The controlled value of the field. When provided, the component operates\n * in controlled mode. This string's value, if present, must match the total\n * number of `<AuthCode.Input>` components.\n *\n * @example\n * ```tsx\n * const [code, setCode] = useState('');\n * <AuthCode.Group value={code} onValueChange={setCode} />\n * ```\n */\n value?: string;\n /**\n * A callback fired whenever the field value changes. This callback is called\n * with the complete auth code value as a string.\n *\n * Use this prop together with `value` to create a controlled component.\n */\n onValueChange?: (value: string) => void;\n /**\n * The initial value of the uncontrolled field. When provided without `value`,\n * the component operates in uncontrolled mode.\n *\n * @example\n * ```tsx\n * <AuthCode.Group defaultValue=\"123\" />\n * ```\n */\n defaultValue?: string;\n /**\n * The type of control to render. Allows \"text\" or \"password\".\n *\n * When set to \"password\", the input characters will be masked.\n *\n * @defaultValue `\"text\"`\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#type\n */\n type?: \"text\" | \"password\";\n /**\n * Whether or not the input elements are disabled.\n *\n * When set to `true`, all `<AuthCode.Input>` components will be disabled\n * and users will not be able to interact with them.\n *\n * @defaultValue `false`\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled\n */\n disabled?: boolean;\n}\n\ninterface AuthCodeFieldProps\n extends AuthCodeCustomField,\n Omit<React.ComponentProps<\"div\">, keyof AuthCodeCustomField> {}\n\nconst AuthCodeField = forwardRef<HTMLDivElement, AuthCodeFieldProps>(\n function AuthCodeField(\n {\n children,\n name,\n validation = { type: \"numeric\" },\n autoSubmit = true,\n onComplete,\n value: userValue,\n onValueChange: userOnValueChange,\n defaultValue,\n type = \"text\",\n disabled = false,\n ...divProps\n }: AuthCodeFieldProps,\n forwardedRef\n ) {\n const inputElements = useRef<Map<HTMLInputElement, true>>(new Map());\n const [value, setValue] = useControllableState<string[]>({\n value: userValue !== undefined ? Array.from(userValue) : undefined,\n defaultValue: defaultValue ? Array.from(defaultValue) : [],\n onValueChange: userOnValueChange\n ? (chars) => userOnValueChange(chars.join(\"\"))\n : undefined,\n });\n\n const validationConfig =\n validation.type == \"custom\"\n ? validation\n : defaultPatternInputMap[validation.type];\n\n const [focusedIndex, setFocusedIndex] = useState(0);\n const [inputCount, setInputCount] = useState(0);\n\n const registerInputRef = useCallback((ref: HTMLInputElement | null) => {\n if (ref) {\n if (!inputElements.current.has(ref)) {\n inputElements.current.set(ref, true);\n setInputCount(inputElements.current.size);\n }\n }\n }, []);\n\n const validateChar = useCallback(\n (char: string): boolean => {\n if (!char) return true;\n\n const regex = validationConfig.regex;\n return regex.test(char);\n },\n [validationConfig]\n );\n\n const validateString = useCallback(\n (str: string): string => {\n return Array.from(str)\n .filter((char) => validateChar(char))\n .join(\"\");\n },\n [validateChar]\n );\n\n const hiddenInputRef = useRef<HTMLInputElement | null>(null);\n\n // consider removing useEffectEvent\n const dispatch = (action: FieldUpdateAction) => {\n // use an ordered dictionary instead?\n const inputs = Array.from(inputElements.current.keys());\n switch (action.type) {\n case \"TYPE_CHAR\": {\n const { char, index: targetIndex } = action;\n\n if (!validateChar(char)) return;\n\n const hasExistingValue = !!value[targetIndex];\n const firstEmptyIndex = Array.from({\n length: inputs.length,\n }).findIndex((_, i) => !value[i]);\n\n const insertionIndex =\n hasExistingValue || firstEmptyIndex === -1\n ? targetIndex\n : firstEmptyIndex;\n\n const newValue = Array.from({ length: inputs.length }, (_, i) => {\n if (i === insertionIndex) return char;\n return value[i] ?? \"\";\n });\n\n flushSync(() => setValue(newValue));\n\n if (insertionIndex < inputs.length - 1) {\n requestAnimationFrame(() =>\n focusInput(inputs.at(insertionIndex + 1))\n );\n } else {\n requestAnimationFrame(() => focusInput(inputs.at(insertionIndex)));\n }\n return;\n }\n\n case \"REMOVE_CHAR\": {\n const { index } = action;\n const totalInputLength = inputs.length;\n\n // Remove character and shift remaining values left\n const leftPortion = value.slice(0, index);\n const rightPortion = value.slice(index + 1);\n const compacted = leftPortion.concat(rightPortion);\n const emptySlots = Array(\n Math.max(0, totalInputLength - compacted.length)\n ).fill(\"\");\n\n flushSync(() => setValue(compacted.concat(emptySlots)));\n\n if (index != 0) {\n focusInput(inputs.at(index - 1));\n } else {\n // index is 0\n focusInput(inputs.at(0));\n }\n return;\n }\n\n case \"CLEAR_CHAR\": {\n const { index } = action;\n const newValue = value.slice();\n newValue[index] = \"\";\n flushSync(() => setValue(newValue));\n return;\n }\n\n case \"SUBMIT_CODE\": {\n const currentValue = value.join(\"\");\n onComplete?.(currentValue);\n hiddenInputRef.current?.form?.requestSubmit();\n break;\n }\n\n case \"PASTE\": {\n const { value } = action;\n const totalInputLength = inputs.length;\n\n const validatedValue = validateString(value);\n if (validatedValue.length === 0) return;\n\n const pastedCharacters = Array.from(value).slice(0, inputs.length);\n const emptySlots = Array(\n Math.max(0, totalInputLength - value.length)\n ).fill(\"\");\n\n flushSync(() => setValue([...pastedCharacters, ...emptySlots]));\n\n if (pastedCharacters.length === totalInputLength) {\n focusInput(inputs.at(-1));\n } else {\n focusInput(inputs.at(value.length));\n }\n return;\n }\n case \"NAVIGATE_PREVIOUS\": {\n const { index } = action;\n if (index > 0) {\n focusInput(inputs.at(index - 1));\n }\n return;\n }\n\n case \"NAVIGATE_NEXT\": {\n const { index } = action;\n if (value[index] && index < inputs.length - 1) {\n focusInput(inputs.at(index + 1));\n return;\n }\n\n const firstEmpty = Array.from({ length: inputs.length }).findIndex(\n (_, i) => !value[i]\n );\n\n // BUG: if a value was deleted in the middle of a sequence, then we would\n // not be able to navigate to the right side of the sequence\n\n focusInput(inputs.at(firstEmpty));\n return;\n }\n\n case \"CLEAR_ALL\": {\n flushSync(() => setValue([]));\n focusInput(inputs.at(0));\n return;\n }\n }\n };\n\n useEffect(() => {\n const concatenatedValue = value.join(\"\");\n const isCodeFullyEntered =\n concatenatedValue.length === inputCount &&\n value.every((v) => v !== \"\") &&\n inputCount > 0;\n\n if (!isCodeFullyEntered) return;\n\n onComplete?.(concatenatedValue);\n\n if (autoSubmit) {\n hiddenInputRef.current?.form?.requestSubmit();\n }\n }, [value, inputCount, onComplete, autoSubmit]);\n\n const contextValue = useMemo(\n () => ({\n dispatch,\n value,\n registerInputRef,\n name,\n hiddenInputRef,\n validation: validationConfig,\n type,\n focusedIndex,\n setFocusedIndex,\n disabled,\n inputCount,\n inputElements: inputElements.current,\n }),\n [value, focusedIndex, disabled, name, type, inputCount]\n );\n\n return (\n <AuthCodeContext.Provider value={contextValue}>\n <div\n // Delegating all input's paste event this parent handler\n // because we want to control the paste for all inputs.\n ref={forwardedRef}\n onPaste={(event) => {\n event.preventDefault();\n const clipboardData = event.clipboardData;\n const pastedText = clipboardData.getData(\"text/plain\");\n\n dispatch({ type: \"PASTE\", value: pastedText });\n }}\n {...divProps}\n >\n <AuthCodeHiddenInput />\n {children}\n </div>\n </AuthCodeContext.Provider>\n );\n }\n);\n\nconst AuthCodeHiddenInput = forwardRef<\n HTMLInputElement,\n Omit<React.ComponentProps<\"input\">, \"ref\">\n>(function AuthCodeHiddenInput(props, ref) {\n const context = useAuthCodeContext();\n const { name, value, hiddenInputRef } = context;\n const memoizedRefs = useCallback(mergeRefs(ref, hiddenInputRef), [\n ref,\n hiddenInputRef,\n ]);\n return (\n <input\n ref={memoizedRefs}\n name={name}\n value={value.join(\"\")}\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n autoSave=\"off\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n required\n {...props}\n type=\"hidden\"\n />\n );\n});\n\n/**\n * Input component for entering a single character of the auth code.\n *\n * **Must be used within `<AuthCode.Group>`**\n *\n * @example\n *\n * <AuthCode.Group>\n * <AuthCode.Input index={0} />\n * <AuthCode.Input index={1} />\n * </AuthCode.Group>\n * */\nexport function AuthCodeInput({ index, ...props }: AuthCodeInputProps) {\n const context = useAuthCodeContext();\n const {\n dispatch,\n registerInputRef,\n validation,\n focusedIndex,\n setFocusedIndex,\n disabled,\n inputCount,\n } = context;\n\n const currentCharacter = context.value[index] || \"\";\n const firstEmptyIndex = Array.from({ length: inputCount }).findIndex(\n (_, i) => !context.value[i]\n );\n const nextEmptyOrLastIndex =\n firstEmptyIndex === -1 ? inputCount - 1 : firstEmptyIndex;\n const mergedInputRef = useCallback(mergeRefs(registerInputRef), [\n registerInputRef,\n ]);\n return (\n <input\n type={context.type}\n tabIndex={index === focusedIndex ? 0 : -1}\n ref={mergedInputRef}\n inputMode={validation.inputMode}\n pattern={validation.pattern}\n aria-label={`One Time Password Character ${\n index + 1\n } out of ${inputCount}`}\n disabled={disabled}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n // Disable password managers\n data-1p-ignore // 1Password\n data-lpignore=\"true\" // LastPass\n data-form-type=\"other\" // Generic password managers\n data-bwignore=\"true\" // Bitwarden\n {...props}\n onPointerDown={(event) => {\n // A click/touch on an input can cause the input to be out of selection so\n // we must prevent that default action and keep the input selected\n // in order to have a singular value\n event.preventDefault();\n const focusTargetIndex = Math.min(index, nextEmptyOrLastIndex);\n const focusTargetElement = Array.from(context.inputElements.keys())[\n focusTargetIndex\n ];\n focusInput(focusTargetElement);\n }}\n onFocus={(event) => {\n // select entire input instead of just focus\n event.target.select();\n setFocusedIndex(index);\n }}\n onInput={(event) => {\n const newInputValue = event.currentTarget.value;\n\n // Only treat as paste if it's an actual paste operation or truly long input\n // Single character duplicates (like \"11\" from typing \"1\" on \"1\") should go through onChange\n const browserInputType = (event.nativeEvent as InputEvent).inputType;\n const isPasteOperation =\n browserInputType === \"insertFromPaste\" ||\n browserInputType === \"insertFromDrop\";\n const isLongInput = newInputValue.length > 2;\n\n if (isPasteOperation || isLongInput) {\n event.preventDefault();\n dispatch({ type: \"PASTE\", value: newInputValue });\n }\n }}\n onChange={(event) => {\n const newInputValue = event.target.value;\n // check if value is valid against pattern\n if (event.target.validity.patternMismatch) return;\n dispatch({ type: \"TYPE_CHAR\", char: newInputValue, index });\n }}\n onKeyDown={mergeEventHandlers(props.onKeyDown, (event) => {\n // onKeyDown describes the intent of the user's key(s) presses\n if (event.metaKey && event.key == \"c\") return;\n\n if (\n isUndoShortcut(event) ||\n isCopyShortcut(event) ||\n isRedoShortcut(event)\n ) {\n event.preventDefault();\n return;\n }\n\n if ((event.metaKey || event.ctrlKey) && event.key === \"Backspace\") {\n dispatch({ type: \"CLEAR_ALL\" });\n return;\n }\n\n switch (event.key) {\n case \"Delete\":\n case \"Backspace\": {\n event.preventDefault();\n dispatch({ type: \"REMOVE_CHAR\", index });\n return;\n }\n\n case \"Enter\": {\n dispatch({ type: \"SUBMIT_CODE\" });\n return;\n }\n\n case \"ArrowLeft\": {\n event.preventDefault();\n dispatch({ type: \"NAVIGATE_PREVIOUS\", index });\n return;\n }\n case \"ArrowRight\": {\n event.preventDefault();\n dispatch({ type: \"NAVIGATE_NEXT\", index });\n return;\n }\n\n // Prevents up and down movements from unselecting\n case \"ArrowUp\":\n case \"ArrowDown\": {\n event.preventDefault();\n return;\n }\n\n default:\n if (event.key === currentCharacter) {\n // this is essentially focusing the next input\n dispatch({ type: \"TYPE_CHAR\", char: currentCharacter, index });\n return;\n }\n\n if (event.metaKey || event.ctrlKey) {\n return;\n }\n\n const hasValue = !!event.currentTarget.value;\n const isFullySelected =\n event.currentTarget.selectionStart === 0 &&\n event.currentTarget.selectionEnd != null &&\n event.currentTarget.selectionEnd > 0;\n\n // When input has value and is fully selected, validate the incoming key\n // to prevent selection loss from invalid keystrokes\n if (hasValue && isFullySelected) {\n // Only validate printable single characters (ignore modifiers, arrows, etc.)\n if (event.key.length === 1) {\n const isValid = validation.regex.test(event.key);\n if (!isValid) {\n event.preventDefault();\n return;\n }\n }\n }\n }\n })}\n value={currentCharacter}\n />\n );\n}\n\n// =================================================\n// CONTEXT\n// =================================================\n\nconst AuthCodeContext = createContext<AuthCodeContextValue | null>(null);\n\nfunction useAuthCodeContext() {\n const value = useContext(AuthCodeContext);\n if (!value) {\n throw new Error(\n \"\\n[awesome-auth-input] <AuthCode.Input> must be used within <AuthCode.Group>.\\n\\n\" +\n \"Make sure all <AuthCode.Input> components are children of <AuthCode.Group>:\\n\\n\" +\n \"Example:\\n\" +\n \" <AuthCode.Group>\\n\" +\n \" <AuthCode.Input index={0} />\\n\" +\n \" <AuthCode.Input index={1} />\\n\" +\n \" </AuthCode.Group>\\n\"\n );\n }\n return value;\n}\n\n// =================================================\n// HELPERS & HOOKS\n// =================================================\n\nfunction mergeEventHandlers<E extends { defaultPrevented: boolean }>(\n currentHandler?: (event: E) => void,\n nextHandler?: (event: E) => void\n) {\n return (event: E) => {\n currentHandler?.(event);\n\n if (!event.defaultPrevented) {\n nextHandler?.(event);\n }\n };\n}\n\nfunction isUndoShortcut(event: React.KeyboardEvent<HTMLInputElement>): boolean {\n return (event.metaKey || event.ctrlKey) && event.key === \"z\";\n}\n\nfunction isRedoShortcut(event: React.KeyboardEvent<HTMLInputElement>): boolean {\n return (\n (event.metaKey || event.ctrlKey) &&\n (event.key === \"y\" || (event.shiftKey && event.key === \"z\"))\n );\n}\n\nfunction isCopyShortcut(event: React.KeyboardEvent<HTMLInputElement>): boolean {\n return (event.metaKey || event.ctrlKey) && event.key === \"c\";\n}\n\ntype MergedRef<T> = Ref<T> | undefined | null;\nfunction mergeRefs<T = any>(...refs: MergedRef<T>[]): React.RefCallback<T> {\n return function (node) {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref != null) {\n ref.current = node;\n }\n });\n };\n}\n\nconst focusInput = (element: HTMLInputElement | undefined) => {\n if (!element) return;\n if (element.disabled) return;\n // ownerDocument is the opened 'window' that owns the element\n // e.g. popups or a browser-in-browser\n if (element.ownerDocument.activeElement === element) {\n element.select();\n } else {\n element.focus();\n }\n};\n\nfunction useControllableState<T>({\n defaultValue,\n onValueChange,\n value,\n}: {\n defaultValue: T;\n onValueChange?: (value: T) => void;\n value?: T;\n}): [T, (newValue: T) => void] {\n const [internalState, setInternalState] = useState<T>(defaultValue);\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : internalState;\n\n const setState = useCallback(\n (newValue: T) => {\n if (!isControlled) {\n setInternalState(newValue);\n }\n onValueChange?.(newValue);\n },\n [isControlled, onValueChange]\n );\n\n return [currentValue, setState];\n}\n\n// =================================================\n// INTERNAL TYPES & CONFIG\n// =================================================\n\ntype InputMode = \"text\" | \"numeric\" | \"none\";\n\ntype ValidationPattern =\n | ({ type: \"custom\" } & CustomValidation)\n | { type: \"alpha\" }\n | { type: \"alphanumeric\" }\n | { type: \"numeric\" };\n\ntype CustomValidation = {\n regex: RegExp;\n pattern: string;\n inputMode: InputMode;\n};\n\ninterface AuthCodeInputProps\n extends Omit<\n React.ComponentProps<\"input\">,\n | \"value\"\n | \"placeholder\"\n | \"disabled\"\n | \"autoComplete\"\n | \"defaultValue\"\n | \"type\"\n > {\n index: number;\n}\n\ninterface AuthCodeContextValue {\n dispatch: React.Dispatch<FieldUpdateAction>;\n value: string[];\n registerInputRef: (ref: HTMLInputElement | null) => void;\n name?: string;\n hiddenInputRef: Ref<HTMLInputElement | null>;\n validation:\n | (typeof defaultPatternInputMap)[DefaultInputTypes]\n | ({ type: \"custom\" } & CustomValidation);\n type: \"text\" | \"password\";\n focusedIndex: number;\n setFocusedIndex: (index: number) => void;\n disabled: boolean;\n inputCount: number;\n inputElements: Map<HTMLInputElement, true>;\n}\n\ntype DefaultInputTypes = \"alpha\" | \"alphanumeric\" | \"numeric\";\n\nconst numericRegex = /^\\d$/;\nconst alphaNumericRegex = /^[a-zA-Z\\d]$/;\nconst alphaRegex = /^[a-zA-Z]$/;\n\nconst defaultPatternInputMap = {\n alpha: {\n type: \"alpha\",\n regex: alphaRegex,\n pattern: \"[a-zA-Z]{1}\",\n inputMode: \"text\",\n },\n alphanumeric: {\n type: \"alphanumeric\",\n regex: alphaNumericRegex,\n pattern: \"[a-zA-Z0-9]{1}\",\n inputMode: \"text\",\n },\n numeric: {\n type: \"numeric\",\n regex: numericRegex,\n pattern: \"[0-9]{1}\",\n inputMode: \"numeric\",\n },\n} as const;\n\ntype FieldUpdateAction =\n | {\n type: \"TYPE_CHAR\";\n char: string;\n index: number;\n }\n | { type: \"PASTE\"; value: string }\n | { type: \"REMOVE_CHAR\"; index: number }\n | { type: \"SUBMIT_CODE\" }\n | { type: \"CLEAR_CHAR\"; index: number }\n | { type: \"NAVIGATE_PREVIOUS\"; index: number }\n | { type: \"NAVIGATE_NEXT\"; index: number }\n | { type: \"CLEAR_ALL\" };\n\n// =================================================\n// EXPORTS\n// =================================================\n\nexport {\n AuthCodeField as Group,\n AuthCodeInput as Input,\n AuthCodeHiddenInput as HiddenInput,\n};\n\nexport type {\n AuthCodeFieldProps as AuthCodeGroupProps,\n AuthCodeInputProps,\n ValidationPattern,\n CustomValidation,\n};\n","/**\n * awesome-auth-input\n * A headless, accessible React component for OTP/auth code inputs\n */\n\nimport {\n Group,\n Input,\n HiddenInput,\n} from \"./AuthCode/OneTimePassword\";\n\nexport type {\n AuthCodeGroupProps,\n AuthCodeInputProps,\n ValidationPattern,\n CustomValidation,\n} from \"./AuthCode/OneTimePassword\";\n\n/**\n * AuthCode - A compound component for OTP/auth code inputs\n *\n * @example\n * ```tsx\n * <AuthCode.Group onComplete={(code) => console.log(code)}>\n * <AuthCode.Input index={0} />\n * <AuthCode.Input index={1} />\n * <AuthCode.Input index={2} />\n * <AuthCode.Input index={3} />\n * </AuthCode.Group>\n * ```\n */\nconst AuthCode = {\n /**\n * The root component that manages state and validation for auth code inputs.\n * All `<AuthCode.Input>` components must be children of this component.\n */\n Group,\n /**\n * Individual input field for a single character of the auth code.\n * Must be used within `<AuthCode.Group>`.\n */\n Input,\n /**\n * Hidden input that contains the full auth code value for form submission.\n * Automatically rendered by Group, but can be customized if needed.\n */\n HiddenInput,\n};\n\nexport { AuthCode };\n\n// Also export individual components for tree-shaking\nexport { Group as AuthCodeGroup, Input as AuthCodeInput, HiddenInput as AuthCodeHiddenInput };\n\n"],"names":["forwardRef","AuthCodeField","useRef","useState","useCallback","flushSync","value","useEffect","useMemo","jsx","jsxs","AuthCodeHiddenInput","createContext","useContext","Group","Input","HiddenInput"],"mappings":";;;;;AA2IA,MAAM,gBAAgBA,MAAAA;AAAAA,EACpB,SAASC,eACP;AAAA,IACE;AAAA,IACA;AAAA,IACA,aAAa,EAAE,MAAM,UAAA;AAAA,IACrB,aAAa;AAAA,IACb;AAAA,IACA,OAAO;AAAA,IACP,eAAe;AAAA,IACf;AAAA,IACA,OAAO;AAAA,IACP,WAAW;AAAA,IACX,GAAG;AAAA,EAAA,GAEL,cACA;AACA,UAAM,gBAAgBC,MAAAA,OAAoC,oBAAI,KAAK;AACnE,UAAM,CAAC,OAAO,QAAQ,IAAI,qBAA+B;AAAA,MACvD,OAAO,cAAc,SAAY,MAAM,KAAK,SAAS,IAAI;AAAA,MACzD,cAAc,eAAe,MAAM,KAAK,YAAY,IAAI,CAAA;AAAA,MACxD,eAAe,oBACX,CAAC,UAAU,kBAAkB,MAAM,KAAK,EAAE,CAAC,IAC3C;AAAA,IAAA,CACL;AAED,UAAM,mBACJ,WAAW,QAAQ,WACf,aACA,uBAAuB,WAAW,IAAI;AAE5C,UAAM,CAAC,cAAc,eAAe,IAAIC,MAAAA,SAAS,CAAC;AAClD,UAAM,CAAC,YAAY,aAAa,IAAIA,MAAAA,SAAS,CAAC;AAE9C,UAAM,mBAAmBC,kBAAY,CAAC,QAAiC;AACrE,UAAI,KAAK;AACP,YAAI,CAAC,cAAc,QAAQ,IAAI,GAAG,GAAG;AACnC,wBAAc,QAAQ,IAAI,KAAK,IAAI;AACnC,wBAAc,cAAc,QAAQ,IAAI;AAAA,QAC1C;AAAA,MACF;AAAA,IACF,GAAG,CAAA,CAAE;AAEL,UAAM,eAAeA,MAAAA;AAAAA,MACnB,CAAC,SAA0B;AACzB,YAAI,CAAC,KAAM,QAAO;AAElB,cAAM,QAAQ,iBAAiB;AAC/B,eAAO,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,MACA,CAAC,gBAAgB;AAAA,IAAA;AAGnB,UAAM,iBAAiBA,MAAAA;AAAAA,MACrB,CAAC,QAAwB;AACvB,eAAO,MAAM,KAAK,GAAG,EAClB,OAAO,CAAC,SAAS,aAAa,IAAI,CAAC,EACnC,KAAK,EAAE;AAAA,MACZ;AAAA,MACA,CAAC,YAAY;AAAA,IAAA;AAGf,UAAM,iBAAiBF,MAAAA,OAAgC,IAAI;AAG3D,UAAM,WAAW,CAAC,WAA8B;;AAE9C,YAAM,SAAS,MAAM,KAAK,cAAc,QAAQ,MAAM;AACtD,cAAQ,OAAO,MAAA;AAAA,QACb,KAAK,aAAa;AAChB,gBAAM,EAAE,MAAM,OAAO,YAAA,IAAgB;AAErC,cAAI,CAAC,aAAa,IAAI,EAAG;AAEzB,gBAAM,mBAAmB,CAAC,CAAC,MAAM,WAAW;AAC5C,gBAAM,kBAAkB,MAAM,KAAK;AAAA,YACjC,QAAQ,OAAO;AAAA,UAAA,CAChB,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAEhC,gBAAM,iBACJ,oBAAoB,oBAAoB,KACpC,cACA;AAEN,gBAAM,WAAW,MAAM,KAAK,EAAE,QAAQ,OAAO,OAAA,GAAU,CAAC,GAAG,MAAM;AAC/D,gBAAI,MAAM,eAAgB,QAAO;AACjC,mBAAO,MAAM,CAAC,KAAK;AAAA,UACrB,CAAC;AAEDG,6BAAU,MAAM,SAAS,QAAQ,CAAC;AAElC,cAAI,iBAAiB,OAAO,SAAS,GAAG;AACtC;AAAA,cAAsB,MACpB,WAAW,OAAO,GAAG,iBAAiB,CAAC,CAAC;AAAA,YAAA;AAAA,UAE5C,OAAO;AACL,kCAAsB,MAAM,WAAW,OAAO,GAAG,cAAc,CAAC,CAAC;AAAA,UACnE;AACA;AAAA,QACF;AAAA,QAEA,KAAK,eAAe;AAClB,gBAAM,EAAE,UAAU;AAClB,gBAAM,mBAAmB,OAAO;AAGhC,gBAAM,cAAc,MAAM,MAAM,GAAG,KAAK;AACxC,gBAAM,eAAe,MAAM,MAAM,QAAQ,CAAC;AAC1C,gBAAM,YAAY,YAAY,OAAO,YAAY;AACjD,gBAAM,aAAa;AAAA,YACjB,KAAK,IAAI,GAAG,mBAAmB,UAAU,MAAM;AAAA,UAAA,EAC/C,KAAK,EAAE;AAETA,mBAAAA,UAAU,MAAM,SAAS,UAAU,OAAO,UAAU,CAAC,CAAC;AAEtD,cAAI,SAAS,GAAG;AACd,uBAAW,OAAO,GAAG,QAAQ,CAAC,CAAC;AAAA,UACjC,OAAO;AAEL,uBAAW,OAAO,GAAG,CAAC,CAAC;AAAA,UACzB;AACA;AAAA,QACF;AAAA,QAEA,KAAK,cAAc;AACjB,gBAAM,EAAE,UAAU;AAClB,gBAAM,WAAW,MAAM,MAAA;AACvB,mBAAS,KAAK,IAAI;AAClBA,6BAAU,MAAM,SAAS,QAAQ,CAAC;AAClC;AAAA,QACF;AAAA,QAEA,KAAK,eAAe;AAClB,gBAAM,eAAe,MAAM,KAAK,EAAE;AAClC,mDAAa;AACb,qCAAe,YAAf,mBAAwB,SAAxB,mBAA8B;AAC9B;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,EAAE,OAAAC,OAAAA,IAAU;AAClB,gBAAM,mBAAmB,OAAO;AAEhC,gBAAM,iBAAiB,eAAeA,MAAK;AAC3C,cAAI,eAAe,WAAW,EAAG;AAEjC,gBAAM,mBAAmB,MAAM,KAAKA,MAAK,EAAE,MAAM,GAAG,OAAO,MAAM;AACjE,gBAAM,aAAa;AAAA,YACjB,KAAK,IAAI,GAAG,mBAAmBA,OAAM,MAAM;AAAA,UAAA,EAC3C,KAAK,EAAE;AAETD,mBAAAA,UAAU,MAAM,SAAS,CAAC,GAAG,kBAAkB,GAAG,UAAU,CAAC,CAAC;AAE9D,cAAI,iBAAiB,WAAW,kBAAkB;AAChD,uBAAW,OAAO,GAAG,EAAE,CAAC;AAAA,UAC1B,OAAO;AACL,uBAAW,OAAO,GAAGC,OAAM,MAAM,CAAC;AAAA,UACpC;AACA;AAAA,QACF;AAAA,QACA,KAAK,qBAAqB;AACxB,gBAAM,EAAE,UAAU;AAClB,cAAI,QAAQ,GAAG;AACb,uBAAW,OAAO,GAAG,QAAQ,CAAC,CAAC;AAAA,UACjC;AACA;AAAA,QACF;AAAA,QAEA,KAAK,iBAAiB;AACpB,gBAAM,EAAE,UAAU;AAClB,cAAI,MAAM,KAAK,KAAK,QAAQ,OAAO,SAAS,GAAG;AAC7C,uBAAW,OAAO,GAAG,QAAQ,CAAC,CAAC;AAC/B;AAAA,UACF;AAEA,gBAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,OAAO,OAAA,CAAQ,EAAE;AAAA,YACvD,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;AAAA,UAAA;AAMpB,qBAAW,OAAO,GAAG,UAAU,CAAC;AAChC;AAAA,QACF;AAAA,QAEA,KAAK,aAAa;AAChBD,6BAAU,MAAM,SAAS,CAAA,CAAE,CAAC;AAC5B,qBAAW,OAAO,GAAG,CAAC,CAAC;AACvB;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ;AAEAE,UAAAA,UAAU,MAAM;;AACd,YAAM,oBAAoB,MAAM,KAAK,EAAE;AACvC,YAAM,qBACJ,kBAAkB,WAAW,cAC7B,MAAM,MAAM,CAAC,MAAM,MAAM,EAAE,KAC3B,aAAa;AAEf,UAAI,CAAC,mBAAoB;AAEzB,+CAAa;AAEb,UAAI,YAAY;AACd,mCAAe,YAAf,mBAAwB,SAAxB,mBAA8B;AAAA,MAChC;AAAA,IACF,GAAG,CAAC,OAAO,YAAY,YAAY,UAAU,CAAC;AAE9C,UAAM,eAAeC,MAAAA;AAAAA,MACnB,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,cAAc;AAAA,MAAA;AAAA,MAE/B,CAAC,OAAO,cAAc,UAAU,MAAM,MAAM,UAAU;AAAA,IAAA;AAGxD,WACEC,2BAAAA,IAAC,gBAAgB,UAAhB,EAAyB,OAAO,cAC/B,UAAAC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QAGC,KAAK;AAAA,QACL,SAAS,CAAC,UAAU;AAClB,gBAAM,eAAA;AACN,gBAAM,gBAAgB,MAAM;AAC5B,gBAAM,aAAa,cAAc,QAAQ,YAAY;AAErD,mBAAS,EAAE,MAAM,SAAS,OAAO,YAAY;AAAA,QAC/C;AAAA,QACC,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAAD,2BAAAA,IAAC,qBAAA,EAAoB;AAAA,UACpB;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,MAAM,sBAAsBT,MAAAA,WAG1B,SAASW,qBAAoB,OAAO,KAAK;AACzC,QAAM,UAAU,mBAAA;AAChB,QAAM,EAAE,MAAM,OAAO,eAAA,IAAmB;AACxC,QAAM,eAAeP,MAAAA,YAAY,UAAU,KAAK,cAAc,GAAG;AAAA,IAC/D;AAAA,IACA;AAAA,EAAA,CACD;AACD,SACEK,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,OAAO,MAAM,KAAK,EAAE;AAAA,MACpB,gBAAe;AAAA,MACf,aAAY;AAAA,MACZ,UAAS;AAAA,MACT,WAAU;AAAA,MACV,cAAa;AAAA,MACb,UAAQ;AAAA,MACP,GAAG;AAAA,MACJ,MAAK;AAAA,IAAA;AAAA,EAAA;AAGX,CAAC;AAcM,SAAS,cAAc,EAAE,OAAO,GAAG,SAA6B;AACrE,QAAM,UAAU,mBAAA;AAChB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,QAAM,mBAAmB,QAAQ,MAAM,KAAK,KAAK;AACjD,QAAM,kBAAkB,MAAM,KAAK,EAAE,QAAQ,WAAA,CAAY,EAAE;AAAA,IACzD,CAAC,GAAG,MAAM,CAAC,QAAQ,MAAM,CAAC;AAAA,EAAA;AAE5B,QAAM,uBACJ,oBAAoB,KAAK,aAAa,IAAI;AAC5C,QAAM,iBAAiBL,MAAAA,YAAY,UAAU,gBAAgB,GAAG;AAAA,IAC9D;AAAA,EAAA,CACD;AACD,SACEK,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM,QAAQ;AAAA,MACd,UAAU,UAAU,eAAe,IAAI;AAAA,MACvC,KAAK;AAAA,MACL,WAAW,WAAW;AAAA,MACtB,SAAS,WAAW;AAAA,MACpB,cAAY,+BACV,QAAQ,CACV,WAAW,UAAU;AAAA,MACrB;AAAA,MACA,cAAa;AAAA,MACb,aAAY;AAAA,MACZ,gBAAe;AAAA,MAEf,kBAAc;AAAA,MACd,iBAAc;AAAA,MACd,kBAAe;AAAA,MACf,iBAAc;AAAA,MACb,GAAG;AAAA,MACJ,eAAe,CAAC,UAAU;AAIxB,cAAM,eAAA;AACN,cAAM,mBAAmB,KAAK,IAAI,OAAO,oBAAoB;AAC7D,cAAM,qBAAqB,MAAM,KAAK,QAAQ,cAAc,KAAA,CAAM,EAChE,gBACF;AACA,mBAAW,kBAAkB;AAAA,MAC/B;AAAA,MACA,SAAS,CAAC,UAAU;AAElB,cAAM,OAAO,OAAA;AACb,wBAAgB,KAAK;AAAA,MACvB;AAAA,MACA,SAAS,CAAC,UAAU;AAClB,cAAM,gBAAgB,MAAM,cAAc;AAI1C,cAAM,mBAAoB,MAAM,YAA2B;AAC3D,cAAM,mBACJ,qBAAqB,qBACrB,qBAAqB;AACvB,cAAM,cAAc,cAAc,SAAS;AAE3C,YAAI,oBAAoB,aAAa;AACnC,gBAAM,eAAA;AACN,mBAAS,EAAE,MAAM,SAAS,OAAO,eAAe;AAAA,QAClD;AAAA,MACF;AAAA,MACA,UAAU,CAAC,UAAU;AACnB,cAAM,gBAAgB,MAAM,OAAO;AAEnC,YAAI,MAAM,OAAO,SAAS,gBAAiB;AAC3C,iBAAS,EAAE,MAAM,aAAa,MAAM,eAAe,OAAO;AAAA,MAC5D;AAAA,MACA,WAAW,mBAAmB,MAAM,WAAW,CAAC,UAAU;AAExD,YAAI,MAAM,WAAW,MAAM,OAAO,IAAK;AAEvC,YACE,eAAe,KAAK,KACpB,eAAe,KAAK,KACpB,eAAe,KAAK,GACpB;AACA,gBAAM,eAAA;AACN;AAAA,QACF;AAEA,aAAK,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,aAAa;AACjE,mBAAS,EAAE,MAAM,aAAa;AAC9B;AAAA,QACF;AAEA,gBAAQ,MAAM,KAAA;AAAA,UACZ,KAAK;AAAA,UACL,KAAK,aAAa;AAChB,kBAAM,eAAA;AACN,qBAAS,EAAE,MAAM,eAAe,MAAA,CAAO;AACvC;AAAA,UACF;AAAA,UAEA,KAAK,SAAS;AACZ,qBAAS,EAAE,MAAM,eAAe;AAChC;AAAA,UACF;AAAA,UAEA,KAAK,aAAa;AAChB,kBAAM,eAAA;AACN,qBAAS,EAAE,MAAM,qBAAqB,MAAA,CAAO;AAC7C;AAAA,UACF;AAAA,UACA,KAAK,cAAc;AACjB,kBAAM,eAAA;AACN,qBAAS,EAAE,MAAM,iBAAiB,MAAA,CAAO;AACzC;AAAA,UACF;AAAA;AAAA,UAGA,KAAK;AAAA,UACL,KAAK,aAAa;AAChB,kBAAM,eAAA;AACN;AAAA,UACF;AAAA,UAEA;AACE,gBAAI,MAAM,QAAQ,kBAAkB;AAElC,uBAAS,EAAE,MAAM,aAAa,MAAM,kBAAkB,OAAO;AAC7D;AAAA,YACF;AAEA,gBAAI,MAAM,WAAW,MAAM,SAAS;AAClC;AAAA,YACF;AAEA,kBAAM,WAAW,CAAC,CAAC,MAAM,cAAc;AACvC,kBAAM,kBACJ,MAAM,cAAc,mBAAmB,KACvC,MAAM,cAAc,gBAAgB,QACpC,MAAM,cAAc,eAAe;AAIrC,gBAAI,YAAY,iBAAiB;AAE/B,kBAAI,MAAM,IAAI,WAAW,GAAG;AAC1B,sBAAM,UAAU,WAAW,MAAM,KAAK,MAAM,GAAG;AAC/C,oBAAI,CAAC,SAAS;AACZ,wBAAM,eAAA;AACN;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,QAAA;AAAA,MAEN,CAAC;AAAA,MACD,OAAO;AAAA,IAAA;AAAA,EAAA;AAGb;AAMA,MAAM,kBAAkBG,MAAAA,cAA2C,IAAI;AAEvE,SAAS,qBAAqB;AAC5B,QAAM,QAAQC,MAAAA,WAAW,eAAe;AACxC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAQJ;AACA,SAAO;AACT;AAMA,SAAS,mBACP,gBACA,aACA;AACA,SAAO,CAAC,UAAa;AACnB,qDAAiB;AAEjB,QAAI,CAAC,MAAM,kBAAkB;AAC3B,iDAAc;AAAA,IAChB;AAAA,EACF;AACF;AAEA,SAAS,eAAe,OAAuD;AAC7E,UAAQ,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ;AAC3D;AAEA,SAAS,eAAe,OAAuD;AAC7E,UACG,MAAM,WAAW,MAAM,aACvB,MAAM,QAAQ,OAAQ,MAAM,YAAY,MAAM,QAAQ;AAE3D;AAEA,SAAS,eAAe,OAAuD;AAC7E,UAAQ,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ;AAC3D;AAGA,SAAS,aAAsB,MAA4C;AACzE,SAAO,SAAU,MAAM;AACrB,SAAK,QAAQ,CAAC,QAAQ;AACpB,UAAI,OAAO,QAAQ,YAAY;AAC7B,YAAI,IAAI;AAAA,MACV,WAAW,OAAO,MAAM;AACtB,YAAI,UAAU;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,MAAM,aAAa,CAAC,YAA0C;AAC5D,MAAI,CAAC,QAAS;AACd,MAAI,QAAQ,SAAU;AAGtB,MAAI,QAAQ,cAAc,kBAAkB,SAAS;AACnD,YAAQ,OAAA;AAAA,EACV,OAAO;AACL,YAAQ,MAAA;AAAA,EACV;AACF;AAEA,SAAS,qBAAwB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAI+B;AAC7B,QAAM,CAAC,eAAe,gBAAgB,IAAIV,MAAAA,SAAY,YAAY;AAElE,QAAM,eAAe,UAAU;AAC/B,QAAM,eAAe,eAAe,QAAQ;AAE5C,QAAM,WAAWC,MAAAA;AAAAA,IACf,CAAC,aAAgB;AACf,UAAI,CAAC,cAAc;AACjB,yBAAiB,QAAQ;AAAA,MAC3B;AACA,qDAAgB;AAAA,IAClB;AAAA,IACA,CAAC,cAAc,aAAa;AAAA,EAAA;AAG9B,SAAO,CAAC,cAAc,QAAQ;AAChC;AAoDA,MAAM,eAAe;AACrB,MAAM,oBAAoB;AAC1B,MAAM,aAAa;AAEnB,MAAM,yBAAyB;AAAA,EAC7B,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,WAAW;AAAA,EAAA;AAAA,EAEb,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,WAAW;AAAA,EAAA;AAAA,EAEb,SAAS;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,WAAW;AAAA,EAAA;AAEf;ACruBA,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,OAKfU;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,EAAA,OAKAC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,EAAA,aAKAC;AACF;;;;;"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * awesome-auth-input
3
+ * A headless, accessible React component for OTP/auth code inputs
4
+ */
5
+ import { Group, Input, HiddenInput } from "./AuthCode/OneTimePassword";
6
+ export type { AuthCodeGroupProps, AuthCodeInputProps, ValidationPattern, CustomValidation, } from "./AuthCode/OneTimePassword";
7
+ /**
8
+ * AuthCode - A compound component for OTP/auth code inputs
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <AuthCode.Group onComplete={(code) => console.log(code)}>
13
+ * <AuthCode.Input index={0} />
14
+ * <AuthCode.Input index={1} />
15
+ * <AuthCode.Input index={2} />
16
+ * <AuthCode.Input index={3} />
17
+ * </AuthCode.Group>
18
+ * ```
19
+ */
20
+ declare const AuthCode: {
21
+ /**
22
+ * The root component that manages state and validation for auth code inputs.
23
+ * All `<AuthCode.Input>` components must be children of this component.
24
+ */
25
+ Group: import("react").ForwardRefExoticComponent<Omit<import(".").AuthCodeGroupProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
26
+ /**
27
+ * Individual input field for a single character of the auth code.
28
+ * Must be used within `<AuthCode.Group>`.
29
+ */
30
+ Input: typeof Input;
31
+ /**
32
+ * Hidden input that contains the full auth code value for form submission.
33
+ * Automatically rendered by Group, but can be customized if needed.
34
+ */
35
+ HiddenInput: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
36
+ };
37
+ export { AuthCode };
38
+ export { Group as AuthCodeGroup, Input as AuthCodeInput, HiddenInput as AuthCodeHiddenInput };
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,KAAK,EACL,KAAK,EACL,WAAW,EACZ,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,QAAQ;IACZ;;;OAGG;;IAEH;;;OAGG;;IAEH;;;OAGG;;CAEJ,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC;AAGpB,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,KAAK,IAAI,aAAa,EAAE,WAAW,IAAI,mBAAmB,EAAE,CAAC"}