hs-uix 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +3 -1
  2. package/common-components.d.ts +319 -68
  3. package/dist/calendar.js +397 -119
  4. package/dist/calendar.mjs +399 -119
  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 +3255 -353
  18. package/dist/index.mjs +3199 -344
  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 +76 -5
  31. package/src/calendar/index.d.ts +108 -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.js CHANGED
@@ -89,7 +89,6 @@ var import_react2 = __toESM(require("react"));
89
89
  var import_ui_extensions2 = require("@hubspot/ui-extensions");
90
90
 
91
91
  // src/common-components/svgDefaults.js
92
- var HS_FONT_FAMILY = '"Lexend Deca", Helvetica, Arial, sans-serif';
93
92
  var HS_TEXT_COLOR = "#33475b";
94
93
 
95
94
  // src/common-components/icons.generated.js
@@ -344,7 +343,7 @@ var GENERATED_ICONS = {
344
343
  "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"] }
345
344
  };
346
345
 
347
- // src/common-components/Icon.js
346
+ // src/common-components/nativeIconNames.js
348
347
  var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
349
348
  "add",
350
349
  "appointment",
@@ -356,12 +355,12 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
356
355
  "block",
357
356
  "book",
358
357
  "bulb",
358
+ "callTranscript",
359
359
  "calling",
360
360
  "callingHangup",
361
361
  "callingMade",
362
362
  "callingMissed",
363
363
  "callingVoicemail",
364
- "callTranscript",
365
364
  "campaigns",
366
365
  "cap",
367
366
  "checkCircle",
@@ -390,13 +389,13 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
390
389
  "enroll",
391
390
  "exclamation",
392
391
  "exclamationCircle",
393
- "facebook",
394
392
  "faceHappy",
395
393
  "faceHappyFilled",
396
394
  "faceNeutral",
397
395
  "faceNeutralFilled",
398
396
  "faceSad",
399
397
  "faceSadFilled",
398
+ "facebook",
400
399
  "favoriteHollow",
401
400
  "file",
402
401
  "filledXCircleIcon",
@@ -537,6 +536,8 @@ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
537
536
  "zoomIn",
538
537
  "zoomOut"
539
538
  ]);
539
+
540
+ // src/common-components/Icon.js
540
541
  var NATIVE_COLORS = /* @__PURE__ */ new Set(["inherit", "alert", "warning", "success"]);
