@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/README.md +241 -169
- package/dist/VvForm.d.ts +167 -39
- package/dist/VvFormField.d.ts +92 -3
- package/dist/VvFormTemplate.d.ts +20 -0
- package/dist/VvFormWrapper.d.ts +121 -19
- package/dist/enums.d.ts +4 -0
- package/dist/index.d.ts +733 -63
- package/dist/index.es.js +431 -282
- package/dist/index.umd.js +1 -1
- package/dist/types.d.ts +110 -11
- package/dist/utils.d.ts +2 -5
- package/package.json +19 -17
- package/src/VvForm.ts +90 -32
- package/src/VvFormField.ts +59 -39
- package/src/VvFormTemplate.ts +179 -0
- package/src/VvFormWrapper.ts +66 -25
- package/src/enums.ts +5 -0
- package/src/index.ts +60 -17
- package/src/types.d.ts +110 -11
- package/src/utils.ts +49 -38
package/src/VvFormField.ts
CHANGED
|
@@ -16,21 +16,25 @@ import {
|
|
|
16
16
|
toRefs,
|
|
17
17
|
watch,
|
|
18
18
|
defineComponent,
|
|
19
|
+
onBeforeUnmount,
|
|
19
20
|
} from 'vue'
|
|
21
|
+
import type { z } from 'zod'
|
|
20
22
|
import { FormFieldType } from './enums'
|
|
21
23
|
import type {
|
|
22
24
|
InjectedFormData,
|
|
23
25
|
InjectedFormWrapperData,
|
|
24
26
|
InjectedFormFieldData,
|
|
25
|
-
|
|
27
|
+
FormFieldComponentOptions,
|
|
28
|
+
Path,
|
|
29
|
+
FormSchema,
|
|
26
30
|
} from './types'
|
|
27
31
|
|
|
28
|
-
export const defineFormField = (
|
|
29
|
-
formProvideKey: InjectionKey<InjectedFormData
|
|
30
|
-
wrapperProvideKey: InjectionKey<InjectedFormWrapperData
|
|
31
|
-
formFieldInjectionKey: InjectionKey<InjectedFormFieldData
|
|
32
|
-
options
|
|
33
|
-
)
|
|
32
|
+
export const defineFormField = <Schema extends FormSchema>(
|
|
33
|
+
formProvideKey: InjectionKey<InjectedFormData<Schema>>,
|
|
34
|
+
wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>,
|
|
35
|
+
formFieldInjectionKey: InjectionKey<InjectedFormFieldData<Schema>>,
|
|
36
|
+
options?: FormFieldComponentOptions,
|
|
37
|
+
) => {
|
|
34
38
|
// define component
|
|
35
39
|
return defineComponent({
|
|
36
40
|
name: 'FieldComponent',
|
|
@@ -47,15 +51,20 @@ export const defineFormField = (
|
|
|
47
51
|
default: undefined,
|
|
48
52
|
},
|
|
49
53
|
name: {
|
|
50
|
-
type: [String, Number, Boolean, Symbol]
|
|
54
|
+
type: [String, Number, Boolean, Symbol] as PropType<
|
|
55
|
+
Path<z.infer<Schema>>
|
|
56
|
+
>,
|
|
51
57
|
required: true,
|
|
52
58
|
},
|
|
53
59
|
props: {
|
|
54
60
|
type: [Object, Function] as PropType<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
Partial<
|
|
62
|
+
| z.infer<Schema>
|
|
63
|
+
| undefined
|
|
64
|
+
| ((
|
|
65
|
+
formData?: Ref<ObjectConstructor>,
|
|
66
|
+
) => Partial<z.infer<Schema>> | undefined)
|
|
67
|
+
>
|
|
59
68
|
>,
|
|
60
69
|
default: () => ({}),
|
|
61
70
|
},
|
|
@@ -67,6 +76,10 @@ export const defineFormField = (
|
|
|
67
76
|
type: [String, Number, Boolean, Array, Object],
|
|
68
77
|
default: undefined,
|
|
69
78
|
},
|
|
79
|
+
lazyLoad: {
|
|
80
|
+
type: Boolean,
|
|
81
|
+
default: false,
|
|
82
|
+
},
|
|
70
83
|
},
|
|
71
84
|
emits: ['invalid', 'valid', 'update:formData', 'update:modelValue'],
|
|
72
85
|
expose: ['invalid', 'invalidLabel', 'errors'],
|
|
@@ -74,22 +87,22 @@ export const defineFormField = (
|
|
|
74
87
|
// v-model
|
|
75
88
|
const modelValue = computed({
|
|
76
89
|
get() {
|
|
77
|
-
if (!
|
|
90
|
+
if (!injectedFormData?.formData) return
|
|
78
91
|
return get(
|
|
79
|
-
Object(
|
|
92
|
+
Object(injectedFormData.formData.value),
|
|
80
93
|
String(props.name),
|
|
81
94
|
)
|
|
82
95
|
},
|
|
83
96
|
set(value) {
|
|
84
|
-
if (!
|
|
97
|
+
if (!injectedFormData?.formData) return
|
|
85
98
|
set(
|
|
86
|
-
Object(
|
|
99
|
+
Object(injectedFormData.formData.value),
|
|
87
100
|
String(props.name),
|
|
88
101
|
value,
|
|
89
102
|
)
|
|
90
103
|
emit('update:modelValue', {
|
|
91
104
|
newValue: modelValue.value,
|
|
92
|
-
formData:
|
|
105
|
+
formData: injectedFormData?.formData,
|
|
93
106
|
})
|
|
94
107
|
},
|
|
95
108
|
})
|
|
@@ -102,21 +115,26 @@ export const defineFormField = (
|
|
|
102
115
|
}
|
|
103
116
|
})
|
|
104
117
|
|
|
118
|
+
onBeforeUnmount(() => {
|
|
119
|
+
unwatchInvalid()
|
|
120
|
+
unwatchInjectedFormData()
|
|
121
|
+
})
|
|
122
|
+
|
|
105
123
|
// inject data from parent form wrapper
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
108
|
-
|
|
124
|
+
const injectedWrapperData = inject(wrapperProvideKey, undefined)
|
|
125
|
+
if (injectedWrapperData) {
|
|
126
|
+
injectedWrapperData.fields.value.add(props.name as string)
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
// inject data from parent form
|
|
112
|
-
const
|
|
130
|
+
const injectedFormData = inject(formProvideKey)
|
|
113
131
|
const { props: fieldProps, name: fieldName } = toRefs(props)
|
|
114
132
|
|
|
115
133
|
const errors = computed(() => {
|
|
116
|
-
if (!
|
|
134
|
+
if (!injectedFormData?.errors.value) {
|
|
117
135
|
return undefined
|
|
118
136
|
}
|
|
119
|
-
return get(
|
|
137
|
+
return get(injectedFormData.errors.value, String(props.name))
|
|
120
138
|
})
|
|
121
139
|
const invalidLabel = computed(() => {
|
|
122
140
|
return errors.value?._errors
|
|
@@ -124,27 +142,30 @@ export const defineFormField = (
|
|
|
124
142
|
const invalid = computed(() => {
|
|
125
143
|
return errors.value !== undefined
|
|
126
144
|
})
|
|
127
|
-
watch(invalid, () => {
|
|
145
|
+
const unwatchInvalid = watch(invalid, () => {
|
|
128
146
|
if (invalid.value) {
|
|
129
147
|
emit('invalid', invalidLabel.value)
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
if (injectedWrapperData) {
|
|
149
|
+
injectedWrapperData.errors.value.set(
|
|
150
|
+
props.name as string,
|
|
151
|
+
{
|
|
152
|
+
_errors: invalidLabel.value,
|
|
153
|
+
},
|
|
154
|
+
)
|
|
134
155
|
}
|
|
135
156
|
} else {
|
|
136
157
|
emit('valid', modelValue.value)
|
|
137
|
-
if (
|
|
138
|
-
|
|
158
|
+
if (injectedWrapperData) {
|
|
159
|
+
injectedWrapperData.errors.value.delete(
|
|
139
160
|
props.name as string,
|
|
140
161
|
)
|
|
141
162
|
}
|
|
142
163
|
}
|
|
143
164
|
})
|
|
144
|
-
watch(
|
|
145
|
-
() =>
|
|
165
|
+
const unwatchInjectedFormData = watch(
|
|
166
|
+
() => injectedFormData?.formData,
|
|
146
167
|
() => {
|
|
147
|
-
emit('update:formData',
|
|
168
|
+
emit('update:formData', injectedFormData?.formData)
|
|
148
169
|
},
|
|
149
170
|
{ deep: true },
|
|
150
171
|
)
|
|
@@ -153,7 +174,7 @@ export const defineFormField = (
|
|
|
153
174
|
}
|
|
154
175
|
const hasFieldProps = computed(() => {
|
|
155
176
|
if (typeof fieldProps.value === 'function') {
|
|
156
|
-
return fieldProps.value(
|
|
177
|
+
return fieldProps.value(injectedFormData?.formData)
|
|
157
178
|
}
|
|
158
179
|
return fieldProps.value
|
|
159
180
|
})
|
|
@@ -188,7 +209,6 @@ export const defineFormField = (
|
|
|
188
209
|
})(props.type as FormFieldType),
|
|
189
210
|
invalidLabel: invalidLabel.value,
|
|
190
211
|
modelValue: modelValue.value,
|
|
191
|
-
errors: props.is ? errors.value : undefined,
|
|
192
212
|
'onUpdate:modelValue': onUpdate,
|
|
193
213
|
}))
|
|
194
214
|
|
|
@@ -207,15 +227,15 @@ export const defineFormField = (
|
|
|
207
227
|
onUpdate,
|
|
208
228
|
invalid: invalid.value,
|
|
209
229
|
invalidLabel: invalidLabel.value,
|
|
210
|
-
formData:
|
|
211
|
-
formErrors:
|
|
230
|
+
formData: injectedFormData?.formData.value,
|
|
231
|
+
formErrors: injectedFormData?.errors.value,
|
|
212
232
|
errors: errors.value,
|
|
213
233
|
}) ?? slots.defalut
|
|
214
234
|
)
|
|
215
235
|
},
|
|
216
236
|
}
|
|
217
237
|
}
|
|
218
|
-
if (!options.lazyLoad) {
|
|
238
|
+
if (!(options?.lazyLoad ?? props.lazyLoad)) {
|
|
219
239
|
let component: string | ConcreteComponent
|
|
220
240
|
switch (props.type) {
|
|
221
241
|
case FormFieldType.select:
|
|
@@ -251,7 +271,7 @@ export const defineFormField = (
|
|
|
251
271
|
}
|
|
252
272
|
}
|
|
253
273
|
return defineAsyncComponent(async () => {
|
|
254
|
-
if (options
|
|
274
|
+
if (options?.sideEffects) {
|
|
255
275
|
await Promise.resolve(options.sideEffects(props.type))
|
|
256
276
|
}
|
|
257
277
|
switch (props.type) {
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { get } from 'ts-dot-prop'
|
|
2
|
+
import {
|
|
3
|
+
type Component,
|
|
4
|
+
type PropType,
|
|
5
|
+
type InjectionKey,
|
|
6
|
+
type DeepReadonly,
|
|
7
|
+
type Ref,
|
|
8
|
+
type VNode,
|
|
9
|
+
defineComponent,
|
|
10
|
+
h,
|
|
11
|
+
inject,
|
|
12
|
+
unref,
|
|
13
|
+
} from 'vue'
|
|
14
|
+
import type { TypeOf, z } from 'zod'
|
|
15
|
+
import type { FormSchema, InjectedFormData, FormTemplateItem } from './types'
|
|
16
|
+
import type { FormStatus } from './enums'
|
|
17
|
+
|
|
18
|
+
export const defineFormTemplate = <Schema extends FormSchema>(
|
|
19
|
+
formProvideKey: InjectionKey<InjectedFormData<Schema>>,
|
|
20
|
+
VvFormField: Component,
|
|
21
|
+
) => {
|
|
22
|
+
const VvFormTemplate: Component = defineComponent({
|
|
23
|
+
props: {
|
|
24
|
+
schema: {
|
|
25
|
+
type: [Array, Function] as PropType<
|
|
26
|
+
| FormTemplateItem<Schema>[]
|
|
27
|
+
| ((
|
|
28
|
+
data?: InjectedFormData<Schema>,
|
|
29
|
+
) => FormTemplateItem<Schema>[])
|
|
30
|
+
>,
|
|
31
|
+
required: true,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
setup(templateProps, { slots: templateSlots }) {
|
|
35
|
+
const injectedFormData = inject(formProvideKey)
|
|
36
|
+
if (!injectedFormData?.formData) return
|
|
37
|
+
const normalizedSchema =
|
|
38
|
+
typeof templateProps.schema === 'function'
|
|
39
|
+
? templateProps.schema(injectedFormData)
|
|
40
|
+
: templateProps.schema
|
|
41
|
+
let lastIf: boolean | undefined = undefined
|
|
42
|
+
return () =>
|
|
43
|
+
normalizedSchema.reduce<(VNode | VNode[] | undefined)[]>(
|
|
44
|
+
(acc, field) => {
|
|
45
|
+
const normalizedField =
|
|
46
|
+
typeof field === 'function'
|
|
47
|
+
? field(injectedFormData)
|
|
48
|
+
: field
|
|
49
|
+
const {
|
|
50
|
+
vvIs,
|
|
51
|
+
vvName,
|
|
52
|
+
vvSlots,
|
|
53
|
+
vvChildren,
|
|
54
|
+
vvIf,
|
|
55
|
+
vvElseIf,
|
|
56
|
+
vvType,
|
|
57
|
+
vvDefaultValue,
|
|
58
|
+
vvShowValid,
|
|
59
|
+
...props
|
|
60
|
+
} = normalizedField
|
|
61
|
+
// conditions
|
|
62
|
+
if (vvIf !== undefined) {
|
|
63
|
+
if (typeof vvIf === 'string') {
|
|
64
|
+
lastIf = Boolean(
|
|
65
|
+
get(
|
|
66
|
+
Object(injectedFormData.formData.value),
|
|
67
|
+
vvIf,
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
} else if (typeof vvIf === 'function') {
|
|
71
|
+
lastIf = unref(vvIf(injectedFormData))
|
|
72
|
+
} else {
|
|
73
|
+
lastIf = unref(vvIf)
|
|
74
|
+
}
|
|
75
|
+
if (!lastIf) {
|
|
76
|
+
return acc
|
|
77
|
+
}
|
|
78
|
+
} else if (
|
|
79
|
+
vvElseIf !== undefined &&
|
|
80
|
+
lastIf !== undefined
|
|
81
|
+
) {
|
|
82
|
+
if (lastIf) {
|
|
83
|
+
return acc
|
|
84
|
+
}
|
|
85
|
+
if (typeof vvElseIf === 'string') {
|
|
86
|
+
lastIf = Boolean(
|
|
87
|
+
get(
|
|
88
|
+
Object(injectedFormData.formData.value),
|
|
89
|
+
vvElseIf,
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
} else if (typeof vvElseIf === 'function') {
|
|
93
|
+
lastIf = unref(vvElseIf(injectedFormData))
|
|
94
|
+
} else {
|
|
95
|
+
lastIf = unref(vvElseIf)
|
|
96
|
+
}
|
|
97
|
+
if (!lastIf) {
|
|
98
|
+
return acc
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
lastIf = undefined
|
|
102
|
+
}
|
|
103
|
+
// children
|
|
104
|
+
const hChildren = vvChildren
|
|
105
|
+
? h(VvFormTemplate, {
|
|
106
|
+
schema: vvChildren,
|
|
107
|
+
})
|
|
108
|
+
: undefined
|
|
109
|
+
// render
|
|
110
|
+
if (vvName) {
|
|
111
|
+
acc.push(
|
|
112
|
+
h(
|
|
113
|
+
VvFormField,
|
|
114
|
+
{
|
|
115
|
+
name: vvName,
|
|
116
|
+
is: vvIs,
|
|
117
|
+
type: vvType,
|
|
118
|
+
defaultValue: vvDefaultValue,
|
|
119
|
+
showValid: vvShowValid,
|
|
120
|
+
props,
|
|
121
|
+
},
|
|
122
|
+
vvSlots ?? hChildren,
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
return acc
|
|
126
|
+
}
|
|
127
|
+
if (vvIs) {
|
|
128
|
+
acc.push(
|
|
129
|
+
h(
|
|
130
|
+
vvIs as Component,
|
|
131
|
+
props,
|
|
132
|
+
vvSlots ?? hChildren,
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
return acc
|
|
136
|
+
}
|
|
137
|
+
if (vvChildren) {
|
|
138
|
+
acc.push(hChildren)
|
|
139
|
+
return acc
|
|
140
|
+
}
|
|
141
|
+
return acc
|
|
142
|
+
},
|
|
143
|
+
[
|
|
144
|
+
templateSlots?.default?.({
|
|
145
|
+
formData: injectedFormData?.formData.value,
|
|
146
|
+
submit: injectedFormData?.submit,
|
|
147
|
+
errors: injectedFormData?.errors.value,
|
|
148
|
+
status: injectedFormData?.status.value,
|
|
149
|
+
invalid: injectedFormData?.invalid.value,
|
|
150
|
+
}),
|
|
151
|
+
],
|
|
152
|
+
)
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* An hack to add types to the default slot
|
|
158
|
+
*/
|
|
159
|
+
return VvFormTemplate as typeof VvFormTemplate & {
|
|
160
|
+
new (): {
|
|
161
|
+
$slots: {
|
|
162
|
+
default: (_: {
|
|
163
|
+
formData: unknown extends
|
|
164
|
+
| Partial<TypeOf<Schema>>
|
|
165
|
+
| undefined
|
|
166
|
+
? undefined
|
|
167
|
+
: Partial<TypeOf<Schema>> | undefined
|
|
168
|
+
submit: () => boolean
|
|
169
|
+
errors: Readonly<
|
|
170
|
+
Ref<DeepReadonly<z.inferFormattedError<Schema>>>
|
|
171
|
+
>
|
|
172
|
+
status: Ref<DeepReadonly<`${FormStatus}` | undefined>>
|
|
173
|
+
invalid: Ref<DeepReadonly<boolean>>
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
|
+
}) => any
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
package/src/VvFormWrapper.ts
CHANGED
|
@@ -10,14 +10,20 @@ import {
|
|
|
10
10
|
toRefs,
|
|
11
11
|
watch,
|
|
12
12
|
h,
|
|
13
|
+
type DeepReadonly,
|
|
13
14
|
} from 'vue'
|
|
14
|
-
import type {
|
|
15
|
+
import type { TypeOf, z } from 'zod'
|
|
16
|
+
import type {
|
|
17
|
+
FormSchema,
|
|
18
|
+
InjectedFormData,
|
|
19
|
+
InjectedFormWrapperData,
|
|
20
|
+
} from './types'
|
|
15
21
|
|
|
16
|
-
export const defineFormWrapper = (
|
|
17
|
-
formProvideKey: InjectionKey<InjectedFormData
|
|
18
|
-
wrapperProvideKey: InjectionKey<InjectedFormWrapperData
|
|
22
|
+
export const defineFormWrapper = <Schema extends FormSchema>(
|
|
23
|
+
formProvideKey: InjectionKey<InjectedFormData<Schema>>,
|
|
24
|
+
wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>,
|
|
19
25
|
) => {
|
|
20
|
-
|
|
26
|
+
const VvFormWrapper = defineComponent({
|
|
21
27
|
name: 'WrapperComponent',
|
|
22
28
|
props: {
|
|
23
29
|
name: {
|
|
@@ -32,10 +38,10 @@ export const defineFormWrapper = (
|
|
|
32
38
|
emits: ['invalid', 'valid'],
|
|
33
39
|
expose: ['fields', 'invalid'],
|
|
34
40
|
setup(props, { emit }) {
|
|
35
|
-
const
|
|
41
|
+
const injectedFormData = inject(formProvideKey)
|
|
36
42
|
const wrapperProvided = inject(wrapperProvideKey, undefined)
|
|
37
43
|
const fields = ref(new Set<string>())
|
|
38
|
-
const
|
|
44
|
+
const fieldsErrors: Ref<
|
|
39
45
|
Map<string, Record<string, { _errors: string[] }>>
|
|
40
46
|
> = ref(new Map())
|
|
41
47
|
const { name } = toRefs(props)
|
|
@@ -43,7 +49,7 @@ export const defineFormWrapper = (
|
|
|
43
49
|
// provide data to child fields
|
|
44
50
|
provide(wrapperProvideKey, {
|
|
45
51
|
name: readonly(name),
|
|
46
|
-
errors,
|
|
52
|
+
errors: fieldsErrors,
|
|
47
53
|
fields,
|
|
48
54
|
})
|
|
49
55
|
|
|
@@ -62,7 +68,7 @@ export const defineFormWrapper = (
|
|
|
62
68
|
|
|
63
69
|
// add fields to parent wrapper
|
|
64
70
|
watch(
|
|
65
|
-
() => new Map(
|
|
71
|
+
() => new Map(fieldsErrors.value),
|
|
66
72
|
(newValue, oldValue) => {
|
|
67
73
|
if (wrapperProvided?.errors) {
|
|
68
74
|
Array.from(oldValue.keys()).forEach((key) => {
|
|
@@ -80,10 +86,10 @@ export const defineFormWrapper = (
|
|
|
80
86
|
)
|
|
81
87
|
|
|
82
88
|
const invalid = computed(() => {
|
|
83
|
-
if (!
|
|
89
|
+
if (!injectedFormData?.invalid.value) {
|
|
84
90
|
return false
|
|
85
91
|
}
|
|
86
|
-
return
|
|
92
|
+
return fieldsErrors.value.size > 0
|
|
87
93
|
})
|
|
88
94
|
|
|
89
95
|
watch(invalid, () => {
|
|
@@ -94,29 +100,64 @@ export const defineFormWrapper = (
|
|
|
94
100
|
}
|
|
95
101
|
})
|
|
96
102
|
|
|
97
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
formData: injectedFormData?.formData,
|
|
105
|
+
errors: injectedFormData?.errors,
|
|
106
|
+
invalid,
|
|
107
|
+
fields,
|
|
108
|
+
fieldsErrors,
|
|
109
|
+
}
|
|
98
110
|
},
|
|
99
111
|
render() {
|
|
100
112
|
if (this.tag) {
|
|
101
|
-
return h(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
113
|
+
return h(this.tag, null, {
|
|
114
|
+
default: () =>
|
|
115
|
+
this.$slots.default?.({
|
|
116
|
+
invalid: this.invalid,
|
|
117
|
+
formData: this.formData,
|
|
118
|
+
errors: this.errors,
|
|
119
|
+
fieldsErrors: this.fieldsErrors,
|
|
120
|
+
}) ?? this.$slots.defalut,
|
|
121
|
+
})
|
|
111
122
|
}
|
|
112
123
|
return (
|
|
113
124
|
this.$slots.default?.({
|
|
114
125
|
invalid: this.invalid,
|
|
115
|
-
formData: this.
|
|
116
|
-
errors: this.
|
|
117
|
-
fieldsErrors: this.
|
|
126
|
+
formData: this.formData,
|
|
127
|
+
errors: this.errors,
|
|
128
|
+
fieldsErrors: this.fieldsErrors,
|
|
118
129
|
}) ?? this.$slots.defalut
|
|
119
130
|
)
|
|
120
131
|
},
|
|
121
132
|
})
|
|
133
|
+
/**
|
|
134
|
+
* An hack to add types to the default slot
|
|
135
|
+
*/
|
|
136
|
+
return VvFormWrapper as typeof VvFormWrapper & {
|
|
137
|
+
new (): {
|
|
138
|
+
$slots: {
|
|
139
|
+
default: (_: {
|
|
140
|
+
invalid: boolean
|
|
141
|
+
formData: unknown extends
|
|
142
|
+
| Partial<TypeOf<Schema>>
|
|
143
|
+
| undefined
|
|
144
|
+
? undefined
|
|
145
|
+
: Partial<TypeOf<Schema>> | undefined
|
|
146
|
+
errors: Readonly<
|
|
147
|
+
Ref<DeepReadonly<z.inferFormattedError<Schema>>>
|
|
148
|
+
>
|
|
149
|
+
fieldsErrors: Map<
|
|
150
|
+
string,
|
|
151
|
+
Record<
|
|
152
|
+
string,
|
|
153
|
+
{
|
|
154
|
+
_errors: string[]
|
|
155
|
+
}
|
|
156
|
+
>
|
|
157
|
+
>
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
159
|
+
}) => any
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
122
163
|
}
|
package/src/enums.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
getCurrentInstance,
|
|
3
|
+
type App,
|
|
4
|
+
inject,
|
|
5
|
+
type InjectionKey,
|
|
6
|
+
type Plugin,
|
|
7
|
+
} from 'vue'
|
|
3
8
|
import { defineFormField } from './VvFormField'
|
|
4
9
|
import { defineForm } from './VvForm'
|
|
5
10
|
import { defineFormWrapper } from './VvFormWrapper'
|
|
11
|
+
import { defineFormTemplate } from './VvFormTemplate'
|
|
6
12
|
import type {
|
|
7
13
|
InjectedFormData,
|
|
8
14
|
InjectedFormWrapperData,
|
|
9
15
|
InjectedFormFieldData,
|
|
10
16
|
FormComposableOptions,
|
|
11
17
|
FormPluginOptions,
|
|
18
|
+
FormTemplateItem,
|
|
19
|
+
Path,
|
|
20
|
+
PathValue,
|
|
21
|
+
FormSchema,
|
|
12
22
|
} from './types'
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
schema:
|
|
24
|
+
const _formFactory = <Schema extends FormSchema>(
|
|
25
|
+
schema: Schema,
|
|
16
26
|
options: FormComposableOptions = {},
|
|
17
27
|
) => {
|
|
18
|
-
// create injection keys
|
|
19
|
-
const formInjectionKey = Symbol() as InjectionKey<InjectedFormData
|
|
20
|
-
const formWrapperInjectionKey =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const formFieldInjectionKey =
|
|
24
|
-
|
|
28
|
+
// create injection keys
|
|
29
|
+
const formInjectionKey = Symbol() as InjectionKey<InjectedFormData<Schema>>
|
|
30
|
+
const formWrapperInjectionKey = Symbol() as InjectionKey<
|
|
31
|
+
InjectedFormWrapperData<Schema>
|
|
32
|
+
>
|
|
33
|
+
const formFieldInjectionKey = Symbol() as InjectionKey<
|
|
34
|
+
InjectedFormFieldData<Schema>
|
|
35
|
+
>
|
|
25
36
|
|
|
26
37
|
// create components
|
|
27
|
-
const VvForm = defineForm(
|
|
38
|
+
const { VvForm, errors, status, formData } = defineForm(
|
|
39
|
+
schema,
|
|
40
|
+
formInjectionKey,
|
|
41
|
+
options,
|
|
42
|
+
)
|
|
28
43
|
const VvFormWrapper = defineFormWrapper(
|
|
29
44
|
formInjectionKey,
|
|
30
45
|
formWrapperInjectionKey,
|
|
@@ -35,14 +50,19 @@ export const formFactory = (
|
|
|
35
50
|
formFieldInjectionKey,
|
|
36
51
|
options,
|
|
37
52
|
)
|
|
53
|
+
const VvFormTemplate = defineFormTemplate(formInjectionKey, VvFormField)
|
|
38
54
|
|
|
39
55
|
return {
|
|
40
56
|
VvForm,
|
|
41
57
|
VvFormWrapper,
|
|
42
58
|
VvFormField,
|
|
59
|
+
VvFormTemplate,
|
|
43
60
|
formInjectionKey,
|
|
44
61
|
formWrapperInjectionKey,
|
|
45
62
|
formFieldInjectionKey,
|
|
63
|
+
errors,
|
|
64
|
+
status,
|
|
65
|
+
formData,
|
|
46
66
|
}
|
|
47
67
|
}
|
|
48
68
|
|
|
@@ -53,7 +73,7 @@ export const createForm = (
|
|
|
53
73
|
): Plugin & Partial<ReturnType<typeof useForm>> => {
|
|
54
74
|
let toReturn: Partial<ReturnType<typeof useForm>> = {}
|
|
55
75
|
if (options.schema) {
|
|
56
|
-
toReturn =
|
|
76
|
+
toReturn = _formFactory(options.schema, options)
|
|
57
77
|
}
|
|
58
78
|
return {
|
|
59
79
|
...toReturn,
|
|
@@ -72,17 +92,25 @@ export const createForm = (
|
|
|
72
92
|
if (toReturn?.VvFormField) {
|
|
73
93
|
app.component('VvFormField', toReturn.VvFormField)
|
|
74
94
|
}
|
|
95
|
+
if (toReturn?.VvFormTemplate) {
|
|
96
|
+
app.component('VvFormTemplate', toReturn.VvFormTemplate)
|
|
97
|
+
}
|
|
75
98
|
}
|
|
76
99
|
},
|
|
77
100
|
}
|
|
78
101
|
}
|
|
79
102
|
|
|
80
|
-
export const useForm = (
|
|
81
|
-
schema:
|
|
103
|
+
export const useForm = <Schema extends FormSchema>(
|
|
104
|
+
schema: Schema,
|
|
82
105
|
options: FormComposableOptions = {},
|
|
83
106
|
) => {
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
if (!getCurrentInstance()) {
|
|
108
|
+
return _formFactory(schema, options)
|
|
109
|
+
}
|
|
110
|
+
return _formFactory(schema, {
|
|
111
|
+
...inject(pluginInjectionKey, {}),
|
|
112
|
+
...options,
|
|
113
|
+
})
|
|
86
114
|
}
|
|
87
115
|
|
|
88
116
|
export { FormFieldType } from './enums'
|
|
@@ -91,6 +119,7 @@ export { defaultObjectBySchema } from './utils'
|
|
|
91
119
|
type FormComponent = ReturnType<typeof defineForm>
|
|
92
120
|
type FormWrapperComponent = ReturnType<typeof defineFormWrapper>
|
|
93
121
|
type FormFieldComponent = ReturnType<typeof defineFormField>
|
|
122
|
+
type FormTemplateComponent = ReturnType<typeof defineFormTemplate>
|
|
94
123
|
|
|
95
124
|
export type {
|
|
96
125
|
InjectedFormData,
|
|
@@ -101,4 +130,18 @@ export type {
|
|
|
101
130
|
FormComponent,
|
|
102
131
|
FormWrapperComponent,
|
|
103
132
|
FormFieldComponent,
|
|
133
|
+
FormTemplateComponent,
|
|
134
|
+
FormTemplateItem,
|
|
135
|
+
Path,
|
|
136
|
+
PathValue,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @deprecated Use `useForm()` instead
|
|
141
|
+
*/
|
|
142
|
+
export const formFactory = <Schema extends FormSchema>(
|
|
143
|
+
schema: Schema,
|
|
144
|
+
options: FormComposableOptions = {},
|
|
145
|
+
) => {
|
|
146
|
+
return _formFactory(schema, options)
|
|
104
147
|
}
|