@bombillazo/rhf-plus 7.62.0-plus.5 → 7.62.0-plus.7

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.
@@ -270,6 +270,8 @@ function useFormState(props) {
270
270
  const [formState, updateFormState] = React.useState(control._formState);
271
271
  const _localProxyFormState = React.useRef({
272
272
  isDirty: false,
273
+ isDirtySinceSubmit: false,
274
+ hasBeenSubmitted: false,
273
275
  isLoading: false,
274
276
  dirtyFields: false,
275
277
  touchedFields: false,
@@ -1234,10 +1236,10 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
1234
1236
  message: '',
1235
1237
  };
1236
1238
 
1237
- var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
1239
+ var validateField = async (field, skippedFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
1238
1240
  const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
1239
1241
  const inputValue = get(formValues, name);
1240
- if (!mount || disabledFieldNames.has(name)) {
1242
+ if (!mount || skippedFieldNames.has(name)) {
1241
1243
  return {};
1242
1244
  }
1243
1245
  const inputRef = refs ? refs[0] : ref;
@@ -1426,6 +1428,7 @@ const defaultOptions = {
1426
1428
  mode: VALIDATION_MODE.onSubmit,
1427
1429
  reValidateMode: VALIDATION_MODE.onChange,
1428
1430
  shouldFocusError: true,
1431
+ shouldSkipReadOnlyValidation: false,
1429
1432
  };
1430
1433
  function createFormControl(props = {}) {
1431
1434
  let _options = {
@@ -1436,6 +1439,8 @@ function createFormControl(props = {}) {
1436
1439
  let _formState = {
1437
1440
  submitCount: 0,
1438
1441
  isDirty: false,
1442
+ isDirtySinceSubmit: false,
1443
+ hasBeenSubmitted: false,
1439
1444
  isReady: false,
1440
1445
  isLoading: _internalLoading,
1441
1446
  isValidating: false,
@@ -1468,6 +1473,7 @@ function createFormControl(props = {}) {
1468
1473
  let _names = {
1469
1474
  mount: new Set(),
1470
1475
  disabled: new Set(),
1476
+ readonly: new Set(),
1471
1477
  unMount: new Set(),
1472
1478
  array: new Set(),
1473
1479
  watch: new Set(),
@@ -1476,6 +1482,8 @@ function createFormControl(props = {}) {
1476
1482
  let timer = 0;
1477
1483
  const _proxyFormState = {
1478
1484
  isDirty: false,
1485
+ isDirtySinceSubmit: false,
1486
+ hasBeenSubmitted: false,
1479
1487
  dirtyFields: false,
1480
1488
  validatingFields: false,
1481
1489
  touchedFields: false,
@@ -1493,6 +1501,8 @@ function createFormControl(props = {}) {
1493
1501
  };
1494
1502
  const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
1495
1503
  const id = createId(props.id);
1504
+ // Track if form was ever submitted (persists through resets)
1505
+ let _hasBeenSubmitted = false;
1496
1506
  const debounce = (callback) => (wait) => {
1497
1507
  clearTimeout(timer);
1498
1508
  timer = setTimeout(callback, wait);
@@ -1616,6 +1626,20 @@ function createFormControl(props = {}) {
1616
1626
  _proxySubscribeFormState.dirtyFields) &&
1617
1627
  isPreviousDirty !== !isCurrentFieldPristine);
1618
1628
  }
1629
+ // Set isDirtySinceSubmit to true if form was ever submitted and a field value is being changed
1630
+ // For change events (not blur/focus), always set if form was ever submitted
1631
+ // shouldDirty is true for onChange events, false for blur
1632
+ if ((_formState.isSubmitted || _hasBeenSubmitted) &&
1633
+ !_formState.isDirtySinceSubmit &&
1634
+ !isBlurEvent &&
1635
+ !isFocusEvent &&
1636
+ shouldDirty) {
1637
+ _formState.isDirtySinceSubmit = output.isDirtySinceSubmit = true;
1638
+ shouldUpdateField =
1639
+ shouldUpdateField ||
1640
+ !!(_proxyFormState.isDirtySinceSubmit ||
1641
+ _proxySubscribeFormState.isDirtySinceSubmit);
1642
+ }
1619
1643
  if (isBlurEvent) {
1620
1644
  const isPreviousFieldTouched = get(_formState.touchedFields, name);
1621
1645
  if (!isPreviousFieldTouched) {
@@ -1717,7 +1741,12 @@ function createFormControl(props = {}) {
1717
1741
  if (isPromiseFunction && _proxyFormState.validatingFields) {
1718
1742
  _updateIsValidating([name], true);
1719
1743
  }
1720
- const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1744
+ // Combine disabled and readonly field names for validation skipping
1745
+ const skipValidationFields = new Set([
1746
+ ..._names.disabled,
1747
+ ..._names.readonly,
1748
+ ]);
1749
+ const fieldError = await validateField(field, skipValidationFields, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1721
1750
  if (isPromiseFunction && _proxyFormState.validatingFields) {
1722
1751
  _updateIsValidating([name]);
1723
1752
  }
@@ -1811,9 +1840,15 @@ function createFormControl(props = {}) {
1811
1840
  }
1812
1841
  }
1813
1842
  }
1814
- (options.shouldDirty || options.shouldTouch) &&
1843
+ const isSubmittedAndDirty = (_formState.isSubmitted || _hasBeenSubmitted) &&
1844
+ !deepEqual(get(_defaultValues, name), fieldValue);
1845
+ // If the form was submitted, track value changes for isDirtySinceSubmit
1846
+ // only when the value actually differs from the default value,
1847
+ // even if shouldDirty is not explicitly set
1848
+ const shouldTrackChange = options.shouldDirty || options.shouldTouch || isSubmittedAndDirty;
1849
+ shouldTrackChange &&
1815
1850
  updateTouchAndDirty(name, fieldValue, options.shouldTouch, false, // isFocusEvent - not applicable for setValue
1816
- options.shouldDirty, true);
1851
+ options.shouldDirty || isSubmittedAndDirty, true);
1817
1852
  options.shouldValidate && trigger(name);
1818
1853
  };
1819
1854
  const setValues = (name, value, options) => {
@@ -1845,12 +1880,25 @@ function createFormControl(props = {}) {
1845
1880
  if ((_proxyFormState.isDirty ||
1846
1881
  _proxyFormState.dirtyFields ||
1847
1882
  _proxySubscribeFormState.isDirty ||
1848
- _proxySubscribeFormState.dirtyFields) &&
1883
+ _proxySubscribeFormState.dirtyFields ||
1884
+ _proxyFormState.isDirtySinceSubmit ||
1885
+ _proxySubscribeFormState.isDirtySinceSubmit) &&
1849
1886
  options.shouldDirty) {
1850
1887
  _subjects.state.next({
1851
1888
  name,
1852
1889
  dirtyFields: getDirtyFields(_defaultValues, _formValues),
1853
1890
  isDirty: _getDirty(name, cloneValue),
1891
+ ...((_formState.isSubmitted || _hasBeenSubmitted) &&
1892
+ !_formState.isDirtySinceSubmit
1893
+ ? { isDirtySinceSubmit: true }
1894
+ : {}),
1895
+ });
1896
+ }
1897
+ else if ((_formState.isSubmitted || _hasBeenSubmitted) &&
1898
+ !_formState.isDirtySinceSubmit) {
1899
+ _subjects.state.next({
1900
+ name,
1901
+ isDirtySinceSubmit: true,
1854
1902
  });
1855
1903
  }
1856
1904
  }
@@ -1904,6 +1952,36 @@ function createFormControl(props = {}) {
1904
1952
  }
1905
1953
  return;
1906
1954
  }
1955
+ // Check if field is readonly and should skip validation (only when flag is enabled)
1956
+ if (_options.shouldSkipReadOnlyValidation && target && target.readOnly) {
1957
+ // Add to readonly fields set for validation skipping
1958
+ _names.readonly.add(name);
1959
+ // For readonly fields, we still want to update the form values
1960
+ // but skip validation (similar to disabled fields behavior)
1961
+ const fieldValue = target.type
1962
+ ? getFieldValue(field._f)
1963
+ : getEventValue(event);
1964
+ const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1965
+ const isFocusEvent = event.type === EVENTS.FOCUS || event.type === EVENTS.FOCUS_IN;
1966
+ const watched = isWatched(name, _names, isBlurEvent || isFocusEvent);
1967
+ // Update form values but skip validation and error handling
1968
+ set(_formValues, name, fieldValue);
1969
+ // Update touch and dirty state
1970
+ const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent, isFocusEvent, !isBlurEvent);
1971
+ const shouldRender = !isEmptyObject(fieldState) || watched;
1972
+ !isBlurEvent &&
1973
+ _subjects.state.next({
1974
+ name,
1975
+ type: event.type,
1976
+ values: cloneObject(_formValues),
1977
+ });
1978
+ return (shouldRender &&
1979
+ _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
1980
+ }
1981
+ else if (_options.shouldSkipReadOnlyValidation) {
1982
+ // Remove from readonly fields set if not readonly anymore (only when flag is enabled)
1983
+ _names.readonly.delete(name);
1984
+ }
1907
1985
  let error;
1908
1986
  let isValid;
1909
1987
  const fieldValue = target.type
@@ -1927,7 +2005,7 @@ function createFormControl(props = {}) {
1927
2005
  else if (field._f.onChange) {
1928
2006
  field._f.onChange(event);
1929
2007
  }
1930
- const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent, isFocusEvent);
2008
+ const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent, isFocusEvent, !isBlurEvent);
1931
2009
  const shouldRender = !isEmptyObject(fieldState) || watched;
1932
2010
  !isBlurEvent &&
1933
2011
  _subjects.state.next({
@@ -1963,7 +2041,12 @@ function createFormControl(props = {}) {
1963
2041
  }
1964
2042
  else {
1965
2043
  _updateIsValidating([name], true);
1966
- error = (await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
2044
+ // Combine disabled and readonly field names for validation skipping
2045
+ const skipValidationFields = new Set([
2046
+ ..._names.disabled,
2047
+ ..._names.readonly,
2048
+ ]);
2049
+ error = (await validateField(field, skipValidationFields, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation, false))[name];
1967
2050
  _updateIsValidating([name]);
1968
2051
  _updateIsFieldValueUpdated(fieldValue);
1969
2052
  if (isFieldValueUpdated) {
@@ -2214,6 +2297,13 @@ function createFormControl(props = {}) {
2214
2297
  },
2215
2298
  });
2216
2299
  updateValidAndValue(name, false, undefined, fieldRef);
2300
+ // Check if field is readonly and should skip validation (only when flag is enabled)
2301
+ if (_options.shouldSkipReadOnlyValidation &&
2302
+ fieldRef &&
2303
+ 'readOnly' in fieldRef &&
2304
+ fieldRef.readOnly) {
2305
+ _names.readonly.add(name);
2306
+ }
2217
2307
  }
2218
2308
  else {
2219
2309
  field = get(_fields, name, {});
@@ -2311,11 +2401,14 @@ function createFormControl(props = {}) {
2311
2401
  _focusError();
2312
2402
  setTimeout(_focusError);
2313
2403
  }
2404
+ _hasBeenSubmitted = true; // Mark that form was submitted at least once
2314
2405
  _subjects.state.next({
2315
2406
  isSubmitted: true,
2316
2407
  isSubmitting: false,
2317
2408
  isSubmitSuccessful: isEmptyObject(_formState.errors) && !onValidError,
2318
2409
  submitCount: _formState.submitCount + 1,
2410
+ isDirtySinceSubmit: false,
2411
+ hasBeenSubmitted: _hasBeenSubmitted,
2319
2412
  errors: _formState.errors,
2320
2413
  });
2321
2414
  if (onValidError) {
@@ -2378,7 +2471,7 @@ function createFormControl(props = {}) {
2378
2471
  if (isHTMLElement(fieldReference)) {
2379
2472
  const form = fieldReference.closest('form');
2380
2473
  if (form) {
2381
- form.reset();
2474
+ HTMLFormElement.prototype.reset.call(form);
2382
2475
  break;
2383
2476
  }
2384
2477
  }
@@ -2411,6 +2504,7 @@ function createFormControl(props = {}) {
2411
2504
  unMount: new Set(),
2412
2505
  array: new Set(),
2413
2506
  disabled: new Set(),
2507
+ readonly: new Set(),
2414
2508
  watch: new Set(),
2415
2509
  watchAll: false,
2416
2510
  focus: '',
@@ -2430,6 +2524,8 @@ function createFormControl(props = {}) {
2430
2524
  ? _formState.isDirty
2431
2525
  : !!(keepStateOptions.keepDefaultValues &&
2432
2526
  !deepEqual(formValues, _defaultValues)),
2527
+ isDirtySinceSubmit: false,
2528
+ hasBeenSubmitted: _hasBeenSubmitted, // Persist the hasBeenSubmitted flag
2433
2529
  isSubmitted: keepStateOptions.keepIsSubmitted
2434
2530
  ? _formState.isSubmitted
2435
2531
  : false,
@@ -2508,6 +2604,28 @@ function createFormControl(props = {}) {
2508
2604
  });
2509
2605
  }
2510
2606
  };
2607
+ const _updateReadonlyFieldTracking = () => {
2608
+ // Re-evaluate all registered fields and update readonly tracking
2609
+ // based on current shouldSkipReadOnlyValidation flag and field readonly state
2610
+ Object.keys(_fields).forEach((fieldName) => {
2611
+ const field = get(_fields, fieldName);
2612
+ if (field && field._f) {
2613
+ // Get the actual DOM element reference
2614
+ const fieldRef = field._f.refs ? field._f.refs[0] : field._f.ref;
2615
+ if (fieldRef && 'readOnly' in fieldRef) {
2616
+ const isFieldReadonly = Boolean(fieldRef.readOnly);
2617
+ const shouldTrackAsReadonly = _options.shouldSkipReadOnlyValidation && isFieldReadonly;
2618
+ // Update readonly tracking set
2619
+ if (shouldTrackAsReadonly) {
2620
+ _names.readonly.add(fieldName);
2621
+ }
2622
+ else {
2623
+ _names.readonly.delete(fieldName);
2624
+ }
2625
+ }
2626
+ }
2627
+ });
2628
+ };
2511
2629
  const setMetadata = (metadata) => {
2512
2630
  let _metadata;
2513
2631
  if (!metadata) {
@@ -2552,6 +2670,7 @@ function createFormControl(props = {}) {
2552
2670
  _removeUnmounted,
2553
2671
  _disableForm,
2554
2672
  _updateIsLoading,
2673
+ _updateReadonlyFieldTracking,
2555
2674
  _subjects,
2556
2675
  _proxyFormState,
2557
2676
  get _fields() {
@@ -2955,6 +3074,8 @@ function useForm(props = {}) {
2955
3074
  const _values = React.useRef(undefined);
2956
3075
  const [formState, updateFormState] = React.useState({
2957
3076
  isDirty: false,
3077
+ isDirtySinceSubmit: false,
3078
+ hasBeenSubmitted: false,
2958
3079
  isValidating: false,
2959
3080
  isLoading: props.isLoading || isFunction(props.defaultValues),
2960
3081
  isSubmitted: false,
@@ -3012,6 +3133,11 @@ function useForm(props = {}) {
3012
3133
  return sub;
3013
3134
  }, [control]);
3014
3135
  React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
3136
+ // Handle shouldSkipReadOnlyValidation flag changes
3137
+ React.useEffect(() => {
3138
+ // Re-evaluate readonly field tracking when the flag changes
3139
+ control._updateReadonlyFieldTracking();
3140
+ }, [control, props.shouldSkipReadOnlyValidation]);
3015
3141
  React.useEffect(() => {
3016
3142
  control._updateIsLoading(props.isLoading);
3017
3143
  }, [control, props.isLoading]);