@bombillazo/rhf-plus 7.62.0-plus.6 → 7.63.0-plus.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/LICENSE +1 -0
- package/README.md +41 -63
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.mjs +138 -36
- package/dist/index.esm.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/logic/createFormControl.d.ts.map +1 -1
- package/dist/logic/getDirtyFields.d.ts +3 -2
- package/dist/logic/getDirtyFields.d.ts.map +1 -1
- package/dist/logic/getValidateError.d.ts.map +1 -1
- package/dist/logic/validateField.d.ts +1 -1
- package/dist/logic/validateField.d.ts.map +1 -1
- package/dist/react-server.esm.mjs +127 -36
- package/dist/react-server.esm.mjs.map +1 -1
- package/dist/types/form.d.ts +17 -3
- package/dist/types/form.d.ts.map +1 -1
- package/dist/types/resolvers.d.ts +2 -2
- package/dist/types/resolvers.d.ts.map +1 -1
- package/dist/useController.d.ts.map +1 -1
- package/dist/useForm.d.ts.map +1 -1
- package/dist/utils/extractFormValues.d.ts +2 -0
- package/dist/utils/extractFormValues.d.ts.map +1 -0
- package/package.json +34 -36
package/dist/index.esm.mjs
CHANGED
|
@@ -448,6 +448,7 @@ function useController(props) {
|
|
|
448
448
|
});
|
|
449
449
|
const _props = React.useRef(props);
|
|
450
450
|
const _previousRules = React.useRef(props.rules);
|
|
451
|
+
const _previousNameRef = React.useRef(undefined);
|
|
451
452
|
const _registerProps = React.useRef(control.register(name, {
|
|
452
453
|
...props.rules,
|
|
453
454
|
value,
|
|
@@ -567,6 +568,10 @@ function useController(props) {
|
|
|
567
568
|
]);
|
|
568
569
|
React.useEffect(() => {
|
|
569
570
|
const _shouldUnregisterField = control._options.shouldUnregister || shouldUnregister;
|
|
571
|
+
const previousName = _previousNameRef.current;
|
|
572
|
+
if (previousName && previousName !== name && !isArrayField) {
|
|
573
|
+
control.unregister(previousName);
|
|
574
|
+
}
|
|
570
575
|
control.register(name, {
|
|
571
576
|
..._props.current.rules,
|
|
572
577
|
...(isBoolean(_props.current.disabled)
|
|
@@ -588,6 +593,7 @@ function useController(props) {
|
|
|
588
593
|
}
|
|
589
594
|
}
|
|
590
595
|
!isArrayField && control.register(name);
|
|
596
|
+
_previousNameRef.current = name;
|
|
591
597
|
return () => {
|
|
592
598
|
(isArrayField
|
|
593
599
|
? _shouldUnregisterField && !control._state.action
|
|
@@ -843,6 +849,26 @@ function deepMerge(target, source) {
|
|
|
843
849
|
return target;
|
|
844
850
|
}
|
|
845
851
|
|
|
852
|
+
function extractFormValues(fieldsState, formValues) {
|
|
853
|
+
const values = {};
|
|
854
|
+
for (const key in fieldsState) {
|
|
855
|
+
if (fieldsState.hasOwnProperty(key)) {
|
|
856
|
+
const fieldState = fieldsState[key];
|
|
857
|
+
const fieldValue = formValues[key];
|
|
858
|
+
if (fieldState && isObject(fieldState) && fieldValue) {
|
|
859
|
+
const nestedFieldsState = extractFormValues(fieldState, fieldValue);
|
|
860
|
+
if (isObject(nestedFieldsState)) {
|
|
861
|
+
values[key] = nestedFieldsState;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else if (fieldsState[key]) {
|
|
865
|
+
values[key] = fieldValue;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return values;
|
|
870
|
+
}
|
|
871
|
+
|
|
846
872
|
var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
|
|
847
873
|
|
|
848
874
|
var isFileInput = (element) => element.type === 'file';
|
|
@@ -924,46 +950,40 @@ var objectHasFunction = (data) => {
|
|
|
924
950
|
return false;
|
|
925
951
|
};
|
|
926
952
|
|
|
953
|
+
function isTraversable(value) {
|
|
954
|
+
return Array.isArray(value) || (isObject(value) && !objectHasFunction(value));
|
|
955
|
+
}
|
|
927
956
|
function markFieldsDirty(data, fields = {}) {
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
}
|
|
936
|
-
else if (!isNullOrUndefined(data[key])) {
|
|
937
|
-
fields[key] = true;
|
|
938
|
-
}
|
|
957
|
+
for (const key in data) {
|
|
958
|
+
if (isTraversable(data[key])) {
|
|
959
|
+
fields[key] = Array.isArray(data[key]) ? [] : {};
|
|
960
|
+
markFieldsDirty(data[key], fields[key]);
|
|
961
|
+
}
|
|
962
|
+
else if (!isNullOrUndefined(data[key])) {
|
|
963
|
+
fields[key] = true;
|
|
939
964
|
}
|
|
940
965
|
}
|
|
941
966
|
return fields;
|
|
942
967
|
}
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
dirtyFieldsFromValues[key] = Array.isArray(data[key])
|
|
952
|
-
? markFieldsDirty(data[key], [])
|
|
953
|
-
: { ...markFieldsDirty(data[key]) };
|
|
954
|
-
}
|
|
955
|
-
else {
|
|
956
|
-
getDirtyFieldsFromDefaultValues(data[key], isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
|
|
957
|
-
}
|
|
968
|
+
function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
|
|
969
|
+
if (!dirtyFieldsFromValues) {
|
|
970
|
+
dirtyFieldsFromValues = markFieldsDirty(formValues);
|
|
971
|
+
}
|
|
972
|
+
for (const key in data) {
|
|
973
|
+
if (isTraversable(data[key])) {
|
|
974
|
+
if (isUndefined(formValues) || isPrimitive(dirtyFieldsFromValues[key])) {
|
|
975
|
+
dirtyFieldsFromValues[key] = markFieldsDirty(data[key], Array.isArray(data[key]) ? [] : {});
|
|
958
976
|
}
|
|
959
977
|
else {
|
|
960
|
-
|
|
978
|
+
getDirtyFields(data[key], isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
|
|
961
979
|
}
|
|
962
980
|
}
|
|
981
|
+
else {
|
|
982
|
+
dirtyFieldsFromValues[key] = !deepEqual(data[key], formValues[key]);
|
|
983
|
+
}
|
|
963
984
|
}
|
|
964
985
|
return dirtyFieldsFromValues;
|
|
965
986
|
}
|
|
966
|
-
var getDirtyFields = (defaultValues, formValues) => getDirtyFieldsFromDefaultValues(defaultValues, formValues, markFieldsDirty(formValues));
|
|
967
987
|
|
|
968
988
|
const defaultResult = {
|
|
969
989
|
value: false,
|
|
@@ -1236,10 +1256,10 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
|
|
|
1236
1256
|
message: '',
|
|
1237
1257
|
};
|
|
1238
1258
|
|
|
1239
|
-
var validateField = async (field,
|
|
1259
|
+
var validateField = async (field, skippedFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
|
|
1240
1260
|
const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
|
|
1241
1261
|
const inputValue = get(formValues, name);
|
|
1242
|
-
if (!mount ||
|
|
1262
|
+
if (!mount || skippedFieldNames.has(name)) {
|
|
1243
1263
|
return {};
|
|
1244
1264
|
}
|
|
1245
1265
|
const inputRef = refs ? refs[0] : ref;
|
|
@@ -1428,6 +1448,7 @@ const defaultOptions = {
|
|
|
1428
1448
|
mode: VALIDATION_MODE.onSubmit,
|
|
1429
1449
|
reValidateMode: VALIDATION_MODE.onChange,
|
|
1430
1450
|
shouldFocusError: true,
|
|
1451
|
+
shouldSkipReadOnlyValidation: false,
|
|
1431
1452
|
};
|
|
1432
1453
|
function createFormControl(props = {}) {
|
|
1433
1454
|
let _options = {
|
|
@@ -1472,6 +1493,7 @@ function createFormControl(props = {}) {
|
|
|
1472
1493
|
let _names = {
|
|
1473
1494
|
mount: new Set(),
|
|
1474
1495
|
disabled: new Set(),
|
|
1496
|
+
readonly: new Set(),
|
|
1475
1497
|
unMount: new Set(),
|
|
1476
1498
|
array: new Set(),
|
|
1477
1499
|
watch: new Set(),
|
|
@@ -1737,11 +1759,16 @@ function createFormControl(props = {}) {
|
|
|
1737
1759
|
const isFieldArrayRoot = _names.array.has(_f.name);
|
|
1738
1760
|
const isPromiseFunction = field._f && hasPromiseValidation(field._f);
|
|
1739
1761
|
if (isPromiseFunction && _proxyFormState.validatingFields) {
|
|
1740
|
-
_updateIsValidating([name], true);
|
|
1762
|
+
_updateIsValidating([_f.name], true);
|
|
1741
1763
|
}
|
|
1742
|
-
|
|
1764
|
+
// Combine disabled and readonly field names for validation skipping
|
|
1765
|
+
const skipValidationFields = new Set([
|
|
1766
|
+
..._names.disabled,
|
|
1767
|
+
..._names.readonly,
|
|
1768
|
+
]);
|
|
1769
|
+
const fieldError = await validateField(field, skipValidationFields, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
|
|
1743
1770
|
if (isPromiseFunction && _proxyFormState.validatingFields) {
|
|
1744
|
-
_updateIsValidating([name]);
|
|
1771
|
+
_updateIsValidating([_f.name]);
|
|
1745
1772
|
}
|
|
1746
1773
|
if (fieldError[_f.name]) {
|
|
1747
1774
|
context.valid = false;
|
|
@@ -1945,6 +1972,36 @@ function createFormControl(props = {}) {
|
|
|
1945
1972
|
}
|
|
1946
1973
|
return;
|
|
1947
1974
|
}
|
|
1975
|
+
// Check if field is readonly and should skip validation (only when flag is enabled)
|
|
1976
|
+
if (_options.shouldSkipReadOnlyValidation && target && target.readOnly) {
|
|
1977
|
+
// Add to readonly fields set for validation skipping
|
|
1978
|
+
_names.readonly.add(name);
|
|
1979
|
+
// For readonly fields, we still want to update the form values
|
|
1980
|
+
// but skip validation (similar to disabled fields behavior)
|
|
1981
|
+
const fieldValue = target.type
|
|
1982
|
+
? getFieldValue(field._f)
|
|
1983
|
+
: getEventValue(event);
|
|
1984
|
+
const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
|
|
1985
|
+
const isFocusEvent = event.type === EVENTS.FOCUS || event.type === EVENTS.FOCUS_IN;
|
|
1986
|
+
const watched = isWatched(name, _names, isBlurEvent || isFocusEvent);
|
|
1987
|
+
// Update form values but skip validation and error handling
|
|
1988
|
+
set(_formValues, name, fieldValue);
|
|
1989
|
+
// Update touch and dirty state
|
|
1990
|
+
const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent, isFocusEvent, !isBlurEvent);
|
|
1991
|
+
const shouldRender = !isEmptyObject(fieldState) || watched;
|
|
1992
|
+
!isBlurEvent &&
|
|
1993
|
+
_subjects.state.next({
|
|
1994
|
+
name,
|
|
1995
|
+
type: event.type,
|
|
1996
|
+
values: cloneObject(_formValues),
|
|
1997
|
+
});
|
|
1998
|
+
return (shouldRender &&
|
|
1999
|
+
_subjects.state.next({ name, ...(watched ? {} : fieldState) }));
|
|
2000
|
+
}
|
|
2001
|
+
else if (_options.shouldSkipReadOnlyValidation) {
|
|
2002
|
+
// Remove from readonly fields set if not readonly anymore (only when flag is enabled)
|
|
2003
|
+
_names.readonly.delete(name);
|
|
2004
|
+
}
|
|
1948
2005
|
let error;
|
|
1949
2006
|
let isValid;
|
|
1950
2007
|
const fieldValue = target.type
|
|
@@ -2004,7 +2061,12 @@ function createFormControl(props = {}) {
|
|
|
2004
2061
|
}
|
|
2005
2062
|
else {
|
|
2006
2063
|
_updateIsValidating([name], true);
|
|
2007
|
-
|
|
2064
|
+
// Combine disabled and readonly field names for validation skipping
|
|
2065
|
+
const skipValidationFields = new Set([
|
|
2066
|
+
..._names.disabled,
|
|
2067
|
+
..._names.readonly,
|
|
2068
|
+
]);
|
|
2069
|
+
error = (await validateField(field, skipValidationFields, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation, false))[name];
|
|
2008
2070
|
_updateIsValidating([name]);
|
|
2009
2071
|
_updateIsFieldValueUpdated(fieldValue);
|
|
2010
2072
|
if (isFieldValueUpdated) {
|
|
@@ -2019,6 +2081,7 @@ function createFormControl(props = {}) {
|
|
|
2019
2081
|
}
|
|
2020
2082
|
if (isFieldValueUpdated) {
|
|
2021
2083
|
field._f.deps &&
|
|
2084
|
+
(!Array.isArray(field._f.deps) || field._f.deps.length > 0) &&
|
|
2022
2085
|
trigger(field._f.deps);
|
|
2023
2086
|
shouldRenderByError(name, isValid, error, fieldState);
|
|
2024
2087
|
}
|
|
@@ -2066,10 +2129,13 @@ function createFormControl(props = {}) {
|
|
|
2066
2129
|
iterateFieldsByAction(_fields, _focusInput, name ? fieldNames : _names.mount);
|
|
2067
2130
|
return validationResult;
|
|
2068
2131
|
};
|
|
2069
|
-
const getValues = (fieldNames) => {
|
|
2070
|
-
|
|
2132
|
+
const getValues = (fieldNames, config) => {
|
|
2133
|
+
let values = {
|
|
2071
2134
|
...(_state.mount ? _formValues : _defaultValues),
|
|
2072
2135
|
};
|
|
2136
|
+
if (config) {
|
|
2137
|
+
values = extractFormValues(config.dirtyFields ? _formState.dirtyFields : _formState.touchedFields, values);
|
|
2138
|
+
}
|
|
2073
2139
|
return isUndefined(fieldNames)
|
|
2074
2140
|
? values
|
|
2075
2141
|
: isString(fieldNames)
|
|
@@ -2255,6 +2321,13 @@ function createFormControl(props = {}) {
|
|
|
2255
2321
|
},
|
|
2256
2322
|
});
|
|
2257
2323
|
updateValidAndValue(name, false, undefined, fieldRef);
|
|
2324
|
+
// Check if field is readonly and should skip validation (only when flag is enabled)
|
|
2325
|
+
if (_options.shouldSkipReadOnlyValidation &&
|
|
2326
|
+
fieldRef &&
|
|
2327
|
+
'readOnly' in fieldRef &&
|
|
2328
|
+
fieldRef.readOnly) {
|
|
2329
|
+
_names.readonly.add(name);
|
|
2330
|
+
}
|
|
2258
2331
|
}
|
|
2259
2332
|
else {
|
|
2260
2333
|
field = get(_fields, name, {});
|
|
@@ -2455,6 +2528,7 @@ function createFormControl(props = {}) {
|
|
|
2455
2528
|
unMount: new Set(),
|
|
2456
2529
|
array: new Set(),
|
|
2457
2530
|
disabled: new Set(),
|
|
2531
|
+
readonly: new Set(),
|
|
2458
2532
|
watch: new Set(),
|
|
2459
2533
|
watchAll: false,
|
|
2460
2534
|
focus: '',
|
|
@@ -2554,6 +2628,28 @@ function createFormControl(props = {}) {
|
|
|
2554
2628
|
});
|
|
2555
2629
|
}
|
|
2556
2630
|
};
|
|
2631
|
+
const _updateReadonlyFieldTracking = () => {
|
|
2632
|
+
// Re-evaluate all registered fields and update readonly tracking
|
|
2633
|
+
// based on current shouldSkipReadOnlyValidation flag and field readonly state
|
|
2634
|
+
Object.keys(_fields).forEach((fieldName) => {
|
|
2635
|
+
const field = get(_fields, fieldName);
|
|
2636
|
+
if (field && field._f) {
|
|
2637
|
+
// Get the actual DOM element reference
|
|
2638
|
+
const fieldRef = field._f.refs ? field._f.refs[0] : field._f.ref;
|
|
2639
|
+
if (fieldRef && 'readOnly' in fieldRef) {
|
|
2640
|
+
const isFieldReadonly = Boolean(fieldRef.readOnly);
|
|
2641
|
+
const shouldTrackAsReadonly = _options.shouldSkipReadOnlyValidation && isFieldReadonly;
|
|
2642
|
+
// Update readonly tracking set
|
|
2643
|
+
if (shouldTrackAsReadonly) {
|
|
2644
|
+
_names.readonly.add(fieldName);
|
|
2645
|
+
}
|
|
2646
|
+
else {
|
|
2647
|
+
_names.readonly.delete(fieldName);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
});
|
|
2652
|
+
};
|
|
2557
2653
|
const setMetadata = (metadata) => {
|
|
2558
2654
|
let _metadata;
|
|
2559
2655
|
if (!metadata) {
|
|
@@ -2598,6 +2694,7 @@ function createFormControl(props = {}) {
|
|
|
2598
2694
|
_removeUnmounted,
|
|
2599
2695
|
_disableForm,
|
|
2600
2696
|
_updateIsLoading,
|
|
2697
|
+
_updateReadonlyFieldTracking,
|
|
2601
2698
|
_subjects,
|
|
2602
2699
|
_proxyFormState,
|
|
2603
2700
|
get _fields() {
|
|
@@ -3060,6 +3157,11 @@ function useForm(props = {}) {
|
|
|
3060
3157
|
return sub;
|
|
3061
3158
|
}, [control]);
|
|
3062
3159
|
React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
|
|
3160
|
+
// Handle shouldSkipReadOnlyValidation flag changes
|
|
3161
|
+
React.useEffect(() => {
|
|
3162
|
+
// Re-evaluate readonly field tracking when the flag changes
|
|
3163
|
+
control._updateReadonlyFieldTracking();
|
|
3164
|
+
}, [control, props.shouldSkipReadOnlyValidation]);
|
|
3063
3165
|
React.useEffect(() => {
|
|
3064
3166
|
control._updateIsLoading(props.isLoading);
|
|
3065
3167
|
}, [control, props.isLoading]);
|