@syncfusion/ej2-inputs 30.2.4 → 31.1.17

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.
Files changed (110) hide show
  1. package/dist/ej2-inputs.min.js +2 -2
  2. package/dist/ej2-inputs.umd.min.js +2 -2
  3. package/dist/ej2-inputs.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-inputs.es2015.js +1 -1
  5. package/dist/es6/ej2-inputs.es2015.js.map +1 -1
  6. package/dist/es6/ej2-inputs.es5.js +1 -1
  7. package/dist/es6/ej2-inputs.es5.js.map +1 -1
  8. package/dist/global/ej2-inputs.min.js +2 -2
  9. package/dist/global/ej2-inputs.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/dist/ts/color-picker/color-picker-model.d.ts +186 -0
  12. package/dist/ts/color-picker/color-picker.d.ts +398 -0
  13. package/dist/ts/color-picker/color-picker.ts +2209 -0
  14. package/dist/ts/color-picker/index.d.ts +5 -0
  15. package/dist/ts/color-picker/index.ts +5 -0
  16. package/dist/ts/common/index.d.ts +4 -0
  17. package/dist/ts/common/index.ts +4 -0
  18. package/dist/ts/common/signature-base.d.ts +340 -0
  19. package/dist/ts/common/signature-base.ts +1170 -0
  20. package/dist/ts/form-validator/form-validator-model.d.ts +121 -0
  21. package/dist/ts/form-validator/form-validator.d.ts +312 -0
  22. package/dist/ts/form-validator/form-validator.ts +1068 -0
  23. package/dist/ts/form-validator/index.d.ts +5 -0
  24. package/dist/ts/form-validator/index.ts +5 -0
  25. package/dist/ts/index.d.ts +18 -0
  26. package/dist/ts/index.ts +18 -0
  27. package/dist/ts/input/index.d.ts +4 -0
  28. package/dist/ts/input/index.ts +4 -0
  29. package/dist/ts/input/input.d.ts +345 -0
  30. package/dist/ts/input/input.ts +1277 -0
  31. package/dist/ts/maskedtextbox/base/index.d.ts +4 -0
  32. package/dist/ts/maskedtextbox/base/index.ts +4 -0
  33. package/dist/ts/maskedtextbox/base/mask-base.d.ts +134 -0
  34. package/dist/ts/maskedtextbox/base/mask-base.ts +1207 -0
  35. package/dist/ts/maskedtextbox/index.d.ts +5 -0
  36. package/dist/ts/maskedtextbox/index.ts +5 -0
  37. package/dist/ts/maskedtextbox/maskedtextbox/index.d.ts +5 -0
  38. package/dist/ts/maskedtextbox/maskedtextbox/index.ts +5 -0
  39. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox-model.d.ts +167 -0
  40. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox.d.ts +307 -0
  41. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox.ts +709 -0
  42. package/dist/ts/numerictextbox/index.d.ts +5 -0
  43. package/dist/ts/numerictextbox/index.ts +5 -0
  44. package/dist/ts/numerictextbox/numerictextbox-model.d.ts +228 -0
  45. package/dist/ts/numerictextbox/numerictextbox.d.ts +431 -0
  46. package/dist/ts/numerictextbox/numerictextbox.ts +1672 -0
  47. package/dist/ts/otp-input/index.d.ts +5 -0
  48. package/dist/ts/otp-input/index.ts +5 -0
  49. package/dist/ts/otp-input/otp-input-model.d.ts +165 -0
  50. package/dist/ts/otp-input/otp-input.d.ts +365 -0
  51. package/dist/ts/otp-input/otp-input.ts +924 -0
  52. package/dist/ts/rating/index.d.ts +5 -0
  53. package/dist/ts/rating/index.ts +5 -0
  54. package/dist/ts/rating/rating-model.d.ts +239 -0
  55. package/dist/ts/rating/rating.d.ts +416 -0
  56. package/dist/ts/rating/rating.ts +1101 -0
  57. package/dist/ts/signature/index.d.ts +5 -0
  58. package/dist/ts/signature/index.ts +5 -0
  59. package/dist/ts/signature/signature-model.d.ts +131 -0
  60. package/dist/ts/signature/signature.d.ts +167 -0
  61. package/dist/ts/signature/signature.ts +266 -0
  62. package/dist/ts/slider/index.d.ts +5 -0
  63. package/dist/ts/slider/index.ts +5 -0
  64. package/dist/ts/slider/slider-model.d.ts +401 -0
  65. package/dist/ts/slider/slider.d.ts +721 -0
  66. package/dist/ts/slider/slider.ts +3497 -0
  67. package/dist/ts/smart-textarea/caret-helper.d.ts +17 -0
  68. package/dist/ts/smart-textarea/caret-helper.ts +95 -0
  69. package/dist/ts/smart-textarea/index.d.ts +5 -0
  70. package/dist/ts/smart-textarea/index.ts +5 -0
  71. package/dist/ts/smart-textarea/smart-textarea-model.d.ts +44 -0
  72. package/dist/ts/smart-textarea/smart-textarea.d.ts +80 -0
  73. package/dist/ts/smart-textarea/smart-textarea.ts +518 -0
  74. package/dist/ts/speech-to-text/index.d.ts +5 -0
  75. package/dist/ts/speech-to-text/index.ts +5 -0
  76. package/dist/ts/speech-to-text/speech-to-text-model.d.ts +270 -0
  77. package/dist/ts/speech-to-text/speech-to-text.d.ts +487 -0
  78. package/dist/ts/speech-to-text/speech-to-text.ts +1008 -0
  79. package/dist/ts/textarea/index.d.ts +5 -0
  80. package/dist/ts/textarea/index.ts +5 -0
  81. package/dist/ts/textarea/textarea-model.d.ts +161 -0
  82. package/dist/ts/textarea/textarea.d.ts +237 -0
  83. package/dist/ts/textarea/textarea.ts +812 -0
  84. package/dist/ts/textbox/index.d.ts +5 -0
  85. package/dist/ts/textbox/index.ts +5 -0
  86. package/dist/ts/textbox/textbox-model.d.ts +155 -0
  87. package/dist/ts/textbox/textbox.d.ts +288 -0
  88. package/dist/ts/textbox/textbox.ts +872 -0
  89. package/dist/ts/uploader/index.d.ts +5 -0
  90. package/dist/ts/uploader/index.ts +5 -0
  91. package/dist/ts/uploader/uploader-model.d.ts +555 -0
  92. package/dist/ts/uploader/uploader.d.ts +1216 -0
  93. package/dist/ts/uploader/uploader.ts +4391 -0
  94. package/package.json +62 -12
  95. package/src/input/input.js +1 -1
  96. package/styles/bootstrap5.3-lite.css +3 -3
  97. package/styles/bootstrap5.3.css +5 -5
  98. package/styles/input/_layout.scss +0 -1
  99. package/styles/input/tailwind3.css +0 -1
  100. package/styles/tailwind-dark-lite.css +3 -3
  101. package/styles/tailwind-dark.css +5 -5
  102. package/styles/tailwind-lite.css +3 -3
  103. package/styles/tailwind.css +5 -5
  104. package/styles/tailwind3-lite.css +0 -1
  105. package/styles/tailwind3.css +0 -1
  106. package/styles/uploader/_bootstrap5.3-definition.scss +1 -1
  107. package/styles/uploader/_tailwind-definition.scss +1 -1
  108. package/styles/uploader/bootstrap5.3.css +5 -5
  109. package/styles/uploader/tailwind-dark.css +5 -5
  110. package/styles/uploader/tailwind.css +5 -5
