hs-uix 1.0.4 → 1.2.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 +7 -4
- package/dist/datatable.js +89 -22
- package/dist/datatable.mjs +89 -22
- package/dist/form.js +246 -64
- package/dist/form.mjs +246 -64
- package/dist/index.js +335 -86
- package/dist/index.mjs +335 -86
- package/form.d.ts +1 -0
- package/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -84,12 +84,19 @@ var computeAutoWidths = (columns, data) => {
|
|
|
84
84
|
columns.forEach((col) => {
|
|
85
85
|
if (col.width && col.cellWidth) return;
|
|
86
86
|
const values = sample.map((row) => row[col.field]).filter((v) => v != null);
|
|
87
|
-
const strings = values.map((v) =>
|
|
87
|
+
const strings = values.map((v) => {
|
|
88
|
+
const s = String(v);
|
|
89
|
+
const truncLen = typeof col.truncate === "number" ? col.truncate : col.truncate && typeof col.truncate === "object" ? col.truncate.maxLength : null;
|
|
90
|
+
return truncLen && s.length > truncLen ? s.slice(0, truncLen) : s;
|
|
91
|
+
});
|
|
88
92
|
let widthHint = null;
|
|
89
93
|
let cellWidthHint = null;
|
|
90
94
|
if (col.editable && col.editType && NARROW_EDIT_TYPES.has(col.editType)) {
|
|
91
95
|
cellWidthHint = "min";
|
|
92
96
|
}
|
|
97
|
+
if (col.truncate === true) {
|
|
98
|
+
cellWidthHint = cellWidthHint || "min";
|
|
99
|
+
}
|
|
93
100
|
if (strings.length > 0) {
|
|
94
101
|
const lengths = strings.map((s) => s.length);
|
|
95
102
|
const maxLen = Math.max(...lengths);
|
|
@@ -155,12 +162,16 @@ var DataTable = ({
|
|
|
155
162
|
// enable fuzzy matching via Fuse.js
|
|
156
163
|
fuzzyOptions,
|
|
157
164
|
// custom Fuse.js options (threshold, distance, etc.)
|
|
165
|
+
showSearch = true,
|
|
166
|
+
// show the SearchInput in the toolbar
|
|
158
167
|
// Filters
|
|
159
168
|
filters = [],
|
|
160
169
|
showFilterBadges = true,
|
|
161
170
|
// show active filter chips/badges
|
|
162
171
|
showClearFiltersButton = true,
|
|
163
172
|
// show "Clear all" filters reset button
|
|
173
|
+
filterInlineLimit = 2,
|
|
174
|
+
// number of filters shown inline before overflow
|
|
164
175
|
// Pagination
|
|
165
176
|
pageSize = 10,
|
|
166
177
|
maxVisiblePageButtons,
|
|
@@ -242,6 +253,8 @@ var DataTable = ({
|
|
|
242
253
|
// optional key to force clear uncontrolled selection memory
|
|
243
254
|
resetSelectionOnQueryChange = true,
|
|
244
255
|
// clear uncontrolled selection on search/filter/sort changes
|
|
256
|
+
showSelectionBar = true,
|
|
257
|
+
// show the selection action bar when rows are selected
|
|
245
258
|
recordLabel,
|
|
246
259
|
// { singular: "Contact", plural: "Contacts" } — defaults to Record/Records
|
|
247
260
|
// -----------------------------------------------------------------------
|
|
@@ -262,11 +275,31 @@ var DataTable = ({
|
|
|
262
275
|
// (row, field, newValue) => void
|
|
263
276
|
onRowEditInput,
|
|
264
277
|
// optional live-input callback: (row, field, inputValue) => void
|
|
278
|
+
onEditStart,
|
|
279
|
+
// (row, field, currentValue) => void — fires when editing begins
|
|
280
|
+
onEditCancel,
|
|
281
|
+
// (row, field) => void — fires when editing is cancelled without commit
|
|
265
282
|
// -----------------------------------------------------------------------
|
|
266
283
|
// Auto-width
|
|
267
284
|
// -----------------------------------------------------------------------
|
|
268
|
-
autoWidth = true
|
|
285
|
+
autoWidth = true,
|
|
269
286
|
// auto-compute column widths from content analysis
|
|
287
|
+
// -----------------------------------------------------------------------
|
|
288
|
+
// Labels / i18n
|
|
289
|
+
// -----------------------------------------------------------------------
|
|
290
|
+
labels,
|
|
291
|
+
// override hardcoded UI strings for i18n
|
|
292
|
+
// -----------------------------------------------------------------------
|
|
293
|
+
// Render overrides (Phase 3 — full replacement escape hatches)
|
|
294
|
+
// -----------------------------------------------------------------------
|
|
295
|
+
renderSelectionBar,
|
|
296
|
+
// ({ selectedIds, selectedCount, displayCount, countLabel, onSelectAll, onDeselectAll, selectionActions }) => ReactNode
|
|
297
|
+
renderEmptyState,
|
|
298
|
+
// ({ title, message }) => ReactNode
|
|
299
|
+
renderLoadingState,
|
|
300
|
+
// ({ label }) => ReactNode
|
|
301
|
+
renderErrorState
|
|
302
|
+
// ({ error, title, message }) => ReactNode
|
|
270
303
|
}) => {
|
|
271
304
|
const initialSortState = useMemo(() => {
|
|
272
305
|
return normalizeSortState(columns, defaultSort);
|
|
@@ -500,11 +533,11 @@ var DataTable = ({
|
|
|
500
533
|
const type = filter.type || "select";
|
|
501
534
|
const prefix = filter.chipLabel || filter.placeholder || filter.name;
|
|
502
535
|
if (type === "multiselect") {
|
|
503
|
-
const
|
|
536
|
+
const labels2 = value.map((v) => {
|
|
504
537
|
var _a;
|
|
505
538
|
return ((_a = filter.options.find((o) => o.value === v)) == null ? void 0 : _a.label) || v;
|
|
506
539
|
}).join(", ");
|
|
507
|
-
chips.push({ key: filter.name, label: `${prefix}: ${
|
|
540
|
+
chips.push({ key: filter.name, label: `${prefix}: ${labels2}` });
|
|
508
541
|
} else if (type === "dateRange") {
|
|
509
542
|
const parts = [];
|
|
510
543
|
if (value.from) parts.push(`from ${formatDateChip(value.from)}`);
|
|
@@ -545,7 +578,17 @@ var DataTable = ({
|
|
|
545
578
|
const countLabel = (n) => n === 1 ? singularLabel : pluralLabel;
|
|
546
579
|
const resolvedEmptyTitle = emptyTitle || "No results found";
|
|
547
580
|
const resolvedEmptyMessage = emptyMessage || `No ${pluralLabel} match your search or filter criteria.`;
|
|
548
|
-
const
|
|
581
|
+
const resolvedSelectedLabel = (labels == null ? void 0 : labels.selected) || ((count, label) => `${count}\xA0${label}\xA0selected`);
|
|
582
|
+
const resolvedSelectAllLabel = (labels == null ? void 0 : labels.selectAll) || ((count, label) => `Select all ${count} ${label}`);
|
|
583
|
+
const resolvedDeselectAllLabel = (labels == null ? void 0 : labels.deselectAll) || "Deselect all";
|
|
584
|
+
const resolvedFiltersButtonLabel = (labels == null ? void 0 : labels.filtersButton) || "Filters";
|
|
585
|
+
const resolvedClearAllLabel = (labels == null ? void 0 : labels.clearAll) || "Clear all";
|
|
586
|
+
const resolvedDateFromLabel = (labels == null ? void 0 : labels.dateFrom) || "From";
|
|
587
|
+
const resolvedDateToLabel = (labels == null ? void 0 : labels.dateTo) || "To";
|
|
588
|
+
const resolvedLoadingLabel = (labels == null ? void 0 : labels.loading) || `Loading ${pluralLabel}...`;
|
|
589
|
+
const resolvedErrorTitle = (labels == null ? void 0 : labels.errorTitle) || "Something went wrong.";
|
|
590
|
+
const resolvedErrorMessage = (labels == null ? void 0 : labels.errorMessage) || "An error occurred while loading data.";
|
|
591
|
+
const resolvedRetryMessage = (labels == null ? void 0 : labels.retryMessage) || "Please try again.";
|
|
549
592
|
const recordCountLabel = rowCountText ? rowCountText(shownOnPageCount, displayCount) : displayCount === totalDataCount ? `${totalDataCount} ${countLabel(totalDataCount)}` : `${displayCount} of ${totalDataCount} ${countLabel(totalDataCount)}`;
|
|
550
593
|
const [internalSelectedIds, setInternalSelectedIds] = useState(/* @__PURE__ */ new Set());
|
|
551
594
|
const selectionResetRef = useRef("");
|
|
@@ -634,7 +677,11 @@ var DataTable = ({
|
|
|
634
677
|
setEditingCell({ rowId, field });
|
|
635
678
|
setEditValue(currentValue);
|
|
636
679
|
setEditError(null);
|
|
637
|
-
|
|
680
|
+
if (onEditStart) {
|
|
681
|
+
const row = data.find((r) => r[rowIdField] === rowId);
|
|
682
|
+
if (row) onEditStart(row, field, currentValue);
|
|
683
|
+
}
|
|
684
|
+
}, [onEditStart, data, rowIdField]);
|
|
638
685
|
const commitEdit = useCallback((row, field, value) => {
|
|
639
686
|
const col = columns.find((c) => c.field === field);
|
|
640
687
|
if (col == null ? void 0 : col.editValidate) {
|
|
@@ -656,6 +703,7 @@ var DataTable = ({
|
|
|
656
703
|
const commit = (val) => commitEdit(row, col.field, val);
|
|
657
704
|
const exitEdit = () => {
|
|
658
705
|
if (editError) return;
|
|
706
|
+
if (onEditCancel) onEditCancel(row, col.field);
|
|
659
707
|
setEditingCell(null);
|
|
660
708
|
setEditValue(null);
|
|
661
709
|
};
|
|
@@ -822,20 +870,26 @@ var DataTable = ({
|
|
|
822
870
|
const rawStr = String(rawValue ?? "");
|
|
823
871
|
if (col.truncate && rawStr.length > 0) {
|
|
824
872
|
if (col.truncate === true) {
|
|
825
|
-
|
|
873
|
+
if (col.renderCell) {
|
|
874
|
+
const content2 = col.renderCell(rawValue, row);
|
|
875
|
+
if (col.editable) {
|
|
876
|
+
return /* @__PURE__ */ React.createElement(Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) }, content2 || "--");
|
|
877
|
+
}
|
|
878
|
+
return content2;
|
|
879
|
+
}
|
|
826
880
|
if (col.editable) {
|
|
827
|
-
return /* @__PURE__ */ React.createElement(Text, { truncate: { tooltipText: rawStr } }, /* @__PURE__ */ React.createElement(Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) },
|
|
881
|
+
return /* @__PURE__ */ React.createElement(Text, { truncate: { tooltipText: rawStr } }, /* @__PURE__ */ React.createElement(Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) }, rawStr || "--"));
|
|
828
882
|
}
|
|
829
|
-
return /* @__PURE__ */ React.createElement(Text, { truncate: { tooltipText: rawStr } },
|
|
883
|
+
return /* @__PURE__ */ React.createElement(Text, { truncate: { tooltipText: rawStr } }, rawStr);
|
|
830
884
|
}
|
|
831
|
-
const maxLen = col.truncate.maxLength || 100;
|
|
885
|
+
const maxLen = typeof col.truncate === "number" ? col.truncate : col.truncate.maxLength || 100;
|
|
832
886
|
if (rawStr.length > maxLen) {
|
|
833
887
|
const truncatedStr = rawStr.slice(0, maxLen) + "\u2026";
|
|
834
|
-
const
|
|
888
|
+
const content2 = col.renderCell ? col.renderCell(truncatedStr, row) : truncatedStr;
|
|
835
889
|
if (col.editable) {
|
|
836
|
-
return /* @__PURE__ */ React.createElement(Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) },
|
|
890
|
+
return /* @__PURE__ */ React.createElement(Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) }, content2 || "--");
|
|
837
891
|
}
|
|
838
|
-
return /* @__PURE__ */ React.createElement(Text, { truncate: { tooltipText: rawStr } },
|
|
892
|
+
return col.renderCell ? content2 : /* @__PURE__ */ React.createElement(Text, { truncate: { tooltipText: rawStr } }, content2 || "--");
|
|
839
893
|
}
|
|
840
894
|
}
|
|
841
895
|
const content = col.renderCell ? col.renderCell(rawValue, row) : rawValue;
|
|
@@ -875,7 +929,7 @@ var DataTable = ({
|
|
|
875
929
|
{
|
|
876
930
|
name: `filter-${filter.name}-from`,
|
|
877
931
|
label: "",
|
|
878
|
-
placeholder:
|
|
932
|
+
placeholder: resolvedDateFromLabel,
|
|
879
933
|
format: "medium",
|
|
880
934
|
value: rangeVal.from,
|
|
881
935
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, from: val })
|
|
@@ -886,7 +940,7 @@ var DataTable = ({
|
|
|
886
940
|
size: "sm",
|
|
887
941
|
name: `filter-${filter.name}-to`,
|
|
888
942
|
label: "",
|
|
889
|
-
placeholder:
|
|
943
|
+
placeholder: resolvedDateToLabel,
|
|
890
944
|
format: "medium",
|
|
891
945
|
value: rangeVal.to,
|
|
892
946
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, to: val })
|
|
@@ -909,7 +963,7 @@ var DataTable = ({
|
|
|
909
963
|
}
|
|
910
964
|
);
|
|
911
965
|
};
|
|
912
|
-
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, searchFields.length > 0 && /* @__PURE__ */ React.createElement(
|
|
966
|
+
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
|
|
913
967
|
SearchInput,
|
|
914
968
|
{
|
|
915
969
|
name: "datatable-search",
|
|
@@ -917,7 +971,7 @@ var DataTable = ({
|
|
|
917
971
|
value: searchTerm,
|
|
918
972
|
onChange: handleSearchChange
|
|
919
973
|
}
|
|
920
|
-
), filters.slice(0,
|
|
974
|
+
), filters.slice(0, filterInlineLimit).map(renderFilterControl), filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(
|
|
921
975
|
Button,
|
|
922
976
|
{
|
|
923
977
|
variant: "transparent",
|
|
@@ -925,16 +979,25 @@ var DataTable = ({
|
|
|
925
979
|
onClick: () => setShowMoreFilters((prev) => !prev)
|
|
926
980
|
},
|
|
927
981
|
/* @__PURE__ */ React.createElement(Icon, { name: "filter", size: "sm" }),
|
|
928
|
-
"
|
|
929
|
-
|
|
982
|
+
" ",
|
|
983
|
+
resolvedFiltersButtonLabel
|
|
984
|
+
)), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "end", gap: "sm", wrap: "wrap" }, filters.slice(filterInlineLimit).map(renderFilterControl)), activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges && activeChips.map((chip) => /* @__PURE__ */ React.createElement(Tag, { key: chip.key, variant: "default", onDelete: () => handleFilterRemove(chip.key) }, chip.label)), showClearFiltersButton && /* @__PURE__ */ React.createElement(
|
|
930
985
|
Button,
|
|
931
986
|
{
|
|
932
987
|
variant: "transparent",
|
|
933
988
|
size: "extra-small",
|
|
934
989
|
onClick: () => handleFilterRemove("all")
|
|
935
990
|
},
|
|
936
|
-
|
|
937
|
-
)))), showRowCount && displayCount > 0 && !(selectable && selectedIds.size > 0) && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), selectable && selectedIds.size > 0 &&
|
|
991
|
+
resolvedClearAllLabel
|
|
992
|
+
)))), showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0) && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), showSelectionBar && selectable && selectedIds.size > 0 && (renderSelectionBar ? renderSelectionBar({
|
|
993
|
+
selectedIds,
|
|
994
|
+
selectedCount: selectedIds.size,
|
|
995
|
+
displayCount,
|
|
996
|
+
countLabel,
|
|
997
|
+
onSelectAll: handleSelectAllRows,
|
|
998
|
+
onDeselectAll: handleDeselectAll,
|
|
999
|
+
selectionActions
|
|
1000
|
+
}) : /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ React.createElement(Text, { inline: true, format: { fontWeight: "demibold" } }, typeof resolvedSelectedLabel === "function" ? resolvedSelectedLabel(selectedIds.size, countLabel(selectedIds.size)) : resolvedSelectedLabel), /* @__PURE__ */ React.createElement(Button, { variant: "transparent", size: "extra-small", onClick: handleSelectAllRows }, typeof resolvedSelectAllLabel === "function" ? resolvedSelectAllLabel(displayCount, countLabel(displayCount)) : resolvedSelectAllLabel), /* @__PURE__ */ React.createElement(Button, { variant: "transparent", size: "extra-small", onClick: handleDeselectAll }, resolvedDeselectAllLabel), selectionActions.map((action, i) => /* @__PURE__ */ React.createElement(
|
|
938
1001
|
Button,
|
|
939
1002
|
{
|
|
940
1003
|
key: i,
|
|
@@ -945,7 +1008,11 @@ var DataTable = ({
|
|
|
945
1008
|
action.icon && /* @__PURE__ */ React.createElement(Icon, { name: action.icon, size: "sm" }),
|
|
946
1009
|
" ",
|
|
947
1010
|
action.label
|
|
948
|
-
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), loading ?
|
|
1011
|
+
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel))))), loading ? renderLoadingState ? renderLoadingState({ label: resolvedLoadingLabel }) : /* @__PURE__ */ React.createElement(LoadingSpinner, { label: resolvedLoadingLabel, layout: "centered" }) : error ? renderErrorState ? renderErrorState({
|
|
1012
|
+
error,
|
|
1013
|
+
title: typeof error === "string" ? error : resolvedErrorTitle,
|
|
1014
|
+
message: typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage
|
|
1015
|
+
}) : /* @__PURE__ */ React.createElement(ErrorState, { title: typeof error === "string" ? error : resolvedErrorTitle }, /* @__PURE__ */ React.createElement(Text, null, typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage)) : displayRows.length === 0 ? renderEmptyState ? renderEmptyState({ title: resolvedEmptyTitle, message: resolvedEmptyMessage }) : /* @__PURE__ */ React.createElement(Flex, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ React.createElement(EmptyState, { title: resolvedEmptyTitle, layout: "vertical" }, /* @__PURE__ */ React.createElement(Text, null, resolvedEmptyMessage))) : /* @__PURE__ */ React.createElement(
|
|
949
1016
|
Table,
|
|
950
1017
|
{
|
|
951
1018
|
bordered,
|
|
@@ -1168,7 +1235,6 @@ var runDefaultFieldValidator = (value, field, allValues) => {
|
|
|
1168
1235
|
if (!isTimeValueObject(value)) return `${errorPrefix} has an invalid time`;
|
|
1169
1236
|
break;
|
|
1170
1237
|
case "datetime": {
|
|
1171
|
-
if (isDateValueObject(value)) break;
|
|
1172
1238
|
if (!isPlainObject(value)) return `${errorPrefix} has an invalid date/time`;
|
|
1173
1239
|
const hasDate = value.date !== void 0;
|
|
1174
1240
|
const hasTime = value.time !== void 0;
|
|
@@ -1234,12 +1300,14 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
|
|
|
1234
1300
|
};
|
|
1235
1301
|
var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
|
|
1236
1302
|
const includeCustomValidators = options.includeCustomValidators !== false;
|
|
1237
|
-
|
|
1303
|
+
const msg = options.messages || {};
|
|
1304
|
+
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
|
|
1238
1305
|
const isRequired = resolveRequired(field, allValues);
|
|
1239
1306
|
const plugin = fieldTypes && fieldTypes[field.type];
|
|
1240
1307
|
const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
|
|
1241
1308
|
if (isRequired && empty) {
|
|
1242
|
-
|
|
1309
|
+
const fn = msg.required || ((label) => `${label} is required`);
|
|
1310
|
+
return typeof fn === "function" ? fn(field.label) : fn;
|
|
1243
1311
|
}
|
|
1244
1312
|
if (empty) return null;
|
|
1245
1313
|
if (field.useDefaultValidators !== false) {
|
|
@@ -1248,23 +1316,27 @@ var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
|
|
|
1248
1316
|
}
|
|
1249
1317
|
if (field.pattern && typeof value === "string") {
|
|
1250
1318
|
if (!field.pattern.test(value)) {
|
|
1251
|
-
return field.patternMessage || "Invalid format";
|
|
1319
|
+
return field.patternMessage || msg.invalidFormat || "Invalid format";
|
|
1252
1320
|
}
|
|
1253
1321
|
}
|
|
1254
1322
|
if (typeof value === "string") {
|
|
1255
1323
|
if (field.minLength != null && value.length < field.minLength) {
|
|
1256
|
-
|
|
1324
|
+
const fn = msg.minLength || ((min) => `Must be at least ${min} characters`);
|
|
1325
|
+
return typeof fn === "function" ? fn(field.minLength) : fn;
|
|
1257
1326
|
}
|
|
1258
1327
|
if (field.maxLength != null && value.length > field.maxLength) {
|
|
1259
|
-
|
|
1328
|
+
const fn = msg.maxLength || ((max) => `Must be no more than ${max} characters`);
|
|
1329
|
+
return typeof fn === "function" ? fn(field.maxLength) : fn;
|
|
1260
1330
|
}
|
|
1261
1331
|
}
|
|
1262
1332
|
if (typeof value === "number") {
|
|
1263
1333
|
if (field.min != null && value < field.min) {
|
|
1264
|
-
|
|
1334
|
+
const fn = msg.minValue || ((min) => `Must be at least ${min}`);
|
|
1335
|
+
return typeof fn === "function" ? fn(field.min) : fn;
|
|
1265
1336
|
}
|
|
1266
1337
|
if (field.max != null && value > field.max) {
|
|
1267
|
-
|
|
1338
|
+
const fn = msg.maxValue || ((max) => `Must be no more than ${max}`);
|
|
1339
|
+
return typeof fn === "function" ? fn(field.max) : fn;
|
|
1268
1340
|
}
|
|
1269
1341
|
}
|
|
1270
1342
|
if (field.type === "date" && isDateValueObject(value)) {
|
|
@@ -1376,6 +1448,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1376
1448
|
// (values, { reset, rawValues }) => void | Promise
|
|
1377
1449
|
transformValues,
|
|
1378
1450
|
// (values) => values — reshape before submit
|
|
1451
|
+
transformInitialValues,
|
|
1452
|
+
// (rawInitialValues) => values — reshape raw data on load
|
|
1379
1453
|
onBeforeSubmit,
|
|
1380
1454
|
// (values) => boolean | Promise<boolean> — intercept submit
|
|
1381
1455
|
onSubmitSuccess,
|
|
@@ -1470,8 +1544,18 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1470
1544
|
// string — warning alert when readOnly
|
|
1471
1545
|
alerts,
|
|
1472
1546
|
// { addAlert, readOnlyTitle, errorTitle, successTitle }
|
|
1473
|
-
errors: controlledErrors
|
|
1547
|
+
errors: controlledErrors,
|
|
1474
1548
|
// controlled validation errors
|
|
1549
|
+
showReadOnlyAlert = true,
|
|
1550
|
+
// show warning Alert when readOnly is true
|
|
1551
|
+
showInlineAlerts = true,
|
|
1552
|
+
// show inline form-level error/success Alerts
|
|
1553
|
+
renderReadOnlyAlert,
|
|
1554
|
+
// (context: { title, message }) => ReactNode — custom readOnly alert renderer
|
|
1555
|
+
renderFieldError,
|
|
1556
|
+
// (error: string, field: object) => ReactNode — custom field error renderer
|
|
1557
|
+
defaultCurrency = "USD"
|
|
1558
|
+
// form-level default ISO 4217 currency code for currency fields
|
|
1475
1559
|
} = props;
|
|
1476
1560
|
const {
|
|
1477
1561
|
onDirtyChange,
|
|
@@ -1483,6 +1567,23 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1483
1567
|
const cancelButtonLabel = (labels == null ? void 0 : labels.cancel) || "Cancel";
|
|
1484
1568
|
const backButtonLabel = (labels == null ? void 0 : labels.back) || "Back";
|
|
1485
1569
|
const nextButtonLabel = (labels == null ? void 0 : labels.next) || "Next";
|
|
1570
|
+
const requiredMessage = (labels == null ? void 0 : labels.required) || ((label) => `${label} is required`);
|
|
1571
|
+
const invalidFormatMessage = (labels == null ? void 0 : labels.invalidFormat) || "Invalid format";
|
|
1572
|
+
const minLengthMessage = (labels == null ? void 0 : labels.minLength) || ((min) => `Must be at least ${min} characters`);
|
|
1573
|
+
const maxLengthMessage = (labels == null ? void 0 : labels.maxLength) || ((max) => `Must be no more than ${max} characters`);
|
|
1574
|
+
const minValueMessage = (labels == null ? void 0 : labels.minValue) || ((min) => `Must be at least ${min}`);
|
|
1575
|
+
const maxValueMessage = (labels == null ? void 0 : labels.maxValue) || ((max) => `Must be no more than ${max}`);
|
|
1576
|
+
const dependentPropertiesLabel = (labels == null ? void 0 : labels.dependentProperties) || "Dependent properties";
|
|
1577
|
+
const repeaterAddLabel = (labels == null ? void 0 : labels.repeaterAdd) || "Add";
|
|
1578
|
+
const repeaterRemoveLabel = (labels == null ? void 0 : labels.repeaterRemove) || "Remove";
|
|
1579
|
+
const validationMessages = labels ? {
|
|
1580
|
+
required: requiredMessage,
|
|
1581
|
+
invalidFormat: invalidFormatMessage,
|
|
1582
|
+
minLength: minLengthMessage,
|
|
1583
|
+
maxLength: maxLengthMessage,
|
|
1584
|
+
minValue: minValueMessage,
|
|
1585
|
+
maxValue: maxValueMessage
|
|
1586
|
+
} : void 0;
|
|
1486
1587
|
const addAlert = alerts == null ? void 0 : alerts.addAlert;
|
|
1487
1588
|
const readOnlyTitle = (alerts == null ? void 0 : alerts.readOnlyTitle) || "Read Only";
|
|
1488
1589
|
const errorTitle = (alerts == null ? void 0 : alerts.errorTitle) || "Error";
|
|
@@ -1512,12 +1613,27 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1512
1613
|
prevSuccessRef.current = formSuccess;
|
|
1513
1614
|
}, [addAlert, formSuccess, successTitle]);
|
|
1514
1615
|
const computeInitialValues = () => {
|
|
1616
|
+
const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
|
|
1515
1617
|
const vals = {};
|
|
1516
1618
|
for (const field of fields) {
|
|
1517
1619
|
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
|
|
1620
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
1621
|
+
for (const item of field.items) {
|
|
1622
|
+
const subFields = field.fields(item);
|
|
1623
|
+
for (const sf of subFields) {
|
|
1624
|
+
const plugin2 = fieldTypes && fieldTypes[sf.type];
|
|
1625
|
+
const emptyValue2 = plugin2 && plugin2.getEmptyValue ? plugin2.getEmptyValue() : getEmptyValue(sf);
|
|
1626
|
+
let init2 = resolved && resolved[sf.name] !== void 0 ? resolved[sf.name] : sf.defaultValue !== void 0 ? sf.defaultValue : emptyValue2;
|
|
1627
|
+
if (sf.transformIn) init2 = sf.transformIn(init2);
|
|
1628
|
+
vals[sf.name] = init2;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
continue;
|
|
1632
|
+
}
|
|
1518
1633
|
const plugin = fieldTypes && fieldTypes[field.type];
|
|
1519
1634
|
const emptyValue = plugin && plugin.getEmptyValue ? plugin.getEmptyValue() : getEmptyValue(field);
|
|
1520
|
-
|
|
1635
|
+
let init = resolved && resolved[field.name] !== void 0 ? resolved[field.name] : field.defaultValue !== void 0 ? field.defaultValue : emptyValue;
|
|
1636
|
+
if (field.transformIn) init = field.transformIn(init);
|
|
1521
1637
|
vals[field.name] = init;
|
|
1522
1638
|
}
|
|
1523
1639
|
return vals;
|
|
@@ -1550,7 +1666,14 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1550
1666
|
formErrorsRef.current = formErrors;
|
|
1551
1667
|
const fieldByName = useMemo2(() => {
|
|
1552
1668
|
const map = /* @__PURE__ */ new Map();
|
|
1553
|
-
for (const field of fields)
|
|
1669
|
+
for (const field of fields) {
|
|
1670
|
+
map.set(field.name, field);
|
|
1671
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
1672
|
+
for (const item of field.items) {
|
|
1673
|
+
for (const sf of field.fields(item)) map.set(sf.name, sf);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1554
1677
|
return map;
|
|
1555
1678
|
}, [fields]);
|
|
1556
1679
|
const isDev = typeof process === "undefined" || !process.env || process.env.NODE_ENV !== "production";
|
|
@@ -1721,7 +1844,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1721
1844
|
const rowValues = { ...allValues, [field.name]: rows };
|
|
1722
1845
|
subFields.forEach((subField) => {
|
|
1723
1846
|
if (subField.visible && !subField.visible(rowValues)) return;
|
|
1724
|
-
const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes);
|
|
1847
|
+
const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
1725
1848
|
if (!err) return;
|
|
1726
1849
|
const key = getRepeaterErrorKey(field.name, rowIdx, subField.name);
|
|
1727
1850
|
errors[key] = err;
|
|
@@ -1748,9 +1871,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1748
1871
|
);
|
|
1749
1872
|
return repeaterResult.errors[name] || null;
|
|
1750
1873
|
}
|
|
1751
|
-
return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes);
|
|
1874
|
+
return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes, { messages: validationMessages });
|
|
1752
1875
|
},
|
|
1753
|
-
[fieldByName, formValues, validateRepeaterField, fieldTypes]
|
|
1876
|
+
[fieldByName, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
1754
1877
|
);
|
|
1755
1878
|
const validateVisibleFields = useCallback2(
|
|
1756
1879
|
(fieldSubset) => {
|
|
@@ -1766,7 +1889,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1766
1889
|
}
|
|
1767
1890
|
continue;
|
|
1768
1891
|
}
|
|
1769
|
-
const err = runValidators(formValues[field.name], field, formValues, fieldTypes);
|
|
1892
|
+
const err = runValidators(formValues[field.name], field, formValues, fieldTypes, { messages: validationMessages });
|
|
1770
1893
|
if (err) {
|
|
1771
1894
|
errors[field.name] = err;
|
|
1772
1895
|
hasErrors = true;
|
|
@@ -1774,14 +1897,14 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1774
1897
|
}
|
|
1775
1898
|
return { errors, hasErrors };
|
|
1776
1899
|
},
|
|
1777
|
-
[visibleFields, formValues, validateRepeaterField, fieldTypes]
|
|
1900
|
+
[visibleFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
1778
1901
|
);
|
|
1779
1902
|
const runAsyncValidation = useCallback2(
|
|
1780
1903
|
(name, value) => {
|
|
1781
1904
|
const field = fieldByName.get(name);
|
|
1782
1905
|
if (!field || field.type === "repeater") return null;
|
|
1783
1906
|
const val = value != null ? value : formValues[name];
|
|
1784
|
-
const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false });
|
|
1907
|
+
const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false, messages: validationMessages });
|
|
1785
1908
|
const prevController = asyncAbortRef.current.get(name);
|
|
1786
1909
|
if (prevController) prevController.abort();
|
|
1787
1910
|
asyncAbortRef.current.delete(name);
|
|
@@ -1972,23 +2095,27 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
1972
2095
|
);
|
|
1973
2096
|
const handleFieldInput = useCallback2(
|
|
1974
2097
|
(name, value) => {
|
|
2098
|
+
handleFieldChange(name, value);
|
|
1975
2099
|
if (!validateOnChange) return;
|
|
1976
2100
|
const err = validateField(name, value);
|
|
1977
2101
|
updateErrors({ [name]: err });
|
|
1978
2102
|
},
|
|
1979
|
-
[validateOnChange, validateField, updateErrors]
|
|
2103
|
+
[validateOnChange, validateField, updateErrors, handleFieldChange]
|
|
1980
2104
|
);
|
|
1981
2105
|
const handleFieldBlur = useCallback2(
|
|
1982
2106
|
(name, value) => {
|
|
1983
|
-
if (!validateOnBlur) return;
|
|
1984
2107
|
const resolvedValue = value != null ? value : formValuesRef.current[name];
|
|
2108
|
+
if (value != null && value !== formValuesRef.current[name]) {
|
|
2109
|
+
handleFieldChange(name, value);
|
|
2110
|
+
}
|
|
2111
|
+
if (!validateOnBlur) return;
|
|
1985
2112
|
const err = validateField(name, resolvedValue);
|
|
1986
2113
|
updateErrors({ [name]: err });
|
|
1987
2114
|
if (!err) {
|
|
1988
2115
|
triggerAsyncValidation(name, resolvedValue);
|
|
1989
2116
|
}
|
|
1990
2117
|
},
|
|
1991
|
-
[validateOnBlur, validateField, updateErrors, triggerAsyncValidation]
|
|
2118
|
+
[validateOnBlur, validateField, updateErrors, triggerAsyncValidation, handleFieldChange]
|
|
1992
2119
|
);
|
|
1993
2120
|
const handleSubmit = useCallback2(
|
|
1994
2121
|
async (e) => {
|
|
@@ -2021,8 +2148,17 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2021
2148
|
const rawValues = {};
|
|
2022
2149
|
for (const key of Object.keys(formValues)) {
|
|
2023
2150
|
const f = fieldByName.get(key);
|
|
2024
|
-
if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList")) continue;
|
|
2025
|
-
rawValues[key] = formValues[key];
|
|
2151
|
+
if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
|
|
2152
|
+
rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
|
|
2153
|
+
}
|
|
2154
|
+
for (const f of fields) {
|
|
2155
|
+
if (f.type !== "fieldGroup" || !f.items || !f.fields) continue;
|
|
2156
|
+
for (const item of f.items) {
|
|
2157
|
+
for (const sf of f.fields(item)) {
|
|
2158
|
+
if (formValues[sf.name] === void 0) continue;
|
|
2159
|
+
rawValues[sf.name] = sf.transformOut ? sf.transformOut(formValues[sf.name]) : formValues[sf.name];
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2026
2162
|
}
|
|
2027
2163
|
const submitValues = transformValues ? transformValues(rawValues) : rawValues;
|
|
2028
2164
|
if (onBeforeSubmit) {
|
|
@@ -2150,6 +2286,12 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2150
2286
|
[replaceErrors]
|
|
2151
2287
|
);
|
|
2152
2288
|
const renderField = (field) => {
|
|
2289
|
+
const fieldError = formErrors[field.name] || null;
|
|
2290
|
+
const rendered = renderFieldInner(field);
|
|
2291
|
+
if (!renderFieldError || !fieldError) return rendered;
|
|
2292
|
+
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, rendered, renderFieldError(fieldError, field));
|
|
2293
|
+
};
|
|
2294
|
+
const renderFieldInner = (field) => {
|
|
2153
2295
|
const fieldValue = formValues[field.name];
|
|
2154
2296
|
const fieldError = formErrors[field.name] || null;
|
|
2155
2297
|
const hasError = !!fieldError;
|
|
@@ -2159,10 +2301,125 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2159
2301
|
const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
|
|
2160
2302
|
if (field.type === "display") {
|
|
2161
2303
|
if (field.render) {
|
|
2162
|
-
return field.render({
|
|
2304
|
+
return field.render({
|
|
2305
|
+
allValues: formValues,
|
|
2306
|
+
setFieldValue: (name, value) => handleFieldChange(name, value),
|
|
2307
|
+
setFieldError: (name, message) => updateErrors({ [name]: message })
|
|
2308
|
+
});
|
|
2163
2309
|
}
|
|
2164
2310
|
return null;
|
|
2165
2311
|
}
|
|
2312
|
+
if (field.type === "fieldGroup") {
|
|
2313
|
+
const items = field.items || [];
|
|
2314
|
+
const fieldsFn = field.fields;
|
|
2315
|
+
if (!fieldsFn) return null;
|
|
2316
|
+
const groupColumns = field.columns || 1;
|
|
2317
|
+
const showItemLabel = field.showItemLabel !== false;
|
|
2318
|
+
return /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap: "xs" }, field.label && /* @__PURE__ */ React2.createElement(Text2, { format: { fontWeight: "demibold" } }, field.label), field.description && /* @__PURE__ */ React2.createElement(Text2, { variant: "microcopy" }, field.description), items.map((item, itemIdx) => {
|
|
2319
|
+
const subFields = fieldsFn(item);
|
|
2320
|
+
return /* @__PURE__ */ React2.createElement(Flex2, { key: item.key || itemIdx, direction: "row", gap: "xs", align: "end" }, showItemLabel && item.label && /* @__PURE__ */ React2.createElement(Box2, { flex: 1 }, itemIdx === 0 ? /* @__PURE__ */ React2.createElement(
|
|
2321
|
+
Input2,
|
|
2322
|
+
{
|
|
2323
|
+
name: `_fieldGroup-label-${field.name}-${itemIdx}`,
|
|
2324
|
+
label: "\xA0",
|
|
2325
|
+
value: item.label,
|
|
2326
|
+
readOnly: true,
|
|
2327
|
+
disabled: true
|
|
2328
|
+
}
|
|
2329
|
+
) : /* @__PURE__ */ React2.createElement(
|
|
2330
|
+
Input2,
|
|
2331
|
+
{
|
|
2332
|
+
name: `_fieldGroup-label-${field.name}-${itemIdx}`,
|
|
2333
|
+
value: item.label,
|
|
2334
|
+
readOnly: true,
|
|
2335
|
+
disabled: true
|
|
2336
|
+
}
|
|
2337
|
+
)), subFields.map((sf) => {
|
|
2338
|
+
const sfValue = formValues[sf.name];
|
|
2339
|
+
const sfError = formErrors[sf.name] || null;
|
|
2340
|
+
const sfLabel = itemIdx === 0 ? sf.label : void 0;
|
|
2341
|
+
const sfReadOnly = sf.readOnly || formReadOnly;
|
|
2342
|
+
const sfDisabled = disabled || sf.disabled || formReadOnly;
|
|
2343
|
+
const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
|
|
2344
|
+
const sfProps = {
|
|
2345
|
+
name: sf.name,
|
|
2346
|
+
label: sfLabel,
|
|
2347
|
+
placeholder: sf.placeholder,
|
|
2348
|
+
description: itemIdx === 0 ? sf.description : void 0,
|
|
2349
|
+
readOnly: sfReadOnly,
|
|
2350
|
+
disabled: sfDisabled,
|
|
2351
|
+
error: !!sfError,
|
|
2352
|
+
validationMessage: sfError || void 0,
|
|
2353
|
+
...sf.fieldProps || {}
|
|
2354
|
+
};
|
|
2355
|
+
let sfElement;
|
|
2356
|
+
switch (sf.type) {
|
|
2357
|
+
case "select":
|
|
2358
|
+
sfElement = /* @__PURE__ */ React2.createElement(
|
|
2359
|
+
Select2,
|
|
2360
|
+
{
|
|
2361
|
+
...sfProps,
|
|
2362
|
+
value: sfValue,
|
|
2363
|
+
options: resolveOptions(sf, formValues),
|
|
2364
|
+
onChange: sfOnChange
|
|
2365
|
+
}
|
|
2366
|
+
);
|
|
2367
|
+
break;
|
|
2368
|
+
case "number":
|
|
2369
|
+
sfElement = /* @__PURE__ */ React2.createElement(
|
|
2370
|
+
NumberInput2,
|
|
2371
|
+
{
|
|
2372
|
+
...sfProps,
|
|
2373
|
+
value: sfValue,
|
|
2374
|
+
onChange: sfOnChange,
|
|
2375
|
+
onBlur: (v) => handleFieldBlur(sf.name, v)
|
|
2376
|
+
}
|
|
2377
|
+
);
|
|
2378
|
+
break;
|
|
2379
|
+
case "toggle":
|
|
2380
|
+
sfElement = /* @__PURE__ */ React2.createElement(
|
|
2381
|
+
Toggle2,
|
|
2382
|
+
{
|
|
2383
|
+
name: sf.name,
|
|
2384
|
+
label: sfLabel || sf.label,
|
|
2385
|
+
checked: !!sfValue,
|
|
2386
|
+
size: sf.size || "md",
|
|
2387
|
+
labelDisplay: sf.labelDisplay || "top",
|
|
2388
|
+
readonly: sfReadOnly,
|
|
2389
|
+
disabled: sfDisabled,
|
|
2390
|
+
onChange: sfOnChange,
|
|
2391
|
+
...sf.fieldProps || {}
|
|
2392
|
+
}
|
|
2393
|
+
);
|
|
2394
|
+
break;
|
|
2395
|
+
case "time":
|
|
2396
|
+
sfElement = /* @__PURE__ */ React2.createElement(
|
|
2397
|
+
TimeInput2,
|
|
2398
|
+
{
|
|
2399
|
+
...sfProps,
|
|
2400
|
+
value: sfValue,
|
|
2401
|
+
interval: sf.interval,
|
|
2402
|
+
onChange: sfOnChange,
|
|
2403
|
+
onBlur: (v) => handleFieldBlur(sf.name, v)
|
|
2404
|
+
}
|
|
2405
|
+
);
|
|
2406
|
+
break;
|
|
2407
|
+
default:
|
|
2408
|
+
sfElement = /* @__PURE__ */ React2.createElement(
|
|
2409
|
+
Input2,
|
|
2410
|
+
{
|
|
2411
|
+
...sfProps,
|
|
2412
|
+
value: sfValue || "",
|
|
2413
|
+
onChange: sfOnChange,
|
|
2414
|
+
onInput: (v) => handleFieldInput(sf.name, v),
|
|
2415
|
+
onBlur: (v) => handleFieldBlur(sf.name, v)
|
|
2416
|
+
}
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
2419
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: sf.name, flex: 1 }, sfElement);
|
|
2420
|
+
}));
|
|
2421
|
+
}));
|
|
2422
|
+
}
|
|
2166
2423
|
if (field.type === "crmPropertyList") {
|
|
2167
2424
|
return /* @__PURE__ */ React2.createElement(
|
|
2168
2425
|
CrmPropertyList,
|
|
@@ -2216,7 +2473,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2216
2473
|
readOnly: isReadOnly,
|
|
2217
2474
|
disabled: isDisabled,
|
|
2218
2475
|
error: hasError,
|
|
2219
|
-
validationMessage: fieldError || void 0,
|
|
2476
|
+
validationMessage: renderFieldError ? void 0 : fieldError || void 0,
|
|
2220
2477
|
...field.loading || validatingFields[field.name] ? { loading: true } : {},
|
|
2221
2478
|
...field.fieldProps || {}
|
|
2222
2479
|
};
|
|
@@ -2286,7 +2543,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2286
2543
|
CurrencyInput2,
|
|
2287
2544
|
{
|
|
2288
2545
|
...commonProps,
|
|
2289
|
-
currency: field.currency ||
|
|
2546
|
+
currency: field.currency || defaultCurrency,
|
|
2290
2547
|
value: fieldValue,
|
|
2291
2548
|
min: field.min,
|
|
2292
2549
|
max: field.max,
|
|
@@ -2464,8 +2721,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2464
2721
|
const renderRemoveControl = repeaterProps.renderRemove;
|
|
2465
2722
|
const renderMoveUpControl = repeaterProps.renderMoveUp;
|
|
2466
2723
|
const renderMoveDownControl = repeaterProps.renderMoveDown;
|
|
2467
|
-
const addLabel = repeaterProps.addLabel ||
|
|
2468
|
-
const removeLabel = repeaterProps.removeLabel ||
|
|
2724
|
+
const addLabel = repeaterProps.addLabel || repeaterAddLabel;
|
|
2725
|
+
const removeLabel = repeaterProps.removeLabel || repeaterRemoveLabel;
|
|
2469
2726
|
const moveUpLabel = repeaterProps.moveUpLabel || "Up";
|
|
2470
2727
|
const moveDownLabel = repeaterProps.moveDownLabel || "Down";
|
|
2471
2728
|
const canEditRows = !isReadOnly && !isDisabled;
|
|
@@ -2499,7 +2756,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2499
2756
|
};
|
|
2500
2757
|
const validateSubField = (rowIdx, subField, subValue, nextRows) => {
|
|
2501
2758
|
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
2502
|
-
const err = runValidators(subValue, subField, rowValues, fieldTypes);
|
|
2759
|
+
const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
2503
2760
|
setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
|
|
2504
2761
|
};
|
|
2505
2762
|
const handleSubFieldChange = (rowIdx, subField, subValue) => {
|
|
@@ -2617,7 +2874,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2617
2874
|
const renderDependentGroup = (parentField, dependents) => {
|
|
2618
2875
|
const firstWithLabel = dependents.find((f) => getDependsOnLabel(f)) || dependents[0];
|
|
2619
2876
|
const firstWithMessage = dependents.find((f) => getDependsOnMessage(f)) || dependents[0];
|
|
2620
|
-
const groupLabel = getDependsOnLabel(firstWithLabel) ||
|
|
2877
|
+
const groupLabel = getDependsOnLabel(firstWithLabel) || dependentPropertiesLabel;
|
|
2621
2878
|
const rawMessage = getDependsOnMessage(firstWithMessage);
|
|
2622
2879
|
const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
|
|
2623
2880
|
return /* @__PURE__ */ React2.createElement(Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React2.createElement(Text2, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ React2.createElement(Link2, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React2.createElement(Tooltip, null, tooltipMessage) }, /* @__PURE__ */ React2.createElement(Icon2, { name: "info" })))), renderFieldSubset(dependents)));
|
|
@@ -2707,38 +2964,19 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2707
2964
|
}
|
|
2708
2965
|
return elements;
|
|
2709
2966
|
};
|
|
2710
|
-
const
|
|
2967
|
+
const renderSingleColumnLayout = (fieldSubset) => {
|
|
2711
2968
|
const fieldList = fieldSubset || visibleFields;
|
|
2712
|
-
const rows = [];
|
|
2713
|
-
let i = 0;
|
|
2714
|
-
while (i < fieldList.length) {
|
|
2715
|
-
const field = fieldList[i];
|
|
2716
|
-
if (field.width === "half" && i + 1 < fieldList.length && fieldList[i + 1].width === "half" && !getDependsOnName(field)) {
|
|
2717
|
-
rows.push({ type: "pair", fields: [fieldList[i], fieldList[i + 1]] });
|
|
2718
|
-
i += 2;
|
|
2719
|
-
} else {
|
|
2720
|
-
rows.push({ type: "single", field });
|
|
2721
|
-
i++;
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
2969
|
const elements = [];
|
|
2725
2970
|
const processedDeps = /* @__PURE__ */ new Set();
|
|
2726
|
-
for (const
|
|
2727
|
-
if (
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
elements.push(
|
|
2735
|
-
/* @__PURE__ */ React2.createElement(React2.Fragment, { key: field.name }, renderField(field))
|
|
2736
|
-
);
|
|
2737
|
-
const dependents = getDependents(field);
|
|
2738
|
-
if (dependents.length > 0) {
|
|
2739
|
-
for (const dep of dependents) processedDeps.add(dep.name);
|
|
2740
|
-
elements.push(renderDependentGroup(field, dependents));
|
|
2741
|
-
}
|
|
2971
|
+
for (const field of fieldList) {
|
|
2972
|
+
if (processedDeps.has(field.name)) continue;
|
|
2973
|
+
elements.push(
|
|
2974
|
+
/* @__PURE__ */ React2.createElement(React2.Fragment, { key: field.name }, renderField(field))
|
|
2975
|
+
);
|
|
2976
|
+
const dependents = getDependents(field);
|
|
2977
|
+
if (dependents.length > 0) {
|
|
2978
|
+
for (const dep of dependents) processedDeps.add(dep.name);
|
|
2979
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
2742
2980
|
}
|
|
2743
2981
|
}
|
|
2744
2982
|
return elements;
|
|
@@ -2749,10 +2987,20 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2749
2987
|
let batch = [];
|
|
2750
2988
|
const flushBatch = () => {
|
|
2751
2989
|
if (batch.length === 0) return;
|
|
2752
|
-
|
|
2753
|
-
|
|
2990
|
+
if (maxColumns) {
|
|
2991
|
+
const chunks = Array.from(
|
|
2992
|
+
{ length: Math.ceil(batch.length / maxColumns) },
|
|
2993
|
+
(_, i) => batch.slice(i * maxColumns, i * maxColumns + maxColumns)
|
|
2994
|
+
);
|
|
2995
|
+
for (const chunk of chunks) {
|
|
2996
|
+
const remainder = maxColumns - chunk.length;
|
|
2997
|
+
elements.push(
|
|
2998
|
+
/* @__PURE__ */ React2.createElement(Flex2, { key: `ag-${chunk[0].name}`, direction: "row", gap }, chunk.map((f) => /* @__PURE__ */ React2.createElement(Box2, { key: f.name, flex: 1 }, renderField(f))), remainder > 0 && /* @__PURE__ */ React2.createElement(Box2, { flex: remainder }))
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
} else {
|
|
2754
3002
|
elements.push(
|
|
2755
|
-
/* @__PURE__ */ React2.createElement(AutoGrid, { key: `ag-${
|
|
3003
|
+
/* @__PURE__ */ React2.createElement(AutoGrid, { key: `ag-${batch[0].name}`, columnWidth, flexible: true, gap }, batch.map((f) => /* @__PURE__ */ React2.createElement(React2.Fragment, { key: f.name }, renderField(f))))
|
|
2756
3004
|
);
|
|
2757
3005
|
}
|
|
2758
3006
|
batch = [];
|
|
@@ -2811,7 +3059,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2811
3059
|
if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
|
|
2812
3060
|
if (columnWidth) return renderAutoGridLayout(fieldSubset);
|
|
2813
3061
|
if (columns > 1) return renderGridLayout(fieldSubset);
|
|
2814
|
-
return
|
|
3062
|
+
return renderSingleColumnLayout(fieldSubset);
|
|
2815
3063
|
};
|
|
2816
3064
|
const renderSections = () => {
|
|
2817
3065
|
const hasSections = Array.isArray(sections) && sections.length > 0;
|
|
@@ -2824,7 +3072,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2824
3072
|
for (const sec of sections) {
|
|
2825
3073
|
const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
|
|
2826
3074
|
if (sectionFields.length === 0) continue;
|
|
2827
|
-
const
|
|
3075
|
+
const sectionContext = { values: formValues, errors: formErrors };
|
|
3076
|
+
const accordionContent = /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields), sec.renderAfter && sec.renderAfter(sectionContext));
|
|
2828
3077
|
const accordion = /* @__PURE__ */ React2.createElement(
|
|
2829
3078
|
Accordion,
|
|
2830
3079
|
{
|
|
@@ -2917,7 +3166,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2917
3166
|
currentStep,
|
|
2918
3167
|
stepNames: steps.map((s) => s.title)
|
|
2919
3168
|
}
|
|
2920
|
-
), formReadOnly && readOnlyMessage && /* @__PURE__ */ React2.createElement(Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage), !addAlert && formError && /* @__PURE__ */ React2.createElement(Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), !addAlert && formSuccess && /* @__PURE__ */ React2.createElement(Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
|
|
3169
|
+
), showReadOnlyAlert && formReadOnly && readOnlyMessage && (renderReadOnlyAlert ? renderReadOnlyAlert({ title: readOnlyTitle, message: readOnlyMessage }) : /* @__PURE__ */ React2.createElement(Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage)), showInlineAlerts && !addAlert && formError && /* @__PURE__ */ React2.createElement(Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), showInlineAlerts && !addAlert && formSuccess && /* @__PURE__ */ React2.createElement(Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
|
|
2921
3170
|
values: formValues,
|
|
2922
3171
|
goNext: handleNext,
|
|
2923
3172
|
goBack: handleBack,
|