Created
December 22, 2022 08:08
-
-
Save r8928/b2bcff51668df67cca5cb26d3d63fb32 to your computer and use it in GitHub Desktop.
Webcomponent to take OTP code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
class OtpInput extends HTMLElement { | |
static formAssociated = true; | |
inputs = []; | |
element; | |
otp_length = 6; | |
value_ = ""; | |
constructor() { | |
super(); | |
this.internals = this.attachInternals(); | |
const shadowRoot = this.attachShadow({ | |
mode: "open", | |
}); | |
shadowRoot.innerHTML = ` | |
<style> | |
div { | |
display: flex; | |
justify-content: center; | |
gap: 10px; | |
} | |
.otp-field { | |
width: 40px; | |
height: 40px; | |
line-height: 40px; | |
text-align: center; | |
vertical-align: middle; | |
border: 1px solid #eee | |
} | |
.otp-field:focus { | |
outline: 2px solid black; | |
border-radius: 5px; | |
} | |
</style> | |
<div></div>`; | |
} | |
connectedCallback() { | |
if (Number(this.getAttribute("otp-length")) > 0) { | |
this.otp_length = Number(this.getAttribute("otp-length")); | |
} | |
this.element = this.shadowRoot.querySelector(`div`); | |
for (let i = 0; i < this.otp_length; i++) { | |
let inputField = document.createElement("input"); | |
inputField.id = "otp-field" + i; | |
inputField.setAttribute("class", "otp-field"); | |
inputField.maxLength = 1; | |
this.element.appendChild(inputField); | |
this.inputs.push(inputField); | |
} | |
this.inputs.forEach((i) => { | |
i.addEventListener("paste", (event) => this.onPaste(event, i)); | |
i.addEventListener("keydown", (event) => this.onKeydown(event, i)); | |
i.addEventListener("input", (event) => this.onInput(event, i)); | |
}); | |
if (this.getAttribute("value")) { | |
this.value = this.getAttribute("value"); | |
} | |
} | |
onPaste = (event, i) => { | |
event.preventDefault(); | |
let elm = i; | |
let value = this.sanitizeInput(event.clipboardData.getData("text")); | |
while (value.length > 0 && elm) { | |
elm.value = value.shift(); | |
if (!elm.nextSibling || !value.length) { | |
elm.focus(); | |
} | |
elm = elm.nextSibling; | |
} | |
this.updateValue(); | |
}; | |
onKeydown = (event, i) => { | |
if (event.key === "Backspace") { | |
i.value = ""; | |
i.previousSibling && i.previousSibling.focus(); | |
event.preventDefault(); | |
} else if (event.key === "ArrowLeft" && i !== 0) { | |
i.previousSibling && i.previousSibling.focus(); | |
} else if (event.key === "ArrowRight" && i !== this.otp_length - 1) { | |
i.nextSibling && i.nextSibling.focus(); | |
} else if ( | |
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(event.key) | |
) { | |
i.setAttribute("type", "text"); | |
i.value = event.key; | |
setTimeout(() => { | |
i.nextSibling && i.nextSibling.focus(); | |
}); | |
} else if ("v" == event.key && event.ctrlKey) { | |
} else if (!["Tab"].includes(event.key)) { | |
if ("preventDefault" in event) { | |
event.preventDefault(); | |
} | |
} | |
this.updateValue(); | |
}; | |
onInput = (event, i) => { | |
event.preventDefault(); | |
return false; | |
}; | |
get value() { | |
return this.value_; | |
} | |
set value(value) { | |
value = this.sanitizeInput(value); | |
for (let i = 0; i < this.otp_length; i++) { | |
this.onKeydown( | |
{ | |
key: value[i], | |
}, | |
this.inputs[i] | |
); | |
} | |
} | |
updateValue() { | |
const value = this.inputs.reduce( | |
(carry, item) => (carry += item.value), | |
"" | |
); | |
if (value.length === this.otp_length) { | |
this.value_ = value; | |
this.internals.setFormValue(this.value_); | |
} else { | |
this.internals.setFormValue(""); | |
} | |
} | |
sanitizeInput(value) { | |
value = String(value).replace(/\D+/g, "").split(""); | |
return value.slice(0, this.otp_length); | |
} | |
get form() { | |
return this.internals.form; | |
} | |
get name() { | |
return this.getAttribute("name"); | |
} | |
get id() { | |
return this.getAttribute("id"); | |
} | |
get type() { | |
return this.localName; | |
} | |
} | |
customElements.define("otp-input", OtpInput); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment