@vuehookform/core 0.4.6 → 0.5.0
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 +8 -0
- package/dist/core/useFieldArray.d.ts +17 -2
- package/dist/index.d.ts +2 -2
- package/dist/types.d.ts +169 -14
- package/dist/useController.d.ts +31 -1
- package/dist/vuehookform.cjs +99 -32
- package/dist/vuehookform.js +99 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,13 +84,21 @@ const addresses = fields('addresses')
|
|
|
84
84
|
|
|
85
85
|
<template>
|
|
86
86
|
<div v-for="field in addresses.value" :key="field.key">
|
|
87
|
+
<!-- Option 1: Scoped methods (recommended) - type-safe, cleaner -->
|
|
88
|
+
<input v-bind="field.register('street')" />
|
|
89
|
+
<input v-bind="field.register('city')" />
|
|
90
|
+
|
|
91
|
+
<!-- Option 2: Manual path building -->
|
|
87
92
|
<input v-bind="register(`addresses.${field.index}.street`)" />
|
|
93
|
+
|
|
88
94
|
<button @click="field.remove()">Remove</button>
|
|
89
95
|
</div>
|
|
90
96
|
<button @click="addresses.append({ street: '', city: '' })">Add Address</button>
|
|
91
97
|
</template>
|
|
92
98
|
```
|
|
93
99
|
|
|
100
|
+
Field array items provide **scoped methods** (`field.register()`, `field.setValue()`, `field.watch()`, etc.) that automatically build the full path with type safety.
|
|
101
|
+
|
|
94
102
|
### Validation Modes
|
|
95
103
|
|
|
96
104
|
```typescript
|
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
import { ComputedRef } from 'vue';
|
|
1
2
|
import { FormContext } from './formContext';
|
|
2
|
-
import { FieldArray, FieldArrayOptions, Path } from '../types';
|
|
3
|
+
import { FieldArray, FieldArrayOptions, Path, RegisterOptions, RegisterReturn, SetValueOptions, FieldState, ErrorOption } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Form methods required by field array for scoped item methods.
|
|
6
|
+
* Passed from useForm to enable type-safe field access within array items.
|
|
7
|
+
*/
|
|
8
|
+
export interface FieldArrayFormMethods {
|
|
9
|
+
register: (name: string, options?: RegisterOptions<unknown>) => RegisterReturn<unknown>;
|
|
10
|
+
setValue: (name: string, value: unknown, options?: SetValueOptions) => void;
|
|
11
|
+
getValues: (name: string) => unknown;
|
|
12
|
+
watch: (name: string) => ComputedRef<unknown>;
|
|
13
|
+
getFieldState: (name: string) => FieldState;
|
|
14
|
+
trigger: (name?: string | string[]) => Promise<boolean>;
|
|
15
|
+
clearErrors: (name?: string | string[]) => void;
|
|
16
|
+
setError: (name: string, error: ErrorOption) => void;
|
|
17
|
+
}
|
|
3
18
|
/**
|
|
4
19
|
* Create field array management functions
|
|
5
20
|
*/
|
|
6
|
-
export declare function createFieldArrayManager<FormValues>(ctx: FormContext<FormValues>, validate: (fieldPath?: string) => Promise<boolean>, setFocus: (name: string) => void): {
|
|
21
|
+
export declare function createFieldArrayManager<FormValues>(ctx: FormContext<FormValues>, validate: (fieldPath?: string) => Promise<boolean>, setFocus: (name: string) => void, formMethods: FieldArrayFormMethods): {
|
|
7
22
|
fields: <TPath extends Path<FormValues>>(name: TPath, options?: FieldArrayOptions) => FieldArray;
|
|
8
23
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
export { useForm } from './useForm';
|
|
18
18
|
export { provideForm, useFormContext, FormContextKey } from './context';
|
|
19
19
|
export { useWatch, type UseWatchOptions } from './useWatch';
|
|
20
|
-
export { useController, type UseControllerOptions, type UseControllerReturn, type ControllerFieldProps, } from './useController';
|
|
20
|
+
export { useController, type UseControllerOptions, type UseControllerReturn, type ControllerFieldProps, type LooseControllerOptions, } from './useController';
|
|
21
21
|
export { useFormState, type UseFormStateOptions, type FormStateKey } from './useFormState';
|
|
22
22
|
export { isFieldError } from './types';
|
|
23
|
-
export type { UseFormOptions, UseFormReturn, UseFormReturn as Control, RegisterOptions, RegisterReturn, FormState, FieldState, FieldErrors, FieldError, FieldErrorValue, ErrorOption, SetErrorsOptions, FieldArray, FieldArrayItem, FieldArrayOptions, FieldArrayRules, FieldArrayFocusOptions, InferSchema, FormValues, FormPath, Path, PathValue, ArrayElement, ArrayPath, FieldPath, ValidationMode, SetFocusOptions, ResetOptions, ResetFieldOptions, AsyncDefaultValues, TriggerOptions, SetValueOptions, UnregisterOptions, CriteriaMode, } from './types';
|
|
23
|
+
export type { UseFormOptions, UseFormReturn, UseFormReturn as Control, LooseControl, RegisterOptions, RegisterReturn, FormState, FieldState, FieldErrors, FieldError, FieldErrorValue, ErrorOption, SetErrorsOptions, FieldArray, FieldArrayItem, FieldArrayOptions, FieldArrayRules, FieldArrayFocusOptions, InferSchema, FormValues, FormPath, Path, PathValue, ArrayElement, ArrayPath, FieldPath, ValidationMode, SetFocusOptions, ResetOptions, ResetFieldOptions, AsyncDefaultValues, TriggerOptions, SetValueOptions, UnregisterOptions, CriteriaMode, } from './types';
|
|
24
24
|
export { get, set, unset, clearPathCache } from './utils/paths';
|
package/dist/types.d.ts
CHANGED
|
@@ -363,15 +363,104 @@ export interface RegisterReturn<TValue = unknown> {
|
|
|
363
363
|
disabled?: boolean;
|
|
364
364
|
}
|
|
365
365
|
/**
|
|
366
|
-
* Field metadata for dynamic arrays
|
|
366
|
+
* Field metadata for dynamic arrays with scoped methods for type-safe field access.
|
|
367
|
+
*
|
|
368
|
+
* Scoped methods provide full type safety when accessing fields within array items,
|
|
369
|
+
* solving the type inference problem with dynamic paths like `items.${index}.name`.
|
|
370
|
+
*
|
|
371
|
+
* @template TItem - The type of items in the array (inferred from field path)
|
|
372
|
+
*
|
|
373
|
+
* @example Basic usage
|
|
374
|
+
* ```ts
|
|
375
|
+
* const items = fields('items')
|
|
376
|
+
* items.value.forEach((field) => {
|
|
377
|
+
* field.register('name') // RegisterReturn<string> - fully typed!
|
|
378
|
+
* field.setValue('price', 25) // Type-checked against TItem
|
|
379
|
+
* field.watch('price') // ComputedRef<number>
|
|
380
|
+
* })
|
|
381
|
+
* ```
|
|
382
|
+
*
|
|
383
|
+
* @example In template with v-for
|
|
384
|
+
* ```vue
|
|
385
|
+
* <div v-for="field in items.value" :key="field.key">
|
|
386
|
+
* <input v-bind="field.register('name')" />
|
|
387
|
+
* <span v-if="field.getFieldState('name').error">
|
|
388
|
+
* {{ field.getFieldState('name').error }}
|
|
389
|
+
* </span>
|
|
390
|
+
* </div>
|
|
391
|
+
* ```
|
|
367
392
|
*/
|
|
368
|
-
export interface FieldArrayItem {
|
|
393
|
+
export interface FieldArrayItem<TItem = unknown> {
|
|
369
394
|
/** Stable key for v-for */
|
|
370
395
|
key: string;
|
|
371
396
|
/** Current index in array */
|
|
372
397
|
index: number;
|
|
373
398
|
/** Remove this item */
|
|
374
399
|
remove: () => void;
|
|
400
|
+
/**
|
|
401
|
+
* Register a field within this array item.
|
|
402
|
+
* Automatically builds the full path (e.g., 'items.0.name').
|
|
403
|
+
*
|
|
404
|
+
* @param name - Field path relative to the item (e.g., 'name', 'address.city')
|
|
405
|
+
* @param options - Registration options (validation, controlled mode, etc.)
|
|
406
|
+
* @returns Props to bind to the input element
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```vue
|
|
410
|
+
* <input v-bind="field.register('name')" />
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
register: <TPath extends Path<TItem>>(name: TPath, options?: RegisterOptions<PathValue<TItem, TPath>>) => RegisterReturn<PathValue<TItem, TPath>>;
|
|
414
|
+
/**
|
|
415
|
+
* Set value for a field within this array item.
|
|
416
|
+
*
|
|
417
|
+
* @param name - Field path relative to the item
|
|
418
|
+
* @param value - New value (typed based on path)
|
|
419
|
+
* @param options - Control side effects (validation, dirty, touched)
|
|
420
|
+
*/
|
|
421
|
+
setValue: <TPath extends Path<TItem>>(name: TPath, value: PathValue<TItem, TPath>, options?: SetValueOptions) => void;
|
|
422
|
+
/**
|
|
423
|
+
* Get current value of a field within this array item.
|
|
424
|
+
*
|
|
425
|
+
* @param name - Field path relative to the item
|
|
426
|
+
* @returns The field's current value
|
|
427
|
+
*/
|
|
428
|
+
getValue: <TPath extends Path<TItem>>(name: TPath) => PathValue<TItem, TPath>;
|
|
429
|
+
/**
|
|
430
|
+
* Watch a field within this array item reactively.
|
|
431
|
+
*
|
|
432
|
+
* @param name - Field path relative to the item
|
|
433
|
+
* @returns ComputedRef that updates when the field changes
|
|
434
|
+
*/
|
|
435
|
+
watch: <TPath extends Path<TItem>>(name: TPath) => ComputedRef<PathValue<TItem, TPath>>;
|
|
436
|
+
/**
|
|
437
|
+
* Get the state of a field within this array item.
|
|
438
|
+
* Note: Returns a snapshot, not reactive. Use watch() or formState for reactive access.
|
|
439
|
+
*
|
|
440
|
+
* @param name - Field path relative to the item
|
|
441
|
+
* @returns Field state with isDirty, isTouched, invalid, error
|
|
442
|
+
*/
|
|
443
|
+
getFieldState: <TPath extends Path<TItem>>(name: TPath) => FieldState;
|
|
444
|
+
/**
|
|
445
|
+
* Trigger validation for fields within this array item.
|
|
446
|
+
*
|
|
447
|
+
* @param name - Optional field path(s) relative to the item. Validates entire item if omitted.
|
|
448
|
+
* @returns Promise resolving to true if valid
|
|
449
|
+
*/
|
|
450
|
+
trigger: <TPath extends Path<TItem>>(name?: TPath | TPath[]) => Promise<boolean>;
|
|
451
|
+
/**
|
|
452
|
+
* Clear errors for fields within this array item.
|
|
453
|
+
*
|
|
454
|
+
* @param name - Optional field path(s) relative to the item. Clears all item errors if omitted.
|
|
455
|
+
*/
|
|
456
|
+
clearErrors: <TPath extends Path<TItem>>(name?: TPath | TPath[]) => void;
|
|
457
|
+
/**
|
|
458
|
+
* Set an error for a field within this array item.
|
|
459
|
+
*
|
|
460
|
+
* @param name - Field path relative to the item
|
|
461
|
+
* @param error - Error option with message
|
|
462
|
+
*/
|
|
463
|
+
setError: <TPath extends Path<TItem>>(name: TPath, error: ErrorOption) => void;
|
|
375
464
|
}
|
|
376
465
|
/**
|
|
377
466
|
* Focus options for field array operations
|
|
@@ -438,7 +527,7 @@ export interface FieldArrayOptions<T = unknown> {
|
|
|
438
527
|
*/
|
|
439
528
|
export interface FieldArray<TItem = unknown> {
|
|
440
529
|
/** Current field items with metadata. Reactive - updates when array methods are called. */
|
|
441
|
-
value: FieldArrayItem[];
|
|
530
|
+
value: FieldArrayItem<TItem>[];
|
|
442
531
|
/** Append item(s) to end of array. Returns false if maxLength exceeded. */
|
|
443
532
|
append: (value: TItem | TItem[], options?: FieldArrayFocusOptions) => boolean;
|
|
444
533
|
/** Prepend item(s) to beginning of array. Returns false if maxLength exceeded. */
|
|
@@ -693,6 +782,20 @@ export interface UseFormOptions<TSchema extends ZodType> {
|
|
|
693
782
|
*/
|
|
694
783
|
shouldUseNativeValidation?: boolean;
|
|
695
784
|
}
|
|
785
|
+
/**
|
|
786
|
+
* Loose control type for reusable components where schema type is unknown.
|
|
787
|
+
* Use this when building form field components that accept any form control.
|
|
788
|
+
*
|
|
789
|
+
* @example
|
|
790
|
+
* ```ts
|
|
791
|
+
* // Reusable field component
|
|
792
|
+
* function FormInput(props: { name: string; control: LooseControl }) {
|
|
793
|
+
* const { field } = useController(props) // No cast needed
|
|
794
|
+
* return <input v-bind="field" />
|
|
795
|
+
* }
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
export type LooseControl = UseFormReturn<ZodType<any>>;
|
|
696
799
|
/**
|
|
697
800
|
* Return value from useForm composable.
|
|
698
801
|
* Provides full type safety with autocomplete for field paths and typed values.
|
|
@@ -712,14 +815,22 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
712
815
|
* <input v-bind="register('email')" />
|
|
713
816
|
* <input v-bind="register('age', { validate: (v) => v >= 0 || 'Must be positive' })" />
|
|
714
817
|
*/
|
|
715
|
-
register:
|
|
818
|
+
register: {
|
|
819
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath, options?: RegisterOptions<PathValue<InferSchema<TSchema>, TPath>>): RegisterReturn<PathValue<InferSchema<TSchema>, TPath>>;
|
|
820
|
+
/** Loose overload for dynamic paths - returns unknown-typed value */
|
|
821
|
+
(name: string, options?: RegisterOptions<unknown>): RegisterReturn<unknown>;
|
|
822
|
+
};
|
|
716
823
|
/**
|
|
717
824
|
* Unregister a field to clean up refs and options
|
|
718
825
|
* Call this when a field is unmounted to prevent memory leaks
|
|
719
826
|
* @param name - Field path to unregister
|
|
720
827
|
* @param options - Options for what state to preserve
|
|
721
828
|
*/
|
|
722
|
-
unregister:
|
|
829
|
+
unregister: {
|
|
830
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath, options?: UnregisterOptions): void;
|
|
831
|
+
/** Loose overload for dynamic paths */
|
|
832
|
+
(name: string, options?: UnregisterOptions): void;
|
|
833
|
+
};
|
|
723
834
|
/**
|
|
724
835
|
* Handle form submission
|
|
725
836
|
* @param onValid - Callback called with valid data
|
|
@@ -747,7 +858,11 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
747
858
|
* @param value - New value (typed to match field)
|
|
748
859
|
* @param options - Options for validation/dirty/touched behavior
|
|
749
860
|
*/
|
|
750
|
-
setValue:
|
|
861
|
+
setValue: {
|
|
862
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath, value: PathValue<InferSchema<TSchema>, TPath>, options?: SetValueOptions): void;
|
|
863
|
+
/** Loose overload for dynamic paths */
|
|
864
|
+
(name: string, value: unknown, options?: SetValueOptions): void;
|
|
865
|
+
};
|
|
751
866
|
/**
|
|
752
867
|
* Reset form to default values
|
|
753
868
|
* @param values - Optional new default values
|
|
@@ -759,7 +874,11 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
759
874
|
* @param name - Field path
|
|
760
875
|
* @param options - Options for what state to preserve (with typed defaultValue)
|
|
761
876
|
*/
|
|
762
|
-
resetField:
|
|
877
|
+
resetField: {
|
|
878
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath, options?: ResetFieldOptions<PathValue<InferSchema<TSchema>, TPath>>): void;
|
|
879
|
+
/** Loose overload for dynamic paths */
|
|
880
|
+
(name: string, options?: ResetFieldOptions<unknown>): void;
|
|
881
|
+
};
|
|
763
882
|
/**
|
|
764
883
|
* Watch field value(s) reactively
|
|
765
884
|
* @overload Watch all form values
|
|
@@ -770,23 +889,39 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
770
889
|
(): ComputedRef<InferSchema<TSchema>>;
|
|
771
890
|
<TPath extends Path<InferSchema<TSchema>>>(name: TPath): ComputedRef<PathValue<InferSchema<TSchema>, TPath>>;
|
|
772
891
|
<TPath extends Path<InferSchema<TSchema>>>(names: TPath[]): ComputedRef<Partial<InferSchema<TSchema>>>;
|
|
892
|
+
/** Loose overload for dynamic paths - returns unknown */
|
|
893
|
+
(name: string): ComputedRef<unknown>;
|
|
894
|
+
/** Loose overload for dynamic path arrays */
|
|
895
|
+
(names: string[]): ComputedRef<Record<string, unknown>>;
|
|
773
896
|
};
|
|
774
897
|
/**
|
|
775
898
|
* Manually trigger validation
|
|
776
899
|
* @param name - Optional field path (validates all if not provided)
|
|
777
900
|
*/
|
|
778
|
-
validate:
|
|
901
|
+
validate: {
|
|
902
|
+
<TPath extends Path<InferSchema<TSchema>>>(name?: TPath): Promise<boolean>;
|
|
903
|
+
/** Loose overload for dynamic paths */
|
|
904
|
+
(name?: string): Promise<boolean>;
|
|
905
|
+
};
|
|
779
906
|
/**
|
|
780
907
|
* Clear errors for specified fields or all errors
|
|
781
908
|
* @param name - Optional field path or array of paths
|
|
782
909
|
*/
|
|
783
|
-
clearErrors:
|
|
910
|
+
clearErrors: {
|
|
911
|
+
<TPath extends Path<InferSchema<TSchema>>>(name?: TPath | TPath[] | 'root' | `root.${string}`): void;
|
|
912
|
+
/** Loose overload for dynamic paths */
|
|
913
|
+
(name?: string | string[]): void;
|
|
914
|
+
};
|
|
784
915
|
/**
|
|
785
916
|
* Set an error for a specific field
|
|
786
917
|
* @param name - Field path or root error
|
|
787
918
|
* @param error - Error option with message
|
|
788
919
|
*/
|
|
789
|
-
setError:
|
|
920
|
+
setError: {
|
|
921
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath | 'root' | `root.${string}`, error: ErrorOption): void;
|
|
922
|
+
/** Loose overload for dynamic paths */
|
|
923
|
+
(name: string, error: ErrorOption): void;
|
|
924
|
+
};
|
|
790
925
|
/**
|
|
791
926
|
* Set multiple errors at once. Useful for server-side validation errors
|
|
792
927
|
* or bulk error handling scenarios.
|
|
@@ -822,7 +957,11 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
822
957
|
* focusField('email')
|
|
823
958
|
* }
|
|
824
959
|
*/
|
|
825
|
-
hasErrors:
|
|
960
|
+
hasErrors: {
|
|
961
|
+
<TPath extends Path<InferSchema<TSchema>>>(fieldPath?: TPath | 'root' | `root.${string}`): boolean;
|
|
962
|
+
/** Loose overload for dynamic paths */
|
|
963
|
+
(fieldPath?: string): boolean;
|
|
964
|
+
};
|
|
826
965
|
/**
|
|
827
966
|
* Get validation errors for the form or a specific field
|
|
828
967
|
*
|
|
@@ -849,24 +988,40 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
849
988
|
(): InferSchema<TSchema>;
|
|
850
989
|
<TPath extends Path<InferSchema<TSchema>>>(name: TPath): PathValue<InferSchema<TSchema>, TPath>;
|
|
851
990
|
<TPath extends Path<InferSchema<TSchema>>>(names: TPath[]): Partial<InferSchema<TSchema>>;
|
|
991
|
+
/** Loose overload for dynamic paths - returns unknown */
|
|
992
|
+
(name: string): unknown;
|
|
993
|
+
/** Loose overload for dynamic path arrays */
|
|
994
|
+
(names: string[]): Record<string, unknown>;
|
|
852
995
|
};
|
|
853
996
|
/**
|
|
854
997
|
* Get the state of an individual field
|
|
855
998
|
* @param name - Field path
|
|
856
999
|
*/
|
|
857
|
-
getFieldState:
|
|
1000
|
+
getFieldState: {
|
|
1001
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath): FieldState;
|
|
1002
|
+
/** Loose overload for dynamic paths */
|
|
1003
|
+
(name: string): FieldState;
|
|
1004
|
+
};
|
|
858
1005
|
/**
|
|
859
1006
|
* Manually trigger validation for specific fields or entire form
|
|
860
1007
|
* @param name - Optional field path or array of paths
|
|
861
1008
|
* @param options - Optional trigger options (e.g., markAsSubmitted)
|
|
862
1009
|
*/
|
|
863
|
-
trigger:
|
|
1010
|
+
trigger: {
|
|
1011
|
+
<TPath extends Path<InferSchema<TSchema>>>(name?: TPath | TPath[], options?: TriggerOptions): Promise<boolean>;
|
|
1012
|
+
/** Loose overload for dynamic paths */
|
|
1013
|
+
(name?: string | string[], options?: TriggerOptions): Promise<boolean>;
|
|
1014
|
+
};
|
|
864
1015
|
/**
|
|
865
1016
|
* Programmatically focus a field
|
|
866
1017
|
* @param name - Field path
|
|
867
1018
|
* @param options - Focus options
|
|
868
1019
|
*/
|
|
869
|
-
setFocus:
|
|
1020
|
+
setFocus: {
|
|
1021
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath, options?: SetFocusOptions): void;
|
|
1022
|
+
/** Loose overload for dynamic paths */
|
|
1023
|
+
(name: string, options?: SetFocusOptions): void;
|
|
1024
|
+
};
|
|
870
1025
|
/**
|
|
871
1026
|
* Form configuration options (mode, reValidateMode).
|
|
872
1027
|
* Useful for composables like useController that need to respect validation modes.
|
package/dist/useController.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ComputedRef, Ref } from 'vue';
|
|
2
2
|
import { ZodType } from 'zod';
|
|
3
|
-
import { UseFormReturn, Path, PathValue, InferSchema, FieldState } from './types';
|
|
3
|
+
import { UseFormReturn, Path, PathValue, InferSchema, FieldState, LooseControl } from './types';
|
|
4
4
|
/**
|
|
5
5
|
* Options for useController composable
|
|
6
6
|
*/
|
|
@@ -36,6 +36,27 @@ export interface UseControllerReturn<TValue> {
|
|
|
36
36
|
/** Current field state (errors, dirty, touched) */
|
|
37
37
|
fieldState: ComputedRef<FieldState>;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Loose options for useController when schema type is unknown.
|
|
41
|
+
* Use this for building reusable field components that work with any form.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // Reusable field component
|
|
46
|
+
* function FormInput(props: { name: string; control: LooseControl }) {
|
|
47
|
+
* const { field, fieldState } = useController(props)
|
|
48
|
+
* // field.value is unknown, fieldState is reactive
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export interface LooseControllerOptions {
|
|
53
|
+
/** Field name/path as a string */
|
|
54
|
+
name: string;
|
|
55
|
+
/** Form control from useForm (uses context if not provided) */
|
|
56
|
+
control?: LooseControl;
|
|
57
|
+
/** Default value for the field */
|
|
58
|
+
defaultValue?: unknown;
|
|
59
|
+
}
|
|
39
60
|
/**
|
|
40
61
|
* Hook for controlled components that need fine-grained control over field state
|
|
41
62
|
*
|
|
@@ -59,5 +80,14 @@ export interface UseControllerReturn<TValue> {
|
|
|
59
80
|
* // />
|
|
60
81
|
* // <span v-if="fieldState.value.error">{{ fieldState.value.error }}</span>
|
|
61
82
|
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example Reusable component with loose typing
|
|
85
|
+
* ```ts
|
|
86
|
+
* // FormInput.vue
|
|
87
|
+
* const props = defineProps<{ name: string; control: LooseControl }>()
|
|
88
|
+
* const { field, fieldState } = useController(props)
|
|
89
|
+
* // No 'as never' cast needed!
|
|
90
|
+
* ```
|
|
62
91
|
*/
|
|
92
|
+
export declare function useController(options: LooseControllerOptions): UseControllerReturn<unknown>;
|
|
63
93
|
export declare function useController<TSchema extends ZodType, TPath extends Path<InferSchema<TSchema>>>(options: UseControllerOptions<TSchema, TPath>): UseControllerReturn<PathValue<InferSchema<TSchema>, TPath>>;
|
package/dist/vuehookform.cjs
CHANGED
|
@@ -839,6 +839,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
839
839
|
let handlers = ctx.fieldHandlers.get(name);
|
|
840
840
|
if (!handlers) {
|
|
841
841
|
const runCustomValidation = async (fieldName, value, requestId, resetGenAtStart) => {
|
|
842
|
+
if (!ctx.fieldRefs.has(fieldName)) return;
|
|
842
843
|
const fieldOpts = ctx.fieldOptions.get(fieldName);
|
|
843
844
|
if (!fieldOpts?.validate || fieldOpts.disabled) return;
|
|
844
845
|
const error = await fieldOpts.validate(value);
|
|
@@ -1009,7 +1010,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
1009
1010
|
unregister
|
|
1010
1011
|
};
|
|
1011
1012
|
}
|
|
1012
|
-
function createFieldArrayManager(ctx, validate, setFocus) {
|
|
1013
|
+
function createFieldArrayManager(ctx, validate, setFocus, formMethods) {
|
|
1013
1014
|
function fields(name, options) {
|
|
1014
1015
|
if (__DEV__) {
|
|
1015
1016
|
const syntaxError = validatePathSyntax(name);
|
|
@@ -1053,16 +1054,76 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1053
1054
|
if (item) indexCache.set(item.key, i);
|
|
1054
1055
|
}
|
|
1055
1056
|
};
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1057
|
+
const buildPath = (index, subPath) => {
|
|
1058
|
+
return subPath ? `${name}.${index}.${subPath}` : `${name}.${index}`;
|
|
1059
|
+
};
|
|
1060
|
+
const createScopedMethods = (indexGetter) => {
|
|
1061
|
+
return {
|
|
1062
|
+
register: (subPath, options$1) => formMethods.register(buildPath(indexGetter(), subPath), options$1),
|
|
1063
|
+
setValue: (subPath, value, options$1) => formMethods.setValue(buildPath(indexGetter(), subPath), value, options$1),
|
|
1064
|
+
getValue: (subPath) => formMethods.getValues(buildPath(indexGetter(), subPath)),
|
|
1065
|
+
watch: (subPath) => formMethods.watch(buildPath(indexGetter(), subPath)),
|
|
1066
|
+
getFieldState: (subPath) => formMethods.getFieldState(buildPath(indexGetter(), subPath)),
|
|
1067
|
+
trigger: (subPath) => {
|
|
1068
|
+
const index = indexGetter();
|
|
1069
|
+
if (!subPath) return formMethods.trigger(buildPath(index));
|
|
1070
|
+
if (Array.isArray(subPath)) return formMethods.trigger(subPath.map((p) => buildPath(index, p)));
|
|
1071
|
+
return formMethods.trigger(buildPath(index, subPath));
|
|
1072
|
+
},
|
|
1073
|
+
clearErrors: (subPath) => {
|
|
1074
|
+
const index = indexGetter();
|
|
1075
|
+
if (!subPath) {
|
|
1076
|
+
formMethods.clearErrors(buildPath(index));
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
if (Array.isArray(subPath)) {
|
|
1080
|
+
formMethods.clearErrors(subPath.map((p) => buildPath(index, p)));
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
formMethods.clearErrors(buildPath(index, subPath));
|
|
1084
|
+
},
|
|
1085
|
+
setError: (subPath, error) => formMethods.setError(buildPath(indexGetter(), subPath), error)
|
|
1086
|
+
};
|
|
1087
|
+
};
|
|
1088
|
+
const createItem = (key) => {
|
|
1089
|
+
const getIndex = () => indexCache.get(key) ?? -1;
|
|
1090
|
+
let scoped = null;
|
|
1091
|
+
const getScoped = () => scoped ??= createScopedMethods(getIndex);
|
|
1092
|
+
return {
|
|
1093
|
+
key,
|
|
1094
|
+
get index() {
|
|
1095
|
+
return getIndex();
|
|
1096
|
+
},
|
|
1097
|
+
remove() {
|
|
1098
|
+
const currentIndex = getIndex();
|
|
1099
|
+
if (currentIndex !== -1) removeAt(currentIndex);
|
|
1100
|
+
},
|
|
1101
|
+
get register() {
|
|
1102
|
+
return getScoped().register;
|
|
1103
|
+
},
|
|
1104
|
+
get setValue() {
|
|
1105
|
+
return getScoped().setValue;
|
|
1106
|
+
},
|
|
1107
|
+
get getValue() {
|
|
1108
|
+
return getScoped().getValue;
|
|
1109
|
+
},
|
|
1110
|
+
get watch() {
|
|
1111
|
+
return getScoped().watch;
|
|
1112
|
+
},
|
|
1113
|
+
get getFieldState() {
|
|
1114
|
+
return getScoped().getFieldState;
|
|
1115
|
+
},
|
|
1116
|
+
get trigger() {
|
|
1117
|
+
return getScoped().trigger;
|
|
1118
|
+
},
|
|
1119
|
+
get clearErrors() {
|
|
1120
|
+
return getScoped().clearErrors;
|
|
1121
|
+
},
|
|
1122
|
+
get setError() {
|
|
1123
|
+
return getScoped().setError;
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
};
|
|
1066
1127
|
if (fa.items.value.length === 0 && fa.values.length > 0) {
|
|
1067
1128
|
fa.items.value = fa.values.map(() => createItem(generateId()));
|
|
1068
1129
|
rebuildIndexCache();
|
|
@@ -1071,7 +1132,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1071
1132
|
if (!focusOptions?.shouldFocus) return;
|
|
1072
1133
|
await (0, vue.nextTick)();
|
|
1073
1134
|
const focusItemOffset = focusOptions?.focusIndex ?? 0;
|
|
1074
|
-
let fieldPath = `${name}.${baseIndex + Math.min(focusItemOffset, addedCount - 1)}`;
|
|
1135
|
+
let fieldPath = `${name}.${baseIndex + Math.max(0, Math.min(focusItemOffset, addedCount - 1))}`;
|
|
1075
1136
|
if (focusOptions?.focusName) fieldPath = `${fieldPath}.${focusOptions.focusName}`;
|
|
1076
1137
|
setFocus(fieldPath);
|
|
1077
1138
|
};
|
|
@@ -1242,13 +1303,10 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1242
1303
|
set(ctx.formData, name, newValues);
|
|
1243
1304
|
const newItems = [...fa.items.value];
|
|
1244
1305
|
const itemA = newItems[indexA];
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
fa.items.value = newItems;
|
|
1250
|
-
swapInCache(indexA, indexB);
|
|
1251
|
-
}
|
|
1306
|
+
newItems[indexA] = newItems[indexB];
|
|
1307
|
+
newItems[indexB] = itemA;
|
|
1308
|
+
fa.items.value = newItems;
|
|
1309
|
+
swapInCache(indexA, indexB);
|
|
1252
1310
|
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1253
1311
|
validateIfNeeded();
|
|
1254
1312
|
return true;
|
|
@@ -1268,16 +1326,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1268
1326
|
}
|
|
1269
1327
|
const newItems = [...fa.items.value];
|
|
1270
1328
|
const [removedItem] = newItems.splice(from, 1);
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
if (item) indexCache.set(item.key, i);
|
|
1280
|
-
}
|
|
1329
|
+
newItems.splice(to, 0, removedItem);
|
|
1330
|
+
fa.items.value = newItems;
|
|
1331
|
+
const minIdx = Math.min(from, to);
|
|
1332
|
+
const maxIdx = Math.max(from, to);
|
|
1333
|
+
const items = fa.items.value;
|
|
1334
|
+
for (let i = minIdx; i <= maxIdx; i++) {
|
|
1335
|
+
const item = items[i];
|
|
1336
|
+
if (item) indexCache.set(item.key, i);
|
|
1281
1337
|
}
|
|
1282
1338
|
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1283
1339
|
validateIfNeeded();
|
|
@@ -1384,8 +1440,6 @@ function useForm(options) {
|
|
|
1384
1440
|
el.focus();
|
|
1385
1441
|
if (focusOptions?.shouldSelect && el instanceof HTMLInputElement && typeof el.select === "function") el.select();
|
|
1386
1442
|
}
|
|
1387
|
-
const setFocusWrapper = (name) => setFocus(name);
|
|
1388
|
-
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
|
|
1389
1443
|
let lastSyncTime = 0;
|
|
1390
1444
|
const SYNC_DEBOUNCE_MS = 16;
|
|
1391
1445
|
function syncWithDebounce() {
|
|
@@ -1529,6 +1583,7 @@ function useForm(options) {
|
|
|
1529
1583
|
Object.assign(ctx.formData, newValues);
|
|
1530
1584
|
if (!opts.keepErrors) {
|
|
1531
1585
|
ctx.errors.value = {};
|
|
1586
|
+
ctx.externalErrors.value = {};
|
|
1532
1587
|
ctx.persistentErrorFields.clear();
|
|
1533
1588
|
}
|
|
1534
1589
|
if (!opts.keepTouched) ctx.touchedFields.value = {};
|
|
@@ -1629,7 +1684,8 @@ function useForm(options) {
|
|
|
1629
1684
|
for (const field of fieldsToClean) {
|
|
1630
1685
|
clearFieldErrors(ctx.errors, field);
|
|
1631
1686
|
clearFieldErrors(ctx.externalErrors, field);
|
|
1632
|
-
|
|
1687
|
+
const prefix = `${field}.`;
|
|
1688
|
+
for (const persistentField of ctx.persistentErrorFields) if (persistentField === field || persistentField.startsWith(prefix)) ctx.persistentErrorFields.delete(persistentField);
|
|
1633
1689
|
}
|
|
1634
1690
|
}
|
|
1635
1691
|
function setError(name, error) {
|
|
@@ -1723,6 +1779,17 @@ function useForm(options) {
|
|
|
1723
1779
|
}
|
|
1724
1780
|
return await validate(name);
|
|
1725
1781
|
}
|
|
1782
|
+
const setFocusWrapper = (name) => setFocus(name);
|
|
1783
|
+
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper, {
|
|
1784
|
+
register: (name, options$1) => register(name, options$1),
|
|
1785
|
+
setValue: (name, value, options$1) => setValue(name, value, options$1),
|
|
1786
|
+
getValues: (name) => getValues(name),
|
|
1787
|
+
watch: (name) => watch$1(name),
|
|
1788
|
+
getFieldState: (name) => getFieldState(name),
|
|
1789
|
+
trigger: (name) => trigger(name),
|
|
1790
|
+
clearErrors: (name) => clearErrors(name),
|
|
1791
|
+
setError: (name, error) => setError(name, error)
|
|
1792
|
+
});
|
|
1726
1793
|
return {
|
|
1727
1794
|
register,
|
|
1728
1795
|
unregister,
|
package/dist/vuehookform.js
CHANGED
|
@@ -837,6 +837,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
837
837
|
let handlers = ctx.fieldHandlers.get(name);
|
|
838
838
|
if (!handlers) {
|
|
839
839
|
const runCustomValidation = async (fieldName, value, requestId, resetGenAtStart) => {
|
|
840
|
+
if (!ctx.fieldRefs.has(fieldName)) return;
|
|
840
841
|
const fieldOpts = ctx.fieldOptions.get(fieldName);
|
|
841
842
|
if (!fieldOpts?.validate || fieldOpts.disabled) return;
|
|
842
843
|
const error = await fieldOpts.validate(value);
|
|
@@ -1007,7 +1008,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
1007
1008
|
unregister
|
|
1008
1009
|
};
|
|
1009
1010
|
}
|
|
1010
|
-
function createFieldArrayManager(ctx, validate, setFocus) {
|
|
1011
|
+
function createFieldArrayManager(ctx, validate, setFocus, formMethods) {
|
|
1011
1012
|
function fields(name, options) {
|
|
1012
1013
|
if (__DEV__) {
|
|
1013
1014
|
const syntaxError = validatePathSyntax(name);
|
|
@@ -1051,16 +1052,76 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1051
1052
|
if (item) indexCache.set(item.key, i);
|
|
1052
1053
|
}
|
|
1053
1054
|
};
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1055
|
+
const buildPath = (index, subPath) => {
|
|
1056
|
+
return subPath ? `${name}.${index}.${subPath}` : `${name}.${index}`;
|
|
1057
|
+
};
|
|
1058
|
+
const createScopedMethods = (indexGetter) => {
|
|
1059
|
+
return {
|
|
1060
|
+
register: (subPath, options$1) => formMethods.register(buildPath(indexGetter(), subPath), options$1),
|
|
1061
|
+
setValue: (subPath, value, options$1) => formMethods.setValue(buildPath(indexGetter(), subPath), value, options$1),
|
|
1062
|
+
getValue: (subPath) => formMethods.getValues(buildPath(indexGetter(), subPath)),
|
|
1063
|
+
watch: (subPath) => formMethods.watch(buildPath(indexGetter(), subPath)),
|
|
1064
|
+
getFieldState: (subPath) => formMethods.getFieldState(buildPath(indexGetter(), subPath)),
|
|
1065
|
+
trigger: (subPath) => {
|
|
1066
|
+
const index = indexGetter();
|
|
1067
|
+
if (!subPath) return formMethods.trigger(buildPath(index));
|
|
1068
|
+
if (Array.isArray(subPath)) return formMethods.trigger(subPath.map((p) => buildPath(index, p)));
|
|
1069
|
+
return formMethods.trigger(buildPath(index, subPath));
|
|
1070
|
+
},
|
|
1071
|
+
clearErrors: (subPath) => {
|
|
1072
|
+
const index = indexGetter();
|
|
1073
|
+
if (!subPath) {
|
|
1074
|
+
formMethods.clearErrors(buildPath(index));
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
if (Array.isArray(subPath)) {
|
|
1078
|
+
formMethods.clearErrors(subPath.map((p) => buildPath(index, p)));
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
formMethods.clearErrors(buildPath(index, subPath));
|
|
1082
|
+
},
|
|
1083
|
+
setError: (subPath, error) => formMethods.setError(buildPath(indexGetter(), subPath), error)
|
|
1084
|
+
};
|
|
1085
|
+
};
|
|
1086
|
+
const createItem = (key) => {
|
|
1087
|
+
const getIndex = () => indexCache.get(key) ?? -1;
|
|
1088
|
+
let scoped = null;
|
|
1089
|
+
const getScoped = () => scoped ??= createScopedMethods(getIndex);
|
|
1090
|
+
return {
|
|
1091
|
+
key,
|
|
1092
|
+
get index() {
|
|
1093
|
+
return getIndex();
|
|
1094
|
+
},
|
|
1095
|
+
remove() {
|
|
1096
|
+
const currentIndex = getIndex();
|
|
1097
|
+
if (currentIndex !== -1) removeAt(currentIndex);
|
|
1098
|
+
},
|
|
1099
|
+
get register() {
|
|
1100
|
+
return getScoped().register;
|
|
1101
|
+
},
|
|
1102
|
+
get setValue() {
|
|
1103
|
+
return getScoped().setValue;
|
|
1104
|
+
},
|
|
1105
|
+
get getValue() {
|
|
1106
|
+
return getScoped().getValue;
|
|
1107
|
+
},
|
|
1108
|
+
get watch() {
|
|
1109
|
+
return getScoped().watch;
|
|
1110
|
+
},
|
|
1111
|
+
get getFieldState() {
|
|
1112
|
+
return getScoped().getFieldState;
|
|
1113
|
+
},
|
|
1114
|
+
get trigger() {
|
|
1115
|
+
return getScoped().trigger;
|
|
1116
|
+
},
|
|
1117
|
+
get clearErrors() {
|
|
1118
|
+
return getScoped().clearErrors;
|
|
1119
|
+
},
|
|
1120
|
+
get setError() {
|
|
1121
|
+
return getScoped().setError;
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
};
|
|
1064
1125
|
if (fa.items.value.length === 0 && fa.values.length > 0) {
|
|
1065
1126
|
fa.items.value = fa.values.map(() => createItem(generateId()));
|
|
1066
1127
|
rebuildIndexCache();
|
|
@@ -1069,7 +1130,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1069
1130
|
if (!focusOptions?.shouldFocus) return;
|
|
1070
1131
|
await nextTick();
|
|
1071
1132
|
const focusItemOffset = focusOptions?.focusIndex ?? 0;
|
|
1072
|
-
let fieldPath = `${name}.${baseIndex + Math.min(focusItemOffset, addedCount - 1)}`;
|
|
1133
|
+
let fieldPath = `${name}.${baseIndex + Math.max(0, Math.min(focusItemOffset, addedCount - 1))}`;
|
|
1073
1134
|
if (focusOptions?.focusName) fieldPath = `${fieldPath}.${focusOptions.focusName}`;
|
|
1074
1135
|
setFocus(fieldPath);
|
|
1075
1136
|
};
|
|
@@ -1240,13 +1301,10 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1240
1301
|
set(ctx.formData, name, newValues);
|
|
1241
1302
|
const newItems = [...fa.items.value];
|
|
1242
1303
|
const itemA = newItems[indexA];
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
fa.items.value = newItems;
|
|
1248
|
-
swapInCache(indexA, indexB);
|
|
1249
|
-
}
|
|
1304
|
+
newItems[indexA] = newItems[indexB];
|
|
1305
|
+
newItems[indexB] = itemA;
|
|
1306
|
+
fa.items.value = newItems;
|
|
1307
|
+
swapInCache(indexA, indexB);
|
|
1250
1308
|
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1251
1309
|
validateIfNeeded();
|
|
1252
1310
|
return true;
|
|
@@ -1266,16 +1324,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1266
1324
|
}
|
|
1267
1325
|
const newItems = [...fa.items.value];
|
|
1268
1326
|
const [removedItem] = newItems.splice(from, 1);
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
if (item) indexCache.set(item.key, i);
|
|
1278
|
-
}
|
|
1327
|
+
newItems.splice(to, 0, removedItem);
|
|
1328
|
+
fa.items.value = newItems;
|
|
1329
|
+
const minIdx = Math.min(from, to);
|
|
1330
|
+
const maxIdx = Math.max(from, to);
|
|
1331
|
+
const items = fa.items.value;
|
|
1332
|
+
for (let i = minIdx; i <= maxIdx; i++) {
|
|
1333
|
+
const item = items[i];
|
|
1334
|
+
if (item) indexCache.set(item.key, i);
|
|
1279
1335
|
}
|
|
1280
1336
|
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1281
1337
|
validateIfNeeded();
|
|
@@ -1382,8 +1438,6 @@ function useForm(options) {
|
|
|
1382
1438
|
el.focus();
|
|
1383
1439
|
if (focusOptions?.shouldSelect && el instanceof HTMLInputElement && typeof el.select === "function") el.select();
|
|
1384
1440
|
}
|
|
1385
|
-
const setFocusWrapper = (name) => setFocus(name);
|
|
1386
|
-
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
|
|
1387
1441
|
let lastSyncTime = 0;
|
|
1388
1442
|
const SYNC_DEBOUNCE_MS = 16;
|
|
1389
1443
|
function syncWithDebounce() {
|
|
@@ -1527,6 +1581,7 @@ function useForm(options) {
|
|
|
1527
1581
|
Object.assign(ctx.formData, newValues);
|
|
1528
1582
|
if (!opts.keepErrors) {
|
|
1529
1583
|
ctx.errors.value = {};
|
|
1584
|
+
ctx.externalErrors.value = {};
|
|
1530
1585
|
ctx.persistentErrorFields.clear();
|
|
1531
1586
|
}
|
|
1532
1587
|
if (!opts.keepTouched) ctx.touchedFields.value = {};
|
|
@@ -1627,7 +1682,8 @@ function useForm(options) {
|
|
|
1627
1682
|
for (const field of fieldsToClean) {
|
|
1628
1683
|
clearFieldErrors(ctx.errors, field);
|
|
1629
1684
|
clearFieldErrors(ctx.externalErrors, field);
|
|
1630
|
-
|
|
1685
|
+
const prefix = `${field}.`;
|
|
1686
|
+
for (const persistentField of ctx.persistentErrorFields) if (persistentField === field || persistentField.startsWith(prefix)) ctx.persistentErrorFields.delete(persistentField);
|
|
1631
1687
|
}
|
|
1632
1688
|
}
|
|
1633
1689
|
function setError(name, error) {
|
|
@@ -1721,6 +1777,17 @@ function useForm(options) {
|
|
|
1721
1777
|
}
|
|
1722
1778
|
return await validate(name);
|
|
1723
1779
|
}
|
|
1780
|
+
const setFocusWrapper = (name) => setFocus(name);
|
|
1781
|
+
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper, {
|
|
1782
|
+
register: (name, options$1) => register(name, options$1),
|
|
1783
|
+
setValue: (name, value, options$1) => setValue(name, value, options$1),
|
|
1784
|
+
getValues: (name) => getValues(name),
|
|
1785
|
+
watch: (name) => watch$1(name),
|
|
1786
|
+
getFieldState: (name) => getFieldState(name),
|
|
1787
|
+
trigger: (name) => trigger(name),
|
|
1788
|
+
clearErrors: (name) => clearErrors(name),
|
|
1789
|
+
setError: (name, error) => setError(name, error)
|
|
1790
|
+
});
|
|
1724
1791
|
return {
|
|
1725
1792
|
register,
|
|
1726
1793
|
unregister,
|
package/package.json
CHANGED