@volverjs/form-vue 0.0.10-beta.1 → 0.0.10-beta.3

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(p,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):(p=typeof globalThis<"u"?globalThis:p||self,t(p["@volverjs/form-vue"]={},p.Vue,p.VueUseCore,p.zod))})(this,function(p,t,N,b){"use strict";function x(e){return Array.isArray(e)}function A(e){return typeof e<"u"}function O(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 B=/^[0-9]+$/,I=["__proto__","prototype","constructor"];function $(e,o,l){const i=A(l)?l:void 0;if(!C(e)||!j(o))return i;const a=E(o);if(a.length!==0){for(const r of a){if(r==="*")continue;const s=function(u){return u.map(d=>g(d)||O(d)?d:x(d)?s(d):d[r])};if(x(e)&&!B.test(r)?e=s(e):e=e[r],g(e)||O(e))break}return g(e)?i:e}}function D(e,o,l){if(!C(e)||!j(o))return;const i=E(o);if(i.length===0)return;const a=i.length;for(let r=0;r<a;r++){const s=i[r];if(r===a-1){e[s]=l;return}if(s==="*"&&x(e)){const u=i.slice(r+1).join(".");for(const d of e)D(d,u,l);return}g(e[s])&&(e[s]={}),e=e[s]}}function E(e){const o=e.split(/[.]|(?:\[(\d|\*)\])/).filter(l=>!!l);return o.some(l=>I.indexOf(l)!==-1)?[]:o}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||{});const _=(e,o,l,i={})=>t.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}},emits:["invalid","valid","update:formData","update:modelValue"],expose:["invalid","invalidLabel","errors"],setup(a,{slots:r,emit:s}){const u=t.computed({get(){if(n!=null&&n.modelValue)return $(Object(n.modelValue.value),String(a.name))},set(f){n!=null&&n.modelValue&&(D(Object(n.modelValue.value),String(a.name),f),s("update:modelValue",{newValue:u.value,formData:n==null?void 0:n.modelValue}))}});t.onMounted(()=>{u.value===void 0&&a.defaultValue!==void 0&&(u.value=a.defaultValue)}),t.onBeforeUnmount(()=>{q(),U()});const d=t.inject(o,void 0);d&&d.fields.value.add(a.name);const n=t.inject(e),{props:h,name:v}=t.toRefs(a),m=t.computed(()=>{if(n!=null&&n.errors.value)return $(n.errors.value,String(a.name))}),V=t.computed(()=>{var f;return(f=m.value)==null?void 0:f._errors}),y=t.computed(()=>m.value!==void 0),q=t.watch(y,()=>{y.value?(s("invalid",V.value),d&&d.errors.value.set(a.name,{_errors:V.value})):(s("valid",u.value),d&&d.errors.value.delete(a.name))}),U=t.watch(()=>n==null?void 0:n.modelValue,()=>{s("update:formData",n==null?void 0:n.modelValue)},{deep:!0}),G=f=>{u.value=f},L=t.computed(()=>typeof h.value=="function"?h.value(n==null?void 0:n.modelValue):h.value),J=t.computed(()=>({...L.value,name:L.value.name??a.name,invalid:y.value,valid:a.showValid?!!(!y.value&&u.value):void 0,type:(f=>{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(f))return f})(a.type),invalidLabel:V.value,modelValue:u.value,errors:a.is?m.value:void 0,"onUpdate:modelValue":G}));return t.provide(l,{name:t.readonly(v),errors:t.readonly(m)}),{component:t.computed(()=>{if(a.type===c.custom)return{render(){var f;return((f=r.default)==null?void 0:f.call(r,{modelValue:u.value,onUpdate:G,invalid:y.value,invalidLabel:V.value,formData:n==null?void 0:n.modelValue.value,formErrors:n==null?void 0:n.errors.value,errors:m.value}))??r.defalut}};if(!i.lazyLoad){let f;switch(a.type){case c.select:f=t.resolveComponent("VvSelect");break;case c.checkbox:f=t.resolveComponent("VvCheckbox");break;case c.radio:f=t.resolveComponent("VvRadio");break;case c.textarea:f=t.resolveComponent("VvTextarea");break;case c.radioGroup:f=t.resolveComponent("VvRadioGroup");break;case c.checkboxGroup:f=t.resolveComponent("VvCheckboxGroup");break;case c.combobox:f=t.resolveComponent("VvCombobox");break;default:f=t.resolveComponent("VvInputText")}if(typeof f!="string")return f;console.warn(`[form-vue warn]: ${f} not found, the component will be loaded asynchronously. To avoid this warning, please set "lazyLoad" option.`)}return t.defineAsyncComponent(async()=>{switch(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:J,invalid:y}},render(){return this.is?t.h(this.is,this.hasProps,this.$slots):this.type===c.custom?t.h(this.component,null,this.$slots):t.h(this.component,this.hasProps,this.$slots)}}),S=(e,o={})=>{const l=r=>{let s=r;for(;s instanceof b.ZodEffects;)s=s.innerType();return s},i=l(e);return{...(i instanceof b.ZodObject?i._def.unknownKeys==="passthrough":!1)?o:{},...Object.fromEntries(Object.entries(i.shape).map(([r,s])=>{const u=o[r],d=l(s);let n;if(d instanceof b.ZodDefault&&(n=d._def.defaultValue()),u===null&&d instanceof b.ZodNullable)return[r,u];if(d instanceof b.ZodSchema){const h=d.safeParse(o[r]);if(h.success)return[r,h.data??n]}return d instanceof b.ZodObject?[r,S(d,u&&typeof u=="object"?u:{})]:[r,n]}))}},K=(e,o,l)=>{const i=t.ref(),a=t.ref(),r=t.ref();return{errors:i,status:a,formData:r,component: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(s,{emit:u}){r.value=S(e,s.modelValue);const d=(l==null?void 0:l.continuosValidation)||s.continuosValidation;t.watch(()=>s.modelValue,v=>{if(v){const m=t.isProxy(v)?t.toRaw(v):v;r.value=typeof(m==null?void 0:m.clone)=="function"?m.clone():JSON.parse(JSON.stringify(m))}},{deep:!0}),N.watchThrottled(r,v=>{(i.value||d)&&n(),(!v||!s.modelValue||JSON.stringify(v)!==JSON.stringify(s.modelValue))&&u("update:modelValue",v)},{deep:!0,throttle:(l==null?void 0:l.updateThrottle)??500});const n=(v=r.value)=>{const m=e.safeParse(v);return m.success?(i.value=void 0,a.value="valid",r.value=m.data,u("update:modelValue",r.value),u("valid",m.data),!0):(i.value=m.error.format(),a.value="invalid",u("invalid",i.value),!1)},h=()=>n()?(u("submit",r.value),!0):!1;return t.provide(o,{modelValue:r,submit:h,errors:t.readonly(i)}),{submit:h}},render(){return t.h("form",{onSubmit:t.withModifiers(this.submit,["prevent"])},this.$slots)}})}},M=(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:i}){const a=t.inject(e),r=t.inject(o,void 0),s=t.ref(new Set),u=t.ref(new Map),{name:d}=t.toRefs(l);t.provide(o,{name:t.readonly(d),errors:u,fields:s}),t.watch(s,h=>{r!=null&&r.fields&&h.forEach(v=>{r==null||r.fields.value.add(v)})},{deep:!0}),t.watch(()=>new Map(u.value),(h,v)=>{r!=null&&r.errors&&(Array.from(v.keys()).forEach(m=>{r.errors.value.delete(m)}),Array.from(h.keys()).forEach(m=>{const V=h.get(m);V&&r.errors.value.set(m,V)}))},{deep:!0});const n=t.computed(()=>a!=null&&a.errors.value?u.value.size>0:!1);return t.watch(n,()=>{n.value?i("invalid"):i("valid")}),{formProvided:a,invalid:n,fields:s,errors:u}},render(){var l,i,a,r,s,u,d,n;return this.tag?t.h(this.tag,null,((r=(a=this.$slots).default)==null?void 0:r.call(a,{invalid:this.invalid,formData:(l=this.formProvided)==null?void 0:l.modelValue.value,errors:(i=this.formProvided)==null?void 0:i.errors.value,fieldsErrors:this.errors}))??this.$slots.defalut):((n=(d=this.$slots).default)==null?void 0:n.call(d,{invalid:this.invalid,formData:(s=this.formProvided)==null?void 0:s.modelValue.value,errors:(u=this.formProvided)==null?void 0:u.errors.value,fieldsErrors:this.errors}))??this.$slots.defalut}}),w=(e,o={})=>{const l=Symbol(),i=Symbol(),a=Symbol(),{component:r,errors:s,status:u,formData:d}=K(e,l,o),n=M(l,i),h=_(l,i,a,o);return{VvForm:r,VvFormWrapper:n,VvFormField:h,formInjectionKey:l,formWrapperInjectionKey:i,formFieldInjectionKey:a,errors:s,status:u,formData:d}},k=Symbol(),W=e=>{let o={};return e.schema&&(o=w(e.schema,e)),{...o,install(l,{global:i=!1}={}){l.provide(k,e),i&&(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(k,{}),...o};return w(e,l)};p.FormFieldType=c,p.createForm=W,p.defaultObjectBySchema=S,p.formFactory=w,p.pluginInjectionKey=k,p.useForm=Z,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})});
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Ref } from 'vue'
2
- import type { ZodFormattedError } from 'zod'
2
+ import type { z, AnyZodObject, ZodEffects } from 'zod'
3
3
  import type { FormFieldType } from './enums'
