@radix-ui/react-one-time-password-field 0.1.0-rc.1744661316162

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/one-time-password-field.tsx"],
4
+ "sourcesContent": ["'use client';\nexport type {\n OneTimePasswordFieldProps,\n OneTimePasswordFieldInputProps,\n OneTimePasswordFieldHiddenInputProps,\n InputValidationType,\n} from './one-time-password-field';\nexport {\n OneTimePasswordField,\n OneTimePasswordFieldInput,\n OneTimePasswordFieldHiddenInput,\n //\n Root,\n Input,\n HiddenInput,\n} from './one-time-password-field';\n", "import { Primitive } from '@radix-ui/react-primitive';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport { composeEventHandlers } from '@radix-ui/primitive';\nimport { unstable_createCollection as createCollection } from '@radix-ui/react-collection';\nimport * as RovingFocusGroup from '@radix-ui/react-roving-focus';\nimport { createRovingFocusGroupScope } from '@radix-ui/react-roving-focus';\nimport { useIsHydrated } from '@radix-ui/react-use-is-hydrated';\nimport * as React from 'react';\nimport { flushSync } from 'react-dom';\nimport type { Scope } from '@radix-ui/react-context';\nimport { createContextScope } from '@radix-ui/react-context';\nimport { useDirection } from '@radix-ui/react-direction';\nimport { clamp } from '@radix-ui/number';\nimport { useEffectEvent } from '@radix-ui/react-use-effect-event';\n\ntype InputType = 'password' | 'text';\ntype AutoComplete = 'off' | 'one-time-code';\n\ntype KeyboardActionDetails =\n | {\n type: 'keydown';\n key: 'Backspace' | 'Delete' | 'Char';\n metaKey: boolean;\n ctrlKey: boolean;\n }\n | { type: 'cut' };\n\ntype UpdateAction =\n | {\n type: 'SET_CHAR';\n char: string;\n index: number;\n event: React.KeyboardEvent | React.ChangeEvent;\n }\n | { type: 'CLEAR_CHAR'; index: number; reason: 'Backspace' | 'Delete' | 'Cut' }\n | { type: 'CLEAR'; reason: 'Reset' | 'Backspace' | 'Delete' }\n | { type: 'PASTE'; value: string };\ntype Dispatcher = React.Dispatch<UpdateAction>;\n\ntype InputValidationType = 'alpha' | 'numeric' | 'alphanumeric' | 'none';\ntype InputValidation = Record<\n Exclude<InputValidationType, 'none'>,\n { type: InputValidationType; regexp: RegExp; pattern: string; inputMode: string }\n>;\n\nconst INPUT_VALIDATION_MAP = {\n numeric: {\n type: 'numeric',\n regexp: /[^\\d]/g,\n pattern: '\\\\d{1}',\n inputMode: 'numeric',\n },\n alpha: {\n type: 'alpha',\n regexp: /[^a-zA-Z]/g,\n pattern: '[a-zA-Z]{1}',\n inputMode: 'text',\n },\n alphanumeric: {\n type: 'alphanumeric',\n regexp: /[^a-zA-Z0-9]/g,\n pattern: '[a-zA-Z0-9]{1}',\n inputMode: 'text',\n },\n} satisfies InputValidation;\n\ninterface OneTimePasswordFieldContextValue {\n value: string[];\n attemptSubmit: () => void;\n hiddenInputRef: React.RefObject<HTMLInputElement | null>;\n validationType: InputValidationType;\n disabled: boolean;\n readOnly: boolean;\n autoComplete: AutoComplete;\n autoFocus: boolean;\n form: string | undefined;\n name: string | undefined;\n placeholder: string | undefined;\n required: boolean;\n type: InputType;\n userActionRef: React.RefObject<KeyboardActionDetails | null>;\n dispatch: Dispatcher;\n orientation: Exclude<RovingFocusGroupProps['orientation'], undefined>;\n preHydrationIndexTracker: React.RefObject<number>;\n isHydrated: boolean;\n}\n\nconst ONE_TIME_PASSWORD_FIELD_NAME = 'OneTimePasswordField';\nconst [Collection, useCollection, createCollectionScope] = createCollection<HTMLInputElement>(\n ONE_TIME_PASSWORD_FIELD_NAME\n);\nconst [createOneTimePasswordFieldContext] = createContextScope(ONE_TIME_PASSWORD_FIELD_NAME, [\n createCollectionScope,\n createRovingFocusGroupScope,\n]);\nconst useRovingFocusGroupScope = createRovingFocusGroupScope();\n\nconst [OneTimePasswordFieldContext, useOneTimePasswordFieldContext] =\n createOneTimePasswordFieldContext<OneTimePasswordFieldContextValue>(ONE_TIME_PASSWORD_FIELD_NAME);\n\ntype RovingFocusGroupProps = React.ComponentPropsWithoutRef<typeof RovingFocusGroup.Root>;\n\ninterface OneTimePasswordFieldOwnProps {\n onValueChange?: (value: string) => void;\n id?: string;\n value?: string;\n defaultValue?: string;\n autoSubmit?: boolean;\n onAutoSubmit?: (value: string) => void;\n validationType?: InputValidationType;\n disabled?: boolean;\n readOnly?: boolean;\n autoComplete?: AutoComplete;\n autoFocus?: boolean;\n form?: string | undefined;\n name?: string | undefined;\n placeholder?: string | undefined;\n required?: boolean;\n type?: InputType;\n //\n dir?: RovingFocusGroupProps['dir'];\n orientation?: RovingFocusGroupProps['orientation'];\n}\n\ntype ScopedProps<P> = P & { __scopeOneTimePasswordField?: Scope };\n\nconst OneTimePasswordFieldCollectionProvider = ({\n __scopeOneTimePasswordField,\n children,\n}: ScopedProps<{ children: React.ReactNode }>) => {\n return (\n <Collection.Provider scope={__scopeOneTimePasswordField}>\n <Collection.Slot scope={__scopeOneTimePasswordField}>{children}</Collection.Slot>\n </Collection.Provider>\n );\n};\n\ninterface OneTimePasswordFieldProps\n extends OneTimePasswordFieldOwnProps,\n Omit<\n React.ComponentPropsWithoutRef<typeof Primitive.div>,\n keyof OneTimePasswordFieldOwnProps\n > {}\n\nconst OneTimePasswordFieldImpl = React.forwardRef<HTMLDivElement, OneTimePasswordFieldProps>(\n function OneTimePasswordFieldImpl(\n {\n __scopeOneTimePasswordField,\n id,\n defaultValue,\n value: valueProp,\n onValueChange,\n autoSubmit,\n children,\n onPaste,\n onAutoSubmit,\n disabled = false,\n readOnly = false,\n autoComplete = 'one-time-code',\n autoFocus = false,\n form,\n name,\n placeholder,\n required = false,\n type = 'password',\n // TODO: Change default to vertical when inputs use vertical writing mode\n orientation = 'horizontal',\n dir,\n validationType = 'numeric',\n ...domProps\n }: ScopedProps<OneTimePasswordFieldProps>,\n forwardedRef\n ) {\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeOneTimePasswordField);\n const direction = useDirection(dir);\n const collection = useCollection(__scopeOneTimePasswordField);\n\n const validation =\n validationType in INPUT_VALIDATION_MAP\n ? INPUT_VALIDATION_MAP[validationType as keyof InputValidation]\n : undefined;\n\n const controlledValue = React.useMemo(() => {\n return valueProp != null ? sanitizeValue(valueProp, validation?.regexp) : undefined;\n }, [valueProp, validation?.regexp]);\n\n const [value, setValue] = useControllableState({\n caller: 'OneTimePasswordField',\n prop: controlledValue,\n defaultProp: defaultValue != null ? sanitizeValue(defaultValue, validation?.regexp) : [],\n onChange: (value) => onValueChange?.(value.filter(Boolean).join('')),\n });\n\n // Update function *specifically* for event handlers.\n const dispatch = useEffectEvent<Dispatcher>((action) => {\n switch (action.type) {\n case 'SET_CHAR': {\n const { index, char } = action;\n const currentTarget = collection.at(index)?.element;\n if (value[index] === char) {\n const next = currentTarget && collection.from(currentTarget, 1)?.element;\n focusInput(next);\n return;\n }\n\n // empty values should be handled in the CLEAR_CHAR action\n if (char === '') {\n return;\n }\n\n if (validation) {\n const regexp = new RegExp(validation.regexp);\n const clean = char.replace(regexp, '');\n if (clean !== char) {\n // not valid; ignore\n return;\n }\n }\n\n // no more space\n if (value.length >= collection.size) {\n // replace current value; move to next input\n const newValue = [...value];\n newValue[index] = char;\n flushSync(() => setValue(newValue));\n const next = currentTarget && collection.from(currentTarget, 1)?.element;\n focusInput(next);\n return;\n }\n\n const newValue = [\n //\n ...value.slice(0, index),\n char,\n ...value.slice(index),\n ];\n\n const lastElement = collection.at(-1)?.element;\n flushSync(() => setValue(newValue));\n if (currentTarget !== lastElement) {\n const next = currentTarget && collection.from(currentTarget, 1)?.element;\n focusInput(next);\n } else {\n currentTarget?.select();\n }\n return;\n }\n\n case 'CLEAR_CHAR': {\n const { index, reason } = action;\n if (!value[index]) {\n return;\n }\n\n const newValue = value.filter((_, i) => i !== index);\n const currentTarget = collection.at(index)?.element;\n const previous = currentTarget && collection.from(currentTarget, -1)?.element;\n\n flushSync(() => setValue(newValue));\n if (reason === 'Backspace') {\n focusInput(previous);\n } else if (reason === 'Delete' || reason === 'Cut') {\n focusInput(currentTarget);\n }\n return;\n }\n\n case 'CLEAR': {\n if (value.length === 0) {\n return;\n }\n\n if (action.reason === 'Backspace' || action.reason === 'Delete') {\n flushSync(() => setValue([]));\n focusInput(collection.at(0)?.element);\n } else {\n setValue([]);\n }\n return;\n }\n\n case 'PASTE': {\n const { value: pastedValue } = action;\n const value = sanitizeValue(pastedValue, validation?.regexp);\n if (!value) {\n return;\n }\n\n flushSync(() => setValue(value));\n focusInput(collection.at(value.length - 1)?.element);\n return;\n }\n }\n });\n\n // re-validate when the validation type changes\n const validationTypeRef = React.useRef(validation);\n React.useEffect(() => {\n if (!validation) {\n return;\n }\n\n if (validationTypeRef.current?.type !== validation.type) {\n validationTypeRef.current = validation;\n setValue(sanitizeValue(value, validation.regexp));\n }\n }, [setValue, validation, value]);\n\n const hiddenInputRef = React.useRef<HTMLInputElement>(null);\n\n const userActionRef = React.useRef<KeyboardActionDetails | null>(null);\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const composedRefs = useComposedRefs(forwardedRef, rootRef);\n\n const firstInput = collection.at(0)?.element;\n const locateForm = React.useCallback(() => {\n let formElement: HTMLFormElement | null | undefined;\n if (form) {\n const associatedElement = (rootRef.current?.ownerDocument ?? document).getElementById(form);\n if (isFormElement(associatedElement)) {\n formElement = associatedElement;\n }\n } else if (hiddenInputRef.current) {\n formElement = hiddenInputRef.current.form;\n } else if (firstInput) {\n formElement = firstInput.form;\n }\n\n return formElement ?? null;\n }, [form, firstInput]);\n\n const attemptSubmit = React.useCallback(() => {\n const formElement = locateForm();\n formElement?.requestSubmit();\n }, [locateForm]);\n\n React.useEffect(() => {\n const form = locateForm();\n if (form) {\n const reset = () => dispatch({ type: 'CLEAR', reason: 'Reset' });\n form.addEventListener('reset', reset);\n return () => form.removeEventListener('reset', reset);\n }\n }, [dispatch, locateForm]);\n\n const currentValue = value.join('');\n const valueRef = React.useRef(currentValue);\n const length = collection.size;\n React.useEffect(() => {\n const previousValue = valueRef.current;\n valueRef.current = currentValue;\n if (previousValue === currentValue) {\n return;\n }\n\n if (autoSubmit && value.every((char) => char !== '') && value.length === length) {\n onAutoSubmit?.(value.join(''));\n attemptSubmit();\n }\n }, [attemptSubmit, autoSubmit, currentValue, length, onAutoSubmit, value]);\n\n // Before hydration (and in SSR) we can track the index of an input during\n // render, as indices calculated by the collection package should almost\n // always align with render order anyway. This ensures that index-dependent\n // attributes are immediately rendered, in case browser extensions rely on\n // those for auto-complete functionality and JS has not hydrated.\n const preHydrationIndexTracker = React.useRef<number>(0);\n const isHydrated = useIsHydrated();\n\n return (\n <OneTimePasswordFieldContext\n scope={__scopeOneTimePasswordField}\n value={value}\n attemptSubmit={attemptSubmit}\n disabled={disabled}\n readOnly={readOnly}\n autoComplete={autoComplete}\n autoFocus={autoFocus}\n form={form}\n name={name}\n placeholder={placeholder}\n required={required}\n type={type}\n hiddenInputRef={hiddenInputRef}\n userActionRef={userActionRef}\n dispatch={dispatch}\n validationType={validationType}\n orientation={orientation}\n preHydrationIndexTracker={preHydrationIndexTracker}\n isHydrated={isHydrated}\n >\n <RovingFocusGroup.Root\n asChild\n {...rovingFocusGroupScope}\n orientation={orientation}\n dir={direction}\n >\n <Primitive.div\n {...domProps}\n role=\"group\"\n ref={composedRefs}\n onPaste={composeEventHandlers(\n onPaste,\n (event: React.ClipboardEvent<HTMLDivElement>) => {\n event.preventDefault();\n const pastedValue = event.clipboardData.getData('Text');\n const value = sanitizeValue(pastedValue, validation?.regexp);\n if (!value) {\n return;\n }\n dispatch({ type: 'PASTE', value: pastedValue });\n }\n )}\n >\n {children}\n </Primitive.div>\n </RovingFocusGroup.Root>\n </OneTimePasswordFieldContext>\n );\n }\n);\n\nconst OneTimePasswordField = React.forwardRef<HTMLDivElement, OneTimePasswordFieldProps>(\n function OneTimePasswordField(props, ref) {\n return (\n <OneTimePasswordFieldCollectionProvider>\n <OneTimePasswordFieldImpl {...props} ref={ref} />\n </OneTimePasswordFieldCollectionProvider>\n );\n }\n);\n\ninterface OneTimePasswordFieldHiddenInputProps\n extends Omit<\n React.ComponentProps<'input'>,\n | keyof 'value'\n | 'defaultValue'\n | 'type'\n | 'onChange'\n | 'readOnly'\n | 'disabled'\n | 'autoComplete'\n | 'autoFocus'\n > {}\n\nconst OneTimePasswordFieldHiddenInput = React.forwardRef<\n HTMLInputElement,\n OneTimePasswordFieldHiddenInputProps\n>(function OneTimePasswordFieldHiddenInput(\n { __scopeOneTimePasswordField, ...props }: ScopedProps<OneTimePasswordFieldHiddenInputProps>,\n forwardedRef\n) {\n const { value, hiddenInputRef } = useOneTimePasswordFieldContext(\n 'OneTimePasswordFieldHiddenInput',\n __scopeOneTimePasswordField\n );\n const ref = useComposedRefs(hiddenInputRef, forwardedRef);\n return (\n <input\n ref={ref}\n {...props}\n type=\"hidden\"\n readOnly\n value={value.join('').trim()}\n autoComplete=\"off\"\n autoFocus={false}\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n autoSave=\"off\"\n spellCheck={false}\n />\n );\n});\n\ninterface OneTimePasswordFieldInputOwnProps {\n autoComplete?: 'one-time-code' | 'off';\n}\n\ninterface OneTimePasswordFieldInputProps\n extends OneTimePasswordFieldInputOwnProps,\n Omit<React.ComponentProps<typeof Primitive.input>, keyof OneTimePasswordFieldInputOwnProps> {}\n\nconst OneTimePasswordFieldInput = React.forwardRef<\n HTMLInputElement,\n OneTimePasswordFieldInputProps\n>(function OneTimePasswordFieldInput(\n { __scopeOneTimePasswordField, ...props }: ScopedProps<OneTimePasswordFieldInputProps>,\n forwardedRef\n) {\n // TODO: warn if these values are passed\n const {\n value: _value,\n defaultValue: _defaultValue,\n disabled: _disabled,\n readOnly: _readOnly,\n autoComplete: _autoComplete,\n autoFocus: _autoFocus,\n form: _form,\n name: _name,\n placeholder: _placeholder,\n required: _required,\n type: _type,\n ...domProps\n } = props as any;\n\n const context = useOneTimePasswordFieldContext(\n 'OneTimePasswordFieldInput',\n __scopeOneTimePasswordField\n );\n const { dispatch, userActionRef, validationType, preHydrationIndexTracker, isHydrated } = context;\n const collection = useCollection(__scopeOneTimePasswordField);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeOneTimePasswordField);\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const [element, setElement] = React.useState<HTMLInputElement | null>(null);\n\n let index: number;\n if (!isHydrated) {\n index = preHydrationIndexTracker.current;\n preHydrationIndexTracker.current++;\n } else {\n index = element ? collection.indexOf(element) : -1;\n }\n\n const composedInputRef = useComposedRefs(forwardedRef, inputRef, setElement);\n const char = context.value[index] ?? '';\n\n const keyboardActionTimeoutRef = React.useRef<number | null>(null);\n React.useEffect(() => {\n return () => {\n window.clearTimeout(keyboardActionTimeoutRef.current!);\n };\n }, []);\n\n const totalValue = context.value.join('').trim();\n const lastSelectableIndex = clamp(totalValue.length, [0, collection.size - 1]);\n const isFocusable = index <= lastSelectableIndex;\n\n const validation =\n validationType in INPUT_VALIDATION_MAP\n ? INPUT_VALIDATION_MAP[validationType as keyof InputValidation]\n : undefined;\n\n return (\n <Collection.ItemSlot scope={__scopeOneTimePasswordField}>\n <RovingFocusGroup.Item\n {...rovingFocusGroupScope}\n asChild\n focusable={!context.disabled && isFocusable}\n active={index === lastSelectableIndex}\n >\n <Primitive.input\n ref={composedInputRef}\n type=\"text\"\n aria-label={`Character ${index + 1} of ${collection.size}`}\n autoComplete={index === 0 ? context.autoComplete : 'off'}\n inputMode={validation?.inputMode}\n maxLength={1}\n pattern={validation?.pattern}\n readOnly={context.readOnly}\n value={char}\n data-radix-otp-input=\"\"\n data-radix-index={index}\n {...domProps}\n onFocus={composeEventHandlers(props.onFocus, (event) => {\n event.currentTarget.select();\n })}\n onCut={composeEventHandlers(props.onCut, (event) => {\n const currentValue = event.currentTarget.value;\n if (currentValue !== '') {\n // In this case the value will be cleared, but we don't want to\n // set it directly because the user may want to prevent default\n // behavior in the onChange handler. The userActionRef will\n // is set temporarily so the change handler can behave correctly\n // in response to the action.\n userActionRef.current = {\n type: 'cut',\n };\n // Set a short timeout to clear the action tracker after the change\n // handler has had time to complete.\n keyboardActionTimeoutRef.current = window.setTimeout(() => {\n userActionRef.current = null;\n }, 10);\n }\n })}\n onChange={composeEventHandlers(props.onChange, (event) => {\n const action = userActionRef.current;\n userActionRef.current = null;\n\n if (action) {\n switch (action.type) {\n case 'cut':\n // TODO: do we want to assume the user wantt to clear the\n // entire value here and copy the code to the clipboard instead\n // of just the value of the given input?\n dispatch({ type: 'CLEAR_CHAR', index, reason: 'Cut' });\n return;\n case 'keydown': {\n if (action.key === 'Char') {\n // update resulting from a keydown event that set a value\n // directly. Ignore.\n return;\n }\n\n const isClearing =\n action.key === 'Backspace' && (action.metaKey || action.ctrlKey);\n if (isClearing) {\n dispatch({ type: 'CLEAR', reason: 'Backspace' });\n } else {\n dispatch({ type: 'CLEAR_CHAR', index, reason: action.key });\n }\n return;\n }\n default:\n return;\n }\n }\n\n // Only update the value if it matches the input pattern\n if (event.target.validity.valid) {\n if (event.target.value === '') {\n let reason: 'Backspace' | 'Delete' | 'Cut' = 'Backspace';\n if (isInputEvent(event.nativeEvent)) {\n const inputType = event.nativeEvent.inputType;\n if (inputType === 'deleteContentBackward') {\n reason = 'Backspace';\n } else if (inputType === 'deleteByCut') {\n reason = 'Cut';\n }\n }\n dispatch({ type: 'CLEAR_CHAR', index, reason });\n } else {\n dispatch({ type: 'SET_CHAR', char: event.target.value, index, event });\n }\n } else {\n const element = event.target;\n requestAnimationFrame(() => {\n if (element.ownerDocument.activeElement === element) {\n element.select();\n }\n });\n }\n })}\n onKeyDown={composeEventHandlers(props.onKeyDown, (event) => {\n switch (event.key) {\n case 'Delete':\n case 'Backspace': {\n const currentValue = event.currentTarget.value;\n // if current value is empty, no change event will fire\n if (currentValue === '') {\n // if the user presses delete when there is no value, noop\n if (event.key === 'Delete') return;\n\n const isClearing = event.metaKey || event.ctrlKey;\n if (isClearing) {\n dispatch({ type: 'CLEAR', reason: 'Backspace' });\n } else {\n const element = event.currentTarget;\n requestAnimationFrame(() => {\n focusInput(collection.from(element, -1)?.element);\n });\n }\n } else {\n // In this case the value will be cleared, but we don't want\n // to set it directly because the user may want to prevent\n // default behavior in the onChange handler. The userActionRef\n // will is set temporarily so the change handler can behave\n // correctly in response to the key vs. clearing the value by\n // setting state externally.\n userActionRef.current = {\n type: 'keydown',\n key: event.key,\n metaKey: event.metaKey,\n ctrlKey: event.ctrlKey,\n };\n // Set a short timeout to clear the action tracker after the change\n // handler has had time to complete.\n keyboardActionTimeoutRef.current = window.setTimeout(() => {\n userActionRef.current = null;\n }, 10);\n }\n\n return;\n }\n case 'Enter': {\n event.preventDefault();\n context.attemptSubmit();\n return;\n }\n case 'ArrowDown':\n case 'ArrowUp': {\n if (context.orientation === 'horizontal') {\n // in horizontal orientation, the up/down will de-select the\n // input instead of moving focus\n event.preventDefault();\n }\n return;\n }\n // TODO: Handle left/right arrow keys in vertical writing mode\n default: {\n if (event.currentTarget.value === event.key) {\n // if current value is same as the key press, no change event\n // will fire. Focus the next input.\n const element = event.currentTarget;\n event.preventDefault();\n focusInput(collection.from(element, 1)?.element);\n return;\n } else if (\n // input already has a value, but...\n event.currentTarget.value &&\n // the value is not selected\n !(\n event.currentTarget.selectionStart === 0 &&\n event.currentTarget.selectionEnd != null &&\n event.currentTarget.selectionEnd > 0\n )\n ) {\n const attemptedValue = event.key;\n if (event.key.length > 1 || event.key === ' ') {\n // not a character; do nothing\n return;\n } else {\n // user is attempting to enter a character, but the input\n // will not update by default since it's limited to a single\n // character.\n const nextInput = collection.from(event.currentTarget, 1)?.element;\n const lastInput = collection.at(-1)?.element;\n if (nextInput !== lastInput && event.currentTarget !== lastInput) {\n // if selection is before the value, set the value of the\n // current input. Otherwise set the value of the next\n // input.\n if (event.currentTarget.selectionStart === 0) {\n dispatch({ type: 'SET_CHAR', char: attemptedValue, index, event });\n } else {\n dispatch({\n type: 'SET_CHAR',\n char: attemptedValue,\n index: index + 1,\n event,\n });\n }\n\n userActionRef.current = {\n type: 'keydown',\n key: 'Char',\n metaKey: event.metaKey,\n ctrlKey: event.ctrlKey,\n };\n keyboardActionTimeoutRef.current = window.setTimeout(() => {\n userActionRef.current = null;\n }, 10);\n }\n }\n }\n }\n }\n })}\n onPointerDown={composeEventHandlers(props.onPointerDown, (event) => {\n if (index > lastSelectableIndex) {\n event.preventDefault();\n const element = collection.at(lastSelectableIndex)?.element;\n focusInput(element);\n }\n })}\n />\n </RovingFocusGroup.Item>\n </Collection.ItemSlot>\n );\n});\n\nconst Root = OneTimePasswordField;\nconst Input = OneTimePasswordFieldInput;\nconst HiddenInput = OneTimePasswordFieldHiddenInput;\n\nexport {\n OneTimePasswordField,\n OneTimePasswordFieldInput,\n OneTimePasswordFieldHiddenInput,\n //\n Root,\n Input,\n HiddenInput,\n};\nexport type {\n OneTimePasswordFieldProps,\n OneTimePasswordFieldInputProps,\n OneTimePasswordFieldHiddenInputProps,\n InputValidationType,\n};\n\nfunction isFormElement(element: Element | null | undefined): element is HTMLFormElement {\n return element?.tagName === 'FORM';\n}\n\nfunction sanitizeValue(value: string | string[], regexp: RegExp | undefined | null) {\n if (Array.isArray(value)) {\n value = value.join('');\n }\n if (regexp) {\n // global regexp is stateful, so we clone it for each call\n regexp = new RegExp(regexp);\n return value.replace(regexp, '').split('').filter(Boolean);\n }\n return value.split('').filter(Boolean);\n}\n\nfunction focusInput(element: HTMLInputElement | null | undefined) {\n if (!element) return;\n if (element.ownerDocument.activeElement === element) {\n // if the element is already focused, select the value in the next\n // animation frame\n window.requestAnimationFrame(() => {\n element.select?.();\n });\n } else {\n element.focus();\n }\n}\n\nfunction isInputEvent(event: Event): event is InputEvent {\n return event.type === 'input';\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAAAA;AAAA;AAAA;;;ACAA,6BAA0B;AAC1B,gCAAgC;AAChC,0CAAqC;AACrC,uBAAqC;AACrC,8BAA8D;AAC9D,uBAAkC;AAClC,gCAA4C;AAC5C,mCAA8B;AAC9B,YAAuB;AACvB,uBAA0B;AAE1B,2BAAmC;AACnC,6BAA6B;AAC7B,oBAAsB;AACtB,oCAA+B;AAuHzB;AAvFN,IAAM,uBAAuB;AAAA,EAC3B,SAAS;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AACF;AAuBA,IAAM,+BAA+B;AACrC,IAAM,CAAC,YAAY,eAAe,qBAAqB,QAAI,wBAAAC;AAAA,EACzD;AACF;AACA,IAAM,CAAC,iCAAiC,QAAI,yCAAmB,8BAA8B;AAAA,EAC3F;AAAA,EACA;AACF,CAAC;AACD,IAAM,+BAA2B,uDAA4B;AAE7D,IAAM,CAAC,6BAA6B,8BAA8B,IAChE,kCAAoE,4BAA4B;AA4BlG,IAAM,yCAAyC,CAAC;AAAA,EAC9C;AAAA,EACA;AACF,MAAkD;AAChD,SACE,4CAAC,WAAW,UAAX,EAAoB,OAAO,6BAC1B,sDAAC,WAAW,MAAX,EAAgB,OAAO,6BAA8B,UAAS,GACjE;AAEJ;AASA,IAAM,2BAAiC;AAAA,EACrC,SAASC,0BACP;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,IACX,eAAe;AAAA,IACf,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,OAAO;AAAA;AAAA,IAEP,cAAc;AAAA,IACd;AAAA,IACA,iBAAiB;AAAA,IACjB,GAAG;AAAA,EACL,GACA,cACA;AACA,UAAM,wBAAwB,yBAAyB,2BAA2B;AAClF,UAAM,gBAAY,qCAAa,GAAG;AAClC,UAAM,aAAa,cAAc,2BAA2B;AAE5D,UAAM,aACJ,kBAAkB,uBACd,qBAAqB,cAAuC,IAC5D;AAEN,UAAM,kBAAwB,cAAQ,MAAM;AAC1C,aAAO,aAAa,OAAO,cAAc,WAAW,YAAY,MAAM,IAAI;AAAA,IAC5E,GAAG,CAAC,WAAW,YAAY,MAAM,CAAC;AAElC,UAAM,CAAC,OAAO,QAAQ,QAAI,0DAAqB;AAAA,MAC7C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,aAAa,gBAAgB,OAAO,cAAc,cAAc,YAAY,MAAM,IAAI,CAAC;AAAA,MACvF,UAAU,CAACC,WAAU,gBAAgBA,OAAM,OAAO,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IACrE,CAAC;AAGD,UAAM,eAAW,8CAA2B,CAAC,WAAW;AACtD,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK,YAAY;AACf,gBAAM,EAAE,OAAO,KAAK,IAAI;AACxB,gBAAM,gBAAgB,WAAW,GAAG,KAAK,GAAG;AAC5C,cAAI,MAAM,KAAK,MAAM,MAAM;AACzB,kBAAM,OAAO,iBAAiB,WAAW,KAAK,eAAe,CAAC,GAAG;AACjE,uBAAW,IAAI;AACf;AAAA,UACF;AAGA,cAAI,SAAS,IAAI;AACf;AAAA,UACF;AAEA,cAAI,YAAY;AACd,kBAAM,SAAS,IAAI,OAAO,WAAW,MAAM;AAC3C,kBAAM,QAAQ,KAAK,QAAQ,QAAQ,EAAE;AACrC,gBAAI,UAAU,MAAM;AAElB;AAAA,YACF;AAAA,UACF;AAGA,cAAI,MAAM,UAAU,WAAW,MAAM;AAEnC,kBAAMC,YAAW,CAAC,GAAG,KAAK;AAC1B,YAAAA,UAAS,KAAK,IAAI;AAClB,4CAAU,MAAM,SAASA,SAAQ,CAAC;AAClC,kBAAM,OAAO,iBAAiB,WAAW,KAAK,eAAe,CAAC,GAAG;AACjE,uBAAW,IAAI;AACf;AAAA,UACF;AAEA,gBAAM,WAAW;AAAA;AAAA,YAEf,GAAG,MAAM,MAAM,GAAG,KAAK;AAAA,YACvB;AAAA,YACA,GAAG,MAAM,MAAM,KAAK;AAAA,UACtB;AAEA,gBAAM,cAAc,WAAW,GAAG,EAAE,GAAG;AACvC,0CAAU,MAAM,SAAS,QAAQ,CAAC;AAClC,cAAI,kBAAkB,aAAa;AACjC,kBAAM,OAAO,iBAAiB,WAAW,KAAK,eAAe,CAAC,GAAG;AACjE,uBAAW,IAAI;AAAA,UACjB,OAAO;AACL,2BAAe,OAAO;AAAA,UACxB;AACA;AAAA,QACF;AAAA,QAEA,KAAK,cAAc;AACjB,gBAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,cAAI,CAAC,MAAM,KAAK,GAAG;AACjB;AAAA,UACF;AAEA,gBAAM,WAAW,MAAM,OAAO,CAAC,GAAG,MAAM,MAAM,KAAK;AACnD,gBAAM,gBAAgB,WAAW,GAAG,KAAK,GAAG;AAC5C,gBAAM,WAAW,iBAAiB,WAAW,KAAK,eAAe,EAAE,GAAG;AAEtE,0CAAU,MAAM,SAAS,QAAQ,CAAC;AAClC,cAAI,WAAW,aAAa;AAC1B,uBAAW,QAAQ;AAAA,UACrB,WAAW,WAAW,YAAY,WAAW,OAAO;AAClD,uBAAW,aAAa;AAAA,UAC1B;AACA;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,cAAI,MAAM,WAAW,GAAG;AACtB;AAAA,UACF;AAEA,cAAI,OAAO,WAAW,eAAe,OAAO,WAAW,UAAU;AAC/D,4CAAU,MAAM,SAAS,CAAC,CAAC,CAAC;AAC5B,uBAAW,WAAW,GAAG,CAAC,GAAG,OAAO;AAAA,UACtC,OAAO;AACL,qBAAS,CAAC,CAAC;AAAA,UACb;AACA;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,EAAE,OAAO,YAAY,IAAI;AAC/B,gBAAMD,SAAQ,cAAc,aAAa,YAAY,MAAM;AAC3D,cAAI,CAACA,QAAO;AACV;AAAA,UACF;AAEA,0CAAU,MAAM,SAASA,MAAK,CAAC;AAC/B,qBAAW,WAAW,GAAGA,OAAM,SAAS,CAAC,GAAG,OAAO;AACnD;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAGD,UAAM,oBAA0B,aAAO,UAAU;AACjD,IAAM,gBAAU,MAAM;AACpB,UAAI,CAAC,YAAY;AACf;AAAA,MACF;AAEA,UAAI,kBAAkB,SAAS,SAAS,WAAW,MAAM;AACvD,0BAAkB,UAAU;AAC5B,iBAAS,cAAc,OAAO,WAAW,MAAM,CAAC;AAAA,MAClD;AAAA,IACF,GAAG,CAAC,UAAU,YAAY,KAAK,CAAC;AAEhC,UAAM,iBAAuB,aAAyB,IAAI;AAE1D,UAAM,gBAAsB,aAAqC,IAAI;AACrE,UAAM,UAAgB,aAA8B,IAAI;AACxD,UAAM,mBAAe,2CAAgB,cAAc,OAAO;AAE1D,UAAM,aAAa,WAAW,GAAG,CAAC,GAAG;AACrC,UAAM,aAAmB,kBAAY,MAAM;AACzC,UAAI;AACJ,UAAI,MAAM;AACR,cAAM,qBAAqB,QAAQ,SAAS,iBAAiB,UAAU,eAAe,IAAI;AAC1F,YAAI,cAAc,iBAAiB,GAAG;AACpC,wBAAc;AAAA,QAChB;AAAA,MACF,WAAW,eAAe,SAAS;AACjC,sBAAc,eAAe,QAAQ;AAAA,MACvC,WAAW,YAAY;AACrB,sBAAc,WAAW;AAAA,MAC3B;AAEA,aAAO,eAAe;AAAA,IACxB,GAAG,CAAC,MAAM,UAAU,CAAC;AAErB,UAAM,gBAAsB,kBAAY,MAAM;AAC5C,YAAM,cAAc,WAAW;AAC/B,mBAAa,cAAc;AAAA,IAC7B,GAAG,CAAC,UAAU,CAAC;AAEf,IAAM,gBAAU,MAAM;AACpB,YAAME,QAAO,WAAW;AACxB,UAAIA,OAAM;AACR,cAAM,QAAQ,MAAM,SAAS,EAAE,MAAM,SAAS,QAAQ,QAAQ,CAAC;AAC/D,QAAAA,MAAK,iBAAiB,SAAS,KAAK;AACpC,eAAO,MAAMA,MAAK,oBAAoB,SAAS,KAAK;AAAA,MACtD;AAAA,IACF,GAAG,CAAC,UAAU,UAAU,CAAC;AAEzB,UAAM,eAAe,MAAM,KAAK,EAAE;AAClC,UAAM,WAAiB,aAAO,YAAY;AAC1C,UAAM,SAAS,WAAW;AAC1B,IAAM,gBAAU,MAAM;AACpB,YAAM,gBAAgB,SAAS;AAC/B,eAAS,UAAU;AACnB,UAAI,kBAAkB,cAAc;AAClC;AAAA,MACF;AAEA,UAAI,cAAc,MAAM,MAAM,CAAC,SAAS,SAAS,EAAE,KAAK,MAAM,WAAW,QAAQ;AAC/E,uBAAe,MAAM,KAAK,EAAE,CAAC;AAC7B,sBAAc;AAAA,MAChB;AAAA,IACF,GAAG,CAAC,eAAe,YAAY,cAAc,QAAQ,cAAc,KAAK,CAAC;AAOzE,UAAM,2BAAiC,aAAe,CAAC;AACvD,UAAM,iBAAa,4CAAc;AAEjC,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QAEA;AAAA,UAAkB;AAAA,UAAjB;AAAA,YACC,SAAO;AAAA,YACN,GAAG;AAAA,YACJ;AAAA,YACA,KAAK;AAAA,YAEL;AAAA,cAAC,iCAAU;AAAA,cAAV;AAAA,gBACE,GAAG;AAAA,gBACJ,MAAK;AAAA,gBACL,KAAK;AAAA,gBACL,aAAS;AAAA,kBACP;AAAA,kBACA,CAAC,UAAgD;AAC/C,0BAAM,eAAe;AACrB,0BAAM,cAAc,MAAM,cAAc,QAAQ,MAAM;AACtD,0BAAMF,SAAQ,cAAc,aAAa,YAAY,MAAM;AAC3D,wBAAI,CAACA,QAAO;AACV;AAAA,oBACF;AACA,6BAAS,EAAE,MAAM,SAAS,OAAO,YAAY,CAAC;AAAA,kBAChD;AAAA,gBACF;AAAA,gBAEC;AAAA;AAAA,YACH;AAAA;AAAA,QACF;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,IAAM,uBAA6B;AAAA,EACjC,SAASG,sBAAqB,OAAO,KAAK;AACxC,WACE,4CAAC,0CACC,sDAAC,4BAA0B,GAAG,OAAO,KAAU,GACjD;AAAA,EAEJ;AACF;AAeA,IAAM,kCAAwC,iBAG5C,SAASC,iCACT,EAAE,6BAA6B,GAAG,MAAM,GACxC,cACA;AACA,QAAM,EAAE,OAAO,eAAe,IAAI;AAAA,IAChC;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAM,2CAAgB,gBAAgB,YAAY;AACxD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACC,GAAG;AAAA,MACJ,MAAK;AAAA,MACL,UAAQ;AAAA,MACR,OAAO,MAAM,KAAK,EAAE,EAAE,KAAK;AAAA,MAC3B,cAAa;AAAA,MACb,WAAW;AAAA,MACX,gBAAe;AAAA,MACf,aAAY;AAAA,MACZ,UAAS;AAAA,MACT,YAAY;AAAA;AAAA,EACd;AAEJ,CAAC;AAUD,IAAM,4BAAkC,iBAGtC,SAASC,2BACT,EAAE,6BAA6B,GAAG,MAAM,GACxC,cACA;AAEA,QAAM;AAAA,IACJ,OAAO;AAAA,IACP,cAAc;AAAA,IACd,UAAU;AAAA,IACV,UAAU;AAAA,IACV,cAAc;AAAA,IACd,WAAW;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,EACF;AACA,QAAM,EAAE,UAAU,eAAe,gBAAgB,0BAA0B,WAAW,IAAI;AAC1F,QAAM,aAAa,cAAc,2BAA2B;AAC5D,QAAM,wBAAwB,yBAAyB,2BAA2B;AAElF,QAAM,WAAiB,aAAyB,IAAI;AACpD,QAAM,CAAC,SAAS,UAAU,IAAU,eAAkC,IAAI;AAE1E,MAAI;AACJ,MAAI,CAAC,YAAY;AACf,YAAQ,yBAAyB;AACjC,6BAAyB;AAAA,EAC3B,OAAO;AACL,YAAQ,UAAU,WAAW,QAAQ,OAAO,IAAI;AAAA,EAClD;AAEA,QAAM,uBAAmB,2CAAgB,cAAc,UAAU,UAAU;AAC3E,QAAM,OAAO,QAAQ,MAAM,KAAK,KAAK;AAErC,QAAM,2BAAiC,aAAsB,IAAI;AACjE,EAAM,gBAAU,MAAM;AACpB,WAAO,MAAM;AACX,aAAO,aAAa,yBAAyB,OAAQ;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,QAAQ,MAAM,KAAK,EAAE,EAAE,KAAK;AAC/C,QAAM,0BAAsB,qBAAM,WAAW,QAAQ,CAAC,GAAG,WAAW,OAAO,CAAC,CAAC;AAC7E,QAAM,cAAc,SAAS;AAE7B,QAAM,aACJ,kBAAkB,uBACd,qBAAqB,cAAuC,IAC5D;AAEN,SACE,4CAAC,WAAW,UAAX,EAAoB,OAAO,6BAC1B;AAAA,IAAkB;AAAA,IAAjB;AAAA,MACE,GAAG;AAAA,MACJ,SAAO;AAAA,MACP,WAAW,CAAC,QAAQ,YAAY;AAAA,MAChC,QAAQ,UAAU;AAAA,MAElB;AAAA,QAAC,iCAAU;AAAA,QAAV;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,cAAY,aAAa,QAAQ,CAAC,OAAO,WAAW,IAAI;AAAA,UACxD,cAAc,UAAU,IAAI,QAAQ,eAAe;AAAA,UACnD,WAAW,YAAY;AAAA,UACvB,WAAW;AAAA,UACX,SAAS,YAAY;AAAA,UACrB,UAAU,QAAQ;AAAA,UAClB,OAAO;AAAA,UACP,wBAAqB;AAAA,UACrB,oBAAkB;AAAA,UACjB,GAAG;AAAA,UACJ,aAAS,uCAAqB,MAAM,SAAS,CAAC,UAAU;AACtD,kBAAM,cAAc,OAAO;AAAA,UAC7B,CAAC;AAAA,UACD,WAAO,uCAAqB,MAAM,OAAO,CAAC,UAAU;AAClD,kBAAM,eAAe,MAAM,cAAc;AACzC,gBAAI,iBAAiB,IAAI;AAMvB,4BAAc,UAAU;AAAA,gBACtB,MAAM;AAAA,cACR;AAGA,uCAAyB,UAAU,OAAO,WAAW,MAAM;AACzD,8BAAc,UAAU;AAAA,cAC1B,GAAG,EAAE;AAAA,YACP;AAAA,UACF,CAAC;AAAA,UACD,cAAU,uCAAqB,MAAM,UAAU,CAAC,UAAU;AACxD,kBAAM,SAAS,cAAc;AAC7B,0BAAc,UAAU;AAExB,gBAAI,QAAQ;AACV,sBAAQ,OAAO,MAAM;AAAA,gBACnB,KAAK;AAIH,2BAAS,EAAE,MAAM,cAAc,OAAO,QAAQ,MAAM,CAAC;AACrD;AAAA,gBACF,KAAK,WAAW;AACd,sBAAI,OAAO,QAAQ,QAAQ;AAGzB;AAAA,kBACF;AAEA,wBAAM,aACJ,OAAO,QAAQ,gBAAgB,OAAO,WAAW,OAAO;AAC1D,sBAAI,YAAY;AACd,6BAAS,EAAE,MAAM,SAAS,QAAQ,YAAY,CAAC;AAAA,kBACjD,OAAO;AACL,6BAAS,EAAE,MAAM,cAAc,OAAO,QAAQ,OAAO,IAAI,CAAC;AAAA,kBAC5D;AACA;AAAA,gBACF;AAAA,gBACA;AACE;AAAA,cACJ;AAAA,YACF;AAGA,gBAAI,MAAM,OAAO,SAAS,OAAO;AAC/B,kBAAI,MAAM,OAAO,UAAU,IAAI;AAC7B,oBAAI,SAAyC;AAC7C,oBAAI,aAAa,MAAM,WAAW,GAAG;AACnC,wBAAM,YAAY,MAAM,YAAY;AACpC,sBAAI,cAAc,yBAAyB;AACzC,6BAAS;AAAA,kBACX,WAAW,cAAc,eAAe;AACtC,6BAAS;AAAA,kBACX;AAAA,gBACF;AACA,yBAAS,EAAE,MAAM,cAAc,OAAO,OAAO,CAAC;AAAA,cAChD,OAAO;AACL,yBAAS,EAAE,MAAM,YAAY,MAAM,MAAM,OAAO,OAAO,OAAO,MAAM,CAAC;AAAA,cACvE;AAAA,YACF,OAAO;AACL,oBAAMC,WAAU,MAAM;AACtB,oCAAsB,MAAM;AAC1B,oBAAIA,SAAQ,cAAc,kBAAkBA,UAAS;AACnD,kBAAAA,SAAQ,OAAO;AAAA,gBACjB;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF,CAAC;AAAA,UACD,eAAW,uCAAqB,MAAM,WAAW,CAAC,UAAU;AAC1D,oBAAQ,MAAM,KAAK;AAAA,cACjB,KAAK;AAAA,cACL,KAAK,aAAa;AAChB,sBAAM,eAAe,MAAM,cAAc;AAEzC,oBAAI,iBAAiB,IAAI;AAEvB,sBAAI,MAAM,QAAQ,SAAU;AAE5B,wBAAM,aAAa,MAAM,WAAW,MAAM;AAC1C,sBAAI,YAAY;AACd,6BAAS,EAAE,MAAM,SAAS,QAAQ,YAAY,CAAC;AAAA,kBACjD,OAAO;AACL,0BAAMA,WAAU,MAAM;AACtB,0CAAsB,MAAM;AAC1B,iCAAW,WAAW,KAAKA,UAAS,EAAE,GAAG,OAAO;AAAA,oBAClD,CAAC;AAAA,kBACH;AAAA,gBACF,OAAO;AAOL,gCAAc,UAAU;AAAA,oBACtB,MAAM;AAAA,oBACN,KAAK,MAAM;AAAA,oBACX,SAAS,MAAM;AAAA,oBACf,SAAS,MAAM;AAAA,kBACjB;AAGA,2CAAyB,UAAU,OAAO,WAAW,MAAM;AACzD,kCAAc,UAAU;AAAA,kBAC1B,GAAG,EAAE;AAAA,gBACP;AAEA;AAAA,cACF;AAAA,cACA,KAAK,SAAS;AACZ,sBAAM,eAAe;AACrB,wBAAQ,cAAc;AACtB;AAAA,cACF;AAAA,cACA,KAAK;AAAA,cACL,KAAK,WAAW;AACd,oBAAI,QAAQ,gBAAgB,cAAc;AAGxC,wBAAM,eAAe;AAAA,gBACvB;AACA;AAAA,cACF;AAAA;AAAA,cAEA,SAAS;AACP,oBAAI,MAAM,cAAc,UAAU,MAAM,KAAK;AAG3C,wBAAMA,WAAU,MAAM;AACtB,wBAAM,eAAe;AACrB,6BAAW,WAAW,KAAKA,UAAS,CAAC,GAAG,OAAO;AAC/C;AAAA,gBACF;AAAA;AAAA,kBAEE,MAAM,cAAc;AAAA,kBAEpB,EACE,MAAM,cAAc,mBAAmB,KACvC,MAAM,cAAc,gBAAgB,QACpC,MAAM,cAAc,eAAe;AAAA,kBAErC;AACA,wBAAM,iBAAiB,MAAM;AAC7B,sBAAI,MAAM,IAAI,SAAS,KAAK,MAAM,QAAQ,KAAK;AAE7C;AAAA,kBACF,OAAO;AAIL,0BAAM,YAAY,WAAW,KAAK,MAAM,eAAe,CAAC,GAAG;AAC3D,0BAAM,YAAY,WAAW,GAAG,EAAE,GAAG;AACrC,wBAAI,cAAc,aAAa,MAAM,kBAAkB,WAAW;AAIhE,0BAAI,MAAM,cAAc,mBAAmB,GAAG;AAC5C,iCAAS,EAAE,MAAM,YAAY,MAAM,gBAAgB,OAAO,MAAM,CAAC;AAAA,sBACnE,OAAO;AACL,iCAAS;AAAA,0BACP,MAAM;AAAA,0BACN,MAAM;AAAA,0BACN,OAAO,QAAQ;AAAA,0BACf;AAAA,wBACF,CAAC;AAAA,sBACH;AAEA,oCAAc,UAAU;AAAA,wBACtB,MAAM;AAAA,wBACN,KAAK;AAAA,wBACL,SAAS,MAAM;AAAA,wBACf,SAAS,MAAM;AAAA,sBACjB;AACA,+CAAyB,UAAU,OAAO,WAAW,MAAM;AACzD,sCAAc,UAAU;AAAA,sBAC1B,GAAG,EAAE;AAAA,oBACP;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF,CAAC;AAAA,UACD,mBAAe,uCAAqB,MAAM,eAAe,CAAC,UAAU;AAClE,gBAAI,QAAQ,qBAAqB;AAC/B,oBAAM,eAAe;AACrB,oBAAMA,WAAU,WAAW,GAAG,mBAAmB,GAAG;AACpD,yBAAWA,QAAO;AAAA,YACpB;AAAA,UACF,CAAC;AAAA;AAAA,MACH;AAAA;AAAA,EACF,GACF;AAEJ,CAAC;AAED,IAAMC,QAAO;AACb,IAAM,QAAQ;AACd,IAAM,cAAc;AAkBpB,SAAS,cAAc,SAAiE;AACtF,SAAO,SAAS,YAAY;AAC9B;AAEA,SAAS,cAAc,OAA0B,QAAmC;AAClF,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,YAAQ,MAAM,KAAK,EAAE;AAAA,EACvB;AACA,MAAI,QAAQ;AAEV,aAAS,IAAI,OAAO,MAAM;AAC1B,WAAO,MAAM,QAAQ,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,OAAO;AAAA,EAC3D;AACA,SAAO,MAAM,MAAM,EAAE,EAAE,OAAO,OAAO;AACvC;AAEA,SAAS,WAAW,SAA8C;AAChE,MAAI,CAAC,QAAS;AACd,MAAI,QAAQ,cAAc,kBAAkB,SAAS;AAGnD,WAAO,sBAAsB,MAAM;AACjC,cAAQ,SAAS;AAAA,IACnB,CAAC;AAAA,EACH,OAAO;AACL,YAAQ,MAAM;AAAA,EAChB;AACF;AAEA,SAAS,aAAa,OAAmC;AACvD,SAAO,MAAM,SAAS;AACxB;",
6
+ "names": ["Root", "createCollection", "OneTimePasswordFieldImpl", "value", "newValue", "form", "OneTimePasswordField", "OneTimePasswordFieldHiddenInput", "OneTimePasswordFieldInput", "element", "Root"]
7
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,580 @@
1
+ "use client";
2
+
3
+ // src/one-time-password-field.tsx
4
+ import { Primitive } from "@radix-ui/react-primitive";
5
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
6
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
7
+ import { composeEventHandlers } from "@radix-ui/primitive";
8
+ import { unstable_createCollection as createCollection } from "@radix-ui/react-collection";
9
+ import * as RovingFocusGroup from "@radix-ui/react-roving-focus";
10
+ import { createRovingFocusGroupScope } from "@radix-ui/react-roving-focus";
11
+ import { useIsHydrated } from "@radix-ui/react-use-is-hydrated";
12
+ import * as React from "react";
13
+ import { flushSync } from "react-dom";
14
+ import { createContextScope } from "@radix-ui/react-context";
15
+ import { useDirection } from "@radix-ui/react-direction";
16
+ import { clamp } from "@radix-ui/number";
17
+ import { useEffectEvent } from "@radix-ui/react-use-effect-event";
18
+ import { jsx } from "react/jsx-runtime";
19
+ var INPUT_VALIDATION_MAP = {
20
+ numeric: {
21
+ type: "numeric",
22
+ regexp: /[^\d]/g,
23
+ pattern: "\\d{1}",
24
+ inputMode: "numeric"
25
+ },
26
+ alpha: {
27
+ type: "alpha",
28
+ regexp: /[^a-zA-Z]/g,
29
+ pattern: "[a-zA-Z]{1}",
30
+ inputMode: "text"
31
+ },
32
+ alphanumeric: {
33
+ type: "alphanumeric",
34
+ regexp: /[^a-zA-Z0-9]/g,
35
+ pattern: "[a-zA-Z0-9]{1}",
36
+ inputMode: "text"
37
+ }
38
+ };
39
+ var ONE_TIME_PASSWORD_FIELD_NAME = "OneTimePasswordField";
40
+ var [Collection, useCollection, createCollectionScope] = createCollection(
41
+ ONE_TIME_PASSWORD_FIELD_NAME
42
+ );
43
+ var [createOneTimePasswordFieldContext] = createContextScope(ONE_TIME_PASSWORD_FIELD_NAME, [
44
+ createCollectionScope,
45
+ createRovingFocusGroupScope
46
+ ]);
47
+ var useRovingFocusGroupScope = createRovingFocusGroupScope();
48
+ var [OneTimePasswordFieldContext, useOneTimePasswordFieldContext] = createOneTimePasswordFieldContext(ONE_TIME_PASSWORD_FIELD_NAME);
49
+ var OneTimePasswordFieldCollectionProvider = ({
50
+ __scopeOneTimePasswordField,
51
+ children
52
+ }) => {
53
+ return /* @__PURE__ */ jsx(Collection.Provider, { scope: __scopeOneTimePasswordField, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: __scopeOneTimePasswordField, children }) });
54
+ };
55
+ var OneTimePasswordFieldImpl = React.forwardRef(
56
+ function OneTimePasswordFieldImpl2({
57
+ __scopeOneTimePasswordField,
58
+ id,
59
+ defaultValue,
60
+ value: valueProp,
61
+ onValueChange,
62
+ autoSubmit,
63
+ children,
64
+ onPaste,
65
+ onAutoSubmit,
66
+ disabled = false,
67
+ readOnly = false,
68
+ autoComplete = "one-time-code",
69
+ autoFocus = false,
70
+ form,
71
+ name,
72
+ placeholder,
73
+ required = false,
74
+ type = "password",
75
+ // TODO: Change default to vertical when inputs use vertical writing mode
76
+ orientation = "horizontal",
77
+ dir,
78
+ validationType = "numeric",
79
+ ...domProps
80
+ }, forwardedRef) {
81
+ const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeOneTimePasswordField);
82
+ const direction = useDirection(dir);
83
+ const collection = useCollection(__scopeOneTimePasswordField);
84
+ const validation = validationType in INPUT_VALIDATION_MAP ? INPUT_VALIDATION_MAP[validationType] : void 0;
85
+ const controlledValue = React.useMemo(() => {
86
+ return valueProp != null ? sanitizeValue(valueProp, validation?.regexp) : void 0;
87
+ }, [valueProp, validation?.regexp]);
88
+ const [value, setValue] = useControllableState({
89
+ caller: "OneTimePasswordField",
90
+ prop: controlledValue,
91
+ defaultProp: defaultValue != null ? sanitizeValue(defaultValue, validation?.regexp) : [],
92
+ onChange: (value2) => onValueChange?.(value2.filter(Boolean).join(""))
93
+ });
94
+ const dispatch = useEffectEvent((action) => {
95
+ switch (action.type) {
96
+ case "SET_CHAR": {
97
+ const { index, char } = action;
98
+ const currentTarget = collection.at(index)?.element;
99
+ if (value[index] === char) {
100
+ const next = currentTarget && collection.from(currentTarget, 1)?.element;
101
+ focusInput(next);
102
+ return;
103
+ }
104
+ if (char === "") {
105
+ return;
106
+ }
107
+ if (validation) {
108
+ const regexp = new RegExp(validation.regexp);
109
+ const clean = char.replace(regexp, "");
110
+ if (clean !== char) {
111
+ return;
112
+ }
113
+ }
114
+ if (value.length >= collection.size) {
115
+ const newValue2 = [...value];
116
+ newValue2[index] = char;
117
+ flushSync(() => setValue(newValue2));
118
+ const next = currentTarget && collection.from(currentTarget, 1)?.element;
119
+ focusInput(next);
120
+ return;
121
+ }
122
+ const newValue = [
123
+ //
124
+ ...value.slice(0, index),
125
+ char,
126
+ ...value.slice(index)
127
+ ];
128
+ const lastElement = collection.at(-1)?.element;
129
+ flushSync(() => setValue(newValue));
130
+ if (currentTarget !== lastElement) {
131
+ const next = currentTarget && collection.from(currentTarget, 1)?.element;
132
+ focusInput(next);
133
+ } else {
134
+ currentTarget?.select();
135
+ }
136
+ return;
137
+ }
138
+ case "CLEAR_CHAR": {
139
+ const { index, reason } = action;
140
+ if (!value[index]) {
141
+ return;
142
+ }
143
+ const newValue = value.filter((_, i) => i !== index);
144
+ const currentTarget = collection.at(index)?.element;
145
+ const previous = currentTarget && collection.from(currentTarget, -1)?.element;
146
+ flushSync(() => setValue(newValue));
147
+ if (reason === "Backspace") {
148
+ focusInput(previous);
149
+ } else if (reason === "Delete" || reason === "Cut") {
150
+ focusInput(currentTarget);
151
+ }
152
+ return;
153
+ }
154
+ case "CLEAR": {
155
+ if (value.length === 0) {
156
+ return;
157
+ }
158
+ if (action.reason === "Backspace" || action.reason === "Delete") {
159
+ flushSync(() => setValue([]));
160
+ focusInput(collection.at(0)?.element);
161
+ } else {
162
+ setValue([]);
163
+ }
164
+ return;
165
+ }
166
+ case "PASTE": {
167
+ const { value: pastedValue } = action;
168
+ const value2 = sanitizeValue(pastedValue, validation?.regexp);
169
+ if (!value2) {
170
+ return;
171
+ }
172
+ flushSync(() => setValue(value2));
173
+ focusInput(collection.at(value2.length - 1)?.element);
174
+ return;
175
+ }
176
+ }
177
+ });
178
+ const validationTypeRef = React.useRef(validation);
179
+ React.useEffect(() => {
180
+ if (!validation) {
181
+ return;
182
+ }
183
+ if (validationTypeRef.current?.type !== validation.type) {
184
+ validationTypeRef.current = validation;
185
+ setValue(sanitizeValue(value, validation.regexp));
186
+ }
187
+ }, [setValue, validation, value]);
188
+ const hiddenInputRef = React.useRef(null);
189
+ const userActionRef = React.useRef(null);
190
+ const rootRef = React.useRef(null);
191
+ const composedRefs = useComposedRefs(forwardedRef, rootRef);
192
+ const firstInput = collection.at(0)?.element;
193
+ const locateForm = React.useCallback(() => {
194
+ let formElement;
195
+ if (form) {
196
+ const associatedElement = (rootRef.current?.ownerDocument ?? document).getElementById(form);
197
+ if (isFormElement(associatedElement)) {
198
+ formElement = associatedElement;
199
+ }
200
+ } else if (hiddenInputRef.current) {
201
+ formElement = hiddenInputRef.current.form;
202
+ } else if (firstInput) {
203
+ formElement = firstInput.form;
204
+ }
205
+ return formElement ?? null;
206
+ }, [form, firstInput]);
207
+ const attemptSubmit = React.useCallback(() => {
208
+ const formElement = locateForm();
209
+ formElement?.requestSubmit();
210
+ }, [locateForm]);
211
+ React.useEffect(() => {
212
+ const form2 = locateForm();
213
+ if (form2) {
214
+ const reset = () => dispatch({ type: "CLEAR", reason: "Reset" });
215
+ form2.addEventListener("reset", reset);
216
+ return () => form2.removeEventListener("reset", reset);
217
+ }
218
+ }, [dispatch, locateForm]);
219
+ const currentValue = value.join("");
220
+ const valueRef = React.useRef(currentValue);
221
+ const length = collection.size;
222
+ React.useEffect(() => {
223
+ const previousValue = valueRef.current;
224
+ valueRef.current = currentValue;
225
+ if (previousValue === currentValue) {
226
+ return;
227
+ }
228
+ if (autoSubmit && value.every((char) => char !== "") && value.length === length) {
229
+ onAutoSubmit?.(value.join(""));
230
+ attemptSubmit();
231
+ }
232
+ }, [attemptSubmit, autoSubmit, currentValue, length, onAutoSubmit, value]);
233
+ const preHydrationIndexTracker = React.useRef(0);
234
+ const isHydrated = useIsHydrated();
235
+ return /* @__PURE__ */ jsx(
236
+ OneTimePasswordFieldContext,
237
+ {
238
+ scope: __scopeOneTimePasswordField,
239
+ value,
240
+ attemptSubmit,
241
+ disabled,
242
+ readOnly,
243
+ autoComplete,
244
+ autoFocus,
245
+ form,
246
+ name,
247
+ placeholder,
248
+ required,
249
+ type,
250
+ hiddenInputRef,
251
+ userActionRef,
252
+ dispatch,
253
+ validationType,
254
+ orientation,
255
+ preHydrationIndexTracker,
256
+ isHydrated,
257
+ children: /* @__PURE__ */ jsx(
258
+ RovingFocusGroup.Root,
259
+ {
260
+ asChild: true,
261
+ ...rovingFocusGroupScope,
262
+ orientation,
263
+ dir: direction,
264
+ children: /* @__PURE__ */ jsx(
265
+ Primitive.div,
266
+ {
267
+ ...domProps,
268
+ role: "group",
269
+ ref: composedRefs,
270
+ onPaste: composeEventHandlers(
271
+ onPaste,
272
+ (event) => {
273
+ event.preventDefault();
274
+ const pastedValue = event.clipboardData.getData("Text");
275
+ const value2 = sanitizeValue(pastedValue, validation?.regexp);
276
+ if (!value2) {
277
+ return;
278
+ }
279
+ dispatch({ type: "PASTE", value: pastedValue });
280
+ }
281
+ ),
282
+ children
283
+ }
284
+ )
285
+ }
286
+ )
287
+ }
288
+ );
289
+ }
290
+ );
291
+ var OneTimePasswordField = React.forwardRef(
292
+ function OneTimePasswordField2(props, ref) {
293
+ return /* @__PURE__ */ jsx(OneTimePasswordFieldCollectionProvider, { children: /* @__PURE__ */ jsx(OneTimePasswordFieldImpl, { ...props, ref }) });
294
+ }
295
+ );
296
+ var OneTimePasswordFieldHiddenInput = React.forwardRef(function OneTimePasswordFieldHiddenInput2({ __scopeOneTimePasswordField, ...props }, forwardedRef) {
297
+ const { value, hiddenInputRef } = useOneTimePasswordFieldContext(
298
+ "OneTimePasswordFieldHiddenInput",
299
+ __scopeOneTimePasswordField
300
+ );
301
+ const ref = useComposedRefs(hiddenInputRef, forwardedRef);
302
+ return /* @__PURE__ */ jsx(
303
+ "input",
304
+ {
305
+ ref,
306
+ ...props,
307
+ type: "hidden",
308
+ readOnly: true,
309
+ value: value.join("").trim(),
310
+ autoComplete: "off",
311
+ autoFocus: false,
312
+ autoCapitalize: "off",
313
+ autoCorrect: "off",
314
+ autoSave: "off",
315
+ spellCheck: false
316
+ }
317
+ );
318
+ });
319
+ var OneTimePasswordFieldInput = React.forwardRef(function OneTimePasswordFieldInput2({ __scopeOneTimePasswordField, ...props }, forwardedRef) {
320
+ const {
321
+ value: _value,
322
+ defaultValue: _defaultValue,
323
+ disabled: _disabled,
324
+ readOnly: _readOnly,
325
+ autoComplete: _autoComplete,
326
+ autoFocus: _autoFocus,
327
+ form: _form,
328
+ name: _name,
329
+ placeholder: _placeholder,
330
+ required: _required,
331
+ type: _type,
332
+ ...domProps
333
+ } = props;
334
+ const context = useOneTimePasswordFieldContext(
335
+ "OneTimePasswordFieldInput",
336
+ __scopeOneTimePasswordField
337
+ );
338
+ const { dispatch, userActionRef, validationType, preHydrationIndexTracker, isHydrated } = context;
339
+ const collection = useCollection(__scopeOneTimePasswordField);
340
+ const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeOneTimePasswordField);
341
+ const inputRef = React.useRef(null);
342
+ const [element, setElement] = React.useState(null);
343
+ let index;
344
+ if (!isHydrated) {
345
+ index = preHydrationIndexTracker.current;
346
+ preHydrationIndexTracker.current++;
347
+ } else {
348
+ index = element ? collection.indexOf(element) : -1;
349
+ }
350
+ const composedInputRef = useComposedRefs(forwardedRef, inputRef, setElement);
351
+ const char = context.value[index] ?? "";
352
+ const keyboardActionTimeoutRef = React.useRef(null);
353
+ React.useEffect(() => {
354
+ return () => {
355
+ window.clearTimeout(keyboardActionTimeoutRef.current);
356
+ };
357
+ }, []);
358
+ const totalValue = context.value.join("").trim();
359
+ const lastSelectableIndex = clamp(totalValue.length, [0, collection.size - 1]);
360
+ const isFocusable = index <= lastSelectableIndex;
361
+ const validation = validationType in INPUT_VALIDATION_MAP ? INPUT_VALIDATION_MAP[validationType] : void 0;
362
+ return /* @__PURE__ */ jsx(Collection.ItemSlot, { scope: __scopeOneTimePasswordField, children: /* @__PURE__ */ jsx(
363
+ RovingFocusGroup.Item,
364
+ {
365
+ ...rovingFocusGroupScope,
366
+ asChild: true,
367
+ focusable: !context.disabled && isFocusable,
368
+ active: index === lastSelectableIndex,
369
+ children: /* @__PURE__ */ jsx(
370
+ Primitive.input,
371
+ {
372
+ ref: composedInputRef,
373
+ type: "text",
374
+ "aria-label": `Character ${index + 1} of ${collection.size}`,
375
+ autoComplete: index === 0 ? context.autoComplete : "off",
376
+ inputMode: validation?.inputMode,
377
+ maxLength: 1,
378
+ pattern: validation?.pattern,
379
+ readOnly: context.readOnly,
380
+ value: char,
381
+ "data-radix-otp-input": "",
382
+ "data-radix-index": index,
383
+ ...domProps,
384
+ onFocus: composeEventHandlers(props.onFocus, (event) => {
385
+ event.currentTarget.select();
386
+ }),
387
+ onCut: composeEventHandlers(props.onCut, (event) => {
388
+ const currentValue = event.currentTarget.value;
389
+ if (currentValue !== "") {
390
+ userActionRef.current = {
391
+ type: "cut"
392
+ };
393
+ keyboardActionTimeoutRef.current = window.setTimeout(() => {
394
+ userActionRef.current = null;
395
+ }, 10);
396
+ }
397
+ }),
398
+ onChange: composeEventHandlers(props.onChange, (event) => {
399
+ const action = userActionRef.current;
400
+ userActionRef.current = null;
401
+ if (action) {
402
+ switch (action.type) {
403
+ case "cut":
404
+ dispatch({ type: "CLEAR_CHAR", index, reason: "Cut" });
405
+ return;
406
+ case "keydown": {
407
+ if (action.key === "Char") {
408
+ return;
409
+ }
410
+ const isClearing = action.key === "Backspace" && (action.metaKey || action.ctrlKey);
411
+ if (isClearing) {
412
+ dispatch({ type: "CLEAR", reason: "Backspace" });
413
+ } else {
414
+ dispatch({ type: "CLEAR_CHAR", index, reason: action.key });
415
+ }
416
+ return;
417
+ }
418
+ default:
419
+ return;
420
+ }
421
+ }
422
+ if (event.target.validity.valid) {
423
+ if (event.target.value === "") {
424
+ let reason = "Backspace";
425
+ if (isInputEvent(event.nativeEvent)) {
426
+ const inputType = event.nativeEvent.inputType;
427
+ if (inputType === "deleteContentBackward") {
428
+ reason = "Backspace";
429
+ } else if (inputType === "deleteByCut") {
430
+ reason = "Cut";
431
+ }
432
+ }
433
+ dispatch({ type: "CLEAR_CHAR", index, reason });
434
+ } else {
435
+ dispatch({ type: "SET_CHAR", char: event.target.value, index, event });
436
+ }
437
+ } else {
438
+ const element2 = event.target;
439
+ requestAnimationFrame(() => {
440
+ if (element2.ownerDocument.activeElement === element2) {
441
+ element2.select();
442
+ }
443
+ });
444
+ }
445
+ }),
446
+ onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
447
+ switch (event.key) {
448
+ case "Delete":
449
+ case "Backspace": {
450
+ const currentValue = event.currentTarget.value;
451
+ if (currentValue === "") {
452
+ if (event.key === "Delete") return;
453
+ const isClearing = event.metaKey || event.ctrlKey;
454
+ if (isClearing) {
455
+ dispatch({ type: "CLEAR", reason: "Backspace" });
456
+ } else {
457
+ const element2 = event.currentTarget;
458
+ requestAnimationFrame(() => {
459
+ focusInput(collection.from(element2, -1)?.element);
460
+ });
461
+ }
462
+ } else {
463
+ userActionRef.current = {
464
+ type: "keydown",
465
+ key: event.key,
466
+ metaKey: event.metaKey,
467
+ ctrlKey: event.ctrlKey
468
+ };
469
+ keyboardActionTimeoutRef.current = window.setTimeout(() => {
470
+ userActionRef.current = null;
471
+ }, 10);
472
+ }
473
+ return;
474
+ }
475
+ case "Enter": {
476
+ event.preventDefault();
477
+ context.attemptSubmit();
478
+ return;
479
+ }
480
+ case "ArrowDown":
481
+ case "ArrowUp": {
482
+ if (context.orientation === "horizontal") {
483
+ event.preventDefault();
484
+ }
485
+ return;
486
+ }
487
+ // TODO: Handle left/right arrow keys in vertical writing mode
488
+ default: {
489
+ if (event.currentTarget.value === event.key) {
490
+ const element2 = event.currentTarget;
491
+ event.preventDefault();
492
+ focusInput(collection.from(element2, 1)?.element);
493
+ return;
494
+ } else if (
495
+ // input already has a value, but...
496
+ event.currentTarget.value && // the value is not selected
497
+ !(event.currentTarget.selectionStart === 0 && event.currentTarget.selectionEnd != null && event.currentTarget.selectionEnd > 0)
498
+ ) {
499
+ const attemptedValue = event.key;
500
+ if (event.key.length > 1 || event.key === " ") {
501
+ return;
502
+ } else {
503
+ const nextInput = collection.from(event.currentTarget, 1)?.element;
504
+ const lastInput = collection.at(-1)?.element;
505
+ if (nextInput !== lastInput && event.currentTarget !== lastInput) {
506
+ if (event.currentTarget.selectionStart === 0) {
507
+ dispatch({ type: "SET_CHAR", char: attemptedValue, index, event });
508
+ } else {
509
+ dispatch({
510
+ type: "SET_CHAR",
511
+ char: attemptedValue,
512
+ index: index + 1,
513
+ event
514
+ });
515
+ }
516
+ userActionRef.current = {
517
+ type: "keydown",
518
+ key: "Char",
519
+ metaKey: event.metaKey,
520
+ ctrlKey: event.ctrlKey
521
+ };
522
+ keyboardActionTimeoutRef.current = window.setTimeout(() => {
523
+ userActionRef.current = null;
524
+ }, 10);
525
+ }
526
+ }
527
+ }
528
+ }
529
+ }
530
+ }),
531
+ onPointerDown: composeEventHandlers(props.onPointerDown, (event) => {
532
+ if (index > lastSelectableIndex) {
533
+ event.preventDefault();
534
+ const element2 = collection.at(lastSelectableIndex)?.element;
535
+ focusInput(element2);
536
+ }
537
+ })
538
+ }
539
+ )
540
+ }
541
+ ) });
542
+ });
543
+ var Root2 = OneTimePasswordField;
544
+ var Input = OneTimePasswordFieldInput;
545
+ var HiddenInput = OneTimePasswordFieldHiddenInput;
546
+ function isFormElement(element) {
547
+ return element?.tagName === "FORM";
548
+ }
549
+ function sanitizeValue(value, regexp) {
550
+ if (Array.isArray(value)) {
551
+ value = value.join("");
552
+ }
553
+ if (regexp) {
554
+ regexp = new RegExp(regexp);
555
+ return value.replace(regexp, "").split("").filter(Boolean);
556
+ }
557
+ return value.split("").filter(Boolean);
558
+ }
559
+ function focusInput(element) {
560
+ if (!element) return;
561
+ if (element.ownerDocument.activeElement === element) {
562
+ window.requestAnimationFrame(() => {
563
+ element.select?.();
564
+ });
565
+ } else {
566
+ element.focus();
567
+ }
568
+ }
569
+ function isInputEvent(event) {
570
+ return event.type === "input";
571
+ }
572
+ export {
573
+ HiddenInput,
574
+ Input,
575
+ OneTimePasswordField,
576
+ OneTimePasswordFieldHiddenInput,
577
+ OneTimePasswordFieldInput,
578
+ Root2 as Root
579
+ };
580
+ //# sourceMappingURL=index.mjs.map