@volverjs/form-vue 1.0.0-beta.1 → 1.0.0-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/src/VvForm.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  type DeepReadonly,
5
5
  type Ref,
6
6
  type PropType,
7
+ type WatchStopHandle,
7
8
  withModifiers,
8
9
  defineComponent,
9
10
  ref,
@@ -15,8 +16,12 @@ import {
15
16
  isProxy,
16
17
  computed,
17
18
  } from 'vue'
18
- import { watchThrottled } from '@vueuse/core'
19
- import type { z, ZodFormattedError, TypeOf } from 'zod'
19
+ import {
20
+ watchIgnorable,
21
+ throttleFilter,
22
+ type IgnoredUpdater,
23
+ } from '@vueuse/core'
24
+ import { type z, type ZodFormattedError, type TypeOf } from 'zod'
20
25
  import type {
21
26
  FormComponentOptions,
22
27
  FormSchema,
@@ -34,21 +39,57 @@ export const defineForm = <Schema extends FormSchema>(
34
39
  ) => {
35
40
  const errors = ref<z.inferFormattedError<Schema> | undefined>()
36
41
  const status = ref<FormStatus | undefined>()
42
+ const invalid = computed(() => status.value === FormStatus.invalid)
37
43
  const formData = ref<Partial<z.infer<Schema> | undefined>>()
44
+
45
+ const validate = async (value = formData.value) => {
46
+ const parseResult = await schema.safeParseAsync(value)
47
+ if (!parseResult.success) {
48
+ errors.value = parseResult.error.format() as ZodFormattedError<
49
+ z.infer<Schema>
50
+ >
51
+ status.value = FormStatus.invalid
52
+ return false
53
+ }
54
+ errors.value = undefined
55
+ status.value = FormStatus.valid
56
+ formData.value = parseResult.data
57
+ return true
58
+ }
59
+
60
+ const submit = async () => {
61
+ if (!(await validate())) {
62
+ return false
63
+ }
64
+ status.value = FormStatus.submitting
65
+ return true
66
+ }
67
+
68
+ const { ignoreUpdates, stop: stopUpdatesWatch } = watchIgnorable(
69
+ formData,
70
+ () => {
71
+ status.value = FormStatus.updated
72
+ },
73
+ {
74
+ deep: true,
75
+ eventFilter: throttleFilter(options?.updateThrottle ?? 500),
76
+ },
77
+ )
78
+
38
79
  const component = defineComponent({
39
- name: 'FormComponent',
80
+ name: 'VvForm',
40
81
  props: {
82
+ continuosValidation: {
83
+ type: Boolean,
84
+ default: false,
85
+ },
41
86
  modelValue: {
42
87
  type: Object,
43
88
  default: () => ({}),
44
89
  },
45
- updateThrottle: {
46
- type: Number,
47
- default: 500,
48
- },
49
- continuosValidation: {
50
- type: Boolean,
51
- default: false,
90
+ tag: {
91
+ type: String,
92
+ default: 'form',
52
93
  },
53
94
  template: {
54
95
  type: [Array, Function] as PropType<FormTemplate<Schema>>,
@@ -63,7 +104,6 @@ export const defineForm = <Schema extends FormSchema>(
63
104
  toRaw(props.modelValue),
64
105
  )
65
106
 
66
- // clone modelValue and update formData
67
107
  watch(
68
108
  () => props.modelValue,
69
109
  (newValue) => {
@@ -80,72 +120,60 @@ export const defineForm = <Schema extends FormSchema>(
80
120
  { deep: true },
81
121
  )
82
122
 
83
- // emit update:modelValue on formData change
84
- watchThrottled(
85
- formData,
86
- async (newValue) => {
123
+ watch(status, async (newValue) => {
124
+ if (newValue === FormStatus.invalid) {
125
+ const toReturn = toRaw(
126
+ errors.value as ZodFormattedError<z.infer<Schema>>,
127
+ )
128
+ emit('invalid', toReturn)
129
+ options?.onInvalid?.(toReturn)
130
+ return
131
+ }
132
+ if (newValue === FormStatus.valid) {
133
+ const toReturn = toRaw(formData.value as z.infer<Schema>)
134
+ emit('valid', toReturn)
135
+ options?.onValid?.(toReturn)
136
+ emit('update:modelValue', toReturn)
137
+ options?.onUpdate?.(toReturn)
138
+ return
139
+ }
140
+ if (newValue === FormStatus.submitting) {
141
+ const toReturn = toRaw(formData.value as z.infer<Schema>)
142
+ emit('submit', toReturn)
143
+ options?.onSubmit?.(toReturn)
144
+ }
145
+ if (newValue === FormStatus.updated) {
87
146
  if (
88
- (errors.value || options?.continuosValidation) ??
147
+ errors.value ||
148
+ options?.continuosValidation ||
89
149
  props.continuosValidation
90
150
  ) {
91
151
  await validate()
92
152
  }
93
153
  if (
94
- !newValue ||
154
+ !formData.value ||
95
155
  !props.modelValue ||
96
- JSON.stringify(newValue) !==
156
+ JSON.stringify(formData.value) !==
97
157
  JSON.stringify(props.modelValue)
98
158
  ) {
99
- emit('update:modelValue', newValue)
100
- options?.onUpdate?.(toRaw(newValue))
159
+ const toReturn = toRaw(
160
+ formData.value as z.infer<Schema>,
161
+ )
162
+ emit('update:modelValue', toReturn)
163
+ options?.onUpdate?.(toReturn)
164
+ }
165
+ if (status.value === FormStatus.updated) {
166
+ status.value = FormStatus.unknown
101
167
  }
102
- },
103
- {
104
- deep: true,
105
- throttle: options?.updateThrottle ?? props.updateThrottle,
106
- },
107
- )
108
-
109
- // validate formData with safeParse
110
- const validate = async (value = formData.value) => {
111
- const parseResult = await schema.safeParseAsync(value)
112
- if (!parseResult.success) {
113
- errors.value =
114
- parseResult.error.format() as ZodFormattedError<
115
- z.infer<Schema>
116
- >
117
- status.value = FormStatus.invalid
118
- emit('invalid', errors.value)
119
- options?.onInvalid?.(toRaw(errors.value))
120
- return false
121
- }
122
- errors.value = undefined
123
- status.value = FormStatus.valid
124
- formData.value = parseResult.data
125
- emit('update:modelValue', formData.value)
126
- options?.onUpdate?.(toRaw(formData.value))
127
- emit('valid', parseResult.data)
128
- options?.onValid?.(toRaw(formData.value))
129
- return true
130
- }
131
-
132
- // emit submit event if form is valid
133
- const submit = async () => {
134
- if (!(await validate())) {
135
- return false
136
168
  }
137
- emit('submit', formData.value as z.infer<Schema>)
138
- options?.onSubmit?.(toRaw(formData.value) as z.infer<Schema>)
139
- return true
140
- }
141
-
142
- const invalid = computed(() => status.value === FormStatus.invalid)
169
+ })
143
170
 
144
- // provide data to children
145
171
  provide(provideKey, {
146
172
  formData,
147
173
  submit,
148
174
  validate,
175
+ ignoreUpdates,
176
+ stopUpdatesWatch,
149
177
  errors: readonly(errors),
150
178
  status: readonly(status),
151
179
  invalid,
@@ -155,6 +183,8 @@ export const defineForm = <Schema extends FormSchema>(
155
183
  formData,
156
184
  submit,
157
185
  validate,
186
+ ignoreUpdates,
187
+ stopUpdatesWatch,
158
188
  errors: readonly(errors),
159
189
  status: readonly(status),
160
190
  invalid,
@@ -166,12 +196,14 @@ export const defineForm = <Schema extends FormSchema>(
166
196
  formData: this.formData,
167
197
  submit: this.submit,
168
198
  validate: this.validate,
199
+ ignoreUpdates: this.ignoreUpdates,
200
+ stopUpdatesWatch: this.stopUpdatesWatch,
169
201
  errors: this.errors,
170
202
  status: this.status,
171
203
  invalid: this.invalid,
172
204
  }) ?? this.$slots.default
173
205
  return h(
174
- 'form',
206
+ this.tag,
175
207
  {
176
208
  onSubmit: withModifiers(this.submit, ['prevent']),
177
209
  },
@@ -196,7 +228,12 @@ export const defineForm = <Schema extends FormSchema>(
196
228
  return {
197
229
  errors,
198
230
  status,
231
+ invalid,
199
232
  formData,
233
+ validate,
234
+ submit,
235
+ ignoreUpdates,
236
+ stopUpdatesWatch,
200
237
  /**
201
238
  * An hack to add types to the default slot
202
239
  */
@@ -211,6 +248,8 @@ export const defineForm = <Schema extends FormSchema>(
211
248
  : Partial<TypeOf<Schema>> | undefined
212
249
  submit: () => Promise<boolean>
213
250
  validate: () => Promise<boolean>
251
+ ignoreUpdates: IgnoredUpdater
252
+ stopUpdatesWatch: WatchStopHandle
214
253
  errors: Readonly<
215
254
  Ref<DeepReadonly<z.inferFormattedError<Schema>>>
216
255
  >
@@ -38,7 +38,7 @@ export const defineFormField = <Schema extends FormSchema>(
38
38
  ) => {
39
39
  // define component
40
40
  return defineComponent({
41
- name: 'FieldComponent',
41
+ name: 'VvFormField',
42
42
  props: {
43
43
  type: {
44
44
  type: String as PropType<`${FormFieldType}`>,
@@ -20,6 +20,7 @@ export const defineFormTemplate = <Schema extends FormSchema>(
20
20
  VvFormField: Component,
21
21
  ) => {
22
22
  const VvFormTemplate = defineComponent({
23
+ name: 'VvFormTemplate',
23
24
  props: {
24
25
  schema: {
25
26
  type: [Array, Function] as PropType<FormTemplate<Schema>>,
@@ -24,7 +24,7 @@ export const defineFormWrapper = <Schema extends FormSchema>(
24
24
  wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>,
25
25
  ) => {
26
26
  const VvFormWrapper = defineComponent({
27
- name: 'WrapperComponent',
27
+ name: 'VvFormWrapper',
28
28
  props: {
29
29
  name: {
30
30
  type: String,
package/src/enums.ts CHANGED
@@ -25,4 +25,7 @@ export enum FormFieldType {
25
25
  export enum FormStatus {
26
26
  invalid = 'invalid',
27
27
  valid = 'valid',
28
+ submitting = 'submitting',
29
+ updated = 'updated',
30
+ unknown = 'unknown',
28
31
  }
package/src/index.ts CHANGED
@@ -48,12 +48,17 @@ const _formFactory = <Schema extends FormSchema>(
48
48
  options,
49
49
  )
50
50
  const VvFormTemplate = defineFormTemplate(formInjectionKey, VvFormField)
51
- const { VvForm, errors, status, formData } = defineForm(
52
- schema,
53
- formInjectionKey,
54
- options,
55
- VvFormTemplate,
56
- )
51
+ const {
52
+ VvForm,
53
+ errors,
54
+ status,
55
+ invalid,
56
+ formData,
57
+ validate,
58
+ submit,
59
+ ignoreUpdates,
60
+ stopUpdatesWatch,
61
+ } = defineForm(schema, formInjectionKey, options, VvFormTemplate)
57
62
 
58
63
  return {
59
64
  VvForm,
@@ -65,7 +70,12 @@ const _formFactory = <Schema extends FormSchema>(
65
70
  formFieldInjectionKey,
66
71
  errors,
67
72
  status,
73
+ invalid,
68
74
  formData,
75
+ validate,
76
+ submit,
77
+ ignoreUpdates,
78
+ stopUpdatesWatch,
69
79
  }
70
80
  }
71
81
 
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { type Component, type DeepReadonly, type Ref } from 'vue'
1
+ import type { Component, DeepReadonly, Ref, WatchStopHandle } from 'vue'
2
2
  import type { z, AnyZodObject, ZodEffects, inferFormattedError } from 'zod'
3
+ import type { IgnoredUpdater } from '@vueuse/core'
3
4
  import type { FormFieldType, FormStatus } from './enums'
4
5
 
5
6
  export type FormSchema =
@@ -47,6 +48,8 @@ export type InjectedFormData<Schema extends FormSchema> = {
47
48
  >
48
49
  submit: () => Promise<boolean>
49
50
  validate: () => Promise<boolean>
51
+ ignoreUpdates: IgnoredUpdater
52
+ stopUpdatesWatch: WatchStopHandle
50
53
  status: Readonly<Ref<FormStatus | undefined>>
51
54
  invalid: Readonly<Ref<boolean>>
52
55
  }