541
542
  var NATIVE_SIZE_TOKENS = {
542
543
  sm: "sm",
@@ -749,6 +750,7 @@ var CollectionFilterControl = ({
749
750
  { key: name, direction: "row", align: "center", gap: "xs" },
750
751
  h2(import_ui_extensions3.DateInput, {
751
752
  name: `${controlName}-from`,
753
+ label: filter.fromLabel ?? labels.dateFrom,
752
754
  placeholder: filter.fromLabel ?? labels.dateFrom,
753
755
  format: "medium",
754
756
  value: rangeValue.from ?? null,
@@ -757,6 +759,7 @@ var CollectionFilterControl = ({
757
759
  h2(Icon, { name: "right", size: "sm" }),
758
760
  h2(import_ui_extensions3.DateInput, {
759
761
  name: `${controlName}-to`,
762
+ label: filter.toLabel ?? labels.dateTo,
760
763
  placeholder: filter.toLabel ?? labels.dateTo,
761
764
  format: "medium",
762
765
  value: rangeValue.to ?? null,
@@ -1211,21 +1214,6 @@ var formatTimeZoneLabel = (tz, atDate = /* @__PURE__ */ new Date()) => {
1211
1214
 
1212
1215
  // src/calendar/svgChips.js
1213
1216
  var toDataUri = (svg) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
1214
- var escapeXml = (s) => String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1215
- var truncateLabel = (value, max) => {
1216
- const s = String(value == null ? "" : value);
1217
- if (!max || s.length <= max) return s;
1218
- return s.slice(0, Math.max(1, max - 1)).trimEnd() + "\u2026";
1219
- };
1220
- var CHIP_PALETTE = {
1221
- default: { fill: "#7FD1DE", text: HS_TEXT_COLOR },
1222
- info: { fill: "#00A4BD", text: "#FFFFFF" },
1223
- success: { fill: "#00BDA5", text: "#FFFFFF" },
1224
- warning: { fill: "#F5C26B", text: HS_TEXT_COLOR },
1225
- error: { fill: "#F2545B", text: "#FFFFFF" },
1226
- danger: { fill: "#F2545B", text: "#FFFFFF" }
1227
- // StatusTag spells red "danger"; accept both
1228
- };
1229
1217
  var DOT_FILL = {
1230
1218
  default: "#7C98B6",
1231
1219
  info: "#00A4BD",
@@ -1240,31 +1228,177 @@ var makeDotDataUri = (variant = "default", size = 8) => {
1240
1228
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}"><circle cx="${r}" cy="${r}" r="${r}" fill="${fill}" /></svg>`;
1241
1229
  return { src: toDataUri(svg), width: size, height: size };
1242
1230
  };
1243
- var makeEventChipDataUri = (opts) => {
1244
- const { label, width, height = 24, variant = "default" } = opts;
1245
- const palette = CHIP_PALETTE[variant] || CHIP_PALETTE.default;
1246
- const accentX = 5;
1247
- const accentW = 3;
1248
- const textX = accentX + accentW + 6;
1249
- const rightPad = 8;
1250
- const maxChars = Math.max(1, Math.floor((width - textX - rightPad) / 6));
1251
- const text = truncateLabel(label, maxChars);
1252
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect x="0.5" y="0.5" width="${width - 1}" height="${height - 1}" rx="4" ry="4" fill="#FFFFFF" stroke="#CBD6E2" stroke-width="1" /><rect x="${accentX}" y="5" width="${accentW}" height="${height - 10}" rx="1.5" ry="1.5" fill="${palette.fill}" /><text x="${textX}" y="${height / 2}" font-family='${HS_FONT_FAMILY}' font-size="12" font-weight="500" fill="${HS_TEXT_COLOR}" text-anchor="start" dominant-baseline="central">${escapeXml(text)}</text></svg>`;
1253
- return { src: toDataUri(svg), width, height };
1254
- };
1255
- var makeMoreDataUri = (opts) => {
1256
- const { label, width, height = 24 } = opts;
1257
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><text x="3" y="${height / 2}" font-family='${HS_FONT_FAMILY}' font-size="13" font-weight="700" fill="#0091AE" text-anchor="start" dominant-baseline="central">${escapeXml(label)}</text></svg>`;
1258
- return { src: toDataUri(svg), width, height };
1259
- };
1260
1231
  var makeSpacerDataUri = (height = 24, width = 4) => {
1261
1232
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect x="0" y="0" width="${width}" height="${height}" fill="#FFFFFF" fill-opacity="0" /></svg>`;
1262
1233
  return { src: toDataUri(svg), width, height };
1263
1234
  };
1264
1235
 
1236
+ // src/calendar/rescheduleUtils.js
1237
+ var shiftDate = (date, shift) => {
1238
+ const d = toDate(date);
1239
+ if (!d) return null;
1240
+ if (!shift || typeof shift !== "object") return new Date(d);
1241
+ const days = (shift.days || 0) + (shift.weeks || 0) * 7;
1242
+ let next = days ? addDays(d, days) : new Date(d);
1243
+ const ms = (shift.hours || 0) * 36e5 + (shift.minutes || 0) * 6e4;
1244
+ if (ms) next = new Date(next.getTime() + ms);
1245
+ return next;
1246
+ };
1247
+ var shiftEvent = (range, shift) => {
1248
+ const start = toDate(range && range.start);
1249
+ if (!start) return null;
1250
+ const end = toDate(range && range.end) || start;
1251
+ return { start: shiftDate(start, shift), end: shiftDate(end, shift) };
1252
+ };
1253
+ var calendarDayDelta = (a, b) => Math.round((startOfDay(b).getTime() - startOfDay(a).getTime()) / MS_PER_DAY);
1254
+ var msIntoDay = (d) => ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
1255
+ var rescheduleToStart = (range, newStart) => {
1256
+ const start = toDate(range && range.start);
1257
+ const target = toDate(newStart);
1258
+ if (!start || !target) return null;
1259
+ const end = toDate(range && range.end) || start;
1260
+ const dayDelta = calendarDayDelta(start, target);
1261
+ const timeDelta = msIntoDay(target) - msIntoDay(start);
1262
+ const endOnDay = addDays(end, dayDelta);
1263
+ if (!timeDelta) return { start: target, end: endOnDay };
1264
+ return {
1265
+ start: target,
1266
+ end: new Date(
1267
+ endOnDay.getFullYear(),
1268
+ endOnDay.getMonth(),
1269
+ endOnDay.getDate(),
1270
+ 0,
1271
+ 0,
1272
+ 0,
1273
+ msIntoDay(endOnDay) + timeDelta
1274
+ )
1275
+ };
1276
+ };
1277
+ var applyDatePick = (start, value) => {
1278
+ if (!isDateValueObject(value)) return null;
1279
+ const s = toDate(start);
1280
+ return new Date(
1281
+ value.year,
1282
+ value.month,
1283
+ value.date,
1284
+ s ? s.getHours() : 0,
1285
+ s ? s.getMinutes() : 0,
1286
+ s ? s.getSeconds() : 0,
1287
+ s ? s.getMilliseconds() : 0
1288
+ );
1289
+ };
1290
+ var DEFAULT_RESCHEDULE_PRESETS = [
1291
+ { label: "+1 hour", shift: { hours: 1 } },
1292
+ { label: "+1 day", shift: { days: 1 } },
1293
+ { label: "Next week", shift: { weeks: 1 } }
1294
+ ];
1295
+ var normalizeRescheduleOptions = (options) => {
1296
+ if (!options) return [];
1297
+ if (options === true) return DEFAULT_RESCHEDULE_PRESETS;
1298
+ if (!Array.isArray(options)) return [];
1299
+ const out = [];
1300
+ for (const opt of options) {
1301
+ if (typeof opt === "function") {
1302
+ out.push({ label: opt.label || opt.name || "Reschedule", getStart: opt });
1303
+ } else if (opt && typeof opt === "object" && opt.label != null) {
1304
+ if (typeof opt.shift === "function") {
1305
+ out.push({ label: opt.label, getStart: opt.shift });
1306
+ } else if (typeof opt.getStart === "function") {
1307
+ out.push({ label: opt.label, getStart: opt.getStart });
1308
+ } else if (opt.shift && typeof opt.shift === "object") {
1309
+ out.push({ label: opt.label, shift: opt.shift });
1310
+ }
1311
+ }
1312
+ }
1313
+ return out;
1314
+ };
1315
+ var resolveRescheduleTarget = (range, option, fnArg) => {
1316
+ if (!range || !toDate(range.start) || !option) return null;
1317
+ if (typeof option.getStart === "function") {
1318
+ const next = toDate(option.getStart(fnArg !== void 0 ? fnArg : range));
1319
+ return next ? rescheduleToStart(range, next) : null;
1320
+ }
1321
+ if (option.shift && typeof option.shift === "object") {
1322
+ return shiftEvent(range, option.shift);
1323
+ }
1324
+ return null;
1325
+ };
1326
+
1327
+ // src/calendar/resourceLanes.js
1328
+ var resolveResourceId = (record, spec) => {
1329
+ if (record == null || spec == null) return null;
1330
+ const value = typeof spec === "function" ? spec(record) : record[spec];
1331
+ if (value == null || value === "") return null;
1332
+ return value;
1333
+ };
1334
+ var buildResourceLanes = (events, options = {}) => {
1335
+ const {
1336
+ resources,
1337
+ resourceLabels,
1338
+ getId,
1339
+ showUnassignedLane = true,
1340
+ unassignedLabel = "Unassigned"
1341
+ } = options;
1342
+ const labelFor = (id) => {
1343
+ if (resourceLabels && resourceLabels[id] != null) return resourceLabels[id];
1344
+ return String(id);
1345
+ };
1346
+ const lanes = [];
1347
+ const byKey = /* @__PURE__ */ new Map();
1348
+ const addLane = (id, label, declared) => {
1349
+ const key = String(id);
1350
+ if (byKey.has(key)) return byKey.get(key);
1351
+ const lane = { id, key, label, events: [], unassigned: false, declared };
1352
+ byKey.set(key, lane);
1353
+ lanes.push(lane);
1354
+ return lane;
1355
+ };
1356
+ (resources || []).forEach((resource) => {
1357
+ if (resource == null) return;
1358
+ if (typeof resource === "object") {
1359
+ addLane(resource.id, resource.label != null ? resource.label : labelFor(resource.id), true);
1360
+ } else {
1361
+ addLane(resource, labelFor(resource), true);
1362
+ }
1363
+ });
1364
+ const unassigned = {
1365
+ id: null,
1366
+ key: "__unassigned__",
1367
+ label: unassignedLabel,
1368
+ events: [],
1369
+ unassigned: true,
1370
+ declared: false
1371
+ };
1372
+ (events || []).forEach((event) => {
1373
+ const id = getId ? getId(event) : null;
1374
+ if (id == null || id === "") {
1375
+ unassigned.events.push(event);
1376
+ return;
1377
+ }
1378
+ const lane = byKey.get(String(id)) || addLane(id, labelFor(id), false);
1379
+ lane.events.push(event);
1380
+ });
1381
+ if (showUnassignedLane && unassigned.events.length > 0) lanes.push(unassigned);
1382
+ return lanes;
1383
+ };
1384
+ var eventsIntersectingRange = (events, rangeStart, rangeEnd) => {
1385
+ const rs = rangeStart.getTime();
1386
+ const re = rangeEnd.getTime();
1387
+ return (events || []).filter((event) => {
1388
+ if (!event || !event.start) return false;
1389
+ const es = event.start.getTime();
1390
+ const ee = (event.end || event.start).getTime();
1391
+ return es <= re && ee >= rs;
1392
+ });
1393
+ };
1394
+ var laneEventsForDay = (events, day) => eventsIntersectingRange(events, startOfDay(day), endOfDay(day)).sort(
1395
+ (a, b) => a.start.getTime() - b.start.getTime()
1396
+ );
1397
+
1265
1398
  // src/calendar/Calendar.jsx
1266
1399
  var DEFAULT_MAX_EVENTS_PER_DAY = 3;
1267
1400
  var ALL_VIEWS = ["month", "week", "day", "agenda"];
1401
+ var ALL_VIEWS_WITH_RESOURCE = ["month", "week", "day", "resource", "agenda"];
1268
1402
  var DEFAULT_DAY_START_HOUR = 8;
1269
1403
  var DEFAULT_DAY_END_HOUR = 20;
1270
1404
  var DEFAULT_TIME_ZONES = [
@@ -1300,7 +1434,8 @@ var VIEW_LABELS = {
1300
1434
  month: "Month",
1301
1435
  week: "Week",
1302
1436
  day: "Day",
1303
- agenda: "Agenda"
1437
+ agenda: "Agenda",
1438
+ resource: "Resource"
1304
1439
  };
1305
1440
  var DEFAULT_LABELS3 = {
1306
1441
  today: "Today",
@@ -1317,7 +1452,11 @@ var DEFAULT_LABELS3 = {
1317
1452
  errorMessage: "An error occurred while loading events.",
1318
1453
  dayDetailTitle: (label) => label,
1319
1454
  open: "Open",
1320
- allDay: "All day"
1455
+ allDay: "All day",
1456
+ reschedule: "Reschedule",
1457
+ pickDate: "Pick date",
1458
+ unassigned: "Unassigned",
1459
+ resource: "Resource"
1321
1460
  };
1322
1461
  var DEFAULT_EVENT_FIELDS = {
1323
1462
  id: "id",
@@ -1352,11 +1491,30 @@ var STATUS_VARIANT = {
1352
1491
  error: "danger",
1353
1492
  danger: "danger"
1354
1493
  };
1494
+ var TAG_VARIANT = {
1495
+ default: "default",
1496
+ info: "info",
1497
+ success: "success",
1498
+ warning: "warning",
1499
+ error: "error",
1500
+ danger: "error"
1501
+ };
1502
+ var MONTH_EVENT_STYLES = /* @__PURE__ */ new Set(["statusTag", "tag"]);
1503
+ var monthLabelMaxChars = (style) => {
1504
+ const overhead = style === "tag" ? 34 : 46;
1505
+ return Math.max(6, Math.floor((MONTH_COL_WIDTH - overhead) / 6.6));
1506
+ };
1507
+ var truncateMonthLabel = (value, max) => {
1508
+ const s = String(value == null ? "" : value);
1509
+ if (!max || s.length <= max) return s;
1510
+ return s.slice(0, max).trimEnd();
1511
+ };
1355
1512
  var MONTH_COL_WIDTH = 160;
1356
1513
  var TIMEGRID_DAY_COL = 150;
1357
1514
  var TIMEGRID_DAY_COL_SINGLE = 560;
1515
+ var RESOURCE_LABEL_COL_WIDTH = "min";
1358
1516
  var HOUR_SLOT_HEIGHT = 64;
1359
- var EventDetail = ({ event, labels }) => {
1517
+ var EventDetail = ({ event, labels, reschedule, idSuffix = "" }) => {
1360
1518
  const { start, end, title, subtitle, href } = event;
1361
1519
  let when = "";
1362
1520
  if (start) {
@@ -1370,11 +1528,27 @@ var EventDetail = ({ event, labels }) => {
1370
1528
  when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
1371
1529
  }
1372
1530
  }
1373
- return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "demibold" } }, title || "--"), when ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy" }, when) : null, subtitle ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, null, subtitle) : null, href ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Link, { href: href.url, external: href.external }, labels.open) : null);
1531
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "demibold" }, truncate: true }, title || "--"), when ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy", truncate: true }, when) : null, subtitle ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { truncate: true }, subtitle) : null, href ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Link, { href: href.url, external: href.external }, labels.open) : null, reschedule ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Divider, null), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy", format: { fontWeight: "demibold" } }, labels.reschedule), reschedule.options.length > 0 ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", gap: "xs", wrap: "wrap" }, reschedule.options.map((option, i) => /* @__PURE__ */ import_react5.default.createElement(
1532
+ import_ui_extensions5.Button,
1533
+ {
1534
+ key: `${option.label}-${i}`,
1535
+ size: "xs",
1536
+ variant: "secondary",
1537
+ onClick: () => reschedule.onPreset(event, option)
1538
+ },
1539
+ option.label
1540
+ ))) : null, /* @__PURE__ */ import_react5.default.createElement(
1541
+ import_ui_extensions5.DateInput,
1542
+ {
1543
+ name: `cal-resched-${event.key}${idSuffix}`,
1544
+ label: labels.pickDate,
1545
+ onChange: (value) => reschedule.onPick(event, value)
1546
+ }
1547
+ )) : null);
1374
1548
  };
1375
- var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1549
+ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "", reschedule = null) => {
1376
1550
  if (mode === "none") return void 0;
1377
- const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ import_react5.default.createElement(EventDetail, { event, labels });
1551
+ const body = renderEventDetail ? renderEventDetail(event) : /* @__PURE__ */ import_react5.default.createElement(EventDetail, { event, labels, reschedule, idSuffix });
1378
1552
  const id = `cal-evt-${event.key}${idSuffix}`;
1379
1553
  if (mode === "modal") {
1380
1554
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Modal, { id, title: event.title || labels.open, width: "small" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.ModalBody, null, body));
@@ -1382,46 +1556,34 @@ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1382
1556
  if (mode === "panel") {
1383
1557
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Panel, { id, title: event.title || labels.open, width: "small", variant: "modal" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.PanelBody, null, body));
1384
1558
  }
1385
- return /* @__PURE__ */ import_react5.default.createElement(import_experimental.Popover, { id, placement: "bottom" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Tile, { compact: true }, body));
1559
+ return /* @__PURE__ */ import_react5.default.createElement(import_experimental.Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Tile, { compact: true }, body));
1386
1560
  };
1387
- var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1561
+ var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule }) => {
1388
1562
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
1389
- const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "");
1563
+ const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-ag${day.getTime()}` : "", reschedule);
1390
1564
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
1391
1565
  const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
1392
1566
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Box, { flex: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy", format: { fontWeight: "demibold" } }, timeLabel))), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Box, { flex: 11 }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(ColorDot, { variant }), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { truncate: true }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Link, { overlay, onClick: handleClick }, event.title || "--")))), event.subtitle ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Box, { flex: 4 }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy", truncate: true }, event.subtitle))) : null);
1393
1567
  };
1394
- var MONTH_CHIP_WIDTH = MONTH_COL_WIDTH - 8;
1395
- var MONTH_CHIP_HEIGHT = 24;
1396
- var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1568
+ var MONTH_SLOT_HEIGHT = 24;
1569
+ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars, reschedule, idScope = "" }) => {
1397
1570
  const isStartDay = !day || !event.start || isSameDay(event.start, day);
1398
- const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
1571
+ const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${idScope}${day.getTime()}` : "", reschedule);
1399
1572
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
1400
1573
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
1401
1574
  const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
1402
1575
  const time = isStartDay && startHasTime ? `${formatTime(event.start)} ` : "";
1403
1576
  const prefix = isStartDay ? "" : "\u2192 ";
1404
- const chip = makeEventChipDataUri({
1405
- label: `${prefix}${time}${event.title || "--"}`,
1406
- width: MONTH_CHIP_WIDTH,
1407
- height: MONTH_CHIP_HEIGHT,
1408
- variant
1409
- });
1410
- return /* @__PURE__ */ import_react5.default.createElement(
1411
- import_ui_extensions5.Image,
1412
- {
1413
- src: chip.src,
1414
- width: chip.width,
1415
- height: chip.height,
1416
- alt: event.title || "",
1417
- overlay,
1418
- onClick: handleClick
1419
- }
1420
- );
1577
+ const maxChars = monthEventMaxChars != null ? monthEventMaxChars : monthLabelMaxChars(monthEventStyle);
1578
+ const label = truncateMonthLabel(`${prefix}${time}${event.title || "--"}`, maxChars);
1579
+ if (monthEventStyle === "tag") {
1580
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Tag, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
1581
+ }
1582
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Link, { overlay, onClick: handleClick }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, label));
1421
1583
  };
1422
- var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1584
+ var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, reschedule, idScope = "" }) => {
1423
1585
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
1424
- const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-more${day.getTime()}` : "-more");
1586
+ const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-more${idScope}${day.getTime()}` : "-more", reschedule);
1425
1587
  const href = event.href;
1426
1588
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Button, { variant: "transparent", size: "sm", href: href ? href.url : void 0, overlay, onClick: handleClick }, event.title || "--");
1427
1589
  };
@@ -1486,6 +1648,40 @@ var Toolbar = ({
1486
1648
  }
1487
1649
  ));
1488
1650
  };
1651
+ var DayChipStack = ({ day, events, maxEventsPerDay, chipProps, labels, idScope = "" }) => {
1652
+ const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
1653
+ const shown = events.slice(0, maxEventsPerDay);
1654
+ const hasOverflow = events.length > maxEventsPerDay;
1655
+ const heightSpacer = /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
1656
+ const slotRow = (key, content) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
1657
+ const slots = [];
1658
+ for (let i = 0; i < maxEventsPerDay; i++) {
1659
+ if (i < shown.length) {
1660
+ slots.push(slotRow(shown[i].key, /* @__PURE__ */ import_react5.default.createElement(MonthChip, { event: shown[i], day, idScope, ...chipProps })));
1661
+ } else {
1662
+ slots.push(/* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
1663
+ }
1664
+ }
1665
+ if (hasOverflow) {
1666
+ slots.push(
1667
+ slotRow(
1668
+ "more",
1669
+ /* @__PURE__ */ import_react5.default.createElement(
1670
+ import_ui_extensions5.Link,
1671
+ {
1672
+ overlay: /* @__PURE__ */ import_react5.default.createElement(import_experimental.Popover, { id: `cal-day-${idScope}${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Tile, { compact: true }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "bold" } }, String(events.length)), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Divider, null), events.map((event, i) => /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, { key: event.key }, /* @__PURE__ */ import_react5.default.createElement(DayListItem, { event, day, idScope, ...chipProps }), i < events.length - 1 ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Divider, null) : null)))))
1673
+ },
1674
+ labels.more(events.length - maxEventsPerDay)
1675
+ )
1676
+ )
1677
+ );
1678
+ } else {
1679
+ slots.push(
1680
+ /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
1681
+ );
1682
+ }
1683
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, slots);
1684
+ };
1489
1685
  var MonthView = ({
1490
1686
  refDate,
1491
1687
  now,
@@ -1500,49 +1696,21 @@ var MonthView = ({
1500
1696
  }) => {
1501
1697
  const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
1502
1698
  const today = now || /* @__PURE__ */ new Date();
1503
- const spacer24 = makeSpacerDataUri(MONTH_CHIP_HEIGHT, MONTH_COL_WIDTH);
1504
1699
  const renderCell = (day) => {
1505
1700
  const dayEvents = eventsForDay(day);
1506
1701
  const inMonth = isSameMonth(day, refDate);
1507
1702
  const isToday = isSameDay(day, today);
1508
1703
  if (renderDayCell) return renderDayCell(day, dayEvents);
1509
- const shown = dayEvents.slice(0, maxEventsPerDay);
1510
- const hasOverflow = dayEvents.length > maxEventsPerDay;
1511
- const slots = [];
1512
- for (let i = 0; i < maxEventsPerDay; i++) {
1513
- if (i < shown.length) {
1514
- slots.push(/* @__PURE__ */ import_react5.default.createElement(MonthChip, { key: shown[i].key, event: shown[i], day, ...chipProps }));
1515
- } else {
1516
- slots.push(
1517
- /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { key: `sp-${i}`, src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
1518
- );
1704
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.AutoGrid, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ import_react5.default.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), /* @__PURE__ */ import_react5.default.createElement(
1705
+ DayChipStack,
1706
+ {
1707
+ day,
1708
+ events: dayEvents,
1709
+ maxEventsPerDay,
1710
+ chipProps,
1711
+ labels
1519
1712
  }
1520
- }
1521
- if (hasOverflow) {
1522
- const more = makeMoreDataUri({
1523
- label: labels.more(dayEvents.length - maxEventsPerDay),
1524
- width: MONTH_COL_WIDTH,
1525
- height: MONTH_CHIP_HEIGHT
1526
- });
1527
- slots.push(
1528
- /* @__PURE__ */ import_react5.default.createElement(
1529
- import_ui_extensions5.Image,
1530
- {
1531
- key: "more",
1532
- src: more.src,
1533
- width: more.width,
1534
- height: more.height,
1535
- alt: labels.more(dayEvents.length - maxEventsPerDay),
1536
- overlay: /* @__PURE__ */ import_react5.default.createElement(import_experimental.Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Tile, { compact: true }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Heading, null, String(dayEvents.length)), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Divider, null), dayEvents.map((event, i) => /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, { key: event.key }, /* @__PURE__ */ import_react5.default.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Divider, null) : null)))))
1537
- }
1538
- )
1539
- );
1540
- } else {
1541
- slots.push(
1542
- /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { key: "more-sp", src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
1543
- );
1544
- }
1545
- return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ import_react5.default.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots);
1713
+ )));
1546
1714
  };
