digitojs 1.0.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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +753 -0
- package/dist/adapters/alpine.d.ts +71 -0
- package/dist/adapters/alpine.d.ts.map +1 -0
- package/dist/adapters/alpine.js +560 -0
- package/dist/adapters/alpine.js.map +1 -0
- package/dist/adapters/react.d.ts +223 -0
- package/dist/adapters/react.d.ts.map +1 -0
- package/dist/adapters/react.js +337 -0
- package/dist/adapters/react.js.map +1 -0
- package/dist/adapters/svelte.d.ts +139 -0
- package/dist/adapters/svelte.d.ts.map +1 -0
- package/dist/adapters/svelte.js +295 -0
- package/dist/adapters/svelte.js.map +1 -0
- package/dist/adapters/vanilla.d.ts +110 -0
- package/dist/adapters/vanilla.d.ts.map +1 -0
- package/dist/adapters/vanilla.js +650 -0
- package/dist/adapters/vanilla.js.map +1 -0
- package/dist/adapters/vue.d.ts +163 -0
- package/dist/adapters/vue.d.ts.map +1 -0
- package/dist/adapters/vue.js +298 -0
- package/dist/adapters/vue.js.map +1 -0
- package/dist/adapters/web-component.d.ts +192 -0
- package/dist/adapters/web-component.d.ts.map +1 -0
- package/dist/adapters/web-component.js +832 -0
- package/dist/adapters/web-component.js.map +1 -0
- package/dist/core/feedback.d.ts +26 -0
- package/dist/core/feedback.d.ts.map +1 -0
- package/dist/core/feedback.js +47 -0
- package/dist/core/feedback.js.map +1 -0
- package/dist/core/filter.d.ts +24 -0
- package/dist/core/filter.d.ts.map +1 -0
- package/dist/core/filter.js +47 -0
- package/dist/core/filter.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +15 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/machine.d.ts +67 -0
- package/dist/core/machine.d.ts.map +1 -0
- package/dist/core/machine.js +328 -0
- package/dist/core/machine.js.map +1 -0
- package/dist/core/timer.d.ts +24 -0
- package/dist/core/timer.d.ts.map +1 -0
- package/dist/core/timer.js +67 -0
- package/dist/core/timer.js.map +1 -0
- package/dist/core/types.d.ts +162 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -0
- package/dist/digito-wc.min.js +254 -0
- package/dist/digito-wc.min.js.map +7 -0
- package/dist/digito.min.js +91 -0
- package/dist/digito.min.js.map +7 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/package.json +109 -0
- package/src/adapters/alpine.ts +666 -0
- package/src/adapters/react.tsx +603 -0
- package/src/adapters/svelte.ts +444 -0
- package/src/adapters/vanilla.ts +810 -0
- package/src/adapters/vue.ts +462 -0
- package/src/adapters/web-component.ts +858 -0
- package/src/core/feedback.ts +44 -0
- package/src/core/filter.ts +48 -0
- package/src/core/index.ts +16 -0
- package/src/core/machine.ts +373 -0
- package/src/core/timer.ts +75 -0
- package/src/core/types.ts +167 -0
- package/src/index.ts +51 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/filter.ts", "../src/core/timer.ts", "../src/core/feedback.ts", "../src/core/machine.ts", "../src/adapters/vanilla.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * digito/core/filter\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Character filtering utilities \u2014 exported for use by all adapters.\n *\n * @author Olawale Balo \u2014 Product Designer + Design Engineer\n * @license MIT\n */\n\nimport type { InputType } from './types.js'\n\n/**\n * Returns `char` if it is valid for `type` (and optional `pattern`), otherwise `''`.\n * Single character input only \u2014 multi-char strings always return `''`.\n *\n * When `pattern` is provided it takes precedence over `type` for validation.\n */\nexport function filterChar(char: string, type: InputType, pattern?: RegExp): string {\n if (!char || char.length !== 1) return ''\n if (pattern !== undefined) {\n // A pattern with the /g flag has stateful lastIndex. Reset it before every\n // test so the same pattern can be reused safely across multiple characters\n // without alternating between matches.\n if (pattern.global) pattern.lastIndex = 0\n return pattern.test(char) ? char : ''\n }\n switch (type) {\n case 'numeric': return /^[0-9]$/.test(char) ? char : ''\n case 'alphabet': return /^[a-zA-Z]$/.test(char) ? char : ''\n case 'alphanumeric': return /^[a-zA-Z0-9]$/.test(char) ? char : ''\n case 'any': return char\n default: return ''\n }\n}\n\n/**\n * Filters every character in `str` using `filterChar`.\n * Used to sanitize pasted strings and controlled-value inputs before distribution.\n *\n * When `pattern` is provided it takes precedence over `type` for each character.\n */\nexport function filterString(str: string, type: InputType, pattern?: RegExp): string {\n // Array.from iterates over Unicode code points, not UTF-16 code units.\n // str.split('') would split emoji and other supplementary-plane characters\n // into surrogate pairs (two strings of length 1 each), causing filterChar\n // to accept broken half-surrogates into slots for type:'any'.\n return Array.from(str).filter(c => filterChar(c, type, pattern) !== '').join('')\n}\n", "/**\n * digito/core/timer\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Standalone countdown timer \u2014 re-exported from core for use by adapters and\n * developers who want to drive their own timer UI.\n *\n * @author Olawale Balo \u2014 Product Designer + Design Engineer\n * @license MIT\n */\n\nimport type { TimerOptions, TimerControls } from './types.js'\n\n/**\n * Create a 1-second countdown timer.\n *\n * Lifecycle notes:\n * - `start()` is idempotent \u2014 it stops any running interval before starting a\n * new one, so calling it twice never produces double-ticking.\n * - If `totalSeconds <= 0`, `onExpire` fires synchronously on `start()` and no\n * interval is created (avoids decrementing to -1 and passing invalid values).\n * - `reset()` stops and restores remaining seconds without restarting.\n * - `restart()` is shorthand for `reset()` followed immediately by `start()`.\n * Used by the vanilla adapter's \"Resend\" button to reset the countdown.\n */\nexport function createTimer(options: TimerOptions): TimerControls {\n const { totalSeconds, onTick, onExpire } = options\n\n let remainingSeconds = totalSeconds\n let intervalId: ReturnType<typeof setInterval> | null = null\n\n /** Stop the running interval. No-op if already stopped. */\n function stop(): void {\n if (intervalId !== null) {\n clearInterval(intervalId)\n intervalId = null\n }\n }\n\n /** Stop the interval and restore `remainingSeconds` to `totalSeconds`. Does not restart. */\n function reset(): void {\n stop()\n remainingSeconds = totalSeconds\n }\n\n /**\n * Start ticking. Stops any existing interval first to prevent double-ticking.\n * If `totalSeconds <= 0`, fires `onExpire` immediately without creating an interval.\n */\n function start(): void {\n stop()\n // Guard: if totalSeconds is zero or negative, fire onExpire immediately\n // without starting an interval. Without this, the first tick would decrement\n // to -1 and pass an invalid value to onTick before calling onExpire.\n if (totalSeconds <= 0) {\n onExpire?.()\n return\n }\n intervalId = setInterval(() => {\n remainingSeconds -= 1\n onTick?.(remainingSeconds)\n if (remainingSeconds <= 0) {\n stop()\n onExpire?.()\n }\n }, 1000)\n }\n\n /** Reset to `totalSeconds` and immediately start ticking. */\n function restart(): void {\n reset()\n start()\n }\n\n return { start, stop, reset, restart }\n}\n", "/**\n * digito/core/feedback\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Optional sensory feedback utilities \u2014 exported so consumers can call them\n * in their own event handlers without reimplementing the Web Audio / vibration\n * boilerplate (\"bring your own feedback\" pattern).\n *\n * Used internally by the core machine when `haptic` / `sound` options are set.\n *\n * @author Olawale Balo \u2014 Product Designer + Design Engineer\n * @license MIT\n */\n\n/**\n * Trigger a short haptic pulse via `navigator.vibrate`.\n * Silently no-ops in environments that don't support the Vibration API\n * (e.g. desktop browsers, Safari, Node.js).\n */\nexport function triggerHapticFeedback(): void {\n try { navigator?.vibrate?.(10) } catch { /* not supported \u2014 fail silently */ }\n}\n\n/**\n * Play a brief 880 Hz tone via the Web Audio API.\n * The AudioContext is closed immediately after the tone ends to prevent\n * Chrome's ~6-concurrent-context limit from being reached across calls.\n * Silently no-ops where Web Audio is unavailable.\n */\nexport function triggerSoundFeedback(): void {\n try {\n const audioCtx = new AudioContext()\n const oscillator = audioCtx.createOscillator()\n const gainNode = audioCtx.createGain()\n oscillator.connect(gainNode)\n gainNode.connect(audioCtx.destination)\n oscillator.frequency.value = 880\n gainNode.gain.setValueAtTime(0.08, audioCtx.currentTime)\n gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.08)\n oscillator.start()\n oscillator.stop(audioCtx.currentTime + 0.08)\n // Close the context after the tone ends \u2014 prevents AudioContext instance leak\n oscillator.onended = () => { audioCtx.close().catch(() => { /* ignore */ }) }\n } catch { /* Web Audio not available \u2014 fail silently */ }\n}\n", "/**\n * digito/core/machine\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Pure OTP state machine \u2014 zero DOM, zero framework, zero side effects.\n * All adapters import `createDigito` from here (via core/index.ts).\n *\n * Subscription system: pass a listener to `subscribe()` to be notified after\n * every state mutation. Compatible with XState / Zustand-style patterns:\n *\n * const otp = createDigito({ length: 6 })\n * const unsub = otp.subscribe(state => console.log(state))\n * // ... later:\n * unsub()\n *\n * @author Olawale Balo \u2014 Product Designer + Design Engineer\n * @license MIT\n */\n\nimport type { DigitoOptions, DigitoState, StateListener, InputType } from './types.js'\nimport { filterChar, filterString } from './filter.js'\nimport { triggerHapticFeedback, triggerSoundFeedback } from './feedback.js'\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// INTERNAL HELPERS\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Returns `true` when every slot contains exactly one character. */\nfunction allSlotsFilled(slotValues: string[]): boolean {\n return slotValues.every(v => v.length === 1)\n}\n\n/** Clamp `index` to the inclusive range `[min, max]`. */\nfunction clampIndex(index: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, index))\n}\n\n/** Join all slot values into a single string (the OTP code). */\nfunction joinSlots(slotValues: string[]): string {\n return slotValues.join('')\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// FACTORY\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Creates a pure OTP state machine.\n * Returns action functions, a `state` getter, a `subscribe` method, and a\n * `getState` snapshot helper \u2014 no DOM, no side effects.\n */\nexport function createDigito(options: DigitoOptions = {}) {\n // Security: guard against invalid length values.\n // Negative numbers would throw a RangeError from Array(); zero would produce\n // a permanently-incomplete input with no slots. Clamp to a safe minimum of 1.\n const rawLength = options.length ?? 6\n // Guard against NaN (e.g. parseInt('', 10) from a missing data-attribute).\n // NaN would propagate through Math.floor and crash Array() with RangeError.\n const length = isNaN(rawLength) ? 6 : Math.max(1, Math.floor(rawLength))\n\n const {\n type = 'numeric' as InputType,\n pattern,\n onComplete,\n onInvalidChar,\n haptic = true,\n sound = false,\n pasteTransformer,\n } = options\n\n // `disabled` is mutable so setDisabled() can toggle it at runtime without\n // requiring the instance to be recreated. Adapters that pass disabled at\n // construction time still work \u2014 the initial value is honoured.\n let disabled = options.disabled ?? false\n\n let state: DigitoState = {\n slotValues: Array(length).fill('') as string[],\n activeSlot: 0,\n hasError: false,\n isComplete: false,\n timerSeconds: options.timer ?? 0,\n }\n\n // \u2500\u2500 Subscription set \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const listeners = new Set<StateListener>()\n\n function applyState(patch: Partial<DigitoState>): DigitoState {\n state = { ...state, ...patch }\n // Notify all subscribers with a deep copy of slotValues so they cannot\n // mutate the live array. A simple { ...state } is a shallow copy \u2014 the\n // slotValues array reference would be shared, letting a subscriber silently\n // corrupt internal state.\n if (listeners.size > 0) {\n const snapshot = { ...state, slotValues: [...state.slotValues] }\n listeners.forEach(fn => fn(snapshot))\n }\n return state\n }\n\n /**\n * Handle for the pending onComplete timeout.\n * Stored so resetState() can cancel it if the user clears the input\n * within the 10ms defer window (e.g. rapid type-then-delete).\n */\n let completeTimeoutId: ReturnType<typeof setTimeout> | null = null\n\n /** Fire onComplete after a short delay so DOM sync can finish first. */\n function notifyCompleteIfReady(slotValues: string[]): void {\n if (!allSlotsFilled(slotValues) || !onComplete) return\n if (haptic) triggerHapticFeedback()\n if (sound) triggerSoundFeedback()\n const code = joinSlots(slotValues)\n if (completeTimeoutId !== null) clearTimeout(completeTimeoutId)\n completeTimeoutId = setTimeout(() => {\n completeTimeoutId = null\n onComplete(code)\n }, 10)\n }\n\n /**\n * Cancel any pending onComplete callback without clearing slot state.\n * Use this after a programmatic fill (e.g. controlled-value sync in adapters)\n * to prevent a parent-driven pre-fill from triggering onComplete as if the\n * user had typed the code.\n */\n function cancelPendingComplete(): void {\n if (completeTimeoutId !== null) {\n clearTimeout(completeTimeoutId)\n completeTimeoutId = null\n }\n }\n\n\n // \u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Process a single character typed into `slotIndex`.\n * Invalid characters leave the slot unchanged and keep focus in place.\n * Out-of-bounds indices are silently ignored to prevent sparse-array corruption.\n */\n function inputChar(slotIndex: number, char: string): DigitoState {\n if (disabled) return state\n if (slotIndex < 0 || slotIndex >= length) return state\n const validChar = filterChar(char, type, pattern)\n if (!validChar) {\n // Fire onInvalidChar for single rejected characters (not empty/multi-char)\n if (char.length === 1) onInvalidChar?.(char, slotIndex)\n // Only notify subscribers if focus actually needs to move. Firing applyState\n // when activeSlot is already slotIndex causes a spurious state notification\n // on every invalid keystroke, which can trigger expensive re-renders.\n if (state.activeSlot !== slotIndex) {\n return applyState({ activeSlot: slotIndex })\n }\n return state\n }\n\n const slotValues = [...state.slotValues]\n slotValues[slotIndex] = validChar\n\n const nextSlot = slotIndex < length - 1 ? slotIndex + 1 : length - 1\n\n const newState = applyState({\n slotValues,\n activeSlot: nextSlot,\n hasError: false,\n isComplete: allSlotsFilled(slotValues),\n })\n\n notifyCompleteIfReady(slotValues)\n return newState\n }\n\n /**\n * Handle backspace at `slotIndex`.\n * Clears the current slot if filled, otherwise clears the previous slot and moves back.\n */\n function deleteChar(slotIndex: number): DigitoState {\n if (disabled) return state\n if (slotIndex < 0 || slotIndex >= length) return state\n const slotValues = [...state.slotValues]\n\n if (slotValues[slotIndex]) {\n slotValues[slotIndex] = ''\n return applyState({ slotValues, activeSlot: slotIndex, isComplete: false })\n }\n\n const prevSlot = clampIndex(slotIndex - 1, 0, length - 1)\n slotValues[prevSlot] = ''\n return applyState({ slotValues, activeSlot: prevSlot, isComplete: false })\n }\n\n /** Move focus one slot to the left, clamped to slot 0. */\n function moveFocusLeft(slotIndex: number): DigitoState {\n return applyState({ activeSlot: clampIndex(slotIndex - 1, 0, length - 1) })\n }\n\n /** Move focus one slot to the right, clamped to the last slot. */\n function moveFocusRight(slotIndex: number): DigitoState {\n return applyState({ activeSlot: clampIndex(slotIndex + 1, 0, length - 1) })\n }\n\n /**\n * Smart paste \u2014 distributes valid characters from `cursorSlot` forward,\n * wrapping around to slot 0 if the string is longer than the remaining slots.\n *\n * Examples (length = 6, type = numeric):\n * paste(0, '123456') \u2192 fills all slots\n * paste(5, '847291') \u2192 slot5='8', slot0='4', slot1='7', slot2='2', slot3='9', slot4='1'\n * paste(0, '84AB91') \u2192 filtered='8491', fills slots 0\u20133, slots 4\u20135 unchanged\n */\n function pasteString(cursorSlot: number, rawText: string): DigitoState {\n if (disabled) return state\n\n let transformed: string\n try {\n transformed = pasteTransformer ? pasteTransformer(rawText) : rawText\n } catch (err) {\n console.warn('[digito] pasteTransformer threw \u2014 using raw paste text.', err)\n transformed = rawText\n }\n\n // Report each rejected character so adapters can provide inline feedback.\n // Tracks the effective write cursor so the reported slot index matches where\n // the character would have landed if it had been valid.\n if (onInvalidChar && transformed) {\n let slotCursor = cursorSlot\n for (const char of Array.from(transformed)) {\n if (filterChar(char, type, pattern)) {\n slotCursor = (slotCursor + 1) % length\n } else {\n onInvalidChar(char, slotCursor)\n }\n }\n }\n\n const validChars = filterString(transformed, type, pattern)\n if (!validChars) return state\n\n const slotValues = [...state.slotValues]\n let writeSlot = cursorSlot\n\n for (let i = 0; i < validChars.length && i < length; i++) {\n slotValues[writeSlot] = validChars[i]\n writeSlot = (writeSlot + 1) % length\n }\n\n const charsWritten = Math.min(validChars.length, length)\n const nextActiveSlot = charsWritten >= length\n ? length - 1\n : (cursorSlot + charsWritten) % length\n\n const newState = applyState({\n slotValues,\n activeSlot: nextActiveSlot,\n hasError: false,\n isComplete: allSlotsFilled(slotValues),\n })\n\n notifyCompleteIfReady(slotValues)\n return newState\n }\n\n /** Set or clear the error state. Triggers haptic feedback when setting. */\n function setError(isError: boolean): DigitoState {\n if (isError && haptic) triggerHapticFeedback()\n return applyState({ hasError: isError })\n }\n\n /** Clear all slots and reset to initial state. Cancels any pending onComplete callback. */\n function resetState(): DigitoState {\n if (completeTimeoutId !== null) {\n clearTimeout(completeTimeoutId)\n completeTimeoutId = null\n }\n return applyState({\n slotValues: Array(length).fill('') as string[],\n activeSlot: 0,\n hasError: false,\n isComplete: false,\n timerSeconds: options.timer ?? 0,\n })\n }\n\n /** Move focus to a specific slot index. */\n function moveFocusTo(slotIndex: number): DigitoState {\n return applyState({ activeSlot: clampIndex(slotIndex, 0, length - 1) })\n }\n\n /**\n * Reactively enable or disable the input.\n * When `true`, inputChar / deleteChar / pasteString are silently ignored.\n * Navigation (moveFocusLeft/Right/To) is always allowed regardless of state.\n *\n * Prefer this over passing `disabled` at construction time whenever you need\n * to toggle disabled at runtime (e.g. during async verification).\n */\n function setDisabled(value: boolean): void {\n disabled = value\n }\n\n /**\n * Subscribe to state changes. The listener is called after every mutation\n * with a shallow copy of the new state.\n *\n * @returns An unsubscribe function \u2014 call it to stop receiving updates.\n *\n * @example\n * ```ts\n * const otp = createDigito({ length: 6 })\n * const unsub = otp.subscribe(state => console.log(state.slotValues))\n * // Later:\n * unsub()\n * ```\n */\n function subscribe(listener: StateListener): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n }\n\n return {\n /** Current state snapshot. */\n get state() { return state },\n\n // Input actions\n inputChar,\n deleteChar,\n moveFocusLeft,\n moveFocusRight,\n pasteString,\n\n // State control\n setError,\n resetState,\n moveFocusTo,\n\n /**\n * Cancel any pending onComplete callback without resetting slot state.\n * Intended for adapter-layer controlled-value syncs where a programmatic\n * fill should not be treated as a user completing the code.\n */\n cancelPendingComplete,\n\n /**\n * Toggle the disabled state at runtime.\n * Affects inputChar, deleteChar, and pasteString only.\n * Navigation actions are always allowed.\n */\n setDisabled,\n\n /** Returns the current joined code string. */\n getCode: () => joinSlots(state.slotValues),\n /**\n * Returns a copy of the current state with a cloned slotValues array.\n * Mutations to the returned object (including its slotValues array) will\n * not affect live state.\n */\n getSnapshot: (): DigitoState => ({ ...state, slotValues: [...state.slotValues] }),\n\n /**\n * Subscribe to state changes. Returns an unsubscribe function.\n * Compatible with XState / Zustand-style patterns.\n */\n subscribe,\n\n /**\n * Returns a copy of the current state with a cloned slotValues array.\n * Equivalent to `digito.getSnapshot()` \u2014 provided for ergonomic parity\n * with state-management libraries that expose a `getState()` method.\n */\n getState: (): DigitoState => ({ ...state, slotValues: [...state.slotValues] }),\n }\n}\n", "/**\n * digito/vanilla\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * DOM adapter using the single-hidden-input architecture.\n *\n * Architecture:\n * One real <input> sits invisibly behind the visual slot divs.\n * It captures ALL keyboard input, paste, and native SMS autofill.\n * The visual slot <div>s are pure mirrors \u2014 they display characters\n * from the hidden input's value, show a fake caret on the active slot,\n * and forward click events to focus the real input.\n *\n * Why this is better than multiple inputs:\n * - autocomplete=\"one-time-code\" works as native single-input autofill\n * - iOS SMS autofill works without any hacks\n * - Screen readers see one real input \u2014 perfect a11y\n * - No focus-juggling between inputs on every keystroke\n * - Password managers can't confuse the slots for separate fields\n *\n * Web OTP API:\n * When supported (Android Chrome), navigator.credentials.get is called\n * automatically to intercept the SMS OTP code without any user interaction.\n * The AbortController is wired to destroy() so the request is cancelled\n * on cleanup. Falls back gracefully in all other environments.\n *\n * Two timer modes:\n * Built-in UI \u2014 omit onTick. Digito renders \"Code expires in [0:60]\"\n * and \"Didn't receive the code? Resend\" automatically.\n * Custom UI \u2014 pass onTick. Digito fires the callback and skips its timer.\n *\n * @author Olawale Balo \u2014 Product Designer + Design Engineer\n * @license MIT\n */\n\nimport {\n createDigito,\n createTimer,\n filterString,\n type DigitoOptions,\n type InputType,\n} from '../core/index.js'\n\nexport { createTimer } from '../core/index.js'\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// PUBLIC TYPES\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** The control surface returned by initDigito for each mounted wrapper. */\nexport type DigitoInstance = {\n /** Clear all slots, restart timer, focus the hidden input. */\n reset: () => void\n /** Reset + fire onResend callback. */\n resend: () => void\n /** Apply or clear the error state on all visual slots. */\n setError: (isError: boolean) => void\n /** Apply or clear the success state on all visual slots. */\n setSuccess: (isSuccess: boolean) => void\n /**\n * Enable or disable the input. When disabled, all keypresses and pastes are\n * silently ignored and the hidden input is set to disabled. Use during async\n * verification to prevent the user from modifying the code mid-request.\n */\n setDisabled: (isDisabled: boolean) => void\n /** Returns the current joined code string. */\n getCode: () => string\n /** Programmatically move focus to a slot index (focuses the hidden input). */\n focus: (slotIndex: number) => void\n /** Remove all event listeners and stop the timer. */\n destroy: () => void\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// STYLES\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst INJECTED_STYLE_ID = 'digito-styles'\n\n/**\n * Injects the digito stylesheet into head exactly once per page.\n *\n * CSS custom properties (set on .digito-wrapper to override):\n * --digito-size Slot width + height (default: 56px)\n * --digito-gap Gap between slots (default: 12px)\n * --digito-radius Slot border radius (default: 10px)\n * --digito-font-size Digit font size (default: 24px)\n * --digito-bg Slot background (default: #FAFAFA)\n * --digito-bg-filled Slot background when filled (default: #FFFFFF)\n * --digito-color Digit text colour (default: #0A0A0A)\n * --digito-border-color Default slot border (default: #E5E5E5)\n * --digito-active-color Active slot border + ring (default: #757575)\n * --digito-error-color Error border, ring + badge (default: #FB2C36)\n * --digito-success-color Success border + ring (default: #00C950)\n * --digito-timer-color Timer label text colour (default: #5C5C5C)\n * --digito-caret-color Fake caret colour (default: #3D3D3D)\n * --digito-separator-color Separator text colour (default: #A1A1A1)\n * --digito-separator-size Separator font size (default: 18px)\n * --digito-masked-size Mask character font size (default: 16px)\n */\nfunction injectStylesOnce(): void {\n if (typeof document === 'undefined') return\n if (document.getElementById(INJECTED_STYLE_ID)) return\n\n const styleEl = document.createElement('style')\n styleEl.id = INJECTED_STYLE_ID\n styleEl.textContent = [\n '.digito-element{position:relative;display:inline-block;line-height:1}',\n '.digito-hidden-input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:none;outline:none;background:transparent;color:transparent;caret-color:transparent;z-index:1;cursor:text;font-size:1px}',\n '.digito-content{display:inline-flex;gap:var(--digito-gap,12px);align-items:center;padding:24px 0 0px;position:relative}',\n '.digito-slot{position:relative;width:var(--digito-size,56px);height:var(--digito-size,56px);border:1px solid var(--digito-border-color,#E5E5E5);border-radius:var(--digito-radius,10px);font-size:var(--digito-font-size,24px);font-weight:600;display:flex;align-items:center;justify-content:center;background:var(--digito-bg,#FAFAFA);color:var(--digito-color,#0A0A0A);transition:border-color 150ms ease,box-shadow 150ms ease,background 150ms ease;user-select:none;-webkit-user-select:none;cursor:text;font-family:inherit}',\n '.digito-slot.is-active{border-color:var(--digito-active-color,#3D3D3D);box-shadow:0 0 0 3px color-mix(in srgb,var(--digito-active-color,#3D3D3D) 10%,transparent);background:var(--digito-bg-filled,#FFFFFF)}',\n '.digito-slot.is-filled{background:var(--digito-bg-filled,#FFFFFF)}',\n '.digito-slot.is-error{border-color:var(--digito-error-color,#FB2C36);box-shadow:0 0 0 3px color-mix(in srgb,var(--digito-error-color,#FB2C36) 12%,transparent)}',\n '.digito-slot.is-success{border-color:var(--digito-success-color,#00C950);box-shadow:0 0 0 3px color-mix(in srgb,var(--digito-success-color,#00C950) 12%,transparent)}',\n '.digito-slot.is-disabled{opacity:0.45;cursor:not-allowed;pointer-events:none}',\n '.digito-caret{position:absolute;width:2px;height:52%;background:var(--digito-caret-color,#3D3D3D);border-radius:1px;animation:digito-blink 1s step-start infinite;pointer-events:none}',\n '@keyframes digito-blink{0%,100%{opacity:1}50%{opacity:0}}',\n '.digito-separator{display:flex;align-items:center;justify-content:center;color:var(--digito-separator-color,#A1A1A1);font-size:var(--digito-separator-size,18px);font-weight:400;user-select:none;flex-shrink:0;}',\n '.digito-slot:not(.is-filled){font-size:var(--digito-placeholder-size,16px);color:var(--digito-placeholder-color,#D3D3D3)}',\n '.digito-slot.is-masked.is-filled{font-size:var(--digito-masked-size,16px)}',\n '.digito-timer{display:flex;align-items:center;gap:8px;font-size:14px;padding:20px 0 0}',\n '.digito-timer-label{color:var(--digito-timer-color,#5C5C5C);font-size:14px}',\n '.digito-timer-badge{display:inline-flex;align-items:center;background:color-mix(in srgb,var(--digito-error-color,#FB2C36) 10%,transparent);color:var(--digito-error-color,#FB2C36);font-weight:500;font-size:14px;padding:2px 10px;border-radius:99px;height: 24px}',\n '.digito-resend{display:none;align-items:center;gap:8px;font-size:14px;color:var(--digito-timer-color,#5C5C5C);padding:20px 0 0}',\n '.digito-resend.is-visible{display:flex}',\n '.digito-resend-btn{display:inline-flex;align-items:center;background:#E8E8E8;border:none;padding:2px 10px;border-radius:99px;color:#0A0A0A;font-weight:500;font-size:14px;transition:background 150ms ease;cursor:pointer;height: 24px}',\n '.digito-resend-btn:hover{background:#E5E5E5}',\n '.digito-resend-btn:disabled{color:#A1A1A1;cursor:not-allowed;background:#F5F5F5}',\n ].join('')\n document.head.appendChild(styleEl)\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// INIT\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Vanilla-only options that extend the core DigitoOptions.\n * These are not part of the shared adapter API.\n */\nexport type VanillaOnlyOptions = Partial<DigitoOptions> & {\n /**\n * Insert a purely visual separator after this slot index (0-based).\n * The separator is aria-hidden, never enters the value, and has no effect on state.\n * Accepts a single position or an array for multiple separators.\n * Default: 0 (no separator).\n * @example separatorAfter: 3 -> [ ][ ][ ] \u2014 [ ][ ][ ] (6-slot field)\n * @example separatorAfter: [2, 4] -> [ ][ ] \u2014 [ ][ ] \u2014 [ ][ ]\n */\n separatorAfter?: number | number[]\n /**\n * The character or string to render as the separator.\n * Default: '\u2014'\n */\n separator?: string\n /**\n * When `true`, each filled slot displays a mask glyph instead of the real\n * character. The hidden input switches to `type=\"password\"` so the OS keyboard\n * and browser autocomplete treat it as a sensitive field.\n *\n * `getCode()` and `onComplete` always return the real characters \u2014 masking is\n * purely visual. Use for PIN entry or any flow where the value should not be\n * visible to shoulder-surfers.\n *\n * Default: `false`.\n */\n masked?: boolean\n /**\n * The glyph displayed in filled slots when `masked` is `true`.\n * Allows substituting the default bullet with any character of your choice\n * (e.g. `'*'`, `'\u2022'`, `'x'`).\n *\n * Readable via the `data-mask-char` HTML attribute:\n * `<div class=\"digito-wrapper\" data-mask-char=\"*\">`.\n *\n * Default: `'\u25CF'` (U+25CF BLACK CIRCLE).\n */\n maskChar?: string\n}\n\n/**\n * Mount digito on one or more wrapper elements.\n *\n * @param target CSS selector or HTMLElement. Default: '.digito-wrapper'\n * @param options Runtime options \u2014 supplement or override data attributes.\n * @returns Array of DigitoInstance objects, one per wrapper found.\n */\nexport function initDigito(\n target: string | HTMLElement = '.digito-wrapper',\n options: VanillaOnlyOptions = {},\n): DigitoInstance[] {\n injectStylesOnce()\n\n const wrapperElements: HTMLElement[] = typeof target === 'string'\n ? Array.from(document.querySelectorAll<HTMLElement>(target))\n : [target]\n\n return wrapperElements.map(wrapperEl => mountOnWrapper(wrapperEl, options))\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// MOUNT\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Web OTP API \u2014 the spec adds OTPCredential to the Credential type but it is\n// not yet in TypeScript's standard DOM lib. Declare it locally.\ninterface OTPCredential extends Credential { code: string }\n\n/** Augmented element type used to store per-instance footer references. */\ntype DigitoWrapper = HTMLElement & {\n __digitoFooterEl?: HTMLDivElement | null\n __digitoResendRowEl?: HTMLDivElement | null\n}\n\nfunction mountOnWrapper(\n wrapperEl: DigitoWrapper,\n options: VanillaOnlyOptions,\n): DigitoInstance {\n // Guard against double-initialisation on the same element. Calling initDigito\n // twice without destroy() would orphan the first instance's event listeners\n // and timers. Warn and clean up the stale DOM before proceeding.\n if (wrapperEl.querySelector('.digito-element')) {\n console.warn('[digito] initDigito() called on an already-initialised wrapper \u2014 call instance.destroy() first to avoid leaks.')\n }\n\n // \u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const slotCount = options.length ?? parseInt(wrapperEl.dataset.length ?? '6', 10)\n const inputType = (options.type ?? wrapperEl.dataset.type ?? 'numeric') as InputType\n const timerSeconds = options.timer ?? parseInt(wrapperEl.dataset.timer ?? '0', 10)\n const resendCooldown = options.resendAfter ?? parseInt(wrapperEl.dataset.resend ?? '30', 10)\n const onResend = options.onResend\n const onComplete = options.onComplete\n const onTickCallback = options.onTick\n const onExpire = options.onExpire\n const pattern = options.pattern\n let isDisabled = options.disabled ?? false\n\n // New options\n const autoFocus = options.autoFocus !== false // default true\n const inputName = options.name\n const onFocusProp = options.onFocus\n const onBlurProp = options.onBlur\n const placeholder = options.placeholder ?? ''\n const selectOnFocus = options.selectOnFocus ?? false\n const blurOnComplete = options.blurOnComplete ?? false\n\n const rawSepAfter = (options as VanillaOnlyOptions).separatorAfter\n ?? (wrapperEl.dataset.separatorAfter !== undefined\n ? wrapperEl.dataset.separatorAfter.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n) && n > 0)\n : [])\n // Normalise to array so all rendering logic is consistent\n const separatorAfterPositions: number[] = Array.isArray(rawSepAfter) ? rawSepAfter : [rawSepAfter]\n\n const separatorChar = (options as VanillaOnlyOptions).separator\n ?? wrapperEl.dataset.separator\n ?? '\u2014'\n const masked = (options as VanillaOnlyOptions).masked ?? wrapperEl.dataset.masked === 'true'\n const maskChar = (options as VanillaOnlyOptions).maskChar ?? wrapperEl.dataset.maskChar ?? '\\u25CF'\n\n // \u2500\u2500 Core state machine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const otpCore = createDigito({\n length: slotCount,\n type: inputType,\n timer: timerSeconds,\n resendAfter: resendCooldown,\n onComplete,\n onTick: onTickCallback,\n onExpire,\n pattern,\n pasteTransformer: options.pasteTransformer,\n onInvalidChar: options.onInvalidChar,\n sound: options.sound ?? false,\n haptic: options.haptic ?? true,\n disabled: isDisabled,\n })\n\n // \u2500\u2500 Build DOM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Clear wrapper using safe DOM removal (avoids innerHTML)\n while (wrapperEl.firstChild) wrapperEl.removeChild(wrapperEl.firstChild)\n\n const rootEl = document.createElement('div')\n rootEl.className = 'digito-element'\n\n const slotRowEl = document.createElement('div')\n slotRowEl.className = 'digito-content'\n\n const slotEls: HTMLDivElement[] = []\n const caretEls: HTMLDivElement[] = []\n\n for (let i = 0; i < slotCount; i++) {\n const slotEl = document.createElement('div')\n slotEl.className = 'digito-slot'\n slotEl.setAttribute('aria-hidden', 'true')\n slotEl.setAttribute('data-slot', String(i))\n\n const caretEl = document.createElement('div')\n caretEl.className = 'digito-caret'\n caretEl.style.display = 'none'\n\n slotEl.appendChild(caretEl)\n caretEls.push(caretEl)\n slotEls.push(slotEl)\n slotRowEl.appendChild(slotEl)\n\n if (separatorAfterPositions.some(pos => pos > 0 && i === pos - 1)) {\n const sepEl = document.createElement('div')\n sepEl.className = 'digito-separator'\n sepEl.textContent = separatorChar\n sepEl.setAttribute('aria-hidden', 'true')\n slotRowEl.appendChild(sepEl)\n }\n }\n\n const hiddenInputEl = document.createElement('input')\n hiddenInputEl.type = masked ? 'password' : 'text'\n hiddenInputEl.inputMode = inputType === 'numeric' ? 'numeric' : 'text'\n hiddenInputEl.autocomplete = 'one-time-code'\n hiddenInputEl.maxLength = slotCount\n hiddenInputEl.className = 'digito-hidden-input'\n if (inputName) hiddenInputEl.name = inputName\n hiddenInputEl.setAttribute('aria-label', `Enter your ${slotCount}-${inputType === 'numeric' ? 'digit' : 'character'} code`)\n hiddenInputEl.setAttribute('spellcheck', 'false')\n hiddenInputEl.setAttribute('autocorrect', 'off')\n hiddenInputEl.setAttribute('autocapitalize', 'off')\n\n rootEl.appendChild(slotRowEl)\n rootEl.appendChild(hiddenInputEl)\n wrapperEl.appendChild(rootEl)\n\n // \u2500\u2500 Password manager badge guard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let disconnectPasswordManagerWatch: () => void = () => {}\n requestAnimationFrame(() => {\n const slotRowWidth = slotRowEl.getBoundingClientRect().width || 0\n disconnectPasswordManagerWatch = watchForPasswordManagerBadge(hiddenInputEl, slotRowWidth)\n })\n\n // \u2500\u2500 Timer + resend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let timerBadgeEl: HTMLSpanElement | null = null\n let resendActionBtn: HTMLButtonElement | null = null\n let mainCountdown: ReturnType<typeof createTimer> | null = null\n let resendCountdown: ReturnType<typeof createTimer> | null = null\n let builtInFooterEl: HTMLDivElement | null = null\n let builtInResendRowEl: HTMLDivElement | null = null\n\n // Remove any footer elements left by a previous mount on this same wrapper.\n // Using stored references is reliable regardless of DOM siblings inserted between them.\n wrapperEl.__digitoFooterEl?.remove()\n wrapperEl.__digitoResendRowEl?.remove()\n wrapperEl.__digitoFooterEl = null\n wrapperEl.__digitoResendRowEl = null\n\n if (timerSeconds > 0) {\n const shouldUseBuiltInFooter = !onTickCallback\n\n if (shouldUseBuiltInFooter) {\n builtInFooterEl = document.createElement('div')\n builtInFooterEl.className = 'digito-timer'\n\n const expiresLabel = document.createElement('span')\n expiresLabel.className = 'digito-timer-label'\n expiresLabel.textContent = 'Code expires in'\n\n timerBadgeEl = document.createElement('span')\n timerBadgeEl.className = 'digito-timer-badge'\n timerBadgeEl.textContent = formatCountdown(timerSeconds)\n\n builtInFooterEl.appendChild(expiresLabel)\n builtInFooterEl.appendChild(timerBadgeEl)\n wrapperEl.insertAdjacentElement('afterend', builtInFooterEl)\n\n builtInResendRowEl = document.createElement('div')\n builtInResendRowEl.className = 'digito-resend'\n\n const didntReceiveLabel = document.createElement('span')\n didntReceiveLabel.textContent = 'Didn\\u2019t receive the code?'\n\n resendActionBtn = document.createElement('button')\n resendActionBtn.className = 'digito-resend-btn'\n resendActionBtn.textContent = 'Resend'\n resendActionBtn.type = 'button'\n\n builtInResendRowEl.appendChild(didntReceiveLabel)\n builtInResendRowEl.appendChild(resendActionBtn)\n builtInFooterEl.insertAdjacentElement('afterend', builtInResendRowEl)\n\n // Store on the wrapper element so subsequent mounts on the same element\n // can clean these up reliably without fragile sibling DOM walks.\n wrapperEl.__digitoFooterEl = builtInFooterEl\n wrapperEl.__digitoResendRowEl = builtInResendRowEl\n }\n\n mainCountdown = createTimer({\n totalSeconds: timerSeconds,\n onTick: (remaining) => {\n if (timerBadgeEl) timerBadgeEl.textContent = formatCountdown(remaining)\n onTickCallback?.(remaining)\n },\n onExpire: () => {\n if (builtInFooterEl) builtInFooterEl.style.display = 'none'\n if (builtInResendRowEl) builtInResendRowEl.classList.add('is-visible')\n onExpire?.()\n },\n })\n mainCountdown.start()\n\n if (shouldUseBuiltInFooter && resendActionBtn) {\n resendActionBtn.addEventListener('click', () => {\n if (!resendActionBtn || !timerBadgeEl || !builtInFooterEl || !builtInResendRowEl) return\n builtInResendRowEl.classList.remove('is-visible')\n builtInFooterEl.style.display = 'flex'\n timerBadgeEl.textContent = formatCountdown(resendCooldown)\n resendCountdown?.stop()\n resendCountdown = createTimer({\n totalSeconds: resendCooldown,\n onTick: (r) => { if (timerBadgeEl) timerBadgeEl.textContent = formatCountdown(r) },\n onExpire: () => {\n if (builtInFooterEl) builtInFooterEl.style.display = 'none'\n if (builtInResendRowEl) builtInResendRowEl.classList.add('is-visible')\n },\n })\n resendCountdown.start()\n onResend?.()\n })\n }\n }\n\n // \u2500\u2500 Web OTP API (SMS autofill) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // navigator.credentials.get({ otp: { transport: ['sms'] } }) intercepts\n // incoming OTP SMSes on Android Chrome without any user gesture.\n // The AbortController is stored so destroy() can cancel the pending request.\n let webOTPController: AbortController | null = null\n\n if (typeof navigator !== 'undefined' && 'credentials' in navigator) {\n webOTPController = new AbortController()\n ;(navigator.credentials.get as (opts: object) => Promise<OTPCredential | null>)({\n otp: { transport: ['sms'] },\n signal: webOTPController.signal,\n }).then((credential) => {\n if (!credential?.code) return\n const valid = filterString(credential.code, inputType, pattern).slice(0, slotCount)\n if (!valid) return\n otpCore.resetState()\n for (let i = 0; i < valid.length; i++) {\n if (valid[i]) otpCore.inputChar(i, valid[i])\n }\n const filledCount = valid.length\n const nextCursor = Math.min(filledCount, slotCount - 1)\n hiddenInputEl.value = valid\n hiddenInputEl.setSelectionRange(nextCursor, nextCursor)\n otpCore.moveFocusTo(nextCursor)\n syncSlotsToDOM()\n }).catch(() => { /* aborted on destroy() or not supported \u2014 fail silently */ })\n }\n\n\n // \u2500\u2500 DOM sync \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n function syncSlotsToDOM(): void {\n const { slotValues, activeSlot, hasError } = otpCore.state\n const inputIsFocused = document.activeElement === hiddenInputEl\n\n slotEls.forEach((slotEl, i) => {\n const char = slotValues[i] ?? ''\n const isActive = i === activeSlot && inputIsFocused\n const isFilled = char.length === 1\n\n let textNode = slotEl.childNodes[1] as Text | undefined\n if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {\n textNode = document.createTextNode('')\n slotEl.appendChild(textNode)\n }\n textNode.nodeValue = masked && char ? maskChar : char || placeholder\n\n slotEl.classList.toggle('is-active', isActive)\n slotEl.classList.toggle('is-filled', isFilled)\n slotEl.classList.toggle('is-masked', masked)\n slotEl.classList.toggle('is-error', hasError)\n if (hasError) slotEl.classList.remove('is-success')\n\n caretEls[i].style.display = isActive && !isFilled ? 'block' : 'none'\n })\n\n // Only update value when it actually differs \u2014 assigning the same string\n // resets selectionStart/End in some browsers, clobbering the cursor.\n const newValue = slotValues.join('')\n if (hiddenInputEl.value !== newValue) hiddenInputEl.value = newValue\n }\n\n // \u2500\u2500 Event handlers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n function onHiddenInputKeydown(event: KeyboardEvent): void {\n const cursorPos = hiddenInputEl.selectionStart ?? 0\n\n if (event.key === 'Backspace') {\n event.preventDefault()\n otpCore.deleteChar(cursorPos)\n syncSlotsToDOM()\n hiddenInputEl.setSelectionRange(otpCore.state.activeSlot, otpCore.state.activeSlot)\n } else if (event.key === 'Tab') {\n if (event.shiftKey) {\n // Shift+Tab: move to previous slot, or exit to previous DOM element from first slot\n if (cursorPos === 0) return\n event.preventDefault()\n otpCore.moveFocusLeft(cursorPos)\n } else {\n // Tab: advance to next slot only if current slot is filled\n // On last slot (filled), fall through to let browser move to next DOM element\n if (!otpCore.state.slotValues[cursorPos]) return\n if (cursorPos >= slotCount - 1) return\n event.preventDefault()\n otpCore.moveFocusRight(cursorPos)\n }\n const nextSlot = otpCore.state.activeSlot\n hiddenInputEl.setSelectionRange(nextSlot, nextSlot)\n syncSlotsToDOM()\n } else if (event.key === 'ArrowLeft') {\n event.preventDefault()\n otpCore.moveFocusLeft(cursorPos)\n const nextSlot = otpCore.state.activeSlot\n hiddenInputEl.setSelectionRange(nextSlot, nextSlot)\n syncSlotsToDOM()\n } else if (event.key === 'ArrowRight') {\n event.preventDefault()\n otpCore.moveFocusRight(cursorPos)\n const nextSlot = otpCore.state.activeSlot\n hiddenInputEl.setSelectionRange(nextSlot, nextSlot)\n syncSlotsToDOM()\n }\n }\n\n function onHiddenInputChange(_event: Event): void {\n const rawValue = hiddenInputEl.value\n\n if (!rawValue) {\n otpCore.resetState()\n hiddenInputEl.value = ''\n hiddenInputEl.setSelectionRange(0, 0)\n syncSlotsToDOM()\n return\n }\n\n const validValue = filterString(rawValue, inputType, pattern)\n\n const newSlotValues = Array(slotCount).fill('') as string[]\n for (let i = 0; i < Math.min(validValue.length, slotCount); i++) {\n newSlotValues[i] = validValue[i]\n }\n\n otpCore.resetState()\n for (let i = 0; i < newSlotValues.length; i++) {\n if (newSlotValues[i]) otpCore.inputChar(i, newSlotValues[i])\n }\n\n const filledCount = newSlotValues.filter(v => v !== '').length\n const nextCursor = Math.min(filledCount, slotCount - 1)\n hiddenInputEl.value = validValue.slice(0, slotCount)\n hiddenInputEl.setSelectionRange(nextCursor, nextCursor)\n otpCore.moveFocusTo(nextCursor)\n syncSlotsToDOM()\n if (blurOnComplete && otpCore.state.isComplete) {\n requestAnimationFrame(() => hiddenInputEl.blur())\n }\n }\n\n function onHiddenInputPaste(event: ClipboardEvent): void {\n event.preventDefault()\n const pastedText = event.clipboardData?.getData('text') ?? ''\n const cursorPos = hiddenInputEl.selectionStart ?? 0\n otpCore.pasteString(cursorPos, pastedText)\n\n const { slotValues, activeSlot } = otpCore.state\n hiddenInputEl.value = slotValues.join('')\n hiddenInputEl.setSelectionRange(activeSlot, activeSlot)\n syncSlotsToDOM()\n if (blurOnComplete && otpCore.state.isComplete) {\n requestAnimationFrame(() => hiddenInputEl.blur())\n }\n }\n\n function onHiddenInputFocus(): void {\n onFocusProp?.()\n // Read activeSlot inside the RAF \u2014 not before it.\n // Browser event order on a fresh click: focus fires \u2192 click fires \u2192 RAF fires.\n // Capturing pos outside the RAF would snapshot activeSlot=0 before the click\n // handler has a chance to call moveFocusTo(clickedSlot), causing the RAF to\n // overwrite the correct slot back to 0.\n requestAnimationFrame(() => {\n const pos = otpCore.state.activeSlot\n const char = otpCore.state.slotValues[pos]\n if (selectOnFocus && char) {\n hiddenInputEl.setSelectionRange(pos, pos + 1)\n } else {\n hiddenInputEl.setSelectionRange(pos, pos)\n }\n syncSlotsToDOM()\n })\n }\n\n function onHiddenInputBlur(): void {\n onBlurProp?.()\n slotEls.forEach(slotEl => slotEl.classList.remove('is-active'))\n caretEls.forEach(caretEl => { caretEl.style.display = 'none' })\n }\n\n hiddenInputEl.addEventListener('keydown', onHiddenInputKeydown)\n hiddenInputEl.addEventListener('input', onHiddenInputChange)\n hiddenInputEl.addEventListener('paste', onHiddenInputPaste)\n hiddenInputEl.addEventListener('focus', onHiddenInputFocus)\n hiddenInputEl.addEventListener('blur', onHiddenInputBlur)\n hiddenInputEl.addEventListener('click', onHiddenInputClick)\n\n function onHiddenInputClick(e: MouseEvent): void {\n if (isDisabled) return\n // Click fires after the browser has already placed the cursor (at 0 due to\n // font-size:1px). Coordinate hit-test to find the intended slot, then\n // override the browser's placement with an explicit setSelectionRange.\n let rawSlot = slotEls.length - 1\n for (let i = 0; i < slotEls.length; i++) {\n if (e.clientX <= slotEls[i].getBoundingClientRect().right) { rawSlot = i; break }\n }\n // Clamp to filled count: setSelectionRange(N, N) on a string of length L\n // silently clamps to L, so cursor ends up at 0 on an empty field. Clamping\n // keeps the visual active slot and the actual cursor position in sync.\n const clickedSlot = Math.min(rawSlot, hiddenInputEl.value.length)\n otpCore.moveFocusTo(clickedSlot)\n const char = otpCore.state.slotValues[clickedSlot]\n if (selectOnFocus && char) {\n hiddenInputEl.setSelectionRange(clickedSlot, clickedSlot + 1)\n } else {\n hiddenInputEl.setSelectionRange(clickedSlot, clickedSlot)\n }\n syncSlotsToDOM()\n }\n\n requestAnimationFrame(() => {\n if (!isDisabled && autoFocus) hiddenInputEl.focus()\n hiddenInputEl.setSelectionRange(0, 0)\n syncSlotsToDOM()\n })\n\n\n // \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n function reset(): void {\n otpCore.resetState()\n hiddenInputEl.value = ''\n if (timerBadgeEl) timerBadgeEl.textContent = formatCountdown(timerSeconds)\n if (builtInFooterEl) builtInFooterEl.style.display = 'flex'\n if (builtInResendRowEl) builtInResendRowEl.classList.remove('is-visible')\n resendCountdown?.stop()\n mainCountdown?.restart()\n requestAnimationFrame(() => {\n hiddenInputEl.focus()\n hiddenInputEl.setSelectionRange(0, 0)\n syncSlotsToDOM()\n })\n }\n\n function resend(): void {\n reset()\n onResend?.()\n }\n\n function setError(isError: boolean): void {\n otpCore.setError(isError)\n syncSlotsToDOM()\n }\n\n function setSuccess(isSuccess: boolean): void {\n slotEls.forEach(slotEl => {\n slotEl.classList.toggle('is-success', isSuccess)\n if (isSuccess) slotEl.classList.remove('is-error')\n })\n }\n\n function setDisabled(value: boolean): void {\n isDisabled = value\n otpCore.setDisabled(value)\n hiddenInputEl.disabled = value\n slotEls.forEach(slotEl => {\n slotEl.classList.toggle('is-disabled', value)\n ;(slotEl as HTMLElement).style.pointerEvents = value ? 'none' : ''\n })\n }\n\n function getCode(): string {\n return otpCore.getCode()\n }\n\n function focus(slotIndex: number): void {\n otpCore.moveFocusTo(slotIndex)\n hiddenInputEl.focus()\n hiddenInputEl.setSelectionRange(slotIndex, slotIndex)\n syncSlotsToDOM()\n }\n\n function destroy(): void {\n hiddenInputEl.removeEventListener('keydown', onHiddenInputKeydown)\n hiddenInputEl.removeEventListener('input', onHiddenInputChange)\n hiddenInputEl.removeEventListener('paste', onHiddenInputPaste)\n hiddenInputEl.removeEventListener('focus', onHiddenInputFocus)\n hiddenInputEl.removeEventListener('blur', onHiddenInputBlur)\n hiddenInputEl.removeEventListener('click', onHiddenInputClick)\n mainCountdown?.stop()\n resendCountdown?.stop()\n disconnectPasswordManagerWatch()\n webOTPController?.abort()\n wrapperEl.__digitoFooterEl = null\n wrapperEl.__digitoResendRowEl = null\n }\n\n return { reset, resend, setError, setSuccess, setDisabled, getCode, focus, destroy }\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// HELPERS\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction formatCountdown(totalSeconds: number): string {\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = totalSeconds % 60\n return minutes > 0\n ? `${minutes}:${String(seconds).padStart(2, '0')}`\n : `0:${String(seconds).padStart(2, '0')}`\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// PASSWORD MANAGER BADGE GUARD\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Password managers (LastPass, 1Password, Dashlane, Bitwarden) inject a small\n// icon badge into or beside <input> elements they detect as credential fields.\n// On OTP inputs this badge physically overlaps the last visual slot.\n//\n// Fix: detect when any of these extensions are active, then push the hidden\n// input's width ~40px wider so the badge renders outside the slot boundary.\n\nconst PASSWORD_MANAGER_SELECTORS = [\n '[data-lastpass-icon-root]',\n '[data-lastpass-root]',\n '[data-op-autofill]',\n '[data-1p-ignore]',\n '[data-dashlane-rid]',\n '[data-dashlane-label]',\n '[data-kwimpalastatus]',\n '[data-bwautofill]',\n 'com-bitwarden-browser-arctic-modal',\n]\n\nconst PASSWORD_MANAGER_BADGE_OFFSET_PX = 40\n\nfunction isPasswordManagerActive(): boolean {\n if (typeof document === 'undefined') return false\n return PASSWORD_MANAGER_SELECTORS.some(sel => {\n try { return document.querySelector(sel) !== null }\n catch { return false }\n })\n}\n\nfunction watchForPasswordManagerBadge(\n hiddenInputEl: HTMLInputElement,\n baseWidthPx: number,\n): () => void {\n if (typeof MutationObserver === 'undefined') return () => {}\n\n function applyOffset(): void {\n hiddenInputEl.style.width = `${baseWidthPx + PASSWORD_MANAGER_BADGE_OFFSET_PX}px`\n }\n\n if (isPasswordManagerActive()) {\n applyOffset()\n return () => {}\n }\n\n const observer = new MutationObserver(() => {\n if (isPasswordManagerActive()) {\n applyOffset()\n observer.disconnect()\n }\n })\n\n observer.observe(document.documentElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\n 'data-lastpass-icon-root',\n 'data-lastpass-root',\n 'data-1p-ignore',\n 'data-dashlane-rid',\n 'data-kwimpalastatus',\n ],\n })\n\n return () => observer.disconnect()\n}\n\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// CDN GLOBAL\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nif (typeof window !== 'undefined') {\n (window as unknown as Record<string, unknown>)['Digito'] = { init: initDigito }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["filterChar", "char", "type", "pattern", "filterString", "str", "c", "createTimer", "options", "totalSeconds", "onTick", "onExpire", "remainingSeconds", "intervalId", "stop", "reset", "start", "restart", "triggerHapticFeedback", "_a", "e", "triggerSoundFeedback", "audioCtx", "oscillator", "gainNode", "allSlotsFilled", "slotValues", "v", "clampIndex", "index", "min", "max", "joinSlots", "createDigito", "options", "_a", "_b", "_c", "rawLength", "length", "type", "pattern", "onComplete", "onInvalidChar", "haptic", "sound", "pasteTransformer", "disabled", "state", "listeners", "applyState", "patch", "__spreadValues", "snapshot", "__spreadProps", "fn", "completeTimeoutId", "notifyCompleteIfReady", "triggerHapticFeedback", "triggerSoundFeedback", "code", "cancelPendingComplete", "inputChar", "slotIndex", "char", "validChar", "filterChar", "nextSlot", "newState", "deleteChar", "prevSlot", "moveFocusLeft", "moveFocusRight", "pasteString", "cursorSlot", "rawText", "transformed", "err", "slotCursor", "validChars", "filterString", "writeSlot", "i", "charsWritten", "nextActiveSlot", "setError", "isError", "resetState", "moveFocusTo", "setDisabled", "value", "subscribe", "listener", "INJECTED_STYLE_ID", "injectStylesOnce", "styleEl", "initDigito", "target", "options", "wrapperEl", "mountOnWrapper", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "_k", "_l", "_m", "_n", "_o", "_p", "_q", "_r", "_s", "_t", "_u", "_v", "slotCount", "inputType", "timerSeconds", "resendCooldown", "onResend", "onComplete", "onTickCallback", "onExpire", "pattern", "isDisabled", "autoFocus", "inputName", "onFocusProp", "onBlurProp", "placeholder", "selectOnFocus", "blurOnComplete", "rawSepAfter", "s", "separatorAfterPositions", "separatorChar", "masked", "maskChar", "otpCore", "createDigito", "rootEl", "slotRowEl", "slotEls", "caretEls", "i", "slotEl", "caretEl", "pos", "sepEl", "hiddenInputEl", "disconnectPasswordManagerWatch", "slotRowWidth", "watchForPasswordManagerBadge", "timerBadgeEl", "resendActionBtn", "mainCountdown", "resendCountdown", "builtInFooterEl", "builtInResendRowEl", "shouldUseBuiltInFooter", "expiresLabel", "formatCountdown", "didntReceiveLabel", "createTimer", "remaining", "webOTPController", "credential", "valid", "filterString", "filledCount", "nextCursor", "syncSlotsToDOM", "slotValues", "activeSlot", "hasError", "inputIsFocused", "char", "isActive", "isFilled", "textNode", "newValue", "onHiddenInputKeydown", "event", "cursorPos", "nextSlot", "onHiddenInputChange", "_event", "rawValue", "validValue", "newSlotValues", "v", "onHiddenInputPaste", "pastedText", "onHiddenInputFocus", "onHiddenInputBlur", "onHiddenInputClick", "e", "rawSlot", "clickedSlot", "reset", "resend", "setError", "isError", "setSuccess", "isSuccess", "setDisabled", "value", "getCode", "focus", "slotIndex", "destroy", "totalSeconds", "minutes", "seconds", "PASSWORD_MANAGER_SELECTORS", "PASSWORD_MANAGER_BADGE_OFFSET_PX", "isPasswordManagerActive", "sel", "baseWidthPx", "applyOffset", "observer"]
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* digito
|
|
3
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
* OTP input library for the modern web.
|
|
5
|
+
* Vanilla JS · React · Vue · Svelte · Alpine · Web Components
|
|
6
|
+
*
|
|
7
|
+
* @author Olawale Balo — Product Designer + Design Engineer
|
|
8
|
+
* @license MIT
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
*/
|
|
11
|
+
export { createDigito, createTimer, filterChar, filterString, triggerHapticFeedback, triggerSoundFeedback, type InputType, type DigitoState, type DigitoOptions, type TimerOptions, type TimerControls, type StateListener, } from './core/index.js';
|
|
12
|
+
export { initDigito, type DigitoInstance, } from './adapters/vanilla.js';
|
|
13
|
+
export { useOTP as useOTPReact, HiddenOTPInput, type SlotRenderProps, } from './adapters/react.js';
|
|
14
|
+
export { useOTP as useOTPVue } from './adapters/vue.js';
|
|
15
|
+
export { useOTP as useOTPSvelte } from './adapters/svelte.js';
|
|
16
|
+
export { DigitoAlpine } from './adapters/alpine.js';
|
|
17
|
+
export { DigitoInput } from './adapters/web-component.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,UAAU,EACV,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAA;AAG9B,OAAO,EACL,MAAM,IAAI,WAAW,EACrB,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAGvD,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGnD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* digito
|
|
3
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
* OTP input library for the modern web.
|
|
5
|
+
* Vanilla JS · React · Vue · Svelte · Alpine · Web Components
|
|
6
|
+
*
|
|
7
|
+
* @author Olawale Balo — Product Designer + Design Engineer
|
|
8
|
+
* @license MIT
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
*/
|
|
11
|
+
// Core — pure logic, zero DOM
|
|
12
|
+
export { createDigito, createTimer, filterChar, filterString, triggerHapticFeedback, triggerSoundFeedback, } from './core/index.js';
|
|
13
|
+
// Vanilla DOM adapter
|
|
14
|
+
export { initDigito, } from './adapters/vanilla.js';
|
|
15
|
+
// React hook + components
|
|
16
|
+
export { useOTP as useOTPReact, HiddenOTPInput, } from './adapters/react.js';
|
|
17
|
+
// Vue composable
|
|
18
|
+
export { useOTP as useOTPVue } from './adapters/vue.js';
|
|
19
|
+
// Svelte store + action
|
|
20
|
+
export { useOTP as useOTPSvelte } from './adapters/svelte.js';
|
|
21
|
+
// Alpine plugin
|
|
22
|
+
export { DigitoAlpine } from './adapters/alpine.js';
|
|
23
|
+
// Web Component
|
|
24
|
+
export { DigitoInput } from './adapters/web-component.js';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,8BAA8B;AAC9B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,GAOrB,MAAM,iBAAiB,CAAA;AAExB,sBAAsB;AACtB,OAAO,EACL,UAAU,GAEX,MAAM,uBAAuB,CAAA;AAE9B,0BAA0B;AAC1B,OAAO,EACL,MAAM,IAAI,WAAW,EACrB,cAAc,GAEf,MAAM,qBAAqB,CAAA;AAE5B,iBAAiB;AACjB,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAEvD,wBAAwB;AACxB,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAE7D,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,gBAAgB;AAChB,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "digitojs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The only framework-agnostic OTP input state machine powering React, Vue, Svelte, Alpine, Vanilla JS, and Web Components from a single core.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./core": {
|
|
15
|
+
"types": "./dist/core/index.d.ts",
|
|
16
|
+
"import": "./dist/core/index.js",
|
|
17
|
+
"require": "./dist/core/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./react": {
|
|
20
|
+
"types": "./dist/adapters/react.d.ts",
|
|
21
|
+
"import": "./dist/adapters/react.js",
|
|
22
|
+
"require": "./dist/adapters/react.js"
|
|
23
|
+
},
|
|
24
|
+
"./vue": {
|
|
25
|
+
"types": "./dist/adapters/vue.d.ts",
|
|
26
|
+
"import": "./dist/adapters/vue.js",
|
|
27
|
+
"require": "./dist/adapters/vue.js"
|
|
28
|
+
},
|
|
29
|
+
"./svelte": {
|
|
30
|
+
"types": "./dist/adapters/svelte.d.ts",
|
|
31
|
+
"import": "./dist/adapters/svelte.js",
|
|
32
|
+
"require": "./dist/adapters/svelte.js"
|
|
33
|
+
},
|
|
34
|
+
"./alpine": {
|
|
35
|
+
"types": "./dist/adapters/alpine.d.ts",
|
|
36
|
+
"import": "./dist/adapters/alpine.js",
|
|
37
|
+
"require": "./dist/adapters/alpine.js"
|
|
38
|
+
},
|
|
39
|
+
"./web-component": {
|
|
40
|
+
"types": "./dist/adapters/web-component.d.ts",
|
|
41
|
+
"import": "./dist/adapters/web-component.js",
|
|
42
|
+
"require": "./dist/adapters/web-component.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"sideEffects": [
|
|
46
|
+
"./dist/adapters/vanilla.js",
|
|
47
|
+
"./dist/adapters/web-component.js"
|
|
48
|
+
],
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"src",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"CHANGELOG.md"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc",
|
|
58
|
+
"build:cdn": "node build-cdn.js",
|
|
59
|
+
"build:all": "npm run build && npm run build:cdn",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"test": "jest --no-coverage",
|
|
62
|
+
"test:coverage": "jest --coverage",
|
|
63
|
+
"test:e2e": "playwright test"
|
|
64
|
+
},
|
|
65
|
+
"keywords": [
|
|
66
|
+
"otp",
|
|
67
|
+
"otp-input",
|
|
68
|
+
"2fa",
|
|
69
|
+
"mfa",
|
|
70
|
+
"pin-input",
|
|
71
|
+
"verification-code",
|
|
72
|
+
"one-time-password",
|
|
73
|
+
"authentication",
|
|
74
|
+
"passcode",
|
|
75
|
+
"otp-field",
|
|
76
|
+
"react-otp",
|
|
77
|
+
"vue-otp",
|
|
78
|
+
"svelte-otp",
|
|
79
|
+
"alpine-otp",
|
|
80
|
+
"vanilla-otp",
|
|
81
|
+
"web-component",
|
|
82
|
+
"sms-autofill",
|
|
83
|
+
"accessible-otp",
|
|
84
|
+
"state-machine",
|
|
85
|
+
"typescript"
|
|
86
|
+
],
|
|
87
|
+
"author": "Olawale Balo — Product Designer + Design Engineer",
|
|
88
|
+
"license": "MIT",
|
|
89
|
+
"peerDependencies": {
|
|
90
|
+
"react": ">=17",
|
|
91
|
+
"svelte": ">=4",
|
|
92
|
+
"vue": ">=3"
|
|
93
|
+
},
|
|
94
|
+
"peerDependenciesOptional": {
|
|
95
|
+
"react": true,
|
|
96
|
+
"vue": true,
|
|
97
|
+
"svelte": true
|
|
98
|
+
},
|
|
99
|
+
"devDependencies": {
|
|
100
|
+
"@playwright/test": "^1.58.2",
|
|
101
|
+
"@types/jest": "^30.0.0",
|
|
102
|
+
"@types/react": "^18.0.0",
|
|
103
|
+
"esbuild": "^0.27.4",
|
|
104
|
+
"jest": "^30.3.0",
|
|
105
|
+
"ts-jest": "^29.4.6",
|
|
106
|
+
"typescript": "^5.4.0"
|
|
107
|
+
},
|
|
108
|
+
"type": "module"
|
|
109
|
+
}
|