UNPKG

ngx-otp-code-input

Version:

ngx-otp-code-input is an Angular component for OTP (One-Time Password) input. This component is highly customizable, allowing for various configurations such as masking, integer-only input, autofocus, and more.

205 lines (198 loc) 14.9 kB
import * as i0 from '@angular/core'; import { Injectable, EventEmitter, Output, Input, ViewChildren, Component, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i2 from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'; import * as i3 from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatFormFieldModule } from '@angular/material/form-field'; class NgxOtpCodeInputService { constructor() { } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); class NgxOtpCodeInputComponent { constructor(renderer, cdr) { this.renderer = renderer; this.cdr = cdr; this.disabled = false; this.readonly = false; this.autofocus = true; // * true by default this.mask = false; // * Essentially used to hide the value, similar to how it is done for password inputs. this.integerOnly = false; // * The input will only accept integer values; any other characters will be ignored. this.tabIndex = false; this.animationConfig = { type: 'fade', duration: '0.3s' }; this.status = null; this.successIcon = 'check_circle'; this.failureIcon = 'cancel'; this.length = 4; // or 6 this.inputClass = ''; this.variant = 'outline'; this.regex = ''; // Emitters this.otpChange = new EventEmitter(); this.otpComplete = new EventEmitter(); this.otpControls = new Array(4).fill(null); } ngOnChanges(changes) { if (changes['animationConfig']) { this.renderer.setStyle(document.documentElement, '--animation-duration', this.animationConfig.duration); } } ngOnInit() { this.otpControls = new Array(this.length).fill(null); } ngAfterViewInit() { if (this.autofocus) { this.otpInput.first.nativeElement.focus(); this.cdr.detectChanges(); } } getAnimationClass() { return `animate-${this.animationConfig.type}`; } onInputChange(event) { const input = event.target; const index = Number(input.getAttribute('data-index')); // * If integerOnly is true then if user try to enetered non integer value make the field empty if (this.integerOnly) { input.value = input.value.replace(/\D/g, ''); } if (input.value.length > 0) { if (index < this.otpControls.length - 1) { this.otpInput.toArray()[index + 1].nativeElement.focus(); } } this.updateOtpValue(); } onKeyDown(event) { const input = event.target; const index = Number(input.getAttribute('data-index')); // Prevent non-integer keys if integerOnly is true // if (this.integerOnly && !/^[0-9]$/.test(event.key) && event.key !== 'Backspace') { // event.preventDefault(); // } if (event.key === 'Backspace' && !input.value && index > 0) { this.otpInput.toArray()[index - 1].nativeElement.focus(); } } onPaste(event) { let pasteData = event.clipboardData?.getData('text').trim() || ''; console.log('pasteData', pasteData); // * If integerOnly is true, filter out non-numeric characters if (this.integerOnly && pasteData) { pasteData = pasteData.replace(/\D/g, ''); } if (pasteData && pasteData.length === this.otpControls.length) { this.otpControls.forEach((_, index) => { const input = this.otpInput.toArray()[index].nativeElement; input.value = pasteData[index]; }); this.updateOtpValue(); // * Focus on the next input after the last filled input const lastIndex = this.otpControls.length - 1; this.otpInput.toArray()[lastIndex].nativeElement.focus(); } event.preventDefault(); } updateOtpValue() { const otpValue = this.otpInput.toArray().map(input => input.nativeElement.value).join(''); this.otpChange.emit(otpValue); // Emit the OTP value // * Check if the OTP is complete if (otpValue.length === this.otpControls.length) { this.otpComplete.emit(otpValue); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: NgxOtpCodeInputComponent, isStandalone: false, selector: "ngx-otp-code-input", inputs: { disabled: "disabled", readonly: "readonly", autofocus: "autofocus", mask: "mask", integerOnly: "integerOnly", tabIndex: "tabIndex", animationConfig: "animationConfig", status: "status", successIcon: "successIcon", failureIcon: "failureIcon", length: "length", inputClass: "inputClass", variant: "variant", regex: "regex" }, outputs: { otpChange: "otpChange", otpComplete: "otpComplete" }, viewQueries: [{ propertyName: "otpInput", predicate: ["otpInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"otp-container\">\r\n <ng-container *ngFor=\"let otpValue of otpControls; let i = index\">\r\n <mat-form-field [appearance]=\"variant\" class=\"otp-input\" [id]=\"'otp_input_' + i\" [ngClass]=\"{\r\n 'otp-success': status === 'success',\r\n 'otp-failed': status === 'failed'\r\n }\">\r\n <input\r\n matInput\r\n [type]=\"mask ? 'password' : 'text'\"\r\n [class]=\"inputClass\"\r\n [attr.tabindex]=\"tabIndex ? i : -1\"\r\n [readonly]=\"readonly ? true : null\"\r\n [disabled]=\"disabled\"\r\n [attr.data-index]=\"i\"\r\n (input)=\"onInputChange($event)\"\r\n [ngClass]=\"getAnimationClass()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (paste)=\"onPaste($event)\"\r\n maxlength=\"1\"\r\n #otpInput\r\n [id]=\"'my_input_' + i\"\r\n [pattern]=\"integerOnly ? '\\\\d*' : ''\"\r\n />\r\n <!-- Add status indicator -->\r\n <mat-icon *ngIf=\"status === 'success'\" class=\"status-icon success\">check_circle</mat-icon>\r\n <mat-icon *ngIf=\"status === 'failed'\" class=\"status-icon failed\">cancel</mat-icon>\r\n </mat-form-field>\r\n </ng-container>\r\n</div>\r\n ", styles: [".otp-container{display:flex;gap:8px}.otp-input{width:40px;height:50px;text-align:center}.otp-input.otp-success{border-color:green}.otp-input.otp-success input{color:green;border-bottom:2px solid green}.otp-input.otp-failed{border-color:red}.otp-input.otp-failed input{color:red;border-bottom:2px solid red}::ng-deep .status-icon.success{color:green!important}::ng-deep .status-icon.failed{color:red!important}input{text-align:center;font-size:18px}:host ::ng-deep .mat-mdc-text-field-wrapper{padding:0 .5rem}:host ::ng-deep .mat-mdc-form-field-infix{font-size:22px}.otp-container .animate-fade{transition:opacity var(--animation-duration);opacity:.5}.otp-container .animate-slide{transition:transform var(--animation-duration);transform:translate(0)}.otp-container .animate-zoom{transition:transform var(--animation-duration);transform:scale(1)}:host{--animation-duration: 1.5s}\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.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-otp-code-input', standalone: false, template: "<div class=\"otp-container\">\r\n <ng-container *ngFor=\"let otpValue of otpControls; let i = index\">\r\n <mat-form-field [appearance]=\"variant\" class=\"otp-input\" [id]=\"'otp_input_' + i\" [ngClass]=\"{\r\n 'otp-success': status === 'success',\r\n 'otp-failed': status === 'failed'\r\n }\">\r\n <input\r\n matInput\r\n [type]=\"mask ? 'password' : 'text'\"\r\n [class]=\"inputClass\"\r\n [attr.tabindex]=\"tabIndex ? i : -1\"\r\n [readonly]=\"readonly ? true : null\"\r\n [disabled]=\"disabled\"\r\n [attr.data-index]=\"i\"\r\n (input)=\"onInputChange($event)\"\r\n [ngClass]=\"getAnimationClass()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (paste)=\"onPaste($event)\"\r\n maxlength=\"1\"\r\n #otpInput\r\n [id]=\"'my_input_' + i\"\r\n [pattern]=\"integerOnly ? '\\\\d*' : ''\"\r\n />\r\n <!-- Add status indicator -->\r\n <mat-icon *ngIf=\"status === 'success'\" class=\"status-icon success\">check_circle</mat-icon>\r\n <mat-icon *ngIf=\"status === 'failed'\" class=\"status-icon failed\">cancel</mat-icon>\r\n </mat-form-field>\r\n </ng-container>\r\n</div>\r\n ", styles: [".otp-container{display:flex;gap:8px}.otp-input{width:40px;height:50px;text-align:center}.otp-input.otp-success{border-color:green}.otp-input.otp-success input{color:green;border-bottom:2px solid green}.otp-input.otp-failed{border-color:red}.otp-input.otp-failed input{color:red;border-bottom:2px solid red}::ng-deep .status-icon.success{color:green!important}::ng-deep .status-icon.failed{color:red!important}input{text-align:center;font-size:18px}:host ::ng-deep .mat-mdc-text-field-wrapper{padding:0 .5rem}:host ::ng-deep .mat-mdc-form-field-infix{font-size:22px}.otp-container .animate-fade{transition:opacity var(--animation-duration);opacity:.5}.otp-container .animate-slide{transition:transform var(--animation-duration);transform:translate(0)}.otp-container .animate-zoom{transition:transform var(--animation-duration);transform:scale(1)}:host{--animation-duration: 1.5s}\n"] }] }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }], propDecorators: { otpInput: [{ type: ViewChildren, args: ['otpInput'] }], disabled: [{ type: Input }], readonly: [{ type: Input }], autofocus: [{ type: Input }], mask: [{ type: Input }], integerOnly: [{ type: Input }], tabIndex: [{ type: Input }], animationConfig: [{ type: Input }], status: [{ type: Input }], successIcon: [{ type: Input }], failureIcon: [{ type: Input }], length: [{ type: Input }], inputClass: [{ type: Input }], variant: [{ type: Input }], regex: [{ type: Input }], otpChange: [{ type: Output }], otpComplete: [{ type: Output }] } }); class NgxOtpCodeInputModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputModule, declarations: [NgxOtpCodeInputComponent], imports: [CommonModule, BrowserAnimationsModule, BrowserModule, MatInputModule, MatFormFieldModule, MatIconModule], exports: [NgxOtpCodeInputComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputModule, imports: [CommonModule, BrowserAnimationsModule, BrowserModule, MatInputModule, MatFormFieldModule, MatIconModule] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxOtpCodeInputModule, decorators: [{ type: NgModule, args: [{ declarations: [ NgxOtpCodeInputComponent ], imports: [ CommonModule, BrowserAnimationsModule, BrowserModule, MatInputModule, MatFormFieldModule, MatIconModule ], exports: [ NgxOtpCodeInputComponent ] }] }] }); /* * Public API Surface of ngx-otp-code-input */ /** * Generated bundle index. Do not edit. */ export { NgxOtpCodeInputComponent, NgxOtpCodeInputModule, NgxOtpCodeInputService }; //# sourceMappingURL=ngx-otp-code-input.mjs.map