UNPKG

@rashidpathiyil/otp-input

Version:

A JavaScript library for handling OTP inputs.

146 lines (145 loc) 6.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class OTP { constructor(elementOrSelector, updateToInput = null, resultRef = null) { this.updateTo = null; // set default options this.emptyChar = ' '; this._resultRef = resultRef; if (typeof elementOrSelector === 'string') this.container = document.querySelector(elementOrSelector); else if (elementOrSelector instanceof HTMLElement) this.container = elementOrSelector; else throw new Error('Invalid argument for constructor.'); if (updateToInput) { if (typeof updateToInput === 'string') this.updateTo = document.querySelector(updateToInput) || null; else if (updateToInput instanceof HTMLInputElement) this.updateTo = updateToInput; else this.updateTo = null; } this.inputs = Array.from(this.container.querySelectorAll('input[type=text], input[type=number], input[type=password]')); const inputCount = this.inputs.length; for (let i = 0; i < inputCount; i++) { const input = this.inputs[i]; input.addEventListener('input', () => { // if not number, restore value if (Number.isNaN(Number(input.value))) { input.value = input.dataset.otpInputRestore || ''; return this._updateValue(); } // if a character is removed, do nothing and save if (input.value.length === 0) return this._saveInputValue(i); // if single character, save the value and go to next input (if any) if (input.value.length === 1) { this._saveInputValue(i); this._updateValue(); if (i + 1 < inputCount) this.inputs[i + 1].focus(); return; } // more multiple character entered (eg. pasted), // and it's the last input of the row, // truncate to single character and save if (i === inputCount - 1) return this._setInputValue(i, input.value); // otherwise, put each character to each of the next input const chars = input.value.split(''); for (let pos = 0; pos < chars.length; pos++) { // if length exceeded the number of inputs, stop if (pos + i >= inputCount) break; // paste value and save this._setInputValue(pos + i, chars[pos]); } // focus the input next to the last pasted character const focus_index = Math.min(inputCount - 1, i + chars.length); this.inputs[focus_index].focus(); }); input.addEventListener('keydown', (e) => { // backspace button if (e.key === 'Backspace' && input.value === '' && i !== 0) { this._setInputValue(i - 1, ''); this.inputs[i - 1].focus(); return; } // delete button if (e.key === 'Delete' && i !== inputCount - 1) { const selectionStart = input.selectionStart || 0; for (let pos = i + selectionStart; pos < inputCount - 1; pos++) this._setInputValue(pos, this.inputs[pos + 1].value); this._setInputValue(inputCount - 1, ''); // restore caret if (input.selectionStart) input.selectionStart = selectionStart; e.preventDefault(); return; } // left button if (e.key === 'ArrowLeft' && (input.selectionStart == null || input.selectionStart === 0)) { if (i > 0) { e.preventDefault(); this.inputs[i - 1].focus(); this.inputs[i - 1].select(); } return; } // right button if (e.key === 'ArrowRight' && (input.selectionStart == null || input.selectionEnd === input.value.length)) { if (i + 1 < inputCount) { e.preventDefault(); this.inputs[i + 1].focus(); this.inputs[i + 1].select(); } } }); } } setEmptyChar(char) { this.emptyChar = char; } getValue() { let value = ''; this.inputs.forEach((input) => { value += (input.value === '') ? this.emptyChar : input.value; }); return value; } ; setValue(value) { if (Number.isNaN(value)) { console.error('Please enter an integer value.'); return; } const stringValue = `${value}`; const chars = stringValue.split(''); for (let i = 0; i < this.inputs.length; i++) this._setInputValue(i, chars[i] || ''); } _setInputValue(index, value) { if (Number.isNaN(Number(value))) return console.error('Please enter an integer value.'); if (!this.inputs[index]) return console.error('Index not found.'); this.inputs[index].value = String(value).substring(0, 1); this._saveInputValue(index); this._updateValue(); } _saveInputValue(index, value) { if (!this.inputs[index]) return console.error('Index not found.'); this.inputs[index].dataset.otpInputRestore = value || this.inputs[index].value; } _updateValue() { const value = this.getValue(); if (this.updateTo) this.updateTo.value = value; if (this._resultRef) this._resultRef.value = value; } ; } exports.default = OTP;