maska
Version:
Simple zero-dependency input mask for Vanilla JS, Vue, Alpine.js and Svelte
192 lines (191 loc) • 9.04 kB
JavaScript
var I = Object.defineProperty;
var S = (n, t, s) => t in n ? I(n, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : n[t] = s;
var A = (n, t, s) => S(n, typeof t != "symbol" ? t + "" : t, s);
const N = {
"#": { pattern: /[0-9]/ },
"@": { pattern: /[a-zA-Z]/ },
"*": { pattern: /[a-zA-Z0-9]/ }
}, R = (n, t, s) => n.replaceAll(t, "").replace(s, ".").replace("..", ".").replace(/[^.\d]/g, ""), C = (n, t, s) => {
var e;
return new Intl.NumberFormat(((e = s.number) == null ? void 0 : e.locale) ?? "en", {
minimumFractionDigits: n,
maximumFractionDigits: t,
roundingMode: "trunc"
});
}, P = (n, t = !0, s) => {
var k, g, b, d;
const e = ((k = s.number) == null ? void 0 : k.unsigned) !== !0 && n.startsWith("-") ? "-" : "", r = ((g = s.number) == null ? void 0 : g.fraction) ?? 0;
let a = C(0, r, s);
const u = a.formatToParts(1000.12), p = ((b = u.find((o) => o.type === "group")) == null ? void 0 : b.value) ?? " ", f = ((d = u.find((o) => o.type === "decimal")) == null ? void 0 : d.value) ?? ".", i = R(n, p, f);
if (Number.isNaN(parseFloat(i))) return e;
const h = i.split(".");
if (h[1] != null && h[1].length >= 1) {
const o = h[1].length <= r ? h[1].length : r;
a = C(o, r, s);
}
let l = a.format(parseFloat(i));
return t ? r > 0 && i.endsWith(".") && !i.slice(0, -1).includes(".") && (l += f) : l = R(l, p, f), e + l;
};
class F {
constructor(t = {}) {
A(this, "opts", {});
A(this, "memo", /* @__PURE__ */ new Map());
const s = { ...t };
if (s.tokens != null) {
s.tokens = s.tokensReplace ? { ...s.tokens } : { ...N, ...s.tokens };
for (const e of Object.values(s.tokens))
typeof e.pattern == "string" && (e.pattern = new RegExp(e.pattern));
} else
s.tokens = N;
Array.isArray(s.mask) && (s.mask.length > 1 ? s.mask = [...s.mask].sort((e, r) => e.length - r.length) : s.mask = s.mask[0] ?? ""), s.mask === "" && (s.mask = null), this.opts = s;
}
masked(t) {
return this.process(String(t), this.findMask(String(t)));
}
unmasked(t) {
return this.process(String(t), this.findMask(String(t)), !1);
}
isEager() {
return this.opts.eager === !0;
}
isReversed() {
return this.opts.reversed === !0;
}
completed(t) {
const s = this.findMask(String(t));
if (this.opts.mask == null || s == null) return !1;
const e = this.process(String(t), s).length;
return typeof this.opts.mask == "string" ? e >= this.opts.mask.length : e >= s.length;
}
findMask(t) {
const s = this.opts.mask;
if (s == null)
return null;
if (typeof s == "string")
return s;
if (typeof s == "function")
return s(t);
const e = this.process(t, s.slice(-1).pop() ?? "", !1);
return s.find((r) => this.process(t, r, !1).length >= e.length) ?? "";
}
escapeMask(t) {
const s = [], e = [];
return t.split("").forEach((r, a) => {
r === "!" && t[a - 1] !== "!" ? e.push(a - e.length) : s.push(r);
}), { mask: s.join(""), escaped: e };
}
process(t, s, e = !0) {
if (this.opts.number != null) return P(t, e, this.opts);
if (s == null) return t;
const r = `v=${t},mr=${s},m=${e ? 1 : 0}`;
if (this.memo.has(r)) return this.memo.get(r);
const { mask: a, escaped: u } = this.escapeMask(s), p = [], f = this.opts.tokens != null ? this.opts.tokens : {}, i = this.isReversed() ? -1 : 1, h = this.isReversed() ? "unshift" : "push", l = this.isReversed() ? 0 : a.length - 1, k = this.isReversed() ? () => o > -1 && c > -1 : () => o < a.length && c < t.length, g = (v) => !this.isReversed() && v <= l || this.isReversed() && v >= l;
let b, d = -1, o = this.isReversed() ? a.length - 1 : 0, c = this.isReversed() ? t.length - 1 : 0, E = !1;
for (; k(); ) {
const v = a.charAt(o), m = f[v], y = (m == null ? void 0 : m.transform) != null ? m.transform(t.charAt(c)) : t.charAt(c);
if (!u.includes(o) && m != null ? (y.match(m.pattern) != null ? (p[h](y), m.repeated ? (d === -1 ? d = o : o === l && o !== d && (o = d - i), l === d && (o -= i)) : m.multiple && (E = !0, o -= i), o += i) : m.multiple ? E && (o += i, c -= i, E = !1) : y === b ? b = void 0 : m.optional && (o += i, c -= i), c += i) : (e && !this.isEager() && p[h](v), y === v && !this.isEager() ? c += i : b = v, this.isEager() || (o += i)), this.isEager())
for (; g(o) && (f[a.charAt(o)] == null || u.includes(o)); ) {
if (e) {
if (p[h](a.charAt(o)), t.charAt(c) === a.charAt(o)) {
o += i, c += i;
continue;
}
} else a.charAt(o) === t.charAt(c) && (c += i);
o += i;
}
}
return this.memo.set(r, p.join("")), this.memo.get(r);
}
}
const w = (n) => JSON.parse(n.replaceAll("'", '"')), T = (n, t = {}) => {
const s = { ...t };
n.dataset.maska != null && n.dataset.maska !== "" && (s.mask = x(n.dataset.maska)), n.dataset.maskaEager != null && (s.eager = M(n.dataset.maskaEager)), n.dataset.maskaReversed != null && (s.reversed = M(n.dataset.maskaReversed)), n.dataset.maskaTokensReplace != null && (s.tokensReplace = M(n.dataset.maskaTokensReplace)), n.dataset.maskaTokens != null && (s.tokens = W(n.dataset.maskaTokens));
const e = {};
return n.dataset.maskaNumberLocale != null && (e.locale = n.dataset.maskaNumberLocale), n.dataset.maskaNumberFraction != null && (e.fraction = parseInt(n.dataset.maskaNumberFraction)), n.dataset.maskaNumberUnsigned != null && (e.unsigned = M(n.dataset.maskaNumberUnsigned)), (n.dataset.maskaNumber != null || Object.values(e).length > 0) && (s.number = e), s;
}, M = (n) => n !== "" ? !!JSON.parse(n) : !0, x = (n) => n.startsWith("[") && n.endsWith("]") ? w(n) : n, W = (n) => {
if (n.startsWith("{") && n.endsWith("}"))
return w(n);
const t = {};
return n.split("|").forEach((s) => {
const e = s.split(":");
t[e[0]] = {
pattern: new RegExp(e[1]),
optional: e[2] === "optional",
multiple: e[2] === "multiple",
repeated: e[2] === "repeated"
};
}), t;
};
class V {
constructor(t, s = {}) {
A(this, "items", /* @__PURE__ */ new Map());
A(this, "eventAbortController");
A(this, "onInput", (t) => {
if (t instanceof CustomEvent && t.type === "input" && !t.isTrusted && !t.bubbles)
return;
const s = t.target, e = this.items.get(s);
if (e === void 0) return;
const r = "inputType" in t && t.inputType.startsWith("delete"), a = e.isEager(), u = r && a && e.unmasked(s.value) === "" ? "" : s.value;
this.fixCursor(s, r, () => this.setValue(s, u));
});
this.options = s, this.eventAbortController = new AbortController(), this.init(this.getInputs(t));
}
update(t = {}) {
this.options = { ...t }, this.init(Array.from(this.items.keys()));
}
updateValue(t) {
var s;
t.value !== "" && t.value !== ((s = this.processInput(t)) == null ? void 0 : s.masked) && this.setValue(t, t.value);
}
destroy() {
this.eventAbortController.abort(), this.items.clear();
}
init(t) {
const s = this.getOptions(this.options);
for (const e of t) {
if (!this.items.has(e)) {
const { signal: a } = this.eventAbortController;
e.addEventListener("input", this.onInput, { capture: !0, signal: a });
}
const r = new F(T(e, s));
this.items.set(e, r), queueMicrotask(() => this.updateValue(e)), e.selectionStart === null && r.isEager() && console.warn("Maska: input of `%s` type is not supported", e.type);
}
}
getInputs(t) {
return typeof t == "string" ? Array.from(document.querySelectorAll(t)) : "length" in t ? Array.from(t) : [t];
}
getOptions(t) {
const { onMaska: s, preProcess: e, postProcess: r, ...a } = t;
return a;
}
fixCursor(t, s, e) {
var k, g;
const r = t.selectionStart, a = t.value;
if (e(), r === null || r === a.length && !s) return;
const u = t.value, p = a.slice(0, r), f = u.slice(0, r), i = (k = this.processInput(t, p)) == null ? void 0 : k.unmasked, h = (g = this.processInput(t, f)) == null ? void 0 : g.unmasked;
if (i === void 0 || h === void 0) return;
let l = r;
p !== f && (l += s ? u.length - a.length : i.length - h.length), t.setSelectionRange(l, l);
}
setValue(t, s) {
const e = this.processInput(t, s);
e !== void 0 && (t.value = e.masked, this.options.onMaska != null && (Array.isArray(this.options.onMaska) ? this.options.onMaska.forEach((r) => r(e)) : this.options.onMaska(e)), t.dispatchEvent(new CustomEvent("maska", { detail: e })), t.dispatchEvent(new CustomEvent("input", { detail: e.masked })));
}
processInput(t, s) {
const e = this.items.get(t);
if (e === void 0) return;
let r = s ?? t.value;
this.options.preProcess != null && (r = this.options.preProcess(r));
let a = e.masked(r);
return this.options.postProcess != null && (a = this.options.postProcess(a)), {
masked: a,
unmasked: e.unmasked(r),
completed: e.completed(r)
};
}
}
export {
F as Mask,
V as MaskInput,
N as tokens
};