ngx-otp-pin-input
Version:
An Angular library for OTP input fields, providing a customizable and user-friendly way to handle one-time passwords.
210 lines (203 loc) • 8.82 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, QueryList, forwardRef, ViewChildren, Output, Input, Component, NgModule } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
const DEFAULT_CONFIG = {
length: 4,
className: 'input',
style: {
width: 40,
height: 40,
fontSize: 26,
spacing: 12,
borderWidth: 2,
borderRadius: 4,
borderColor: '#BDBDBD',
borderFocusColor: '#3700B3'
}
};
class OtpInputComponent {
constructor() {
this.config = {
length: 4,
className: 'input',
style: {
width: 40,
height: 40,
fontSize: 26,
spacing: 12,
borderWidth: 2,
borderRadius: 4,
borderColor: '#BDBDBD',
borderFocusColor: '#3700B3'
}
};
this.disabled = false;
this.onOtpChange = new EventEmitter();
this.defaultConfig = DEFAULT_CONFIG;
this.controls = Array.from({ length: this.config.length || this.defaultConfig.length }, () => '');
this.currentIndex = 0;
this.otpInputs = new QueryList();
this.onChange = () => { };
this.onTouched = () => { };
this.value = '';
}
ngOnChanges(changes) {
if (changes['config']) {
this.controls = Array.from({ length: this.config.length || this.defaultConfig.length }, () => '');
this.config = { ...this.defaultConfig, ...this.config };
}
}
writeValue(value) {
this.value = value?.toString() || '';
this.otpInputs.forEach((input, index) => {
input.nativeElement.value = this.value[index] || '';
});
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
onInput(event) {
if (this.disabled)
return;
const target = event.target;
const val = target.value;
if (isNaN(Number(val))) {
target.value = '';
return;
}
if (val !== '') {
const next = target.nextElementSibling;
if (next) {
next.focus();
}
this.emitOtpChange();
}
}
onKeyup(event) {
if (this.disabled)
return;
const target = event.target;
const key = event.key.toLowerCase();
if (key === 'backspace' || key === 'delete') {
target.value = '';
const prev = target.previousElementSibling;
if (prev) {
prev.focus();
}
this.emitOtpChange();
return;
}
else if (key === 'arrowleft') {
const prev = target.previousElementSibling;
if (prev) {
prev.focus();
}
return;
}
else if (key === 'arrowright') {
const next = target.nextElementSibling;
if (next) {
next.focus();
}
return;
}
}
onPaste(event) {
if (this.disabled)
return;
const clipboardData = event.clipboardData;
if (!clipboardData)
return;
const pastedData = clipboardData.getData('text');
if (!/^\d+$/.test(pastedData)) {
event.preventDefault();
return;
}
this.otpInputs.forEach((input, index) => {
if (index < pastedData.length) {
input.nativeElement.value = pastedData[index];
}
else {
input.nativeElement.value = '';
}
input.nativeElement.dispatchEvent(new Event('input'));
});
this.emitOtpChange();
}
inputStyle(index) {
return {
'width': `${this.config?.style?.width ?? this.defaultConfig.style.width}px`,
'height': `${this.config?.style?.height ?? this.defaultConfig.style.height}px`,
'font-size': `${this.config?.style?.fontSize ?? this.defaultConfig.style.fontSize}px`,
'border-width': `${this.config?.style?.borderWidth ?? this.defaultConfig.style.borderWidth}px`,
'border-radius': `${this.config?.style?.borderRadius ?? this.defaultConfig.style.borderRadius}px`,
'border-color': !this.disabled && this.currentIndex == index
? (this.config?.style?.borderFocusColor ?? this.defaultConfig.style.borderFocusColor)
: (this.config?.style?.borderColor ?? this.defaultConfig.style.borderColor),
};
}
emitOtpChange() {
if (this.disabled)
return;
let otp = '';
this.otpInputs.forEach((input, index) => {
otp += input.nativeElement.value;
});
this.value = otp;
this.onOtpChange.emit(otp);
this.onChange(otp);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: OtpInputComponent, selector: "ngx-otp-input", inputs: { config: "config", disabled: "disabled" }, outputs: { onOtpChange: "onOtpChange" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OtpInputComponent),
multi: true
}
], viewQueries: [{ propertyName: "otpInputs", predicate: ["otpInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"inputs\" [style.gap.px]=\"config.style?.spacing || defaultConfig.style.spacing\">\r\n @for (input of controls;let i = $index; track i) {\r\n <input (input)=\"onInput($event)\" (keyup)=\"onKeyup($event)\" (paste)=\"onPaste($event)\" (focus)=\"currentIndex = i\"\r\n type=\"text\" inputmode=\"numeric\" maxlength=\"1\" class=\"input {{config.className}}\" [style]=\"inputStyle(i)\"\r\n [disabled]=\"disabled\" #otpInput />\r\n }\r\n</div>", styles: [".inputs{display:inline-flex}.input{border:none;border-style:solid;text-align:center;outline:none}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OtpInputComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-otp-input', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OtpInputComponent),
multi: true
}
], template: "<div class=\"inputs\" [style.gap.px]=\"config.style?.spacing || defaultConfig.style.spacing\">\r\n @for (input of controls;let i = $index; track i) {\r\n <input (input)=\"onInput($event)\" (keyup)=\"onKeyup($event)\" (paste)=\"onPaste($event)\" (focus)=\"currentIndex = i\"\r\n type=\"text\" inputmode=\"numeric\" maxlength=\"1\" class=\"input {{config.className}}\" [style]=\"inputStyle(i)\"\r\n [disabled]=\"disabled\" #otpInput />\r\n }\r\n</div>", styles: [".inputs{display:inline-flex}.input{border:none;border-style:solid;text-align:center;outline:none}\n"] }]
}], propDecorators: { config: [{
type: Input
}], disabled: [{
type: Input
}], onOtpChange: [{
type: Output
}], otpInputs: [{
type: ViewChildren,
args: ['otpInput']
}] } });
class NgxOtpInput {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NgxOtpInput, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: NgxOtpInput, declarations: [OtpInputComponent], exports: [OtpInputComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NgxOtpInput }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NgxOtpInput, decorators: [{
type: NgModule,
args: [{
declarations: [OtpInputComponent],
exports: [OtpInputComponent]
}]
}] });
/*
* Public API Surface of otp-input
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxOtpInput, OtpInputComponent };
//# sourceMappingURL=ngx-otp-pin-input.mjs.map