UNPKG

@volverjs/form-vue

Version:

Vue 3 Forms with @volverjs/ui-vue

1,066 lines (1,065 loc) 29.4 kB
import { ref as L, computed as O, readonly as G, defineComponent as C, h as F, withModifiers as te, toRaw as B, watch as A, isProxy as ve, onMounted as q, provide as T, toRefs as Q, useId as oe, inject as U, onBeforeUnmount as P, unref as _, resolveComponent as W, defineAsyncComponent as ce, getCurrentInstance as me } from "vue"; import { watchIgnorable as pe, throttleFilter as ye } from "@vueuse/core"; import { ZodObject as K, ZodDefault as he, ZodNullable as be, ZodSchema as ge, ZodArray as Ve, ZodRecord as we, ZodEffects as re, ZodOptional as ae, ZodError as De } from "zod"; var w = /* @__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))(w || {}), x = /* @__PURE__ */ ((e) => (e.invalid = "invalid", e.valid = "valid", e.submitting = "submitting", e.reset = "reset", e.updated = "updated", e.unknown = "unknown", e))(x || {}); function Z(e, i = {}) { const a = (u) => { let o = u; for (; o instanceof re; ) o = o.innerType(); return o instanceof ae && (o = o._def.innerType), o; }, v = (u) => { let o = u; for (; o instanceof re; ) o = o.innerType(); return o instanceof ae; }, n = a(e); return { ...(n instanceof K ? n._def.unknownKeys === "passthrough" : !1) ? i : {}, ...Object.fromEntries( Object.entries(n.shape).map( ([u, o]) => { const r = i[u], p = v(o); let d = a(o), c; if (d instanceof he && (c = d._def.defaultValue(), d = d._def.innerType), r === null && d instanceof be) return [u, r]; if (r == null && p) return [u, c]; if (d instanceof ge) { const m = o.safeParse(r); if (m.success) return [u, m.data ?? c]; } if (d instanceof Ve && Array.isArray(r) && r.length) { const m = a(d._def.type); if (m instanceof K) return [ u, r.map( (j) => Z( m, j && typeof j == "object" ? j : void 0 ) ) ]; } if (d instanceof we && r) { const m = a(d._def.valueType); if (m instanceof K) return [u, Object.keys(r).reduce((j, y) => (j[y] = Z(m, r[y]), j), {})]; } return d instanceof K ? [ u, Z( d, r && typeof r == "object" ? r : c ) ] : [u, c]; } ) ) }; } function je(e, i, a, v, n) { const l = L(), u = L(), o = O(() => u.value === x.invalid), r = L(), p = L(!1); let d; const c = (D) => { const S = Z(e, D); if (a?.class) { const t = a.class; return new t(S); } return S; }, m = async (D = r.value, S) => { if (d = S?.fields, p.value) return !0; const t = S?.superRefine ? await e.superRefine(S.superRefine).safeParseAsync(D) : await e.safeParseAsync(D); if (!t.success) { if (u.value = x.invalid, !d?.size) return l.value = t.error.format(), !1; const V = t.error.issues.filter( ($) => d?.has($.path.join(".")) ); return V.length ? (l.value = new De(V).format(), !1) : (l.value = void 0, !0); } return l.value = void 0, u.value = x.valid, r.value = c(t.data), !0; }, j = () => { l.value = void 0, u.value = void 0, d = void 0; }, y = () => { r.value = c(), j(), u.value = x.reset; }, f = async (D) => p.value || !await m(void 0, D) ? !1 : (u.value = x.submitting, !0), { ignoreUpdates: h, stop: g } = pe( r, () => { u.value = x.updated; }, { deep: !0, eventFilter: ye(a?.updateThrottle ?? 500) } ), I = G(l), R = G(u), k = C({ name: "VvForm", props: { continuousValidation: { type: Boolean, default: !1 }, modelValue: { type: Object, default: () => ({}) }, readonly: { type: Boolean, default: a?.readonly ?? !1 }, tag: { type: String, default: "form" }, template: { type: [Array, Function], default: void 0 }, superRefine: { type: Function, default: void 0 }, validateFields: { type: Array, default: void 0 } }, emits: [ "invalid", "submit", "update:modelValue", "update:readonly", "valid", "reset" ], expose: [ "errors", "invalid", "readonly", "status", "submit", "tag", "template", "valid", "validate", "clear", "reset" ], slots: Object, setup(D, { emit: S }) { return r.value = c(B(D.modelValue)), A( () => D.modelValue, (t) => { if (t) { const V = ve(t) ? B(t) : t; if (JSON.stringify(V) === JSON.stringify(B(r.value))) return; r.value = typeof V?.clone == "function" ? V.clone() : JSON.parse(JSON.stringify(V)); } }, { deep: !0 } ), A(u, async (t) => { if (t === x.invalid) { const V = B(l.value); S("invalid", V), a?.onInvalid?.( V ); return; } if (t === x.valid) { const V = B(r.value); S("valid", V), a?.onValid?.(V), S("update:modelValue", V), a?.onUpdate?.(V); return; } if (t === x.submitting) { const V = B(r.value); S("submit", V), a?.onSubmit?.(V); return; } if (t === x.reset) { const V = B(r.value); S("reset", V), a?.onReset?.(V); return; } if (t === x.updated) { if ((l.value || a?.continuousValidation || D.continuousValidation) && await m(void 0, { superRefine: D.superRefine, fields: d ?? new Set(D.validateFields) }), !r.value || !D.modelValue || JSON.stringify(r.value) !== JSON.stringify(D.modelValue)) { const V = B(r.value); S("update:modelValue", V), a?.onUpdate?.(V); } u.value === x.updated && (u.value = x.unknown); } }), q(() => { p.value = D.readonly; }), A( () => D.readonly, (t) => { p.value = t; } ), A(p, (t) => { t !== D.readonly && S("update:readonly", p.value); }), T(i, { clear: j, errors: I, formData: r, ignoreUpdates: h, invalid: o, readonly: p, reset: y, status: R, stopUpdatesWatch: g, submit: f, validate: m, wrappers: n }), { clear: j, errors: I, formData: r, ignoreUpdates: h, invalid: o, isReadonly: p, reset: y, status: R, stopUpdatesWatch: g, submit: () => f({ superRefine: D.superRefine, fields: new Set(D.validateFields) }), validate: m, wrappers: n }; }, render() { const D = () => this.$slots?.default?.({ errors: I.value, formData: r.value, invalid: o.value, readonly: p.value, status: R.value, wrappers: n, clear: j, ignoreUpdates: h, reset: y, stopUpdatesWatch: g, submit: f, validate: m }) ?? this.$slots.default; return F( this.tag, { onSubmit: te(this.submit, ["prevent"]), onReset: te(this.reset, ["prevent"]) }, (this.template ?? a?.template) && v ? [ F( v, { schema: this.template ?? a?.template }, { default: D } ) ] : { default: D } ); } }); return { clear: j, errors: l, formData: r, ignoreUpdates: h, invalid: o, readonly: p, reset: y, status: u, wrappers: n, stopUpdatesWatch: g, submit: f, validate: m, VvForm: k }; } function H(e) { return Array.isArray(e); } function Oe(e) { return !1; } function ne(e) { return e === null; } function le(e) { return typeof e == "object"; } function ue(e) { return typeof e == "string"; } function z(e) { return typeof e > "u"; } const Se = /^[0-9]+$/, Re = ["__proto__", "prototype", "constructor"]; function N(e, i, a) { const v = Oe() ? a : void 0; if (!le(e) || !ue(i)) return v; const n = ie(i); if (n.length !== 0) { for (const l of n) { if (l === "*") continue; const u = function(o) { return o.map((r) => z(r) || ne(r) ? r : H(r) ? u(r) : r[l]); }; if (H(e) && !Se.test(l) ? e = u(e) : e = e[l], z(e) || ne(e)) break; } return z(e) ? v : e; } } function X(e, i, a) { if (!le(e) || !ue(i)) return; const v = ie(i); if (v.length === 0) return; const n = v.length; for (let l = 0; l < n; l++) { const u = v[l]; if (l === n - 1) { e[u] = a; return; } if (u === "*" && H(e)) { const o = v.slice(l + 1).join("."); for (const r of e) X(r, o, a); return; } z(e[u]) && (e[u] = {}), e = e[u]; } } function ie(e) { const i = e.split(/[.]|(?:\[(\d|\*)\])/).filter((a) => !!a); return i.some((a) => Re.indexOf(a) !== -1) ? [] : i; } function xe(e, i, a, v) { return C({ name: "VvFormField", props: { type: { type: String, validator: (n) => Object.values(w).includes(n), default: w.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 }, readonly: { type: Boolean, default: void 0 } }, emits: [ "invalid", "update:formData", "update:modelValue", "valid" ], expose: [ "component", "errors", "hasProps", "invalid", "invalidLabel", "is", "type" ], slots: Object, setup(n, { slots: l, emit: u }) { const { props: o, name: r } = Q(n), p = oe(), d = U(i, void 0); d && d.fields.value.set(p, n.name); const c = U(e), m = O({ get() { if (c?.formData) return N( new Object(c.formData.value), String(n.name) ); }, set(t) { c?.formData && (X( new Object(c.formData.value), String(n.name), t ), u("update:modelValue", { newValue: m.value, formData: c?.formData })); } }); q(() => { m.value === void 0 && n.defaultValue !== void 0 && (m.value = n.defaultValue); }), P(() => { d && d.fields.value.delete(p); }); const j = O(() => { if (c?.errors.value) return N(c.errors.value, String(n.name)); }), y = O(() => j.value?._errors), f = O(() => j.value !== void 0), h = A(f, (t) => { if (t) { u("invalid", j.value), d && d.errors.value.set( String(n.name), j.value ); return; } u("valid", m.value), d && d.errors.value.delete( n.name ); }), g = A( () => c?.formData, () => { u("update:formData", c?.formData); }, { deep: !0 } ); P(() => { h(), g(); }); const I = (t) => { t instanceof InputEvent && (t = t.target.value), m.value = t; }, R = O(() => { let t = o.value; return typeof t == "function" && (t = t(c?.formData)), Object.keys(t).reduce( (V, $) => (V[$] = _(t[$]), V), {} ); }), k = O(() => c?.readonly.value || d?.readonly.value ? !0 : R.value.readonly ?? n.readonly), D = O(() => ({ ...R.value, name: R.value.name ?? n.name, invalid: f.value, valid: n.showValid ? !!(!f.value && m.value) : void 0, type: ((t) => { if ([ w.color, w.date, w.datetimeLocal, w.email, w.month, w.number, w.password, w.search, w.tel, w.text, w.time, w.url, w.week ].includes(t)) return t; })(n.type), invalidLabel: y.value, modelValue: m.value, readonly: k.value, "onUpdate:modelValue": I })); return T(a, { name: G(r), errors: G(j) }), { component: O(() => { if (n.type === w.custom) return { render() { return l.default?.({ errors: j.value, formData: c?.formData.value, formErrors: c?.errors.value, invalid: f.value, invalidLabel: y.value, modelValue: m.value, readonly: k.value, onUpdate: I, submit: c?.submit, validate: c?.validate }) ?? l.default; } }; if (!(v?.lazyLoad ?? n.lazyLoad)) { let t; switch (n.type) { case w.select: t = W("VvSelect"); break; case w.checkbox: t = W("VvCheckbox"); break; case w.radio: t = W("VvRadio"); break; case w.textarea: t = W("VvTextarea"); break; case w.radioGroup: t = W("VvRadioGroup"); break; case w.checkboxGroup: t = W("VvCheckboxGroup"); break; case w.combobox: t = W("VvCombobox"); break; default: t = W("VvInputText"); } if (typeof t != "string") return t; console.warn( `[@volverjs/form-vue]: ${t} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.` ); } return ce(async () => { switch (v?.sideEffects && await Promise.resolve(v.sideEffects(n.type)), n.type) { case w.textarea: return import("@volverjs/ui-vue/vv-textarea"); case w.radio: return import("@volverjs/ui-vue/vv-radio"); case w.radioGroup: return import("@volverjs/ui-vue/vv-radio-group"); case w.checkbox: return import("@volverjs/ui-vue/vv-checkbox"); case w.checkboxGroup: return import("@volverjs/ui-vue/vv-checkbox-group"); case w.select: return import("@volverjs/ui-vue/vv-select"); case w.combobox: return import("@volverjs/ui-vue/vv-combobox"); } return import("@volverjs/ui-vue/vv-input-text"); }); }), hasProps: D, invalid: f }; }, render() { return this.is ? F(this.is, this.hasProps, this.$slots) : this.type === w.custom ? F(this.component, null, this.$slots) : F(this.component, this.hasProps, this.$slots); } }); } function Ie(e, i, a) { return C({ name: "VvFormFieldsGroup", props: { is: { type: [Object, String], default: void 0 }, names: { type: [Array, Object], required: !0 }, props: { type: [Object, Function], default: () => ({}) }, showValid: { type: Boolean, default: !1 }, defaultValues: { type: [Object], default: void 0 }, readonly: { type: Boolean, default: void 0 } }, emits: [ "invalid", "update:formData", "update:modelValue", "valid" ], expose: [ "component", "errors", "hasProps", "invalid", "invalidLabels", "is" ], slots: Object, setup(v, { slots: n, emit: l }) { const { props: u, names: o, defaultValues: r } = Q(v), p = oe(), d = O(() => Array.isArray(o.value) ? o.value : Object.values(o.value)), c = O(() => Array.isArray(o.value) ? o.value : Object.keys(o.value)), m = O(() => Array.isArray(o.value) ? o.value.reduce((s, b) => (s[String(b)] = b, s), {}) : o.value), j = O(() => Object.keys(m.value).reduce((s, b) => (s[String(m.value[b])] = b, s), {})), y = U(i, void 0); y && d.value.forEach((s) => { y.fields.value.set(`${p}-${s}`, s); }); const f = U(e), h = O({ get() { return f?.formData ? c.value.reduce((s, b) => (s[b] = N( new Object(f.formData.value), m.value[b] ), s), {}) : {}; }, set(s) { f?.formData && (c.value.forEach((b) => { X( new Object(f.formData.value), m.value[b], s?.[b] ); }), l("update:modelValue", { newValue: h.value, formData: f?.formData })); } }); q(() => { r.value && d.value.forEach((s) => { r.value?.[s] !== void 0 && h.value[s] === void 0 && (h.value = { ...h.value, [s]: r.value?.[s] }); }); }), P(() => { y && d.value.forEach((s) => { y.fields.value.delete( `${p}-${s}` ); }); }); const g = O(() => { if (!f?.errors.value) return; const s = d.value.reduce((b, E) => { if (!f.errors.value) return b; const ee = N(f.errors.value, String(E)); return ee === void 0 || (b[String(E)] = ee), b; }, {}); if (Object.keys(s).length !== 0) return s; }), I = O(() => { if (!g.value) return; const s = Object.keys(g.value).reduce((b, E) => (g.value?.[E] && (b[j.value[E]] = g.value[E]._errors), b), {}); if (Object.keys(s).length !== 0) return s; }), R = O(() => g.value !== void 0), k = O(() => c.value.reduce((s, b) => (s[b] = !!g.value?.[j.value[b]], s), {})), D = A(R, () => { if (R.value) { l("invalid", g.value), y && d.value.forEach((s) => { if (!g.value?.[s]) { y.errors.value.delete( s ); return; } y.errors.value.set( s, g.value?.[s] ); }); return; } l("valid", h.value), y && d.value.forEach((s) => { y.errors.value.delete( s ); }); }), S = A( () => f?.formData, () => { l("update:formData", f?.formData); }, { deep: !0 } ); P(() => { D(), S(); }); const t = (s) => { h.value = s; }, V = (s, b) => { b instanceof InputEvent && (b = b.target.value), c.value.includes(s) && (h.value = { ...h.value, [s]: b }); }, $ = O(() => { let s = u.value; return typeof s == "function" && (s = s(f?.formData)), Object.keys(s).reduce( (b, E) => (b[E] = _(s[E]), b), {} ); }), Y = O(() => f?.readonly.value ? !0 : $.value.readonly ?? v.readonly), de = O(() => c.value.reduce((s, b) => (s[`onUpdate:${b}`] = (E) => { V(b, E); }, s), { "onUpdate:modelValue": t })), fe = O(() => ({ ...de.value, ...$.value, ...h.value, modelValue: h.value, names: $.value.name ?? d.value, invalid: R.value, invalids: k.value, valid: v.showValid ? !!(!R.value && h.value) : void 0, invalidLabels: I.value, readonly: Y.value })); return T(a, { names: G(o), errors: G(g) }), { component: O(() => ({ render() { return n.default?.({ errors: g.value, formData: f?.formData.value, formErrors: f?.errors.value, invalid: R.value, invalids: k.value, invalidLabels: I.value, modelValue: h.value, onUpdate: t, onUpdateField: V, readonly: Y.value, submit: f?.submit, validate: f?.validate }) ?? n.default; } })), hasProps: fe, invalid: R }; }, render() { return this.is ? F(this.is, this.hasProps, this.$slots) : F(this.component, null, this.$slots); } }); } function Fe(e, i) { return C({ name: "VvFormWrapper", props: { name: { type: String, required: !0 }, tag: { type: String, default: void 0 }, readonly: { type: Boolean, default: !1 } }, emits: ["invalid", "valid"], expose: [ "clear", "errors", "fields", "fieldsErrors", "formData", "invalid", "readonly", "reset", "submit", "tag", "validate", "validateWrapper" ], slots: Object, setup(a, { emit: v }) { const n = U(e), l = U(i, void 0), u = L(/* @__PURE__ */ new Map()), o = L(/* @__PURE__ */ new Map()), { name: r } = Q(a), p = O(() => n?.invalid.value ? o.value.size > 0 : !1); A(p, (y) => { if (y) { v("invalid"); return; } v("valid"); }); const d = O(() => n?.readonly.value || a.readonly), c = { name: G(r), errors: o, invalid: G(p), readonly: G(d), fields: u }; T(i, c); const m = O(() => new Map(u.value)); A( m, (y, f) => { l?.fields && (f.forEach((h, g) => { y.has(g) || l?.fields.value.delete(g); }), y.forEach((h, g) => { l?.fields.value.has(g) || l?.fields.value.set(g, h); })); }, { deep: !0 } ), A( o, (y) => { l?.errors && u.value.forEach((f) => { if (y.has(f) || l.errors.value.delete(f), y.has(f)) { const h = y.get(f); h && l.errors.value.set(f, h); } }); }, { deep: !0 } ), q(() => { if (!n?.wrappers || !r.value) { console.warn("[@volverjs/form-vue]: Invalid wrapper registration state"); return; } if (n.wrappers.has(r.value)) { console.warn(`[@volverjs/form-vue]: wrapper name "${r.value}" is already used`); return; } n.wrappers.set(r.value, c); }), P(() => { n?.wrappers && r.value && n.wrappers.delete(r.value); }); const j = () => n?.validate(void 0, { fields: new Set(u.value.values()) }) ?? Promise.resolve(!0); return { errors: n?.errors, fields: u, fieldsErrors: o, formData: n?.formData, invalid: p, readonly: d, clear: n?.clear, reset: n?.reset, submit: n?.submit, validate: n?.validate, validateWrapper: j }; }, render() { const a = () => this.$slots.default?.({ errors: this.errors, fieldsErrors: this.fieldsErrors, formData: this.formData, invalid: this.invalid, readonly: this.readonly, clear: this.clear, reset: this.reset, submit: this.submit, validate: this.validate, validateWrapper: this.validateWrapper }); return this.tag ? F(this.tag, null, { default: a }) : a(); } }); } function ke(e, i) { const a = C({ name: "VvFormTemplate", props: { schema: { type: [Array, Function], required: !0 }, scope: { type: Object, default: () => ({}) } }, slots: Object, setup(v, { slots: n }) { const l = U(e); if (l?.formData) return () => { const u = typeof v.schema == "function" ? v.schema( l, v.scope ) : v.schema; let o; const r = u.reduce((p, d) => { const c = typeof d == "function" ? d(l, v.scope) : d, { vvIs: m, vvName: j, vvSlots: y, vvChildren: f, vvIf: h, vvElseIf: g, vvType: I, vvDefaultValue: R, vvShowValid: k, vvContent: D, ...S } = c; if (h !== void 0) { if (typeof h == "string" ? o = !!N( new Object(l.formData.value), h ) : typeof h == "function" ? o = _(h(l)) : o = _(h), !o) return p; } else if (g !== void 0 && o !== void 0) { if (o || (typeof g == "string" ? o = !!N( new Object(l.formData.value), g ) : typeof g == "function" ? o = _(g(l)) : o = _(g), !o)) return p; } else o = void 0; let t; return f && (typeof m == "string" ? t = F(a, { schema: f }) : t = { default: (V) => F(a, { schema: f, scope: V }) }), j ? (p.push( F( i, { name: j, is: m, type: I, defaultValue: R, showValid: k, props: S }, y ?? t ?? D ) ), p) : m ? (p.push( F( m, S, y ?? t ?? D ) ), p) : (t && ("default" in t ? p.push(t.default(v.scope)) : p.push(t)), p); }, []); return r.push( n?.default?.({ errors: l?.errors.value, formData: l?.formData.value, invalid: l?.invalid.value, status: l?.status.value, submit: l?.submit, validate: l?.validate, clear: l?.clear, reset: l?.reset }) ), r; }; } }); return a; } function J(e, i = {}) { const a = Symbol("formInjectionKey"), v = Symbol("formWrapperInjectionKey"), n = Symbol("formFieldInjectionKey"), l = Symbol("formFieldsGroupInjectionKey"), u = Fe( a, v ), o = xe( a, v, n, i ), r = Ie( a, v, l ), p = ke(a, o), d = /* @__PURE__ */ new Map(), { clear: c, errors: m, formData: j, ignoreUpdates: y, invalid: f, readonly: h, reset: g, status: I, stopUpdatesWatch: R, submit: k, validate: D, VvForm: S } = je(e, a, i, p, d); return { clear: c, errors: m, formData: j, formFieldInjectionKey: n, formInjectionKey: a, formWrapperInjectionKey: v, ignoreUpdates: y, invalid: f, readonly: h, reset: g, status: I, stopUpdatesWatch: R, submit: k, validate: D, wrappers: d, VvForm: S, VvFormField: o, VvFormFieldsGroup: r, VvFormTemplate: p, VvFormWrapper: u }; } const se = Symbol("pluginInjectionKey"); function Be(e) { let i = {}; return e.schema && (i = J(e.schema, e)), { ...i, install(a, { global: v = !1 } = {}) { a.provide(se, e), v && (a.config.globalProperties.$vvForm = e, i?.VvForm && a.component("VvForm", i.VvForm), i?.VvFormWrapper && a.component("VvFormWrapper", i.VvFormWrapper), i?.VvFormField && a.component("VvFormField", i.VvFormField), i?.VvFormFieldsGroup && a.component("VvFormFieldsGroup", i.VvFormFieldsGroup), i?.VvFormTemplate && a.component("VvFormTemplate", i.VvFormTemplate)); } }; } const M = /* @__PURE__ */ new Map(); function We(e, i = {}) { if (i.scope && M.has(i.scope)) return M.get(i.scope); if (!me()) { const v = J(e, i); return i.scope && M.set(i.scope, v), v; } const a = J( e, { ...U(se, {}), ...i } ); return i.scope && M.set(i.scope, a), a; } function Ue(e, i = {}) { return J(e, i); } export { w as FormFieldType, Be as createForm, Z as defaultObjectBySchema, Ue as formType, se as pluginInjectionKey, We as useForm };