@volverjs/form-vue 1.0.0-beta.22 → 1.0.0-beta.24

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,
@@ -237,14 +239,16 @@ The wrapper status is invalid if at least one of the fields inside it is invalid
237
239
  ```vue
238
240
  <template>
239
241
  <VvForm>
242
+ <!-- main VvFormWrapper -->
240
243
  <VvFormWrapper v-slot="{ invalid }">
244
+ <!-- add VvFormFields to wrapper -->
241
245
  <div class="form-section">
242
246
  <span v-if="invalid">There is a validation error</span>
243
- <!-- form fields of section -->
247
+ <!-- nested VvFormWrapper -->
244
248
  <VvFormWrapper v-slot="{ invalid: groupInvalid }">
245
249
  <div class="form-section__group">
246
250
  <span v-if="groupInvalid">There is a validation error</span>
247
- <!-- form fields of the group -->
251
+ <!-- add VvFormFields to nested wrapper -->
248
252
  </div>
249
253
  </VvFormWrapper>
250
254
  </div>
@@ -298,6 +302,141 @@ By default UI components must be installed globally, they can be lazy-loaded wit
298
302
 
299
303
  Check the [`VvFormField` documentation](./docs/VvFormField.md) to learn more about form fields.
300
304
 
305
+ ### VvFormFieldsGroup
306
+
307
+ `VvFormFieldsGroup` allow you to render a group of form fields inside a form.
308
+
309
+ It automatically bind the form data through the `names` attribute. For nested objects, use the `names` attribute with **dot notation**.
310
+
311
+ ```vue
312
+ <template>
313
+ <VvForm>
314
+ <VvFormFieldsGroup
315
+ v-slot="{ modelValue, invalids, invalidLabels, onUpdateField }"
316
+ :names="['firstName', 'lastName']"
317
+ >
318
+ <fieldset>
319
+ <p>
320
+ <label for="firstName">First Name</label>
321
+ <input
322
+ id="firstName"
323
+ type="text"
324
+ name="firstName"
325
+ :value="modelValue.firstName"
326
+ :aria-invalid="invalids.firstName"
327
+ :aria-errormessage="invalids.firstName ? 'first-name-alert' : undefined"
328
+ @input="onUpdateField('firstName', $event)"
329
+ >
330
+ <small v-if="invalids.firstName" id="first-name-alert" role="alert">
331
+ {{ invalidLabels?.firstName }}
332
+ </small>
333
+ </p>
334
+ <p>
335
+ <label for="lastName">Last Name</label>
336
+ <input
337
+ id="lastName"
338
+ type="text"
339
+ name="lastName"
340
+ :value="modelValue.lastName"
341
+ :aria-invalid="invalids.lastName"
342
+ :aria-errormessage="invalids.lastName ? 'last-name-alert' : undefined"
343
+ @input="onUpdateField('lastName', $event)"
344
+ >
345
+ <small v-if="invalids.lastName" id="last-name-alert" role="alert">
346
+ {{ invalidLabels?.lastName }}
347
+ </small>
348
+ </p>
349
+ </fieldset>
350
+ </VvFormFieldsGroup>
351
+ </VvForm>
352
+ </template>
353
+ ```
354
+
355
+ Alternatively, you can create a custom component to render the group of form fields.
356
+
357
+ ```vue
358
+ // MyFieldsGroup.vue
359
+ <script lang="ts" setup>
360
+ defineProps<{
361
+ invalids: Record<string, boolean>
362
+ invalidLabels?: Record<string, string[]>
363
+ }>()
364
+ // v-model:first-name
365
+ const firstName = defineModel<string>('firstName', { default: '' })
366
+ // v-model:last-name
367
+ const lastName = defineModel<string>('lastName', { default: '' })
368
+ </script>
369
+
370
+ <template>
371
+ <fieldset>
372
+ <p>
373
+ <label for="firstName">First Name</label>
374
+ <input
375
+ id="firstName"
376
+ v-model="firstName"
377
+ type="text"
378
+ name="firstName"
379
+ :aria-invalid="invalids.firstName"
380
+ :aria-errormessage="invalids.firstName ? 'first-name-alert' : undefined"
381
+ >
382
+ <small v-if="invalids.firstName" id="first-name-alert" role="alert">
383
+ {{ invalidLabels?.firstName }}
384
+ </small>
385
+ </p>
386
+ <p>
387
+ <label for="lastName">Last Name</label>
388
+ <input
389
+ id="lastName"
390
+ v-model="lastName"
391
+ type="text"
392
+ name="lastName"
393
+ :aria-invalid="invalids.lastName"
394
+ :aria-errormessage="invalids.lastName ? 'last-name-alert' : undefined"
395
+ >
396
+ <small v-if="invalids.lastName" id="last-name-alert" role="alert">
397
+ {{ invalidLabels?.lastName }}
398
+ </small>
399
+ </p>
400
+ </fieldset>
401
+ </template>
402
+ ```
403
+
404
+ An than use it inside the `VvFormFieldsGroup` with the `:is` attribute.
405
+
406
+ ```vue
407
+ <script>
408
+ import MyFieldsGroup from './MyFieldsGroup.vue'
409
+ </script>
410
+
411
+ <template>
412
+ <VvForm>
413
+ <VvFormFieldsGroup
414
+ :is="MyFieldsGroup"
415
+ :names="['firstName', 'lastName']"
416
+ />
417
+ </VvForm>
418
+ </template>
419
+ ```
420
+
421
+ 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.
422
+
423
+ ```vue
424
+ <script>
425
+ import MyCustomComponent from './MyCustomComponent.vue'
426
+ </script>
427
+
428
+ <template>
429
+ <VvForm>
430
+ <VvFormFieldsGroup
431
+ :is="MyCustomComponent"
432
+ :names="{
433
+ myCustomComponentVModel: 'path.to.form.field',
434
+ }"
435
+ />
436
+ </VvForm>
437
+ </template>
438
+ ```
439
+
301
440
  ## VvFormTemplate
302
441
 
303
442
  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 +642,15 @@ import { z } from 'zod'
503
642
  import { defaultObjectBySchema } from '@volverjs/form-vue'
504
643
 
505
644
  const schema = z.object({
506
- name: z.string().default('John'),
507
- surname: z.string().default('Doe')
645
+ firstName: z.string().default('John'),
646
+ lastName: z.string().default('Doe')
508
647
  })
509
648
 
510
649
  const defaultObject = defaultObjectBySchema(schema)
511
- // defaultObject = { name: 'John', surname: 'Doe' }
650
+ // defaultObject = { firstName: 'John', lastName: 'Doe' }
512
651
 
513
652
  const defaultObject = defaultObjectBySchema(schema, { name: 'Jane' })
514
- // defaultObject = { name: 'Jane', surname: 'Doe' }
653
+ // defaultObject = { firstName: 'Jane', lastName: 'Doe' }
515
654
  ```
