UNPKG

vue3-otp-input

Version:

A fully customizable, OTP (one-time password) input component built with Vue 3.x and Vue Composition API.

195 lines (194 loc) 7.39 kB
import { defineComponent as F, ref as f, computed as K, watch as T, onMounted as $, createElementBlock as h, openBlock as c, createElementVNode as B, createCommentVNode as j, normalizeClass as M, Fragment as R, renderList as N, createBlock as W } from "vue"; const H = { style: { display: "flex", "align-items": "center" } }, S = ["type", "inputmode", "placeholder", "disabled", "maxlength", "value"], V = { key: 0 }, q = ["innerHTML"], z = /* @__PURE__ */ F({ __name: "single-otp-input", props: { inputType: { default: "tel" }, inputmode: { default: "numeric" }, value: { default: "" }, separator: { default: "" }, focus: { type: Boolean, default: !1 }, inputClasses: { default: "" }, conditionalClass: { default: "" }, shouldAutoFocus: { type: Boolean, default: !1 }, isLastChild: { type: Boolean, default: !1 }, placeholder: { default: "" }, isDisabled: { type: Boolean, default: !1 } }, emits: ["on-change", "on-keydown", "on-paste", "on-focus", "on-blur"], setup(I, { emit: b }) { const s = I, l = b, i = f(s.value || ""), n = f(null), a = (e) => { const u = e.target.value; return u && u.trim().length > 1 ? (e.clipboardData = { getData: () => u.trim() }, l("on-paste", e)) : l("on-change", u); }, v = (e) => e >= 65 && e <= 90, D = (e) => e >= 48 && e <= 57 || e >= 96 && e <= 105, k = (e) => { s.isDisabled && e.preventDefault(); const u = e || window.event, r = u.which ? u.which : u.keyCode; if (/iPhone|iPad|iPod/.test(navigator.userAgent)) { l("on-keydown", e); return; } D(r) || s.inputType === "letter-numeric" && v(r) || [8, 9, 13, 37, 39, 46, 86].includes(r) ? l("on-keydown", e) : u.preventDefault(); }, m = (e) => l("on-paste", e), d = () => (n.value.select(), l("on-focus")), y = () => l("on-blur"), g = K( () => ["letter-numeric", "number"].includes(s.inputType) ? "text" : s.inputType ); return T( () => s.value, (e, u) => { e !== u && (i.value = e); } ), T( () => s.focus, (e, u) => { u !== e && n.value && s.focus && (n.value.focus(), n.value.select()); } ), $(() => { n.value && s.focus && s.shouldAutoFocus && (n.value.focus(), n.value.select()); }), (e, u) => (c(), h("div", H, [ B("input", { "data-test": "single-input", type: g.value, inputmode: e.inputmode, placeholder: e.placeholder, disabled: e.isDisabled, ref_key: "input", ref: n, min: "0", max: "9", maxlength: e.isLastChild ? 1 : void 0, pattern: "[0-9]", value: i.value, class: M([e.inputClasses, e.conditionalClass, { "is-complete": i.value }]), onInput: a, onKeydown: k, onPaste: m, onFocus: d, onBlur: y }, null, 42, S), !e.isLastChild && e.separator ? (c(), h("span", V, [ B("span", { innerHTML: e.separator }, null, 8, q) ])) : j("", !0) ])); } }), G = { style: { display: "flex" }, class: "otp-input-container" }, J = { key: 0, autocomplete: "off", name: "hidden", type: "text", style: { display: "none" } }, Q = 8, U = 37, X = 39, Y = 46, x = /* @__PURE__ */ F({ __name: "vue3-otp-input", props: { value: { default: "" }, numInputs: { default: 4 }, separator: { default: "" }, inputClasses: { default: "" }, conditionalClass: { default: () => [] }, inputType: {}, inputmode: { default: "text" }, shouldAutoFocus: { type: Boolean, default: !1 }, placeholder: { default: () => [] }, isDisabled: { type: Boolean, default: !1 }, shouldFocusOrder: { type: Boolean } }, emits: ["update:value", "on-change", "on-complete"], setup(I, { expose: b, emit: s }) { const l = I, i = s, n = f(0), a = f([]), v = f([]); T( () => l.value, (t) => { if (t.length === l.numInputs || a.value.length === 0) { const o = t.split(""); a.value = o; } }, { immediate: !0 } ); const D = (t) => { n.value = t; }, k = () => { n.value !== l.numInputs - 1 && (n.value = -1); }, m = () => a.value.join("").length === l.numInputs ? (i("update:value", a.value.join("")), i("on-complete", a.value.join(""))) : "Wait until the user enters the required number of characters", d = (t) => { n.value = Math.max(Math.min(l.numInputs - 1, t), 0); }, y = () => { d(n.value + 1); }, g = () => { d(n.value - 1); }, e = (t) => { v.value = Object.assign([], a.value), a.value[n.value] = t.toString(), v.value.join("") !== a.value.join("") && (i("update:value", a.value.join("")), i("on-change", a.value.join("")), m()); }, u = (t) => { t.preventDefault(); const o = t.clipboardData.getData("text/plain").slice(0, l.numInputs - n.value).split(""); if (l.inputType === "number" && !o.join("").match(/^\d+$/) || l.inputType === "letter-numeric" && !o.join("").match(/^\w+$/)) return "Invalid pasted data"; const p = a.value.slice(0, n.value).concat(o); return p.slice(0, l.numInputs).forEach(function(O, C) { a.value[C] = O; }), d(p.slice(0, l.numInputs).length), m(); }, r = (t) => { e(t), n.value < l.numInputs - 1 && y(); }, A = () => { a.value.length > 0 && (i("update:value", ""), i("on-change", "")), a.value = [], n.value = 0; }, P = (t) => { const o = t.split(""); o.length === l.numInputs && (a.value = o, i("update:value", a.value.join("")), i("on-complete", a.value.join(""))); }, E = (t, o) => { switch (t.keyCode) { case Q: t.preventDefault(), e(""), g(); break; case Y: t.preventDefault(), e(""); break; case U: t.preventDefault(), g(); break; case X: t.preventDefault(), y(); break; default: L(o); break; } }, L = (t) => { l.shouldFocusOrder && setTimeout(() => { const o = a.value.join("").length; t - o >= 0 && (n.value = o, a.value[t] = ""); }, 100); }; return b({ clearInput: A, fillInput: P }), (t, o) => (c(), h("div", G, [ t.inputType === "password" ? (c(), h("input", J)) : j("", !0), (c(!0), h(R, null, N(t.numInputs, (_, p) => { var O, C; return c(), W(z, { key: p, focus: n.value === p, value: a.value[p], separator: t.separator, "input-type": t.inputType, inputmode: t.inputmode, "input-classes": t.inputClasses, conditionalClass: (O = t.conditionalClass) == null ? void 0 : O[p], "is-last-child": p === t.numInputs - 1, "should-auto-focus": t.shouldAutoFocus, placeholder: (C = t.placeholder) == null ? void 0 : C[p], "is-disabled": t.isDisabled, onOnChange: r, onOnKeydown: (w) => E(w, p), onOnPaste: u, onOnFocus: (w) => D(p), onOnBlur: k }, null, 8, ["focus", "value", "separator", "input-type", "inputmode", "input-classes", "conditionalClass", "is-last-child", "should-auto-focus", "placeholder", "is-disabled", "onOnKeydown", "onOnFocus"]); }), 128)) ])); } }); export { x as default };