vue-formify
Version:
Build powerful, type-safe forms in Vue.
521 lines (520 loc) • 17.6 kB
JavaScript
import { reactive as ne, inject as Y, getCurrentInstance as le, computed as D, warn as se, onBeforeUnmount as ue, onMounted as Q, nextTick as k, ref as $, defineComponent as x, h as M, toValue as ie, provide as oe, watch as ce, resolveDynamicComponent as ve } from "vue";
const l = ne({}), G = /^\[(.+?)\]$/gm, q = /(^.*\[|\].*$)/g, B = /\[(\d*)\]/gm, C = /\[(\d+)\]/, R = /\[\d+\]/, fe = (e, t, a) => {
if (e && !(e in l[t].values)) {
const r = Z(e, a);
l[t].values = T(l[t].values, r);
}
}, de = (e, t) => {
let a = e.as || "";
return !e.as && !t && (a = "input"), a;
}, me = (e, t, a) => t.default ? typeof e == "string" || !e ? t.default(a()) : {
default: () => t.default?.(a())
} : t.default, Z = (e, t) => {
const a = e.match(G);
return a ? {
[a[0].replace(q, "")]: t
} : e.split(".").reduceRight((u, i) => {
const s = i.match(B), m = i.replace(R, ""), h = i.match(C)?.[1];
return s ? {
[m]: {
value: {
[`[${h}]`]: u
}
}
} : { [i]: u };
}, t);
}, X = (e, t = "") => {
let a = {};
for (const r in e) {
const n = t ? `${t}.${r}` : r;
if (typeof e[r] == "object" && e[r] !== null && !Array.isArray(e[r])) {
const u = X(e[r], n);
a = { ...a, ...u };
} else
a[n] = e[r];
}
return a;
}, f = (e, t) => {
if (!t)
return;
const a = t.match(G);
if (a) {
const i = a[0].replace(q, "");
return e[i];
}
let r = e;
const n = t.split("."), u = n.pop();
for (const i of n) {
if (i.match(B)) {
const m = i.replace(R, ""), h = i.match(C)?.[1];
r = r?.[m]?.value?.[`[${h}]`];
} else
r = r[i];
if (!r)
return;
}
if (u && u.match(B)) {
const s = u.replace(R, ""), m = u.match(C)?.[1];
return u && r?.[s]?.value?.[`[${m}]`];
}
return u && r[u];
}, z = (e, t) => {
const a = t.match(G);
if (a) {
const i = a[0].replace(q, "");
delete e[i];
}
let r = e;
const n = t.split("."), u = n.pop();
for (const i of n) {
if (i.match(B)) {
const m = i.replace(R, ""), h = i.match(C)?.[1];
r = r?.[m]?.value?.[`[${h}]`];
} else
r = r[i];
if (!r)
return;
}
if (u) {
if (u.match(B)) {
const s = u.replace(R, ""), m = u.match(C)?.[1];
delete r?.[s]?.value?.[`[${m}]`], r?.[s]?.value && !Object.keys(r?.[s]?.value).length && (r[s].value = []);
} else
delete r[u];
Object.keys(r).length === 0 && z(e, n.join("."));
}
};
function L(e) {
return typeof e == "boolean" ? e : Array.isArray(e) ? e.every((t) => L(t)) : typeof e == "object" && e !== null ? Object.values(e).every((t) => L(t)) : !1;
}
function K(e) {
return typeof e == "string" && e ? !0 : Array.isArray(e) ? e.some((t) => K(t)) : typeof e == "object" && e !== null ? Object.values(e).some((t) => K(t)) : !1;
}
const ee = (e, t) => f(e, t)?.error, A = (e, t = "value") => {
const a = {};
for (const r in e)
if (e[r] && typeof e[r] == "object" && "value" in e[r])
if (typeof e[r].value == "object")
if (Array.isArray(e[r].value) && (a[r] = []), Object.keys(e[r].value)?.[0]?.match?.(G))
for (const n in e[r].value)
!a[r] && !e[r].ignore && (a[r] = []), t in e[r].value[n] ? e[r].value[n][t] && a[r].push(e[r].value[n][t]) : !e[r].ignore && a[r].push(A(e[r].value[n], t));
else
!e[r].ignore && (a[r] = e[r][t]);
else
!e[r].ignore && (a[r] = e[r][t]);
else typeof e[r] == "object" && (a[r] = A(e[r], t));
return a;
}, ye = (e) => {
const t = [];
if (!Array.isArray(e.value) && typeof e.value == "object" && !(e.value instanceof Date))
for (const a in e.value)
t.push(e.value[a].value);
else
return e.value || typeof e.value == "boolean" ? e.value : "";
return t;
}, T = (...e) => {
const t = (a) => a && typeof a == "object";
return e.reduce((a, r) => (Object.keys(r).forEach((n) => {
const u = a[n], i = r[n];
Array.isArray(u) && Array.isArray(i) ? a[n] = u.concat(...i) : t(u) && t(i) ? a[n] = T(u, i) : a[n] = i;
}), a), {});
}, te = (e, t = "") => {
const a = new FormData();
return Object.keys(e).reduce((r, n) => {
const u = t ? `${t}.${n}` : n;
if (typeof e[n] == "object" && e[n] !== null && !Array.isArray(e[n]) && !(e[n] instanceof Date) && !(e[n] instanceof File) && !(e[n] instanceof Blob)) {
const i = te(e[n], u);
r += i;
} else
Array.isArray(e[n]) ? e[n].forEach((i) => {
a.append(`${u}[]`, i);
}) : a.append(`${u}`, e[n]);
return r;
}, ""), a;
}, he = (e) => {
if (e.path?.length) {
let t = "";
for (const a of e.path) {
const r = typeof a == "object" ? a.key : a;
if (typeof r == "string")
t ? t += `.${r}` : t += r;
else if (typeof r == "number")
t += `[${r}]`;
else
return null;
}
return t;
}
return null;
}, ge = async (e) => {
try {
return await e;
} catch {
return;
}
};
class b {
static eventListeners = {};
static on(t, a) {
this.eventListeners[t] || (this.eventListeners[t] = []), this.eventListeners[t].push(a);
}
static off(t, a) {
const r = this.eventListeners[t];
r && (this.eventListeners[t] = r.filter((n) => n !== a));
}
static emit(t, a) {
const r = this.eventListeners[t];
r && r.forEach((n) => n(a));
}
}
const U = async (e, t, a, r) => {
const n = await e["~standard"].validate(t);
if (n.issues) {
for (const u of n.issues)
a(r || he(u), u.message);
return !1;
}
return !0;
}, ae = (e, t = !1) => {
const { uid: a, preserveForm: r, mode: n, isSubmitted: u } = Y("formData", /* @__PURE__ */ Object.create({})), i = le(), s = e.name, m = {
value: f(l[a].values, s)?.value ?? void 0,
error: void 0,
ignore: e.ignore,
isDirty: !1,
isTouched: !1,
isValid: !0
}, h = D({
get() {
if (c.value)
return c.value.value;
},
set(o) {
if (t || Array.isArray(o)) {
c.value.value = [];
for (let g = 0; g < o.length; g++)
H({ [`[${g}]`]: { value: o[g] } }, s);
return c.value.value;
}
c.value.value = o || typeof o == "boolean" ? o : "";
}
}), c = D(() => f(l[a].values, s)), y = D(() => !(c.value?.error && (c.value.isDirty || u.value || n === "onSubmit"))), d = D(() => c.value?.isTouched), p = D(() => c.value?.isDirty), S = D(() => ye(h)), _ = () => {
if (t || Array.isArray(f(l[a].initialValues, s))) {
if (f(l[a].initialValues, s)) {
const o = f(l[a].initialValues, s);
let g = {};
for (let I = 0; I < o.length; I++)
g = {
...g,
[`[${I}]`]: {
value: o[I],
error: void 0
}
};
return g;
}
return [];
}
return f(l[a].values, s).value ?? e.modelValue ?? e.default ?? f(l[a].initialValues, s) ?? "";
}, v = async () => {
if (e?.rule) {
const o = await U(e.rule, h.value, O, s);
c.value.isValid = o;
}
}, O = (o, g) => {
f(l[a].values, o) && (f(l[a].values, o).error = g);
}, E = (o) => o.type === "checkbox" ? J(o.checked) : o.type === "file" ? o.multiple ? o.files : o.files?.[0] : o.type === "select-multiple" ? Array.from(o.options).filter((j) => j.selected).map((j) => j.value) : o.value, N = (o) => h.value = o, J = (o) => o ? e.trueValue || !0 : e.falseValue || !1, P = () => c.value?.isDirty || u.value || n === "onSubmit" ? c.value?.error : void 0, w = () => {
c.value?.error && (c.value.error = void 0), f(l[a].values, s.replace(/\[\d+\]/, ""))?.error && (f(l[a].values, s.replace(/\[\d+\]/, "")).error = void 0);
}, V = async (o) => {
typeof o == "object" && "target" in o ? N(E(o.target)) : N(o), c.value.error = void 0, c.value && !c.value?.isTouched && (c.value.isTouched = !0), c.value?.isTouched && m.value !== h.value && (c.value.isDirty = !0), w(), n === "onChange" && (b.emit("validate"), v());
}, F = () => {
w();
}, W = async () => {
c.value && !c.value?.isTouched && (c.value.isTouched = !0), await v();
}, H = (o, g = s) => {
f(l[a].values, g).value = T(f(l[a].values, g).value, o), f(l[a].values, g).error = void 0;
};
fe(s, a, JSON.parse(JSON.stringify(m))), s ? c.value.value = _() : se("`name` prop is required"), ue(() => {
e.preserve || r || z(l[a].values, s);
}), Q(async () => {
await k(), h.value && !Array.isArray(h.value) && (c.value.isDirty = !0), i?.subTree?.el && "as" in e && e.as === "select" && Array.from(i.subTree.el.options).forEach((g) => {
m.value?.includes?.(g.value) && (g.selected = !0);
});
});
const re = $({
modelValue: S,
value: S,
"onUpdate:modelValue": (o) => {
h.value = o, n === "onChange" && (b.emit("validate"), v());
},
isTouched: d.value,
onInput: V,
onFocus: F,
onBlur: W
});
return {
value: h,
isValid: y,
isTouched: d,
isDirty: p,
inputProps: re,
getError: P,
setError: O,
setArrayValue: H
};
}, pe = () => x(
(e, { slots: t, emit: a, attrs: r }) => {
const n = $([]), { getError: u, setArrayValue: i } = ae(e, !0), { uid: s } = Y("formData", /* @__PURE__ */ Object.create({})), m = () => {
!e.ignore && (f(l[s].values, e.name).error = void 0), n.value.push({
id: n.value.length,
name: e.name
});
}, h = async (y) => {
f(l[s].values, e.name).error = void 0;
const d = n.value.findIndex((p) => p.id === y);
for (let p = d + 1; p < n.value.length; p++) {
const S = `${e.name}[${p}]`, _ = `${e.name}[${p - 1}]`;
f(l[s].values, S).error = void 0, f(l[s].values, _).error = void 0, f(l[s].values, _) && i({ [`[${p - 1}]`]: JSON.parse(JSON.stringify(f(l[s].values, S))) });
}
await k(), z(l[s].values, `${n.value[n.value.length - 1].name}[${n.value.length - 1}]`), n.value.splice(-1);
};
return (async () => {
n.value = [];
const y = l[s].initialValues?.[e.name] || l[s].values[e.name].value;
y && (y.forEach?.(() => {
n.value.push({
id: n.value.length,
name: e.name
});
}), await k(), y.forEach?.((d, p) => {
typeof d == "object" ? Object.keys(d).forEach((S) => {
i({
[`[${p}]`]: {
[S]: {
value: d[S]
}
}
});
}) : i({
[`[${p}]`]: {
value: d
}
});
}));
})(), () => M(
"div",
{ ...e, ...r, ...a },
t.default?.({ fields: ie(n), add: m, remove: h, error: u })
);
},
{
name: "FieldArray",
props: {
name: { type: String, default: "" },
default: { type: [String, Array, Boolean, Number, Object], default: void 0 },
preserve: { type: Boolean, default: !1 },
modelValue: { type: [String, Array, Boolean, Number, Object], default: void 0 },
as: { type: String, default: "input" },
rule: { type: Object, default: void 0 }
},
emits: ["update:modelValue"],
slots: Object
}
), Ve = () => x(
(e, { slots: t, emit: a, attrs: r }) => {
const { uid: n, mode: u, isSubmitted: i } = Y("formData", /* @__PURE__ */ Object.create({})), s = () => {
if (u === "onSubmit" || i.value || f(l[n].values, e.errorFor)?.isDirty)
return ee(l[n].values, e.errorFor);
};
return () => M(
"span",
{ ...e, ...r, ...a },
t.default ? t.default?.({ error: s() }) : Array.isArray(s()) ? s()?.[0] : s()
);
},
{
name: "FieldError",
props: {
errorFor: {
type: String,
default: void 0
}
},
slots: Object
}
), Oe = (e) => {
const t = e?.name || Math.floor(Math.random() * Date.now()), a = $(!1), r = $(!1), n = $(!1);
let u = /* @__PURE__ */ Object.create({});
const i = (v = !1) => {
n.value = !1, v ? (l[t].initialValues = /* @__PURE__ */ Object.create({}), l[t].values = /* @__PURE__ */ Object.create({}), u = JSON.stringify(/* @__PURE__ */ Object.create({}))) : l[t].values = JSON.parse(u), l[t].key++;
}, s = (v) => async () => {
await v?.(d.value);
}, m = (v, O) => {
if (f(l[t].values, v))
Array.isArray(O) || (f(l[t].values, v).value = O);
else {
const E = Z(v, { value: O, error: void 0 });
l[t].values = T(l[t].values, E);
}
}, h = (v) => {
const O = X(v);
for (const E in O)
f(l[t].values, E) && "value" in f(l[t].values, E) && (f(l[t].values, E).value = O[E]);
}, c = (v) => {
l[t].initialValues = T(l[t].initialValues, A(l[t].values), v), l[t].key++;
}, y = (v, O) => {
f(l[t].values, v) && (f(l[t].values, v).error = O, f(l[t].values, v).isValid = !1);
}, d = D({
get: () => A(l[t]?.values),
set: (v) => v
}), p = D(() => L(A(l[t]?.values, "isDirty"))), S = D(() => p.value && !K(A(l[t]?.values, "error")));
return l[t] ? d.value = A(l[t].values) : l[t] = {
values: /* @__PURE__ */ Object.create({}),
initialValues: e?.initialValues || /* @__PURE__ */ Object.create({}),
key: 0
}, oe("formData", {
uid: t,
preserveForm: e?.preserve,
mode: e?.mode,
isSubmitted: n
}), {
cmp: x((v, { slots: O, emit: E, attrs: N }) => {
const J = (V) => ee(l[t].values, V), P = async (V) => {
V.preventDefault(), n.value = !0;
let F = d.value;
if (e?.schema && !await U(e.schema, A(l[t].values), y))
return V.preventDefault();
v?.enctype === "multipart/form-data" && (F = te(A(l[t].values))), !v.action && (V.preventDefault(), a.value = !0, await ge(v.onSubmit?.(F, V)), a.value = !1);
}, w = () => {
const V = X(l[t].initialValues);
for (const F in V)
!l[t].values[F]?.value && m(F, V[F]);
};
return ce(d, (V, F) => {
d.value = V, r.value && JSON.stringify(V) !== JSON.stringify(F) && v.onValueChange && b.emit("value-change", t), e?.mode === "onChange" && p.value && b.emit("validate");
}, { deep: !0 }), Object.keys(v.initialValues).length && (l[t].initialValues = T(l[t].initialValues, v.initialValues)), Q(async () => {
w(), u = JSON.stringify(l[t].values), v?.onValueChange && l[t] && b.on("value-change", (V) => {
V === t && E("value-change", d.value);
}), e?.mode === "onChange" && b.on("validate", async () => {
if (e?.schema)
return await U(e.schema, A(l[t].values), y);
}), await k(), r.value = !0;
}), () => M(
"form",
{ ...v, ...N, ...E, key: l[t].key, onSubmit: P },
O.default?.({ values: d.value, getError: J, isDirty: p.value, isValid: S.value })
);
}, {
name: "FormComponent",
props: {
enctype: { type: String, default: "application/x-www-form-urlencoded" },
initialValues: { type: Object, default: /* @__PURE__ */ Object.create({}) },
onValueChange: {
type: Function,
default: void 0
},
onSubmit: {
type: Function,
default: void 0
}
},
emits: ["submit", "value-change"],
slots: Object
}),
values: d,
isSubmitting: a,
reset: i,
setInitialValues: c,
setValue: m,
setValues: h,
setError: y,
handleSubmit: s
};
}, Ee = () => x(
(e, { slots: t, attrs: a }) => {
const {
value: r,
isValid: n,
isTouched: u,
isDirty: i,
inputProps: s,
getError: m
} = ae(e), h = D(() => {
const y = {
...a,
name: e.name,
error: m(),
onInput: (d) => {
s.value.onInput?.(d), typeof a.onInput == "function" && a.onInput();
},
onChange: (d) => {
s.value.onInput?.(d), typeof a.onChange == "function" && a.onChange();
},
onFocus: () => {
s.value.onFocus?.(), typeof a.onFocus == "function" && a.onFocus();
},
onBlur: async () => {
s.value.onBlur?.(), typeof a.onBlur == "function" && a.onBlur();
}
};
return y.type === "checkbox" && r.value && (y.checked = !0), a.type !== "file" && e.as !== "select" && !y.multiple && (y.value = r.value), y;
}), c = () => ({
field: {
...s.value,
error: m(),
isValid: n.value,
isTouched: u.value,
isDirty: i.value,
onFocus: s.value.onFocus,
onBlur: s.value.onBlur
}
});
return () => {
const y = ve(de(e, !!t.default)), d = me(y, t, c);
return y ? M(
y,
{
...e,
...h.value,
...s.value,
isValid: n.value,
isTouched: u.value,
isDirty: i.value
},
d
) : d;
};
},
{
name: "Field",
inheritAttrs: !1,
props: {
name: { type: String, default: "" },
default: { type: [String, Array, Boolean, Number, Object], default: void 0 },
preserve: { type: Boolean, default: !1 },
modelValue: { type: [String, Array, Boolean, Number, Object], default: void 0 },
as: { type: String, default: void 0 },
rule: { type: [Object, Function], default: void 0 }
},
emits: ["update:modelValue"]
}
), Fe = (e) => {
const t = Oe(e), a = Ee(), r = pe(), n = Ve();
return {
Form: t.cmp,
Field: a,
FieldArray: r,
Error: n,
reset: t.reset,
setInitialValues: t.setInitialValues,
setValue: t.setValue,
setValues: t.setValues,
setError: t.setError,
handleSubmit: t.handleSubmit,
values: t.values,
isSubmitting: t.isSubmitting
};
};
export {
Fe as useForm,
ae as useInput
};