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.
Files changed (49) hide show
  1. package/README.md +3 -1
  2. package/common-components.d.ts +319 -68
  3. package/dist/calendar.js +355 -57
  4. package/dist/calendar.mjs +356 -57
  5. package/dist/common-components.js +3546 -88
  6. package/dist/common-components.mjs +3530 -84
  7. package/dist/datatable.js +108 -18
  8. package/dist/datatable.mjs +108 -18
  9. package/dist/experimental.js +2876 -0
  10. package/dist/experimental.mjs +2883 -0
  11. package/dist/feed.js +267 -38
  12. package/dist/feed.mjs +260 -37
  13. package/dist/filter.js +1379 -0
  14. package/dist/filter.mjs +1334 -0
  15. package/dist/form.js +222 -26
  16. package/dist/form.mjs +227 -27
  17. package/dist/index.js +3208 -287
  18. package/dist/index.mjs +3156 -283
  19. package/dist/kanban.js +282 -62
  20. package/dist/kanban.mjs +273 -61
  21. package/dist/safe.js +9207 -0
  22. package/dist/safe.mjs +9298 -0
  23. package/dist/utils.js +491 -75
  24. package/dist/utils.mjs +491 -75
  25. package/experimental.d.ts +1 -0
  26. package/filter.d.ts +1 -0
  27. package/index.d.ts +45 -3
  28. package/package.json +19 -1
  29. package/safe.d.ts +1 -0
  30. package/src/calendar/README.md +74 -5
  31. package/src/calendar/index.d.ts +95 -1
  32. package/src/common-components/README.md +140 -1
  33. package/src/datatable/README.md +0 -2
  34. package/src/experimental/README.md +126 -0
  35. package/src/experimental/index.d.ts +346 -0
  36. package/src/feed/README.md +69 -0
  37. package/src/feed/index.d.ts +103 -0
  38. package/src/filter/README.md +148 -0
  39. package/src/filter/index.d.ts +221 -0
  40. package/src/form/README.md +132 -4
  41. package/src/form/index.d.ts +82 -1
  42. package/src/kanban/README.md +119 -6
  43. package/src/kanban/index.d.ts +153 -2
  44. package/src/safe/README.md +108 -0
  45. package/src/safe/index.d.ts +158 -0
  46. package/src/utils/README.md +39 -0
  47. package/src/wizard/README.md +158 -0
  48. package/src/wizard/index.d.ts +138 -0
  49. package/utils.d.ts +17 -0
package/dist/calendar.mjs CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  AutoGrid,
6
6
  Box as Box2,
7
7
  Button as Button3,
8
+ DateInput as DateInput2,
8
9
  Divider,
9
10
  EmptyState,
10
11
  Flex as Flex4,
@@ -346,7 +347,7 @@ var GENERATED_ICONS = {
346
347
  "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"] }
347
348
  };
348
349
 
349
- // src/common-components/Icon.js
350
+ // src/common-components/nativeIconNames.js
350
351
  var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
351
352
  "add",
352
353
  "appointment",
@@ -358,12 +359,12 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
358
359
  "block",
359
360
  "book",
360
361
  "bulb",
362
+ "callTranscript",
361
363
  "calling",
362
364
  "callingHangup",
363
365
  "callingMade",
364
366
  "callingMissed",
365
367
  "callingVoicemail",
366
- "callTranscript",
367
368
  "campaigns",
368
369
  "cap",
369
370
  "checkCircle",
@@ -392,13 +393,13 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
392
393
  "enroll",
393
394
  "exclamation",
394
395
  "exclamationCircle",
395
- "facebook",
396
396
  "faceHappy",
397
397
  "faceHappyFilled",
398
398
  "faceNeutral",
399
399
  "faceNeutralFilled",
400
400
  "faceSad",
401
401
  "faceSadFilled",
402
+ "facebook",
402
403
  "favoriteHollow",
403
404
  "file",
404
405
  "filledXCircleIcon",
@@ -539,6 +540,8 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
539
540
  "zoomIn",
540
541
  "zoomOut"
541
542
  ]);
543
+
544
+ // src/common-components/Icon.js
542
545
  var NATIVE_COLORS = /* @__PURE__ */ new Set(["inherit", "alert", "warning", "success"]);
