hs-uix 2.1.0 → 2.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 +3 -1
- package/common-components.d.ts +319 -68
- package/dist/calendar.js +397 -119
- package/dist/calendar.mjs +399 -119
- package/dist/common-components.js +3546 -88
- package/dist/common-components.mjs +3530 -84
- package/dist/datatable.js +108 -18
- package/dist/datatable.mjs +108 -18
- package/dist/experimental.js +2876 -0
- package/dist/experimental.mjs +2883 -0
- package/dist/feed.js +267 -38
- package/dist/feed.mjs +260 -37
- package/dist/filter.js +1379 -0
- package/dist/filter.mjs +1334 -0
- package/dist/form.js +222 -26
- package/dist/form.mjs +227 -27
- package/dist/index.js +3255 -353
- package/dist/index.mjs +3199 -344
- package/dist/kanban.js +282 -62
- package/dist/kanban.mjs +273 -61
- package/dist/safe.js +9207 -0
- package/dist/safe.mjs +9298 -0
- package/dist/utils.js +491 -75
- package/dist/utils.mjs +491 -75
- package/experimental.d.ts +1 -0
- package/filter.d.ts +1 -0
- package/index.d.ts +45 -3
- package/package.json +19 -1
- package/safe.d.ts +1 -0
- package/src/calendar/README.md +76 -5
- package/src/calendar/index.d.ts +108 -1
- package/src/common-components/README.md +140 -1
- package/src/datatable/README.md +0 -2
- package/src/experimental/README.md +126 -0
- package/src/experimental/index.d.ts +346 -0
- package/src/feed/README.md +69 -0
- package/src/feed/index.d.ts +103 -0
- package/src/filter/README.md +148 -0
- package/src/filter/index.d.ts +221 -0
- package/src/form/README.md +132 -4
- package/src/form/index.d.ts +82 -1
- package/src/kanban/README.md +119 -6
- package/src/kanban/index.d.ts +153 -2
- package/src/safe/README.md +108 -0
- package/src/safe/index.d.ts +158 -0
- package/src/utils/README.md +39 -0
- package/src/wizard/README.md +158 -0
- package/src/wizard/index.d.ts +138 -0
- package/utils.d.ts +17 -0
package/dist/index.mjs
CHANGED
|
@@ -267,6 +267,7 @@ var HS_TEXT_COLOR = "#33475b";
|
|
|
267
267
|
var HS_SUBTLE_BG = "#F5F8FA";
|
|
268
268
|
var HS_MUTED_TEXT = "#7C98B6";
|
|
269
269
|
var HS_NEUTRAL_CHIP = "#CBD6E2";
|
|
270
|
+
var SKELETON_FILL = "#DFE3EB";
|
|
270
271
|
var HS_TAG_SUBTLE_BORDER = "#7C98B6";
|
|
271
272
|
var HS_TAG_TEXT_COLOR = HS_TEXT_COLOR;
|
|
272
273
|
var HS_TAG_FONT_SIZE = 12;
|
|
@@ -529,7 +530,7 @@ var GENERATED_ICONS = {
|
|
|
529
530
|
"ZoomOut": { "viewBox": "0 0 32 32", "paths": ["M14.42 26.75c2.85 0 5.47-.97 7.56-2.6l-.03.02 5.28 5.34a1.619 1.619 0 0 0 2.76-1.15c0-.45-.18-.85-.47-1.14l-5.33-5.33c1.59-2.06 2.55-4.68 2.55-7.52C26.74 7.54 21.2 2 14.37 2S2 7.55 2 14.38s5.54 12.37 12.37 12.37h.05m0-21.55c5.06 0 9.16 4.1 9.16 9.16s-4.1 9.16-9.16 9.16-9.16-4.1-9.16-9.16c.01-5.05 4.11-9.14 9.16-9.15Zm-4.31 10.78h8.62c.89 0 1.62-.72 1.62-1.62s-.72-1.62-1.62-1.62h-8.62c-.89 0-1.62.72-1.62 1.62s.72 1.62 1.62 1.62"] }
|
|
530
531
|
};
|
|
531
532
|
|
|
532
|
-
// src/common-components/
|
|
533
|
+
// src/common-components/nativeIconNames.js
|
|
533
534
|
var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
534
535
|
"add",
|
|
535
536
|
"appointment",
|
|
@@ -541,12 +542,12 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
|
541
542
|
"block",
|
|
542
543
|
"book",
|
|
543
544
|
"bulb",
|
|
545
|
+
"callTranscript",
|
|
544
546
|
"calling",
|
|
545
547
|
"callingHangup",
|
|
546
548
|
"callingMade",
|
|
547
549
|
"callingMissed",
|
|
548
550
|
"callingVoicemail",
|
|
549
|
-
"callTranscript",
|
|
550
551
|
"campaigns",
|
|
551
552
|
"cap",
|
|
552
553
|
"checkCircle",
|
|
@@ -575,13 +576,13 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
|
575
576
|
"enroll",
|
|
576
577
|
"exclamation",
|
|
577
578
|
"exclamationCircle",
|
|
578
|
-
"facebook",
|
|
579
579
|
"faceHappy",
|
|
580
580
|
"faceHappyFilled",
|
|
581
581
|
"faceNeutral",
|
|
582
582
|
"faceNeutralFilled",
|
|
583
583
|
"faceSad",
|
|
584
584
|
"faceSadFilled",
|
|
585
|
+
"facebook",
|
|
585
586
|
"favoriteHollow",
|
|
586
587
|
"file",
|
|
587
588
|
"filledXCircleIcon",
|
|
@@ -722,6 +723,8 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
|
722
723
|
"zoomIn",
|
|
723
724
|
"zoomOut"
|
|
724
725
|
]);
|
|
726
|
+
|
|
727
|
+
// src/common-components/Icon.js
|
|
725
728
|
var NATIVE_COLORS = /* @__PURE__ */ new Set(["inherit", "alert", "warning", "success"]);
|
|
726
729
|
var NATIVE_SIZE_TOKENS = {
|
|
727
730
|
sm: "sm",
|
|
@@ -963,6 +966,7 @@ var CollectionFilterControl = ({
|
|
|
963
966
|
{ key: name, direction: "row", align: "center", gap: "xs" },
|
|
964
967
|
h3(DateInput, {
|
|
965
968
|
name: `${controlName}-from`,
|
|
969
|
+
label: filter.fromLabel ?? labels.dateFrom,
|
|
966
970
|
placeholder: filter.fromLabel ?? labels.dateFrom,
|
|
967
971
|
format: "medium",
|
|
968
972
|
value: rangeValue.from ?? null,
|
|
@@ -971,6 +975,7 @@ var CollectionFilterControl = ({
|
|
|
971
975
|
h3(Icon, { name: "right", size: "sm" }),
|
|
972
976
|
h3(DateInput, {
|
|
973
977
|
name: `${controlName}-to`,
|
|
978
|
+
label: filter.toLabel ?? labels.dateTo,
|
|
974
979
|
placeholder: filter.toLabel ?? labels.dateTo,
|
|
975
980
|
format: "medium",
|
|
976
981
|
value: rangeValue.to ?? null,
|
|
@@ -1109,6 +1114,45 @@ var editValidationError = (result) => {
|
|
|
1109
1114
|
return typeof result === "string" ? result : "Invalid value";
|
|
1110
1115
|
};
|
|
1111
1116
|
|
|
1117
|
+
// src/datatable/rowExpansion.js
|
|
1118
|
+
var extractRowId = (row, rowIdField = "id", fallback = void 0) => {
|
|
1119
|
+
const id = row == null ? void 0 : row[rowIdField];
|
|
1120
|
+
return id != null ? id : fallback;
|
|
1121
|
+
};
|
|
1122
|
+
var normalizeExpandedIds = (ids) => {
|
|
1123
|
+
if (ids == null) return /* @__PURE__ */ new Set();
|
|
1124
|
+
const list = ids instanceof Set ? [...ids] : Array.isArray(ids) ? ids : [ids];
|
|
1125
|
+
return new Set(list.filter((id) => id != null));
|
|
1126
|
+
};
|
|
1127
|
+
var expandRowId = (expandedIds, rowId, expandSingle = false) => {
|
|
1128
|
+
if (rowId == null) return expandedIds;
|
|
1129
|
+
if (expandSingle) return /* @__PURE__ */ new Set([rowId]);
|
|
1130
|
+
const next = new Set(expandedIds);
|
|
1131
|
+
next.add(rowId);
|
|
1132
|
+
return next;
|
|
1133
|
+
};
|
|
1134
|
+
var collapseRowId = (expandedIds, rowId) => {
|
|
1135
|
+
if (rowId == null || !expandedIds.has(rowId)) return expandedIds;
|
|
1136
|
+
const next = new Set(expandedIds);
|
|
1137
|
+
next.delete(rowId);
|
|
1138
|
+
return next;
|
|
1139
|
+
};
|
|
1140
|
+
var toggleExpandedId = (expandedIds, rowId, expandSingle = false) => {
|
|
1141
|
+
if (rowId == null) return expandedIds;
|
|
1142
|
+
return expandedIds.has(rowId) ? collapseRowId(expandedIds, rowId) : expandRowId(expandedIds, rowId, expandSingle);
|
|
1143
|
+
};
|
|
1144
|
+
var withDetailRows = (items, expandedIds, rowIdField = "id") => {
|
|
1145
|
+
if (!expandedIds || expandedIds.size === 0) return items;
|
|
1146
|
+
const out = [];
|
|
1147
|
+
items.forEach((item) => {
|
|
1148
|
+
out.push(item);
|
|
1149
|
+
if (item.type === "data" && expandedIds.has(extractRowId(item.row, rowIdField))) {
|
|
1150
|
+
out.push({ type: "detail", row: item.row });
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
return out;
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1112
1156
|
// src/datatable/DataTable.jsx
|
|
1113
1157
|
import {
|
|
1114
1158
|
Box as Box2,
|
|
@@ -1348,6 +1392,21 @@ var DataTable = ({
|
|
|
1348
1392
|
hideRowActionsWhenSelectionActive = false,
|
|
1349
1393
|
// hide row action column while selected-row action bar is visible
|
|
1350
1394
|
// -----------------------------------------------------------------------
|
|
1395
|
+
// Row expansion (detail rows)
|
|
1396
|
+
// -----------------------------------------------------------------------
|
|
1397
|
+
renderExpandedRow,
|
|
1398
|
+
// (row) => ReactNode — providing this enables the feature
|
|
1399
|
+
expandedRowIds: externalExpandedRowIds,
|
|
1400
|
+
// controlled — array of expanded row IDs
|
|
1401
|
+
defaultExpandedRowIds,
|
|
1402
|
+
// uncontrolled — initially expanded row IDs
|
|
1403
|
+
onExpandedRowsChange,
|
|
1404
|
+
// (expandedRowIds[]) => void
|
|
1405
|
+
expandOn = "icon",
|
|
1406
|
+
// "icon" (chevron toggle column) | "row" (click row content)
|
|
1407
|
+
expandSingle = false,
|
|
1408
|
+
// accordion mode — expanding a row collapses the others
|
|
1409
|
+
// -----------------------------------------------------------------------
|
|
1351
1410
|
// Inline editing
|
|
1352
1411
|
// -----------------------------------------------------------------------
|
|
1353
1412
|
editMode,
|
|
@@ -1592,6 +1651,25 @@ var DataTable = ({
|
|
|
1592
1651
|
});
|
|
1593
1652
|
return rows;
|
|
1594
1653
|
}, [groupedData, paginatedRows, expandedGroups]);
|
|
1654
|
+
const expandable = typeof renderExpandedRow === "function";
|
|
1655
|
+
const showExpandColumn = expandable && expandOn === "icon";
|
|
1656
|
+
const [internalExpandedRowIds, setInternalExpandedRowIds] = useState2(
|
|
1657
|
+
() => normalizeExpandedIds(defaultExpandedRowIds)
|
|
1658
|
+
);
|
|
1659
|
+
const expandedRowIds = useMemo(
|
|
1660
|
+
() => externalExpandedRowIds != null ? normalizeExpandedIds(externalExpandedRowIds) : internalExpandedRowIds,
|
|
1661
|
+
[externalExpandedRowIds, internalExpandedRowIds]
|
|
1662
|
+
);
|
|
1663
|
+
const toggleRowExpanded = useCallback2((rowId) => {
|
|
1664
|
+
if (rowId == null) return;
|
|
1665
|
+
const next = toggleExpandedId(expandedRowIds, rowId, expandSingle);
|
|
1666
|
+
if (externalExpandedRowIds == null) setInternalExpandedRowIds(next);
|
|
1667
|
+
if (onExpandedRowsChange) onExpandedRowsChange([...next]);
|
|
1668
|
+
}, [expandedRowIds, expandSingle, externalExpandedRowIds, onExpandedRowsChange]);
|
|
1669
|
+
const renderedRows = useMemo(() => {
|
|
1670
|
+
if (!expandable) return displayRows;
|
|
1671
|
+
return withDetailRows(displayRows, expandedRowIds, rowIdField);
|
|
1672
|
+
}, [expandable, displayRows, expandedRowIds, rowIdField]);
|
|
1595
1673
|
const footerData = serverSide ? data : filteredData;
|
|
1596
1674
|
const activeChips = useMemo(
|
|
1597
1675
|
() => buildActiveFilterChips(filters, filterValues),
|
|
@@ -1743,6 +1821,7 @@ var DataTable = ({
|
|
|
1743
1821
|
const type = col.editType || "text";
|
|
1744
1822
|
const rowId = row[rowIdField];
|
|
1745
1823
|
const fieldName = `edit-${rowId}-${col.field}`;
|
|
1824
|
+
const inputLabel = typeof col.label === "string" ? col.label : col.field;
|
|
1746
1825
|
const commit = (val) => commitEdit(row, col.field, val);
|
|
1747
1826
|
const exitEdit = () => {
|
|
1748
1827
|
if (editError) return;
|
|
@@ -1783,15 +1862,15 @@ var DataTable = ({
|
|
|
1783
1862
|
case "multiselect":
|
|
1784
1863
|
return /* @__PURE__ */ React6.createElement(MultiSelect2, { ...extra, name: fieldName, label: "", value: editValue || [], onChange: commit, options: resolveEditOptions(col, data) });
|
|
1785
1864
|
case "date":
|
|
1786
|
-
return /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: fieldName, label:
|
|
1865
|
+
return /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: fieldName, label: inputLabel, value: editValue, onChange: commit });
|
|
1787
1866
|
case "time":
|
|
1788
|
-
return /* @__PURE__ */ React6.createElement(TimeInput, { ...extra, name: fieldName, label:
|
|
1867
|
+
return /* @__PURE__ */ React6.createElement(TimeInput, { ...extra, name: fieldName, label: inputLabel, value: editValue, onChange: commit });
|
|
1789
1868
|
case "datetime":
|
|
1790
|
-
return /* @__PURE__ */ React6.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: `${fieldName}-date`, label:
|
|
1869
|
+
return /* @__PURE__ */ React6.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: `${fieldName}-date`, label: `${inputLabel} date`, value: editValue == null ? void 0 : editValue.date, onChange: (val) => {
|
|
1791
1870
|
const next = { ...editValue, date: val };
|
|
1792
1871
|
handleInput(next);
|
|
1793
1872
|
commitEdit(row, col.field, next, { keepEditing: true });
|
|
1794
|
-
}, onBlur: maybeExitDatetimeEdit }), /* @__PURE__ */ React6.createElement(TimeInput, { ...extra.timeProps || {}, name: `${fieldName}-time`, label:
|
|
1873
|
+
}, onBlur: maybeExitDatetimeEdit }), /* @__PURE__ */ React6.createElement(TimeInput, { ...extra.timeProps || {}, name: `${fieldName}-time`, label: `${inputLabel} time`, value: editValue == null ? void 0 : editValue.time, onChange: (val) => {
|
|
1795
1874
|
const next = { ...editValue, time: val };
|
|
1796
1875
|
handleInput(next);
|
|
1797
1876
|
commitEdit(row, col.field, next, { keepEditing: true });
|
|
@@ -1805,7 +1884,8 @@ var DataTable = ({
|
|
|
1805
1884
|
}
|
|
1806
1885
|
};
|
|
1807
1886
|
const resolvedEditMode = editMode || (columns.some((col) => col.editable) ? "discrete" : null);
|
|
1808
|
-
const useColumnRendering = selectable || !!resolvedEditMode || editingRowId != null || showRowActionsColumn || !renderRow;
|
|
1887
|
+
const useColumnRendering = selectable || !!resolvedEditMode || editingRowId != null || showRowActionsColumn || expandable || !renderRow;
|
|
1888
|
+
const totalColumnCount = columns.length + (selectable ? 1 : 0) + (showExpandColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
|
|
1809
1889
|
const autoWidths = useMemo(
|
|
1810
1890
|
() => autoWidth ? computeAutoWidths(columns, data) : {},
|
|
1811
1891
|
[columns, data, autoWidth]
|
|
@@ -1824,6 +1904,7 @@ var DataTable = ({
|
|
|
1824
1904
|
const type = col.editType || "text";
|
|
1825
1905
|
const rowId = row[rowIdField];
|
|
1826
1906
|
const fieldName = `inline-${rowId}-${col.field}`;
|
|
1907
|
+
const inputLabel = typeof col.label === "string" ? col.label : col.field;
|
|
1827
1908
|
const cellKey = `${rowId}-${col.field}`;
|
|
1828
1909
|
const value = row[col.field];
|
|
1829
1910
|
const validate = col.editValidate;
|
|
@@ -1872,13 +1953,13 @@ var DataTable = ({
|
|
|
1872
1953
|
case "multiselect":
|
|
1873
1954
|
return /* @__PURE__ */ React6.createElement(MultiSelect2, { ...extra, name: fieldName, label: "", value: value || [], onChange: fire, options: resolveEditOptions(col, data) });
|
|
1874
1955
|
case "date":
|
|
1875
|
-
return /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: fieldName, label:
|
|
1956
|
+
return /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: fieldName, label: inputLabel, value, onChange: fire });
|
|
1876
1957
|
case "time":
|
|
1877
|
-
return /* @__PURE__ */ React6.createElement(TimeInput, { ...extra, name: fieldName, label:
|
|
1958
|
+
return /* @__PURE__ */ React6.createElement(TimeInput, { ...extra, name: fieldName, label: inputLabel, value, onChange: fire });
|
|
1878
1959
|
case "datetime":
|
|
1879
|
-
return /* @__PURE__ */ React6.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: `${fieldName}-date`, label:
|
|
1960
|
+
return /* @__PURE__ */ React6.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, /* @__PURE__ */ React6.createElement(DateInput2, { ...extra, name: `${fieldName}-date`, label: `${inputLabel} date`, value: value == null ? void 0 : value.date, onChange: (val) => {
|
|
1880
1961
|
fire({ ...value, date: val });
|
|
1881
|
-
} }), /* @__PURE__ */ React6.createElement(TimeInput, { ...extra.timeProps || {}, name: `${fieldName}-time`, label:
|
|
1962
|
+
} }), /* @__PURE__ */ React6.createElement(TimeInput, { ...extra.timeProps || {}, name: `${fieldName}-time`, label: `${inputLabel} time`, value: value == null ? void 0 : value.time, onChange: (val) => {
|
|
1882
1963
|
fire({ ...value, time: val });
|
|
1883
1964
|
} }));
|
|
1884
1965
|
case "toggle":
|
|
@@ -2032,7 +2113,7 @@ var DataTable = ({
|
|
|
2032
2113
|
checked: allVisibleSelected,
|
|
2033
2114
|
onChange: handleSelectAll
|
|
2034
2115
|
}
|
|
2035
|
-
)), columns.map((col) => {
|
|
2116
|
+
)), showExpandColumn && /* @__PURE__ */ React6.createElement(TableHeader, { width: "min" }), columns.map((col) => {
|
|
2036
2117
|
const headerAlign = resolvedEditMode === "inline" && col.editable ? void 0 : col.align;
|
|
2037
2118
|
return /* @__PURE__ */ React6.createElement(
|
|
2038
2119
|
TableHeader,
|
|
@@ -2046,8 +2127,8 @@ var DataTable = ({
|
|
|
2046
2127
|
col.description ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, col.label, "\xA0", /* @__PURE__ */ React6.createElement(Link2, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React6.createElement(Tooltip, null, col.description) }, /* @__PURE__ */ React6.createElement(Icon, { name: "info", screenReaderText: typeof col.description === "string" ? col.description : void 0 }))) : col.label
|
|
2047
2128
|
);
|
|
2048
2129
|
}), showRowActionsColumn && /* @__PURE__ */ React6.createElement(TableHeader, { width: "min" }))),
|
|
2049
|
-
/* @__PURE__ */ React6.createElement(TableBody, null,
|
|
2050
|
-
(item, idx) => item.type === "group-header" ? /* @__PURE__ */ React6.createElement(TableRow, { key: `group-${item.group.key}` }, selectable && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }), columns.map((col, colIdx) => {
|
|
2130
|
+
/* @__PURE__ */ React6.createElement(TableBody, null, renderedRows.map(
|
|
2131
|
+
(item, idx) => item.type === "group-header" ? /* @__PURE__ */ React6.createElement(TableRow, { key: `group-${item.group.key}` }, selectable && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }), showExpandColumn && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }), columns.map((col, colIdx) => {
|
|
2051
2132
|
var _a, _b, _c;
|
|
2052
2133
|
return /* @__PURE__ */ React6.createElement(TableCell, { key: col.field, width: getCellWidth(col), align: colIdx === 0 ? void 0 : col.align }, colIdx === 0 ? /* @__PURE__ */ React6.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, /* @__PURE__ */ React6.createElement(
|
|
2053
2134
|
Icon,
|
|
@@ -2064,7 +2145,7 @@ var DataTable = ({
|
|
|
2064
2145
|
},
|
|
2065
2146
|
/* @__PURE__ */ React6.createElement(Text2, { format: { fontWeight: "demibold" } }, item.group.label)
|
|
2066
2147
|
)) : ((_a = groupBy.aggregations) == null ? void 0 : _a[col.field]) ? groupBy.aggregations[col.field](item.group.rows, item.group.key) : ((_c = (_b = groupBy.groupValues) == null ? void 0 : _b[item.group.key]) == null ? void 0 : _c[col.field]) ?? "");
|
|
2067
|
-
}), showRowActionsColumn && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" })) : useColumnRendering ? /* @__PURE__ */ React6.createElement(TableRow, { key: item.row[rowIdField] ?? idx }, selectable && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }, /* @__PURE__ */ React6.createElement(
|
|
2148
|
+
}), showRowActionsColumn && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" })) : item.type === "detail" ? /* @__PURE__ */ React6.createElement(TableRow, { key: `detail-${item.row[rowIdField] ?? idx}` }, /* @__PURE__ */ React6.createElement(TableCell, { colSpan: totalColumnCount }, renderExpandedRow(item.row))) : useColumnRendering ? /* @__PURE__ */ React6.createElement(TableRow, { key: item.row[rowIdField] ?? idx }, selectable && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }, /* @__PURE__ */ React6.createElement(
|
|
2068
2149
|
Checkbox,
|
|
2069
2150
|
{
|
|
2070
2151
|
name: `select-${item.row[rowIdField]}`,
|
|
@@ -2072,13 +2153,22 @@ var DataTable = ({
|
|
|
2072
2153
|
checked: selectedIds.has(item.row[rowIdField]),
|
|
2073
2154
|
onChange: (checked) => handleSelectRow(item.row[rowIdField], checked)
|
|
2074
2155
|
}
|
|
2156
|
+
)), showExpandColumn && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }, /* @__PURE__ */ React6.createElement(
|
|
2157
|
+
Icon,
|
|
2158
|
+
{
|
|
2159
|
+
name: expandedRowIds.has(item.row[rowIdField]) ? "upCarat" : "downCarat",
|
|
2160
|
+
onClick: () => toggleRowExpanded(item.row[rowIdField]),
|
|
2161
|
+
screenReaderText: expandedRowIds.has(item.row[rowIdField]) ? "Collapse row" : "Expand row"
|
|
2162
|
+
}
|
|
2075
2163
|
)), columns.map((col) => {
|
|
2076
2164
|
const rowId = item.row[rowIdField];
|
|
2077
2165
|
const isDiscreteEditing = resolvedEditMode === "discrete" && (editingCell == null ? void 0 : editingCell.rowId) === rowId && (editingCell == null ? void 0 : editingCell.field) === col.field;
|
|
2078
2166
|
const isRowEditing = editingRowId != null && rowId === editingRowId && col.editable;
|
|
2079
2167
|
const isShowingInput = isDiscreteEditing || isRowEditing || resolvedEditMode === "inline" && col.editable;
|
|
2080
2168
|
const cellAlign = isShowingInput ? void 0 : col.align;
|
|
2081
|
-
|
|
2169
|
+
const cellContent = renderCellContent(item.row, col);
|
|
2170
|
+
const wrapInRowToggle = expandable && expandOn === "row" && !col.editable && !isShowingInput;
|
|
2171
|
+
return /* @__PURE__ */ React6.createElement(TableCell, { key: col.field, width: isDiscreteEditing || isRowEditing ? "auto" : getCellWidth(col), align: cellAlign }, wrapInRowToggle ? /* @__PURE__ */ React6.createElement(Link2, { variant: "dark", onClick: () => toggleRowExpanded(rowId) }, cellContent) : cellContent);
|
|
2082
2172
|
}), showRowActionsColumn && /* @__PURE__ */ React6.createElement(TableCell, { width: "min" }, /* @__PURE__ */ React6.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, (() => {
|
|
2083
2173
|
const resolvedRowActions = typeof rowActions === "function" ? rowActions(item.row) : rowActions;
|
|
2084
2174
|
const actions = Array.isArray(resolvedRowActions) ? resolvedRowActions : [];
|
|
@@ -2095,13 +2185,14 @@ var DataTable = ({
|
|
|
2095
2185
|
));
|
|
2096
2186
|
})()))) : renderRow(item.row)
|
|
2097
2187
|
)),
|
|
2098
|
-
(footer || columns.some((col) => col.footer)) && /* @__PURE__ */ React6.createElement(TableFooter, null, typeof footer === "function" ? footer(footerData) : /* @__PURE__ */ React6.createElement(TableRow, null, selectable && /* @__PURE__ */ React6.createElement(TableHeader, { width: "min" }), columns.map((col) => {
|
|
2188
|
+
(footer || columns.some((col) => col.footer)) && /* @__PURE__ */ React6.createElement(TableFooter, null, typeof footer === "function" ? footer(footerData) : /* @__PURE__ */ React6.createElement(TableRow, null, selectable && /* @__PURE__ */ React6.createElement(TableHeader, { width: "min" }), showExpandColumn && /* @__PURE__ */ React6.createElement(TableHeader, { width: "min" }), columns.map((col) => {
|
|
2099
2189
|
const footerDef = col.footer;
|
|
2100
2190
|
const content = typeof footerDef === "function" ? footerDef(footerData) : footerDef || "";
|
|
2101
2191
|
return /* @__PURE__ */ React6.createElement(TableHeader, { key: col.field, align: col.align }, content);
|
|
2102
2192
|
}), showRowActionsColumn && /* @__PURE__ */ React6.createElement(TableHeader, { width: "min" })))
|
|
2103
2193
|
));
|
|
2104
2194
|
};
|
|
2195
|
+
DataTable.displayName = "DataTable";
|
|
2105
2196
|
|
|
2106
2197
|
// src/form/FormBuilder.jsx
|
|
2107
2198
|
import React7, {
|
|
@@ -2140,7 +2231,12 @@ import {
|
|
|
2140
2231
|
TimeInput as TimeInput2,
|
|
2141
2232
|
Toggle as Toggle2,
|
|
2142
2233
|
Checkbox as Checkbox2,
|
|
2143
|
-
ToggleGroup
|
|
2234
|
+
ToggleGroup,
|
|
2235
|
+
Modal,
|
|
2236
|
+
ModalBody,
|
|
2237
|
+
ModalFooter,
|
|
2238
|
+
LoadingSpinner,
|
|
2239
|
+
useExtensionActions
|
|
2144
2240
|
} from "@hubspot/ui-extensions";
|
|
2145
2241
|
import {
|
|
2146
2242
|
CrmPropertyList,
|
|
@@ -2467,8 +2563,25 @@ var resolveDependentCascade = ({ name, value, fields, values, getEmptyValueForFi
|
|
|
2467
2563
|
return { newValues, changedDependents };
|
|
2468
2564
|
};
|
|
2469
2565
|
|
|
2566
|
+
// src/form/formDirty.js
|
|
2567
|
+
var isFormDirty = (values, initialValues) => !deepEqual(values || {}, initialValues || {});
|
|
2568
|
+
var getDirtyFields = (values, initialValues) => {
|
|
2569
|
+
const current = values || {};
|
|
2570
|
+
const baseline = initialValues || {};
|
|
2571
|
+
const names = [...Object.keys(current)];
|
|
2572
|
+
for (const name of Object.keys(baseline)) {
|
|
2573
|
+
if (!Object.prototype.hasOwnProperty.call(current, name)) names.push(name);
|
|
2574
|
+
}
|
|
2575
|
+
return names.filter((name) => !deepEqual(current[name], baseline[name]));
|
|
2576
|
+
};
|
|
2577
|
+
|
|
2470
2578
|
// src/form/FormBuilder.jsx
|
|
2471
2579
|
var getRepeaterErrorKey = (fieldName, rowIdx, subFieldName) => `${fieldName}[${rowIdx}].${subFieldName}`;
|
|
2580
|
+
var withFieldLoadingSpinner = (element, loading) => {
|
|
2581
|
+
if (!loading) return element;
|
|
2582
|
+
return /* @__PURE__ */ React7.createElement(Flex5, { direction: "row", gap: "xs", align: "end" }, /* @__PURE__ */ React7.createElement(Box3, { flex: 1 }, element), /* @__PURE__ */ React7.createElement(LoadingSpinner, { size: "xs", layout: "inline" }));
|
|
2583
|
+
};
|
|
2584
|
+
var formBuilderInstanceCounter = 0;
|
|
2472
2585
|
var fieldSetHasErrors = (errors, fields) => {
|
|
2473
2586
|
if (!errors || !fields || fields.length === 0) return false;
|
|
2474
2587
|
const names = new Set(fields.map((field) => field.name));
|
|
@@ -2568,8 +2681,10 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2568
2681
|
// controlled loading state
|
|
2569
2682
|
disabled = false,
|
|
2570
2683
|
// disable entire form
|
|
2571
|
-
renderButtons: renderButtonsProp
|
|
2684
|
+
renderButtons: renderButtonsProp,
|
|
2572
2685
|
// custom action row renderer
|
|
2686
|
+
confirmDiscard
|
|
2687
|
+
// true | { title, message, confirmLabel, cancelLabel } — confirm before the built-in Cancel discards dirty changes
|
|
2573
2688
|
} = props;
|
|
2574
2689
|
const {
|
|
2575
2690
|
columns = 1,
|
|
@@ -2688,6 +2803,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2688
2803
|
"readOnly",
|
|
2689
2804
|
"alwaysEditable",
|
|
2690
2805
|
"disabled",
|
|
2806
|
+
"loading",
|
|
2691
2807
|
"defaultValue",
|
|
2692
2808
|
"fieldProps",
|
|
2693
2809
|
"colSpan",
|
|
@@ -2944,7 +3060,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2944
3060
|
syncDirtyBaseline(values);
|
|
2945
3061
|
}, [values, syncDirtyBaseline]);
|
|
2946
3062
|
const isDirty = useMemo2(() => {
|
|
2947
|
-
return
|
|
3063
|
+
return isFormDirty(formValues, initialSnapshot.current);
|
|
2948
3064
|
}, [formValues]);
|
|
2949
3065
|
const prevDirtyRef = useRef3(false);
|
|
2950
3066
|
useEffect3(() => {
|
|
@@ -2953,6 +3069,27 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
2953
3069
|
if (onDirtyChange) onDirtyChange(isDirty);
|
|
2954
3070
|
}
|
|
2955
3071
|
}, [isDirty, onDirtyChange]);
|
|
3072
|
+
let hostActions = null;
|
|
3073
|
+
try {
|
|
3074
|
+
hostActions = useExtensionActions();
|
|
3075
|
+
} catch (err) {
|
|
3076
|
+
hostActions = null;
|
|
3077
|
+
}
|
|
3078
|
+
const discardModalIdRef = useRef3(null);
|
|
3079
|
+
if (discardModalIdRef.current === null) {
|
|
3080
|
+
formBuilderInstanceCounter += 1;
|
|
3081
|
+
discardModalIdRef.current = `hs-uix-form-discard-${formBuilderInstanceCounter}`;
|
|
3082
|
+
}
|
|
3083
|
+
const discardConfig = confirmDiscard ? confirmDiscard === true ? {} : confirmDiscard : null;
|
|
3084
|
+
const discardTitle = discardConfig && discardConfig.title || "Discard changes?";
|
|
3085
|
+
const discardMessage = discardConfig && discardConfig.message || "You have unsaved changes. If you discard now, they will be lost.";
|
|
3086
|
+
const discardConfirmLabel = discardConfig && discardConfig.confirmLabel || "Discard changes";
|
|
3087
|
+
const discardCancelLabel = discardConfig && discardConfig.cancelLabel || "Keep editing";
|
|
3088
|
+
const closeDiscardModal = useCallback3(() => {
|
|
3089
|
+
if (hostActions && typeof hostActions.closeOverlay === "function") {
|
|
3090
|
+
hostActions.closeOverlay(discardModalIdRef.current);
|
|
3091
|
+
}
|
|
3092
|
+
}, [hostActions]);
|
|
2956
3093
|
const autoSaveTimerRef = useRef3(null);
|
|
2957
3094
|
const autoSaveRef = useRef3(autoSave);
|
|
2958
3095
|
autoSaveRef.current = autoSave;
|
|
@@ -3591,6 +3728,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3591
3728
|
},
|
|
3592
3729
|
getValues: () => formValues,
|
|
3593
3730
|
isDirty: () => isDirty,
|
|
3731
|
+
getDirtyFields: () => getDirtyFields(formValuesRef.current, initialSnapshot.current),
|
|
3594
3732
|
setFieldValue: (name, value) => handleFieldChange(name, value),
|
|
3595
3733
|
setFieldError: (name, message) => updateErrors({ [name]: message }),
|
|
3596
3734
|
setErrors: (errors) => {
|
|
@@ -3610,7 +3748,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3610
3748
|
const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
|
|
3611
3749
|
const fieldFormReadOnly = field.alwaysEditable ? false : formReadOnly;
|
|
3612
3750
|
const isReadOnly = field.readOnly || fieldFormReadOnly;
|
|
3613
|
-
const
|
|
3751
|
+
const isFieldLoading = !!field.loading;
|
|
3752
|
+
const isDisabled = disabled || resolveDisabled(field, formValues) || fieldFormReadOnly || isFieldLoading;
|
|
3614
3753
|
const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
|
|
3615
3754
|
if (field.type === "display" || field.type === "slot") {
|
|
3616
3755
|
if (field.render) {
|
|
@@ -3794,7 +3933,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3794
3933
|
disabled: isDisabled,
|
|
3795
3934
|
error: hasError,
|
|
3796
3935
|
validationMessage: renderFieldError ? void 0 : fieldError || void 0,
|
|
3797
|
-
...
|
|
3936
|
+
...validatingFields[field.name] ? { loading: true } : {},
|
|
3798
3937
|
...field.fieldProps || {}
|
|
3799
3938
|
};
|
|
3800
3939
|
const options = resolveOptions(field, formValues);
|
|
@@ -3952,25 +4091,31 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3952
4091
|
)));
|
|
3953
4092
|
}
|
|
3954
4093
|
case "select":
|
|
3955
|
-
return
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
4094
|
+
return withFieldLoadingSpinner(
|
|
4095
|
+
/* @__PURE__ */ React7.createElement(
|
|
4096
|
+
Select3,
|
|
4097
|
+
{
|
|
4098
|
+
...commonProps,
|
|
4099
|
+
value: fieldValue,
|
|
4100
|
+
options,
|
|
4101
|
+
variant: field.variant,
|
|
4102
|
+
onChange: fieldOnChange
|
|
4103
|
+
}
|
|
4104
|
+
),
|
|
4105
|
+
isFieldLoading
|
|
3964
4106
|
);
|
|
3965
4107
|
case "multiselect":
|
|
3966
|
-
return
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
4108
|
+
return withFieldLoadingSpinner(
|
|
4109
|
+
/* @__PURE__ */ React7.createElement(
|
|
4110
|
+
MultiSelect3,
|
|
4111
|
+
{
|
|
4112
|
+
...commonProps,
|
|
4113
|
+
value: fieldValue || [],
|
|
4114
|
+
options,
|
|
4115
|
+
onChange: fieldOnChange
|
|
4116
|
+
}
|
|
4117
|
+
),
|
|
4118
|
+
isFieldLoading
|
|
3974
4119
|
);
|
|
3975
4120
|
case "toggle":
|
|
3976
4121
|
return /* @__PURE__ */ React7.createElement(
|
|
@@ -4491,6 +4636,30 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
4491
4636
|
if (layout) return renderExplicitLayout();
|
|
4492
4637
|
return renderFieldSubset(visibleFields);
|
|
4493
4638
|
};
|
|
4639
|
+
const renderCancelButton = () => {
|
|
4640
|
+
if (discardConfig && isDirty) {
|
|
4641
|
+
return /* @__PURE__ */ React7.createElement(
|
|
4642
|
+
Button4,
|
|
4643
|
+
{
|
|
4644
|
+
variant: "secondary",
|
|
4645
|
+
disabled,
|
|
4646
|
+
overlay: /* @__PURE__ */ React7.createElement(Modal, { id: discardModalIdRef.current, title: discardTitle, width: "small" }, /* @__PURE__ */ React7.createElement(ModalBody, null, /* @__PURE__ */ React7.createElement(Text3, null, discardMessage)), /* @__PURE__ */ React7.createElement(ModalFooter, null, /* @__PURE__ */ React7.createElement(Button4, { variant: "secondary", onClick: closeDiscardModal }, discardCancelLabel), /* @__PURE__ */ React7.createElement(
|
|
4647
|
+
Button4,
|
|
4648
|
+
{
|
|
4649
|
+
variant: "destructive",
|
|
4650
|
+
onClick: () => {
|
|
4651
|
+
closeDiscardModal();
|
|
4652
|
+
if (onCancel) onCancel();
|
|
4653
|
+
}
|
|
4654
|
+
},
|
|
4655
|
+
discardConfirmLabel
|
|
4656
|
+
)))
|
|
4657
|
+
},
|
|
4658
|
+
cancelButtonLabel
|
|
4659
|
+
);
|
|
4660
|
+
}
|
|
4661
|
+
return /* @__PURE__ */ React7.createElement(Button4, { variant: "secondary", onClick: onCancel, disabled }, cancelButtonLabel);
|
|
4662
|
+
};
|
|
4494
4663
|
const renderButtons = () => {
|
|
4495
4664
|
if (submitPosition === "none" || formReadOnly) return null;
|
|
4496
4665
|
const isLastStep = !isMultiStep || currentStep === steps.length - 1;
|
|
@@ -4504,6 +4673,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
4504
4673
|
totalSteps: isMultiStep ? steps.length : 1,
|
|
4505
4674
|
disabled,
|
|
4506
4675
|
loading: isLoading,
|
|
4676
|
+
isDirty,
|
|
4507
4677
|
labels: {
|
|
4508
4678
|
submit: submitButtonLabel,
|
|
4509
4679
|
cancel: cancelButtonLabel,
|
|
@@ -4519,7 +4689,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
4519
4689
|
return renderButtonsProp(buttonContext);
|
|
4520
4690
|
}
|
|
4521
4691
|
if (isMultiStep) {
|
|
4522
|
-
return /* @__PURE__ */ React7.createElement(Flex5, { direction: "row", justify: "between", align: "center" }, !isFirstStep ? /* @__PURE__ */ React7.createElement(Button4, { variant: "secondary", onClick: handleBack, disabled }, backButtonLabel) : showCancel ?
|
|
4692
|
+
return /* @__PURE__ */ React7.createElement(Flex5, { direction: "row", justify: "between", align: "center" }, !isFirstStep ? /* @__PURE__ */ React7.createElement(Button4, { variant: "secondary", onClick: handleBack, disabled }, backButtonLabel) : showCancel ? renderCancelButton() : /* @__PURE__ */ React7.createElement(Text3, null, " "), /* @__PURE__ */ React7.createElement(Inline, { gap: "small" }, /* @__PURE__ */ React7.createElement(Text3, { variant: "microcopy" }, "Step ", currentStep + 1, " of ", steps.length), isLastStep ? /* @__PURE__ */ React7.createElement(
|
|
4523
4693
|
LoadingButton,
|
|
4524
4694
|
{
|
|
4525
4695
|
variant: submitVariant,
|
|
@@ -4530,7 +4700,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
4530
4700
|
submitButtonLabel
|
|
4531
4701
|
) : /* @__PURE__ */ React7.createElement(Button4, { variant: "primary", onClick: handleNext, disabled }, nextButtonLabel)));
|
|
4532
4702
|
}
|
|
4533
|
-
return /* @__PURE__ */ React7.createElement(Flex5, { direction: "row", justify: singleStepJustify, gap: "sm" }, showCancel &&
|
|
4703
|
+
return /* @__PURE__ */ React7.createElement(Flex5, { direction: "row", justify: singleStepJustify, gap: "sm" }, showCancel && renderCancelButton(), /* @__PURE__ */ React7.createElement(
|
|
4534
4704
|
LoadingButton,
|
|
4535
4705
|
{
|
|
4536
4706
|
variant: submitVariant,
|
|
@@ -4570,10 +4740,231 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
4570
4740
|
formContent
|
|
4571
4741
|
);
|
|
4572
4742
|
});
|
|
4743
|
+
FormBuilder.displayName = "FormBuilder";
|
|
4744
|
+
|
|
4745
|
+
// src/form/hubspotSchema.js
|
|
4746
|
+
var coerceBool = (value) => value === true || value === "true" || value === "Yes" || value === "yes" || value === "1";
|
|
4747
|
+
var coerceNumber = (value) => {
|
|
4748
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
4749
|
+
if (typeof value === "number") return value;
|
|
4750
|
+
const parsed = Number(value);
|
|
4751
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
4752
|
+
};
|
|
4753
|
+
var mapPropertyOptions = (options) => {
|
|
4754
|
+
if (!Array.isArray(options)) return void 0;
|
|
4755
|
+
const mapped = options.filter((opt) => opt && opt.hidden !== true).map((opt) => {
|
|
4756
|
+
const result = { label: opt.label != null ? opt.label : String(opt.value), value: opt.value };
|
|
4757
|
+
if (opt.description != null && opt.description !== "") result.description = opt.description;
|
|
4758
|
+
return result;
|
|
4759
|
+
});
|
|
4760
|
+
return mapped;
|
|
4761
|
+
};
|
|
4762
|
+
var resolveFieldType = (property) => {
|
|
4763
|
+
const { type, fieldType } = property;
|
|
4764
|
+
if (type === "datetime") return "datetime";
|
|
4765
|
+
if (type === "date" || fieldType === "date") return "date";
|
|
4766
|
+
switch (fieldType) {
|
|
4767
|
+
case "select":
|
|
4768
|
+
return "select";
|
|
4769
|
+
// FormBuilder's radio rendering is the "radioGroup" type (native
|
|
4770
|
+
// ToggleGroup with toggleType="radioButtonList").
|
|
4771
|
+
case "radio":
|
|
4772
|
+
return "radioGroup";
|
|
4773
|
+
// HubSpot "checkbox" = multiple checkboxes over an enumeration → multiselect.
|
|
4774
|
+
case "checkbox":
|
|
4775
|
+
return "multiselect";
|
|
4776
|
+
case "booleancheckbox":
|
|
4777
|
+
return "toggle";
|
|
4778
|
+
case "number":
|
|
4779
|
+
return "number";
|
|
4780
|
+
case "textarea":
|
|
4781
|
+
return "textarea";
|
|
4782
|
+
case "text":
|
|
4783
|
+
case "phonenumber":
|
|
4784
|
+
return "text";
|
|
4785
|
+
default:
|
|
4786
|
+
break;
|
|
4787
|
+
}
|
|
4788
|
+
switch (type) {
|
|
4789
|
+
case "enumeration":
|
|
4790
|
+
return "select";
|
|
4791
|
+
case "number":
|
|
4792
|
+
return "number";
|
|
4793
|
+
case "bool":
|
|
4794
|
+
return "toggle";
|
|
4795
|
+
default:
|
|
4796
|
+
return "text";
|
|
4797
|
+
}
|
|
4798
|
+
};
|
|
4799
|
+
var isPropertyReadOnly = (property) => property.calculated === true || property.modificationMetadata && property.modificationMetadata.readOnlyValue === true;
|
|
4800
|
+
var resolveRequiredOverride = (requiredOverrides, name) => {
|
|
4801
|
+
if (!requiredOverrides) return void 0;
|
|
4802
|
+
if (Array.isArray(requiredOverrides)) {
|
|
4803
|
+
return requiredOverrides.includes(name) ? true : void 0;
|
|
4804
|
+
}
|
|
4805
|
+
if (Object.prototype.hasOwnProperty.call(requiredOverrides, name)) {
|
|
4806
|
+
return !!requiredOverrides[name];
|
|
4807
|
+
}
|
|
4808
|
+
return void 0;
|
|
4809
|
+
};
|
|
4810
|
+
var fieldsFromHubSpotProperties = (properties, options = {}) => {
|
|
4811
|
+
if (!Array.isArray(properties)) return [];
|
|
4812
|
+
const {
|
|
4813
|
+
include,
|
|
4814
|
+
exclude,
|
|
4815
|
+
overrides,
|
|
4816
|
+
requiredOverrides,
|
|
4817
|
+
includeDescriptions = false
|
|
4818
|
+
} = options;
|
|
4819
|
+
const includeSet = Array.isArray(include) ? new Set(include) : null;
|
|
4820
|
+
const excludeSet = Array.isArray(exclude) ? new Set(exclude) : null;
|
|
4821
|
+
let selected = properties.filter((property) => {
|
|
4822
|
+
if (!property || !property.name) return false;
|
|
4823
|
+
if (includeSet && !includeSet.has(property.name)) return false;
|
|
4824
|
+
if (excludeSet && excludeSet.has(property.name)) return false;
|
|
4825
|
+
if (property.hidden === true && !includeSet) return false;
|
|
4826
|
+
return true;
|
|
4827
|
+
});
|
|
4828
|
+
if (includeSet) {
|
|
4829
|
+
const order = new Map(include.map((name, idx) => [name, idx]));
|
|
4830
|
+
selected = [...selected].sort((a, b) => order.get(a.name) - order.get(b.name));
|
|
4831
|
+
}
|
|
4832
|
+
return selected.map((property) => {
|
|
4833
|
+
const type = resolveFieldType(property);
|
|
4834
|
+
const field = {
|
|
4835
|
+
name: property.name,
|
|
4836
|
+
type,
|
|
4837
|
+
label: property.label || property.name
|
|
4838
|
+
};
|
|
4839
|
+
if (includeDescriptions && property.description) {
|
|
4840
|
+
field.description = property.description;
|
|
4841
|
+
}
|
|
4842
|
+
if (type === "select" || type === "multiselect" || type === "radioGroup") {
|
|
4843
|
+
field.options = mapPropertyOptions(property.options) || [];
|
|
4844
|
+
}
|
|
4845
|
+
if (type === "toggle") {
|
|
4846
|
+
field.transformIn = coerceBool;
|
|
4847
|
+
field.transformOut = (value) => !!value;
|
|
4848
|
+
}
|
|
4849
|
+
if (type === "number") {
|
|
4850
|
+
field.transformIn = coerceNumber;
|
|
4851
|
+
}
|
|
4852
|
+
if (isPropertyReadOnly(property)) {
|
|
4853
|
+
field.readOnly = true;
|
|
4854
|
+
}
|
|
4855
|
+
const required = resolveRequiredOverride(requiredOverrides, property.name);
|
|
4856
|
+
if (required !== void 0) field.required = required;
|
|
4857
|
+
const override = overrides && overrides[property.name];
|
|
4858
|
+
return override ? { ...field, ...override } : field;
|
|
4859
|
+
});
|
|
4860
|
+
};
|
|
4573
4861
|
|
|
4574
4862
|
// src/kanban/Kanban.jsx
|
|
4575
4863
|
import React10, { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
4576
4864
|
|
|
4865
|
+
// src/kanban/kanbanLanes.js
|
|
4866
|
+
var UNASSIGNED_LANE_KEY = "__unassigned";
|
|
4867
|
+
var UNKNOWN_STAGE_KEY = "__unknown";
|
|
4868
|
+
var getLaneKey = (row, swimlaneBy) => {
|
|
4869
|
+
const raw = typeof swimlaneBy === "function" ? swimlaneBy(row) : row == null ? void 0 : row[swimlaneBy];
|
|
4870
|
+
if (raw == null || raw === "") return UNASSIGNED_LANE_KEY;
|
|
4871
|
+
return String(raw);
|
|
4872
|
+
};
|
|
4873
|
+
var orderLaneKeys = (seenKeys, swimlaneOrder) => {
|
|
4874
|
+
const seen = Array.isArray(seenKeys) ? seenKeys : [];
|
|
4875
|
+
if (!Array.isArray(swimlaneOrder) || swimlaneOrder.length === 0) return [...seen];
|
|
4876
|
+
const explicit = [];
|
|
4877
|
+
for (const key of swimlaneOrder) {
|
|
4878
|
+
const normalized = String(key);
|
|
4879
|
+
if (!explicit.includes(normalized)) explicit.push(normalized);
|
|
4880
|
+
}
|
|
4881
|
+
const rest = seen.filter((key) => !explicit.includes(key));
|
|
4882
|
+
return [...explicit, ...rest];
|
|
4883
|
+
};
|
|
4884
|
+
var partitionLanes = (rows, { swimlaneBy, swimlaneOrder } = {}) => {
|
|
4885
|
+
const rowsByLane = {};
|
|
4886
|
+
const firstSeen = [];
|
|
4887
|
+
for (const row of rows || []) {
|
|
4888
|
+
const key = getLaneKey(row, swimlaneBy);
|
|
4889
|
+
if (!rowsByLane[key]) {
|
|
4890
|
+
rowsByLane[key] = [];
|
|
4891
|
+
firstSeen.push(key);
|
|
4892
|
+
}
|
|
4893
|
+
rowsByLane[key].push(row);
|
|
4894
|
+
}
|
|
4895
|
+
const laneKeys = orderLaneKeys(firstSeen, swimlaneOrder);
|
|
4896
|
+
for (const key of laneKeys) {
|
|
4897
|
+
if (!rowsByLane[key]) rowsByLane[key] = [];
|
|
4898
|
+
}
|
|
4899
|
+
return { laneKeys, rowsByLane };
|
|
4900
|
+
};
|
|
4901
|
+
var resolveLaneLabel = (laneKey, swimlaneLabels, rows, unassignedLabel) => {
|
|
4902
|
+
if (typeof swimlaneLabels === "function") {
|
|
4903
|
+
const out = swimlaneLabels(laneKey, rows || []);
|
|
4904
|
+
if (out != null) return out;
|
|
4905
|
+
} else if (swimlaneLabels && typeof swimlaneLabels === "object") {
|
|
4906
|
+
const out = swimlaneLabels[laneKey];
|
|
4907
|
+
if (out != null) return out;
|
|
4908
|
+
}
|
|
4909
|
+
if (laneKey === UNASSIGNED_LANE_KEY) return unassignedLabel || "Unassigned";
|
|
4910
|
+
return String(laneKey);
|
|
4911
|
+
};
|
|
4912
|
+
var bucketRowsByStage = (rows, stages, getStage) => {
|
|
4913
|
+
const map = {};
|
|
4914
|
+
for (const stage of stages || []) map[stage.value] = [];
|
|
4915
|
+
for (const row of rows || []) {
|
|
4916
|
+
const key = getStage(row);
|
|
4917
|
+
if (map[key]) {
|
|
4918
|
+
map[key].push(row);
|
|
4919
|
+
} else if ((stages || []).length > 0) {
|
|
4920
|
+
if (!map[UNKNOWN_STAGE_KEY]) map[UNKNOWN_STAGE_KEY] = [];
|
|
4921
|
+
map[UNKNOWN_STAGE_KEY].push(row);
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
return map;
|
|
4925
|
+
};
|
|
4926
|
+
var sortBuckets = (buckets, comparator) => {
|
|
4927
|
+
if (!comparator) return buckets;
|
|
4928
|
+
const out = {};
|
|
4929
|
+
for (const key of Object.keys(buckets || {})) {
|
|
4930
|
+
out[key] = [...buckets[key]].sort(comparator);
|
|
4931
|
+
}
|
|
4932
|
+
return out;
|
|
4933
|
+
};
|
|
4934
|
+
var resolveWipLimit = (stage, wipLimits) => {
|
|
4935
|
+
const override = wipLimits ? wipLimits[stage == null ? void 0 : stage.value] : void 0;
|
|
4936
|
+
const limit = override != null ? override : stage == null ? void 0 : stage.wipLimit;
|
|
4937
|
+
if (typeof limit !== "number" || !Number.isFinite(limit) || limit < 0) return null;
|
|
4938
|
+
return limit;
|
|
4939
|
+
};
|
|
4940
|
+
var computeStageCounts = (stages, buckets, stageMeta) => {
|
|
4941
|
+
const counts = {};
|
|
4942
|
+
for (const stage of stages || []) {
|
|
4943
|
+
const meta = stageMeta ? stageMeta[stage.value] : void 0;
|
|
4944
|
+
counts[stage.value] = meta && meta.totalCount != null ? meta.totalCount : ((buckets == null ? void 0 : buckets[stage.value]) || []).length;
|
|
4945
|
+
}
|
|
4946
|
+
return counts;
|
|
4947
|
+
};
|
|
4948
|
+
var evaluateWip = (stages, counts, wipLimits) => {
|
|
4949
|
+
const out = {};
|
|
4950
|
+
for (const stage of stages || []) {
|
|
4951
|
+
const limit = resolveWipLimit(stage, wipLimits);
|
|
4952
|
+
const count = (counts == null ? void 0 : counts[stage.value]) || 0;
|
|
4953
|
+
out[stage.value] = { count, limit, exceeded: limit != null && count > limit };
|
|
4954
|
+
}
|
|
4955
|
+
return out;
|
|
4956
|
+
};
|
|
4957
|
+
var findNewlyExceededWip = (prev, next) => {
|
|
4958
|
+
const events = [];
|
|
4959
|
+
for (const stageId of Object.keys(next || {})) {
|
|
4960
|
+
const entry = next[stageId];
|
|
4961
|
+
if (!entry || !entry.exceeded) continue;
|
|
4962
|
+
if (prev && prev[stageId] && prev[stageId].exceeded) continue;
|
|
4963
|
+
events.push({ stageId, count: entry.count, limit: entry.limit });
|
|
4964
|
+
}
|
|
4965
|
+
return events;
|
|
4966
|
+
};
|
|
4967
|
+
|
|
4577
4968
|
// src/common-components/CollectionSortSelect.js
|
|
4578
4969
|
import React8, { useId as useId2 } from "react";
|
|
4579
4970
|
import { Select as Select4 } from "@hubspot/ui-extensions";
|
|
@@ -4845,7 +5236,7 @@ var StyledText = ({
|
|
|
4845
5236
|
const nativeVariant = NATIVE_TAG_VARIANT_ALIASES[background == null ? void 0 : background.variant] ?? (background == null ? void 0 : background.variant) ?? "default";
|
|
4846
5237
|
return React9.createElement(Tag2, { variant: nativeVariant }, resolvedText);
|
|
4847
5238
|
}
|
|
4848
|
-
const { src, width: w, height:
|
|
5239
|
+
const { src, width: w, height: h7 } = makeStyledTextDataUri(resolvedText, {
|
|
4849
5240
|
variant,
|
|
4850
5241
|
format,
|
|
4851
5242
|
orientation,
|
|
@@ -4861,7 +5252,7 @@ var StyledText = ({
|
|
|
4861
5252
|
return React9.createElement(Image2, {
|
|
4862
5253
|
src,
|
|
4863
5254
|
width: w,
|
|
4864
|
-
height:
|
|
5255
|
+
height: h7,
|
|
4865
5256
|
alt: alt ?? String(resolvedText)
|
|
4866
5257
|
});
|
|
4867
5258
|
};
|
|
@@ -4882,11 +5273,12 @@ import {
|
|
|
4882
5273
|
Image as Image3,
|
|
4883
5274
|
Inline as Inline2,
|
|
4884
5275
|
Link as Link4,
|
|
4885
|
-
LoadingSpinner,
|
|
5276
|
+
LoadingSpinner as LoadingSpinner2,
|
|
4886
5277
|
Select as Select5,
|
|
4887
5278
|
Statistics,
|
|
4888
5279
|
StatisticsItem,
|
|
4889
5280
|
StatisticsTrend,
|
|
5281
|
+
StatusTag,
|
|
4890
5282
|
Tag as Tag3,
|
|
4891
5283
|
Text as Text4,
|
|
4892
5284
|
Tile as Tile3
|
|
@@ -4924,6 +5316,12 @@ var DEFAULT_LABELS3 = {
|
|
|
4924
5316
|
errorTitle: "Something went wrong.",
|
|
4925
5317
|
errorMessage: "An error occurred while loading data.",
|
|
4926
5318
|
cardCount: (n) => String(n),
|
|
5319
|
+
// "5 / 4" — count vs WIP limit in the stage header. The slash format is the
|
|
5320
|
+
// standard kanban convention; override for tighter ("5/4") or verbose forms.
|
|
5321
|
+
wipCount: (count, limit) => `${count} / ${limit}`,
|
|
5322
|
+
overWip: "Over WIP",
|
|
5323
|
+
laneCount: (n) => String(n),
|
|
5324
|
+
unassignedLane: "Unassigned",
|
|
4927
5325
|
moveTo: "Move",
|
|
4928
5326
|
clearAll: "Clear all",
|
|
4929
5327
|
selectAll: (count, label) => `Select all ${count} ${label}`,
|
|
@@ -5092,7 +5490,7 @@ var StageControl = ({
|
|
|
5092
5490
|
labels
|
|
5093
5491
|
}) => {
|
|
5094
5492
|
if (isChanging) {
|
|
5095
|
-
return /* @__PURE__ */ React10.createElement(
|
|
5493
|
+
return /* @__PURE__ */ React10.createElement(LoadingSpinner2, { size: "xs" });
|
|
5096
5494
|
}
|
|
5097
5495
|
const availableStages = (stages || []).filter(
|
|
5098
5496
|
(stage) => stage.value === currentStage.value || canStageReceiveRow(stage, row, canMove)
|
|
@@ -5150,10 +5548,14 @@ var KanbanColumn = ({
|
|
|
5150
5548
|
onToggleCollapsed,
|
|
5151
5549
|
columnFooter,
|
|
5152
5550
|
countDisplay,
|
|
5551
|
+
wip,
|
|
5552
|
+
compactEmpty,
|
|
5153
5553
|
labels,
|
|
5154
5554
|
children
|
|
5155
5555
|
}) => {
|
|
5156
|
-
const
|
|
5556
|
+
const hasWipLimit = wip != null && wip.limit != null;
|
|
5557
|
+
const countLabel = hasWipLimit ? labels.wipCount(wip.count, wip.limit) : labels.cardCount(totalCount != null ? totalCount : bucketCount);
|
|
5558
|
+
const overWipNode = wip && wip.exceeded ? /* @__PURE__ */ React10.createElement(StatusTag, { variant: "warning" }, labels.overWip) : null;
|
|
5157
5559
|
const countNode = countDisplay === "text" ? /* @__PURE__ */ React10.createElement(Text4, { format: { fontWeight: "demibold" } }, countLabel) : countDisplay === "none" ? null : /* @__PURE__ */ React10.createElement(Tag3, { variant: "default" }, countLabel);
|
|
5158
5560
|
if (collapsed) {
|
|
5159
5561
|
const rotated = makeRotatedLabelDataUri(stage.label);
|
|
@@ -5186,8 +5588,11 @@ var KanbanColumn = ({
|
|
|
5186
5588
|
}
|
|
5187
5589
|
) : null));
|
|
5188
5590
|
}
|
|
5591
|
+
if (compactEmpty && !loading && rows.length === 0 && bucketCount === 0) {
|
|
5592
|
+
return /* @__PURE__ */ React10.createElement(Tile3, { compact: true }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", align: "center", justify: "between", gap: "xs" }, /* @__PURE__ */ React10.createElement(Text4, { variant: "microcopy", format: { fontWeight: "demibold" } }, stage.shortLabel || stage.label), /* @__PURE__ */ React10.createElement(Text4, { variant: "microcopy", format: { italic: true } }, labels.emptyColumn)));
|
|
5593
|
+
}
|
|
5189
5594
|
const footerContent = stage.footer ? stage.footer(rows) : columnFooter ? columnFooter(rows, stage) : null;
|
|
5190
|
-
return /* @__PURE__ */ React10.createElement(Tile3, { compact: true }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "column", gap: "xs" }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", align: "center", justify: "between", gap: "xs" }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React10.createElement(Text4, { format: { fontWeight: "demibold" } }, stage.shortLabel || stage.label), countNode, loading ? /* @__PURE__ */ React10.createElement(
|
|
5595
|
+
return /* @__PURE__ */ React10.createElement(Tile3, { compact: true }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "column", gap: "xs" }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", align: "center", justify: "between", gap: "xs" }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React10.createElement(Text4, { format: { fontWeight: "demibold" } }, stage.shortLabel || stage.label), countNode, overWipNode, loading ? /* @__PURE__ */ React10.createElement(LoadingSpinner2, { size: "xs" }) : null), /* @__PURE__ */ React10.createElement(Button5, { variant: "transparent", size: "sm", onClick: onToggleCollapsed, tooltip: "Collapse" }, /* @__PURE__ */ React10.createElement(Icon, { name: "left", size: "sm", screenReaderText: `Collapse ${stage.label}` }))), footerContent ? /* @__PURE__ */ React10.createElement(Text4, { variant: "microcopy" }, footerContent) : null, /* @__PURE__ */ React10.createElement(Divider2, null), children, error ? /* @__PURE__ */ React10.createElement(Alert2, { variant: "danger", title: labels.errorTitle }, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", gap: "xs", align: "center" }, /* @__PURE__ */ React10.createElement(Text4, { variant: "microcopy" }, error), onLoadMore ? /* @__PURE__ */ React10.createElement(Button5, { variant: "transparent", size: "xs", onClick: () => onLoadMore(stage.value) }, labels.retryLoadMore) : null)) : null, !error && hasMore && onLoadMore && !loading && bucketCount > 0 ? /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", justify: "center" }, /* @__PURE__ */ React10.createElement(Link4, { onClick: () => onLoadMore(stage.value) }, labels.loadMore(bucketCount, totalCount))) : null, !error && loading && hasMore ? /* @__PURE__ */ React10.createElement(LoadingSpinner2, { size: "sm", layout: "centered", label: labels.loadingMore }) : null, !error && !hasMore && bucketCount > rows.length ? /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", justify: "center" }, /* @__PURE__ */ React10.createElement(Link4, { onClick: onToggleExpanded }, expanded ? labels.showLess : labels.showMore(rows.length, bucketCount))) : null, rows.length === 0 && bucketCount === 0 && !loading ? /* @__PURE__ */ React10.createElement(Text4, { variant: "microcopy", format: { italic: true } }, labels.emptyColumn) : null));
|
|
5191
5596
|
};
|
|
5192
5597
|
var renderMetricsPanel = (metrics) => {
|
|
5193
5598
|
if (!metrics) return null;
|
|
@@ -5226,14 +5631,14 @@ var KanbanToolbar = ({
|
|
|
5226
5631
|
sortOptions,
|
|
5227
5632
|
sortValue,
|
|
5228
5633
|
onSortChange,
|
|
5229
|
-
|
|
5230
|
-
|
|
5634
|
+
showMetricsButton,
|
|
5635
|
+
metricsPanel,
|
|
5231
5636
|
onToggleMetrics,
|
|
5232
5637
|
labels,
|
|
5233
5638
|
toolbarLeftFlex,
|
|
5234
5639
|
toolbarRightFlex
|
|
5235
5640
|
}) => {
|
|
5236
|
-
const rightControls = (sortOptions == null ? void 0 : sortOptions.length) > 0 ||
|
|
5641
|
+
const rightControls = (sortOptions == null ? void 0 : sortOptions.length) > 0 || showMetricsButton ? /* @__PURE__ */ React10.createElement(React10.Fragment, null, sortOptions && sortOptions.length > 0 ? /* @__PURE__ */ React10.createElement(
|
|
5237
5642
|
CollectionSortSelect,
|
|
5238
5643
|
{
|
|
5239
5644
|
name: "kanban-sort",
|
|
@@ -5242,7 +5647,7 @@ var KanbanToolbar = ({
|
|
|
5242
5647
|
options: sortOptions,
|
|
5243
5648
|
onChange: onSortChange
|
|
5244
5649
|
}
|
|
5245
|
-
) : null,
|
|
5650
|
+
) : null, showMetricsButton ? /* @__PURE__ */ React10.createElement(Button5, { variant: "secondary", size: "small", onClick: onToggleMetrics }, /* @__PURE__ */ React10.createElement(Icon, { name: "gauge", size: "sm" }), " ", labels.metricsButton) : null) : null;
|
|
5246
5651
|
return /* @__PURE__ */ React10.createElement(
|
|
5247
5652
|
CollectionToolbar,
|
|
5248
5653
|
{
|
|
@@ -5269,7 +5674,7 @@ var KanbanToolbar = ({
|
|
|
5269
5674
|
onRemove: onFilterRemove
|
|
5270
5675
|
},
|
|
5271
5676
|
right: rightControls,
|
|
5272
|
-
footer:
|
|
5677
|
+
footer: metricsPanel,
|
|
5273
5678
|
labels,
|
|
5274
5679
|
leftFlex: toolbarLeftFlex,
|
|
5275
5680
|
rightFlex: toolbarRightFlex
|
|
@@ -5321,6 +5726,18 @@ var Kanban = ({
|
|
|
5321
5726
|
// --- Per-stage pagination ---
|
|
5322
5727
|
stageMeta,
|
|
5323
5728
|
onLoadMore,
|
|
5729
|
+
// --- WIP limits ---
|
|
5730
|
+
wipLimits,
|
|
5731
|
+
onWipExceeded,
|
|
5732
|
+
// --- Swimlanes ---
|
|
5733
|
+
swimlaneBy,
|
|
5734
|
+
swimlaneLabels,
|
|
5735
|
+
swimlaneOrder,
|
|
5736
|
+
collapseLanes = true,
|
|
5737
|
+
collapsedLanes,
|
|
5738
|
+
defaultCollapsedLanes,
|
|
5739
|
+
onCollapsedLanesChange,
|
|
5740
|
+
metricsPerLane = false,
|
|
5324
5741
|
// --- Selection ---
|
|
5325
5742
|
selectable = false,
|
|
5326
5743
|
selectedIds,
|
|
@@ -5385,6 +5802,9 @@ var Kanban = ({
|
|
|
5385
5802
|
const [internalFilters, setInternalFilters] = useState4(() => getEmptyFilterValues(filters));
|
|
5386
5803
|
const [internalSort, setInternalSort] = useState4(defaultSort || (((_a = sortOptions == null ? void 0 : sortOptions[0]) == null ? void 0 : _a.value) ?? ""));
|
|
5387
5804
|
const [internalCollapsed, setInternalCollapsed] = useState4([]);
|
|
5805
|
+
const [internalCollapsedLanes, setInternalCollapsedLanes] = useState4(
|
|
5806
|
+
() => defaultCollapsedLanes || []
|
|
5807
|
+
);
|
|
5388
5808
|
const [internalExpanded, setInternalExpanded] = useState4([]);
|
|
5389
5809
|
const [internalSelection, setInternalSelection] = useState4([]);
|
|
5390
5810
|
const [internalShowMetrics, setInternalShowMetrics] = useState4(false);
|
|
@@ -5401,6 +5821,7 @@ var Kanban = ({
|
|
|
5401
5821
|
const resolvedFilters = filterValues != null ? filterValues : internalFilters;
|
|
5402
5822
|
const resolvedSort = sort != null ? sort : internalSort;
|
|
5403
5823
|
const resolvedCollapsed = collapsedStages != null ? collapsedStages : internalCollapsed;
|
|
5824
|
+
const resolvedCollapsedLanes = collapsedLanes != null ? collapsedLanes : internalCollapsedLanes;
|
|
5404
5825
|
const resolvedExpanded = expandedStages != null ? expandedStages : internalExpanded;
|
|
5405
5826
|
const resolvedSelection = selectedIds != null ? selectedIds : internalSelection;
|
|
5406
5827
|
const searchEnabled = showSearch && Array.isArray(searchFields) && searchFields.length > 0;
|
|
@@ -5417,9 +5838,10 @@ var Kanban = ({
|
|
|
5417
5838
|
search: overrides.search != null ? overrides.search : resolvedSearch,
|
|
5418
5839
|
filters: overrides.filters != null ? overrides.filters : resolvedFilters,
|
|
5419
5840
|
sort: overrides.sort != null ? overrides.sort : resolvedSort || null,
|
|
5420
|
-
collapsedStages: overrides.collapsedStages != null ? overrides.collapsedStages : resolvedCollapsed
|
|
5841
|
+
collapsedStages: overrides.collapsedStages != null ? overrides.collapsedStages : resolvedCollapsed,
|
|
5842
|
+
collapsedLanes: overrides.collapsedLanes != null ? overrides.collapsedLanes : resolvedCollapsedLanes
|
|
5421
5843
|
});
|
|
5422
|
-
}, [onParamsChange, resolvedCollapsed, resolvedFilters, resolvedSearch, resolvedSort]);
|
|
5844
|
+
}, [onParamsChange, resolvedCollapsed, resolvedCollapsedLanes, resolvedFilters, resolvedSearch, resolvedSort]);
|
|
5423
5845
|
const lastAppliedSearchRef = useRef4(searchValue != null ? searchValue : "");
|
|
5424
5846
|
useEffect4(() => {
|
|
5425
5847
|
if (searchValue == null) return;
|
|
@@ -5478,6 +5900,15 @@ var Kanban = ({
|
|
|
5478
5900
|
},
|
|
5479
5901
|
[fireParamsChange, resolvedCollapsed, collapsedStages, onCollapsedStagesChange]
|
|
5480
5902
|
);
|
|
5903
|
+
const handleLaneCollapsed = useCallback4(
|
|
5904
|
+
(laneKey) => {
|
|
5905
|
+
const next = resolvedCollapsedLanes.includes(laneKey) ? resolvedCollapsedLanes.filter((k) => k !== laneKey) : [...resolvedCollapsedLanes, laneKey];
|
|
5906
|
+
if (onCollapsedLanesChange) onCollapsedLanesChange(next);
|
|
5907
|
+
if (collapsedLanes == null) setInternalCollapsedLanes(next);
|
|
5908
|
+
fireParamsChange({ collapsedLanes: next });
|
|
5909
|
+
},
|
|
5910
|
+
[fireParamsChange, resolvedCollapsedLanes, collapsedLanes, onCollapsedLanesChange]
|
|
5911
|
+
);
|
|
5481
5912
|
const handleExpanded = useCallback4(
|
|
5482
5913
|
(stageValue) => {
|
|
5483
5914
|
const next = resolvedExpanded.includes(stageValue) ? resolvedExpanded.filter((v) => v !== stageValue) : [...resolvedExpanded, stageValue];
|
|
@@ -5546,33 +5977,45 @@ var Kanban = ({
|
|
|
5546
5977
|
}
|
|
5547
5978
|
return result;
|
|
5548
5979
|
}, [data, resolvedSearch, resolvedFilters, filters, searchEnabled, searchFields, fuzzySearch, fuzzyOptions]);
|
|
5549
|
-
const buckets = useMemo3(
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
const key = getStageFor(row);
|
|
5554
|
-
if (map[key]) {
|
|
5555
|
-
map[key].push(row);
|
|
5556
|
-
} else if (stages.length > 0) {
|
|
5557
|
-
if (!map.__unknown) map.__unknown = [];
|
|
5558
|
-
map.__unknown.push(row);
|
|
5559
|
-
}
|
|
5560
|
-
}
|
|
5561
|
-
return map;
|
|
5562
|
-
}, [filteredData, stages, getStageFor]);
|
|
5980
|
+
const buckets = useMemo3(
|
|
5981
|
+
() => bucketRowsByStage(filteredData, stages, getStageFor),
|
|
5982
|
+
[filteredData, stages, getStageFor]
|
|
5983
|
+
);
|
|
5563
5984
|
const sortComparator = useMemo3(() => {
|
|
5564
5985
|
if (!sortOptions || !resolvedSort) return null;
|
|
5565
5986
|
const opt = sortOptions.find((s) => s.value === resolvedSort);
|
|
5566
5987
|
return (opt == null ? void 0 : opt.comparator) || null;
|
|
5567
5988
|
}, [sortOptions, resolvedSort]);
|
|
5568
|
-
const sortedBuckets = useMemo3(() =>
|
|
5569
|
-
|
|
5989
|
+
const sortedBuckets = useMemo3(() => sortBuckets(buckets, sortComparator), [buckets, sortComparator]);
|
|
5990
|
+
const hasLanes = swimlaneBy != null;
|
|
5991
|
+
const laneData = useMemo3(() => {
|
|
5992
|
+
if (!hasLanes) return null;
|
|
5993
|
+
return partitionLanes(filteredData, { swimlaneBy, swimlaneOrder });
|
|
5994
|
+
}, [hasLanes, filteredData, swimlaneBy, swimlaneOrder]);
|
|
5995
|
+
const laneBuckets = useMemo3(() => {
|
|
5996
|
+
if (!laneData) return null;
|
|
5570
5997
|
const out = {};
|
|
5571
|
-
for (const
|
|
5572
|
-
out[
|
|
5998
|
+
for (const laneKey of laneData.laneKeys) {
|
|
5999
|
+
out[laneKey] = sortBuckets(
|
|
6000
|
+
bucketRowsByStage(laneData.rowsByLane[laneKey] || [], stages, getStageFor),
|
|
6001
|
+
sortComparator
|
|
6002
|
+
);
|
|
5573
6003
|
}
|
|
5574
6004
|
return out;
|
|
5575
|
-
}, [
|
|
6005
|
+
}, [laneData, stages, getStageFor, sortComparator]);
|
|
6006
|
+
const wipByStage = useMemo3(
|
|
6007
|
+
() => evaluateWip(stages, computeStageCounts(stages, buckets, stageMeta), wipLimits),
|
|
6008
|
+
[stages, buckets, stageMeta, wipLimits]
|
|
6009
|
+
);
|
|
6010
|
+
const prevWipRef = useRef4({});
|
|
6011
|
+
useEffect4(() => {
|
|
6012
|
+
const newlyExceeded = findNewlyExceededWip(prevWipRef.current, wipByStage);
|
|
6013
|
+
prevWipRef.current = wipByStage;
|
|
6014
|
+
if (!onWipExceeded) return;
|
|
6015
|
+
for (const event of newlyExceeded) {
|
|
6016
|
+
onWipExceeded(event.stageId, event.count, event.limit);
|
|
6017
|
+
}
|
|
6018
|
+
}, [wipByStage, onWipExceeded]);
|
|
5576
6019
|
const activeChips = useMemo3(
|
|
5577
6020
|
() => buildActiveFilterChips(filters, resolvedFilters),
|
|
5578
6021
|
[filters, resolvedFilters]
|
|
@@ -5699,6 +6142,52 @@ var Kanban = ({
|
|
|
5699
6142
|
selectionActions: selectionActions || [],
|
|
5700
6143
|
labels
|
|
5701
6144
|
};
|
|
6145
|
+
const metricsProvided = metrics != null && (!Array.isArray(metrics) || metrics.length > 0);
|
|
6146
|
+
const perLaneMetricsActive = hasLanes && metricsPerLane && typeof metrics === "function";
|
|
6147
|
+
const globalMetricsContent = metricsProvided && !perLaneMetricsActive ? typeof metrics === "function" ? metrics(filteredData, null) : metrics : null;
|
|
6148
|
+
const renderStageColumns = (bucketMap, laneKey) => {
|
|
6149
|
+
const inLane = laneKey != null;
|
|
6150
|
+
return /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", gap: "sm", wrap: "nowrap" }, stages.map((stage) => {
|
|
6151
|
+
const stageRows = bucketMap[stage.value] || [];
|
|
6152
|
+
const meta = inLane ? void 0 : stageMeta == null ? void 0 : stageMeta[stage.value];
|
|
6153
|
+
const isExpanded = resolvedExpanded.includes(stage.value);
|
|
6154
|
+
const clamp = isExpanded ? maxCardsExpanded : maxCardsPerColumn;
|
|
6155
|
+
const visibleRows = stageRows.slice(0, clamp);
|
|
6156
|
+
const isCollapsed = resolvedCollapsed.includes(stage.value);
|
|
6157
|
+
const stageWip = wipByStage[stage.value];
|
|
6158
|
+
const wip = inLane ? (stageWip == null ? void 0 : stageWip.exceeded) ? { count: stageWip.count, limit: null, exceeded: true } : null : stageWip;
|
|
6159
|
+
return /* @__PURE__ */ React10.createElement(
|
|
6160
|
+
AutoGrid2,
|
|
6161
|
+
{
|
|
6162
|
+
key: inLane ? `${laneKey}::${stage.value}` : stage.value,
|
|
6163
|
+
columnWidth: isCollapsed ? 72 : effectiveColumnWidth
|
|
6164
|
+
},
|
|
6165
|
+
/* @__PURE__ */ React10.createElement(
|
|
6166
|
+
KanbanColumn,
|
|
6167
|
+
{
|
|
6168
|
+
stage,
|
|
6169
|
+
rows: visibleRows,
|
|
6170
|
+
bucketCount: stageRows.length,
|
|
6171
|
+
totalCount: meta == null ? void 0 : meta.totalCount,
|
|
6172
|
+
hasMore: meta == null ? void 0 : meta.hasMore,
|
|
6173
|
+
loading: meta == null ? void 0 : meta.loading,
|
|
6174
|
+
error: meta == null ? void 0 : meta.error,
|
|
6175
|
+
onLoadMore: inLane ? void 0 : onLoadMore,
|
|
6176
|
+
expanded: isExpanded,
|
|
6177
|
+
onToggleExpanded: () => handleExpanded(stage.value),
|
|
6178
|
+
collapsed: isCollapsed,
|
|
6179
|
+
onToggleCollapsed: () => handleCollapsed(stage.value),
|
|
6180
|
+
columnFooter,
|
|
6181
|
+
countDisplay,
|
|
6182
|
+
wip,
|
|
6183
|
+
compactEmpty: inLane,
|
|
6184
|
+
labels
|
|
6185
|
+
},
|
|
6186
|
+
visibleRows.map((row) => renderCardNode(row, stage))
|
|
6187
|
+
)
|
|
6188
|
+
);
|
|
6189
|
+
}));
|
|
6190
|
+
};
|
|
5702
6191
|
const mainContent = error ? renderErrorState ? renderErrorState({
|
|
5703
6192
|
error,
|
|
5704
6193
|
title: labels.errorTitle,
|
|
@@ -5710,35 +6199,32 @@ var Kanban = ({
|
|
|
5710
6199
|
) : filteredData.length === 0 ? renderEmptyState ? renderEmptyState({
|
|
5711
6200
|
title: labels.emptyTitle,
|
|
5712
6201
|
message: labels.emptyMessage
|
|
5713
|
-
}) : /* @__PURE__ */ React10.createElement(Tile3, null, /* @__PURE__ */ React10.createElement(Flex6, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ React10.createElement(EmptyState2, { title: labels.emptyTitle, layout: "vertical" }, /* @__PURE__ */ React10.createElement(Text4, null, labels.emptyMessage)))) : /* @__PURE__ */ React10.createElement(Flex6, { direction: "
|
|
5714
|
-
const
|
|
5715
|
-
const
|
|
5716
|
-
const
|
|
5717
|
-
const
|
|
5718
|
-
const
|
|
5719
|
-
const
|
|
5720
|
-
|
|
5721
|
-
|
|
6202
|
+
}) : /* @__PURE__ */ React10.createElement(Tile3, null, /* @__PURE__ */ React10.createElement(Flex6, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ React10.createElement(EmptyState2, { title: labels.emptyTitle, layout: "vertical" }, /* @__PURE__ */ React10.createElement(Text4, null, labels.emptyMessage)))) : hasLanes ? /* @__PURE__ */ React10.createElement(Flex6, { direction: "column", gap: "md" }, ((laneData == null ? void 0 : laneData.laneKeys) || []).map((laneKey, laneIndex) => {
|
|
6203
|
+
const laneRows = laneData.rowsByLane[laneKey] || [];
|
|
6204
|
+
const laneLabel = resolveLaneLabel(laneKey, swimlaneLabels, laneRows, labels.unassignedLane);
|
|
6205
|
+
const laneLabelText = typeof laneLabel === "string" ? laneLabel : String(laneKey);
|
|
6206
|
+
const isLaneCollapsed = collapseLanes && resolvedCollapsedLanes.includes(laneKey);
|
|
6207
|
+
const laneCountLabel = labels.laneCount(laneRows.length);
|
|
6208
|
+
const laneCountNode = countDisplay === "none" ? null : countDisplay === "text" ? /* @__PURE__ */ React10.createElement(Text4, { format: { fontWeight: "demibold" } }, laneCountLabel) : /* @__PURE__ */ React10.createElement(Tag3, { variant: "default" }, laneCountLabel);
|
|
6209
|
+
const laneMetricsNode = !isLaneCollapsed && perLaneMetricsActive && resolvedShowMetrics ? renderMetricsPanel(metrics(laneRows, laneKey)) : null;
|
|
6210
|
+
return /* @__PURE__ */ React10.createElement(Flex6, { key: laneKey, direction: "column", gap: "xs" }, laneIndex > 0 ? /* @__PURE__ */ React10.createElement(Divider2, null) : null, /* @__PURE__ */ React10.createElement(Flex6, { direction: "row", align: "center", gap: "xs" }, collapseLanes ? /* @__PURE__ */ React10.createElement(
|
|
6211
|
+
Button5,
|
|
5722
6212
|
{
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
hasMore: meta == null ? void 0 : meta.hasMore,
|
|
5728
|
-
loading: meta == null ? void 0 : meta.loading,
|
|
5729
|
-
error: meta == null ? void 0 : meta.error,
|
|
5730
|
-
onLoadMore,
|
|
5731
|
-
expanded: isExpanded,
|
|
5732
|
-
onToggleExpanded: () => handleExpanded(stage.value),
|
|
5733
|
-
collapsed: isCollapsed,
|
|
5734
|
-
onToggleCollapsed: () => handleCollapsed(stage.value),
|
|
5735
|
-
columnFooter,
|
|
5736
|
-
countDisplay,
|
|
5737
|
-
labels
|
|
6213
|
+
variant: "transparent",
|
|
6214
|
+
size: "sm",
|
|
6215
|
+
onClick: () => handleLaneCollapsed(laneKey),
|
|
6216
|
+
tooltip: isLaneCollapsed ? `Expand ${laneLabelText}` : `Collapse ${laneLabelText}`
|
|
5738
6217
|
},
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
6218
|
+
/* @__PURE__ */ React10.createElement(
|
|
6219
|
+
Icon,
|
|
6220
|
+
{
|
|
6221
|
+
name: isLaneCollapsed ? "right" : "down",
|
|
6222
|
+
size: "sm",
|
|
6223
|
+
screenReaderText: isLaneCollapsed ? `Expand ${laneLabelText}` : `Collapse ${laneLabelText}`
|
|
6224
|
+
}
|
|
6225
|
+
)
|
|
6226
|
+
) : null, /* @__PURE__ */ React10.createElement(Text4, { format: { fontWeight: "demibold" } }, laneLabel), laneCountNode), laneMetricsNode, !isLaneCollapsed ? renderStageColumns((laneBuckets == null ? void 0 : laneBuckets[laneKey]) || {}, laneKey) : null);
|
|
6227
|
+
})) : renderStageColumns(sortedBuckets, null);
|
|
5742
6228
|
const resolvedShowClearFiltersButton = showClearFiltersButton ?? showFilterBadges;
|
|
5743
6229
|
return /* @__PURE__ */ React10.createElement(Flex6, { direction: "column", gap: "sm" }, /* @__PURE__ */ React10.createElement(
|
|
5744
6230
|
KanbanToolbar,
|
|
@@ -5758,8 +6244,8 @@ var Kanban = ({
|
|
|
5758
6244
|
sortOptions,
|
|
5759
6245
|
sortValue: resolvedSort,
|
|
5760
6246
|
onSortChange: handleSort,
|
|
5761
|
-
|
|
5762
|
-
|
|
6247
|
+
showMetricsButton: metricsProvided,
|
|
6248
|
+
metricsPanel: resolvedShowMetrics && globalMetricsContent ? renderMetricsPanel(globalMetricsContent) : null,
|
|
5763
6249
|
onToggleMetrics: toggleMetrics,
|
|
5764
6250
|
labels,
|
|
5765
6251
|
toolbarLeftFlex,
|
|
@@ -5767,6 +6253,7 @@ var Kanban = ({
|
|
|
5767
6253
|
}
|
|
5768
6254
|
), showSelectionBar && selectable && selectedCount > 0 ? renderSelectionBar ? renderSelectionBar(selectionBarProps) : /* @__PURE__ */ React10.createElement(DefaultSelectionBar, { ...selectionBarProps }) : null, mainContent);
|
|
5769
6255
|
};
|
|
6256
|
+
Kanban.displayName = "Kanban";
|
|
5770
6257
|
|
|
5771
6258
|
// src/kanban/KanbanCardActions.jsx
|
|
5772
6259
|
import React11 from "react";
|
|
@@ -5847,7 +6334,7 @@ import {
|
|
|
5847
6334
|
Inline as Inline3,
|
|
5848
6335
|
Link as Link5,
|
|
5849
6336
|
List,
|
|
5850
|
-
StatusTag,
|
|
6337
|
+
StatusTag as StatusTag2,
|
|
5851
6338
|
Tab,
|
|
5852
6339
|
Tabs,
|
|
5853
6340
|
Tag as Tag4,
|
|
@@ -5992,6 +6479,110 @@ var AvatarStack = ({
|
|
|
5992
6479
|
});
|
|
5993
6480
|
};
|
|
5994
6481
|
|
|
6482
|
+
// src/feed/feedLiveBuffer.js
|
|
6483
|
+
var isDateValueObject2 = (v) => v != null && typeof v === "object" && typeof v.year === "number" && typeof v.month === "number" && typeof v.date === "number";
|
|
6484
|
+
var toTimestampMs = (value) => {
|
|
6485
|
+
if (value == null || value === "") return null;
|
|
6486
|
+
if (value instanceof Date) {
|
|
6487
|
+
const time2 = value.getTime();
|
|
6488
|
+
return Number.isNaN(time2) ? null : time2;
|
|
6489
|
+
}
|
|
6490
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
6491
|
+
if (isDateValueObject2(value)) {
|
|
6492
|
+
return new Date(value.year, value.month, value.date, value.hour || 0, value.minute || 0).getTime();
|
|
6493
|
+
}
|
|
6494
|
+
const parsed = new Date(value);
|
|
6495
|
+
const time = parsed.getTime();
|
|
6496
|
+
return Number.isNaN(time) ? null : time;
|
|
6497
|
+
};
|
|
6498
|
+
var defaultGetId = (item, index) => (item == null ? void 0 : item.id) ?? (item == null ? void 0 : item.key) ?? index;
|
|
6499
|
+
var partitionNewItems = (prevNewestTs, items, getTs, options = {}) => {
|
|
6500
|
+
const { knownIds = null, getId = defaultGetId } = options;
|
|
6501
|
+
const safeItems = Array.isArray(items) ? items : [];
|
|
6502
|
+
const firstLoad = prevNewestTs == null;
|
|
6503
|
+
const visible = [];
|
|
6504
|
+
const visibleIds = [];
|
|
6505
|
+
const buffered = [];
|
|
6506
|
+
const bufferedIds = [];
|
|
6507
|
+
let newestTs = typeof prevNewestTs === "number" ? prevNewestTs : null;
|
|
6508
|
+
safeItems.forEach((item, index) => {
|
|
6509
|
+
const id = getId(item, index);
|
|
6510
|
+
const ts = toTimestampMs(typeof getTs === "function" ? getTs(item) : void 0);
|
|
6511
|
+
const isKnown = knownIds != null && id !== void 0 && knownIds.has(id);
|
|
6512
|
+
const isNewArrival = !firstLoad && !isKnown && ts != null && ts > prevNewestTs;
|
|
6513
|
+
if (isNewArrival) {
|
|
6514
|
+
buffered.push(item);
|
|
6515
|
+
bufferedIds.push(id);
|
|
6516
|
+
return;
|
|
6517
|
+
}
|
|
6518
|
+
visible.push(item);
|
|
6519
|
+
visibleIds.push(id);
|
|
6520
|
+
if (ts != null && (newestTs == null || ts > newestTs)) newestTs = ts;
|
|
6521
|
+
});
|
|
6522
|
+
return { visible, buffered, visibleIds, bufferedIds, newestTs };
|
|
6523
|
+
};
|
|
6524
|
+
var flushBuffer = (visible, buffered, getTs) => {
|
|
6525
|
+
const safeVisible = Array.isArray(visible) ? visible : [];
|
|
6526
|
+
const safeBuffered = Array.isArray(buffered) ? buffered : [];
|
|
6527
|
+
const items = [...safeBuffered, ...safeVisible];
|
|
6528
|
+
let newestTs = null;
|
|
6529
|
+
items.forEach((item) => {
|
|
6530
|
+
const ts = toTimestampMs(typeof getTs === "function" ? getTs(item) : void 0);
|
|
6531
|
+
if (ts != null && (newestTs == null || ts > newestTs)) newestTs = ts;
|
|
6532
|
+
});
|
|
6533
|
+
return { items, flushed: safeBuffered, newestTs };
|
|
6534
|
+
};
|
|
6535
|
+
|
|
6536
|
+
// src/feed/feedTypePresets.js
|
|
6537
|
+
var hasValue = (value) => value != null && value !== false && value !== "";
|
|
6538
|
+
var DEFAULT_FEED_TYPE_PRESETS = {
|
|
6539
|
+
call: { icon: "calling", label: "Call" },
|
|
6540
|
+
email: { icon: "email", label: "Email" },
|
|
6541
|
+
incoming_email: { icon: "inbox", label: "Incoming email" },
|
|
6542
|
+
forwarded_email: { icon: "forward", label: "Forwarded email" },
|
|
6543
|
+
meeting: { icon: "appointment", label: "Meeting" },
|
|
6544
|
+
note: { icon: "comment", label: "Note" },
|
|
6545
|
+
task: { icon: "tasks", label: "Task" },
|
|
6546
|
+
sms: { icon: "messages", label: "SMS" },
|
|
6547
|
+
whatsapp: { icon: "messages", label: "WhatsApp" },
|
|
6548
|
+
linkedin_message: { icon: "linkedin", label: "LinkedIn message" },
|
|
6549
|
+
postal_mail: { icon: "send", label: "Postal mail" },
|
|
6550
|
+
conversation: { icon: "questionAnswer", label: "Conversation" }
|
|
6551
|
+
};
|
|
6552
|
+
var lookupTypePreset = (type, presets) => {
|
|
6553
|
+
if (!presets || typeof presets !== "object") return null;
|
|
6554
|
+
if (type == null || type === "") return null;
|
|
6555
|
+
if (Object.prototype.hasOwnProperty.call(presets, type)) return presets[type];
|
|
6556
|
+
const lower = String(type).toLowerCase();
|
|
6557
|
+
if (Object.prototype.hasOwnProperty.call(presets, lower)) return presets[lower];
|
|
6558
|
+
const snake = lower.replace(/[\s-]+/g, "_");
|
|
6559
|
+
if (Object.prototype.hasOwnProperty.call(presets, snake)) return presets[snake];
|
|
6560
|
+
return null;
|
|
6561
|
+
};
|
|
6562
|
+
var applyTypePreset = (item, typePresets) => {
|
|
6563
|
+
if (item == null || typeof item !== "object") return item;
|
|
6564
|
+
const preset = lookupTypePreset(item.type, typePresets);
|
|
6565
|
+
if (!preset || typeof preset !== "object") return item;
|
|
6566
|
+
let next = null;
|
|
6567
|
+
const fill = (key, value) => {
|
|
6568
|
+
if (next === null) next = { ...item };
|
|
6569
|
+
next[key] = value;
|
|
6570
|
+
};
|
|
6571
|
+
if (!hasValue(item.icon) && !hasValue(item.iconName) && hasValue(preset.icon)) {
|
|
6572
|
+
fill("iconName", preset.icon);
|
|
6573
|
+
}
|
|
6574
|
+
if (!hasValue(item.iconColor) && hasValue(preset.color)) {
|
|
6575
|
+
fill("iconColor", preset.color);
|
|
6576
|
+
}
|
|
6577
|
+
if (!hasValue(item.typeLabel) && hasValue(preset.label)) {
|
|
6578
|
+
fill("typeLabel", preset.label);
|
|
6579
|
+
}
|
|
6580
|
+
if (item.statusVariant == null && item.outcomeVariant == null && item.severityVariant == null && hasValue(preset.statusVariant)) {
|
|
6581
|
+
fill("statusVariant", preset.statusVariant);
|
|
6582
|
+
}
|
|
6583
|
+
return next ?? item;
|
|
6584
|
+
};
|
|
6585
|
+
|
|
5995
6586
|
// src/feed/Feed.jsx
|
|
5996
6587
|
var DEFAULT_LABELS4 = {
|
|
5997
6588
|
search: "Search activity...",
|
|
@@ -6005,6 +6596,8 @@ var DEFAULT_LABELS4 = {
|
|
|
6005
6596
|
loadingMessage: "This should only take a moment.",
|
|
6006
6597
|
loadingMore: "Loading...",
|
|
6007
6598
|
loadMore: "View more",
|
|
6599
|
+
newItems: (count) => count === 1 ? "Show 1 new item" : `Show ${count} new items`,
|
|
6600
|
+
newItemTag: "New",
|
|
6008
6601
|
collapseAll: "Collapse all",
|
|
6009
6602
|
expandAll: "Expand all",
|
|
6010
6603
|
emptyTitle: "No activity yet",
|
|
@@ -6017,7 +6610,15 @@ var DEFAULT_LABELS4 = {
|
|
|
6017
6610
|
var DEFAULT_RECORD_LABEL = { singular: "item", plural: "items" };
|
|
6018
6611
|
var DEFAULT_SEARCH_FIELDS = ["title", "subject", "body", "description", "content", "preview", "type", "typeLabel", "actorName", "author"];
|
|
6019
6612
|
var DEFAULT_PAGE_SIZE = 5;
|
|
6020
|
-
var
|
|
6613
|
+
var EMPTY_ITEMS = [];
|
|
6614
|
+
var INITIAL_LIVE_STATE = {
|
|
6615
|
+
source: null,
|
|
6616
|
+
watermark: null,
|
|
6617
|
+
bufferedKeys: EMPTY_ITEMS,
|
|
6618
|
+
knownKeys: EMPTY_ITEMS,
|
|
6619
|
+
newKeys: EMPTY_ITEMS
|
|
6620
|
+
};
|
|
6621
|
+
var hasValue2 = (value) => value != null && value !== false && value !== "";
|
|
6021
6622
|
var keepWordsTogether = (value) => typeof value === "string" ? value.replace(/\s+/g, "\xA0") : value;
|
|
6022
6623
|
var getItemKey = (item, index, getKey) => {
|
|
6023
6624
|
if (typeof getKey === "function") return getKey(item, index);
|
|
@@ -6044,11 +6645,11 @@ var pickHeaderActions = (item) => item == null ? void 0 : item.headerActions;
|
|
|
6044
6645
|
var itemHasExpandableContent = (item, fields) => {
|
|
6045
6646
|
if ((item == null ? void 0 : item.collapsible) === false) return false;
|
|
6046
6647
|
if ((item == null ? void 0 : item.collapsible) === true) return true;
|
|
6047
|
-
if (
|
|
6048
|
-
if (
|
|
6049
|
-
if (
|
|
6050
|
-
if (
|
|
6051
|
-
if (
|
|
6648
|
+
if (hasValue2(pickBody(item))) return true;
|
|
6649
|
+
if (hasValue2(pickActor(item))) return true;
|
|
6650
|
+
if (hasValue2(item == null ? void 0 : item.actions)) return true;
|
|
6651
|
+
if (hasValue2(item == null ? void 0 : item.footer)) return true;
|
|
6652
|
+
if (hasValue2(item == null ? void 0 : item.meta) || hasValue2(item == null ? void 0 : item.metadata)) return true;
|
|
6052
6653
|
if (Array.isArray(fields)) {
|
|
6053
6654
|
if (fields.some((f) => ["body", "footer"].includes(f.placement ?? "body"))) return true;
|
|
6054
6655
|
}
|
|
@@ -6130,7 +6731,7 @@ var getRecordLabel = (recordLabel, count) => {
|
|
|
6130
6731
|
var FeedActorAvatar = ({ item, avatarSize }) => {
|
|
6131
6732
|
const actor = pickActor(item);
|
|
6132
6733
|
const avatar = (item == null ? void 0 : item.avatar) ?? pickActorAvatar(actor);
|
|
6133
|
-
if (!
|
|
6734
|
+
if (!hasValue2(avatar)) return null;
|
|
6134
6735
|
return /* @__PURE__ */ React13.createElement(
|
|
6135
6736
|
AvatarStack,
|
|
6136
6737
|
{
|
|
@@ -6142,10 +6743,10 @@ var FeedActorAvatar = ({ item, avatarSize }) => {
|
|
|
6142
6743
|
);
|
|
6143
6744
|
};
|
|
6144
6745
|
var FeedTypeIcon = ({ item, iconSize }) => {
|
|
6145
|
-
if (
|
|
6746
|
+
if (hasValue2(item == null ? void 0 : item.icon) && typeof item.icon !== "string") return item.icon;
|
|
6146
6747
|
const iconName = typeof (item == null ? void 0 : item.icon) === "string" ? item.icon : item == null ? void 0 : item.iconName;
|
|
6147
|
-
if (!
|
|
6148
|
-
return /* @__PURE__ */ React13.createElement(Icon, { name: iconName, size: iconSize, purpose: "decorative" });
|
|
6748
|
+
if (!hasValue2(iconName)) return null;
|
|
6749
|
+
return /* @__PURE__ */ React13.createElement(Icon, { name: iconName, size: iconSize, color: item == null ? void 0 : item.iconColor, purpose: "decorative" });
|
|
6149
6750
|
};
|
|
6150
6751
|
var FeedActions = ({ actions }) => {
|
|
6151
6752
|
if (!Array.isArray(actions) || actions.length === 0) return actions || null;
|
|
@@ -6168,14 +6769,14 @@ var FeedField = ({ field, item, index }) => {
|
|
|
6168
6769
|
if (field.visible && !field.visible(item)) return null;
|
|
6169
6770
|
const value = getValue(item, field.field);
|
|
6170
6771
|
const rendered = field.render ? field.render(value, item, index) : value;
|
|
6171
|
-
if (!
|
|
6772
|
+
if (!hasValue2(rendered)) return null;
|
|
6172
6773
|
if (field.href) {
|
|
6173
6774
|
const href = typeof field.href === "function" ? field.href(item) : field.href;
|
|
6174
6775
|
return /* @__PURE__ */ React13.createElement(Link5, { href }, rendered);
|
|
6175
6776
|
}
|
|
6176
6777
|
if (field.type === "status") {
|
|
6177
6778
|
const variant = typeof field.variant === "function" ? field.variant(value, item) : field.variant;
|
|
6178
|
-
return /* @__PURE__ */ React13.createElement(
|
|
6779
|
+
return /* @__PURE__ */ React13.createElement(StatusTag2, { variant: variant ?? "default" }, rendered);
|
|
6179
6780
|
}
|
|
6180
6781
|
if (field.type === "tag") {
|
|
6181
6782
|
const variant = typeof field.variant === "function" ? field.variant(value, item) : field.variant;
|
|
@@ -6205,7 +6806,7 @@ var renderPlacedFields = ({ fields, placement, item, index, inline = false }) =>
|
|
|
6205
6806
|
return /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: "xs" }, nodes);
|
|
6206
6807
|
};
|
|
6207
6808
|
var renderHeaderActions = (headerActions) => {
|
|
6208
|
-
if (!
|
|
6809
|
+
if (!hasValue2(headerActions)) return null;
|
|
6209
6810
|
if (!Array.isArray(headerActions)) return headerActions;
|
|
6210
6811
|
return /* @__PURE__ */ React13.createElement(Inline3, { gap: "sm", align: "center" }, headerActions.filter(Boolean).map((action, index) => /* @__PURE__ */ React13.createElement(
|
|
6211
6812
|
Link5,
|
|
@@ -6227,6 +6828,8 @@ var DefaultFeedItem = ({
|
|
|
6227
6828
|
collapsible,
|
|
6228
6829
|
expanded,
|
|
6229
6830
|
onToggleExpanded,
|
|
6831
|
+
isNew,
|
|
6832
|
+
newItemTagLabel,
|
|
6230
6833
|
renderActor,
|
|
6231
6834
|
renderTimestamp,
|
|
6232
6835
|
renderMeta,
|
|
@@ -6246,7 +6849,7 @@ var DefaultFeedItem = ({
|
|
|
6246
6849
|
const body = pickBody(item);
|
|
6247
6850
|
const avatar = /* @__PURE__ */ React13.createElement(FeedActorAvatar, { item, avatarSize });
|
|
6248
6851
|
const typeIcon = /* @__PURE__ */ React13.createElement(FeedTypeIcon, { item, iconSize });
|
|
6249
|
-
const hasAvatarNode =
|
|
6852
|
+
const hasAvatarNode = hasValue2(item == null ? void 0 : item.avatar) || hasValue2(pickActorAvatar(rawActor));
|
|
6250
6853
|
const titleFields = fieldsForPlacement(fields, "title");
|
|
6251
6854
|
const titleField = titleFields.length > 0 ? /* @__PURE__ */ React13.createElement(FeedField, { field: titleFields[0], item, index }) : null;
|
|
6252
6855
|
const subtitleFields = renderPlacedFields({ fields, placement: "subtitle", item, index, inline: true });
|
|
@@ -6254,8 +6857,8 @@ var DefaultFeedItem = ({
|
|
|
6254
6857
|
const bodyFields = renderPlacedFields({ fields, placement: "body", item, index });
|
|
6255
6858
|
const footerFields = renderPlacedFields({ fields, placement: "footer", item, index, inline: true });
|
|
6256
6859
|
const titleContent = titleField ?? (item == null ? void 0 : item.title) ?? (item == null ? void 0 : item.subject);
|
|
6257
|
-
const title =
|
|
6258
|
-
const titleText =
|
|
6860
|
+
const title = hasValue2(item == null ? void 0 : item.href) ? /* @__PURE__ */ React13.createElement(Link5, { href: item.href }, titleContent) : titleContent;
|
|
6861
|
+
const titleText = hasValue2(title) ? /* @__PURE__ */ React13.createElement(Text6, { format: { fontWeight: "demibold" }, truncate: true }, title) : null;
|
|
6259
6862
|
const headerLeft = /* @__PURE__ */ React13.createElement(Flex8, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, collapsible ? /* @__PURE__ */ React13.createElement(Box5, { flex: "none", alignSelf: "center" }, /* @__PURE__ */ React13.createElement(Link5, { variant: "dark", onClick: onToggleExpanded }, /* @__PURE__ */ React13.createElement(
|
|
6260
6863
|
Icon,
|
|
6261
6864
|
{
|
|
@@ -6263,10 +6866,10 @@ var DefaultFeedItem = ({
|
|
|
6263
6866
|
size: "md",
|
|
6264
6867
|
screenReaderText: expanded ? "Collapse" : "Expand"
|
|
6265
6868
|
}
|
|
6266
|
-
))) : null, typeIcon, titleText);
|
|
6267
|
-
const headerRight =
|
|
6869
|
+
))) : null, typeIcon, titleText, isNew ? /* @__PURE__ */ React13.createElement(Box5, { flex: "none" }, /* @__PURE__ */ React13.createElement(Tag4, { variant: "info" }, newItemTagLabel ?? "New")) : null);
|
|
6870
|
+
const headerRight = hasValue2(headerActions) || hasValue2(timestamp) || hasValue2(type) ? /* @__PURE__ */ React13.createElement(Inline3, { gap: "sm", align: "center" }, renderHeaderActions(headerActions), hasValue2(type) && /* @__PURE__ */ React13.createElement(Text6, { variant: "microcopy" }, type), hasValue2(timestamp) && /* @__PURE__ */ React13.createElement(Text6, { variant: "microcopy" }, formatTimestamp(timestamp))) : null;
|
|
6268
6871
|
const showBody = !collapsible || expanded;
|
|
6269
|
-
return /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: compact ? "xs" : "sm" }, /* @__PURE__ */ React13.createElement(Flex8, { direction: "row", justify: "between", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ React13.createElement(Box5, { flex: 1 }, headerLeft), headerRight), showBody ? /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: compact ? "xs" : "sm" }, (hasAvatarNode ||
|
|
6872
|
+
return /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: compact ? "xs" : "sm" }, /* @__PURE__ */ React13.createElement(Flex8, { direction: "row", justify: "between", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ React13.createElement(Box5, { flex: 1 }, headerLeft), headerRight), showBody ? /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: compact ? "xs" : "sm" }, (hasAvatarNode || hasValue2(actor) || hasValue2(subtitleFields) || hasValue2(status)) && /* @__PURE__ */ React13.createElement(Flex8, { direction: "row", align: "center", gap: "xs", wrap: "wrap" }, hasAvatarNode ? avatar : null, hasValue2(actor) && /* @__PURE__ */ React13.createElement(Text6, { variant: "microcopy" }, actor), subtitleFields, hasValue2(status) && /* @__PURE__ */ React13.createElement(StatusTag2, { variant: statusVariant, hollow: true }, status)), hasValue2(body) && /* @__PURE__ */ React13.createElement(Text6, null, body), bodyFields, Array.isArray(meta) ? meta.length > 0 ? /* @__PURE__ */ React13.createElement(List, { variant: "inline-divided" }, meta) : metaFields : hasValue2(meta) ? /* @__PURE__ */ React13.createElement(Inline3, { gap: "xs" }, meta) : metaFields ? metaFields : null, hasValue2(actions) && /* @__PURE__ */ React13.createElement(FeedActions, { actions }), (hasValue2(footer) || hasValue2(footerFields)) && /* @__PURE__ */ React13.createElement(Inline3, { gap: "xs", align: "center" }, footerFields, footer)) : null);
|
|
6270
6873
|
};
|
|
6271
6874
|
var applyTab = (items, activeTab, tabField) => {
|
|
6272
6875
|
if (!activeTab || activeTab === "all") return items;
|
|
@@ -6490,10 +7093,19 @@ var Feed = ({
|
|
|
6490
7093
|
collapsedIds,
|
|
6491
7094
|
onCollapsedIdsChange,
|
|
6492
7095
|
showCollapseToggle = true,
|
|
6493
|
-
alignToolbarWithGroups = "auto"
|
|
7096
|
+
alignToolbarWithGroups = "auto",
|
|
7097
|
+
newItemsBehavior = "immediate",
|
|
7098
|
+
onNewItemsFlush,
|
|
7099
|
+
highlightNew = false,
|
|
7100
|
+
typePresets
|
|
6494
7101
|
}) => {
|
|
6495
|
-
const labels = { ...DEFAULT_LABELS4, ...labelOverrides || {} };
|
|
6496
|
-
const safeItems = Array.isArray(items) ? items :
|
|
7102
|
+
const labels = useMemo4(() => ({ ...DEFAULT_LABELS4, ...labelOverrides || {} }), [labelOverrides]);
|
|
7103
|
+
const safeItems = Array.isArray(items) ? items : EMPTY_ITEMS;
|
|
7104
|
+
const resolvedTypePresets = typePresets === true ? DEFAULT_FEED_TYPE_PRESETS : typePresets;
|
|
7105
|
+
const presetItems = useMemo4(
|
|
7106
|
+
() => resolvedTypePresets ? safeItems.map((item) => applyTypePreset(item, resolvedTypePresets)) : safeItems,
|
|
7107
|
+
[safeItems, resolvedTypePresets]
|
|
7108
|
+
);
|
|
6497
7109
|
const [internalTab, setInternalTab] = useState5(tabValue ?? defaultTab ?? "all");
|
|
6498
7110
|
const [internalSearch, setInternalSearch] = useState5(searchValue ?? "");
|
|
6499
7111
|
const [internalFilters, setInternalFilters] = useState5(filterValues ?? defaultFilterValues);
|
|
@@ -6506,6 +7118,47 @@ var Feed = ({
|
|
|
6506
7118
|
return [];
|
|
6507
7119
|
};
|
|
6508
7120
|
const [internalCollapsedIds, setInternalCollapsedIds] = useState5(computeInitialCollapsed);
|
|
7121
|
+
const bufferNewItems = newItemsBehavior === "pill";
|
|
7122
|
+
const highlightMs = typeof highlightNew === "number" && highlightNew > 0 ? highlightNew : 0;
|
|
7123
|
+
const trackNewItems = bufferNewItems || highlightMs > 0;
|
|
7124
|
+
const [liveState, setLiveState] = useState5(INITIAL_LIVE_STATE);
|
|
7125
|
+
const liveKeyOf = (item, index) => getItemKey(item, index, getKey);
|
|
7126
|
+
if (trackNewItems && liveState.source !== presetItems) {
|
|
7127
|
+
const firstLoad = liveState.source === null;
|
|
7128
|
+
const partition = partitionNewItems(liveState.watermark, presetItems, pickTimestamp, {
|
|
7129
|
+
knownIds: new Set(liveState.knownKeys),
|
|
7130
|
+
getId: liveKeyOf
|
|
7131
|
+
});
|
|
7132
|
+
const now = Date.now();
|
|
7133
|
+
const keptNewKeys = highlightMs > 0 ? liveState.newKeys.filter((entry) => now - entry.at < highlightMs) : EMPTY_ITEMS;
|
|
7134
|
+
if (bufferNewItems) {
|
|
7135
|
+
setLiveState({
|
|
7136
|
+
source: presetItems,
|
|
7137
|
+
watermark: partition.newestTs,
|
|
7138
|
+
bufferedKeys: partition.bufferedIds,
|
|
7139
|
+
knownKeys: partition.visibleIds,
|
|
7140
|
+
newKeys: keptNewKeys
|
|
7141
|
+
});
|
|
7142
|
+
} else {
|
|
7143
|
+
const { newestTs } = flushBuffer(partition.visible, partition.buffered, pickTimestamp);
|
|
7144
|
+
setLiveState({
|
|
7145
|
+
source: presetItems,
|
|
7146
|
+
watermark: newestTs ?? partition.newestTs,
|
|
7147
|
+
bufferedKeys: EMPTY_ITEMS,
|
|
7148
|
+
knownKeys: [...partition.visibleIds, ...partition.bufferedIds],
|
|
7149
|
+
newKeys: highlightMs > 0 && !firstLoad ? [...keptNewKeys, ...partition.bufferedIds.map((key) => ({ key, at: now }))] : keptNewKeys
|
|
7150
|
+
});
|
|
7151
|
+
}
|
|
7152
|
+
}
|
|
7153
|
+
const bufferedKeySet = useMemo4(() => new Set(liveState.bufferedKeys), [liveState.bufferedKeys]);
|
|
7154
|
+
const newKeySet = useMemo4(
|
|
7155
|
+
() => new Set(liveState.newKeys.map((entry) => entry.key)),
|
|
7156
|
+
[liveState.newKeys]
|
|
7157
|
+
);
|
|
7158
|
+
const sourceItems = useMemo4(() => {
|
|
7159
|
+
if (!bufferNewItems || bufferedKeySet.size === 0) return presetItems;
|
|
7160
|
+
return presetItems.filter((item, index) => !bufferedKeySet.has(getItemKey(item, index, getKey)));
|
|
7161
|
+
}, [presetItems, bufferNewItems, bufferedKeySet, getKey]);
|
|
6509
7162
|
const activeTab = tabValue !== void 0 ? tabValue : internalTab;
|
|
6510
7163
|
const activeSearch = searchValue !== void 0 ? searchValue : internalSearch;
|
|
6511
7164
|
const activeFilters = filterValues !== void 0 ? filterValues : internalFilters;
|
|
@@ -6528,6 +7181,17 @@ var Feed = ({
|
|
|
6528
7181
|
useEffect5(() => {
|
|
6529
7182
|
if (Array.isArray(collapsedIds)) setInternalCollapsedIds(collapsedIds);
|
|
6530
7183
|
}, [collapsedIds]);
|
|
7184
|
+
useEffect5(() => {
|
|
7185
|
+
if (!highlightMs || liveState.newKeys.length === 0) return void 0;
|
|
7186
|
+
const earliestAt = Math.min(...liveState.newKeys.map((entry) => entry.at));
|
|
7187
|
+
const timer = setTimeout(() => {
|
|
7188
|
+
setLiveState((prev) => ({
|
|
7189
|
+
...prev,
|
|
7190
|
+
newKeys: prev.newKeys.filter((entry) => Date.now() - entry.at < highlightMs)
|
|
7191
|
+
}));
|
|
7192
|
+
}, Math.max(16, earliestAt + highlightMs - Date.now()));
|
|
7193
|
+
return () => clearTimeout(timer);
|
|
7194
|
+
}, [highlightMs, liveState.newKeys]);
|
|
6531
7195
|
const emitParamsChange = (next) => {
|
|
6532
7196
|
if (typeof onParamsChange === "function") {
|
|
6533
7197
|
onParamsChange({ tab: activeTab, search: activeSearch, filters: activeFilters, sort: activeSort, ...next });
|
|
@@ -6581,13 +7245,40 @@ var Feed = ({
|
|
|
6581
7245
|
setCollapsedIds(collapsibleKeys);
|
|
6582
7246
|
};
|
|
6583
7247
|
const handleExpandAll = () => setCollapsedIds([]);
|
|
7248
|
+
const handleFlushNewItems = () => {
|
|
7249
|
+
const flushedItems = [];
|
|
7250
|
+
const flushedKeys = [];
|
|
7251
|
+
const keptItems = [];
|
|
7252
|
+
presetItems.forEach((item, index) => {
|
|
7253
|
+
const key = getItemKey(item, index, getKey);
|
|
7254
|
+
if (bufferedKeySet.has(key)) {
|
|
7255
|
+
flushedItems.push(item);
|
|
7256
|
+
flushedKeys.push(key);
|
|
7257
|
+
} else {
|
|
7258
|
+
keptItems.push(item);
|
|
7259
|
+
}
|
|
7260
|
+
});
|
|
7261
|
+
const { newestTs } = flushBuffer(keptItems, flushedItems, pickTimestamp);
|
|
7262
|
+
const now = Date.now();
|
|
7263
|
+
setLiveState((prev) => ({
|
|
7264
|
+
source: presetItems,
|
|
7265
|
+
watermark: newestTs ?? prev.watermark,
|
|
7266
|
+
bufferedKeys: EMPTY_ITEMS,
|
|
7267
|
+
knownKeys: presetItems.map((item, index) => getItemKey(item, index, getKey)),
|
|
7268
|
+
newKeys: highlightMs > 0 ? [
|
|
7269
|
+
...prev.newKeys.filter((entry) => now - entry.at < highlightMs),
|
|
7270
|
+
...flushedKeys.map((key) => ({ key, at: now }))
|
|
7271
|
+
] : prev.newKeys
|
|
7272
|
+
}));
|
|
7273
|
+
onNewItemsFlush == null ? void 0 : onNewItemsFlush(flushedItems);
|
|
7274
|
+
};
|
|
6584
7275
|
const processedItems = useMemo4(() => {
|
|
6585
|
-
if (serverSide) return
|
|
6586
|
-
const tabbed = applyTab(
|
|
7276
|
+
if (serverSide) return sourceItems;
|
|
7277
|
+
const tabbed = applyTab(sourceItems, activeTab, tabField);
|
|
6587
7278
|
const searched = applySearch(tabbed, activeSearch, searchFields);
|
|
6588
7279
|
const filtered = applyFilters(searched, filters, activeFilters);
|
|
6589
7280
|
return applySort(filtered, activeSort, sortOptions);
|
|
6590
|
-
}, [
|
|
7281
|
+
}, [sourceItems, activeTab, tabField, activeSearch, searchFields, filters, activeFilters, activeSort, sortOptions, serverSide]);
|
|
6591
7282
|
const visibleItems = useMemo4(
|
|
6592
7283
|
() => processedItems.slice(0, Math.max(0, resolvedMaxItems)),
|
|
6593
7284
|
[processedItems, resolvedMaxItems]
|
|
@@ -6614,8 +7305,8 @@ var Feed = ({
|
|
|
6614
7305
|
const canViewMore = visibleItems.length < processedItems.length;
|
|
6615
7306
|
const shouldShowExternalLoadMore = hasMore && onLoadMore;
|
|
6616
7307
|
const normalizedTabs = useMemo4(
|
|
6617
|
-
() => normalizeTabs(tabs,
|
|
6618
|
-
[tabs,
|
|
7308
|
+
() => normalizeTabs(tabs, presetItems, tabField, labels),
|
|
7309
|
+
[tabs, presetItems, tabField, labels]
|
|
6619
7310
|
);
|
|
6620
7311
|
const resolvedShowTabs = showTabs ?? normalizedTabs.length > 1;
|
|
6621
7312
|
const sortControl = Array.isArray(sortOptions) && sortOptions.length > 0 ? /* @__PURE__ */ React13.createElement(
|
|
@@ -6630,7 +7321,7 @@ var Feed = ({
|
|
|
6630
7321
|
) : null;
|
|
6631
7322
|
const countControl = showItemCount ? /* @__PURE__ */ React13.createElement(CollectionCount, { text: itemCountLabel }) : null;
|
|
6632
7323
|
const toolbarRight = sortControl || countControl ? /* @__PURE__ */ React13.createElement(Inline3, { gap: "sm", align: "center" }, sortControl, countControl) : null;
|
|
6633
|
-
const firstGroupHasLabel = groups.length > 0 &&
|
|
7324
|
+
const firstGroupHasLabel = groups.length > 0 && hasValue2(groups[0].label);
|
|
6634
7325
|
const hasLeftToolbarControls = resolvedShowSearch || Array.isArray(filters) && filters.length > 0 || activeChips.length > 0;
|
|
6635
7326
|
const alignControlsWithFirstGroup = !renderToolbar && showToolbar && !loading && !error && processedItems.length > 0 && !hasLeftToolbarControls && !!toolbarRight && firstGroupHasLabel && (alignToolbarWithGroups === true || alignToolbarWithGroups === "auto" && (groupByDate || !!groupBy));
|
|
6636
7327
|
const toolbarNode = renderToolbar ? renderToolbar({
|
|
@@ -6680,15 +7371,21 @@ var Feed = ({
|
|
|
6680
7371
|
return activeCollapsedIds.includes(getItemKey(item, i >= 0 ? i : idx, getKey));
|
|
6681
7372
|
});
|
|
6682
7373
|
const collapseToggle = showCollapseToggle && collapsibleVisibleItems.length > 1 && !loading && !error ? /* @__PURE__ */ React13.createElement(Link5, { onClick: allCollapsed ? handleExpandAll : handleCollapseAll }, keepWordsTogether(allCollapsed ? labels.expandAll : labels.collapseAll)) : null;
|
|
6683
|
-
if (
|
|
6684
|
-
const headerBody = /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: "xs" },
|
|
6685
|
-
const headerRight =
|
|
7374
|
+
if (hasValue2(title) || hasValue2(description) || hasValue2(actions) || hasValue2(children) || collapseToggle) {
|
|
7375
|
+
const headerBody = /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", gap: "xs" }, hasValue2(title) && /* @__PURE__ */ React13.createElement(Text6, { format: { fontWeight: "demibold" } }, title), hasValue2(description) && /* @__PURE__ */ React13.createElement(Text6, null, description), children);
|
|
7376
|
+
const headerRight = hasValue2(actions) || collapseToggle ? /* @__PURE__ */ React13.createElement(Inline3, { gap: "sm", align: "center" }, actions, collapseToggle) : null;
|
|
6686
7377
|
content.push(
|
|
6687
7378
|
/* @__PURE__ */ React13.createElement(Flex8, { key: "header", direction: "row", justify: "between", align: "start", gap: "sm" }, headerBody, headerRight)
|
|
6688
7379
|
);
|
|
6689
7380
|
}
|
|
6690
7381
|
const bodyContent = [];
|
|
6691
7382
|
if (toolbarNode) bodyContent.push(/* @__PURE__ */ React13.createElement(React13.Fragment, { key: "toolbar" }, toolbarNode));
|
|
7383
|
+
const pendingNewCount = bufferNewItems ? liveState.bufferedKeys.length : 0;
|
|
7384
|
+
if (pendingNewCount > 0 && !loading && !error) {
|
|
7385
|
+
bodyContent.push(
|
|
7386
|
+
/* @__PURE__ */ React13.createElement(Flex8, { key: "new-items-pill", direction: "row", justify: "center" }, /* @__PURE__ */ React13.createElement(Button7, { variant: "secondary", size: "small", onClick: handleFlushNewItems }, typeof labels.newItems === "function" ? labels.newItems(pendingNewCount) : labels.newItems))
|
|
7387
|
+
);
|
|
7388
|
+
}
|
|
6692
7389
|
if (loading) {
|
|
6693
7390
|
bodyContent.push(
|
|
6694
7391
|
renderLoadingState ? renderLoadingState({ label: labels.loading }) : (
|
|
@@ -6705,14 +7402,14 @@ var Feed = ({
|
|
|
6705
7402
|
message: labels.errorMessage
|
|
6706
7403
|
}) : /* @__PURE__ */ React13.createElement(Alert3, { key: "error", variant: "danger", title: typeof error === "string" ? error : labels.errorTitle }, /* @__PURE__ */ React13.createElement(Text6, null, labels.errorMessage))
|
|
6707
7404
|
);
|
|
6708
|
-
} else if (processedItems.length === 0) {
|
|
7405
|
+
} else if (processedItems.length === 0 && pendingNewCount === 0) {
|
|
6709
7406
|
bodyContent.push(
|
|
6710
7407
|
renderEmptyState ? renderEmptyState({ title: labels.emptyTitle, message: labels.emptyMessage }) : /* @__PURE__ */ React13.createElement(Tile4, { key: "empty" }, /* @__PURE__ */ React13.createElement(Flex8, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ React13.createElement(EmptyState3, { title: labels.emptyTitle, layout: "vertical" }, /* @__PURE__ */ React13.createElement(Text6, null, labels.emptyMessage))))
|
|
6711
7408
|
);
|
|
6712
7409
|
} else {
|
|
6713
7410
|
bodyContent.push(
|
|
6714
|
-
/* @__PURE__ */ React13.createElement(Flex8, { key: "items", direction: "column", gap: compact ? "xs" : gap }, groups.map((group, groupIndex) => /* @__PURE__ */ React13.createElement(Flex8, { key: group.key, direction: "column", gap: compact ? "xs" : gap },
|
|
6715
|
-
const globalIndex =
|
|
7411
|
+
/* @__PURE__ */ React13.createElement(Flex8, { key: "items", direction: "column", gap: compact ? "xs" : gap }, groups.map((group, groupIndex) => /* @__PURE__ */ React13.createElement(Flex8, { key: group.key, direction: "column", gap: compact ? "xs" : gap }, hasValue2(group.label) && (alignControlsWithFirstGroup && groupIndex === 0 ? /* @__PURE__ */ React13.createElement(Flex8, { direction: "row", justify: "between", align: "center", gap: "sm" }, /* @__PURE__ */ React13.createElement(Text6, { format: { fontWeight: "demibold" } }, group.label), toolbarRight) : /* @__PURE__ */ React13.createElement(Text6, { format: { fontWeight: "demibold" } }, group.label)), group.items.map((item, index) => {
|
|
7412
|
+
const globalIndex = presetItems.indexOf(item);
|
|
6716
7413
|
const itemIndex = globalIndex >= 0 ? globalIndex : index;
|
|
6717
7414
|
const key = getItemKey(item, itemIndex, getKey);
|
|
6718
7415
|
const node = renderItem ? renderItem(item, itemIndex) : /* @__PURE__ */ React13.createElement(
|
|
@@ -6727,6 +7424,8 @@ var Feed = ({
|
|
|
6727
7424
|
collapsible: collapsible !== false && itemHasExpandableContent(item, fields),
|
|
6728
7425
|
expanded: !activeCollapsedIds.includes(key),
|
|
6729
7426
|
onToggleExpanded: () => toggleItemExpanded(key),
|
|
7427
|
+
isNew: highlightMs > 0 && newKeySet.has(key),
|
|
7428
|
+
newItemTagLabel: labels.newItemTag,
|
|
6730
7429
|
renderActor,
|
|
6731
7430
|
renderTimestamp,
|
|
6732
7431
|
renderMeta,
|
|
@@ -6773,13 +7472,16 @@ var Feed = ({
|
|
|
6773
7472
|
if (container === "card" || container === "tile") return /* @__PURE__ */ React13.createElement(Tile4, { compact: true }, feed);
|
|
6774
7473
|
return feed;
|
|
6775
7474
|
};
|
|
7475
|
+
Feed.displayName = "Feed";
|
|
6776
7476
|
|
|
6777
7477
|
// src/calendar/Calendar.jsx
|
|
6778
7478
|
import React14, { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo5, useState as useState6 } from "react";
|
|
6779
7479
|
import {
|
|
6780
7480
|
Alert as Alert4,
|
|
7481
|
+
AutoGrid as AutoGrid3,
|
|
6781
7482
|
Box as Box6,
|
|
6782
7483
|
Button as Button8,
|
|
7484
|
+
DateInput as DateInput4,
|
|
6783
7485
|
Divider as Divider4,
|
|
6784
7486
|
EmptyState as EmptyState4,
|
|
6785
7487
|
Flex as Flex9,
|
|
@@ -6787,9 +7489,9 @@ import {
|
|
|
6787
7489
|
Image as Image5,
|
|
6788
7490
|
Inline as Inline4,
|
|
6789
7491
|
Link as Link6,
|
|
6790
|
-
LoadingSpinner as
|
|
6791
|
-
Modal,
|
|
6792
|
-
ModalBody,
|
|
7492
|
+
LoadingSpinner as LoadingSpinner3,
|
|
7493
|
+
Modal as Modal2,
|
|
7494
|
+
ModalBody as ModalBody2,
|
|
6793
7495
|
Panel,
|
|
6794
7496
|
PanelBody,
|
|
6795
7497
|
Select as Select6,
|
|
@@ -6799,7 +7501,7 @@ import {
|
|
|
6799
7501
|
TableHead as TableHead2,
|
|
6800
7502
|
TableHeader as TableHeader2,
|
|
6801
7503
|
TableRow as TableRow2,
|
|
6802
|
-
StatusTag as
|
|
7504
|
+
StatusTag as StatusTag3,
|
|
6803
7505
|
Tag as Tag5,
|
|
6804
7506
|
Text as Text7,
|
|
6805
7507
|
Tile as Tile5
|
|
@@ -6808,7 +7510,7 @@ import { Popover } from "@hubspot/ui-extensions/experimental";
|
|
|
6808
7510
|
|
|
6809
7511
|
// src/calendar/dateUtils.js
|
|
6810
7512
|
var MS_PER_DAY = 864e5;
|
|
6811
|
-
var
|
|
7513
|
+
var isDateValueObject3 = (v) => v != null && typeof v === "object" && typeof v.year === "number" && typeof v.month === "number" && typeof v.date === "number";
|
|
6812
7514
|
var fromEpoch = (ms) => {
|
|
6813
7515
|
const d = new Date(ms);
|
|
6814
7516
|
if (Number.isNaN(d.getTime())) return null;
|
|
@@ -6818,7 +7520,7 @@ var fromEpoch = (ms) => {
|
|
|
6818
7520
|
var toDate2 = (value) => {
|
|
6819
7521
|
if (value == null || value === "") return null;
|
|
6820
7522
|
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
|
6821
|
-
if (
|
|
7523
|
+
if (isDateValueObject3(value)) {
|
|
6822
7524
|
return new Date(
|
|
6823
7525
|
value.year,
|
|
6824
7526
|
value.month,
|
|
@@ -6913,7 +7615,7 @@ var buildHours = (startHour = 8, endHour = 20) => {
|
|
|
6913
7615
|
let e = Math.max(0, Math.min(23, Math.round(endHour)));
|
|
6914
7616
|
if (s > e) [s, e] = [e, s];
|
|
6915
7617
|
const hours = [];
|
|
6916
|
-
for (let
|
|
7618
|
+
for (let h7 = s; h7 <= e; h7 += 1) hours.push(h7);
|
|
6917
7619
|
return hours;
|
|
6918
7620
|
};
|
|
6919
7621
|
var WEEKDAY_LONG = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
@@ -7009,21 +7711,6 @@ var formatTimeZoneLabel = (tz, atDate = /* @__PURE__ */ new Date()) => {
|
|
|
7009
7711
|
|
|
7010
7712
|
// src/calendar/svgChips.js
|
|
7011
7713
|
var toDataUri = (svg) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
7012
|
-
var escapeXml = (s) => String(s == null ? "" : s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
7013
|
-
var truncateLabel = (value, max) => {
|
|
7014
|
-
const s = String(value == null ? "" : value);
|
|
7015
|
-
if (!max || s.length <= max) return s;
|
|
7016
|
-
return s.slice(0, Math.max(1, max - 1)).trimEnd() + "\u2026";
|
|
7017
|
-
};
|
|
7018
|
-
var CHIP_PALETTE = {
|
|
7019
|
-
default: { fill: "#7FD1DE", text: HS_TEXT_COLOR },
|
|
7020
|
-
info: { fill: "#00A4BD", text: "#FFFFFF" },
|
|
7021
|
-
success: { fill: "#00BDA5", text: "#FFFFFF" },
|
|
7022
|
-
warning: { fill: "#F5C26B", text: HS_TEXT_COLOR },
|
|
7023
|
-
error: { fill: "#F2545B", text: "#FFFFFF" },
|
|
7024
|
-
danger: { fill: "#F2545B", text: "#FFFFFF" }
|
|
7025
|
-
// StatusTag spells red "danger"; accept both
|
|
7026
|
-
};
|
|
7027
7714
|
var DOT_FILL = {
|
|
7028
7715
|
default: "#7C98B6",
|
|
7029
7716
|
info: "#00A4BD",
|
|
@@ -7038,31 +7725,177 @@ var makeDotDataUri = (variant = "default", size = 8) => {
|
|
|
7038
7725
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}"><circle cx="${r}" cy="${r}" r="${r}" fill="${fill}" /></svg>`;
|
|
7039
7726
|
return { src: toDataUri(svg), width: size, height: size };
|
|
7040
7727
|
};
|
|
7041
|
-
var makeEventChipDataUri = (opts) => {
|
|
7042
|
-
const { label, width, height = 24, variant = "default" } = opts;
|
|
7043
|
-
const palette = CHIP_PALETTE[variant] || CHIP_PALETTE.default;
|
|
7044
|
-
const accentX = 5;
|
|
7045
|
-
const accentW = 3;
|
|
7046
|
-
const textX = accentX + accentW + 6;
|
|
7047
|
-
const rightPad = 8;
|
|
7048
|
-
const maxChars = Math.max(1, Math.floor((width - textX - rightPad) / 6));
|
|
7049
|
-
const text = truncateLabel(label, maxChars);
|
|
7050
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect x="0.5" y="0.5" width="${width - 1}" height="${height - 1}" rx="4" ry="4" fill="#FFFFFF" stroke="#CBD6E2" stroke-width="1" /><rect x="${accentX}" y="5" width="${accentW}" height="${height - 10}" rx="1.5" ry="1.5" fill="${palette.fill}" /><text x="${textX}" y="${height / 2}" font-family='${HS_FONT_FAMILY}' font-size="12" font-weight="500" fill="${HS_TEXT_COLOR}" text-anchor="start" dominant-baseline="central">${escapeXml(text)}</text></svg>`;
|
|
7051
|
-
return { src: toDataUri(svg), width, height };
|
|
7052
|
-
};
|
|
7053
|
-
var makeMoreDataUri = (opts) => {
|
|
7054
|
-
const { label, width, height = 24 } = opts;
|
|
7055
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><text x="3" y="${height / 2}" font-family='${HS_FONT_FAMILY}' font-size="13" font-weight="700" fill="#0091AE" text-anchor="start" dominant-baseline="central">${escapeXml(label)}</text></svg>`;
|
|
7056
|
-
return { src: toDataUri(svg), width, height };
|
|
7057
|
-
};
|
|
7058
7728
|
var makeSpacerDataUri = (height = 24, width = 4) => {
|
|
7059
7729
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect x="0" y="0" width="${width}" height="${height}" fill="#FFFFFF" fill-opacity="0" /></svg>`;
|
|
7060
7730
|
return { src: toDataUri(svg), width, height };
|
|
7061
7731
|
};
|
|
7062
7732
|
|
|
7733
|
+
// src/calendar/rescheduleUtils.js
|
|
7734
|
+
var shiftDate = (date, shift) => {
|
|
7735
|
+
const d = toDate2(date);
|
|
7736
|
+
if (!d) return null;
|
|
7737
|
+
if (!shift || typeof shift !== "object") return new Date(d);
|
|
7738
|
+
const days = (shift.days || 0) + (shift.weeks || 0) * 7;
|
|
7739
|
+
let next = days ? addDays(d, days) : new Date(d);
|
|
7740
|
+
const ms = (shift.hours || 0) * 36e5 + (shift.minutes || 0) * 6e4;
|
|
7741
|
+
if (ms) next = new Date(next.getTime() + ms);
|
|
7742
|
+
return next;
|
|
7743
|
+
};
|
|
7744
|
+
var shiftEvent = (range, shift) => {
|
|
7745
|
+
const start = toDate2(range && range.start);
|
|
7746
|
+
if (!start) return null;
|
|
7747
|
+
const end = toDate2(range && range.end) || start;
|
|
7748
|
+
return { start: shiftDate(start, shift), end: shiftDate(end, shift) };
|
|
7749
|
+
};
|
|
7750
|
+
var calendarDayDelta = (a, b) => Math.round((startOfDay(b).getTime() - startOfDay(a).getTime()) / MS_PER_DAY);
|
|
7751
|
+
var msIntoDay = (d) => ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
|
|
7752
|
+
var rescheduleToStart = (range, newStart) => {
|
|
7753
|
+
const start = toDate2(range && range.start);
|
|
7754
|
+
const target = toDate2(newStart);
|
|
7755
|
+
if (!start || !target) return null;
|
|
7756
|
+
const end = toDate2(range && range.end) || start;
|
|
7757
|
+
const dayDelta = calendarDayDelta(start, target);
|
|
7758
|
+
const timeDelta = msIntoDay(target) - msIntoDay(start);
|
|
7759
|
+
const endOnDay = addDays(end, dayDelta);
|
|
7760
|
+
if (!timeDelta) return { start: target, end: endOnDay };
|
|
7761
|
+
return {
|
|
7762
|
+
start: target,
|
|
7763
|
+
end: new Date(
|
|
7764
|
+
endOnDay.getFullYear(),
|
|
7765
|
+
endOnDay.getMonth(),
|
|
7766
|
+
endOnDay.getDate(),
|
|
7767
|
+
0,
|
|
7768
|
+
0,
|
|
7769
|
+
0,
|
|
7770
|
+
msIntoDay(endOnDay) + timeDelta
|
|
7771
|
+
)
|
|
7772
|
+
};
|
|
7773
|
+
};
|
|
7774
|
+
var applyDatePick = (start, value) => {
|
|
7775
|
+
if (!isDateValueObject3(value)) return null;
|
|
7776
|
+
const s = toDate2(start);
|
|
7777
|
+
return new Date(
|
|
7778
|
+
value.year,
|
|
7779
|
+
value.month,
|
|
7780
|
+
value.date,
|
|
7781
|
+
s ? s.getHours() : 0,
|
|
7782
|
+
s ? s.getMinutes() : 0,
|
|
7783
|
+
s ? s.getSeconds() : 0,
|
|
7784
|
+
s ? s.getMilliseconds() : 0
|
|
7785
|
+
);
|
|
7786
|
+
};
|
|
7787
|
+
var DEFAULT_RESCHEDULE_PRESETS = [
|
|
7788
|
+
{ label: "+1 hour", shift: { hours: 1 } },
|
|
7789
|
+
{ label: "+1 day", shift: { days: 1 } },
|
|
7790
|
+
{ label: "Next week", shift: { weeks: 1 } }
|
|
7791
|
+
];
|
|
7792
|
+
var normalizeRescheduleOptions = (options) => {
|
|
7793
|
+
if (!options) return [];
|
|
7794
|
+
if (options === true) return DEFAULT_RESCHEDULE_PRESETS;
|
|
7795
|
+
if (!Array.isArray(options)) return [];
|
|
7796
|
+
const out = [];
|
|
7797
|
+
for (const opt of options) {
|
|
7798
|
+
if (typeof opt === "function") {
|
|
7799
|
+
out.push({ label: opt.label || opt.name || "Reschedule", getStart: opt });
|
|
7800
|
+
} else if (opt && typeof opt === "object" && opt.label != null) {
|
|
7801
|
+
if (typeof opt.shift === "function") {
|
|
7802
|
+
out.push({ label: opt.label, getStart: opt.shift });
|
|
7803
|
+
} else if (typeof opt.getStart === "function") {
|
|
7804
|
+
out.push({ label: opt.label, getStart: opt.getStart });
|
|
7805
|
+
} else if (opt.shift && typeof opt.shift === "object") {
|
|
7806
|
+
out.push({ label: opt.label, shift: opt.shift });
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
}
|
|
7810
|
+
return out;
|
|
7811
|
+
};
|
|
7812
|
+
var resolveRescheduleTarget = (range, option, fnArg) => {
|
|
7813
|
+
if (!range || !toDate2(range.start) || !option) return null;
|
|
7814
|
+
if (typeof option.getStart === "function") {
|
|
7815
|
+
const next = toDate2(option.getStart(fnArg !== void 0 ? fnArg : range));
|
|
7816
|
+
return next ? rescheduleToStart(range, next) : null;
|
|
7817
|
+
}
|
|
7818
|
+
if (option.shift && typeof option.shift === "object") {
|
|
7819
|
+
return shiftEvent(range, option.shift);
|
|
7820
|
+
}
|
|
7821
|
+
return null;
|
|
7822
|
+
};
|
|
7823
|
+
|
|
7824
|
+
// src/calendar/resourceLanes.js
|
|
7825
|
+
var resolveResourceId = (record, spec) => {
|
|
7826
|
+
if (record == null || spec == null) return null;
|
|
7827
|
+
const value = typeof spec === "function" ? spec(record) : record[spec];
|
|
7828
|
+
if (value == null || value === "") return null;
|
|
7829
|
+
return value;
|
|
7830
|
+
};
|
|
7831
|
+
var buildResourceLanes = (events, options = {}) => {
|
|
7832
|
+
const {
|
|
7833
|
+
resources,
|
|
7834
|
+
resourceLabels,
|
|
7835
|
+
getId,
|
|
7836
|
+
showUnassignedLane = true,
|
|
7837
|
+
unassignedLabel = "Unassigned"
|
|
7838
|
+
} = options;
|
|
7839
|
+
const labelFor = (id) => {
|
|
7840
|
+
if (resourceLabels && resourceLabels[id] != null) return resourceLabels[id];
|
|
7841
|
+
return String(id);
|
|
7842
|
+
};
|
|
7843
|
+
const lanes = [];
|
|
7844
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7845
|
+
const addLane = (id, label, declared) => {
|
|
7846
|
+
const key = String(id);
|
|
7847
|
+
if (byKey.has(key)) return byKey.get(key);
|
|
7848
|
+
const lane = { id, key, label, events: [], unassigned: false, declared };
|
|
7849
|
+
byKey.set(key, lane);
|
|
7850
|
+
lanes.push(lane);
|
|
7851
|
+
return lane;
|
|
7852
|
+
};
|
|
7853
|
+
(resources || []).forEach((resource) => {
|
|
7854
|
+
if (resource == null) return;
|
|
7855
|
+
if (typeof resource === "object") {
|
|
7856
|
+
addLane(resource.id, resource.label != null ? resource.label : labelFor(resource.id), true);
|
|
7857
|
+
} else {
|
|
7858
|
+
addLane(resource, labelFor(resource), true);
|
|
7859
|
+
}
|
|
7860
|
+
});
|
|
7861
|
+
const unassigned = {
|
|
7862
|
+
id: null,
|
|
7863
|
+
key: "__unassigned__",
|
|
7864
|
+
label: unassignedLabel,
|
|
7865
|
+
events: [],
|
|
7866
|
+
unassigned: true,
|
|
7867
|
+
declared: false
|
|
7868
|
+
};
|
|
7869
|
+
(events || []).forEach((event) => {
|
|
7870
|
+
const id = getId ? getId(event) : null;
|
|
7871
|
+
if (id == null || id === "") {
|
|
7872
|
+
unassigned.events.push(event);
|
|
7873
|
+
return;
|
|
7874
|
+
}
|
|
7875
|
+
const lane = byKey.get(String(id)) || addLane(id, labelFor(id), false);
|
|
7876
|
+
lane.events.push(event);
|
|
7877
|
+
});
|
|
7878
|
+
if (showUnassignedLane && unassigned.events.length > 0) lanes.push(unassigned);
|
|
7879
|
+
return lanes;
|
|
7880
|
+
};
|
|
7881
|
+
var eventsIntersectingRange = (events, rangeStart, rangeEnd) => {
|
|
7882
|
+
const rs = rangeStart.getTime();
|
|
7883
|
+
const re = rangeEnd.getTime();
|
|
7884
|
+
return (events || []).filter((event) => {
|
|
7885
|
+
if (!event || !event.start) return false;
|
|
7886
|
+
const es = event.start.getTime();
|
|
7887
|
+
const ee = (event.end || event.start).getTime();
|
|
7888
|
+
return es <= re && ee >= rs;
|
|
7889
|
+
});
|
|
7890
|
+
};
|
|
7891
|
+
var laneEventsForDay = (events, day) => eventsIntersectingRange(events, startOfDay(day), endOfDay(day)).sort(
|
|
7892
|
+
(a, b) => a.start.getTime() - b.start.getTime()
|
|
7893
|
+
);
|
|
7894
|
+
|
|
7063
7895
|
// src/calendar/Calendar.jsx
|
|
7064
7896
|
var DEFAULT_MAX_EVENTS_PER_DAY = 3;
|
|
7065
7897
|
var ALL_VIEWS = ["month", "week", "day", "agenda"];
|
|
7898
|
+
var ALL_VIEWS_WITH_RESOURCE = ["month", "week", "day", "resource", "agenda"];
|
|
7066
7899
|
var DEFAULT_DAY_START_HOUR = 8;
|
|
7067
7900
|
var DEFAULT_DAY_END_HOUR = 20;
|
|
7068
7901
|
var DEFAULT_TIME_ZONES = [
|
|
@@ -7098,7 +7931,8 @@ var VIEW_LABELS = {
|
|
|
7098
7931
|
month: "Month",
|
|
7099
7932
|
week: "Week",
|
|
7100
7933
|
day: "Day",
|
|
7101
|
-
agenda: "Agenda"
|
|
7934
|
+
agenda: "Agenda",
|
|
7935
|
+
resource: "Resource"
|
|
7102
7936
|
};
|
|
7103
7937
|
var DEFAULT_LABELS5 = {
|
|
7104
7938
|
today: "Today",
|
|
@@ -7115,7 +7949,11 @@ var DEFAULT_LABELS5 = {
|
|
|
7115
7949
|
errorMessage: "An error occurred while loading events.",
|
|
7116
7950
|
dayDetailTitle: (label) => label,
|
|
7117
7951
|
open: "Open",
|
|
7118
|
-
allDay: "All day"
|
|
7952
|
+
allDay: "All day",
|
|
7953
|
+
reschedule: "Reschedule",
|
|
7954
|
+
pickDate: "Pick date",
|
|
7955
|
+
unassigned: "Unassigned",
|
|
7956
|
+
resource: "Resource"
|
|
7119
7957
|
};
|
|
7120
7958
|
var DEFAULT_EVENT_FIELDS = {
|
|
7121
7959
|
id: "id",
|
|
@@ -7150,11 +7988,30 @@ var STATUS_VARIANT = {
|
|
|
7150
7988
|
error: "danger",
|
|
7151
7989
|
danger: "danger"
|
|
7152
7990
|
};
|
|
7991
|
+
var TAG_VARIANT = {
|
|
7992
|
+
default: "default",
|
|
7993
|
+
info: "info",
|
|
7994
|
+
success: "success",
|
|
7995
|
+
warning: "warning",
|
|
7996
|
+
error: "error",
|
|
7997
|
+
danger: "error"
|
|
7998
|
+
};
|
|
7999
|
+
var MONTH_EVENT_STYLES = /* @__PURE__ */ new Set(["statusTag", "tag"]);
|
|
8000
|
+
var monthLabelMaxChars = (style) => {
|
|
8001
|
+
const overhead = style === "tag" ? 34 : 46;
|
|
8002
|
+
return Math.max(6, Math.floor((MONTH_COL_WIDTH - overhead) / 6.6));
|
|
8003
|
+
};
|
|
8004
|
+
var truncateMonthLabel = (value, max) => {
|
|
8005
|
+
const s = String(value == null ? "" : value);
|
|
8006
|
+
if (!max || s.length <= max) return s;
|
|
8007
|
+
return s.slice(0, max).trimEnd();
|
|
8008
|
+
};
|
|
7153
8009
|
var MONTH_COL_WIDTH = 160;
|
|
7154
8010
|
var TIMEGRID_DAY_COL = 150;
|
|
7155
8011
|
var TIMEGRID_DAY_COL_SINGLE = 560;
|
|
8012
|
+
var RESOURCE_LABEL_COL_WIDTH = "min";
|
|
7156
8013
|
var HOUR_SLOT_HEIGHT = 64;
|
|
7157
|
-
var EventDetail = ({ event, labels }) => {
|
|
8014
|
+
var EventDetail = ({ event, labels, reschedule, idSuffix = "" }) => {
|
|
7158
8015
|
const { start, end, title, subtitle, href } = event;
|
|
7159
8016
|
let when = "";
|
|
7160
8017
|
if (start) {
|
|
@@ -7168,58 +8025,62 @@ var EventDetail = ({ event, labels }) => {
|
|
|
7168
8025
|
when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
|
|
7169
8026
|
}
|
|
7170
8027
|
}
|
|
7171
|
-
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, title || "--"), when ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, when) : null, subtitle ? /* @__PURE__ */ React14.createElement(Text7,
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
8028
|
+
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" }, truncate: true }, title || "--"), when ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", truncate: true }, when) : null, subtitle ? /* @__PURE__ */ React14.createElement(Text7, { truncate: true }, subtitle) : null, href ? /* @__PURE__ */ React14.createElement(Link6, { href: href.url, external: href.external }, labels.open) : null, reschedule ? /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(Divider4, null), /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: "demibold" } }, labels.reschedule), reschedule.options.length > 0 ? /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", gap: "xs", wrap: "wrap" }, reschedule.options.map((option, i) => /* @__PURE__ */ React14.createElement(
|
|
8029
|
+
Button8,
|
|
8030
|
+
{
|
|
8031
|
+
key: `${option.label}-${i}`,
|
|
8032
|
+
size: "xs",
|
|
8033
|
+
variant: "secondary",
|
|
8034
|
+
onClick: () => reschedule.onPreset(event, option)
|
|
8035
|
+
},
|
|
8036
|
+
option.label
|
|
8037
|
+
))) : null, /* @__PURE__ */ React14.createElement(
|
|
8038
|
+
DateInput4,
|
|
8039
|
+
{
|
|
8040
|
+
name: `cal-resched-${event.key}${idSuffix}`,
|
|
8041
|
+
label: labels.pickDate,
|
|
8042
|
+
onChange: (value) => reschedule.onPick(event, value)
|
|
8043
|
+
}
|
|
8044
|
+
)) : null);
|
|
8045
|
+
};
|
|
8046
|
+
var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "", reschedule = null) => {
|
|
8047
|
+
if (mode === "none") return void 0;
|
|
8048
|
+
const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ React14.createElement(EventDetail, { event, labels, reschedule, idSuffix });
|
|
7176
8049
|
const id = `cal-evt-${event.key}${idSuffix}`;
|
|
7177
8050
|
if (mode === "modal") {
|
|
7178
|
-
return /* @__PURE__ */ React14.createElement(
|
|
8051
|
+
return /* @__PURE__ */ React14.createElement(Modal2, { id, title: event.title || labels.open, width: "small" }, /* @__PURE__ */ React14.createElement(ModalBody2, null, body));
|
|
7179
8052
|
}
|
|
7180
8053
|
if (mode === "panel") {
|
|
7181
8054
|
return /* @__PURE__ */ React14.createElement(Panel, { id, title: event.title || labels.open, width: "small", variant: "modal" }, /* @__PURE__ */ React14.createElement(PanelBody, null, body));
|
|
7182
8055
|
}
|
|
7183
|
-
return /* @__PURE__ */ React14.createElement(Popover, { id, placement: "bottom" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, body));
|
|
8056
|
+
return /* @__PURE__ */ React14.createElement(Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, body));
|
|
7184
8057
|
};
|
|
7185
|
-
var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
|
|
8058
|
+
var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule }) => {
|
|
7186
8059
|
const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
|
|
7187
|
-
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "");
|
|
8060
|
+
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "", reschedule);
|
|
7188
8061
|
const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
|
|
7189
8062
|
const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
|
|
7190
8063
|
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ React14.createElement(Box6, { flex: 2 }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center" }, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: "demibold" } }, timeLabel))), /* @__PURE__ */ React14.createElement(Box6, { flex: 11 }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React14.createElement(ColorDot, { variant }), /* @__PURE__ */ React14.createElement(Text7, { truncate: true }, /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, event.title || "--")))), event.subtitle ? /* @__PURE__ */ React14.createElement(Box6, { flex: 4 }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center" }, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", truncate: true }, event.subtitle))) : null);
|
|
7191
8064
|
};
|
|
7192
|
-
var
|
|
7193
|
-
var
|
|
7194
|
-
var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
|
|
8065
|
+
var MONTH_SLOT_HEIGHT = 24;
|
|
8066
|
+
var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars, reschedule, idScope = "" }) => {
|
|
7195
8067
|
const isStartDay = !day || !event.start || isSameDay(event.start, day);
|
|
7196
|
-
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
|
|
8068
|
+
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${idScope}${day.getTime()}` : "", reschedule);
|
|
7197
8069
|
const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
|
|
7198
8070
|
const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
|
|
7199
8071
|
const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
|
|
7200
8072
|
const time = isStartDay && startHasTime ? `${formatTime(event.start)} ` : "";
|
|
7201
8073
|
const prefix = isStartDay ? "" : "\u2192 ";
|
|
7202
|
-
const
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
});
|
|
7208
|
-
return /* @__PURE__ */ React14.createElement(
|
|
7209
|
-
Image5,
|
|
7210
|
-
{
|
|
7211
|
-
src: chip.src,
|
|
7212
|
-
width: chip.width,
|
|
7213
|
-
height: chip.height,
|
|
7214
|
-
alt: event.title || "",
|
|
7215
|
-
overlay,
|
|
7216
|
-
onClick: handleClick
|
|
7217
|
-
}
|
|
7218
|
-
);
|
|
8074
|
+
const maxChars = monthEventMaxChars != null ? monthEventMaxChars : monthLabelMaxChars(monthEventStyle);
|
|
8075
|
+
const label = truncateMonthLabel(`${prefix}${time}${event.title || "--"}`, maxChars);
|
|
8076
|
+
if (monthEventStyle === "tag") {
|
|
8077
|
+
return /* @__PURE__ */ React14.createElement(Tag5, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
|
|
8078
|
+
}
|
|
8079
|
+
return /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(StatusTag3, { variant: STATUS_VARIANT[variant] || "default" }, label));
|
|
7219
8080
|
};
|
|
7220
|
-
var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
|
|
8081
|
+
var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule, idScope = "" }) => {
|
|
7221
8082
|
const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
|
|
7222
|
-
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-more${day.getTime()}` : "-more");
|
|
8083
|
+
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-more${idScope}${day.getTime()}` : "-more", reschedule);
|
|
7223
8084
|
const href = event.href;
|
|
7224
8085
|
return /* @__PURE__ */ React14.createElement(Button8, { variant: "transparent", size: "sm", href: href ? href.url : void 0, overlay, onClick: handleClick }, event.title || "--");
|
|
7225
8086
|
};
|
|
@@ -7284,6 +8145,40 @@ var Toolbar = ({
|
|
|
7284
8145
|
}
|
|
7285
8146
|
));
|
|
7286
8147
|
};
|
|
8148
|
+
var DayChipStack = ({ day, events, maxEventsPerDay, chipProps, labels, idScope = "" }) => {
|
|
8149
|
+
const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
|
|
8150
|
+
const shown = events.slice(0, maxEventsPerDay);
|
|
8151
|
+
const hasOverflow = events.length > maxEventsPerDay;
|
|
8152
|
+
const heightSpacer = /* @__PURE__ */ React14.createElement(Image5, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
|
|
8153
|
+
const slotRow = (key, content) => /* @__PURE__ */ React14.createElement(Flex9, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
|
|
8154
|
+
const slots = [];
|
|
8155
|
+
for (let i = 0; i < maxEventsPerDay; i++) {
|
|
8156
|
+
if (i < shown.length) {
|
|
8157
|
+
slots.push(slotRow(shown[i].key, /* @__PURE__ */ React14.createElement(MonthChip, { event: shown[i], day, idScope, ...chipProps })));
|
|
8158
|
+
} else {
|
|
8159
|
+
slots.push(/* @__PURE__ */ React14.createElement(Image5, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
if (hasOverflow) {
|
|
8163
|
+
slots.push(
|
|
8164
|
+
slotRow(
|
|
8165
|
+
"more",
|
|
8166
|
+
/* @__PURE__ */ React14.createElement(
|
|
8167
|
+
Link6,
|
|
8168
|
+
{
|
|
8169
|
+
overlay: /* @__PURE__ */ React14.createElement(Popover, { id: `cal-day-${idScope}${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "sm" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "bold" } }, String(events.length)), /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ React14.createElement(Divider4, null), events.map((event, i) => /* @__PURE__ */ React14.createElement(React14.Fragment, { key: event.key }, /* @__PURE__ */ React14.createElement(DayListItem, { event, day, idScope, ...chipProps }), i < events.length - 1 ? /* @__PURE__ */ React14.createElement(Divider4, null) : null)))))
|
|
8170
|
+
},
|
|
8171
|
+
labels.more(events.length - maxEventsPerDay)
|
|
8172
|
+
)
|
|
8173
|
+
)
|
|
8174
|
+
);
|
|
8175
|
+
} else {
|
|
8176
|
+
slots.push(
|
|
8177
|
+
/* @__PURE__ */ React14.createElement(Image5, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
|
|
8178
|
+
);
|
|
8179
|
+
}
|
|
8180
|
+
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, slots);
|
|
8181
|
+
};
|
|
7287
8182
|
var MonthView = ({
|
|
7288
8183
|
refDate,
|
|
7289
8184
|
now,
|
|
@@ -7298,51 +8193,23 @@ var MonthView = ({
|
|
|
7298
8193
|
}) => {
|
|
7299
8194
|
const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
|
|
7300
8195
|
const today = now || /* @__PURE__ */ new Date();
|
|
7301
|
-
const spacer24 = makeSpacerDataUri(MONTH_CHIP_HEIGHT, MONTH_COL_WIDTH);
|
|
7302
8196
|
const renderCell = (day) => {
|
|
7303
8197
|
const dayEvents = eventsForDay(day);
|
|
7304
8198
|
const inMonth = isSameMonth(day, refDate);
|
|
7305
8199
|
const isToday = isSameDay(day, today);
|
|
7306
8200
|
if (renderDayCell) return renderDayCell(day, dayEvents);
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
/* @__PURE__ */ React14.createElement(Image5, { key: `sp-${i}`, src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
|
|
7316
|
-
);
|
|
8201
|
+
return /* @__PURE__ */ React14.createElement(AutoGrid3, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ React14.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), /* @__PURE__ */ React14.createElement(
|
|
8202
|
+
DayChipStack,
|
|
8203
|
+
{
|
|
8204
|
+
day,
|
|
8205
|
+
events: dayEvents,
|
|
8206
|
+
maxEventsPerDay,
|
|
8207
|
+
chipProps,
|
|
8208
|
+
labels
|
|
7317
8209
|
}
|
|
7318
|
-
|
|
7319
|
-
if (hasOverflow) {
|
|
7320
|
-
const more = makeMoreDataUri({
|
|
7321
|
-
label: labels.more(dayEvents.length - maxEventsPerDay),
|
|
7322
|
-
width: MONTH_COL_WIDTH,
|
|
7323
|
-
height: MONTH_CHIP_HEIGHT
|
|
7324
|
-
});
|
|
7325
|
-
slots.push(
|
|
7326
|
-
/* @__PURE__ */ React14.createElement(
|
|
7327
|
-
Image5,
|
|
7328
|
-
{
|
|
7329
|
-
key: "more",
|
|
7330
|
-
src: more.src,
|
|
7331
|
-
width: more.width,
|
|
7332
|
-
height: more.height,
|
|
7333
|
-
alt: labels.more(dayEvents.length - maxEventsPerDay),
|
|
7334
|
-
overlay: /* @__PURE__ */ React14.createElement(Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "sm" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ React14.createElement(Heading, null, String(dayEvents.length)), /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ React14.createElement(Divider4, null), dayEvents.map((event, i) => /* @__PURE__ */ React14.createElement(React14.Fragment, { key: event.key }, /* @__PURE__ */ React14.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ React14.createElement(Divider4, null) : null)))))
|
|
7335
|
-
}
|
|
7336
|
-
)
|
|
7337
|
-
);
|
|
7338
|
-
} else {
|
|
7339
|
-
slots.push(
|
|
7340
|
-
/* @__PURE__ */ React14.createElement(Image5, { key: "more-sp", src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
|
|
7341
|
-
);
|
|
7342
|
-
}
|
|
7343
|
-
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ React14.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots);
|
|
8210
|
+
)));
|
|
7344
8211
|
};
|
|
7345
|
-
return /* @__PURE__ */ React14.createElement(Table2, { bordered: true, flush: true }, /* @__PURE__ */ React14.createElement(TableHead2, null, /* @__PURE__ */ React14.createElement(TableRow2, null, headers.map((
|
|
8212
|
+
return /* @__PURE__ */ React14.createElement(Table2, { bordered: true, flush: true }, /* @__PURE__ */ React14.createElement(TableHead2, null, /* @__PURE__ */ React14.createElement(TableRow2, null, headers.map((h7) => /* @__PURE__ */ React14.createElement(TableHeader2, { key: h7, width: "min", align: "center" }, h7.toUpperCase())))), /* @__PURE__ */ React14.createElement(TableBody2, null, weeks.map((week, wi) => {
|
|
7346
8213
|
const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
|
|
7347
8214
|
return /* @__PURE__ */ React14.createElement(TableRow2, { key: wi }, days.map((day) => /* @__PURE__ */ React14.createElement(TableCell2, { key: day.getTime(), width: "min" }, renderCell(day))));
|
|
7348
8215
|
})));
|
|
@@ -7362,13 +8229,40 @@ var AgendaView = ({ rangeStart, rangeEnd, eventsForDay, chipProps, labels, rende
|
|
|
7362
8229
|
}
|
|
7363
8230
|
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "lg" }, days.map(({ day, events }) => /* @__PURE__ */ React14.createElement(Flex9, { key: day.getTime(), direction: "column", gap: "sm" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", justify: "between", align: "end" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, formatDayTitle(day)), /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, `${events.length} ${events.length === 1 ? "event" : "events"}`)), /* @__PURE__ */ React14.createElement(Divider4, null), events.map((event) => /* @__PURE__ */ React14.createElement(AgendaEventRow, { key: event.key, event, day, ...chipProps })))));
|
|
7364
8231
|
};
|
|
8232
|
+
var ResourceView = ({ days, now, lanes, maxEventsPerDay, chipProps, labels, renderEmptyState }) => {
|
|
8233
|
+
const today = now || /* @__PURE__ */ new Date();
|
|
8234
|
+
if (!lanes || lanes.length === 0) {
|
|
8235
|
+
if (renderEmptyState) return renderEmptyState({});
|
|
8236
|
+
return /* @__PURE__ */ React14.createElement(EmptyState4, { title: labels.noEventsTitle }, /* @__PURE__ */ React14.createElement(Text7, null, labels.noEventsMessage));
|
|
8237
|
+
}
|
|
8238
|
+
const rangeStart = startOfDay(days[0]);
|
|
8239
|
+
const rangeEnd = endOfDay(days[days.length - 1]);
|
|
8240
|
+
return /* @__PURE__ */ React14.createElement(Table2, { bordered: true, flush: true }, /* @__PURE__ */ React14.createElement(TableHead2, null, /* @__PURE__ */ React14.createElement(TableRow2, null, /* @__PURE__ */ React14.createElement(TableHeader2, { width: RESOURCE_LABEL_COL_WIDTH }, String(labels.resource).toUpperCase()), days.map((day) => {
|
|
8241
|
+
const isToday = isSameDay(day, today);
|
|
8242
|
+
const label = `${formatWeekdayShort(day)} ${formatMonthShort(day)} ${day.getDate()}`;
|
|
8243
|
+
return /* @__PURE__ */ React14.createElement(TableHeader2, { key: day.getTime(), width: "min", align: "center" }, isToday ? `${label} \xB7 Today` : label);
|
|
8244
|
+
}))), /* @__PURE__ */ React14.createElement(TableBody2, null, lanes.map((lane, laneIndex) => {
|
|
8245
|
+
const visible = eventsIntersectingRange(lane.events, rangeStart, rangeEnd);
|
|
8246
|
+
return /* @__PURE__ */ React14.createElement(TableRow2, { key: lane.key }, /* @__PURE__ */ React14.createElement(TableCell2, { width: RESOURCE_LABEL_COL_WIDTH }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "flush" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, lane.label), /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, `${visible.length} ${visible.length === 1 ? "event" : "events"}`))), days.map((day) => /* @__PURE__ */ React14.createElement(TableCell2, { key: day.getTime(), width: "min" }, /* @__PURE__ */ React14.createElement(AutoGrid3, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ React14.createElement(
|
|
8247
|
+
DayChipStack,
|
|
8248
|
+
{
|
|
8249
|
+
day,
|
|
8250
|
+
events: laneEventsForDay(visible, day),
|
|
8251
|
+
maxEventsPerDay,
|
|
8252
|
+
chipProps,
|
|
8253
|
+
labels,
|
|
8254
|
+
idScope: `r${laneIndex}-`
|
|
8255
|
+
}
|
|
8256
|
+
)))));
|
|
8257
|
+
})));
|
|
8258
|
+
};
|
|
7365
8259
|
var formatTimedDuration = (start, end) => {
|
|
7366
8260
|
const mins = Math.max(0, Math.round(((end || start).getTime() - start.getTime()) / 6e4));
|
|
7367
|
-
const
|
|
8261
|
+
const h7 = Math.floor(mins / 60);
|
|
7368
8262
|
const m = mins % 60;
|
|
7369
|
-
if (
|
|
7370
|
-
if (m === 0) return `${
|
|
7371
|
-
return `${
|
|
8263
|
+
if (h7 === 0) return `${m} min`;
|
|
8264
|
+
if (m === 0) return `${h7} hr`;
|
|
8265
|
+
return `${h7} hr ${m} min`;
|
|
7372
8266
|
};
|
|
7373
8267
|
var hourSpan = (event) => {
|
|
7374
8268
|
const start = event.start;
|
|
@@ -7383,6 +8277,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7383
8277
|
const today = now || /* @__PURE__ */ new Date();
|
|
7384
8278
|
const centerDays = days.length === 1;
|
|
7385
8279
|
const dayColWidth = days.length === 1 ? TIMEGRID_DAY_COL_SINGLE : TIMEGRID_DAY_COL;
|
|
8280
|
+
const weekTitleMaxChars = Math.max(6, Math.floor((TIMEGRID_DAY_COL - 46) / 6.6));
|
|
7386
8281
|
const todayInView = days.some((d) => isSameDay(d, today));
|
|
7387
8282
|
const nowHour = today.getHours();
|
|
7388
8283
|
const dayData = days.map((day) => {
|
|
@@ -7408,7 +8303,8 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7408
8303
|
chipProps.overlayMode,
|
|
7409
8304
|
chipProps.renderEventDetail,
|
|
7410
8305
|
chipProps.labels,
|
|
7411
|
-
`-tg${mode}${hour}-${dayMs}
|
|
8306
|
+
`-tg${mode}${hour}-${dayMs}`,
|
|
8307
|
+
chipProps.reschedule
|
|
7412
8308
|
);
|
|
7413
8309
|
const handleClick = chipProps.onEventClick ? () => chipProps.onEventClick(e.raw, e) : void 0;
|
|
7414
8310
|
const variant = VALID_VARIANTS.has(e.color) ? e.color : "default";
|
|
@@ -7427,10 +8323,10 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7427
8323
|
} else if (mode === "cont") {
|
|
7428
8324
|
sub = `\u2191 cont. through ${endLabel}`;
|
|
7429
8325
|
}
|
|
7430
|
-
|
|
8326
|
+
const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
|
|
8327
|
+
return /* @__PURE__ */ React14.createElement(Flex9, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(StatusTag3, { variant: STATUS_VARIANT[variant] || "default" }, titleLabel)), sub ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, sub) : null);
|
|
7431
8328
|
};
|
|
7432
|
-
const
|
|
7433
|
-
const dayCell = (key, content) => /* @__PURE__ */ React14.createElement(TableCell2, { key, width: centerDays ? "max" : "min", align: "left" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, content, centerDays ? null : /* @__PURE__ */ React14.createElement(Image5, { src: daySpacer.src, width: daySpacer.width, height: daySpacer.height, alt: "" })));
|
|
8329
|
+
const dayCell = (key, content) => /* @__PURE__ */ React14.createElement(TableCell2, { key, width: centerDays ? "max" : "min", align: "left" }, centerDays ? /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, content) : /* @__PURE__ */ React14.createElement(AutoGrid3, { columnWidth: dayColWidth, gap: "flush" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, content)));
|
|
7434
8330
|
const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
|
|
7435
8331
|
const emptyCell = null;
|
|
7436
8332
|
return /* @__PURE__ */ React14.createElement(Table2, { bordered: true, flush: true, density: "compact" }, /* @__PURE__ */ React14.createElement(TableHead2, null, /* @__PURE__ */ React14.createElement(TableRow2, null, /* @__PURE__ */ React14.createElement(TableHeader2, { width: "min" }, "TIME"), dayData.map(({ day }) => {
|
|
@@ -7447,7 +8343,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7447
8343
|
return /* @__PURE__ */ React14.createElement(TableRow2, { key: hour }, /* @__PURE__ */ React14.createElement(TableCell2, { width: "min" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React14.createElement(Image5, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }), isNow ? (
|
|
7448
8344
|
// Trailing nbsp pads the chip's right edge so the final letter
|
|
7449
8345
|
// ("M" in "11 AM") isn't clipped by the min-width TIME column.
|
|
7450
|
-
/* @__PURE__ */ React14.createElement(
|
|
8346
|
+
/* @__PURE__ */ React14.createElement(StatusTag3, { variant: "info" }, `${formatHourLabel(hour)}\xA0`)
|
|
7451
8347
|
) : /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, formatHourLabel(hour)))), dayData.map(({ day, timed }) => {
|
|
7452
8348
|
const occupying = timed.map((t) => {
|
|
7453
8349
|
let visStart = Math.max(t.sHour, dayStartHour);
|
|
@@ -7486,9 +8382,21 @@ var Calendar = (props) => {
|
|
|
7486
8382
|
weekStartsOn = 0,
|
|
7487
8383
|
hideWeekends = false,
|
|
7488
8384
|
maxEventsPerDay = DEFAULT_MAX_EVENTS_PER_DAY,
|
|
8385
|
+
// month-grid event token style: "statusTag" (dot + text, default) | "tag" (pill)
|
|
8386
|
+
monthEventStyle = "statusTag",
|
|
8387
|
+
// max characters for a month-cell label before "…" (default derived per style)
|
|
8388
|
+
monthEventMaxChars,
|
|
7489
8389
|
// time grid (week / day)
|
|
7490
8390
|
dayStartHour = DEFAULT_DAY_START_HOUR,
|
|
7491
8391
|
dayEndHour = DEFAULT_DAY_END_HOUR,
|
|
8392
|
+
// resource / lane view (rows = resources, columns = the focused week's days)
|
|
8393
|
+
resources,
|
|
8394
|
+
resourceField,
|
|
8395
|
+
resourceLabels,
|
|
8396
|
+
showUnassignedLane = true,
|
|
8397
|
+
// drag-free reschedule (presets + date picker in the event-detail overlay)
|
|
8398
|
+
rescheduleOptions,
|
|
8399
|
+
onEventReschedule,
|
|
7492
8400
|
// timezone
|
|
7493
8401
|
timeZone: controlledTimeZone,
|
|
7494
8402
|
defaultTimeZone,
|
|
@@ -7527,10 +8435,12 @@ var Calendar = (props) => {
|
|
|
7527
8435
|
const fields = useMemo5(() => ({ ...DEFAULT_EVENT_FIELDS, ...eventFields || {} }), [eventFields]);
|
|
7528
8436
|
const [internalView, setInternalView] = useState6(defaultView);
|
|
7529
8437
|
const view = controlledView != null ? controlledView : internalView;
|
|
8438
|
+
const resourceEnabled = resources && resources.length > 0 || resourceField != null;
|
|
7530
8439
|
const enabledViews = useMemo5(() => {
|
|
7531
|
-
const
|
|
7532
|
-
|
|
7533
|
-
|
|
8440
|
+
const all = resourceEnabled ? ALL_VIEWS_WITH_RESOURCE : ALL_VIEWS;
|
|
8441
|
+
const base = viewsProp && viewsProp.length > 0 ? viewsProp : all;
|
|
8442
|
+
return base.filter((v) => all.includes(v));
|
|
8443
|
+
}, [viewsProp, resourceEnabled]);
|
|
7534
8444
|
const setView = useCallback5(
|
|
7535
8445
|
(next) => {
|
|
7536
8446
|
if (controlledView == null) setInternalView(next);
|
|
@@ -7555,7 +8465,9 @@ var Calendar = (props) => {
|
|
|
7555
8465
|
const focusedDate = (controlledFocusedDate != null ? toDate2(controlledFocusedDate) : internalDate) || startOfDay(nowWall);
|
|
7556
8466
|
const stepFor = useCallback5(
|
|
7557
8467
|
(dir) => {
|
|
7558
|
-
if (view === "week" || view === "agenda"
|
|
8468
|
+
if (view === "week" || view === "agenda" || view === "resource") {
|
|
8469
|
+
return addDays(focusedDate, dir * 7);
|
|
8470
|
+
}
|
|
7559
8471
|
if (view === "day") return addDays(focusedDate, dir);
|
|
7560
8472
|
return addMonths(focusedDate, dir);
|
|
7561
8473
|
},
|
|
@@ -7582,7 +8494,7 @@ var Calendar = (props) => {
|
|
|
7582
8494
|
rangeEnd: endOfDay(flat[flat.length - 1])
|
|
7583
8495
|
};
|
|
7584
8496
|
}
|
|
7585
|
-
if (view === "week") {
|
|
8497
|
+
if (view === "week" || view === "resource") {
|
|
7586
8498
|
const days = buildWeekDays(focusedDate, weekStartsOn, hideWeekends);
|
|
7587
8499
|
return {
|
|
7588
8500
|
weeks: null,
|
|
@@ -7655,14 +8567,18 @@ var Calendar = (props) => {
|
|
|
7655
8567
|
);
|
|
7656
8568
|
const normalized = useMemo5(
|
|
7657
8569
|
() => (events || []).map((raw, index) => {
|
|
7658
|
-
const
|
|
7659
|
-
const
|
|
8570
|
+
const sourceStart = toDate2(resolveField(raw, fields.start));
|
|
8571
|
+
const sourceEnd = toDate2(resolveField(raw, fields.end));
|
|
8572
|
+
const start = toWallClock(sourceStart, timeZone);
|
|
8573
|
+
const endRaw = toWallClock(sourceEnd, timeZone);
|
|
7660
8574
|
const id = resolveField(raw, fields.id);
|
|
7661
8575
|
return {
|
|
7662
8576
|
key: id != null ? String(id) : `evt-${index}`,
|
|
7663
8577
|
id,
|
|
7664
8578
|
start,
|
|
7665
8579
|
end: endRaw || start,
|
|
8580
|
+
sourceStart,
|
|
8581
|
+
sourceEnd: sourceEnd || sourceStart,
|
|
7666
8582
|
title: resolveField(raw, fields.title),
|
|
7667
8583
|
subtitle: resolveField(raw, fields.subtitle),
|
|
7668
8584
|
color: resolveField(raw, fields.color),
|
|
@@ -7699,13 +8615,60 @@ var Calendar = (props) => {
|
|
|
7699
8615
|
}, [rangeKey]);
|
|
7700
8616
|
const title = useMemo5(() => {
|
|
7701
8617
|
if (view === "day") return formatDayTitle(focusedDate);
|
|
7702
|
-
if (view === "week" || view === "agenda") {
|
|
7703
|
-
const days = buildWeekDays(focusedDate, weekStartsOn, view
|
|
8618
|
+
if (view === "week" || view === "agenda" || view === "resource") {
|
|
8619
|
+
const days = buildWeekDays(focusedDate, weekStartsOn, view !== "agenda" && hideWeekends);
|
|
7704
8620
|
return formatRangeTitle(days[0], days[days.length - 1]);
|
|
7705
8621
|
}
|
|
7706
8622
|
return formatMonthTitle(focusedDate);
|
|
7707
8623
|
}, [view, focusedDate, weekStartsOn, hideWeekends]);
|
|
7708
|
-
const
|
|
8624
|
+
const handleReschedulePreset = useCallback5(
|
|
8625
|
+
(event, option) => {
|
|
8626
|
+
const target = resolveRescheduleTarget(
|
|
8627
|
+
{ start: event.sourceStart, end: event.sourceEnd },
|
|
8628
|
+
option,
|
|
8629
|
+
event
|
|
8630
|
+
);
|
|
8631
|
+
if (target && onEventReschedule) onEventReschedule(event.raw, target, event);
|
|
8632
|
+
},
|
|
8633
|
+
[onEventReschedule]
|
|
8634
|
+
);
|
|
8635
|
+
const handleReschedulePick = useCallback5(
|
|
8636
|
+
(event, value) => {
|
|
8637
|
+
const newStart = applyDatePick(event.sourceStart, value);
|
|
8638
|
+
if (!newStart) return;
|
|
8639
|
+
const target = rescheduleToStart({ start: event.sourceStart, end: event.sourceEnd }, newStart);
|
|
8640
|
+
if (target && onEventReschedule) onEventReschedule(event.raw, target, event);
|
|
8641
|
+
},
|
|
8642
|
+
[onEventReschedule]
|
|
8643
|
+
);
|
|
8644
|
+
const reschedule = useMemo5(() => {
|
|
8645
|
+
if (!rescheduleOptions) return null;
|
|
8646
|
+
return {
|
|
8647
|
+
options: normalizeRescheduleOptions(rescheduleOptions),
|
|
8648
|
+
onPreset: handleReschedulePreset,
|
|
8649
|
+
onPick: handleReschedulePick
|
|
8650
|
+
};
|
|
8651
|
+
}, [rescheduleOptions, handleReschedulePreset, handleReschedulePick]);
|
|
8652
|
+
const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
|
|
8653
|
+
const chipProps = {
|
|
8654
|
+
overlayMode,
|
|
8655
|
+
renderEventDetail,
|
|
8656
|
+
onEventClick,
|
|
8657
|
+
labels,
|
|
8658
|
+
monthEventStyle: safeMonthEventStyle,
|
|
8659
|
+
monthEventMaxChars,
|
|
8660
|
+
reschedule
|
|
8661
|
+
};
|
|
8662
|
+
const resourceLaneData = useMemo5(() => {
|
|
8663
|
+
if (view !== "resource") return null;
|
|
8664
|
+
return buildResourceLanes(queried, {
|
|
8665
|
+
resources,
|
|
8666
|
+
resourceLabels,
|
|
8667
|
+
getId: (e) => resolveResourceId(e.raw, resourceField),
|
|
8668
|
+
showUnassignedLane,
|
|
8669
|
+
unassignedLabel: labels.unassigned
|
|
8670
|
+
});
|
|
8671
|
+
}, [view, queried, resources, resourceLabels, resourceField, showUnassignedLane, labels]);
|
|
7709
8672
|
const timeZoneOptions = useMemo5(() => {
|
|
7710
8673
|
if (!showTimeZoneSelect) return null;
|
|
7711
8674
|
const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
|
|
@@ -7747,7 +8710,7 @@ var Calendar = (props) => {
|
|
|
7747
8710
|
);
|
|
7748
8711
|
let body;
|
|
7749
8712
|
if (loading) {
|
|
7750
|
-
body = renderLoadingState ? renderLoadingState({}) : /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", justify: "center" }, /* @__PURE__ */ React14.createElement(
|
|
8713
|
+
body = renderLoadingState ? renderLoadingState({}) : /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", justify: "center" }, /* @__PURE__ */ React14.createElement(LoadingSpinner3, { label: labels.loading }));
|
|
7751
8714
|
} else if (error) {
|
|
7752
8715
|
body = renderErrorState ? renderErrorState({ error }) : /* @__PURE__ */ React14.createElement(Alert4, { title: labels.errorTitle, variant: "error" }, typeof error === "string" ? error : labels.errorMessage);
|
|
7753
8716
|
} else if (view === "month") {
|
|
@@ -7766,6 +8729,19 @@ var Calendar = (props) => {
|
|
|
7766
8729
|
labels
|
|
7767
8730
|
}
|
|
7768
8731
|
);
|
|
8732
|
+
} else if (view === "resource") {
|
|
8733
|
+
body = /* @__PURE__ */ React14.createElement(
|
|
8734
|
+
ResourceView,
|
|
8735
|
+
{
|
|
8736
|
+
days: gridDays,
|
|
8737
|
+
now: nowWall,
|
|
8738
|
+
lanes: resourceLaneData,
|
|
8739
|
+
maxEventsPerDay,
|
|
8740
|
+
chipProps,
|
|
8741
|
+
labels,
|
|
8742
|
+
renderEmptyState
|
|
8743
|
+
}
|
|
8744
|
+
);
|
|
7769
8745
|
} else if (view === "week" || view === "day") {
|
|
7770
8746
|
body = /* @__PURE__ */ React14.createElement(
|
|
7771
8747
|
TimeGridView,
|
|
@@ -7795,9 +8771,696 @@ var Calendar = (props) => {
|
|
|
7795
8771
|
}
|
|
7796
8772
|
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "sm" }, toolbar, body);
|
|
7797
8773
|
};
|
|
8774
|
+
Calendar.displayName = "Calendar";
|
|
8775
|
+
|
|
8776
|
+
// src/filter/FilterBuilder.jsx
|
|
8777
|
+
import React15, { useMemo as useMemo6, useState as useState7 } from "react";
|
|
8778
|
+
import {
|
|
8779
|
+
Box as Box7,
|
|
8780
|
+
Button as Button9,
|
|
8781
|
+
DateInput as DateInput5,
|
|
8782
|
+
Flex as Flex10,
|
|
8783
|
+
Input as Input3,
|
|
8784
|
+
MultiSelect as MultiSelect4,
|
|
8785
|
+
NumberInput as NumberInput3,
|
|
8786
|
+
Select as Select7,
|
|
8787
|
+
Text as Text8,
|
|
8788
|
+
Tile as Tile6
|
|
8789
|
+
} from "@hubspot/ui-extensions";
|
|
8790
|
+
|
|
8791
|
+
// src/filter/filterTree.js
|
|
8792
|
+
var NUMERIC_OPERATORS = ["EQ", "NEQ", "GT", "GTE", "LT", "LTE", "BETWEEN", "HAS_PROPERTY", "NOT_HAS_PROPERTY"];
|
|
8793
|
+
var FILTER_OPERATORS = {
|
|
8794
|
+
string: ["EQ", "NEQ", "CONTAINS_TOKEN", "NOT_CONTAINS_TOKEN", "HAS_PROPERTY", "NOT_HAS_PROPERTY"],
|
|
8795
|
+
number: NUMERIC_OPERATORS,
|
|
8796
|
+
date: NUMERIC_OPERATORS,
|
|
8797
|
+
datetime: NUMERIC_OPERATORS,
|
|
8798
|
+
enum: ["IN", "NOT_IN", "HAS_PROPERTY", "NOT_HAS_PROPERTY"],
|
|
8799
|
+
bool: ["EQ"]
|
|
8800
|
+
};
|
|
8801
|
+
var ALL_OPERATORS = [...new Set(Object.values(FILTER_OPERATORS).flat())];
|
|
8802
|
+
var BASE_OPERATOR_LABELS = {
|
|
8803
|
+
EQ: "is equal to",
|
|
8804
|
+
NEQ: "is not equal to",
|
|
8805
|
+
CONTAINS_TOKEN: "contains",
|
|
8806
|
+
NOT_CONTAINS_TOKEN: "doesn't contain",
|
|
8807
|
+
GT: "is greater than",
|
|
8808
|
+
GTE: "is greater than or equal to",
|
|
8809
|
+
LT: "is less than",
|
|
8810
|
+
LTE: "is less than or equal to",
|
|
8811
|
+
BETWEEN: "is between",
|
|
8812
|
+
IN: "is any of",
|
|
8813
|
+
NOT_IN: "is none of",
|
|
8814
|
+
HAS_PROPERTY: "is known",
|
|
8815
|
+
NOT_HAS_PROPERTY: "is unknown"
|
|
8816
|
+
};
|
|
8817
|
+
var DATE_OPERATOR_LABELS = {
|
|
8818
|
+
EQ: "is",
|
|
8819
|
+
NEQ: "is not",
|
|
8820
|
+
GT: "is after",
|
|
8821
|
+
GTE: "is on or after",
|
|
8822
|
+
LT: "is before",
|
|
8823
|
+
LTE: "is on or before"
|
|
8824
|
+
};
|
|
8825
|
+
var getOperatorOptions = (type, labelOverrides) => {
|
|
8826
|
+
const operators = FILTER_OPERATORS[type] || [];
|
|
8827
|
+
const dateLabels = type === "date" || type === "datetime" ? DATE_OPERATOR_LABELS : null;
|
|
8828
|
+
return operators.map((operator) => ({
|
|
8829
|
+
label: (labelOverrides == null ? void 0 : labelOverrides[operator]) ?? (dateLabels == null ? void 0 : dateLabels[operator]) ?? BASE_OPERATOR_LABELS[operator] ?? operator,
|
|
8830
|
+
value: operator
|
|
8831
|
+
}));
|
|
8832
|
+
};
|
|
8833
|
+
var operatorExpectsValue = (operator) => operator !== "HAS_PROPERTY" && operator !== "NOT_HAS_PROPERTY";
|
|
8834
|
+
var operatorExpectsHighValue = (operator) => operator === "BETWEEN";
|
|
8835
|
+
var operatorExpectsValues = (operator) => operator === "IN" || operator === "NOT_IN";
|
|
8836
|
+
var isGroupNode = (node) => node != null && node.type === "group";
|
|
8837
|
+
var isConditionNode = (node) => node != null && node.type === "condition";
|
|
8838
|
+
var createCondition = (property = "", operator = "", value, highValue) => {
|
|
8839
|
+
const node = { type: "condition", property, operator };
|
|
8840
|
+
if (value !== void 0) node.value = value;
|
|
8841
|
+
if (highValue !== void 0) node.highValue = highValue;
|
|
8842
|
+
return node;
|
|
8843
|
+
};
|
|
8844
|
+
var createGroup = (operator = "AND", filters = []) => ({
|
|
8845
|
+
type: "group",
|
|
8846
|
+
operator,
|
|
8847
|
+
filters
|
|
8848
|
+
});
|
|
8849
|
+
var getNodeAtPath = (tree, path) => {
|
|
8850
|
+
let node = tree;
|
|
8851
|
+
for (const index of path || []) {
|
|
8852
|
+
if (!isGroupNode(node) || !Array.isArray(node.filters)) return void 0;
|
|
8853
|
+
node = node.filters[index];
|
|
8854
|
+
if (node === void 0) return void 0;
|
|
8855
|
+
}
|
|
8856
|
+
return node;
|
|
8857
|
+
};
|
|
8858
|
+
var updateNodeAtPath = (node, path, fn) => {
|
|
8859
|
+
if (!path || path.length === 0) return fn(node);
|
|
8860
|
+
if (!isGroupNode(node) || !Array.isArray(node.filters)) {
|
|
8861
|
+
throw new Error("filterTree: path descends into a non-group node");
|
|
8862
|
+
}
|
|
8863
|
+
const [index, ...rest] = path;
|
|
8864
|
+
if (typeof index !== "number" || index < 0 || index >= node.filters.length) {
|
|
8865
|
+
throw new Error(`filterTree: no filter at index ${index} (group has ${node.filters.length})`);
|
|
8866
|
+
}
|
|
8867
|
+
const filters = node.filters.slice();
|
|
8868
|
+
filters[index] = updateNodeAtPath(filters[index], rest, fn);
|
|
8869
|
+
return { ...node, filters };
|
|
8870
|
+
};
|
|
8871
|
+
var addFilter = (tree, path, node) => updateNodeAtPath(tree, path, (group) => {
|
|
8872
|
+
if (!isGroupNode(group)) {
|
|
8873
|
+
throw new Error("filterTree: addFilter path must point at a group node");
|
|
8874
|
+
}
|
|
8875
|
+
return { ...group, filters: [...group.filters || [], node] };
|
|
8876
|
+
});
|
|
8877
|
+
var updateFilter = (tree, path, patch) => updateNodeAtPath(
|
|
8878
|
+
tree,
|
|
8879
|
+
path,
|
|
8880
|
+
(node) => typeof patch === "function" ? patch(node) : { ...node, ...patch }
|
|
8881
|
+
);
|
|
8882
|
+
var removeFilter = (tree, path, options = {}) => {
|
|
8883
|
+
if (!path || path.length === 0) {
|
|
8884
|
+
throw new Error("filterTree: cannot remove the root group");
|
|
8885
|
+
}
|
|
8886
|
+
const doRemove = (currentTree, targetPath) => {
|
|
8887
|
+
const parentPath = targetPath.slice(0, -1);
|
|
8888
|
+
const index = targetPath[targetPath.length - 1];
|
|
8889
|
+
return updateNodeAtPath(currentTree, parentPath, (group) => {
|
|
8890
|
+
if (!isGroupNode(group)) {
|
|
8891
|
+
throw new Error("filterTree: removeFilter parent is not a group node");
|
|
8892
|
+
}
|
|
8893
|
+
if (typeof index !== "number" || index < 0 || index >= group.filters.length) {
|
|
8894
|
+
throw new Error(`filterTree: no filter at index ${index} (group has ${group.filters.length})`);
|
|
8895
|
+
}
|
|
8896
|
+
return { ...group, filters: group.filters.filter((_, i) => i !== index) };
|
|
8897
|
+
});
|
|
8898
|
+
};
|
|
8899
|
+
let next = doRemove(tree, path);
|
|
8900
|
+
if (options.pruneEmptyGroups) {
|
|
8901
|
+
let parentPath = path.slice(0, -1);
|
|
8902
|
+
while (parentPath.length > 0) {
|
|
8903
|
+
const parent = getNodeAtPath(next, parentPath);
|
|
8904
|
+
if (!isGroupNode(parent) || parent.filters.length > 0) break;
|
|
8905
|
+
next = doRemove(next, parentPath);
|
|
8906
|
+
parentPath = parentPath.slice(0, -1);
|
|
8907
|
+
}
|
|
8908
|
+
}
|
|
8909
|
+
return next;
|
|
8910
|
+
};
|
|
8911
|
+
var cloneNode = (node) => {
|
|
8912
|
+
if (Array.isArray(node)) return node.map(cloneNode);
|
|
8913
|
+
if (node !== null && typeof node === "object") {
|
|
8914
|
+
const copy = {};
|
|
8915
|
+
for (const key of Object.keys(node)) copy[key] = cloneNode(node[key]);
|
|
8916
|
+
return copy;
|
|
8917
|
+
}
|
|
8918
|
+
return node;
|
|
8919
|
+
};
|
|
8920
|
+
var duplicateFilter = (tree, path) => {
|
|
8921
|
+
if (!path || path.length === 0) {
|
|
8922
|
+
throw new Error("filterTree: cannot duplicate the root group");
|
|
8923
|
+
}
|
|
8924
|
+
const parentPath = path.slice(0, -1);
|
|
8925
|
+
const index = path[path.length - 1];
|
|
8926
|
+
return updateNodeAtPath(tree, parentPath, (group) => {
|
|
8927
|
+
if (!isGroupNode(group)) {
|
|
8928
|
+
throw new Error("filterTree: duplicateFilter parent is not a group node");
|
|
8929
|
+
}
|
|
8930
|
+
if (typeof index !== "number" || index < 0 || index >= group.filters.length) {
|
|
8931
|
+
throw new Error(`filterTree: no filter at index ${index} (group has ${group.filters.length})`);
|
|
8932
|
+
}
|
|
8933
|
+
const filters = group.filters.slice();
|
|
8934
|
+
filters.splice(index + 1, 0, cloneNode(group.filters[index]));
|
|
8935
|
+
return { ...group, filters };
|
|
8936
|
+
});
|
|
8937
|
+
};
|
|
8938
|
+
var countConditions = (node) => {
|
|
8939
|
+
if (isConditionNode(node)) return 1;
|
|
8940
|
+
if (!isGroupNode(node) || !Array.isArray(node.filters)) return 0;
|
|
8941
|
+
return node.filters.reduce((sum, child) => sum + countConditions(child), 0);
|
|
8942
|
+
};
|
|
8943
|
+
var changeConditionProperty = (condition, property) => {
|
|
8944
|
+
const name = typeof property === "string" ? property : (property == null ? void 0 : property.name) ?? "";
|
|
8945
|
+
const type = typeof property === "object" && property !== null ? property.type : void 0;
|
|
8946
|
+
const operators = FILTER_OPERATORS[type] || [];
|
|
8947
|
+
const operator = operators.includes(condition == null ? void 0 : condition.operator) ? condition.operator : operators[0] ?? "";
|
|
8948
|
+
const next = { type: "condition", property: name, operator };
|
|
8949
|
+
if (operatorExpectsValues(operator)) next.value = [];
|
|
8950
|
+
return next;
|
|
8951
|
+
};
|
|
8952
|
+
var changeConditionOperator = (condition, operator) => {
|
|
8953
|
+
const next = { type: "condition", property: (condition == null ? void 0 : condition.property) ?? "", operator };
|
|
8954
|
+
if (!operatorExpectsValue(operator)) return next;
|
|
8955
|
+
if (operatorExpectsValues(operator)) {
|
|
8956
|
+
next.value = Array.isArray(condition == null ? void 0 : condition.value) ? condition.value : [];
|
|
8957
|
+
return next;
|
|
8958
|
+
}
|
|
8959
|
+
if ((condition == null ? void 0 : condition.value) !== void 0 && !Array.isArray(condition.value)) {
|
|
8960
|
+
next.value = condition.value;
|
|
8961
|
+
}
|
|
8962
|
+
if (operatorExpectsHighValue(operator) && (condition == null ? void 0 : condition.highValue) !== void 0) {
|
|
8963
|
+
next.highValue = condition.highValue;
|
|
8964
|
+
}
|
|
8965
|
+
return next;
|
|
8966
|
+
};
|
|
8967
|
+
var isMissing = (value) => value == null || value === "";
|
|
8968
|
+
var validateTree = (tree, properties) => {
|
|
8969
|
+
const errors = [];
|
|
8970
|
+
const byName = Array.isArray(properties) ? new Map(properties.map((property) => [property.name, property])) : null;
|
|
8971
|
+
if (!isGroupNode(tree)) {
|
|
8972
|
+
return { valid: false, errors: [{ path: [], message: "Root must be a group node." }] };
|
|
8973
|
+
}
|
|
8974
|
+
const visitCondition = (node, path) => {
|
|
8975
|
+
if (!node.property) {
|
|
8976
|
+
errors.push({ path, message: "Condition is missing a property." });
|
|
8977
|
+
return;
|
|
8978
|
+
}
|
|
8979
|
+
let type;
|
|
8980
|
+
if (byName) {
|
|
8981
|
+
const def = byName.get(node.property);
|
|
8982
|
+
if (!def) {
|
|
8983
|
+
errors.push({ path, message: `Unknown property "${node.property}".` });
|
|
8984
|
+
return;
|
|
8985
|
+
}
|
|
8986
|
+
type = def.type;
|
|
8987
|
+
}
|
|
8988
|
+
const allowed = type !== void 0 ? FILTER_OPERATORS[type] || [] : ALL_OPERATORS;
|
|
8989
|
+
if (!node.operator) {
|
|
8990
|
+
errors.push({ path, message: "Condition is missing an operator." });
|
|
8991
|
+
return;
|
|
8992
|
+
}
|
|
8993
|
+
if (!allowed.includes(node.operator)) {
|
|
8994
|
+
errors.push({
|
|
8995
|
+
path,
|
|
8996
|
+
message: type !== void 0 ? `Operator "${node.operator}" is not valid for type "${type}".` : `Unknown operator "${node.operator}".`
|
|
8997
|
+
});
|
|
8998
|
+
return;
|
|
8999
|
+
}
|
|
9000
|
+
if (!operatorExpectsValue(node.operator)) return;
|
|
9001
|
+
if (operatorExpectsValues(node.operator)) {
|
|
9002
|
+
if (!Array.isArray(node.value) || node.value.length === 0) {
|
|
9003
|
+
errors.push({ path, message: `Operator "${node.operator}" requires at least one value.` });
|
|
9004
|
+
}
|
|
9005
|
+
return;
|
|
9006
|
+
}
|
|
9007
|
+
if (isMissing(node.value)) {
|
|
9008
|
+
errors.push({ path, message: `Operator "${node.operator}" requires a value.` });
|
|
9009
|
+
}
|
|
9010
|
+
if (operatorExpectsHighValue(node.operator) && isMissing(node.highValue)) {
|
|
9011
|
+
errors.push({ path, message: 'Operator "BETWEEN" requires an upper bound (highValue).' });
|
|
9012
|
+
}
|
|
9013
|
+
};
|
|
9014
|
+
const visit = (node, path) => {
|
|
9015
|
+
if (node == null || typeof node !== "object") {
|
|
9016
|
+
errors.push({ path, message: "Filter node must be an object." });
|
|
9017
|
+
return;
|
|
9018
|
+
}
|
|
9019
|
+
if (node.type === "group") {
|
|
9020
|
+
if (node.operator !== "AND" && node.operator !== "OR") {
|
|
9021
|
+
errors.push({ path, message: `Group operator must be "AND" or "OR" (got "${node.operator}").` });
|
|
9022
|
+
}
|
|
9023
|
+
if (!Array.isArray(node.filters)) {
|
|
9024
|
+
errors.push({ path, message: "Group filters must be an array." });
|
|
9025
|
+
return;
|
|
9026
|
+
}
|
|
9027
|
+
if (node.filters.length === 0 && path.length > 0) {
|
|
9028
|
+
errors.push({ path, message: "Group has no filters." });
|
|
9029
|
+
}
|
|
9030
|
+
node.filters.forEach((child, index) => visit(child, [...path, index]));
|
|
9031
|
+
return;
|
|
9032
|
+
}
|
|
9033
|
+
if (node.type === "condition") {
|
|
9034
|
+
visitCondition(node, path);
|
|
9035
|
+
return;
|
|
9036
|
+
}
|
|
9037
|
+
errors.push({ path, message: `Unknown node type "${node.type}".` });
|
|
9038
|
+
};
|
|
9039
|
+
visit(tree, []);
|
|
9040
|
+
return { valid: errors.length === 0, errors };
|
|
9041
|
+
};
|
|
9042
|
+
var isDateValueObject4 = (value) => value != null && typeof value === "object" && typeof value.year === "number" && typeof value.month === "number" && typeof value.date === "number";
|
|
9043
|
+
var dateValueToTimestamp = (dateObj) => new Date(dateObj.year, dateObj.month, dateObj.date).getTime();
|
|
9044
|
+
var coerceCrmValue = (value) => {
|
|
9045
|
+
if (isDateValueObject4(value)) return dateValueToTimestamp(value);
|
|
9046
|
+
if (typeof value === "boolean") return String(value);
|
|
9047
|
+
return value;
|
|
9048
|
+
};
|
|
9049
|
+
var conditionToCrmFilter = (condition, options = {}) => {
|
|
9050
|
+
const { coerceValues = true } = options;
|
|
9051
|
+
if (!isConditionNode(condition)) {
|
|
9052
|
+
throw new Error("filterTree: conditionToCrmFilter expects a condition node");
|
|
9053
|
+
}
|
|
9054
|
+
if (!condition.property) {
|
|
9055
|
+
throw new Error("filterTree: condition is missing a property");
|
|
9056
|
+
}
|
|
9057
|
+
if (!condition.operator) {
|
|
9058
|
+
throw new Error(`filterTree: condition on "${condition.property}" is missing an operator`);
|
|
9059
|
+
}
|
|
9060
|
+
const coerce = coerceValues ? coerceCrmValue : (value) => value;
|
|
9061
|
+
const filter = { propertyName: condition.property, operator: condition.operator };
|
|
9062
|
+
if (!operatorExpectsValue(condition.operator)) return filter;
|
|
9063
|
+
if (operatorExpectsValues(condition.operator)) {
|
|
9064
|
+
const values = Array.isArray(condition.value) ? condition.value : condition.value == null ? [] : [condition.value];
|
|
9065
|
+
filter.values = values.map(coerce);
|
|
9066
|
+
return filter;
|
|
9067
|
+
}
|
|
9068
|
+
filter.value = coerce(condition.value);
|
|
9069
|
+
if (operatorExpectsHighValue(condition.operator)) {
|
|
9070
|
+
filter.highValue = coerce(condition.highValue);
|
|
9071
|
+
}
|
|
9072
|
+
return filter;
|
|
9073
|
+
};
|
|
9074
|
+
var nodeToDnf = (node, path, options) => {
|
|
9075
|
+
if (isConditionNode(node)) {
|
|
9076
|
+
return [[conditionToCrmFilter(node, options)]];
|
|
9077
|
+
}
|
|
9078
|
+
if (!isGroupNode(node)) {
|
|
9079
|
+
throw new Error(`filterTree: unknown node type "${node == null ? void 0 : node.type}" at [${path.join(", ")}]`);
|
|
9080
|
+
}
|
|
9081
|
+
if (!Array.isArray(node.filters) || node.filters.length === 0) {
|
|
9082
|
+
throw new Error(
|
|
9083
|
+
`filterTree: empty group at [${path.join(", ")}] cannot be converted \u2014 remove it or add a condition (run validateTree first)`
|
|
9084
|
+
);
|
|
9085
|
+
}
|
|
9086
|
+
const childDnfs = node.filters.map((child, index) => nodeToDnf(child, [...path, index], options));
|
|
9087
|
+
if (node.operator === "OR") {
|
|
9088
|
+
return childDnfs.flat();
|
|
9089
|
+
}
|
|
9090
|
+
return childDnfs.reduce(
|
|
9091
|
+
(acc, childDnf) => {
|
|
9092
|
+
const out = [];
|
|
9093
|
+
for (const left of acc) {
|
|
9094
|
+
for (const right of childDnf) out.push([...left, ...right]);
|
|
9095
|
+
}
|
|
9096
|
+
return out;
|
|
9097
|
+
},
|
|
9098
|
+
[[]]
|
|
9099
|
+
);
|
|
9100
|
+
};
|
|
9101
|
+
var toCrmSearchFilterGroups = (tree, options = {}) => {
|
|
9102
|
+
const {
|
|
9103
|
+
maxGroups = 5,
|
|
9104
|
+
maxFiltersPerGroup = 6,
|
|
9105
|
+
maxTotalFilters = 18,
|
|
9106
|
+
enforceLimits = true,
|
|
9107
|
+
coerceValues = true
|
|
9108
|
+
} = options;
|
|
9109
|
+
if (!isGroupNode(tree)) {
|
|
9110
|
+
throw new Error("filterTree: toCrmSearchFilterGroups expects a group node at the root");
|
|
9111
|
+
}
|
|
9112
|
+
if (!Array.isArray(tree.filters) || tree.filters.length === 0) {
|
|
9113
|
+
return { filterGroups: [] };
|
|
9114
|
+
}
|
|
9115
|
+
const conjunctions = nodeToDnf(tree, [], { coerceValues });
|
|
9116
|
+
if (enforceLimits) {
|
|
9117
|
+
if (conjunctions.length > maxGroups) {
|
|
9118
|
+
throw new Error(
|
|
9119
|
+
`filterTree: tree expands to ${conjunctions.length} filterGroups; HubSpot CRM search allows at most ${maxGroups}. Reduce OR branches (each OR nested under an AND multiplies groups).`
|
|
9120
|
+
);
|
|
9121
|
+
}
|
|
9122
|
+
const oversized = conjunctions.findIndex((filters) => filters.length > maxFiltersPerGroup);
|
|
9123
|
+
if (oversized !== -1) {
|
|
9124
|
+
throw new Error(
|
|
9125
|
+
`filterTree: filterGroup ${oversized} has ${conjunctions[oversized].length} filters; HubSpot CRM search allows at most ${maxFiltersPerGroup} per group.`
|
|
9126
|
+
);
|
|
9127
|
+
}
|
|
9128
|
+
const total = conjunctions.reduce((sum, filters) => sum + filters.length, 0);
|
|
9129
|
+
if (total > maxTotalFilters) {
|
|
9130
|
+
throw new Error(
|
|
9131
|
+
`filterTree: tree expands to ${total} total filters; HubSpot CRM search allows at most ${maxTotalFilters}.`
|
|
9132
|
+
);
|
|
9133
|
+
}
|
|
9134
|
+
}
|
|
9135
|
+
return { filterGroups: conjunctions.map((filters) => ({ filters })) };
|
|
9136
|
+
};
|
|
9137
|
+
|
|
9138
|
+
// src/filter/FilterBuilder.jsx
|
|
9139
|
+
var DEFAULT_LABELS6 = {
|
|
9140
|
+
addFilter: "Add filter",
|
|
9141
|
+
addGroup: "Add filter group",
|
|
9142
|
+
remove: "Remove filter",
|
|
9143
|
+
removeGroup: "Delete group",
|
|
9144
|
+
cloneGroup: "Clone group",
|
|
9145
|
+
group: "Group",
|
|
9146
|
+
and: "AND",
|
|
9147
|
+
or: "OR",
|
|
9148
|
+
property: "Select a property",
|
|
9149
|
+
operator: "Select an operator",
|
|
9150
|
+
value: "Enter a value",
|
|
9151
|
+
values: "Select values",
|
|
9152
|
+
between: "and",
|
|
9153
|
+
empty: "No filters yet.",
|
|
9154
|
+
true: "True",
|
|
9155
|
+
false: "False"
|
|
9156
|
+
};
|
|
9157
|
+
var GROUP_OPERATOR_OPTIONS = (labels) => [
|
|
9158
|
+
{ label: labels.and, value: "AND" },
|
|
9159
|
+
{ label: labels.or, value: "OR" }
|
|
9160
|
+
];
|
|
9161
|
+
var normalizeTree = (tree) => isGroupNode(tree) ? tree : createGroup("AND", []);
|
|
9162
|
+
var pathName = (prefix, path, suffix) => `${prefix}-${path.length ? path.join("-") : "root"}-${suffix}`;
|
|
9163
|
+
var ValueEditor = ({ condition, propertyDef, path, namePrefix, labels, readOnly, onPatch }) => {
|
|
9164
|
+
const { operator } = condition;
|
|
9165
|
+
if (!operator || !operatorExpectsValue(operator)) return null;
|
|
9166
|
+
const type = (propertyDef == null ? void 0 : propertyDef.type) || "string";
|
|
9167
|
+
const setValue = (value) => onPatch(path, { value });
|
|
9168
|
+
const setHighValue = (highValue) => onPatch(path, { highValue });
|
|
9169
|
+
if (operatorExpectsValues(operator)) {
|
|
9170
|
+
return /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9171
|
+
MultiSelect4,
|
|
9172
|
+
{
|
|
9173
|
+
label: "",
|
|
9174
|
+
name: pathName(namePrefix, path, "value"),
|
|
9175
|
+
placeholder: labels.values,
|
|
9176
|
+
options: (propertyDef == null ? void 0 : propertyDef.options) || [],
|
|
9177
|
+
value: Array.isArray(condition.value) ? condition.value : [],
|
|
9178
|
+
readOnly,
|
|
9179
|
+
onChange: setValue
|
|
9180
|
+
}
|
|
9181
|
+
));
|
|
9182
|
+
}
|
|
9183
|
+
if (type === "bool") {
|
|
9184
|
+
return /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9185
|
+
Select7,
|
|
9186
|
+
{
|
|
9187
|
+
label: "",
|
|
9188
|
+
name: pathName(namePrefix, path, "value"),
|
|
9189
|
+
placeholder: labels.value,
|
|
9190
|
+
options: [
|
|
9191
|
+
{ label: labels.true, value: "true" },
|
|
9192
|
+
{ label: labels.false, value: "false" }
|
|
9193
|
+
],
|
|
9194
|
+
value: condition.value,
|
|
9195
|
+
readOnly,
|
|
9196
|
+
onChange: setValue
|
|
9197
|
+
}
|
|
9198
|
+
));
|
|
9199
|
+
}
|
|
9200
|
+
if (type === "enum") {
|
|
9201
|
+
return /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9202
|
+
Select7,
|
|
9203
|
+
{
|
|
9204
|
+
label: "",
|
|
9205
|
+
name: pathName(namePrefix, path, "value"),
|
|
9206
|
+
placeholder: labels.value,
|
|
9207
|
+
options: (propertyDef == null ? void 0 : propertyDef.options) || [],
|
|
9208
|
+
value: condition.value,
|
|
9209
|
+
readOnly,
|
|
9210
|
+
onChange: setValue
|
|
9211
|
+
}
|
|
9212
|
+
));
|
|
9213
|
+
}
|
|
9214
|
+
const isBetween = operatorExpectsHighValue(operator);
|
|
9215
|
+
if (type === "number") {
|
|
9216
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9217
|
+
NumberInput3,
|
|
9218
|
+
{
|
|
9219
|
+
label: "",
|
|
9220
|
+
name: pathName(namePrefix, path, "value"),
|
|
9221
|
+
placeholder: labels.value,
|
|
9222
|
+
value: condition.value ?? "",
|
|
9223
|
+
readOnly,
|
|
9224
|
+
onChange: setValue
|
|
9225
|
+
}
|
|
9226
|
+
)), isBetween && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Text8, { variant: "microcopy" }, labels.between), /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9227
|
+
NumberInput3,
|
|
9228
|
+
{
|
|
9229
|
+
label: "",
|
|
9230
|
+
name: pathName(namePrefix, path, "high-value"),
|
|
9231
|
+
placeholder: labels.value,
|
|
9232
|
+
value: condition.highValue ?? "",
|
|
9233
|
+
readOnly,
|
|
9234
|
+
onChange: setHighValue
|
|
9235
|
+
}
|
|
9236
|
+
))));
|
|
9237
|
+
}
|
|
9238
|
+
if (type === "date" || type === "datetime") {
|
|
9239
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9240
|
+
DateInput5,
|
|
9241
|
+
{
|
|
9242
|
+
label: "",
|
|
9243
|
+
name: pathName(namePrefix, path, "value"),
|
|
9244
|
+
format: "medium",
|
|
9245
|
+
value: condition.value ?? null,
|
|
9246
|
+
readOnly,
|
|
9247
|
+
onChange: setValue
|
|
9248
|
+
}
|
|
9249
|
+
)), isBetween && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Text8, { variant: "microcopy" }, labels.between), /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9250
|
+
DateInput5,
|
|
9251
|
+
{
|
|
9252
|
+
label: "",
|
|
9253
|
+
name: pathName(namePrefix, path, "high-value"),
|
|
9254
|
+
format: "medium",
|
|
9255
|
+
value: condition.highValue ?? null,
|
|
9256
|
+
readOnly,
|
|
9257
|
+
onChange: setHighValue
|
|
9258
|
+
}
|
|
9259
|
+
))));
|
|
9260
|
+
}
|
|
9261
|
+
return /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9262
|
+
Input3,
|
|
9263
|
+
{
|
|
9264
|
+
label: "",
|
|
9265
|
+
name: pathName(namePrefix, path, "value"),
|
|
9266
|
+
placeholder: labels.value,
|
|
9267
|
+
value: condition.value ?? "",
|
|
9268
|
+
readOnly,
|
|
9269
|
+
onChange: setValue
|
|
9270
|
+
}
|
|
9271
|
+
));
|
|
9272
|
+
};
|
|
9273
|
+
var IconButton = ({ icon, label, onClick, size }) => /* @__PURE__ */ React15.createElement(Button9, { size: "extra-small", variant: "transparent", onClick }, /* @__PURE__ */ React15.createElement(Icon, { name: icon, screenReaderText: label, ...size ? { size } : {} }));
|
|
9274
|
+
var ConditionRow = ({
|
|
9275
|
+
condition,
|
|
9276
|
+
path,
|
|
9277
|
+
properties,
|
|
9278
|
+
propertyOptions,
|
|
9279
|
+
namePrefix,
|
|
9280
|
+
labels,
|
|
9281
|
+
operatorLabels,
|
|
9282
|
+
readOnly,
|
|
9283
|
+
onPropertyChange,
|
|
9284
|
+
onOperatorChange,
|
|
9285
|
+
onPatch,
|
|
9286
|
+
onRemove
|
|
9287
|
+
}) => {
|
|
9288
|
+
const propertyDef = properties.find((property) => property.name === condition.property);
|
|
9289
|
+
const operatorOptions = getOperatorOptions(propertyDef == null ? void 0 : propertyDef.type, operatorLabels);
|
|
9290
|
+
return /* @__PURE__ */ React15.createElement(Flex10, { direction: "row", gap: "xs", align: "center", wrap: "wrap" }, /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9291
|
+
Select7,
|
|
9292
|
+
{
|
|
9293
|
+
label: "",
|
|
9294
|
+
name: pathName(namePrefix, path, "property"),
|
|
9295
|
+
placeholder: labels.property,
|
|
9296
|
+
options: propertyOptions,
|
|
9297
|
+
value: condition.property || void 0,
|
|
9298
|
+
readOnly,
|
|
9299
|
+
onChange: (name) => onPropertyChange(path, name)
|
|
9300
|
+
}
|
|
9301
|
+
)), /* @__PURE__ */ React15.createElement(Box7, { flex: 1 }, /* @__PURE__ */ React15.createElement(
|
|
9302
|
+
Select7,
|
|
9303
|
+
{
|
|
9304
|
+
label: "",
|
|
9305
|
+
name: pathName(namePrefix, path, "operator"),
|
|
9306
|
+
placeholder: labels.operator,
|
|
9307
|
+
options: operatorOptions,
|
|
9308
|
+
value: condition.operator || void 0,
|
|
9309
|
+
readOnly: readOnly || !condition.property,
|
|
9310
|
+
onChange: (operator) => onOperatorChange(path, operator)
|
|
9311
|
+
}
|
|
9312
|
+
)), /* @__PURE__ */ React15.createElement(
|
|
9313
|
+
ValueEditor,
|
|
9314
|
+
{
|
|
9315
|
+
condition,
|
|
9316
|
+
propertyDef,
|
|
9317
|
+
path,
|
|
9318
|
+
namePrefix,
|
|
9319
|
+
labels,
|
|
9320
|
+
readOnly,
|
|
9321
|
+
onPatch
|
|
9322
|
+
}
|
|
9323
|
+
), !readOnly && /* @__PURE__ */ React15.createElement(IconButton, { icon: "remove", label: labels.remove, onClick: () => onRemove(path) }));
|
|
9324
|
+
};
|
|
9325
|
+
var GroupOperatorSeparator = ({ group, path, namePrefix, labels, readOnly, index, onGroupOperatorChange }) => {
|
|
9326
|
+
if (readOnly) {
|
|
9327
|
+
return /* @__PURE__ */ React15.createElement(Text8, { variant: "microcopy", format: { fontWeight: "demibold" } }, group.operator === "OR" ? labels.or : labels.and);
|
|
9328
|
+
}
|
|
9329
|
+
return /* @__PURE__ */ React15.createElement(Box7, { alignSelf: "start" }, /* @__PURE__ */ React15.createElement(
|
|
9330
|
+
Select7,
|
|
9331
|
+
{
|
|
9332
|
+
label: "",
|
|
9333
|
+
name: pathName(namePrefix, path, `separator-${index}`),
|
|
9334
|
+
variant: "transparent",
|
|
9335
|
+
options: GROUP_OPERATOR_OPTIONS(labels),
|
|
9336
|
+
value: group.operator,
|
|
9337
|
+
onChange: (operator) => onGroupOperatorChange(path, operator)
|
|
9338
|
+
}
|
|
9339
|
+
));
|
|
9340
|
+
};
|
|
9341
|
+
var GroupEditor = ({ group, path, depth, ctx }) => {
|
|
9342
|
+
const { labels, maxDepth, readOnly, namePrefix, handlers } = ctx;
|
|
9343
|
+
const isRoot = path.length === 0;
|
|
9344
|
+
const filters = Array.isArray(group.filters) ? group.filters : [];
|
|
9345
|
+
let groupNumber = 0;
|
|
9346
|
+
const children = filters.map((child, index) => {
|
|
9347
|
+
const childPath = [...path, index];
|
|
9348
|
+
if (isGroupNode(child)) groupNumber += 1;
|
|
9349
|
+
const row = isGroupNode(child) ? /* @__PURE__ */ React15.createElement(Tile6, { compact: true }, /* @__PURE__ */ React15.createElement(Flex10, { direction: "column", gap: "xs" }, /* @__PURE__ */ React15.createElement(Flex10, { direction: "row", justify: "between", align: "center" }, /* @__PURE__ */ React15.createElement(Text8, { format: { fontWeight: "demibold" } }, `${labels.group}\xA0${groupNumber}`), !readOnly && /* @__PURE__ */ React15.createElement(Flex10, { direction: "row", gap: "xs", justify: "end" }, /* @__PURE__ */ React15.createElement(
|
|
9350
|
+
IconButton,
|
|
9351
|
+
{
|
|
9352
|
+
icon: "copy",
|
|
9353
|
+
label: labels.cloneGroup,
|
|
9354
|
+
size: "sm",
|
|
9355
|
+
onClick: () => handlers.onDuplicate(childPath)
|
|
9356
|
+
}
|
|
9357
|
+
), /* @__PURE__ */ React15.createElement(
|
|
9358
|
+
IconButton,
|
|
9359
|
+
{
|
|
9360
|
+
icon: "delete",
|
|
9361
|
+
label: labels.removeGroup,
|
|
9362
|
+
size: "sm",
|
|
9363
|
+
onClick: () => handlers.onRemove(childPath)
|
|
9364
|
+
}
|
|
9365
|
+
))), /* @__PURE__ */ React15.createElement(GroupEditor, { group: child, path: childPath, depth: depth + 1, ctx }))) : /* @__PURE__ */ React15.createElement(
|
|
9366
|
+
ConditionRow,
|
|
9367
|
+
{
|
|
9368
|
+
condition: child,
|
|
9369
|
+
path: childPath,
|
|
9370
|
+
properties: ctx.properties,
|
|
9371
|
+
propertyOptions: ctx.propertyOptions,
|
|
9372
|
+
namePrefix,
|
|
9373
|
+
labels,
|
|
9374
|
+
operatorLabels: ctx.operatorLabels,
|
|
9375
|
+
readOnly,
|
|
9376
|
+
onPropertyChange: handlers.onPropertyChange,
|
|
9377
|
+
onOperatorChange: handlers.onOperatorChange,
|
|
9378
|
+
onPatch: handlers.onPatch,
|
|
9379
|
+
onRemove: handlers.onRemove
|
|
9380
|
+
}
|
|
9381
|
+
);
|
|
9382
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, { key: childPath.join("-") }, index > 0 && /* @__PURE__ */ React15.createElement(
|
|
9383
|
+
GroupOperatorSeparator,
|
|
9384
|
+
{
|
|
9385
|
+
group,
|
|
9386
|
+
path,
|
|
9387
|
+
namePrefix,
|
|
9388
|
+
labels,
|
|
9389
|
+
readOnly,
|
|
9390
|
+
index,
|
|
9391
|
+
onGroupOperatorChange: handlers.onGroupOperatorChange
|
|
9392
|
+
}
|
|
9393
|
+
), row);
|
|
9394
|
+
});
|
|
9395
|
+
return (
|
|
9396
|
+
// Nested groups pack tight (row / AND-OR / row reads as one unit, like
|
|
9397
|
+
// HubSpot's builder); the root keeps more air between its sections.
|
|
9398
|
+
/* @__PURE__ */ React15.createElement(Flex10, { direction: "column", gap: isRoot ? "sm" : "xs" }, isRoot && filters.length === 0 && /* @__PURE__ */ React15.createElement(Text8, { variant: "microcopy" }, labels.empty), children, !readOnly && /* @__PURE__ */ React15.createElement(Flex10, { direction: "row", gap: "md", align: "center" }, /* @__PURE__ */ React15.createElement(
|
|
9399
|
+
Button9,
|
|
9400
|
+
{
|
|
9401
|
+
size: "extra-small",
|
|
9402
|
+
variant: "transparent",
|
|
9403
|
+
onClick: () => handlers.onAddCondition(path)
|
|
9404
|
+
},
|
|
9405
|
+
/* @__PURE__ */ React15.createElement(Icon, { name: "add" }),
|
|
9406
|
+
" ",
|
|
9407
|
+
labels.addFilter
|
|
9408
|
+
), depth < maxDepth && /* @__PURE__ */ React15.createElement(
|
|
9409
|
+
Button9,
|
|
9410
|
+
{
|
|
9411
|
+
size: "extra-small",
|
|
9412
|
+
variant: "transparent",
|
|
9413
|
+
onClick: () => handlers.onAddGroup(path)
|
|
9414
|
+
},
|
|
9415
|
+
/* @__PURE__ */ React15.createElement(Icon, { name: "add" }),
|
|
9416
|
+
" ",
|
|
9417
|
+
labels.addGroup
|
|
9418
|
+
)))
|
|
9419
|
+
);
|
|
9420
|
+
};
|
|
9421
|
+
var FilterBuilder = ({
|
|
9422
|
+
properties = [],
|
|
9423
|
+
value,
|
|
9424
|
+
defaultValue,
|
|
9425
|
+
onChange,
|
|
9426
|
+
maxDepth = 2,
|
|
9427
|
+
labels: labelOverrides,
|
|
9428
|
+
operatorLabels,
|
|
9429
|
+
readOnly = false,
|
|
9430
|
+
namePrefix = "filter-builder",
|
|
9431
|
+
...rest
|
|
9432
|
+
}) => {
|
|
9433
|
+
const labels = useMemo6(() => ({ ...DEFAULT_LABELS6, ...labelOverrides || {} }), [labelOverrides]);
|
|
9434
|
+
const [internalTree, setInternalTree] = useState7(() => normalizeTree(defaultValue));
|
|
9435
|
+
const isControlled = value !== void 0;
|
|
9436
|
+
const tree = isControlled ? normalizeTree(value) : internalTree;
|
|
9437
|
+
const propertyOptions = useMemo6(
|
|
9438
|
+
() => properties.map((property) => ({ label: property.label ?? property.name, value: property.name })),
|
|
9439
|
+
[properties]
|
|
9440
|
+
);
|
|
9441
|
+
const commit = (next) => {
|
|
9442
|
+
if (!isControlled) setInternalTree(next);
|
|
9443
|
+
onChange == null ? void 0 : onChange(next);
|
|
9444
|
+
};
|
|
9445
|
+
const handlers = {
|
|
9446
|
+
onAddCondition: (groupPath) => commit(addFilter(tree, groupPath, createCondition())),
|
|
9447
|
+
onAddGroup: (groupPath) => commit(addFilter(tree, groupPath, createGroup("AND", [createCondition()]))),
|
|
9448
|
+
onRemove: (path) => commit(removeFilter(tree, path, { pruneEmptyGroups: true })),
|
|
9449
|
+
onDuplicate: (path) => commit(duplicateFilter(tree, path)),
|
|
9450
|
+
onGroupOperatorChange: (groupPath, operator) => commit(updateFilter(tree, groupPath, { operator })),
|
|
9451
|
+
onPropertyChange: (path, name) => {
|
|
9452
|
+
const def = properties.find((property) => property.name === name);
|
|
9453
|
+
commit(updateFilter(tree, path, (node) => changeConditionProperty(node, def ?? name)));
|
|
9454
|
+
},
|
|
9455
|
+
onOperatorChange: (path, operator) => commit(updateFilter(tree, path, (node) => changeConditionOperator(node, operator))),
|
|
9456
|
+
onPatch: (path, patch) => commit(updateFilter(tree, path, patch))
|
|
9457
|
+
};
|
|
9458
|
+
const ctx = { properties, propertyOptions, labels, operatorLabels, maxDepth, readOnly, namePrefix, handlers };
|
|
9459
|
+
return /* @__PURE__ */ React15.createElement(Flex10, { direction: "column", gap: "sm", ...rest }, /* @__PURE__ */ React15.createElement(GroupEditor, { group: tree, path: [], depth: 1, ctx }));
|
|
9460
|
+
};
|
|
7798
9461
|
|
|
7799
9462
|
// src/common-components/AutoTag.js
|
|
7800
|
-
import
|
|
9463
|
+
import React16 from "react";
|
|
7801
9464
|
import { Tag as Tag6 } from "@hubspot/ui-extensions";
|
|
7802
9465
|
|
|
7803
9466
|
// src/utils/tagVariants.js
|
|
@@ -7996,7 +9659,7 @@ var AutoTag = ({
|
|
|
7996
9659
|
overrides,
|
|
7997
9660
|
fallback
|
|
7998
9661
|
});
|
|
7999
|
-
return
|
|
9662
|
+
return React16.createElement(
|
|
8000
9663
|
Tag6,
|
|
8001
9664
|
{ variant: resolvedVariant, ...props },
|
|
8002
9665
|
displayValue
|
|
@@ -8004,8 +9667,8 @@ var AutoTag = ({
|
|
|
8004
9667
|
};
|
|
8005
9668
|
|
|
8006
9669
|
// src/common-components/AutoStatusTag.js
|
|
8007
|
-
import
|
|
8008
|
-
import { StatusTag as
|
|
9670
|
+
import React17 from "react";
|
|
9671
|
+
import { StatusTag as StatusTag4 } from "@hubspot/ui-extensions";
|
|
8009
9672
|
var AutoStatusTag = ({
|
|
8010
9673
|
value,
|
|
8011
9674
|
status,
|
|
@@ -8021,20 +9684,20 @@ var AutoStatusTag = ({
|
|
|
8021
9684
|
overrides,
|
|
8022
9685
|
fallback
|
|
8023
9686
|
});
|
|
8024
|
-
return
|
|
8025
|
-
|
|
9687
|
+
return React17.createElement(
|
|
9688
|
+
StatusTag4,
|
|
8026
9689
|
{ variant: resolvedVariant, ...props },
|
|
8027
9690
|
displayValue
|
|
8028
9691
|
);
|
|
8029
9692
|
};
|
|
8030
9693
|
|
|
8031
9694
|
// src/common-components/CrmLookupSelect.js
|
|
8032
|
-
import
|
|
8033
|
-
import { MultiSelect as
|
|
9695
|
+
import React19, { useMemo as useMemo8, useState as useState9 } from "react";
|
|
9696
|
+
import { MultiSelect as MultiSelect5, Select as Select8, useDebounce as useDebounce2 } from "@hubspot/ui-extensions";
|
|
8034
9697
|
|
|
8035
9698
|
// src/utils/crmSearchAdapters.js
|
|
8036
|
-
import
|
|
8037
|
-
import { useCrmSearch, Flex as
|
|
9699
|
+
import React18, { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useRef as useRef5, useState as useState8 } from "react";
|
|
9700
|
+
import { useCrmSearch, Flex as Flex11, Text as Text9 } from "@hubspot/ui-extensions";
|
|
8038
9701
|
|
|
8039
9702
|
// src/utils/objectPath.js
|
|
8040
9703
|
var getByPath = (obj, path) => {
|
|
@@ -8151,9 +9814,9 @@ var useCrmSearchDataSource = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) =>
|
|
|
8151
9814
|
error,
|
|
8152
9815
|
mapResponse
|
|
8153
9816
|
} = options;
|
|
8154
|
-
const config =
|
|
9817
|
+
const config = useMemo7(() => buildCrmSearchConfig(params, options), [params, options]);
|
|
8155
9818
|
const response = useCrmSearch(config, format);
|
|
8156
|
-
return
|
|
9819
|
+
return useMemo7(() => {
|
|
8157
9820
|
var _a;
|
|
8158
9821
|
const rows = mapResponse ? mapResponse(response) : normalizeCrmSearchRows(response, { idField: rowIdField, ...row || EMPTY_OBJECT });
|
|
8159
9822
|
const resolvedTotal = typeof totalCount === "function" ? totalCount(response) : totalCount ?? pickTotal(response, rows.length);
|
|
@@ -8191,7 +9854,7 @@ var crmSearchResultToOption = (row, options = EMPTY_OBJECT) => {
|
|
|
8191
9854
|
var useCrmSearchOptions = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
|
|
8192
9855
|
const dataSource = useCrmSearchDataSource(params, options);
|
|
8193
9856
|
const optionConfig = options.option || options;
|
|
8194
|
-
return
|
|
9857
|
+
return useMemo7(() => ({
|
|
8195
9858
|
...dataSource,
|
|
8196
9859
|
options: dataSource.rows.map((row) => crmSearchResultToOption(row, optionConfig))
|
|
8197
9860
|
}), [dataSource, optionConfig]);
|
|
@@ -8320,29 +9983,29 @@ var CrmDataTable = ({
|
|
|
8320
9983
|
...props
|
|
8321
9984
|
}) => {
|
|
8322
9985
|
var _a, _b;
|
|
8323
|
-
const [params, setParams] =
|
|
8324
|
-
const resolvedProperties =
|
|
8325
|
-
const resolvedColumns =
|
|
9986
|
+
const [params, setParams] = useState8({ search: "", filters: {}, sort: null });
|
|
9987
|
+
const resolvedProperties = useMemo7(() => properties, [properties]);
|
|
9988
|
+
const resolvedColumns = useMemo7(
|
|
8326
9989
|
() => columns || inferCrmColumns(resolvedProperties),
|
|
8327
9990
|
[columns, resolvedProperties]
|
|
8328
9991
|
);
|
|
8329
9992
|
const resolvedSearchFields = searchFields || resolvedProperties;
|
|
8330
|
-
const autoFilterFields =
|
|
9993
|
+
const autoFilterFields = useMemo7(
|
|
8331
9994
|
() => normalizeAutoFilterFields(autoFilters, resolvedProperties),
|
|
8332
9995
|
[autoFilters, resolvedProperties]
|
|
8333
9996
|
);
|
|
8334
9997
|
const autoFilterLabelsRef = useRef5({});
|
|
8335
|
-
const defaultPropertyMap =
|
|
9998
|
+
const defaultPropertyMap = useMemo7(
|
|
8336
9999
|
() => Object.fromEntries(resolvedProperties.map((property) => [property, property])),
|
|
8337
10000
|
[resolvedProperties]
|
|
8338
10001
|
);
|
|
8339
10002
|
const effectivePropertyMap = propertyMap || defaultPropertyMap;
|
|
8340
|
-
const resolvedSortMap =
|
|
10003
|
+
const resolvedSortMap = useMemo7(
|
|
8341
10004
|
() => sortMap || ((sort) => crmSortsFromState(sort, effectivePropertyMap)),
|
|
8342
10005
|
[sortMap, effectivePropertyMap]
|
|
8343
10006
|
);
|
|
8344
10007
|
const resolvedMapRecord = mapRecord || defaultCrmMapRecord;
|
|
8345
|
-
const dataSourceOptions =
|
|
10008
|
+
const dataSourceOptions = useMemo7(
|
|
8346
10009
|
() => ({
|
|
8347
10010
|
objectType: resolveCrmObjectType(objectType),
|
|
8348
10011
|
properties: resolvedProperties,
|
|
@@ -8356,15 +10019,15 @@ var CrmDataTable = ({
|
|
|
8356
10019
|
}),
|
|
8357
10020
|
[objectType, resolvedProperties, pageLength, format, filterMap, effectivePropertyMap, resolvedSortMap, rowIdField, resolvedMapRecord]
|
|
8358
10021
|
);
|
|
8359
|
-
const [serverQuerying, setServerQuerying] =
|
|
10022
|
+
const [serverQuerying, setServerQuerying] = useState8(!!serverSide);
|
|
8360
10023
|
const effectiveParams = serverQuerying ? params : EMPTY_CRM_PARAMS;
|
|
8361
10024
|
const dataSource = useCrmSearchDataSource(effectiveParams, dataSourceOptions);
|
|
8362
|
-
const queryKey =
|
|
10025
|
+
const queryKey = useMemo7(
|
|
8363
10026
|
() => stableStringify({ effectiveParams, objectType, properties: resolvedProperties, pageLength }),
|
|
8364
10027
|
[effectiveParams, objectType, resolvedProperties, pageLength]
|
|
8365
10028
|
);
|
|
8366
|
-
const [accumulatedRows, setAccumulatedRows] =
|
|
8367
|
-
const [requestedPage, setRequestedPage] =
|
|
10029
|
+
const [accumulatedRows, setAccumulatedRows] = useState8(EMPTY_ARRAY);
|
|
10030
|
+
const [requestedPage, setRequestedPage] = useState8(1);
|
|
8368
10031
|
const lastQueryKeyRef = useRef5(queryKey);
|
|
8369
10032
|
const loadedRows = accumulatedRows.length ? accumulatedRows : dataSource.data;
|
|
8370
10033
|
useEffect7(() => {
|
|
@@ -8393,7 +10056,7 @@ var CrmDataTable = ({
|
|
|
8393
10056
|
useEffect7(() => {
|
|
8394
10057
|
ensurePageLoaded(requestedPage);
|
|
8395
10058
|
}, [requestedPage, ensurePageLoaded]);
|
|
8396
|
-
const generatedFilters =
|
|
10059
|
+
const generatedFilters = useMemo7(
|
|
8397
10060
|
() => buildAutoFiltersFromRows({
|
|
8398
10061
|
rows: loadedRows,
|
|
8399
10062
|
fields: autoFilterFields,
|
|
@@ -8403,7 +10066,7 @@ var CrmDataTable = ({
|
|
|
8403
10066
|
[loadedRows, autoFilterFields, autoFilterMaxOptions]
|
|
8404
10067
|
);
|
|
8405
10068
|
const resolvedFilters = filters || generatedFilters;
|
|
8406
|
-
const table =
|
|
10069
|
+
const table = React18.createElement(DataTable, {
|
|
8407
10070
|
title: title || `${prettifyPropertyName(objectType)} records`,
|
|
8408
10071
|
data: loadedRows,
|
|
8409
10072
|
loading: dataSource.loading || ((_b = dataSource.response) == null ? void 0 : _b.isRefetching),
|
|
@@ -8427,11 +10090,11 @@ var CrmDataTable = ({
|
|
|
8427
10090
|
const total = dataSource.totalCount;
|
|
8428
10091
|
const capped = typeof total === "number" && total > loadedRows.length;
|
|
8429
10092
|
if (!capped) return table;
|
|
8430
|
-
return
|
|
8431
|
-
|
|
10093
|
+
return React18.createElement(
|
|
10094
|
+
Flex11,
|
|
8432
10095
|
{ direction: "column", gap: "xs" },
|
|
8433
|
-
|
|
8434
|
-
|
|
10096
|
+
React18.createElement(
|
|
10097
|
+
Text9,
|
|
8435
10098
|
{ variant: "microcopy" },
|
|
8436
10099
|
dataSource.hasMore ? `Loaded ${loadedRows.length} of ${total} matching. Use the table pagination to load more CRM results.` : `Showing ${loadedRows.length} of ${total} matching. Refine your search or filters to narrow the results.`
|
|
8437
10100
|
),
|
|
@@ -8465,25 +10128,25 @@ var CrmKanban = ({
|
|
|
8465
10128
|
...props
|
|
8466
10129
|
}) => {
|
|
8467
10130
|
var _a, _b;
|
|
8468
|
-
const [params, setParams] =
|
|
8469
|
-
const resolvedProperties =
|
|
10131
|
+
const [params, setParams] = useState8(EMPTY_CRM_PARAMS);
|
|
10132
|
+
const resolvedProperties = useMemo7(() => properties, [properties]);
|
|
8470
10133
|
const resolvedSearchFields = searchFields || resolvedProperties;
|
|
8471
|
-
const autoFilterFields =
|
|
10134
|
+
const autoFilterFields = useMemo7(
|
|
8472
10135
|
() => normalizeAutoFilterFields(autoFilters, resolvedProperties),
|
|
8473
10136
|
[autoFilters, resolvedProperties]
|
|
8474
10137
|
);
|
|
8475
10138
|
const autoFilterLabelsRef = useRef5({});
|
|
8476
|
-
const defaultPropertyMap =
|
|
10139
|
+
const defaultPropertyMap = useMemo7(
|
|
8477
10140
|
() => Object.fromEntries(resolvedProperties.map((property) => [property, property])),
|
|
8478
10141
|
[resolvedProperties]
|
|
8479
10142
|
);
|
|
8480
10143
|
const effectivePropertyMap = propertyMap || defaultPropertyMap;
|
|
8481
|
-
const resolvedSortMap =
|
|
10144
|
+
const resolvedSortMap = useMemo7(
|
|
8482
10145
|
() => sortMap || ((sort) => crmSortsFromState(sort, effectivePropertyMap)),
|
|
8483
10146
|
[sortMap, effectivePropertyMap]
|
|
8484
10147
|
);
|
|
8485
10148
|
const resolvedMapRecord = mapRecord || defaultCrmMapRecord;
|
|
8486
|
-
const dataSourceOptions =
|
|
10149
|
+
const dataSourceOptions = useMemo7(
|
|
8487
10150
|
() => ({
|
|
8488
10151
|
objectType: resolveCrmObjectType(objectType),
|
|
8489
10152
|
properties: resolvedProperties,
|
|
@@ -8497,14 +10160,14 @@ var CrmKanban = ({
|
|
|
8497
10160
|
}),
|
|
8498
10161
|
[objectType, resolvedProperties, pageLength, format, filterMap, effectivePropertyMap, resolvedSortMap, rowIdField, resolvedMapRecord]
|
|
8499
10162
|
);
|
|
8500
|
-
const [serverQuerying, setServerQuerying] =
|
|
10163
|
+
const [serverQuerying, setServerQuerying] = useState8(!!serverSide);
|
|
8501
10164
|
const effectiveParams = serverQuerying ? params : EMPTY_CRM_PARAMS;
|
|
8502
10165
|
const dataSource = useCrmSearchDataSource(effectiveParams, dataSourceOptions);
|
|
8503
|
-
const queryKey =
|
|
10166
|
+
const queryKey = useMemo7(
|
|
8504
10167
|
() => stableStringify({ effectiveParams, objectType, properties: resolvedProperties, pageLength }),
|
|
8505
10168
|
[effectiveParams, objectType, resolvedProperties, pageLength]
|
|
8506
10169
|
);
|
|
8507
|
-
const [accumulatedRows, setAccumulatedRows] =
|
|
10170
|
+
const [accumulatedRows, setAccumulatedRows] = useState8(EMPTY_ARRAY);
|
|
8508
10171
|
const lastQueryKeyRef = useRef5(queryKey);
|
|
8509
10172
|
const loadedRows = accumulatedRows.length ? accumulatedRows : dataSource.data;
|
|
8510
10173
|
useEffect7(() => {
|
|
@@ -8531,7 +10194,7 @@ var CrmKanban = ({
|
|
|
8531
10194
|
if (!dataSource.hasMore || dataSource.loading || ((_a2 = dataSource.response) == null ? void 0 : _a2.isRefetching)) return;
|
|
8532
10195
|
(_c = (_b2 = dataSource.pagination) == null ? void 0 : _b2.nextPage) == null ? void 0 : _c.call(_b2);
|
|
8533
10196
|
}, [onLoadMore, dataSource.hasMore, dataSource.loading, dataSource.response, dataSource.pagination]);
|
|
8534
|
-
const generatedFilters =
|
|
10197
|
+
const generatedFilters = useMemo7(
|
|
8535
10198
|
() => buildAutoFiltersFromRows({
|
|
8536
10199
|
rows: loadedRows,
|
|
8537
10200
|
fields: autoFilterFields,
|
|
@@ -8541,7 +10204,7 @@ var CrmKanban = ({
|
|
|
8541
10204
|
[loadedRows, autoFilterFields, autoFilterMaxOptions]
|
|
8542
10205
|
);
|
|
8543
10206
|
const resolvedFilters = filters || generatedFilters;
|
|
8544
|
-
const resolvedStages =
|
|
10207
|
+
const resolvedStages = useMemo7(() => {
|
|
8545
10208
|
if (stages) return stages;
|
|
8546
10209
|
const seen = [];
|
|
8547
10210
|
for (const row of loadedRows) {
|
|
@@ -8553,7 +10216,7 @@ var CrmKanban = ({
|
|
|
8553
10216
|
label: typeof stageLabels === "function" ? stageLabels(value) : stageLabels && stageLabels[value] || prettifyPropertyName(String(value))
|
|
8554
10217
|
}));
|
|
8555
10218
|
}, [stages, stageLabels, loadedRows, groupBy]);
|
|
8556
|
-
const resolvedStageMeta =
|
|
10219
|
+
const resolvedStageMeta = useMemo7(() => {
|
|
8557
10220
|
if (stageMeta || !dataSource.hasMore) return stageMeta;
|
|
8558
10221
|
return Object.fromEntries(resolvedStages.map((stage) => {
|
|
8559
10222
|
var _a2;
|
|
@@ -8567,7 +10230,7 @@ var CrmKanban = ({
|
|
|
8567
10230
|
];
|
|
8568
10231
|
}));
|
|
8569
10232
|
}, [stageMeta, dataSource.hasMore, dataSource.loading, dataSource.response, dataSource.totalCount, resolvedStages]);
|
|
8570
|
-
const board =
|
|
10233
|
+
const board = React18.createElement(Kanban, {
|
|
8571
10234
|
title: title || `${prettifyPropertyName(objectType)} board`,
|
|
8572
10235
|
data: loadedRows,
|
|
8573
10236
|
loading: dataSource.loading || ((_b = dataSource.response) == null ? void 0 : _b.isRefetching),
|
|
@@ -8590,17 +10253,19 @@ var CrmKanban = ({
|
|
|
8590
10253
|
const total = dataSource.totalCount;
|
|
8591
10254
|
const capped = typeof total === "number" && total > loadedRows.length;
|
|
8592
10255
|
if (!capped) return board;
|
|
8593
|
-
return
|
|
8594
|
-
|
|
10256
|
+
return React18.createElement(
|
|
10257
|
+
Flex11,
|
|
8595
10258
|
{ direction: "column", gap: "xs" },
|
|
8596
|
-
|
|
8597
|
-
|
|
10259
|
+
React18.createElement(
|
|
10260
|
+
Text9,
|
|
8598
10261
|
{ variant: "microcopy" },
|
|
8599
10262
|
dataSource.hasMore ? `Loaded ${loadedRows.length} of ${total} matching. Use Load more to fetch more CRM results.` : `Showing ${loadedRows.length} of ${total} matching. Refine your search or filters to narrow the results.`
|
|
8600
10263
|
),
|
|
8601
10264
|
board
|
|
8602
10265
|
);
|
|
8603
10266
|
};
|
|
10267
|
+
CrmDataTable.displayName = "CrmDataTable";
|
|
10268
|
+
CrmKanban.displayName = "CrmKanban";
|
|
8604
10269
|
|
|
8605
10270
|
// src/common-components/CrmLookupSelect.js
|
|
8606
10271
|
var EMPTY_ARRAY2 = [];
|
|
@@ -8653,12 +10318,12 @@ var CrmLookupSelect = ({
|
|
|
8653
10318
|
loadingOption,
|
|
8654
10319
|
selectProps = EMPTY_OBJECT2
|
|
8655
10320
|
}) => {
|
|
8656
|
-
const [inputValue, setInputValue] =
|
|
8657
|
-
const [pickedOptions, setPickedOptions] =
|
|
10321
|
+
const [inputValue, setInputValue] = useState9(query || "");
|
|
10322
|
+
const [pickedOptions, setPickedOptions] = useState9(EMPTY_ARRAY2);
|
|
8658
10323
|
const debouncedInput = useDebounce2(inputValue, debounce > 0 ? debounce : 1);
|
|
8659
10324
|
const search = debounce > 0 ? debouncedInput : inputValue;
|
|
8660
10325
|
const effectiveSearch = search && search.length >= minSearchLength ? search : "";
|
|
8661
|
-
const optionConfig =
|
|
10326
|
+
const optionConfig = useMemo8(
|
|
8662
10327
|
() => makeOptionConfig({ option, labelProperty, valueProperty, descriptionProperty }),
|
|
8663
10328
|
[option, labelProperty, valueProperty, descriptionProperty]
|
|
8664
10329
|
);
|
|
@@ -8676,7 +10341,7 @@ var CrmLookupSelect = ({
|
|
|
8676
10341
|
);
|
|
8677
10342
|
const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
|
|
8678
10343
|
const hasQuery = effectiveSearch.length > 0;
|
|
8679
|
-
const options =
|
|
10344
|
+
const options = useMemo8(() => {
|
|
8680
10345
|
const remembered = [...selectedOptions || EMPTY_ARRAY2, ...pickedOptions];
|
|
8681
10346
|
const baseOptions = mergeSelectedOptions(dataSource.options || EMPTY_ARRAY2, remembered, value);
|
|
8682
10347
|
if (isSearching && loadingOption) return [loadingOption, ...baseOptions];
|
|
@@ -8715,7 +10380,281 @@ var CrmLookupSelect = ({
|
|
|
8715
10380
|
},
|
|
8716
10381
|
...selectProps
|
|
8717
10382
|
};
|
|
8718
|
-
return
|
|
10383
|
+
return React19.createElement(multiple ? MultiSelect5 : Select8, commonProps);
|
|
10384
|
+
};
|
|
10385
|
+
|
|
10386
|
+
// src/common-components/CrmRecordPicker.js
|
|
10387
|
+
import React20, { useMemo as useMemo9, useRef as useRef6, useState as useState10 } from "react";
|
|
10388
|
+
import { Flex as Flex12, MultiSelect as MultiSelect6, SearchInput as SearchInput2, Select as Select9, useDebounce as useDebounce3 } from "@hubspot/ui-extensions";
|
|
10389
|
+
|
|
10390
|
+
// src/common-components/recordPickerCore.js
|
|
10391
|
+
var EMPTY_ARRAY3 = [];
|
|
10392
|
+
var CREATE_OPTION_VALUE = "__create__";
|
|
10393
|
+
var isRecordLike = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
10394
|
+
var getRecordId = (record) => {
|
|
10395
|
+
if (!isRecordLike(record)) return void 0;
|
|
10396
|
+
const id = record.objectId ?? record.id ?? record.hs_object_id ?? getByPath(record, "properties.hs_object_id");
|
|
10397
|
+
return id == null ? void 0 : String(id);
|
|
10398
|
+
};
|
|
10399
|
+
var toList = (value) => Array.isArray(value) ? value : value == null || value === "" ? EMPTY_ARRAY3 : [value];
|
|
10400
|
+
var normalizeRecordSelection = (value) => {
|
|
10401
|
+
const ids = [];
|
|
10402
|
+
const records = [];
|
|
10403
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10404
|
+
for (const entry of toList(value)) {
|
|
10405
|
+
const rawId = isRecordLike(entry) ? getRecordId(entry) : entry;
|
|
10406
|
+
const id = rawId == null || rawId === "" ? rawId : String(rawId);
|
|
10407
|
+
if (id == null || id === "" || seen.has(id)) continue;
|
|
10408
|
+
seen.add(id);
|
|
10409
|
+
ids.push(id);
|
|
10410
|
+
if (isRecordLike(entry)) records.push(entry);
|
|
10411
|
+
}
|
|
10412
|
+
return { ids, records };
|
|
10413
|
+
};
|
|
10414
|
+
var recordToPickerOption = (record, config = {}) => {
|
|
10415
|
+
const { labelField, descriptionField, fallbackLabel = "Untitled record" } = config;
|
|
10416
|
+
const label = (labelField ? getByPath(record, labelField) : void 0) ?? (record == null ? void 0 : record.name) ?? getByPath(record, "properties.name") ?? fallbackLabel;
|
|
10417
|
+
const option = { label, value: getRecordId(record) };
|
|
10418
|
+
const description = descriptionField ? getByPath(record, descriptionField) : void 0;
|
|
10419
|
+
if (description != null && description !== "") option.description = description;
|
|
10420
|
+
return option;
|
|
10421
|
+
};
|
|
10422
|
+
var mergePickerOptions = (options, selectedOptions) => {
|
|
10423
|
+
const base = Array.isArray(options) ? options : EMPTY_ARRAY3;
|
|
10424
|
+
const selected = toList(selectedOptions);
|
|
10425
|
+
if (!selected.length) return base;
|
|
10426
|
+
const existing = new Set(base.map((option) => option == null ? void 0 : option.value));
|
|
10427
|
+
const missing = selected.filter((option) => option && !existing.has(option.value));
|
|
10428
|
+
return missing.length ? [...missing, ...base] : base;
|
|
10429
|
+
};
|
|
10430
|
+
var enforceSelectionMax = (ids, max) => {
|
|
10431
|
+
const list = toList(ids);
|
|
10432
|
+
if (!Number.isFinite(max) || max <= 0 || list.length <= max) return list;
|
|
10433
|
+
return list.slice(0, max);
|
|
10434
|
+
};
|
|
10435
|
+
var shouldShowCreateOption = ({
|
|
10436
|
+
allowCreate,
|
|
10437
|
+
searchTerm,
|
|
10438
|
+
options,
|
|
10439
|
+
searching = false,
|
|
10440
|
+
createPending = false,
|
|
10441
|
+
atMax = false
|
|
10442
|
+
} = {}) => {
|
|
10443
|
+
if (!allowCreate || typeof allowCreate.onCreate !== "function") return false;
|
|
10444
|
+
if (createPending || searching || atMax) return false;
|
|
10445
|
+
const term = String(searchTerm ?? "").trim();
|
|
10446
|
+
if (!term) return false;
|
|
10447
|
+
const lower = term.toLowerCase();
|
|
10448
|
+
return !(options || EMPTY_ARRAY3).some(
|
|
10449
|
+
(option) => String((option == null ? void 0 : option.label) ?? "").trim().toLowerCase() === lower
|
|
10450
|
+
);
|
|
10451
|
+
};
|
|
10452
|
+
var makeCreateOption = (term, label) => ({
|
|
10453
|
+
label: typeof label === "function" ? label(term) : label || `Create "${term}"`,
|
|
10454
|
+
value: CREATE_OPTION_VALUE
|
|
10455
|
+
});
|
|
10456
|
+
var splitCreateSelection = (next) => {
|
|
10457
|
+
const list = toList(next);
|
|
10458
|
+
const ids = list.filter((value) => value !== CREATE_OPTION_VALUE);
|
|
10459
|
+
return { ids, create: ids.length !== list.length };
|
|
10460
|
+
};
|
|
10461
|
+
var mapIdsToRecords = (ids, recordsById) => {
|
|
10462
|
+
const lookup = recordsById instanceof Map ? (id) => recordsById.get(id) : (id) => recordsById ? recordsById[id] : void 0;
|
|
10463
|
+
return toList(ids).map((id) => lookup(id) ?? { objectId: id });
|
|
10464
|
+
};
|
|
10465
|
+
var upsertRecords = (records, additions) => {
|
|
10466
|
+
const incoming = toList(additions).filter((record) => getRecordId(record) != null);
|
|
10467
|
+
if (!incoming.length) return Array.isArray(records) ? records : EMPTY_ARRAY3;
|
|
10468
|
+
const byId = new Map(toList(records).map((record) => [getRecordId(record), record]));
|
|
10469
|
+
for (const record of incoming) byId.set(getRecordId(record), record);
|
|
10470
|
+
return [...byId.values()];
|
|
10471
|
+
};
|
|
10472
|
+
|
|
10473
|
+
// src/common-components/CrmRecordPicker.js
|
|
10474
|
+
var EMPTY_ARRAY4 = [];
|
|
10475
|
+
var defaultMapRecord = (record) => ({ objectId: record.objectId, ...record.properties });
|
|
10476
|
+
var CrmRecordPicker = ({
|
|
10477
|
+
objectType,
|
|
10478
|
+
properties = EMPTY_ARRAY4,
|
|
10479
|
+
labelField,
|
|
10480
|
+
descriptionField,
|
|
10481
|
+
value,
|
|
10482
|
+
defaultValue,
|
|
10483
|
+
onChange,
|
|
10484
|
+
multi = true,
|
|
10485
|
+
max,
|
|
10486
|
+
placeholder,
|
|
10487
|
+
label,
|
|
10488
|
+
name,
|
|
10489
|
+
required,
|
|
10490
|
+
readOnly,
|
|
10491
|
+
error,
|
|
10492
|
+
validationMessage,
|
|
10493
|
+
description,
|
|
10494
|
+
tooltip,
|
|
10495
|
+
variant,
|
|
10496
|
+
pageLength = 20,
|
|
10497
|
+
debounce = 300,
|
|
10498
|
+
minSearchLength = 0,
|
|
10499
|
+
filterMap,
|
|
10500
|
+
allowCreate = false,
|
|
10501
|
+
fallbackLabel = "Untitled record",
|
|
10502
|
+
format,
|
|
10503
|
+
baseConfig,
|
|
10504
|
+
onSearchChange,
|
|
10505
|
+
...rest
|
|
10506
|
+
}) => {
|
|
10507
|
+
const isControlled = value !== void 0;
|
|
10508
|
+
const [internalValue, setInternalValue] = useState10(defaultValue);
|
|
10509
|
+
const effectiveValue = isControlled ? value : internalValue;
|
|
10510
|
+
const selection = useMemo9(() => normalizeRecordSelection(effectiveValue), [effectiveValue]);
|
|
10511
|
+
const [inputValue, setInputValue] = useState10("");
|
|
10512
|
+
const [seenRecords, setSeenRecords] = useState10(EMPTY_ARRAY4);
|
|
10513
|
+
const [createPending, setCreatePending] = useState10(false);
|
|
10514
|
+
const [createError, setCreateError] = useState10(null);
|
|
10515
|
+
const createPendingRef = useRef6(false);
|
|
10516
|
+
const debouncedInput = useDebounce3(inputValue, debounce > 0 ? debounce : 1);
|
|
10517
|
+
const search = debounce > 0 ? debouncedInput : inputValue;
|
|
10518
|
+
const effectiveSearch = search && search.length >= minSearchLength ? search : "";
|
|
10519
|
+
const optionConfig = useMemo9(
|
|
10520
|
+
() => ({ labelField, descriptionField, fallbackLabel }),
|
|
10521
|
+
[labelField, descriptionField, fallbackLabel]
|
|
10522
|
+
);
|
|
10523
|
+
const searchParams = useMemo9(() => ({ search: effectiveSearch }), [effectiveSearch]);
|
|
10524
|
+
const dataSourceOptions = useMemo9(
|
|
10525
|
+
() => ({
|
|
10526
|
+
objectType: resolveCrmObjectType(objectType),
|
|
10527
|
+
properties,
|
|
10528
|
+
pageLength,
|
|
10529
|
+
format,
|
|
10530
|
+
filterMap,
|
|
10531
|
+
baseConfig,
|
|
10532
|
+
row: { mapRecord: defaultMapRecord },
|
|
10533
|
+
option: { mapOption: (row) => recordToPickerOption(row, optionConfig) }
|
|
10534
|
+
}),
|
|
10535
|
+
[objectType, properties, pageLength, format, filterMap, baseConfig, optionConfig]
|
|
10536
|
+
);
|
|
10537
|
+
const dataSource = useCrmSearchOptions(searchParams, dataSourceOptions);
|
|
10538
|
+
const recordsById = useMemo9(() => {
|
|
10539
|
+
const map = /* @__PURE__ */ new Map();
|
|
10540
|
+
for (const record of selection.records) map.set(getRecordId(record), record);
|
|
10541
|
+
for (const record of seenRecords) map.set(getRecordId(record), record);
|
|
10542
|
+
for (const row of dataSource.rows || EMPTY_ARRAY4) {
|
|
10543
|
+
const id = getRecordId(row);
|
|
10544
|
+
if (id != null) map.set(id, row);
|
|
10545
|
+
}
|
|
10546
|
+
return map;
|
|
10547
|
+
}, [selection.records, seenRecords, dataSource.rows]);
|
|
10548
|
+
const selectedOptions = useMemo9(
|
|
10549
|
+
() => selection.ids.map((id) => {
|
|
10550
|
+
const record = recordsById.get(id);
|
|
10551
|
+
return record ? recordToPickerOption(record, optionConfig) : { label: String(id), value: id };
|
|
10552
|
+
}),
|
|
10553
|
+
[selection.ids, recordsById, optionConfig]
|
|
10554
|
+
);
|
|
10555
|
+
const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
|
|
10556
|
+
const atMax = multi && Number.isFinite(max) && max > 0 && selection.ids.length >= max;
|
|
10557
|
+
const options = useMemo9(() => {
|
|
10558
|
+
const merged = mergePickerOptions(dataSource.options || EMPTY_ARRAY4, selectedOptions);
|
|
10559
|
+
const showCreate = shouldShowCreateOption({
|
|
10560
|
+
allowCreate,
|
|
10561
|
+
searchTerm: effectiveSearch,
|
|
10562
|
+
options: merged,
|
|
10563
|
+
searching: isSearching,
|
|
10564
|
+
createPending,
|
|
10565
|
+
atMax
|
|
10566
|
+
});
|
|
10567
|
+
if (!showCreate) return merged;
|
|
10568
|
+
return [...merged, makeCreateOption(effectiveSearch.trim(), allowCreate == null ? void 0 : allowCreate.label)];
|
|
10569
|
+
}, [dataSource.options, selectedOptions, allowCreate, effectiveSearch, isSearching, createPending, atMax]);
|
|
10570
|
+
const commitChange = (ids, extraRecords) => {
|
|
10571
|
+
let map = recordsById;
|
|
10572
|
+
if (extraRecords && extraRecords.length) {
|
|
10573
|
+
map = new Map(recordsById);
|
|
10574
|
+
for (const record of extraRecords) {
|
|
10575
|
+
const id = getRecordId(record);
|
|
10576
|
+
if (id != null) map.set(id, record);
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10579
|
+
const trimmed = multi ? enforceSelectionMax(ids, max) : ids.slice(0, 1);
|
|
10580
|
+
const records = mapIdsToRecords(trimmed, map);
|
|
10581
|
+
if (!isControlled) setInternalValue(multi ? trimmed : trimmed[0] ?? null);
|
|
10582
|
+
if (onChange) {
|
|
10583
|
+
if (multi) onChange(trimmed, records);
|
|
10584
|
+
else onChange(trimmed[0] ?? null, records[0] ?? null);
|
|
10585
|
+
}
|
|
10586
|
+
};
|
|
10587
|
+
const startCreate = (term, baseIds) => {
|
|
10588
|
+
const onCreate = allowCreate && typeof allowCreate.onCreate === "function" ? allowCreate.onCreate : null;
|
|
10589
|
+
if (!onCreate || createPendingRef.current) return;
|
|
10590
|
+
createPendingRef.current = true;
|
|
10591
|
+
setCreatePending(true);
|
|
10592
|
+
setCreateError(null);
|
|
10593
|
+
Promise.resolve(onCreate(term)).then((created) => {
|
|
10594
|
+
const record = isRecordLike(created) ? created : created != null && created !== "" ? { objectId: created, name: term } : null;
|
|
10595
|
+
const id = getRecordId(record);
|
|
10596
|
+
if (id == null) return;
|
|
10597
|
+
setSeenRecords((prev) => upsertRecords(prev, [record]));
|
|
10598
|
+
const nextIds = multi ? [...baseIds.filter((v) => v !== id), id] : [id];
|
|
10599
|
+
commitChange(nextIds, [record]);
|
|
10600
|
+
}).catch((err) => {
|
|
10601
|
+
setCreateError((err == null ? void 0 : err.message) || "Could not create the record.");
|
|
10602
|
+
}).finally(() => {
|
|
10603
|
+
createPendingRef.current = false;
|
|
10604
|
+
setCreatePending(false);
|
|
10605
|
+
});
|
|
10606
|
+
};
|
|
10607
|
+
const handleChange = (next) => {
|
|
10608
|
+
if (createError) setCreateError(null);
|
|
10609
|
+
const { ids, create } = splitCreateSelection(next);
|
|
10610
|
+
const picked = ids.map((id) => recordsById.get(id)).filter(Boolean);
|
|
10611
|
+
if (picked.length) setSeenRecords((prev) => upsertRecords(prev, picked));
|
|
10612
|
+
if (create) {
|
|
10613
|
+
startCreate(effectiveSearch.trim(), ids);
|
|
10614
|
+
if (multi) commitChange(ids);
|
|
10615
|
+
return;
|
|
10616
|
+
}
|
|
10617
|
+
commitChange(ids);
|
|
10618
|
+
};
|
|
10619
|
+
const handleSearchInput = (next) => {
|
|
10620
|
+
setInputValue(next || "");
|
|
10621
|
+
if (onSearchChange) onSearchChange(next || "");
|
|
10622
|
+
};
|
|
10623
|
+
const commonProps = {
|
|
10624
|
+
name,
|
|
10625
|
+
label,
|
|
10626
|
+
value: multi ? selection.ids : selection.ids[0],
|
|
10627
|
+
options,
|
|
10628
|
+
placeholder: placeholder || (createPending ? "Creating record..." : dataSource.loading ? "Searching CRM..." : "Search CRM records..."),
|
|
10629
|
+
description,
|
|
10630
|
+
tooltip,
|
|
10631
|
+
required,
|
|
10632
|
+
readOnly: readOnly || createPending,
|
|
10633
|
+
error: error || !!createError || !!dataSource.error,
|
|
10634
|
+
validationMessage: validationMessage || createError || (typeof dataSource.error === "string" ? dataSource.error : void 0),
|
|
10635
|
+
variant,
|
|
10636
|
+
onChange: handleChange,
|
|
10637
|
+
...rest
|
|
10638
|
+
};
|
|
10639
|
+
if (!multi) {
|
|
10640
|
+
return React20.createElement(Select9, { ...commonProps, onInput: handleSearchInput });
|
|
10641
|
+
}
|
|
10642
|
+
return React20.createElement(
|
|
10643
|
+
Flex12,
|
|
10644
|
+
{ direction: "column", gap: "xs" },
|
|
10645
|
+
React20.createElement(SearchInput2, {
|
|
10646
|
+
name: name ? `${name}-search` : void 0,
|
|
10647
|
+
label: "",
|
|
10648
|
+
placeholder: "Search CRM records...",
|
|
10649
|
+
value: inputValue,
|
|
10650
|
+
readOnly: readOnly || createPending,
|
|
10651
|
+
onInput: handleSearchInput,
|
|
10652
|
+
// The clear "x" emits onChange (not onInput) — wire both so clearing
|
|
10653
|
+
// resets the term (same pattern as CollectionToolbar).
|
|
10654
|
+
onChange: handleSearchInput
|
|
10655
|
+
}),
|
|
10656
|
+
React20.createElement(MultiSelect6, commonProps)
|
|
10657
|
+
);
|
|
8719
10658
|
};
|
|
8720
10659
|
|
|
8721
10660
|
// src/common-components/datePresets.js
|
|
@@ -8740,12 +10679,480 @@ var HS_DATE_DIRECTION_LABELS = {
|
|
|
8740
10679
|
desc: "Descending"
|
|
8741
10680
|
};
|
|
8742
10681
|
|
|
10682
|
+
// src/common-components/dateRangePresets.js
|
|
10683
|
+
var DATE_RANGE_CUSTOM_VALUE = "custom";
|
|
10684
|
+
var DATE_FILTER_OPERATORS = [
|
|
10685
|
+
{ label: "is", value: "InRollingDateRange" },
|
|
10686
|
+
{ label: "is equal to", value: "Equal" },
|
|
10687
|
+
{ label: "is before", value: "BeforeDateStaticOrDynamic" },
|
|
10688
|
+
{ label: "is after", value: "AfterDateStaticOrDynamic" },
|
|
10689
|
+
{ label: "is between", value: "InRange" },
|
|
10690
|
+
{ label: "is more than", value: "GreaterRolling" },
|
|
10691
|
+
{ label: "is less than", value: "LessRolling" },
|
|
10692
|
+
{ label: "is known", value: "Known" },
|
|
10693
|
+
{ label: "is unknown", value: "NotKnown" }
|
|
10694
|
+
];
|
|
10695
|
+
var DATE_ROLLING_UNIT_OPTIONS = [
|
|
10696
|
+
{ label: "day ago", value: "day:backward" },
|
|
10697
|
+
{ label: "days from now", value: "day:forward" },
|
|
10698
|
+
{ label: "week ago", value: "week:backward" },
|
|
10699
|
+
{ label: "weeks from now", value: "week:forward" },
|
|
10700
|
+
{ label: "month ago", value: "month:backward" },
|
|
10701
|
+
{ label: "months from now", value: "month:forward" },
|
|
10702
|
+
{ label: "year ago", value: "year:backward" },
|
|
10703
|
+
{ label: "years from now", value: "year:forward" }
|
|
10704
|
+
];
|
|
10705
|
+
var toHsDateValue = (date) => {
|
|
10706
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) return null;
|
|
10707
|
+
return { year: date.getFullYear(), month: date.getMonth(), date: date.getDate() };
|
|
10708
|
+
};
|
|
10709
|
+
var compareHsDateValues = (a, b) => {
|
|
10710
|
+
if (!a || !b) return 0;
|
|
10711
|
+
return a.year - b.year || a.month - b.month || a.date - b.date;
|
|
10712
|
+
};
|
|
10713
|
+
var isValidDateRange = (range) => {
|
|
10714
|
+
if (!range) return true;
|
|
10715
|
+
return compareHsDateValues(range.from, range.to) <= 0;
|
|
10716
|
+
};
|
|
10717
|
+
var dayAt = (now, offset = 0) => toHsDateValue(new Date(now.getFullYear(), now.getMonth(), now.getDate() + offset));
|
|
10718
|
+
var monthStart = (now, monthOffset = 0) => toHsDateValue(new Date(now.getFullYear(), now.getMonth() + monthOffset, 1));
|
|
10719
|
+
var monthEnd = (now, monthOffset = 0) => toHsDateValue(new Date(now.getFullYear(), now.getMonth() + monthOffset + 1, 0));
|
|
10720
|
+
var quarterStartMonth = (now, quarterOffset = 0) => Math.floor(now.getMonth() / 3) * 3 + quarterOffset * 3;
|
|
10721
|
+
var presetToRange = (presetKey, now = /* @__PURE__ */ new Date()) => {
|
|
10722
|
+
if (!presetKey || presetKey === DATE_RANGE_CUSTOM_VALUE) return null;
|
|
10723
|
+
if (!(now instanceof Date) || Number.isNaN(now.getTime())) return null;
|
|
10724
|
+
const dow = now.getDay();
|
|
10725
|
+
const year = now.getFullYear();
|
|
10726
|
+
const qm = quarterStartMonth(now);
|
|
10727
|
+
switch (presetKey) {
|
|
10728
|
+
case "today":
|
|
10729
|
+
return { from: dayAt(now, 0), to: dayAt(now, 0) };
|
|
10730
|
+
case "yesterday":
|
|
10731
|
+
return { from: dayAt(now, -1), to: dayAt(now, -1) };
|
|
10732
|
+
case "tomorrow":
|
|
10733
|
+
return { from: dayAt(now, 1), to: dayAt(now, 1) };
|
|
10734
|
+
case "this_week":
|
|
10735
|
+
return { from: dayAt(now, -dow), to: dayAt(now, 6 - dow) };
|
|
10736
|
+
case "last_week":
|
|
10737
|
+
return { from: dayAt(now, -dow - 7), to: dayAt(now, -dow - 1) };
|
|
10738
|
+
case "7d":
|
|
10739
|
+
return { from: dayAt(now, -6), to: dayAt(now, 0) };
|
|
10740
|
+
case "30d":
|
|
10741
|
+
return { from: dayAt(now, -29), to: dayAt(now, 0) };
|
|
10742
|
+
case "90d":
|
|
10743
|
+
return { from: dayAt(now, -89), to: dayAt(now, 0) };
|
|
10744
|
+
case "this_month":
|
|
10745
|
+
return { from: monthStart(now, 0), to: monthEnd(now, 0) };
|
|
10746
|
+
case "last_month":
|
|
10747
|
+
return { from: monthStart(now, -1), to: monthEnd(now, -1) };
|
|
10748
|
+
case "this_quarter":
|
|
10749
|
+
return {
|
|
10750
|
+
from: toHsDateValue(new Date(year, qm, 1)),
|
|
10751
|
+
to: toHsDateValue(new Date(year, qm + 3, 0))
|
|
10752
|
+
};
|
|
10753
|
+
case "last_quarter":
|
|
10754
|
+
return {
|
|
10755
|
+
from: toHsDateValue(new Date(year, qm - 3, 1)),
|
|
10756
|
+
to: toHsDateValue(new Date(year, qm, 0))
|
|
10757
|
+
};
|
|
10758
|
+
case "this_year":
|
|
10759
|
+
return { from: { year, month: 0, date: 1 }, to: { year, month: 11, date: 31 } };
|
|
10760
|
+
case "last_year":
|
|
10761
|
+
return {
|
|
10762
|
+
from: { year: year - 1, month: 0, date: 1 },
|
|
10763
|
+
to: { year: year - 1, month: 11, date: 31 }
|
|
10764
|
+
};
|
|
10765
|
+
default:
|
|
10766
|
+
return null;
|
|
10767
|
+
}
|
|
10768
|
+
};
|
|
10769
|
+
|
|
10770
|
+
// src/common-components/DateRangePicker.js
|
|
10771
|
+
import React21, { useState as useState11 } from "react";
|
|
10772
|
+
import {
|
|
10773
|
+
AutoGrid as AutoGrid4,
|
|
10774
|
+
Box as Box8,
|
|
10775
|
+
DateInput as DateInput6,
|
|
10776
|
+
Flex as Flex13,
|
|
10777
|
+
Link as Link7,
|
|
10778
|
+
NumberInput as NumberInput4,
|
|
10779
|
+
Select as Select10,
|
|
10780
|
+
Text as Text10
|
|
10781
|
+
} from "@hubspot/ui-extensions";
|
|
10782
|
+
var h6 = React21.createElement;
|
|
10783
|
+
var IN_ROLLING = "InRollingDateRange";
|
|
10784
|
+
var EQUAL = "Equal";
|
|
10785
|
+
var BEFORE = "BeforeDateStaticOrDynamic";
|
|
10786
|
+
var AFTER = "AfterDateStaticOrDynamic";
|
|
10787
|
+
var GREATER_ROLLING = "GreaterRolling";
|
|
10788
|
+
var LESS_ROLLING = "LessRolling";
|
|
10789
|
+
var IN_RANGE = "InRange";
|
|
10790
|
+
var KNOWN = "Known";
|
|
10791
|
+
var NOT_KNOWN = "NotKnown";
|
|
10792
|
+
var EMPTY_RANGE = { from: null, to: null };
|
|
10793
|
+
var EMPTY_DATE = { date: null };
|
|
10794
|
+
var COMPACT_LABEL = "";
|
|
10795
|
+
var STATIC_DATE_OPERATORS = /* @__PURE__ */ new Set([EQUAL, BEFORE, AFTER]);
|
|
10796
|
+
var ROLLING_OPERATORS = /* @__PURE__ */ new Set([GREATER_ROLLING, LESS_ROLLING]);
|
|
10797
|
+
var PRESENCE_OPERATORS = /* @__PURE__ */ new Set([KNOWN, NOT_KNOWN]);
|
|
10798
|
+
var keyOfDate = (v) => v ? `${v.year}-${v.month}-${v.date}` : "";
|
|
10799
|
+
var keyOfRange = (r) => `${keyOfDate(r == null ? void 0 : r.from)}|${keyOfDate(r == null ? void 0 : r.to)}`;
|
|
10800
|
+
var isRangeLike = (value) => value && typeof value === "object" && ("from" in value || "to" in value) && !("operator" in value);
|
|
10801
|
+
var normalizeValue = (value) => {
|
|
10802
|
+
if (isRangeLike(value)) {
|
|
10803
|
+
return { operator: IN_RANGE, from: value.from ?? null, to: value.to ?? null };
|
|
10804
|
+
}
|
|
10805
|
+
if (!value || typeof value !== "object") {
|
|
10806
|
+
return { operator: IN_ROLLING, preset: "today" };
|
|
10807
|
+
}
|
|
10808
|
+
const operator = value.operator || IN_ROLLING;
|
|
10809
|
+
if (operator === IN_RANGE) {
|
|
10810
|
+
return { operator, from: value.from ?? null, to: value.to ?? null };
|
|
10811
|
+
}
|
|
10812
|
+
if (STATIC_DATE_OPERATORS.has(operator)) {
|
|
10813
|
+
return { operator, date: value.date ?? null };
|
|
10814
|
+
}
|
|
10815
|
+
if (ROLLING_OPERATORS.has(operator)) {
|
|
10816
|
+
return {
|
|
10817
|
+
operator,
|
|
10818
|
+
amount: Number.isFinite(Number(value.amount)) ? Number(value.amount) : 1,
|
|
10819
|
+
unit: value.unit || "day",
|
|
10820
|
+
direction: value.direction || "backward"
|
|
10821
|
+
};
|
|
10822
|
+
}
|
|
10823
|
+
if (PRESENCE_OPERATORS.has(operator)) {
|
|
10824
|
+
return { operator };
|
|
10825
|
+
}
|
|
10826
|
+
return { operator: IN_ROLLING, preset: value.preset || value.value || "today" };
|
|
10827
|
+
};
|
|
10828
|
+
var rangeFromValue = (value) => ({
|
|
10829
|
+
from: (value == null ? void 0 : value.from) ?? null,
|
|
10830
|
+
to: (value == null ? void 0 : value.to) ?? null
|
|
10831
|
+
});
|
|
10832
|
+
var getPresetOptions = (presets, customPresetLabel) => {
|
|
10833
|
+
const presetList = presets === true ? HS_DATE_PRESETS : Array.isArray(presets) ? presets : null;
|
|
10834
|
+
if (!presetList) return null;
|
|
10835
|
+
return [
|
|
10836
|
+
...presetList.map((p) => ({ label: p.label, value: p.value })),
|
|
10837
|
+
...presetList.some((p) => p.value === DATE_RANGE_CUSTOM_VALUE) ? [] : [{ label: customPresetLabel, value: DATE_RANGE_CUSTOM_VALUE }]
|
|
10838
|
+
];
|
|
10839
|
+
};
|
|
10840
|
+
var DateRangePicker = ({
|
|
10841
|
+
value,
|
|
10842
|
+
defaultValue,
|
|
10843
|
+
onChange,
|
|
10844
|
+
label,
|
|
10845
|
+
name = "date-range",
|
|
10846
|
+
field,
|
|
10847
|
+
defaultField,
|
|
10848
|
+
onFieldChange,
|
|
10849
|
+
showFieldSelect = false,
|
|
10850
|
+
fieldOptions = [],
|
|
10851
|
+
operator,
|
|
10852
|
+
defaultOperator = IN_ROLLING,
|
|
10853
|
+
onOperatorChange,
|
|
10854
|
+
showOperatorSelect = true,
|
|
10855
|
+
operatorOptions = DATE_FILTER_OPERATORS,
|
|
10856
|
+
presets = true,
|
|
10857
|
+
rollingUnitOptions = DATE_ROLLING_UNIT_OPTIONS,
|
|
10858
|
+
direction = "row",
|
|
10859
|
+
clearable = false,
|
|
10860
|
+
min,
|
|
10861
|
+
max,
|
|
10862
|
+
fromLabel = "Start date",
|
|
10863
|
+
toLabel = "End date",
|
|
10864
|
+
dateLabel = "Date",
|
|
10865
|
+
showDateLabels = false,
|
|
10866
|
+
format = "medium",
|
|
10867
|
+
presetPlaceholder = "Enter value",
|
|
10868
|
+
customPresetLabel = "Custom",
|
|
10869
|
+
clearLabel = "Clear",
|
|
10870
|
+
invalidRangeMessage = "Start date must be on or before end date",
|
|
10871
|
+
readOnly = false,
|
|
10872
|
+
gap,
|
|
10873
|
+
gridColumnWidth = 260
|
|
10874
|
+
}) => {
|
|
10875
|
+
const isControlled = value !== void 0;
|
|
10876
|
+
const controlledValue = normalizeValue(value);
|
|
10877
|
+
const [internalValue, setInternalValue] = useState11(
|
|
10878
|
+
() => normalizeValue(defaultValue ?? { operator: defaultOperator })
|
|
10879
|
+
);
|
|
10880
|
+
const [internalField, setInternalField] = useState11(
|
|
10881
|
+
() => {
|
|
10882
|
+
var _a;
|
|
10883
|
+
return defaultField ?? ((_a = fieldOptions == null ? void 0 : fieldOptions[0]) == null ? void 0 : _a.value) ?? "";
|
|
10884
|
+
}
|
|
10885
|
+
);
|
|
10886
|
+
const current = normalizeValue(isControlled ? controlledValue : internalValue);
|
|
10887
|
+
const currentOperator = operator || current.operator || defaultOperator;
|
|
10888
|
+
const currentField = field !== void 0 ? field : internalField;
|
|
10889
|
+
const resolvedCurrent = normalizeValue({ ...current, operator: currentOperator });
|
|
10890
|
+
const [pending, setPending] = useState11(null);
|
|
10891
|
+
const [lastPreset, setLastPreset] = useState11({
|
|
10892
|
+
key: resolvedCurrent.preset || "",
|
|
10893
|
+
rangeKey: null
|
|
10894
|
+
});
|
|
10895
|
+
const isColumn = direction === "column";
|
|
10896
|
+
const presetOptions = getPresetOptions(presets, customPresetLabel);
|
|
10897
|
+
const showClear = clearable && !readOnly && (resolvedCurrent.preset || resolvedCurrent.date || resolvedCurrent.amount || resolvedCurrent.from || resolvedCurrent.to || pending);
|
|
10898
|
+
const emit = (next, meta = {}) => {
|
|
10899
|
+
const normalized = normalizeValue(next);
|
|
10900
|
+
if (!isControlled) setInternalValue(normalized);
|
|
10901
|
+
if (normalized.operator !== currentOperator) {
|
|
10902
|
+
onOperatorChange == null ? void 0 : onOperatorChange(normalized.operator);
|
|
10903
|
+
}
|
|
10904
|
+
onChange == null ? void 0 : onChange(normalized, {
|
|
10905
|
+
operator: normalized.operator,
|
|
10906
|
+
field: currentField || null,
|
|
10907
|
+
preset: normalized.operator === IN_ROLLING ? normalized.preset ?? null : null,
|
|
10908
|
+
...meta
|
|
10909
|
+
});
|
|
10910
|
+
};
|
|
10911
|
+
const handleFieldChange = (nextField) => {
|
|
10912
|
+
if (field === void 0) setInternalField(nextField);
|
|
10913
|
+
onFieldChange == null ? void 0 : onFieldChange(nextField);
|
|
10914
|
+
onChange == null ? void 0 : onChange(resolvedCurrent, {
|
|
10915
|
+
operator: resolvedCurrent.operator,
|
|
10916
|
+
field: nextField || null,
|
|
10917
|
+
preset: resolvedCurrent.operator === IN_ROLLING ? resolvedCurrent.preset ?? null : null
|
|
10918
|
+
});
|
|
10919
|
+
};
|
|
10920
|
+
const handleOperatorChange = (nextOperator) => {
|
|
10921
|
+
setPending(null);
|
|
10922
|
+
if (nextOperator === IN_RANGE) {
|
|
10923
|
+
emit({ operator: IN_RANGE, ...EMPTY_RANGE }, { previousOperator: currentOperator });
|
|
10924
|
+
} else if (STATIC_DATE_OPERATORS.has(nextOperator)) {
|
|
10925
|
+
emit({ operator: nextOperator, ...EMPTY_DATE }, { previousOperator: currentOperator });
|
|
10926
|
+
} else if (ROLLING_OPERATORS.has(nextOperator)) {
|
|
10927
|
+
emit(
|
|
10928
|
+
{ operator: nextOperator, amount: 1, unit: "day", direction: "backward" },
|
|
10929
|
+
{ previousOperator: currentOperator }
|
|
10930
|
+
);
|
|
10931
|
+
} else if (PRESENCE_OPERATORS.has(nextOperator)) {
|
|
10932
|
+
emit({ operator: nextOperator }, { previousOperator: currentOperator });
|
|
10933
|
+
} else {
|
|
10934
|
+
emit({ operator: IN_ROLLING, preset: "today" }, { previousOperator: currentOperator });
|
|
10935
|
+
}
|
|
10936
|
+
};
|
|
10937
|
+
const handlePresetChange = (preset) => {
|
|
10938
|
+
if (!preset || preset === DATE_RANGE_CUSTOM_VALUE) {
|
|
10939
|
+
emit({ operator: IN_ROLLING, preset: DATE_RANGE_CUSTOM_VALUE });
|
|
10940
|
+
return;
|
|
10941
|
+
}
|
|
10942
|
+
const option = presets === true ? HS_DATE_PRESETS.find((p) => p.value === preset) : Array.isArray(presets) ? presets.find((p) => p.value === preset) : null;
|
|
10943
|
+
const range = option && typeof option.getRange === "function" ? option.getRange(/* @__PURE__ */ new Date()) : presetToRange(preset);
|
|
10944
|
+
setLastPreset({ key: preset, rangeKey: range ? keyOfRange(range) : null });
|
|
10945
|
+
emit({ operator: IN_ROLLING, preset }, { range });
|
|
10946
|
+
};
|
|
10947
|
+
const handleRollingUnitChange = (compound) => {
|
|
10948
|
+
const [unit, unitDirection] = String(compound || "day:backward").split(":");
|
|
10949
|
+
emit({
|
|
10950
|
+
operator: currentOperator,
|
|
10951
|
+
amount: resolvedCurrent.amount || 1,
|
|
10952
|
+
unit,
|
|
10953
|
+
direction: unitDirection || "backward"
|
|
10954
|
+
});
|
|
10955
|
+
};
|
|
10956
|
+
const handleDateChange = (side, next) => {
|
|
10957
|
+
const displayRange = {
|
|
10958
|
+
...rangeFromValue(resolvedCurrent),
|
|
10959
|
+
...pending ? { [pending.side]: pending.value } : {}
|
|
10960
|
+
};
|
|
10961
|
+
const candidate = { ...displayRange, [side]: next ?? null };
|
|
10962
|
+
if (isValidDateRange(candidate)) {
|
|
10963
|
+
setPending(null);
|
|
10964
|
+
setLastPreset({ key: "", rangeKey: null });
|
|
10965
|
+
emit({ operator: IN_RANGE, ...candidate });
|
|
10966
|
+
} else {
|
|
10967
|
+
setPending({ side, value: next ?? null });
|
|
10968
|
+
}
|
|
10969
|
+
};
|
|
10970
|
+
const handleStaticDateChange = (next) => {
|
|
10971
|
+
emit({ operator: currentOperator, date: next ?? null });
|
|
10972
|
+
};
|
|
10973
|
+
const handleClear = () => {
|
|
10974
|
+
setPending(null);
|
|
10975
|
+
setLastPreset({ key: "", rangeKey: null });
|
|
10976
|
+
if (currentOperator === IN_RANGE) {
|
|
10977
|
+
emit({ operator: IN_RANGE, ...EMPTY_RANGE });
|
|
10978
|
+
} else if (STATIC_DATE_OPERATORS.has(currentOperator)) {
|
|
10979
|
+
emit({ operator: currentOperator, ...EMPTY_DATE });
|
|
10980
|
+
} else if (ROLLING_OPERATORS.has(currentOperator)) {
|
|
10981
|
+
emit({ operator: currentOperator, amount: 1, unit: "day", direction: "backward" });
|
|
10982
|
+
} else if (PRESENCE_OPERATORS.has(currentOperator)) {
|
|
10983
|
+
emit({ operator: currentOperator });
|
|
10984
|
+
} else {
|
|
10985
|
+
emit({ operator: IN_ROLLING, preset: "" });
|
|
10986
|
+
}
|
|
10987
|
+
};
|
|
10988
|
+
const operatorSelect = showOperatorSelect ? h6(Select10, {
|
|
10989
|
+
key: "operator",
|
|
10990
|
+
name: `${name}-operator`,
|
|
10991
|
+
label: COMPACT_LABEL,
|
|
10992
|
+
options: operatorOptions,
|
|
10993
|
+
value: currentOperator,
|
|
10994
|
+
onChange: handleOperatorChange,
|
|
10995
|
+
readOnly
|
|
10996
|
+
}) : null;
|
|
10997
|
+
const fieldSelect = showFieldSelect ? h6(Select10, {
|
|
10998
|
+
key: "field",
|
|
10999
|
+
name: `${name}-field`,
|
|
11000
|
+
label: "",
|
|
11001
|
+
options: fieldOptions,
|
|
11002
|
+
value: currentField,
|
|
11003
|
+
onChange: handleFieldChange,
|
|
11004
|
+
readOnly
|
|
11005
|
+
}) : null;
|
|
11006
|
+
let valueInput = null;
|
|
11007
|
+
const fromInputLabel = showDateLabels ? fromLabel : COMPACT_LABEL;
|
|
11008
|
+
const toInputLabel = showDateLabels ? toLabel : COMPACT_LABEL;
|
|
11009
|
+
const singleDateInputLabel = showDateLabels ? dateLabel : COMPACT_LABEL;
|
|
11010
|
+
if (currentOperator === IN_RANGE) {
|
|
11011
|
+
const committed = rangeFromValue(resolvedCurrent);
|
|
11012
|
+
const display = {
|
|
11013
|
+
...committed,
|
|
11014
|
+
...pending ? { [pending.side]: pending.value } : {}
|
|
11015
|
+
};
|
|
11016
|
+
const invalidSide = pending ? pending.side : null;
|
|
11017
|
+
valueInput = [
|
|
11018
|
+
h6(DateInput6, {
|
|
11019
|
+
key: "from",
|
|
11020
|
+
name: `${name}-from`,
|
|
11021
|
+
label: fromInputLabel,
|
|
11022
|
+
format,
|
|
11023
|
+
value: display.from ?? null,
|
|
11024
|
+
onChange: (next) => handleDateChange("from", next),
|
|
11025
|
+
min,
|
|
11026
|
+
max,
|
|
11027
|
+
readOnly,
|
|
11028
|
+
error: invalidSide === "from",
|
|
11029
|
+
validationMessage: invalidSide === "from" ? invalidRangeMessage : void 0
|
|
11030
|
+
}),
|
|
11031
|
+
isColumn ? null : h6(Text10, { key: "to" }, "to"),
|
|
11032
|
+
h6(DateInput6, {
|
|
11033
|
+
key: "toDate",
|
|
11034
|
+
name: `${name}-to`,
|
|
11035
|
+
label: toInputLabel,
|
|
11036
|
+
format,
|
|
11037
|
+
value: display.to ?? null,
|
|
11038
|
+
onChange: (next) => handleDateChange("to", next),
|
|
11039
|
+
min,
|
|
11040
|
+
max,
|
|
11041
|
+
readOnly,
|
|
11042
|
+
error: invalidSide === "to",
|
|
11043
|
+
validationMessage: invalidSide === "to" ? invalidRangeMessage : void 0
|
|
11044
|
+
})
|
|
11045
|
+
];
|
|
11046
|
+
} else if (STATIC_DATE_OPERATORS.has(currentOperator)) {
|
|
11047
|
+
valueInput = h6(DateInput6, {
|
|
11048
|
+
key: "date",
|
|
11049
|
+
name: `${name}-date`,
|
|
11050
|
+
label: singleDateInputLabel,
|
|
11051
|
+
format,
|
|
11052
|
+
value: resolvedCurrent.date ?? null,
|
|
11053
|
+
onChange: handleStaticDateChange,
|
|
11054
|
+
min,
|
|
11055
|
+
max,
|
|
11056
|
+
readOnly
|
|
11057
|
+
});
|
|
11058
|
+
} else if (ROLLING_OPERATORS.has(currentOperator)) {
|
|
11059
|
+
const compound = `${resolvedCurrent.unit || "day"}:${resolvedCurrent.direction || "backward"}`;
|
|
11060
|
+
valueInput = [
|
|
11061
|
+
h6(NumberInput4, {
|
|
11062
|
+
key: "amount",
|
|
11063
|
+
name: `${name}-amount`,
|
|
11064
|
+
label: COMPACT_LABEL,
|
|
11065
|
+
min: 0,
|
|
11066
|
+
value: resolvedCurrent.amount ?? 1,
|
|
11067
|
+
onChange: (amount) => emit({
|
|
11068
|
+
operator: currentOperator,
|
|
11069
|
+
amount: Number.isFinite(Number(amount)) ? Number(amount) : 0,
|
|
11070
|
+
unit: resolvedCurrent.unit || "day",
|
|
11071
|
+
direction: resolvedCurrent.direction || "backward"
|
|
11072
|
+
}),
|
|
11073
|
+
readOnly
|
|
11074
|
+
}),
|
|
11075
|
+
h6(Select10, {
|
|
11076
|
+
key: "unit",
|
|
11077
|
+
name: `${name}-rolling-unit`,
|
|
11078
|
+
label: COMPACT_LABEL,
|
|
11079
|
+
options: rollingUnitOptions,
|
|
11080
|
+
value: compound,
|
|
11081
|
+
onChange: handleRollingUnitChange,
|
|
11082
|
+
readOnly
|
|
11083
|
+
})
|
|
11084
|
+
];
|
|
11085
|
+
} else if (PRESENCE_OPERATORS.has(currentOperator)) {
|
|
11086
|
+
valueInput = null;
|
|
11087
|
+
} else {
|
|
11088
|
+
const range = presetToRange(resolvedCurrent.preset);
|
|
11089
|
+
const presetValue = resolvedCurrent.preset || (lastPreset.rangeKey && range && lastPreset.rangeKey === keyOfRange(range) ? lastPreset.key : "");
|
|
11090
|
+
valueInput = h6(Select10, {
|
|
11091
|
+
key: "preset",
|
|
11092
|
+
name: `${name}-preset`,
|
|
11093
|
+
label: COMPACT_LABEL,
|
|
11094
|
+
placeholder: presetPlaceholder,
|
|
11095
|
+
options: presetOptions || [],
|
|
11096
|
+
value: presetValue,
|
|
11097
|
+
onChange: handlePresetChange,
|
|
11098
|
+
readOnly
|
|
11099
|
+
});
|
|
11100
|
+
}
|
|
11101
|
+
const valueChildren = [
|
|
11102
|
+
...Array.isArray(valueInput) ? valueInput : valueInput ? [valueInput] : [],
|
|
11103
|
+
showClear ? h6(Link7, { key: "clear", onClick: handleClear }, clearLabel) : null
|
|
11104
|
+
];
|
|
11105
|
+
const children = [operatorSelect, ...valueChildren];
|
|
11106
|
+
if (fieldSelect) {
|
|
11107
|
+
const rowChildren = [
|
|
11108
|
+
h6(Box8, { key: "field-box", flex: "auto", alignSelf: "stretch" }, fieldSelect),
|
|
11109
|
+
operatorSelect ? h6(Box8, { key: "operator-box", flex: "auto", alignSelf: "stretch" }, operatorSelect) : null,
|
|
11110
|
+
...valueChildren.map(
|
|
11111
|
+
(child, index) => (child == null ? void 0 : child.type) === Text10 || (child == null ? void 0 : child.type) === Link7 ? child : h6(Box8, { key: `value-box-${index}`, flex: "auto", alignSelf: "stretch" }, child)
|
|
11112
|
+
)
|
|
11113
|
+
].filter(Boolean);
|
|
11114
|
+
const fieldControl = h6(
|
|
11115
|
+
AutoGrid4,
|
|
11116
|
+
{
|
|
11117
|
+
columnWidth: gridColumnWidth,
|
|
11118
|
+
flexible: true,
|
|
11119
|
+
gap: gap ?? "xs"
|
|
11120
|
+
},
|
|
11121
|
+
...rowChildren
|
|
11122
|
+
);
|
|
11123
|
+
if (!label) return fieldControl;
|
|
11124
|
+
return h6(
|
|
11125
|
+
Flex13,
|
|
11126
|
+
{ direction: "column", gap: "xs" },
|
|
11127
|
+
h6(Text10, { format: { fontWeight: "demibold" } }, label),
|
|
11128
|
+
fieldControl
|
|
11129
|
+
);
|
|
11130
|
+
}
|
|
11131
|
+
const control = h6(
|
|
11132
|
+
Flex13,
|
|
11133
|
+
{
|
|
11134
|
+
direction: isColumn ? "column" : "row",
|
|
11135
|
+
align: isColumn ? "stretch" : "center",
|
|
11136
|
+
gap: gap ?? (isColumn ? "sm" : "xs"),
|
|
11137
|
+
wrap: isColumn ? void 0 : "wrap"
|
|
11138
|
+
},
|
|
11139
|
+
...children
|
|
11140
|
+
);
|
|
11141
|
+
if (!label) return control;
|
|
11142
|
+
return h6(
|
|
11143
|
+
Flex13,
|
|
11144
|
+
{ direction: "column", gap: "xs" },
|
|
11145
|
+
h6(Text10, { format: { fontWeight: "demibold" } }, label),
|
|
11146
|
+
control
|
|
11147
|
+
);
|
|
11148
|
+
};
|
|
11149
|
+
|
|
8743
11150
|
// src/common-components/KeyValueList.js
|
|
8744
|
-
import
|
|
8745
|
-
import { DescriptionList as DescriptionList3, DescriptionListItem as DescriptionListItem3, Flex as
|
|
11151
|
+
import React22 from "react";
|
|
11152
|
+
import { DescriptionList as DescriptionList3, DescriptionListItem as DescriptionListItem3, Flex as Flex14 } from "@hubspot/ui-extensions";
|
|
8746
11153
|
var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
|
|
8747
11154
|
const rows = items.map(
|
|
8748
|
-
(item, index) =>
|
|
11155
|
+
(item, index) => React22.createElement(
|
|
8749
11156
|
DescriptionListItem3,
|
|
8750
11157
|
{
|
|
8751
11158
|
key: item.key ?? item.label ?? `kv-${index}`,
|
|
@@ -8754,16 +11161,17 @@ var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
|
|
|
8754
11161
|
item.value
|
|
8755
11162
|
)
|
|
8756
11163
|
);
|
|
8757
|
-
return
|
|
8758
|
-
|
|
11164
|
+
return React22.createElement(
|
|
11165
|
+
Flex14,
|
|
8759
11166
|
{ direction: "column", gap },
|
|
8760
|
-
|
|
11167
|
+
React22.createElement(DescriptionList3, { direction }, ...rows)
|
|
8761
11168
|
);
|
|
8762
11169
|
};
|
|
11170
|
+
KeyValueList.displayName = "KeyValueList";
|
|
8763
11171
|
|
|
8764
11172
|
// src/common-components/SectionHeader.js
|
|
8765
|
-
import
|
|
8766
|
-
import { Flex as
|
|
11173
|
+
import React23 from "react";
|
|
11174
|
+
import { Flex as Flex15, Heading as Heading2, Text as Text11 } from "@hubspot/ui-extensions";
|
|
8767
11175
|
var SectionHeader = ({
|
|
8768
11176
|
title,
|
|
8769
11177
|
description,
|
|
@@ -8774,12 +11182,12 @@ var SectionHeader = ({
|
|
|
8774
11182
|
}) => {
|
|
8775
11183
|
const body = [];
|
|
8776
11184
|
if (title != null) {
|
|
8777
|
-
body.push(
|
|
11185
|
+
body.push(React23.createElement(Heading2, { key: "title", as: titleAs }, title));
|
|
8778
11186
|
}
|
|
8779
11187
|
if (description != null) {
|
|
8780
11188
|
body.push(
|
|
8781
|
-
|
|
8782
|
-
|
|
11189
|
+
React23.createElement(
|
|
11190
|
+
Text11,
|
|
8783
11191
|
{ key: "description", variant: "microcopy" },
|
|
8784
11192
|
description
|
|
8785
11193
|
)
|
|
@@ -8788,10 +11196,10 @@ var SectionHeader = ({
|
|
|
8788
11196
|
if (children != null) {
|
|
8789
11197
|
body.push(children);
|
|
8790
11198
|
}
|
|
8791
|
-
const content =
|
|
11199
|
+
const content = React23.createElement(Flex15, { direction: "column", gap }, ...body);
|
|
8792
11200
|
if (actions == null) return content;
|
|
8793
|
-
return
|
|
8794
|
-
|
|
11201
|
+
return React23.createElement(
|
|
11202
|
+
Flex15,
|
|
8795
11203
|
{ direction: "row", justify: "between", align: "start", gap: "sm" },
|
|
8796
11204
|
content,
|
|
8797
11205
|
actions
|
|
@@ -8799,8 +11207,8 @@ var SectionHeader = ({
|
|
|
8799
11207
|
};
|
|
8800
11208
|
|
|
8801
11209
|
// src/common-components/Spinner.js
|
|
8802
|
-
import
|
|
8803
|
-
import { Text as
|
|
11210
|
+
import React24, { useEffect as useEffect8, useRef as useRef7, useState as useState12 } from "react";
|
|
11211
|
+
import { Text as Text12 } from "@hubspot/ui-extensions";
|
|
8804
11212
|
|
|
8805
11213
|
// src/common-components/spinners.js
|
|
8806
11214
|
var BRAILLE_DOT_MAP = [
|
|
@@ -9140,8 +11548,8 @@ var Spinner = ({
|
|
|
9140
11548
|
const preset = SPINNERS[name] || SPINNERS[DEFAULT_NAME];
|
|
9141
11549
|
const resolvedFrames = Array.isArray(frames) && frames.length > 0 ? frames : preset.frames;
|
|
9142
11550
|
const resolvedInterval = Number.isFinite(interval) ? interval : preset.interval;
|
|
9143
|
-
const [index, setIndex] =
|
|
9144
|
-
const indexRef =
|
|
11551
|
+
const [index, setIndex] = useState12(0);
|
|
11552
|
+
const indexRef = useRef7(0);
|
|
9145
11553
|
indexRef.current = index;
|
|
9146
11554
|
useEffect8(() => {
|
|
9147
11555
|
if (paused || resolvedFrames.length <= 1) return void 0;
|
|
@@ -9158,10 +11566,10 @@ var Spinner = ({
|
|
|
9158
11566
|
const frame = resolvedFrames[index % resolvedFrames.length];
|
|
9159
11567
|
const suffix = children != null ? children : label;
|
|
9160
11568
|
if (suffix == null || suffix === "") {
|
|
9161
|
-
return
|
|
11569
|
+
return React24.createElement(Text12, rest, frame);
|
|
9162
11570
|
}
|
|
9163
|
-
return
|
|
9164
|
-
|
|
11571
|
+
return React24.createElement(
|
|
11572
|
+
Text12,
|
|
9165
11573
|
rest,
|
|
9166
11574
|
frame,
|
|
9167
11575
|
gap,
|
|
@@ -9169,6 +11577,378 @@ var Spinner = ({
|
|
|
9169
11577
|
);
|
|
9170
11578
|
};
|
|
9171
11579
|
|
|
11580
|
+
// src/safe/catalogs.js
|
|
11581
|
+
var ICON_NAME_ALIASES2 = {
|
|
11582
|
+
alert: "warning",
|
|
11583
|
+
// "alert" is a color, not an icon
|
|
11584
|
+
check: "success",
|
|
11585
|
+
checkmark: "success",
|
|
11586
|
+
danger: "xCircle",
|
|
11587
|
+
// "danger" is a StatusTag variant
|
|
11588
|
+
duplicate: "copy",
|
|
11589
|
+
error: "xCircle",
|
|
11590
|
+
// "error" is a Tag variant, not an icon
|
|
11591
|
+
trash: "delete",
|
|
11592
|
+
pencil: "edit",
|
|
11593
|
+
arrowLeft: "left",
|
|
11594
|
+
arrowRight: "right",
|
|
11595
|
+
arrowUp: "upCarat",
|
|
11596
|
+
arrowDown: "downCarat",
|
|
11597
|
+
cog: "settings",
|
|
11598
|
+
gear: "settings",
|
|
11599
|
+
close: "xCircle",
|
|
11600
|
+
plus: "add",
|
|
11601
|
+
minus: "remove",
|
|
11602
|
+
ok: "success"
|
|
11603
|
+
};
|
|
11604
|
+
var EMPTY_STATE_IMAGES = /* @__PURE__ */ new Set([
|
|
11605
|
+
"addOnReporting",
|
|
11606
|
+
"announcement",
|
|
11607
|
+
"api",
|
|
11608
|
+
"automatedTesting",
|
|
11609
|
+
"beta",
|
|
11610
|
+
"building",
|
|
11611
|
+
"callingSetUp",
|
|
11612
|
+
"companies",
|
|
11613
|
+
"components",
|
|
11614
|
+
"cone",
|
|
11615
|
+
"contacts",
|
|
11616
|
+
"contentStrategy",
|
|
11617
|
+
"customObjects",
|
|
11618
|
+
"customerExperience",
|
|
11619
|
+
"customerSupport",
|
|
11620
|
+
"deals",
|
|
11621
|
+
"developerSecurityUpdate",
|
|
11622
|
+
"electronicSignature",
|
|
11623
|
+
"electronicSignatureEmptyState",
|
|
11624
|
+
"emailConfirmation",
|
|
11625
|
+
"emptyStateCharts",
|
|
11626
|
+
"idea",
|
|
11627
|
+
"integrations",
|
|
11628
|
+
"leads",
|
|
11629
|
+
"lock",
|
|
11630
|
+
"missedGoal",
|
|
11631
|
+
"multipleObjects",
|
|
11632
|
+
"object",
|
|
11633
|
+
"productsShoppingCart",
|
|
11634
|
+
"registration",
|
|
11635
|
+
"sandboxAddOn",
|
|
11636
|
+
"social",
|
|
11637
|
+
"store",
|
|
11638
|
+
"storeDisabled",
|
|
11639
|
+
"successfullyConnectedEmail",
|
|
11640
|
+
"target",
|
|
11641
|
+
"task",
|
|
11642
|
+
"voteAndSearch",
|
|
11643
|
+
"meetings",
|
|
11644
|
+
"tickets"
|
|
11645
|
+
]);
|
|
11646
|
+
var EMPTY_STATE_IMAGE_ALIASES = {
|
|
11647
|
+
"new-project": "components",
|
|
11648
|
+
newProject: "components",
|
|
11649
|
+
empty: "components",
|
|
11650
|
+
default: "components"
|
|
11651
|
+
};
|
|
11652
|
+
var TREND_DIRECTIONS = /* @__PURE__ */ new Set(["increase", "decrease"]);
|
|
11653
|
+
var TREND_DIRECTION_ALIASES = {
|
|
11654
|
+
increasing: "increase",
|
|
11655
|
+
decreasing: "decrease",
|
|
11656
|
+
up: "increase",
|
|
11657
|
+
down: "decrease",
|
|
11658
|
+
positive: "increase",
|
|
11659
|
+
negative: "decrease"
|
|
11660
|
+
};
|
|
11661
|
+
var SAFE_ARRAY_PROPS = {
|
|
11662
|
+
// native @hubspot/ui-extensions
|
|
11663
|
+
Select: ["options"],
|
|
11664
|
+
MultiSelect: ["options"],
|
|
11665
|
+
ToggleGroup: ["options"],
|
|
11666
|
+
StepIndicator: ["stepNames"],
|
|
11667
|
+
// hs-uix
|
|
11668
|
+
DataTable: ["data", "columns", "searchFields", "filters", "selectionActions"],
|
|
11669
|
+
Kanban: ["data", "stages"],
|
|
11670
|
+
FormBuilder: ["fields"],
|
|
11671
|
+
AvatarStack: ["items"],
|
|
11672
|
+
KeyValueList: ["items"],
|
|
11673
|
+
Feed: ["items", "fields"],
|
|
11674
|
+
Calendar: ["events"],
|
|
11675
|
+
CrmKanban: ["cardFields"]
|
|
11676
|
+
};
|
|
11677
|
+
var SAFE_DERIVE_PROPS = {
|
|
11678
|
+
CrmDataTable: ["columns"],
|
|
11679
|
+
CrmKanban: ["stages"]
|
|
11680
|
+
};
|
|
11681
|
+
|
|
11682
|
+
// src/safe/warnings.js
|
|
11683
|
+
var warned = /* @__PURE__ */ new Set();
|
|
11684
|
+
function warnOnce(key, message) {
|
|
11685
|
+
if (warned.has(key)) return;
|
|
11686
|
+
warned.add(key);
|
|
11687
|
+
console.warn(message);
|
|
11688
|
+
}
|
|
11689
|
+
function resetSafeWarnings() {
|
|
11690
|
+
warned.clear();
|
|
11691
|
+
}
|
|
11692
|
+
|
|
11693
|
+
// src/safe/withSafeArrayProps.js
|
|
11694
|
+
import React25 from "react";
|
|
11695
|
+
var arrayProp = (value, componentName, propName) => {
|
|
11696
|
+
if (Array.isArray(value)) return value;
|
|
11697
|
+
if (value == null) return [];
|
|
11698
|
+
warnOnce(
|
|
11699
|
+
`${componentName}-${propName}-not-array`,
|
|
11700
|
+
`[hs-uix/safe] ${componentName}.${propName} must be an array \u2014 received ${typeof value}; using [].`
|
|
11701
|
+
);
|
|
11702
|
+
return [];
|
|
11703
|
+
};
|
|
11704
|
+
function withSafeArrayProps(Component, componentName, propNames, derivePropNames = []) {
|
|
11705
|
+
if (!Component) return void 0;
|
|
11706
|
+
const SafeComponent = React25.forwardRef((props, ref) => {
|
|
11707
|
+
const next = { ...props || {} };
|
|
11708
|
+
for (const propName of propNames) {
|
|
11709
|
+
next[propName] = arrayProp(next[propName], componentName, propName);
|
|
11710
|
+
}
|
|
11711
|
+
for (const propName of derivePropNames) {
|
|
11712
|
+
const value = next[propName];
|
|
11713
|
+
if (value == null || Array.isArray(value)) continue;
|
|
11714
|
+
warnOnce(
|
|
11715
|
+
`${componentName}-${propName}-not-array`,
|
|
11716
|
+
`[hs-uix/safe] ${componentName}.${propName} must be an array \u2014 received ${typeof value}; omitting it so ${componentName} derives it automatically.`
|
|
11717
|
+
);
|
|
11718
|
+
delete next[propName];
|
|
11719
|
+
}
|
|
11720
|
+
return React25.createElement(Component, ref != null ? { ...next, ref } : next);
|
|
11721
|
+
});
|
|
11722
|
+
SafeComponent.displayName = `Safe${componentName}`;
|
|
11723
|
+
return SafeComponent;
|
|
11724
|
+
}
|
|
11725
|
+
|
|
11726
|
+
// src/safe/SafeIcon.js
|
|
11727
|
+
import React26 from "react";
|
|
11728
|
+
import { Icon as Icon2 } from "@hubspot/ui-extensions";
|
|
11729
|
+
var SafeIcon = (props) => {
|
|
11730
|
+
const { name, ...rest } = props || {};
|
|
11731
|
+
if (typeof name === "string" && NATIVE_ICON_NAMES.has(name)) {
|
|
11732
|
+
return React26.createElement(Icon2, { name, ...rest });
|
|
11733
|
+
}
|
|
11734
|
+
const alias = ICON_NAME_ALIASES2[name];
|
|
11735
|
+
if (alias && NATIVE_ICON_NAMES.has(alias)) {
|
|
11736
|
+
warnOnce(
|
|
11737
|
+
`icon-alias-${name}`,
|
|
11738
|
+
`[hs-uix/safe] Icon name "${name}" is not in the catalog \u2014 auto-repaired to "${alias}".`
|
|
11739
|
+
);
|
|
11740
|
+
return React26.createElement(Icon2, { name: alias, ...rest });
|
|
11741
|
+
}
|
|
11742
|
+
warnOnce(
|
|
11743
|
+
`icon-invalid-${name}`,
|
|
11744
|
+
`[hs-uix/safe] Icon name "${name}" is not in the catalog. Rendering a red xCircle placeholder. See NATIVE_ICON_NAMES for the valid list.`
|
|
11745
|
+
);
|
|
11746
|
+
return React26.createElement(Icon2, {
|
|
11747
|
+
...rest,
|
|
11748
|
+
name: "xCircle",
|
|
11749
|
+
color: "alert",
|
|
11750
|
+
screenReaderText: `Invalid icon: ${name ?? "(missing)"}`
|
|
11751
|
+
});
|
|
11752
|
+
};
|
|
11753
|
+
SafeIcon.displayName = "SafeIcon";
|
|
11754
|
+
|
|
11755
|
+
// src/safe/SafeEmptyState.js
|
|
11756
|
+
import React27 from "react";
|
|
11757
|
+
import { EmptyState as EmptyState5 } from "@hubspot/ui-extensions";
|
|
11758
|
+
var SafeEmptyState = (props) => {
|
|
11759
|
+
const { imageName, ...rest } = props || {};
|
|
11760
|
+
if (imageName == null || EMPTY_STATE_IMAGES.has(imageName)) {
|
|
11761
|
+
return React27.createElement(EmptyState5, { imageName, ...rest });
|
|
11762
|
+
}
|
|
11763
|
+
const alias = EMPTY_STATE_IMAGE_ALIASES[imageName];
|
|
11764
|
+
const fallback = alias && EMPTY_STATE_IMAGES.has(alias) ? alias : "components";
|
|
11765
|
+
warnOnce(
|
|
11766
|
+
`emptystate-image-${imageName}`,
|
|
11767
|
+
`[hs-uix/safe] EmptyState imageName "${imageName}" is not valid \u2014 using "${fallback}". See EMPTY_STATE_IMAGES for the valid list.`
|
|
11768
|
+
);
|
|
11769
|
+
return React27.createElement(EmptyState5, { imageName: fallback, ...rest });
|
|
11770
|
+
};
|
|
11771
|
+
SafeEmptyState.displayName = "SafeEmptyState";
|
|
11772
|
+
|
|
11773
|
+
// src/safe/SafeStatisticsTrend.js
|
|
11774
|
+
import React28 from "react";
|
|
11775
|
+
import { StatisticsTrend as StatisticsTrend2 } from "@hubspot/ui-extensions";
|
|
11776
|
+
var SafeStatisticsTrend = (props) => {
|
|
11777
|
+
const { direction, ...rest } = props || {};
|
|
11778
|
+
if (typeof direction !== "string" || TREND_DIRECTIONS.has(direction)) {
|
|
11779
|
+
return React28.createElement(StatisticsTrend2, { direction, ...rest });
|
|
11780
|
+
}
|
|
11781
|
+
const alias = TREND_DIRECTION_ALIASES[direction];
|
|
11782
|
+
if (alias && TREND_DIRECTIONS.has(alias)) {
|
|
11783
|
+
warnOnce(
|
|
11784
|
+
`trend-alias-${direction}`,
|
|
11785
|
+
`[hs-uix/safe] StatisticsTrend direction "${direction}" \u2192 "${alias}". Valid values: increase, decrease.`
|
|
11786
|
+
);
|
|
11787
|
+
return React28.createElement(StatisticsTrend2, { direction: alias, ...rest });
|
|
11788
|
+
}
|
|
11789
|
+
warnOnce(
|
|
11790
|
+
`trend-invalid-${direction}`,
|
|
11791
|
+
`[hs-uix/safe] StatisticsTrend direction "${direction}" is not valid \u2014 defaulting to "increase".`
|
|
11792
|
+
);
|
|
11793
|
+
return React28.createElement(StatisticsTrend2, { ...rest, direction: "increase" });
|
|
11794
|
+
};
|
|
11795
|
+
SafeStatisticsTrend.displayName = "SafeStatisticsTrend";
|
|
11796
|
+
|
|
11797
|
+
// src/safe/SafePopover.js
|
|
11798
|
+
import React29 from "react";
|
|
11799
|
+
import { Tile as Tile7 } from "@hubspot/ui-extensions";
|
|
11800
|
+
import { Popover as Popover2 } from "@hubspot/ui-extensions/experimental";
|
|
11801
|
+
var SafePopover = (props) => {
|
|
11802
|
+
const { children, ...rest } = props || {};
|
|
11803
|
+
return React29.createElement(
|
|
11804
|
+
Popover2,
|
|
11805
|
+
rest,
|
|
11806
|
+
React29.createElement(Tile7, { compact: true }, children)
|
|
11807
|
+
);
|
|
11808
|
+
};
|
|
11809
|
+
SafePopover.displayName = "SafePopover";
|
|
11810
|
+
|
|
11811
|
+
// src/safe/safeComponents.js
|
|
11812
|
+
import { MultiSelect as MultiSelect7, Select as Select11, StepIndicator as StepIndicator2, ToggleGroup as ToggleGroup2 } from "@hubspot/ui-extensions";
|
|
11813
|
+
var wrap = (Component, name) => withSafeArrayProps(Component, name, SAFE_ARRAY_PROPS[name] ?? [], SAFE_DERIVE_PROPS[name] ?? []);
|
|
11814
|
+
var SafeSelect = wrap(Select11, "Select");
|
|
11815
|
+
var SafeMultiSelect = wrap(MultiSelect7, "MultiSelect");
|
|
11816
|
+
var SafeToggleGroup = wrap(ToggleGroup2, "ToggleGroup");
|
|
11817
|
+
var SafeStepIndicator = wrap(StepIndicator2, "StepIndicator");
|
|
11818
|
+
var SafeDataTable = wrap(DataTable, "DataTable");
|
|
11819
|
+
var SafeKanban = wrap(Kanban, "Kanban");
|
|
11820
|
+
var SafeFormBuilder = wrap(FormBuilder, "FormBuilder");
|
|
11821
|
+
var SafeAvatarStack = wrap(AvatarStack, "AvatarStack");
|
|
11822
|
+
var SafeKeyValueList = wrap(KeyValueList, "KeyValueList");
|
|
11823
|
+
var SafeFeed = wrap(Feed, "Feed");
|
|
11824
|
+
var SafeCalendar = wrap(Calendar, "Calendar");
|
|
11825
|
+
var SafeCrmDataTable = wrap(CrmDataTable, "CrmDataTable");
|
|
11826
|
+
var SafeCrmKanban = wrap(CrmKanban, "CrmKanban");
|
|
11827
|
+
|
|
11828
|
+
// src/utils/applyPatches.js
|
|
11829
|
+
function applyPatches(doc, patches) {
|
|
11830
|
+
if (!patches || patches.length === 0) return doc;
|
|
11831
|
+
let out = doc ?? {};
|
|
11832
|
+
for (const op of patches) {
|
|
11833
|
+
out = applyOne(out, op);
|
|
11834
|
+
}
|
|
11835
|
+
return out;
|
|
11836
|
+
}
|
|
11837
|
+
function applyOne(doc, op) {
|
|
11838
|
+
const path = parsePointer(op.path);
|
|
11839
|
+
switch (op.op) {
|
|
11840
|
+
case "add":
|
|
11841
|
+
return setAt(doc, path, op.value, true);
|
|
11842
|
+
case "replace":
|
|
11843
|
+
return setAt(doc, path, op.value, false);
|
|
11844
|
+
case "remove":
|
|
11845
|
+
return removeAt(doc, path);
|
|
11846
|
+
case "move": {
|
|
11847
|
+
const from = parsePointer(op.from);
|
|
11848
|
+
const value = getAt(doc, from);
|
|
11849
|
+
const removed = removeAt(doc, from);
|
|
11850
|
+
return setAt(removed, path, value, true);
|
|
11851
|
+
}
|
|
11852
|
+
case "copy": {
|
|
11853
|
+
const from = parsePointer(op.from);
|
|
11854
|
+
const value = getAt(doc, from);
|
|
11855
|
+
return setAt(doc, path, deepClone2(value), true);
|
|
11856
|
+
}
|
|
11857
|
+
default:
|
|
11858
|
+
console.warn("[hs-uix] applyPatches: ignoring unsupported op:", op.op);
|
|
11859
|
+
return doc;
|
|
11860
|
+
}
|
|
11861
|
+
}
|
|
11862
|
+
function parsePointer(pointer) {
|
|
11863
|
+
if (!pointer || pointer === "/") return [];
|
|
11864
|
+
if (pointer[0] !== "/") {
|
|
11865
|
+
throw new Error(`invalid JSON Pointer (must start with /): ${pointer}`);
|
|
11866
|
+
}
|
|
11867
|
+
return pointer.slice(1).split("/").map((seg) => seg.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
11868
|
+
}
|
|
11869
|
+
function getAt(obj, segs) {
|
|
11870
|
+
let cur = obj;
|
|
11871
|
+
for (const seg of segs) {
|
|
11872
|
+
if (cur == null) return void 0;
|
|
11873
|
+
if (Array.isArray(cur)) {
|
|
11874
|
+
cur = cur[Number(seg)];
|
|
11875
|
+
} else if (typeof cur === "object") {
|
|
11876
|
+
cur = Object.prototype.hasOwnProperty.call(cur, seg) ? cur[seg] : void 0;
|
|
11877
|
+
} else {
|
|
11878
|
+
return void 0;
|
|
11879
|
+
}
|
|
11880
|
+
}
|
|
11881
|
+
return cur;
|
|
11882
|
+
}
|
|
11883
|
+
function setAt(obj, segs, value, insert) {
|
|
11884
|
+
if (segs.length === 0) return value;
|
|
11885
|
+
const [head, ...rest] = segs;
|
|
11886
|
+
if (Array.isArray(obj)) {
|
|
11887
|
+
const next = obj.slice();
|
|
11888
|
+
const idx = head === "-" ? next.length : Number(head);
|
|
11889
|
+
if (rest.length === 0) {
|
|
11890
|
+
if (head === "-") {
|
|
11891
|
+
next.push(value);
|
|
11892
|
+
} else if (insert && Number.isInteger(idx) && idx >= 0 && idx <= next.length) {
|
|
11893
|
+
next.splice(idx, 0, value);
|
|
11894
|
+
} else {
|
|
11895
|
+
next[idx] = value;
|
|
11896
|
+
}
|
|
11897
|
+
return next;
|
|
11898
|
+
}
|
|
11899
|
+
const existing2 = idx >= 0 && idx < next.length ? next[idx] : void 0;
|
|
11900
|
+
next[idx] = setAt(
|
|
11901
|
+
existing2 ?? (looksLikeArrayIndex(rest[0]) ? [] : {}),
|
|
11902
|
+
rest,
|
|
11903
|
+
value,
|
|
11904
|
+
insert
|
|
11905
|
+
);
|
|
11906
|
+
return next;
|
|
11907
|
+
}
|
|
11908
|
+
const base = obj && typeof obj === "object" ? obj : {};
|
|
11909
|
+
if (rest.length === 0) {
|
|
11910
|
+
return { ...base, [head]: value };
|
|
11911
|
+
}
|
|
11912
|
+
const existing = base[head];
|
|
11913
|
+
const child = setAt(
|
|
11914
|
+
existing ?? (looksLikeArrayIndex(rest[0]) ? [] : {}),
|
|
11915
|
+
rest,
|
|
11916
|
+
value,
|
|
11917
|
+
insert
|
|
11918
|
+
);
|
|
11919
|
+
return { ...base, [head]: child };
|
|
11920
|
+
}
|
|
11921
|
+
function removeAt(obj, segs) {
|
|
11922
|
+
if (segs.length === 0) return void 0;
|
|
11923
|
+
const [head, ...rest] = segs;
|
|
11924
|
+
if (Array.isArray(obj)) {
|
|
11925
|
+
const idx = Number(head);
|
|
11926
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= obj.length) return obj;
|
|
11927
|
+
const next = obj.slice();
|
|
11928
|
+
if (rest.length === 0) {
|
|
11929
|
+
next.splice(idx, 1);
|
|
11930
|
+
} else {
|
|
11931
|
+
next[idx] = removeAt(next[idx], rest);
|
|
11932
|
+
}
|
|
11933
|
+
return next;
|
|
11934
|
+
}
|
|
11935
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
11936
|
+
if (rest.length === 0) {
|
|
11937
|
+
const next = { ...obj };
|
|
11938
|
+
delete next[head];
|
|
11939
|
+
return next;
|
|
11940
|
+
}
|
|
11941
|
+
if (!(head in obj)) return obj;
|
|
11942
|
+
return { ...obj, [head]: removeAt(obj[head], rest) };
|
|
11943
|
+
}
|
|
11944
|
+
function looksLikeArrayIndex(seg) {
|
|
11945
|
+
return seg === "-" || /^\d+$/.test(seg);
|
|
11946
|
+
}
|
|
11947
|
+
function deepClone2(v) {
|
|
11948
|
+
const json = v === void 0 ? void 0 : JSON.stringify(v);
|
|
11949
|
+
return json === void 0 ? void 0 : JSON.parse(json);
|
|
11950
|
+
}
|
|
11951
|
+
|
|
9172
11952
|
// src/utils/collections.js
|
|
9173
11953
|
var sumBy = (items, keyOrFn) => (items || []).reduce((total, item) => {
|
|
9174
11954
|
const value = typeof keyOrFn === "function" ? keyOrFn(item) : item == null ? void 0 : item[keyOrFn];
|
|
@@ -9256,9 +12036,19 @@ export {
|
|
|
9256
12036
|
CrmDataTable,
|
|
9257
12037
|
CrmKanban,
|
|
9258
12038
|
CrmLookupSelect,
|
|
12039
|
+
CrmRecordPicker,
|
|
12040
|
+
DATE_FILTER_OPERATORS,
|
|
12041
|
+
DATE_RANGE_CUSTOM_VALUE,
|
|
12042
|
+
DATE_ROLLING_UNIT_OPTIONS,
|
|
12043
|
+
DEFAULT_FEED_TYPE_PRESETS,
|
|
9259
12044
|
DEFAULT_SVG_FONT_WEIGHT,
|
|
9260
12045
|
DataTable,
|
|
12046
|
+
DateRangePicker,
|
|
12047
|
+
EMPTY_STATE_IMAGES,
|
|
12048
|
+
EMPTY_STATE_IMAGE_ALIASES,
|
|
12049
|
+
FILTER_OPERATORS,
|
|
9261
12050
|
Feed,
|
|
12051
|
+
FilterBuilder,
|
|
9262
12052
|
FormBuilder,
|
|
9263
12053
|
HS_DATE_DIRECTION_LABELS,
|
|
9264
12054
|
HS_DATE_PRESETS,
|
|
@@ -9277,21 +12067,61 @@ export {
|
|
|
9277
12067
|
HS_TEXT_COLOR,
|
|
9278
12068
|
ICONS,
|
|
9279
12069
|
ICON_NAMES,
|
|
12070
|
+
ICON_NAME_ALIASES2 as ICON_NAME_ALIASES,
|
|
9280
12071
|
Icon,
|
|
9281
12072
|
Kanban,
|
|
9282
12073
|
KanbanCardActions,
|
|
9283
12074
|
KeyValueList,
|
|
12075
|
+
NATIVE_ICON_NAMES,
|
|
9284
12076
|
NATIVE_ICON_NAME_LIST,
|
|
12077
|
+
SAFE_ARRAY_PROPS,
|
|
12078
|
+
SAFE_DERIVE_PROPS,
|
|
12079
|
+
SKELETON_FILL,
|
|
9285
12080
|
SPINNERS,
|
|
9286
12081
|
SPINNER_NAMES,
|
|
12082
|
+
SafeAvatarStack,
|
|
12083
|
+
SafeCalendar,
|
|
12084
|
+
SafeCrmDataTable,
|
|
12085
|
+
SafeCrmKanban,
|
|
12086
|
+
SafeDataTable,
|
|
12087
|
+
SafeEmptyState,
|
|
12088
|
+
SafeFeed,
|
|
12089
|
+
SafeFormBuilder,
|
|
12090
|
+
SafeIcon,
|
|
12091
|
+
SafeKanban,
|
|
12092
|
+
SafeKeyValueList,
|
|
12093
|
+
SafeMultiSelect,
|
|
12094
|
+
SafePopover,
|
|
12095
|
+
SafeSelect,
|
|
12096
|
+
SafeStatisticsTrend,
|
|
12097
|
+
SafeStepIndicator,
|
|
12098
|
+
SafeToggleGroup,
|
|
9287
12099
|
SectionHeader,
|
|
9288
12100
|
Spinner,
|
|
9289
12101
|
StyledText,
|
|
12102
|
+
TREND_DIRECTIONS,
|
|
12103
|
+
TREND_DIRECTION_ALIASES,
|
|
12104
|
+
UNASSIGNED_LANE_KEY,
|
|
12105
|
+
addFilter,
|
|
12106
|
+
applyPatches,
|
|
12107
|
+
applyTypePreset,
|
|
9290
12108
|
buildCrmSearchConfig,
|
|
9291
12109
|
buildOptions,
|
|
12110
|
+
changeConditionOperator,
|
|
12111
|
+
changeConditionProperty,
|
|
12112
|
+
compareHsDateValues,
|
|
12113
|
+
computeStageCounts,
|
|
12114
|
+
conditionToCrmFilter,
|
|
12115
|
+
countConditions,
|
|
12116
|
+
createCondition,
|
|
12117
|
+
createGroup,
|
|
9292
12118
|
createStatusTagSortComparator,
|
|
9293
12119
|
crmSearchResultToOption,
|
|
12120
|
+
evaluateWip,
|
|
12121
|
+
fieldsFromHubSpotProperties,
|
|
12122
|
+
findNewlyExceededWip,
|
|
9294
12123
|
findOptionLabel2 as findOptionLabel,
|
|
12124
|
+
flushBuffer,
|
|
9295
12125
|
formatCurrency,
|
|
9296
12126
|
formatCurrencyCompact,
|
|
9297
12127
|
formatDate,
|
|
@@ -9299,7 +12129,14 @@ export {
|
|
|
9299
12129
|
formatPercentage,
|
|
9300
12130
|
getAutoStatusTagVariant,
|
|
9301
12131
|
getAutoTagVariant,
|
|
12132
|
+
getLaneKey,
|
|
12133
|
+
getNodeAtPath,
|
|
12134
|
+
getOperatorOptions,
|
|
9302
12135
|
gridToBraille,
|
|
12136
|
+
isConditionNode,
|
|
12137
|
+
isGroupNode,
|
|
12138
|
+
isValidDateRange,
|
|
12139
|
+
lookupTypePreset,
|
|
9303
12140
|
makeAvatarStackDataUri,
|
|
9304
12141
|
makeCrmSearchMultiSelectField,
|
|
9305
12142
|
makeCrmSearchSelectField,
|
|
@@ -9308,10 +12145,28 @@ export {
|
|
|
9308
12145
|
makeStyledTextDataUri,
|
|
9309
12146
|
normalizeCrmSearchRecord,
|
|
9310
12147
|
normalizeCrmSearchRows,
|
|
12148
|
+
operatorExpectsHighValue,
|
|
12149
|
+
operatorExpectsValue,
|
|
12150
|
+
operatorExpectsValues,
|
|
12151
|
+
orderLaneKeys,
|
|
12152
|
+
partitionLanes,
|
|
12153
|
+
partitionNewItems,
|
|
12154
|
+
presetToRange,
|
|
12155
|
+
removeFilter,
|
|
12156
|
+
resetSafeWarnings,
|
|
9311
12157
|
resolveCrmObjectType,
|
|
12158
|
+
resolveLaneLabel,
|
|
12159
|
+
resolveWipLimit,
|
|
9312
12160
|
sumBy,
|
|
9313
12161
|
svgToIconEntry,
|
|
12162
|
+
toCrmSearchFilterGroups,
|
|
12163
|
+
toHsDateValue,
|
|
12164
|
+
toTimestampMs,
|
|
12165
|
+
updateFilter,
|
|
9314
12166
|
useCrmSearchDataSource,
|
|
9315
12167
|
useCrmSearchOptions,
|
|
9316
|
-
useFormPrefill
|
|
12168
|
+
useFormPrefill,
|
|
12169
|
+
validateTree,
|
|
12170
|
+
warnOnce,
|
|
12171
|
+
withSafeArrayProps
|
|
9317
12172
|
};
|