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.js
CHANGED
|
@@ -91,12 +91,19 @@ var computeAutoWidths = (columns, data) => {
|
|
|
91
91
|
columns.forEach((col) => {
|
|
92
92
|
if (col.width && col.cellWidth) return;
|
|
93
93
|
const values = sample.map((row) => row[col.field]).filter((v) => v != null);
|
|
94
|
-
const strings = values.map((v) =>
|
|
94
|
+
const strings = values.map((v) => {
|
|
95
|
+
const s = String(v);
|
|
96
|
+
const truncLen = typeof col.truncate === "number" ? col.truncate : col.truncate && typeof col.truncate === "object" ? col.truncate.maxLength : null;
|
|
97
|
+
return truncLen && s.length > truncLen ? s.slice(0, truncLen) : s;
|
|
98
|
+
});
|
|
95
99
|
let widthHint = null;
|
|
96
100
|
let cellWidthHint = null;
|
|
97
101
|
if (col.editable && col.editType && NARROW_EDIT_TYPES.has(col.editType)) {
|
|
98
102
|
cellWidthHint = "min";
|
|
99
103
|
}
|
|
104
|
+
if (col.truncate === true) {
|
|
105
|
+
cellWidthHint = cellWidthHint || "min";
|
|
106
|
+
}
|
|
100
107
|
if (strings.length > 0) {
|
|
101
108
|
const lengths = strings.map((s) => s.length);
|
|
102
109
|
const maxLen = Math.max(...lengths);
|
|
@@ -162,12 +169,16 @@ var DataTable = ({
|
|
|
162
169
|
// enable fuzzy matching via Fuse.js
|
|
163
170
|
fuzzyOptions,
|
|
164
171
|
// custom Fuse.js options (threshold, distance, etc.)
|
|
172
|
+
showSearch = true,
|
|
173
|
+
// show the SearchInput in the toolbar
|
|
165
174
|
// Filters
|
|
166
175
|
filters = [],
|
|
167
176
|
showFilterBadges = true,
|
|
168
177
|
// show active filter chips/badges
|
|
169
178
|
showClearFiltersButton = true,
|
|
170
179
|
// show "Clear all" filters reset button
|
|
180
|
+
filterInlineLimit = 2,
|
|
181
|
+
// number of filters shown inline before overflow
|
|
171
182
|
// Pagination
|
|
172
183
|
pageSize = 10,
|
|
173
184
|
maxVisiblePageButtons,
|
|
@@ -249,6 +260,8 @@ var DataTable = ({
|
|
|
249
260
|
// optional key to force clear uncontrolled selection memory
|
|
250
261
|
resetSelectionOnQueryChange = true,
|
|
251
262
|
// clear uncontrolled selection on search/filter/sort changes
|
|
263
|
+
showSelectionBar = true,
|
|
264
|
+
// show the selection action bar when rows are selected
|
|
252
265
|
recordLabel,
|
|
253
266
|
// { singular: "Contact", plural: "Contacts" } — defaults to Record/Records
|
|
254
267
|
// -----------------------------------------------------------------------
|
|
@@ -269,11 +282,31 @@ var DataTable = ({
|
|
|
269
282
|
// (row, field, newValue) => void
|
|
270
283
|
onRowEditInput,
|
|
271
284
|
// optional live-input callback: (row, field, inputValue) => void
|
|
285
|
+
onEditStart,
|
|
286
|
+
// (row, field, currentValue) => void — fires when editing begins
|
|
287
|
+
onEditCancel,
|
|
288
|
+
// (row, field) => void — fires when editing is cancelled without commit
|
|
272
289
|
// -----------------------------------------------------------------------
|
|
273
290
|
// Auto-width
|
|
274
291
|
// -----------------------------------------------------------------------
|
|
275
|
-
autoWidth = true
|
|
292
|
+
autoWidth = true,
|
|
276
293
|
// auto-compute column widths from content analysis
|
|
294
|
+
// -----------------------------------------------------------------------
|
|
295
|
+
// Labels / i18n
|
|
296
|
+
// -----------------------------------------------------------------------
|
|
297
|
+
labels,
|
|
298
|
+
// override hardcoded UI strings for i18n
|
|
299
|
+
// -----------------------------------------------------------------------
|
|
300
|
+
// Render overrides (Phase 3 — full replacement escape hatches)
|
|
301
|
+
// -----------------------------------------------------------------------
|
|
302
|
+
renderSelectionBar,
|
|
303
|
+
// ({ selectedIds, selectedCount, displayCount, countLabel, onSelectAll, onDeselectAll, selectionActions }) => ReactNode
|
|
304
|
+
renderEmptyState,
|
|
305
|
+
// ({ title, message }) => ReactNode
|
|
306
|
+
renderLoadingState,
|
|
307
|
+
// ({ label }) => ReactNode
|
|
308
|
+
renderErrorState
|
|
309
|
+
// ({ error, title, message }) => ReactNode
|
|
277
310
|
}) => {
|
|
278
311
|
const initialSortState = (0, import_react.useMemo)(() => {
|
|
279
312
|
return normalizeSortState(columns, defaultSort);
|
|
@@ -507,11 +540,11 @@ var DataTable = ({
|
|
|
507
540
|
const type = filter.type || "select";
|
|
508
541
|
const prefix = filter.chipLabel || filter.placeholder || filter.name;
|
|
509
542
|
if (type === "multiselect") {
|
|
510
|
-
const
|
|
543
|
+
const labels2 = value.map((v) => {
|
|
511
544
|
var _a;
|
|
512
545
|
return ((_a = filter.options.find((o) => o.value === v)) == null ? void 0 : _a.label) || v;
|
|
513
546
|
}).join(", ");
|
|
514
|
-
chips.push({ key: filter.name, label: `${prefix}: ${
|
|
547
|
+
chips.push({ key: filter.name, label: `${prefix}: ${labels2}` });
|
|
515
548
|
} else if (type === "dateRange") {
|
|
516
549
|
const parts = [];
|
|
517
550
|
if (value.from) parts.push(`from ${formatDateChip(value.from)}`);
|
|
@@ -552,7 +585,17 @@ var DataTable = ({
|
|
|
552
585
|
const countLabel = (n) => n === 1 ? singularLabel : pluralLabel;
|
|
553
586
|
const resolvedEmptyTitle = emptyTitle || "No results found";
|
|
554
587
|
const resolvedEmptyMessage = emptyMessage || `No ${pluralLabel} match your search or filter criteria.`;
|
|
555
|
-
const
|
|
588
|
+
const resolvedSelectedLabel = (labels == null ? void 0 : labels.selected) || ((count, label) => `${count}\xA0${label}\xA0selected`);
|
|
589
|
+
const resolvedSelectAllLabel = (labels == null ? void 0 : labels.selectAll) || ((count, label) => `Select all ${count} ${label}`);
|
|
590
|
+
const resolvedDeselectAllLabel = (labels == null ? void 0 : labels.deselectAll) || "Deselect all";
|
|
591
|
+
const resolvedFiltersButtonLabel = (labels == null ? void 0 : labels.filtersButton) || "Filters";
|
|
592
|
+
const resolvedClearAllLabel = (labels == null ? void 0 : labels.clearAll) || "Clear all";
|
|
593
|
+
const resolvedDateFromLabel = (labels == null ? void 0 : labels.dateFrom) || "From";
|
|
594
|
+
const resolvedDateToLabel = (labels == null ? void 0 : labels.dateTo) || "To";
|
|
595
|
+
const resolvedLoadingLabel = (labels == null ? void 0 : labels.loading) || `Loading ${pluralLabel}...`;
|
|
596
|
+
const resolvedErrorTitle = (labels == null ? void 0 : labels.errorTitle) || "Something went wrong.";
|
|
597
|
+
const resolvedErrorMessage = (labels == null ? void 0 : labels.errorMessage) || "An error occurred while loading data.";
|
|
598
|
+
const resolvedRetryMessage = (labels == null ? void 0 : labels.retryMessage) || "Please try again.";
|
|
556
599
|
const recordCountLabel = rowCountText ? rowCountText(shownOnPageCount, displayCount) : displayCount === totalDataCount ? `${totalDataCount} ${countLabel(totalDataCount)}` : `${displayCount} of ${totalDataCount} ${countLabel(totalDataCount)}`;
|
|
557
600
|
const [internalSelectedIds, setInternalSelectedIds] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
558
601
|
const selectionResetRef = (0, import_react.useRef)("");
|
|
@@ -641,7 +684,11 @@ var DataTable = ({
|
|
|
641
684
|
setEditingCell({ rowId, field });
|
|
642
685
|
setEditValue(currentValue);
|
|
643
686
|
setEditError(null);
|
|
644
|
-
|
|
687
|
+
if (onEditStart) {
|
|
688
|
+
const row = data.find((r) => r[rowIdField] === rowId);
|
|
689
|
+
if (row) onEditStart(row, field, currentValue);
|
|
690
|
+
}
|
|
691
|
+
}, [onEditStart, data, rowIdField]);
|
|
645
692
|
const commitEdit = (0, import_react.useCallback)((row, field, value) => {
|
|
646
693
|
const col = columns.find((c) => c.field === field);
|
|
647
694
|
if (col == null ? void 0 : col.editValidate) {
|
|
@@ -663,6 +710,7 @@ var DataTable = ({
|
|
|
663
710
|
const commit = (val) => commitEdit(row, col.field, val);
|
|
664
711
|
const exitEdit = () => {
|
|
665
712
|
if (editError) return;
|
|
713
|
+
if (onEditCancel) onEditCancel(row, col.field);
|
|
666
714
|
setEditingCell(null);
|
|
667
715
|
setEditValue(null);
|
|
668
716
|
};
|
|
@@ -829,20 +877,26 @@ var DataTable = ({
|
|
|
829
877
|
const rawStr = String(rawValue ?? "");
|
|
830
878
|
if (col.truncate && rawStr.length > 0) {
|
|
831
879
|
if (col.truncate === true) {
|
|
832
|
-
|
|
880
|
+
if (col.renderCell) {
|
|
881
|
+
const content2 = col.renderCell(rawValue, row);
|
|
882
|
+
if (col.editable) {
|
|
883
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) }, content2 || "--");
|
|
884
|
+
}
|
|
885
|
+
return content2;
|
|
886
|
+
}
|
|
833
887
|
if (col.editable) {
|
|
834
|
-
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { truncate: { tooltipText: rawStr } }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) },
|
|
888
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { truncate: { tooltipText: rawStr } }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) }, rawStr || "--"));
|
|
835
889
|
}
|
|
836
|
-
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { truncate: { tooltipText: rawStr } },
|
|
890
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { truncate: { tooltipText: rawStr } }, rawStr);
|
|
837
891
|
}
|
|
838
|
-
const maxLen = col.truncate.maxLength || 100;
|
|
892
|
+
const maxLen = typeof col.truncate === "number" ? col.truncate : col.truncate.maxLength || 100;
|
|
839
893
|
if (rawStr.length > maxLen) {
|
|
840
894
|
const truncatedStr = rawStr.slice(0, maxLen) + "\u2026";
|
|
841
|
-
const
|
|
895
|
+
const content2 = col.renderCell ? col.renderCell(truncatedStr, row) : truncatedStr;
|
|
842
896
|
if (col.editable) {
|
|
843
|
-
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) },
|
|
897
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { variant: "dark", onClick: () => startEditing(rowId, col.field, rawValue) }, content2 || "--");
|
|
844
898
|
}
|
|
845
|
-
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { truncate: { tooltipText: rawStr } },
|
|
899
|
+
return col.renderCell ? content2 : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { truncate: { tooltipText: rawStr } }, content2 || "--");
|
|
846
900
|
}
|
|
847
901
|
}
|
|
848
902
|
const content = col.renderCell ? col.renderCell(rawValue, row) : rawValue;
|
|
@@ -882,7 +936,7 @@ var DataTable = ({
|
|
|
882
936
|
{
|
|
883
937
|
name: `filter-${filter.name}-from`,
|
|
884
938
|
label: "",
|
|
885
|
-
placeholder:
|
|
939
|
+
placeholder: resolvedDateFromLabel,
|
|
886
940
|
format: "medium",
|
|
887
941
|
value: rangeVal.from,
|
|
888
942
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, from: val })
|
|
@@ -893,7 +947,7 @@ var DataTable = ({
|
|
|
893
947
|
size: "sm",
|
|
894
948
|
name: `filter-${filter.name}-to`,
|
|
895
949
|
label: "",
|
|
896
|
-
placeholder:
|
|
950
|
+
placeholder: resolvedDateToLabel,
|
|
897
951
|
format: "medium",
|
|
898
952
|
value: rangeVal.to,
|
|
899
953
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, to: val })
|
|
@@ -916,7 +970,7 @@ var DataTable = ({
|
|
|
916
970
|
}
|
|
917
971
|
);
|
|
918
972
|
};
|
|
919
|
-
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
|
|
973
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
|
|
920
974
|
import_ui_extensions.SearchInput,
|
|
921
975
|
{
|
|
922
976
|
name: "datatable-search",
|
|
@@ -924,7 +978,7 @@ var DataTable = ({
|
|
|
924
978
|
value: searchTerm,
|
|
925
979
|
onChange: handleSearchChange
|
|
926
980
|
}
|
|
927
|
-
), filters.slice(0,
|
|
981
|
+
), filters.slice(0, filterInlineLimit).map(renderFilterControl), filters.length > filterInlineLimit && /* @__PURE__ */ import_react.default.createElement(
|
|
928
982
|
import_ui_extensions.Button,
|
|
929
983
|
{
|
|
930
984
|
variant: "transparent",
|
|
@@ -932,16 +986,25 @@ var DataTable = ({
|
|
|
932
986
|
onClick: () => setShowMoreFilters((prev) => !prev)
|
|
933
987
|
},
|
|
934
988
|
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "filter", size: "sm" }),
|
|
935
|
-
"
|
|
936
|
-
|
|
989
|
+
" ",
|
|
990
|
+
resolvedFiltersButtonLabel
|
|
991
|
+
)), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "end", gap: "sm", wrap: "wrap" }, filters.slice(filterInlineLimit).map(renderFilterControl)), activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges && activeChips.map((chip) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tag, { key: chip.key, variant: "default", onDelete: () => handleFilterRemove(chip.key) }, chip.label)), showClearFiltersButton && /* @__PURE__ */ import_react.default.createElement(
|
|
937
992
|
import_ui_extensions.Button,
|
|
938
993
|
{
|
|
939
994
|
variant: "transparent",
|
|
940
995
|
size: "extra-small",
|
|
941
996
|
onClick: () => handleFilterRemove("all")
|
|
942
997
|
},
|
|
943
|
-
|
|
944
|
-
)))), showRowCount && displayCount > 0 && !(selectable && selectedIds.size > 0) && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), selectable && selectedIds.size > 0 &&
|
|
998
|
+
resolvedClearAllLabel
|
|
999
|
+
)))), showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0) && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), showSelectionBar && selectable && selectedIds.size > 0 && (renderSelectionBar ? renderSelectionBar({
|
|
1000
|
+
selectedIds,
|
|
1001
|
+
selectedCount: selectedIds.size,
|
|
1002
|
+
displayCount,
|
|
1003
|
+
countLabel,
|
|
1004
|
+
onSelectAll: handleSelectAllRows,
|
|
1005
|
+
onDeselectAll: handleDeselectAll,
|
|
1006
|
+
selectionActions
|
|
1007
|
+
}) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { inline: true, format: { fontWeight: "demibold" } }, typeof resolvedSelectedLabel === "function" ? resolvedSelectedLabel(selectedIds.size, countLabel(selectedIds.size)) : resolvedSelectedLabel), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "transparent", size: "extra-small", onClick: handleSelectAllRows }, typeof resolvedSelectAllLabel === "function" ? resolvedSelectAllLabel(displayCount, countLabel(displayCount)) : resolvedSelectAllLabel), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "transparent", size: "extra-small", onClick: handleDeselectAll }, resolvedDeselectAllLabel), selectionActions.map((action, i) => /* @__PURE__ */ import_react.default.createElement(
|
|
945
1008
|
import_ui_extensions.Button,
|
|
946
1009
|
{
|
|
947
1010
|
key: i,
|
|
@@ -952,7 +1015,11 @@ var DataTable = ({
|
|
|
952
1015
|
action.icon && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: action.icon, size: "sm" }),
|
|
953
1016
|
" ",
|
|
954
1017
|
action.label
|
|
955
|
-
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), loading ?
|
|
1018
|
+
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel))))), loading ? renderLoadingState ? renderLoadingState({ label: resolvedLoadingLabel }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.LoadingSpinner, { label: resolvedLoadingLabel, layout: "centered" }) : error ? renderErrorState ? renderErrorState({
|
|
1019
|
+
error,
|
|
1020
|
+
title: typeof error === "string" ? error : resolvedErrorTitle,
|
|
1021
|
+
message: typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage
|
|
1022
|
+
}) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.ErrorState, { title: typeof error === "string" ? error : resolvedErrorTitle }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, null, typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage)) : displayRows.length === 0 ? renderEmptyState ? renderEmptyState({ title: resolvedEmptyTitle, message: resolvedEmptyMessage }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.EmptyState, { title: resolvedEmptyTitle, layout: "vertical" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, null, resolvedEmptyMessage))) : /* @__PURE__ */ import_react.default.createElement(
|
|
956
1023
|
import_ui_extensions.Table,
|
|
957
1024
|
{
|
|
958
1025
|
bordered,
|
|
@@ -1135,7 +1202,6 @@ var runDefaultFieldValidator = (value, field, allValues) => {
|
|
|
1135
1202
|
if (!isTimeValueObject(value)) return `${errorPrefix} has an invalid time`;
|
|
1136
1203
|
break;
|
|
1137
1204
|
case "datetime": {
|
|
1138
|
-
if (isDateValueObject(value)) break;
|
|
1139
1205
|
if (!isPlainObject(value)) return `${errorPrefix} has an invalid date/time`;
|
|
1140
1206
|
const hasDate = value.date !== void 0;
|
|
1141
1207
|
const hasTime = value.time !== void 0;
|
|
@@ -1201,12 +1267,14 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
|
|
|
1201
1267
|
};
|
|
1202
1268
|
var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
|
|
1203
1269
|
const includeCustomValidators = options.includeCustomValidators !== false;
|
|
1204
|
-
|
|
1270
|
+
const msg = options.messages || {};
|
|
1271
|
+
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
|
|
1205
1272
|
const isRequired = resolveRequired(field, allValues);
|
|
1206
1273
|
const plugin = fieldTypes && fieldTypes[field.type];
|
|
1207
1274
|
const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
|
|
1208
1275
|
if (isRequired && empty) {
|
|
1209
|
-
|
|
1276
|
+
const fn = msg.required || ((label) => `${label} is required`);
|
|
1277
|
+
return typeof fn === "function" ? fn(field.label) : fn;
|
|
1210
1278
|
}
|
|
1211
1279
|
if (empty) return null;
|
|
1212
1280
|
if (field.useDefaultValidators !== false) {
|
|
@@ -1215,23 +1283,27 @@ var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
|
|
|
1215
1283
|
}
|
|
1216
1284
|
if (field.pattern && typeof value === "string") {
|
|
1217
1285
|
if (!field.pattern.test(value)) {
|
|
1218
|
-
return field.patternMessage || "Invalid format";
|
|
1286
|
+
return field.patternMessage || msg.invalidFormat || "Invalid format";
|
|
1219
1287
|
}
|
|
1220
1288
|
}
|
|
1221
1289
|
if (typeof value === "string") {
|
|
1222
1290
|
if (field.minLength != null && value.length < field.minLength) {
|
|
1223
|
-
|
|
1291
|
+
const fn = msg.minLength || ((min) => `Must be at least ${min} characters`);
|
|
1292
|
+
return typeof fn === "function" ? fn(field.minLength) : fn;
|
|
1224
1293
|
}
|
|
1225
1294
|
if (field.maxLength != null && value.length > field.maxLength) {
|
|
1226
|
-
|
|
1295
|
+
const fn = msg.maxLength || ((max) => `Must be no more than ${max} characters`);
|
|
1296
|
+
return typeof fn === "function" ? fn(field.maxLength) : fn;
|
|
1227
1297
|
}
|
|
1228
1298
|
}
|
|
1229
1299
|
if (typeof value === "number") {
|
|
1230
1300
|
if (field.min != null && value < field.min) {
|
|
1231
|
-
|
|
1301
|
+
const fn = msg.minValue || ((min) => `Must be at least ${min}`);
|
|
1302
|
+
return typeof fn === "function" ? fn(field.min) : fn;
|
|
1232
1303
|
}
|
|
1233
1304
|
if (field.max != null && value > field.max) {
|
|
1234
|
-
|
|
1305
|
+
const fn = msg.maxValue || ((max) => `Must be no more than ${max}`);
|
|
1306
|
+
return typeof fn === "function" ? fn(field.max) : fn;
|
|
1235
1307
|
}
|
|
1236
1308
|
}
|
|
1237
1309
|
if (field.type === "date" && isDateValueObject(value)) {
|
|
@@ -1343,6 +1415,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1343
1415
|
// (values, { reset, rawValues }) => void | Promise
|
|
1344
1416
|
transformValues,
|
|
1345
1417
|
// (values) => values — reshape before submit
|
|
1418
|
+
transformInitialValues,
|
|
1419
|
+
// (rawInitialValues) => values — reshape raw data on load
|
|
1346
1420
|
onBeforeSubmit,
|
|
1347
1421
|
// (values) => boolean | Promise<boolean> — intercept submit
|
|
1348
1422
|
onSubmitSuccess,
|
|
@@ -1437,8 +1511,18 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1437
1511
|
// string — warning alert when readOnly
|
|
1438
1512
|
alerts,
|
|
1439
1513
|
// { addAlert, readOnlyTitle, errorTitle, successTitle }
|
|
1440
|
-
errors: controlledErrors
|
|
1514
|
+
errors: controlledErrors,
|
|
1441
1515
|
// controlled validation errors
|
|
1516
|
+
showReadOnlyAlert = true,
|
|
1517
|
+
// show warning Alert when readOnly is true
|
|
1518
|
+
showInlineAlerts = true,
|
|
1519
|
+
// show inline form-level error/success Alerts
|
|
1520
|
+
renderReadOnlyAlert,
|
|
1521
|
+
// (context: { title, message }) => ReactNode — custom readOnly alert renderer
|
|
1522
|
+
renderFieldError,
|
|
1523
|
+
// (error: string, field: object) => ReactNode — custom field error renderer
|
|
1524
|
+
defaultCurrency = "USD"
|
|
1525
|
+
// form-level default ISO 4217 currency code for currency fields
|
|
1442
1526
|
} = props;
|
|
1443
1527
|
const {
|
|
1444
1528
|
onDirtyChange,
|
|
@@ -1450,6 +1534,23 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1450
1534
|
const cancelButtonLabel = (labels == null ? void 0 : labels.cancel) || "Cancel";
|
|
1451
1535
|
const backButtonLabel = (labels == null ? void 0 : labels.back) || "Back";
|
|
1452
1536
|
const nextButtonLabel = (labels == null ? void 0 : labels.next) || "Next";
|
|
1537
|
+
const requiredMessage = (labels == null ? void 0 : labels.required) || ((label) => `${label} is required`);
|
|
1538
|
+
const invalidFormatMessage = (labels == null ? void 0 : labels.invalidFormat) || "Invalid format";
|
|
1539
|
+
const minLengthMessage = (labels == null ? void 0 : labels.minLength) || ((min) => `Must be at least ${min} characters`);
|
|
1540
|
+
const maxLengthMessage = (labels == null ? void 0 : labels.maxLength) || ((max) => `Must be no more than ${max} characters`);
|
|
1541
|
+
const minValueMessage = (labels == null ? void 0 : labels.minValue) || ((min) => `Must be at least ${min}`);
|
|
1542
|
+
const maxValueMessage = (labels == null ? void 0 : labels.maxValue) || ((max) => `Must be no more than ${max}`);
|
|
1543
|
+
const dependentPropertiesLabel = (labels == null ? void 0 : labels.dependentProperties) || "Dependent properties";
|
|
1544
|
+
const repeaterAddLabel = (labels == null ? void 0 : labels.repeaterAdd) || "Add";
|
|
1545
|
+
const repeaterRemoveLabel = (labels == null ? void 0 : labels.repeaterRemove) || "Remove";
|
|
1546
|
+
const validationMessages = labels ? {
|
|
1547
|
+
required: requiredMessage,
|
|
1548
|
+
invalidFormat: invalidFormatMessage,
|
|
1549
|
+
minLength: minLengthMessage,
|
|
1550
|
+
maxLength: maxLengthMessage,
|
|
1551
|
+
minValue: minValueMessage,
|
|
1552
|
+
maxValue: maxValueMessage
|
|
1553
|
+
} : void 0;
|
|
1453
1554
|
const addAlert = alerts == null ? void 0 : alerts.addAlert;
|
|
1454
1555
|
const readOnlyTitle = (alerts == null ? void 0 : alerts.readOnlyTitle) || "Read Only";
|
|
1455
1556
|
const errorTitle = (alerts == null ? void 0 : alerts.errorTitle) || "Error";
|
|
@@ -1479,12 +1580,27 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1479
1580
|
prevSuccessRef.current = formSuccess;
|
|
1480
1581
|
}, [addAlert, formSuccess, successTitle]);
|
|
1481
1582
|
const computeInitialValues = () => {
|
|
1583
|
+
const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
|
|
1482
1584
|
const vals = {};
|
|
1483
1585
|
for (const field of fields) {
|
|
1484
1586
|
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
|
|
1587
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
1588
|
+
for (const item of field.items) {
|
|
1589
|
+
const subFields = field.fields(item);
|
|
1590
|
+
for (const sf of subFields) {
|
|
1591
|
+
const plugin2 = fieldTypes && fieldTypes[sf.type];
|
|
1592
|
+
const emptyValue2 = plugin2 && plugin2.getEmptyValue ? plugin2.getEmptyValue() : getEmptyValue(sf);
|
|
1593
|
+
let init2 = resolved && resolved[sf.name] !== void 0 ? resolved[sf.name] : sf.defaultValue !== void 0 ? sf.defaultValue : emptyValue2;
|
|
1594
|
+
if (sf.transformIn) init2 = sf.transformIn(init2);
|
|
1595
|
+
vals[sf.name] = init2;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1485
1600
|
const plugin = fieldTypes && fieldTypes[field.type];
|
|
1486
1601
|
const emptyValue = plugin && plugin.getEmptyValue ? plugin.getEmptyValue() : getEmptyValue(field);
|
|
1487
|
-
|
|
1602
|
+
let init = resolved && resolved[field.name] !== void 0 ? resolved[field.name] : field.defaultValue !== void 0 ? field.defaultValue : emptyValue;
|
|
1603
|
+
if (field.transformIn) init = field.transformIn(init);
|
|
1488
1604
|
vals[field.name] = init;
|
|
1489
1605
|
}
|
|
1490
1606
|
return vals;
|
|
@@ -1517,7 +1633,14 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1517
1633
|
formErrorsRef.current = formErrors;
|
|
1518
1634
|
const fieldByName = (0, import_react2.useMemo)(() => {
|
|
1519
1635
|
const map = /* @__PURE__ */ new Map();
|
|
1520
|
-
for (const field of fields)
|
|
1636
|
+
for (const field of fields) {
|
|
1637
|
+
map.set(field.name, field);
|
|
1638
|
+
if (field.type === "fieldGroup" && field.items && field.fields) {
|
|
1639
|
+
for (const item of field.items) {
|
|
1640
|
+
for (const sf of field.fields(item)) map.set(sf.name, sf);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1521
1644
|
return map;
|
|
1522
1645
|
}, [fields]);
|
|
1523
1646
|
const isDev = typeof process === "undefined" || !process.env || process.env.NODE_ENV !== "production";
|
|
@@ -1688,7 +1811,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1688
1811
|
const rowValues = { ...allValues, [field.name]: rows };
|
|
1689
1812
|
subFields.forEach((subField) => {
|
|
1690
1813
|
if (subField.visible && !subField.visible(rowValues)) return;
|
|
1691
|
-
const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes);
|
|
1814
|
+
const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
1692
1815
|
if (!err) return;
|
|
1693
1816
|
const key = getRepeaterErrorKey(field.name, rowIdx, subField.name);
|
|
1694
1817
|
errors[key] = err;
|
|
@@ -1715,9 +1838,9 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1715
1838
|
);
|
|
1716
1839
|
return repeaterResult.errors[name] || null;
|
|
1717
1840
|
}
|
|
1718
|
-
return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes);
|
|
1841
|
+
return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes, { messages: validationMessages });
|
|
1719
1842
|
},
|
|
1720
|
-
[fieldByName, formValues, validateRepeaterField, fieldTypes]
|
|
1843
|
+
[fieldByName, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
1721
1844
|
);
|
|
1722
1845
|
const validateVisibleFields = (0, import_react2.useCallback)(
|
|
1723
1846
|
(fieldSubset) => {
|
|
@@ -1733,7 +1856,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1733
1856
|
}
|
|
1734
1857
|
continue;
|
|
1735
1858
|
}
|
|
1736
|
-
const err = runValidators(formValues[field.name], field, formValues, fieldTypes);
|
|
1859
|
+
const err = runValidators(formValues[field.name], field, formValues, fieldTypes, { messages: validationMessages });
|
|
1737
1860
|
if (err) {
|
|
1738
1861
|
errors[field.name] = err;
|
|
1739
1862
|
hasErrors = true;
|
|
@@ -1741,14 +1864,14 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1741
1864
|
}
|
|
1742
1865
|
return { errors, hasErrors };
|
|
1743
1866
|
},
|
|
1744
|
-
[visibleFields, formValues, validateRepeaterField, fieldTypes]
|
|
1867
|
+
[visibleFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
|
|
1745
1868
|
);
|
|
1746
1869
|
const runAsyncValidation = (0, import_react2.useCallback)(
|
|
1747
1870
|
(name, value) => {
|
|
1748
1871
|
const field = fieldByName.get(name);
|
|
1749
1872
|
if (!field || field.type === "repeater") return null;
|
|
1750
1873
|
const val = value != null ? value : formValues[name];
|
|
1751
|
-
const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false });
|
|
1874
|
+
const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false, messages: validationMessages });
|
|
1752
1875
|
const prevController = asyncAbortRef.current.get(name);
|
|
1753
1876
|
if (prevController) prevController.abort();
|
|
1754
1877
|
asyncAbortRef.current.delete(name);
|
|
@@ -1939,23 +2062,27 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1939
2062
|
);
|
|
1940
2063
|
const handleFieldInput = (0, import_react2.useCallback)(
|
|
1941
2064
|
(name, value) => {
|
|
2065
|
+
handleFieldChange(name, value);
|
|
1942
2066
|
if (!validateOnChange) return;
|
|
1943
2067
|
const err = validateField(name, value);
|
|
1944
2068
|
updateErrors({ [name]: err });
|
|
1945
2069
|
},
|
|
1946
|
-
[validateOnChange, validateField, updateErrors]
|
|
2070
|
+
[validateOnChange, validateField, updateErrors, handleFieldChange]
|
|
1947
2071
|
);
|
|
1948
2072
|
const handleFieldBlur = (0, import_react2.useCallback)(
|
|
1949
2073
|
(name, value) => {
|
|
1950
|
-
if (!validateOnBlur) return;
|
|
1951
2074
|
const resolvedValue = value != null ? value : formValuesRef.current[name];
|
|
2075
|
+
if (value != null && value !== formValuesRef.current[name]) {
|
|
2076
|
+
handleFieldChange(name, value);
|
|
2077
|
+
}
|
|
2078
|
+
if (!validateOnBlur) return;
|
|
1952
2079
|
const err = validateField(name, resolvedValue);
|
|
1953
2080
|
updateErrors({ [name]: err });
|
|
1954
2081
|
if (!err) {
|
|
1955
2082
|
triggerAsyncValidation(name, resolvedValue);
|
|
1956
2083
|
}
|
|
1957
2084
|
},
|
|
1958
|
-
[validateOnBlur, validateField, updateErrors, triggerAsyncValidation]
|
|
2085
|
+
[validateOnBlur, validateField, updateErrors, triggerAsyncValidation, handleFieldChange]
|
|
1959
2086
|
);
|
|
1960
2087
|
const handleSubmit = (0, import_react2.useCallback)(
|
|
1961
2088
|
async (e) => {
|
|
@@ -1988,8 +2115,17 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
1988
2115
|
const rawValues = {};
|
|
1989
2116
|
for (const key of Object.keys(formValues)) {
|
|
1990
2117
|
const f = fieldByName.get(key);
|
|
1991
|
-
if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList")) continue;
|
|
1992
|
-
rawValues[key] = formValues[key];
|
|
2118
|
+
if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
|
|
2119
|
+
rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
|
|
2120
|
+
}
|
|
2121
|
+
for (const f of fields) {
|
|
2122
|
+
if (f.type !== "fieldGroup" || !f.items || !f.fields) continue;
|
|
2123
|
+
for (const item of f.items) {
|
|
2124
|
+
for (const sf of f.fields(item)) {
|
|
2125
|
+
if (formValues[sf.name] === void 0) continue;
|
|
2126
|
+
rawValues[sf.name] = sf.transformOut ? sf.transformOut(formValues[sf.name]) : formValues[sf.name];
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
1993
2129
|
}
|
|
1994
2130
|
const submitValues = transformValues ? transformValues(rawValues) : rawValues;
|
|
1995
2131
|
if (onBeforeSubmit) {
|
|
@@ -2117,6 +2253,12 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2117
2253
|
[replaceErrors]
|
|
2118
2254
|
);
|
|
2119
2255
|
const renderField = (field) => {
|
|
2256
|
+
const fieldError = formErrors[field.name] || null;
|
|
2257
|
+
const rendered = renderFieldInner(field);
|
|
2258
|
+
if (!renderFieldError || !fieldError) return rendered;
|
|
2259
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, rendered, renderFieldError(fieldError, field));
|
|
2260
|
+
};
|
|
2261
|
+
const renderFieldInner = (field) => {
|
|
2120
2262
|
const fieldValue = formValues[field.name];
|
|
2121
2263
|
const fieldError = formErrors[field.name] || null;
|
|
2122
2264
|
const hasError = !!fieldError;
|
|
@@ -2126,10 +2268,125 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2126
2268
|
const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
|
|
2127
2269
|
if (field.type === "display") {
|
|
2128
2270
|
if (field.render) {
|
|
2129
|
-
return field.render({
|
|
2271
|
+
return field.render({
|
|
2272
|
+
allValues: formValues,
|
|
2273
|
+
setFieldValue: (name, value) => handleFieldChange(name, value),
|
|
2274
|
+
setFieldError: (name, message) => updateErrors({ [name]: message })
|
|
2275
|
+
});
|
|
2130
2276
|
}
|
|
2131
2277
|
return null;
|
|
2132
2278
|
}
|
|
2279
|
+
if (field.type === "fieldGroup") {
|
|
2280
|
+
const items = field.items || [];
|
|
2281
|
+
const fieldsFn = field.fields;
|
|
2282
|
+
if (!fieldsFn) return null;
|
|
2283
|
+
const groupColumns = field.columns || 1;
|
|
2284
|
+
const showItemLabel = field.showItemLabel !== false;
|
|
2285
|
+
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), field.description && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { variant: "microcopy" }, field.description), items.map((item, itemIdx) => {
|
|
2286
|
+
const subFields = fieldsFn(item);
|
|
2287
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { key: item.key || itemIdx, direction: "row", gap: "xs", align: "end" }, showItemLabel && item.label && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { flex: 1 }, itemIdx === 0 ? /* @__PURE__ */ import_react2.default.createElement(
|
|
2288
|
+
import_ui_extensions2.Input,
|
|
2289
|
+
{
|
|
2290
|
+
name: `_fieldGroup-label-${field.name}-${itemIdx}`,
|
|
2291
|
+
label: "\xA0",
|
|
2292
|
+
value: item.label,
|
|
2293
|
+
readOnly: true,
|
|
2294
|
+
disabled: true
|
|
2295
|
+
}
|
|
2296
|
+
) : /* @__PURE__ */ import_react2.default.createElement(
|
|
2297
|
+
import_ui_extensions2.Input,
|
|
2298
|
+
{
|
|
2299
|
+
name: `_fieldGroup-label-${field.name}-${itemIdx}`,
|
|
2300
|
+
value: item.label,
|
|
2301
|
+
readOnly: true,
|
|
2302
|
+
disabled: true
|
|
2303
|
+
}
|
|
2304
|
+
)), subFields.map((sf) => {
|
|
2305
|
+
const sfValue = formValues[sf.name];
|
|
2306
|
+
const sfError = formErrors[sf.name] || null;
|
|
2307
|
+
const sfLabel = itemIdx === 0 ? sf.label : void 0;
|
|
2308
|
+
const sfReadOnly = sf.readOnly || formReadOnly;
|
|
2309
|
+
const sfDisabled = disabled || sf.disabled || formReadOnly;
|
|
2310
|
+
const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
|
|
2311
|
+
const sfProps = {
|
|
2312
|
+
name: sf.name,
|
|
2313
|
+
label: sfLabel,
|
|
2314
|
+
placeholder: sf.placeholder,
|
|
2315
|
+
description: itemIdx === 0 ? sf.description : void 0,
|
|
2316
|
+
readOnly: sfReadOnly,
|
|
2317
|
+
disabled: sfDisabled,
|
|
2318
|
+
error: !!sfError,
|
|
2319
|
+
validationMessage: sfError || void 0,
|
|
2320
|
+
...sf.fieldProps || {}
|
|
2321
|
+
};
|
|
2322
|
+
let sfElement;
|
|
2323
|
+
switch (sf.type) {
|
|
2324
|
+
case "select":
|
|
2325
|
+
sfElement = /* @__PURE__ */ import_react2.default.createElement(
|
|
2326
|
+
import_ui_extensions2.Select,
|
|
2327
|
+
{
|
|
2328
|
+
...sfProps,
|
|
2329
|
+
value: sfValue,
|
|
2330
|
+
options: resolveOptions(sf, formValues),
|
|
2331
|
+
onChange: sfOnChange
|
|
2332
|
+
}
|
|
2333
|
+
);
|
|
2334
|
+
break;
|
|
2335
|
+
case "number":
|
|
2336
|
+
sfElement = /* @__PURE__ */ import_react2.default.createElement(
|
|
2337
|
+
import_ui_extensions2.NumberInput,
|
|
2338
|
+
{
|
|
2339
|
+
...sfProps,
|
|
2340
|
+
value: sfValue,
|
|
2341
|
+
onChange: sfOnChange,
|
|
2342
|
+
onBlur: (v) => handleFieldBlur(sf.name, v)
|
|
2343
|
+
}
|
|
2344
|
+
);
|
|
2345
|
+
break;
|
|
2346
|
+
case "toggle":
|
|
2347
|
+
sfElement = /* @__PURE__ */ import_react2.default.createElement(
|
|
2348
|
+
import_ui_extensions2.Toggle,
|
|
2349
|
+
{
|
|
2350
|
+
name: sf.name,
|
|
2351
|
+
label: sfLabel || sf.label,
|
|
2352
|
+
checked: !!sfValue,
|
|
2353
|
+
size: sf.size || "md",
|
|
2354
|
+
labelDisplay: sf.labelDisplay || "top",
|
|
2355
|
+
readonly: sfReadOnly,
|
|
2356
|
+
disabled: sfDisabled,
|
|
2357
|
+
onChange: sfOnChange,
|
|
2358
|
+
...sf.fieldProps || {}
|
|
2359
|
+
}
|
|
2360
|
+
);
|
|
2361
|
+
break;
|
|
2362
|
+
case "time":
|
|
2363
|
+
sfElement = /* @__PURE__ */ import_react2.default.createElement(
|
|
2364
|
+
import_ui_extensions2.TimeInput,
|
|
2365
|
+
{
|
|
2366
|
+
...sfProps,
|
|
2367
|
+
value: sfValue,
|
|
2368
|
+
interval: sf.interval,
|
|
2369
|
+
onChange: sfOnChange,
|
|
2370
|
+
onBlur: (v) => handleFieldBlur(sf.name, v)
|
|
2371
|
+
}
|
|
2372
|
+
);
|
|
2373
|
+
break;
|
|
2374
|
+
default:
|
|
2375
|
+
sfElement = /* @__PURE__ */ import_react2.default.createElement(
|
|
2376
|
+
import_ui_extensions2.Input,
|
|
2377
|
+
{
|
|
2378
|
+
...sfProps,
|
|
2379
|
+
value: sfValue || "",
|
|
2380
|
+
onChange: sfOnChange,
|
|
2381
|
+
onInput: (v) => handleFieldInput(sf.name, v),
|
|
2382
|
+
onBlur: (v) => handleFieldBlur(sf.name, v)
|
|
2383
|
+
}
|
|
2384
|
+
);
|
|
2385
|
+
}
|
|
2386
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { key: sf.name, flex: 1 }, sfElement);
|
|
2387
|
+
}));
|
|
2388
|
+
}));
|
|
2389
|
+
}
|
|
2133
2390
|
if (field.type === "crmPropertyList") {
|
|
2134
2391
|
return /* @__PURE__ */ import_react2.default.createElement(
|
|
2135
2392
|
import_crm.CrmPropertyList,
|
|
@@ -2183,7 +2440,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2183
2440
|
readOnly: isReadOnly,
|
|
2184
2441
|
disabled: isDisabled,
|
|
2185
2442
|
error: hasError,
|
|
2186
|
-
validationMessage: fieldError || void 0,
|
|
2443
|
+
validationMessage: renderFieldError ? void 0 : fieldError || void 0,
|
|
2187
2444
|
...field.loading || validatingFields[field.name] ? { loading: true } : {},
|
|
2188
2445
|
...field.fieldProps || {}
|
|
2189
2446
|
};
|
|
@@ -2253,7 +2510,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2253
2510
|
import_ui_extensions2.CurrencyInput,
|
|
2254
2511
|
{
|
|
2255
2512
|
...commonProps,
|
|
2256
|
-
currency: field.currency ||
|
|
2513
|
+
currency: field.currency || defaultCurrency,
|
|
2257
2514
|
value: fieldValue,
|
|
2258
2515
|
min: field.min,
|
|
2259
2516
|
max: field.max,
|
|
@@ -2431,8 +2688,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2431
2688
|
const renderRemoveControl = repeaterProps.renderRemove;
|
|
2432
2689
|
const renderMoveUpControl = repeaterProps.renderMoveUp;
|
|
2433
2690
|
const renderMoveDownControl = repeaterProps.renderMoveDown;
|
|
2434
|
-
const addLabel = repeaterProps.addLabel ||
|
|
2435
|
-
const removeLabel = repeaterProps.removeLabel ||
|
|
2691
|
+
const addLabel = repeaterProps.addLabel || repeaterAddLabel;
|
|
2692
|
+
const removeLabel = repeaterProps.removeLabel || repeaterRemoveLabel;
|
|
2436
2693
|
const moveUpLabel = repeaterProps.moveUpLabel || "Up";
|
|
2437
2694
|
const moveDownLabel = repeaterProps.moveDownLabel || "Down";
|
|
2438
2695
|
const canEditRows = !isReadOnly && !isDisabled;
|
|
@@ -2466,7 +2723,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2466
2723
|
};
|
|
2467
2724
|
const validateSubField = (rowIdx, subField, subValue, nextRows) => {
|
|
2468
2725
|
const rowValues = { ...formValues, [field.name]: nextRows };
|
|
2469
|
-
const err = runValidators(subValue, subField, rowValues, fieldTypes);
|
|
2726
|
+
const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
|
|
2470
2727
|
setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
|
|
2471
2728
|
};
|
|
2472
2729
|
const handleSubFieldChange = (rowIdx, subField, subValue) => {
|
|
@@ -2584,7 +2841,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2584
2841
|
const renderDependentGroup = (parentField, dependents) => {
|
|
2585
2842
|
const firstWithLabel = dependents.find((f) => getDependsOnLabel(f)) || dependents[0];
|
|
2586
2843
|
const firstWithMessage = dependents.find((f) => getDependsOnMessage(f)) || dependents[0];
|
|
2587
|
-
const groupLabel = getDependsOnLabel(firstWithLabel) ||
|
|
2844
|
+
const groupLabel = getDependsOnLabel(firstWithLabel) || dependentPropertiesLabel;
|
|
2588
2845
|
const rawMessage = getDependsOnMessage(firstWithMessage);
|
|
2589
2846
|
const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
|
|
2590
2847
|
return /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "column", gap }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Link, { inline: true, variant: "dark", overlay: /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Tooltip, null, tooltipMessage) }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Icon, { name: "info" })))), renderFieldSubset(dependents)));
|
|
@@ -2674,38 +2931,19 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2674
2931
|
}
|
|
2675
2932
|
return elements;
|
|
2676
2933
|
};
|
|
2677
|
-
const
|
|
2934
|
+
const renderSingleColumnLayout = (fieldSubset) => {
|
|
2678
2935
|
const fieldList = fieldSubset || visibleFields;
|
|
2679
|
-
const rows = [];
|
|
2680
|
-
let i = 0;
|
|
2681
|
-
while (i < fieldList.length) {
|
|
2682
|
-
const field = fieldList[i];
|
|
2683
|
-
if (field.width === "half" && i + 1 < fieldList.length && fieldList[i + 1].width === "half" && !getDependsOnName(field)) {
|
|
2684
|
-
rows.push({ type: "pair", fields: [fieldList[i], fieldList[i + 1]] });
|
|
2685
|
-
i += 2;
|
|
2686
|
-
} else {
|
|
2687
|
-
rows.push({ type: "single", field });
|
|
2688
|
-
i++;
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
2936
|
const elements = [];
|
|
2692
2937
|
const processedDeps = /* @__PURE__ */ new Set();
|
|
2693
|
-
for (const
|
|
2694
|
-
if (
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
elements.push(
|
|
2702
|
-
/* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, { key: field.name }, renderField(field))
|
|
2703
|
-
);
|
|
2704
|
-
const dependents = getDependents(field);
|
|
2705
|
-
if (dependents.length > 0) {
|
|
2706
|
-
for (const dep of dependents) processedDeps.add(dep.name);
|
|
2707
|
-
elements.push(renderDependentGroup(field, dependents));
|
|
2708
|
-
}
|
|
2938
|
+
for (const field of fieldList) {
|
|
2939
|
+
if (processedDeps.has(field.name)) continue;
|
|
2940
|
+
elements.push(
|
|
2941
|
+
/* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, { key: field.name }, renderField(field))
|
|
2942
|
+
);
|
|
2943
|
+
const dependents = getDependents(field);
|
|
2944
|
+
if (dependents.length > 0) {
|
|
2945
|
+
for (const dep of dependents) processedDeps.add(dep.name);
|
|
2946
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
2709
2947
|
}
|
|
2710
2948
|
}
|
|
2711
2949
|
return elements;
|
|
@@ -2716,10 +2954,20 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2716
2954
|
let batch = [];
|
|
2717
2955
|
const flushBatch = () => {
|
|
2718
2956
|
if (batch.length === 0) return;
|
|
2719
|
-
|
|
2720
|
-
|
|
2957
|
+
if (maxColumns) {
|
|
2958
|
+
const chunks = Array.from(
|
|
2959
|
+
{ length: Math.ceil(batch.length / maxColumns) },
|
|
2960
|
+
(_, i) => batch.slice(i * maxColumns, i * maxColumns + maxColumns)
|
|
2961
|
+
);
|
|
2962
|
+
for (const chunk of chunks) {
|
|
2963
|
+
const remainder = maxColumns - chunk.length;
|
|
2964
|
+
elements.push(
|
|
2965
|
+
/* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { key: `ag-${chunk[0].name}`, direction: "row", gap }, chunk.map((f) => /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { key: f.name, flex: 1 }, renderField(f))), remainder > 0 && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { flex: remainder }))
|
|
2966
|
+
);
|
|
2967
|
+
}
|
|
2968
|
+
} else {
|
|
2721
2969
|
elements.push(
|
|
2722
|
-
/* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.AutoGrid, { key: `ag-${
|
|
2970
|
+
/* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.AutoGrid, { key: `ag-${batch[0].name}`, columnWidth, flexible: true, gap }, batch.map((f) => /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, { key: f.name }, renderField(f))))
|
|
2723
2971
|
);
|
|
2724
2972
|
}
|
|
2725
2973
|
batch = [];
|
|
@@ -2778,7 +3026,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2778
3026
|
if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
|
|
2779
3027
|
if (columnWidth) return renderAutoGridLayout(fieldSubset);
|
|
2780
3028
|
if (columns > 1) return renderGridLayout(fieldSubset);
|
|
2781
|
-
return
|
|
3029
|
+
return renderSingleColumnLayout(fieldSubset);
|
|
2782
3030
|
};
|
|
2783
3031
|
const renderSections = () => {
|
|
2784
3032
|
const hasSections = Array.isArray(sections) && sections.length > 0;
|
|
@@ -2791,7 +3039,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2791
3039
|
for (const sec of sections) {
|
|
2792
3040
|
const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
|
|
2793
3041
|
if (sectionFields.length === 0) continue;
|
|
2794
|
-
const
|
|
3042
|
+
const sectionContext = { values: formValues, errors: formErrors };
|
|
3043
|
+
const accordionContent = /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields), sec.renderAfter && sec.renderAfter(sectionContext));
|
|
2795
3044
|
const accordion = /* @__PURE__ */ import_react2.default.createElement(
|
|
2796
3045
|
import_ui_extensions2.Accordion,
|
|
2797
3046
|
{
|
|
@@ -2884,7 +3133,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
|
|
|
2884
3133
|
currentStep,
|
|
2885
3134
|
stepNames: steps.map((s) => s.title)
|
|
2886
3135
|
}
|
|
2887
|
-
), formReadOnly && readOnlyMessage && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage), !addAlert && formError && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), !addAlert && formSuccess && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
|
|
3136
|
+
), showReadOnlyAlert && formReadOnly && readOnlyMessage && (renderReadOnlyAlert ? renderReadOnlyAlert({ title: readOnlyTitle, message: readOnlyMessage }) : /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage)), showInlineAlerts && !addAlert && formError && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), showInlineAlerts && !addAlert && formSuccess && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
|
|
2888
3137
|
values: formValues,
|
|
2889
3138
|
goNext: handleNext,
|
|
2890
3139
|
goBack: handleBack,
|