@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.
- package/README.md +3 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.mjs +135 -9
- 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/validateField.d.ts +1 -1
- package/dist/logic/validateField.d.ts.map +1 -1
- package/dist/react-server.esm.mjs +126 -9
- package/dist/react-server.esm.mjs.map +1 -1
- package/dist/types/form.d.ts +7 -0
- package/dist/types/form.d.ts.map +1 -1
- package/dist/useForm.d.ts.map +1 -1
- package/dist/useFormState.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.esm.mjs
CHANGED
|
@@ -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,
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
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]);
|