hs-uix 1.4.0 → 1.4.1
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/dist/datatable.js +39 -28
- package/dist/datatable.mjs +39 -28
- package/dist/form.js +204 -76
- package/dist/form.mjs +204 -76
- package/dist/index.js +243 -104
- package/dist/index.mjs +243 -104
- package/package.json +1 -1
package/dist/form.js
CHANGED
|
@@ -667,9 +667,10 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
667
667
|
const inputDebounceRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
668
668
|
const rowKeyRef = (0, import_react.useRef)(/* @__PURE__ */ new WeakMap());
|
|
669
669
|
const rowKeyCounterRef = (0, import_react.useRef)(0);
|
|
670
|
+
const controlledBaselineLockedRef = (0, import_react.useRef)(false);
|
|
670
671
|
const initialSnapshot = (0, import_react.useRef)(null);
|
|
671
672
|
if (initialSnapshot.current === null) {
|
|
672
|
-
initialSnapshot.current = deepClone(computeInitialValues());
|
|
673
|
+
initialSnapshot.current = deepClone(values != null ? values : computeInitialValues());
|
|
673
674
|
}
|
|
674
675
|
const formValues = values != null ? values : internalValues;
|
|
675
676
|
const formErrors = controlledErrors != null ? controlledErrors : internalErrors;
|
|
@@ -681,6 +682,10 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
681
682
|
const draftValuesRef = (0, import_react.useRef)(null);
|
|
682
683
|
formValuesRef.current = formValues;
|
|
683
684
|
formErrorsRef.current = formErrors;
|
|
685
|
+
const syncDirtyBaseline = (0, import_react.useCallback)((nextValues) => {
|
|
686
|
+
initialSnapshot.current = deepClone(nextValues || {});
|
|
687
|
+
prevAutoSaveValues.current = deepClone(nextValues || {});
|
|
688
|
+
}, []);
|
|
684
689
|
const fieldByName = (0, import_react.useMemo)(() => {
|
|
685
690
|
const map = /* @__PURE__ */ new Map();
|
|
686
691
|
for (const field of fields) {
|
|
@@ -757,6 +762,11 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
757
762
|
if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
|
|
758
763
|
};
|
|
759
764
|
}, []);
|
|
765
|
+
(0, import_react.useEffect)(() => {
|
|
766
|
+
if (values == null) return;
|
|
767
|
+
if (controlledBaselineLockedRef.current) return;
|
|
768
|
+
syncDirtyBaseline(values);
|
|
769
|
+
}, [values, syncDirtyBaseline]);
|
|
760
770
|
const isDirty = (0, import_react.useMemo)(() => {
|
|
761
771
|
return !deepEqual(formValues, initialSnapshot.current);
|
|
762
772
|
}, [formValues]);
|
|
@@ -875,6 +885,50 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
875
885
|
},
|
|
876
886
|
[fieldTypes]
|
|
877
887
|
);
|
|
888
|
+
const setRepeaterSubFieldError = (0, import_react.useCallback)(
|
|
889
|
+
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
890
|
+
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
891
|
+
const merged = { ...formErrorsRef.current };
|
|
892
|
+
if (errorMessage) {
|
|
893
|
+
merged[key] = errorMessage;
|
|
894
|
+
} else {
|
|
895
|
+
delete merged[key];
|
|
896
|
+
}
|
|
897
|
+
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
898
|
+
const match = k.match(/\[(\d+)\]\./);
|
|
899
|
+
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
900
|
+
return { key: k, row };
|
|
901
|
+
}).sort((a, b) => a.row - b.row);
|
|
902
|
+
if (subErrors.length > 0) {
|
|
903
|
+
const first = subErrors[0];
|
|
904
|
+
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
905
|
+
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
906
|
+
delete merged[fieldName];
|
|
907
|
+
}
|
|
908
|
+
replaceErrors(merged);
|
|
909
|
+
},
|
|
910
|
+
[replaceErrors]
|
|
911
|
+
);
|
|
912
|
+
const expandValidationFields = (0, import_react.useCallback)(
|
|
913
|
+
(fieldSubset) => {
|
|
914
|
+
const toValidate = fieldSubset || visibleFields;
|
|
915
|
+
const expanded = [];
|
|
916
|
+
for (const field of toValidate) {
|
|
917
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
918
|
+
for (const item of field.items) {
|
|
919
|
+
for (const subField of field.fields(item)) {
|
|
920
|
+
if (subField.visible && !subField.visible(formValues)) continue;
|
|
921
|
+
expanded.push(subField);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
expanded.push(field);
|
|
927
|
+
}
|
|
928
|
+
return expanded;
|
|
929
|
+
},
|
|
930
|
+
[visibleFields, formValues]
|
|
931
|
+
);
|
|
878
932
|
const validateField = (0, import_react.useCallback)(
|
|
879
933
|
(name, value) => {
|
|
880
934
|
const field = fieldByName.get(name);
|
|
@@ -894,7 +948,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
894
948
|
);
|
|
895
949
|
const validateVisibleFields = (0, import_react.useCallback)(
|
|
896
950
|
(fieldSubset) => {
|
|
897
|
-
const toValidate = fieldSubset
|
|
951
|
+
const toValidate = expandValidationFields(fieldSubset);
|
|
898
952
|
const errors = {};
|
|
899
953
|
let hasErrors = false;
|
|
900
954
|
for (const field of toValidate) {
|
|
@@ -914,52 +968,54 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
914
968
|
}
|
|
915
969
|
return { errors, hasErrors };
|
|
916
970
|
},
|
|
917
|
-
[
|
|
971
|
+
[expandValidationFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
918
972
|
);
|
|
919
|
-
const
|
|
920
|
-
(
|
|
921
|
-
const field =
|
|
922
|
-
if (!field || field.type === "repeater") return null;
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
973
|
+
const runAsyncValidationTarget = (0, import_react.useCallback)(
|
|
974
|
+
(target) => {
|
|
975
|
+
const { validationKey, field, value, allValues, applyError } = target || {};
|
|
976
|
+
if (!field || !validationKey || field.type === "repeater" || field.type === "fieldGroup") return null;
|
|
977
|
+
const syncError = runValidators(value, field, allValues, fieldTypes, {
|
|
978
|
+
includeCustomValidators: false,
|
|
979
|
+
messages: validationMessages
|
|
980
|
+
});
|
|
981
|
+
const prevController = asyncAbortRef.current.get(validationKey);
|
|
926
982
|
if (prevController) prevController.abort();
|
|
927
|
-
asyncAbortRef.current.delete(
|
|
983
|
+
asyncAbortRef.current.delete(validationKey);
|
|
928
984
|
setValidatingFields((prev) => {
|
|
929
|
-
if (!prev[
|
|
985
|
+
if (!prev[validationKey]) return prev;
|
|
930
986
|
const next = { ...prev };
|
|
931
|
-
delete next[
|
|
987
|
+
delete next[validationKey];
|
|
932
988
|
return next;
|
|
933
989
|
});
|
|
934
990
|
if (syncError) return null;
|
|
935
|
-
const version = (asyncValidationVersionRef.current.get(
|
|
936
|
-
asyncValidationVersionRef.current.set(
|
|
991
|
+
const version = (asyncValidationVersionRef.current.get(validationKey) || 0) + 1;
|
|
992
|
+
asyncValidationVersionRef.current.set(validationKey, version);
|
|
937
993
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
938
|
-
if (controller) asyncAbortRef.current.set(
|
|
994
|
+
if (controller) asyncAbortRef.current.set(validationKey, controller);
|
|
939
995
|
let asyncPromises;
|
|
940
996
|
try {
|
|
941
997
|
asyncPromises = collectAsyncValidatorPromises(
|
|
942
|
-
|
|
998
|
+
value,
|
|
943
999
|
field,
|
|
944
|
-
|
|
1000
|
+
allValues,
|
|
945
1001
|
controller ? { signal: controller.signal } : void 0
|
|
946
1002
|
);
|
|
947
1003
|
} catch (err) {
|
|
948
|
-
|
|
1004
|
+
applyError((err == null ? void 0 : err.message) || "Validation failed");
|
|
949
1005
|
return null;
|
|
950
1006
|
}
|
|
951
1007
|
if (asyncPromises.length === 0) {
|
|
952
|
-
asyncAbortRef.current.delete(
|
|
1008
|
+
asyncAbortRef.current.delete(validationKey);
|
|
953
1009
|
return null;
|
|
954
1010
|
}
|
|
955
1011
|
const validationPromise = Promise.all(asyncPromises).then(
|
|
956
1012
|
(results) => {
|
|
957
|
-
if (asyncValidationVersionRef.current.get(
|
|
958
|
-
asyncValidationRef.current.delete(
|
|
959
|
-
asyncAbortRef.current.delete(
|
|
1013
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
1014
|
+
asyncValidationRef.current.delete(validationKey);
|
|
1015
|
+
asyncAbortRef.current.delete(validationKey);
|
|
960
1016
|
setValidatingFields((prev) => {
|
|
961
1017
|
const next = { ...prev };
|
|
962
|
-
delete next[
|
|
1018
|
+
delete next[validationKey];
|
|
963
1019
|
return next;
|
|
964
1020
|
});
|
|
965
1021
|
let err = null;
|
|
@@ -970,50 +1026,128 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
970
1026
|
break;
|
|
971
1027
|
}
|
|
972
1028
|
}
|
|
973
|
-
|
|
1029
|
+
applyError(err);
|
|
974
1030
|
},
|
|
975
1031
|
(rejection) => {
|
|
976
|
-
if (asyncValidationVersionRef.current.get(
|
|
977
|
-
asyncValidationRef.current.delete(
|
|
978
|
-
asyncAbortRef.current.delete(
|
|
1032
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
1033
|
+
asyncValidationRef.current.delete(validationKey);
|
|
1034
|
+
asyncAbortRef.current.delete(validationKey);
|
|
979
1035
|
setValidatingFields((prev) => {
|
|
980
1036
|
const next = { ...prev };
|
|
981
|
-
delete next[
|
|
1037
|
+
delete next[validationKey];
|
|
982
1038
|
return next;
|
|
983
1039
|
});
|
|
984
1040
|
if (rejection && rejection.name === "AbortError") return;
|
|
985
|
-
|
|
1041
|
+
applyError((rejection == null ? void 0 : rejection.message) || "Validation failed");
|
|
986
1042
|
}
|
|
987
1043
|
);
|
|
988
|
-
asyncValidationRef.current.set(
|
|
989
|
-
setValidatingFields((prev) => ({ ...prev, [
|
|
1044
|
+
asyncValidationRef.current.set(validationKey, validationPromise);
|
|
1045
|
+
setValidatingFields((prev) => ({ ...prev, [validationKey]: true }));
|
|
990
1046
|
return validationPromise;
|
|
991
1047
|
},
|
|
992
|
-
[
|
|
1048
|
+
[fieldTypes, validationMessages]
|
|
993
1049
|
);
|
|
994
|
-
const
|
|
1050
|
+
const runAsyncValidation = (0, import_react.useCallback)(
|
|
995
1051
|
(name, value) => {
|
|
996
1052
|
const field = fieldByName.get(name);
|
|
997
|
-
if (!field || field.type === "repeater") return;
|
|
998
|
-
|
|
1053
|
+
if (!field || field.type === "repeater" || field.type === "fieldGroup") return null;
|
|
1054
|
+
return runAsyncValidationTarget({
|
|
1055
|
+
validationKey: name,
|
|
1056
|
+
field,
|
|
1057
|
+
value: value != null ? value : formValues[name],
|
|
1058
|
+
allValues: formValues,
|
|
1059
|
+
applyError: (errorMessage) => updateErrors({ [name]: errorMessage })
|
|
1060
|
+
});
|
|
1061
|
+
},
|
|
1062
|
+
[fieldByName, formValues, runAsyncValidationTarget, updateErrors]
|
|
1063
|
+
);
|
|
1064
|
+
const triggerAsyncValidationTarget = (0, import_react.useCallback)(
|
|
1065
|
+
(target) => {
|
|
1066
|
+
if (!(target == null ? void 0 : target.field) || !target.validationKey) return;
|
|
1067
|
+
const debounceMs = target.field.validateDebounce;
|
|
999
1068
|
if (debounceMs && debounceMs > 0) {
|
|
1000
|
-
const existing = debounceTimersRef.current.get(
|
|
1069
|
+
const existing = debounceTimersRef.current.get(target.validationKey);
|
|
1001
1070
|
if (existing) clearTimeout(existing);
|
|
1002
1071
|
const timer = setTimeout(() => {
|
|
1003
|
-
debounceTimersRef.current.delete(
|
|
1004
|
-
|
|
1072
|
+
debounceTimersRef.current.delete(target.validationKey);
|
|
1073
|
+
runAsyncValidationTarget(target);
|
|
1005
1074
|
}, debounceMs);
|
|
1006
|
-
debounceTimersRef.current.set(
|
|
1075
|
+
debounceTimersRef.current.set(target.validationKey, timer);
|
|
1007
1076
|
} else {
|
|
1008
|
-
|
|
1077
|
+
runAsyncValidationTarget(target);
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
[runAsyncValidationTarget]
|
|
1081
|
+
);
|
|
1082
|
+
const triggerAsyncValidation = (0, import_react.useCallback)(
|
|
1083
|
+
(name, value) => {
|
|
1084
|
+
const field = fieldByName.get(name);
|
|
1085
|
+
if (!field || field.type === "repeater" || field.type === "fieldGroup") return;
|
|
1086
|
+
triggerAsyncValidationTarget({
|
|
1087
|
+
validationKey: name,
|
|
1088
|
+
field,
|
|
1089
|
+
value: value != null ? value : formValuesRef.current[name],
|
|
1090
|
+
allValues: formValuesRef.current,
|
|
1091
|
+
applyError: (errorMessage) => updateErrors({ [name]: errorMessage })
|
|
1092
|
+
});
|
|
1093
|
+
},
|
|
1094
|
+
[fieldByName, triggerAsyncValidationTarget, updateErrors]
|
|
1095
|
+
);
|
|
1096
|
+
const getAsyncValidationTargets = (0, import_react.useCallback)(
|
|
1097
|
+
(fieldSubset) => {
|
|
1098
|
+
const toValidate = fieldSubset || visibleFields;
|
|
1099
|
+
const targets = [];
|
|
1100
|
+
for (const field of toValidate) {
|
|
1101
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
1102
|
+
for (const item of field.items) {
|
|
1103
|
+
for (const subField of field.fields(item)) {
|
|
1104
|
+
if (subField.visible && !subField.visible(formValues)) continue;
|
|
1105
|
+
targets.push({
|
|
1106
|
+
validationKey: subField.name,
|
|
1107
|
+
field: subField,
|
|
1108
|
+
value: formValues[subField.name],
|
|
1109
|
+
allValues: formValues,
|
|
1110
|
+
applyError: (errorMessage) => updateErrors({ [subField.name]: errorMessage })
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
continue;
|
|
1115
|
+
}
|
|
1116
|
+
if (field.type === "repeater") {
|
|
1117
|
+
const rows = Array.isArray(formValues[field.name]) ? formValues[field.name] : [];
|
|
1118
|
+
const subFields = field.fields || [];
|
|
1119
|
+
rows.forEach((row, rowIdx) => {
|
|
1120
|
+
const rowValues = { ...formValues, [field.name]: rows };
|
|
1121
|
+
subFields.forEach((subField) => {
|
|
1122
|
+
if (subField.visible && !subField.visible(rowValues)) return;
|
|
1123
|
+
targets.push({
|
|
1124
|
+
validationKey: getRepeaterErrorKey(field.name, rowIdx, subField.name),
|
|
1125
|
+
field: subField,
|
|
1126
|
+
value: row == null ? void 0 : row[subField.name],
|
|
1127
|
+
allValues: rowValues,
|
|
1128
|
+
applyError: (errorMessage) => setRepeaterSubFieldError(field.name, rowIdx, subField.name, errorMessage)
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
targets.push({
|
|
1135
|
+
validationKey: field.name,
|
|
1136
|
+
field,
|
|
1137
|
+
value: formValues[field.name],
|
|
1138
|
+
allValues: formValues,
|
|
1139
|
+
applyError: (errorMessage) => updateErrors({ [field.name]: errorMessage })
|
|
1140
|
+
});
|
|
1009
1141
|
}
|
|
1142
|
+
return targets;
|
|
1010
1143
|
},
|
|
1011
|
-
[
|
|
1144
|
+
[visibleFields, formValues, setRepeaterSubFieldError, updateErrors]
|
|
1012
1145
|
);
|
|
1013
1146
|
const commitValues = (0, import_react.useCallback)(
|
|
1014
1147
|
(nextValues) => {
|
|
1015
1148
|
formValuesRef.current = nextValues;
|
|
1016
1149
|
if (values != null) {
|
|
1150
|
+
controlledBaselineLockedRef.current = true;
|
|
1017
1151
|
if (onChange) onChange(nextValues);
|
|
1018
1152
|
} else {
|
|
1019
1153
|
setInternalValues(nextValues);
|
|
@@ -1031,7 +1165,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1031
1165
|
[commitValues]
|
|
1032
1166
|
);
|
|
1033
1167
|
const handleFieldChange = (0, import_react.useCallback)(
|
|
1034
|
-
(name, value) => {
|
|
1168
|
+
(name, value, options = {}) => {
|
|
1169
|
+
const { clearNestedErrors = true } = options;
|
|
1035
1170
|
const newValues = { ...formValuesRef.current, [name]: value };
|
|
1036
1171
|
const queue = [name];
|
|
1037
1172
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -1072,9 +1207,11 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1072
1207
|
if (formErrorsRef.current[name] != null) {
|
|
1073
1208
|
clearedErrors[name] = null;
|
|
1074
1209
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1210
|
+
if (clearNestedErrors) {
|
|
1211
|
+
for (const key of Object.keys(formErrorsRef.current)) {
|
|
1212
|
+
if (key.startsWith(`${name}[`)) {
|
|
1213
|
+
clearedErrors[key] = null;
|
|
1214
|
+
}
|
|
1078
1215
|
}
|
|
1079
1216
|
}
|
|
1080
1217
|
draftValuesRef.current = newValues;
|
|
@@ -1143,7 +1280,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1143
1280
|
replaceErrors(errors);
|
|
1144
1281
|
return;
|
|
1145
1282
|
}
|
|
1146
|
-
const asyncSubmitValidations = allVisibleFields.map((
|
|
1283
|
+
const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
1147
1284
|
if (asyncSubmitValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
1148
1285
|
const pendingValidations = [
|
|
1149
1286
|
.../* @__PURE__ */ new Set([
|
|
@@ -1158,6 +1295,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1158
1295
|
const reset = () => {
|
|
1159
1296
|
const fresh = computeInitialValues();
|
|
1160
1297
|
if (values == null) setInternalValues(fresh);
|
|
1298
|
+
controlledBaselineLockedRef.current = false;
|
|
1161
1299
|
replaceErrors({});
|
|
1162
1300
|
initialSnapshot.current = deepClone(fresh);
|
|
1163
1301
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -1197,7 +1335,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1197
1335
|
if (controlledLoading == null) setInternalLoading(false);
|
|
1198
1336
|
}
|
|
1199
1337
|
},
|
|
1200
|
-
[validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName,
|
|
1338
|
+
[validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
|
|
1201
1339
|
);
|
|
1202
1340
|
const handleNext = (0, import_react.useCallback)(async () => {
|
|
1203
1341
|
if (!isMultiStep) return;
|
|
@@ -1209,7 +1347,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1209
1347
|
replaceErrors({ ...formErrorsRef.current, ...errors });
|
|
1210
1348
|
return;
|
|
1211
1349
|
}
|
|
1212
|
-
const asyncStepValidations = stepFields.map((
|
|
1350
|
+
const asyncStepValidations = getAsyncValidationTargets(stepFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
1213
1351
|
if (asyncStepValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
1214
1352
|
const pendingValidations = [
|
|
1215
1353
|
.../* @__PURE__ */ new Set([
|
|
@@ -1234,7 +1372,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1234
1372
|
} else {
|
|
1235
1373
|
setInternalStep(nextStep);
|
|
1236
1374
|
}
|
|
1237
|
-
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields,
|
|
1375
|
+
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields, getAsyncValidationTargets, runAsyncValidationTarget]);
|
|
1238
1376
|
const handleBack = (0, import_react.useCallback)(() => {
|
|
1239
1377
|
if (!isMultiStep) return;
|
|
1240
1378
|
const prevStep = Math.max(currentStep - 1, 0);
|
|
@@ -1266,6 +1404,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1266
1404
|
reset: () => {
|
|
1267
1405
|
const fresh = computeInitialValues();
|
|
1268
1406
|
if (values == null) setInternalValues(fresh);
|
|
1407
|
+
controlledBaselineLockedRef.current = false;
|
|
1269
1408
|
replaceErrors({});
|
|
1270
1409
|
initialSnapshot.current = deepClone(fresh);
|
|
1271
1410
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -1278,30 +1417,6 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1278
1417
|
replaceErrors(errors);
|
|
1279
1418
|
}
|
|
1280
1419
|
}));
|
|
1281
|
-
const setRepeaterSubFieldError = (0, import_react.useCallback)(
|
|
1282
|
-
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
1283
|
-
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
1284
|
-
const merged = { ...formErrorsRef.current };
|
|
1285
|
-
if (errorMessage) {
|
|
1286
|
-
merged[key] = errorMessage;
|
|
1287
|
-
} else {
|
|
1288
|
-
delete merged[key];
|
|
1289
|
-
}
|
|
1290
|
-
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
1291
|
-
const match = k.match(/\[(\d+)\]\./);
|
|
1292
|
-
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
1293
|
-
return { key: k, row };
|
|
1294
|
-
}).sort((a, b) => a.row - b.row);
|
|
1295
|
-
if (subErrors.length > 0) {
|
|
1296
|
-
const first = subErrors[0];
|
|
1297
|
-
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
1298
|
-
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
1299
|
-
delete merged[fieldName];
|
|
1300
|
-
}
|
|
1301
|
-
replaceErrors(merged);
|
|
1302
|
-
},
|
|
1303
|
-
[replaceErrors]
|
|
1304
|
-
);
|
|
1305
1420
|
const renderField = (field) => {
|
|
1306
1421
|
const fieldError = formErrors[field.name] || null;
|
|
1307
1422
|
const rendered = renderFieldInner(field);
|
|
@@ -1781,12 +1896,13 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1781
1896
|
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
1782
1897
|
const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
1783
1898
|
setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
|
|
1899
|
+
return err;
|
|
1784
1900
|
};
|
|
1785
1901
|
const handleSubFieldChange = (rowIdx, subField, subValue) => {
|
|
1786
1902
|
const updated = rows.map(
|
|
1787
1903
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
1788
1904
|
);
|
|
1789
|
-
handleFieldChange(field.name, updated);
|
|
1905
|
+
handleFieldChange(field.name, updated, { clearNestedErrors: false });
|
|
1790
1906
|
if (validateOnChange) {
|
|
1791
1907
|
validateSubField(rowIdx, subField, subValue, updated);
|
|
1792
1908
|
}
|
|
@@ -1796,13 +1912,24 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1796
1912
|
const nextRows = rows.map(
|
|
1797
1913
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
1798
1914
|
);
|
|
1799
|
-
validateSubField(rowIdx, subField, subValue, nextRows);
|
|
1915
|
+
const err = validateSubField(rowIdx, subField, subValue, nextRows);
|
|
1916
|
+
if (err) return;
|
|
1917
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, subField.name);
|
|
1918
|
+
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
1919
|
+
triggerAsyncValidationTarget({
|
|
1920
|
+
validationKey,
|
|
1921
|
+
field: subField,
|
|
1922
|
+
value: subValue,
|
|
1923
|
+
allValues: rowValues,
|
|
1924
|
+
applyError: (errorMessage) => setRepeaterSubFieldError(field.name, rowIdx, subField.name, errorMessage)
|
|
1925
|
+
});
|
|
1800
1926
|
};
|
|
1801
1927
|
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, field.label && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, field.label, isRequired ? " *" : ""), field.description && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy" }, field.description), rows.map((row, rowIdx) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: getRowKey(field.name, row, rowIdx), direction: "row", gap: "xs", align: "end" }, subFields.map((sf) => {
|
|
1802
1928
|
const sfValue = row[sf.name];
|
|
1803
1929
|
const sfLabel = rowIdx === 0 ? sf.label : void 0;
|
|
1804
1930
|
const sfOptions = resolveOptions(sf, { ...formValues, [field.name]: rows });
|
|
1805
1931
|
const sfError = formErrors[getRepeaterErrorKey(field.name, rowIdx, sf.name)] || null;
|
|
1932
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, sf.name);
|
|
1806
1933
|
const sfProps = {
|
|
1807
1934
|
name: `${field.name}-${rowIdx}-${sf.name}`,
|
|
1808
1935
|
label: sfLabel,
|
|
@@ -1811,6 +1938,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1811
1938
|
disabled: resolveDisabled(sf, formValues) || isDisabled,
|
|
1812
1939
|
error: !!sfError,
|
|
1813
1940
|
validationMessage: sfError || void 0,
|
|
1941
|
+
...validatingFields[validationKey] ? { loading: true } : {},
|
|
1814
1942
|
...sf.fieldProps || {}
|
|
1815
1943
|
};
|
|
1816
1944
|
let sfElement;
|