516
655
 
517
656
  `defaultObjectBySchema` can be used with nested objects.
@@ -521,8 +660,8 @@ import { z } from 'zod'
521
660
  import { defaultObjectBySchema } from '@volverjs/form-vue'
522
661
 
523
662
  const schema = z.object({
524
- name: z.string().default('John'),
525
- surname: z.string().default('Doe'),
663
+ firstName: z.string().default('John'),
664
+ lastName: z.string().default('Doe'),
526
665
  address: z.object({
527
666
  street: z.string().default('Main Street'),
528
667
  number: z.number().default(1)
@@ -530,7 +669,7 @@ const schema = z.object({
530
669
  })
531
670
 
532
671
  const defaultObject = defaultObjectBySchema(schema)
533
- // defaultObject = { name: 'John', surname: 'Doe', address: { street: 'Main Street', number: 1 } }
672
+ // defaultObject = { firstName: 'John', lastName: 'Doe', address: { street: 'Main Street', number: 1 } }
534
673
  ```
535
674
 
536
675
  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 +680,8 @@ import { defaultObjectBySchema } from '@volverjs/form-vue'
541
680
 
542
681
  const schema = z
543
682
  .object({
544
- name: z.string().default('John'),
545
- surname: z.string().default('Doe'),
683
+ firstName: z.string().default('John'),
684
+ lastName: z.string().default('Doe'),
546
685
  address: z.object({
547
686
  street: z.string().default('Main Street'),
548
687
  number: z.number().default(1)
@@ -557,7 +696,7 @@ const defaultObject = defaultObjectBySchema(schema, {
557
696
  height: '1.9',
558
697
  email: 'john.doe@test.com'
559
698
  })
560
- // defaultObject = { name: 'John', surname: 'Doe', address: { street: 'Main Street', number: 1 }, age: null, height: 1.9, weight: 80, email: 'john.doe@test.com' }
699
+ // defaultObject = { firstName: 'John', lastName: 'Doe', address: { street: 'Main Street', number: 1 }, age: null, height: 1.9, weight: 80, email: 'john.doe@test.com' }
561
700
  ```
562
701
 
563
702
  ## 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;
124
- modelValue: unknown;
123
+ invalidLabel?: string[];
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>;