@volverjs/form-vue 1.0.0-beta.1 → 1.0.0-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/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,
@@ -35,20 +40,55 @@ export const defineForm = <Schema extends FormSchema>(
35
40
  const errors = ref<z.inferFormattedError<Schema> | undefined>()
36
41
  const status = ref<FormStatus | undefined>()
37
42
  const formData = ref<Partial<z.infer<Schema> | undefined>>()
43
+
44
+ const validate = async (value = formData.value) => {
45
+ const parseResult = await schema.safeParseAsync(value)
46
+ if (!parseResult.success) {
47
+ errors.value = parseResult.error.format() as ZodFormattedError<
48
+ z.infer<Schema>
49
+ >
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
+ }
58
+
59
+ const submit = async () => {
60
+ if (!(await validate())) {
61
+ return false
62
+ }
63
+ status.value = FormStatus.submitting
64
+ return true
65
+ }
66
+
67
+ const { ignoreUpdates, stop: stopUpdatesWatch } = watchIgnorable(
68
+ formData,
69
+ () => {
70
+ status.value = FormStatus.updated
71
+ },
72
+ {
73
+ deep: true,
74
+ eventFilter: throttleFilter(options?.updateThrottle ?? 500),
75
+ },
76
+ )
77
+
38
78
  const component = defineComponent({
39
- name: 'FormComponent',
79
+ name: 'VvForm',
40
80
  props: {
81
+ continuosValidation: {
82
+ type: Boolean,
83
+ default: false,
84
+ },
41
85
  modelValue: {
42
86
  type: Object,
43
87
  default: () => ({}),
44
88
  },
45
- updateThrottle: {
46
- type: Number,
47
- default: 500,
48
- },
49
- continuosValidation: {
50
- type: Boolean,
51
- default: false,
89
+ tag: {
90
+ type: String,
91
+ default: 'form',
52
92
  },
53
93
  template: {
54
94
  type: [Array, Function] as PropType<FormTemplate<Schema>>,
@@ -63,7 +103,6 @@ export const defineForm = <Schema extends FormSchema>(
63
103
  toRaw(props.modelValue),
64
104
  )
65
105
 
66
- // clone modelValue and update formData
67
106
  watch(
68
107
  () => props.modelValue,
69
108
  (newValue) => {
@@ -80,72 +119,62 @@ export const defineForm = <Schema extends FormSchema>(
80
119
  { deep: true },
81
120
  )
82
121
 
83
- // emit update:modelValue on formData change
84
- watchThrottled(
85
- formData,
86
- async (newValue) => {
122
+ watch(status, async (newValue) => {
123
+ if (newValue === FormStatus.invalid) {
124
+ const toReturn = toRaw(
125
+ errors.value as ZodFormattedError<z.infer<Schema>>,
126
+ )
127
+ emit('invalid', toReturn)
128
+ options?.onInvalid?.(toReturn)
129
+ return
130
+ }
131
+ if (newValue === FormStatus.valid) {
132
+ const toReturn = toRaw(formData.value as z.infer<Schema>)
133
+ emit('valid', toReturn)
134
+ options?.onValid?.(toReturn)
135
+ emit('update:modelValue', toReturn)
136
+ options?.onUpdate?.(toReturn)
137
+ return
138
+ }
139
+ if (newValue === FormStatus.submitting) {
140
+ const toReturn = toRaw(formData.value as z.infer<Schema>)
141
+ emit('submit', toReturn)
142
+ options?.onSubmit?.(toReturn)
143
+ }
144
+ if (newValue === FormStatus.updated) {
87
145
  if (
88
- (errors.value || options?.continuosValidation) ??
146
+ errors.value ||
147
+ options?.continuosValidation ||
89
148
  props.continuosValidation
90
149
  ) {
91
150
  await validate()
92
151
  }
93
152
  if (
94
- !newValue ||
153
+ !formData.value ||
95
154
  !props.modelValue ||
96
- JSON.stringify(newValue) !==
155
+ JSON.stringify(formData.value) !==
97
156
  JSON.stringify(props.modelValue)
98
157
  ) {
99
- emit('update:modelValue', newValue)
100
- options?.onUpdate?.(toRaw(newValue))
158
+ const toReturn = toRaw(
159
+ formData.value as z.infer<Schema>,
160
+ )
161
+ emit('update:modelValue', toReturn)
162
+ options?.onUpdate?.(toReturn)
163
+ }
164
+ if (status.value === FormStatus.updated) {
165
+ status.value = FormStatus.unknown
101
166
  }
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
167
  }
137
- emit('submit', formData.value as z.infer<Schema>)
138
- options?.onSubmit?.(toRaw(formData.value) as z.infer<Schema>)
139
- return true
140
- }
168
+ })
141
169
 
142
170
  const invalid = computed(() => status.value === FormStatus.invalid)
143
171
 
144
- // provide data to children
145
172
  provide(provideKey, {
146
173
  formData,
147
174
  submit,
148
175
  validate,
176
+ ignoreUpdates,
177
+ stopUpdatesWatch,
149
178
  errors: readonly(errors),
150
179
  status: readonly(status),
151
180
  invalid,
@@ -155,6 +184,8 @@ export const defineForm = <Schema extends FormSchema>(
155
184
  formData,
156
185
  submit,
157
186
  validate,
187
+ ignoreUpdates,
188
+ stopUpdatesWatch,
158
189
  errors: readonly(errors),
159
190
  status: readonly(status),
160
191
  invalid,
@@ -166,12 +197,14 @@ export const defineForm = <Schema extends FormSchema>(
166
197
  formData: this.formData,
167
198
  submit: this.submit,
168
199
  validate: this.validate,
200
+ ignoreUpdates: this.ignoreUpdates,
201
+ stopUpdatesWatch: this.stopUpdatesWatch,
169
202
  errors: this.errors,
170
203
  status: this.status,
171
204
  invalid: this.invalid,
172
205
  }) ?? this.$slots.default
173
206
  return h(
174
- 'form',
207
+ this.tag,
175
208
  {
176
209
  onSubmit: withModifiers(this.submit, ['prevent']),
177
210
  },
@@ -197,6 +230,10 @@ export const defineForm = <Schema extends FormSchema>(
197
230
  errors,
198
231
  status,
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,16 @@ 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
+ formData,
56
+ validate,
57
+ submit,
58
+ ignoreUpdates,
59
+ stopUpdatesWatch,
60
+ } = defineForm(schema, formInjectionKey, options, VvFormTemplate)
57
61
 
58
62
  return {
59
63
  VvForm,
@@ -66,6 +70,10 @@ const _formFactory = <Schema extends FormSchema>(
66
70
  errors,
67
71
  status,
68
72
  formData,
73
+ validate,
74
+ submit,
75
+ ignoreUpdates,
76
+ stopUpdatesWatch,
69
77
  }
70
78
  }
71
79
 
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
  }