@volverjs/form-vue 1.0.0-beta.11 → 1.0.0-beta.13

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/src/VvForm.ts CHANGED
@@ -1,314 +1,310 @@
1
1
  import {
2
- type Component,
3
- type InjectionKey,
4
- type DeepReadonly,
5
- type Ref,
6
- type PropType,
7
- type WatchStopHandle,
8
- withModifiers,
9
- defineComponent,
10
- ref,
11
- provide,
12
- readonly as makeReadonly,
13
- watch,
14
- h,
15
- toRaw,
16
- isProxy,
17
- computed,
18
- onMounted,
2
+ type Component,
3
+ type InjectionKey,
4
+ type DeepReadonly,
5
+ type Ref,
6
+ type PropType,
7
+ type WatchStopHandle,
8
+ withModifiers,
9
+ defineComponent,
10
+ ref,
11
+ provide,
12
+ readonly as makeReadonly,
13
+ watch,
14
+ h,
15
+ toRaw,
16
+ isProxy,
17
+ computed,
18
+ onMounted,
19
19
  } from 'vue'
20
20
  import {
21
- watchIgnorable,
22
- throttleFilter,
23
- type IgnoredUpdater,
21
+ watchIgnorable,
22
+ throttleFilter,
23
+ type IgnoredUpdater,
24
24
  } from '@vueuse/core'
25
- import { type z } from 'zod'
25
+ import type { z } from 'zod'
26
26
  import type {
27
- FormComponentOptions,
28
- FormSchema,
29
- FormTemplate,
30
- InjectedFormData,
27
+ FormComponentOptions,
28
+ FormSchema,
29
+ FormTemplate,
30
+ InjectedFormData,
31
31
  } from './types'
32
32
  import { FormStatus } from './enums'
33
33
  import { defaultObjectBySchema } from './utils'
34
34
 
