@ionic/core 8.6.4 → 8.6.5-dev.11752243397.18475074
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/components/ion-input-otp.js +118 -64
- package/dist/cjs/ion-input-otp.cjs.entry.js +117 -64
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/input-otp/input-otp.js +119 -65
- package/dist/docs.json +1 -1
- package/dist/esm/ion-input-otp.entry.js +117 -64
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-77e2daa9.entry.js +4 -0
- package/dist/types/components/input-otp/input-otp.d.ts +12 -8
- package/hydrate/index.js +118 -64
- package/hydrate/index.mjs +118 -64
- package/package.json +1 -1
- package/dist/ionic/p-e30ff968.entry.js +0 -4
|
@@ -21,6 +21,7 @@ export class InputOTP {
|
|
|
21
21
|
this.isKeyboardNavigation = false;
|
|
22
22
|
this.inputValues = [];
|
|
23
23
|
this.hasFocus = false;
|
|
24
|
+
this.previousInputValues = [];
|
|
24
25
|
/**
|
|
25
26
|
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
|
26
27
|
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
|
@@ -121,19 +122,12 @@ export class InputOTP {
|
|
|
121
122
|
}
|
|
122
123
|
};
|
|
123
124
|
/**
|
|
124
|
-
* Handles keyboard navigation
|
|
125
|
+
* Handles keyboard navigation for the OTP component.
|
|
125
126
|
*
|
|
126
127
|
* Navigation:
|
|
127
128
|
* - Backspace: Clears current input and moves to previous box if empty
|
|
128
129
|
* - Arrow Left/Right: Moves focus between input boxes
|
|
129
130
|
* - Tab: Allows normal tab navigation between components
|
|
130
|
-
*
|
|
131
|
-
* Input Behavior:
|
|
132
|
-
* - Validates input against the allowed pattern
|
|
133
|
-
* - When entering a key in a filled box:
|
|
134
|
-
* - Shifts existing values right if there is room
|
|
135
|
-
* - Updates the value of the input group
|
|
136
|
-
* - Prevents default behavior to avoid automatic focus shift
|
|
137
131
|
*/
|
|
138
132
|
this.onKeyDown = (index) => (event) => {
|
|
139
133
|
const { length } = this;
|
|
@@ -188,69 +182,132 @@ export class InputOTP {
|
|
|
188
182
|
// Let all tab events proceed normally
|
|
189
183
|
return;
|
|
190
184
|
}
|
|
191
|
-
// If the input box contains a value and the key being
|
|
192
|
-
// entered is a valid key for the input box update the value
|
|
193
|
-
// and shift the values to the right if there is room.
|
|
194
|
-
if (this.inputValues[index] && this.validKeyPattern.test(event.key)) {
|
|
195
|
-
if (!this.inputValues[length - 1]) {
|
|
196
|
-
for (let i = length - 1; i > index; i--) {
|
|
197
|
-
this.inputValues[i] = this.inputValues[i - 1];
|
|
198
|
-
this.inputRefs[i].value = this.inputValues[i] || '';
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
this.inputValues[index] = event.key;
|
|
202
|
-
this.inputRefs[index].value = event.key;
|
|
203
|
-
this.updateValue(event);
|
|
204
|
-
// Prevent default to avoid the browser from
|
|
205
|
-
// automatically moving the focus to the next input
|
|
206
|
-
event.preventDefault();
|
|
207
|
-
}
|
|
208
185
|
};
|
|
186
|
+
/**
|
|
187
|
+
* Processes all input scenarios for each input box.
|
|
188
|
+
*
|
|
189
|
+
* This function manages:
|
|
190
|
+
* 1. Autofill handling
|
|
191
|
+
* 2. Input validation
|
|
192
|
+
* 3. Full selection replacement or typing in an empty box
|
|
193
|
+
* 4. Inserting in the middle with available space (shifting)
|
|
194
|
+
* 5. Single character replacement
|
|
195
|
+
*/
|
|
209
196
|
this.onInput = (index) => (event) => {
|
|
197
|
+
var _a, _b;
|
|
210
198
|
const { length, validKeyPattern } = this;
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
199
|
+
const input = event.target;
|
|
200
|
+
const value = input.value;
|
|
201
|
+
const previousValue = this.previousInputValues[index] || '';
|
|
202
|
+
// 1. Autofill handling
|
|
203
|
+
// If the length of the value increases by more than 1 from the previous
|
|
204
|
+
// value, treat this as autofill. This is to prevent the case where the
|
|
205
|
+
// user is typing a single character into an input box containing a value
|
|
206
|
+
// as that will trigger this function with a value length of 2 characters.
|
|
207
|
+
const isAutofill = value.length - previousValue.length > 1;
|
|
208
|
+
if (isAutofill) {
|
|
209
|
+
// Distribute valid characters across input boxes
|
|
215
210
|
const validChars = value
|
|
216
211
|
.split('')
|
|
217
212
|
.filter((char) => validKeyPattern.test(char))
|
|
218
213
|
.slice(0, length);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
this.inputRefs.forEach((input) => {
|
|
225
|
-
input.value = '';
|
|
226
|
-
});
|
|
227
|
-
});
|
|
214
|
+
for (let i = 0; i < length; i++) {
|
|
215
|
+
this.inputValues[i] = validChars[i] || '';
|
|
216
|
+
if (this.inputRefs[i] != null) {
|
|
217
|
+
this.inputRefs[i].value = validChars[i] || '';
|
|
218
|
+
}
|
|
228
219
|
}
|
|
229
|
-
// Update the value of the input group and emit the input change event
|
|
230
|
-
this.value = validChars.join('');
|
|
231
220
|
this.updateValue(event);
|
|
232
|
-
// Focus the
|
|
233
|
-
// are filled after a small delay to ensure the input boxes have been
|
|
234
|
-
// updated before moving the focus
|
|
221
|
+
// Focus the next empty input or the last one
|
|
235
222
|
setTimeout(() => {
|
|
236
|
-
var _a;
|
|
237
223
|
const nextIndex = validChars.length < length ? validChars.length : length - 1;
|
|
238
|
-
(
|
|
224
|
+
if (this.inputRefs[nextIndex] != null) {
|
|
225
|
+
this.inputRefs[nextIndex].focus();
|
|
226
|
+
}
|
|
239
227
|
}, 20);
|
|
228
|
+
this.previousInputValues = [...this.inputValues];
|
|
240
229
|
return;
|
|
241
230
|
}
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
231
|
+
// 2. Input validation
|
|
232
|
+
// If the character entered is invalid (does not match the pattern),
|
|
233
|
+
// restore the previous value and exit
|
|
234
|
+
if (value.length > 0 && !validKeyPattern.test(value[value.length - 1])) {
|
|
235
|
+
input.value = this.inputValues[index] || '';
|
|
236
|
+
this.previousInputValues[index] = this.inputValues[index] || '';
|
|
237
|
+
this.previousInputValues = [...this.inputValues];
|
|
246
238
|
return;
|
|
247
239
|
}
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
this.
|
|
251
|
-
|
|
240
|
+
// 3. Full selection replacement or typing in an empty box
|
|
241
|
+
// If the user selects all text in the input box and types, or if the
|
|
242
|
+
// input box is empty, replace only this input box. If the box is empty,
|
|
243
|
+
// move to the next box, otherwise stay focused on this box.
|
|
244
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === value.length;
|
|
245
|
+
const isEmpty = !this.inputValues[index];
|
|
246
|
+
if (isAllSelected || isEmpty) {
|
|
247
|
+
this.inputValues[index] = value;
|
|
248
|
+
input.value = value;
|
|
249
|
+
this.previousInputValues[index] = value;
|
|
250
|
+
this.updateValue(event);
|
|
252
251
|
this.focusNext(index);
|
|
252
|
+
this.previousInputValues = [...this.inputValues];
|
|
253
|
+
return;
|
|
253
254
|
}
|
|
255
|
+
// 4. Inserting in the middle with available space (shifting)
|
|
256
|
+
// If typing in a filled input box and there are empty boxes at the end,
|
|
257
|
+
// shift all values starting at the current box to the right, and insert
|
|
258
|
+
// the new character at the current box.
|
|
259
|
+
const hasAvailableBoxAtEnd = this.inputValues[this.inputValues.length - 1] === '';
|
|
260
|
+
if (this.inputValues[index] && hasAvailableBoxAtEnd && value.length === 2) {
|
|
261
|
+
// Get the inserted character (from event or by diffing value/previousValue)
|
|
262
|
+
let newChar = event.data;
|
|
263
|
+
if (!newChar) {
|
|
264
|
+
newChar = value.split('').find((c, i) => c !== previousValue[i]) || value[value.length - 1];
|
|
265
|
+
}
|
|
266
|
+
// Validate the new character before shifting
|
|
267
|
+
if (!validKeyPattern.test(newChar)) {
|
|
268
|
+
input.value = this.inputValues[index] || '';
|
|
269
|
+
this.previousInputValues[index] = this.inputValues[index] || '';
|
|
270
|
+
this.previousInputValues = [...this.inputValues];
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// Shift values right from the end to the insertion point
|
|
274
|
+
for (let i = this.inputValues.length - 1; i > index; i--) {
|
|
275
|
+
this.inputValues[i] = this.inputValues[i - 1];
|
|
276
|
+
if (this.inputRefs[i] != null) {
|
|
277
|
+
this.inputRefs[i].value = this.inputValues[i] || '';
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
this.inputValues[index] = newChar;
|
|
281
|
+
if (this.inputRefs[index] != null) {
|
|
282
|
+
this.inputRefs[index].value = newChar;
|
|
283
|
+
}
|
|
284
|
+
this.previousInputValues[index] = newChar;
|
|
285
|
+
this.updateValue(event);
|
|
286
|
+
this.previousInputValues = [...this.inputValues];
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
// 5. Single character replacement
|
|
290
|
+
// Handles replacing a single character in a box containing a value based
|
|
291
|
+
// on the cursor position. We need the cursor position to determine which
|
|
292
|
+
// character was the last character typed. For example, if the user types "2"
|
|
293
|
+
// in an input box with the cursor at the beginning of the value of "6",
|
|
294
|
+
// the value will be "26", but we want to grab the "2" as the last character
|
|
295
|
+
// typed.
|
|
296
|
+
const cursorPos = (_a = input.selectionStart) !== null && _a !== void 0 ? _a : value.length;
|
|
297
|
+
const newCharIndex = cursorPos - 1;
|
|
298
|
+
const newChar = (_b = value[newCharIndex]) !== null && _b !== void 0 ? _b : value[0];
|
|
299
|
+
// Check if the new character is valid before updating the value
|
|
300
|
+
if (!validKeyPattern.test(newChar)) {
|
|
301
|
+
input.value = this.inputValues[index] || '';
|
|
302
|
+
this.previousInputValues[index] = this.inputValues[index] || '';
|
|
303
|
+
this.previousInputValues = [...this.inputValues];
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
input.value = newChar;
|
|
307
|
+
this.inputValues[index] = newChar;
|
|
308
|
+
this.previousInputValues[index] = newChar;
|
|
309
|
+
this.updateValue(event);
|
|
310
|
+
this.previousInputValues = [...this.inputValues];
|
|
254
311
|
};
|
|
255
312
|
/**
|
|
256
313
|
* Handles pasting text into the input OTP component.
|
|
@@ -260,7 +317,7 @@ export class InputOTP {
|
|
|
260
317
|
* the next empty input after pasting.
|
|
261
318
|
*/
|
|
262
319
|
this.onPaste = (event) => {
|
|
263
|
-
var _a, _b
|
|
320
|
+
var _a, _b;
|
|
264
321
|
const { inputRefs, length, validKeyPattern } = this;
|
|
265
322
|
event.preventDefault();
|
|
266
323
|
const pastedText = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text');
|
|
@@ -287,13 +344,8 @@ export class InputOTP {
|
|
|
287
344
|
this.updateValue(event);
|
|
288
345
|
// Focus the next empty input after pasting
|
|
289
346
|
// If all boxes are filled, focus the last input
|
|
290
|
-
const nextEmptyIndex = validChars.length;
|
|
291
|
-
|
|
292
|
-
(_b = inputRefs[nextEmptyIndex]) === null || _b === void 0 ? void 0 : _b.focus();
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
(_c = inputRefs[length - 1]) === null || _c === void 0 ? void 0 : _c.focus();
|
|
296
|
-
}
|
|
347
|
+
const nextEmptyIndex = validChars.length < length ? validChars.length : length - 1;
|
|
348
|
+
(_b = inputRefs[nextEmptyIndex]) === null || _b === void 0 ? void 0 : _b.focus();
|
|
297
349
|
};
|
|
298
350
|
}
|
|
299
351
|
/**
|
|
@@ -432,6 +484,7 @@ export class InputOTP {
|
|
|
432
484
|
});
|
|
433
485
|
// Update the value without emitting events
|
|
434
486
|
this.value = this.inputValues.join('');
|
|
487
|
+
this.previousInputValues = [...this.inputValues];
|
|
435
488
|
}
|
|
436
489
|
/**
|
|
437
490
|
* Updates the value of the input group.
|
|
@@ -553,7 +606,7 @@ export class InputOTP {
|
|
|
553
606
|
const tabbableIndex = this.getTabbableIndex();
|
|
554
607
|
const pattern = this.getPattern();
|
|
555
608
|
const hasDescription = ((_b = (_a = el.querySelector('.input-otp-description')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()) !== '';
|
|
556
|
-
return (h(Host, { key: '
|
|
609
|
+
return (h(Host, { key: '084b4f7d148a55aef6b4b51c11483ee51d70d3bd', class: createColorClasses(color, {
|
|
557
610
|
[mode]: true,
|
|
558
611
|
'has-focus': hasFocus,
|
|
559
612
|
[`input-otp-size-${size}`]: true,
|
|
@@ -561,10 +614,10 @@ export class InputOTP {
|
|
|
561
614
|
[`input-otp-fill-${fill}`]: true,
|
|
562
615
|
'input-otp-disabled': disabled,
|
|
563
616
|
'input-otp-readonly': readonly,
|
|
564
|
-
}) }, h("div", Object.assign({ key: '
|
|
617
|
+
}) }, h("div", Object.assign({ key: '9d797deb7170bf6e4cc1acf70cca0b5d4ef51610', role: "group", "aria-label": "One-time password input", class: "input-otp-group" }, inheritedAttributes), Array.from({ length }).map((_, index) => (h(Fragment, null, h("div", { class: "native-wrapper" }, h("input", { class: "native-input", id: `${inputId}-${index}`, "aria-label": `Input ${index + 1} of ${length}`, type: "text", autoCapitalize: autocapitalize, inputmode: inputmode, pattern: pattern, disabled: disabled, readOnly: readonly, tabIndex: index === tabbableIndex ? 0 : -1, value: inputValues[index] || '', autocomplete: "one-time-code", ref: (el) => (inputRefs[index] = el), onInput: this.onInput(index), onBlur: this.onBlur, onFocus: this.onFocus(index), onKeyDown: this.onKeyDown(index), onPaste: this.onPaste })), this.showSeparator(index) && h("div", { class: "input-otp-separator" }))))), h("div", { key: 'a0463205729699430560032a68ade2e2ffa49b61', class: {
|
|
565
618
|
'input-otp-description': true,
|
|
566
619
|
'input-otp-description-hidden': !hasDescription,
|
|
567
|
-
} }, h("slot", { key: '
|
|
620
|
+
} }, h("slot", { key: '287fdaf0375cda3dcfafa2762d7daebf6f2bfe68' }))));
|
|
568
621
|
}
|
|
569
622
|
static get is() { return "ion-input-otp"; }
|
|
570
623
|
static get encapsulation() { return "scoped"; }
|
|
@@ -849,7 +902,8 @@ export class InputOTP {
|
|
|
849
902
|
static get states() {
|
|
850
903
|
return {
|
|
851
904
|
"inputValues": {},
|
|
852
|
-
"hasFocus": {}
|
|
905
|
+
"hasFocus": {},
|
|
906
|
+
"previousInputValues": {}
|
|
853
907
|
};
|
|
854
908
|
}
|
|
855
909
|
static get events() {
|
package/dist/docs.json
CHANGED
|
@@ -30,6 +30,7 @@ const InputOTP = class {
|
|
|
30
30
|
this.isKeyboardNavigation = false;
|
|
31
31
|
this.inputValues = [];
|
|
32
32
|
this.hasFocus = false;
|
|
33
|
+
this.previousInputValues = [];
|
|
33
34
|
/**
|
|
34
35
|
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
|
35
36
|
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
|
@@ -130,19 +131,12 @@ const InputOTP = class {
|
|
|
130
131
|
}
|
|
131
132
|
};
|
|
132
133
|
/**
|
|
133
|
-
* Handles keyboard navigation
|
|
134
|
+
* Handles keyboard navigation for the OTP component.
|
|
134
135
|
*
|
|
135
136
|
* Navigation:
|
|
136
137
|
* - Backspace: Clears current input and moves to previous box if empty
|
|
137
138
|
* - Arrow Left/Right: Moves focus between input boxes
|
|
138
139
|
* - Tab: Allows normal tab navigation between components
|
|
139
|
-
*
|
|
140
|
-
* Input Behavior:
|
|
141
|
-
* - Validates input against the allowed pattern
|
|
142
|
-
* - When entering a key in a filled box:
|
|
143
|
-
* - Shifts existing values right if there is room
|
|
144
|
-
* - Updates the value of the input group
|
|
145
|
-
* - Prevents default behavior to avoid automatic focus shift
|
|
146
140
|
*/
|
|
147
141
|
this.onKeyDown = (index) => (event) => {
|
|
148
142
|
const { length } = this;
|
|
@@ -197,69 +191,132 @@ const InputOTP = class {
|
|
|
197
191
|
// Let all tab events proceed normally
|
|
198
192
|
return;
|
|
199
193
|
}
|
|
200
|
-
// If the input box contains a value and the key being
|
|
201
|
-
// entered is a valid key for the input box update the value
|
|
202
|
-
// and shift the values to the right if there is room.
|
|
203
|
-
if (this.inputValues[index] && this.validKeyPattern.test(event.key)) {
|
|
204
|
-
if (!this.inputValues[length - 1]) {
|
|
205
|
-
for (let i = length - 1; i > index; i--) {
|
|
206
|
-
this.inputValues[i] = this.inputValues[i - 1];
|
|
207
|
-
this.inputRefs[i].value = this.inputValues[i] || '';
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
this.inputValues[index] = event.key;
|
|
211
|
-
this.inputRefs[index].value = event.key;
|
|
212
|
-
this.updateValue(event);
|
|
213
|
-
// Prevent default to avoid the browser from
|
|
214
|
-
// automatically moving the focus to the next input
|
|
215
|
-
event.preventDefault();
|
|
216
|
-
}
|
|
217
194
|
};
|
|
195
|
+
/**
|
|
196
|
+
* Processes all input scenarios for each input box.
|
|
197
|
+
*
|
|
198
|
+
* This function manages:
|
|
199
|
+
* 1. Autofill handling
|
|
200
|
+
* 2. Input validation
|
|
201
|
+
* 3. Full selection replacement or typing in an empty box
|
|
202
|
+
* 4. Inserting in the middle with available space (shifting)
|
|
203
|
+
* 5. Single character replacement
|
|
204
|
+
*/
|
|
218
205
|
this.onInput = (index) => (event) => {
|
|
206
|
+
var _a, _b;
|
|
219
207
|
const { length, validKeyPattern } = this;
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
208
|
+
const input = event.target;
|
|
209
|
+
const value = input.value;
|
|
210
|
+
const previousValue = this.previousInputValues[index] || '';
|
|
211
|
+
// 1. Autofill handling
|
|
212
|
+
// If the length of the value increases by more than 1 from the previous
|
|
213
|
+
// value, treat this as autofill. This is to prevent the case where the
|
|
214
|
+
// user is typing a single character into an input box containing a value
|
|
215
|
+
// as that will trigger this function with a value length of 2 characters.
|
|
216
|
+
const isAutofill = value.length - previousValue.length > 1;
|
|
217
|
+
if (isAutofill) {
|
|
218
|
+
// Distribute valid characters across input boxes
|
|
224
219
|
const validChars = value
|
|
225
220
|
.split('')
|
|
226
221
|
.filter((char) => validKeyPattern.test(char))
|
|
227
222
|
.slice(0, length);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.inputRefs.forEach((input) => {
|
|
234
|
-
input.value = '';
|
|
235
|
-
});
|
|
236
|
-
});
|
|
223
|
+
for (let i = 0; i < length; i++) {
|
|
224
|
+
this.inputValues[i] = validChars[i] || '';
|
|
225
|
+
if (this.inputRefs[i] != null) {
|
|
226
|
+
this.inputRefs[i].value = validChars[i] || '';
|
|
227
|
+
}
|
|
237
228
|
}
|
|
238
|
-
// Update the value of the input group and emit the input change event
|
|
239
|
-
this.value = validChars.join('');
|
|
240
229
|
this.updateValue(event);
|
|
241
|
-
// Focus the
|
|
242
|
-
// are filled after a small delay to ensure the input boxes have been
|
|
243
|
-
// updated before moving the focus
|
|
230
|
+
// Focus the next empty input or the last one
|
|
244
231
|
setTimeout(() => {
|
|
245
|
-
var _a;
|
|
246
232
|
const nextIndex = validChars.length < length ? validChars.length : length - 1;
|
|
247
|
-
(
|
|
233
|
+
if (this.inputRefs[nextIndex] != null) {
|
|
234
|
+
this.inputRefs[nextIndex].focus();
|
|
235
|
+
}
|
|
248
236
|
}, 20);
|
|
237
|
+
this.previousInputValues = [...this.inputValues];
|
|
249
238
|
return;
|
|
250
239
|
}
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
240
|
+
// 2. Input validation
|
|
241
|
+
// If the character entered is invalid (does not match the pattern),
|
|
242
|
+
// restore the previous value and exit
|
|
243
|
+
if (value.length > 0 && !validKeyPattern.test(value[value.length - 1])) {
|
|
244
|
+
input.value = this.inputValues[index] || '';
|
|
245
|
+
this.previousInputValues[index] = this.inputValues[index] || '';
|
|
246
|
+
this.previousInputValues = [...this.inputValues];
|
|
255
247
|
return;
|
|
256
248
|
}
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
this.
|
|
260
|
-
|
|
249
|
+
// 3. Full selection replacement or typing in an empty box
|
|
250
|
+
// If the user selects all text in the input box and types, or if the
|
|
251
|
+
// input box is empty, replace only this input box. If the box is empty,
|
|
252
|
+
// move to the next box, otherwise stay focused on this box.
|
|
253
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === value.length;
|
|
254
|
+
const isEmpty = !this.inputValues[index];
|
|
255
|
+
if (isAllSelected || isEmpty) {
|
|
256
|
+
this.inputValues[index] = value;
|
|
257
|
+
input.value = value;
|
|
258
|
+
this.previousInputValues[index] = value;
|
|
259
|
+
this.updateValue(event);
|
|
261
260
|
this.focusNext(index);
|
|
261
|
+
this.previousInputValues = [...this.inputValues];
|
|
262
|
+
return;
|
|
262
263
|
}
|
|
264
|
+
// 4. Inserting in the middle with available space (shifting)
|
|
265
|
+
// If typing in a filled input box and there are empty boxes at the end,
|
|
266
|
+
// shift all values starting at the current box to the right, and insert
|
|
267
|
+
// the new character at the current box.
|
|
268
|
+
const hasAvailableBoxAtEnd = this.inputValues[this.inputValues.length - 1] === '';
|
|
269
|
+
if (this.inputValues[index] && hasAvailableBoxAtEnd && value.length === 2) {
|
|
270
|
+
// Get the inserted character (from event or by diffing value/previousValue)
|
|
271
|
+
let newChar = event.data;
|
|
272
|
+
if (!newChar) {
|
|
273
|
+
newChar = value.split('').find((c, i) => c !== previousValue[i]) || value[value.length - 1];
|
|
274
|
+
}
|
|
275
|
+
// Validate the new character before shifting
|
|
276
|
+
if (!validKeyPattern.test(newChar)) {
|
|
277
|
+
input.value = this.inputValues[index] || '';
|
|
278
|
+
this.previousInputValues[index] = this.inputValues[index] || '';
|
|
279
|
+
this.previousInputValues = [...this.inputValues];
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
// Shift values right from the end to the insertion point
|
|
283
|
+
for (let i = this.inputValues.length - 1; i > index; i--) {
|
|
284
|
+
this.inputValues[i] = this.inputValues[i - 1];
|
|
285
|
+
if (this.inputRefs[i] != null) {
|
|
286
|
+
this.inputRefs[i].value = this.inputValues[i] || '';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
this.inputValues[index] = newChar;
|
|
290
|
+
if (this.inputRefs[index] != null) {
|
|
291
|
+
this.inputRefs[index].value = newChar;
|
|
292
|
+
}
|
|
293
|
+
this.previousInputValues[index] = newChar;
|
|
294
|
+
this.updateValue(event);
|
|
295
|
+
this.previousInputValues = [...this.inputValues];
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
// 5. Single character replacement
|
|
299
|
+
// Handles replacing a single character in a box containing a value based
|
|
300
|
+
// on the cursor position. We need the cursor position to determine which
|
|
301
|
+
// character was the last character typed. For example, if the user types "2"
|
|
302
|
+
// in an input box with the cursor at the beginning of the value of "6",
|
|
303
|
+
// the value will be "26", but we want to grab the "2" as the last character
|
|
304
|
+
// typed.
|
|
305
|
+
const cursorPos = (_a = input.selectionStart) !== null && _a !== void 0 ? _a : value.length;
|
|
306
|
+
const newCharIndex = cursorPos - 1;
|
|
307
|
+
const newChar = (_b = value[newCharIndex]) !== null && _b !== void 0 ? _b : value[0];
|
|
308
|
+
// Check if the new character is valid before updating the value
|
|
309
|
+
if (!validKeyPattern.test(newChar)) {
|
|
310
|
+
input.value = this.inputValues[index] || '';
|
|
311
|
+
this.previousInputValues[index] = this.inputValues[index] || '';
|
|
312
|
+
this.previousInputValues = [...this.inputValues];
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
input.value = newChar;
|
|
316
|
+
this.inputValues[index] = newChar;
|
|
317
|
+
this.previousInputValues[index] = newChar;
|
|
318
|
+
this.updateValue(event);
|
|
319
|
+
this.previousInputValues = [...this.inputValues];
|
|
263
320
|
};
|
|
264
321
|
/**
|
|
265
322
|
* Handles pasting text into the input OTP component.
|
|
@@ -269,7 +326,7 @@ const InputOTP = class {
|
|
|
269
326
|
* the next empty input after pasting.
|
|
270
327
|
*/
|
|
271
328
|
this.onPaste = (event) => {
|
|
272
|
-
var _a, _b
|
|
329
|
+
var _a, _b;
|
|
273
330
|
const { inputRefs, length, validKeyPattern } = this;
|
|
274
331
|
event.preventDefault();
|
|
275
332
|
const pastedText = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text');
|
|
@@ -296,13 +353,8 @@ const InputOTP = class {
|
|
|
296
353
|
this.updateValue(event);
|
|
297
354
|
// Focus the next empty input after pasting
|
|
298
355
|
// If all boxes are filled, focus the last input
|
|
299
|
-
const nextEmptyIndex = validChars.length;
|
|
300
|
-
|
|
301
|
-
(_b = inputRefs[nextEmptyIndex]) === null || _b === void 0 ? void 0 : _b.focus();
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
304
|
-
(_c = inputRefs[length - 1]) === null || _c === void 0 ? void 0 : _c.focus();
|
|
305
|
-
}
|
|
356
|
+
const nextEmptyIndex = validChars.length < length ? validChars.length : length - 1;
|
|
357
|
+
(_b = inputRefs[nextEmptyIndex]) === null || _b === void 0 ? void 0 : _b.focus();
|
|
306
358
|
};
|
|
307
359
|
}
|
|
308
360
|
/**
|
|
@@ -441,6 +493,7 @@ const InputOTP = class {
|
|
|
441
493
|
});
|
|
442
494
|
// Update the value without emitting events
|
|
443
495
|
this.value = this.inputValues.join('');
|
|
496
|
+
this.previousInputValues = [...this.inputValues];
|
|
444
497
|
}
|
|
445
498
|
/**
|
|
446
499
|
* Updates the value of the input group.
|
|
@@ -562,7 +615,7 @@ const InputOTP = class {
|
|
|
562
615
|
const tabbableIndex = this.getTabbableIndex();
|
|
563
616
|
const pattern = this.getPattern();
|
|
564
617
|
const hasDescription = ((_b = (_a = el.querySelector('.input-otp-description')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()) !== '';
|
|
565
|
-
return (h(Host, { key: '
|
|
618
|
+
return (h(Host, { key: '084b4f7d148a55aef6b4b51c11483ee51d70d3bd', class: createColorClasses(color, {
|
|
566
619
|
[mode]: true,
|
|
567
620
|
'has-focus': hasFocus,
|
|
568
621
|
[`input-otp-size-${size}`]: true,
|
|
@@ -570,10 +623,10 @@ const InputOTP = class {
|
|
|
570
623
|
[`input-otp-fill-${fill}`]: true,
|
|
571
624
|
'input-otp-disabled': disabled,
|
|
572
625
|
'input-otp-readonly': readonly,
|
|
573
|
-
}) }, h("div", Object.assign({ key: '
|
|
626
|
+
}) }, h("div", Object.assign({ key: '9d797deb7170bf6e4cc1acf70cca0b5d4ef51610', role: "group", "aria-label": "One-time password input", class: "input-otp-group" }, inheritedAttributes), Array.from({ length }).map((_, index) => (h(Fragment, null, h("div", { class: "native-wrapper" }, h("input", { class: "native-input", id: `${inputId}-${index}`, "aria-label": `Input ${index + 1} of ${length}`, type: "text", autoCapitalize: autocapitalize, inputmode: inputmode, pattern: pattern, disabled: disabled, readOnly: readonly, tabIndex: index === tabbableIndex ? 0 : -1, value: inputValues[index] || '', autocomplete: "one-time-code", ref: (el) => (inputRefs[index] = el), onInput: this.onInput(index), onBlur: this.onBlur, onFocus: this.onFocus(index), onKeyDown: this.onKeyDown(index), onPaste: this.onPaste })), this.showSeparator(index) && h("div", { class: "input-otp-separator" }))))), h("div", { key: 'a0463205729699430560032a68ade2e2ffa49b61', class: {
|
|
574
627
|
'input-otp-description': true,
|
|
575
628
|
'input-otp-description-hidden': !hasDescription,
|
|
576
|
-
} }, h("slot", { key: '
|
|
629
|
+
} }, h("slot", { key: '287fdaf0375cda3dcfafa2762d7daebf6f2bfe68' }))));
|
|
577
630
|
}
|
|
578
631
|
get el() { return getElement(this); }
|
|
579
632
|
static get watchers() { return {
|