543
546
  var NATIVE_SIZE_TOKENS = {
544
547
  sm: "sm",
@@ -751,6 +754,7 @@ var CollectionFilterControl = ({
751
754
  { key: name, direction: "row", align: "center", gap: "xs" },
752
755
  h2(DateInput, {
753
756
  name: `${controlName}-from`,
757
+ label: filter.fromLabel ?? labels.dateFrom,
754
758
  placeholder: filter.fromLabel ?? labels.dateFrom,
755
759
  format: "medium",
756
760
  value: rangeValue.from ?? null,
@@ -759,6 +763,7 @@ var CollectionFilterControl = ({
759
763
  h2(Icon, { name: "right", size: "sm" }),
760
764
  h2(DateInput, {
761
765
  name: `${controlName}-to`,
766
+ label: filter.toLabel ?? labels.dateTo,
762
767
  placeholder: filter.toLabel ?? labels.dateTo,
763
768
  format: "medium",
764
769
  value: rangeValue.to ?? null,
@@ -1232,9 +1237,172 @@ var makeSpacerDataUri = (height = 24, width = 4) => {
1232
1237
  return { src: toDataUri(svg), width, height };
1233
1238
  };
1234
1239
 
1240
+ // src/calendar/rescheduleUtils.js
1241
+ var shiftDate = (date, shift) => {
1242
+ const d = toDate(date);
1243
+ if (!d) return null;
1244
+ if (!shift || typeof shift !== "object") return new Date(d);
1245
+ const days = (shift.days || 0) + (shift.weeks || 0) * 7;
1246
+ let next = days ? addDays(d, days) : new Date(d);
1247
+ const ms = (shift.hours || 0) * 36e5 + (shift.minutes || 0) * 6e4;
1248
+ if (ms) next = new Date(next.getTime() + ms);
1249
+ return next;
1250
+ };
1251
+ var shiftEvent = (range, shift) => {
1252
+ const start = toDate(range && range.start);
1253
+ if (!start) return null;
1254
+ const end = toDate(range && range.end) || start;
1255
+ return { start: shiftDate(start, shift), end: shiftDate(end, shift) };
1256
+ };
1257
+ var calendarDayDelta = (a, b) => Math.round((startOfDay(b).getTime() - startOfDay(a).getTime()) / MS_PER_DAY);
1258
+ var msIntoDay = (d) => ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
1259
+ var rescheduleToStart = (range, newStart) => {
1260
+ const start = toDate(range && range.start);
1261
+ const target = toDate(newStart);
1262
+ if (!start || !target) return null;
1263
+ const end = toDate(range && range.end) || start;
1264
+ const dayDelta = calendarDayDelta(start, target);
1265
+ const timeDelta = msIntoDay(target) - msIntoDay(start);
1266
+ const endOnDay = addDays(end, dayDelta);
1267
+ if (!timeDelta) return { start: target, end: endOnDay };
1268
+ return {
1269
+ start: target,
1270
+ end: new Date(
1271
+ endOnDay.getFullYear(),
1272
+ endOnDay.getMonth(),
1273
+ endOnDay.getDate(),
1274
+ 0,
1275
+ 0,
1276
+ 0,
1277
+ msIntoDay(endOnDay) + timeDelta
1278
+ )
1279
+ };
1280
+ };
1281
+ var applyDatePick = (start, value) => {
1282
+ if (!isDateValueObject(value)) return null;
1283
+ const s = toDate(start);
1284
+ return new Date(
1285
+ value.year,
1286
+ value.month,
1287
+ value.date,
1288
+ s ? s.getHours() : 0,
1289
+ s ? s.getMinutes() : 0,
1290
+ s ? s.getSeconds() : 0,
1291
+ s ? s.getMilliseconds() : 0
1292
+ );
1293
+ };
1294
+ var DEFAULT_RESCHEDULE_PRESETS = [
1295
+ { label: "+1 hour", shift: { hours: 1 } },
1296
+ { label: "+1 day", shift: { days: 1 } },
1297
+ { label: "Next week", shift: { weeks: 1 } }
1298
+ ];
1299
+ var normalizeRescheduleOptions = (options) => {
1300
+ if (!options) return [];
1301
+ if (options === true) return DEFAULT_RESCHEDULE_PRESETS;
1302
+ if (!Array.isArray(options)) return [];
1303
+ const out = [];
1304
+ for (const opt of options) {
1305
+ if (typeof opt === "function") {
1306
+ out.push({ label: opt.label || opt.name || "Reschedule", getStart: opt });
1307
+ } else if (opt && typeof opt === "object" && opt.label != null) {
1308
+ if (typeof opt.shift === "function") {
1309
+ out.push({ label: opt.label, getStart: opt.shift });
1310
+ } else if (typeof opt.getStart === "function") {
1311
+ out.push({ label: opt.label, getStart: opt.getStart });
1312
+ } else if (opt.shift && typeof opt.shift === "object") {
1313
+ out.push({ label: opt.label, shift: opt.shift });
1314
+ }
1315
+ }
1316
+ }
1317
+ return out;
1318
+ };
1319
+ var resolveRescheduleTarget = (range, option, fnArg) => {
1320
+ if (!range || !toDate(range.start) || !option) return null;
1321
+ if (typeof option.getStart === "function") {
1322
+ const next = toDate(option.getStart(fnArg !== void 0 ? fnArg : range));
1323
+ return next ? rescheduleToStart(range, next) : null;
1324
+ }
1325
+ if (option.shift && typeof option.shift === "object") {
1326
+ return shiftEvent(range, option.shift);
1327
+ }
1328
+ return null;
1329
+ };
1330
+
1331
+ // src/calendar/resourceLanes.js
1332
+ var resolveResourceId = (record, spec) => {
1333
+ if (record == null || spec == null) return null;
1334
+ const value = typeof spec === "function" ? spec(record) : record[spec];
1335
+ if (value == null || value === "") return null;
1336
+ return value;
1337
+ };
1338
+ var buildResourceLanes = (events, options = {}) => {
1339
+ const {
1340
+ resources,
1341
+ resourceLabels,
1342
+ getId,
1343
+ showUnassignedLane = true,
1344
+ unassignedLabel = "Unassigned"
1345
+ } = options;
1346
+ const labelFor = (id) => {
1347
+ if (resourceLabels && resourceLabels[id] != null) return resourceLabels[id];
1348
+ return String(id);
1349
+ };
1350
+ const lanes = [];
1351
+ const byKey = /* @__PURE__ */ new Map();
1352
+ const addLane = (id, label, declared) => {
1353
+ const key = String(id);
1354
+ if (byKey.has(key)) return byKey.get(key);
1355
+ const lane = { id, key, label, events: [], unassigned: false, declared };
1356
+ byKey.set(key, lane);
1357
+ lanes.push(lane);
1358
+ return lane;
1359
+ };
1360
+ (resources || []).forEach((resource) => {
1361
+ if (resource == null) return;
1362
+ if (typeof resource === "object") {
1363
+ addLane(resource.id, resource.label != null ? resource.label : labelFor(resource.id), true);
1364
+ } else {
1365
+ addLane(resource, labelFor(resource), true);
1366
+ }
1367
+ });
1368
+ const unassigned = {
1369
+ id: null,
1370
+ key: "__unassigned__",
1371
+ label: unassignedLabel,
1372
+ events: [],
1373
+ unassigned: true,
1374
+ declared: false
1375
+ };
1376
+ (events || []).forEach((event) => {
1377
+ const id = getId ? getId(event) : null;
1378
+ if (id == null || id === "") {
1379
+ unassigned.events.push(event);
1380
+ return;
1381
+ }
1382
+ const lane = byKey.get(String(id)) || addLane(id, labelFor(id), false);
1383
+ lane.events.push(event);
1384
+ });
1385
+ if (showUnassignedLane && unassigned.events.length > 0) lanes.push(unassigned);
1386
+ return lanes;
1387
+ };
1388
+ var eventsIntersectingRange = (events, rangeStart, rangeEnd) => {
1389
+ const rs = rangeStart.getTime();
1390
+ const re = rangeEnd.getTime();
1391
+ return (events || []).filter((event) => {
1392
+ if (!event || !event.start) return false;
1393
+ const es = event.start.getTime();
1394
+ const ee = (event.end || event.start).getTime();
1395
+ return es <= re && ee >= rs;
1396
+ });
1397
+ };
1398
+ var laneEventsForDay = (events, day) => eventsIntersectingRange(events, startOfDay(day), endOfDay(day)).sort(
1399
+ (a, b) => a.start.getTime() - b.start.getTime()
1400
+ );
1401
+
1235
1402
  // src/calendar/Calendar.jsx
1236
1403
  var DEFAULT_MAX_EVENTS_PER_DAY = 3;
1237
1404
  var ALL_VIEWS = ["month", "week", "day", "agenda"];
1405
+ var ALL_VIEWS_WITH_RESOURCE = ["month", "week", "day", "resource", "agenda"];
1238
1406
  var DEFAULT_DAY_START_HOUR = 8;
1239
1407
  var DEFAULT_DAY_END_HOUR = 20;
1240
1408
  var DEFAULT_TIME_ZONES = [
@@ -1270,7 +1438,8 @@ var VIEW_LABELS = {
1270
1438
  month: "Month",
1271
1439
  week: "Week",
1272
1440
  day: "Day",
1273
- agenda: "Agenda"
1441
+ agenda: "Agenda",
1442
+ resource: "Resource"
1274
1443
  };
1275
1444
  var DEFAULT_LABELS3 = {
1276
1445
  today: "Today",
@@ -1287,7 +1456,11 @@ var DEFAULT_LABELS3 = {
1287
1456
  errorMessage: "An error occurred while loading events.",
1288
1457
  dayDetailTitle: (label) => label,
1289
1458
  open: "Open",
1290
- allDay: "All day"
1459
+ allDay: "All day",
1460
+ reschedule: "Reschedule",
1461
+ pickDate: "Pick date",
1462
+ unassigned: "Unassigned",
1463
+ resource: "Resource"
1291
1464
  };
1292
1465
  var DEFAULT_EVENT_FIELDS = {
1293
1466
  id: "id",
@@ -1343,8 +1516,9 @@ var truncateMonthLabel = (value, max) => {
1343
1516
  var MONTH_COL_WIDTH = 160;
1344
1517
  var TIMEGRID_DAY_COL = 150;
1345
1518
  var TIMEGRID_DAY_COL_SINGLE = 560;
1519
+ var RESOURCE_LABEL_COL_WIDTH = "min";
1346
1520
  var HOUR_SLOT_HEIGHT = 64;
1347
- var EventDetail = ({ event, labels }) => {
1521
+ var EventDetail = ({ event, labels, reschedule, idSuffix = "" }) => {
1348
1522
  const { start, end, title, subtitle, href } = event;
1349
1523
  let when = "";
1350
1524
  if (start) {
@@ -1358,11 +1532,27 @@ var EventDetail = ({ event, labels }) => {
1358
1532
  when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
1359
1533
  }
1360
1534
  }
1361
- return /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" }, truncate: true }, title || "--"), when ? /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", truncate: true }, when) : null, subtitle ? /* @__PURE__ */ React5.createElement(Text, { truncate: true }, subtitle) : null, href ? /* @__PURE__ */ React5.createElement(Link2, { href: href.url, external: href.external }, labels.open) : null);
1535
+ return /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" }, truncate: true }, title || "--"), when ? /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", truncate: true }, when) : null, subtitle ? /* @__PURE__ */ React5.createElement(Text, { truncate: true }, subtitle) : null, href ? /* @__PURE__ */ React5.createElement(Link2, { href: href.url, external: href.external }, labels.open) : null, reschedule ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Divider, null), /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", format: { fontWeight: "demibold" } }, labels.reschedule), reschedule.options.length > 0 ? /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", gap: "xs", wrap: "wrap" }, reschedule.options.map((option, i) => /* @__PURE__ */ React5.createElement(
1536
+ Button3,
1537
+ {
1538
+ key: `${option.label}-${i}`,
1539
+ size: "xs",
1540
+ variant: "secondary",
1541
+ onClick: () => reschedule.onPreset(event, option)
1542
+ },
1543
+ option.label
1544
+ ))) : null, /* @__PURE__ */ React5.createElement(
1545
+ DateInput2,
1546
+ {
1547
+ name: `cal-resched-${event.key}${idSuffix}`,
1548
+ label: labels.pickDate,
1549
+ onChange: (value) => reschedule.onPick(event, value)
1550
+ }
1551
+ )) : null);
1362
1552
  };
1363
- var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1553
+ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "", reschedule = null) => {
1364
1554
  if (mode === "none") return void 0;
1365
- const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ React5.createElement(EventDetail, { event, labels });
1555
+ const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ React5.createElement(EventDetail, { event, labels, reschedule, idSuffix });
1366
1556
  const id = `cal-evt-${event.key}${idSuffix}`;
1367
1557
  if (mode === "modal") {
1368
1558
  return /* @__PURE__ */ React5.createElement(Modal, { id, title: event.title || labels.open, width: "small" }, /* @__PURE__ */ React5.createElement(ModalBody, null, body));
@@ -1372,17 +1562,17 @@ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1372
1562
  }
1373
1563
  return /* @__PURE__ */ React5.createElement(Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ React5.createElement(Tile, { compact: true }, body));
1374
1564
  };
1375
- var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1565
+ var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule }) => {
1376
1566
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
1377
- const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "");
1567
+ const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "", reschedule);
1378
1568
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
1379
1569
  const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