35
- export const defineForm = <Schema extends FormSchema>(
36
- schema: Schema,
37
- provideKey: InjectionKey<InjectedFormData<Schema>>,
38
- options?: FormComponentOptions<Schema>,
39
- VvFormTemplate?: Component,
40
- ) => {
41
- const errors = ref<z.inferFormattedError<Schema> | undefined>()
42
- const status = ref<FormStatus | undefined>()
43
- const invalid = computed(() => status.value === FormStatus.invalid)
44
- const formData = ref<Partial<z.infer<Schema> | undefined>>()
45
- const readonly = ref<boolean>(false)
35
+ export function defineForm<Schema extends FormSchema>(schema: Schema, provideKey: InjectionKey<InjectedFormData<Schema>>, options?: FormComponentOptions<Schema>, VvFormTemplate?: Component) {
36
+ const errors = ref<z.inferFormattedError<Schema> | undefined>()
37
+ const status = ref<FormStatus | undefined>()
38
+ const invalid = computed(() => status.value === FormStatus.invalid)
39
+ const formData = ref<Partial<z.infer<Schema> | undefined>>()
40
+ const readonly = ref<boolean>(false)
46
41
 
47
- const validate = async (value = formData.value) => {
48
- if (readonly.value) {
49
- return true
50
- }
51
- const parseResult = await schema.safeParseAsync(value)
52
- if (!parseResult.success) {
53
- errors.value =
54
- parseResult.error.format() as z.inferFormattedError<Schema>
55
- status.value = FormStatus.invalid
56
- return false
57
- }
58
- errors.value = undefined
59
- status.value = FormStatus.valid
60
- formData.value = parseResult.data
61
- return true
62
- }
42
+ const validate = async (value = formData.value) => {
43
+ if (readonly.value) {
44
+ return true
45
+ }
46
+ const parseResult = await schema.safeParseAsync(value)
47
+ if (!parseResult.success) {
48
+ errors.value
49
+ = parseResult.error.format() as z.inferFormattedError<Schema>
50
+ status.value = FormStatus.invalid
51
+ return false
52
+ }
53
+ errors.value = undefined
54
+ status.value = FormStatus.valid
55
+ formData.value = parseResult.data
56
+ return true
57
+ }
63
58
 
64
- const submit = async () => {
65
- if (readonly.value) {
66
- return false
67
- }
68
- if (!(await validate())) {
69
- return false
70
- }
71
- status.value = FormStatus.submitting
72
- return true
73
- }
59
+ const submit = async () => {
60
+ if (readonly.value) {
61
+ return false
62
+ }
63
+ if (!(await validate())) {
64
+ return false
65
+ }
66
+ status.value = FormStatus.submitting
67
+ return true
68
+ }
74
69
 
75
- const { ignoreUpdates, stop: stopUpdatesWatch } = watchIgnorable(
76
- formData,
77
- () => {
78
- status.value = FormStatus.updated
79
- },
80
- {
81
- deep: true,
82
- eventFilter: throttleFilter(options?.updateThrottle ?? 500),
83
- },
84
- )
70
+ const { ignoreUpdates, stop: stopUpdatesWatch } = watchIgnorable(
71
+ formData,
72
+ () => {
73
+ status.value = FormStatus.updated
74
+ },
75
+ {
76
+ deep: true,
77
+ eventFilter: throttleFilter(options?.updateThrottle ?? 500),
78
+ },
79
+ )
85
80
 
86
- const component = defineComponent({
87
- name: 'VvForm',
88
- props: {
89
- continuousValidation: {
90
- type: Boolean,
91
- default: false,
92
- },
93
- modelValue: {
94
- type: Object,
95
- default: () => ({}),
96
- },
97
- readonly: {
98
- type: Boolean,
99
- default: options?.readonly ?? false,
100
- },
101
- tag: {
102
- type: String,
103
- default: 'form',
104
- },
105
- template: {
106
- type: [Array, Function] as PropType<FormTemplate<Schema>>,
107
- default: undefined,
108
- },
109
- },
110
- emits: [
111
- 'invalid',
112
- 'valid',
113
- 'submit',
114
- 'update:modelValue',
115
- 'update:readonly',
116
- ],
117
- expose: [
118
- 'submit',
119
- 'validate',
120
- 'errors',
121
- 'status',
122
- 'valid',
123
- 'invalid',
124
- 'readonly',
125
- ],
126
- setup(props, { emit }) {
127
- formData.value = defaultObjectBySchema(
128
- schema,
129
- toRaw(props.modelValue),
130
- )
81
+ const component = defineComponent({
82
+ name: 'VvForm',
83
+ props: {
84
+ continuousValidation: {
85
+ type: Boolean,
86
+ default: false,
87
+ },
88
+ modelValue: {
89
+ type: Object,
90
+ default: () => ({}),
91
+ },
92
+ readonly: {
93
+ type: Boolean,
94
+ default: options?.readonly ?? false,
95
+ },
96
+ tag: {
97
+ type: String,
98
+ default: 'form',
99
+ },
100
+ template: {
101
+ type: [Array, Function] as PropType<FormTemplate<Schema>>,
102
+ default: undefined,
103
+ },
104
+ },
105
+ emits: [
106
+ 'invalid',
107
+ 'valid',
108
+ 'submit',
109
+ 'update:modelValue',
110
+ 'update:readonly',
111
+ ],
112
+ expose: [
113
+ 'submit',
114
+ 'validate',
115
+ 'errors',
116
+ 'status',
117
+ 'valid',
118
+ 'invalid',
119
+ 'readonly',
120
+ ],
121
+ setup(props, { emit }) {
122
+ formData.value = defaultObjectBySchema(
123
+ schema,
124
+ toRaw(props.modelValue),
125
+ )
131
126
 
132
- watch(
133
- () => props.modelValue,
134
- (newValue) => {
135
- if (newValue) {
136
- const original = isProxy(newValue)
137
- ? toRaw(newValue)
138
- : newValue
127
+ watch(
128
+ () => props.modelValue,
129
+ (newValue) => {
130
+ if (newValue) {
131
+ const original = isProxy(newValue)
132
+ ? toRaw(newValue)
133
+ : newValue
139
134
 
140
- if (
141
- JSON.stringify(original) ===
142
- JSON.stringify(toRaw(formData.value))
143
- ) {
144
- return
145
- }
135
+ if (
136
+ JSON.stringify(original)
137
+ === JSON.stringify(toRaw(formData.value))
138
+ ) {
139
+ return
140
+ }
146
141
 
147
- formData.value =
148
- typeof original?.clone === 'function'
149
- ? original.clone()
150
- : JSON.parse(JSON.stringify(original))
151
- }
152
- },
153
- { deep: true },
154
- )
142
+ formData.value
143
+ = typeof original?.clone === 'function'
144
+ ? original.clone()
145
+ : JSON.parse(JSON.stringify(original))
146
+ }
147
+ },
148
+ { deep: true },
149
+ )
155
150
 
156
- watch(status, async (newValue) => {
157
- if (newValue === FormStatus.invalid) {
158
- const toReturn = toRaw(errors.value)
159
- emit('invalid', toReturn)
160
- options?.onInvalid?.(toReturn)
161
- return
162
- }
163
- if (newValue === FormStatus.valid) {
164
- const toReturn = toRaw(formData.value)
165
- emit('valid', toReturn)
166
- options?.onValid?.(toReturn)
167
- emit('update:modelValue', toReturn)
168
- options?.onUpdate?.(toReturn)
169
- return
170
- }
171
- if (newValue === FormStatus.submitting) {
172
- const toReturn = toRaw(formData.value)
173
- emit('submit', toReturn)
174
- options?.onSubmit?.(toReturn)
175
- }
176
- if (newValue === FormStatus.updated) {
177
- if (
178
- errors.value ||
179
- options?.continuousValidation ||
180
- props.continuousValidation
181
- ) {
182
- await validate()
183
- }
184
- if (
185
- !formData.value ||
186
- !props.modelValue ||
187
- JSON.stringify(formData.value) !==
188
- JSON.stringify(props.modelValue)
189
- ) {
190
- const toReturn = toRaw(formData.value)
191
- emit('update:modelValue', toReturn)
192
- options?.onUpdate?.(toReturn)
193
- }
194
- if (status.value === FormStatus.updated) {
195
- status.value = FormStatus.unknown
196
- }
197
- }
198
- })
151
+ watch(status, async (newValue) => {
152
+ if (newValue === FormStatus.invalid) {
153
+ const toReturn = toRaw(errors.value)
154
+ emit('invalid', toReturn)
155
+ options?.onInvalid?.(
156
+ toReturn as z.inferFormattedError<Schema> | undefined,
157
+ )
158
+ return
159
+ }
160
+ if (newValue === FormStatus.valid) {
161
+ const toReturn = toRaw(formData.value)
162
+ emit('valid', toReturn)
163
+ options?.onValid?.(toReturn)
164
+ emit('update:modelValue', toReturn)
165
+ options?.onUpdate?.(toReturn)
166
+ return
167
+ }
168
+ if (newValue === FormStatus.submitting) {
169
+ const toReturn = toRaw(formData.value)
170
+ emit('submit', toReturn)
171
+ options?.onSubmit?.(toReturn)
172
+ }
173
+ if (newValue === FormStatus.updated) {
174
+ if (
175
+ errors.value
176
+ || options?.continuousValidation
177
+ || props.continuousValidation
178
+ ) {
179
+ await validate()
180
+ }
181
+ if (
182
+ !formData.value
183
+ || !props.modelValue
184
+ || JSON.stringify(formData.value)
185
+ !== JSON.stringify(props.modelValue)
186
+ ) {
187
+ const toReturn = toRaw(formData.value)
188
+ emit('update:modelValue', toReturn)
189
+ options?.onUpdate?.(toReturn)
190
+ }
191
+ if (status.value === FormStatus.updated) {
192
+ status.value = FormStatus.unknown
193
+ }
194
+ }
195
+ })
199
196
 
200
- // readonly
201
- onMounted(() => {
202
- readonly.value = props.readonly
203
- })
204
- watch(
205
- () => props.readonly,
206
- (newValue) => {
207
- readonly.value = newValue
208
- },
209
- )
210
- watch(readonly, (newValue) => {
211
- if (newValue !== props.readonly) {
212
- emit('update:readonly', readonly.value)
213
- }
214
- })
197
+ // readonly
198
+ onMounted(() => {
199
+ readonly.value = props.readonly
200
+ })
201
+ watch(
202
+ () => props.readonly,
203
+ (newValue) => {
204
+ readonly.value = newValue
205
+ },
206
+ )
207
+ watch(readonly, (newValue) => {
208
+ if (newValue !== props.readonly) {
209
+ emit('update:readonly', readonly.value)
210
+ }
211
+ })
215
212
 
216
- provide(provideKey, {
217
- formData,
218
- submit,
219
- validate,
220
- ignoreUpdates,
221
- stopUpdatesWatch,
222
- errors: makeReadonly(errors),
223
- status: makeReadonly(status),
224
- invalid,
225
- readonly,
226
- })
213
+ provide(provideKey, {
214
+ formData,
215
+ submit,
216
+ validate,
217
+ ignoreUpdates,
218
+ stopUpdatesWatch,
219
+ errors: makeReadonly(errors),
220
+ status: makeReadonly(status),
221
+ invalid,
222
+ readonly,
223
+ })
227
224
 
228
- return {
229
- formData,
230
- submit,
231
- validate,
232
- ignoreUpdates,
233
- stopUpdatesWatch,
234
- errors: makeReadonly(errors),
235
- status: makeReadonly(status),
236
- invalid,
237
- isReadonly: readonly,
238
- }
239
- },
240
- render() {
241
- const defaultSlot = () =>
242
- this.$slots?.default?.({
243
- formData: this.formData,
244
- submit: this.submit,
245
- validate: this.validate,
246
- ignoreUpdates: this.ignoreUpdates,
247
- stopUpdatesWatch: this.stopUpdatesWatch,
248
- errors: this.errors,
249
- status: this.status,
250
- invalid: this.invalid,
251
- readonly: this.isReadonly,
252
- }) ?? this.$slots.default
253
- return h(
254
- this.tag,
255
- {
256
- onSubmit: withModifiers(this.submit, ['prevent']),
257
- },
258
- (this.template ?? options?.template) && VvFormTemplate
259
- ? [
260
- h(
261
- VvFormTemplate,
262
- {
263
- schema: this.template ?? options?.template,
264
- },
265
- {
266
- default: defaultSlot,
267
- },
268
- ),
269
- ]
270
- : {
271
- default: defaultSlot,
272
- },
273
- )
274
- },
275
- })
276
- return {
277
- errors,
278
- status,
279
- invalid,
280
- readonly,
281
- formData,
282
- validate,
283
- submit,
284
- ignoreUpdates,
285
- stopUpdatesWatch,
286
- /**
287
- * An hack to add types to the default slot
288
- */
289
- VvForm: component as typeof component & {
290
- new (): {
291
- $slots: {
292
- default: (_: {
293
- formData: unknown extends
294
- | Partial<z.TypeOf<Schema>>
295
- | undefined
296
- ? undefined
297
- : Partial<z.TypeOf<Schema>> | undefined
298
- submit: () => Promise<boolean>
299
- validate: () => Promise<boolean>
300
- ignoreUpdates: IgnoredUpdater
301
- stopUpdatesWatch: WatchStopHandle
302
- errors: Readonly<
225
+ return {
226
+ formData,
227
+ submit,
228
+ validate,
229
+ ignoreUpdates,
230
+ stopUpdatesWatch,
231
+ errors: makeReadonly(errors),
232
+ status: makeReadonly(status),
233
+ invalid,
234
+ isReadonly: readonly,
235
+ }
236
+ },
237
+ render() {
238
+ const defaultSlot = () =>
239
+ this.$slots?.default?.({
240
+ formData: this.formData,
241
+ submit: this.submit,
242
+ validate: this.validate,
243
+ ignoreUpdates: this.ignoreUpdates,
244
+ stopUpdatesWatch: this.stopUpdatesWatch,
245
+ errors: this.errors,
246
+ status: this.status,
247
+ invalid: this.invalid,
248
+ readonly: this.isReadonly,
249
+ }) ?? this.$slots.default
250
+ return h(
251
+ this.tag,
252
+ {
253
+ onSubmit: withModifiers(this.submit, ['prevent']),
254
+ },
255
+ (this.template ?? options?.template) && VvFormTemplate
256
+ ? [
257
+ h(
258
+ VvFormTemplate,
259
+ {
260
+ schema: this.template ?? options?.template,
261
+ },
262
+ {
263
+ default: defaultSlot,
264
+ },
265
+ ),
266
+ ]
267
+ : {
268
+ default: defaultSlot,
269
+ },
270
+ )
271
+ },
272
+ })
273
+ return {
274
+ errors,
275
+ status,
276
+ invalid,
277
+ readonly,
278
+ formData,
279
+ validate,
280
+ submit,
281
+ ignoreUpdates,
282
+ stopUpdatesWatch,
283
+ /**
284
+ * An hack to add types to the default slot
285
+ */
286
+ VvForm: component as typeof component & {
287
+ new (): {
288
+ $slots: {
289
+ default: (_: {
290
+ formData: unknown extends
291
+ | Partial<z.TypeOf<Schema>>
292
+ | undefined
293
+ ? undefined
294
+ : Partial<z.TypeOf<Schema>> | undefined
295
+ submit: () => Promise<boolean>
296
+ validate: () => Promise<boolean>
297
+ ignoreUpdates: IgnoredUpdater
298
+ stopUpdatesWatch: WatchStopHandle
299
+ errors: Readonly<
303
300
  Ref<DeepReadonly<z.inferFormattedError<Schema>>>
304
301
  >
305
- status: Ref<DeepReadonly<`${FormStatus}` | undefined>>
306
- invalid: Ref<DeepReadonly<boolean>>
307
- readonly: Ref<boolean>
308
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
309
- }) => any
310
- }
311
- }
312
- },
313
- }
302
+ status: Ref<DeepReadonly<`${FormStatus}` | undefined>>
303
+ invalid: Ref<DeepReadonly<boolean>>
304
+ readonly: Ref<boolean>
305
+ }) => any
306
+ }
307
+ }
308
+ },
309
+ }
314
310
  }