@volverjs/form-vue 1.0.0-beta.21 → 1.0.0-beta.23

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 CHANGED
@@ -181,14 +181,15 @@ import { useForm } from '@volverjs/form-vue'
181
181
  import { z } from 'zod'
182
182
 
183
183
  const schema = z.object({
184
- name: z.string(),
185
- surname: z.string()
184
+ firstName: z.string(),
185
+ lastName: z.string()
186
186
  })
187
187
 
188
188
  const {
189
189
  VvForm,
190
190
  VvFormWrapper,
191
191
  VvFormField,
192
+ VvFormFieldsGroup,
192
193
  VvFormTemplate,
193
194
  formData,
194
195
  status,
@@ -201,6 +202,7 @@ export default {
201
202
  VvForm,
202
203
  VvFormWrapper,
203
204
  VvFormField,
205
+ VvFormFieldsGroup,
204
206
  VvFormTemplate,
205
207
  formData,
206
208
  status,
@@ -277,7 +279,7 @@ It automatically bind the form data through the `name` attribute. For nested obj
277
279
  @input="onUpdate"
278
280
  >
279
281
  <small v-if="invalid" id="last-name-alert" role="alert">
280
- {{ invalidLabel }}
282
+ {{ invalidLabel.join(', ') }}
281
283
  </small>
282
284
  </VvFormField>
283
285
  </VvForm>
@@ -298,6 +300,141 @@ By default UI components must be installed globally, they can be lazy-loaded wit
298
300
 
299
301
  Check the [`VvFormField` documentation](./docs/VvFormField.md) to learn more about form fields.
300
302
 
303
+ ### VvFormFieldsGroup
304
+
305
+ `VvFormFieldsGroup` allow you to render a group of form fields inside a form.
306
+
307
+ It automatically bind the form data through the `names` attribute. For nested objects, use the `names` attribute with **dot notation**.
308
+
309
+ ```vue
310
+ <template>
311
+ <VvForm>
312
+ <VvFormFieldsGroup
313
+ v-slot="{ modelValue, invalids, invalidLabels, onUpdateField }"
314
+ :name="['firstName', 'lastName']"
315
+ >
316
+ <fieldset>
317
+ <p>
318
+ <label for="firstName">First Name</label>
319
+ <input
320
+ id="firstName"
321
+ type="text"
322
+ name="firstName"
323
+ :value="modelValue.firstName"
324
+ :aria-invalid="invalids.firstName"
325
+ :aria-errormessage="invalids.firstName ? 'first-name-alert' : undefined"
326
+ @input="onUpdateField('firstName', $event)"
327
+ >
328
+ <small v-if="invalids.firstName" id="first-name-alert" role="alert">
329
+ {{ invalidLabels?.firstName.join(', ') }}
330
+ </small>
331
+ </p>
332
+ <p>
333
+ <label for="lastName">Last Name</label>
334
+ <input
335
+ id="lastName"
336
+ type="text"
337
+ name="lastName"
338
+ :value="modelValue.lastName"
339
+ :aria-invalid="invalids.lastName"
340
+ :aria-errormessage="invalids.lastName ? 'last-name-alert' : undefined"
341
+ @input="onUpdateField('lastName', $event)"
342
+ >
343
+ <small v-if="invalids.lastName" id="last-name-alert" role="alert">
344
+ {{ invalidLabels?.lastName.join(', ') }}
345
+ </small>
346
+ </p>
347
+ </fieldset>
348
+ </VvFormFieldsGroup>
349
+ </VvForm>
350
+ </template>
351
+ ```
352
+
353
+ Alternatively, you can create a custom component to render the group of form fields.
354
+
355
+ ```vue
356
+ // MyFieldsGroup.vue
357
+ <script lang="ts" setup>
358
+ defineProps<{
359
+ invalids: Record<string, boolean>
360
+ invalidLabels?: Record<string, string[]>
361
+ }>()
362
+ // v-model:first-name
363
+ const firstName = defineModel<string>('firstName', { default: '' })
364
+ // v-model:last-name
365
+ const lastName = defineModel<string>('lastName', { default: '' })
366
+ </script>
367
+
368
+ <template>
369
+ <fieldset>
370
+ <p>
371
+ <label for="firstName">First Name</label>
372
+ <input
373
+ id="firstName"
374
+ v-model="firstName"
375
+ type="text"
376
+ name="firstName"
377
+ :aria-invalid="invalids.firstName"
378
+ :aria-errormessage="invalids.firstName ? 'first-name-alert' : undefined"
379
+ >
380
+ <small v-if="invalids.firstName" id="first-name-alert" role="alert">
381
+ {{ invalidLabels?.firstName }}
382
+ </small>
383
+ </p>
384
+ <p>
385
+ <label for="lastName">Last Name</label>
386
+ <input
387
+ id="lastName"
388
+ v-model="lastName"
389
+ type="text"
390
+ name="lastName"
391
+ :aria-invalid="invalids.lastName"
392
+ :aria-errormessage="invalids.lastName ? 'last-name-alert' : undefined"
393
+ >
394
+ <small v-if="invalids.lastName" id="last-name-alert" role="alert">
395
+ {{ invalidLabels?.lastName }}
396
+ </small>
397
+ </p>
398
+ </fieldset>
399
+ </template>
400
+ ```
401
+
402
+ An than use it inside the `VvFormFieldsGroup` with the `:is` attribute.
403
+
404
+ ```vue
405
+ <script>
406
+ import MyFieldsGroup from './MyFieldsGroup.vue'
407
+ </script>
408
+
409
+ <template>
410
+ <VvForm>
411
+ <VvFormFieldsGroup
412
+ :is="MyFieldsGroup"
413
+ :names="['firstName', 'lastName']"
414
+ />
415
+ </VvForm>
416
+ </template>
417
+ ```
418
+
419
+ You can also map the form fields to the components v-models. The `:names` attribute can be an object with the component v-models as keys and the form fields names as values.
420
+
421
+ ```vue
422
+ <script>
423
+ import MyCustomComponent from './MyCustomComponent.vue'
424
+ </script>
425
+
426
+ <template>
427
+ <VvForm>
428
+ <VvFormFieldsGroup
429
+ :is="MyCustomComponent"
430
+ :names="{
431
+ myCustomComponentVModel: 'path.to.form.field',
432
+ }"
433
+ />
434
+ </VvForm>
435
+ </template>
436
+ ```
437
+
301
438
  ## VvFormTemplate
302
439
 
303
440
  Forms can also be created using a template. A template is an **array of objects** that describes the form fields. All properties that are **not listed** below are passed to the component **as props**.
@@ -503,15 +640,15 @@ import { z } from 'zod'
503
640
  import { defaultObjectBySchema } from '@volverjs/form-vue'
504
641
 
505
642
  const schema = z.object({
506
- name: z.string().default('John'),
507
- surname: z.string().default('Doe')
643
+ firstName: z.string().default('John'),
644
+ lastName: z.string().default('Doe')
508
645
  })
509
646
 
510
647
  const defaultObject = defaultObjectBySchema(schema)
511
- // defaultObject = { name: 'John', surname: 'Doe' }
648
+ // defaultObject = { firstName: 'John', lastName: 'Doe' }
512
649
 
513
650
  const defaultObject = defaultObjectBySchema(schema, { name: 'Jane' })
514
- // defaultObject = { name: 'Jane', surname: 'Doe' }
651
+ // defaultObject = { firstName: 'Jane', lastName: 'Doe' }
515
652
  ```
516
653
 
517
654
  `defaultObjectBySchema` can be used with nested objects.
@@ -521,8 +658,8 @@ import { z } from 'zod'
521
658
  import { defaultObjectBySchema } from '@volverjs/form-vue'
522
659
 
523
660
  const schema = z.object({
524
- name: z.string().default('John'),
525
- surname: z.string().default('Doe'),
661
+ firstName: z.string().default('John'),
662
+ lastName: z.string().default('Doe'),
526
663
  address: z.object({
527
664
  street: z.string().default('Main Street'),
528
665
  number: z.number().default(1)
@@ -530,7 +667,7 @@ const schema = z.object({
530
667
  })
531
668
 
532
669
  const defaultObject = defaultObjectBySchema(schema)
533
- // defaultObject = { name: 'John', surname: 'Doe', address: { street: 'Main Street', number: 1 } }
670
+ // defaultObject = { firstName: 'John', lastName: 'Doe', address: { street: 'Main Street', number: 1 } }
534
671
  ```
535
672
 
536
673
  Other Zod methods are also supported: [`z.nullable()`](https://github.com/colinhacks/zod#nullable), [`z.coerce`](https://github.com/colinhacks/zod#coercion-for-primitives) and [`z.passthrough()`](https://github.com/colinhacks/zod#passthrough).
@@ -541,8 +678,8 @@ import { defaultObjectBySchema } from '@volverjs/form-vue'
541
678
 
542
679
  const schema = z
543
680
  .object({
544
- name: z.string().default('John'),
545
- surname: z.string().default('Doe'),
681
+ firstName: z.string().default('John'),
682
+ lastName: z.string().default('Doe'),
546
683
  address: z.object({
547
684
  street: z.string().default('Main Street'),
548
685
  number: z.number().default(1)
@@ -557,7 +694,7 @@ const defaultObject = defaultObjectBySchema(schema, {
557
694
  height: '1.9',
558
695
  email: 'john.doe@test.com'
559
696
  })
560
- // defaultObject = { name: 'John', surname: 'Doe', address: { street: 'Main Street', number: 1 }, age: null, height: 1.9, weight: 80, email: 'john.doe@test.com' }
697
+ // defaultObject = { firstName: 'John', lastName: 'Doe', address: { street: 'Main Street', number: 1 }, age: null, height: 1.9, weight: 80, email: 'john.doe@test.com' }
561
698
  ```
562
699
 
563
700
  ## License
package/dist/VvForm.d.ts CHANGED
@@ -47,7 +47,7 @@ export declare function defineForm<Schema extends FormSchema>(schema: Schema, pr
47
47
  stopUpdatesWatch: import('vue').WatchStopHandle;
48
48
  submit: () => Promise<boolean>;
49
49
  validate: (value?: Partial<z.TypeOf<Schema> | undefined>, fields?: Set<string>) => Promise<boolean>;
50
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("invalid" | "valid" | "reset" | "update:modelValue" | "submit" | "update:readonly")[], "invalid" | "valid" | "reset" | "update:modelValue" | "submit" | "update:readonly", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
50
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("invalid" | "valid" | "reset" | "submit" | "update:modelValue" | "update:readonly")[], "invalid" | "valid" | "reset" | "submit" | "update:modelValue" | "update:readonly", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
51
51
  continuousValidation: {
52
52
  type: BooleanConstructor;
53
53
  default: boolean;
@@ -71,16 +71,16 @@ export declare function defineForm<Schema extends FormSchema>(schema: Schema, pr
71
71
  }>> & Readonly<{
72
72
  onInvalid?: ((...args: any[]) => any) | undefined;
73
73
  onValid?: ((...args: any[]) => any) | undefined;
74
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
75
74
  onReset?: ((...args: any[]) => any) | undefined;
76
75
  onSubmit?: ((...args: any[]) => any) | undefined;
76
+ "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
77
77
  "onUpdate:readonly"?: ((...args: any[]) => any) | undefined;
78
78
  }>, {
79
79
  readonly: boolean;
80
- template: FormTemplate<Schema>;
81
- modelValue: Record<string, any>;
82
80
  tag: string;
81
+ template: FormTemplate<Schema>;
83
82
  continuousValidation: boolean;
83
+ modelValue: Record<string, any>;
84
84
  }, SlotsType<{
85
85
  default: {
86
86
  errors: Readonly<import('vue').Ref<import('vue').DeepReadonly<z.inferFormattedError<Schema, string>> | undefined, import('vue').DeepReadonly<z.inferFormattedError<Schema, string>> | undefined>>;
@@ -95,5 +95,5 @@ export declare function defineForm<Schema extends FormSchema>(schema: Schema, pr
95
95
  clear: () => void;
96
96
  reset: () => void;
97
97
  };
98
- }>, {}, {}, "invalid" | "valid" | "reset" | "status" | "clear" | "errors" | "submit" | "validate" | "readonly" | "template" | "tag", import('vue').ComponentProvideOptions, true, {}, any>;
98
+ }>, {}, {}, "invalid" | "valid" | "reset" | "status" | "readonly" | "clear" | "submit" | "errors" | "tag" | "template" | "validate", import('vue').ComponentProvideOptions, true, {}, any>;
99
99
  };
@@ -25,7 +25,7 @@ export declare function defineFormField<Schema extends FormSchema>(formProvideKe
25
25
  default: boolean;
26
26
  };
27
27
  defaultValue: {
28
- type: (StringConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor)[];
28
+ type: (ObjectConstructor | ArrayConstructor | BooleanConstructor | StringConstructor | NumberConstructor)[];
29
29
  default: undefined;
30
30
  };
31
31
  lazyLoad: {
@@ -60,7 +60,7 @@ export declare function defineFormField<Schema extends FormSchema>(formProvideKe
60
60
  'onUpdate:modelValue': (value: unknown) => void;
61
61
  }>;
62
62
  invalid: import('vue').ComputedRef<boolean>;
63
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("invalid" | "valid" | "update:formData" | "update:modelValue")[], "invalid" | "valid" | "update:formData" | "update:modelValue", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
63
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("invalid" | "valid" | "update:modelValue" | "update:formData")[], "invalid" | "valid" | "update:modelValue" | "update:formData", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
64
64
  type: {
65
65
  type: PropType<`${FormFieldType}`>;
66
66
  validator: (value: FormFieldType) => boolean;
@@ -83,7 +83,7 @@ export declare function defineFormField<Schema extends FormSchema>(formProvideKe
83
83
  default: boolean;
84
84
  };
85
85
  defaultValue: {
86
- type: (StringConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor)[];
86
+ type: (ObjectConstructor | ArrayConstructor | BooleanConstructor | StringConstructor | NumberConstructor)[];
87
87
  default: undefined;
88
88
  };
89
89
  lazyLoad: {
@@ -97,10 +97,11 @@ export declare function defineFormField<Schema extends FormSchema>(formProvideKe
97
97
  }>> & Readonly<{
98
98
  onInvalid?: ((...args: any[]) => any) | undefined;
99
99
  onValid?: ((...args: any[]) => any) | undefined;
100
- "onUpdate:formData"?: ((...args: any[]) => any) | undefined;
101
100
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
101
+ "onUpdate:formData"?: ((...args: any[]) => any) | undefined;
102
102
  }>, {
103
103
  type: "number" | "text" | "email" | "password" | "tel" | "url" | "search" | "date" | "time" | "datetime-local" | "month" | "week" | "color" | "select" | "checkbox" | "radio" | "textarea" | "radioGroup" | "checkboxGroup" | "combobox" | "custom";
104
+ readonly: boolean;
104
105
  props: [{
105
106
  type: PropType<Partial<z.infer<Schema> | undefined | ((formData?: Ref<ObjectConstructor>) => Partial<z.infer<Schema>> | undefined)>>;
106
107
  default: () => {};
@@ -112,7 +113,6 @@ export declare function defineFormField<Schema extends FormSchema>(formProvideKe
112
113
  showValid: boolean;
113
114
  defaultValue: string | number | boolean | unknown[] | Record<string, any>;
114
115
  lazyLoad: boolean;
115
- readonly: boolean;
116
116
  }, SlotsType<{
117
117
  [key: string]: any;
118
118
  default: {
@@ -120,11 +120,11 @@ export declare function defineFormField<Schema extends FormSchema>(formProvideKe
120
120
  formData?: Partial<TypeOf<Schema>>;
121
121
  formErrors?: DeepReadonly<inferFormattedError<Schema, string>>;
122
122
  invalid: boolean;
123
- invalidLabel: string;
123
+ invalidLabel?: string[];
124
124
  modelValue: any;
125
125
  onUpdate: (value: unknown) => void;
126
126
  readonly: boolean;
127
127
  submit?: InjectedFormData<Schema>["submit"];
128
128
  validate?: InjectedFormData<Schema>["validate"];
129
129
  };
130
- }>, {}, {}, "invalid" | "type" | "component" | "errors" | "hasProps" | "invalidLabel" | "is", import('vue').ComponentProvideOptions, true, {}, any>;
130
+ }>, {}, {}, "invalid" | "type" | "errors" | "component" | "hasProps" | "invalidLabel" | "is", import('vue').ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,109 @@
1
+ import { Component, DeepReadonly, InjectionKey, PropType, Ref, SlotsType } from 'vue';
2
+ import { inferFormattedError, TypeOf, z } from 'zod';
3
+ import { FormSchema, InjectedFormData, InjectedFormFieldsGroupData, InjectedFormWrapperData, Path } from './types';
4
+ export declare function defineFormFieldsGroup<Schema extends FormSchema>(formProvideKey: InjectionKey<InjectedFormData<Schema>>, wrapperProvideKey: InjectionKey<InjectedFormWrapperData<Schema>>, formFieldsGroupInjectionKey: InjectionKey<InjectedFormFieldsGroupData<Schema>>): import('vue').DefineComponent<import('vue').ExtractPropTypes<{
5
+ is: {
6
+ type: PropType<Component | string>;
7
+ default: undefined;
8
+ };
9
+ names: {
10
+ type: PropType<Path<z.infer<Schema>>[] | Record<string, Path<z.infer<Schema>>>>;
11
+ required: true;
12
+ };
13
+ props: {
14
+ type: PropType<Partial<z.infer<Schema> | undefined | ((formData?: Ref<ObjectConstructor>) => Partial<z.infer<Schema>> | undefined)>>;
15
+ default: () => {};
16
+ };
17
+ showValid: {
18
+ type: BooleanConstructor;
19
+ default: boolean;
20
+ };
21
+ defaultValues: {
22
+ type: PropType<Record<Path<z.infer<Schema>>, any>>;
23
+ default: undefined;
24
+ };
25
+ readonly: {
26
+ type: BooleanConstructor;
27
+ default: undefined;
28
+ };
29
+ }>, {
30
+ component: import('vue').ComputedRef<{
31
+ render(): import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
32
+ [key: string]: any;
33
+ }>[];
34
+ }>;
35
+ hasProps: import('vue').ComputedRef<{
36
+ names: {};
37
+ invalid: boolean;
38
+ invalids: Record<string, boolean>;
39
+ valid: boolean | undefined;
40
+ invalidLabels: Record<string, string[]> | undefined;
41
+ modelValue: Record<string, any>;
42
+ readonly: boolean;
43
+ }>;
44
+ invalid: import('vue').ComputedRef<boolean>;
45
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("invalid" | "valid" | "update:modelValue" | "update:formData")[], "invalid" | "valid" | "update:modelValue" | "update:formData", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
46
+ is: {
47
+ type: PropType<Component | string>;
48
+ default: undefined;
49
+ };
50
+ names: {
51
+ type: PropType<Path<z.infer<Schema>>[] | Record<string, Path<z.infer<Schema>>>>;
52
+ required: true;
53
+ };
54
+ props: {
55
+ type: PropType<Partial<z.infer<Schema> | undefined | ((formData?: Ref<ObjectConstructor>) => Partial<z.infer<Schema>> | undefined)>>;
56
+ default: () => {};
57
+ };
58
+ showValid: {
59
+ type: BooleanConstructor;
60
+ default: boolean;
61
+ };
62
+ defaultValues: {
63
+ type: PropType<Record<Path<z.infer<Schema>>, any>>;
64
+ default: undefined;
65
+ };
66
+ readonly: {
67
+ type: BooleanConstructor;
68
+ default: undefined;
69
+ };
70
+ }>> & Readonly<{
71
+ onInvalid?: ((...args: any[]) => any) | undefined;
72
+ onValid?: ((...args: any[]) => any) | undefined;
73
+ "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
74
+ "onUpdate:formData"?: ((...args: any[]) => any) | undefined;
75
+ }>, {
76
+ readonly: boolean;
77
+ props: [{
78
+ type: PropType<Partial<z.infer<Schema> | undefined | ((formData?: Ref<ObjectConstructor>) => Partial<z.infer<Schema>> | undefined)>>;
79
+ default: () => {};
80
+ }] extends [import('vue').Prop<infer V, infer D>] ? unknown extends V ? keyof V extends never ? import('@vue/shared').IfAny<V, V, D> : V : V : {
81
+ type: PropType<Partial<z.infer<Schema> | undefined | ((formData?: Ref<ObjectConstructor>) => Partial<z.infer<Schema>> | undefined)>>;
82
+ default: () => {};
83
+ };
84
+ is: string | Component;
85
+ showValid: boolean;
86
+ defaultValues: [{
87
+ type: PropType<Record<Path<z.infer<Schema>>, any>>;
88
+ default: undefined;
89
+ }] extends [import('vue').Prop<infer V, infer D>] ? unknown extends V ? keyof V extends never ? import('@vue/shared').IfAny<V, V, D> : V : V : {
90
+ type: PropType<Record<Path<z.infer<Schema>>, any>>;
91
+ default: undefined;
92
+ };
93
+ }, SlotsType<{
94
+ [key: string]: any;
95
+ default: {
96
+ errors?: Record<Path<z.infer<Schema>>, z.inferFormattedError<Schema>>;
97
+ formData?: Partial<TypeOf<Schema>>;
98
+ formErrors?: DeepReadonly<inferFormattedError<Schema, string>>;
99
+ invalid: boolean;
100
+ invalids: Record<string, boolean>;
101
+ invalidLabels?: Record<string, string[]>;
102
+ modelValue: Record<string, any>;
103
+ onUpdate: (value: Record<string, any>) => void;
104
+ onUpdateField: (name: string, value: any) => void;
105
+ readonly: boolean;
106
+ submit?: InjectedFormData<Schema>["submit"];
107
+ validate?: InjectedFormData<Schema>["validate"];
108
+ };
109
+ }>, {}, {}, "invalid" | "errors" | "component" | "hasProps" | "is" | "invalidLabels", import('vue').ComponentProvideOptions, true, {}, any>;
@@ -48,4 +48,4 @@ export declare function defineFormWrapper<Schema extends FormSchema>(formProvide
48
48
  clear?: InjectedFormData<Schema>["clear"];
49
49
  reset?: InjectedFormData<Schema>["reset"];
50
50
  };
51
- }>, {}, {}, "invalid" | "reset" | "clear" | "errors" | "submit" | "validate" | "formData" | "tag" | "fields" | "fieldsErrors" | "validateWrapper", import('vue').ComponentProvideOptions, true, {}, any>;
51
+ }>, {}, {}, "invalid" | "reset" | "clear" | "submit" | "errors" | "tag" | "validate" | "formData" | "fields" | "fieldsErrors" | "validateWrapper", import('vue').ComponentProvideOptions, true, {}, any>;