hs-uix 1.3.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 +228 -87
- package/dist/form.mjs +228 -87
- package/dist/index.js +267 -115
- package/dist/index.mjs +267 -115
- package/package.json +1 -1
package/dist/form.js
CHANGED
|
@@ -55,6 +55,7 @@ var getEmptyValue = (field) => {
|
|
|
55
55
|
case "datetime":
|
|
56
56
|
return void 0;
|
|
57
57
|
case "display":
|
|
58
|
+
case "slot":
|
|
58
59
|
case "crmPropertyList":
|
|
59
60
|
case "crmAssociationPropertyList":
|
|
60
61
|
return void 0;
|
|
@@ -198,7 +199,7 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
|
|
|
198
199
|
var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
|
|
199
200
|
const includeCustomValidators = options.includeCustomValidators !== false;
|
|
200
201
|
const msg = options.messages || {};
|
|
201
|
-
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
|
|
202
|
+
if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
|
|
202
203
|
const isRequired = resolveRequired(field, allValues);
|
|
203
204
|
const plugin = fieldTypes && fieldTypes[field.type];
|
|
204
205
|
const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
|
|
@@ -421,6 +422,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
421
422
|
// explicit row layout array (overrides columns + columnWidth)
|
|
422
423
|
sections,
|
|
423
424
|
// FormBuilderSection[] — accordion field grouping
|
|
425
|
+
groups,
|
|
426
|
+
// Record<string, FormBuilderGroupOptions> — per-group rendering options keyed by group name
|
|
424
427
|
gap = "sm",
|
|
425
428
|
// gap between fields
|
|
426
429
|
showRequiredIndicator = true,
|
|
@@ -630,7 +633,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
630
633
|
const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
|
|
631
634
|
const vals = {};
|
|
632
635
|
for (const field of fields) {
|
|
633
|
-
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
|
|
636
|
+
if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
|
|
634
637
|
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
635
638
|
for (const item of field.items) {
|
|
636
639
|
const subFields = field.fields(item);
|
|
@@ -664,9 +667,10 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
664
667
|
const inputDebounceRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
665
668
|
const rowKeyRef = (0, import_react.useRef)(/* @__PURE__ */ new WeakMap());
|
|
666
669
|
const rowKeyCounterRef = (0, import_react.useRef)(0);
|
|
670
|
+
const controlledBaselineLockedRef = (0, import_react.useRef)(false);
|
|
667
671
|
const initialSnapshot = (0, import_react.useRef)(null);
|
|
668
672
|
if (initialSnapshot.current === null) {
|
|
669
|
-
initialSnapshot.current = deepClone(computeInitialValues());
|
|
673
|
+
initialSnapshot.current = deepClone(values != null ? values : computeInitialValues());
|
|
670
674
|
}
|
|
671
675
|
const formValues = values != null ? values : internalValues;
|
|
672
676
|
const formErrors = controlledErrors != null ? controlledErrors : internalErrors;
|
|
@@ -678,6 +682,10 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
678
682
|
const draftValuesRef = (0, import_react.useRef)(null);
|
|
679
683
|
formValuesRef.current = formValues;
|
|
680
684
|
formErrorsRef.current = formErrors;
|
|
685
|
+
const syncDirtyBaseline = (0, import_react.useCallback)((nextValues) => {
|
|
686
|
+
initialSnapshot.current = deepClone(nextValues || {});
|
|
687
|
+
prevAutoSaveValues.current = deepClone(nextValues || {});
|
|
688
|
+
}, []);
|
|
681
689
|
const fieldByName = (0, import_react.useMemo)(() => {
|
|
682
690
|
const map = /* @__PURE__ */ new Map();
|
|
683
691
|
for (const field of fields) {
|
|
@@ -754,6 +762,11 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
754
762
|
if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
|
|
755
763
|
};
|
|
756
764
|
}, []);
|
|
765
|
+
(0, import_react.useEffect)(() => {
|
|
766
|
+
if (values == null) return;
|
|
767
|
+
if (controlledBaselineLockedRef.current) return;
|
|
768
|
+
syncDirtyBaseline(values);
|
|
769
|
+
}, [values, syncDirtyBaseline]);
|
|
757
770
|
const isDirty = (0, import_react.useMemo)(() => {
|
|
758
771
|
return !deepEqual(formValues, initialSnapshot.current);
|
|
759
772
|
}, [formValues]);
|
|
@@ -872,6 +885,50 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
872
885
|
},
|
|
873
886
|
[fieldTypes]
|
|
874
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
|
+
);
|
|
875
932
|
const validateField = (0, import_react.useCallback)(
|
|
876
933
|
(name, value) => {
|
|
877
934
|
const field = fieldByName.get(name);
|
|
@@ -891,7 +948,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
891
948
|
);
|
|
892
949
|
const validateVisibleFields = (0, import_react.useCallback)(
|
|
893
950
|
(fieldSubset) => {
|
|
894
|
-
const toValidate = fieldSubset
|
|
951
|
+
const toValidate = expandValidationFields(fieldSubset);
|
|
895
952
|
const errors = {};
|
|
896
953
|
let hasErrors = false;
|
|
897
954
|
for (const field of toValidate) {
|
|
@@ -911,52 +968,54 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
911
968
|
}
|
|
912
969
|
return { errors, hasErrors };
|
|
913
970
|
},
|
|
914
|
-
[
|
|
971
|
+
[expandValidationFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
915
972
|
);
|
|
916
|
-
const
|
|
917
|
-
(
|
|
918
|
-
const field =
|
|
919
|
-
if (!field || field.type === "repeater") return null;
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
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);
|
|
923
982
|
if (prevController) prevController.abort();
|
|
924
|
-
asyncAbortRef.current.delete(
|
|
983
|
+
asyncAbortRef.current.delete(validationKey);
|
|
925
984
|
setValidatingFields((prev) => {
|
|
926
|
-
if (!prev[
|
|
985
|
+
if (!prev[validationKey]) return prev;
|
|
927
986
|
const next = { ...prev };
|
|
928
|
-
delete next[
|
|
987
|
+
delete next[validationKey];
|
|
929
988
|
return next;
|
|
930
989
|
});
|
|
931
990
|
if (syncError) return null;
|
|
932
|
-
const version = (asyncValidationVersionRef.current.get(
|
|
933
|
-
asyncValidationVersionRef.current.set(
|
|
991
|
+
const version = (asyncValidationVersionRef.current.get(validationKey) || 0) + 1;
|
|
992
|
+
asyncValidationVersionRef.current.set(validationKey, version);
|
|
934
993
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
935
|
-
if (controller) asyncAbortRef.current.set(
|
|
994
|
+
if (controller) asyncAbortRef.current.set(validationKey, controller);
|
|
936
995
|
let asyncPromises;
|
|
937
996
|
try {
|
|
938
997
|
asyncPromises = collectAsyncValidatorPromises(
|
|
939
|
-
|
|
998
|
+
value,
|
|
940
999
|
field,
|
|
941
|
-
|
|
1000
|
+
allValues,
|
|
942
1001
|
controller ? { signal: controller.signal } : void 0
|
|
943
1002
|
);
|
|
944
1003
|
} catch (err) {
|
|
945
|
-
|
|
1004
|
+
applyError((err == null ? void 0 : err.message) || "Validation failed");
|
|
946
1005
|
return null;
|
|
947
1006
|
}
|
|
948
1007
|
if (asyncPromises.length === 0) {
|
|
949
|
-
asyncAbortRef.current.delete(
|
|
1008
|
+
asyncAbortRef.current.delete(validationKey);
|
|
950
1009
|
return null;
|
|
951
1010
|
}
|
|
952
1011
|
const validationPromise = Promise.all(asyncPromises).then(
|
|
953
1012
|
(results) => {
|
|
954
|
-
if (asyncValidationVersionRef.current.get(
|
|
955
|
-
asyncValidationRef.current.delete(
|
|
956
|
-
asyncAbortRef.current.delete(
|
|
1013
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
1014
|
+
asyncValidationRef.current.delete(validationKey);
|
|
1015
|
+
asyncAbortRef.current.delete(validationKey);
|
|
957
1016
|
setValidatingFields((prev) => {
|
|
958
1017
|
const next = { ...prev };
|
|
959
|
-
delete next[
|
|
1018
|
+
delete next[validationKey];
|
|
960
1019
|
return next;
|
|
961
1020
|
});
|
|
962
1021
|
let err = null;
|
|
@@ -967,50 +1026,128 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
967
1026
|
break;
|
|
968
1027
|
}
|
|
969
1028
|
}
|
|
970
|
-
|
|
1029
|
+
applyError(err);
|
|
971
1030
|
},
|
|
972
1031
|
(rejection) => {
|
|
973
|
-
if (asyncValidationVersionRef.current.get(
|
|
974
|
-
asyncValidationRef.current.delete(
|
|
975
|
-
asyncAbortRef.current.delete(
|
|
1032
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
1033
|
+
asyncValidationRef.current.delete(validationKey);
|
|
1034
|
+
asyncAbortRef.current.delete(validationKey);
|
|
976
1035
|
setValidatingFields((prev) => {
|
|
977
1036
|
const next = { ...prev };
|
|
978
|
-
delete next[
|
|
1037
|
+
delete next[validationKey];
|
|
979
1038
|
return next;
|
|
980
1039
|
});
|
|
981
1040
|
if (rejection && rejection.name === "AbortError") return;
|
|
982
|
-
|
|
1041
|
+
applyError((rejection == null ? void 0 : rejection.message) || "Validation failed");
|
|
983
1042
|
}
|
|
984
1043
|
);
|
|
985
|
-
asyncValidationRef.current.set(
|
|
986
|
-
setValidatingFields((prev) => ({ ...prev, [
|
|
1044
|
+
asyncValidationRef.current.set(validationKey, validationPromise);
|
|
1045
|
+
setValidatingFields((prev) => ({ ...prev, [validationKey]: true }));
|
|
987
1046
|
return validationPromise;
|
|
988
1047
|
},
|
|
989
|
-
[
|
|
1048
|
+
[fieldTypes, validationMessages]
|
|
990
1049
|
);
|
|
991
|
-
const
|
|
1050
|
+
const runAsyncValidation = (0, import_react.useCallback)(
|
|
992
1051
|
(name, value) => {
|
|
993
1052
|
const field = fieldByName.get(name);
|
|
994
|
-
if (!field || field.type === "repeater") return;
|
|
995
|
-
|
|
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;
|
|
996
1068
|
if (debounceMs && debounceMs > 0) {
|
|
997
|
-
const existing = debounceTimersRef.current.get(
|
|
1069
|
+
const existing = debounceTimersRef.current.get(target.validationKey);
|
|
998
1070
|
if (existing) clearTimeout(existing);
|
|
999
1071
|
const timer = setTimeout(() => {
|
|
1000
|
-
debounceTimersRef.current.delete(
|
|
1001
|
-
|
|
1072
|
+
debounceTimersRef.current.delete(target.validationKey);
|
|
1073
|
+
runAsyncValidationTarget(target);
|
|
1002
1074
|
}, debounceMs);
|
|
1003
|
-
debounceTimersRef.current.set(
|
|
1075
|
+
debounceTimersRef.current.set(target.validationKey, timer);
|
|
1004
1076
|
} else {
|
|
1005
|
-
|
|
1077
|
+
runAsyncValidationTarget(target);
|
|
1006
1078
|
}
|
|
1007
1079
|
},
|
|
1008
|
-
[
|
|
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
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
return targets;
|
|
1143
|
+
},
|
|
1144
|
+
[visibleFields, formValues, setRepeaterSubFieldError, updateErrors]
|
|
1009
1145
|
);
|
|
1010
1146
|
const commitValues = (0, import_react.useCallback)(
|
|
1011
1147
|
(nextValues) => {
|
|
1012
1148
|
formValuesRef.current = nextValues;
|
|
1013
1149
|
if (values != null) {
|
|
1150
|
+
controlledBaselineLockedRef.current = true;
|
|
1014
1151
|
if (onChange) onChange(nextValues);
|
|
1015
1152
|
} else {
|
|
1016
1153
|
setInternalValues(nextValues);
|
|
@@ -1028,7 +1165,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1028
1165
|
[commitValues]
|
|
1029
1166
|
);
|
|
1030
1167
|
const handleFieldChange = (0, import_react.useCallback)(
|
|
1031
|
-
(name, value) => {
|
|
1168
|
+
(name, value, options = {}) => {
|
|
1169
|
+
const { clearNestedErrors = true } = options;
|
|
1032
1170
|
const newValues = { ...formValuesRef.current, [name]: value };
|
|
1033
1171
|
const queue = [name];
|
|
1034
1172
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -1069,9 +1207,11 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1069
1207
|
if (formErrorsRef.current[name] != null) {
|
|
1070
1208
|
clearedErrors[name] = null;
|
|
1071
1209
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1210
|
+
if (clearNestedErrors) {
|
|
1211
|
+
for (const key of Object.keys(formErrorsRef.current)) {
|
|
1212
|
+
if (key.startsWith(`${name}[`)) {
|
|
1213
|
+
clearedErrors[key] = null;
|
|
1214
|
+
}
|
|
1075
1215
|
}
|
|
1076
1216
|
}
|
|
1077
1217
|
draftValuesRef.current = newValues;
|
|
@@ -1140,7 +1280,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1140
1280
|
replaceErrors(errors);
|
|
1141
1281
|
return;
|
|
1142
1282
|
}
|
|
1143
|
-
const asyncSubmitValidations = allVisibleFields.map((
|
|
1283
|
+
const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
1144
1284
|
if (asyncSubmitValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
1145
1285
|
const pendingValidations = [
|
|
1146
1286
|
.../* @__PURE__ */ new Set([
|
|
@@ -1155,6 +1295,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1155
1295
|
const reset = () => {
|
|
1156
1296
|
const fresh = computeInitialValues();
|
|
1157
1297
|
if (values == null) setInternalValues(fresh);
|
|
1298
|
+
controlledBaselineLockedRef.current = false;
|
|
1158
1299
|
replaceErrors({});
|
|
1159
1300
|
initialSnapshot.current = deepClone(fresh);
|
|
1160
1301
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -1162,7 +1303,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1162
1303
|
const rawValues = {};
|
|
1163
1304
|
for (const key of Object.keys(formValues)) {
|
|
1164
1305
|
const f = fieldByName.get(key);
|
|
1165
|
-
if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
|
|
1306
|
+
if (f && (f.type === "display" || f.type === "slot" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
|
|
1166
1307
|
rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
|
|
1167
1308
|
}
|
|
1168
1309
|
for (const f of fields) {
|
|
@@ -1194,7 +1335,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1194
1335
|
if (controlledLoading == null) setInternalLoading(false);
|
|
1195
1336
|
}
|
|
1196
1337
|
},
|
|
1197
|
-
[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]
|
|
1198
1339
|
);
|
|
1199
1340
|
const handleNext = (0, import_react.useCallback)(async () => {
|
|
1200
1341
|
if (!isMultiStep) return;
|
|
@@ -1206,7 +1347,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1206
1347
|
replaceErrors({ ...formErrorsRef.current, ...errors });
|
|
1207
1348
|
return;
|
|
1208
1349
|
}
|
|
1209
|
-
const asyncStepValidations = stepFields.map((
|
|
1350
|
+
const asyncStepValidations = getAsyncValidationTargets(stepFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
1210
1351
|
if (asyncStepValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
1211
1352
|
const pendingValidations = [
|
|
1212
1353
|
.../* @__PURE__ */ new Set([
|
|
@@ -1231,7 +1372,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1231
1372
|
} else {
|
|
1232
1373
|
setInternalStep(nextStep);
|
|
1233
1374
|
}
|
|
1234
|
-
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields,
|
|
1375
|
+
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields, getAsyncValidationTargets, runAsyncValidationTarget]);
|
|
1235
1376
|
const handleBack = (0, import_react.useCallback)(() => {
|
|
1236
1377
|
if (!isMultiStep) return;
|
|
1237
1378
|
const prevStep = Math.max(currentStep - 1, 0);
|
|
@@ -1263,6 +1404,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1263
1404
|
reset: () => {
|
|
1264
1405
|
const fresh = computeInitialValues();
|
|
1265
1406
|
if (values == null) setInternalValues(fresh);
|
|
1407
|
+
controlledBaselineLockedRef.current = false;
|
|
1266
1408
|
replaceErrors({});
|
|
1267
1409
|
initialSnapshot.current = deepClone(fresh);
|
|
1268
1410
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -1275,30 +1417,6 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1275
1417
|
replaceErrors(errors);
|
|
1276
1418
|
}
|
|
1277
1419
|
}));
|
|
1278
|
-
const setRepeaterSubFieldError = (0, import_react.useCallback)(
|
|
1279
|
-
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
1280
|
-
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
1281
|
-
const merged = { ...formErrorsRef.current };
|
|
1282
|
-
if (errorMessage) {
|
|
1283
|
-
merged[key] = errorMessage;
|
|
1284
|
-
} else {
|
|
1285
|
-
delete merged[key];
|
|
1286
|
-
}
|
|
1287
|
-
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
1288
|
-
const match = k.match(/\[(\d+)\]\./);
|
|
1289
|
-
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
1290
|
-
return { key: k, row };
|
|
1291
|
-
}).sort((a, b) => a.row - b.row);
|
|
1292
|
-
if (subErrors.length > 0) {
|
|
1293
|
-
const first = subErrors[0];
|
|
1294
|
-
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
1295
|
-
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
1296
|
-
delete merged[fieldName];
|
|
1297
|
-
}
|
|
1298
|
-
replaceErrors(merged);
|
|
1299
|
-
},
|
|
1300
|
-
[replaceErrors]
|
|
1301
|
-
);
|
|
1302
1420
|
const renderField = (field) => {
|
|
1303
1421
|
const fieldError = formErrors[field.name] || null;
|
|
1304
1422
|
const rendered = renderFieldInner(field);
|
|
@@ -1313,7 +1431,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1313
1431
|
const isReadOnly = field.readOnly || formReadOnly;
|
|
1314
1432
|
const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
|
|
1315
1433
|
const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
|
|
1316
|
-
if (field.type === "display") {
|
|
1434
|
+
if (field.type === "display" || field.type === "slot") {
|
|
1317
1435
|
if (field.render) {
|
|
1318
1436
|
return field.render({
|
|
1319
1437
|
values: formValues,
|
|
@@ -1778,12 +1896,13 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1778
1896
|
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
1779
1897
|
const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
1780
1898
|
setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
|
|
1899
|
+
return err;
|
|
1781
1900
|
};
|
|
1782
1901
|
const handleSubFieldChange = (rowIdx, subField, subValue) => {
|
|
1783
1902
|
const updated = rows.map(
|
|
1784
1903
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
1785
1904
|
);
|
|
1786
|
-
handleFieldChange(field.name, updated);
|
|
1905
|
+
handleFieldChange(field.name, updated, { clearNestedErrors: false });
|
|
1787
1906
|
if (validateOnChange) {
|
|
1788
1907
|
validateSubField(rowIdx, subField, subValue, updated);
|
|
1789
1908
|
}
|
|
@@ -1793,13 +1912,24 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1793
1912
|
const nextRows = rows.map(
|
|
1794
1913
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
1795
1914
|
);
|
|
1796
|
-
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
|
+
});
|
|
1797
1926
|
};
|
|
1798
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) => {
|
|
1799
1928
|
const sfValue = row[sf.name];
|
|
1800
1929
|
const sfLabel = rowIdx === 0 ? sf.label : void 0;
|
|
1801
1930
|
const sfOptions = resolveOptions(sf, { ...formValues, [field.name]: rows });
|
|
1802
1931
|
const sfError = formErrors[getRepeaterErrorKey(field.name, rowIdx, sf.name)] || null;
|
|
1932
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, sf.name);
|
|
1803
1933
|
const sfProps = {
|
|
1804
1934
|
name: `${field.name}-${rowIdx}-${sf.name}`,
|
|
1805
1935
|
label: sfLabel,
|
|
@@ -1808,6 +1938,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1808
1938
|
disabled: resolveDisabled(sf, formValues) || isDisabled,
|
|
1809
1939
|
error: !!sfError,
|
|
1810
1940
|
validationMessage: sfError || void 0,
|
|
1941
|
+
...validatingFields[validationKey] ? { loading: true } : {},
|
|
1811
1942
|
...sf.fieldProps || {}
|
|
1812
1943
|
};
|
|
1813
1944
|
let sfElement;
|
|
@@ -1885,7 +2016,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1885
2016
|
const getFieldColSpan = (field) => {
|
|
1886
2017
|
if (field.colSpan != null) return Math.min(field.colSpan, columns);
|
|
1887
2018
|
if (field.width === "full" && columns > 1) return columns;
|
|
1888
|
-
if (columns > 1 && (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
|
|
2019
|
+
if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
|
|
1889
2020
|
return 1;
|
|
1890
2021
|
};
|
|
1891
2022
|
const getDependents = (parentField) => visibleFields.filter(
|
|
@@ -1910,7 +2041,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
1910
2041
|
const colSpan = (field) => {
|
|
1911
2042
|
if (field.colSpan != null) return Math.min(field.colSpan, cols);
|
|
1912
2043
|
if (field.width === "full" && cols > 1) return cols;
|
|
1913
|
-
if (cols > 1 && (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
|
|
2044
|
+
if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
|
|
1914
2045
|
return 1;
|
|
1915
2046
|
};
|
|
1916
2047
|
const flushRow = () => {
|
|
@@ -2074,13 +2205,23 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
|
|
|
2074
2205
|
const elements = [];
|
|
2075
2206
|
for (let i = 0; i < chunks.length; i++) {
|
|
2076
2207
|
const chunk = chunks[i];
|
|
2077
|
-
|
|
2208
|
+
const opts = chunk.group && groups && groups[chunk.group] || {};
|
|
2209
|
+
const showDivider = opts.showDivider !== false;
|
|
2210
|
+
const showLabel = opts.showLabel !== false;
|
|
2211
|
+
if (i > 0 && showDivider) {
|
|
2078
2212
|
elements.push(/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Divider, { key: `group-div-${i}` }));
|
|
2079
2213
|
}
|
|
2080
|
-
if (chunk.group) {
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2214
|
+
if (chunk.group && showLabel) {
|
|
2215
|
+
if (typeof opts.renderHeader === "function") {
|
|
2216
|
+
const header = opts.renderHeader(chunk.group, chunk.fields, formValues);
|
|
2217
|
+
if (header) elements.push(
|
|
2218
|
+
/* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: `group-header-${i}` }, header)
|
|
2219
|
+
);
|
|
2220
|
+
} else {
|
|
2221
|
+
elements.push(
|
|
2222
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, opts.label || chunk.group)
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2084
2225
|
}
|
|
2085
2226
|
const chunkElements = renderFn(chunk.fields);
|
|
2086
2227
|
if (Array.isArray(chunkElements)) {
|