@vuehookform/core 0.2.7 → 0.2.10
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/dist/core/domSync.d.ts +29 -0
- package/dist/core/fieldState.d.ts +36 -0
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +10 -4
- package/dist/useWatch.d.ts +8 -2
- package/dist/vuehookform.cjs +52 -59
- package/dist/vuehookform.js +52 -59
- package/package.json +6 -8
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { RegisterOptions } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Sync values from uncontrolled DOM inputs to form data
|
|
5
|
+
*
|
|
6
|
+
* This reads the current DOM state from uncontrolled inputs and updates
|
|
7
|
+
* the formData object. Used before form submission and when getting values.
|
|
8
|
+
*
|
|
9
|
+
* @param fieldRefs - Map of field names to their DOM element refs
|
|
10
|
+
* @param fieldOptions - Map of field names to their registration options
|
|
11
|
+
* @param formData - The reactive form data object to update
|
|
12
|
+
*/
|
|
13
|
+
export declare function syncUncontrolledInputs(fieldRefs: Map<string, Ref<HTMLInputElement | null>>, fieldOptions: Map<string, RegisterOptions>, formData: Record<string, unknown>): void;
|
|
14
|
+
/**
|
|
15
|
+
* Update a single DOM element with a new value
|
|
16
|
+
*
|
|
17
|
+
* Handles both checkbox and text inputs appropriately.
|
|
18
|
+
*
|
|
19
|
+
* @param el - The DOM input element to update
|
|
20
|
+
* @param value - The value to set
|
|
21
|
+
*/
|
|
22
|
+
export declare function updateDomElement(el: HTMLInputElement, value: unknown): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get value from a DOM element based on its type
|
|
25
|
+
*
|
|
26
|
+
* @param el - The DOM input element
|
|
27
|
+
* @returns The current value (checked state for checkboxes, value for others)
|
|
28
|
+
*/
|
|
29
|
+
export declare function getDomElementValue(el: HTMLInputElement): unknown;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ShallowRef } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Mark a field as dirty (value has changed from default)
|
|
4
|
+
*
|
|
5
|
+
* @param dirtyFields - The reactive dirty fields record
|
|
6
|
+
* @param fieldName - Name of the field to mark as dirty
|
|
7
|
+
*/
|
|
8
|
+
export declare function markFieldDirty(dirtyFields: ShallowRef<Record<string, boolean>>, fieldName: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Mark a field as touched (user has interacted with it)
|
|
11
|
+
*
|
|
12
|
+
* @param touchedFields - The reactive touched fields record
|
|
13
|
+
* @param fieldName - Name of the field to mark as touched
|
|
14
|
+
*/
|
|
15
|
+
export declare function markFieldTouched(touchedFields: ShallowRef<Record<string, boolean>>, fieldName: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Clear dirty state for a field
|
|
18
|
+
*
|
|
19
|
+
* @param dirtyFields - The reactive dirty fields record
|
|
20
|
+
* @param fieldName - Name of the field to clear
|
|
21
|
+
*/
|
|
22
|
+
export declare function clearFieldDirty(dirtyFields: ShallowRef<Record<string, boolean>>, fieldName: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Clear touched state for a field
|
|
25
|
+
*
|
|
26
|
+
* @param touchedFields - The reactive touched fields record
|
|
27
|
+
* @param fieldName - Name of the field to clear
|
|
28
|
+
*/
|
|
29
|
+
export declare function clearFieldTouched(touchedFields: ShallowRef<Record<string, boolean>>, fieldName: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Clear errors for a field and its nested paths
|
|
32
|
+
*
|
|
33
|
+
* @param errors - The reactive errors record
|
|
34
|
+
* @param fieldName - Name of the field (clears exact match and all nested paths)
|
|
35
|
+
*/
|
|
36
|
+
export declare function clearFieldErrors<T>(errors: ShallowRef<Record<string, T>>, fieldName: string): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
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, } from './useController';
|
|
21
21
|
export { useFormState, type UseFormStateOptions, type FormStateKey } from './useFormState';
|
|
22
22
|
export { isFieldError } from './types';
|
|
23
23
|
export type { UseFormOptions, UseFormReturn, RegisterOptions, RegisterReturn, FormState, FieldState, FieldErrors, FieldError, FieldErrorValue, ErrorOption, FieldArray, FieldArrayItem, InferSchema, FormValues, FormPath, Path, PathValue, ArrayElement, ArrayPath, ValidationMode, SetFocusOptions, ResetOptions, ResetFieldOptions, AsyncDefaultValues, } from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export type ArrayPath<T> = {
|
|
|
74
74
|
* type ItemType = PathValue<Form, 'items.0'> // { id: number }
|
|
75
75
|
* type ItemId = PathValue<Form, 'items.0.id'> // number
|
|
76
76
|
*/
|
|
77
|
-
export type PathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue<T[K], Rest> : T extends Array<infer U> ? K extends `${number}` ? PathValue<U, Rest> : never : never : P extends keyof T ? T[P] : T extends Array<infer U> ? P extends `${number}` ? U : never : never;
|
|
77
|
+
export type PathValue<T, P extends string> = T extends unknown ? P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue<T[K], Rest> : T extends Array<infer U> ? K extends `${number}` ? PathValue<U, Rest> : never : never : P extends keyof T ? T[P] : T extends Array<infer U> ? P extends `${number}` ? U : never : never : never;
|
|
78
78
|
/**
|
|
79
79
|
* Single field error with type and message
|
|
80
80
|
*/
|
|
@@ -490,9 +490,15 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
490
490
|
resetField: <TPath extends Path<InferSchema<TSchema>>>(name: TPath, options?: ResetFieldOptions<PathValue<InferSchema<TSchema>, TPath>>) => void;
|
|
491
491
|
/**
|
|
492
492
|
* Watch field value(s) reactively
|
|
493
|
-
* @
|
|
493
|
+
* @overload Watch all form values
|
|
494
|
+
* @overload Watch single field value by path
|
|
495
|
+
* @overload Watch multiple field values by paths array
|
|
494
496
|
*/
|
|
495
|
-
watch:
|
|
497
|
+
watch: {
|
|
498
|
+
(): ComputedRef<InferSchema<TSchema>>;
|
|
499
|
+
<TPath extends Path<InferSchema<TSchema>>>(name: TPath): ComputedRef<PathValue<InferSchema<TSchema>, TPath>>;
|
|
500
|
+
<TPath extends Path<InferSchema<TSchema>>>(names: TPath[]): ComputedRef<Partial<InferSchema<TSchema>>>;
|
|
501
|
+
};
|
|
496
502
|
/**
|
|
497
503
|
* Manually trigger validation
|
|
498
504
|
* @param name - Optional field path (validates all if not provided)
|
|
@@ -502,7 +508,7 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
502
508
|
* Clear errors for specified fields or all errors
|
|
503
509
|
* @param name - Optional field path or array of paths
|
|
504
510
|
*/
|
|
505
|
-
clearErrors: <TPath extends Path<InferSchema<TSchema>>>(name?: TPath | TPath[]) => void;
|
|
511
|
+
clearErrors: <TPath extends Path<InferSchema<TSchema>>>(name?: TPath | TPath[] | 'root' | `root.${string}`) => void;
|
|
506
512
|
/**
|
|
507
513
|
* Set an error for a specific field
|
|
508
514
|
* @param name - Field path or root error
|
package/dist/useWatch.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ComputedRef } from 'vue';
|
|
2
2
|
import { ZodType } from 'zod';
|
|
3
|
-
import { UseFormReturn, Path, InferSchema } from './types';
|
|
3
|
+
import { UseFormReturn, Path, PathValue, InferSchema } from './types';
|
|
4
4
|
/**
|
|
5
5
|
* Options for useWatch composable
|
|
6
6
|
*/
|
|
@@ -37,4 +37,10 @@ export interface UseWatchOptions<TSchema extends ZodType, TPath extends Path<Inf
|
|
|
37
37
|
* const status = useWatch({ name: 'status', defaultValue: 'pending' })
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
-
export declare function useWatch<TSchema extends ZodType
|
|
40
|
+
export declare function useWatch<TSchema extends ZodType>(options?: Omit<UseWatchOptions<TSchema, Path<InferSchema<TSchema>>>, 'name'>): ComputedRef<InferSchema<TSchema>>;
|
|
41
|
+
export declare function useWatch<TSchema extends ZodType, TPath extends Path<InferSchema<TSchema>>>(options: UseWatchOptions<TSchema, TPath> & {
|
|
42
|
+
name: TPath;
|
|
43
|
+
}): ComputedRef<PathValue<InferSchema<TSchema>, TPath>>;
|
|
44
|
+
export declare function useWatch<TSchema extends ZodType, TPath extends Path<InferSchema<TSchema>>>(options: UseWatchOptions<TSchema, TPath> & {
|
|
45
|
+
name: TPath[];
|
|
46
|
+
}): ComputedRef<Partial<InferSchema<TSchema>>>;
|
package/dist/vuehookform.cjs
CHANGED
|
@@ -144,7 +144,7 @@ function createFormContext(options) {
|
|
|
144
144
|
options
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
|
-
function clearFieldErrors(errors, fieldPath) {
|
|
147
|
+
function clearFieldErrors$1(errors, fieldPath) {
|
|
148
148
|
const newErrors = { ...errors };
|
|
149
149
|
for (const key of Object.keys(newErrors)) if (key === fieldPath || key.startsWith(`${fieldPath}.`)) delete newErrors[key];
|
|
150
150
|
return newErrors;
|
|
@@ -221,7 +221,7 @@ function createValidation(ctx) {
|
|
|
221
221
|
ctx.errorDelayTimers.delete(fieldPath);
|
|
222
222
|
}
|
|
223
223
|
ctx.pendingErrors.delete(fieldPath);
|
|
224
|
-
return clearFieldErrors(ctx.errors.value, fieldPath);
|
|
224
|
+
return clearFieldErrors$1(ctx.errors.value, fieldPath);
|
|
225
225
|
}
|
|
226
226
|
function clearAllPendingErrors() {
|
|
227
227
|
for (const timer of ctx.errorDelayTimers.values()) clearTimeout(timer);
|
|
@@ -648,6 +648,45 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
648
648
|
}
|
|
649
649
|
return { fields };
|
|
650
650
|
}
|
|
651
|
+
function syncUncontrolledInputs(fieldRefs, fieldOptions, formData) {
|
|
652
|
+
for (const [name, fieldRef] of Array.from(fieldRefs.entries())) {
|
|
653
|
+
const el = fieldRef.value;
|
|
654
|
+
if (el) {
|
|
655
|
+
if (!fieldOptions.get(name)?.controlled) set(formData, name, el.type === "checkbox" ? el.checked : el.value);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function updateDomElement(el, value) {
|
|
660
|
+
if (el.type === "checkbox") el.checked = value;
|
|
661
|
+
else el.value = value;
|
|
662
|
+
}
|
|
663
|
+
function markFieldDirty(dirtyFields, fieldName) {
|
|
664
|
+
dirtyFields.value = {
|
|
665
|
+
...dirtyFields.value,
|
|
666
|
+
[fieldName]: true
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function markFieldTouched(touchedFields, fieldName) {
|
|
670
|
+
touchedFields.value = {
|
|
671
|
+
...touchedFields.value,
|
|
672
|
+
[fieldName]: true
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function clearFieldDirty(dirtyFields, fieldName) {
|
|
676
|
+
const newDirty = { ...dirtyFields.value };
|
|
677
|
+
delete newDirty[fieldName];
|
|
678
|
+
dirtyFields.value = newDirty;
|
|
679
|
+
}
|
|
680
|
+
function clearFieldTouched(touchedFields, fieldName) {
|
|
681
|
+
const newTouched = { ...touchedFields.value };
|
|
682
|
+
delete newTouched[fieldName];
|
|
683
|
+
touchedFields.value = newTouched;
|
|
684
|
+
}
|
|
685
|
+
function clearFieldErrors(errors, fieldName) {
|
|
686
|
+
const newErrors = { ...errors.value };
|
|
687
|
+
for (const key of Object.keys(newErrors)) if (key === fieldName || key.startsWith(`${fieldName}.`)) delete newErrors[key];
|
|
688
|
+
errors.value = newErrors;
|
|
689
|
+
}
|
|
651
690
|
function useForm(options) {
|
|
652
691
|
const ctx = createFormContext(options);
|
|
653
692
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
@@ -693,15 +732,7 @@ function useForm(options) {
|
|
|
693
732
|
ctx.submitCount.value++;
|
|
694
733
|
ctx.isSubmitSuccessful.value = false;
|
|
695
734
|
try {
|
|
696
|
-
|
|
697
|
-
const el = fieldRef.value;
|
|
698
|
-
if (el) {
|
|
699
|
-
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
700
|
-
const value = el.type === "checkbox" ? el.checked : el.value;
|
|
701
|
-
set(ctx.formData, name, value);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
735
|
+
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
705
736
|
if (await validate()) {
|
|
706
737
|
await onValid(ctx.formData);
|
|
707
738
|
ctx.isSubmitSuccessful.value = true;
|
|
@@ -719,21 +750,11 @@ function useForm(options) {
|
|
|
719
750
|
}
|
|
720
751
|
function setValue(name, value, setValueOptions) {
|
|
721
752
|
set(ctx.formData, name, value);
|
|
722
|
-
if (setValueOptions?.shouldDirty !== false) ctx.dirtyFields
|
|
723
|
-
|
|
724
|
-
[name]: true
|
|
725
|
-
};
|
|
726
|
-
if (setValueOptions?.shouldTouch) ctx.touchedFields.value = {
|
|
727
|
-
...ctx.touchedFields.value,
|
|
728
|
-
[name]: true
|
|
729
|
-
};
|
|
753
|
+
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
|
|
754
|
+
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
|
|
730
755
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
731
756
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
732
|
-
if (fieldRef?.value)
|
|
733
|
-
const el = fieldRef.value;
|
|
734
|
-
if (el.type === "checkbox") el.checked = value;
|
|
735
|
-
else el.value = value;
|
|
736
|
-
}
|
|
757
|
+
if (fieldRef?.value) updateDomElement(fieldRef.value, value);
|
|
737
758
|
}
|
|
738
759
|
if (setValueOptions?.shouldValidate) validate(name);
|
|
739
760
|
}
|
|
@@ -757,7 +778,7 @@ function useForm(options) {
|
|
|
757
778
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
758
779
|
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
759
780
|
ctx.fieldArrays.clear();
|
|
760
|
-
for (const [name, fieldRef] of ctx.fieldRefs.entries()) {
|
|
781
|
+
for (const [name, fieldRef] of Array.from(ctx.fieldRefs.entries())) {
|
|
761
782
|
const el = fieldRef.value;
|
|
762
783
|
if (el) {
|
|
763
784
|
const value = get(newValues, name);
|
|
@@ -780,30 +801,12 @@ function useForm(options) {
|
|
|
780
801
|
else set(ctx.defaultValues, name, defaultValue);
|
|
781
802
|
const clonedValue = defaultValue !== void 0 ? JSON.parse(JSON.stringify(defaultValue)) : void 0;
|
|
782
803
|
set(ctx.formData, name, clonedValue);
|
|
783
|
-
if (!opts.keepError)
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
ctx.errors.value = newErrors;
|
|
787
|
-
}
|
|
788
|
-
if (!opts.keepDirty) {
|
|
789
|
-
const newDirty = { ...ctx.dirtyFields.value };
|
|
790
|
-
delete newDirty[name];
|
|
791
|
-
ctx.dirtyFields.value = newDirty;
|
|
792
|
-
}
|
|
793
|
-
if (!opts.keepTouched) {
|
|
794
|
-
const newTouched = { ...ctx.touchedFields.value };
|
|
795
|
-
delete newTouched[name];
|
|
796
|
-
ctx.touchedFields.value = newTouched;
|
|
797
|
-
}
|
|
804
|
+
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
805
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
806
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
798
807
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
799
808
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
800
|
-
if (fieldRef?.value)
|
|
801
|
-
const el = fieldRef.value;
|
|
802
|
-
if (clonedValue !== void 0) if (el.type === "checkbox") el.checked = clonedValue;
|
|
803
|
-
else el.value = clonedValue;
|
|
804
|
-
else if (el.type === "checkbox") el.checked = false;
|
|
805
|
-
else el.value = "";
|
|
806
|
-
}
|
|
809
|
+
if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
|
|
807
810
|
}
|
|
808
811
|
}
|
|
809
812
|
function watch$1(name) {
|
|
@@ -823,9 +826,7 @@ function useForm(options) {
|
|
|
823
826
|
return;
|
|
824
827
|
}
|
|
825
828
|
const fieldsToClean = Array.isArray(name) ? name : [name];
|
|
826
|
-
const
|
|
827
|
-
for (const field of fieldsToClean) for (const key of Object.keys(newErrors)) if (key === field || key.startsWith(`${field}.`)) delete newErrors[key];
|
|
828
|
-
ctx.errors.value = newErrors;
|
|
829
|
+
for (const field of fieldsToClean) clearFieldErrors(ctx.errors, field);
|
|
829
830
|
}
|
|
830
831
|
function setError(name, error) {
|
|
831
832
|
const newErrors = { ...ctx.errors.value };
|
|
@@ -836,15 +837,7 @@ function useForm(options) {
|
|
|
836
837
|
ctx.errors.value = newErrors;
|
|
837
838
|
}
|
|
838
839
|
function getValues(nameOrNames) {
|
|
839
|
-
|
|
840
|
-
const el = fieldRef.value;
|
|
841
|
-
if (el) {
|
|
842
|
-
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
843
|
-
const value = el.type === "checkbox" ? el.checked : el.value;
|
|
844
|
-
set(ctx.formData, name, value);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
840
|
+
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
848
841
|
if (nameOrNames === void 0) return { ...ctx.formData };
|
|
849
842
|
if (Array.isArray(nameOrNames)) {
|
|
850
843
|
const result = {};
|
package/dist/vuehookform.js
CHANGED
|
@@ -143,7 +143,7 @@ function createFormContext(options) {
|
|
|
143
143
|
options
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
|
-
function clearFieldErrors(errors, fieldPath) {
|
|
146
|
+
function clearFieldErrors$1(errors, fieldPath) {
|
|
147
147
|
const newErrors = { ...errors };
|
|
148
148
|
for (const key of Object.keys(newErrors)) if (key === fieldPath || key.startsWith(`${fieldPath}.`)) delete newErrors[key];
|
|
149
149
|
return newErrors;
|
|
@@ -220,7 +220,7 @@ function createValidation(ctx) {
|
|
|
220
220
|
ctx.errorDelayTimers.delete(fieldPath);
|
|
221
221
|
}
|
|
222
222
|
ctx.pendingErrors.delete(fieldPath);
|
|
223
|
-
return clearFieldErrors(ctx.errors.value, fieldPath);
|
|
223
|
+
return clearFieldErrors$1(ctx.errors.value, fieldPath);
|
|
224
224
|
}
|
|
225
225
|
function clearAllPendingErrors() {
|
|
226
226
|
for (const timer of ctx.errorDelayTimers.values()) clearTimeout(timer);
|
|
@@ -647,6 +647,45 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
647
647
|
}
|
|
648
648
|
return { fields };
|
|
649
649
|
}
|
|
650
|
+
function syncUncontrolledInputs(fieldRefs, fieldOptions, formData) {
|
|
651
|
+
for (const [name, fieldRef] of Array.from(fieldRefs.entries())) {
|
|
652
|
+
const el = fieldRef.value;
|
|
653
|
+
if (el) {
|
|
654
|
+
if (!fieldOptions.get(name)?.controlled) set(formData, name, el.type === "checkbox" ? el.checked : el.value);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
function updateDomElement(el, value) {
|
|
659
|
+
if (el.type === "checkbox") el.checked = value;
|
|
660
|
+
else el.value = value;
|
|
661
|
+
}
|
|
662
|
+
function markFieldDirty(dirtyFields, fieldName) {
|
|
663
|
+
dirtyFields.value = {
|
|
664
|
+
...dirtyFields.value,
|
|
665
|
+
[fieldName]: true
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function markFieldTouched(touchedFields, fieldName) {
|
|
669
|
+
touchedFields.value = {
|
|
670
|
+
...touchedFields.value,
|
|
671
|
+
[fieldName]: true
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function clearFieldDirty(dirtyFields, fieldName) {
|
|
675
|
+
const newDirty = { ...dirtyFields.value };
|
|
676
|
+
delete newDirty[fieldName];
|
|
677
|
+
dirtyFields.value = newDirty;
|
|
678
|
+
}
|
|
679
|
+
function clearFieldTouched(touchedFields, fieldName) {
|
|
680
|
+
const newTouched = { ...touchedFields.value };
|
|
681
|
+
delete newTouched[fieldName];
|
|
682
|
+
touchedFields.value = newTouched;
|
|
683
|
+
}
|
|
684
|
+
function clearFieldErrors(errors, fieldName) {
|
|
685
|
+
const newErrors = { ...errors.value };
|
|
686
|
+
for (const key of Object.keys(newErrors)) if (key === fieldName || key.startsWith(`${fieldName}.`)) delete newErrors[key];
|
|
687
|
+
errors.value = newErrors;
|
|
688
|
+
}
|
|
650
689
|
function useForm(options) {
|
|
651
690
|
const ctx = createFormContext(options);
|
|
652
691
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
@@ -692,15 +731,7 @@ function useForm(options) {
|
|
|
692
731
|
ctx.submitCount.value++;
|
|
693
732
|
ctx.isSubmitSuccessful.value = false;
|
|
694
733
|
try {
|
|
695
|
-
|
|
696
|
-
const el = fieldRef.value;
|
|
697
|
-
if (el) {
|
|
698
|
-
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
699
|
-
const value = el.type === "checkbox" ? el.checked : el.value;
|
|
700
|
-
set(ctx.formData, name, value);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
734
|
+
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
704
735
|
if (await validate()) {
|
|
705
736
|
await onValid(ctx.formData);
|
|
706
737
|
ctx.isSubmitSuccessful.value = true;
|
|
@@ -718,21 +749,11 @@ function useForm(options) {
|
|
|
718
749
|
}
|
|
719
750
|
function setValue(name, value, setValueOptions) {
|
|
720
751
|
set(ctx.formData, name, value);
|
|
721
|
-
if (setValueOptions?.shouldDirty !== false) ctx.dirtyFields
|
|
722
|
-
|
|
723
|
-
[name]: true
|
|
724
|
-
};
|
|
725
|
-
if (setValueOptions?.shouldTouch) ctx.touchedFields.value = {
|
|
726
|
-
...ctx.touchedFields.value,
|
|
727
|
-
[name]: true
|
|
728
|
-
};
|
|
752
|
+
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
|
|
753
|
+
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
|
|
729
754
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
730
755
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
731
|
-
if (fieldRef?.value)
|
|
732
|
-
const el = fieldRef.value;
|
|
733
|
-
if (el.type === "checkbox") el.checked = value;
|
|
734
|
-
else el.value = value;
|
|
735
|
-
}
|
|
756
|
+
if (fieldRef?.value) updateDomElement(fieldRef.value, value);
|
|
736
757
|
}
|
|
737
758
|
if (setValueOptions?.shouldValidate) validate(name);
|
|
738
759
|
}
|
|
@@ -756,7 +777,7 @@ function useForm(options) {
|
|
|
756
777
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
757
778
|
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
758
779
|
ctx.fieldArrays.clear();
|
|
759
|
-
for (const [name, fieldRef] of ctx.fieldRefs.entries()) {
|
|
780
|
+
for (const [name, fieldRef] of Array.from(ctx.fieldRefs.entries())) {
|
|
760
781
|
const el = fieldRef.value;
|
|
761
782
|
if (el) {
|
|
762
783
|
const value = get(newValues, name);
|
|
@@ -779,30 +800,12 @@ function useForm(options) {
|
|
|
779
800
|
else set(ctx.defaultValues, name, defaultValue);
|
|
780
801
|
const clonedValue = defaultValue !== void 0 ? JSON.parse(JSON.stringify(defaultValue)) : void 0;
|
|
781
802
|
set(ctx.formData, name, clonedValue);
|
|
782
|
-
if (!opts.keepError)
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
ctx.errors.value = newErrors;
|
|
786
|
-
}
|
|
787
|
-
if (!opts.keepDirty) {
|
|
788
|
-
const newDirty = { ...ctx.dirtyFields.value };
|
|
789
|
-
delete newDirty[name];
|
|
790
|
-
ctx.dirtyFields.value = newDirty;
|
|
791
|
-
}
|
|
792
|
-
if (!opts.keepTouched) {
|
|
793
|
-
const newTouched = { ...ctx.touchedFields.value };
|
|
794
|
-
delete newTouched[name];
|
|
795
|
-
ctx.touchedFields.value = newTouched;
|
|
796
|
-
}
|
|
803
|
+
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
804
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
805
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
797
806
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
798
807
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
799
|
-
if (fieldRef?.value)
|
|
800
|
-
const el = fieldRef.value;
|
|
801
|
-
if (clonedValue !== void 0) if (el.type === "checkbox") el.checked = clonedValue;
|
|
802
|
-
else el.value = clonedValue;
|
|
803
|
-
else if (el.type === "checkbox") el.checked = false;
|
|
804
|
-
else el.value = "";
|
|
805
|
-
}
|
|
808
|
+
if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
|
|
806
809
|
}
|
|
807
810
|
}
|
|
808
811
|
function watch$1(name) {
|
|
@@ -822,9 +825,7 @@ function useForm(options) {
|
|
|
822
825
|
return;
|
|
823
826
|
}
|
|
824
827
|
const fieldsToClean = Array.isArray(name) ? name : [name];
|
|
825
|
-
const
|
|
826
|
-
for (const field of fieldsToClean) for (const key of Object.keys(newErrors)) if (key === field || key.startsWith(`${field}.`)) delete newErrors[key];
|
|
827
|
-
ctx.errors.value = newErrors;
|
|
828
|
+
for (const field of fieldsToClean) clearFieldErrors(ctx.errors, field);
|
|
828
829
|
}
|
|
829
830
|
function setError(name, error) {
|
|
830
831
|
const newErrors = { ...ctx.errors.value };
|
|
@@ -835,15 +836,7 @@ function useForm(options) {
|
|
|
835
836
|
ctx.errors.value = newErrors;
|
|
836
837
|
}
|
|
837
838
|
function getValues(nameOrNames) {
|
|
838
|
-
|
|
839
|
-
const el = fieldRef.value;
|
|
840
|
-
if (el) {
|
|
841
|
-
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
842
|
-
const value = el.type === "checkbox" ? el.checked : el.value;
|
|
843
|
-
set(ctx.formData, name, value);
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
839
|
+
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
847
840
|
if (nameOrNames === void 0) return { ...ctx.formData };
|
|
848
841
|
if (Array.isArray(nameOrNames)) {
|
|
849
842
|
const result = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vuehookform/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "TypeScript-first form library for Vue 3, inspired by React Hook Form. Form-level state management with Zod validation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/vuehookform.cjs",
|
|
@@ -23,14 +23,15 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"dev": "vite",
|
|
25
25
|
"build": "run-p type-check \"build-only {@}\" --",
|
|
26
|
-
"build:lib": "
|
|
26
|
+
"build:lib": "run-p type-check build-lib-only",
|
|
27
|
+
"build-lib-only": "vite build --config vite.config.lib.ts",
|
|
27
28
|
"preview": "vite preview",
|
|
28
29
|
"build-only": "vite build",
|
|
29
30
|
"type-check": "vue-tsc --build",
|
|
30
31
|
"lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
|
|
31
32
|
"lint:eslint": "eslint . --fix --cache",
|
|
32
33
|
"lint": "run-s lint:*",
|
|
33
|
-
"format": "prettier --write --experimental-cli src/",
|
|
34
|
+
"format": "prettier --write --experimental-cli src/ tests/",
|
|
34
35
|
"test": "vitest",
|
|
35
36
|
"test:run": "vitest run",
|
|
36
37
|
"test:coverage": "vitest run --coverage",
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"zod": "^3.0.0 || ^4.0.0"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
71
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
70
72
|
"@prettier/plugin-oxc": "^0.0.5",
|
|
71
73
|
"@tsconfig/node24": "^24.0.3",
|
|
72
74
|
"@types/node": "^24.10.1",
|
|
@@ -75,7 +77,6 @@
|
|
|
75
77
|
"@vue/eslint-config-prettier": "^10.2.0",
|
|
76
78
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
77
79
|
"@vue/test-utils": "^2.4.6",
|
|
78
|
-
"@vue/tsconfig": "^0.8.1",
|
|
79
80
|
"eslint": "^9.39.1",
|
|
80
81
|
"eslint-plugin-oxlint": "~1.29.0",
|
|
81
82
|
"eslint-plugin-vue": "~10.5.1",
|
|
@@ -93,10 +94,7 @@
|
|
|
93
94
|
"vue": "^3.5.25",
|
|
94
95
|
"vue-router": "^4.6.3",
|
|
95
96
|
"vue-tsc": "^3.1.5",
|
|
96
|
-
"zod": "^4.2.1"
|
|
97
|
-
},
|
|
98
|
-
"dependencies": {
|
|
99
|
-
"@tailwindcss/vite": "^4.1.18",
|
|
97
|
+
"zod": "^4.2.1",
|
|
100
98
|
"material-icons": "^1.13.14",
|
|
101
99
|
"material-symbols": "^0.40.2",
|
|
102
100
|
"tailwindcss": "^4.1.18"
|