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/feed.mjs
CHANGED
|
@@ -545,7 +545,7 @@ var GENERATED_ICONS = {
|
|
|
545
545
|
"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"] }
|
|
546
546
|
};
|
|
547
547
|
|
|
548
|
-
// src/common-components/
|
|
548
|
+
// src/common-components/nativeIconNames.js
|
|
549
549
|
var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
550
550
|
"add",
|
|
551
551
|
"appointment",
|
|
@@ -557,12 +557,12 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
|
557
557
|
"block",
|
|
558
558
|
"book",
|
|
559
559
|
"bulb",
|
|
560
|
+
"callTranscript",
|
|
560
561
|
"calling",
|
|
561
562
|
"callingHangup",
|
|
562
563
|
"callingMade",
|
|
563
564
|
"callingMissed",
|
|
564
565
|
"callingVoicemail",
|
|
565
|
-
"callTranscript",
|
|
566
566
|
"campaigns",
|
|
567
567
|
"cap",
|
|
568
568
|
"checkCircle",
|
|
@@ -591,13 +591,13 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
|
591
591
|
"enroll",
|
|
592
592
|
"exclamation",
|
|
593
593
|
"exclamationCircle",
|
|
594
|
-
"facebook",
|
|
595
594
|
"faceHappy",
|
|
596
595
|
"faceHappyFilled",
|
|
597
596
|
"faceNeutral",
|
|
598
597
|
"faceNeutralFilled",
|
|
599
598
|
"faceSad",
|
|
600
599
|
"faceSadFilled",
|
|
600
|
+
"facebook",
|
|
601
601
|
"favoriteHollow",
|
|
602
602
|
"file",
|
|
603
603
|
"filledXCircleIcon",
|
|
@@ -738,6 +738,8 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
|
|
|
738
738
|
"zoomIn",
|
|
739
739
|
"zoomOut"
|
|
740
740
|
]);
|
|
741
|
+
|
|
742
|
+
// src/common-components/Icon.js
|
|
741
743
|
var NATIVE_COLORS = /* @__PURE__ */ new Set(["inherit", "alert", "warning", "success"]);
|
|
742
744
|
var NATIVE_SIZE_TOKENS = {
|
|
743
745
|
sm: "sm",
|
|
@@ -950,6 +952,7 @@ var CollectionFilterControl = ({
|
|
|
950
952
|
{ key: name, direction: "row", align: "center", gap: "xs" },
|
|
951
953
|
h4(DateInput, {
|
|
952
954
|
name: `${controlName}-from`,
|
|
955
|
+
label: filter.fromLabel ?? labels.dateFrom,
|
|
953
956
|
placeholder: filter.fromLabel ?? labels.dateFrom,
|
|
954
957
|
format: "medium",
|
|
955
958
|
value: rangeValue.from ?? null,
|
|
@@ -958,6 +961,7 @@ var CollectionFilterControl = ({
|
|
|
958
961
|
h4(Icon, { name: "right", size: "sm" }),
|
|
959
962
|
h4(DateInput, {
|
|
960
963
|
name: `${controlName}-to`,
|
|
964
|
+
label: filter.toLabel ?? labels.dateTo,
|
|
961
965
|
placeholder: filter.toLabel ?? labels.dateTo,
|
|
962
966
|
format: "medium",
|
|
963
967
|
value: rangeValue.to ?? null,
|
|
@@ -1158,6 +1162,110 @@ var buildActiveFilterChips = (filters, values = {}, options = {}) => {
|
|
|
1158
1162
|
return chips;
|
|
1159
1163
|
};
|
|
1160
1164
|
|
|
1165
|
+
// src/feed/feedLiveBuffer.js
|
|
1166
|
+
var isDateValueObject = (v) => v != null && typeof v === "object" && typeof v.year === "number" && typeof v.month === "number" && typeof v.date === "number";
|
|
1167
|
+
var toTimestampMs = (value) => {
|
|
1168
|
+
if (value == null || value === "") return null;
|
|
1169
|
+
if (value instanceof Date) {
|
|
1170
|
+
const time2 = value.getTime();
|
|
1171
|
+
return Number.isNaN(time2) ? null : time2;
|
|
1172
|
+
}
|
|
1173
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
1174
|
+
if (isDateValueObject(value)) {
|
|
1175
|
+
return new Date(value.year, value.month, value.date, value.hour || 0, value.minute || 0).getTime();
|
|
1176
|
+
}
|
|
1177
|
+
const parsed = new Date(value);
|
|
1178
|
+
const time = parsed.getTime();
|
|
1179
|
+
return Number.isNaN(time) ? null : time;
|
|
1180
|
+
};
|
|
1181
|
+
var defaultGetId = (item, index) => (item == null ? void 0 : item.id) ?? (item == null ? void 0 : item.key) ?? index;
|
|
1182
|
+
var partitionNewItems = (prevNewestTs, items, getTs, options = {}) => {
|
|
1183
|
+
const { knownIds = null, getId = defaultGetId } = options;
|
|
1184
|
+
const safeItems = Array.isArray(items) ? items : [];
|
|
1185
|
+
const firstLoad = prevNewestTs == null;
|
|
1186
|
+
const visible = [];
|
|
1187
|
+
const visibleIds = [];
|
|
1188
|
+
const buffered = [];
|
|
1189
|
+
const bufferedIds = [];
|
|
1190
|
+
let newestTs = typeof prevNewestTs === "number" ? prevNewestTs : null;
|
|
1191
|
+
safeItems.forEach((item, index) => {
|
|
1192
|
+
const id = getId(item, index);
|
|
1193
|
+
const ts = toTimestampMs(typeof getTs === "function" ? getTs(item) : void 0);
|
|
1194
|
+
const isKnown = knownIds != null && id !== void 0 && knownIds.has(id);
|
|
1195
|
+
const isNewArrival = !firstLoad && !isKnown && ts != null && ts > prevNewestTs;
|
|
1196
|
+
if (isNewArrival) {
|
|
1197
|
+
buffered.push(item);
|
|
1198
|
+
bufferedIds.push(id);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
visible.push(item);
|
|
1202
|
+
visibleIds.push(id);
|
|
1203
|
+
if (ts != null && (newestTs == null || ts > newestTs)) newestTs = ts;
|
|
1204
|
+
});
|
|
1205
|
+
return { visible, buffered, visibleIds, bufferedIds, newestTs };
|
|
1206
|
+
};
|
|
1207
|
+
var flushBuffer = (visible, buffered, getTs) => {
|
|
1208
|
+
const safeVisible = Array.isArray(visible) ? visible : [];
|
|
1209
|
+
const safeBuffered = Array.isArray(buffered) ? buffered : [];
|
|
1210
|
+
const items = [...safeBuffered, ...safeVisible];
|
|
1211
|
+
let newestTs = null;
|
|
1212
|
+
items.forEach((item) => {
|
|
1213
|
+
const ts = toTimestampMs(typeof getTs === "function" ? getTs(item) : void 0);
|
|
1214
|
+
if (ts != null && (newestTs == null || ts > newestTs)) newestTs = ts;
|
|
1215
|
+
});
|
|
1216
|
+
return { items, flushed: safeBuffered, newestTs };
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// src/feed/feedTypePresets.js
|
|
1220
|
+
var hasValue = (value) => value != null && value !== false && value !== "";
|
|
1221
|
+
var DEFAULT_FEED_TYPE_PRESETS = {
|
|
1222
|
+
call: { icon: "calling", label: "Call" },
|
|
1223
|
+
email: { icon: "email", label: "Email" },
|
|
1224
|
+
incoming_email: { icon: "inbox", label: "Incoming email" },
|
|
1225
|
+
forwarded_email: { icon: "forward", label: "Forwarded email" },
|
|
1226
|
+
meeting: { icon: "appointment", label: "Meeting" },
|
|
1227
|
+
note: { icon: "comment", label: "Note" },
|
|
1228
|
+
task: { icon: "tasks", label: "Task" },
|
|
1229
|
+
sms: { icon: "messages", label: "SMS" },
|
|
1230
|
+
whatsapp: { icon: "messages", label: "WhatsApp" },
|
|
1231
|
+
linkedin_message: { icon: "linkedin", label: "LinkedIn message" },
|
|
1232
|
+
postal_mail: { icon: "send", label: "Postal mail" },
|
|
1233
|
+
conversation: { icon: "questionAnswer", label: "Conversation" }
|
|
1234
|
+
};
|
|
1235
|
+
var lookupTypePreset = (type, presets) => {
|
|
1236
|
+
if (!presets || typeof presets !== "object") return null;
|
|
1237
|
+
if (type == null || type === "") return null;
|
|
1238
|
+
if (Object.prototype.hasOwnProperty.call(presets, type)) return presets[type];
|
|
1239
|
+
const lower = String(type).toLowerCase();
|
|
1240
|
+
if (Object.prototype.hasOwnProperty.call(presets, lower)) return presets[lower];
|
|
1241
|
+
const snake = lower.replace(/[\s-]+/g, "_");
|
|
1242
|
+
if (Object.prototype.hasOwnProperty.call(presets, snake)) return presets[snake];
|
|
1243
|
+
return null;
|
|
1244
|
+
};
|
|
1245
|
+
var applyTypePreset = (item, typePresets) => {
|
|
1246
|
+
if (item == null || typeof item !== "object") return item;
|
|
1247
|
+
const preset = lookupTypePreset(item.type, typePresets);
|
|
1248
|
+
if (!preset || typeof preset !== "object") return item;
|
|
1249
|
+
let next = null;
|
|
1250
|
+
const fill = (key, value) => {
|
|
1251
|
+
if (next === null) next = { ...item };
|
|
1252
|
+
next[key] = value;
|
|
1253
|
+
};
|
|
1254
|
+
if (!hasValue(item.icon) && !hasValue(item.iconName) && hasValue(preset.icon)) {
|
|
1255
|
+
fill("iconName", preset.icon);
|
|
1256
|
+
}
|
|
1257
|
+
if (!hasValue(item.iconColor) && hasValue(preset.color)) {
|
|
1258
|
+
fill("iconColor", preset.color);
|
|
1259
|
+
}
|
|
1260
|
+
if (!hasValue(item.typeLabel) && hasValue(preset.label)) {
|
|
1261
|
+
fill("typeLabel", preset.label);
|
|
1262
|
+
}
|
|
1263
|
+
if (item.statusVariant == null && item.outcomeVariant == null && item.severityVariant == null && hasValue(preset.statusVariant)) {
|
|
1264
|
+
fill("statusVariant", preset.statusVariant);
|
|
1265
|
+
}
|
|
1266
|
+
return next ?? item;
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1161
1269
|
// src/feed/Feed.jsx
|
|
1162
1270
|
var DEFAULT_LABELS3 = {
|
|
1163
1271
|
search: "Search activity...",
|
|
@@ -1171,6 +1279,8 @@ var DEFAULT_LABELS3 = {
|
|
|
1171
1279
|
loadingMessage: "This should only take a moment.",
|
|
1172
1280
|
loadingMore: "Loading...",
|
|
1173
1281
|
loadMore: "View more",
|
|
1282
|
+
newItems: (count) => count === 1 ? "Show 1 new item" : `Show ${count} new items`,
|
|
1283
|
+
newItemTag: "New",
|
|
1174
1284
|
collapseAll: "Collapse all",
|
|
1175
1285
|
expandAll: "Expand all",
|
|
1176
1286
|
emptyTitle: "No activity yet",
|
|
@@ -1183,7 +1293,15 @@ var DEFAULT_LABELS3 = {
|
|
|
1183
1293
|
var DEFAULT_RECORD_LABEL = { singular: "item", plural: "items" };
|
|
1184
1294
|
var DEFAULT_SEARCH_FIELDS = ["title", "subject", "body", "description", "content", "preview", "type", "typeLabel", "actorName", "author"];
|
|
1185
1295
|
var DEFAULT_PAGE_SIZE = 5;
|
|
1186
|
-
var
|
|
1296
|
+
var EMPTY_ITEMS = [];
|
|
1297
|
+
var INITIAL_LIVE_STATE = {
|
|
1298
|
+
source: null,
|
|
1299
|
+
watermark: null,
|
|
1300
|
+
bufferedKeys: EMPTY_ITEMS,
|
|
1301
|
+
knownKeys: EMPTY_ITEMS,
|
|
1302
|
+
newKeys: EMPTY_ITEMS
|
|
1303
|
+
};
|
|
1304
|
+
var hasValue2 = (value) => value != null && value !== false && value !== "";
|
|
1187
1305
|
var keepWordsTogether = (value) => typeof value === "string" ? value.replace(/\s+/g, "\xA0") : value;
|
|
1188
1306
|
var getItemKey = (item, index, getKey) => {
|
|
1189
1307
|
if (typeof getKey === "function") return getKey(item, index);
|
|
@@ -1210,11 +1328,11 @@ var pickHeaderActions = (item) => item == null ? void 0 : item.headerActions;
|
|
|
1210
1328
|
var itemHasExpandableContent = (item, fields) => {
|
|
1211
1329
|
if ((item == null ? void 0 : item.collapsible) === false) return false;
|
|
1212
1330
|
if ((item == null ? void 0 : item.collapsible) === true) return true;
|
|
1213
|
-
if (
|
|
1214
|
-
if (
|
|
1215
|
-
if (
|
|
1216
|
-
if (
|
|
1217
|
-
if (
|
|
1331
|
+
if (hasValue2(pickBody(item))) return true;
|
|
1332
|
+
if (hasValue2(pickActor(item))) return true;
|
|
1333
|
+
if (hasValue2(item == null ? void 0 : item.actions)) return true;
|
|
1334
|
+
if (hasValue2(item == null ? void 0 : item.footer)) return true;
|
|
1335
|
+
if (hasValue2(item == null ? void 0 : item.meta) || hasValue2(item == null ? void 0 : item.metadata)) return true;
|
|
1218
1336
|
if (Array.isArray(fields)) {
|
|
1219
1337
|
if (fields.some((f) => ["body", "footer"].includes(f.placement ?? "body"))) return true;
|
|
1220
1338
|
}
|
|
@@ -1296,7 +1414,7 @@ var getRecordLabel = (recordLabel, count) => {
|
|
|
1296
1414
|
var FeedActorAvatar = ({ item, avatarSize }) => {
|
|
1297
1415
|
const actor = pickActor(item);
|
|
1298
1416
|
const avatar = (item == null ? void 0 : item.avatar) ?? pickActorAvatar(actor);
|
|
1299
|
-
if (!
|
|
1417
|
+
if (!hasValue2(avatar)) return null;
|
|
1300
1418
|
return /* @__PURE__ */ React8.createElement(
|
|
1301
1419
|
AvatarStack,
|
|
1302
1420
|
{
|
|
@@ -1308,10 +1426,10 @@ var FeedActorAvatar = ({ item, avatarSize }) => {
|
|
|
1308
1426
|
);
|
|
1309
1427
|
};
|
|
1310
1428
|
var FeedTypeIcon = ({ item, iconSize }) => {
|
|
1311
|
-
if (
|
|
1429
|
+
if (hasValue2(item == null ? void 0 : item.icon) && typeof item.icon !== "string") return item.icon;
|
|
1312
1430
|
const iconName = typeof (item == null ? void 0 : item.icon) === "string" ? item.icon : item == null ? void 0 : item.iconName;
|
|
1313
|
-
if (!
|
|
1314
|
-
return /* @__PURE__ */ React8.createElement(Icon, { name: iconName, size: iconSize, purpose: "decorative" });
|
|
1431
|
+
if (!hasValue2(iconName)) return null;
|
|
1432
|
+
return /* @__PURE__ */ React8.createElement(Icon, { name: iconName, size: iconSize, color: item == null ? void 0 : item.iconColor, purpose: "decorative" });
|
|
1315
1433
|
};
|
|
1316
1434
|
var FeedActions = ({ actions }) => {
|
|
1317
1435
|
if (!Array.isArray(actions) || actions.length === 0) return actions || null;
|
|
@@ -1334,7 +1452,7 @@ var FeedField = ({ field, item, index }) => {
|
|
|
1334
1452
|
if (field.visible && !field.visible(item)) return null;
|
|
1335
1453
|
const value = getValue(item, field.field);
|
|
1336
1454
|
const rendered = field.render ? field.render(value, item, index) : value;
|
|
1337
|
-
if (!
|
|
1455
|
+
if (!hasValue2(rendered)) return null;
|
|
1338
1456
|
if (field.href) {
|
|
1339
1457
|
const href = typeof field.href === "function" ? field.href(item) : field.href;
|
|
1340
1458
|
return /* @__PURE__ */ React8.createElement(Link2, { href }, rendered);
|
|
@@ -1371,7 +1489,7 @@ var renderPlacedFields = ({ fields, placement, item, index, inline = false }) =>
|
|
|
1371
1489
|
return /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: "xs" }, nodes);
|
|
1372
1490
|
};
|
|
1373
1491
|
var renderHeaderActions = (headerActions) => {
|
|
1374
|
-
if (!
|
|
1492
|
+
if (!hasValue2(headerActions)) return null;
|
|
1375
1493
|
if (!Array.isArray(headerActions)) return headerActions;
|
|
1376
1494
|
return /* @__PURE__ */ React8.createElement(Inline, { gap: "sm", align: "center" }, headerActions.filter(Boolean).map((action, index) => /* @__PURE__ */ React8.createElement(
|
|
1377
1495
|
Link2,
|
|
@@ -1393,6 +1511,8 @@ var DefaultFeedItem = ({
|
|
|
1393
1511
|
collapsible,
|
|
1394
1512
|
expanded,
|
|
1395
1513
|
onToggleExpanded,
|
|
1514
|
+
isNew,
|
|
1515
|
+
newItemTagLabel,
|
|
1396
1516
|
renderActor,
|
|
1397
1517
|
renderTimestamp,
|
|
1398
1518
|
renderMeta,
|
|
@@ -1412,7 +1532,7 @@ var DefaultFeedItem = ({
|
|
|
1412
1532
|
const body = pickBody(item);
|
|
1413
1533
|
const avatar = /* @__PURE__ */ React8.createElement(FeedActorAvatar, { item, avatarSize });
|
|
1414
1534
|
const typeIcon = /* @__PURE__ */ React8.createElement(FeedTypeIcon, { item, iconSize });
|
|
1415
|
-
const hasAvatarNode =
|
|
1535
|
+
const hasAvatarNode = hasValue2(item == null ? void 0 : item.avatar) || hasValue2(pickActorAvatar(rawActor));
|
|
1416
1536
|
const titleFields = fieldsForPlacement(fields, "title");
|
|
1417
1537
|
const titleField = titleFields.length > 0 ? /* @__PURE__ */ React8.createElement(FeedField, { field: titleFields[0], item, index }) : null;
|
|
1418
1538
|
const subtitleFields = renderPlacedFields({ fields, placement: "subtitle", item, index, inline: true });
|
|
@@ -1420,8 +1540,8 @@ var DefaultFeedItem = ({
|
|
|
1420
1540
|
const bodyFields = renderPlacedFields({ fields, placement: "body", item, index });
|
|
1421
1541
|
const footerFields = renderPlacedFields({ fields, placement: "footer", item, index, inline: true });
|
|
1422
1542
|
const titleContent = titleField ?? (item == null ? void 0 : item.title) ?? (item == null ? void 0 : item.subject);
|
|
1423
|
-
const title =
|
|
1424
|
-
const titleText =
|
|
1543
|
+
const title = hasValue2(item == null ? void 0 : item.href) ? /* @__PURE__ */ React8.createElement(Link2, { href: item.href }, titleContent) : titleContent;
|
|
1544
|
+
const titleText = hasValue2(title) ? /* @__PURE__ */ React8.createElement(Text2, { format: { fontWeight: "demibold" }, truncate: true }, title) : null;
|
|
1425
1545
|
const headerLeft = /* @__PURE__ */ React8.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "nowrap" }, collapsible ? /* @__PURE__ */ React8.createElement(Box2, { flex: "none", alignSelf: "center" }, /* @__PURE__ */ React8.createElement(Link2, { variant: "dark", onClick: onToggleExpanded }, /* @__PURE__ */ React8.createElement(
|
|
1426
1546
|
Icon,
|
|
1427
1547
|
{
|
|
@@ -1429,10 +1549,10 @@ var DefaultFeedItem = ({
|
|
|
1429
1549
|
size: "md",
|
|
1430
1550
|
screenReaderText: expanded ? "Collapse" : "Expand"
|
|
1431
1551
|
}
|
|
1432
|
-
))) : null, typeIcon, titleText);
|
|
1433
|
-
const headerRight =
|
|
1552
|
+
))) : null, typeIcon, titleText, isNew ? /* @__PURE__ */ React8.createElement(Box2, { flex: "none" }, /* @__PURE__ */ React8.createElement(Tag2, { variant: "info" }, newItemTagLabel ?? "New")) : null);
|
|
1553
|
+
const headerRight = hasValue2(headerActions) || hasValue2(timestamp) || hasValue2(type) ? /* @__PURE__ */ React8.createElement(Inline, { gap: "sm", align: "center" }, renderHeaderActions(headerActions), hasValue2(type) && /* @__PURE__ */ React8.createElement(Text2, { variant: "microcopy" }, type), hasValue2(timestamp) && /* @__PURE__ */ React8.createElement(Text2, { variant: "microcopy" }, formatTimestamp(timestamp))) : null;
|
|
1434
1554
|
const showBody = !collapsible || expanded;
|
|
1435
|
-
return /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: compact ? "xs" : "sm" }, /* @__PURE__ */ React8.createElement(Flex4, { direction: "row", justify: "between", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ React8.createElement(Box2, { flex: 1 }, headerLeft), headerRight), showBody ? /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: compact ? "xs" : "sm" }, (hasAvatarNode ||
|
|
1555
|
+
return /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: compact ? "xs" : "sm" }, /* @__PURE__ */ React8.createElement(Flex4, { direction: "row", justify: "between", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ React8.createElement(Box2, { flex: 1 }, headerLeft), headerRight), showBody ? /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: compact ? "xs" : "sm" }, (hasAvatarNode || hasValue2(actor) || hasValue2(subtitleFields) || hasValue2(status)) && /* @__PURE__ */ React8.createElement(Flex4, { direction: "row", align: "center", gap: "xs", wrap: "wrap" }, hasAvatarNode ? avatar : null, hasValue2(actor) && /* @__PURE__ */ React8.createElement(Text2, { variant: "microcopy" }, actor), subtitleFields, hasValue2(status) && /* @__PURE__ */ React8.createElement(StatusTag, { variant: statusVariant, hollow: true }, status)), hasValue2(body) && /* @__PURE__ */ React8.createElement(Text2, null, body), bodyFields, Array.isArray(meta) ? meta.length > 0 ? /* @__PURE__ */ React8.createElement(List, { variant: "inline-divided" }, meta) : metaFields : hasValue2(meta) ? /* @__PURE__ */ React8.createElement(Inline, { gap: "xs" }, meta) : metaFields ? metaFields : null, hasValue2(actions) && /* @__PURE__ */ React8.createElement(FeedActions, { actions }), (hasValue2(footer) || hasValue2(footerFields)) && /* @__PURE__ */ React8.createElement(Inline, { gap: "xs", align: "center" }, footerFields, footer)) : null);
|
|
1436
1556
|
};
|
|
1437
1557
|
var applyTab = (items, activeTab, tabField) => {
|
|
1438
1558
|
if (!activeTab || activeTab === "all") return items;
|
|
@@ -1656,10 +1776,19 @@ var Feed = ({
|
|
|
1656
1776
|
collapsedIds,
|
|
1657
1777
|
onCollapsedIdsChange,
|
|
1658
1778
|
showCollapseToggle = true,
|
|
1659
|
-
alignToolbarWithGroups = "auto"
|
|
1779
|
+
alignToolbarWithGroups = "auto",
|
|
1780
|
+
newItemsBehavior = "immediate",
|
|
1781
|
+
onNewItemsFlush,
|
|
1782
|
+
highlightNew = false,
|
|
1783
|
+
typePresets
|
|
1660
1784
|
}) => {
|
|
1661
|
-
const labels = { ...DEFAULT_LABELS3, ...labelOverrides || {} };
|
|
1662
|
-
const safeItems = Array.isArray(items) ? items :
|
|
1785
|
+
const labels = useMemo(() => ({ ...DEFAULT_LABELS3, ...labelOverrides || {} }), [labelOverrides]);
|
|
1786
|
+
const safeItems = Array.isArray(items) ? items : EMPTY_ITEMS;
|
|
1787
|
+
const resolvedTypePresets = typePresets === true ? DEFAULT_FEED_TYPE_PRESETS : typePresets;
|
|
1788
|
+
const presetItems = useMemo(
|
|
1789
|
+
() => resolvedTypePresets ? safeItems.map((item) => applyTypePreset(item, resolvedTypePresets)) : safeItems,
|
|
1790
|
+
[safeItems, resolvedTypePresets]
|
|
1791
|
+
);
|
|
1663
1792
|
const [internalTab, setInternalTab] = useState2(tabValue ?? defaultTab ?? "all");
|
|
1664
1793
|
const [internalSearch, setInternalSearch] = useState2(searchValue ?? "");
|
|
1665
1794
|
const [internalFilters, setInternalFilters] = useState2(filterValues ?? defaultFilterValues);
|
|
@@ -1672,6 +1801,47 @@ var Feed = ({
|
|
|
1672
1801
|
return [];
|
|
1673
1802
|
};
|
|
1674
1803
|
const [internalCollapsedIds, setInternalCollapsedIds] = useState2(computeInitialCollapsed);
|
|
1804
|
+
const bufferNewItems = newItemsBehavior === "pill";
|
|
1805
|
+
const highlightMs = typeof highlightNew === "number" && highlightNew > 0 ? highlightNew : 0;
|
|
1806
|
+
const trackNewItems = bufferNewItems || highlightMs > 0;
|
|
1807
|
+
const [liveState, setLiveState] = useState2(INITIAL_LIVE_STATE);
|
|
1808
|
+
const liveKeyOf = (item, index) => getItemKey(item, index, getKey);
|
|
1809
|
+
if (trackNewItems && liveState.source !== presetItems) {
|
|
1810
|
+
const firstLoad = liveState.source === null;
|
|
1811
|
+
const partition = partitionNewItems(liveState.watermark, presetItems, pickTimestamp, {
|
|
1812
|
+
knownIds: new Set(liveState.knownKeys),
|
|
1813
|
+
getId: liveKeyOf
|
|
1814
|
+
});
|
|
1815
|
+
const now = Date.now();
|
|
1816
|
+
const keptNewKeys = highlightMs > 0 ? liveState.newKeys.filter((entry) => now - entry.at < highlightMs) : EMPTY_ITEMS;
|
|
1817
|
+
if (bufferNewItems) {
|
|
1818
|
+
setLiveState({
|
|
1819
|
+
source: presetItems,
|
|
1820
|
+
watermark: partition.newestTs,
|
|
1821
|
+
bufferedKeys: partition.bufferedIds,
|
|
1822
|
+
knownKeys: partition.visibleIds,
|
|
1823
|
+
newKeys: keptNewKeys
|
|
1824
|
+
});
|
|
1825
|
+
} else {
|
|
1826
|
+
const { newestTs } = flushBuffer(partition.visible, partition.buffered, pickTimestamp);
|
|
1827
|
+
setLiveState({
|
|
1828
|
+
source: presetItems,
|
|
1829
|
+
watermark: newestTs ?? partition.newestTs,
|
|
1830
|
+
bufferedKeys: EMPTY_ITEMS,
|
|
1831
|
+
knownKeys: [...partition.visibleIds, ...partition.bufferedIds],
|
|
1832
|
+
newKeys: highlightMs > 0 && !firstLoad ? [...keptNewKeys, ...partition.bufferedIds.map((key) => ({ key, at: now }))] : keptNewKeys
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
const bufferedKeySet = useMemo(() => new Set(liveState.bufferedKeys), [liveState.bufferedKeys]);
|
|
1837
|
+
const newKeySet = useMemo(
|
|
1838
|
+
() => new Set(liveState.newKeys.map((entry) => entry.key)),
|
|
1839
|
+
[liveState.newKeys]
|
|
1840
|
+
);
|
|
1841
|
+
const sourceItems = useMemo(() => {
|
|
1842
|
+
if (!bufferNewItems || bufferedKeySet.size === 0) return presetItems;
|
|
1843
|
+
return presetItems.filter((item, index) => !bufferedKeySet.has(getItemKey(item, index, getKey)));
|
|
1844
|
+
}, [presetItems, bufferNewItems, bufferedKeySet, getKey]);
|
|
1675
1845
|
const activeTab = tabValue !== void 0 ? tabValue : internalTab;
|
|
1676
1846
|
const activeSearch = searchValue !== void 0 ? searchValue : internalSearch;
|
|
1677
1847
|
const activeFilters = filterValues !== void 0 ? filterValues : internalFilters;
|
|
@@ -1694,6 +1864,17 @@ var Feed = ({
|
|
|
1694
1864
|
useEffect(() => {
|
|
1695
1865
|
if (Array.isArray(collapsedIds)) setInternalCollapsedIds(collapsedIds);
|
|
1696
1866
|
}, [collapsedIds]);
|
|
1867
|
+
useEffect(() => {
|
|
1868
|
+
if (!highlightMs || liveState.newKeys.length === 0) return void 0;
|
|
1869
|
+
const earliestAt = Math.min(...liveState.newKeys.map((entry) => entry.at));
|
|
1870
|
+
const timer = setTimeout(() => {
|
|
1871
|
+
setLiveState((prev) => ({
|
|
1872
|
+
...prev,
|
|
1873
|
+
newKeys: prev.newKeys.filter((entry) => Date.now() - entry.at < highlightMs)
|
|
1874
|
+
}));
|
|
1875
|
+
}, Math.max(16, earliestAt + highlightMs - Date.now()));
|
|
1876
|
+
return () => clearTimeout(timer);
|
|
1877
|
+
}, [highlightMs, liveState.newKeys]);
|
|
1697
1878
|
const emitParamsChange = (next) => {
|
|
1698
1879
|
if (typeof onParamsChange === "function") {
|
|
1699
1880
|
onParamsChange({ tab: activeTab, search: activeSearch, filters: activeFilters, sort: activeSort, ...next });
|
|
@@ -1747,13 +1928,40 @@ var Feed = ({
|
|
|
1747
1928
|
setCollapsedIds(collapsibleKeys);
|
|
1748
1929
|
};
|
|
1749
1930
|
const handleExpandAll = () => setCollapsedIds([]);
|
|
1931
|
+
const handleFlushNewItems = () => {
|
|
1932
|
+
const flushedItems = [];
|
|
1933
|
+
const flushedKeys = [];
|
|
1934
|
+
const keptItems = [];
|
|
1935
|
+
presetItems.forEach((item, index) => {
|
|
1936
|
+
const key = getItemKey(item, index, getKey);
|
|
1937
|
+
if (bufferedKeySet.has(key)) {
|
|
1938
|
+
flushedItems.push(item);
|
|
1939
|
+
flushedKeys.push(key);
|
|
1940
|
+
} else {
|
|
1941
|
+
keptItems.push(item);
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
const { newestTs } = flushBuffer(keptItems, flushedItems, pickTimestamp);
|
|
1945
|
+
const now = Date.now();
|
|
1946
|
+
setLiveState((prev) => ({
|
|
1947
|
+
source: presetItems,
|
|
1948
|
+
watermark: newestTs ?? prev.watermark,
|
|
1949
|
+
bufferedKeys: EMPTY_ITEMS,
|
|
1950
|
+
knownKeys: presetItems.map((item, index) => getItemKey(item, index, getKey)),
|
|
1951
|
+
newKeys: highlightMs > 0 ? [
|
|
1952
|
+
...prev.newKeys.filter((entry) => now - entry.at < highlightMs),
|
|
1953
|
+
...flushedKeys.map((key) => ({ key, at: now }))
|
|
1954
|
+
] : prev.newKeys
|
|
1955
|
+
}));
|
|
1956
|
+
onNewItemsFlush == null ? void 0 : onNewItemsFlush(flushedItems);
|
|
1957
|
+
};
|
|
1750
1958
|
const processedItems = useMemo(() => {
|
|
1751
|
-
if (serverSide) return
|
|
1752
|
-
const tabbed = applyTab(
|
|
1959
|
+
if (serverSide) return sourceItems;
|
|
1960
|
+
const tabbed = applyTab(sourceItems, activeTab, tabField);
|
|
1753
1961
|
const searched = applySearch(tabbed, activeSearch, searchFields);
|
|
1754
1962
|
const filtered = applyFilters(searched, filters, activeFilters);
|
|
1755
1963
|
return applySort(filtered, activeSort, sortOptions);
|
|
1756
|
-
}, [
|
|
1964
|
+
}, [sourceItems, activeTab, tabField, activeSearch, searchFields, filters, activeFilters, activeSort, sortOptions, serverSide]);
|
|
1757
1965
|
const visibleItems = useMemo(
|
|
1758
1966
|
() => processedItems.slice(0, Math.max(0, resolvedMaxItems)),
|
|
1759
1967
|
[processedItems, resolvedMaxItems]
|
|
@@ -1780,8 +1988,8 @@ var Feed = ({
|
|
|
1780
1988
|
const canViewMore = visibleItems.length < processedItems.length;
|
|
1781
1989
|
const shouldShowExternalLoadMore = hasMore && onLoadMore;
|
|
1782
1990
|
const normalizedTabs = useMemo(
|
|
1783
|
-
() => normalizeTabs(tabs,
|
|
1784
|
-
[tabs,
|
|
1991
|
+
() => normalizeTabs(tabs, presetItems, tabField, labels),
|
|
1992
|
+
[tabs, presetItems, tabField, labels]
|
|
1785
1993
|
);
|
|
1786
1994
|
const resolvedShowTabs = showTabs ?? normalizedTabs.length > 1;
|
|
1787
1995
|
const sortControl = Array.isArray(sortOptions) && sortOptions.length > 0 ? /* @__PURE__ */ React8.createElement(
|
|
@@ -1796,7 +2004,7 @@ var Feed = ({
|
|
|
1796
2004
|
) : null;
|
|
1797
2005
|
const countControl = showItemCount ? /* @__PURE__ */ React8.createElement(CollectionCount, { text: itemCountLabel }) : null;
|
|
1798
2006
|
const toolbarRight = sortControl || countControl ? /* @__PURE__ */ React8.createElement(Inline, { gap: "sm", align: "center" }, sortControl, countControl) : null;
|
|
1799
|
-
const firstGroupHasLabel = groups.length > 0 &&
|
|
2007
|
+
const firstGroupHasLabel = groups.length > 0 && hasValue2(groups[0].label);
|
|
1800
2008
|
const hasLeftToolbarControls = resolvedShowSearch || Array.isArray(filters) && filters.length > 0 || activeChips.length > 0;
|
|
1801
2009
|
const alignControlsWithFirstGroup = !renderToolbar && showToolbar && !loading && !error && processedItems.length > 0 && !hasLeftToolbarControls && !!toolbarRight && firstGroupHasLabel && (alignToolbarWithGroups === true || alignToolbarWithGroups === "auto" && (groupByDate || !!groupBy));
|
|
1802
2010
|
const toolbarNode = renderToolbar ? renderToolbar({
|
|
@@ -1846,15 +2054,21 @@ var Feed = ({
|
|
|
1846
2054
|
return activeCollapsedIds.includes(getItemKey(item, i >= 0 ? i : idx, getKey));
|
|
1847
2055
|
});
|
|
1848
2056
|
const collapseToggle = showCollapseToggle && collapsibleVisibleItems.length > 1 && !loading && !error ? /* @__PURE__ */ React8.createElement(Link2, { onClick: allCollapsed ? handleExpandAll : handleCollapseAll }, keepWordsTogether(allCollapsed ? labels.expandAll : labels.collapseAll)) : null;
|
|
1849
|
-
if (
|
|
1850
|
-
const headerBody = /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: "xs" },
|
|
1851
|
-
const headerRight =
|
|
2057
|
+
if (hasValue2(title) || hasValue2(description) || hasValue2(actions) || hasValue2(children) || collapseToggle) {
|
|
2058
|
+
const headerBody = /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", gap: "xs" }, hasValue2(title) && /* @__PURE__ */ React8.createElement(Text2, { format: { fontWeight: "demibold" } }, title), hasValue2(description) && /* @__PURE__ */ React8.createElement(Text2, null, description), children);
|
|
2059
|
+
const headerRight = hasValue2(actions) || collapseToggle ? /* @__PURE__ */ React8.createElement(Inline, { gap: "sm", align: "center" }, actions, collapseToggle) : null;
|
|
1852
2060
|
content.push(
|
|
1853
2061
|
/* @__PURE__ */ React8.createElement(Flex4, { key: "header", direction: "row", justify: "between", align: "start", gap: "sm" }, headerBody, headerRight)
|
|
1854
2062
|
);
|
|
1855
2063
|
}
|
|
1856
2064
|
const bodyContent = [];
|
|
1857
2065
|
if (toolbarNode) bodyContent.push(/* @__PURE__ */ React8.createElement(React8.Fragment, { key: "toolbar" }, toolbarNode));
|
|
2066
|
+
const pendingNewCount = bufferNewItems ? liveState.bufferedKeys.length : 0;
|
|
2067
|
+
if (pendingNewCount > 0 && !loading && !error) {
|
|
2068
|
+
bodyContent.push(
|
|
2069
|
+
/* @__PURE__ */ React8.createElement(Flex4, { key: "new-items-pill", direction: "row", justify: "center" }, /* @__PURE__ */ React8.createElement(Button3, { variant: "secondary", size: "small", onClick: handleFlushNewItems }, typeof labels.newItems === "function" ? labels.newItems(pendingNewCount) : labels.newItems))
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
1858
2072
|
if (loading) {
|
|
1859
2073
|
bodyContent.push(
|
|
1860
2074
|
renderLoadingState ? renderLoadingState({ label: labels.loading }) : (
|
|
@@ -1871,14 +2085,14 @@ var Feed = ({
|
|
|
1871
2085
|
message: labels.errorMessage
|
|
1872
2086
|
}) : /* @__PURE__ */ React8.createElement(Alert, { key: "error", variant: "danger", title: typeof error === "string" ? error : labels.errorTitle }, /* @__PURE__ */ React8.createElement(Text2, null, labels.errorMessage))
|
|
1873
2087
|
);
|
|
1874
|
-
} else if (processedItems.length === 0) {
|
|
2088
|
+
} else if (processedItems.length === 0 && pendingNewCount === 0) {
|
|
1875
2089
|
bodyContent.push(
|
|
1876
2090
|
renderEmptyState ? renderEmptyState({ title: labels.emptyTitle, message: labels.emptyMessage }) : /* @__PURE__ */ React8.createElement(Tile, { key: "empty" }, /* @__PURE__ */ React8.createElement(Flex4, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ React8.createElement(EmptyState, { title: labels.emptyTitle, layout: "vertical" }, /* @__PURE__ */ React8.createElement(Text2, null, labels.emptyMessage))))
|
|
1877
2091
|
);
|
|
1878
2092
|
} else {
|
|
1879
2093
|
bodyContent.push(
|
|
1880
|
-
/* @__PURE__ */ React8.createElement(Flex4, { key: "items", direction: "column", gap: compact ? "xs" : gap }, groups.map((group, groupIndex) => /* @__PURE__ */ React8.createElement(Flex4, { key: group.key, direction: "column", gap: compact ? "xs" : gap },
|
|
1881
|
-
const globalIndex =
|
|
2094
|
+
/* @__PURE__ */ React8.createElement(Flex4, { key: "items", direction: "column", gap: compact ? "xs" : gap }, groups.map((group, groupIndex) => /* @__PURE__ */ React8.createElement(Flex4, { key: group.key, direction: "column", gap: compact ? "xs" : gap }, hasValue2(group.label) && (alignControlsWithFirstGroup && groupIndex === 0 ? /* @__PURE__ */ React8.createElement(Flex4, { direction: "row", justify: "between", align: "center", gap: "sm" }, /* @__PURE__ */ React8.createElement(Text2, { format: { fontWeight: "demibold" } }, group.label), toolbarRight) : /* @__PURE__ */ React8.createElement(Text2, { format: { fontWeight: "demibold" } }, group.label)), group.items.map((item, index) => {
|
|
2095
|
+
const globalIndex = presetItems.indexOf(item);
|
|
1882
2096
|
const itemIndex = globalIndex >= 0 ? globalIndex : index;
|
|
1883
2097
|
const key = getItemKey(item, itemIndex, getKey);
|
|
1884
2098
|
const node = renderItem ? renderItem(item, itemIndex) : /* @__PURE__ */ React8.createElement(
|
|
@@ -1893,6 +2107,8 @@ var Feed = ({
|
|
|
1893
2107
|
collapsible: collapsible !== false && itemHasExpandableContent(item, fields),
|
|
1894
2108
|
expanded: !activeCollapsedIds.includes(key),
|
|
1895
2109
|
onToggleExpanded: () => toggleItemExpanded(key),
|
|
2110
|
+
isNew: highlightMs > 0 && newKeySet.has(key),
|
|
2111
|
+
newItemTagLabel: labels.newItemTag,
|
|
1896
2112
|
renderActor,
|
|
1897
2113
|
renderTimestamp,
|
|
1898
2114
|
renderMeta,
|
|
@@ -1939,6 +2155,13 @@ var Feed = ({
|
|
|
1939
2155
|
if (container === "card" || container === "tile") return /* @__PURE__ */ React8.createElement(Tile, { compact: true }, feed);
|
|
1940
2156
|
return feed;
|
|
1941
2157
|
};
|
|
2158
|
+
Feed.displayName = "Feed";
|
|
1942
2159
|
export {
|
|
1943
|
-
|
|
2160
|
+
DEFAULT_FEED_TYPE_PRESETS,
|
|
2161
|
+
Feed,
|
|
2162
|
+
applyTypePreset,
|
|
2163
|
+
flushBuffer,
|
|
2164
|
+
lookupTypePreset,
|
|
2165
|
+
partitionNewItems,
|
|
2166
|
+
toTimestampMs
|
|
1944
2167
|
};
|