@volverjs/form-vue 0.0.10-beta.1 → 0.0.10-beta.2
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/VvForm.d.ts +48 -31
- package/dist/VvFormField.d.ts +94 -2
- package/dist/VvFormWrapper.d.ts +15 -2
- package/dist/index.d.ts +198 -12
- package/dist/index.es.js +259 -236
- package/dist/index.umd.js +1 -1
- package/dist/types.d.ts +23 -8
- package/dist/utils.d.ts +8 -0
- package/package.json +9 -9
- package/src/VvForm.ts +117 -97
- package/src/VvFormField.ts +26 -11
- package/src/VvFormWrapper.ts +13 -7
- package/src/index.ts +30 -10
- package/src/types.d.ts +23 -8
- package/src/utils.ts +26 -14
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
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(f=>g(f)||O(f)?f:x(f)?s(f):f[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 f of e)D(f,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(d){n!=null&&n.modelValue&&(D(Object(n.modelValue.value),String(a.name),d),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 f=t.inject(o,void 0);f&&f.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 d;return(d=m.value)==null?void 0:d._errors}),y=t.computed(()=>m.value!==void 0),q=t.watch(y,()=>{y.value?(s("invalid",V.value),f&&f.errors.value.set(a.name,{_errors:V.value})):(s("valid",u.value),f&&f.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=d=>{u.value=d},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:(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: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 d;return((d=r.default)==null?void 0:d.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 d;switch(a.type){case c.select:d=t.resolveComponent("VvSelect");break;case c.checkbox:d=t.resolveComponent("VvCheckbox");break;case c.radio:d=t.resolveComponent("VvRadio");break;case c.textarea:d=t.resolveComponent("VvTextarea");break;case c.radioGroup:d=t.resolveComponent("VvRadioGroup");break;case c.checkboxGroup:d=t.resolveComponent("VvCheckboxGroup");break;case c.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(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],f=l(s);let n;if(f instanceof b.ZodDefault&&(n=f._def.defaultValue()),u===null&&f instanceof b.ZodNullable)return[r,u];if(f instanceof b.ZodSchema){const h=f.safeParse(o[r]);if(h.success)return[r,h.data??n]}return f instanceof b.ZodObject?[r,S(f,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 f=(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||f)&&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("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:f}=t.toRefs(l);t.provide(o,{name:t.readonly(f),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,f,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=(f=this.$slots).default)==null?void 0:n.call(f,{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:f}=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:f}},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 {
|
|
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<
|
|
17
|
-
|
|
18
|
-
|
|
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<
|
|
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<
|
|
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.
|
|
22
|
+
"version": "0.0.10-beta.2",
|
|
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.
|
|
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.
|
|
45
|
+
"@playwright/experimental-ct-vue": "^1.32.3",
|
|
46
46
|
"@testing-library/vue": "^7.0.0",
|
|
47
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
48
|
-
"@typescript-eslint/parser": "^5.
|
|
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.
|
|
55
|
+
"eslint": "^8.38.0",
|
|
56
56
|
"eslint-config-prettier": "^8.8.0",
|
|
57
57
|
"eslint-plugin-prettier": "^4.2.1",
|
|
58
|
-
"happy-dom": "^
|
|
58
|
+
"happy-dom": "^9.1.9",
|
|
59
59
|
"prettier": "^2.8.7",
|
|
60
|
-
"typescript": "^5.0.
|
|
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.
|
|
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
|
|
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,129 @@ export enum FormStatus {
|
|
|
21
26
|
valid = 'valid',
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
export const defineForm =
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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('valid', parseResult.data)
|
|
122
|
+
return true
|
|
98
123
|
}
|
|
99
|
-
errors.value = undefined
|
|
100
|
-
status.value = FormStatus.valid
|
|
101
|
-
localModelValue.value = parseResult.data
|
|
102
|
-
emit('valid', parseResult.data)
|
|
103
|
-
return true
|
|
104
|
-
}
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
125
|
+
// submit
|
|
126
|
+
const submit = () => {
|
|
127
|
+
if (!parseModelValue()) {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
emit('submit', localModelValue.value)
|
|
131
|
+
return true
|
|
110
132
|
}
|
|
111
|
-
emit('submit', localModelValue.value)
|
|
112
|
-
return true
|
|
113
|
-
}
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
134
|
+
// provide
|
|
135
|
+
provide(provideKey, {
|
|
136
|
+
modelValue: localModelValue,
|
|
137
|
+
submit,
|
|
138
|
+
errors: readonly(errors),
|
|
139
|
+
})
|
|
121
140
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
return { submit }
|
|
142
|
+
},
|
|
143
|
+
render() {
|
|
144
|
+
return h(
|
|
145
|
+
'form',
|
|
146
|
+
{
|
|
147
|
+
onSubmit: withModifiers(this.submit, ['prevent']),
|
|
148
|
+
},
|
|
149
|
+
this.$slots,
|
|
150
|
+
)
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
}
|
|
134
154
|
}
|
package/src/VvFormField.ts
CHANGED
|
@@ -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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
)
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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)
|
package/src/VvFormWrapper.ts
CHANGED
|
@@ -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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
+
const formInjectionKey = Symbol() as InjectionKey<InjectedFormData<Schema>>
|
|
25
|
+
const formWrapperInjectionKey = Symbol() as InjectionKey<
|
|
26
|
+
InjectedFormWrapperData<Schema>
|
|
27
|
+
>
|
|
22
28
|
|
|
23
|
-
const formFieldInjectionKey =
|
|
24
|
-
|
|
29
|
+
const formFieldInjectionKey = Symbol() as InjectionKey<
|
|
30
|
+
InjectedFormFieldData<Schema>
|
|
31
|
+
>
|
|
25
32
|
|
|
26
33
|
// create components
|
|
27
|
-
const
|
|
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
|
-
|
|
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 {
|
|
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<
|
|
17
|
-
|
|
18
|
-
|
|
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<
|
|
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<
|
|
45
|
+
errors: Readonly<Ref<DeepReadonly<z.inferFormattedError<Schema>>>>
|
|
31
46
|
}
|