1547
1715
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Table, { bordered: true, flush: true }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHead, null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableRow, null, headers.map((h4) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHeader, { key: h4, width: "min", align: "center" }, h4.toUpperCase())))), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableBody, null, weeks.map((week, wi) => {
1548
1716
  const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
@@ -1564,6 +1732,33 @@ var AgendaView = ({ rangeStart, rangeEnd, eventsForDay, chipProps, labels, rende
1564
1732
  }
1565
1733
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "lg" }, days.map(({ day, events }) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { key: day.getTime(), direction: "column", gap: "sm" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "row", justify: "between", align: "end" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "demibold" } }, formatDayTitle(day)), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy" }, `${events.length} ${events.length === 1 ? "event" : "events"}`)), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Divider, null), events.map((event) => /* @__PURE__ */ import_react5.default.createElement(AgendaEventRow, { key: event.key, event, day, ...chipProps })))));
1566
1734
  };
1735
+ var ResourceView = ({ days, now, lanes, maxEventsPerDay, chipProps, labels, renderEmptyState }) => {
1736
+ const today = now || /* @__PURE__ */ new Date();
1737
+ if (!lanes || lanes.length === 0) {
1738
+ if (renderEmptyState) return renderEmptyState({});
1739
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.EmptyState, { title: labels.noEventsTitle }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, null, labels.noEventsMessage));
1740
+ }
1741
+ const rangeStart = startOfDay(days[0]);
1742
+ const rangeEnd = endOfDay(days[days.length - 1]);
1743
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Table, { bordered: true, flush: true }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHead, null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableRow, null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHeader, { width: RESOURCE_LABEL_COL_WIDTH }, String(labels.resource).toUpperCase()), days.map((day) => {
1744
+ const isToday = isSameDay(day, today);
1745
+ const label = `${formatWeekdayShort(day)} ${formatMonthShort(day)} ${day.getDate()}`;
1746
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHeader, { key: day.getTime(), width: "min", align: "center" }, isToday ? `${label} \xB7 Today` : label);
1747
+ }))), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableBody, null, lanes.map((lane, laneIndex) => {
1748
+ const visible = eventsIntersectingRange(lane.events, rangeStart, rangeEnd);
1749
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableRow, { key: lane.key }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableCell, { width: RESOURCE_LABEL_COL_WIDTH }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "flush" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "demibold" } }, lane.label), /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy" }, `${visible.length} ${visible.length === 1 ? "event" : "events"}`))), days.map((day) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableCell, { key: day.getTime(), width: "min" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.AutoGrid, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ import_react5.default.createElement(
1750
+ DayChipStack,
1751
+ {
1752
+ day,
1753
+ events: laneEventsForDay(visible, day),
1754
+ maxEventsPerDay,
1755
+ chipProps,
1756
+ labels,
1757
+ idScope: `r${laneIndex}-`
1758
+ }
1759
+ )))));
1760
+ })));
1761
+ };
1567
1762
  var formatTimedDuration = (start, end) => {
1568
1763
  const mins = Math.max(0, Math.round(((end || start).getTime() - start.getTime()) / 6e4));
1569
1764
  const h4 = Math.floor(mins / 60);
@@ -1585,6 +1780,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1585
1780
  const today = now || /* @__PURE__ */ new Date();
1586
1781
  const centerDays = days.length === 1;
1587
1782
  const dayColWidth = days.length === 1 ? TIMEGRID_DAY_COL_SINGLE : TIMEGRID_DAY_COL;
1783
+ const weekTitleMaxChars = Math.max(6, Math.floor((TIMEGRID_DAY_COL - 46) / 6.6));
1588
1784
  const todayInView = days.some((d) => isSameDay(d, today));
1589
1785
  const nowHour = today.getHours();
1590
1786
  const dayData = days.map((day) => {
@@ -1610,7 +1806,8 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1610
1806
  chipProps.overlayMode,
1611
1807
  chipProps.renderEventDetail,
1612
1808
  chipProps.labels,
1613
- `-tg${mode}${hour}-${dayMs}`
1809
+ `-tg${mode}${hour}-${dayMs}`,
1810
+ chipProps.reschedule
1614
1811
  );
1615
1812
  const handleClick = chipProps.onEventClick ? () => chipProps.onEventClick(e.raw, e) : void 0;
1616
1813
  const variant = VALID_VARIANTS.has(e.color) ? e.color : "default";
@@ -1629,10 +1826,10 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1629
1826
  } else if (mode === "cont") {
1630
1827
  sub = `\u2191 cont. through ${endLabel}`;
1631
1828
  }
1632
- return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Link, { overlay, onClick: handleClick }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, e.title || "--")), sub ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy" }, sub) : null);
1829
+ const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
1830
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Link, { overlay, onClick: handleClick }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, titleLabel)), sub ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { variant: "microcopy" }, sub) : null);
1633
1831
  };
