UNPKG

vue-formify

Version:

Build powerful, type-safe forms in Vue.

521 lines (520 loc) 17.6 kB
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 };