@volverjs/form-vue 0.0.9 → 0.0.10-beta.10

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(v,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue"),require("@vueuse/core"),require("zod")):typeof define=="function"&&define.amd?define(["exports","vue","@vueuse/core","zod"],t):(v=typeof globalThis<"u"?globalThis:v||self,t(v["@volverjs/form-vue"]={},v.Vue,v.VueUseCore,v.zod))})(this,function(v,t,A,y){"use strict";function x(e){return Array.isArray(e)}function D(e){return typeof e<"u"}function S(e){return e===null}function C(e){return typeof e=="object"}function j(e){return typeof e=="string"}function g(e){return typeof e>"u"}const _=/^[0-9]+$/,B=["__proto__","prototype","constructor"];function $(e,o,l){const u=D(l)?l:void 0;if(!C(e)||!j(o))return u;const r=G(o);if(r.length!==0){for(const n of r){if(n==="*")continue;const c=function(i){return i.map(f=>g(f)||S(f)?f:x(f)?c(f):f[n])};if(x(e)&&!_.test(n)?e=c(e):e=e[n],g(e)||S(e))break}return g(e)?u:e}}function E(e,o,l){if(!C(e)||!j(o))return;const u=G(o);if(u.length===0)return;const r=u.length;for(let n=0;n<r;n++){const c=u[n];if(n===r-1){e[c]=l;return}if(c==="*"&&x(e)){const i=u.slice(n+1).join(".");for(const f of e)E(f,i,l);return}g(e[c])&&(e[c]={}),e=e[c]}}function G(e){const o=e.split(/[.]|(?:\[(\d|\*)\])/).filter(l=>!!l);return o.some(l=>B.indexOf(l)!==-1)?[]:o}var s=(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="datetimeLocal",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))(s||{});const K=(e,o,l,u={})=>t.defineComponent({name:"FieldComponent",props:{type:{type:String,validator:r=>Object.values(s).includes(r),default:s.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}},emits:["invalid","valid","update:formData","update:modelValue"],expose:["invalid","invalidLabel","errors"],setup(r,{slots:n,emit:c}){const i=t.computed({get(){if(a!=null&&a.modelValue)return $(Object(a.modelValue.value),String(r.name))},set(d){a!=null&&a.modelValue&&(E(Object(a.modelValue.value),String(r.name),d),c("update:modelValue",{newValue:i.value,formData:a==null?void 0:a.modelValue}))}});t.onMounted(()=>{i.value===void 0&&r.defaultValue!==void 0&&(i.value=r.defaultValue)});const f=t.inject(o,void 0);f&&f.fields.value.add(r.name);const a=t.inject(e),{props:p,name:h}=t.toRefs(r),m=t.computed(()=>{if(a!=null&&a.errors.value)return $(a.errors.value,String(r.name))}),V=t.computed(()=>{var d;return(d=m.value)==null?void 0:d._errors}),b=t.computed(()=>m.value!==void 0);t.watch(b,()=>{b.value?(c("invalid",V.value),f&&f.errors.value.set(r.name,{_errors:V.value})):(c("valid",i.value),f&&f.errors.value.delete(r.name))}),t.watch(()=>a==null?void 0:a.modelValue,()=>{c("update:formData",a==null?void 0:a.modelValue)},{deep:!0});const L=d=>{i.value=d},N=t.computed(()=>typeof p.value=="function"?p.value(a==null?void 0:a.modelValue):p.value),q=t.computed(()=>({...N.value,name:N.value.name??r.name,invalid:b.value,valid:r.showValid?!!(!b.value&&i.value):void 0,type:(d=>{if([s.text,s.number,s.email,s.password,s.tel,s.url,s.search,s.date,s.time,s.datetimeLocal,s.month,s.week,s.color].includes(d))return d})(r.type),invalidLabel:V.value,modelValue:i.value,errors:r.is?m.value:void 0,"onUpdate:modelValue":L}));return t.provide(l,{name:t.readonly(h),errors:t.readonly(m)}),{component:t.computed(()=>{if(r.type===s.custom)return{render(){var d;return((d=n.default)==null?void 0:d.call(n,{modelValue:i.value,onUpdate:L,invalid:b.value,invalidLabel:V.value,formData:a==null?void 0:a.modelValue.value,formErrors:a==null?void 0:a.errors.value,errors:m.value}))??n.defalut}};if(!u.lazyLoad){let d;switch(r.type){case s.select:d=t.resolveComponent("VvSelect");break;case s.checkbox:d=t.resolveComponent("VvCheckbox");break;case s.radio:d=t.resolveComponent("VvRadio");break;case s.textarea:d=t.resolveComponent("VvTextarea");break;case s.radioGroup:d=t.resolveComponent("VvRadioGroup");break;case s.checkboxGroup:d=t.resolveComponent("VvCheckboxGroup");break;case s.combobox:d=t.resolveComponent("VvCombobox");break;default:d=t.resolveComponent("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 t.defineAsyncComponent(async()=>{switch(u.sideEffects&&await Promise.resolve(u.sideEffects(r.type)),r.type){case s.textarea:return import("@volverjs/ui-vue/vv-textarea");case s.radio:return import("@volverjs/ui-vue/vv-radio");case s.radioGroup:return import("@volverjs/ui-vue/vv-radio-group");case s.checkbox:return import("@volverjs/ui-vue/vv-checkbox");case s.checkboxGroup:return import("@volverjs/ui-vue/vv-checkbox-group");case s.combobox:return import("@volverjs/ui-vue/vv-combobox")}return import("@volverjs/ui-vue/vv-input-text")})}),hasProps:q,invalid:b}},render(){return this.is?t.h(this.is,this.hasProps,this.$slots):this.type===s.custom?t.h(this.component,null,this.$slots):t.h(this.component,this.hasProps,this.$slots)}}),k=(e,o={})=>{const l=e instanceof y.ZodEffects?e.innerType().shape:e.shape;return{...(e instanceof y.ZodObject?e._def.unknownKeys==="passthrough":!1)?o:{},...Object.fromEntries(Object.entries(l).map(([r,n])=>{const c=o[r];let i;if(n instanceof y.ZodDefault&&(i=n._def.defaultValue()),c===null&&n instanceof y.ZodNullable)return[r,c];if(n instanceof y.ZodSchema){const f=n.safeParse(o[r]);if(f.success)return[r,f.data??i]}return n instanceof y.ZodObject?[r,k(n,c&&typeof c=="object"?c:{})]:[r,i]}))}},M=(e,o,l)=>t.defineComponent({name:"FormComponent",props:{modelValue:{type:Object,default:()=>({})},continuosValidation:{type:Boolean,default:!1}},emits:["invalid","valid","submit","update:modelValue"],expose:["submit","errors","status"],setup(u,{emit:r}){const n=t.ref(k(e,u.modelValue)),c=(l==null?void 0:l.continuosValidation)||u.continuosValidation;t.watch(()=>u.modelValue,h=>{if(h){const m=t.isProxy(h)?t.toRaw(h):h;n.value=typeof(m==null?void 0:m.clone)=="function"?m.clone():JSON.parse(JSON.stringify(m))}},{deep:!0}),A.watchThrottled(n,h=>{(i.value||c)&&a(),(!h||!u.modelValue||JSON.stringify(h)!==JSON.stringify(u.modelValue))&&r("update:modelValue",h)},{deep:!0,throttle:(l==null?void 0:l.updateThrottle)??500});const i=t.ref(),f=t.ref(),a=(h=n.value)=>{const m=e.safeParse(h);return m.success?(i.value=void 0,f.value="valid",n.value=m.data,r("valid",m.data),!0):(i.value=m.error.format(),f.value="invalid",r("invalid",i.value),!1)},p=()=>a()?(r("submit",n.value),!0):!1;return t.provide(o,{modelValue:n,submit:p,errors:t.readonly(i)}),{submit:p}},render(){return t.h("form",{onSubmit:t.withModifiers(this.submit,["prevent"])},this.$slots)}}),W=(e,o)=>t.defineComponent({name:"WrapperComponent",props:{name:{type:String,required:!0},tag:{type:String,default:void 0}},emits:["invalid","valid"],expose:["fields","invalid"],setup(l,{emit:u}){const r=t.inject(e),n=t.inject(o,void 0),c=t.ref(new Set),i=t.ref(new Map),{name:f}=t.toRefs(l);t.provide(o,{name:t.readonly(f),errors:i,fields:c}),t.watch(c,p=>{n!=null&&n.fields&&p.forEach(h=>{n==null||n.fields.value.add(h)})},{deep:!0}),t.watch(()=>new Map(i.value),(p,h)=>{n!=null&&n.errors&&(Array.from(h.keys()).forEach(m=>{n.errors.value.delete(m)}),Array.from(p.keys()).forEach(m=>{const V=p.get(m);V&&n.errors.value.set(m,V)}))},{deep:!0});const a=t.computed(()=>r!=null&&r.errors.value?i.value.size>0:!1);return t.watch(a,()=>{a.value?u("invalid"):u("valid")}),{formProvided:r,invalid:a,fields:c,errors:i}},render(){var l,u,r,n,c,i,f,a;return this.tag?t.h(this.tag,null,((n=(r=this.$slots).default)==null?void 0:n.call(r,{invalid:this.invalid,formData:(l=this.formProvided)==null?void 0:l.modelValue,errors:(u=this.formProvided)==null?void 0:u.errors,fieldsErrors:this.errors}))??this.$slots.defalut):((a=(f=this.$slots).default)==null?void 0:a.call(f,{invalid:this.invalid,formData:(c=this.formProvided)==null?void 0:c.modelValue,errors:(i=this.formProvided)==null?void 0:i.errors,fieldsErrors:this.errors}))??this.$slots.defalut}}),w=(e,o={})=>{const l=Symbol(),u=Symbol(),r=Symbol(),n=M(e,l,o),c=W(l,u),i=K(l,u,r,o);return{VvForm:n,VvFormWrapper:c,VvFormField:i,formInjectionKey:l,formWrapperInjectionKey:u,formFieldInjectionKey:r}},O=Symbol(),I=e=>{let o={};return e.schema&&(o=w(e.schema,e)),{...o,install(l,{global:u=!1}={}){l.provide(O,e),u&&(l.config.globalProperties.$vvForm=e,o!=null&&o.VvForm&&l.component("VvForm",o.VvForm),o!=null&&o.VvFormWrapper&&l.component("VvFormWrapper",o.VvFormWrapper),o!=null&&o.VvFormField&&l.component("VvFormField",o.VvFormField))}}},Z=(e,o={})=>{const l={...t.inject(O,{}),...o};return w(e,l)};v.FormFieldType=s,v.createForm=I,v.defaultObjectBySchema=k,v.formFactory=w,v.pluginInjectionKey=O,v.useForm=Z,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})});
1
+ (function(y,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):(y=typeof globalThis<"u"?globalThis:y||self,r(y["@volverjs/form-vue"]={},y.Vue,y.VueUseCore,y.zod))})(this,function(y,r,M,x){"use strict";function I(e){return Array.isArray(e)}function Z(e){return typeof e<"u"}function G(e){return e===null}function W(e){return typeof e=="object"}function A(e){return typeof e=="string"}function k(e){return typeof e>"u"}const z=/^[0-9]+$/,U=["__proto__","prototype","constructor"];function C(e,s,f){const i=Z(f)?f:void 0;if(!W(e)||!A(s))return i;const a=K(s);if(a.length!==0){for(const t of a){if(t==="*")continue;const l=function(o){return o.map(u=>k(u)||G(u)?u:I(u)?l(u):u[t])};if(I(e)&&!z.test(t)?e=l(e):e=e[t],k(e)||G(e))break}return k(e)?i:e}}function _(e,s,f){if(!W(e)||!A(s))return;const i=K(s);if(i.length===0)return;const a=i.length;for(let t=0;t<a;t++){const l=i[t];if(t===a-1){e[l]=f;return}if(l==="*"&&I(e)){const o=i.slice(t+1).join(".");for(const u of e)_(u,o,f);return}k(e[l])&&(e[l]={}),e=e[l]}}function K(e){const s=e.split(/[.]|(?:\[(\d|\*)\])/).filter(f=>!!f);return s.some(f=>U.indexOf(f)!==-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="datetimeLocal",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||{}),O=(e=>(e.invalid="invalid",e.valid="valid",e))(O||{});const J=(e,s,f,i)=>r.defineComponent({name:"FieldComponent",props:{type:{type:String,validator:a=>Object.values(c).includes(a),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}},emits:["invalid","valid","update:formData","update:modelValue"],expose:["invalid","invalidLabel","errors"],setup(a,{slots:t,emit:l}){const o=r.computed({get(){if(n!=null&&n.formData)return C(Object(n.formData.value),String(a.name))},set(d){n!=null&&n.formData&&(_(Object(n.formData.value),String(a.name),d),l("update:modelValue",{newValue:o.value,formData:n==null?void 0:n.formData}))}});r.onMounted(()=>{o.value===void 0&&a.defaultValue!==void 0&&(o.value=a.defaultValue)}),r.onBeforeUnmount(()=>{w(),g()});const u=r.inject(s,void 0);u&&u.fields.value.add(a.name);const n=r.inject(e),{props:h,name:b}=r.toRefs(a),p=r.computed(()=>{if(n!=null&&n.errors.value)return C(n.errors.value,String(a.name))}),m=r.computed(()=>{var d;return(d=p.value)==null?void 0:d._errors}),v=r.computed(()=>p.value!==void 0),w=r.watch(v,()=>{v.value?(l("invalid",m.value),u&&u.errors.value.set(a.name,{_errors:m.value})):(l("valid",o.value),u&&u.errors.value.delete(a.name))}),g=r.watch(()=>n==null?void 0:n.formData,()=>{l("update:formData",n==null?void 0:n.formData)},{deep:!0}),V=d=>{o.value=d},E=r.computed(()=>typeof h.value=="function"?h.value(n==null?void 0:n.formData):h.value),L=r.computed(()=>({...E.value,name:E.value.name??a.name,invalid:v.value,valid:a.showValid?!!(!v.value&&o.value):void 0,type:(d=>{if([c.text,c.number,c.email,c.password,c.tel,c.url,c.search,c.date,c.time,c.datetimeLocal,c.month,c.week,c.color].includes(d))return d})(a.type),invalidLabel:m.value,modelValue:o.value,"onUpdate:modelValue":V}));return r.provide(f,{name:r.readonly(b),errors:r.readonly(p)}),{component:r.computed(()=>{if(a.type===c.custom)return{render(){var d;return((d=t.default)==null?void 0:d.call(t,{modelValue:o.value,onUpdate:V,invalid:v.value,invalidLabel:m.value,formData:n==null?void 0:n.formData.value,formErrors:n==null?void 0:n.errors.value,errors:p.value}))??t.defalut}};if(!((i==null?void 0:i.lazyLoad)??a.lazyLoad)){let d;switch(a.type){case c.select:d=r.resolveComponent("VvSelect");break;case c.checkbox:d=r.resolveComponent("VvCheckbox");break;case c.radio:d=r.resolveComponent("VvRadio");break;case c.textarea:d=r.resolveComponent("VvTextarea");break;case c.radioGroup:d=r.resolveComponent("VvRadioGroup");break;case c.checkboxGroup:d=r.resolveComponent("VvCheckboxGroup");break;case c.combobox:d=r.resolveComponent("VvCombobox");break;default:d=r.resolveComponent("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 r.defineAsyncComponent(async()=>{switch(i!=null&&i.sideEffects&&await Promise.resolve(i.sideEffects(a.type)),a.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.combobox:return import("@volverjs/ui-vue/vv-combobox")}return import("@volverjs/ui-vue/vv-input-text")})}),hasProps:L,invalid:v}},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)}}),$=(e,s={})=>{const f=t=>{let l=t;for(;l instanceof x.ZodEffects;)l=l.innerType();return l},i=f(e);return{...(i instanceof x.ZodObject?i._def.unknownKeys==="passthrough":!1)?s:{},...Object.fromEntries(Object.entries(i.shape).map(([t,l])=>{const o=s[t],u=f(l);let n;if(u instanceof x.ZodDefault&&(n=u._def.defaultValue()),o===null&&u instanceof x.ZodNullable)return[t,o];if(u instanceof x.ZodSchema){const h=l.safeParse(o);if(h.success)return[t,h.data??n]}return u instanceof x.ZodObject?[t,$(u,o&&typeof o=="object"?o:{})]:[t,n]}))}},P=(e,s,f)=>{const i=r.ref(),a=r.ref(),t=r.ref(),l=r.defineComponent({name:"FormComponent",props:{modelValue:{type:Object,default:()=>({})},updateThrottle:{type:Number,default:500},continuosValidation:{type:Boolean,default:!1}},emits:["invalid","valid","submit","update:modelValue"],expose:["submit","errors","status"],setup(o,{emit:u}){t.value=$(e,r.toRaw(o.modelValue));const n=(f==null?void 0:f.continuosValidation)??o.continuosValidation;r.watch(()=>o.modelValue,m=>{if(m){const v=r.isProxy(m)?r.toRaw(m):m;t.value=typeof(v==null?void 0:v.clone)=="function"?v.clone():JSON.parse(JSON.stringify(v))}},{deep:!0}),M.watchThrottled(t,m=>{(i.value||n)&&h(),(!m||!o.modelValue||JSON.stringify(m)!==JSON.stringify(o.modelValue))&&u("update:modelValue",m)},{deep:!0,throttle:(f==null?void 0:f.updateThrottle)??o.updateThrottle});const h=(m=t.value)=>{const v=e.safeParse(m);return v.success?(i.value=void 0,a.value=O.valid,t.value=v.data,u("update:modelValue",t.value),u("valid",v.data),!0):(i.value=v.error.format(),a.value=O.invalid,u("invalid",i.value),!1)},b=()=>h()?(u("submit",t.value),!0):!1,p=r.computed(()=>a.value===O.invalid);return r.provide(s,{formData:t,submit:b,errors:r.readonly(i),status:r.readonly(a),invalid:p}),{formData:t,submit:b,errors:r.readonly(i),status:r.readonly(a),invalid:p}},render(){return r.h("form",{onSubmit:r.withModifiers(this.submit,["prevent"])},{default:()=>{var o,u;return((u=(o=this.$slots)==null?void 0:o.default)==null?void 0:u.call(o,{formData:this.formData,submit:this.submit,errors:this.errors,status:this.status,invalid:this.invalid}))??this.$slots.default}})}});return{errors:i,status:a,formData:t,VvForm:l}},T=(e,s)=>r.defineComponent({name:"WrapperComponent",props:{name:{type:String,required:!0},tag:{type:String,default:void 0}},emits:["invalid","valid"],expose:["fields","invalid"],setup(i,{emit:a}){const t=r.inject(e),l=r.inject(s,void 0),o=r.ref(new Set),u=r.ref(new Map),{name:n}=r.toRefs(i);r.provide(s,{name:r.readonly(n),errors:u,fields:o}),r.watch(o,b=>{l!=null&&l.fields&&b.forEach(p=>{l==null||l.fields.value.add(p)})},{deep:!0}),r.watch(()=>new Map(u.value),(b,p)=>{l!=null&&l.errors&&(Array.from(p.keys()).forEach(m=>{l.errors.value.delete(m)}),Array.from(b.keys()).forEach(m=>{const v=b.get(m);v&&l.errors.value.set(m,v)}))},{deep:!0});const h=r.computed(()=>t!=null&&t.invalid.value?u.value.size>0:!1);return r.watch(h,()=>{h.value?a("invalid"):a("valid")}),{formData:t==null?void 0:t.formData,errors:t==null?void 0:t.errors,invalid:h,fields:o,fieldsErrors:u}},render(){var i,a;return this.tag?r.h(this.tag,null,{default:()=>{var t,l;return((l=(t=this.$slots).default)==null?void 0:l.call(t,{invalid:this.invalid,formData:this.formData,errors:this.errors,fieldsErrors:this.fieldsErrors}))??this.$slots.defalut}}):((a=(i=this.$slots).default)==null?void 0:a.call(i,{invalid:this.invalid,formData:this.formData,errors:this.errors,fieldsErrors:this.fieldsErrors}))??this.$slots.defalut}}),R=(e,s)=>{const f=r.defineComponent({props:{schema:{type:[Array,Function],required:!0}},setup(i,{slots:a}){const t=r.inject(e);if(!(t!=null&&t.formData))return;const l=typeof i.schema=="function"?i.schema(t):i.schema;let o;return()=>{var u;return l.reduce((n,h)=>{const b=typeof h=="function"?h(t):h,{vvIs:p,vvName:m,vvSlots:v,vvChildren:w,vvIf:g,vvElseIf:V,vvType:E,vvDefaultValue:L,vvShowValid:q,...d}=b;if(g!==void 0){if(typeof g=="string"?o=!!C(Object(t.formData.value),g):typeof g=="function"?o=r.unref(g(t)):o=r.unref(g),!o)return n}else if(V!==void 0&&o!==void 0){if(o||(typeof V=="string"?o=!!C(Object(t.formData.value),V):typeof V=="function"?o=r.unref(V(t)):o=r.unref(V),!o))return n}else o=void 0;const N=w?r.h(f,{schema:w}):void 0;return m?(n.push(r.h(s,{name:m,is:p,type:E,defaultValue:L,showValid:q,props:d},v??N)),n):p?(n.push(r.h(p,d,v??N)),n):(w&&n.push(N),n)},[(u=a==null?void 0:a.default)==null?void 0:u.call(a,{formData:t==null?void 0:t.formData.value,submit:t==null?void 0:t.submit,errors:t==null?void 0:t.errors.value,status:t==null?void 0:t.status.value,invalid:t==null?void 0:t.invalid.value})])}}});return f},S=(e,s={})=>{const f=Symbol(),i=Symbol(),a=Symbol(),{VvForm:t,errors:l,status:o,formData:u}=P(e,f,s),n=T(f,i),h=J(f,i,a,s),b=R(f,h);return{VvForm:t,VvFormWrapper:n,VvFormField:h,VvFormTemplate:b,formInjectionKey:f,formWrapperInjectionKey:i,formFieldInjectionKey:a,errors:l,status:o,formData:u}},B=Symbol(),D=e=>{let s={};return e.schema&&(s=S(e.schema,e)),{...s,install(f,{global:i=!1}={}){f.provide(B,e),i&&(f.config.globalProperties.$vvForm=e,s!=null&&s.VvForm&&f.component("VvForm",s.VvForm),s!=null&&s.VvFormWrapper&&f.component("VvFormWrapper",s.VvFormWrapper),s!=null&&s.VvFormField&&f.component("VvFormField",s.VvFormField),s!=null&&s.VvFormTemplate&&f.component("VvFormTemplate",s.VvFormTemplate))}}},H=(e,s={})=>r.getCurrentInstance()?S(e,{...r.inject(B,{}),...s}):S(e,s),Q=(e,s={})=>S(e,s);y.FormFieldType=c,y.createForm=D,y.defaultObjectBySchema=$,y.formFactory=Q,y.pluginInjectionKey=B,y.useForm=H,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"})});
package/dist/types.d.ts CHANGED
@@ -1,31 +1,130 @@
1
1
  import type { Ref } from 'vue'
2
- import type { ZodFormattedError } from 'zod'
3
- import type { FormFieldType } from './enums'
2
+ import type { z, AnyZodObject, ZodEffects } from 'zod'
3
+ import type { FormFieldType, FormStatus } from './enums'
4
4
 
5
- export type FormComposableOptions = {
5
+ export type FormSchema =
6
+ | AnyZodObject
7
+ | ZodEffects<AnyZodObject>
8
+ | ZodEffects<ZodEffects<AnyZodObject>>
9
+
10
+ export type FormFieldComponentOptions = {
6
11
  lazyLoad?: boolean
12
+ sideEffects?: (type: `${FormFieldType}`) => Promise | void
13
+ }
14
+
15
+ export type FormComponentOptions = {
7
16
  updateThrottle?: number
8
17
  continuosValidation?: boolean
9
- sideEffects?: (type: `${FormFieldType}`) => Promise | void
10
18
  }
11
19
 
20
+ export type FormComposableOptions = FormFieldComponentOptions &
21
+ FormComponentOptions
22
+
12
23
  export type FormPluginOptions = {
13
24
  schema?: ZodSchema
14
25
  } & FormComposableOptions
15
26
 
16
- export type InjectedFormData<Type = Recrod<string | number, unknown>> = {
17
- modelValue: Ref<Type>
18
- errors: Ref<ZodFormattedError<Type>>
27
+ export type InjectedFormData<Schema extends FormSchema> = {
28
+ formData: Ref<Partial<z.infer<Schema>> | undefined>
29
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
19
30
  submit: () => boolean
31
+ status: Readonly<Ref<FormStatus | undefined>>
32
+ invalid: Readonly<Ref<boolean>>
20
33
  }
21
34
 
22
- export type InjectedFormWrapperData = {
35
+ export type InjectedFormWrapperData<Schema extends FormSchema> = {
23
36
  name: Ref<string>
24
37
  fields: Ref<Set<string>>
25
- errors: Ref<Map<string, Record<string, { _errors: string[] }>>>
38
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
26
39
  }
27
40
 
28
- export type InjectedFormFieldData = {
41
+ export type InjectedFormFieldData<Schema extends FormSchema> = {
29
42
  name: Ref<string>
30
- errors: Ref<Map<string, Record<string, { _errors: string[] }>>>
43
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
31
44
  }
45
+
46
+ export type Primitive =
47
+ | null
48
+ | undefined
49
+ | string
50
+ | number
51
+ | boolean
52
+ | symbol
53
+ | bigint
54
+
55
+ type ArrayKey = number
56
+
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ type IsTuple<T extends readonly any[]> = number extends T['length']
59
+ ? false
60
+ : true
61
+
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ type TupleKeys<T extends readonly any[]> = Exclude<keyof T, keyof any[]>
64
+
65
+ export type PathConcat<
66
+ TKey extends string | number,
67
+ TValue,
68
+ > = TValue extends Primitive ? `${TKey}` : `${TKey}` | `${TKey}.${Path<TValue>}`
69
+
70
+ export type Path<T> = T extends readonly (infer V)[]
71
+ ? IsTuple<T> extends true
72
+ ? {
73
+ [K in TupleKeys<T>]-?: PathConcat<K & string, T[K]>
74
+ }[TupleKeys<T>]
75
+ : PathConcat<ArrayKey, V>
76
+ : {
77
+ [K in keyof T]-?: PathConcat<K & string, T[K]>
78
+ }[keyof T]
79
+
80
+ export type PathValue<T, TPath extends Path<T> | ArrayPath<T>> = T extends any
81
+ ? TPath extends `${infer K}.${infer R}`
82
+ ? K extends keyof T
83
+ ? R extends Path<T[K]>
84
+ ? undefined extends T[K]
85
+ ? PathValue<T[K], R> | undefined
86
+ : PathValue<T[K], R>
87
+ : never
88
+ : K extends `${ArrayKey}`
89
+ ? T extends readonly (infer V)[]
90
+ ? PathValue<V, R & Path<V>>
91
+ : never
92
+ : never
93
+ : TPath extends keyof T
94
+ ? T[TPath]
95
+ : TPath extends `${ArrayKey}`
96
+ ? T extends readonly (infer V)[]
97
+ ? V
98
+ : never
99
+ : never
100
+ : never
101
+
102
+ export type AnyBoolean<Schema> =
103
+ | boolean
104
+ | Ref<boolean>
105
+ | ((data?: InjectedFormData<Schema>) => boolean | Ref<boolean>)
106
+
107
+ export type SimpleFormTemplateItem<Schema extends FormSchema> = Record<
108
+ string,
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ any
111
+ > & {
112
+ vvIs?: string | Component
113
+ vvName?: Path<z.infer<Schema>>
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ vvSlots?: Record<string, any>
116
+ vvChildren?: Array<
117
+ | SimpleFormTemplateItem<Schema>
118
+ | ((data?: InjectedFormData<Schema>) => SimpleFormTemplateItem<Schema>)
119
+ >
120
+ vvIf?: AnyBoolean<Schema> | Path<z.infer<Schema>>
121
+ vvElseIf?: AnyBoolean<Schema> | Path<z.infer<Schema>>
122
+ vvType?: `${FormFieldType}`
123
+ vvShowValid?: boolean
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ vvDefaultValue?: any
126
+ }
127
+
128
+ export type FormTemplateItem<Schema extends FormSchema> =
129
+ | SimpleFormTemplateItem<Schema>
130
+ | ((data?: InjectedFormData<Schema>) => SimpleFormTemplateItem<Schema>)
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,3 @@
1
1
  import { type z } from 'zod';
2
- export declare const defaultObjectBySchema: <Schema extends z.AnyZodObject | z.ZodEffects<z.AnyZodObject, {
3
- [x: string]: any;
4
- }, {
5
- [x: string]: any;
6
- }>>(schema: Schema, original?: Partial<z.TypeOf<Schema>>) => Partial<z.TypeOf<Schema>>;
2
+ import type { FormSchema } from './types';
3
+ export declare const defaultObjectBySchema: <Schema extends FormSchema>(schema: Schema, original?: Partial<z.TypeOf<Schema>>) => Partial<z.TypeOf<Schema>>;
package/package.json CHANGED
@@ -19,13 +19,14 @@
19
19
  "bugs": {
20
20
  "url": "https://github.com/volverjs/form-vue/issues"
21
21
  },
22
- "version": "0.0.9",
22
+ "version": "0.0.10-beta.10",
23
23
  "engines": {
24
24
  "node": ">= 16.x"
25
25
  },
26
26
  "packageManager": "pnpm@7.8.0",
27
27
  "type": "module",
28
28
  "main": "./dist/index.js",
29
+ "module": "./dist/index.js",
29
30
  "types": "./dist/index.d.ts",
30
31
  "files": [
31
32
  "dist",
@@ -34,34 +35,34 @@
34
35
  "*.d.ts"
35
36
  ],
36
37
  "dependencies": {
37
- "@volverjs/ui-vue": "0.0.5-beta.1",
38
- "@vueuse/core": "^9.13.0",
38
+ "@volverjs/ui-vue": "0.0.6-beta.7",
39
+ "@vueuse/core": "^10.1.2",
39
40
  "ts-dot-prop": "^2.1.2",
40
41
  "vue": "^3.2.47",
41
42
  "zod": "^3.21.4"
42
43
  },
43
44
  "devDependencies": {
44
- "@playwright/experimental-ct-vue": "^1.31.2",
45
+ "@playwright/experimental-ct-vue": "^1.32.3",
45
46
  "@testing-library/vue": "^7.0.0",
46
- "@typescript-eslint/eslint-plugin": "^5.55.0",
47
- "@typescript-eslint/parser": "^5.55.0",
48
- "@vitejs/plugin-vue": "^4.1.0",
47
+ "@typescript-eslint/eslint-plugin": "^5.59.2",
48
+ "@typescript-eslint/parser": "^5.59.2",
49
+ "@vitejs/plugin-vue": "^4.2.1",
49
50
  "@volverjs/style": "^0.1.8",
50
51
  "@vue/compiler-sfc": "^3.2.47",
51
52
  "@vue/runtime-core": "^3.2.47",
52
- "@vue/test-utils": "^2.3.1",
53
+ "@vue/test-utils": "^2.3.2",
53
54
  "copy": "^0.3.2",
54
- "eslint": "^8.36.0",
55
- "eslint-config-prettier": "^8.7.0",
55
+ "eslint": "^8.39.0",
56
+ "eslint-config-prettier": "^8.8.0",
56
57
  "eslint-plugin-prettier": "^4.2.1",
57
- "happy-dom": "^8.9.0",
58
- "prettier": "^2.8.4",
59
- "typescript": "^5.0.2",
60
- "vite": "^4.2.0",
58
+ "happy-dom": "^9.10.4",
59
+ "prettier": "^2.8.8",
60
+ "typescript": "^5.0.4",
61
+ "vite": "^4.3.4",
61
62
  "vite-plugin-eslint": "^1.8.1",
62
- "vite-plugin-externalize-deps": "^0.5.0",
63
- "vitest": "^0.29.3",
64
- "vue-tsc": "^1.2.0"
63
+ "vite-plugin-externalize-deps": "^0.6.0",
64
+ "vitest": "^0.30.1",
65
+ "vue-tsc": "^1.6.3"
65
66
  },
66
67
  "typesVersions": {
67
68
  "*": {
@@ -83,6 +84,7 @@
83
84
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
84
85
  "type-check": "tsc --noEmit",
85
86
  "build": "vite build && vue-tsc --declaration --emitDeclarationOnly && copy src/**/types.d.ts dist",
87
+ "test": "pnpm run test-vitest && pnpm run test-playwright",
86
88
  "test-vitest": "vitest run",
87
89
  "test-vitest-watch": "vitest",
88
90
  "test-playwright": "playwright test -c playwright-ct.config.ts",
package/src/VvForm.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  type InjectionKey,
3
+ type DeepReadonly,
4
+ type Ref,
3
5
  withModifiers,
4
6
  defineComponent,
5
7
  ref,
@@ -9,33 +11,37 @@ import {
9
11
  h,
10
12
  toRaw,
11
13
  isProxy,
14
+ computed,
12
15
  } from 'vue'
13
16
  import { watchThrottled } from '@vueuse/core'
14
-
15
- import type { AnyZodObject, ZodEffects } from 'zod'
16
- import type { InjectedFormData } from './types'
17
+ import type { z, ZodFormattedError, TypeOf } from 'zod'
18
+ import type {
19
+ FormComponentOptions,
20
+ FormSchema,
21
+ InjectedFormData,
22
+ } from './types'
23
+ import { FormStatus } from './enums'
17
24
  import { defaultObjectBySchema } from './utils'
18
25
 
19
- export enum FormStatus {
20
- invalid = 'invalid',
21
- valid = 'valid',
22
- }
23
-
24
- export const defineForm = (
25
- schema: AnyZodObject | ZodEffects<AnyZodObject>,
26
- provideKey: InjectionKey<InjectedFormData>,
27
- options?: {
28
- updateThrottle?: number
29
- continuosValidation?: boolean
30
- },
26
+ export const defineForm = <Schema extends FormSchema>(
27
+ schema: Schema,
28
+ provideKey: InjectionKey<InjectedFormData<Schema>>,
29
+ options?: FormComponentOptions,
31
30
  ) => {
32
- return defineComponent({
31
+ const errors = ref<ZodFormattedError<z.infer<Schema>>>()
32
+ const status = ref<FormStatus | undefined>()
33
+ const formData = ref<Partial<z.infer<Schema> | undefined>>()
34
+ const component = defineComponent({
33
35
  name: 'FormComponent',
34
36
  props: {
35
37
  modelValue: {
36
38
  type: Object,
37
39
  default: () => ({}),
38
40
  },
41
+ updateThrottle: {
42
+ type: Number,
43
+ default: 500,
44
+ },
39
45
  continuosValidation: {
40
46
  type: Boolean,
41
47
  default: false,
@@ -44,12 +50,13 @@ export const defineForm = (
44
50
  emits: ['invalid', 'valid', 'submit', 'update:modelValue'],
45
51
  expose: ['submit', 'errors', 'status'],
46
52
  setup(props, { emit }) {
47
- const localModelValue = ref(
48
- defaultObjectBySchema(schema, props.modelValue),
53
+ formData.value = defaultObjectBySchema(
54
+ schema,
55
+ toRaw(props.modelValue),
49
56
  )
50
57
 
51
58
  const keepValidation =
52
- options?.continuosValidation || props.continuosValidation
59
+ options?.continuosValidation ?? props.continuosValidation
53
60
 
54
61
  watch(
55
62
  () => props.modelValue,
@@ -58,7 +65,7 @@ export const defineForm = (
58
65
  const original = isProxy(newValue)
59
66
  ? toRaw(newValue)
60
67
  : newValue
61
- localModelValue.value =
68
+ formData.value =
62
69
  typeof original?.clone === 'function'
63
70
  ? original.clone()
64
71
  : JSON.parse(JSON.stringify(original))
@@ -68,7 +75,7 @@ export const defineForm = (
68
75
  )
69
76
  // v-model
70
77
  watchThrottled(
71
- localModelValue,
78
+ formData,
72
79
  (newValue) => {
73
80
  if (errors.value || keepValidation) {
74
81
  parseModelValue()
@@ -82,23 +89,27 @@ export const defineForm = (
82
89
  emit('update:modelValue', newValue)
83
90
  }
84
91
  },
85
- { deep: true, throttle: options?.updateThrottle ?? 500 },
92
+ {
93
+ deep: true,
94
+ throttle: options?.updateThrottle ?? props.updateThrottle,
95
+ },
86
96
  )
87
97
 
88
- // validation
89
- const errors = ref()
90
- const status = ref()
91
- const parseModelValue = (value = localModelValue.value) => {
98
+ const parseModelValue = (value = formData.value) => {
92
99
  const parseResult = schema.safeParse(value)
93
100
  if (!parseResult.success) {
94
- errors.value = parseResult.error.format()
101
+ errors.value =
102
+ parseResult.error.format() as ZodFormattedError<
103
+ z.infer<Schema>
104
+ >
95
105
  status.value = FormStatus.invalid
96
106
  emit('invalid', errors.value)
97
107
  return false
98
108
  }
99
109
  errors.value = undefined
100
110
  status.value = FormStatus.valid
101
- localModelValue.value = parseResult.data
111
+ formData.value = parseResult.data
112
+ emit('update:modelValue', formData.value)
102
113
  emit('valid', parseResult.data)
103
114
  return true
104
115
  }
@@ -108,18 +119,28 @@ export const defineForm = (
108
119
  if (!parseModelValue()) {
109
120
  return false
110
121
  }
111
- emit('submit', localModelValue.value)
122
+ emit('submit', formData.value)
112
123
  return true
113
124
  }
114
125
 
126
+ const invalid = computed(() => status.value === FormStatus.invalid)
127
+
115
128
  // provide
116
129
  provide(provideKey, {
117
- modelValue: localModelValue,
130
+ formData,
118
131
  submit,
119
132
  errors: readonly(errors),
133
+ status: readonly(status),
134
+ invalid,
120
135
  })
121
136
 
122
- return { submit }
137
+ return {
138
+ formData,
139
+ submit,
140
+ errors: readonly(errors),
141
+ status: readonly(status),
142
+ invalid,
143
+ }
123
144
  },
124
145
  render() {
125
146
  return h(
@@ -127,8 +148,45 @@ export const defineForm = (
127
148
  {
128
149
  onSubmit: withModifiers(this.submit, ['prevent']),
129
150
  },
130
- this.$slots,
151
+ {
152
+ default: () =>
153
+ this.$slots?.default?.({
154
+ formData: this.formData,
155
+ submit: this.submit,
156
+ errors: this.errors,
157
+ status: this.status,
158
+ invalid: this.invalid,
159
+ }) ?? this.$slots.default,
160
+ },
131
161
  )
132
162
  },
133
163
  })
164
+ return {
165
+ errors,
166
+ status,
167
+ formData,
168
+ /**
169
+ * An hack to add types to the default slot
170
+ */
171
+ VvForm: component as typeof component & {
172
+ new (): {
173
+ $slots: {
174
+ default: (_: {
175
+ formData: unknown extends
176
+ | Partial<TypeOf<Schema>>
177
+ | undefined
178
+ ? undefined
179
+ : Partial<TypeOf<Schema>> | undefined
180
+ submit: () => boolean
181
+ errors: Readonly<
182
+ Ref<DeepReadonly<z.inferFormattedError<Schema>>>
183
+ >
184
+ status: Ref<DeepReadonly<`${FormStatus}` | undefined>>
185
+ invalid: Ref<DeepReadonly<boolean>>
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ }) => any
188
+ }
189
+ }
190
+ },
191
+ }
134
192
  }