@ruc-lib/input-otp 2.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/README.md +107 -0
- package/esm2020/index.mjs +4 -0
- package/esm2020/lib/input-otp/input-otp.component.mjs +142 -0
- package/esm2020/lib/ruclib-input-otp.module.mjs +20 -0
- package/esm2020/modal/constants.mjs +2 -0
- package/esm2020/modal/input-otp-config.mjs +2 -0
- package/esm2020/ruc-lib-input-otp.mjs +5 -0
- package/fesm2015/ruc-lib-input-otp.mjs +168 -0
- package/fesm2015/ruc-lib-input-otp.mjs.map +1 -0
- package/fesm2020/ruc-lib-input-otp.mjs +167 -0
- package/fesm2020/ruc-lib-input-otp.mjs.map +1 -0
- package/index.d.ts +3 -0
- package/lib/input-otp/input-otp.component.d.ts +25 -0
- package/lib/ruclib-input-otp.module.d.ts +10 -0
- package/modal/constants.d.ts +1 -0
- package/modal/input-otp-config.d.ts +18 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# ruclib-carousel
|
|
2
|
+
|
|
3
|
+
This library provides a flexible and customizable Input OTP component for your Angular applications. Users can integrate the Input OTP component using Angular services or selectors with ease. Below are the features, usage instructions, and integration details
|
|
4
|
+
|
|
5
|
+
## RUC Library Installation Guide
|
|
6
|
+
|
|
7
|
+
Users can easily install the RUC (Ruxptest Library) from npm. Additionally, they can also choose to install individual components based on their requirements.
|
|
8
|
+
Install RUC Library.
|
|
9
|
+
|
|
10
|
+
To install the entire RUC library:
|
|
11
|
+
|
|
12
|
+
`npm install @uxpractice/ruc-lib`
|
|
13
|
+
|
|
14
|
+
## Install Individual Components
|
|
15
|
+
If users only need the carousel component, they can install it separately:
|
|
16
|
+
|
|
17
|
+
`npm install @ruc-lib/input-otp`
|
|
18
|
+
|
|
19
|
+
After installing the the required package successfully, we have to import the CSS in our style.scss file in following way.
|
|
20
|
+
|
|
21
|
+
`@import "../../../node_modules/primeng/resources/themes/lara-light-indigo/theme.css";`
|
|
22
|
+
`@import "../../../node_modules/primeng/resources/primeng.min.css";`
|
|
23
|
+
|
|
24
|
+
here path of the scss file is subject to change as per choice of the installation
|
|
25
|
+
|
|
26
|
+
for library
|
|
27
|
+
`@import "../node_modules/primeng/resources/themes/lara-light-indigo/theme.css";`
|
|
28
|
+
`@import "../node_modules/primeng/resources/primeng.min.css";`
|
|
29
|
+
|
|
30
|
+
for seperate package
|
|
31
|
+
`@import "../node_modules/primeng/resources/themes/lara-light-indigo/theme.css";`
|
|
32
|
+
`@import "../node_modules/primeng/resources/primeng.min.css";`
|
|
33
|
+
|
|
34
|
+
# import required modules
|
|
35
|
+
|
|
36
|
+
Import ContextModule into app.module.ts file.
|
|
37
|
+
|
|
38
|
+
for library
|
|
39
|
+
|
|
40
|
+
`import { RuclibInputOtpModule } from '@uxpractice/ruc-lib/carousel'`
|
|
41
|
+
|
|
42
|
+
for seperate package
|
|
43
|
+
|
|
44
|
+
`import { RuclibInputOtpModule } from '@ruc-lib/carousel'`
|
|
45
|
+
|
|
46
|
+
# Features
|
|
47
|
+
User can have Basic Input
|
|
48
|
+
User can have Masking support
|
|
49
|
+
User can have Sizing functionality
|
|
50
|
+
User can Integer only support
|
|
51
|
+
User can have Custom OTP length
|
|
52
|
+
User can have Auto focus and auto jump
|
|
53
|
+
User can have Paste support
|
|
54
|
+
User can have Auto Submit on Complete
|
|
55
|
+
User can have Input Timeout/Expiry
|
|
56
|
+
User should enter the Otp input within the specified time limit (optional)
|
|
57
|
+
User can have custom templates/ Layouts (circle, rectangle and underscore lines ) for the Input OTP
|
|
58
|
+
User can validate input otp validation if the OTP is correct or not
|
|
59
|
+
User can have Copy and Paste Protection
|
|
60
|
+
|
|
61
|
+
# Use component selector
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
<uxp-input-otp (rucEvent)="passEvent($event)" [rucInputData]="inputObjectDataInputOtp"></uxp-input-otp>
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
# Input and Output
|
|
70
|
+
|
|
71
|
+
# Inputs
|
|
72
|
+
rucInputData -> It is the configuration input to configure the Input OTP component
|
|
73
|
+
|
|
74
|
+
customTheme -> It is the name of the theme.
|
|
75
|
+
# Output
|
|
76
|
+
|
|
77
|
+
# rucInputData (sample object)
|
|
78
|
+
|
|
79
|
+
# Detail definition of the each property can be found in type definition file.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
const defaultConfig = {
|
|
83
|
+
length: 6,
|
|
84
|
+
autoFocus: true,
|
|
85
|
+
integerOnly: true,
|
|
86
|
+
autoSubmit: true,
|
|
87
|
+
mask: false,
|
|
88
|
+
size: 'medium', // 'medium' as literal
|
|
89
|
+
copyProtection: true,
|
|
90
|
+
templateType: 'rectangle', // as literal
|
|
91
|
+
validationState: undefined,
|
|
92
|
+
timeLimit: undefined,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const Default: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
rucInputData: { ...defaultConfig },
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
# Contribution
|
|
103
|
+
Contributions are welcome! Feel free to open issues or pull requests for any enhancements or fixes.
|
|
104
|
+
|
|
105
|
+
# Acknowledgements
|
|
106
|
+
Thank you for choosing the Input OTP Component Library. If you have any feedback or suggestions, please let us know!
|
|
107
|
+
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './lib/ruclib-input-otp.module';
|
|
2
|
+
export * from './lib/input-otp/input-otp.component';
|
|
3
|
+
export * from './modal/input-otp-config';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYywrQkFBK0IsQ0FBQztBQUM5QyxjQUFjLHFDQUFxQyxDQUFDO0FBQ3BELGNBQWMsMEJBQTBCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL2xpYi9ydWNsaWItaW5wdXQtb3RwLm1vZHVsZSc7XHJcbmV4cG9ydCAqIGZyb20gJy4vbGliL2lucHV0LW90cC9pbnB1dC1vdHAuY29tcG9uZW50JztcclxuZXhwb3J0ICogZnJvbSAnLi9tb2RhbC9pbnB1dC1vdHAtY29uZmlnJztcclxuIl19
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, } from '@angular/core';
|
|
2
|
+
import { BACKSPACE_KEY } from '../../modal/constants';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/common";
|
|
5
|
+
import * as i2 from "@angular/forms";
|
|
6
|
+
export class InputOtpComponent {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.DEFAULT_OTP_LENGTH = 6;
|
|
9
|
+
this.TIME_LEFT = "Time left";
|
|
10
|
+
this.EXPIRED_MSG = "OTP expired. Please request a new one.";
|
|
11
|
+
this.rucEvent = new EventEmitter();
|
|
12
|
+
this.rucInputData = {};
|
|
13
|
+
this.timeLeft = 0;
|
|
14
|
+
this.timerId = null;
|
|
15
|
+
this.otpValues = [];
|
|
16
|
+
}
|
|
17
|
+
ngOnInit() {
|
|
18
|
+
this.otpValues = Array(this.rucInputData.length || 6).fill('');
|
|
19
|
+
if (this.rucInputData.timeLimit) {
|
|
20
|
+
this.timeLeft = this.rucInputData.timeLimit;
|
|
21
|
+
}
|
|
22
|
+
// Set default for copyProtection
|
|
23
|
+
if (this.rucInputData.copyProtection === undefined) {
|
|
24
|
+
this.rucInputData.copyProtection = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
ngAfterViewInit() {
|
|
28
|
+
// Merge input config with defaults
|
|
29
|
+
this.otpValues = Array(this.rucInputData.length || 6).fill('');
|
|
30
|
+
if (this.rucInputData.autoFocus) {
|
|
31
|
+
setTimeout(() => this.inputs.first?.nativeElement.focus(), 0); // Auto-focus first input
|
|
32
|
+
}
|
|
33
|
+
if (this.rucInputData.timeLimit) {
|
|
34
|
+
this.timeLeft = this.rucInputData.timeLimit;
|
|
35
|
+
this.timerId = setInterval(() => {
|
|
36
|
+
this.timeLeft--;
|
|
37
|
+
if (this.timeLeft <= 0) {
|
|
38
|
+
clearInterval(this.timerId);
|
|
39
|
+
this.disableAllInputs();
|
|
40
|
+
this.rucEvent.emit({ eventName: 'timeout', eventOutput: null });
|
|
41
|
+
}
|
|
42
|
+
}, 1000);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Handle OTP input
|
|
46
|
+
handleInput(event, index) {
|
|
47
|
+
const input = event.target;
|
|
48
|
+
const value = input.value;
|
|
49
|
+
if (this.rucInputData.integerOnly && !/^\d*$/.test(value)) {
|
|
50
|
+
input.value = ''; // Allow integers only
|
|
51
|
+
this.otpValues[index] = '';
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.otpValues[index] = value.slice(-1); // Take last digit
|
|
55
|
+
input.value = this.otpValues[index];
|
|
56
|
+
// Auto-jump to next input
|
|
57
|
+
if (value && index < (this.rucInputData.length || 6) - 1 && this.rucInputData.autoFocus) {
|
|
58
|
+
this.inputs.toArray()[index + 1].nativeElement.focus();
|
|
59
|
+
}
|
|
60
|
+
// Auto-submit on complete
|
|
61
|
+
if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {
|
|
62
|
+
if (this.timerId) {
|
|
63
|
+
clearInterval(this.timerId);
|
|
64
|
+
this.timerId = null;
|
|
65
|
+
}
|
|
66
|
+
this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Handle Keyboard navigation
|
|
70
|
+
handleKeyDown(event, index) {
|
|
71
|
+
const input = event.target;
|
|
72
|
+
if (event.key === BACKSPACE_KEY && !input.value && index > 0 && this.rucInputData.autoFocus) {
|
|
73
|
+
this.inputs.toArray()[index - 1].nativeElement.focus();
|
|
74
|
+
}
|
|
75
|
+
else if (event.key === 'ArrowLeft' && index > 0) {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
this.inputs.toArray()[index - 1].nativeElement.focus();
|
|
78
|
+
}
|
|
79
|
+
else if (event.key === 'ArrowRight' && index < (this.rucInputData.length || 6) - 1) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
this.inputs.toArray()[index + 1].nativeElement.focus();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//Handle Paste Event
|
|
85
|
+
handlePaste(event) {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
let pasteData = event.clipboardData?.getData('text');
|
|
88
|
+
if (this.rucInputData.integerOnly) {
|
|
89
|
+
pasteData = pasteData?.replace(/\D/g, ''); // Extract digits only
|
|
90
|
+
}
|
|
91
|
+
if (!pasteData)
|
|
92
|
+
return;
|
|
93
|
+
const pasteLength = Math.min(pasteData.length, this.rucInputData.length || 6);
|
|
94
|
+
for (let i = 0; i < pasteLength; i++) {
|
|
95
|
+
this.otpValues[i] = pasteData[i];
|
|
96
|
+
this.inputs.toArray()[i].nativeElement.value = pasteData[i];
|
|
97
|
+
}
|
|
98
|
+
// Focus last filled input or last input
|
|
99
|
+
const focusIndex = pasteLength < (this.rucInputData.length || 6) ? pasteLength : (this.rucInputData.length || 6) - 1;
|
|
100
|
+
this.inputs.toArray()[focusIndex].nativeElement.focus();
|
|
101
|
+
// Auto-submit if complete
|
|
102
|
+
if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {
|
|
103
|
+
if (this.timerId) {
|
|
104
|
+
clearInterval(this.timerId);
|
|
105
|
+
this.timerId = null;
|
|
106
|
+
}
|
|
107
|
+
this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//Check if OTP is completed
|
|
111
|
+
isOtpComplete(otpArray) {
|
|
112
|
+
return otpArray.every(val => val.trim() !== '');
|
|
113
|
+
}
|
|
114
|
+
// Disable inputs
|
|
115
|
+
disableAllInputs() {
|
|
116
|
+
this.inputs.forEach((inputRef) => {
|
|
117
|
+
inputRef.nativeElement.disabled = true;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//Clear Interval
|
|
121
|
+
ngOnDestroy() {
|
|
122
|
+
if (this.timerId) {
|
|
123
|
+
clearInterval(this.timerId);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
InputOtpComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputOtpComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
128
|
+
InputOtpComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: InputOtpComponent, selector: "uxp-input-otp", inputs: { customTheme: "customTheme", rucInputData: "rucInputData" }, outputs: { rucEvent: "rucEvent" }, viewQueries: [{ propertyName: "inputs", predicate: ["otpInput"], descendants: true }], ngImport: i0, template: "<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n", styles: [".otp-container{display:flex;gap:10px;justify-content:center}.otp-input{text-align:center;font-size:18px;border:1px solid #ccc;border-radius:4px;outline:none;transition:border-color .2s;color:inherit}.otp-input:focus{border-color:#007bff;box-shadow:0 0 5px #007bff4d}.otp-input::-webkit-outer-spin-button,.otp-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.otp-input[type=number]{-moz-appearance:textfield}.small .otp-input{width:30px;height:30px;font-size:14px}.medium .otp-input{width:40px;height:40px;font-size:18px}.large .otp-input{width:50px;height:50px;font-size:22px}.otp-timer{text-align:center;font-size:.9rem;color:#d9534f;font-weight:700}.otp-timeout-message{text-align:center;color:#d9534f;font-size:.9rem;margin-top:.5rem}.otp-input.rectangle{border-radius:8px}.otp-input.circle{border-radius:50%}.otp-input.underscore{border:none;border-bottom:2px solid #ccc;border-radius:0;background-color:transparent}.otp-input.is-valid{border-color:#28a745;background-color:#e6ffed}.otp-input.is-invalid{border-color:#dc3545;background-color:#ffe6e6}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputOtpComponent, decorators: [{
|
|
130
|
+
type: Component,
|
|
131
|
+
args: [{ selector: 'uxp-input-otp', template: "<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n", styles: [".otp-container{display:flex;gap:10px;justify-content:center}.otp-input{text-align:center;font-size:18px;border:1px solid #ccc;border-radius:4px;outline:none;transition:border-color .2s;color:inherit}.otp-input:focus{border-color:#007bff;box-shadow:0 0 5px #007bff4d}.otp-input::-webkit-outer-spin-button,.otp-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.otp-input[type=number]{-moz-appearance:textfield}.small .otp-input{width:30px;height:30px;font-size:14px}.medium .otp-input{width:40px;height:40px;font-size:18px}.large .otp-input{width:50px;height:50px;font-size:22px}.otp-timer{text-align:center;font-size:.9rem;color:#d9534f;font-weight:700}.otp-timeout-message{text-align:center;color:#d9534f;font-size:.9rem;margin-top:.5rem}.otp-input.rectangle{border-radius:8px}.otp-input.circle{border-radius:50%}.otp-input.underscore{border:none;border-bottom:2px solid #ccc;border-radius:0;background-color:transparent}.otp-input.is-valid{border-color:#28a745;background-color:#e6ffed}.otp-input.is-invalid{border-color:#dc3545;background-color:#ffe6e6}\n"] }]
|
|
132
|
+
}], propDecorators: { rucEvent: [{
|
|
133
|
+
type: Output
|
|
134
|
+
}], customTheme: [{
|
|
135
|
+
type: Input
|
|
136
|
+
}], rucInputData: [{
|
|
137
|
+
type: Input
|
|
138
|
+
}], inputs: [{
|
|
139
|
+
type: ViewChildren,
|
|
140
|
+
args: ['otpInput']
|
|
141
|
+
}] } });
|
|
142
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQtb3RwLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvaW5wdXQtb3RwL2lucHV0LW90cC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL2lucHV0LW90cC9pbnB1dC1vdHAuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFNBQVMsRUFDVCxLQUFLLEVBQ0wsTUFBTSxFQUNOLFlBQVksRUFDWixZQUFZLEVBRVosU0FBUyxHQUlWLE1BQU0sZUFBZSxDQUFDO0FBRXZCLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQzs7OztBQU90RCxNQUFNLE9BQU8saUJBQWlCO0lBTDlCO1FBTVcsdUJBQWtCLEdBQUksQ0FBQyxDQUFDO1FBQ3hCLGNBQVMsR0FBRyxXQUFXLENBQUM7UUFDeEIsZ0JBQVcsR0FBRyx3Q0FBd0MsQ0FBQTtRQUVyRCxhQUFRLEdBQUcsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUdwQyxpQkFBWSxHQUFtQixFQUFFLENBQUM7UUFDM0MsYUFBUSxHQUFHLENBQUMsQ0FBQztRQUNiLFlBQU8sR0FBUSxJQUFJLENBQUM7UUFJcEIsY0FBUyxHQUFhLEVBQUUsQ0FBQztLQTRIMUI7SUExSEMsUUFBUTtRQUNOLElBQUksQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUMvRCxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsU0FBUyxFQUFFO1lBQy9CLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxTQUFTLENBQUM7U0FDN0M7UUFFSCxpQ0FBaUM7UUFDakMsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLGNBQWMsS0FBSyxTQUFTLEVBQUU7WUFDbEQsSUFBSSxDQUFD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QUFhLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUM1QixJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQzthQUNyQjtZQUNELElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1NBQ3RGO0lBQ0gsQ0FBQztJQUVELDJCQUEyQjtJQUNuQixhQUFhLENBQUMsUUFBa0I7UUFDdEMsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFSCxpQkFBaUI7SUFDUCxnQkFBZ0I7UUFDdEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtZQUMvQixRQUFRLENBQUMsYUFBYSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDekMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsZ0JBQWdCO0lBQ2hCLFdBQVc7UUFDVCxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDaEIsYUFBYSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUM3QjtJQUNILENBQUM7OytHQXpJVSxpQkFBaUI7bUdBQWpCLGlCQUFpQixxUENwQjlCLHE4Q0E4QkE7NEZEVmEsaUJBQWlCO2tCQUw3QixTQUFTOytCQUNFLGVBQWU7OEJBU2YsUUFBUTtzQkFBakIsTUFBTTtnQkFDRSxXQUFXO3NCQUFuQixLQUFLO2dCQUVHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBSW9CLE1BQU07c0JBQS9CLFlBQVk7dUJBQUMsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XHJcbiAgQ29tcG9uZW50LFxyXG4gIElucHV0LFxyXG4gIE91dHB1dCxcclxuICBFdmVudEVtaXR0ZXIsXHJcbiAgVmlld0NoaWxkcmVuLFxyXG4gIEVsZW1lbnRSZWYsXHJcbiAgUXVlcnlMaXN0LFxyXG4gIEFmdGVyVmlld0luaXQsXHJcbiAgT25EZXN0cm95LFxyXG4gIE9uSW5pdCxcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgSW5wdXRPdHBDb25maWcgfSBmcm9tICcuLi8uLi9tb2RhbC9pbnB1dC1vdHAtY29uZmlnJztcclxuaW1wb3J0IHsgQkFDS1NQQUNFX0tFWSB9IGZyb20gJy4uLy4uL21vZGFsL2NvbnN0YW50cyc7XHJcblxyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ3V4cC1pbnB1dC1vdHAnLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9pbnB1dC1vdHAuY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlVXJsczogWycuL2lucHV0LW90cC5jb21wb25lbnQuc2NzcyddXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBJbnB1dE90cENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcclxuICByZWFkb25seSBERUZBVUxUX09UUF9MRU5HVEggID0gNjtcclxuICByZWFkb25seSBUSU1FX0xFRlQgPSBcIlRpbWUgbGVmdFwiO1xyXG4gIHJlYWRvbmx5IEVYUElSRURfTVNHID0gXCJPVFAgZXhwaXJlZC4gUGxlYXNlIHJlcXVlc3QgYSBuZXcgb25lLlwiXHJcblxyXG4gIEBPdXRwdXQoKSBydWNFdmVudCA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xyXG4gIEBJbnB1dCgpIGN1c3RvbVRoZW1lPzogc3RyaW5nO1xyXG5cclxuICBASW5wdXQoKSBydWNJbnB1dERhdGE6IElucHV0T3RwQ29uZmlnID0ge307XHJcbiAgdGltZUxlZnQgPSAwO1xyXG4gIHRpbWVySWQ6IGFueSA9IG51bGw7XHJcblxyXG4gIEBWaWV3Q2hpbGRyZW4oJ290cElucHV0JykgaW5wdXRzITogUXVlcnlMaXN0PEVsZW1lbnRSZWY8SFRNTElucHV0RWxlbWVudD4+O1xyXG5cclxuICBvdHBWYWx1ZXM6IHN0cmluZ1tdID0gW107XHJcblxyXG4gIG5nT25Jbml0KCk6IHZvaWQge1xyXG4gICAgdGhpcy5vdHBWYWx1ZXMgPSBBcnJheSh0aGlzLnJ1Y0lucHV0RGF0YS5sZW5ndGggfHwgNikuZmlsbCgnJyk7XHJcbiAgICBpZiAodGhpcy5ydWNJbnB1dERhdGEudGltZUxpbWl0KSB7XHJcbiAgICAgIHRoaXMudGltZUxlZnQgPSB0aGlzLnJ1Y0lucHV0RGF0YS50aW1lTGltaXQ7XHJcbiAgICB9XHJcblxyXG4gIC8vIFNldCBkZWZhdWx0IGZvciBjb3B5UHJvdGVjdGlvblxyXG4gIGlmICh0aGlzLnJ1Y0lucHV0RGF0YS5jb3B5UHJvdGVjdGlvbiA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICB0aGlzLnJ1Y0lucHV0RGF0YS5jb3B5UHJvdGVjdGlvbiA9IHRydWU7XHJcbiAgfVxyXG4gIH1cclxuXHJcbiAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xyXG4gICAgLy8gTWVyZ2UgaW5wdXQgY29uZmlnIHdpdGggZGVmYXVsdHNcclxuICAgIHRoaXMub3RwVmFsdWVzID0gQXJyYXkodGhpcy5ydWNJbnB1dERhdGEubGVuZ3RoIHx8IDYpLmZpbGwoJycpO1xyXG4gICAgaWYgKHRoaXMucnVjSW5wdXREYXRhLmF1dG9Gb2N1cykge1xyXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuaW5wdXRzLmZpcnN0Py5uYXRpdmVFbGVtZW50LmZvY3VzKCksIDApOyAvLyBBdXRvLWZvY3VzIGZpcnN0IGlucHV0XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHRoaXMucnVjSW5wdXREYXRhLnRpbWVMaW1pdCkge1xyXG4gICAgICB0aGlzLnRpbWVMZWZ0ID0gdGhpcy5ydWNJbnB1dERhdGEudGltZUxpbWl0O1xyXG4gICAgICB0aGlzLnRpbWVySWQgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICAgICAgdGhpcy50aW1lTGVmdC0tO1xyXG4gICAgICAgIGlmICh0aGlzLnRpbWVMZWZ0IDw9IDApIHtcclxuICAgICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aW1lcklkKTtcclxuICAgICAgICAgIHRoaXMuZGlzYWJsZUFsbElucHV0cygpO1xyXG4gICAgICAgICAgdGhpcy5ydWNFdmVudC5lbWl0KHsgZXZlbnROYW1lOiAndGltZW91dCcsIGV2ZW50T3V0cHV0OiBudWxsIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgfSwgMTAwMCk7XHJcbiAgICB9XHJcbiAgfVxyXG4vLyBIYW5kbGUgT1RQIGlucHV0XHJcbiAgaGFuZGxlSW5wdXQoZXZlbnQ6IEV2ZW50LCBpbmRleDogbnVtYmVyKTogdm9pZCB7XHJcbiAgICBjb25zdCBpbnB1dCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50O1xyXG4gICAgY29uc3QgdmFsdWUgPSBpbnB1dC52YWx1ZTtcclxuXHJcbiAgICBpZiAodGhpcy5ydWNJbnB1dERhdGEuaW50ZWdlck9ubHkgJiYgIS9eXFxkKiQvLnRlc3QodmFsdWUpKSB7XHJcbiAgICAgIGlucHV0LnZhbHVlID0gJyc7IC8vIEFsbG93IGludGVnZXJzIG9ubHlcclxuICAgICAgdGhpcy5vdHBWYWx1ZXNbaW5kZXhdID0gJyc7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLm90cFZhbHVlc1tpbmRleF0gPSB2YWx1ZS5zbGljZSgtMSk7IC8vIFRha2UgbGFzdCBkaWdpdFxyXG4gICAgaW5wdXQudmFsdWUgPSB0aGlzLm90cFZhbHVlc1tpbmRleF07XHJcblxyXG4gICAgLy8gQXV0by1qdW1wIHRvIG5leHQgaW5wdXRcclxuICAgIGlmICh2YWx1ZSAmJiBpbmRleCA8ICh0aGlzLnJ1Y0lucHV0RGF0YS5sZW5ndGggfHwgNikgLSAxICYmIHRoaXMucnVjSW5wdXREYXRhLmF1dG9Gb2N1cykge1xyXG4gICAgICB0aGlzLmlucHV0cy50b0FycmF5KClbaW5kZXggKyAxXS5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQXV0by1zdWJtaXQgb24gY29tcGxldGVcclxuICAgIGlmICh0aGlzLnJ1Y0lucHV0RGF0YS5hdXRvU3VibWl0ICYmIHRoaXMuaXNPdHBDb21wbGV0ZSh0aGlzLm90cFZhbHVlcykpIHtcclxuICAgICAgaWYgKHRoaXMudGltZXJJZCkge1xyXG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aW1lcklkKTtcclxuICAgICAgICB0aGlzLnRpbWVySWQgPSBudWxsO1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMucnVjRXZlbnQuZW1pdCh7IGV2ZW50TmFtZTogJ2NvbXBsZXRlZCcsIGV2ZW50T3V0cHV0OiB0aGlzLm90cFZhbHVlcy5qb2luKCcnKSB9KTtcclxuICAgIH1cclxuICB9XHJcblxyXG4vLyBIYW5kbGUgS2V5Ym9hcmQgbmF2aWdhdGlvblxyXG4gIGhhbmRsZUtleURvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGluZGV4OiBudW1iZXIpOiB2b2lkIHtcclxuICAgIGNvbnN0IGlucHV0ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxJbnB1dEVsZW1lbnQ7XHJcblxyXG4gICAgaWYgKGV2ZW50LmtleSA9PT0gQkFDS1NQQUNFX0tFWSAmJiAhaW5wdXQudmFsdWUgJiYgaW5kZXggPiAwICYmIHRoaXMucnVjSW5wdXREYXRhLmF1dG9Gb2N1cykge1xyXG4gICAgICB0aGlzLmlucHV0cy50b0FycmF5KClbaW5kZXggLSAxXS5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XHJcbiAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ0Fycm93TGVmdCcgJiYgaW5kZXggPiAwKSB7XHJcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgIHRoaXMuaW5wdXRzLnRvQXJyYXkoKVtpbmRleCAtIDFdLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKTtcclxuICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnQXJyb3dSaWdodCcgJiYgaW5kZXggPCAodGhpcy5ydWNJbnB1dERhdGEubGVuZ3RoIHx8IDYpIC0gMSkge1xyXG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICB0aGlzLmlucHV0cy50b0FycmF5KClbaW5kZXggKyAxXS5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuLy9IYW5kbGUgUGFzdGUgRXZlbnRcclxuICBoYW5kbGVQYXN0ZShldmVudDogQ2xpcGJvYXJkRXZlbnQpOiB2b2lkIHtcclxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICBsZXQgcGFzdGVEYXRhID0gZXZlbnQuY2xpcGJvYXJkRGF0YT8uZ2V0RGF0YSgndGV4dCcpO1xyXG4gICAgaWYgKHRoaXMucnVjSW5wdXREYXRhLmludGVnZXJPbmx5KSB7XHJcbiAgICAgIHBhc3RlRGF0YSA9IHBhc3RlRGF0YT8ucmVwbGFjZSgvXFxEL2csICcnKTsgLy8gRXh0cmFjdCBkaWdpdHMgb25seVxyXG4gICAgfVxyXG4gICAgaWYgKCFwYXN0ZURhdGEpIHJldHVybjtcclxuXHJcbiAgICBjb25zdCBwYXN0ZUxlbmd0aCA9IE1hdGgubWluKHBhc3RlRGF0YS5sZW5ndGgsIHRoaXMucnVjSW5wdXREYXRhLmxlbmd0aCB8fCA2KTtcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGFzdGVMZW5ndGg7IGkrKykge1xyXG4gICAgICB0aGlzLm90cFZhbHVlc1tpXSA9IHBhc3RlRGF0YVtpXTtcclxuICAgICAgdGhpcy5pbnB1dHMudG9BcnJheSgpW2ldLm5hdGl2ZUVsZW1lbnQudmFsdWUgPSBwYXN0ZURhdGFbaV07XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRm9jdXMgbGFzdCBmaWxsZWQgaW5wdXQgb3IgbGFzdCBpbnB1dFxyXG4gICAgY29uc3QgZm9jdXNJbmRleCA9IHBhc3RlTGVuZ3RoIDwgKHRoaXMucnVjSW5wdXREYXRhLmxlbmd0aCB8fCA2KSA/IHBhc3RlTGVuZ3RoIDogKHRoaXMucnVjSW5wdXREYXRhLmxlbmd0aCB8fCA2KSAtIDE7XHJcbiAgICB0aGlzLmlucHV0cy50b0FycmF5KClbZm9jdXNJbmRleF0ubmF0aXZlRWxlbWVudC5mb2N1cygpO1xyXG5cclxuICAgIC8vIEF1dG8tc3VibWl0IGlmIGNvbXBsZXRlXHJcbiAgICBpZiAodGhpcy5ydWNJbnB1dERhdGEuYXV0b1N1Ym1pdCAmJiB0aGlzLmlzT3RwQ29tcGxldGUodGhpcy5vdHBWYWx1ZXMpKSB7XHJcbiAgICAgIGlmICh0aGlzLnRpbWVySWQpIHtcclxuICAgICAgICBjbGVhckludGVydmFsKHRoaXMudGltZXJJZCk7XHJcbiAgICAgICAgdGhpcy50aW1lcklkID0gbnVsbDtcclxuICAgICAgfVxyXG4gICAgICB0aGlzLnJ1Y0V2ZW50LmVtaXQoeyBldmVudE5hbWU6ICdjb21wbGV0ZWQnLCBldmVudE91dHB1dDogdGhpcy5vdHBWYWx1ZXMuam9pbignJykgfSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvL0NoZWNrIGlmIE9UUCBpcyBjb21wbGV0ZWRcclxuICBwcml2YXRlIGlzT3RwQ29tcGxldGUob3RwQXJyYXk6IHN0cmluZ1tdKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gb3RwQXJyYXkuZXZlcnkodmFsID0+IHZhbC50cmltKCkgIT09ICcnKTtcclxuICB9XHJcblxyXG4vLyBEaXNhYmxlIGlucHV0c1xyXG4gIHByaXZhdGUgZGlzYWJsZUFsbElucHV0cygpOiB2b2lkIHtcclxuICAgIHRoaXMuaW5wdXRzLmZvckVhY2goKGlucHV0UmVmKSA9PiB7XHJcbiAgICAgIGlucHV0UmVmLm5hdGl2ZUVsZW1lbnQuZGlzYWJsZWQgPSB0cnVlO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvL0NsZWFyIEludGVydmFsXHJcbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy50aW1lcklkKSB7XHJcbiAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aW1lcklkKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cInt7Y3VzdG9tVGhlbWV9fSBvdHAtY29udGFpbmVyXCIgW25nQ2xhc3NdPVwicnVjSW5wdXREYXRhLnNpemUgfHwgJ21lZGl1bSdcIiAocGFzdGUpPVwiaGFuZGxlUGFzdGUoJGV2ZW50KVwiPlxyXG4gIDxpbnB1dCAqbmdGb3I9XCJsZXQgXyBvZiBbXS5jb25zdHJ1Y3RvcihydWNJbnB1dERhdGEubGVuZ3RoIHx8IERFRkFVTFRfT1RQX0xFTkdUSCk7IGxldCBpID0gaW5kZXhcIlxyXG4gICAgICAgICAjb3RwSW5wdXRcclxuICAgICAgICAgW3R5cGVdPVwicnVjSW5wdXREYXRhLm1hc2sgPyAncGFzc3dvcmQnIDogJ3RleHQnXCJcclxuICAgICAgICAgW25nQ2xhc3NdPVwiW1xyXG4gICAgICAgICdvdHAtaW5wdXQnLFxyXG4gICAgICAgIHJ1Y0lucHV0RGF0YS50ZW1wbGF0ZVR5cGUgfHwgJ3JlY3RhbmdsZScsXHJcbiAgICAgICAgcnVjSW5wdXREYXRhLnZhbGlkYXRpb25TdGF0ZSA9PT0gJ3ZhbGlkJyA/ICdpcy12YWxpZCcgOiAnJyxcclxuICAgICAgICBydWNJbnB1dERhdGEudmFsaWRhdGlvblN0YXRlID09PSAnaW52YWxpZCcgPyAnaXMtaW52YWxpZCcgOiAnJ1xyXG4gICAgICBdXCJcclxuICAgICAgICAgbWF4bGVuZ3RoPVwiMVwiXHJcbiAgICAgICAgIFsobmdNb2RlbCldPVwib3RwVmFsdWVzW2ldXCJcclxuICAgICAgICAgKGlucHV0KT1cImhhbmRsZUlucHV0KCRldmVudCwgaSlcIlxyXG4gICAgICAgICAoY29weSk9XCJydWNJbnB1dERhdGEuY29weVByb3RlY3Rpb24gJiYgJGV2ZW50LnByZXZlbnREZWZhdWx0KClcIlxyXG4gICAgICAgICAocGFzdGUpPVwicnVjSW5wdXREYXRhLmNvcHlQcm90ZWN0aW9uICYmICRldmVudC5wcmV2ZW50RGVmYXVsdCgpXCIgICAgXHJcbiAgICAgICAgIChjb250ZXh0bWVudSk9XCJydWNJbnB1dERhdGEuY29weVByb3RlY3Rpb24gJiYgJGV2ZW50LnByZXZlbnREZWZhdWx0KClcIiAgICAgXHJcbiAgICAgICAgIChrZXlkb3duKT1cImhhbmRsZUtleURvd24oJGV2ZW50LCBpKVwiXHJcbiAgICAgICAgIFthdHRyLmlucHV0bW9kZV09XCJydWNJbnB1dERhdGEuaW50ZWdlck9ubHkgPyAnbnVtZXJpYycgOiAndGV4dCdcIlxyXG4gICAgICAgICBbcGF0dGVybl09XCJydWNJbnB1dERhdGEuaW50ZWdlck9ubHkgPyAnWzAtOV0qJyA6ICcuKidcIlxyXG4gICAgICAgICBhdXRvY29tcGxldGU9XCJvbmUtdGltZS1jb2RlXCI+XHJcbjwvZGl2PlxyXG48IS0tIFRpbWVyIC0tPlxyXG48ZGl2ICpuZ0lmPVwicnVjSW5wdXREYXRhLnRpbWVMaW1pdFwiIGNsYXNzPVwib3RwLXRpbWVyXCI+XHJcbiAge3tUSU1FX0xFRlR9fToge3sgdGltZUxlZnQgfX1zXHJcbjwvZGl2PlxyXG48IS0tIFRpbWVvdXQgbWVzc2FnZSAtLT5cclxuPGRpdiAqbmdJZj1cInJ1Y0lucHV0RGF0YS50aW1lTGltaXQgJiYgdGltZUxlZnQgPT09IDBcIiBjbGFzcz1cIm90cC10aW1lb3V0LW1lc3NhZ2VcIj5cclxuICB7e0VYUElSRURfTVNHfX1cclxuPC9kaXY+XHJcblxyXG4iXX0=
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NgModule } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
+
import { FormsModule } from '@angular/forms';
|
|
5
|
+
import { InputOtpComponent } from './input-otp/input-otp.component';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
export class RuclibInputOtpModule {
|
|
8
|
+
}
|
|
9
|
+
RuclibInputOtpModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
10
|
+
RuclibInputOtpModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, declarations: [InputOtpComponent], imports: [CommonModule, MatIconModule, FormsModule], exports: [InputOtpComponent] });
|
|
11
|
+
RuclibInputOtpModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, imports: [CommonModule, MatIconModule, FormsModule] });
|
|
12
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, decorators: [{
|
|
13
|
+
type: NgModule,
|
|
14
|
+
args: [{
|
|
15
|
+
imports: [CommonModule, MatIconModule, FormsModule],
|
|
16
|
+
declarations: [InputOtpComponent],
|
|
17
|
+
exports: [InputOtpComponent]
|
|
18
|
+
}]
|
|
19
|
+
}] });
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVjbGliLWlucHV0LW90cC5tb2R1bGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3J1Y2xpYi1pbnB1dC1vdHAubW9kdWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0saUNBQWlDLENBQUM7O0FBUXBFLE1BQU0sT0FBTyxvQkFBb0I7O2tIQUFwQixvQkFBb0I7bUhBQXBCLG9CQUFvQixpQkFKaEIsaUJBQWlCLGFBRHRCLFlBQVksRUFBRSxhQUFhLEVBQUUsV0FBVyxhQUV2QyxpQkFBaUI7bUhBR2pCLG9CQUFvQixZQUxyQixZQUFZLEVBQUUsYUFBYSxFQUFFLFdBQVc7NEZBS3ZDLG9CQUFvQjtrQkFOaEMsUUFBUTttQkFBQztvQkFDUixPQUFPLEVBQUUsQ0FBQyxZQUFZLEVBQUUsYUFBYSxFQUFFLFdBQVcsQ0FBQztvQkFDbkQsWUFBWSxFQUFFLENBQUMsaUJBQWlCLENBQUM7b0JBQ2pDLE9BQU8sRUFBRyxDQUFDLGlCQUFpQixDQUFDO2lCQUU5QiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE5nTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IE1hdEljb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pY29uJztcclxuaW1wb3J0IHsgRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XHJcbmltcG9ydCB7IElucHV0T3RwQ29tcG9uZW50IH0gZnJvbSAnLi9pbnB1dC1vdHAvaW5wdXQtb3RwLmNvbXBvbmVudCc7XHJcblxyXG5ATmdNb2R1bGUoe1xyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGUsIE1hdEljb25Nb2R1bGUsIEZvcm1zTW9kdWxlXSxcclxuICBkZWNsYXJhdGlvbnM6IFtJbnB1dE90cENvbXBvbmVudF0sXHJcbiAgZXhwb3J0cyA6IFtJbnB1dE90cENvbXBvbmVudF1cclxuXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBSdWNsaWJJbnB1dE90cE1vZHVsZSB7fVxyXG4iXX0=
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export const BACKSPACE_KEY = "Backspace";
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21vZGFsL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsV0FBVyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGNvbnN0IEJBQ0tTUEFDRV9LRVkgPSBcIkJhY2tzcGFjZVwiIl19
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQtb3RwLWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tb2RhbC9pbnB1dC1vdHAtY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgaW50ZXJmYWNlIElucHV0T3RwQ29uZmlnIHtcclxuICAvKiogTnVtYmVyIG9mIE9UUCBkaWdpdHMgKi9cclxuICBsZW5ndGg/OiBudW1iZXI7XHJcbiAgLyoqIE1hc2sgaW5wdXQgYXMgcGFzc3dvcmQgKi9cclxuICBtYXNrPzogYm9vbGVhbjtcclxuICAvKiogU2l6ZXM6IHNtYWxsLCBtZWRpdW0sIGxhcmdlICovXHJcbiAgc2l6ZT86ICdzbWFsbCcgfCAnbWVkaXVtJyB8ICdsYXJnZSc7XHJcbiAgLyoqIE9ubHkgYWxsb3cgaW50ZWdlcnMgMC05ICovXHJcbiAgaW50ZWdlck9ubHk/OiBib29sZWFuO1xyXG4gIC8qKiBBdXRvZm9jdXMgYW5kIGF1dG8tanVtcCAqL1xyXG4gIGF1dG9Gb2N1cz86IGJvb2xlYW47XHJcbiAgYXV0b1N1Ym1pdD86IGJvb2xlYW47XHJcbiAgdGltZUxpbWl0PzogbnVtYmVyOyAvLyBPcHRpb25hbCB0aW1lb3V0IGluIHNlY29uZHNcclxuICAvL2NvcHkgYW5kIHBhc3RlIHByb3RlY3Rpb25cclxuICBjb3B5UHJvdGVjdGlvbj8gOiBib29sZWFuXHJcbiAgLy90ZW1wbGF0ZSBzdHlsZVxyXG4gIHRlbXBsYXRlVHlwZT86ICdyZWN0YW5nbGUnIHwgJ2NpcmNsZScgfCAndW5kZXJzY29yZSc7XHJcbiAgLyoqIFZhbGlkYXRpb24gc3RhdGUgZm9yIHZpc3VhbCBmZWVkYmFjayAqL1xyXG52YWxpZGF0aW9uU3RhdGU/OiAndmFsaWQnIHwgJ2ludmFsaWQnIHwgdW5kZWZpbmVkO1xyXG4vKiogT3B0aW9uYWwgc2VwYXJhdG9yIHRvIGJlIGFkZGVkIGJldHdlZW4gT1RQIGRpZ2l0cyBpbiB0aGUgb3V0cHV0ICovXHJcbiAgfSJdfQ==
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated bundle index. Do not edit.
|
|
3
|
+
*/
|
|
4
|
+
export * from './index';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVjLWxpYi1pbnB1dC1vdHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVjLWxpYi1pbnB1dC1vdHAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLFNBQVMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9pbmRleCc7XG4iXX0=
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { EventEmitter, Component, Output, Input, ViewChildren, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
6
|
+
import * as i2 from '@angular/forms';
|
|
7
|
+
import { FormsModule } from '@angular/forms';
|
|
8
|
+
|
|
9
|
+
const BACKSPACE_KEY = "Backspace";
|
|
10
|
+
|
|
11
|
+
class InputOtpComponent {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.DEFAULT_OTP_LENGTH = 6;
|
|
14
|
+
this.TIME_LEFT = "Time left";
|
|
15
|
+
this.EXPIRED_MSG = "OTP expired. Please request a new one.";
|
|
16
|
+
this.rucEvent = new EventEmitter();
|
|
17
|
+
this.rucInputData = {};
|
|
18
|
+
this.timeLeft = 0;
|
|
19
|
+
this.timerId = null;
|
|
20
|
+
this.otpValues = [];
|
|
21
|
+
}
|
|
22
|
+
ngOnInit() {
|
|
23
|
+
this.otpValues = Array(this.rucInputData.length || 6).fill('');
|
|
24
|
+
if (this.rucInputData.timeLimit) {
|
|
25
|
+
this.timeLeft = this.rucInputData.timeLimit;
|
|
26
|
+
}
|
|
27
|
+
// Set default for copyProtection
|
|
28
|
+
if (this.rucInputData.copyProtection === undefined) {
|
|
29
|
+
this.rucInputData.copyProtection = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
ngAfterViewInit() {
|
|
33
|
+
// Merge input config with defaults
|
|
34
|
+
this.otpValues = Array(this.rucInputData.length || 6).fill('');
|
|
35
|
+
if (this.rucInputData.autoFocus) {
|
|
36
|
+
setTimeout(() => { var _a; return (_a = this.inputs.first) === null || _a === void 0 ? void 0 : _a.nativeElement.focus(); }, 0); // Auto-focus first input
|
|
37
|
+
}
|
|
38
|
+
if (this.rucInputData.timeLimit) {
|
|
39
|
+
this.timeLeft = this.rucInputData.timeLimit;
|
|
40
|
+
this.timerId = setInterval(() => {
|
|
41
|
+
this.timeLeft--;
|
|
42
|
+
if (this.timeLeft <= 0) {
|
|
43
|
+
clearInterval(this.timerId);
|
|
44
|
+
this.disableAllInputs();
|
|
45
|
+
this.rucEvent.emit({ eventName: 'timeout', eventOutput: null });
|
|
46
|
+
}
|
|
47
|
+
}, 1000);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Handle OTP input
|
|
51
|
+
handleInput(event, index) {
|
|
52
|
+
const input = event.target;
|
|
53
|
+
const value = input.value;
|
|
54
|
+
if (this.rucInputData.integerOnly && !/^\d*$/.test(value)) {
|
|
55
|
+
input.value = ''; // Allow integers only
|
|
56
|
+
this.otpValues[index] = '';
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.otpValues[index] = value.slice(-1); // Take last digit
|
|
60
|
+
input.value = this.otpValues[index];
|
|
61
|
+
// Auto-jump to next input
|
|
62
|
+
if (value && index < (this.rucInputData.length || 6) - 1 && this.rucInputData.autoFocus) {
|
|
63
|
+
this.inputs.toArray()[index + 1].nativeElement.focus();
|
|
64
|
+
}
|
|
65
|
+
// Auto-submit on complete
|
|
66
|
+
if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {
|
|
67
|
+
if (this.timerId) {
|
|
68
|
+
clearInterval(this.timerId);
|
|
69
|
+
this.timerId = null;
|
|
70
|
+
}
|
|
71
|
+
this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Handle Keyboard navigation
|
|
75
|
+
handleKeyDown(event, index) {
|
|
76
|
+
const input = event.target;
|
|
77
|
+
if (event.key === BACKSPACE_KEY && !input.value && index > 0 && this.rucInputData.autoFocus) {
|
|
78
|
+
this.inputs.toArray()[index - 1].nativeElement.focus();
|
|
79
|
+
}
|
|
80
|
+
else if (event.key === 'ArrowLeft' && index > 0) {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
this.inputs.toArray()[index - 1].nativeElement.focus();
|
|
83
|
+
}
|
|
84
|
+
else if (event.key === 'ArrowRight' && index < (this.rucInputData.length || 6) - 1) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
this.inputs.toArray()[index + 1].nativeElement.focus();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//Handle Paste Event
|
|
90
|
+
handlePaste(event) {
|
|
91
|
+
var _a;
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
let pasteData = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text');
|
|
94
|
+
if (this.rucInputData.integerOnly) {
|
|
95
|
+
pasteData = pasteData === null || pasteData === void 0 ? void 0 : pasteData.replace(/\D/g, ''); // Extract digits only
|
|
96
|
+
}
|
|
97
|
+
if (!pasteData)
|
|
98
|
+
return;
|
|
99
|
+
const pasteLength = Math.min(pasteData.length, this.rucInputData.length || 6);
|
|
100
|
+
for (let i = 0; i < pasteLength; i++) {
|
|
101
|
+
this.otpValues[i] = pasteData[i];
|
|
102
|
+
this.inputs.toArray()[i].nativeElement.value = pasteData[i];
|
|
103
|
+
}
|
|
104
|
+
// Focus last filled input or last input
|
|
105
|
+
const focusIndex = pasteLength < (this.rucInputData.length || 6) ? pasteLength : (this.rucInputData.length || 6) - 1;
|
|
106
|
+
this.inputs.toArray()[focusIndex].nativeElement.focus();
|
|
107
|
+
// Auto-submit if complete
|
|
108
|
+
if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {
|
|
109
|
+
if (this.timerId) {
|
|
110
|
+
clearInterval(this.timerId);
|
|
111
|
+
this.timerId = null;
|
|
112
|
+
}
|
|
113
|
+
this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//Check if OTP is completed
|
|
117
|
+
isOtpComplete(otpArray) {
|
|
118
|
+
return otpArray.every(val => val.trim() !== '');
|
|
119
|
+
}
|
|
120
|
+
// Disable inputs
|
|
121
|
+
disableAllInputs() {
|
|
122
|
+
this.inputs.forEach((inputRef) => {
|
|
123
|
+
inputRef.nativeElement.disabled = true;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//Clear Interval
|
|
127
|
+
ngOnDestroy() {
|
|
128
|
+
if (this.timerId) {
|
|
129
|
+
clearInterval(this.timerId);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
InputOtpComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputOtpComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
134
|
+
InputOtpComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: InputOtpComponent, selector: "uxp-input-otp", inputs: { customTheme: "customTheme", rucInputData: "rucInputData" }, outputs: { rucEvent: "rucEvent" }, viewQueries: [{ propertyName: "inputs", predicate: ["otpInput"], descendants: true }], ngImport: i0, template: "<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n", styles: [".otp-container{display:flex;gap:10px;justify-content:center}.otp-input{text-align:center;font-size:18px;border:1px solid #ccc;border-radius:4px;outline:none;transition:border-color .2s;color:inherit}.otp-input:focus{border-color:#007bff;box-shadow:0 0 5px #007bff4d}.otp-input::-webkit-outer-spin-button,.otp-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.otp-input[type=number]{-moz-appearance:textfield}.small .otp-input{width:30px;height:30px;font-size:14px}.medium .otp-input{width:40px;height:40px;font-size:18px}.large .otp-input{width:50px;height:50px;font-size:22px}.otp-timer{text-align:center;font-size:.9rem;color:#d9534f;font-weight:700}.otp-timeout-message{text-align:center;color:#d9534f;font-size:.9rem;margin-top:.5rem}.otp-input.rectangle{border-radius:8px}.otp-input.circle{border-radius:50%}.otp-input.underscore{border:none;border-bottom:2px solid #ccc;border-radius:0;background-color:transparent}.otp-input.is-valid{border-color:#28a745;background-color:#e6ffed}.otp-input.is-invalid{border-color:#dc3545;background-color:#ffe6e6}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputOtpComponent, decorators: [{
|
|
136
|
+
type: Component,
|
|
137
|
+
args: [{ selector: 'uxp-input-otp', template: "<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n", styles: [".otp-container{display:flex;gap:10px;justify-content:center}.otp-input{text-align:center;font-size:18px;border:1px solid #ccc;border-radius:4px;outline:none;transition:border-color .2s;color:inherit}.otp-input:focus{border-color:#007bff;box-shadow:0 0 5px #007bff4d}.otp-input::-webkit-outer-spin-button,.otp-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.otp-input[type=number]{-moz-appearance:textfield}.small .otp-input{width:30px;height:30px;font-size:14px}.medium .otp-input{width:40px;height:40px;font-size:18px}.large .otp-input{width:50px;height:50px;font-size:22px}.otp-timer{text-align:center;font-size:.9rem;color:#d9534f;font-weight:700}.otp-timeout-message{text-align:center;color:#d9534f;font-size:.9rem;margin-top:.5rem}.otp-input.rectangle{border-radius:8px}.otp-input.circle{border-radius:50%}.otp-input.underscore{border:none;border-bottom:2px solid #ccc;border-radius:0;background-color:transparent}.otp-input.is-valid{border-color:#28a745;background-color:#e6ffed}.otp-input.is-invalid{border-color:#dc3545;background-color:#ffe6e6}\n"] }]
|
|
138
|
+
}], propDecorators: { rucEvent: [{
|
|
139
|
+
type: Output
|
|
140
|
+
}], customTheme: [{
|
|
141
|
+
type: Input
|
|
142
|
+
}], rucInputData: [{
|
|
143
|
+
type: Input
|
|
144
|
+
}], inputs: [{
|
|
145
|
+
type: ViewChildren,
|
|
146
|
+
args: ['otpInput']
|
|
147
|
+
}] } });
|
|
148
|
+
|
|
149
|
+
class RuclibInputOtpModule {
|
|
150
|
+
}
|
|
151
|
+
RuclibInputOtpModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
152
|
+
RuclibInputOtpModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, declarations: [InputOtpComponent], imports: [CommonModule, MatIconModule, FormsModule], exports: [InputOtpComponent] });
|
|
153
|
+
RuclibInputOtpModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, imports: [CommonModule, MatIconModule, FormsModule] });
|
|
154
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, decorators: [{
|
|
155
|
+
type: NgModule,
|
|
156
|
+
args: [{
|
|
157
|
+
imports: [CommonModule, MatIconModule, FormsModule],
|
|
158
|
+
declarations: [InputOtpComponent],
|
|
159
|
+
exports: [InputOtpComponent]
|
|
160
|
+
}]
|
|
161
|
+
}] });
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Generated bundle index. Do not edit.
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
export { InputOtpComponent, RuclibInputOtpModule };
|
|
168
|
+
//# sourceMappingURL=ruc-lib-input-otp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ruc-lib-input-otp.mjs","sources":["../../src/modal/constants.ts","../../src/lib/input-otp/input-otp.component.ts","../../src/lib/input-otp/input-otp.component.html","../../src/lib/ruclib-input-otp.module.ts","../../src/ruc-lib-input-otp.ts"],"sourcesContent":["export const BACKSPACE_KEY = \"Backspace\"","import {\r\n Component,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ViewChildren,\r\n ElementRef,\r\n QueryList,\r\n AfterViewInit,\r\n OnDestroy,\r\n OnInit,\r\n} from '@angular/core';\r\nimport { InputOtpConfig } from '../../modal/input-otp-config';\r\nimport { BACKSPACE_KEY } from '../../modal/constants';\r\n\r\n@Component({\r\n selector: 'uxp-input-otp',\r\n templateUrl: './input-otp.component.html',\r\n styleUrls: ['./input-otp.component.scss']\r\n})\r\nexport class InputOtpComponent implements OnInit, AfterViewInit, OnDestroy {\r\n readonly DEFAULT_OTP_LENGTH = 6;\r\n readonly TIME_LEFT = \"Time left\";\r\n readonly EXPIRED_MSG = \"OTP expired. Please request a new one.\"\r\n\r\n @Output() rucEvent = new EventEmitter<any>();\r\n @Input() customTheme?: string;\r\n\r\n @Input() rucInputData: InputOtpConfig = {};\r\n timeLeft = 0;\r\n timerId: any = null;\r\n\r\n @ViewChildren('otpInput') inputs!: QueryList<ElementRef<HTMLInputElement>>;\r\n\r\n otpValues: string[] = [];\r\n\r\n ngOnInit(): void {\r\n this.otpValues = Array(this.rucInputData.length || 6).fill('');\r\n if (this.rucInputData.timeLimit) {\r\n this.timeLeft = this.rucInputData.timeLimit;\r\n }\r\n\r\n // Set default for copyProtection\r\n if (this.rucInputData.copyProtection === undefined) {\r\n this.rucInputData.copyProtection = true;\r\n }\r\n }\r\n\r\n ngAfterViewInit(): void {\r\n // Merge input config with defaults\r\n this.otpValues = Array(this.rucInputData.length || 6).fill('');\r\n if (this.rucInputData.autoFocus) {\r\n setTimeout(() => this.inputs.first?.nativeElement.focus(), 0); // Auto-focus first input\r\n }\r\n\r\n if (this.rucInputData.timeLimit) {\r\n this.timeLeft = this.rucInputData.timeLimit;\r\n this.timerId = setInterval(() => {\r\n this.timeLeft--;\r\n if (this.timeLeft <= 0) {\r\n clearInterval(this.timerId);\r\n this.disableAllInputs();\r\n this.rucEvent.emit({ eventName: 'timeout', eventOutput: null });\r\n }\r\n }, 1000);\r\n }\r\n }\r\n// Handle OTP input\r\n handleInput(event: Event, index: number): void {\r\n const input = event.target as HTMLInputElement;\r\n const value = input.value;\r\n\r\n if (this.rucInputData.integerOnly && !/^\\d*$/.test(value)) {\r\n input.value = ''; // Allow integers only\r\n this.otpValues[index] = '';\r\n return;\r\n }\r\n\r\n this.otpValues[index] = value.slice(-1); // Take last digit\r\n input.value = this.otpValues[index];\r\n\r\n // Auto-jump to next input\r\n if (value && index < (this.rucInputData.length || 6) - 1 && this.rucInputData.autoFocus) {\r\n this.inputs.toArray()[index + 1].nativeElement.focus();\r\n }\r\n\r\n // Auto-submit on complete\r\n if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {\r\n if (this.timerId) {\r\n clearInterval(this.timerId);\r\n this.timerId = null;\r\n }\r\n this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });\r\n }\r\n }\r\n\r\n// Handle Keyboard navigation\r\n handleKeyDown(event: KeyboardEvent, index: number): void {\r\n const input = event.target as HTMLInputElement;\r\n\r\n if (event.key === BACKSPACE_KEY && !input.value && index > 0 && this.rucInputData.autoFocus) {\r\n this.inputs.toArray()[index - 1].nativeElement.focus();\r\n } else if (event.key === 'ArrowLeft' && index > 0) {\r\n event.preventDefault();\r\n this.inputs.toArray()[index - 1].nativeElement.focus();\r\n } else if (event.key === 'ArrowRight' && index < (this.rucInputData.length || 6) - 1) {\r\n event.preventDefault();\r\n this.inputs.toArray()[index + 1].nativeElement.focus();\r\n }\r\n }\r\n\r\n//Handle Paste Event\r\n handlePaste(event: ClipboardEvent): void {\r\n event.preventDefault();\r\n let pasteData = event.clipboardData?.getData('text');\r\n if (this.rucInputData.integerOnly) {\r\n pasteData = pasteData?.replace(/\\D/g, ''); // Extract digits only\r\n }\r\n if (!pasteData) return;\r\n\r\n const pasteLength = Math.min(pasteData.length, this.rucInputData.length || 6);\r\n for (let i = 0; i < pasteLength; i++) {\r\n this.otpValues[i] = pasteData[i];\r\n this.inputs.toArray()[i].nativeElement.value = pasteData[i];\r\n }\r\n\r\n // Focus last filled input or last input\r\n const focusIndex = pasteLength < (this.rucInputData.length || 6) ? pasteLength : (this.rucInputData.length || 6) - 1;\r\n this.inputs.toArray()[focusIndex].nativeElement.focus();\r\n\r\n // Auto-submit if complete\r\n if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {\r\n if (this.timerId) {\r\n clearInterval(this.timerId);\r\n this.timerId = null;\r\n }\r\n this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });\r\n }\r\n }\r\n\r\n //Check if OTP is completed\r\n private isOtpComplete(otpArray: string[]): boolean {\r\n return otpArray.every(val => val.trim() !== '');\r\n }\r\n\r\n// Disable inputs\r\n private disableAllInputs(): void {\r\n this.inputs.forEach((inputRef) => {\r\n inputRef.nativeElement.disabled = true;\r\n });\r\n }\r\n\r\n //Clear Interval\r\n ngOnDestroy(): void {\r\n if (this.timerId) {\r\n clearInterval(this.timerId);\r\n }\r\n }\r\n}\r\n","<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n","import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { MatIconModule } from '@angular/material/icon';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { InputOtpComponent } from './input-otp/input-otp.component';\r\n\r\n@NgModule({\r\n imports: [CommonModule, MatIconModule, FormsModule],\r\n declarations: [InputOtpComponent],\r\n exports : [InputOtpComponent]\r\n\r\n})\r\nexport class RuclibInputOtpModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { EventEmitter, Component, Output, Input, ViewChildren, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
6
|
+
import * as i2 from '@angular/forms';
|
|
7
|
+
import { FormsModule } from '@angular/forms';
|
|
8
|
+
|
|
9
|
+
const BACKSPACE_KEY = "Backspace";
|
|
10
|
+
|
|
11
|
+
class InputOtpComponent {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.DEFAULT_OTP_LENGTH = 6;
|
|
14
|
+
this.TIME_LEFT = "Time left";
|
|
15
|
+
this.EXPIRED_MSG = "OTP expired. Please request a new one.";
|
|
16
|
+
this.rucEvent = new EventEmitter();
|
|
17
|
+
this.rucInputData = {};
|
|
18
|
+
this.timeLeft = 0;
|
|
19
|
+
this.timerId = null;
|
|
20
|
+
this.otpValues = [];
|
|
21
|
+
}
|
|
22
|
+
ngOnInit() {
|
|
23
|
+
this.otpValues = Array(this.rucInputData.length || 6).fill('');
|
|
24
|
+
if (this.rucInputData.timeLimit) {
|
|
25
|
+
this.timeLeft = this.rucInputData.timeLimit;
|
|
26
|
+
}
|
|
27
|
+
// Set default for copyProtection
|
|
28
|
+
if (this.rucInputData.copyProtection === undefined) {
|
|
29
|
+
this.rucInputData.copyProtection = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
ngAfterViewInit() {
|
|
33
|
+
// Merge input config with defaults
|
|
34
|
+
this.otpValues = Array(this.rucInputData.length || 6).fill('');
|
|
35
|
+
if (this.rucInputData.autoFocus) {
|
|
36
|
+
setTimeout(() => this.inputs.first?.nativeElement.focus(), 0); // Auto-focus first input
|
|
37
|
+
}
|
|
38
|
+
if (this.rucInputData.timeLimit) {
|
|
39
|
+
this.timeLeft = this.rucInputData.timeLimit;
|
|
40
|
+
this.timerId = setInterval(() => {
|
|
41
|
+
this.timeLeft--;
|
|
42
|
+
if (this.timeLeft <= 0) {
|
|
43
|
+
clearInterval(this.timerId);
|
|
44
|
+
this.disableAllInputs();
|
|
45
|
+
this.rucEvent.emit({ eventName: 'timeout', eventOutput: null });
|
|
46
|
+
}
|
|
47
|
+
}, 1000);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Handle OTP input
|
|
51
|
+
handleInput(event, index) {
|
|
52
|
+
const input = event.target;
|
|
53
|
+
const value = input.value;
|
|
54
|
+
if (this.rucInputData.integerOnly && !/^\d*$/.test(value)) {
|
|
55
|
+
input.value = ''; // Allow integers only
|
|
56
|
+
this.otpValues[index] = '';
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.otpValues[index] = value.slice(-1); // Take last digit
|
|
60
|
+
input.value = this.otpValues[index];
|
|
61
|
+
// Auto-jump to next input
|
|
62
|
+
if (value && index < (this.rucInputData.length || 6) - 1 && this.rucInputData.autoFocus) {
|
|
63
|
+
this.inputs.toArray()[index + 1].nativeElement.focus();
|
|
64
|
+
}
|
|
65
|
+
// Auto-submit on complete
|
|
66
|
+
if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {
|
|
67
|
+
if (this.timerId) {
|
|
68
|
+
clearInterval(this.timerId);
|
|
69
|
+
this.timerId = null;
|
|
70
|
+
}
|
|
71
|
+
this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Handle Keyboard navigation
|
|
75
|
+
handleKeyDown(event, index) {
|
|
76
|
+
const input = event.target;
|
|
77
|
+
if (event.key === BACKSPACE_KEY && !input.value && index > 0 && this.rucInputData.autoFocus) {
|
|
78
|
+
this.inputs.toArray()[index - 1].nativeElement.focus();
|
|
79
|
+
}
|
|
80
|
+
else if (event.key === 'ArrowLeft' && index > 0) {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
this.inputs.toArray()[index - 1].nativeElement.focus();
|
|
83
|
+
}
|
|
84
|
+
else if (event.key === 'ArrowRight' && index < (this.rucInputData.length || 6) - 1) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
this.inputs.toArray()[index + 1].nativeElement.focus();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//Handle Paste Event
|
|
90
|
+
handlePaste(event) {
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
let pasteData = event.clipboardData?.getData('text');
|
|
93
|
+
if (this.rucInputData.integerOnly) {
|
|
94
|
+
pasteData = pasteData?.replace(/\D/g, ''); // Extract digits only
|
|
95
|
+
}
|
|
96
|
+
if (!pasteData)
|
|
97
|
+
return;
|
|
98
|
+
const pasteLength = Math.min(pasteData.length, this.rucInputData.length || 6);
|
|
99
|
+
for (let i = 0; i < pasteLength; i++) {
|
|
100
|
+
this.otpValues[i] = pasteData[i];
|
|
101
|
+
this.inputs.toArray()[i].nativeElement.value = pasteData[i];
|
|
102
|
+
}
|
|
103
|
+
// Focus last filled input or last input
|
|
104
|
+
const focusIndex = pasteLength < (this.rucInputData.length || 6) ? pasteLength : (this.rucInputData.length || 6) - 1;
|
|
105
|
+
this.inputs.toArray()[focusIndex].nativeElement.focus();
|
|
106
|
+
// Auto-submit if complete
|
|
107
|
+
if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {
|
|
108
|
+
if (this.timerId) {
|
|
109
|
+
clearInterval(this.timerId);
|
|
110
|
+
this.timerId = null;
|
|
111
|
+
}
|
|
112
|
+
this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//Check if OTP is completed
|
|
116
|
+
isOtpComplete(otpArray) {
|
|
117
|
+
return otpArray.every(val => val.trim() !== '');
|
|
118
|
+
}
|
|
119
|
+
// Disable inputs
|
|
120
|
+
disableAllInputs() {
|
|
121
|
+
this.inputs.forEach((inputRef) => {
|
|
122
|
+
inputRef.nativeElement.disabled = true;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//Clear Interval
|
|
126
|
+
ngOnDestroy() {
|
|
127
|
+
if (this.timerId) {
|
|
128
|
+
clearInterval(this.timerId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
InputOtpComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputOtpComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
133
|
+
InputOtpComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: InputOtpComponent, selector: "uxp-input-otp", inputs: { customTheme: "customTheme", rucInputData: "rucInputData" }, outputs: { rucEvent: "rucEvent" }, viewQueries: [{ propertyName: "inputs", predicate: ["otpInput"], descendants: true }], ngImport: i0, template: "<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n", styles: [".otp-container{display:flex;gap:10px;justify-content:center}.otp-input{text-align:center;font-size:18px;border:1px solid #ccc;border-radius:4px;outline:none;transition:border-color .2s;color:inherit}.otp-input:focus{border-color:#007bff;box-shadow:0 0 5px #007bff4d}.otp-input::-webkit-outer-spin-button,.otp-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.otp-input[type=number]{-moz-appearance:textfield}.small .otp-input{width:30px;height:30px;font-size:14px}.medium .otp-input{width:40px;height:40px;font-size:18px}.large .otp-input{width:50px;height:50px;font-size:22px}.otp-timer{text-align:center;font-size:.9rem;color:#d9534f;font-weight:700}.otp-timeout-message{text-align:center;color:#d9534f;font-size:.9rem;margin-top:.5rem}.otp-input.rectangle{border-radius:8px}.otp-input.circle{border-radius:50%}.otp-input.underscore{border:none;border-bottom:2px solid #ccc;border-radius:0;background-color:transparent}.otp-input.is-valid{border-color:#28a745;background-color:#e6ffed}.otp-input.is-invalid{border-color:#dc3545;background-color:#ffe6e6}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
134
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputOtpComponent, decorators: [{
|
|
135
|
+
type: Component,
|
|
136
|
+
args: [{ selector: 'uxp-input-otp', template: "<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n", styles: [".otp-container{display:flex;gap:10px;justify-content:center}.otp-input{text-align:center;font-size:18px;border:1px solid #ccc;border-radius:4px;outline:none;transition:border-color .2s;color:inherit}.otp-input:focus{border-color:#007bff;box-shadow:0 0 5px #007bff4d}.otp-input::-webkit-outer-spin-button,.otp-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.otp-input[type=number]{-moz-appearance:textfield}.small .otp-input{width:30px;height:30px;font-size:14px}.medium .otp-input{width:40px;height:40px;font-size:18px}.large .otp-input{width:50px;height:50px;font-size:22px}.otp-timer{text-align:center;font-size:.9rem;color:#d9534f;font-weight:700}.otp-timeout-message{text-align:center;color:#d9534f;font-size:.9rem;margin-top:.5rem}.otp-input.rectangle{border-radius:8px}.otp-input.circle{border-radius:50%}.otp-input.underscore{border:none;border-bottom:2px solid #ccc;border-radius:0;background-color:transparent}.otp-input.is-valid{border-color:#28a745;background-color:#e6ffed}.otp-input.is-invalid{border-color:#dc3545;background-color:#ffe6e6}\n"] }]
|
|
137
|
+
}], propDecorators: { rucEvent: [{
|
|
138
|
+
type: Output
|
|
139
|
+
}], customTheme: [{
|
|
140
|
+
type: Input
|
|
141
|
+
}], rucInputData: [{
|
|
142
|
+
type: Input
|
|
143
|
+
}], inputs: [{
|
|
144
|
+
type: ViewChildren,
|
|
145
|
+
args: ['otpInput']
|
|
146
|
+
}] } });
|
|
147
|
+
|
|
148
|
+
class RuclibInputOtpModule {
|
|
149
|
+
}
|
|
150
|
+
RuclibInputOtpModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
151
|
+
RuclibInputOtpModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, declarations: [InputOtpComponent], imports: [CommonModule, MatIconModule, FormsModule], exports: [InputOtpComponent] });
|
|
152
|
+
RuclibInputOtpModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, imports: [CommonModule, MatIconModule, FormsModule] });
|
|
153
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibInputOtpModule, decorators: [{
|
|
154
|
+
type: NgModule,
|
|
155
|
+
args: [{
|
|
156
|
+
imports: [CommonModule, MatIconModule, FormsModule],
|
|
157
|
+
declarations: [InputOtpComponent],
|
|
158
|
+
exports: [InputOtpComponent]
|
|
159
|
+
}]
|
|
160
|
+
}] });
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Generated bundle index. Do not edit.
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
export { InputOtpComponent, RuclibInputOtpModule };
|
|
167
|
+
//# sourceMappingURL=ruc-lib-input-otp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ruc-lib-input-otp.mjs","sources":["../../src/modal/constants.ts","../../src/lib/input-otp/input-otp.component.ts","../../src/lib/input-otp/input-otp.component.html","../../src/lib/ruclib-input-otp.module.ts","../../src/ruc-lib-input-otp.ts"],"sourcesContent":["export const BACKSPACE_KEY = \"Backspace\"","import {\r\n Component,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ViewChildren,\r\n ElementRef,\r\n QueryList,\r\n AfterViewInit,\r\n OnDestroy,\r\n OnInit,\r\n} from '@angular/core';\r\nimport { InputOtpConfig } from '../../modal/input-otp-config';\r\nimport { BACKSPACE_KEY } from '../../modal/constants';\r\n\r\n@Component({\r\n selector: 'uxp-input-otp',\r\n templateUrl: './input-otp.component.html',\r\n styleUrls: ['./input-otp.component.scss']\r\n})\r\nexport class InputOtpComponent implements OnInit, AfterViewInit, OnDestroy {\r\n readonly DEFAULT_OTP_LENGTH = 6;\r\n readonly TIME_LEFT = \"Time left\";\r\n readonly EXPIRED_MSG = \"OTP expired. Please request a new one.\"\r\n\r\n @Output() rucEvent = new EventEmitter<any>();\r\n @Input() customTheme?: string;\r\n\r\n @Input() rucInputData: InputOtpConfig = {};\r\n timeLeft = 0;\r\n timerId: any = null;\r\n\r\n @ViewChildren('otpInput') inputs!: QueryList<ElementRef<HTMLInputElement>>;\r\n\r\n otpValues: string[] = [];\r\n\r\n ngOnInit(): void {\r\n this.otpValues = Array(this.rucInputData.length || 6).fill('');\r\n if (this.rucInputData.timeLimit) {\r\n this.timeLeft = this.rucInputData.timeLimit;\r\n }\r\n\r\n // Set default for copyProtection\r\n if (this.rucInputData.copyProtection === undefined) {\r\n this.rucInputData.copyProtection = true;\r\n }\r\n }\r\n\r\n ngAfterViewInit(): void {\r\n // Merge input config with defaults\r\n this.otpValues = Array(this.rucInputData.length || 6).fill('');\r\n if (this.rucInputData.autoFocus) {\r\n setTimeout(() => this.inputs.first?.nativeElement.focus(), 0); // Auto-focus first input\r\n }\r\n\r\n if (this.rucInputData.timeLimit) {\r\n this.timeLeft = this.rucInputData.timeLimit;\r\n this.timerId = setInterval(() => {\r\n this.timeLeft--;\r\n if (this.timeLeft <= 0) {\r\n clearInterval(this.timerId);\r\n this.disableAllInputs();\r\n this.rucEvent.emit({ eventName: 'timeout', eventOutput: null });\r\n }\r\n }, 1000);\r\n }\r\n }\r\n// Handle OTP input\r\n handleInput(event: Event, index: number): void {\r\n const input = event.target as HTMLInputElement;\r\n const value = input.value;\r\n\r\n if (this.rucInputData.integerOnly && !/^\\d*$/.test(value)) {\r\n input.value = ''; // Allow integers only\r\n this.otpValues[index] = '';\r\n return;\r\n }\r\n\r\n this.otpValues[index] = value.slice(-1); // Take last digit\r\n input.value = this.otpValues[index];\r\n\r\n // Auto-jump to next input\r\n if (value && index < (this.rucInputData.length || 6) - 1 && this.rucInputData.autoFocus) {\r\n this.inputs.toArray()[index + 1].nativeElement.focus();\r\n }\r\n\r\n // Auto-submit on complete\r\n if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {\r\n if (this.timerId) {\r\n clearInterval(this.timerId);\r\n this.timerId = null;\r\n }\r\n this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });\r\n }\r\n }\r\n\r\n// Handle Keyboard navigation\r\n handleKeyDown(event: KeyboardEvent, index: number): void {\r\n const input = event.target as HTMLInputElement;\r\n\r\n if (event.key === BACKSPACE_KEY && !input.value && index > 0 && this.rucInputData.autoFocus) {\r\n this.inputs.toArray()[index - 1].nativeElement.focus();\r\n } else if (event.key === 'ArrowLeft' && index > 0) {\r\n event.preventDefault();\r\n this.inputs.toArray()[index - 1].nativeElement.focus();\r\n } else if (event.key === 'ArrowRight' && index < (this.rucInputData.length || 6) - 1) {\r\n event.preventDefault();\r\n this.inputs.toArray()[index + 1].nativeElement.focus();\r\n }\r\n }\r\n\r\n//Handle Paste Event\r\n handlePaste(event: ClipboardEvent): void {\r\n event.preventDefault();\r\n let pasteData = event.clipboardData?.getData('text');\r\n if (this.rucInputData.integerOnly) {\r\n pasteData = pasteData?.replace(/\\D/g, ''); // Extract digits only\r\n }\r\n if (!pasteData) return;\r\n\r\n const pasteLength = Math.min(pasteData.length, this.rucInputData.length || 6);\r\n for (let i = 0; i < pasteLength; i++) {\r\n this.otpValues[i] = pasteData[i];\r\n this.inputs.toArray()[i].nativeElement.value = pasteData[i];\r\n }\r\n\r\n // Focus last filled input or last input\r\n const focusIndex = pasteLength < (this.rucInputData.length || 6) ? pasteLength : (this.rucInputData.length || 6) - 1;\r\n this.inputs.toArray()[focusIndex].nativeElement.focus();\r\n\r\n // Auto-submit if complete\r\n if (this.rucInputData.autoSubmit && this.isOtpComplete(this.otpValues)) {\r\n if (this.timerId) {\r\n clearInterval(this.timerId);\r\n this.timerId = null;\r\n }\r\n this.rucEvent.emit({ eventName: 'completed', eventOutput: this.otpValues.join('') });\r\n }\r\n }\r\n\r\n //Check if OTP is completed\r\n private isOtpComplete(otpArray: string[]): boolean {\r\n return otpArray.every(val => val.trim() !== '');\r\n }\r\n\r\n// Disable inputs\r\n private disableAllInputs(): void {\r\n this.inputs.forEach((inputRef) => {\r\n inputRef.nativeElement.disabled = true;\r\n });\r\n }\r\n\r\n //Clear Interval\r\n ngOnDestroy(): void {\r\n if (this.timerId) {\r\n clearInterval(this.timerId);\r\n }\r\n }\r\n}\r\n","<div class=\"{{customTheme}} otp-container\" [ngClass]=\"rucInputData.size || 'medium'\" (paste)=\"handlePaste($event)\">\r\n <input *ngFor=\"let _ of [].constructor(rucInputData.length || DEFAULT_OTP_LENGTH); let i = index\"\r\n #otpInput\r\n [type]=\"rucInputData.mask ? 'password' : 'text'\"\r\n [ngClass]=\"[\r\n 'otp-input',\r\n rucInputData.templateType || 'rectangle',\r\n rucInputData.validationState === 'valid' ? 'is-valid' : '',\r\n rucInputData.validationState === 'invalid' ? 'is-invalid' : ''\r\n ]\"\r\n maxlength=\"1\"\r\n [(ngModel)]=\"otpValues[i]\"\r\n (input)=\"handleInput($event, i)\"\r\n (copy)=\"rucInputData.copyProtection && $event.preventDefault()\"\r\n (paste)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (contextmenu)=\"rucInputData.copyProtection && $event.preventDefault()\" \r\n (keydown)=\"handleKeyDown($event, i)\"\r\n [attr.inputmode]=\"rucInputData.integerOnly ? 'numeric' : 'text'\"\r\n [pattern]=\"rucInputData.integerOnly ? '[0-9]*' : '.*'\"\r\n autocomplete=\"one-time-code\">\r\n</div>\r\n<!-- Timer -->\r\n<div *ngIf=\"rucInputData.timeLimit\" class=\"otp-timer\">\r\n {{TIME_LEFT}}: {{ timeLeft }}s\r\n</div>\r\n<!-- Timeout message -->\r\n<div *ngIf=\"rucInputData.timeLimit && timeLeft === 0\" class=\"otp-timeout-message\">\r\n {{EXPIRED_MSG}}\r\n</div>\r\n\r\n","import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { MatIconModule } from '@angular/material/icon';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { InputOtpComponent } from './input-otp/input-otp.component';\r\n\r\n@NgModule({\r\n imports: [CommonModule, MatIconModule, FormsModule],\r\n declarations: [InputOtpComponent],\r\n exports : [InputOtpComponent]\r\n\r\n})\r\nexport class RuclibInputOtpModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EventEmitter, ElementRef, QueryList, AfterViewInit, OnDestroy, OnInit } from '@angular/core';
|
|
2
|
+
import { InputOtpConfig } from '../../modal/input-otp-config';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class InputOtpComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
5
|
+
readonly DEFAULT_OTP_LENGTH = 6;
|
|
6
|
+
readonly TIME_LEFT = "Time left";
|
|
7
|
+
readonly EXPIRED_MSG = "OTP expired. Please request a new one.";
|
|
8
|
+
rucEvent: EventEmitter<any>;
|
|
9
|
+
customTheme?: string;
|
|
10
|
+
rucInputData: InputOtpConfig;
|
|
11
|
+
timeLeft: number;
|
|
12
|
+
timerId: any;
|
|
13
|
+
inputs: QueryList<ElementRef<HTMLInputElement>>;
|
|
14
|
+
otpValues: string[];
|
|
15
|
+
ngOnInit(): void;
|
|
16
|
+
ngAfterViewInit(): void;
|
|
17
|
+
handleInput(event: Event, index: number): void;
|
|
18
|
+
handleKeyDown(event: KeyboardEvent, index: number): void;
|
|
19
|
+
handlePaste(event: ClipboardEvent): void;
|
|
20
|
+
private isOtpComplete;
|
|
21
|
+
private disableAllInputs;
|
|
22
|
+
ngOnDestroy(): void;
|
|
23
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<InputOtpComponent, never>;
|
|
24
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<InputOtpComponent, "uxp-input-otp", never, { "customTheme": "customTheme"; "rucInputData": "rucInputData"; }, { "rucEvent": "rucEvent"; }, never, never, false, never>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import * as i1 from "./input-otp/input-otp.component";
|
|
3
|
+
import * as i2 from "@angular/common";
|
|
4
|
+
import * as i3 from "@angular/material/icon";
|
|
5
|
+
import * as i4 from "@angular/forms";
|
|
6
|
+
export declare class RuclibInputOtpModule {
|
|
7
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RuclibInputOtpModule, never>;
|
|
8
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<RuclibInputOtpModule, [typeof i1.InputOtpComponent], [typeof i2.CommonModule, typeof i3.MatIconModule, typeof i4.FormsModule], [typeof i1.InputOtpComponent]>;
|
|
9
|
+
static ɵinj: i0.ɵɵInjectorDeclaration<RuclibInputOtpModule>;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const BACKSPACE_KEY = "Backspace";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface InputOtpConfig {
|
|
2
|
+
/** Number of OTP digits */
|
|
3
|
+
length?: number;
|
|
4
|
+
/** Mask input as password */
|
|
5
|
+
mask?: boolean;
|
|
6
|
+
/** Sizes: small, medium, large */
|
|
7
|
+
size?: 'small' | 'medium' | 'large';
|
|
8
|
+
/** Only allow integers 0-9 */
|
|
9
|
+
integerOnly?: boolean;
|
|
10
|
+
/** Autofocus and auto-jump */
|
|
11
|
+
autoFocus?: boolean;
|
|
12
|
+
autoSubmit?: boolean;
|
|
13
|
+
timeLimit?: number;
|
|
14
|
+
copyProtection?: boolean;
|
|
15
|
+
templateType?: 'rectangle' | 'circle' | 'underscore';
|
|
16
|
+
/** Validation state for visual feedback */
|
|
17
|
+
validationState?: 'valid' | 'invalid' | undefined;
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ruc-lib/input-otp",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@angular/common": "^17.0.0 || ^16.0.0 || ^15.0.0",
|
|
7
|
+
"@angular/core": "^17.0.0 || ^16.0.0 || ^15.0.0",
|
|
8
|
+
"@angular/material": "^15.2.9 || ^14.0.0 || ^13.0.0"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"tslib": "^2.3.0"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"module": "fesm2015/ruc-lib-input-otp.mjs",
|
|
18
|
+
"es2020": "fesm2020/ruc-lib-input-otp.mjs",
|
|
19
|
+
"esm2020": "esm2020/ruc-lib-input-otp.mjs",
|
|
20
|
+
"fesm2020": "fesm2020/ruc-lib-input-otp.mjs",
|
|
21
|
+
"fesm2015": "fesm2015/ruc-lib-input-otp.mjs",
|
|
22
|
+
"typings": "index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": {
|
|
25
|
+
"default": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./index.d.ts",
|
|
29
|
+
"esm2020": "./esm2020/ruc-lib-input-otp.mjs",
|
|
30
|
+
"es2020": "./fesm2020/ruc-lib-input-otp.mjs",
|
|
31
|
+
"es2015": "./fesm2015/ruc-lib-input-otp.mjs",
|
|
32
|
+
"node": "./fesm2015/ruc-lib-input-otp.mjs",
|
|
33
|
+
"default": "./fesm2020/ruc-lib-input-otp.mjs"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|