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
JavaScript
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