UNPKG

@engr-lukman/vue-secure-pin

Version:

A secure PIN input component for Vue.js applications with virtual keyboard and PIN masking, designed for financial applications

170 lines (169 loc) 5.69 kB
import { ref as m, computed as C, defineComponent as $, createElementBlock as b, openBlock as _, createCommentVNode as g, createElementVNode as u, toDisplayString as v, Fragment as x, renderList as B, onMounted as w, onUnmounted as H, normalizeClass as S, unref as k, createVNode as I } from "vue"; function R(i) { const { maxLength: f } = i, e = m(""), o = m(""), a = C( () => e.value.length >= f ); return { pinMasked: e, pin: o, isMaxLengthReached: a, clear: () => { e.value = "", o.value = ""; }, pinHandler: (p) => { a.value || (e.value = `${e.value || ""}*`, o.value = `${o.value || ""}${p}`); } }; } const s = { maxLength: 5, isError: !1, errorMessage: "", placeholder: "Enter PIN", isEnableShuffle: !0, showKeyboard: !0, title: "Use this keyboard to enter your PIN", clearLabel: "Clear" }, O = { key: 0, class: "numeric-keyboard__title" }, V = { class: "numeric-keyboard" }, A = { class: "numeric-keyboard__grid" }, U = ["onClick"], j = /* @__PURE__ */ $({ __name: "NumericKeyboard", props: { isEnableShuffle: { type: Boolean, default: s.isEnableShuffle }, isMaxLengthReached: { type: Boolean, default: !1 }, title: { default: s.title }, clearLabel: { default: s.clearLabel } }, emits: ["input", "clear"], setup(i, { emit: f }) { const e = i, o = f, a = m([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), d = (r) => { if (!e.isEnableShuffle) return r; const t = [...r]; for (let n = t.length - 1; n > 0; n--) { const h = Math.floor(Math.random() * (n + 1)); [t[n], t[h]] = [t[h], t[n]]; } return t; }, c = C( () => e.isEnableShuffle ? d([...a.value]) : a.value ), p = (r) => { e.isMaxLengthReached || (e.isEnableShuffle && (a.value = d(a.value)), o("input", r)); }; return (r, t) => (_(), b("div", null, [ r.title ? (_(), b("div", O, v(r.title), 1)) : g("", !0), u("div", V, [ u("div", A, [ (_(!0), b(x, null, B(c.value.slice(0, 9), (n, h) => (_(), b("button", { key: h, class: "numeric-keyboard__button", type: "button", onClick: (y) => p(n) }, v(n), 9, U))), 128)), u("button", { class: "numeric-keyboard__button numeric-keyboard__button--double", type: "button", onClick: t[0] || (t[0] = (n) => p(c.value[c.value.length - 1])) }, v(c.value[c.value.length - 1]), 1), u("button", { class: "numeric-keyboard__button numeric-keyboard__button--clear", type: "button", onClick: t[1] || (t[1] = (n) => r.$emit("clear")) }, v(r.clearLabel), 1) ]) ]) ])); } }), P = (i, f) => { const e = i.__vccOpts || i; for (const [o, a] of f) e[o] = a; return e; }, z = /* @__PURE__ */ P(j, [["__scopeId", "data-v-c58e0ec8"]]), D = { class: "pin" }, F = ["data-placeholder"], q = { class: "pin__masked-value" }, G = { key: 0, class: "pin__error", role: "alert" }, J = /* @__PURE__ */ $({ __name: "SecurePin", props: { maxLength: { default: s.maxLength }, isError: { type: Boolean, default: s.isError }, errorMessage: { default: s.errorMessage }, placeholder: { default: s.placeholder }, isEnableShuffle: { type: Boolean, default: s.isEnableShuffle }, showKeyboard: { type: Boolean, default: s.showKeyboard }, title: { default: s.title }, clearLabel: { default: s.clearLabel } }, emits: ["callbackEvent"], setup(i, { expose: f, emit: e }) { const o = i, a = e, d = m(o.showKeyboard), c = m(null), { pinMasked: p, pin: r, isMaxLengthReached: t, clear: n, pinHandler: h } = R({ maxLength: o.maxLength }), y = () => { d.value = !d.value; }, E = () => { n(), a("callbackEvent", { pin: "", pinMasked: "" }); }, N = (l) => { h(l), a("callbackEvent", { pin: r.value, pinMasked: p.value }); }, L = (l) => { c.value && !c.value.contains(l.target) && !l.target.closest(".pin__toggle") && (d.value = !1); }; return f({ clearHandler: E, toggleKeyboard: y }), w(() => { document.addEventListener("click", L); }), H(() => { document.removeEventListener("click", L); }), (l, M) => (_(), b("div", D, [ u("div", { class: S(["pin__field", { "pin__field--error": l.isError }]) }, [ u("div", { class: S(["pin__display", { "pin__display--empty": !k(p) }]), "data-placeholder": l.placeholder }, [ u("div", q, v(k(p)), 1) ], 10, F), u("div", { class: "pin__actions" }, [ u("div", { class: "pin__toggle", onClick: y }, M[0] || (M[0] = [ u("span", { class: "pin__icon" }, "⌨️", -1) ])) ]), l.isError ? (_(), b("div", G, v(l.errorMessage), 1)) : g("", !0) ], 2), d.value ? (_(), b("div", { key: 0, ref_key: "keyboardPopup", ref: c, class: "pin__keyboard" }, [ I(z, { "is-enable-shuffle": l.isEnableShuffle, "is-max-length-reached": k(t), title: l.title, "clear-label": l.clearLabel, onInput: N, onClear: E }, null, 8, ["is-enable-shuffle", "is-max-length-reached", "title", "clear-label"]) ], 512)) : g("", !0) ])); } }), K = /* @__PURE__ */ P(J, [["__scopeId", "data-v-c41fe822"]]); function Q(i) { i.component("SecurePin", K); } const W = { install: Q, SecurePin: K }; export { K as SecurePin, W as default }; //# sourceMappingURL=vue-secure-pin.es.js.map