1380
1570
  return /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ React5.createElement(Box2, { flex: 2 }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center" }, /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", format: { fontWeight: "demibold" } }, timeLabel))), /* @__PURE__ */ React5.createElement(Box2, { flex: 11 }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React5.createElement(ColorDot, { variant }), /* @__PURE__ */ React5.createElement(Text, { truncate: true }, /* @__PURE__ */ React5.createElement(Link2, { overlay, onClick: handleClick }, event.title || "--")))), event.subtitle ? /* @__PURE__ */ React5.createElement(Box2, { flex: 4 }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center" }, /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", truncate: true }, event.subtitle))) : null);
1381
1571
  };
1382
1572
  var MONTH_SLOT_HEIGHT = 24;
1383
- var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars }) => {
1573
+ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars, reschedule, idScope = "" }) => {
1384
1574
  const isStartDay = !day || !event.start || isSameDay(event.start, day);
1385
- const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
1575
+ const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${idScope}${day.getTime()}` : "", reschedule);
1386
1576
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
1387
1577
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
1388
1578
  const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
@@ -1395,9 +1585,9 @@ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, lab
1395
1585
  }
1396
1586
  return /* @__PURE__ */ React5.createElement(Link2, { overlay, onClick: handleClick }, /* @__PURE__ */ React5.createElement(StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, label));
1397
1587
  };
1398
- var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1588
+ var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule, idScope = "" }) => {
1399
1589
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
1400
- const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-more${day.getTime()}` : "-more");
1590
+ const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-more${idScope}${day.getTime()}` : "-more", reschedule);
1401
1591
  const href = event.href;
1402
1592
  return /* @__PURE__ */ React5.createElement(Button3, { variant: "transparent", size: "sm", href: href ? href.url : void 0, overlay, onClick: handleClick }, event.title || "--");
1403
1593
  };
@@ -1462,6 +1652,40 @@ var Toolbar = ({
1462
1652
  }
1463
1653
  ));
1464
1654
  };
1655
+ var DayChipStack = ({ day, events, maxEventsPerDay, chipProps, labels, idScope = "" }) => {
1656
+ const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
1657
+ const shown = events.slice(0, maxEventsPerDay);
1658
+ const hasOverflow = events.length > maxEventsPerDay;
1659
+ const heightSpacer = /* @__PURE__ */ React5.createElement(Image2, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
1660
+ const slotRow = (key, content) => /* @__PURE__ */ React5.createElement(Flex4, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
1661
+ const slots = [];
1662
+ for (let i = 0; i < maxEventsPerDay; i++) {
1663
+ if (i < shown.length) {
1664
+ slots.push(slotRow(shown[i].key, /* @__PURE__ */ React5.createElement(MonthChip, { event: shown[i], day, idScope, ...chipProps })));
1665
+ } else {
1666
+ slots.push(/* @__PURE__ */ React5.createElement(Image2, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
1667
+ }
1668
+ }
1669
+ if (hasOverflow) {
1670
+ slots.push(
1671
+ slotRow(
1672
+ "more",
1673
+ /* @__PURE__ */ React5.createElement(
1674
+ Link2,
1675
+ {
1676
+ overlay: /* @__PURE__ */ React5.createElement(Popover, { id: `cal-day-${idScope}${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ React5.createElement(Tile, { compact: true }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "sm" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "bold" } }, String(events.length)), /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ React5.createElement(Divider, null), events.map((event, i) => /* @__PURE__ */ React5.createElement(React5.Fragment, { key: event.key }, /* @__PURE__ */ React5.createElement(DayListItem, { event, day, idScope, ...chipProps }), i < events.length - 1 ? /* @__PURE__ */ React5.createElement(Divider, null) : null)))))
1677
+ },
1678
+ labels.more(events.length - maxEventsPerDay)
1679
+ )
1680
+ )
1681
+ );
1682
+ } else {
1683
+ slots.push(
1684
+ /* @__PURE__ */ React5.createElement(Image2, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
1685
+ );
1686
+ }
1687
+ return /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, slots);
1688
+ };
1465
1689
  var MonthView = ({
1466
1690
  refDate,
1467
1691
  now,
@@ -1476,43 +1700,21 @@ var MonthView = ({
1476
1700
  }) => {
1477
1701
  const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
1478
1702
  const today = now || /* @__PURE__ */ new Date();
1479
- const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
1480
1703
  const renderCell = (day) => {
1481
1704
  const dayEvents = eventsForDay(day);
1482
1705
  const inMonth = isSameMonth(day, refDate);
1483
1706
  const isToday = isSameDay(day, today);
1484
1707
  if (renderDayCell) return renderDayCell(day, dayEvents);
1485
- const shown = dayEvents.slice(0, maxEventsPerDay);
1486
- const hasOverflow = dayEvents.length > maxEventsPerDay;
1487
- const heightSpacer = /* @__PURE__ */ React5.createElement(Image2, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
1488
- const slotRow = (key, content) => /* @__PURE__ */ React5.createElement(Flex4, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
1489
- const slots = [];
1490
- for (let i = 0; i < maxEventsPerDay; i++) {
1491
- if (i < shown.length) {
1492
- slots.push(slotRow(shown[i].key, /* @__PURE__ */ React5.createElement(MonthChip, { event: shown[i], day, ...chipProps })));
1493
- } else {
1494
- slots.push(/* @__PURE__ */ React5.createElement(Image2, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
1708
+ return /* @__PURE__ */ React5.createElement(AutoGrid, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ React5.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), /* @__PURE__ */ React5.createElement(
1709
+ DayChipStack,
1710
+ {
1711
+ day,
1712
+ events: dayEvents,
1713
+ maxEventsPerDay,
1714
+ chipProps,
1715
+ labels
1495
1716
  }
1496
- }
1497
- if (hasOverflow) {
1498
- slots.push(
1499
- slotRow(
1500
- "more",
1501
- /* @__PURE__ */ React5.createElement(
1502
- Link2,
1503
- {
1504
- overlay: /* @__PURE__ */ React5.createElement(Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ React5.createElement(Tile, { compact: true }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "sm" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "bold" } }, String(dayEvents.length)), /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ React5.createElement(Divider, null), dayEvents.map((event, i) => /* @__PURE__ */ React5.createElement(React5.Fragment, { key: event.key }, /* @__PURE__ */ React5.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ React5.createElement(Divider, null) : null)))))
1505
- },
1506
- labels.more(dayEvents.length - maxEventsPerDay)
1507
- )
1508
- )
1509
- );
1510
- } else {
1511
- slots.push(
1512
- /* @__PURE__ */ React5.createElement(Image2, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
1513
- );
1514
- }
1515
- return /* @__PURE__ */ React5.createElement(AutoGrid, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ React5.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots));
1717
+ )));
1516
1718
  };
1517
1719
  return /* @__PURE__ */ React5.createElement(Table, { bordered: true, flush: true }, /* @__PURE__ */ React5.createElement(TableHead, null, /* @__PURE__ */ React5.createElement(TableRow, null, headers.map((h4) => /* @__PURE__ */ React5.createElement(TableHeader, { key: h4, width: "min", align: "center" }, h4.toUpperCase())))), /* @__PURE__ */ React5.createElement(TableBody, null, weeks.map((week, wi) => {
1518
1720
  const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
@@ -1534,6 +1736,33 @@ var AgendaView = ({ rangeStart, rangeEnd, eventsForDay, chipProps, labels, rende
1534
1736
  }
1535
1737
  return /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "lg" }, days.map(({ day, events }) => /* @__PURE__ */ React5.createElement(Flex4, { key: day.getTime(), direction: "column", gap: "sm" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", justify: "between", align: "end" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" } }, formatDayTitle(day)), /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy" }, `${events.length} ${events.length === 1 ? "event" : "events"}`)), /* @__PURE__ */ React5.createElement(Divider, null), events.map((event) => /* @__PURE__ */ React5.createElement(AgendaEventRow, { key: event.key, event, day, ...chipProps })))));
1536
1738
  };
1739
+ var ResourceView = ({ days, now, lanes, maxEventsPerDay, chipProps, labels, renderEmptyState }) => {
1740
+ const today = now || /* @__PURE__ */ new Date();
1741
+ if (!lanes || lanes.length === 0) {
1742
+ if (renderEmptyState) return renderEmptyState({});
1743
+ return /* @__PURE__ */ React5.createElement(EmptyState, { title: labels.noEventsTitle }, /* @__PURE__ */ React5.createElement(Text, null, labels.noEventsMessage));
1744
+ }
1745
+ const rangeStart = startOfDay(days[0]);
1746
+ const rangeEnd = endOfDay(days[days.length - 1]);
1747
+ return /* @__PURE__ */ React5.createElement(Table, { bordered: true, flush: true }, /* @__PURE__ */ React5.createElement(TableHead, null, /* @__PURE__ */ React5.createElement(TableRow, null, /* @__PURE__ */ React5.createElement(TableHeader, { width: RESOURCE_LABEL_COL_WIDTH }, String(labels.resource).toUpperCase()), days.map((day) => {
1748
+ const isToday = isSameDay(day, today);
1749
+ const label = `${formatWeekdayShort(day)} ${formatMonthShort(day)} ${day.getDate()}`;
1750
+ return /* @__PURE__ */ React5.createElement(TableHeader, { key: day.getTime(), width: "min", align: "center" }, isToday ? `${label} \xB7 Today` : label);
1751
+ }))), /* @__PURE__ */ React5.createElement(TableBody, null, lanes.map((lane, laneIndex) => {
1752
+ const visible = eventsIntersectingRange(lane.events, rangeStart, rangeEnd);
1753
+ return /* @__PURE__ */ React5.createElement(TableRow, { key: lane.key }, /* @__PURE__ */ React5.createElement(TableCell, { width: RESOURCE_LABEL_COL_WIDTH }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "flush" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" } }, lane.label), /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy" }, `${visible.length} ${visible.length === 1 ? "event" : "events"}`))), days.map((day) => /* @__PURE__ */ React5.createElement(TableCell, { key: day.getTime(), width: "min" }, /* @__PURE__ */ React5.createElement(AutoGrid, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ React5.createElement(
1754
+ DayChipStack,
1755
+ {
1756
+ day,
1757
+ events: laneEventsForDay(visible, day),
1758
+ maxEventsPerDay,
1759
+ chipProps,
1760
+ labels,
1761
+ idScope: `r${laneIndex}-`
1762
+ }
1763
+ )))));
1764
+ })));
1765
+ };
1537
1766
  var formatTimedDuration = (start, end) => {
1538
1767
  const mins = Math.max(0, Math.round(((end || start).getTime() - start.getTime()) / 6e4));
1539
1768
  const h4 = Math.floor(mins / 60);
@@ -1581,7 +1810,8 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1581
1810
  chipProps.overlayMode,
1582
1811
  chipProps.renderEventDetail,
1583
1812
  chipProps.labels,
1584
- `-tg${mode}${hour}-${dayMs}`
1813
+ `-tg${mode}${hour}-${dayMs}`,
1814
+ chipProps.reschedule
1585
1815
  );
1586
1816
  const handleClick = chipProps.onEventClick ? () => chipProps.onEventClick(e.raw, e) : void 0;
1587
1817
  const variant = VALID_VARIANTS.has(e.color) ? e.color : "default";
@@ -1666,6 +1896,14 @@ var Calendar = (props) => {
1666
1896
  // time grid (week / day)
1667
1897
  dayStartHour = DEFAULT_DAY_START_HOUR,
1668
1898
  dayEndHour = DEFAULT_DAY_END_HOUR,
1899
+ // resource / lane view (rows = resources, columns = the focused week's days)
1900
+ resources,
1901
+ resourceField,
1902
+ resourceLabels,
1903
+ showUnassignedLane = true,
1904
+ // drag-free reschedule (presets + date picker in the event-detail overlay)
1905
+ rescheduleOptions,
1906
+ onEventReschedule,
1669
1907
  // timezone
1670
1908
  timeZone: controlledTimeZone,
1671
1909
  defaultTimeZone,
@@ -1704,10 +1942,12 @@ var Calendar = (props) => {
1704
1942
  const fields = useMemo(() => ({ ...DEFAULT_EVENT_FIELDS, ...eventFields || {} }), [eventFields]);
1705
1943
  const [internalView, setInternalView] = useState2(defaultView);
1706
1944
  const view = controlledView != null ? controlledView : internalView;
1945
+ const resourceEnabled = resources && resources.length > 0 || resourceField != null;
1707
1946
  const enabledViews = useMemo(() => {
1708
- const base = viewsProp && viewsProp.length > 0 ? viewsProp : ALL_VIEWS;
1709
- return base.filter((v) => ALL_VIEWS.includes(v));
1710
- }, [viewsProp]);
1947
+ const all = resourceEnabled ? ALL_VIEWS_WITH_RESOURCE : ALL_VIEWS;
1948
+ const base = viewsProp && viewsProp.length > 0 ? viewsProp : all;
1949
+ return base.filter((v) => all.includes(v));
1950
+ }, [viewsProp, resourceEnabled]);
1711
1951
  const setView = useCallback(
1712
1952
  (next) => {
1713
1953
  if (controlledView == null) setInternalView(next);
@@ -1732,7 +1972,9 @@ var Calendar = (props) => {
1732
1972
  const focusedDate = (controlledFocusedDate != null ? toDate(controlledFocusedDate) : internalDate) || startOfDay(nowWall);
1733
1973
  const stepFor = useCallback(
1734
1974
  (dir) => {
1735
- if (view === "week" || view === "agenda") return addDays(focusedDate, dir * 7);
1975
+ if (view === "week" || view === "agenda" || view === "resource") {
1976
+ return addDays(focusedDate, dir * 7);
1977
+ }
1736
1978
  if (view === "day") return addDays(focusedDate, dir);
1737
1979
  return addMonths(focusedDate, dir);
1738
1980
  },
@@ -1759,7 +2001,7 @@ var Calendar = (props) => {
1759
2001
  rangeEnd: endOfDay(flat[flat.length - 1])
1760
2002
  };
1761
2003
  }
1762
- if (view === "week") {
2004
+ if (view === "week" || view === "resource") {
1763
2005
  const days = buildWeekDays(focusedDate, weekStartsOn, hideWeekends);
1764
2006
  return {
1765
2007
  weeks: null,
@@ -1832,14 +2074,18 @@ var Calendar = (props) => {
1832
2074
  );
1833
2075
  const normalized = useMemo(
1834
2076
  () => (events || []).map((raw, index) => {
1835
- const start = toWallClock(toDate(resolveField(raw, fields.start)), timeZone);
1836
- const endRaw = toWallClock(toDate(resolveField(raw, fields.end)), timeZone);
2077
+ const sourceStart = toDate(resolveField(raw, fields.start));
2078
+ const sourceEnd = toDate(resolveField(raw, fields.end));
2079
+ const start = toWallClock(sourceStart, timeZone);
2080
+ const endRaw = toWallClock(sourceEnd, timeZone);
1837
2081
  const id = resolveField(raw, fields.id);
1838
2082
  return {
1839
2083
  key: id != null ? String(id) : `evt-${index}`,
1840
2084
  id,
1841
2085
  start,
1842
2086
  end: endRaw || start,
2087
+ sourceStart,
2088
+ sourceEnd: sourceEnd || sourceStart,
1843
2089
  title: resolveField(raw, fields.title),
1844
2090
  subtitle: resolveField(raw, fields.subtitle),
1845
2091
  color: resolveField(raw, fields.color),
@@ -1876,12 +2122,40 @@ var Calendar = (props) => {
1876
2122
  }, [rangeKey]);
1877
2123
  const title = useMemo(() => {
1878
2124
  if (view === "day") return formatDayTitle(focusedDate);
1879
- if (view === "week" || view === "agenda") {
1880
- const days = buildWeekDays(focusedDate, weekStartsOn, view === "week" && hideWeekends);
2125
+ if (view === "week" || view === "agenda" || view === "resource") {
2126
+ const days = buildWeekDays(focusedDate, weekStartsOn, view !== "agenda" && hideWeekends);
1881
2127
  return formatRangeTitle(days[0], days[days.length - 1]);
1882
2128
  }
1883
2129
  return formatMonthTitle(focusedDate);
1884
2130
  }, [view, focusedDate, weekStartsOn, hideWeekends]);
2131
+ const handleReschedulePreset = useCallback(
2132
+ (event, option) => {
2133
+ const target = resolveRescheduleTarget(
2134
+ { start: event.sourceStart, end: event.sourceEnd },
2135
+ option,
2136
+ event
2137
+ );
2138
+ if (target && onEventReschedule) onEventReschedule(event.raw, target, event);
2139
+ },
2140
+ [onEventReschedule]
2141
+ );
2142
+ const handleReschedulePick = useCallback(
2143
+ (event, value) => {
2144
+ const newStart = applyDatePick(event.sourceStart, value);
2145
+ if (!newStart) return;
2146
+ const target = rescheduleToStart({ start: event.sourceStart, end: event.sourceEnd }, newStart);
2147
+ if (target && onEventReschedule) onEventReschedule(event.raw, target, event);
2148
+ },
2149
+ [onEventReschedule]
2150
+ );
2151
+ const reschedule = useMemo(() => {
2152
+ if (!rescheduleOptions) return null;
2153
+ return {
2154
+ options: normalizeRescheduleOptions(rescheduleOptions),
2155
+ onPreset: handleReschedulePreset,
2156
+ onPick: handleReschedulePick
2157
+ };
2158
+ }, [rescheduleOptions, handleReschedulePreset, handleReschedulePick]);
1885
2159
  const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
1886
2160
  const chipProps = {
1887
2161
  overlayMode,
@@ -1889,8 +2163,19 @@ var Calendar = (props) => {
1889
2163
  onEventClick,
1890
2164
  labels,
1891
2165
  monthEventStyle: safeMonthEventStyle,
1892
- monthEventMaxChars
2166
+ monthEventMaxChars,
2167
+ reschedule
1893
2168
  };
2169
+ const resourceLaneData = useMemo(() => {
2170
+ if (view !== "resource") return null;
2171
+ return buildResourceLanes(queried, {
2172
+ resources,
2173
+ resourceLabels,
2174
+ getId: (e) => resolveResourceId(e.raw, resourceField),
2175
+ showUnassignedLane,
2176
+ unassignedLabel: labels.unassigned
2177
+ });
2178
+ }, [view, queried, resources, resourceLabels, resourceField, showUnassignedLane, labels]);
1894
2179
  const timeZoneOptions = useMemo(() => {
1895
2180
  if (!showTimeZoneSelect) return null;
1896
2181
  const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
@@ -1951,6 +2236,19 @@ var Calendar = (props) => {
1951
2236
  labels
1952
2237
  }
1953
2238
  );
2239
+ } else if (view === "resource") {
2240
+ body = /* @__PURE__ */ React5.createElement(
2241
+ ResourceView,
2242
+ {
2243
+ days: gridDays,
2244
+ now: nowWall,
2245
+ lanes: resourceLaneData,
2246
+ maxEventsPerDay,
2247
+ chipProps,
2248
+ labels,
2249
+ renderEmptyState
2250
+ }
2251
+ );
1954
2252
  } else if (view === "week" || view === "day") {
1955
2253
  body = /* @__PURE__ */ React5.createElement(
1956
2254
  TimeGridView,
@@ -1980,6 +2278,7 @@ var Calendar = (props) => {
1980
2278
  }
1981
2279
  return /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "sm" }, toolbar, body);
1982
2280
  };
2281
+ Calendar.displayName = "Calendar";
1983
2282
  export {
1984
2283
  Calendar
1985
2284
  };