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/index.js
CHANGED
|
@@ -509,28 +509,33 @@ var DataTable = ({
|
|
|
509
509
|
return next;
|
|
510
510
|
});
|
|
511
511
|
}, []);
|
|
512
|
-
const
|
|
513
|
-
if (!groupedData) return
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (expandedGroups.has(group.key)) {
|
|
518
|
-
group.rows.forEach((row) => flat.push({ type: "data", row }));
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
return flat;
|
|
522
|
-
}, [groupedData, sortedData, data, serverSide, expandedGroups]);
|
|
523
|
-
const totalItems = serverSide ? totalCount || data.length : flatRows.length;
|
|
512
|
+
const datasetRows = (0, import_react.useMemo)(() => {
|
|
513
|
+
if (!groupedData) return serverSide ? data : sortedData;
|
|
514
|
+
return groupedData.flatMap((group) => group.rows);
|
|
515
|
+
}, [groupedData, sortedData, data, serverSide]);
|
|
516
|
+
const totalItems = serverSide ? totalCount || data.length : datasetRows.length;
|
|
524
517
|
const pageCount = Math.ceil(totalItems / pageSize);
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
} else {
|
|
529
|
-
displayRows = flatRows.slice(
|
|
518
|
+
const paginatedRows = (0, import_react.useMemo)(() => {
|
|
519
|
+
if (serverSide) return datasetRows;
|
|
520
|
+
return datasetRows.slice(
|
|
530
521
|
(activePage - 1) * pageSize,
|
|
531
522
|
activePage * pageSize
|
|
532
523
|
);
|
|
533
|
-
}
|
|
524
|
+
}, [serverSide, datasetRows, activePage, pageSize]);
|
|
525
|
+
const displayRows = (0, import_react.useMemo)(() => {
|
|
526
|
+
if (!groupedData) return paginatedRows.map((row) => ({ type: "data", row }));
|
|
527
|
+
const pageRows = new Set(paginatedRows);
|
|
528
|
+
const rows = [];
|
|
529
|
+
groupedData.forEach((group) => {
|
|
530
|
+
const groupPageRows = group.rows.filter((row) => pageRows.has(row));
|
|
531
|
+
if (groupPageRows.length === 0) return;
|
|
532
|
+
rows.push({ type: "group-header", group });
|
|
533
|
+
if (expandedGroups.has(group.key)) {
|
|
534
|
+
groupPageRows.forEach((row) => rows.push({ type: "data", row }));
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
return rows;
|
|
538
|
+
}, [groupedData, paginatedRows, expandedGroups]);
|
|
534
539
|
const footerData = serverSide ? data : filteredData;
|
|
535
540
|
const activeChips = (0, import_react.useMemo)(() => {
|
|
536
541
|
const chips = [];
|
|
@@ -641,8 +646,8 @@ var DataTable = ({
|
|
|
641
646
|
return displayRows.filter((r) => r.type === "data").map((r) => r.row[rowIdField]).filter((id) => id != null);
|
|
642
647
|
}, [serverSide, data, displayRows, rowIdField]);
|
|
643
648
|
const allRowIds = (0, import_react.useMemo)(
|
|
644
|
-
() =>
|
|
645
|
-
[
|
|
649
|
+
() => datasetRows.map((row) => row[rowIdField]).filter((id) => id != null),
|
|
650
|
+
[datasetRows, rowIdField]
|
|
646
651
|
);
|
|
647
652
|
const handleSelectRow = (0, import_react.useCallback)((rowId, checked) => {
|
|
648
653
|
const next = new Set(selectedIds);
|
|
@@ -689,19 +694,25 @@ var DataTable = ({
|
|
|
689
694
|
if (row) onEditStart(row, field, currentValue);
|
|
690
695
|
}
|
|
691
696
|
}, [onEditStart, data, rowIdField]);
|
|
692
|
-
const commitEdit = (0, import_react.useCallback)((row, field, value) => {
|
|
697
|
+
const commitEdit = (0, import_react.useCallback)((row, field, value, options = {}) => {
|
|
698
|
+
const { keepEditing = false } = options;
|
|
693
699
|
const col = columns.find((c) => c.field === field);
|
|
694
700
|
if (col == null ? void 0 : col.editValidate) {
|
|
695
701
|
const result = col.editValidate(value, row);
|
|
696
702
|
if (result !== true && result !== void 0 && result !== null) {
|
|
697
703
|
setEditError(typeof result === "string" ? result : "Invalid value");
|
|
698
|
-
return;
|
|
704
|
+
return false;
|
|
699
705
|
}
|
|
700
706
|
}
|
|
701
707
|
if (onRowEdit) onRowEdit(row, field, value);
|
|
702
|
-
|
|
703
|
-
|
|
708
|
+
if (!keepEditing) {
|
|
709
|
+
setEditingCell(null);
|
|
710
|
+
setEditValue(null);
|
|
711
|
+
} else {
|
|
712
|
+
setEditValue(value);
|
|
713
|
+
}
|
|
704
714
|
setEditError(null);
|
|
715
|
+
return true;
|
|
705
716
|
}, [onRowEdit, columns]);
|
|
706
717
|
const renderEditControl = (col, row) => {
|
|
707
718
|
const type = col.editType || "text";
|
|
@@ -760,12 +771,12 @@ var DataTable = ({
|
|
|
760
771
|
case "datetime":
|
|
761
772
|
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.DateInput, { ...extra, name: `${fieldName}-date`, label: "", value: editValue == null ? void 0 : editValue.date, onChange: (val) => {
|
|
762
773
|
const next = { ...editValue, date: val };
|
|
763
|
-
|
|
764
|
-
|
|
774
|
+
handleInput(next);
|
|
775
|
+
commitEdit(row, col.field, next, { keepEditing: true });
|
|
765
776
|
}, onBlur: maybeExitDatetimeEdit }), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.TimeInput, { ...extra.timeProps || {}, name: `${fieldName}-time`, label: "", value: editValue == null ? void 0 : editValue.time, onChange: (val) => {
|
|
766
777
|
const next = { ...editValue, time: val };
|
|
767
|
-
|
|
768
|
-
|
|
778
|
+
handleInput(next);
|
|
779
|
+
commitEdit(row, col.field, next, { keepEditing: true });
|
|
769
780
|
}, onBlur: maybeExitDatetimeEdit }));
|
|
770
781
|
case "toggle":
|
|
771
782
|
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Toggle, { ...extra, name: fieldName, label: "", checked: !!editValue, onChange: commit });
|
|
@@ -1738,9 +1749,10 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1738
1749
|
const inputDebounceRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
1739
1750
|
const rowKeyRef = (0, import_react2.useRef)(/* @__PURE__ */ new WeakMap());
|
|
1740
1751
|
const rowKeyCounterRef = (0, import_react2.useRef)(0);
|
|
1752
|
+
const controlledBaselineLockedRef = (0, import_react2.useRef)(false);
|
|
1741
1753
|
const initialSnapshot = (0, import_react2.useRef)(null);
|
|
1742
1754
|
if (initialSnapshot.current === null) {
|
|
1743
|
-
initialSnapshot.current = deepClone(computeInitialValues());
|
|
1755
|
+
initialSnapshot.current = deepClone(values != null ? values : computeInitialValues());
|
|
1744
1756
|
}
|
|
1745
1757
|
const formValues = values != null ? values : internalValues;
|
|
1746
1758
|
const formErrors = controlledErrors != null ? controlledErrors : internalErrors;
|
|
@@ -1752,6 +1764,10 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1752
1764
|
const draftValuesRef = (0, import_react2.useRef)(null);
|
|
1753
1765
|
formValuesRef.current = formValues;
|
|
1754
1766
|
formErrorsRef.current = formErrors;
|
|
1767
|
+
const syncDirtyBaseline = (0, import_react2.useCallback)((nextValues) => {
|
|
1768
|
+
initialSnapshot.current = deepClone(nextValues || {});
|
|
1769
|
+
prevAutoSaveValues.current = deepClone(nextValues || {});
|
|
1770
|
+
}, []);
|
|
1755
1771
|
const fieldByName = (0, import_react2.useMemo)(() => {
|
|
1756
1772
|
const map = /* @__PURE__ */ new Map();
|
|
1757
1773
|
for (const field of fields) {
|
|
@@ -1828,6 +1844,11 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1828
1844
|
if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
|
|
1829
1845
|
};
|
|
1830
1846
|
}, []);
|
|
1847
|
+
(0, import_react2.useEffect)(() => {
|
|
1848
|
+
if (values == null) return;
|
|
1849
|
+
if (controlledBaselineLockedRef.current) return;
|
|
1850
|
+
syncDirtyBaseline(values);
|
|
1851
|
+
}, [values, syncDirtyBaseline]);
|
|
1831
1852
|
const isDirty = (0, import_react2.useMemo)(() => {
|
|
1832
1853
|
return !deepEqual(formValues, initialSnapshot.current);
|
|
1833
1854
|
}, [formValues]);
|
|
@@ -1946,6 +1967,50 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1946
1967
|
},
|
|
1947
1968
|
[fieldTypes]
|
|
1948
1969
|
);
|
|
1970
|
+
const setRepeaterSubFieldError = (0, import_react2.useCallback)(
|
|
1971
|
+
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
1972
|
+
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
1973
|
+
const merged = { ...formErrorsRef.current };
|
|
1974
|
+
if (errorMessage) {
|
|
1975
|
+
merged[key] = errorMessage;
|
|
1976
|
+
} else {
|
|
1977
|
+
delete merged[key];
|
|
1978
|
+
}
|
|
1979
|
+
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
1980
|
+
const match = k.match(/\[(\d+)\]\./);
|
|
1981
|
+
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
1982
|
+
return { key: k, row };
|
|
1983
|
+
}).sort((a, b) => a.row - b.row);
|
|
1984
|
+
if (subErrors.length > 0) {
|
|
1985
|
+
const first = subErrors[0];
|
|
1986
|
+
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
1987
|
+
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
1988
|
+
delete merged[fieldName];
|
|
1989
|
+
}
|
|
1990
|
+
replaceErrors(merged);
|
|
1991
|
+
},
|
|
1992
|
+
[replaceErrors]
|
|
1993
|
+
);
|
|
1994
|
+
const expandValidationFields = (0, import_react2.useCallback)(
|
|
1995
|
+
(fieldSubset) => {
|
|
1996
|
+
const toValidate = fieldSubset || visibleFields;
|
|
1997
|
+
const expanded = [];
|
|
1998
|
+
for (const field of toValidate) {
|
|
1999
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
2000
|
+
for (const item of field.items) {
|
|
2001
|
+
for (const subField of field.fields(item)) {
|
|
2002
|
+
if (subField.visible && !subField.visible(formValues)) continue;
|
|
2003
|
+
expanded.push(subField);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
continue;
|
|
2007
|
+
}
|
|
2008
|
+
expanded.push(field);
|
|
2009
|
+
}
|
|
2010
|
+
return expanded;
|
|
2011
|
+
},
|
|
2012
|
+
[visibleFields, formValues]
|
|
2013
|
+
);
|
|
1949
2014
|
const validateField = (0, import_react2.useCallback)(
|
|
1950
2015
|
(name, value) => {
|
|
1951
2016
|
const field = fieldByName.get(name);
|
|
@@ -1965,7 +2030,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1965
2030
|
);
|
|
1966
2031
|
const validateVisibleFields = (0, import_react2.useCallback)(
|
|
1967
2032
|
(fieldSubset) => {
|
|
1968
|
-
const toValidate = fieldSubset
|
|
2033
|
+
const toValidate = expandValidationFields(fieldSubset);
|
|
1969
2034
|
const errors = {};
|
|
1970
2035
|
let hasErrors = false;
|
|
1971
2036
|
for (const field of toValidate) {
|
|
@@ -1985,52 +2050,54 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1985
2050
|
}
|
|
1986
2051
|
return { errors, hasErrors };
|
|
1987
2052
|
},
|
|
1988
|
-
[
|
|
2053
|
+
[expandValidationFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
1989
2054
|
);
|
|
1990
|
-
const
|
|
1991
|
-
(
|
|
1992
|
-
const field =
|
|
1993
|
-
if (!field || field.type === "repeater") return null;
|
|
1994
|
-
const
|
|
1995
|
-
|
|
1996
|
-
|
|
2055
|
+
const runAsyncValidationTarget = (0, import_react2.useCallback)(
|
|
2056
|
+
(target) => {
|
|
2057
|
+
const { validationKey, field, value, allValues, applyError } = target || {};
|
|
2058
|
+
if (!field || !validationKey || field.type === "repeater" || field.type === "fieldGroup") return null;
|
|
2059
|
+
const syncError = runValidators(value, field, allValues, fieldTypes, {
|
|
2060
|
+
includeCustomValidators: false,
|
|
2061
|
+
messages: validationMessages
|
|
2062
|
+
});
|
|
2063
|
+
const prevController = asyncAbortRef.current.get(validationKey);
|
|
1997
2064
|
if (prevController) prevController.abort();
|
|
1998
|
-
asyncAbortRef.current.delete(
|
|
2065
|
+
asyncAbortRef.current.delete(validationKey);
|
|
1999
2066
|
setValidatingFields((prev) => {
|
|
2000
|
-
if (!prev[
|
|
2067
|
+
if (!prev[validationKey]) return prev;
|
|
2001
2068
|
const next = { ...prev };
|
|
2002
|
-
delete next[
|
|
2069
|
+
delete next[validationKey];
|
|
2003
2070
|
return next;
|
|
2004
2071
|
});
|
|
2005
2072
|
if (syncError) return null;
|
|
2006
|
-
const version = (asyncValidationVersionRef.current.get(
|
|
2007
|
-
asyncValidationVersionRef.current.set(
|
|
2073
|
+
const version = (asyncValidationVersionRef.current.get(validationKey) || 0) + 1;
|
|
2074
|
+
asyncValidationVersionRef.current.set(validationKey, version);
|
|
2008
2075
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
2009
|
-
if (controller) asyncAbortRef.current.set(
|
|
2076
|
+
if (controller) asyncAbortRef.current.set(validationKey, controller);
|
|
2010
2077
|
let asyncPromises;
|
|
2011
2078
|
try {
|
|
2012
2079
|
asyncPromises = collectAsyncValidatorPromises(
|
|
2013
|
-
|
|
2080
|
+
value,
|
|
2014
2081
|
field,
|
|
2015
|
-
|
|
2082
|
+
allValues,
|
|
2016
2083
|
controller ? { signal: controller.signal } : void 0
|
|
2017
2084
|
);
|
|
2018
2085
|
} catch (err) {
|
|
2019
|
-
|
|
2086
|
+
applyError((err == null ? void 0 : err.message) || "Validation failed");
|
|
2020
2087
|
return null;
|
|
2021
2088
|
}
|
|
2022
2089
|
if (asyncPromises.length === 0) {
|
|
2023
|
-
asyncAbortRef.current.delete(
|
|
2090
|
+
asyncAbortRef.current.delete(validationKey);
|
|
2024
2091
|
return null;
|
|
2025
2092
|
}
|
|
2026
2093
|
const validationPromise = Promise.all(asyncPromises).then(
|
|
2027
2094
|
(results) => {
|
|
2028
|
-
if (asyncValidationVersionRef.current.get(
|
|
2029
|
-
asyncValidationRef.current.delete(
|
|
2030
|
-
asyncAbortRef.current.delete(
|
|
2095
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
2096
|
+
asyncValidationRef.current.delete(validationKey);
|
|
2097
|
+
asyncAbortRef.current.delete(validationKey);
|
|
2031
2098
|
setValidatingFields((prev) => {
|
|
2032
2099
|
const next = { ...prev };
|
|
2033
|
-
delete next[
|
|
2100
|
+
delete next[validationKey];
|
|
2034
2101
|
return next;
|
|
2035
2102
|
});
|
|
2036
2103
|
let err = null;
|
|
@@ -2041,50 +2108,128 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2041
2108
|
break;
|
|
2042
2109
|
}
|
|
2043
2110
|
}
|
|
2044
|
-
|
|
2111
|
+
applyError(err);
|
|
2045
2112
|
},
|
|
2046
2113
|
(rejection) => {
|
|
2047
|
-
if (asyncValidationVersionRef.current.get(
|
|
2048
|
-
asyncValidationRef.current.delete(
|
|
2049
|
-
asyncAbortRef.current.delete(
|
|
2114
|
+
if (asyncValidationVersionRef.current.get(validationKey) !== version) return;
|
|
2115
|
+
asyncValidationRef.current.delete(validationKey);
|
|
2116
|
+
asyncAbortRef.current.delete(validationKey);
|
|
2050
2117
|
setValidatingFields((prev) => {
|
|
2051
2118
|
const next = { ...prev };
|
|
2052
|
-
delete next[
|
|
2119
|
+
delete next[validationKey];
|
|
2053
2120
|
return next;
|
|
2054
2121
|
});
|
|
2055
2122
|
if (rejection && rejection.name === "AbortError") return;
|
|
2056
|
-
|
|
2123
|
+
applyError((rejection == null ? void 0 : rejection.message) || "Validation failed");
|
|
2057
2124
|
}
|
|
2058
2125
|
);
|
|
2059
|
-
asyncValidationRef.current.set(
|
|
2060
|
-
setValidatingFields((prev) => ({ ...prev, [
|
|
2126
|
+
asyncValidationRef.current.set(validationKey, validationPromise);
|
|
2127
|
+
setValidatingFields((prev) => ({ ...prev, [validationKey]: true }));
|
|
2061
2128
|
return validationPromise;
|
|
2062
2129
|
},
|
|
2063
|
-
[
|
|
2130
|
+
[fieldTypes, validationMessages]
|
|
2064
2131
|
);
|
|
2065
|
-
const
|
|
2132
|
+
const runAsyncValidation = (0, import_react2.useCallback)(
|
|
2066
2133
|
(name, value) => {
|
|
2067
2134
|
const field = fieldByName.get(name);
|
|
2068
|
-
if (!field || field.type === "repeater") return;
|
|
2069
|
-
|
|
2135
|
+
if (!field || field.type === "repeater" || field.type === "fieldGroup") return null;
|
|
2136
|
+
return runAsyncValidationTarget({
|
|
2137
|
+
validationKey: name,
|
|
2138
|
+
field,
|
|
2139
|
+
value: value != null ? value : formValues[name],
|
|
2140
|
+
allValues: formValues,
|
|
2141
|
+
applyError: (errorMessage) => updateErrors({ [name]: errorMessage })
|
|
2142
|
+
});
|
|
2143
|
+
},
|
|
2144
|
+
[fieldByName, formValues, runAsyncValidationTarget, updateErrors]
|
|
2145
|
+
);
|
|
2146
|
+
const triggerAsyncValidationTarget = (0, import_react2.useCallback)(
|
|
2147
|
+
(target) => {
|
|
2148
|
+
if (!(target == null ? void 0 : target.field) || !target.validationKey) return;
|
|
2149
|
+
const debounceMs = target.field.validateDebounce;
|
|
2070
2150
|
if (debounceMs && debounceMs > 0) {
|
|
2071
|
-
const existing = debounceTimersRef.current.get(
|
|
2151
|
+
const existing = debounceTimersRef.current.get(target.validationKey);
|
|
2072
2152
|
if (existing) clearTimeout(existing);
|
|
2073
2153
|
const timer = setTimeout(() => {
|
|
2074
|
-
debounceTimersRef.current.delete(
|
|
2075
|
-
|
|
2154
|
+
debounceTimersRef.current.delete(target.validationKey);
|
|
2155
|
+
runAsyncValidationTarget(target);
|
|
2076
2156
|
}, debounceMs);
|
|
2077
|
-
debounceTimersRef.current.set(
|
|
2157
|
+
debounceTimersRef.current.set(target.validationKey, timer);
|
|
2078
2158
|
} else {
|
|
2079
|
-
|
|
2159
|
+
runAsyncValidationTarget(target);
|
|
2080
2160
|
}
|
|
2081
2161
|
},
|
|
2082
|
-
[
|
|
2162
|
+
[runAsyncValidationTarget]
|
|
2163
|
+
);
|
|
2164
|
+
const triggerAsyncValidation = (0, import_react2.useCallback)(
|
|
2165
|
+
(name, value) => {
|
|
2166
|
+
const field = fieldByName.get(name);
|
|
2167
|
+
if (!field || field.type === "repeater" || field.type === "fieldGroup") return;
|
|
2168
|
+
triggerAsyncValidationTarget({
|
|
2169
|
+
validationKey: name,
|
|
2170
|
+
field,
|
|
2171
|
+
value: value != null ? value : formValuesRef.current[name],
|
|
2172
|
+
allValues: formValuesRef.current,
|
|
2173
|
+
applyError: (errorMessage) => updateErrors({ [name]: errorMessage })
|
|
2174
|
+
});
|
|
2175
|
+
},
|
|
2176
|
+
[fieldByName, triggerAsyncValidationTarget, updateErrors]
|
|
2177
|
+
);
|
|
2178
|
+
const getAsyncValidationTargets = (0, import_react2.useCallback)(
|
|
2179
|
+
(fieldSubset) => {
|
|
2180
|
+
const toValidate = fieldSubset || visibleFields;
|
|
2181
|
+
const targets = [];
|
|
2182
|
+
for (const field of toValidate) {
|
|
2183
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
2184
|
+
for (const item of field.items) {
|
|
2185
|
+
for (const subField of field.fields(item)) {
|
|
2186
|
+
if (subField.visible && !subField.visible(formValues)) continue;
|
|
2187
|
+
targets.push({
|
|
2188
|
+
validationKey: subField.name,
|
|
2189
|
+
field: subField,
|
|
2190
|
+
value: formValues[subField.name],
|
|
2191
|
+
allValues: formValues,
|
|
2192
|
+
applyError: (errorMessage) => updateErrors({ [subField.name]: errorMessage })
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2198
|
+
if (field.type === "repeater") {
|
|
2199
|
+
const rows = Array.isArray(formValues[field.name]) ? formValues[field.name] : [];
|
|
2200
|
+
const subFields = field.fields || [];
|
|
2201
|
+
rows.forEach((row, rowIdx) => {
|
|
2202
|
+
const rowValues = { ...formValues, [field.name]: rows };
|
|
2203
|
+
subFields.forEach((subField) => {
|
|
2204
|
+
if (subField.visible && !subField.visible(rowValues)) return;
|
|
2205
|
+
targets.push({
|
|
2206
|
+
validationKey: getRepeaterErrorKey(field.name, rowIdx, subField.name),
|
|
2207
|
+
field: subField,
|
|
2208
|
+
value: row == null ? void 0 : row[subField.name],
|
|
2209
|
+
allValues: rowValues,
|
|
2210
|
+
applyError: (errorMessage) => setRepeaterSubFieldError(field.name, rowIdx, subField.name, errorMessage)
|
|
2211
|
+
});
|
|
2212
|
+
});
|
|
2213
|
+
});
|
|
2214
|
+
continue;
|
|
2215
|
+
}
|
|
2216
|
+
targets.push({
|
|
2217
|
+
validationKey: field.name,
|
|
2218
|
+
field,
|
|
2219
|
+
value: formValues[field.name],
|
|
2220
|
+
allValues: formValues,
|
|
2221
|
+
applyError: (errorMessage) => updateErrors({ [field.name]: errorMessage })
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
return targets;
|
|
2225
|
+
},
|
|
2226
|
+
[visibleFields, formValues, setRepeaterSubFieldError, updateErrors]
|
|
2083
2227
|
);
|
|
2084
2228
|
const commitValues = (0, import_react2.useCallback)(
|
|
2085
2229
|
(nextValues) => {
|
|
2086
2230
|
formValuesRef.current = nextValues;
|
|
2087
2231
|
if (values != null) {
|
|
2232
|
+
controlledBaselineLockedRef.current = true;
|
|
2088
2233
|
if (onChange) onChange(nextValues);
|
|
2089
2234
|
} else {
|
|
2090
2235
|
setInternalValues(nextValues);
|
|
@@ -2102,7 +2247,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2102
2247
|
[commitValues]
|
|
2103
2248
|
);
|
|
2104
2249
|
const handleFieldChange = (0, import_react2.useCallback)(
|
|
2105
|
-
(name, value) => {
|
|
2250
|
+
(name, value, options = {}) => {
|
|
2251
|
+
const { clearNestedErrors = true } = options;
|
|
2106
2252
|
const newValues = { ...formValuesRef.current, [name]: value };
|
|
2107
2253
|
const queue = [name];
|
|
2108
2254
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -2143,9 +2289,11 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2143
2289
|
if (formErrorsRef.current[name] != null) {
|
|
2144
2290
|
clearedErrors[name] = null;
|
|
2145
2291
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2292
|
+
if (clearNestedErrors) {
|
|
2293
|
+
for (const key of Object.keys(formErrorsRef.current)) {
|
|
2294
|
+
if (key.startsWith(`${name}[`)) {
|
|
2295
|
+
clearedErrors[key] = null;
|
|
2296
|
+
}
|
|
2149
2297
|
}
|
|
2150
2298
|
}
|
|
2151
2299
|
draftValuesRef.current = newValues;
|
|
@@ -2214,7 +2362,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2214
2362
|
replaceErrors(errors);
|
|
2215
2363
|
return;
|
|
2216
2364
|
}
|
|
2217
|
-
const asyncSubmitValidations = allVisibleFields.map((
|
|
2365
|
+
const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
2218
2366
|
if (asyncSubmitValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
2219
2367
|
const pendingValidations = [
|
|
2220
2368
|
.../* @__PURE__ */ new Set([
|
|
@@ -2229,6 +2377,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2229
2377
|
const reset = () => {
|
|
2230
2378
|
const fresh = computeInitialValues();
|
|
2231
2379
|
if (values == null) setInternalValues(fresh);
|
|
2380
|
+
controlledBaselineLockedRef.current = false;
|
|
2232
2381
|
replaceErrors({});
|
|
2233
2382
|
initialSnapshot.current = deepClone(fresh);
|
|
2234
2383
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -2268,7 +2417,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2268
2417
|
if (controlledLoading == null) setInternalLoading(false);
|
|
2269
2418
|
}
|
|
2270
2419
|
},
|
|
2271
|
-
[validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName,
|
|
2420
|
+
[validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
|
|
2272
2421
|
);
|
|
2273
2422
|
const handleNext = (0, import_react2.useCallback)(async () => {
|
|
2274
2423
|
if (!isMultiStep) return;
|
|
@@ -2280,7 +2429,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2280
2429
|
replaceErrors({ ...formErrorsRef.current, ...errors });
|
|
2281
2430
|
return;
|
|
2282
2431
|
}
|
|
2283
|
-
const asyncStepValidations = stepFields.map((
|
|
2432
|
+
const asyncStepValidations = getAsyncValidationTargets(stepFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
|
|
2284
2433
|
if (asyncStepValidations.length > 0 || asyncValidationRef.current.size > 0) {
|
|
2285
2434
|
const pendingValidations = [
|
|
2286
2435
|
.../* @__PURE__ */ new Set([
|
|
@@ -2305,7 +2454,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2305
2454
|
} else {
|
|
2306
2455
|
setInternalStep(nextStep);
|
|
2307
2456
|
}
|
|
2308
|
-
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields,
|
|
2457
|
+
}, [isMultiStep, validateStepOnNext, steps, currentStep, formValues, validateVisibleFields, controlledStep, onStepChange, replaceErrors, allVisibleFields, getAsyncValidationTargets, runAsyncValidationTarget]);
|
|
2309
2458
|
const handleBack = (0, import_react2.useCallback)(() => {
|
|
2310
2459
|
if (!isMultiStep) return;
|
|
2311
2460
|
const prevStep = Math.max(currentStep - 1, 0);
|
|
@@ -2337,6 +2486,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2337
2486
|
reset: () => {
|
|
2338
2487
|
const fresh = computeInitialValues();
|
|
2339
2488
|
if (values == null) setInternalValues(fresh);
|
|
2489
|
+
controlledBaselineLockedRef.current = false;
|
|
2340
2490
|
replaceErrors({});
|
|
2341
2491
|
initialSnapshot.current = deepClone(fresh);
|
|
2342
2492
|
prevAutoSaveValues.current = deepClone(fresh);
|
|
@@ -2349,30 +2499,6 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2349
2499
|
replaceErrors(errors);
|
|
2350
2500
|
}
|
|
2351
2501
|
}));
|
|
2352
|
-
const setRepeaterSubFieldError = (0, import_react2.useCallback)(
|
|
2353
|
-
(fieldName, rowIdx, subFieldName, errorMessage) => {
|
|
2354
|
-
const key = getRepeaterErrorKey(fieldName, rowIdx, subFieldName);
|
|
2355
|
-
const merged = { ...formErrorsRef.current };
|
|
2356
|
-
if (errorMessage) {
|
|
2357
|
-
merged[key] = errorMessage;
|
|
2358
|
-
} else {
|
|
2359
|
-
delete merged[key];
|
|
2360
|
-
}
|
|
2361
|
-
const subErrors = Object.keys(merged).filter((k) => k.startsWith(`${fieldName}[`)).map((k) => {
|
|
2362
|
-
const match = k.match(/\[(\d+)\]\./);
|
|
2363
|
-
const row = match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
2364
|
-
return { key: k, row };
|
|
2365
|
-
}).sort((a, b) => a.row - b.row);
|
|
2366
|
-
if (subErrors.length > 0) {
|
|
2367
|
-
const first = subErrors[0];
|
|
2368
|
-
merged[fieldName] = `Row ${first.row + 1}: ${merged[first.key]}`;
|
|
2369
|
-
} else if (!merged[fieldName] || merged[fieldName].startsWith("Row ")) {
|
|
2370
|
-
delete merged[fieldName];
|
|
2371
|
-
}
|
|
2372
|
-
replaceErrors(merged);
|
|
2373
|
-
},
|
|
2374
|
-
[replaceErrors]
|
|
2375
|
-
);
|
|
2376
2502
|
const renderField = (field) => {
|
|
2377
2503
|
const fieldError = formErrors[field.name] || null;
|
|
2378
2504
|
const rendered = renderFieldInner(field);
|
|
@@ -2852,12 +2978,13 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2852
2978
|
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
2853
2979
|
const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
2854
2980
|
setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
|
|
2981
|
+
return err;
|
|
2855
2982
|
};
|
|
2856
2983
|
const handleSubFieldChange = (rowIdx, subField, subValue) => {
|
|
2857
2984
|
const updated = rows.map(
|
|
2858
2985
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
2859
2986
|
);
|
|
2860
|
-
handleFieldChange(field.name, updated);
|
|
2987
|
+
handleFieldChange(field.name, updated, { clearNestedErrors: false });
|
|
2861
2988
|
if (validateOnChange) {
|
|
2862
2989
|
validateSubField(rowIdx, subField, subValue, updated);
|
|
2863
2990
|
}
|
|
@@ -2867,13 +2994,24 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2867
2994
|
const nextRows = rows.map(
|
|
2868
2995
|
(row, i) => i === rowIdx ? { ...row, [subField.name]: subValue } : row
|
|
2869
2996
|
);
|
|
2870
|
-
validateSubField(rowIdx, subField, subValue, nextRows);
|
|
2997
|
+
const err = validateSubField(rowIdx, subField, subValue, nextRows);
|
|
2998
|
+
if (err) return;
|
|
2999
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, subField.name);
|
|
3000
|
+
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
3001
|
+
triggerAsyncValidationTarget({
|
|
3002
|
+
validationKey,
|
|
3003
|
+
field: subField,
|
|
3004
|
+
value: subValue,
|
|
3005
|
+
allValues: rowValues,
|
|
3006
|
+
applyError: (errorMessage) => setRepeaterSubFieldError(field.name, rowIdx, subField.name, errorMessage)
|
|
3007
|
+
});
|
|
2871
3008
|
};
|
|
2872
3009
|
return /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "column", gap: "xs" }, field.label && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { format: { fontWeight: "demibold" } }, field.label, isRequired ? " *" : ""), field.description && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { variant: "microcopy" }, field.description), rows.map((row, rowIdx) => /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { key: getRowKey(field.name, row, rowIdx), direction: "row", gap: "xs", align: "end" }, subFields.map((sf) => {
|
|
2873
3010
|
const sfValue = row[sf.name];
|
|
2874
3011
|
const sfLabel = rowIdx === 0 ? sf.label : void 0;
|
|
2875
3012
|
const sfOptions = resolveOptions(sf, { ...formValues, [field.name]: rows });
|
|
2876
3013
|
const sfError = formErrors[getRepeaterErrorKey(field.name, rowIdx, sf.name)] || null;
|
|
3014
|
+
const validationKey = getRepeaterErrorKey(field.name, rowIdx, sf.name);
|
|
2877
3015
|
const sfProps = {
|
|
2878
3016
|
name: `${field.name}-${rowIdx}-${sf.name}`,
|
|
2879
3017
|
label: sfLabel,
|
|
@@ -2882,6 +3020,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2882
3020
|
disabled: resolveDisabled(sf, formValues) || isDisabled,
|
|
2883
3021
|
error: !!sfError,
|
|
2884
3022
|
validationMessage: sfError || void 0,
|
|
3023
|
+
...validatingFields[validationKey] ? { loading: true } : {},
|
|
2885
3024
|
...sf.fieldProps || {}
|
|
2886
3025
|
};
|
|
2887
3026
|
let sfElement;
|