1634
- const daySpacer = makeSpacerDataUri(1, dayColWidth);
1635
- const dayCell = (key, content) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableCell, { key, width: centerDays ? "max" : "min", align: "left" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, content, centerDays ? null : /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { src: daySpacer.src, width: daySpacer.width, height: daySpacer.height, alt: "" })));
1832
+ const dayCell = (key, content) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableCell, { key, width: centerDays ? "max" : "min", align: "left" }, centerDays ? /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, content) : /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.AutoGrid, { columnWidth: dayColWidth, gap: "flush" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "xs" }, content)));
1636
1833
  const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
1637
1834
  const emptyCell = null;
1638
1835
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Table, { bordered: true, flush: true, density: "compact" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHead, null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableRow, null, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.TableHeader, { width: "min" }, "TIME"), dayData.map(({ day }) => {
@@ -1688,9 +1885,21 @@ var Calendar = (props) => {
1688
1885
  weekStartsOn = 0,
1689
1886
  hideWeekends = false,
1690
1887
  maxEventsPerDay = DEFAULT_MAX_EVENTS_PER_DAY,
1888
+ // month-grid event token style: "statusTag" (dot + text, default) | "tag" (pill)
1889
+ monthEventStyle = "statusTag",
1890
+ // max characters for a month-cell label before "…" (default derived per style)
1891
+ monthEventMaxChars,
1691
1892
  // time grid (week / day)
1692
1893
  dayStartHour = DEFAULT_DAY_START_HOUR,
1693
1894
  dayEndHour = DEFAULT_DAY_END_HOUR,
1895
+ // resource / lane view (rows = resources, columns = the focused week's days)
1896
+ resources,
1897
+ resourceField,
1898
+ resourceLabels,
1899
+ showUnassignedLane = true,
1900
+ // drag-free reschedule (presets + date picker in the event-detail overlay)
1901
+ rescheduleOptions,
1902
+ onEventReschedule,
1694
1903
  // timezone
1695
1904
  timeZone: controlledTimeZone,
1696
1905
  defaultTimeZone,
@@ -1729,10 +1938,12 @@ var Calendar = (props) => {
1729
1938
  const fields = (0, import_react5.useMemo)(() => ({ ...DEFAULT_EVENT_FIELDS, ...eventFields || {} }), [eventFields]);
1730
1939
  const [internalView, setInternalView] = (0, import_react5.useState)(defaultView);
1731
1940
  const view = controlledView != null ? controlledView : internalView;
1941
+ const resourceEnabled = resources && resources.length > 0 || resourceField != null;
1732
1942
  const enabledViews = (0, import_react5.useMemo)(() => {
1733
- const base = viewsProp && viewsProp.length > 0 ? viewsProp : ALL_VIEWS;
1734
- return base.filter((v) => ALL_VIEWS.includes(v));
1735
- }, [viewsProp]);
1943
+ const all = resourceEnabled ? ALL_VIEWS_WITH_RESOURCE : ALL_VIEWS;
1944
+ const base = viewsProp && viewsProp.length > 0 ? viewsProp : all;
1945
+ return base.filter((v) => all.includes(v));
1946
+ }, [viewsProp, resourceEnabled]);
1736
1947
  const setView = (0, import_react5.useCallback)(
1737
1948
  (next) => {
1738
1949
  if (controlledView == null) setInternalView(next);
@@ -1757,7 +1968,9 @@ var Calendar = (props) => {
1757
1968
  const focusedDate = (controlledFocusedDate != null ? toDate(controlledFocusedDate) : internalDate) || startOfDay(nowWall);
1758
1969
  const stepFor = (0, import_react5.useCallback)(
1759
1970
  (dir) => {
1760
- if (view === "week" || view === "agenda") return addDays(focusedDate, dir * 7);
1971
+ if (view === "week" || view === "agenda" || view === "resource") {
1972
+ return addDays(focusedDate, dir * 7);
1973
+ }
1761
1974
  if (view === "day") return addDays(focusedDate, dir);
1762
1975
  return addMonths(focusedDate, dir);
1763
1976
  },
@@ -1784,7 +1997,7 @@ var Calendar = (props) => {
1784
1997
  rangeEnd: endOfDay(flat[flat.length - 1])
1785
1998
  };
1786
1999
  }
1787
- if (view === "week") {
2000
+ if (view === "week" || view === "resource") {
1788
2001
  const days = buildWeekDays(focusedDate, weekStartsOn, hideWeekends);
1789
2002
  return {
1790
2003
  weeks: null,
@@ -1857,14 +2070,18 @@ var Calendar = (props) => {
1857
2070
  );
1858
2071
  const normalized = (0, import_react5.useMemo)(
1859
2072
  () => (events || []).map((raw, index) => {
1860
- const start = toWallClock(toDate(resolveField(raw, fields.start)), timeZone);
1861
- const endRaw = toWallClock(toDate(resolveField(raw, fields.end)), timeZone);
2073
+ const sourceStart = toDate(resolveField(raw, fields.start));
2074
+ const sourceEnd = toDate(resolveField(raw, fields.end));
2075
+ const start = toWallClock(sourceStart, timeZone);
2076
+ const endRaw = toWallClock(sourceEnd, timeZone);
1862
2077
  const id = resolveField(raw, fields.id);
1863
2078
  return {
1864
2079
  key: id != null ? String(id) : `evt-${index}`,
1865
2080
  id,
1866
2081
  start,
1867
2082
  end: endRaw || start,
2083
+ sourceStart,
2084
+ sourceEnd: sourceEnd || sourceStart,
1868
2085
  title: resolveField(raw, fields.title),
1869
2086
  subtitle: resolveField(raw, fields.subtitle),
1870
2087
  color: resolveField(raw, fields.color),
@@ -1901,13 +2118,60 @@ var Calendar = (props) => {
1901
2118
  }, [rangeKey]);
1902
2119
  const title = (0, import_react5.useMemo)(() => {
1903
2120
  if (view === "day") return formatDayTitle(focusedDate);
1904
- if (view === "week" || view === "agenda") {
1905
- const days = buildWeekDays(focusedDate, weekStartsOn, view === "week" && hideWeekends);
2121
+ if (view === "week" || view === "agenda" || view === "resource") {
2122
+ const days = buildWeekDays(focusedDate, weekStartsOn, view !== "agenda" && hideWeekends);
1906
2123
  return formatRangeTitle(days[0], days[days.length - 1]);
1907
2124
  }
1908
2125
  return formatMonthTitle(focusedDate);
1909
2126
  }, [view, focusedDate, weekStartsOn, hideWeekends]);
1910
- const chipProps = { overlayMode, renderEventDetail, onEventClick, labels };
2127
+ const handleReschedulePreset = (0, import_react5.useCallback)(
2128
+ (event, option) => {
2129
+ const target = resolveRescheduleTarget(
2130
+ { start: event.sourceStart, end: event.sourceEnd },
2131
+ option,
2132
+ event
2133
+ );
2134
+ if (target && onEventReschedule) onEventReschedule(event.raw, target, event);
2135
+ },
2136
+ [onEventReschedule]
2137
+ );
2138
+ const handleReschedulePick = (0, import_react5.useCallback)(
2139
+ (event, value) => {
2140
+ const newStart = applyDatePick(event.sourceStart, value);
2141
+ if (!newStart) return;
2142
+ const target = rescheduleToStart({ start: event.sourceStart, end: event.sourceEnd }, newStart);
2143
+ if (target && onEventReschedule) onEventReschedule(event.raw, target, event);
2144
+ },
2145
+ [onEventReschedule]
2146
+ );
2147
+ const reschedule = (0, import_react5.useMemo)(() => {
2148
+ if (!rescheduleOptions) return null;
2149
+ return {
2150
+ options: normalizeRescheduleOptions(rescheduleOptions),
2151
+ onPreset: handleReschedulePreset,
2152
+ onPick: handleReschedulePick
2153
+ };
2154
+ }, [rescheduleOptions, handleReschedulePreset, handleReschedulePick]);
2155
+ const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
2156
+ const chipProps = {
2157
+ overlayMode,
2158
+ renderEventDetail,
2159
+ onEventClick,
2160
+ labels,
2161
+ monthEventStyle: safeMonthEventStyle,
2162
+ monthEventMaxChars,
2163
+ reschedule
2164
+ };
2165
+ const resourceLaneData = (0, import_react5.useMemo)(() => {
2166
+ if (view !== "resource") return null;
2167
+ return buildResourceLanes(queried, {
2168
+ resources,
2169
+ resourceLabels,
2170
+ getId: (e) => resolveResourceId(e.raw, resourceField),
2171
+ showUnassignedLane,
2172
+ unassignedLabel: labels.unassigned
2173
+ });
2174
+ }, [view, queried, resources, resourceLabels, resourceField, showUnassignedLane, labels]);
1911
2175
  const timeZoneOptions = (0, import_react5.useMemo)(() => {
1912
2176
  if (!showTimeZoneSelect) return null;
1913
2177
  const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
@@ -1968,6 +2232,19 @@ var Calendar = (props) => {
1968
2232
  labels
1969
2233
  }
1970
2234
  );
2235
+ } else if (view === "resource") {
2236
+ body = /* @__PURE__ */ import_react5.default.createElement(
2237
+ ResourceView,
2238
+ {
2239
+ days: gridDays,
2240
+ now: nowWall,
2241
+ lanes: resourceLaneData,
2242
+ maxEventsPerDay,
2243
+ chipProps,
2244
+ labels,
2245
+ renderEmptyState
2246
+ }
2247
+ );
1971
2248
  } else if (view === "week" || view === "day") {
1972
2249
  body = /* @__PURE__ */ import_react5.default.createElement(
1973
2250
  TimeGridView,
@@ -1997,6 +2274,7 @@ var Calendar = (props) => {
1997
2274
  }
1998
2275
  return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { direction: "column", gap: "sm" }, toolbar, body);
1999
2276
  };
2277
+ Calendar.displayName = "Calendar";
2000
2278
  // Annotate the CommonJS export names for ESM import in node:
2001
2279
  0 && (module.exports = {
2002
2280
  Calendar