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,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* digito/core/types
|
|
3
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
* Shared TypeScript interfaces and type aliases used across the core modules.
|
|
5
|
+
*
|
|
6
|
+
* @author Olawale Balo — Product Designer + Design Engineer
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** The set of characters each slot will accept. */
|
|
11
|
+
export type InputType = 'numeric' | 'alphabet' | 'alphanumeric' | 'any'
|
|
12
|
+
|
|
13
|
+
/** Snapshot of the OTP field state at any point in time. */
|
|
14
|
+
export type DigitoState = {
|
|
15
|
+
/** Current value of each slot. Empty string means unfilled. */
|
|
16
|
+
slotValues: string[]
|
|
17
|
+
/** Index of the currently focused slot. */
|
|
18
|
+
activeSlot: number
|
|
19
|
+
/** Whether an error state is active. */
|
|
20
|
+
hasError: boolean
|
|
21
|
+
/** True when every slot contains a valid character. */
|
|
22
|
+
isComplete: boolean
|
|
23
|
+
/**
|
|
24
|
+
* Mirrors the initial timer value — NOT a live countdown.
|
|
25
|
+
* The live countdown is managed by each adapter layer.
|
|
26
|
+
* Do not use this field to read remaining time; use the adapter's onTick callback instead.
|
|
27
|
+
*/
|
|
28
|
+
timerSeconds: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Configuration options passed to `createDigito` or `initDigito`. */
|
|
32
|
+
export type DigitoOptions = {
|
|
33
|
+
/** Number of input slots. Default: `6`. */
|
|
34
|
+
length?: number
|
|
35
|
+
/** Character set accepted by each slot. Default: `'numeric'`. */
|
|
36
|
+
type?: InputType
|
|
37
|
+
/** Countdown duration in seconds. `0` disables the timer. Default: `0`. */
|
|
38
|
+
timer?: number
|
|
39
|
+
/** Resend cooldown in seconds after the user clicks Resend. Default: `30`. */
|
|
40
|
+
resendAfter?: number
|
|
41
|
+
/** Called with the joined code string when all slots are filled. */
|
|
42
|
+
onComplete?: (code: string) => void
|
|
43
|
+
/** Called every second with the remaining seconds. Use to drive a custom timer UI. */
|
|
44
|
+
onTick?: (remainingSeconds: number) => void
|
|
45
|
+
/** Called when the countdown reaches zero. */
|
|
46
|
+
onExpire?: () => void
|
|
47
|
+
/** Called when the resend action is triggered. */
|
|
48
|
+
onResend?: () => void
|
|
49
|
+
/** Vibrate on completion and error via `navigator.vibrate`. Default: `true`. */
|
|
50
|
+
haptic?: boolean
|
|
51
|
+
/** Play a short tone on completion via Web Audio API. Default: `false`. */
|
|
52
|
+
sound?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* When `true`, all input actions (typing, backspace, paste) are silently ignored.
|
|
55
|
+
* Use this during async verification to prevent the user modifying the code.
|
|
56
|
+
* Default: `false`.
|
|
57
|
+
*/
|
|
58
|
+
disabled?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* Arbitrary per-character regex. When provided, each typed/pasted character must
|
|
61
|
+
* match this pattern to be accepted into a slot.
|
|
62
|
+
*
|
|
63
|
+
* Takes precedence over the named `type` for character validation only —
|
|
64
|
+
* `type` still controls `inputMode` and ARIA labels on the hidden input.
|
|
65
|
+
*
|
|
66
|
+
* The regex should match a **single character**:
|
|
67
|
+
* @example pattern: /^[0-9A-F]$/ — uppercase hex only
|
|
68
|
+
* @example pattern: /^[2-9A-HJ-NP-Z]$/ — ambiguity-free alphanumeric (no 0/O, 1/I/L)
|
|
69
|
+
*/
|
|
70
|
+
pattern?: RegExp
|
|
71
|
+
/**
|
|
72
|
+
* Optional transform applied to the raw clipboard text before it is filtered
|
|
73
|
+
* and distributed into slots. Runs before `filterString` inside `pasteString()`.
|
|
74
|
+
*
|
|
75
|
+
* Use to strip formatting from pasted codes that real users copy from emails or
|
|
76
|
+
* SMS messages (e.g. `"G-123456"` → `"123456"`, `"123 456"` → `"123456"`).
|
|
77
|
+
*
|
|
78
|
+
* The return value is then passed through the normal `filterString` + `pattern`
|
|
79
|
+
* validation, so you only need to handle the structural formatting — character
|
|
80
|
+
* validity is still enforced automatically.
|
|
81
|
+
*
|
|
82
|
+
* @example pasteTransformer: (raw) => raw.replace(/\s+|-/g, '')
|
|
83
|
+
* @example pasteTransformer: (raw) => raw.toUpperCase()
|
|
84
|
+
*/
|
|
85
|
+
pasteTransformer?: (raw: string) => string
|
|
86
|
+
/**
|
|
87
|
+
* Auto-focus the hidden input when the component mounts.
|
|
88
|
+
* Set to `false` to prevent the field from stealing focus on load.
|
|
89
|
+
* Default: `true`.
|
|
90
|
+
*/
|
|
91
|
+
autoFocus?: boolean
|
|
92
|
+
/**
|
|
93
|
+
* The `name` attribute to set on the hidden input for native HTML form
|
|
94
|
+
* submission and `FormData` compatibility.
|
|
95
|
+
* @example name: 'otp' → FormData includes otp=123456
|
|
96
|
+
*/
|
|
97
|
+
name?: string
|
|
98
|
+
/**
|
|
99
|
+
* Called when the hidden input gains browser focus.
|
|
100
|
+
* Use to show contextual help or update surrounding UI.
|
|
101
|
+
*/
|
|
102
|
+
onFocus?: () => void
|
|
103
|
+
/**
|
|
104
|
+
* Called when the hidden input loses browser focus.
|
|
105
|
+
* Use to trigger validation or hide contextual help.
|
|
106
|
+
*/
|
|
107
|
+
onBlur?: () => void
|
|
108
|
+
/**
|
|
109
|
+
* Character to display in empty (unfilled) slots as a visual hint.
|
|
110
|
+
* Common choices: `'○'`, `'_'`, `'·'`, `'•'`.
|
|
111
|
+
* Default: `''` (blank — no placeholder).
|
|
112
|
+
* @example placeholder: '○'
|
|
113
|
+
*/
|
|
114
|
+
placeholder?: string
|
|
115
|
+
/**
|
|
116
|
+
* When `true`, focusing a slot that already contains a character selects that
|
|
117
|
+
* character so the next keystroke replaces it in-place.
|
|
118
|
+
* When `false` (default), the cursor is placed at the slot position and the
|
|
119
|
+
* existing character must be deleted before a new one can be entered.
|
|
120
|
+
* Default: `false`.
|
|
121
|
+
*/
|
|
122
|
+
selectOnFocus?: boolean
|
|
123
|
+
/**
|
|
124
|
+
* When `true`, the hidden input is automatically blurred when all slots are
|
|
125
|
+
* filled. Removes focus styling and hides the fake caret once the code is
|
|
126
|
+
* complete. Useful for flows that immediately submit or verify on completion.
|
|
127
|
+
* Default: `false`.
|
|
128
|
+
*/
|
|
129
|
+
blurOnComplete?: boolean
|
|
130
|
+
/**
|
|
131
|
+
* Called when the user types or pastes a character that is rejected by the
|
|
132
|
+
* current `type` or `pattern` filter.
|
|
133
|
+
*
|
|
134
|
+
* Receives the raw rejected character and the zero-based slot index where
|
|
135
|
+
* entry was attempted. Use to display inline feedback such as
|
|
136
|
+
* "Only digits are allowed" or highlight the offending slot.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* onInvalidChar: (char, index) => console.warn(`Rejected "${char}" at slot ${index}`)
|
|
140
|
+
*/
|
|
141
|
+
onInvalidChar?: (char: string, index: number) => void
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Options for the standalone `createTimer` utility. */
|
|
145
|
+
export type TimerOptions = {
|
|
146
|
+
/** Total countdown duration in seconds. */
|
|
147
|
+
totalSeconds: number
|
|
148
|
+
/** Called every second with the remaining seconds. */
|
|
149
|
+
onTick?: (remainingSeconds: number) => void
|
|
150
|
+
/** Called when the countdown reaches zero. */
|
|
151
|
+
onExpire?: () => void
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Controls returned by `createTimer`. */
|
|
155
|
+
export type TimerControls = {
|
|
156
|
+
/** Start the countdown. */
|
|
157
|
+
start: () => void
|
|
158
|
+
/** Stop and pause the countdown. */
|
|
159
|
+
stop: () => void
|
|
160
|
+
/** Reset remaining time back to `totalSeconds` without starting. */
|
|
161
|
+
reset: () => void
|
|
162
|
+
/** Reset and immediately start again. */
|
|
163
|
+
restart: () => void
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Listener function invoked after every state mutation in `createDigito`. */
|
|
167
|
+
export type StateListener = (state: DigitoState) => void
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
|
|
12
|
+
// Core — pure logic, zero DOM
|
|
13
|
+
export {
|
|
14
|
+
createDigito,
|
|
15
|
+
createTimer,
|
|
16
|
+
filterChar,
|
|
17
|
+
filterString,
|
|
18
|
+
triggerHapticFeedback,
|
|
19
|
+
triggerSoundFeedback,
|
|
20
|
+
type InputType,
|
|
21
|
+
type DigitoState,
|
|
22
|
+
type DigitoOptions,
|
|
23
|
+
type TimerOptions,
|
|
24
|
+
type TimerControls,
|
|
25
|
+
type StateListener,
|
|
26
|
+
} from './core/index.js'
|
|
27
|
+
|
|
28
|
+
// Vanilla DOM adapter
|
|
29
|
+
export {
|
|
30
|
+
initDigito,
|
|
31
|
+
type DigitoInstance,
|
|
32
|
+
} from './adapters/vanilla.js'
|
|
33
|
+
|
|
34
|
+
// React hook + components
|
|
35
|
+
export {
|
|
36
|
+
useOTP as useOTPReact,
|
|
37
|
+
HiddenOTPInput,
|
|
38
|
+
type SlotRenderProps,
|
|
39
|
+
} from './adapters/react.js'
|
|
40
|
+
|
|
41
|
+
// Vue composable
|
|
42
|
+
export { useOTP as useOTPVue } from './adapters/vue.js'
|
|
43
|
+
|
|
44
|
+
// Svelte store + action
|
|
45
|
+
export { useOTP as useOTPSvelte } from './adapters/svelte.js'
|
|
46
|
+
|
|
47
|
+
// Alpine plugin
|
|
48
|
+
export { DigitoAlpine } from './adapters/alpine.js'
|
|
49
|
+
|
|
50
|
+
// Web Component
|
|
51
|
+
export { DigitoInput } from './adapters/web-component.js'
|