angular-otp-box
Version:
Angular otp input field component for web applications. Easy to integrate and use.
271 lines (260 loc) • 12.3 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/forms'), require('rxjs'), require('rxjs/operators'), require('@angular/common')) :
typeof define === 'function' && define.amd ? define('angular-otp-box', ['exports', '@angular/core', '@angular/forms', 'rxjs', 'rxjs/operators', '@angular/common'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['angular-otp-box'] = {}, global.ng.core, global.ng.forms, global.rxjs, global.rxjs.operators, global.ng.common));
}(this, (function (exports, core, forms, rxjs, operators, common) { 'use strict';
var KeysPipe = /** @class */ (function () {
function KeysPipe() {
}
KeysPipe.prototype.transform = function (value) {
return Object.keys(value);
};
return KeysPipe;
}());
KeysPipe.decorators = [
{ type: core.Pipe, args: [{
name: 'keys'
},] }
];
var CounterDirective = /** @class */ (function () {
function CounterDirective() {
var _this = this;
this._counterSource$ = new rxjs.Subject();
this._subscription = rxjs.Subscription.EMPTY;
this.interval = 1000;
this.value = new core.EventEmitter();
this._subscription = this._counterSource$.pipe(operators.switchMap(function (_a) {
var interval = _a.interval, count = _a.count;
return rxjs.timer(0, interval).pipe(operators.take(count), operators.tap(function () { return _this.value.emit(--count); }));
})).subscribe();
}
CounterDirective.prototype.ngOnChanges = function (changes) {
this.startTimer();
};
CounterDirective.prototype.startTimer = function () {
this._counterSource$.next({ count: this.counter, interval: this.interval });
};
CounterDirective.prototype.ngOnDestroy = function () {
this._subscription.unsubscribe();
};
return CounterDirective;
}());
CounterDirective.decorators = [
{ type: core.Directive, args: [{
selector: '[counter]'
},] }
];
CounterDirective.ctorParameters = function () { return []; };
CounterDirective.propDecorators = {
counter: [{ type: core.Input }],
value: [{ type: core.Output }]
};
var OtpInputComponent = /** @class */ (function () {
function OtpInputComponent(keysPipe) {
this.keysPipe = keysPipe;
this.setting = {
length: 4,
timer: 0,
timerType: 0
};
this.onValueChange = new core.EventEmitter();
this.inputControls = new Array(this.setting.length);
this.componentKey = Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
}
OtpInputComponent.prototype.ngOnInit = function () {
console.log(this.setting);
this.otpForm = new forms.FormGroup({});
for (var index = 0; index < this.setting.length; index++) {
this.otpForm.addControl(this.getControlName(index), new forms.FormControl());
}
};
OtpInputComponent.prototype.ngAfterViewInit = function () {
var containerItem = document.getElementById("c_" + this.componentKey);
if (containerItem) {
var ele = containerItem.getElementsByClassName('.otp-input')[0];
if (ele && ele.focus) {
ele.focus();
}
}
};
OtpInputComponent.prototype.getControlName = function (idx) {
return "ctrl_" + idx;
};
OtpInputComponent.prototype.isLeftArrow = function (e) {
return this.isKeyCode(e, 37);
};
OtpInputComponent.prototype.isRightArrow = function (e) {
return this.isKeyCode(e, 39);
};
OtpInputComponent.prototype.isBackspaceOrDelete = function (e) {
return e.key === "Backspace" || e.key === "Delete" || this.isKeyCode(e, 8) || this.isKeyCode(e, 46);
};
OtpInputComponent.prototype.isKeyCode = function (e, targetCode) {
var key = e.keyCode || e.charCode;
if (key == targetCode) {
return true;
}
return false;
};
OtpInputComponent.prototype.keyUp = function (e, inputIdx) {
var nextInputId = this.appendKey("otp_" + (inputIdx + 1));
var prevInputId = this.appendKey("otp_" + (inputIdx - 1));
if (this.isRightArrow(e)) {
this.setSelected(nextInputId);
return;
}
if (this.isLeftArrow(e)) {
this.setSelected(prevInputId);
return;
}
var isBackspace = this.isBackspaceOrDelete(e);
if (isBackspace && !e.target.value) {
this.setSelected(prevInputId);
this.rebuildValue();
return;
}
if (!e.target.value) {
return;
}
if (this.isValidEntry(e)) {
this.focusTo(nextInputId);
}
this.rebuildValue();
};
OtpInputComponent.prototype.appendKey = function (id) {
return id + "_" + this.componentKey;
};
OtpInputComponent.prototype.setSelected = function (eleId) {
this.focusTo(eleId);
var ele = document.getElementById(eleId);
if (ele && ele.setSelectionRange) {
setTimeout(function () {
ele.setSelectionRange(0, 1);
}, 0);
}
};
OtpInputComponent.prototype.isValidEntry = function (e) {
var inp = String.fromCharCode(e.keyCode);
var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
return isMobile || /[a-zA-Z0-9-_]/.test(inp) || (this.setting.allowKeyCodes && this.setting.allowKeyCodes.includes(e.keyCode)) || (e.keyCode >= 96 && e.keyCode <= 105);
};
OtpInputComponent.prototype.focusTo = function (eleId) {
var ele = document.getElementById(eleId);
if (ele) {
ele.focus();
ele.selectionStart = ele.selectionEnd = 100;
}
};
OtpInputComponent.prototype.rebuildValue = function () {
var _this = this;
var val = '';
this.keysPipe.transform(this.otpForm.controls).forEach(function (k) {
if (_this.otpForm.controls[k].value) {
val += _this.otpForm.controls[k].value;
}
});
this.onValueChange.emit(val);
};
OtpInputComponent.prototype.onCounterChange = function (e) {
this.counter = e;
if (this.counter == 0) {
this.onValueChange.emit(-1);
}
};
OtpInputComponent.prototype.ressendOtp = function () {
this.CounterDirective.first.startTimer();
this.onValueChange.emit(-2);
};
OtpInputComponent.prototype.formatSecsToMins = function (time) {
// Hours, minutes and seconds
var hrs = ~~(time / 3600);
var mins = ~~((time % 3600) / 60);
var secs = ~~time % 60;
// Output like "1:01" or "4:03:59" or "123:03:59"
var ret = "";
if (hrs > 0) {
ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
}
ret += "" + mins + ":" + (secs < 10 ? "0" : "");
ret += "" + secs;
return ret;
};
return OtpInputComponent;
}());
OtpInputComponent.decorators = [
{ type: core.Component, args: [{
selector: 'otp',
template: "<div class=\"otp-container {{setting.wrapperClass}}\" id=\"c_{{componentKey}}\" *ngIf=\"otpForm?.controls\"\n [ngStyle]=\"setting.wrapperStyles\">\n <input \n [type]=\"setting.numbersOnly ? 'tel' : 'text'\" \n numberOnly [disabledNumberOnly]=\"!setting.numbersOnly\"\n [ngStyle]=\"setting.inputStyles\" \n maxlength=\"1\" \n class=\"otp-input {{setting.inputClass}}\" \n autocomplete=\"off\"\n *ngFor=\"let item of otpForm?.controls | keys; let i = index\" \n [formControl]=\"otpForm.controls[item]\"\n id=\"otp_{{i}}_{{componentKey}}\" \n (keyup)=\"keyUp($event, i)\"\n >\n <ng-container counter [counter]=\"setting.timer\" (value)=\"onCounterChange($event)\">\n <div>\n <button class=\"btn {{setting.btnClass}}\" [disabled]=\"counter != 0\" (click)=\"ressendOtp()\">\n Resend OTP \n <span *ngIf=\"counter != 0\">\n <ng-container *ngIf=\"!setting.timerType\">\n in {{ counter }} seconds.\n </ng-container>\n <ng-container *ngIf=\"setting.timerType\">\n in {{ formatSecsToMins(counter) }} minutes.\n </ng-container>\n </span>\n </button>\n </div>\n </ng-container>\n</div>",
styles: [".otp-input{width:2em;height:2em;border-radius:4px;border:1px solid #c5c5c5;text-align:center;font-size:28px}.otp-input:focus{outline-offset:0;outline-color:#2b91e2;outline-style:auto;outline-width:5px}.otp-container .otp-input:not(:last-child){margin-right:8px}@media screen and (max-width:767px){.otp-input{font-size:24px}}@media screen and (max-width:420px){.otp-input{font-size:18px}}"]
},] }
];
OtpInputComponent.ctorParameters = function () { return [
{ type: KeysPipe }
]; };
OtpInputComponent.propDecorators = {
setting: [{ type: core.Input }],
onValueChange: [{ type: core.Output }],
CounterDirective: [{ type: core.ViewChildren, args: [CounterDirective,] }]
};
var NumberOnly = /** @class */ (function () {
function NumberOnly(_elRef, _renderer) {
this._elRef = _elRef;
this._renderer = _renderer;
}
NumberOnly.prototype.ngOnInit = function () {
if (!this.disabledNumberOnly) {
this._renderer.setAttribute(this._elRef.nativeElement, 'onkeypress', 'return (event.charCode >= 48 && event.charCode <= 57) || event.charCode == 0');
}
};
return NumberOnly;
}());
NumberOnly.decorators = [
{ type: core.Directive, args: [{
selector: '[numberOnly]'
},] }
];
NumberOnly.ctorParameters = function () { return [
{ type: core.ElementRef },
{ type: core.Renderer2 }
]; };
NumberOnly.propDecorators = {
disabledNumberOnly: [{ type: core.Input }]
};
var AngularOtpLibModule = /** @class */ (function () {
function AngularOtpLibModule() {
}
return AngularOtpLibModule;
}());
AngularOtpLibModule.decorators = [
{ type: core.NgModule, args: [{
declarations: [
OtpInputComponent,
KeysPipe,
NumberOnly,
CounterDirective
],
imports: [
common.CommonModule,
forms.FormsModule,
forms.ReactiveFormsModule
],
exports: [
OtpInputComponent
],
providers: [KeysPipe]
},] }
];
/*
* Public API Surface of angular-otp-box
*/
/**
* Generated bundle index. Do not edit.
*/
exports.AngularOtpLibModule = AngularOtpLibModule;
exports.OtpInputComponent = OtpInputComponent;
exports.ɵa = CounterDirective;
exports.ɵb = KeysPipe;
exports.ɵc = NumberOnly;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=angular-otp-box.umd.js.map