UNPKG

@volverjs/form-vue

Version:

Vue 3 Forms with @volverjs/ui-vue

629 lines (628 loc) 18.6 kB
import { defineComponent as W, computed as g, onMounted as D, onBeforeUnmount as j, inject as I, toRefs as R, watch as E, unref as _, provide as M, readonly as p, resolveComponent as O, defineAsyncComponent as F, h as x, ref as $, toRaw as S, isProxy as ee, withModifiers as re, getCurrentInstance as te } from "vue"; import { watchThrottled as ae } from "@vueuse/core"; import { ZodObject as K, ZodDefault as ue, ZodNullable as ne, ZodSchema as le, ZodArray as se, ZodEffects as ie, ZodOptional as fe } from "zod"; function U(e) { return Array.isArray(e); } function oe(e) { return typeof e < "u"; } function P(e) { return e === null; } function H(e) { return typeof e == "object"; } function Q(e) { return typeof e == "string"; } function N(e) { return typeof e > "u"; } const de = /^[0-9]+$/, ve = ["__proto__", "prototype", "constructor"]; function G(e, l, t) { const f = oe(t) ? t : void 0; if (!H(e) || !Q(l)) return f; const n = X(l); if (n.length !== 0) { for (const r of n) { if (r === "*") continue; const u = function(i) { return i.map((s) => N(s) || P(s) ? s : U(s) ? u(s) : s[r]); }; if (U(e) && !de.test(r) ? e = u(e) : e = e[r], N(e) || P(e)) break; } return N(e) ? f : e; } } function T(e, l, t) { if (!H(e) || !Q(l)) return; const f = X(l); if (f.length === 0) return; const n = f.length; for (let r = 0; r < n; r++) { const u = f[r]; if (r === n - 1) { e[u] = t; return; } if (u === "*" && U(e)) { const i = f.slice(r + 1).join("."); for (const s of e) T(s, i, t); return; } N(e[u]) && (e[u] = {}), e = e[u]; } } function X(e) { const l = e.split(/[.]|(?:\[(\d|\*)\])/).filter((t) => !!t); return l.some((t) => ve.indexOf(t) !== -1) ? [] : l; } var v = /* @__PURE__ */ ((e) => (e.text = "text", e.number = "number", e.email = "email", e.password = "password", e.tel = "tel", e.url = "url", e.search = "search", e.date = "date", e.time = "time", e.datetimeLocal = "datetime-local", e.month = "month", e.week = "week", e.color = "color", e.select = "select", e.checkbox = "checkbox", e.radio = "radio", e.textarea = "textarea", e.radioGroup = "radioGroup", e.checkboxGroup = "checkboxGroup", e.combobox = "combobox", e.custom = "custom", e))(v || {}), B = /* @__PURE__ */ ((e) => (e.invalid = "invalid", e.valid = "valid", e))(B || {}); const ce = (e, l, t, f) => W({ name: "FieldComponent", props: { type: { type: String, validator: (n) => Object.values(v).includes(n), default: v.custom }, is: { type: [Object, String], default: void 0 }, name: { type: [String, Number, Boolean, Symbol], required: !0 }, props: { type: [Object, Function], default: () => ({}) }, showValid: { type: Boolean, default: !1 }, defaultValue: { type: [String, Number, Boolean, Array, Object], default: void 0 }, lazyLoad: { type: Boolean, default: !1 } }, emits: ["invalid", "valid", "update:formData", "update:modelValue"], expose: ["invalid", "invalidLabel", "errors"], setup(n, { slots: r, emit: u }) { const i = g({ get() { if (a != null && a.formData) return G( Object(a.formData.value), String(n.name) ); }, set(d) { a != null && a.formData && (T( Object(a.formData.value), String(n.name), d ), u("update:modelValue", { newValue: i.value, formData: a == null ? void 0 : a.formData })); } }); D(() => { i.value === void 0 && n.defaultValue !== void 0 && (i.value = n.defaultValue); }), j(() => { w(), k(); }); const s = I(l, void 0); s && s.fields.value.add(n.name); const a = I(e), { props: o, name: h } = R(n), b = g(() => { if (a != null && a.errors.value) return G(a.errors.value, String(n.name)); }), c = g(() => { var d; return (d = b.value) == null ? void 0 : d._errors; }), m = g(() => b.value !== void 0), w = E(m, () => { m.value ? (u("invalid", c.value), s && s.errors.value.set( n.name, { _errors: c.value } )) : (u("valid", i.value), s && s.errors.value.delete( n.name )); }), k = E( () => a == null ? void 0 : a.formData, () => { u("update:formData", a == null ? void 0 : a.formData); }, { deep: !0 } ), y = (d) => { i.value = d; }, V = g(() => { let d = o.value; return typeof d == "function" && (d = d(a == null ? void 0 : a.formData)), Object.keys(d).reduce( (A, C) => (A[C] = _(d[C]), A), {} ); }), z = g(() => ({ ...V.value, name: V.value.name ?? n.name, invalid: m.value, valid: n.showValid ? !!(!m.value && i.value) : void 0, type: ((d) => { if ([ v.text, v.number, v.email, v.password, v.tel, v.url, v.search, v.date, v.time, v.datetimeLocal, v.month, v.week, v.color ].includes(d)) return d; })(n.type), invalidLabel: c.value, modelValue: i.value, "onUpdate:modelValue": y })); return M(t, { name: p(h), errors: p(b) }), { component: g(() => { if (n.type === v.custom) return { render() { var d; return ((d = r.default) == null ? void 0 : d.call(r, { modelValue: i.value, onUpdate: y, submit: a == null ? void 0 : a.submit, validate: a == null ? void 0 : a.validate, invalid: m.value, invalidLabel: c.value, formData: a == null ? void 0 : a.formData.value, formErrors: a == null ? void 0 : a.errors.value, errors: b.value })) ?? r.defalut; } }; if (!((f == null ? void 0 : f.lazyLoad) ?? n.lazyLoad)) { let d; switch (n.type) { case v.select: d = O("VvSelect"); break; case v.checkbox: d = O("VvCheckbox"); break; case v.radio: d = O("VvRadio"); break; case v.textarea: d = O("VvTextarea"); break; case v.radioGroup: d = O("VvRadioGroup"); break; case v.checkboxGroup: d = O("VvCheckboxGroup"); break; case v.combobox: d = O("VvCombobox"); break; default: d = O("VvInputText"); } if (typeof d != "string") return d; console.warn( `[form-vue warn]: ${d} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.` ); } return F(async () => { switch (f != null && f.sideEffects && await Promise.resolve(f.sideEffects(n.type)), n.type) { case v.textarea: return import("@volverjs/ui-vue/vv-textarea"); case v.radio: return import("@volverjs/ui-vue/vv-radio"); case v.radioGroup: return import("@volverjs/ui-vue/vv-radio-group"); case v.checkbox: return import("@volverjs/ui-vue/vv-checkbox"); case v.checkboxGroup: return import("@volverjs/ui-vue/vv-checkbox-group"); case v.select: return import("@volverjs/ui-vue/vv-select"); case v.combobox: return import("@volverjs/ui-vue/vv-combobox"); } return import("@volverjs/ui-vue/vv-input-text"); }); }), hasProps: z, invalid: m }; }, render() { return this.is ? x(this.is, this.hasProps, this.$slots) : this.type === v.custom ? x(this.component, null, this.$slots) : x(this.component, this.hasProps, this.$slots); } }), J = (e, l = {}) => { const t = (r) => { let u = r; for (; u instanceof ie; ) u = u.innerType(); return u instanceof fe && (u = u._def.innerType), u; }, f = t(e); return { ...(f instanceof K ? f._def.unknownKeys === "passthrough" : !1) ? l : {}, ...Object.fromEntries( Object.entries(f.shape).map( ([r, u]) => { const i = l[r]; let s = t(u), a; if (s instanceof ue && (a = s._def.defaultValue(), s = s._def.innerType), i === null && s instanceof ne) return [r, i]; if (s instanceof le) { const o = u.safeParse(i); if (o.success) return [r, o.data ?? a]; } if (s instanceof se && Array.isArray(i) && i.length) { const o = t(s._def.type); if (o instanceof K) return [ r, i.map( (h) => J( o, h && typeof h == "object" ? h : void 0 ) ) ?? a ]; } return s instanceof K ? [ r, J( s, i && typeof i == "object" ? i : a ) ] : [r, a]; } ) ) }; }, me = (e, l, t, f) => { const n = $(), r = $(), u = $(), i = W({ name: "FormComponent", props: { modelValue: { type: Object, default: () => ({}) }, updateThrottle: { type: Number, default: 500 }, continuosValidation: { type: Boolean, default: !1 }, template: { type: [Array, Function], default: void 0 } }, emits: ["invalid", "valid", "submit", "update:modelValue"], expose: ["submit", "validate", "errors", "status", "valid", "invalid"], setup(s, { emit: a }) { u.value = J( e, S(s.modelValue) ), E( () => s.modelValue, (c) => { if (c) { const m = ee(c) ? S(c) : c; u.value = typeof (m == null ? void 0 : m.clone) == "function" ? m.clone() : JSON.parse(JSON.stringify(m)); } }, { deep: !0 } ), ae( u, (c) => { var m; ((n.value || (t == null ? void 0 : t.continuosValidation)) ?? s.continuosValidation) && o(), (!c || !s.modelValue || JSON.stringify(c) !== JSON.stringify(s.modelValue)) && (a("update:modelValue", c), (m = t == null ? void 0 : t.onUpdate) == null || m.call(t, S(c))); }, { deep: !0, throttle: (t == null ? void 0 : t.updateThrottle) ?? s.updateThrottle } ); const o = (c = u.value) => { var w, k, y; const m = e.safeParse(c); return m.success ? (n.value = void 0, r.value = B.valid, u.value = m.data, a("update:modelValue", u.value), (k = t == null ? void 0 : t.onUpdate) == null || k.call(t, S(u.value)), a("valid", m.data), (y = t == null ? void 0 : t.onValid) == null || y.call(t, S(u.value)), !0) : (n.value = m.error.format(), r.value = B.invalid, a("invalid", n.value), (w = t == null ? void 0 : t.onInvalid) == null || w.call(t, S(n.value)), !1); }, h = () => { var c; return o() ? (a("submit", u.value), (c = t == null ? void 0 : t.onSubmit) == null || c.call(t, S(u.value)), !0) : !1; }, b = g(() => r.value === B.invalid); return M(l, { formData: u, submit: h, validate: o, errors: p(n), status: p(r), invalid: b }), { formData: u, submit: h, validate: o, errors: p(n), status: p(r), invalid: b }; }, render() { const s = () => { var a, o; return ((o = (a = this.$slots) == null ? void 0 : a.default) == null ? void 0 : o.call(a, { formData: this.formData, submit: this.submit, validate: this.validate, errors: this.errors, status: this.status, invalid: this.invalid })) ?? this.$slots.default; }; return x( "form", { onSubmit: re(this.submit, ["prevent"]) }, (this.template ?? (t == null ? void 0 : t.template)) && f ? [ x( f, { schema: this.template ?? (t == null ? void 0 : t.template) }, { default: s } ) ] : { default: s } ); } }); return { errors: n, status: r, formData: u, /** * An hack to add types to the default slot */ VvForm: i }; }, he = (e, l) => W({ name: "WrapperComponent", props: { name: { type: String, required: !0 }, tag: { type: String, default: void 0 } }, emits: ["invalid", "valid"], expose: ["fields", "invalid"], setup(f, { emit: n }) { const r = I(e), u = I(l, void 0), i = $(/* @__PURE__ */ new Set()), s = $(/* @__PURE__ */ new Map()), { name: a } = R(f); M(l, { name: p(a), errors: s, fields: i }), E( i, (h) => { u != null && u.fields && h.forEach((b) => { u == null || u.fields.value.add(b); }); }, { deep: !0 } ), E( () => new Map(s.value), (h, b) => { u != null && u.errors && (Array.from(b.keys()).forEach((c) => { u.errors.value.delete(c); }), Array.from(h.keys()).forEach((c) => { const m = h.get(c); m && u.errors.value.set(c, m); })); }, { deep: !0 } ); const o = g(() => r != null && r.invalid.value ? s.value.size > 0 : !1); return E(o, () => { o.value ? n("invalid") : n("valid"); }), { formData: r == null ? void 0 : r.formData, errors: r == null ? void 0 : r.errors, submit: r == null ? void 0 : r.submit, validate: r == null ? void 0 : r.validate, invalid: o, fields: i, fieldsErrors: s }; }, render() { var f, n; return this.tag ? x(this.tag, null, { default: () => { var r, u; return ((u = (r = this.$slots).default) == null ? void 0 : u.call(r, { invalid: this.invalid, formData: this.formData, submit: this.submit, validate: this.validate, errors: this.errors, fieldsErrors: this.fieldsErrors })) ?? this.$slots.defalut; } }) : ((n = (f = this.$slots).default) == null ? void 0 : n.call(f, { invalid: this.invalid, formData: this.formData, submit: this.submit, validate: this.validate, errors: this.errors, fieldsErrors: this.fieldsErrors })) ?? this.$slots.defalut; } }), be = (e, l) => { const t = W({ props: { schema: { type: [Array, Function], required: !0 } }, setup(f, { slots: n }) { const r = I(e); if (r != null && r.formData) return () => { var a; const u = typeof f.schema == "function" ? f.schema(r) : f.schema; let i; const s = u.reduce((o, h) => { const b = typeof h == "function" ? h(r) : h, { vvIs: c, vvName: m, vvSlots: w, vvChildren: k, vvIf: y, vvElseIf: V, vvType: z, vvDefaultValue: q, vvShowValid: d, vvContent: A, ...C } = b; if (y !== void 0) { if (typeof y == "string" ? i = !!G( Object(r.formData.value), y ) : typeof y == "function" ? i = _(y(r)) : i = _(y), !i) return o; } else if (V !== void 0 && i !== void 0) { if (i || (typeof V == "string" ? i = !!G( Object(r.formData.value), V ) : typeof V == "function" ? i = _(V(r)) : i = _(V), !i)) return o; } else i = void 0; const Z = k ? x(t, { schema: k }) : void 0; return m ? (o.push( x( l, { name: m, is: c, type: z, defaultValue: q, showValid: d, props: C }, w ?? Z ?? A ) ), o) : c ? (o.push( x( c, C, w ?? Z ?? A ) ), o) : (k && o.push(Z), o); }, []); return s.push( (a = n == null ? void 0 : n.default) == null ? void 0 : a.call(n, { formData: r == null ? void 0 : r.formData.value, submit: r == null ? void 0 : r.submit, validate: r == null ? void 0 : r.validate, errors: r == null ? void 0 : r.errors.value, status: r == null ? void 0 : r.status.value, invalid: r == null ? void 0 : r.invalid.value }) ), s; }; } }); return t; }, L = (e, l = {}) => { const t = Symbol(), f = Symbol(), n = Symbol(), r = he( t, f ), u = ce( t, f, n, l ), i = be(t, u), { VvForm: s, errors: a, status: o, formData: h } = me( e, t, l, i ); return { VvForm: s, VvFormWrapper: r, VvFormField: u, VvFormTemplate: i, formInjectionKey: t, formWrapperInjectionKey: f, formFieldInjectionKey: n, errors: a, status: o, formData: h }; }, Y = Symbol(), xe = (e) => { let l = {}; return e.schema && (l = L(e.schema, e)), { ...l, install(t, { global: f = !1 } = {}) { t.provide(Y, e), f && (t.config.globalProperties.$vvForm = e, l != null && l.VvForm && t.component("VvForm", l.VvForm), l != null && l.VvFormWrapper && t.component("VvFormWrapper", l.VvFormWrapper), l != null && l.VvFormField && t.component("VvFormField", l.VvFormField), l != null && l.VvFormTemplate && t.component("VvFormTemplate", l.VvFormTemplate)); } }; }, ke = (e, l = {}) => te() ? L( e, { ...I(Y, {}), ...l } ) : L(e, l), Oe = (e, l = {}) => L(e, l); export { v as FormFieldType, xe as createForm, J as defaultObjectBySchema, Oe as formFactory, Y as pluginInjectionKey, ke as useForm };