UNPKG

ngx-otp-input

Version:

One Time Password input library for Angular (14+)

348 lines (337 loc) 18.9 kB
import * as i0 from '@angular/core'; import { EventEmitter, Directive, ContentChildren, Input, Output, HostListener, ContentChild, Component } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i2 from '@angular/forms'; import { FormArray, FormControl, Validators, ReactiveFormsModule } from '@angular/forms'; class PasteDirective { constructor() { this.handlePaste = new EventEmitter(); } onPaste(event) { event.preventDefault(); const clipboardData = event.clipboardData?.getData('text'); if (clipboardData && this.regexp.test(clipboardData)) { const values = clipboardData.split(''); this.inputs.forEach((input, index) => { if (values[index]) { input.nativeElement.value = values[index]; } }); this.handlePaste.emit(values); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PasteDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: PasteDirective, isStandalone: true, selector: "[ngxOtpPaste]", inputs: { regexp: "regexp" }, outputs: { handlePaste: "handlePaste" }, host: { listeners: { "paste": "onPaste($event)" } }, queries: [{ propertyName: "inputs", predicate: ["otpInputElement"], descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PasteDirective, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[ngxOtpPaste]', }] }], propDecorators: { inputs: [{ type: ContentChildren, args: ['otpInputElement', { descendants: true }] }], regexp: [{ type: Input }], handlePaste: [{ type: Output }], onPaste: [{ type: HostListener, args: ['paste', ['$event']] }] } }); class AutoFocusDirective { ngAfterContentInit() { if (this.ngxAutoFocus && this.firstInput) { this.firstInput.nativeElement.focus(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AutoFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: AutoFocusDirective, isStandalone: true, selector: "[ngxAutoFocus]", inputs: { ngxAutoFocus: "ngxAutoFocus" }, queries: [{ propertyName: "firstInput", first: true, predicate: ["otpInputElement"], descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AutoFocusDirective, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[ngxAutoFocus]', }] }], propDecorators: { firstInput: [{ type: ContentChild, args: ['otpInputElement', { static: false }] }], ngxAutoFocus: [{ type: Input }] } }); class InputNavigationsDirective { constructor() { this.inputsArray = []; this.valueChange = new EventEmitter(); } ngAfterContentInit() { this.inputsArray = this.inputs.toArray(); } findInputIndex(target) { return this.inputsArray.findIndex((input) => input.nativeElement === target); } setFocus(index) { if (index >= 0 && index < this.inputs.length) { this.inputsArray[index].nativeElement.focus(); } } onArrowLeft(event) { const index = this.findInputIndex(event.target); if (index > 0) { this.setFocus(index - 1); } } onArrowRight(event) { const index = this.findInputIndex(event.target); if (index < this.inputs.length - 1) { this.setFocus(index + 1); } } onBackspace(event) { const index = this.findInputIndex(event.target); if (index >= 0) { this.valueChange.emit([index, '']); this.setFocus(index - 1); event.preventDefault(); } } onKeyUp(event) { const index = this.findInputIndex(event.target); if (event.target.value?.match(this.regexp)) { this.valueChange.emit([index, event.target.value]); this.setFocus(index + 1); } else { this.inputsArray[index].nativeElement.value = ''; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: InputNavigationsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: InputNavigationsDirective, isStandalone: true, selector: "[ngxInputNavigations]", inputs: { regexp: "regexp" }, outputs: { valueChange: "valueChange" }, host: { listeners: { "keydown.arrowLeft": "onArrowLeft($event)", "keydown.arrowRight": "onArrowRight($event)", "keydown.backspace": "onBackspace($event)", "input": "onKeyUp($event)" } }, queries: [{ propertyName: "inputs", predicate: ["otpInputElement"], descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: InputNavigationsDirective, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[ngxInputNavigations]', }] }], propDecorators: { inputs: [{ type: ContentChildren, args: ['otpInputElement', { descendants: true }] }], regexp: [{ type: Input }], valueChange: [{ type: Output }], onArrowLeft: [{ type: HostListener, args: ['keydown.arrowLeft', ['$event']] }], onArrowRight: [{ type: HostListener, args: ['keydown.arrowRight', ['$event']] }], onBackspace: [{ type: HostListener, args: ['keydown.backspace', ['$event']] }], onKeyUp: [{ type: HostListener, args: ['input', ['$event']] }] } }); class AutoBlurDirective { constructor() { this.inputHTMLElements = []; } ngOnChanges(changes) { if (this.ngxAutoBlur && this.inputHTMLElements.length > 0 && changes['isFormValid'].currentValue) { this.inputHTMLElements.forEach((input) => { input.blur(); }); } } ngAfterContentInit() { this.inputs.forEach((input) => { this.inputHTMLElements.push(input.nativeElement); }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AutoBlurDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: AutoBlurDirective, isStandalone: true, selector: "[ngxAutoBlur]", inputs: { ngxAutoBlur: "ngxAutoBlur", isFormValid: "isFormValid" }, queries: [{ propertyName: "inputs", predicate: ["otpInputElement"], descendants: true }], usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AutoBlurDirective, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[ngxAutoBlur]', }] }], propDecorators: { inputs: [{ type: ContentChildren, args: ['otpInputElement', { descendants: true }] }], ngxAutoBlur: [{ type: Input }], isFormValid: [{ type: Input }] } }); class AriaLabelsDirective { ngAfterContentInit() { this.setAriaLabelsAttrs(); } getDefaultAriaLabelText(index) { return `One Time Password Input Number ${index + 1}`; } setAriaLabelsAttrs() { this.inputs.forEach((input, index) => { input.nativeElement.setAttribute('aria-label', this.ngxOtpAriaLabels[index] ?? this.getDefaultAriaLabelText(index)); }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AriaLabelsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: AriaLabelsDirective, isStandalone: true, selector: "[ngxOtpAriaLabels]", inputs: { ngxOtpAriaLabels: "ngxOtpAriaLabels" }, queries: [{ propertyName: "inputs", predicate: ["otpInputElement"], descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AriaLabelsDirective, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[ngxOtpAriaLabels]', }] }], propDecorators: { inputs: [{ type: ContentChildren, args: ['otpInputElement', { descendants: true }] }], ngxOtpAriaLabels: [{ type: Input }] } }); const defaultOptions = { otpLength: 6, autoFocus: true, autoBlur: true, hideInputValues: false, regexp: /^[0-9]+$/, showBlinkingCursor: true, ariaLabels: [], inputMode: 'numeric', }; var NgxOtpStatus; (function (NgxOtpStatus) { NgxOtpStatus["SUCCESS"] = "success"; NgxOtpStatus["FAILED"] = "failed"; })(NgxOtpStatus || (NgxOtpStatus = {})); class NgxOtpInputComponent { constructor() { this.ngxOtpOptions = defaultOptions; this.disabled = false; this.otpChange = new EventEmitter(); this.otpComplete = new EventEmitter(); } set options(customOptions) { this.ngxOtpOptions = { ...defaultOptions, ...customOptions }; } // For testing purposes get ngxOtpOptionsInUse() { return this.ngxOtpOptions; } get inputType() { return this.ngxOtpOptions.hideInputValues ? 'password' : 'text'; } get isOTPSuccess() { return this.status === NgxOtpStatus.SUCCESS; } get isOTPFailed() { return this.status === NgxOtpStatus.FAILED; } ngOnInit() { this.initOtpInputArray(); } ngOnChanges(changes) { const otpChange = changes['otp']; if (otpChange?.currentValue) { if (!otpChange.firstChange) { this.setInitialOtp(otpChange.currentValue); } else { this.ngxOtpOptions.autoFocus = false; } } } initOtpInputArray() { this.ngxOtpInputArray = new FormArray(Array.from({ length: this.ngxOtpOptions.otpLength }, () => new FormControl('', Validators.required))); if (this.otp) { this.setInitialOtp(this.otp); } } setInitialOtp(otp) { if (this.ngxOtpOptions.regexp.test(otp)) { const otpValueArray = otp.split(''); otpValueArray.forEach((value, index) => { this.ngxOtpInputArray.controls[index].setValue(value ?? ''); }); this.emitOtpValueChange(); if (otpValueArray.length !== this.ngxOtpOptions.otpLength) { console.warn('OTP length does not match the provided otpLength option!'); } } else { throw new Error('Invalid OTP provided for the component <ngx-otp-input>'); } } handleInputChanges($event) { const [index, value] = $event; this.ngxOtpInputArray.controls[index].setValue(value); this.emitOtpValueChange(); } handlePasteChange($event) { if ($event.length === this.ngxOtpOptions.otpLength) { this.ngxOtpInputArray.setValue($event); } else { $event.map((value, index) => { this.ngxOtpInputArray.controls[index]?.setValue?.(value); }); } this.emitOtpValueChange(); } emitOtpValueChange() { this.otpChange.emit(this.ngxOtpInputArray.value); if (this.ngxOtpInputArray.valid) { this.otpComplete.emit(this.ngxOtpInputArray.value.join('')); } } isInputFilled(index) { return !!this.ngxOtpInputArray.controls[index].value; } reset() { this.ngxOtpInputArray.reset(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: NgxOtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: NgxOtpInputComponent, isStandalone: true, selector: "ngx-otp-input", inputs: { options: "options", status: "status", disabled: "disabled", otp: "otp" }, outputs: { otpChange: "otpChange", otpComplete: "otpComplete" }, usesOnChanges: true, ngImport: i0, template: "<form\n ngxOtpPaste\n ngxInputNavigations\n [regexp]=\"ngxOtpOptions.regexp!\"\n [isFormValid]=\"ngxOtpInputArray.valid\"\n [ngxAutoFocus]=\"ngxOtpOptions.autoFocus!\"\n [ngxAutoBlur]=\"ngxOtpOptions.autoBlur!\"\n [ngxOtpAriaLabels]=\"ngxOtpOptions.ariaLabels!\"\n [ngClass]=\"{\n 'ngx-blinking-cursor': ngxOtpOptions.showBlinkingCursor\n }\"\n (valueChange)=\"handleInputChanges($event)\"\n (handlePaste)=\"handlePasteChange($event)\"\n class=\"ngx-otp-input-form\"\n data-testid=\"ngx-otp-input-form\"\n>\n <input\n *ngFor=\"let control of ngxOtpInputArray.controls; let i = index\"\n #otpInputElement\n [value]=\"control.value\"\n [type]=\"inputType\"\n [inputMode]=\"ngxOtpOptions.inputMode\"\n [disabled]=\"disabled\"\n [ngClass]=\"{\n 'ngx-otp-input-disabled': disabled,\n 'ngx-otp-input-filled': isInputFilled(i),\n 'ngx-otp-input-success': isOTPSuccess,\n 'ngx-otp-input-failed': isOTPFailed\n }\"\n class=\"ngx-otp-input-box\"\n maxlength=\"1\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n data-testid=\"ngx-otp-input-box\"\n />\n</form>\n", styles: [".ngx-otp-input-form{display:inline-flex;gap:.5rem;caret-color:transparent}.ngx-blinking-cursor{caret-color:initial}.ngx-otp-input-box{width:30px;height:35px;padding:.5rem;font-size:1.5rem;text-align:center;border:1px solid #c4c4c4;border-radius:.5rem;outline:none}.ngx-otp-input-box:focus{border-color:#007bff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: PasteDirective, selector: "[ngxOtpPaste]", inputs: ["regexp"], outputs: ["handlePaste"] }, { kind: "directive", type: AutoFocusDirective, selector: "[ngxAutoFocus]", inputs: ["ngxAutoFocus"] }, { kind: "directive", type: InputNavigationsDirective, selector: "[ngxInputNavigations]", inputs: ["regexp"], outputs: ["valueChange"] }, { kind: "directive", type: AutoBlurDirective, selector: "[ngxAutoBlur]", inputs: ["ngxAutoBlur", "isFormValid"] }, { kind: "directive", type: AriaLabelsDirective, selector: "[ngxOtpAriaLabels]", inputs: ["ngxOtpAriaLabels"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: NgxOtpInputComponent, decorators: [{ type: Component, args: [{ standalone: true, imports: [ CommonModule, ReactiveFormsModule, PasteDirective, AutoFocusDirective, InputNavigationsDirective, AutoBlurDirective, AriaLabelsDirective, ], selector: 'ngx-otp-input', template: "<form\n ngxOtpPaste\n ngxInputNavigations\n [regexp]=\"ngxOtpOptions.regexp!\"\n [isFormValid]=\"ngxOtpInputArray.valid\"\n [ngxAutoFocus]=\"ngxOtpOptions.autoFocus!\"\n [ngxAutoBlur]=\"ngxOtpOptions.autoBlur!\"\n [ngxOtpAriaLabels]=\"ngxOtpOptions.ariaLabels!\"\n [ngClass]=\"{\n 'ngx-blinking-cursor': ngxOtpOptions.showBlinkingCursor\n }\"\n (valueChange)=\"handleInputChanges($event)\"\n (handlePaste)=\"handlePasteChange($event)\"\n class=\"ngx-otp-input-form\"\n data-testid=\"ngx-otp-input-form\"\n>\n <input\n *ngFor=\"let control of ngxOtpInputArray.controls; let i = index\"\n #otpInputElement\n [value]=\"control.value\"\n [type]=\"inputType\"\n [inputMode]=\"ngxOtpOptions.inputMode\"\n [disabled]=\"disabled\"\n [ngClass]=\"{\n 'ngx-otp-input-disabled': disabled,\n 'ngx-otp-input-filled': isInputFilled(i),\n 'ngx-otp-input-success': isOTPSuccess,\n 'ngx-otp-input-failed': isOTPFailed\n }\"\n class=\"ngx-otp-input-box\"\n maxlength=\"1\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n data-testid=\"ngx-otp-input-box\"\n />\n</form>\n", styles: [".ngx-otp-input-form{display:inline-flex;gap:.5rem;caret-color:transparent}.ngx-blinking-cursor{caret-color:initial}.ngx-otp-input-box{width:30px;height:35px;padding:.5rem;font-size:1.5rem;text-align:center;border:1px solid #c4c4c4;border-radius:.5rem;outline:none}.ngx-otp-input-box:focus{border-color:#007bff}\n"] }] }], propDecorators: { options: [{ type: Input }], status: [{ type: Input }], disabled: [{ type: Input }], otp: [{ type: Input }], otpChange: [{ type: Output }], otpComplete: [{ type: Output }] } }); /* * Public API Surface of ngx-otp-input */ /** * Generated bundle index. Do not edit. */ export { NgxOtpInputComponent, NgxOtpStatus }; //# sourceMappingURL=ngx-otp-input.mjs.map