@volverjs/form-vue 1.0.0-beta.22 → 1.0.0-beta.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(x,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("vue"),require("@vueuse/core"),require("zod")):typeof define=="function"&&define.amd?define(["exports","vue","@vueuse/core","zod"],r):(x=typeof globalThis<"u"?globalThis:x||self,r(x["@volverjs/form-vue"]={},x.Vue,x.VueUseCore,x.zod))})(this,function(x,r,K,k){"use strict";function U(e){return Array.isArray(e)}function H(e){return typeof e<"u"}function L(e){return e===null}function $(e){return typeof e=="object"}function M(e){return typeof e=="string"}function Z(e){return typeof e>"u"}const Q=/^[0-9]+$/,X=["__proto__","prototype","constructor"];function B(e,s,t){const o=H(t)?t:void 0;if(!$(e)||!M(s))return o;const n=J(s);if(n.length!==0){for(const l of n){if(l==="*")continue;const f=function(u){return u.map(a=>Z(a)||L(a)?a:U(a)?f(a):a[l])};if(U(e)&&!Q.test(l)?e=f(e):e=e[l],Z(e)||L(e))break}return Z(e)?o:e}}function q(e,s,t){if(!$(e)||!M(s))return;const o=J(s);if(o.length===0)return;const n=o.length;for(let l=0;l<n;l++){const f=o[l];if(l===n-1){e[f]=t;return}if(f==="*"&&U(e)){const u=o.slice(l+1).join(".");for(const a of e)q(a,u,t);return}Z(e[f])&&(e[f]={}),e=e[f]}}function J(e){const s=e.split(/[.]|(?:\[(\d|\*)\])/).filter(t=>!!t);return s.some(t=>X.indexOf(t)!==-1)?[]:s}var c=(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))(c||{}),S=(e=>(e.invalid="invalid",e.valid="valid",e.submitting="submitting",e.reset="reset",e.updated="updated",e.unknown="unknown",e))(S||{});function Y(e,s,t,o){return r.defineComponent({name:"VvFormField",props:{type:{type:String,validator:n=>Object.values(c).includes(n),default:c.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:f}){const u=r.inject(s,void 0);u&&u.fields.value.add(n.name);const a=r.inject(e),{props:p,name:d}=r.toRefs(n),v=r.computed({get(){if(a!=null&&a.formData)return B(new Object(a.formData.value),String(n.name))},set(i){a!=null&&a.formData&&(q(new Object(a.formData.value),String(n.name),i),f("update:modelValue",{newValue:v.value,formData:a==null?void 0:a.formData}))}});r.onMounted(()=>{v.value===void 0&&n.defaultValue!==void 0&&(v.value=n.defaultValue)});const h=r.computed(()=>{if(a!=null&&a.errors.value)return B(a.errors.value,String(n.name))}),m=r.computed(()=>{var i;return(i=h.value)==null?void 0:i._errors}),b=r.computed(()=>h.value!==void 0),R=r.watch(b,()=>{b.value?(f("invalid",m.value),u&&u.errors.value.set(n.name,{_errors:m.value})):(f("valid",v.value),u&&u.errors.value.delete(n.name))}),E=r.watch(()=>a==null?void 0:a.formData,()=>{f("update:formData",a==null?void 0:a.formData)},{deep:!0});r.onBeforeUnmount(()=>{R(),E()});const O=i=>{v.value=i},C=r.computed(()=>{let i=p.value;return typeof i=="function"&&(i=i(a==null?void 0:a.formData)),Object.keys(i).reduce((W,I)=>(W[I]=r.unref(i[I]),W),{})}),V=r.computed(()=>a!=null&&a.readonly.value?!0:C.value.readonly??n.readonly),g=r.computed(()=>({...C.value,name:C.value.name??n.name,invalid:b.value,valid:n.showValid?!!(!b.value&&v.value):void 0,type:(i=>{if([c.color,c.date,c.datetimeLocal,c.email,c.month,c.number,c.password,c.search,c.tel,c.text,c.time,c.url,c.week].includes(i))return i})(n.type),invalidLabel:m.value,modelValue:v.value,readonly:V.value,"onUpdate:modelValue":O}));return r.provide(t,{name:r.readonly(d),errors:r.readonly(h)}),{component:r.computed(()=>{if(n.type===c.custom)return{render(){var i;return((i=l.default)==null?void 0:i.call(l,{errors:h.value,formData:a==null?void 0:a.formData.value,formErrors:a==null?void 0:a.errors.value,invalid:b.value,invalidLabel:m.value,modelValue:v.value,onUpdate:O,readonly:V.value,submit:a==null?void 0:a.submit,validate:a==null?void 0:a.validate}))??l.default}};if(!((o==null?void 0:o.lazyLoad)??n.lazyLoad)){let i;switch(n.type){case c.select:i=r.resolveComponent("VvSelect");break;case c.checkbox:i=r.resolveComponent("VvCheckbox");break;case c.radio:i=r.resolveComponent("VvRadio");break;case c.textarea:i=r.resolveComponent("VvTextarea");break;case c.radioGroup:i=r.resolveComponent("VvRadioGroup");break;case c.checkboxGroup:i=r.resolveComponent("VvCheckboxGroup");break;case c.combobox:i=r.resolveComponent("VvCombobox");break;default:i=r.resolveComponent("VvInputText")}if(typeof i!="string")return i;console.warn(`[form-vue warn]: ${i} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.`)}return r.defineAsyncComponent(async()=>{switch(o!=null&&o.sideEffects&&await Promise.resolve(o.sideEffects(n.type)),n.type){case c.textarea:return import("@volverjs/ui-vue/vv-textarea");case c.radio:return import("@volverjs/ui-vue/vv-radio");case c.radioGroup:return import("@volverjs/ui-vue/vv-radio-group");case c.checkbox:return import("@volverjs/ui-vue/vv-checkbox");case c.checkboxGroup:return import("@volverjs/ui-vue/vv-checkbox-group");case c.select:return import("@volverjs/ui-vue/vv-select");case c.combobox:return import("@volverjs/ui-vue/vv-combobox")}return import("@volverjs/ui-vue/vv-input-text")})}),hasProps:g,invalid:b}},render(){return this.is?r.h(this.is,this.hasProps,this.$slots):this.type===c.custom?r.h(this.component,null,this.$slots):r.h(this.component,this.hasProps,this.$slots)}})}function A(e,s={}){const t=f=>{let u=f;for(;u instanceof k.ZodEffects;)u=u.innerType();return u instanceof k.ZodOptional&&(u=u._def.innerType),u},o=f=>{let u=f;for(;u instanceof k.ZodEffects;)u=u.innerType();return u instanceof k.ZodOptional},n=t(e);return{...(n instanceof k.ZodObject?n._def.unknownKeys==="passthrough":!1)?s:{},...Object.fromEntries(Object.entries(n.shape).map(([f,u])=>{const a=s[f],p=o(u);let d=t(u),v;if(d instanceof k.ZodDefault&&(v=d._def.defaultValue(),d=d._def.innerType),a===null&&d instanceof k.ZodNullable)return[f,a];if(a==null&&p)return[f,v];if(d instanceof k.ZodSchema){const h=u.safeParse(a);if(h.success)return[f,h.data??v]}if(d instanceof k.ZodArray&&Array.isArray(a)&&a.length){const h=t(d._def.type);if(h instanceof k.ZodObject)return[f,a.map(m=>A(h,m&&typeof m=="object"?m:void 0))]}if(d instanceof k.ZodRecord&&a){const h=t(d._def.valueType);if(h instanceof k.ZodObject)return[f,Object.keys(a).reduce((m,b)=>(m[b]=A(h,a[b]),m),{})]}return d instanceof k.ZodObject?[f,A(d,a&&typeof a=="object"?a:v)]:[f,v]}))}}function z(e,s,t,o){const n=r.ref(),l=r.ref(),f=r.computed(()=>l.value===S.invalid),u=r.ref(),a=r.ref(!1);let p;const d=async(V=u.value,g)=>{if(p=g,a.value)return!0;const y=await e.safeParseAsync(V);if(!y.success){if(l.value=S.invalid,!g)return n.value=y.error.format(),!1;const i=y.error.issues.filter(W=>g.has(W.path.join(".")));return i.length?(n.value=new k.ZodError(i).format(),!1):(n.value=void 0,!0)}return n.value=void 0,l.value=S.valid,u.value=y.data,!0},v=()=>{n.value=void 0,l.value=void 0,p=void 0},h=()=>{u.value=A(e),v(),l.value=S.reset},m=async()=>a.value||!await d()?!1:(l.value=S.submitting,!0),{ignoreUpdates:b,stop:R}=K.watchIgnorable(u,()=>{l.value=S.updated},{deep:!0,eventFilter:K.throttleFilter((t==null?void 0:t.updateThrottle)??500)}),E=r.readonly(n),O=r.readonly(l),C=r.defineComponent({name:"VvForm",props:{continuousValidation:{type:Boolean,default:!1},modelValue:{type:Object,default:()=>({})},readonly:{type:Boolean,default:(t==null?void 0:t.readonly)??!1},tag:{type:String,default:"form"},template:{type:[Array,Function],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(V,{emit:g}){return u.value=A(e,r.toRaw(V.modelValue)),r.watch(()=>V.modelValue,y=>{if(y){const i=r.isProxy(y)?r.toRaw(y):y;if(JSON.stringify(i)===JSON.stringify(r.toRaw(u.value)))return;u.value=typeof(i==null?void 0:i.clone)=="function"?i.clone():JSON.parse(JSON.stringify(i))}},{deep:!0}),r.watch(l,async y=>{var i,W,I,N,T,P;if(y===S.invalid){const w=r.toRaw(n.value);g("invalid",w),(i=t==null?void 0:t.onInvalid)==null||i.call(t,w);return}if(y===S.valid){const w=r.toRaw(u.value);g("valid",w),(W=t==null?void 0:t.onValid)==null||W.call(t,w),g("update:modelValue",w),(I=t==null?void 0:t.onUpdate)==null||I.call(t,w);return}if(y===S.submitting){const w=r.toRaw(u.value);g("submit",w),(N=t==null?void 0:t.onSubmit)==null||N.call(t,w);return}if(y===S.reset){const w=r.toRaw(u.value);g("reset",w),(T=t==null?void 0:t.onReset)==null||T.call(t,w);return}if(y===S.updated){if((n.value||t!=null&&t.continuousValidation||V.continuousValidation)&&await d(void 0,p),!u.value||!V.modelValue||JSON.stringify(u.value)!==JSON.stringify(V.modelValue)){const w=r.toRaw(u.value);g("update:modelValue",w),(P=t==null?void 0:t.onUpdate)==null||P.call(t,w)}l.value===S.updated&&(l.value=S.unknown)}}),r.onMounted(()=>{a.value=V.readonly}),r.watch(()=>V.readonly,y=>{a.value=y}),r.watch(a,y=>{y!==V.readonly&&g("update:readonly",a.value)}),r.provide(s,{clear:v,errors:E,formData:u,ignoreUpdates:b,invalid:f,readonly:a,reset:h,status:O,stopUpdatesWatch:R,submit:m,validate:d}),{clear:v,errors:E,formData:u,ignoreUpdates:b,invalid:f,isReadonly:a,reset:h,status:O,stopUpdatesWatch:R,submit:m,validate:d}},render(){const V=()=>{var g,y;return((y=(g=this.$slots)==null?void 0:g.default)==null?void 0:y.call(g,{clear:v,errors:E,formData:u,ignoreUpdates:b,invalid:f,readonly:a,reset:h,status:O,stopUpdatesWatch:R,submit:m,validate:d}))??this.$slots.default};return r.h(this.tag,{onSubmit:r.withModifiers(this.submit,["prevent"]),onReset:r.withModifiers(this.reset,["prevent"])},(this.template??(t==null?void 0:t.template))&&o?[r.h(o,{schema:this.template??(t==null?void 0:t.template)},{default:V})]:{default:V})}});return{clear:v,errors:n,formData:u,ignoreUpdates:b,invalid:f,readonly:a,reset:h,status:l,stopUpdatesWatch:R,submit:m,validate:d,VvForm:C}}function j(e,s){return r.defineComponent({name:"VvFormWrapper",props:{name:{type:String,required:!0},tag:{type:String,default:void 0}},emits:["invalid","valid"],expose:["clear","errors","fields","fieldsErrors","formData","invalid","reset","submit","tag","validate","validateWrapper"],slots:Object,setup(t,{emit:o}){const n=r.inject(e),l=r.inject(s,void 0),f=r.ref(new Set),u=r.ref(new Map),{name:a}=r.toRefs(t);r.provide(s,{name:r.readonly(a),errors:u,fields:f}),r.watch(f,v=>{l!=null&&l.fields&&v.forEach(h=>{l==null||l.fields.value.add(h)})},{deep:!0}),r.watch(()=>new Map(u.value),(v,h)=>{l!=null&&l.errors&&(Array.from(h.keys()).forEach(m=>{l.errors.value.delete(m)}),Array.from(v.keys()).forEach(m=>{const b=v.get(m);b&&l.errors.value.set(m,b)}))},{deep:!0});const p=r.computed(()=>n!=null&&n.invalid.value?u.value.size>0:!1);r.watch(p,()=>{p.value?o("invalid"):o("valid")});const d=()=>(n==null?void 0:n.validate(void 0,f.value))??Promise.resolve(!0);return{clear:n==null?void 0:n.clear,errors:n==null?void 0:n.errors,fields:f,fieldsErrors:u,formData:n==null?void 0:n.formData,invalid:p,reset:n==null?void 0:n.reset,submit:n==null?void 0:n.submit,validate:n==null?void 0:n.validate,validateWrapper:d}},render(){const t=()=>{var o,n;return(n=(o=this.$slots).default)==null?void 0:n.call(o,{clear:this.clear,errors:this.errors,fieldsErrors:this.fieldsErrors,formData:this.formData,invalid:this.invalid,reset:this.reset,submit:this.submit,validate:this.validate,validateWrapper:this.validateWrapper})};return this.tag?r.h(this.tag,null,{default:t}):t()}})}function D(e,s){const t=r.defineComponent({name:"VvFormTemplate",props:{schema:{type:[Array,Function],required:!0},scope:{type:Object,default:()=>({})}},slots:Object,setup(o,{slots:n}){const l=r.inject(e);if(l!=null&&l.formData)return()=>{var p;const f=typeof o.schema=="function"?o.schema(l,o.scope):o.schema;let u;const a=f.reduce((d,v)=>{const h=typeof v=="function"?v(l,o.scope):v,{vvIs:m,vvName:b,vvSlots:R,vvChildren:E,vvIf:O,vvElseIf:C,vvType:V,vvDefaultValue:g,vvShowValid:y,vvContent:i,...W}=h;if(O!==void 0){if(typeof O=="string"?u=!!B(new Object(l.formData.value),O):typeof O=="function"?u=r.unref(O(l)):u=r.unref(O),!u)return d}else if(C!==void 0&&u!==void 0){if(u||(typeof C=="string"?u=!!B(new Object(l.formData.value),C):typeof C=="function"?u=r.unref(C(l)):u=r.unref(C),!u))return d}else u=void 0;let I;return E&&(typeof m=="string"?I=r.h(t,{schema:E}):I={default:N=>r.h(t,{schema:E,scope:N})}),b?(d.push(r.h(s,{name:b,is:m,type:V,defaultValue:g,showValid:y,props:W},R??I??i)),d):m?(d.push(r.h(m,W,R??I??i)),d):(I&&("default"in I?d.push(I.default(o.scope)):d.push(I)),d)},[]);return a.push((p=n==null?void 0:n.default)==null?void 0:p.call(n,{errors:l==null?void 0:l.errors.value,formData:l==null?void 0:l.formData.value,invalid:l==null?void 0:l.invalid.value,status:l==null?void 0:l.status.value,submit:l==null?void 0:l.submit,validate:l==null?void 0:l.validate,clear:l==null?void 0:l.clear,reset:l==null?void 0:l.reset})),a}}});return t}function _(e,s={}){const t=Symbol("formInjectionKey"),o=Symbol("formWrapperInjectionKey"),n=Symbol("formFieldInjectionKey"),l=j(t,o),f=Y(t,o,n,s),u=D(t,f),{clear:a,errors:p,formData:d,ignoreUpdates:v,invalid:h,readonly:m,reset:b,status:R,stopUpdatesWatch:E,submit:O,validate:C,VvForm:V}=z(e,t,s,u);return{clear:a,errors:p,formData:d,formFieldInjectionKey:n,formInjectionKey:t,formWrapperInjectionKey:o,ignoreUpdates:v,invalid:h,readonly:m,reset:b,status:R,stopUpdatesWatch:E,submit:O,validate:C,VvForm:V,VvFormField:f,VvFormTemplate:u,VvFormWrapper:l}}const G=Symbol("pluginInjectionKey");function F(e){let s={};return e.schema&&(s=_(e.schema,e)),{...s,install(t,{global:o=!1}={}){t.provide(G,e),o&&(t.config.globalProperties.$vvForm=e,s!=null&&s.VvForm&&t.component("VvForm",s.VvForm),s!=null&&s.VvFormWrapper&&t.component("VvFormWrapper",s.VvFormWrapper),s!=null&&s.VvFormField&&t.component("VvFormField",s.VvFormField),s!=null&&s.VvFormTemplate&&t.component("VvFormTemplate",s.VvFormTemplate))}}}function ee(e,s={}){return r.getCurrentInstance()?_(e,{...r.inject(G,{}),...s}):_(e,s)}function re(e,s={}){return _(e,s)}x.FormFieldType=c,x.createForm=F,x.defaultObjectBySchema=A,x.formFactory=re,x.pluginInjectionKey=G,x.useForm=ee,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});
1
+ (function(A,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("@vueuse/core"),require("zod")):typeof define=="function"&&define.amd?define(["exports","vue","@vueuse/core","zod"],e):(A=typeof globalThis<"u"?globalThis:A||self,e(A["@volverjs/form-vue"]={},A.Vue,A.VueUseCore,A.zod))})(this,function(A,e,T,B){"use strict";var p=(r=>(r.text="text",r.number="number",r.email="email",r.password="password",r.tel="tel",r.url="url",r.search="search",r.date="date",r.time="time",r.datetimeLocal="datetime-local",r.month="month",r.week="week",r.color="color",r.select="select",r.checkbox="checkbox",r.radio="radio",r.textarea="textarea",r.radioGroup="radioGroup",r.checkboxGroup="checkboxGroup",r.combobox="combobox",r.custom="custom",r))(p||{}),U=(r=>(r.invalid="invalid",r.valid="valid",r.submitting="submitting",r.reset="reset",r.updated="updated",r.unknown="unknown",r))(U||{});function L(r,f={}){const t=v=>{let u=v;for(;u instanceof B.ZodEffects;)u=u.innerType();return u instanceof B.ZodOptional&&(u=u._def.innerType),u},c=v=>{let u=v;for(;u instanceof B.ZodEffects;)u=u.innerType();return u instanceof B.ZodOptional},a=t(r);return{...(a instanceof B.ZodObject?a._def.unknownKeys==="passthrough":!1)?f:{},...Object.fromEntries(Object.entries(a.shape).map(([v,u])=>{const d=f[v],V=c(u);let l=t(u),h;if(l instanceof B.ZodDefault&&(h=l._def.defaultValue(),l=l._def.innerType),d===null&&l instanceof B.ZodNullable)return[v,d];if(d==null&&V)return[v,h];if(l instanceof B.ZodSchema){const b=u.safeParse(d);if(b.success)return[v,b.data??h]}if(l instanceof B.ZodArray&&Array.isArray(d)&&d.length){const b=t(l._def.type);if(b instanceof B.ZodObject)return[v,d.map(y=>L(b,y&&typeof y=="object"?y:void 0))]}if(l instanceof B.ZodRecord&&d){const b=t(l._def.valueType);if(b instanceof B.ZodObject)return[v,Object.keys(d).reduce((y,o)=>(y[o]=L(b,d[o]),y),{})]}return l instanceof B.ZodObject?[v,L(l,d&&typeof d=="object"?d:h)]:[v,h]}))}}function z(r,f,t,c){const a=e.ref(),n=e.ref(),v=e.computed(()=>n.value===U.invalid),u=e.ref(),d=e.ref(!1);let V;const l=async(x=u.value,w)=>{if(V=w,d.value)return!0;const g=await r.safeParseAsync(x);if(!g.success){if(n.value=U.invalid,!w)return a.value=g.error.format(),!1;const s=g.error.issues.filter(W=>w.has(W.path.join(".")));return s.length?(a.value=new B.ZodError(s).format(),!1):(a.value=void 0,!0)}return a.value=void 0,n.value=U.valid,u.value=g.data,!0},h=()=>{a.value=void 0,n.value=void 0,V=void 0},b=()=>{u.value=L(r),h(),n.value=U.reset},y=async()=>d.value||!await l()?!1:(n.value=U.submitting,!0),{ignoreUpdates:o,stop:O}=T.watchIgnorable(u,()=>{n.value=U.updated},{deep:!0,eventFilter:T.throttleFilter((t==null?void 0:t.updateThrottle)??500)}),S=e.readonly(a),E=e.readonly(n),k=e.defineComponent({name:"VvForm",props:{continuousValidation:{type:Boolean,default:!1},modelValue:{type:Object,default:()=>({})},readonly:{type:Boolean,default:(t==null?void 0:t.readonly)??!1},tag:{type:String,default:"form"},template:{type:[Array,Function],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(x,{emit:w}){return u.value=L(r,e.toRaw(x.modelValue)),e.watch(()=>x.modelValue,g=>{if(g){const s=e.isProxy(g)?e.toRaw(g):g;if(JSON.stringify(s)===JSON.stringify(e.toRaw(u.value)))return;u.value=typeof(s==null?void 0:s.clone)=="function"?s.clone():JSON.parse(JSON.stringify(s))}},{deep:!0}),e.watch(n,async g=>{var s,W,R,G,$,M;if(g===U.invalid){const C=e.toRaw(a.value);w("invalid",C),(s=t==null?void 0:t.onInvalid)==null||s.call(t,C);return}if(g===U.valid){const C=e.toRaw(u.value);w("valid",C),(W=t==null?void 0:t.onValid)==null||W.call(t,C),w("update:modelValue",C),(R=t==null?void 0:t.onUpdate)==null||R.call(t,C);return}if(g===U.submitting){const C=e.toRaw(u.value);w("submit",C),(G=t==null?void 0:t.onSubmit)==null||G.call(t,C);return}if(g===U.reset){const C=e.toRaw(u.value);w("reset",C),($=t==null?void 0:t.onReset)==null||$.call(t,C);return}if(g===U.updated){if((a.value||t!=null&&t.continuousValidation||x.continuousValidation)&&await l(void 0,V),!u.value||!x.modelValue||JSON.stringify(u.value)!==JSON.stringify(x.modelValue)){const C=e.toRaw(u.value);w("update:modelValue",C),(M=t==null?void 0:t.onUpdate)==null||M.call(t,C)}n.value===U.updated&&(n.value=U.unknown)}}),e.onMounted(()=>{d.value=x.readonly}),e.watch(()=>x.readonly,g=>{d.value=g}),e.watch(d,g=>{g!==x.readonly&&w("update:readonly",d.value)}),e.provide(f,{clear:h,errors:S,formData:u,ignoreUpdates:o,invalid:v,readonly:d,reset:b,status:E,stopUpdatesWatch:O,submit:y,validate:l}),{clear:h,errors:S,formData:u,ignoreUpdates:o,invalid:v,isReadonly:d,reset:b,status:E,stopUpdatesWatch:O,submit:y,validate:l}},render(){const x=()=>{var w,g;return((g=(w=this.$slots)==null?void 0:w.default)==null?void 0:g.call(w,{clear:h,errors:S,formData:u,ignoreUpdates:o,invalid:v,readonly:d,reset:b,status:E,stopUpdatesWatch:O,submit:y,validate:l}))??this.$slots.default};return e.h(this.tag,{onSubmit:e.withModifiers(this.submit,["prevent"]),onReset:e.withModifiers(this.reset,["prevent"])},(this.template??(t==null?void 0:t.template))&&c?[e.h(c,{schema:this.template??(t==null?void 0:t.template)},{default:x})]:{default:x})}});return{clear:h,errors:a,formData:u,ignoreUpdates:o,invalid:v,readonly:d,reset:b,status:n,stopUpdatesWatch:O,submit:y,validate:l,VvForm:k}}function P(r){return Array.isArray(r)}function j(r){return typeof r<"u"}function H(r){return r===null}function Q(r){return typeof r=="object"}function X(r){return typeof r=="string"}function _(r){return typeof r>"u"}const D=/^[0-9]+$/,F=["__proto__","prototype","constructor"];function Z(r,f,t){const c=j(t)?t:void 0;if(!Q(r)||!X(f))return c;const a=Y(f);if(a.length!==0){for(const n of a){if(n==="*")continue;const v=function(u){return u.map(d=>_(d)||H(d)?d:P(d)?v(d):d[n])};if(P(r)&&!D.test(n)?r=v(r):r=r[n],_(r)||H(r))break}return _(r)?c:r}}function q(r,f,t){if(!Q(r)||!X(f))return;const c=Y(f);if(c.length===0)return;const a=c.length;for(let n=0;n<a;n++){const v=c[n];if(n===a-1){r[v]=t;return}if(v==="*"&&P(r)){const u=c.slice(n+1).join(".");for(const d of r)q(d,u,t);return}_(r[v])&&(r[v]={}),r=r[v]}}function Y(r){const f=r.split(/[.]|(?:\[(\d|\*)\])/).filter(t=>!!t);return f.some(t=>F.indexOf(t)!==-1)?[]:f}function ee(r,f,t,c){return e.defineComponent({name:"VvFormField",props:{type:{type:String,validator:a=>Object.values(p).includes(a),default:p.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(a,{slots:n,emit:v}){const{props:u,name:d}=e.toRefs(a),V=e.inject(f,void 0);V&&V.fields.value.add(a.name);const l=e.inject(r),h=e.computed({get(){if(l!=null&&l.formData)return Z(new Object(l.formData.value),String(a.name))},set(s){l!=null&&l.formData&&(q(new Object(l.formData.value),String(a.name),s),v("update:modelValue",{newValue:h.value,formData:l==null?void 0:l.formData}))}});e.onMounted(()=>{h.value===void 0&&a.defaultValue!==void 0&&(h.value=a.defaultValue)});const b=e.computed(()=>{if(l!=null&&l.errors.value)return Z(l.errors.value,String(a.name))}),y=e.computed(()=>{var s;return(s=b.value)==null?void 0:s._errors}),o=e.computed(()=>b.value!==void 0),O=e.watch(o,()=>{if(o.value){v("invalid",b.value),V&&V.errors.value.set(String(a.name),b.value);return}v("valid",h.value),V&&V.errors.value.delete(a.name)}),S=e.watch(()=>l==null?void 0:l.formData,()=>{v("update:formData",l==null?void 0:l.formData)},{deep:!0});e.onBeforeUnmount(()=>{O(),S()});const E=s=>{s instanceof InputEvent&&(s=s.target.value),h.value=s},k=e.computed(()=>{let s=u.value;return typeof s=="function"&&(s=s(l==null?void 0:l.formData)),Object.keys(s).reduce((W,R)=>(W[R]=e.unref(s[R]),W),{})}),x=e.computed(()=>l!=null&&l.readonly.value?!0:k.value.readonly??a.readonly),w=e.computed(()=>({...k.value,name:k.value.name??a.name,invalid:o.value,valid:a.showValid?!!(!o.value&&h.value):void 0,type:(s=>{if([p.color,p.date,p.datetimeLocal,p.email,p.month,p.number,p.password,p.search,p.tel,p.text,p.time,p.url,p.week].includes(s))return s})(a.type),invalidLabel:y.value,modelValue:h.value,readonly:x.value,"onUpdate:modelValue":E}));return e.provide(t,{name:e.readonly(d),errors:e.readonly(b)}),{component:e.computed(()=>{if(a.type===p.custom)return{render(){var s;return((s=n.default)==null?void 0:s.call(n,{errors:b.value,formData:l==null?void 0:l.formData.value,formErrors:l==null?void 0:l.errors.value,invalid:o.value,invalidLabel:y.value,modelValue:h.value,onUpdate:E,readonly:x.value,submit:l==null?void 0:l.submit,validate:l==null?void 0:l.validate}))??n.default}};if(!((c==null?void 0:c.lazyLoad)??a.lazyLoad)){let s;switch(a.type){case p.select:s=e.resolveComponent("VvSelect");break;case p.checkbox:s=e.resolveComponent("VvCheckbox");break;case p.radio:s=e.resolveComponent("VvRadio");break;case p.textarea:s=e.resolveComponent("VvTextarea");break;case p.radioGroup:s=e.resolveComponent("VvRadioGroup");break;case p.checkboxGroup:s=e.resolveComponent("VvCheckboxGroup");break;case p.combobox:s=e.resolveComponent("VvCombobox");break;default:s=e.resolveComponent("VvInputText")}if(typeof s!="string")return s;console.warn(`[form-vue warn]: ${s} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.`)}return e.defineAsyncComponent(async()=>{switch(c!=null&&c.sideEffects&&await Promise.resolve(c.sideEffects(a.type)),a.type){case p.textarea:return import("@volverjs/ui-vue/vv-textarea");case p.radio:return import("@volverjs/ui-vue/vv-radio");case p.radioGroup:return import("@volverjs/ui-vue/vv-radio-group");case p.checkbox:return import("@volverjs/ui-vue/vv-checkbox");case p.checkboxGroup:return import("@volverjs/ui-vue/vv-checkbox-group");case p.select:return import("@volverjs/ui-vue/vv-select");case p.combobox:return import("@volverjs/ui-vue/vv-combobox")}return import("@volverjs/ui-vue/vv-input-text")})}),hasProps:w,invalid:o}},render(){return this.is?e.h(this.is,this.hasProps,this.$slots):this.type===p.custom?e.h(this.component,null,this.$slots):e.h(this.component,this.hasProps,this.$slots)}})}function re(r,f,t){return e.defineComponent({name:"VvFormFieldsGroup",props:{is:{type:[Object,String],default:void 0},names:{type:[Array],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(c,{slots:a,emit:n}){const{props:v,names:u,defaultValues:d}=e.toRefs(c),V=e.computed(()=>Array.isArray(u.value)?u.value:Object.values(u.value)),l=e.computed(()=>Array.isArray(u.value)?u.value:Object.keys(u.value)),h=e.computed(()=>Array.isArray(u.value)?u.value.reduce((i,m)=>(i[String(m)]=m,i),{}):u.value),b=e.computed(()=>Object.keys(h.value).reduce((i,m)=>(i[String(h.value[m])]=m,i),{})),y=e.inject(f,void 0);y&&V.value.forEach(i=>{y.fields.value.add(i)});const o=e.inject(r),O=e.computed({get(){return o!=null&&o.formData?l.value.reduce((i,m)=>(i[m]=Z(new Object(o.formData.value),h.value[m]),i),{}):{}},set(i){o!=null&&o.formData&&(l.value.forEach(m=>{q(new Object(o.formData.value),h.value[m],i==null?void 0:i[m])}),n("update:modelValue",{newValue:O.value,formData:o==null?void 0:o.formData}))}});e.onMounted(()=>{d.value&&V.value.forEach(i=>{var m,I;((m=d.value)==null?void 0:m[i])!==void 0&&O.value[i]===void 0&&(O.value={...O.value,[i]:(I=d.value)==null?void 0:I[i]})})});const S=e.computed(()=>{if(!(o!=null&&o.errors.value))return;const i=V.value.reduce((m,I)=>{if(!o.errors.value)return m;const N=Z(o.errors.value,String(I));return N===void 0||(m[String(I)]=N),m},{});if(Object.keys(i).length!==0)return i}),E=e.computed(()=>{if(!S.value)return;const i=Object.keys(S.value).reduce((m,I)=>{var N;return(N=S.value)!=null&&N[I]&&(m[b.value[I]]=S.value[I]._errors),m},{});if(Object.keys(i).length!==0)return i}),k=e.computed(()=>S.value!==void 0),x=e.computed(()=>l.value.reduce((i,m)=>{var I;return i[m]=!!((I=S.value)!=null&&I[b.value[m]]),i},{})),w=e.watch(k,()=>{if(k.value){n("invalid",S.value),y&&V.value.forEach(i=>{var m,I;if(!((m=S.value)!=null&&m[i])){y.errors.value.delete(i);return}y.errors.value.set(i,(I=S.value)==null?void 0:I[i])});return}n("valid",O.value),y&&V.value.forEach(i=>{y.errors.value.delete(i)})}),g=e.watch(()=>o==null?void 0:o.formData,()=>{n("update:formData",o==null?void 0:o.formData)},{deep:!0});e.onBeforeUnmount(()=>{w(),g()});const s=i=>{O.value=i},W=(i,m)=>{m instanceof InputEvent&&(m=m.target.value),l.value.includes(i)&&(O.value={...O.value,[i]:m})},R=e.computed(()=>{let i=v.value;return typeof i=="function"&&(i=i(o==null?void 0:o.formData)),Object.keys(i).reduce((m,I)=>(m[I]=e.unref(i[I]),m),{})}),G=e.computed(()=>o!=null&&o.readonly.value?!0:R.value.readonly??c.readonly),$=e.computed(()=>l.value.reduce((i,m)=>(i[`onUpdate:${m}`]=I=>{W(m,I)},i),{"onUpdate:modelValue":s})),M=e.computed(()=>({...$.value,...R.value,names:R.value.name??V.value,invalid:k.value,invalids:x.value,valid:c.showValid?!!(!k.value&&O.value):void 0,invalidLabels:E.value,modelValue:O.value,readonly:G.value}));return e.provide(t,{names:e.readonly(u),errors:e.readonly(S)}),{component:e.computed(()=>({render(){var i;return((i=a.default)==null?void 0:i.call(a,{errors:S.value,formData:o==null?void 0:o.formData.value,formErrors:o==null?void 0:o.errors.value,invalid:k.value,invalids:x.value,invalidLabels:E.value,modelValue:O.value,onUpdate:s,onUpdateField:W,readonly:G.value,submit:o==null?void 0:o.submit,validate:o==null?void 0:o.validate}))??a.default}})),hasProps:M,invalid:k}},render(){return this.is?e.h(this.is,this.hasProps,this.$slots):e.h(this.component,null,this.$slots)}})}function te(r,f){return e.defineComponent({name:"VvFormWrapper",props:{name:{type:String,required:!0},tag:{type:String,default:void 0}},emits:["invalid","valid"],expose:["clear","errors","fields","fieldsErrors","formData","invalid","reset","submit","tag","validate","validateWrapper"],slots:Object,setup(t,{emit:c}){const a=e.inject(r),n=e.inject(f,void 0),v=e.ref(new Set),u=e.ref(new Map),{name:d}=e.toRefs(t);e.provide(f,{name:e.readonly(d),errors:u,fields:v}),e.watch(v,h=>{n!=null&&n.fields&&h.forEach(b=>{n==null||n.fields.value.add(b)})},{deep:!0}),e.watch(()=>new Map(u.value),(h,b)=>{n!=null&&n.errors&&(Array.from(b.keys()).forEach(y=>{n.errors.value.delete(y)}),Array.from(h.keys()).forEach(y=>{const o=h.get(y);o&&n.errors.value.set(y,o)}))},{deep:!0});const V=e.computed(()=>a!=null&&a.invalid.value?u.value.size>0:!1);e.watch(V,()=>{V.value?c("invalid"):c("valid")});const l=()=>(a==null?void 0:a.validate(void 0,v.value))??Promise.resolve(!0);return{clear:a==null?void 0:a.clear,errors:a==null?void 0:a.errors,fields:v,fieldsErrors:u,formData:a==null?void 0:a.formData,invalid:V,reset:a==null?void 0:a.reset,submit:a==null?void 0:a.submit,validate:a==null?void 0:a.validate,validateWrapper:l}},render(){const t=()=>{var c,a;return(a=(c=this.$slots).default)==null?void 0:a.call(c,{clear:this.clear,errors:this.errors,fieldsErrors:this.fieldsErrors,formData:this.formData,invalid:this.invalid,reset:this.reset,submit:this.submit,validate:this.validate,validateWrapper:this.validateWrapper})};return this.tag?e.h(this.tag,null,{default:t}):t()}})}function ae(r,f){const t=e.defineComponent({name:"VvFormTemplate",props:{schema:{type:[Array,Function],required:!0},scope:{type:Object,default:()=>({})}},slots:Object,setup(c,{slots:a}){const n=e.inject(r);if(n!=null&&n.formData)return()=>{var V;const v=typeof c.schema=="function"?c.schema(n,c.scope):c.schema;let u;const d=v.reduce((l,h)=>{const b=typeof h=="function"?h(n,c.scope):h,{vvIs:y,vvName:o,vvSlots:O,vvChildren:S,vvIf:E,vvElseIf:k,vvType:x,vvDefaultValue:w,vvShowValid:g,vvContent:s,...W}=b;if(E!==void 0){if(typeof E=="string"?u=!!Z(new Object(n.formData.value),E):typeof E=="function"?u=e.unref(E(n)):u=e.unref(E),!u)return l}else if(k!==void 0&&u!==void 0){if(u||(typeof k=="string"?u=!!Z(new Object(n.formData.value),k):typeof k=="function"?u=e.unref(k(n)):u=e.unref(k),!u))return l}else u=void 0;let R;return S&&(typeof y=="string"?R=e.h(t,{schema:S}):R={default:G=>e.h(t,{schema:S,scope:G})}),o?(l.push(e.h(f,{name:o,is:y,type:x,defaultValue:w,showValid:g,props:W},O??R??s)),l):y?(l.push(e.h(y,W,O??R??s)),l):(R&&("default"in R?l.push(R.default(c.scope)):l.push(R)),l)},[]);return d.push((V=a==null?void 0:a.default)==null?void 0:V.call(a,{errors:n==null?void 0:n.errors.value,formData:n==null?void 0:n.formData.value,invalid:n==null?void 0:n.invalid.value,status:n==null?void 0:n.status.value,submit:n==null?void 0:n.submit,validate:n==null?void 0:n.validate,clear:n==null?void 0:n.clear,reset:n==null?void 0:n.reset})),d}}});return t}function K(r,f={}){const t=Symbol("formInjectionKey"),c=Symbol("formWrapperInjectionKey"),a=Symbol("formFieldInjectionKey"),n=Symbol("formFieldsGroupInjectionKey"),v=te(t,c),u=ee(t,c,a,f),d=re(t,c,n),V=ae(t,u),{clear:l,errors:h,formData:b,ignoreUpdates:y,invalid:o,readonly:O,reset:S,status:E,stopUpdatesWatch:k,submit:x,validate:w,VvForm:g}=z(r,t,f,V);return{clear:l,errors:h,formData:b,formFieldInjectionKey:a,formInjectionKey:t,formWrapperInjectionKey:c,ignoreUpdates:y,invalid:o,readonly:O,reset:S,status:E,stopUpdatesWatch:k,submit:x,validate:w,VvForm:g,VvFormField:u,VvFormFieldsGroup:d,VvFormTemplate:V,VvFormWrapper:v}}const J=Symbol("pluginInjectionKey");function ne(r){let f={};return r.schema&&(f=K(r.schema,r)),{...f,install(t,{global:c=!1}={}){t.provide(J,r),c&&(t.config.globalProperties.$vvForm=r,f!=null&&f.VvForm&&t.component("VvForm",f.VvForm),f!=null&&f.VvFormWrapper&&t.component("VvFormWrapper",f.VvFormWrapper),f!=null&&f.VvFormField&&t.component("VvFormField",f.VvFormField),f!=null&&f.VvFormTemplate&&t.component("VvFormTemplate",f.VvFormTemplate))}}}function le(r,f={}){return e.getCurrentInstance()?K(r,{...e.inject(J,{}),...f}):K(r,f)}function ue(r,f={}){return K(r,f)}A.FormFieldType=p,A.createForm=ne,A.defaultObjectBySchema=L,A.formFactory=ue,A.pluginInjectionKey=J,A.useForm=le,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})});
package/dist/types.d.ts CHANGED
@@ -45,9 +45,13 @@ export type InjectedFormWrapperData<Schema extends FormSchema> = {
45
45
  errors: Ref<Map<string, z.inferFormattedError<Schema>>>;
46
46
  };
47
47
  export type InjectedFormFieldData<Schema extends FormSchema> = {
48
- name: Ref<string>;
48
+ name: Readonly<Ref<Path<z.infer<Schema>>>>;
49
49
  errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>;
50
50
  };
51
+ export type InjectedFormFieldsGroupData<Schema extends FormSchema> = {
52
+ names: DeepReadonly<Ref<Path<z.infer<Schema>>[]>>;
53
+ errors: Readonly<Ref<DeepReadonly<Record<string, z.inferFormattedError<Schema>> | undefined>>>;
54
+ };
51
55
  export type Primitive = null | undefined | string | number | boolean | symbol | bigint;
52
56
  type IsTuple<T extends readonly any[]> = number extends T['length'] ? false : true;
53
57
  type TupleKeys<T extends readonly any[]> = Exclude<keyof T, keyof any[]>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@volverjs/form-vue",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.22",
4
+ "version": "1.0.0-beta.23",
5
5
  "description": "Vue 3 Forms with @volverjs/ui-vue",
6
6
  "author": "8 Wave S.r.l.",
7
7
  "license": "MIT",
@@ -51,10 +51,10 @@
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@volverjs/ui-vue": "^0.*",
54
- "@vueuse/core": "^10.11.*",
55
- "ts-dot-prop": "^2.1.*",
56
- "vue": "^3.4.*",
57
- "zod": "^3.23.*"
54
+ "@vueuse/core": "^10.*",
55
+ "ts-dot-prop": "^2.*",
56
+ "vue": "^3.*",
57
+ "zod": "^3.*"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@antfu/eslint-config": "^3.8.0",
@@ -67,8 +67,8 @@
67
67
  "@vue/runtime-core": "^3.5.12",
68
68
  "@vue/test-utils": "^2.4.6",
69
69
  "copy": "^0.3.2",
70
- "eslint": "^9.13.0",
71
- "happy-dom": "^15.7.4",
70
+ "eslint": "^9.14.0",
71
+ "happy-dom": "^15.10.1",
72
72
  "typescript": "^5.6.3",
73
73
  "vite": "^5.4.10",
74
74
  "vite-plugin-dts": "^4.3.0",
@@ -104,8 +104,8 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
104
104
  formData?: Partial<TypeOf<Schema>>
105
105
  formErrors?: DeepReadonly<inferFormattedError<Schema, string>>
106
106
  invalid: boolean
107
- invalidLabel: string
108
- modelValue: unknown
107
+ invalidLabel?: string[]
108
+ modelValue: any
109
109
  onUpdate: (value: unknown) => void
110
110
  readonly: boolean
111
111
  submit?: InjectedFormData<Schema>['submit']
@@ -113,6 +113,8 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
113
113
  }
114
114
  }>,
115
115
  setup(props, { slots, emit }) {
116
+ const { props: fieldProps, name: fieldName } = toRefs(props)
117
+
116
118
  // inject data from parent form wrapper
117
119
  const injectedWrapperData = inject(wrapperProvideKey, undefined)
118
120
  if (injectedWrapperData) {
@@ -121,21 +123,22 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
121
123
 
122
124
  // inject data from parent form
123
125
  const injectedFormData = inject(formProvideKey)
124
- const { props: fieldProps, name: fieldName } = toRefs(props)
125
126
 
126
127
  // v-model
127
128
  const modelValue = computed({
128
129
  get() {
129
- if (!injectedFormData?.formData)
130
+ if (!injectedFormData?.formData) {
130
131
  return
132
+ }
131
133
  return get(
132
134
  new Object(injectedFormData.formData.value),
133
135
  String(props.name),
134
136
  )
135
137
  },
136
138
  set(value) {
137
- if (!injectedFormData?.formData)
139
+ if (!injectedFormData?.formData) {
138
140
  return
141
+ }
139
142
  set(
140
143
  new Object(injectedFormData.formData.value),
141
144
  String(props.name),
@@ -170,23 +173,20 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
170
173
  })
171
174
  const unwatchInvalid = watch(invalid, () => {
172
175
  if (invalid.value) {
173
- emit('invalid', invalidLabel.value)
176
+ emit('invalid', errors.value)
174
177
  if (injectedWrapperData) {
175
178
  injectedWrapperData.errors.value.set(
176
- props.name as string,
177
- {
178
- _errors: invalidLabel.value,
179
- } as z.inferFormattedError<Schema>,
179
+ String(props.name),
180
+ errors.value,
180
181
  )
181
182
  }
183
+ return
182
184
  }
183
- else {
184
- emit('valid', modelValue.value)
185
- if (injectedWrapperData) {
186
- injectedWrapperData.errors.value.delete(
187
- props.name as string,
188
- )
189
- }
185
+ emit('valid', modelValue.value)
186
+ if (injectedWrapperData) {
187
+ injectedWrapperData.errors.value.delete(
188
+ props.name as string,
189
+ )
190
190
  }
191
191
  })
192
192
  const unwatchInjectedFormData = watch(
@@ -201,6 +201,9 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
201
201
  unwatchInjectedFormData()
202
202
  })
203
203
  const onUpdate = (value: unknown) => {
204
+ if (value instanceof InputEvent) {
205
+ value = (value.target as HTMLInputElement).value
206
+ }
204
207
  modelValue.value = value
205
208
  }
206
209
  const hasFieldProps = computed(() => {
@@ -259,7 +262,7 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
259
262
 
260
263
  // provide data to children
261
264
  provide(formFieldInjectionKey, {
262
- name: readonly(fieldName as Ref<string>),
265
+ name: readonly(fieldName) as Readonly<Ref<Path<z.infer<Schema>>>>,
263
266
  errors: readonly(errors),
264
267
  })
265
268
 
@@ -315,11 +318,9 @@ export function defineFormField<Schema extends FormSchema>(formProvideKey: Injec
315
318
  if (typeof component !== 'string') {
316
319
  return component
317
320
  }
318
- else {
319
- console.warn(
320
- `[form-vue warn]: ${component} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.`,
321
- )
322
- }
321
+ console.warn(
322
+ `[form-vue warn]: ${component} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.`,
323
+ )
323
324
  }
324
325
  return defineAsyncComponent(async () => {
325
326
  if (options?.sideEffects) {
@@ -0,0 +1,375 @@
1
+ import { get, set } from 'ts-dot-prop'
2
+ import {
3
+ type Component,
4
+ type DeepReadonly,
5
+ type InjectionKey,
6
+ type PropType,
7
+ type Ref,
8
+ type SlotsType,
9
+ computed,
10
+ defineComponent,
11
+ h,
12
+ inject,
13
+ onBeforeUnmount,
14
+ onMounted,
15
+ provide,
16
+ readonly,
17
+ toRefs,
18
+ unref,
19
+ watch,
20
+ } from 'vue'
21
+ import type { inferFormattedError, TypeOf, z } from 'zod'
22
+ import type {
23
+ FormSchema,
24
+ InjectedFormData,
25
+ InjectedFormFieldsGroupData,
26
+ InjectedFormWrapperData,
27
+ Path,
28
+ } from './types'
29
+
30
+ export function defineFormFieldsGroup<Schema extends FormSchema>(formProvideKey: InjectionKey<InjectedFormData<Schema>>, wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>, formFieldsGroupInjectionKey: InjectionKey<InjectedFormFieldsGroupData<Schema>>) {
31
+ return defineComponent({
32
+ name: 'VvFormFieldsGroup',
33
+ props: {
34
+ is: {
35
+ type: [Object, String] as PropType<Component | string>,
36
+ default: undefined,
37
+ },
38
+ names: {
39
+ type: [Array] as PropType<
40
+ Path<z.infer<Schema>>[] | Record<string, Path<z.infer<Schema>>>
41
+ >,
42
+ required: true,
43
+ },
44
+ props: {
45
+ type: [Object, Function] as PropType<
46
+ Partial<
47
+ | z.infer<Schema>
48
+ | undefined
49
+ | ((
50
+ formData?: Ref<ObjectConstructor>,
51
+ ) => Partial<z.infer<Schema>> | undefined)
52
+ >
53
+ >,
54
+ default: () => ({}),
55
+ },
56
+ showValid: {
57
+ type: Boolean,
58
+ default: false,
59
+ },
60
+ defaultValues: {
61
+ type: [Object] as PropType<
62
+ Record<Path<z.infer<Schema>>, any>
63
+ >,
64
+ default: undefined,
65
+ },
66
+ readonly: {
67
+ type: Boolean,
68
+ default: undefined,
69
+ },
70
+ },
71
+ emits: [
72
+ 'invalid',
73
+ 'update:formData',
74
+ 'update:modelValue',
75
+ 'valid',
76
+ ],
77
+ expose: [
78
+ 'component',
79
+ 'errors',
80
+ 'hasProps',
81
+ 'invalid',
82
+ 'invalidLabels',
83
+ 'is',
84
+ ],
85
+ slots: Object as SlotsType<{
86
+ [key: string]: any
87
+ default: {
88
+ errors?: Record<Path<z.infer<Schema>>, z.inferFormattedError<Schema>>
89
+ formData?: Partial<TypeOf<Schema>>
90
+ formErrors?: DeepReadonly<inferFormattedError<Schema, string>>
91
+ invalid: boolean
92
+ invalids: Record<string, boolean>
93
+ invalidLabels?: Record<string, string[]>
94
+ modelValue: Record<string, any>
95
+ onUpdate: (value: Record<string, any>) => void
96
+ onUpdateField: (name: string, value: any) => void
97
+ readonly: boolean
98
+ submit?: InjectedFormData<Schema>['submit']
99
+ validate?: InjectedFormData<Schema>['validate']
100
+ }
101
+ }>,
102
+ setup(props, { slots, emit }) {
103
+ const { props: fieldProps, names: fieldsNames, defaultValues } = toRefs(props)
104
+ const names = computed<Path<z.infer<Schema>>[]>(() => {
105
+ if (Array.isArray(fieldsNames.value)) {
106
+ return fieldsNames.value
107
+ }
108
+ return Object.values(fieldsNames.value)
109
+ })
110
+ const namesKeys = computed(() => {
111
+ if (Array.isArray(fieldsNames.value)) {
112
+ return fieldsNames.value
113
+ }
114
+ return Object.keys(fieldsNames.value)
115
+ })
116
+ const namesMap = computed(() => {
117
+ if (Array.isArray(fieldsNames.value)) {
118
+ return fieldsNames.value.reduce<Record<string, Path<z.infer<Schema>>>>((
119
+ acc,
120
+ name,
121
+ ) => {
122
+ acc[String(name)] = name
123
+ return acc
124
+ }, {})
125
+ }
126
+ return fieldsNames.value
127
+ })
128
+ const namesKeysMap = computed(() => {
129
+ return Object.keys(namesMap.value).reduce<Record<string, string>>((acc, key) => {
130
+ acc[String(namesMap.value[key])] = key
131
+ return acc
132
+ }, {})
133
+ })
134
+
135
+ // inject data from parent form wrapper
136
+ const injectedWrapperData = inject(wrapperProvideKey, undefined)
137
+ if (injectedWrapperData) {
138
+ names.value.forEach((name) => {
139
+ injectedWrapperData.fields.value.add(name as string)
140
+ })
141
+ }
142
+
143
+ // inject data from parent form
144
+ const injectedFormData = inject(formProvideKey)
145
+
146
+ // v-model
147
+ const modelValue = computed({
148
+ get() {
149
+ if (!injectedFormData?.formData) {
150
+ return {}
151
+ }
152
+ return namesKeys.value.reduce<Record<string, any>>((acc, nameKey) => {
153
+ acc[nameKey] = get(
154
+ new Object(injectedFormData.formData.value),
155
+ namesMap.value[nameKey],
156
+ )
157
+ return acc
158
+ }, {})
159
+ },
160
+ set(value) {
161
+ if (!injectedFormData?.formData) {
162
+ return
163
+ }
164
+ namesKeys.value.forEach((nameKey) => {
165
+ set(
166
+ new Object(injectedFormData.formData.value),
167
+ namesMap.value[nameKey],
168
+ value?.[nameKey],
169
+ )
170
+ })
171
+ emit('update:modelValue', {
172
+ newValue: modelValue.value,
173
+ formData: injectedFormData?.formData,
174
+ })
175
+ },
176
+ })
177
+ onMounted(() => {
178
+ if (
179
+ defaultValues.value
180
+ ) {
181
+ names.value.forEach((name) => {
182
+ if (defaultValues.value?.[name] === undefined) {
183
+ return
184
+ }
185
+ if (modelValue.value[name] !== undefined) {
186
+ return
187
+ }
188
+ modelValue.value = {
189
+ ...modelValue.value,
190
+ [name]: defaultValues.value?.[name],
191
+ }
192
+ })
193
+ }
194
+ })
195
+
196
+ const errors = computed(() => {
197
+ if (!injectedFormData?.errors.value) {
198
+ return undefined
199
+ }
200
+ const toReturn = names.value.reduce<Record<string, z.inferFormattedError<Schema>>>((acc, name) => {
201
+ if (!injectedFormData.errors.value) {
202
+ return acc
203
+ }
204
+ const error = get(injectedFormData.errors.value, String(name))
205
+ if (error === undefined) {
206
+ return acc
207
+ }
208
+ acc[String(name)] = error
209
+ return acc
210
+ }, {})
211
+ if (Object.keys(toReturn).length === 0) {
212
+ return undefined
213
+ }
214
+ return toReturn
215
+ })
216
+ const invalidLabels = computed(() => {
217
+ if (!errors.value) {
218
+ return
219
+ }
220
+ const toReturn = Object.keys(errors.value).reduce<Record<string, string[]>>((acc, name) => {
221
+ if (!errors.value?.[name]) {
222
+ return acc
223
+ }
224
+ acc[namesKeysMap.value[name]] = errors.value[name]._errors
225
+ return acc
226
+ }, {})
227
+ if (Object.keys(toReturn).length === 0) {
228
+ return
229
+ }
230
+ return toReturn
231
+ })
232
+ const invalid = computed(() => {
233
+ return errors.value !== undefined
234
+ })
235
+ const invalids = computed(() => {
236
+ return namesKeys.value.reduce<Record<string, boolean>>((acc, name) => {
237
+ acc[name] = Boolean(errors.value?.[namesKeysMap.value[name]])
238
+ return acc
239
+ }, {})
240
+ })
241
+ const unwatchInvalid = watch(invalid, () => {
242
+ if (invalid.value) {
243
+ emit('invalid', errors.value)
244
+ if (injectedWrapperData) {
245
+ names.value.forEach((name) => {
246
+ if (!errors.value?.[name]) {
247
+ injectedWrapperData.errors.value.delete(
248
+ name,
249
+ )
250
+ return
251
+ }
252
+ injectedWrapperData.errors.value.set(
253
+ name,
254
+ errors.value?.[name],
255
+ )
256
+ })
257
+ }
258
+ return
259
+ }
260
+ emit('valid', modelValue.value)
261
+ if (injectedWrapperData) {
262
+ names.value.forEach((name) => {
263
+ injectedWrapperData.errors.value.delete(
264
+ name,
265
+ )
266
+ })
267
+ }
268
+ })
269
+ const unwatchInjectedFormData = watch(
270
+ () => injectedFormData?.formData,
271
+ () => {
272
+ emit('update:formData', injectedFormData?.formData)
273
+ },
274
+ { deep: true },
275
+ )
276
+ onBeforeUnmount(() => {
277
+ unwatchInvalid()
278
+ unwatchInjectedFormData()
279
+ })
280
+ const onUpdate = (value: Record<string, any>) => {
281
+ modelValue.value = value
282
+ }
283
+ const onUpdateField = (name: string, value: unknown) => {
284
+ if (value instanceof InputEvent) {
285
+ value = (value.target as HTMLInputElement).value
286
+ }
287
+ if (!namesKeys.value.includes(name)) {
288
+ return
289
+ }
290
+ modelValue.value = {
291
+ ...modelValue.value,
292
+ [name]: value,
293
+ }
294
+ }
295
+ const hasFieldProps = computed(() => {
296
+ let toReturn = fieldProps.value
297
+ if (typeof toReturn === 'function') {
298
+ toReturn = toReturn(injectedFormData?.formData)
299
+ }
300
+ return Object.keys(toReturn).reduce<Record<string, unknown>>(
301
+ (acc, key) => {
302
+ acc[key] = unref(toReturn[key])
303
+ return acc
304
+ },
305
+ {},
306
+ )
307
+ })
308
+ const isReadonly = computed(() => {
309
+ if (injectedFormData?.readonly.value) {
310
+ return true
311
+ }
312
+ return (hasFieldProps.value.readonly ?? props.readonly) as boolean
313
+ })
314
+ const onUpdateEvents = computed(() => {
315
+ return namesKeys.value.reduce<Record<string, (value: any) => void>>((acc, name) => {
316
+ acc[`onUpdate:${name}`] = (value) => {
317
+ onUpdateField(name, value)
318
+ }
319
+ return acc
320
+ }, {
321
+ 'onUpdate:modelValue': onUpdate,
322
+ })
323
+ })
324
+ const hasProps = computed(() => ({
325
+ ...onUpdateEvents.value,
326
+ ...hasFieldProps.value,
327
+ names: hasFieldProps.value.name ?? names.value,
328
+ invalid: invalid.value,
329
+ invalids: invalids.value,
330
+ valid: props.showValid
331
+ ? Boolean(!invalid.value && modelValue.value)
332
+ : undefined,
333
+ invalidLabels: invalidLabels.value,
334
+ modelValue: modelValue.value,
335
+ readonly: isReadonly.value,
336
+ }))
337
+
338
+ // provide data to children
339
+ provide(formFieldsGroupInjectionKey, {
340
+ names: readonly(fieldsNames) as DeepReadonly<Ref<Path<z.infer<Schema>>[]>>,
341
+ errors: readonly(errors),
342
+ })
343
+
344
+ // define component
345
+ const component = computed(() => ({
346
+ render() {
347
+ return (
348
+ slots.default?.({
349
+ errors: errors.value,
350
+ formData: injectedFormData?.formData.value,
351
+ formErrors: injectedFormData?.errors.value,
352
+ invalid: invalid.value,
353
+ invalids: invalids.value,
354
+ invalidLabels: invalidLabels.value,
355
+ modelValue: modelValue.value,
356
+ onUpdate,
357
+ onUpdateField,
358
+ readonly: isReadonly.value,
359
+ submit: injectedFormData?.submit,
360
+ validate: injectedFormData?.validate,
361
+ }) ?? slots.default
362
+ )
363
+ },
364
+ }))
365
+
366
+ return { component, hasProps, invalid }
367
+ },
368
+ render() {
369
+ if (this.is) {
370
+ return h(this.is, this.hasProps, this.$slots)
371
+ }
372
+ return h(this.component, null, this.$slots)
373
+ },
374
+ })
375
+ }