digitojs 1.2.0 → 1.2.2
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 +12 -4
- package/README.md +19 -19
- package/dist/adapters/alpine.d.ts.map +1 -1
- package/dist/adapters/alpine.js +22 -5
- package/dist/adapters/alpine.js.map +1 -1
- package/dist/adapters/svelte.d.ts +5 -0
- package/dist/adapters/svelte.d.ts.map +1 -1
- package/dist/adapters/svelte.js +22 -6
- package/dist/adapters/svelte.js.map +1 -1
- package/dist/adapters/vanilla.d.ts +6 -0
- package/dist/adapters/vanilla.d.ts.map +1 -1
- package/dist/adapters/vanilla.js +13 -2
- package/dist/adapters/vanilla.js.map +1 -1
- package/dist/adapters/web-component.d.ts +6 -0
- package/dist/adapters/web-component.d.ts.map +1 -1
- package/dist/adapters/web-component.js +18 -0
- package/dist/adapters/web-component.js.map +1 -1
- package/dist/core/machine.d.ts.map +1 -1
- package/dist/core/machine.js +4 -0
- package/dist/core/machine.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/digito-wc.min.js +3 -3
- package/dist/digito-wc.min.js.map +3 -3
- package/dist/digito.min.js +2 -2
- package/dist/digito.min.js.map +4 -4
- package/package.json +2 -2
- package/src/adapters/alpine.ts +22 -5
- package/src/adapters/svelte.ts +29 -8
- package/src/adapters/vanilla.ts +19 -2
- package/src/adapters/web-component.ts +18 -0
- package/src/cdn.ts +24 -0
- package/src/core/machine.ts +4 -0
- package/src/core/types.ts +4 -0
package/src/adapters/svelte.ts
CHANGED
|
@@ -112,6 +112,11 @@ export type UseOTPResult = {
|
|
|
112
112
|
setError: (isError: boolean) => void
|
|
113
113
|
/** Enable or disable the field at runtime. */
|
|
114
114
|
setDisabled: (value: boolean) => void
|
|
115
|
+
/**
|
|
116
|
+
* Toggle readOnly at runtime. When `true`, all slot mutations are blocked
|
|
117
|
+
* but focus, navigation, and copy remain fully functional.
|
|
118
|
+
*/
|
|
119
|
+
setReadOnly: (value: boolean) => void
|
|
115
120
|
/** Programmatically move focus to a slot index. */
|
|
116
121
|
focus: (slotIndex: number) => void
|
|
117
122
|
/**
|
|
@@ -195,12 +200,14 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
195
200
|
const store = writable(digito.state)
|
|
196
201
|
const timerStore = writable(timerSecs)
|
|
197
202
|
const isDisabledStore = writable(initialDisabled)
|
|
203
|
+
const isReadOnlyStore = writable(readOnlyOpt)
|
|
198
204
|
const separatorAfterStore = writable(separatorAfterOpt)
|
|
199
205
|
const separatorStore = writable(separatorOpt)
|
|
200
206
|
const maskedStore = writable(maskedOpt)
|
|
201
207
|
const maskCharStore = writable(maskCharOpt)
|
|
202
208
|
|
|
203
|
-
let inputEl:
|
|
209
|
+
let inputEl: HTMLInputElement | null = null
|
|
210
|
+
let isReadOnly: boolean = readOnlyOpt
|
|
204
211
|
|
|
205
212
|
// ── sync() ─────────────────────────────────────────────────────────────────
|
|
206
213
|
function sync(suppressOnChange = false): void {
|
|
@@ -280,14 +287,14 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
280
287
|
const pos = node.selectionStart ?? 0
|
|
281
288
|
if (e.key === 'Backspace') {
|
|
282
289
|
e.preventDefault()
|
|
283
|
-
if (
|
|
290
|
+
if (isReadOnly) return
|
|
284
291
|
digito.deleteChar(pos)
|
|
285
292
|
sync()
|
|
286
293
|
const next = digito.state.activeSlot
|
|
287
294
|
requestAnimationFrame(() => node.setSelectionRange(next, next))
|
|
288
295
|
} else if (e.key === 'Delete') {
|
|
289
296
|
e.preventDefault()
|
|
290
|
-
if (
|
|
297
|
+
if (isReadOnly) return
|
|
291
298
|
digito.clearSlot(pos)
|
|
292
299
|
sync()
|
|
293
300
|
requestAnimationFrame(() => node.setSelectionRange(pos, pos))
|
|
@@ -321,7 +328,7 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
321
328
|
}
|
|
322
329
|
|
|
323
330
|
function onChange(e: Event): void {
|
|
324
|
-
if (get(isDisabledStore) ||
|
|
331
|
+
if (get(isDisabledStore) || isReadOnly) return
|
|
325
332
|
const raw = (e.target as HTMLInputElement).value
|
|
326
333
|
if (!raw) {
|
|
327
334
|
digito.resetState()
|
|
@@ -344,7 +351,7 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
344
351
|
}
|
|
345
352
|
|
|
346
353
|
function onPaste(e: ClipboardEvent): void {
|
|
347
|
-
if (get(isDisabledStore) ||
|
|
354
|
+
if (get(isDisabledStore) || isReadOnly) return
|
|
348
355
|
e.preventDefault()
|
|
349
356
|
const text = e.clipboardData?.getData('text') ?? ''
|
|
350
357
|
const pos = node.selectionStart ?? 0
|
|
@@ -423,6 +430,19 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
423
430
|
digito.setDisabled(value)
|
|
424
431
|
}
|
|
425
432
|
|
|
433
|
+
function setReadOnly(value: boolean): void {
|
|
434
|
+
isReadOnly = value
|
|
435
|
+
isReadOnlyStore.set(value)
|
|
436
|
+
digito.setReadOnly(value)
|
|
437
|
+
if (inputEl) {
|
|
438
|
+
if (value) {
|
|
439
|
+
inputEl.setAttribute('aria-readonly', 'true')
|
|
440
|
+
} else {
|
|
441
|
+
inputEl.removeAttribute('aria-readonly')
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
426
446
|
function focus(slotIndex: number): void {
|
|
427
447
|
digito.moveFocusTo(slotIndex)
|
|
428
448
|
inputEl?.focus()
|
|
@@ -442,12 +462,12 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
442
462
|
|
|
443
463
|
// Derived wrapper data attributes for CSS/Tailwind targeting
|
|
444
464
|
const wrapperAttrs = derived(
|
|
445
|
-
[store, isDisabledStore],
|
|
446
|
-
([$s, $dis]: [DigitoState, boolean]) => ({
|
|
465
|
+
[store, isDisabledStore, isReadOnlyStore],
|
|
466
|
+
([$s, $dis, $ro]: [DigitoState, boolean, boolean]) => ({
|
|
447
467
|
...($s.isComplete ? { 'data-complete': '' } : {}),
|
|
448
468
|
...($s.hasError ? { 'data-invalid': '' } : {}),
|
|
449
469
|
...($dis ? { 'data-disabled': '' } : {}),
|
|
450
|
-
...(
|
|
470
|
+
...($ro ? { 'data-readonly': '' } : {}),
|
|
451
471
|
})
|
|
452
472
|
)
|
|
453
473
|
|
|
@@ -470,6 +490,7 @@ export function useOTP(options: SvelteOTPOptions = {}): UseOTPResult {
|
|
|
470
490
|
reset,
|
|
471
491
|
setError,
|
|
472
492
|
setDisabled,
|
|
493
|
+
setReadOnly,
|
|
473
494
|
setValue,
|
|
474
495
|
focus,
|
|
475
496
|
}
|
package/src/adapters/vanilla.ts
CHANGED
|
@@ -63,6 +63,12 @@ export type DigitoInstance = {
|
|
|
63
63
|
* verification to prevent the user from modifying the code mid-request.
|
|
64
64
|
*/
|
|
65
65
|
setDisabled: (isDisabled: boolean) => void
|
|
66
|
+
/**
|
|
67
|
+
* Toggle readOnly at runtime. When `true`, all slot mutations are blocked
|
|
68
|
+
* but focus, navigation, and copy remain fully functional.
|
|
69
|
+
* Distinct from `disabled` — no opacity/cursor change, `aria-readonly` is set.
|
|
70
|
+
*/
|
|
71
|
+
setReadOnly: (isReadOnly: boolean) => void
|
|
66
72
|
/** Returns the current joined code string. */
|
|
67
73
|
getCode: () => string
|
|
68
74
|
/** Programmatically move focus to a slot index (focuses the hidden input). */
|
|
@@ -238,7 +244,7 @@ function mountOnWrapper(
|
|
|
238
244
|
const onExpire = options.onExpire
|
|
239
245
|
const pattern = options.pattern
|
|
240
246
|
let isDisabled = options.disabled ?? false
|
|
241
|
-
|
|
247
|
+
let isReadOnly = options.readOnly ?? false
|
|
242
248
|
const defaultValue = options.defaultValue ?? ''
|
|
243
249
|
|
|
244
250
|
// New options
|
|
@@ -717,6 +723,17 @@ function mountOnWrapper(
|
|
|
717
723
|
})
|
|
718
724
|
}
|
|
719
725
|
|
|
726
|
+
function setReadOnly(value: boolean): void {
|
|
727
|
+
isReadOnly = value
|
|
728
|
+
otpCore.setReadOnly(value)
|
|
729
|
+
if (value) {
|
|
730
|
+
hiddenInputEl.setAttribute('aria-readonly', 'true')
|
|
731
|
+
} else {
|
|
732
|
+
hiddenInputEl.removeAttribute('aria-readonly')
|
|
733
|
+
}
|
|
734
|
+
syncSlotsToDOM()
|
|
735
|
+
}
|
|
736
|
+
|
|
720
737
|
function getCode(): string {
|
|
721
738
|
return otpCore.getCode()
|
|
722
739
|
}
|
|
@@ -743,7 +760,7 @@ function mountOnWrapper(
|
|
|
743
760
|
wrapperEl.__digitoResendRowEl = null
|
|
744
761
|
}
|
|
745
762
|
|
|
746
|
-
return { reset, resend, setError, setSuccess, setDisabled, getCode, focus, destroy }
|
|
763
|
+
return { reset, resend, setError, setSuccess, setDisabled, setReadOnly, getCode, focus, destroy }
|
|
747
764
|
}
|
|
748
765
|
|
|
749
766
|
|
|
@@ -867,6 +867,24 @@ class DigitoInput extends HTMLElement {
|
|
|
867
867
|
}
|
|
868
868
|
}
|
|
869
869
|
|
|
870
|
+
/**
|
|
871
|
+
* Toggle readOnly at runtime. When `true`, all slot mutations are blocked
|
|
872
|
+
* but focus, navigation, and copy remain fully functional.
|
|
873
|
+
* Distinct from `disabled` — no opacity/cursor change, `aria-readonly` is set.
|
|
874
|
+
*/
|
|
875
|
+
setReadOnly(value: boolean): void {
|
|
876
|
+
this._isReadOnly = value
|
|
877
|
+
this.digito?.setReadOnly(value)
|
|
878
|
+
if (this.hiddenInput) {
|
|
879
|
+
if (value) {
|
|
880
|
+
this.hiddenInput.setAttribute('aria-readonly', 'true')
|
|
881
|
+
} else {
|
|
882
|
+
this.hiddenInput.removeAttribute('aria-readonly')
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
this.syncSlotsToDOM()
|
|
886
|
+
}
|
|
887
|
+
|
|
870
888
|
/** Returns the current code as a joined string (e.g. `"123456"`). */
|
|
871
889
|
getCode(): string {
|
|
872
890
|
return this.digito?.getCode() ?? ''
|
package/src/cdn.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDN entry point for Digito.
|
|
3
|
+
*
|
|
4
|
+
* Exposes all public browser-facing APIs as a single window.Digito global.
|
|
5
|
+
* This file is the entrypoint for dist/digito.min.js only — it is not
|
|
6
|
+
* part of the npm package exports.
|
|
7
|
+
*
|
|
8
|
+
* Usage after loading the CDN script:
|
|
9
|
+
* const { initDigito, createDigito, filterChar, filterString, createTimer } = window.Digito
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Vanilla adapter
|
|
13
|
+
export { initDigito } from './adapters/vanilla'
|
|
14
|
+
|
|
15
|
+
// Core state machine + utilities
|
|
16
|
+
export {
|
|
17
|
+
createDigito,
|
|
18
|
+
createTimer,
|
|
19
|
+
formatCountdown,
|
|
20
|
+
filterChar,
|
|
21
|
+
filterString,
|
|
22
|
+
triggerHapticFeedback,
|
|
23
|
+
triggerSoundFeedback,
|
|
24
|
+
} from './core'
|
package/src/core/machine.ts
CHANGED
|
@@ -82,6 +82,8 @@ export function createDigito(options: DigitoOptions = {}) {
|
|
|
82
82
|
hasError: false,
|
|
83
83
|
isComplete: false,
|
|
84
84
|
timerSeconds: options.timer ?? 0,
|
|
85
|
+
isDisabled: options.disabled ?? false,
|
|
86
|
+
isReadOnly: options.readOnly ?? false,
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
// ── Subscription set ──────────────────────────────────────────────────────
|
|
@@ -313,6 +315,7 @@ export function createDigito(options: DigitoOptions = {}) {
|
|
|
313
315
|
*/
|
|
314
316
|
function setDisabled(value: boolean): void {
|
|
315
317
|
disabled = value
|
|
318
|
+
applyState({ isDisabled: value })
|
|
316
319
|
}
|
|
317
320
|
|
|
318
321
|
/**
|
|
@@ -321,6 +324,7 @@ export function createDigito(options: DigitoOptions = {}) {
|
|
|
321
324
|
*/
|
|
322
325
|
function setReadOnly(value: boolean): void {
|
|
323
326
|
readOnly = value
|
|
327
|
+
applyState({ isReadOnly: value })
|
|
324
328
|
}
|
|
325
329
|
|
|
326
330
|
/**
|
package/src/core/types.ts
CHANGED
|
@@ -26,6 +26,10 @@ export type DigitoState = {
|
|
|
26
26
|
* Do not use this field to read remaining time; use the adapter's onTick callback instead.
|
|
27
27
|
*/
|
|
28
28
|
timerSeconds: number
|
|
29
|
+
/** Whether the input is currently disabled. Reflects the latest `setDisabled()` call. */
|
|
30
|
+
isDisabled: boolean
|
|
31
|
+
/** Whether the input is currently read-only. Reflects the latest `setReadOnly()` call. */
|
|
32
|
+
isReadOnly: boolean
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/** Configuration options passed to `createDigito` or `initDigito`. */
|