@@ -0,0 +1,924 @@
1
+ import { INotifyPropertyChanged, NotifyPropertyChanges, Component, Property, getUniqueID, isNullOrUndefined, addClass, attributes, removeClass, remove, EmitType, Event, EventHandler } from '@syncfusion/ej2-base';
2
+ import { OtpInputModel } from './otp-input-model';
3
+
4
+ const INPUTFIELD: string = 'e-otp-input-field';
5
+ const RTL: string = 'e-rtl';
6
+
7
+ /**
8
+ * Specifies the type of input for the Otp (One-Time Password) input component.
9
+ */
10
+ export enum OtpInputType {
11
+ /**
12
+ * Specifies the type of input to be number for the Otp input.
13
+ */
14
+ Number = 'number',
15
+ /**
16
+ * Specifies the type of input to be text for the Otp input.
17
+ */
18
+ Text = 'text',
19
+ /**
20
+ * Specifies the type of input to be password for the Otp input.
21
+ */
22
+ Password = 'password'
23
+ }
24
+
25
+ /**
26
+ * Specifies the style variant for the Otp (One-Time Password) input component.
27
+ */
28
+ export enum OtpInputStyle {
29
+ /**
30
+ * Specifies the style of the Otp input to be outlined.
31
+ */
32
+ Outlined = 'outlined',
33
+ /**
34
+ * Specifies the style of the Otp input to be underlined.
35
+ */
36
+ Underlined = 'underlined',
37
+ /**
38
+ * Specifies the style of the Otp input to be filled.
39
+ */
40
+ Filled = 'filled',
41
+ }
42
+
43
+ /**
44
+ * Enum for the case transformation options for OTP input text.
45
+ *
46
+ * @readonly
47
+ * @enum {string}
48
+ */
49
+ export enum TextTransform {
50
+ /**
51
+ * No case transformation. The input text remains unchanged.
52
+ */
53
+ None = 'none',
54
+ /**
55
+ * Convert the input text to uppercase.
56
+ */
57
+ Uppercase = 'uppercase',
58
+ /**
59
+ * Convert the input text to lowercase.
60
+ */
61
+ Lowercase = 'lowercase',
62
+ }
63
+
64
+ /**
65
+ * Provides information about valueChanged event callback
66
+ */
67
+ export interface OtpChangedEventArgs {
68
+ /**
69
+ * Provides the Otp input container element.
70
+ */
71
+ element: HTMLElement;
72
+
73
+ /**
74
+ * Provides the original event.
75
+ */
76
+ event: Event;
77
+
78
+ /**
79
+ * Provides whether the change is triggered by user interaction.
80
+ */
81
+ isInteracted: boolean;
82
+
83
+ /**
84
+ * Provides the previous value of the Otp input.
85
+ */
86
+ previousValue: string | number;
87
+
88
+ /**
89
+ * Provides the current value of the Otp input.
90
+ */
91
+ value: string | number;
92
+ }
93
+
94
+ /**
95
+ * Provides information about focus event callback
96
+ */
97
+ export interface OtpFocusEventArgs {
98
+ /**
99
+ * Provides the Otp input container element.
100
+ */
101
+ element: HTMLElement;
102
+
103
+ /**
104
+ * Provides the original event.
105
+ */
106
+ event: Event;
107
+
108
+ /**
109
+ * Provides whether the change is triggered by user interaction.
110
+ */
111
+ isInteracted: boolean;
112
+
113
+ /**
114
+ * Provides the current value of the Otp input.
115
+ */
116
+ value: string | number;
117
+
118
+ /**
119
+ * The index of the OTP input field that is currently focused.
120
+ */
121
+ index: number;
122
+ }
123
+
124
+ /**
125
+ * Provides information about input event callback
126
+ */
127
+ export interface OtpInputEventArgs {
128
+ /**
129
+ * Provides the Otp input container element.
130
+ */
131
+ element: HTMLElement;
132
+
133
+ /**
134
+ * Provides the original event.
135
+ */
136
+ event: Event;
137
+
138
+ /**
139
+ * Provides the previous value of the Otp input.
140
+ */
141
+ previousValue: string | number;
142
+
143
+ /**
144
+ * Provides the current value of the Otp input.
145
+ */
146
+ value: string | number;
147
+
148
+ /**
149
+ * The index of the OTP input field that is currently focused.
150
+ */
151
+ index: number;
152
+ }
153
+
154
+ /**
155
+ * Represents the Otp component that allows the user to enter the otp values.
156
+ * ```html
157
+ * <div id='OTPInput'></div>
158
+ * ```
159
+ * ```typescript
160
+ * <script>
161
+ * var OtpinputObj = new OtpInput();
162
+ * OtpinputObj.appendTo('#OTPInput');
163
+ * </script>
164
+ * ```
165
+ */
166
+
167
+ @NotifyPropertyChanges
168
+ export class OtpInput extends Component<HTMLElement> implements INotifyPropertyChanged {
169
+ /**
170
+ * Specifies the length of the Otp (One-Time Password) to be entered by the user.
171
+ * This determines the number of input fields in the Otp Input.
172
+ *
173
+ * {% codeBlock src='otp-input/length/index.md' %}{% endcodeBlock %}
174
+ *
175
+ * @default 4
176
+ */
177
+ @Property(4)
178
+ public length: number;
179
+
180
+ /**
181
+ * Specifies the value of the Otp (One-Time Password) input.
182
+ * This can be a string or a number, representing the Otp value entered by the user.
183
+ *
184
+ * {% codeBlock src='otp-input/value/index.md' %}{% endcodeBlock %}
185
+ *
186
+ * @default ''
187
+ * @aspType string
188
+ */
189
+ @Property('')
190
+ public value: string | number;
191
+
192
+ /**
193
+ * Specifies the input type of the Otp.
194
+ *
195
+ * {% codeBlock src='otp-input/type/index.md' %}{% endcodeBlock %}
196
+ *
197
+ * @isenumeration true
198
+ * @default OtpInputType.Number
199
+ * @asptype OtpInputType
200
+ */
201
+ @Property(OtpInputType.Number)
202
+ public type: string | OtpInputType;
203
+
204
+ /**
205
+ * Specifies the separator used to separate each input field in the Otp Input component.
206
+ * The separator is displayed between each input field.
207
+ *
208
+ * {% codeBlock src='otp-input/separator/index.md' %}{% endcodeBlock %}
209
+ *
210
+ * @default ''
211
+ */
212
+ @Property('')
213
+ public separator: string;
214
+
215
+ /**
216
+ * Specifies the text that is shown as a hint/placeholder until the user focuses on or enters a value in the Otp Input.
217
+ * If a single text is provided, it will be used for all input fields; otherwise, each text letter will be used for each field.
218
+ *
219
+ * {% codeBlock src='otp-input/placeholder/index.md' %}{% endcodeBlock %}
220
+ *
221
+ * @default ''
222
+ */
223
+ @Property('')
224
+ public placeholder: string;
225
+
226
+ /**
227
+ * Specifies the style variant for the input fields in the Otp Input component.
228
+ *
229
+ * {% codeBlock src='otp-input/stylingMode/index.md' %}{% endcodeBlock %}
230
+ *
231
+ * @isenumeration true
232
+ * @default OtpInputStyle.Outlined
233
+ * @asptype OtpInputStyle
234
+ */
235
+ @Property(OtpInputStyle.Outlined)
236
+ public stylingMode: string | OtpInputStyle;
237
+
238
+ /**
239
+ * Specifies whether the Otp input component is disabled.
240
+ * When set to true, the component is disabled and user input is not allowed.
241
+ *
242
+ * @default false
243
+ */
244
+ @Property(false)
245
+ public disabled: boolean;
246
+
247
+ /**
248
+ * Defines one or more CSS classes that can be used to customize the appearance of the Otp (One-Time Password) input component.
249
+ *
250
+ * @default ''
251
+ */
252
+ @Property('')
253
+ public cssClass: string;
254
+
255
+ /**
256
+ * Specifies whether the OTP input field should automatically receive focus when the component is rendered.
257
+ *
258
+ * @type {boolean}
259
+ * @default false
260
+ */
261
+ @Property(false)
262
+ public autoFocus: boolean;
263
+
264
+ /**
265
+ * Specifies the case transformation for the OTP input text.
266
+ *
267
+ * Valid values are:
268
+ * - `TextTransform.Uppercase` for uppercase transformation.
269
+ * - `TextTransform.Lowercase` for lowercase transformation.
270
+ * - `TextTransform.None` for no transformation.
271
+ *
272
+ * @isenumeration true
273
+ * @asptype TextTransform
274
+ * @type {TextTransform}
275
+ * @default TextTransform.None
276
+ */
277
+ @Property(TextTransform.None)
278
+ public textTransform: string | TextTransform;
279
+
280
+ /**
281
+ * Specifies additional HTML attributes to be applied to the Otp (One-Time Password) input component.
282
+ *
283
+ * {% codeBlock src='otp-input/htmlAttributes/index.md' %}{% endcodeBlock %}
284
+ *
285
+ * @default {}
286
+ */
287
+ @Property({})
288
+ public htmlAttributes: { [key: string]: string };
289
+
290
+ /**
291
+ * Defines the ARIA-label attribute for each input field in the Otp (One-Time Password) input component.
292
+ * Each string in the array corresponds to the ARIA-label attribute for each input field.
293
+ *
294
+ * {% codeBlock src='otp-input/ariaLabels/index.md' %}{% endcodeBlock %}
295
+ *
296
+ * @default []
297
+ */
298
+ @Property([])
299
+ public ariaLabels: string[];
300
+
301
+ /**
302
+ * Event triggers after the creation of the Otp Input.
303
+ *
304
+ * @event created
305
+ */
306
+ @Event()
307
+ public created: EmitType<Event>;
308
+
309
+ /**
310
+ * Event triggers after the value is changed and the Otp input is focused out.
311
+ *
312
+ * @event change
313
+ */
314
+ @Event()
315
+ public valueChanged: EmitType<OtpChangedEventArgs>;
316
+
317
+ /**
318
+ * Event triggers when the Otp input is focused.
319
+ *
320
+ * @event focus
321
+ */
322
+ @Event()
323
+ public focus: EmitType<OtpFocusEventArgs>;
324
+
325
+ /**
326
+ * Event triggers when the Otp input is focused out.
327
+ *
328
+ * @event blur
329
+ */
330
+ @Event()
331
+ public blur: EmitType<OtpFocusEventArgs>;
332
+
333
+ /**
334
+ * Event triggers each time when the value of each Otp input is changed.
335
+ *
336
+ * @event input
337
+ */
338
+ @Event()
339
+ public input: EmitType<OtpInputEventArgs>;
340
+
341
+ /* Private variables */
342
+ private inputs: HTMLInputElement[] = [];
343
+ private previousValue: string = '';
344
+ private hiddenInputEle: HTMLInputElement;
345
+ private separatorElements: HTMLSpanElement[] = [];
346
+ private shouldFireFocus: boolean = true;
347
+ private shouldFireBlur: boolean = true;
348
+ private isFocusInCalled: boolean = false;
349
+ private isFocusOutCalled: boolean = false;
350
+
351
+ constructor(options?: OtpInputModel, element?: string | HTMLElement) {
352
+ super(options, <HTMLElement | string> element);
353
+ }
354
+
355
+ protected preRender(): void {
356
+ if (!this.element.id) {
357
+ this.element.id = getUniqueID('e-' + this.getModuleName());
358
+ }
359
+ }
360
+
361
+ public render(): void {
362
+ this.initialize();
363
+ }
364
+
365
+ private initialize(): void {
366
+ attributes(this.element, { 'role': 'group' });
367
+ this.renderInputs();
368
+ this.renderSeparator(1, this.inputs.length);
369
+ this.addPlaceHolder();
370
+ this.updateCssClass(this.cssClass);
371
+ this.updateVariantClass();
372
+ this.updateAriaLabel(this.ariaLabels);
373
+ this.setElementAttributes(this.htmlAttributes, this.element);
374
+ if (this.enableRtl) {
375
+ this.element.classList.add(RTL);
376
+ }
377
+ this.previousValue = this.value.toString();
378
+ if (this.autoFocus) { this.focusIn(); }
379
+ }
380
+
381
+ private renderInputs(): void {
382
+ this.hiddenInputEle = this.createElement('input', {
383
+ id: 'otpInput_hidden',
384
+ attrs: {
385
+ name: this.element.id,
386
+ type: 'hidden',
387
+ value: this.type === 'number' ? this.value.toString().replace(/\D/g, '') : this.value.toString(),
388
+ autoComplete: 'off'
389
+ }
390
+ });
391
+ this.element.appendChild(this.hiddenInputEle);
392
+ for (let i: number = 0; i < this.length; i++) {
393
+ this.createOtpInput(i);
394
+ }
395
+ }
396
+
397
+ private createOtpInput(index: number): void {
398
+ const valueContainer: string[] | undefined = this.getDefaultValue();
399
+ let inputValue: string = '';
400
+ if (valueContainer) {
401
+ const valueAtIndex: string = valueContainer[parseInt(index.toString(), 10)];
402
+ if (this.type === 'number') {
403
+ if (!isNaN(Number(valueAtIndex))) {
404
+ inputValue = valueAtIndex;
405
+ }
406
+ } else {
407
+ inputValue = valueAtIndex || '';
408
+ }
409
+ }
410
+ const inputEle: HTMLInputElement = this.createElement('input', {
411
+ id: `${this.element.id}-${index + 1}`,
412
+ className: INPUTFIELD + ' ' + 'e-input',
413
+ attrs: {
414
+ maxlength: '1',
415
+ type: this.type,
416
+ inputmode: this.htmlAttributes['inputmode'] || (this.type === 'number' ? 'numeric' : 'text')
417
+ }
418
+ });
419
+ if (this.disabled) {
420
+ inputEle.setAttribute('disabled', 'disabled');
421
+ }
422
+ this.element.appendChild(inputEle);
423
+ this.inputs.push(inputEle);
424
+ if (inputValue) {
425
+ inputEle.value = inputValue;
426
+ }
427
+ this.wireEvents(inputEle, index);
428
+ }
429
+
430
+ private handleWheelEvent = (e: WheelEvent): void => {
431
+ e.preventDefault();
432
+ };
433
+
434
+ private renderSeparator(index: number, length: number): void {
435
+ if (this.separator.length > 0) {
436
+ for (let i: number = index; i < length; i++) {
437
+ const separatorElement: HTMLSpanElement = this.createElement('span', {
438
+ className: 'e-otp-separator'
439
+ });
440
+ separatorElement.textContent = this.separator;
441
+ this.separatorElements.push(separatorElement);
442
+ this.element.insertBefore(separatorElement, this.inputs[parseInt(i.toString(), 10)]);
443
+ }
444
+ }
445
+ }
446
+
447
+ private updateSeparatorValue(): void {
448
+ if (this.separator === '') {
449
+ this.separatorElements.forEach((element: HTMLSpanElement) => remove(element));
450
+ this.separatorElements = [];
451
+ } else {
452
+ this.separatorElements.forEach((element: HTMLSpanElement) => {
453
+ element.textContent = this.separator;
454
+ });
455
+ }
456
+ }
457
+
458
+ private addPlaceHolder(): void {
459
+ for (let i: number = 0; i < this.inputs.length; i++) {
460
+ const placeholderValue: string = this.placeholder.length <= 1 ? this.placeholder : this.placeholder.charAt(i);
461
+ this.setElementAttributes({ 'placeholder': placeholderValue}, this.inputs[parseInt(i.toString(), 10)]);
462
+ }
463
+ }
464
+
465
+ private updateInputType(inputType: string): void {
466
+ const inputMode: string = this.htmlAttributes['inputmode'] || (inputType === 'number' ? 'numeric' : 'text');
467
+ this.inputs.forEach((input: HTMLInputElement) => {
468
+ input.type = inputType;
469
+ input.setAttribute('inputmode', inputMode);
470
+ });
471
+ }
472
+
473
+ private getDefaultValue (): string[] | undefined {
474
+ let extractedValue: string = typeof this.value === 'number' ? this.value.toString() : this.value;
475
+ if (this.textTransform) {
476
+ extractedValue = this.getTransformedText(extractedValue);
477
+ }
478
+ // To remove the white space if present.
479
+ const value: string = extractedValue.replace(/\s/g, '');
480
+ return value.length > 0 ? value.split('') : undefined;
481
+ }
482
+
483
+ private getTransformedText(transformingText: string): string {
484
+ const transformedText: string = this.textTransform.toLowerCase() === TextTransform.Lowercase ? transformingText.toLowerCase() :
485
+ this.textTransform.toLowerCase() === TextTransform.Uppercase ? transformingText.toUpperCase() : transformingText;
486
+ return transformedText;
487
+ }
488
+
489
+ private handleInputChange(index: number, event: KeyboardEvent): void {
490
+ const currentInputElement: HTMLInputElement = this.inputs[parseInt(index.toString(), 10)];
491
+ if (currentInputElement && index < this.length - 1 && currentInputElement.value.length > 0) {
492
+ const nextInputElement: HTMLInputElement = this.inputs[parseInt(index.toString(), 10) + 1];
493
+ this.shouldFireFocus = this.shouldFireBlur = false;
494
+ nextInputElement.focus();
495
+ if (nextInputElement && nextInputElement.value.length > 0) {
496
+ nextInputElement.select();
497
+ }
498
+ }
499
+ const target: HTMLInputElement = event.target as HTMLInputElement;
500
+ if (target.value.length > 1) {
501
+ target.value = target.value.slice(0, 1);
502
+ }
503
+ if (this.textTransform) {
504
+ target.value = this.getTransformedText(target.value);
505
+ }
506
+ this.triggerInputEvent(index, event);
507
+ this.triggerValuechanged(event, true);
508
+ }
509
+
510
+ private handleKeyAction(index: number, event: KeyboardEvent): void {
511
+ if (event.key.length > 1 && !(
512
+ (index === 0 && event.key === 'Backspace') ||
513
+ (index === this.length - 1 && event.key === 'Delete'))) {
514
+ this.shouldFireFocus = this.shouldFireBlur = false;
515
+ }
516
+ const currentInputElement: HTMLInputElement = this.inputs[parseInt(index.toString(), 10)];
517
+ const previousInputElement: HTMLInputElement = this.inputs[parseInt(index.toString(), 10) - 1];
518
+ const nextInputElement: HTMLInputElement = this.inputs[parseInt(index.toString(), 10) + 1];
519
+ if (event.key === 'Delete') {
520
+ let value: string = '';
521
+ if (currentInputElement.value.length > 0) {
522
+ value = currentInputElement.value;
523
+ currentInputElement.value = '';
524
+ }
525
+ else if (index !== this.inputs.length - 1) {
526
+ value = nextInputElement.value;
527
+ nextInputElement.value = '';
528
+ nextInputElement.focus();
529
+ }
530
+ if (value.length > 0){
531
+ this.triggerInputEvent(index, event);
532
+ }
533
+ }
534
+ else if (event.key === 'Backspace') {
535
+ if (index !== 0 && currentInputElement.value.length === 0) {
536
+ const previousValue: string = previousInputElement.value;
537
+ previousInputElement.value = '';
538
+ previousInputElement.focus();
539
+ if (previousValue.length > 0) {
540
+ this.triggerInputEvent(index, event);
541
+ }
542
+ }
543
+ }
544
+ else if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
545
+ if (event.key === 'ArrowLeft' && index > 0) {
546
+ previousInputElement.focus();
547
+ previousInputElement.select();
548
+ }
549
+ else if (event.key === 'ArrowRight' && index < this.inputs.length - 1) {
550
+ nextInputElement.focus();
551
+ nextInputElement.select();
552
+ }
553
+ event.preventDefault();
554
+ }
555
+ else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
556
+ event.preventDefault();
557
+ }
558
+ else if (event.key !== 'Tab' && !event.shiftKey && !event.ctrlKey) {
559
+ if (this.type === 'number' && (/\D/.test(event.key.toLocaleLowerCase()))) {
560
+ event.preventDefault();
561
+ }
562
+ }
563
+ }
564
+
565
+ private handleSelection(index: number): void {
566
+ const currentInputElement: HTMLInputElement = this.inputs[parseInt(index.toString(), 10)];
567
+ if (currentInputElement.value) {
568
+ currentInputElement.select();
569
+ }
570
+ }
571
+
572
+ private handleFocus(index: number, event: MouseEvent | TouchEvent | KeyboardEvent): void {
573
+ addClass([this.inputs[parseInt(index.toString(), 10)]], 'e-otp-input-focus');
574
+ if (this.shouldFireFocus) {
575
+ const eventArgs: OtpFocusEventArgs = {
576
+ element: this.element,
577
+ event: event,
578
+ index: index,
579
+ isInteracted: this.isFocusInCalled ? false : true,
580
+ value: this.value
581
+ };
582
+ this.trigger('focus', eventArgs);
583
+ }
584
+ this.shouldFireFocus = true;
585
+ }
586
+
587
+ private handleBlur(index: number, event: MouseEvent | TouchEvent | KeyboardEvent): void {
588
+ removeClass([this.inputs[parseInt(index.toString(), 10)]], 'e-otp-input-focus');
589
+ if (this.shouldFireBlur) {
590
+ const eventArgs: OtpFocusEventArgs = {
591
+ element: this.element,
592
+ event: event,
593
+ value: this.value,
594
+ index: index,
595
+ isInteracted: this.isFocusOutCalled ? false : true
596
+ };
597
+ this.trigger('blur', eventArgs);
598
+ }
599
+ this.shouldFireBlur = true;
600
+ }
601
+
602
+ private handlePaste(index: number, event: ClipboardEvent): void {
603
+ const clipboardData: DataTransfer = event.clipboardData;
604
+ if (clipboardData) {
605
+ const pastedText: string = clipboardData.getData('text');
606
+ const pastedValues: string[] = pastedText.split('');
607
+ let pastedValueIndex: number = 0;
608
+ for (let i: number = index; i < this.inputs.length; i++) {
609
+ if (pastedValues.length > 0 && pastedValues[parseInt(pastedValueIndex.toString(), 10)]) {
610
+ this.inputs[parseInt(i.toString(), 10)].value = pastedValues[parseInt(pastedValueIndex.toString(), 10)];
611
+ pastedValueIndex++;
612
+ this.updateValueProperty();
613
+ }
614
+ }
615
+ this.focusIn();
616
+ this.triggerValuechanged(event, true);
617
+ }
618
+ }
619
+
620
+ private triggerInputEvent (index: number, event: KeyboardEvent): void {
621
+ const previousValue: string = this.value.toString();
622
+ this.updateValueProperty();
623
+ const inputEventArgs: OtpInputEventArgs = {
624
+ element: this.element,
625
+ event: event,
626
+ previousValue: previousValue,
627
+ value: this.value.toString(),
628
+ index: index
629
+ };
630
+ this.trigger('input', inputEventArgs);
631
+ }
632
+
633
+ private triggerValuechanged (event?: MouseEvent | TouchEvent | KeyboardEvent | ClipboardEvent, isInteracted?: boolean): void {
634
+ if (this.length === this.value.toString().length) {
635
+ if (this.previousValue !== this.value) {
636
+ const eventArgs: OtpChangedEventArgs = {
637
+ element: this.element,
638
+ event: event,
639
+ isInteracted: isInteracted ? isInteracted : false,
640
+ previousValue: this.previousValue,
641
+ value: this.value
642
+ };
643
+ this.trigger('valueChanged', eventArgs);
644
+ this.previousValue = this.value.toString();
645
+ }
646
+ }
647
+ }
648
+
649
+ private wireEvents (inputEle: HTMLInputElement, index: number): void {
650
+ EventHandler.add(inputEle, 'focus', this.handleFocus.bind(this, index), this);
651
+ EventHandler.add(inputEle, 'blur', this.handleBlur.bind(this, index), this);
652
+ EventHandler.add(inputEle, 'input', this.handleInputChange.bind(this, index), this);
653
+ EventHandler.add(inputEle, 'keydown', this.handleKeyAction.bind(this, index), this);
654
+ EventHandler.add(inputEle, 'click', this.handleSelection.bind(this, index), this);
655
+ EventHandler.add(inputEle, 'paste', this.handlePaste.bind(this, index), this);
656
+ EventHandler.add(inputEle, 'wheel', this.handleWheelEvent, this);
657
+ }
658
+
659
+ private unWireEvents (): void {
660
+ for (let i: number = 0; i < this.inputs.length; i++) {
661
+ const currentInputElement: HTMLInputElement = this.inputs[parseInt(i.toString(), 10)];
662
+ EventHandler.remove(currentInputElement, 'focus', this.handleFocus.bind(this, i));
663
+ EventHandler.remove(currentInputElement, 'blur', this.handleBlur.bind(this, i));
664
+ EventHandler.remove(currentInputElement, 'input', this.handleInputChange.bind(this, i));
665
+ EventHandler.remove(currentInputElement, 'keydown', this.handleKeyAction.bind(this, i));
666
+ EventHandler.remove(currentInputElement, 'click', this.handleSelection.bind(this, i));
667
+ EventHandler.remove(currentInputElement, 'paste', this.handlePaste.bind(this, i));
668
+ EventHandler.remove(currentInputElement, 'wheel', this.handleWheelEvent);
669
+ }
670
+ }
671
+
672
+ private updateValueProperty(): void {
673
+ let value: string = '';
674
+ this.inputs.forEach((input: HTMLInputElement) => {
675
+ value += input.value;
676
+ });
677
+ const prevOnChange: boolean = this.isProtectedOnChange;
678
+ this.isProtectedOnChange = true;
679
+ this.value = typeof this.value === 'number' ? parseInt(value, 10) : value;
680
+ this.isProtectedOnChange = prevOnChange;
681
+ this.hiddenInputEle.value = this.value.toString();
682
+ }
683
+
684
+ private updateInputValue(previousValue: string | number): void {
685
+ let stringifiedValue: string = this.value.toString();
686
+ if (this.textTransform) {
687
+ stringifiedValue = this.getTransformedText(stringifiedValue);
688
+ }
689
+ const previousStringValue: string = previousValue.toString();
690
+ for (let i: number = 0; i < this.inputs.length; i++) {
691
+ if (previousStringValue.charAt(i) !== stringifiedValue.charAt(i)) {
692
+ this.inputs[parseInt(i.toString(), 10)].value = stringifiedValue.charAt(i);
693
+ this.hiddenInputEle.value = stringifiedValue;
694
+ }
695
+ }
696
+ this.focusIn();
697
+ }
698
+
699
+ private updateCssClass(addCss: string, removeCss: string = ''): void {
700
+ let cssClasses: string[];
701
+ if (removeCss) {
702
+ cssClasses = removeCss.trim().split(' ');
703
+ this.element.classList.remove(...cssClasses);
704
+ }
705
+ if (addCss) {
706
+ cssClasses = addCss.trim().split(' ');
707
+ this.element.classList.add(...cssClasses);
708
+ }
709
+ }
710
+
711
+ private updateVariantClass(): void {
712
+ const variantClass: string = this.stylingMode.toLocaleLowerCase() === 'outlined' ? 'outline' : this.stylingMode.toLocaleLowerCase();
713
+ const validClasses: string[] = ['underlined', 'filled', 'outline'];
714
+ if (validClasses.indexOf(variantClass) !== -1) {
715
+ removeClass([this.element], validClasses.map((cls: string) => `e-${cls}`));
716
+ addClass([this.element], `e-${variantClass}`);
717
+ }
718
+ }
719
+
720
+ private updateAriaLabel(customAriaLabel: string[]): void {
721
+ this.inputs.forEach((input: HTMLInputElement, index: number) => {
722
+ const defaultLabel: string = `Enter Otp Character ${index + 1}`;
723
+ const ariaLabel: string =
724
+ customAriaLabel && customAriaLabel.length > 0
725
+ ? customAriaLabel[parseInt(index.toString(), 10)] || defaultLabel
726
+ : defaultLabel;
727
+ input.setAttribute('aria-label', ariaLabel);
728
+ });
729
+ }
730
+
731
+ private updateDisabledState(): void {
732
+ this.inputs.forEach((input: HTMLInputElement) => {
733
+ if (this.disabled) { input.setAttribute('disabled', 'disabled'); }
734
+ else { input.removeAttribute('disabled'); }
735
+ });
736
+ }
737
+
738
+ private setElementAttributes (htmlAttributes : {[key: string]: string}, element: HTMLElement): void {
739
+ if (!isNullOrUndefined(htmlAttributes)) {
740
+ for (const key in htmlAttributes) {
741
+ if (key === 'class') {
742
+ const elementClass: string = htmlAttributes['class'].replace(/\s+/g, ' ').trim();
743
+ if (elementClass) {
744
+ addClass([element], elementClass.split(' '));
745
+ }
746
+ }
747
+ else if (key === 'inputmode') {
748
+ this.setInputMode(htmlAttributes[`${key}`]);
749
+ }
750
+ else if (key === 'name' && this.element.id === element.id) {
751
+ this.hiddenInputEle.setAttribute(key, htmlAttributes[`${key}`]);
752
+ }
753
+ else {
754
+ element.setAttribute(key, htmlAttributes[`${key}`]);
755
+ }
756
+ }
757
+ }
758
+ }
759
+
760
+ private setInputMode(inputModeValue: string): void {
761
+ for (let i: number = 0; i < this.inputs.length; i++) {
762
+ this.inputs[parseInt(i.toString(), 10)].setAttribute('inputmode', inputModeValue);
763
+ }
764
+ }
765
+
766
+ private handleLengthChange (currentValue: number, previousValue: number): void {
767
+ const isLengthAdded: boolean = (currentValue - previousValue) > 0;
768
+ if (isLengthAdded) {
769
+ for (let i: number = previousValue; i < currentValue; i++) {
770
+ this.createOtpInput(i);
771
+ }
772
+ this.renderSeparator(previousValue, currentValue);
773
+ this.addPlaceHolder();
774
+ this.updateAriaLabel(this.ariaLabels);
775
+ }
776
+ else {
777
+ if (currentValue >= 0 && this.inputs.length > 0) {
778
+ for (let i: number = currentValue; i < this.inputs.length; i++) {
779
+ remove(this.inputs[parseInt(i.toString(), 10)]);
780
+ }
781
+ this.inputs.splice(currentValue);
782
+ if (this.separatorElements.length > 0) {
783
+ // separator should be completely removed when length is 0 or 1;
784
+ const index: number = currentValue === 0 ? 0 : currentValue - 1;
785
+ for (let i: number = index; i < this.separatorElements.length; i++) {
786
+ remove(this.separatorElements[parseInt(i.toString(), 10)]);
787
+ }
788
+ this.separatorElements.splice(index);
789
+ }
790
+ }
791
+ }
792
+ }
793
+
794
+ /**
795
+ * To get component name.
796
+ *
797
+ * @returns {string} - Module Name
798
+ * @private
799
+ */
800
+ public getModuleName(): string {
801
+ return 'otpinput';
802
+ }
803
+
804
+ /**
805
+ * To get the properties to be maintained in the persisted state.
806
+ *
807
+ * @returns {string} - Persist data
808
+ */
809
+ protected getPersistData(): string {
810
+ return this.addOnPersist([]);
811
+ }
812
+
813
+ /**
814
+ * Destroy the Otp input.
815
+ *
816
+ * @returns {void}
817
+ */
818
+ public destroy(): void {
819
+ super.destroy();
820
+ this.unWireEvents();
821
+ this.inputs.forEach((input: HTMLInputElement) => { remove(input); });
822
+ this.separatorElements.forEach((separatorElement: HTMLSpanElement) => { remove(separatorElement); });
823
+ remove(this.hiddenInputEle);
824
+ removeClass([this.element], ['e-underlined', 'e-filled', 'e-outline', 'e-rtl']);
825
+ if (this.cssClass) {
826
+ removeClass([this.element], this.cssClass.trim().split(' '));
827
+ }
828
+ this.element.removeAttribute('role');
829
+ this.inputs = [];
830
+ this.separatorElements = [];
831
+ this.hiddenInputEle = null;
832
+ }
833
+
834
+ /**
835
+ * Sets the focus to the Otp input for interaction.
836
+ *
837
+ * @returns {void}
838
+ */
839
+ public focusIn (): void {
840
+ this.isFocusInCalled = true;
841
+ let focusIndex: number = this.inputs.length - 1;
842
+ for ( let index: number = 0; index < this.inputs.length; index++) {
843
+ if (!(this.inputs[parseInt(index.toString(), 10)].value.length > 0)) {
844
+ focusIndex = index;
845
+ break;
846
+ }
847
+ }
848
+ this.inputs[parseInt(focusIndex.toString(), 10)].focus();
849
+ this.isFocusInCalled = false;
850
+ }
851
+
852
+ /**
853
+ * Remove the focus from Otp input, if it is in focus state.
854
+ *
855
+ * @returns {void}
856
+ */
857
+ public focusOut (): void {
858
+ this.isFocusOutCalled = true;
859
+ this.inputs.forEach((input: HTMLInputElement) => {
860
+ input.blur();
861
+ });
862
+ this.isFocusOutCalled = false;
863
+ }
864
+
865
+ /**
866
+ * Called internally if any of the property value changed.
867
+ *
868
+ * @param {OtpInputModel} newProp - Specifies new properties
869
+ * @param {OtpInputModel} oldProp - Specifies old properties
870
+ * @returns {void}
871
+ * @private
872
+ */
873
+ public onPropertyChanged(newProp: OtpInputModel, oldProp?: OtpInputModel): void {
874
+ for (const prop of Object.keys(newProp)) {
875
+ switch (prop) {
876
+ case 'textTransform':
877
+ this.updateInputValue(this.value);
878
+ break;
879
+ case 'value':
880
+ this.updateInputValue(oldProp.value);
881
+ this.triggerValuechanged();
882
+ break;
883
+ case 'placeholder':
884
+ this.addPlaceHolder();
885
+ break;
886
+ case 'disabled':
887
+ this.updateDisabledState();
888
+ break;
889
+ case 'cssClass':
890
+ this.updateCssClass(newProp.cssClass, oldProp.cssClass);
891
+ break;
892
+ case 'separator':
893
+ if (oldProp.separator === '') {
894
+ this.renderSeparator(1, this.inputs.length);
895
+ }
896
+ else {
897
+ this.updateSeparatorValue();
898
+ }
899
+ break;
900
+ case 'htmlAttributes':
901
+ this.setElementAttributes(newProp.htmlAttributes, this.element);
902
+ break;
903
+ case 'type':
904
+ this.updateInputType(newProp.type);
905
+ break;
906
+ case 'stylingMode':
907
+ this.updateVariantClass();
908
+ break;
909
+ case 'ariaLabels':
910
+ this.updateAriaLabel(newProp.ariaLabels);
911
+ break;
912
+ case 'length':
913
+ this.handleLengthChange(newProp.length, oldProp.length);
914
+ break;
915
+ case 'enableRtl':
916
+ this.element.classList[this.enableRtl ? 'add' : 'remove'](RTL);
917
+ break;
918
+ case 'autoFocus':
919
+ if (this.autoFocus) { this.focusIn(); }
920
+ break;
921
+ }
922
+ }
923
+ }
924
+ }