4
4
 
5
5
  export type FormComposableOptions = {
@@ -13,19 +13,34 @@ export type FormPluginOptions = {
13
13
  schema?: ZodSchema
14
14
  } & FormComposableOptions
15
15
 
16
- export type InjectedFormData<Type = Recrod<string | number, unknown>> = {
17
- modelValue: Ref<Type>
18
- errors: Ref<ZodFormattedError<Type>>
16
+ export type InjectedFormData<
17
+ Schema extends
18
+ | AnyZodObject
19
+ | ZodEffects<AnyZodObject>
20
+ | ZodEffects<ZodEffects<AnyZodObject>>,
21
+ > = {
22
+ modelValue: Ref<Partial<z.infer<Schema>> | undefined>
23
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
19
24
  submit: () => boolean
20
25
  }
21
26
 
22
- export type InjectedFormWrapperData = {
27
+ export type InjectedFormWrapperData<
28
+ Schema extends
29
+ | AnyZodObject
30
+ | ZodEffects<AnyZodObject>
31
+ | ZodEffects<ZodEffects<AnyZodObject>>,
32
+ > = {
23
33
  name: Ref<string>
24
34
  fields: Ref<Set<string>>
25
- errors: Ref<Map<string, Record<string, { _errors: string[] }>>>
35
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
26
36
  }
27
37
 
28
- export type InjectedFormFieldData = {
38
+ export type InjectedFormFieldData<
39
+ Schema extends
40
+ | AnyZodObject
41
+ | ZodEffects<AnyZodObject>
42
+ | ZodEffects<ZodEffects<AnyZodObject>>,
43
+ > = {
29
44
  name: Ref<string>
30
- errors: Ref<Map<string, Record<string, { _errors: string[] }>>>
45
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
31
46
  }
package/dist/utils.d.ts CHANGED
@@ -3,4 +3,12 @@ export declare const defaultObjectBySchema: <Schema extends z.AnyZodObject | z.Z
3
3
  [x: string]: any;
4
4
  }, {
5
5
  [x: string]: any;
6
+ }> | z.ZodEffects<z.ZodEffects<z.AnyZodObject, {
7
+ [x: string]: any;
8
+ }, {
9
+ [x: string]: any;
10
+ }>, {
11
+ [x: string]: any;
12
+ }, {
13
+ [x: string]: any;
6
14
  }>>(schema: Schema, original?: Partial<z.TypeOf<Schema>>) => Partial<z.TypeOf<Schema>>;
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "bugs": {
20
20
  "url": "https://github.com/volverjs/form-vue/issues"
21
21
  },
22
- "version": "0.0.10-beta.1",
22
+ "version": "0.0.10-beta.3",
23
23
  "engines": {
24
24
  "node": ">= 16.x"
25
25
  },
@@ -35,33 +35,33 @@
35
35
  "*.d.ts"
36
36
  ],
37
37
  "dependencies": {
38
- "@volverjs/ui-vue": "0.0.5-beta.9",
38
+ "@volverjs/ui-vue": "0.0.6-beta.6",
39
39
  "@vueuse/core": "^9.13.0",
40
40
  "ts-dot-prop": "^2.1.2",
41
41
  "vue": "^3.2.47",
42
42
  "zod": "^3.21.4"
43
43
  },
44
44
  "devDependencies": {
45
- "@playwright/experimental-ct-vue": "^1.32.1",
45
+ "@playwright/experimental-ct-vue": "^1.32.3",
46
46
  "@testing-library/vue": "^7.0.0",
47
- "@typescript-eslint/eslint-plugin": "^5.57.0",
48
- "@typescript-eslint/parser": "^5.57.0",
47
+ "@typescript-eslint/eslint-plugin": "^5.58.0",
48
+ "@typescript-eslint/parser": "^5.58.0",
49
49
  "@vitejs/plugin-vue": "^4.1.0",
50
50
  "@volverjs/style": "^0.1.8",
51
51
  "@vue/compiler-sfc": "^3.2.47",
52
52
  "@vue/runtime-core": "^3.2.47",
53
53
  "@vue/test-utils": "^2.3.2",
54
54
  "copy": "^0.3.2",
55
- "eslint": "^8.37.0",
55
+ "eslint": "^8.38.0",
56
56
  "eslint-config-prettier": "^8.8.0",
57
57
  "eslint-plugin-prettier": "^4.2.1",
58
- "happy-dom": "^8.9.0",
58
+ "happy-dom": "^9.1.9",
59
59
  "prettier": "^2.8.7",
60
- "typescript": "^5.0.2",
60
+ "typescript": "^5.0.4",
61
61
  "vite": "^4.2.1",
62
62
  "vite-plugin-eslint": "^1.8.1",
63
63
  "vite-plugin-externalize-deps": "^0.5.0",
64
- "vitest": "^0.29.8",
64
+ "vitest": "^0.30.1",
65
65
  "vue-tsc": "^1.2.0"
66
66
  },
67
67
  "typesVersions": {
package/src/VvForm.ts CHANGED
@@ -12,7 +12,12 @@ import {
12
12
  } from 'vue'
13
13
  import { watchThrottled } from '@vueuse/core'
14
14
 
15
- import type { AnyZodObject, ZodEffects } from 'zod'
15
+ import {
16
+ type z,
17
+ type ZodFormattedError,
18
+ type AnyZodObject,
19
+ type ZodEffects,
20
+ } from 'zod'
16
21
  import type { InjectedFormData } from './types'
17
22
  import { defaultObjectBySchema } from './utils'
18
23
 
@@ -21,114 +26,130 @@ export enum FormStatus {
21
26
  valid = 'valid',
22
27
  }
23
28
 
24
- export const defineForm = (
25
- schema: AnyZodObject | ZodEffects<AnyZodObject>,
26
- provideKey: InjectionKey<InjectedFormData>,
29
+ export const defineForm = <
30
+ Schema extends
31
+ | AnyZodObject
32
+ | ZodEffects<AnyZodObject>
33
+ | ZodEffects<ZodEffects<AnyZodObject>>,
34
+ >(
35
+ schema: Schema,
36
+ provideKey: InjectionKey<InjectedFormData<Schema>>,
27
37
  options?: {
28
38
  updateThrottle?: number
29
39
  continuosValidation?: boolean
30
40
  },
31
41
  ) => {
32
- return defineComponent({
33
- name: 'FormComponent',
34
- props: {
35
- modelValue: {
36
- type: Object,
37
- default: () => ({}),
38
- },
39
- continuosValidation: {
40
- type: Boolean,
41
- default: false,
42
- },
43
- },
44
- emits: ['invalid', 'valid', 'submit', 'update:modelValue'],
45
- expose: ['submit', 'errors', 'status'],
46
- setup(props, { emit }) {
47
- const localModelValue = ref(
48
- defaultObjectBySchema(schema, props.modelValue),
49
- )
50
-
51
- const keepValidation =
52
- options?.continuosValidation || props.continuosValidation
42
+ const errors = ref<ZodFormattedError<z.infer<Schema>>>()
43
+ const status = ref<FormStatus | undefined>()
44
+ const localModelValue = ref<Partial<z.infer<Schema> | undefined>>()
53
45
 
54
- watch(
55
- () => props.modelValue,
56
- (newValue) => {
57
- if (newValue) {
58
- const original = isProxy(newValue)
59
- ? toRaw(newValue)
60
- : newValue
61
- localModelValue.value =
62
- typeof original?.clone === 'function'
63
- ? original.clone()
64
- : JSON.parse(JSON.stringify(original))
65
- }
46
+ return {
47
+ errors,
48
+ status,
49
+ formData: localModelValue,
50
+ component: defineComponent({
51
+ name: 'FormComponent',
52
+ props: {
53
+ modelValue: {
54
+ type: Object,
55
+ default: () => ({}),
66
56
  },
67
- { deep: true },
68
- )
69
- // v-model
70
- watchThrottled(
71
- localModelValue,
72
- (newValue) => {
73
- if (errors.value || keepValidation) {
74
- parseModelValue()
75
- }
76
- if (
77
- !newValue ||
78
- !props.modelValue ||
79
- JSON.stringify(newValue) !==
80
- JSON.stringify(props.modelValue)
81
- ) {
82
- emit('update:modelValue', newValue)
83
- }
57
+ continuosValidation: {
58
+ type: Boolean,
59
+ default: false,
84
60
  },
85
- { deep: true, throttle: options?.updateThrottle ?? 500 },
86
- )
61
+ },
62
+ emits: ['invalid', 'valid', 'submit', 'update:modelValue'],
63
+ expose: ['submit', 'errors', 'status'],
64
+ setup(props, { emit }) {
65
+ localModelValue.value = defaultObjectBySchema(
66
+ schema,
67
+ props.modelValue,
68
+ )
87
69
 
88
- // validation
89
- const errors = ref()
90
- const status = ref()
91
- const parseModelValue = (value = localModelValue.value) => {
92
- const parseResult = schema.safeParse(value)
93
- if (!parseResult.success) {
94
- errors.value = parseResult.error.format()
95
- status.value = FormStatus.invalid
96
- emit('invalid', errors.value)
97
- return false
70
+ const keepValidation =
71
+ options?.continuosValidation || props.continuosValidation
72
+
73
+ watch(
74
+ () => props.modelValue,
75
+ (newValue) => {
76
+ if (newValue) {
77
+ const original = isProxy(newValue)
78
+ ? toRaw(newValue)
79
+ : newValue
80
+ localModelValue.value =
81
+ typeof original?.clone === 'function'
82
+ ? original.clone()
83
+ : JSON.parse(JSON.stringify(original))
84
+ }
85
+ },
86
+ { deep: true },
87
+ )
88
+ // v-model
89
+ watchThrottled(
90
+ localModelValue,
91
+ (newValue) => {
92
+ if (errors.value || keepValidation) {
93
+ parseModelValue()
94
+ }
95
+ if (
96
+ !newValue ||
97
+ !props.modelValue ||
98
+ JSON.stringify(newValue) !==
99
+ JSON.stringify(props.modelValue)
100
+ ) {
101
+ emit('update:modelValue', newValue)
102
+ }
103
+ },
104
+ { deep: true, throttle: options?.updateThrottle ?? 500 },
105
+ )
106
+
107
+ const parseModelValue = (value = localModelValue.value) => {
108
+ const parseResult = schema.safeParse(value)
109
+ if (!parseResult.success) {
110
+ errors.value =
111
+ parseResult.error.format() as ZodFormattedError<
112
+ z.infer<Schema>
113
+ >
114
+ status.value = FormStatus.invalid
115
+ emit('invalid', errors.value)
116
+ return false
117
+ }
118
+ errors.value = undefined
119
+ status.value = FormStatus.valid
120
+ localModelValue.value = parseResult.data
121
+ emit('update:modelValue', localModelValue.value)
122
+ emit('valid', parseResult.data)
123
+ return true
98
124
  }
99
- errors.value = undefined
100
- status.value = FormStatus.valid
101
- localModelValue.value = parseResult.data
102
- emit('valid', parseResult.data)
103
- return true
104
- }
105
125
 
106
- // submit
107
- const submit = () => {
108
- if (!parseModelValue()) {
109
- return false
126
+ // submit
127
+ const submit = () => {
128
+ if (!parseModelValue()) {
129
+ return false
130
+ }
131
+ emit('submit', localModelValue.value)
132
+ return true
110
133
  }
111
- emit('submit', localModelValue.value)
112
- return true
113
- }
114
134
 
115
- // provide
116
- provide(provideKey, {
117
- modelValue: localModelValue,
118
- submit,
119
- errors: readonly(errors),
120
- })
135
+ // provide
136
+ provide(provideKey, {
137
+ modelValue: localModelValue,
138
+ submit,
139
+ errors: readonly(errors),
140
+ })
121
141
 
122
- return { submit }
123
- },
124
- render() {
125
- return h(
126
- 'form',
127
- {
128
- onSubmit: withModifiers(this.submit, ['prevent']),
129
- },
130
- this.$slots,
131
- )
132
- },
133
- })
142
+ return { submit }
143
+ },
144
+ render() {
145
+ return h(
146
+ 'form',
147
+ {
148
+ onSubmit: withModifiers(this.submit, ['prevent']),
149
+ },
150
+ this.$slots,
151
+ )
152
+ },
153
+ }),
154
+ }
134
155
  }
@@ -16,7 +16,9 @@ import {
16
16
  toRefs,
17
17
  watch,
18
18
  defineComponent,
19
+ onBeforeUnmount,
19
20
  } from 'vue'
21
+ import type { AnyZodObject, ZodEffects, z } from 'zod'
20
22
  import { FormFieldType } from './enums'
21
23
  import type {
22
24
  InjectedFormData,
@@ -25,12 +27,17 @@ import type {
25
27
  FormComposableOptions,
26
28
  } from './types'
27
29
 
28
- export const defineFormField = (
29
- formProvideKey: InjectionKey<InjectedFormData>,
30
- wrapperProvideKey: InjectionKey<InjectedFormWrapperData>,
31
- formFieldInjectionKey: InjectionKey<InjectedFormFieldData>,
30
+ export const defineFormField = <
31
+ Schema extends
32
+ | AnyZodObject
33
+ | ZodEffects<AnyZodObject>
34
+ | ZodEffects<ZodEffects<AnyZodObject>>,
35
+ >(
36
+ formProvideKey: InjectionKey<InjectedFormData<Schema>>,
37
+ wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>,
38
+ formFieldInjectionKey: InjectionKey<InjectedFormFieldData<Schema>>,
32
39
  options: FormComposableOptions = {},
33
- ): Component => {
40
+ ) => {
34
41
  // define component
35
42
  return defineComponent({
36
43
  name: 'FieldComponent',
@@ -52,10 +59,13 @@ export const defineFormField = (
52
59
  },
53
60
  props: {
54
61
  type: [Object, Function] as PropType<
55
- | Record<string, unknown>
56
- | ((
57
- formData?: Ref<ObjectConstructor>,
58
- ) => Record<string, unknown>)
62
+ Partial<
63
+ | z.infer<Schema>
64
+ | undefined
65
+ | ((
66
+ formData?: Ref<ObjectConstructor>,
67
+ ) => Partial<z.infer<Schema>> | undefined)
68
+ >
59
69
  >,
60
70
  default: () => ({}),
61
71
  },
@@ -102,6 +112,11 @@ export const defineFormField = (
102
112
  }
103
113
  })
104
114
 
115
+ onBeforeUnmount(() => {
116
+ unwatchInvalid()
117
+ unwatchFormProvided()
118
+ })
119
+
105
120
  // inject data from parent form wrapper
106
121
  const wrapperProvided = inject(wrapperProvideKey, undefined)
107
122
  if (wrapperProvided) {
@@ -124,7 +139,7 @@ export const defineFormField = (
124
139
  const invalid = computed(() => {
125
140
  return errors.value !== undefined
126
141
  })
127
- watch(invalid, () => {
142
+ const unwatchInvalid = watch(invalid, () => {
128
143
  if (invalid.value) {
129
144
  emit('invalid', invalidLabel.value)
130
145
  if (wrapperProvided) {
@@ -141,7 +156,7 @@ export const defineFormField = (
141
156
  }
142
157
  }
143
158
  })
144
- watch(
159
+ const unwatchFormProvided = watch(
145
160
  () => formProvided?.modelValue,
146
161
  () => {
147
162
  emit('update:formData', formProvided?.modelValue)
@@ -11,11 +11,17 @@ import {
11
11
  watch,
12
12
  h,
13
13
  } from 'vue'
14
+ import type { AnyZodObject, ZodEffects } from 'zod'
14
15
  import type { InjectedFormData, InjectedFormWrapperData } from './types'
15
16
 
16
- export const defineFormWrapper = (
17
- formProvideKey: InjectionKey<InjectedFormData>,
18
- wrapperProvideKey: InjectionKey<InjectedFormWrapperData>,
17
+ export const defineFormWrapper = <
18
+ Schema extends
19
+ | AnyZodObject
20
+ | ZodEffects<AnyZodObject>
21
+ | ZodEffects<ZodEffects<AnyZodObject>>,
22
+ >(
23
+ formProvideKey: InjectionKey<InjectedFormData<Schema>>,
24
+ wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>,
19
25
  ) => {
20
26
  return defineComponent({
21
27
  name: 'WrapperComponent',
@@ -103,8 +109,8 @@ export const defineFormWrapper = (
103
109
  null,
104
110
  this.$slots.default?.({
105
111
  invalid: this.invalid,
106
- formData: this.formProvided?.modelValue,
107
- errors: this.formProvided?.errors,
112
+ formData: this.formProvided?.modelValue.value,
113
+ errors: this.formProvided?.errors.value,
108
114
  fieldsErrors: this.errors,
109
115
  }) ?? this.$slots.defalut,
110
116
  )
@@ -112,8 +118,8 @@ export const defineFormWrapper = (
112
118
  return (
113
119
  this.$slots.default?.({
114
120
  invalid: this.invalid,
115
- formData: this.formProvided?.modelValue,
116
- errors: this.formProvided?.errors,
121
+ formData: this.formProvided?.modelValue.value,
122
+ errors: this.formProvided?.errors.value,
117
123
  fieldsErrors: this.errors,
118
124
  }) ?? this.$slots.defalut
119
125
  )
package/src/index.ts CHANGED
@@ -11,20 +11,32 @@ import type {
11
11
  FormPluginOptions,
12
12
  } from './types'
13
13
 
14
- export const formFactory = (
15
- schema: AnyZodObject | ZodEffects<AnyZodObject>,
14
+ export const formFactory = <
15
+ Schema extends
16
+ | AnyZodObject
17
+ | ZodEffects<AnyZodObject>
18
+ | ZodEffects<ZodEffects<AnyZodObject>>,
19
+ >(
20
+ schema: Schema,
16
21
  options: FormComposableOptions = {},
17
22
  ) => {
18
23
  // create injection keys form provide/inject
19
- const formInjectionKey = Symbol() as InjectionKey<InjectedFormData>
20
- const formWrapperInjectionKey =
21
- Symbol() as InjectionKey<InjectedFormWrapperData>
24
+ const formInjectionKey = Symbol() as InjectionKey<InjectedFormData<Schema>>
25
+ const formWrapperInjectionKey = Symbol() as InjectionKey<
26
+ InjectedFormWrapperData<Schema>
27
+ >
22
28
 
23
- const formFieldInjectionKey =
24
- Symbol() as InjectionKey<InjectedFormFieldData>
29
+ const formFieldInjectionKey = Symbol() as InjectionKey<
30
+ InjectedFormFieldData<Schema>
31
+ >
25
32
 
26
33
  // create components
27
- const VvForm = defineForm(schema, formInjectionKey, options)
34
+ const {
35
+ component: VvForm,
36
+ errors,
37
+ status,
38
+ formData,
39
+ } = defineForm(schema, formInjectionKey, options)
28
40
  const VvFormWrapper = defineFormWrapper(
29
41
  formInjectionKey,
30
42
  formWrapperInjectionKey,
@@ -43,6 +55,9 @@ export const formFactory = (
43
55
  formInjectionKey,
44
56
  formWrapperInjectionKey,
45
57
  formFieldInjectionKey,
58
+ errors,
59
+ status,
60
+ formData,
46
61
  }
47
62
  }
48
63
 
@@ -77,8 +92,13 @@ export const createForm = (
77
92
  }
78
93
  }
79
94
 
80
- export const useForm = (
81
- schema: AnyZodObject | ZodEffects<AnyZodObject>,
95
+ export const useForm = <
96
+ Schema extends
97
+ | AnyZodObject
98
+ | ZodEffects<AnyZodObject>
99
+ | ZodEffects<ZodEffects<AnyZodObject>>,
100
+ >(
101
+ schema: Schema,
82
102
  options: FormComposableOptions = {},
83
103
  ) => {
84
104
  const hasOptions = { ...inject(pluginInjectionKey, {}), ...options }
package/src/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Ref } from 'vue'
2
- import type { ZodFormattedError } from 'zod'
2
+ import type { z, AnyZodObject, ZodEffects } from 'zod'
3
3
  import type { FormFieldType } from './enums'
4
4
 
5
5
  export type FormComposableOptions = {
@@ -13,19 +13,34 @@ export type FormPluginOptions = {
13
13
  schema?: ZodSchema
14
14
  } & FormComposableOptions
15
15
 
16
- export type InjectedFormData<Type = Recrod<string | number, unknown>> = {
17
- modelValue: Ref<Type>
18
- errors: Ref<ZodFormattedError<Type>>
16
+ export type InjectedFormData<
17
+ Schema extends
18
+ | AnyZodObject
19
+ | ZodEffects<AnyZodObject>
20
+ | ZodEffects<ZodEffects<AnyZodObject>>,
21
+ > = {
22
+ modelValue: Ref<Partial<z.infer<Schema>> | undefined>
23
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
19
24
  submit: () => boolean
20
25
  }
21
26
 
22
- export type InjectedFormWrapperData = {
27
+ export type InjectedFormWrapperData<
28
+ Schema extends
29
+ | AnyZodObject
30
+ | ZodEffects<AnyZodObject>
31
+ | ZodEffects<ZodEffects<AnyZodObject>>,
32
+ > = {
23
33
  name: Ref<string>
24
34
  fields: Ref<Set<string>>
25
- errors: Ref<Map<string, Record<string, { _errors: string[] }>>>
35
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
26
36
  }
27
37
 
28
- export type InjectedFormFieldData = {
38
+ export type InjectedFormFieldData<
39
+ Schema extends
40
+ | AnyZodObject
41
+ | ZodEffects<AnyZodObject>
42
+ | ZodEffects<ZodEffects<AnyZodObject>>,
43
+ > = {
29
44
  name: Ref<string>
30
- errors: Ref<Map<string, Record<string, { _errors: string[] }>>>
45
+ errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
31
46
  }