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
JavaScript
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
};