hs-uix 2.1.1 → 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 +355 -57
- package/dist/calendar.mjs +356 -57
- 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 +3208 -287
- package/dist/index.mjs +3156 -283
- 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 +74 -5
- package/src/calendar/index.d.ts +95 -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,6 +7472,7 @@ 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";
|
|
@@ -6781,6 +7481,7 @@ import {
|
|
|
6781
7481
|
AutoGrid as AutoGrid3,
|
|
6782
7482
|
Box as Box6,
|
|
6783
7483
|
Button as Button8,
|
|
7484
|
+
DateInput as DateInput4,
|
|
6784
7485
|
Divider as Divider4,
|
|
6785
7486
|
EmptyState as EmptyState4,
|
|
6786
7487
|
Flex as Flex9,
|
|
@@ -6788,9 +7489,9 @@ import {
|
|
|
6788
7489
|
Image as Image5,
|
|
6789
7490
|
Inline as Inline4,
|
|
6790
7491
|
Link as Link6,
|
|
6791
|
-
LoadingSpinner as
|
|
6792
|
-
Modal,
|
|
6793
|
-
ModalBody,
|
|
7492
|
+
LoadingSpinner as LoadingSpinner3,
|
|
7493
|
+
Modal as Modal2,
|
|
7494
|
+
ModalBody as ModalBody2,
|
|
6794
7495
|
Panel,
|
|
6795
7496
|
PanelBody,
|
|
6796
7497
|
Select as Select6,
|
|
@@ -6800,7 +7501,7 @@ import {
|
|
|
6800
7501
|
TableHead as TableHead2,
|
|
6801
7502
|
TableHeader as TableHeader2,
|
|
6802
7503
|
TableRow as TableRow2,
|
|
6803
|
-
StatusTag as
|
|
7504
|
+
StatusTag as StatusTag3,
|
|
6804
7505
|
Tag as Tag5,
|
|
6805
7506
|
Text as Text7,
|
|
6806
7507
|
Tile as Tile5
|
|
@@ -6809,7 +7510,7 @@ import { Popover } from "@hubspot/ui-extensions/experimental";
|
|
|
6809
7510
|
|
|
6810
7511
|
// src/calendar/dateUtils.js
|
|
6811
7512
|
var MS_PER_DAY = 864e5;
|
|
6812
|
-
var
|
|
7513
|
+
var isDateValueObject3 = (v) => v != null && typeof v === "object" && typeof v.year === "number" && typeof v.month === "number" && typeof v.date === "number";
|
|
6813
7514
|
var fromEpoch = (ms) => {
|
|
6814
7515
|
const d = new Date(ms);
|
|
6815
7516
|
if (Number.isNaN(d.getTime())) return null;
|
|
@@ -6819,7 +7520,7 @@ var fromEpoch = (ms) => {
|
|
|
6819
7520
|
var toDate2 = (value) => {
|
|
6820
7521
|
if (value == null || value === "") return null;
|
|
6821
7522
|
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
|
6822
|
-
if (
|
|
7523
|
+
if (isDateValueObject3(value)) {
|
|
6823
7524
|
return new Date(
|
|
6824
7525
|
value.year,
|
|
6825
7526
|
value.month,
|
|
@@ -6914,7 +7615,7 @@ var buildHours = (startHour = 8, endHour = 20) => {
|
|
|
6914
7615
|
let e = Math.max(0, Math.min(23, Math.round(endHour)));
|
|
6915
7616
|
if (s > e) [s, e] = [e, s];
|
|
6916
7617
|
const hours = [];
|
|
6917
|
-
for (let
|
|
7618
|
+
for (let h7 = s; h7 <= e; h7 += 1) hours.push(h7);
|
|
6918
7619
|
return hours;
|
|
6919
7620
|
};
|
|
6920
7621
|
var WEEKDAY_LONG = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
@@ -7029,9 +7730,172 @@ var makeSpacerDataUri = (height = 24, width = 4) => {
|
|
|
7029
7730
|
return { src: toDataUri(svg), width, height };
|
|
7030
7731
|
};
|
|
7031
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
|
+
|
|
7032
7895
|
// src/calendar/Calendar.jsx
|
|
7033
7896
|
var DEFAULT_MAX_EVENTS_PER_DAY = 3;
|
|
7034
7897
|
var ALL_VIEWS = ["month", "week", "day", "agenda"];
|
|
7898
|
+
var ALL_VIEWS_WITH_RESOURCE = ["month", "week", "day", "resource", "agenda"];
|
|
7035
7899
|
var DEFAULT_DAY_START_HOUR = 8;
|
|
7036
7900
|
var DEFAULT_DAY_END_HOUR = 20;
|
|
7037
7901
|
var DEFAULT_TIME_ZONES = [
|
|
@@ -7067,7 +7931,8 @@ var VIEW_LABELS = {
|
|
|
7067
7931
|
month: "Month",
|
|
7068
7932
|
week: "Week",
|
|
7069
7933
|
day: "Day",
|
|
7070
|
-
agenda: "Agenda"
|
|
7934
|
+
agenda: "Agenda",
|
|
7935
|
+
resource: "Resource"
|
|
7071
7936
|
};
|
|
7072
7937
|
var DEFAULT_LABELS5 = {
|
|
7073
7938
|
today: "Today",
|
|
@@ -7084,7 +7949,11 @@ var DEFAULT_LABELS5 = {
|
|
|
7084
7949
|
errorMessage: "An error occurred while loading events.",
|
|
7085
7950
|
dayDetailTitle: (label) => label,
|
|
7086
7951
|
open: "Open",
|
|
7087
|
-
allDay: "All day"
|
|
7952
|
+
allDay: "All day",
|
|
7953
|
+
reschedule: "Reschedule",
|
|
7954
|
+
pickDate: "Pick date",
|
|
7955
|
+
unassigned: "Unassigned",
|
|
7956
|
+
resource: "Resource"
|
|
7088
7957
|
};
|
|
7089
7958
|
var DEFAULT_EVENT_FIELDS = {
|
|
7090
7959
|
id: "id",
|
|
@@ -7140,8 +8009,9 @@ var truncateMonthLabel = (value, max) => {
|
|
|
7140
8009
|
var MONTH_COL_WIDTH = 160;
|
|
7141
8010
|
var TIMEGRID_DAY_COL = 150;
|
|
7142
8011
|
var TIMEGRID_DAY_COL_SINGLE = 560;
|
|
8012
|
+
var RESOURCE_LABEL_COL_WIDTH = "min";
|
|
7143
8013
|
var HOUR_SLOT_HEIGHT = 64;
|
|
7144
|
-
var EventDetail = ({ event, labels }) => {
|
|
8014
|
+
var EventDetail = ({ event, labels, reschedule, idSuffix = "" }) => {
|
|
7145
8015
|
const { start, end, title, subtitle, href } = event;
|
|
7146
8016
|
let when = "";
|
|
7147
8017
|
if (start) {
|
|
@@ -7155,31 +8025,47 @@ var EventDetail = ({ event, labels }) => {
|
|
|
7155
8025
|
when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
|
|
7156
8026
|
}
|
|
7157
8027
|
}
|
|
7158
|
-
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)
|
|
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);
|
|
7159
8045
|
};
|
|
7160
|
-
var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
|
|
8046
|
+
var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "", reschedule = null) => {
|
|
7161
8047
|
if (mode === "none") return void 0;
|
|
7162
|
-
const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ React14.createElement(EventDetail, { event, labels });
|
|
8048
|
+
const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ React14.createElement(EventDetail, { event, labels, reschedule, idSuffix });
|
|
7163
8049
|
const id = `cal-evt-${event.key}${idSuffix}`;
|
|
7164
8050
|
if (mode === "modal") {
|
|
7165
|
-
return /* @__PURE__ */ React14.createElement(
|
|
8051
|
+
return /* @__PURE__ */ React14.createElement(Modal2, { id, title: event.title || labels.open, width: "small" }, /* @__PURE__ */ React14.createElement(ModalBody2, null, body));
|
|
7166
8052
|
}
|
|
7167
8053
|
if (mode === "panel") {
|
|
7168
8054
|
return /* @__PURE__ */ React14.createElement(Panel, { id, title: event.title || labels.open, width: "small", variant: "modal" }, /* @__PURE__ */ React14.createElement(PanelBody, null, body));
|
|
7169
8055
|
}
|
|
7170
8056
|
return /* @__PURE__ */ React14.createElement(Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, body));
|
|
7171
8057
|
};
|
|
7172
|
-
var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
|
|
8058
|
+
var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule }) => {
|
|
7173
8059
|
const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
|
|
7174
|
-
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "");
|
|
8060
|
+
const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "", reschedule);
|
|
7175
8061
|
const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
|
|
7176
8062
|
const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
|
|
7177
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);
|
|
7178
8064
|
};
|
|
7179
8065
|
var MONTH_SLOT_HEIGHT = 24;
|
|
7180
|
-
var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars }) => {
|
|
8066
|
+
var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars, reschedule, idScope = "" }) => {
|
|
7181
8067
|
const isStartDay = !day || !event.start || isSameDay(event.start, day);
|
|
7182
|
-
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);
|
|
7183
8069
|
const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
|
|
7184
8070
|
const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
|
|
7185
8071
|
const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
|
|
@@ -7190,11 +8076,11 @@ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, lab
|
|
|
7190
8076
|
if (monthEventStyle === "tag") {
|
|
7191
8077
|
return /* @__PURE__ */ React14.createElement(Tag5, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
|
|
7192
8078
|
}
|
|
7193
|
-
return /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(
|
|
8079
|
+
return /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(StatusTag3, { variant: STATUS_VARIANT[variant] || "default" }, label));
|
|
7194
8080
|
};
|
|
7195
|
-
var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
|
|
8081
|
+
var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule, idScope = "" }) => {
|
|
7196
8082
|
const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
|
|
7197
|
-
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);
|
|
7198
8084
|
const href = event.href;
|
|
7199
8085
|
return /* @__PURE__ */ React14.createElement(Button8, { variant: "transparent", size: "sm", href: href ? href.url : void 0, overlay, onClick: handleClick }, event.title || "--");
|
|
7200
8086
|
};
|
|
@@ -7259,6 +8145,40 @@ var Toolbar = ({
|
|
|
7259
8145
|
}
|
|
7260
8146
|
));
|
|
7261
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
|
+
};
|
|
7262
8182
|
var MonthView = ({
|
|
7263
8183
|
refDate,
|
|
7264
8184
|
now,
|
|
@@ -7273,45 +8193,23 @@ var MonthView = ({
|
|
|
7273
8193
|
}) => {
|
|
7274
8194
|
const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
|
|
7275
8195
|
const today = now || /* @__PURE__ */ new Date();
|
|
7276
|
-
const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
|
|
7277
8196
|
const renderCell = (day) => {
|
|
7278
8197
|
const dayEvents = eventsForDay(day);
|
|
7279
8198
|
const inMonth = isSameMonth(day, refDate);
|
|
7280
8199
|
const isToday = isSameDay(day, today);
|
|
7281
8200
|
if (renderDayCell) return renderDayCell(day, dayEvents);
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
} else {
|
|
7291
|
-
slots.push(/* @__PURE__ */ React14.createElement(Image5, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
|
|
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
|
|
7292
8209
|
}
|
|
7293
|
-
|
|
7294
|
-
if (hasOverflow) {
|
|
7295
|
-
slots.push(
|
|
7296
|
-
slotRow(
|
|
7297
|
-
"more",
|
|
7298
|
-
/* @__PURE__ */ React14.createElement(
|
|
7299
|
-
Link6,
|
|
7300
|
-
{
|
|
7301
|
-
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", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "bold" } }, 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)))))
|
|
7302
|
-
},
|
|
7303
|
-
labels.more(dayEvents.length - maxEventsPerDay)
|
|
7304
|
-
)
|
|
7305
|
-
)
|
|
7306
|
-
);
|
|
7307
|
-
} else {
|
|
7308
|
-
slots.push(
|
|
7309
|
-
/* @__PURE__ */ React14.createElement(Image5, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
|
|
7310
|
-
);
|
|
7311
|
-
}
|
|
7312
|
-
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()))), slots));
|
|
8210
|
+
)));
|
|
7313
8211
|
};
|
|
7314
|
-
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) => {
|
|
7315
8213
|
const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
|
|
7316
8214
|
return /* @__PURE__ */ React14.createElement(TableRow2, { key: wi }, days.map((day) => /* @__PURE__ */ React14.createElement(TableCell2, { key: day.getTime(), width: "min" }, renderCell(day))));
|
|
7317
8215
|
})));
|
|
@@ -7331,13 +8229,40 @@ var AgendaView = ({ rangeStart, rangeEnd, eventsForDay, chipProps, labels, rende
|
|
|
7331
8229
|
}
|
|
7332
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 })))));
|
|
7333
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
|
+
};
|
|
7334
8259
|
var formatTimedDuration = (start, end) => {
|
|
7335
8260
|
const mins = Math.max(0, Math.round(((end || start).getTime() - start.getTime()) / 6e4));
|
|
7336
|
-
const
|
|
8261
|
+
const h7 = Math.floor(mins / 60);
|
|
7337
8262
|
const m = mins % 60;
|
|
7338
|
-
if (
|
|
7339
|
-
if (m === 0) return `${
|
|
7340
|
-
return `${
|
|
8263
|
+
if (h7 === 0) return `${m} min`;
|
|
8264
|
+
if (m === 0) return `${h7} hr`;
|
|
8265
|
+
return `${h7} hr ${m} min`;
|
|
7341
8266
|
};
|
|
7342
8267
|
var hourSpan = (event) => {
|
|
7343
8268
|
const start = event.start;
|
|
@@ -7378,7 +8303,8 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7378
8303
|
chipProps.overlayMode,
|
|
7379
8304
|
chipProps.renderEventDetail,
|
|
7380
8305
|
chipProps.labels,
|
|
7381
|
-
`-tg${mode}${hour}-${dayMs}
|
|
8306
|
+
`-tg${mode}${hour}-${dayMs}`,
|
|
8307
|
+
chipProps.reschedule
|
|
7382
8308
|
);
|
|
7383
8309
|
const handleClick = chipProps.onEventClick ? () => chipProps.onEventClick(e.raw, e) : void 0;
|
|
7384
8310
|
const variant = VALID_VARIANTS.has(e.color) ? e.color : "default";
|
|
@@ -7398,7 +8324,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7398
8324
|
sub = `\u2191 cont. through ${endLabel}`;
|
|
7399
8325
|
}
|
|
7400
8326
|
const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
|
|
7401
|
-
return /* @__PURE__ */ React14.createElement(Flex9, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(
|
|
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);
|
|
7402
8328
|
};
|
|
7403
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)));
|
|
7404
8330
|
const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
|
|
@@ -7417,7 +8343,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
|
|
|
7417
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 ? (
|
|
7418
8344
|
// Trailing nbsp pads the chip's right edge so the final letter
|
|
7419
8345
|
// ("M" in "11 AM") isn't clipped by the min-width TIME column.
|
|
7420
|
-
/* @__PURE__ */ React14.createElement(
|
|
8346
|
+
/* @__PURE__ */ React14.createElement(StatusTag3, { variant: "info" }, `${formatHourLabel(hour)}\xA0`)
|
|
7421
8347
|
) : /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, formatHourLabel(hour)))), dayData.map(({ day, timed }) => {
|
|
7422
8348
|
const occupying = timed.map((t) => {
|
|
7423
8349
|
let visStart = Math.max(t.sHour, dayStartHour);
|
|
@@ -7463,6 +8389,14 @@ var Calendar = (props) => {
|
|
|
7463
8389
|
// time grid (week / day)
|
|
7464
8390
|
dayStartHour = DEFAULT_DAY_START_HOUR,
|
|
7465
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,
|
|
7466
8400
|
// timezone
|
|
7467
8401
|
timeZone: controlledTimeZone,
|
|
7468
8402
|
defaultTimeZone,
|
|
@@ -7501,10 +8435,12 @@ var Calendar = (props) => {
|
|
|
7501
8435
|
const fields = useMemo5(() => ({ ...DEFAULT_EVENT_FIELDS, ...eventFields || {} }), [eventFields]);
|
|
7502
8436
|
const [internalView, setInternalView] = useState6(defaultView);
|
|
7503
8437
|
const view = controlledView != null ? controlledView : internalView;
|
|
8438
|
+
const resourceEnabled = resources && resources.length > 0 || resourceField != null;
|
|
7504
8439
|
const enabledViews = useMemo5(() => {
|
|
7505
|
-
const
|
|
7506
|
-
|
|
7507
|
-
|
|
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]);
|
|
7508
8444
|
const setView = useCallback5(
|
|
7509
8445
|
(next) => {
|
|
7510
8446
|
if (controlledView == null) setInternalView(next);
|
|
@@ -7529,7 +8465,9 @@ var Calendar = (props) => {
|
|
|
7529
8465
|
const focusedDate = (controlledFocusedDate != null ? toDate2(controlledFocusedDate) : internalDate) || startOfDay(nowWall);
|
|
7530
8466
|
const stepFor = useCallback5(
|
|
7531
8467
|
(dir) => {
|
|
7532
|
-
if (view === "week" || view === "agenda"
|
|
8468
|
+
if (view === "week" || view === "agenda" || view === "resource") {
|
|
8469
|
+
return addDays(focusedDate, dir * 7);
|
|
8470
|
+
}
|
|
7533
8471
|
if (view === "day") return addDays(focusedDate, dir);
|
|
7534
8472
|
return addMonths(focusedDate, dir);
|
|
7535
8473
|
},
|
|
@@ -7556,7 +8494,7 @@ var Calendar = (props) => {
|
|
|
7556
8494
|
rangeEnd: endOfDay(flat[flat.length - 1])
|
|
7557
8495
|
};
|
|
7558
8496
|
}
|
|
7559
|
-
if (view === "week") {
|
|
8497
|
+
if (view === "week" || view === "resource") {
|
|
7560
8498
|
const days = buildWeekDays(focusedDate, weekStartsOn, hideWeekends);
|
|
7561
8499
|
return {
|
|
7562
8500
|
weeks: null,
|
|
@@ -7629,14 +8567,18 @@ var Calendar = (props) => {
|
|
|
7629
8567
|
);
|
|
7630
8568
|
const normalized = useMemo5(
|
|
7631
8569
|
() => (events || []).map((raw, index) => {
|
|
7632
|
-
const
|
|
7633
|
-
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);
|
|
7634
8574
|
const id = resolveField(raw, fields.id);
|
|
7635
8575
|
return {
|
|
7636
8576
|
key: id != null ? String(id) : `evt-${index}`,
|
|
7637
8577
|
id,
|
|
7638
8578
|
start,
|
|
7639
8579
|
end: endRaw || start,
|
|
8580
|
+
sourceStart,
|
|
8581
|
+
sourceEnd: sourceEnd || sourceStart,
|
|
7640
8582
|
title: resolveField(raw, fields.title),
|
|
7641
8583
|
subtitle: resolveField(raw, fields.subtitle),
|
|
7642
8584
|
color: resolveField(raw, fields.color),
|
|
@@ -7673,12 +8615,40 @@ var Calendar = (props) => {
|
|
|
7673
8615
|
}, [rangeKey]);
|
|
7674
8616
|
const title = useMemo5(() => {
|
|
7675
8617
|
if (view === "day") return formatDayTitle(focusedDate);
|
|
7676
|
-
if (view === "week" || view === "agenda") {
|
|
7677
|
-
const days = buildWeekDays(focusedDate, weekStartsOn, view
|
|
8618
|
+
if (view === "week" || view === "agenda" || view === "resource") {
|
|
8619
|
+
const days = buildWeekDays(focusedDate, weekStartsOn, view !== "agenda" && hideWeekends);
|
|
7678
8620
|
return formatRangeTitle(days[0], days[days.length - 1]);
|
|
7679
8621
|
}
|
|
7680
8622
|
return formatMonthTitle(focusedDate);
|
|
7681
8623
|
}, [view, focusedDate, weekStartsOn, hideWeekends]);
|
|
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]);
|
|
7682
8652
|
const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
|
|
7683
8653
|
const chipProps = {
|
|
7684
8654
|
overlayMode,
|
|
@@ -7686,8 +8656,19 @@ var Calendar = (props) => {
|
|
|
7686
8656
|
onEventClick,
|
|
7687
8657
|
labels,
|
|
7688
8658
|
monthEventStyle: safeMonthEventStyle,
|
|
7689
|
-
monthEventMaxChars
|
|
8659
|
+
monthEventMaxChars,
|
|
8660
|
+
reschedule
|
|
7690
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]);
|
|
7691
8672
|
const timeZoneOptions = useMemo5(() => {
|
|
7692
8673
|
if (!showTimeZoneSelect) return null;
|
|
7693
8674
|
const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
|
|
@@ -7729,7 +8710,7 @@ var Calendar = (props) => {
|
|
|
7729
8710
|
);
|
|
7730
8711
|
let body;
|
|
7731
8712
|
if (loading) {
|
|
7732
|
-
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 }));
|
|
7733
8714
|
} else if (error) {
|
|
7734
8715
|
body = renderErrorState ? renderErrorState({ error }) : /* @__PURE__ */ React14.createElement(Alert4, { title: labels.errorTitle, variant: "error" }, typeof error === "string" ? error : labels.errorMessage);
|
|
7735
8716
|
} else if (view === "month") {
|
|
@@ -7748,6 +8729,19 @@ var Calendar = (props) => {
|
|
|
7748
8729
|
labels
|
|
7749
8730
|
}
|
|
7750
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
|
+
);
|
|
7751
8745
|
} else if (view === "week" || view === "day") {
|
|
7752
8746
|
body = /* @__PURE__ */ React14.createElement(
|
|
7753
8747
|
TimeGridView,
|
|
@@ -7777,9 +8771,696 @@ var Calendar = (props) => {
|
|
|
7777
8771
|
}
|
|
7778
8772
|
return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "sm" }, toolbar, body);
|
|
7779
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
|
+
};
|
|
7780
9461
|
|
|
7781
9462
|
// src/common-components/AutoTag.js
|
|
7782
|
-
import
|
|
9463
|
+
import React16 from "react";
|
|
7783
9464
|
import { Tag as Tag6 } from "@hubspot/ui-extensions";
|
|
7784
9465
|
|
|
7785
9466
|
// src/utils/tagVariants.js
|
|
@@ -7978,7 +9659,7 @@ var AutoTag = ({
|
|
|
7978
9659
|
overrides,
|
|
7979
9660
|
fallback
|
|
7980
9661
|
});
|
|
7981
|
-
return
|
|
9662
|
+
return React16.createElement(
|
|
7982
9663
|
Tag6,
|
|
7983
9664
|
{ variant: resolvedVariant, ...props },
|
|
7984
9665
|
displayValue
|
|
@@ -7986,8 +9667,8 @@ var AutoTag = ({
|
|
|
7986
9667
|
};
|
|
7987
9668
|
|
|
7988
9669
|
// src/common-components/AutoStatusTag.js
|
|
7989
|
-
import
|
|
7990
|
-
import { StatusTag as
|
|
9670
|
+
import React17 from "react";
|
|
9671
|
+
import { StatusTag as StatusTag4 } from "@hubspot/ui-extensions";
|
|
7991
9672
|
var AutoStatusTag = ({
|
|
7992
9673
|
value,
|
|
7993
9674
|
status,
|
|
@@ -8003,20 +9684,20 @@ var AutoStatusTag = ({
|
|
|
8003
9684
|
overrides,
|
|
8004
9685
|
fallback
|
|
8005
9686
|
});
|
|
8006
|
-
return
|
|
8007
|
-
|
|
9687
|
+
return React17.createElement(
|
|
9688
|
+
StatusTag4,
|
|
8008
9689
|
{ variant: resolvedVariant, ...props },
|
|
8009
9690
|
displayValue
|
|
8010
9691
|
);
|
|
8011
9692
|
};
|
|
8012
9693
|
|
|
8013
9694
|
// src/common-components/CrmLookupSelect.js
|
|
8014
|
-
import
|
|
8015
|
-
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";
|
|
8016
9697
|
|
|
8017
9698
|
// src/utils/crmSearchAdapters.js
|
|
8018
|
-
import
|
|
8019
|
-
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";
|
|
8020
9701
|
|
|
8021
9702
|
// src/utils/objectPath.js
|
|
8022
9703
|
var getByPath = (obj, path) => {
|
|
@@ -8133,9 +9814,9 @@ var useCrmSearchDataSource = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) =>
|
|
|
8133
9814
|
error,
|
|
8134
9815
|
mapResponse
|
|
8135
9816
|
} = options;
|
|
8136
|
-
const config =
|
|
9817
|
+
const config = useMemo7(() => buildCrmSearchConfig(params, options), [params, options]);
|
|
8137
9818
|
const response = useCrmSearch(config, format);
|
|
8138
|
-
return
|
|
9819
|
+
return useMemo7(() => {
|
|
8139
9820
|
var _a;
|
|
8140
9821
|
const rows = mapResponse ? mapResponse(response) : normalizeCrmSearchRows(response, { idField: rowIdField, ...row || EMPTY_OBJECT });
|
|
8141
9822
|
const resolvedTotal = typeof totalCount === "function" ? totalCount(response) : totalCount ?? pickTotal(response, rows.length);
|
|
@@ -8173,7 +9854,7 @@ var crmSearchResultToOption = (row, options = EMPTY_OBJECT) => {
|
|
|
8173
9854
|
var useCrmSearchOptions = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
|
|
8174
9855
|
const dataSource = useCrmSearchDataSource(params, options);
|
|
8175
9856
|
const optionConfig = options.option || options;
|
|
8176
|
-
return
|
|
9857
|
+
return useMemo7(() => ({
|
|
8177
9858
|
...dataSource,
|
|
8178
9859
|
options: dataSource.rows.map((row) => crmSearchResultToOption(row, optionConfig))
|
|
8179
9860
|
}), [dataSource, optionConfig]);
|
|
@@ -8302,29 +9983,29 @@ var CrmDataTable = ({
|
|
|
8302
9983
|
...props
|
|
8303
9984
|
}) => {
|
|
8304
9985
|
var _a, _b;
|
|
8305
|
-
const [params, setParams] =
|
|
8306
|
-
const resolvedProperties =
|
|
8307
|
-
const resolvedColumns =
|
|
9986
|
+
const [params, setParams] = useState8({ search: "", filters: {}, sort: null });
|
|
9987
|
+
const resolvedProperties = useMemo7(() => properties, [properties]);
|
|
9988
|
+
const resolvedColumns = useMemo7(
|
|
8308
9989
|
() => columns || inferCrmColumns(resolvedProperties),
|
|
8309
9990
|
[columns, resolvedProperties]
|
|
8310
9991
|
);
|
|
8311
9992
|
const resolvedSearchFields = searchFields || resolvedProperties;
|
|
8312
|
-
const autoFilterFields =
|
|
9993
|
+
const autoFilterFields = useMemo7(
|
|
8313
9994
|
() => normalizeAutoFilterFields(autoFilters, resolvedProperties),
|
|
8314
9995
|
[autoFilters, resolvedProperties]
|
|
8315
9996
|
);
|
|
8316
9997
|
const autoFilterLabelsRef = useRef5({});
|
|
8317
|
-
const defaultPropertyMap =
|
|
9998
|
+
const defaultPropertyMap = useMemo7(
|
|
8318
9999
|
() => Object.fromEntries(resolvedProperties.map((property) => [property, property])),
|
|
8319
10000
|
[resolvedProperties]
|
|
8320
10001
|
);
|
|
8321
10002
|
const effectivePropertyMap = propertyMap || defaultPropertyMap;
|
|
8322
|
-
const resolvedSortMap =
|
|
10003
|
+
const resolvedSortMap = useMemo7(
|
|
8323
10004
|
() => sortMap || ((sort) => crmSortsFromState(sort, effectivePropertyMap)),
|
|
8324
10005
|
[sortMap, effectivePropertyMap]
|
|
8325
10006
|
);
|
|
8326
10007
|
const resolvedMapRecord = mapRecord || defaultCrmMapRecord;
|
|
8327
|
-
const dataSourceOptions =
|
|
10008
|
+
const dataSourceOptions = useMemo7(
|
|
8328
10009
|
() => ({
|
|
8329
10010
|
objectType: resolveCrmObjectType(objectType),
|
|
8330
10011
|
properties: resolvedProperties,
|
|
@@ -8338,15 +10019,15 @@ var CrmDataTable = ({
|
|
|
8338
10019
|
}),
|
|
8339
10020
|
[objectType, resolvedProperties, pageLength, format, filterMap, effectivePropertyMap, resolvedSortMap, rowIdField, resolvedMapRecord]
|
|
8340
10021
|
);
|
|
8341
|
-
const [serverQuerying, setServerQuerying] =
|
|
10022
|
+
const [serverQuerying, setServerQuerying] = useState8(!!serverSide);
|
|
8342
10023
|
const effectiveParams = serverQuerying ? params : EMPTY_CRM_PARAMS;
|
|
8343
10024
|
const dataSource = useCrmSearchDataSource(effectiveParams, dataSourceOptions);
|
|
8344
|
-
const queryKey =
|
|
10025
|
+
const queryKey = useMemo7(
|
|
8345
10026
|
() => stableStringify({ effectiveParams, objectType, properties: resolvedProperties, pageLength }),
|
|
8346
10027
|
[effectiveParams, objectType, resolvedProperties, pageLength]
|
|
8347
10028
|
);
|
|
8348
|
-
const [accumulatedRows, setAccumulatedRows] =
|
|
8349
|
-
const [requestedPage, setRequestedPage] =
|
|
10029
|
+
const [accumulatedRows, setAccumulatedRows] = useState8(EMPTY_ARRAY);
|
|
10030
|
+
const [requestedPage, setRequestedPage] = useState8(1);
|
|
8350
10031
|
const lastQueryKeyRef = useRef5(queryKey);
|
|
8351
10032
|
const loadedRows = accumulatedRows.length ? accumulatedRows : dataSource.data;
|
|
8352
10033
|
useEffect7(() => {
|
|
@@ -8375,7 +10056,7 @@ var CrmDataTable = ({
|
|
|
8375
10056
|
useEffect7(() => {
|
|
8376
10057
|
ensurePageLoaded(requestedPage);
|
|
8377
10058
|
}, [requestedPage, ensurePageLoaded]);
|
|
8378
|
-
const generatedFilters =
|
|
10059
|
+
const generatedFilters = useMemo7(
|
|
8379
10060
|
() => buildAutoFiltersFromRows({
|
|
8380
10061
|
rows: loadedRows,
|
|
8381
10062
|
fields: autoFilterFields,
|
|
@@ -8385,7 +10066,7 @@ var CrmDataTable = ({
|
|
|
8385
10066
|
[loadedRows, autoFilterFields, autoFilterMaxOptions]
|
|
8386
10067
|
);
|
|
8387
10068
|
const resolvedFilters = filters || generatedFilters;
|
|
8388
|
-
const table =
|
|
10069
|
+
const table = React18.createElement(DataTable, {
|
|
8389
10070
|
title: title || `${prettifyPropertyName(objectType)} records`,
|
|
8390
10071
|
data: loadedRows,
|
|
8391
10072
|
loading: dataSource.loading || ((_b = dataSource.response) == null ? void 0 : _b.isRefetching),
|
|
@@ -8409,11 +10090,11 @@ var CrmDataTable = ({
|
|
|
8409
10090
|
const total = dataSource.totalCount;
|
|
8410
10091
|
const capped = typeof total === "number" && total > loadedRows.length;
|
|
8411
10092
|
if (!capped) return table;
|
|
8412
|
-
return
|
|
8413
|
-
|
|
10093
|
+
return React18.createElement(
|
|
10094
|
+
Flex11,
|
|
8414
10095
|
{ direction: "column", gap: "xs" },
|
|
8415
|
-
|
|
8416
|
-
|
|
10096
|
+
React18.createElement(
|
|
10097
|
+
Text9,
|
|
8417
10098
|
{ variant: "microcopy" },
|
|
8418
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.`
|
|
8419
10100
|
),
|
|
@@ -8447,25 +10128,25 @@ var CrmKanban = ({
|
|
|
8447
10128
|
...props
|
|
8448
10129
|
}) => {
|
|
8449
10130
|
var _a, _b;
|
|
8450
|
-
const [params, setParams] =
|
|
8451
|
-
const resolvedProperties =
|
|
10131
|
+
const [params, setParams] = useState8(EMPTY_CRM_PARAMS);
|
|
10132
|
+
const resolvedProperties = useMemo7(() => properties, [properties]);
|
|
8452
10133
|
const resolvedSearchFields = searchFields || resolvedProperties;
|
|
8453
|
-
const autoFilterFields =
|
|
10134
|
+
const autoFilterFields = useMemo7(
|
|
8454
10135
|
() => normalizeAutoFilterFields(autoFilters, resolvedProperties),
|
|
8455
10136
|
[autoFilters, resolvedProperties]
|
|
8456
10137
|
);
|
|
8457
10138
|
const autoFilterLabelsRef = useRef5({});
|
|
8458
|
-
const defaultPropertyMap =
|
|
10139
|
+
const defaultPropertyMap = useMemo7(
|
|
8459
10140
|
() => Object.fromEntries(resolvedProperties.map((property) => [property, property])),
|
|
8460
10141
|
[resolvedProperties]
|
|
8461
10142
|
);
|
|
8462
10143
|
const effectivePropertyMap = propertyMap || defaultPropertyMap;
|
|
8463
|
-
const resolvedSortMap =
|
|
10144
|
+
const resolvedSortMap = useMemo7(
|
|
8464
10145
|
() => sortMap || ((sort) => crmSortsFromState(sort, effectivePropertyMap)),
|
|
8465
10146
|
[sortMap, effectivePropertyMap]
|
|
8466
10147
|
);
|
|
8467
10148
|
const resolvedMapRecord = mapRecord || defaultCrmMapRecord;
|
|
8468
|
-
const dataSourceOptions =
|
|
10149
|
+
const dataSourceOptions = useMemo7(
|
|
8469
10150
|
() => ({
|
|
8470
10151
|
objectType: resolveCrmObjectType(objectType),
|
|
8471
10152
|
properties: resolvedProperties,
|
|
@@ -8479,14 +10160,14 @@ var CrmKanban = ({
|
|
|
8479
10160
|
}),
|
|
8480
10161
|
[objectType, resolvedProperties, pageLength, format, filterMap, effectivePropertyMap, resolvedSortMap, rowIdField, resolvedMapRecord]
|
|
8481
10162
|
);
|
|
8482
|
-
const [serverQuerying, setServerQuerying] =
|
|
10163
|
+
const [serverQuerying, setServerQuerying] = useState8(!!serverSide);
|
|
8483
10164
|
const effectiveParams = serverQuerying ? params : EMPTY_CRM_PARAMS;
|
|
8484
10165
|
const dataSource = useCrmSearchDataSource(effectiveParams, dataSourceOptions);
|
|
8485
|
-
const queryKey =
|
|
10166
|
+
const queryKey = useMemo7(
|
|
8486
10167
|
() => stableStringify({ effectiveParams, objectType, properties: resolvedProperties, pageLength }),
|
|
8487
10168
|
[effectiveParams, objectType, resolvedProperties, pageLength]
|
|
8488
10169
|
);
|
|
8489
|
-
const [accumulatedRows, setAccumulatedRows] =
|
|
10170
|
+
const [accumulatedRows, setAccumulatedRows] = useState8(EMPTY_ARRAY);
|
|
8490
10171
|
const lastQueryKeyRef = useRef5(queryKey);
|
|
8491
10172
|
const loadedRows = accumulatedRows.length ? accumulatedRows : dataSource.data;
|
|
8492
10173
|
useEffect7(() => {
|
|
@@ -8513,7 +10194,7 @@ var CrmKanban = ({
|
|
|
8513
10194
|
if (!dataSource.hasMore || dataSource.loading || ((_a2 = dataSource.response) == null ? void 0 : _a2.isRefetching)) return;
|
|
8514
10195
|
(_c = (_b2 = dataSource.pagination) == null ? void 0 : _b2.nextPage) == null ? void 0 : _c.call(_b2);
|
|
8515
10196
|
}, [onLoadMore, dataSource.hasMore, dataSource.loading, dataSource.response, dataSource.pagination]);
|
|
8516
|
-
const generatedFilters =
|
|
10197
|
+
const generatedFilters = useMemo7(
|
|
8517
10198
|
() => buildAutoFiltersFromRows({
|
|
8518
10199
|
rows: loadedRows,
|
|
8519
10200
|
fields: autoFilterFields,
|
|
@@ -8523,7 +10204,7 @@ var CrmKanban = ({
|
|
|
8523
10204
|
[loadedRows, autoFilterFields, autoFilterMaxOptions]
|
|
8524
10205
|
);
|
|
8525
10206
|
const resolvedFilters = filters || generatedFilters;
|
|
8526
|
-
const resolvedStages =
|
|
10207
|
+
const resolvedStages = useMemo7(() => {
|
|
8527
10208
|
if (stages) return stages;
|
|
8528
10209
|
const seen = [];
|
|
8529
10210
|
for (const row of loadedRows) {
|
|
@@ -8535,7 +10216,7 @@ var CrmKanban = ({
|
|
|
8535
10216
|
label: typeof stageLabels === "function" ? stageLabels(value) : stageLabels && stageLabels[value] || prettifyPropertyName(String(value))
|
|
8536
10217
|
}));
|
|
8537
10218
|
}, [stages, stageLabels, loadedRows, groupBy]);
|
|
8538
|
-
const resolvedStageMeta =
|
|
10219
|
+
const resolvedStageMeta = useMemo7(() => {
|
|
8539
10220
|
if (stageMeta || !dataSource.hasMore) return stageMeta;
|
|
8540
10221
|
return Object.fromEntries(resolvedStages.map((stage) => {
|
|
8541
10222
|
var _a2;
|
|
@@ -8549,7 +10230,7 @@ var CrmKanban = ({
|
|
|
8549
10230
|
];
|
|
8550
10231
|
}));
|
|
8551
10232
|
}, [stageMeta, dataSource.hasMore, dataSource.loading, dataSource.response, dataSource.totalCount, resolvedStages]);
|
|
8552
|
-
const board =
|
|
10233
|
+
const board = React18.createElement(Kanban, {
|
|
8553
10234
|
title: title || `${prettifyPropertyName(objectType)} board`,
|
|
8554
10235
|
data: loadedRows,
|
|
8555
10236
|
loading: dataSource.loading || ((_b = dataSource.response) == null ? void 0 : _b.isRefetching),
|
|
@@ -8572,17 +10253,19 @@ var CrmKanban = ({
|
|
|
8572
10253
|
const total = dataSource.totalCount;
|
|
8573
10254
|
const capped = typeof total === "number" && total > loadedRows.length;
|
|
8574
10255
|
if (!capped) return board;
|
|
8575
|
-
return
|
|
8576
|
-
|
|
10256
|
+
return React18.createElement(
|
|
10257
|
+
Flex11,
|
|
8577
10258
|
{ direction: "column", gap: "xs" },
|
|
8578
|
-
|
|
8579
|
-
|
|
10259
|
+
React18.createElement(
|
|
10260
|
+
Text9,
|
|
8580
10261
|
{ variant: "microcopy" },
|
|
8581
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.`
|
|
8582
10263
|
),
|
|
8583
10264
|
board
|
|
8584
10265
|
);
|
|
8585
10266
|
};
|
|
10267
|
+
CrmDataTable.displayName = "CrmDataTable";
|
|
10268
|
+
CrmKanban.displayName = "CrmKanban";
|
|
8586
10269
|
|
|
8587
10270
|
// src/common-components/CrmLookupSelect.js
|
|
8588
10271
|
var EMPTY_ARRAY2 = [];
|
|
@@ -8635,12 +10318,12 @@ var CrmLookupSelect = ({
|
|
|
8635
10318
|
loadingOption,
|
|
8636
10319
|
selectProps = EMPTY_OBJECT2
|
|
8637
10320
|
}) => {
|
|
8638
|
-
const [inputValue, setInputValue] =
|
|
8639
|
-
const [pickedOptions, setPickedOptions] =
|
|
10321
|
+
const [inputValue, setInputValue] = useState9(query || "");
|
|
10322
|
+
const [pickedOptions, setPickedOptions] = useState9(EMPTY_ARRAY2);
|
|
8640
10323
|
const debouncedInput = useDebounce2(inputValue, debounce > 0 ? debounce : 1);
|
|
8641
10324
|
const search = debounce > 0 ? debouncedInput : inputValue;
|
|
8642
10325
|
const effectiveSearch = search && search.length >= minSearchLength ? search : "";
|
|
8643
|
-
const optionConfig =
|
|
10326
|
+
const optionConfig = useMemo8(
|
|
8644
10327
|
() => makeOptionConfig({ option, labelProperty, valueProperty, descriptionProperty }),
|
|
8645
10328
|
[option, labelProperty, valueProperty, descriptionProperty]
|
|
8646
10329
|
);
|
|
@@ -8658,7 +10341,7 @@ var CrmLookupSelect = ({
|
|
|
8658
10341
|
);
|
|
8659
10342
|
const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
|
|
8660
10343
|
const hasQuery = effectiveSearch.length > 0;
|
|
8661
|
-
const options =
|
|
10344
|
+
const options = useMemo8(() => {
|
|
8662
10345
|
const remembered = [...selectedOptions || EMPTY_ARRAY2, ...pickedOptions];
|
|
8663
10346
|
const baseOptions = mergeSelectedOptions(dataSource.options || EMPTY_ARRAY2, remembered, value);
|
|
8664
10347
|
if (isSearching && loadingOption) return [loadingOption, ...baseOptions];
|
|
@@ -8697,7 +10380,281 @@ var CrmLookupSelect = ({
|
|
|
8697
10380
|
},
|
|
8698
10381
|
...selectProps
|
|
8699
10382
|
};
|
|
8700
|
-
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
|
+
);
|
|
8701
10658
|
};
|
|
8702
10659
|
|
|
8703
10660
|
// src/common-components/datePresets.js
|
|
@@ -8722,12 +10679,480 @@ var HS_DATE_DIRECTION_LABELS = {
|
|
|
8722
10679
|
desc: "Descending"
|
|
8723
10680
|
};
|
|
8724
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
|
+
|
|
8725
11150
|
// src/common-components/KeyValueList.js
|
|
8726
|
-
import
|
|
8727
|
-
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";
|
|
8728
11153
|
var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
|
|
8729
11154
|
const rows = items.map(
|
|
8730
|
-
(item, index) =>
|
|
11155
|
+
(item, index) => React22.createElement(
|
|
8731
11156
|
DescriptionListItem3,
|
|
8732
11157
|
{
|
|
8733
11158
|
key: item.key ?? item.label ?? `kv-${index}`,
|
|
@@ -8736,16 +11161,17 @@ var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
|
|
|
8736
11161
|
item.value
|
|
8737
11162
|
)
|
|
8738
11163
|
);
|
|
8739
|
-
return
|
|
8740
|
-
|
|
11164
|
+
return React22.createElement(
|
|
11165
|
+
Flex14,
|
|
8741
11166
|
{ direction: "column", gap },
|
|
8742
|
-
|
|
11167
|
+
React22.createElement(DescriptionList3, { direction }, ...rows)
|
|
8743
11168
|
);
|
|
8744
11169
|
};
|
|
11170
|
+
KeyValueList.displayName = "KeyValueList";
|
|
8745
11171
|
|
|
8746
11172
|
// src/common-components/SectionHeader.js
|
|
8747
|
-
import
|
|
8748
|
-
import { Flex as
|
|
11173
|
+
import React23 from "react";
|
|
11174
|
+
import { Flex as Flex15, Heading as Heading2, Text as Text11 } from "@hubspot/ui-extensions";
|
|
8749
11175
|
var SectionHeader = ({
|
|
8750
11176
|
title,
|
|
8751
11177
|
description,
|
|
@@ -8756,12 +11182,12 @@ var SectionHeader = ({
|
|
|
8756
11182
|
}) => {
|
|
8757
11183
|
const body = [];
|
|
8758
11184
|
if (title != null) {
|
|
8759
|
-
body.push(
|
|
11185
|
+
body.push(React23.createElement(Heading2, { key: "title", as: titleAs }, title));
|
|
8760
11186
|
}
|
|
8761
11187
|
if (description != null) {
|
|
8762
11188
|
body.push(
|
|
8763
|
-
|
|
8764
|
-
|
|
11189
|
+
React23.createElement(
|
|
11190
|
+
Text11,
|
|
8765
11191
|
{ key: "description", variant: "microcopy" },
|
|
8766
11192
|
description
|
|
8767
11193
|
)
|
|
@@ -8770,10 +11196,10 @@ var SectionHeader = ({
|
|
|
8770
11196
|
if (children != null) {
|
|
8771
11197
|
body.push(children);
|
|
8772
11198
|
}
|
|
8773
|
-
const content =
|
|
11199
|
+
const content = React23.createElement(Flex15, { direction: "column", gap }, ...body);
|
|
8774
11200
|
if (actions == null) return content;
|
|
8775
|
-
return
|
|
8776
|
-
|
|
11201
|
+
return React23.createElement(
|
|
11202
|
+
Flex15,
|
|
8777
11203
|
{ direction: "row", justify: "between", align: "start", gap: "sm" },
|
|
8778
11204
|
content,
|
|
8779
11205
|
actions
|
|
@@ -8781,8 +11207,8 @@ var SectionHeader = ({
|
|
|
8781
11207
|
};
|
|
8782
11208
|
|
|
8783
11209
|
// src/common-components/Spinner.js
|
|
8784
|
-
import
|
|
8785
|
-
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";
|
|
8786
11212
|
|
|
8787
11213
|
// src/common-components/spinners.js
|
|
8788
11214
|
var BRAILLE_DOT_MAP = [
|
|
@@ -9122,8 +11548,8 @@ var Spinner = ({
|
|
|
9122
11548
|
const preset = SPINNERS[name] || SPINNERS[DEFAULT_NAME];
|
|
9123
11549
|
const resolvedFrames = Array.isArray(frames) && frames.length > 0 ? frames : preset.frames;
|
|
9124
11550
|
const resolvedInterval = Number.isFinite(interval) ? interval : preset.interval;
|
|
9125
|
-
const [index, setIndex] =
|
|
9126
|
-
const indexRef =
|
|
11551
|
+
const [index, setIndex] = useState12(0);
|
|
11552
|
+
const indexRef = useRef7(0);
|
|
9127
11553
|
indexRef.current = index;
|
|
9128
11554
|
useEffect8(() => {
|
|
9129
11555
|
if (paused || resolvedFrames.length <= 1) return void 0;
|
|
@@ -9140,10 +11566,10 @@ var Spinner = ({
|
|
|
9140
11566
|
const frame = resolvedFrames[index % resolvedFrames.length];
|
|
9141
11567
|
const suffix = children != null ? children : label;
|
|
9142
11568
|
if (suffix == null || suffix === "") {
|
|
9143
|
-
return
|
|
11569
|
+
return React24.createElement(Text12, rest, frame);
|
|
9144
11570
|
}
|
|
9145
|
-
return
|
|
9146
|
-
|
|
11571
|
+
return React24.createElement(
|
|
11572
|
+
Text12,
|
|
9147
11573
|
rest,
|
|
9148
11574
|
frame,
|
|
9149
11575
|
gap,
|
|
@@ -9151,6 +11577,378 @@ var Spinner = ({
|
|
|
9151
11577
|
);
|
|
9152
11578
|
};
|
|
9153
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
|
+
|
|
9154
11952
|
// src/utils/collections.js
|
|
9155
11953
|
var sumBy = (items, keyOrFn) => (items || []).reduce((total, item) => {
|
|
9156
11954
|
const value = typeof keyOrFn === "function" ? keyOrFn(item) : item == null ? void 0 : item[keyOrFn];
|
|
@@ -9238,9 +12036,19 @@ export {
|
|
|
9238
12036
|
CrmDataTable,
|
|
9239
12037
|
CrmKanban,
|
|
9240
12038
|
CrmLookupSelect,
|
|
12039
|
+
CrmRecordPicker,
|
|
12040
|
+
DATE_FILTER_OPERATORS,
|
|
12041
|
+
DATE_RANGE_CUSTOM_VALUE,
|
|
12042
|
+
DATE_ROLLING_UNIT_OPTIONS,
|
|
12043
|
+
DEFAULT_FEED_TYPE_PRESETS,
|
|
9241
12044
|
DEFAULT_SVG_FONT_WEIGHT,
|
|
9242
12045
|
DataTable,
|
|
12046
|
+
DateRangePicker,
|
|
12047
|
+
EMPTY_STATE_IMAGES,
|
|
12048
|
+
EMPTY_STATE_IMAGE_ALIASES,
|
|
12049
|
+
FILTER_OPERATORS,
|
|
9243
12050
|
Feed,
|
|
12051
|
+
FilterBuilder,
|
|
9244
12052
|
FormBuilder,
|
|
9245
12053
|
HS_DATE_DIRECTION_LABELS,
|
|
9246
12054
|
HS_DATE_PRESETS,
|
|
@@ -9259,21 +12067,61 @@ export {
|
|
|
9259
12067
|
HS_TEXT_COLOR,
|
|
9260
12068
|
ICONS,
|
|
9261
12069
|
ICON_NAMES,
|
|
12070
|
+
ICON_NAME_ALIASES2 as ICON_NAME_ALIASES,
|
|
9262
12071
|
Icon,
|
|
9263
12072
|
Kanban,
|
|
9264
12073
|
KanbanCardActions,
|
|
9265
12074
|
KeyValueList,
|
|
12075
|
+
NATIVE_ICON_NAMES,
|
|
9266
12076
|
NATIVE_ICON_NAME_LIST,
|
|
12077
|
+
SAFE_ARRAY_PROPS,
|
|
12078
|
+
SAFE_DERIVE_PROPS,
|
|
12079
|
+
SKELETON_FILL,
|
|
9267
12080
|
SPINNERS,
|
|
9268
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,
|
|
9269
12099
|
SectionHeader,
|
|
9270
12100
|
Spinner,
|
|
9271
12101
|
StyledText,
|
|
12102
|
+
TREND_DIRECTIONS,
|
|
12103
|
+
TREND_DIRECTION_ALIASES,
|
|
12104
|
+
UNASSIGNED_LANE_KEY,
|
|
12105
|
+
addFilter,
|
|
12106
|
+
applyPatches,
|
|
12107
|
+
applyTypePreset,
|
|
9272
12108
|
buildCrmSearchConfig,
|
|
9273
12109
|
buildOptions,
|
|
12110
|
+
changeConditionOperator,
|
|
12111
|
+
changeConditionProperty,
|
|
12112
|
+
compareHsDateValues,
|
|
12113
|
+
computeStageCounts,
|
|
12114
|
+
conditionToCrmFilter,
|
|
12115
|
+
countConditions,
|
|
12116
|
+
createCondition,
|
|
12117
|
+
createGroup,
|
|
9274
12118
|
createStatusTagSortComparator,
|
|
9275
12119
|
crmSearchResultToOption,
|
|
12120
|
+
evaluateWip,
|
|
12121
|
+
fieldsFromHubSpotProperties,
|
|
12122
|
+
findNewlyExceededWip,
|
|
9276
12123
|
findOptionLabel2 as findOptionLabel,
|
|
12124
|
+
flushBuffer,
|
|
9277
12125
|
formatCurrency,
|
|
9278
12126
|
formatCurrencyCompact,
|
|
9279
12127
|
formatDate,
|
|
@@ -9281,7 +12129,14 @@ export {
|
|
|
9281
12129
|
formatPercentage,
|
|
9282
12130
|
getAutoStatusTagVariant,
|
|
9283
12131
|
getAutoTagVariant,
|
|
12132
|
+
getLaneKey,
|
|
12133
|
+
getNodeAtPath,
|
|
12134
|
+
getOperatorOptions,
|
|
9284
12135
|
gridToBraille,
|
|
12136
|
+
isConditionNode,
|
|
12137
|
+
isGroupNode,
|
|
12138
|
+
isValidDateRange,
|
|
12139
|
+
lookupTypePreset,
|
|
9285
12140
|
makeAvatarStackDataUri,
|
|
9286
12141
|
makeCrmSearchMultiSelectField,
|
|
9287
12142
|
makeCrmSearchSelectField,
|
|
@@ -9290,10 +12145,28 @@ export {
|
|
|
9290
12145
|
makeStyledTextDataUri,
|
|
9291
12146
|
normalizeCrmSearchRecord,
|
|
9292
12147
|
normalizeCrmSearchRows,
|
|
12148
|
+
operatorExpectsHighValue,
|
|
12149
|
+
operatorExpectsValue,
|
|
12150
|
+
operatorExpectsValues,
|
|
12151
|
+
orderLaneKeys,
|
|
12152
|
+
partitionLanes,
|
|
12153
|
+
partitionNewItems,
|
|
12154
|
+
presetToRange,
|
|
12155
|
+
removeFilter,
|
|
12156
|
+
resetSafeWarnings,
|
|
9293
12157
|
resolveCrmObjectType,
|
|
12158
|
+
resolveLaneLabel,
|
|
12159
|
+
resolveWipLimit,
|
|
9294
12160
|
sumBy,
|
|
9295
12161
|
svgToIconEntry,
|
|
12162
|
+
toCrmSearchFilterGroups,
|
|
12163
|
+
toHsDateValue,
|
|
12164
|
+
toTimestampMs,
|
|
12165
|
+
updateFilter,
|
|
9296
12166
|
useCrmSearchDataSource,
|
|
9297
12167
|
useCrmSearchOptions,
|
|
9298
|
-
useFormPrefill
|
|
12168
|
+
useFormPrefill,
|
|
12169
|
+
validateTree,
|
|
12170
|
+
warnOnce,
|
|
12171
|
+
withSafeArrayProps
|
|
9299
12172
|
};
|