hs-uix 1.4.0 → 1.5.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/README.md +20 -2
- package/common-components.d.ts +55 -0
- package/dist/common-components.js +260 -0
- package/dist/common-components.mjs +221 -0
- package/dist/datatable.js +74 -38
- package/dist/datatable.mjs +74 -38
- package/dist/form.js +204 -76
- package/dist/form.mjs +204 -76
- package/dist/index.js +486 -114
- package/dist/index.mjs +481 -114
- package/dist/utils.js +269 -0
- package/dist/utils.mjs +230 -0
- package/index.d.ts +21 -0
- package/package.json +14 -2
- package/utils.d.ts +59 -0
package/dist/form.mjs
CHANGED
|
@@ -671,9 +671,10 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
671
671
|
const inputDebounceRef = useRef(/* @__PURE__ */ new Map());
|
|
672
672
|
const rowKeyRef = useRef(/* @__PURE__ */ new WeakMap());
|
|
673
673
|
const rowKeyCounterRef = useRef(0);
|
|
674
|
+
const controlledBaselineLockedRef = useRef(false);
|
|
674
675
|
const initialSnapshot = useRef(null);
|
|
675
676
|
if (initialSnapshot.current === null) {
|
|
676
|
-
initialSnapshot.current = deepClone(computeInitialValues());
|
|
677
|
+
initialSnapshot.current = deepClone(values != null ? values : computeInitialValues());
|
|
677
678
|
}
|
|
678
679
|
const formValues = values != null ? values : internalValues;
|
|
679
680
|
const formErrors = controlledErrors != null ? controlledErrors : internalErrors;
|
|
@@ -685,6 +686,10 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
685
686
|
const draftValuesRef = useRef(null);
|
|
686
687
|
formValuesRef.current = formValues;
|
|
687
688
|
formErrorsRef.current = formErrors;
|
|
689
|
+
const syncDirtyBaseline = useCallback((nextValues) => {
|
|
690
|
+
initialSnapshot.current = deepClone(nextValues || {});
|
|
691
|
+
prevAutoSaveValues.current = deepClone(nextValues || {});
|
|
692
|
+
}, []);
|
|
688
693
|
const fieldByName = useMemo(() => {
|
|
689
694
|
const map = /* @__PURE__ */ new Map();
|
|
690
695
|
for (const field of fields) {
|
|
@@ -761,6 +766,11 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
761
766
|
if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
|
|
762
767
|
};
|
|
763
768
|
}, []);
|
|
769
|
+
useEffect(() => {
|
|
770
|
+
if (values == null) return;
|
|
771
|
+
if (controlledBaselineLockedRef.current) return;
|
|
772
|
+
syncDirtyBaseline(values);
|
|
773
|
+
}, [values, syncDirtyBaseline]);
|
|
764
774
|
const isDirty = useMemo(() => {
|
|
765
775
|
return !deepEqual(formValues, initialSnapshot.current);
|
|
766
776
|
}, [formValues]);
|
|
@@ -879,6 +889,50 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
879
889
|
},
|
|
880
890
|
[fieldTypes]
|
|
881
891
|
);
|
|
892
|
+
const setRepeaterSubFieldError = useCallback(
|
|
893
|
+
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
894
|
+
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
895
|
+
const merged = { ...formErrorsRef.current };
|
|
896
|
+
if (errorMessage) {
|
|
897
|
+
merged[key] = errorMessage;
|
|
898
|
+
} else {
|
|
899
|
+
delete merged[key];
|
|
900
|
+
}
|
|
901
|
+
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
902
|
+
const match = k.match(/\[(\d+)\]\./);
|
|
903
|
+
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
904
|
+
return { key: k, row };
|
|
905
|
+
}).sort((a, b) => a.row - b.row);
|
|
906
|
+
if (subErrors.length > 0) {
|
|
907
|
+
const first = subErrors[0];
|
|
908
|
+
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
909
|
+
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
910
|
+
delete merged[fieldName];
|
|
911
|
+
}
|
|
912
|
+
replaceErrors(merged);
|
|
913
|
+
},
|
|
914
|
+
[replaceErrors]
|
|
915
|
+
);
|
|
916
|
+
const expandValidationFields = useCallback(
|
|
917
|
+
(fieldSubset) => {
|
|
918
|
+
const toValidate = fieldSubset || visibleFields;
|
|
919
|
+
const expanded = [];
|
|
920
|
+
for (const field of toValidate) {
|
|
921
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
922
|
+
for (const item of field.items) {
|
|
923
|
+
for (const subField of field.fields(item)) {
|
|
924
|
+
if (subField.visible && !subField.visible(formValues)) continue;
|
|
925
|
+
expanded.push(subField);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
expanded.push(field);
|
|
931
|
+
}
|
|
932
|
+
return expanded;
|
|
933
|
+
},
|
|
934
|
+
[visibleFields, formValues]
|
|
935
|
+
);
|
|
882
936
|
const validateField = useCallback(
|
|
883
937
|
(name, value) => {
|
|
884
938
|
const field = fieldByName.get(name);
|
|
@@ -898,7 +952,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
898
952
|
);
|
|
899
953
|
const validateVisibleFields = useCallback(
|
|
900
954
|
(fieldSubset) => {
|
|
901
|
-
const toValidate = fieldSubset
|
|
955
|
+
const toValidate = expandValidationFields(fieldSubset);
|
|
902
956
|
const errors = {};
|
|
903
957
|
let hasErrors = false;
|
|
904
958
|
for (const field of toValidate) {
|
|
@@ -918,52 +972,54 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
918
972
|
}
|
|
919
973
|
return { errors, hasErrors };
|
|
920
974
|
},
|
|
921
|
-
[
|
|
975
|
+
[expandValidationFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
922
976
|
);
|
|
923
|
-
const
|
|
924
|
-
(
|
|
925
|
-
const field =
|
|
926
|
-
if (!field || field.type === "repeater") return null;
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
977
|
+
const runAsyncValidationTarget = useCallback(
|
|
978
|
+
(target) => {
|
|
979
|
+
const { validationKey, field, value, allValues, applyError } = target || {};
|
|
980
|
+
if (!field || !validationKey || field.type === "repeater" || field.type === "fieldGroup") return null;
|
|
981
|
+
const syncError = runValidators(value, field, allValues, fieldTypes, {
|
|
982
|
+
includeCustomValidators: false,
|
|
983
|
+
messages: validationMessages
|
|
984
|
+
});
|
|
985
|
+
const prevController = asyncAbortRef.current.get(validationKey);
|
|
930
986
|
if (prevController) prevController.abort();
|
|
931
|
-
asyncAbortRef.current.delete(
|
|
987
|
+
asyncAbortRef.current.delete(validationKey);
|
|
932
988
|
setValidatingFields((prev) => {
|
|
933
|
-
if (!prev[
|
|
989
|
+
if (!prev[validationKey]) return prev;
|
|
934
990
|
const next = { ...prev };
|
|
935
|
-
delete next[
|
|
991
|
+
delete next[validationKey];
|
|
936
992
|
return next;
|
|
937
993
|
});
|
|
938
994
|
if (syncError) return null;
|
|
939
|
-
const version = (asyncValidationVersionRef.current.get(
|
|
940
|
-
asyncValidationVersionRef.current.set(
|
|
995
|
+
const version = (asyncValidationVersionRef.current.get(validationKey) || 0) + 1;
|
|
996
|
+
asyncValidationVersionRef.current.set(validationKey, version);
|
|
941
997
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
942
|
-
if (controller) asyncAbortRef.current.set(
|
|
998
|
+
if (controller) asyncAbortRef.current.set(validationKey, controller);
|
|
943
999
|
let asyncPromises;
|
|
944
1000
|
try {
|
|
945
1001
|
asyncPromises = collectAsyncValidatorPromises(
|
|
946
|
-
|
|
1002
|
+
value,
|
|
947
1003
|
field,
|
|
948
|
-
|
|
1004
|
+
allValues,
|
|
949
1005
|
controller ? { signal: controller.signal } : void 0
|
|
950
1006
|
);
|
|
951
1007
|
} catch (err) {
|
|
952
|
-
|
|
1008
|
+
applyError((err == null ? void 0 : err.message) || "Validation failed");
|
|
953
1009
|
return null;
|
|
954
1010
|
}
|
|
955
1011
|
if (asyncPromises.length === 0) {
|
|
956
|
-
asyncAbortRef.current.delete(
|
|
1012
|
+
asyncAbortRef.current.delete(validationKey);
|
|
957
1013
|
return null;
|
|
958
1014
|
}
|
|
959
1015
|
const validationPromise = Promise.all(asyncPromises).then(
|
|
960
1016
|
(results) => {
|
|
961
|
-
if (asyncValidationVersionRef.current.get(
|
|
962
|
-
asyncValidationRef.current.delete(
|
|
963
|
-
asyncAbortRef.current.delete(
|
|
1017
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
1018
|
+
asyncValidationRef.current.delete(validationKey);
|
|
1019
|
+
asyncAbortRef.current.delete(validationKey);
|
|
964
1020
|
setValidatingFields((prev) => {
|
|
965
1021
|
const next = { ...prev };
|
|
966
|
-
delete next[
|
|
1022
|
+
delete next[validationKey];
|
|
967
1023
|
return next;
|
|
968
1024
|
});
|
|
969
1025
|
let err = null;
|
|
@@ -974,50 +1030,128 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
974
1030
|
break;
|
|
975
1031
|
}
|
|
976
1032
|
}
|
|
977
|
-
|
|
1033
|
+
applyError(err);
|
|
978
1034
|
},
|
|
979
1035
|
(rejection) => {
|
|
980
|
-
if (asyncValidationVersionRef.current.get(
|
|
981
|
-
asyncValidationRef.current.delete(
|
|
982
|
-
asyncAbortRef.current.delete(
|
|
1036
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
1037
|
+
asyncValidationRef.current.delete(validationKey);
|
|
1038
|
+
asyncAbortRef.current.delete(validationKey);
|
|
983
1039
|
setValidatingFields((prev) => {
|
|
984
1040
|
const next = { ...prev };
|
|
985
|
-
delete next[
|
|
1041
|
+
delete next[validationKey];
|
|
986
1042
|
return next;
|
|
987
1043
|
});
|
|
988
1044
|
if (rejection && rejection.name === "AbortError") return;
|
|
989
|
-
|
|
1045
|
+
applyError((rejection == null ? void 0 : rejection.message) || "Validation failed");
|
|
990
1046
|
}
|
|
991
1047
|
);
|
|
992
|
-
asyncValidationRef.current.set(
|
|
993
|
-
setValidatingFields((prev) => ({ ...prev, [
|
|
1048
|
+
asyncValidationRef.current.set(validationKey, validationPromise);
|
|
1049
|
+
setValidatingFields((prev) => ({ ...prev, [validationKey]: true }));
|
|
994
1050
|
return validationPromise;
|
|
995
1051
|
},
|
|
996
|
-
[
|
|
1052
|
+
[fieldTypes, validationMessages]
|
|
997
1053
|
);
|
|
998
|
-
const
|
|
1054
|
+
const runAsyncValidation = useCallback(
|
|
999
1055
|
(name, value) => {
|
|
1000
1056
|
const field = fieldByName.get(name);
|
|
1001
|
-
if (!field || field.type === "repeater") return;
|
|
1002
|
-
|
|
1057
|
+
if (!field || field.type === "repeater" || field.type === "fieldGroup") return null;
|
|
1058
|
+
return runAsyncValidationTarget({
|
|
1059
|
+
validationKey: name,
|
|
1060
|
+
field,
|
|
1061
|
+
value: value != null ? value : formValues[name],
|
|
1062
|
+
allValues: formValues,
|
|
1063
|
+
applyError: (errorMessage) => updateErrors({ [name]: errorMessage })
|
|
1064
|
+
});
|
|
1065
|
+
},
|
|
1066
|
+
[fieldByName, formValues, runAsyncValidationTarget, updateErrors]
|
|
1067
|
+
);
|
|
1068
|
+
const triggerAsyncValidationTarget = useCallback(
|
|
1069
|
+
(target) => {
|
|
1070
|
+
if (!(target == null ? void 0 : target.field) || !target.validationKey) return;
|
|
1071
|
+
const debounceMs = target.field.validateDebounce;
|
|
1003
1072
|
if (debounceMs && debounceMs > 0) {
|
|
1004
|
-
const existing = debounceTimersRef.current.get(
|
|
1073
|
+
const existing = debounceTimersRef.current.get(target.validationKey);
|
|
1005
1074
|
if (existing) clearTimeout(existing);
|
|
1006
1075
|
const timer = setTimeout(() => {
|
|
1007
|
-
debounceTimersRef.current.delete(
|
|
1008
|
-
|
|
1076
|
+
debounceTimersRef.current.delete(target.validationKey);
|
|
1077
|
+
runAsyncValidationTarget(target);
|
|
1009
1078
|
}, debounceMs);
|
|
1010
|
-
debounceTimersRef.current.set(
|
|
1079
|
+
debounceTimersRef.current.set(target.validationKey, timer);
|
|
1011
1080
|
} else {
|
|
1012
|
-
|
|
1081
|
+
runAsyncValidationTarget(target);
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
[runAsyncValidationTarget]
|
|
1085
|
+
);
|
|
1086
|
+
const triggerAsyncValidation = useCallback(
|
|
1087
|
+
(name, value) => {
|
|
1088
|
+
const field = fieldByName.get(name);
|
|
1089
|
+
if (!field || field.type === "repeater" || field.type === "fieldGroup") return;
|
|
1090
|
+
triggerAsyncValidationTarget({
|
|
1091
|
+
validationKey: name,
|
|
1092
|
+
field,
|
|
1093
|
+
value: value != null ? value : formValuesRef.current[name],
|
|
1094
|
+
allValues: formValuesRef.current,
|
|
1095
|
+
applyError: (errorMessage) => updateErrors({ [name]: errorMessage })
|
|
1096
|
+
});
|
|
1097
|
+
},
|
|
1098
|
+
[fieldByName, triggerAsyncValidationTarget, updateErrors]
|
|
1099
|
+
);
|
|
1100
|
+
const getAsyncValidationTargets = useCallback(
|
|
1101
|
+
(fieldSubset) => {
|
|
1102
|
+
const toValidate = fieldSubset || visibleFields;
|
|
1103
|
+
const targets = [];
|
|
1104
|
+
for (const field of toValidate) {
|
|
1105
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
1106
|
+
for (const item of field.items) {
|
|
1107
|
+
for (const subField of field.fields(item)) {
|
|
1108
|
+
if (subField.visible && !subField.visible(formValues)) continue;
|
|
1109
|
+
targets.push({
|
|
1110
|
+
validationKey: subField.name,
|
|
1111
|
+
field: subField,
|
|
1112
|
+
value: formValues[subField.name],
|
|
1113
|
+
allValues: formValues,
|
|
1114
|
+
applyError: (errorMessage) => updateErrors({ [subField.name]: errorMessage })
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
if (field.type === "repeater") {
|
|
1121
|
+
const rows = Array.isArray(formValues[field.name]) ? formValues[field.name] : [];
|
|
1122
|
+
const subFields = field.fields || [];
|
|
1123
|
+
rows.forEach((row, rowIdx) => {
|
|
1124
|
+
const rowValues = { ...formValues, [field.name]: rows };
|
|
1125
|
+
subFields.forEach((subField) => {
|
|
1126
|
+
if (subField.visible && !subField.visible(rowValues)) return;
|
|
1127
|
+
targets.push({
|
|
1128
|
+
validationKey: getRepeaterErrorKey(field.name, rowIdx, subField.name),
|
|
1129
|
+
field: subField,
|
|
1130
|
+
value: row == null ? void 0 : row[subField.name],
|
|
1131
|
+
allValues: rowValues,
|
|
1132
|
+
applyError: (errorMessage) => setRepeaterSubFieldError(field.name, rowIdx, subField.name, errorMessage)
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
});
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
targets.push({
|
|
1139
|
+
validationKey: field.name,
|
|
1140
|
+
field,
|
|
1141
|
+
value: formValues[field.name],
|
|
1142
|
+
allValues: formValues,
|
|
1143
|
+
applyError: (errorMessage) => updateErrors({ [field.name]: errorMessage })
|
|
1144
|
+
});
|
|
1013
1145
|
}
|
|
1146
|
+
return targets;
|
|
1014
1147
|
},
|
|
1015
|
-
[
|
|
1148
|
+
[visibleFields, formValues, setRepeaterSubFieldError, updateErrors]
|
|
1016
1149
|
);
|
|
1017
1150
|
const commitValues = useCallback(
|
|
1018
1151
|
(nextValues) => {
|
|
1019
1152
|
formValuesRef.current = nextValues;
|
|
1020
1153
|
if (values != null) {
|
|
1154
|
+
controlledBaselineLockedRef.current = true;
|
|
1021
1155
|
if (onChange) onChange(nextValues);
|
|
1022
1156
|
} else {
|
|
1023
1157
|
setInternalValues(nextValues);
|
|
@@ -1035,7 +1169,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1035
1169
|
[commitValues]
|
|
1036
1170
|
);
|
|
1037
1171
|
const handleFieldChange = useCallback(
|
|
1038
|
-
(name, value) => {
|
|
1172
|
+
(name, value, options = {}) => {
|
|
1173
|
+
const { clearNestedErrors = true } = options;
|
|
1039
1174
|
const newValues = { ...formValuesRef.current, [name]: value };
|
|
1040
1175
|
const queue = [name];
|
|
1041
1176
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -1076,9 +1211,11 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1076
1211
|
if (formErrorsRef.current[name] != null) {
|
|
1077
1212
|
clearedErrors[name] = null;
|
|
1078
1213
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1214
|
+
if (clearNestedErrors) {
|
|
1215
|
+
for (const key of Object.keys(formErrorsRef.current)) {
|
|
1216
|
+
if (key.startsWith(`${name}[`)) {
|
|
1217
|
+
clearedErrors[key] = null;
|
|
1218
|
+
}
|
|
1082
1219
|
}
|
|
1083
1220
|
}
|
|
1084
1221
|
draftValuesRef.current = newValues;
|
|
@@ -1147,7 +1284,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1147
1284
|
replaceErrors(errors);
|
|
1148
1285
|
return;
|
|
1149
1286
|
}
|
|
1150
|
-
const asyncSubmitValidations = allVisibleFields.map((
|
|
1287
|
+
const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
1151
1288
|
if (asyncSubmitValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
1152
1289
|
const pendingValidations = [
|
|
1153
1290
|
.../* @__PURE__ */ new Set([
|
|
@@ -1162,6 +1299,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1162
1299
|
const reset = () => {
|
|
1163
1300
|
const fresh = computeInitialValues();
|
|
1164
1301
|
if (values == null) setInternalValues(fresh);
|
|
1302
|
+
controlledBaselineLockedRef.current = false;
|
|
1165
1303
|
replaceErrors({});
|
|
1166
1304
|
initialSnapshot.current = deepClone(fresh);
|
|
1167
1305
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -1201,7 +1339,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1201
1339
|
if (controlledLoading == null) setInternalLoading(false);
|
|
1202
1340
|
}
|
|
1203
1341
|
},
|
|
1204
|
-
[validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName,
|
|
1342
|
+
[validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
|
|
1205
1343
|
);
|
|
1206
1344
|
const handleNext = useCallback(async () => {
|
|
1207
1345
|
if (!isMultiStep) return;
|
|
@@ -1213,7 +1351,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1213
1351
|
replaceErrors({ ...formErrorsRef.current, ...errors });
|
|
1214
1352
|
return;
|
|
1215
1353
|
}
|
|
1216
|
-
const asyncStepValidations = stepFields.map((
|
|
1354
|
+
const asyncStepValidations = getAsyncValidationTargets(stepFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
1217
1355
|
if (asyncStepValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
1218
1356
|
const pendingValidations = [
|
|
1219
1357
|
.../* @__PURE__ */ new Set([
|
|
@@ -1238,7 +1376,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1238
1376
|
} else {
|
|
1239
1377
|
setInternalStep(nextStep);
|
|
1240
1378
|
}
|
|
1241
|
-
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields,
|
|
1379
|
+
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields, getAsyncValidationTargets, runAsyncValidationTarget]);
|
|
1242
1380
|
const handleBack = useCallback(() => {
|
|
1243
1381
|
if (!isMultiStep) return;
|
|
1244
1382
|
const prevStep = Math.max(currentStep - 1, 0);
|
|
@@ -1270,6 +1408,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1270
1408
|
reset: () => {
|
|
1271
1409
|
const fresh = computeInitialValues();
|
|
1272
1410
|
if (values == null) setInternalValues(fresh);
|
|
1411
|
+
controlledBaselineLockedRef.current = false;
|
|
1273
1412
|
replaceErrors({});
|
|
1274
1413
|
initialSnapshot.current = deepClone(fresh);
|
|
1275
1414
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -1282,30 +1421,6 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1282
1421
|
replaceErrors(errors);
|
|
1283
1422
|
}
|
|
1284
1423
|
}));
|
|
1285
|
-
const setRepeaterSubFieldError = useCallback(
|
|
1286
|
-
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
1287
|
-
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
1288
|
-
const merged = { ...formErrorsRef.current };
|
|
1289
|
-
if (errorMessage) {
|
|
1290
|
-
merged[key] = errorMessage;
|
|
1291
|
-
} else {
|
|
1292
|
-
delete merged[key];
|
|
1293
|
-
}
|
|
1294
|
-
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
1295
|
-
const match = k.match(/\[(\d+)\]\./);
|
|
1296
|
-
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
1297
|
-
return { key: k, row };
|
|
1298
|
-
}).sort((a, b) => a.row - b.row);
|
|
1299
|
-
if (subErrors.length > 0) {
|
|
1300
|
-
const first = subErrors[0];
|
|
1301
|
-
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
1302
|
-
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
1303
|
-
delete merged[fieldName];
|
|
1304
|
-
}
|
|
1305
|
-
replaceErrors(merged);
|
|
1306
|
-
},
|
|
1307
|
-
[replaceErrors]
|
|
1308
|
-
);
|
|
1309
1424
|
const renderField = (field) => {
|
|
1310
1425
|
const fieldError = formErrors[field.name] || null;
|
|
1311
1426
|
const rendered = renderFieldInner(field);
|
|
@@ -1785,12 +1900,13 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1785
1900
|
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
1786
1901
|
const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
1787
1902
|
setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
|
|
1903
|
+
return err;
|
|
1788
1904
|
};
|
|
1789
1905
|
const handleSubFieldChange = (rowIdx, subField, subValue) => {
|
|
1790
1906
|
const updated = rows.map(
|
|
1791
1907
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
1792
1908
|
);
|
|
1793
|
-
handleFieldChange(field.name, updated);
|
|
1909
|
+
handleFieldChange(field.name, updated, { clearNestedErrors: false });
|
|
1794
1910
|
if (validateOnChange) {
|
|
1795
1911
|
validateSubField(rowIdx, subField, subValue, updated);
|
|
1796
1912
|
}
|
|
@@ -1800,13 +1916,24 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1800
1916
|
const nextRows = rows.map(
|
|
1801
1917
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
1802
1918
|
);
|
|
1803
|
-
validateSubField(rowIdx, subField, subValue, nextRows);
|
|
1919
|
+
const err = validateSubField(rowIdx, subField, subValue, nextRows);
|
|
1920
|
+
if (err) return;
|
|
1921
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, subField.name);
|
|
1922
|
+
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
1923
|
+
triggerAsyncValidationTarget({
|
|
1924
|
+
validationKey,
|
|
1925
|
+
field: subField,
|
|
1926
|
+
value: subValue,
|
|
1927
|
+
allValues: rowValues,
|
|
1928
|
+
applyError: (errorMessage) => setRepeaterSubFieldError(field.name, rowIdx, subField.name, errorMessage)
|
|
1929
|
+
});
|
|
1804
1930
|
};
|
|
1805
1931
|
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, field.label && /* @__PURE__ */ React.createElement(Text, { format: { fontWeight: "demibold" } }, field.label, isRequired ? " *" : ""), field.description && /* @__PURE__ */ React.createElement(Text, { variant: "microcopy" }, field.description), rows.map((row, rowIdx) => /* @__PURE__ */ React.createElement(Flex, { key: getRowKey(field.name, row, rowIdx), direction: "row", gap: "xs", align: "end" }, subFields.map((sf) => {
|
|
1806
1932
|
const sfValue = row[sf.name];
|
|
1807
1933
|
const sfLabel = rowIdx === 0 ? sf.label : void 0;
|
|
1808
1934
|
const sfOptions = resolveOptions(sf, { ...formValues, [field.name]: rows });
|
|
1809
1935
|
const sfError = formErrors[getRepeaterErrorKey(field.name, rowIdx, sf.name)] || null;
|
|
1936
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, sf.name);
|
|
1810
1937
|
const sfProps = {
|
|
1811
1938
|
name: `${field.name}-${rowIdx}-${sf.name}`,
|
|
1812
1939
|
label: sfLabel,
|
|
@@ -1815,6 +1942,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1815
1942
|
disabled: resolveDisabled(sf, formValues) || isDisabled,
|
|
1816
1943
|
error: !!sfError,
|
|
1817
1944
|
validationMessage: sfError || void 0,
|
|
1945
|
+
...validatingFields[validationKey] ? { loading: true } : {},
|
|
1818
1946
|
...sf.fieldProps || {}
|
|
1819
1947
|
};
|
|
1820
1948
|
let sfElement;
|