@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.
@@ -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 isParentNodeArray = Array.isArray(data);
929
- if (isObject(data) || isParentNodeArray) {
930
- for (const key in data) {
931
- if (Array.isArray(data[key]) ||
932
- (isObject(data[key]) && !objectHasFunction(data[key]))) {
933
- fields[key] = Array.isArray(data[key]) ? [] : {};
934
- markFieldsDirty(data[key], fields[key]);
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 getDirtyFieldsFromDefaultValues(data, formValues, dirtyFieldsFromValues) {
944
- const isParentNodeArray = Array.isArray(data);
945
- if (isObject(data) || isParentNodeArray) {
946
- for (const key in data) {
947
- if (Array.isArray(data[key]) ||
948
- (isObject(data[key]) && !objectHasFunction(data[key]))) {
949
- if (isUndefined(formValues) ||
950
- isPrimitive(dirtyFieldsFromValues[key])) {
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
- dirtyFieldsFromValues[key] = !deepEqual(data[key], formValues[key]);
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, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
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 || disabledFieldNames.has(name)) {
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
- const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
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
- error = (await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
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
- const values = {
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]);