hs-uix 2.1.0 → 2.1.1

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/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
@@ -1211,21 +1210,6 @@ var formatTimeZoneLabel = (tz, atDate = /* @__PURE__ */ new Date()) => {
1211
1210
 
1212
1211
  // src/calendar/svgChips.js
1213
1212
  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
1213
  var DOT_FILL = {
1230
1214
  default: "#7C98B6",
1231
1215
  info: "#00A4BD",
@@ -1240,23 +1224,6 @@ var makeDotDataUri = (variant = "default", size = 8) => {
1240
1224
  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
1225
  return { src: toDataUri(svg), width: size, height: size };
1242
1226
  };
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
1227
  var makeSpacerDataUri = (height = 24, width = 4) => {
1261
1228
  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
1229
  return { src: toDataUri(svg), width, height };
@@ -1352,6 +1319,24 @@ var STATUS_VARIANT = {
1352
1319
  error: "danger",
1353
1320
  danger: "danger"
1354
1321
  };
1322
+ var TAG_VARIANT = {
1323
+ default: "default",
1324
+ info: "info",
1325
+ success: "success",
1326
+ warning: "warning",
1327
+ error: "error",
1328
+ danger: "error"
1329
+ };
1330
+ var MONTH_EVENT_STYLES = /* @__PURE__ */ new Set(["statusTag", "tag"]);
1331
+ var monthLabelMaxChars = (style) => {
1332
+ const overhead = style === "tag" ? 34 : 46;
1333
+ return Math.max(6, Math.floor((MONTH_COL_WIDTH - overhead) / 6.6));
1334
+ };
1335
+ var truncateMonthLabel = (value, max) => {
1336
+ const s = String(value == null ? "" : value);
1337
+ if (!max || s.length <= max) return s;
1338
+ return s.slice(0, max).trimEnd();
1339
+ };
1355
1340
  var MONTH_COL_WIDTH = 160;
1356
1341
  var TIMEGRID_DAY_COL = 150;
1357
1342
  var TIMEGRID_DAY_COL_SINGLE = 560;
@@ -1370,7 +1355,7 @@ var EventDetail = ({ event, labels }) => {
1370
1355
  when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
1371
1356
  }
1372
1357
  }
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);
1358
+ 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);
1374
1359
  };
1375
1360
  var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1376
1361
  if (mode === "none") return void 0;
@@ -1382,7 +1367,7 @@ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1382
1367
  if (mode === "panel") {
1383
1368
  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
1369
  }
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));
1370
+ 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
1371
  };
1387
1372
  var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1388
1373
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
@@ -1391,9 +1376,8 @@ var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick
1391
1376
  const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
1392
1377
  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
1378
  };
1394
- var MONTH_CHIP_WIDTH = MONTH_COL_WIDTH - 8;
1395
- var MONTH_CHIP_HEIGHT = 24;
1396
- var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1379
+ var MONTH_SLOT_HEIGHT = 24;
1380
+ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars }) => {
1397
1381
  const isStartDay = !day || !event.start || isSameDay(event.start, day);
1398
1382
  const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
1399
1383
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -1401,23 +1385,12 @@ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, lab
1401
1385
  const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
1402
1386
  const time = isStartDay && startHasTime ? `${formatTime(event.start)} ` : "";
1403
1387
  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
- );
1388
+ const maxChars = monthEventMaxChars != null ? monthEventMaxChars : monthLabelMaxChars(monthEventStyle);
1389
+ const label = truncateMonthLabel(`${prefix}${time}${event.title || "--"}`, maxChars);
1390
+ if (monthEventStyle === "tag") {
1391
+ return /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Tag, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
1392
+ }
1393
+ 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
1394
  };
1422
1395
  var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1423
1396
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -1500,7 +1473,7 @@ var MonthView = ({
1500
1473
  }) => {
1501
1474
  const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
1502
1475
  const today = now || /* @__PURE__ */ new Date();
1503
- const spacer24 = makeSpacerDataUri(MONTH_CHIP_HEIGHT, MONTH_COL_WIDTH);
1476
+ const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
1504
1477
  const renderCell = (day) => {
1505
1478
  const dayEvents = eventsForDay(day);
1506
1479
  const inMonth = isSameMonth(day, refDate);
@@ -1508,41 +1481,35 @@ var MonthView = ({
1508
1481
  if (renderDayCell) return renderDayCell(day, dayEvents);
1509
1482
  const shown = dayEvents.slice(0, maxEventsPerDay);
1510
1483
  const hasOverflow = dayEvents.length > maxEventsPerDay;
1484
+ const heightSpacer = /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
1485
+ const slotRow = (key, content) => /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Flex, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
1511
1486
  const slots = [];
1512
1487
  for (let i = 0; i < maxEventsPerDay; i++) {
1513
1488
  if (i < shown.length) {
1514
- slots.push(/* @__PURE__ */ import_react5.default.createElement(MonthChip, { key: shown[i].key, event: shown[i], day, ...chipProps }));
1489
+ slots.push(slotRow(shown[i].key, /* @__PURE__ */ import_react5.default.createElement(MonthChip, { event: shown[i], day, ...chipProps })));
1515
1490
  } 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
- );
1491
+ slots.push(/* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
1519
1492
  }
1520
1493
  }
1521
1494
  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
1495
  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
- }
1496
+ slotRow(
1497
+ "more",
1498
+ /* @__PURE__ */ import_react5.default.createElement(
1499
+ import_ui_extensions5.Link,
1500
+ {
1501
+ 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", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Text, { format: { fontWeight: "bold" } }, 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)))))
1502
+ },
1503
+ labels.more(dayEvents.length - maxEventsPerDay)
1504
+ )
1538
1505
  )
1539
1506
  );
1540
1507
  } else {
1541
1508
  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: "" })
1509
+ /* @__PURE__ */ import_react5.default.createElement(import_ui_extensions5.Image, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
1543
1510
  );
1544
1511
  }
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);
1512
+ 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()))), slots));
1546
1513
  };
1547
1514
  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
1515
  const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
@@ -1585,6 +1552,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1585
1552
  const today = now || /* @__PURE__ */ new Date();
1586
1553
  const centerDays = days.length === 1;
1587
1554
  const dayColWidth = days.length === 1 ? TIMEGRID_DAY_COL_SINGLE : TIMEGRID_DAY_COL;
1555
+ const weekTitleMaxChars = Math.max(6, Math.floor((TIMEGRID_DAY_COL - 46) / 6.6));
1588
1556
  const todayInView = days.some((d) => isSameDay(d, today));
1589
1557
  const nowHour = today.getHours();
1590
1558
  const dayData = days.map((day) => {
@@ -1629,10 +1597,10 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1629
1597
  } else if (mode === "cont") {
1630
1598
  sub = `\u2191 cont. through ${endLabel}`;
1631
1599
  }
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);
1600
+ const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
1601
+ 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
1602
  };
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: "" })));
1603
+ 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
1604
  const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
1637
1605
  const emptyCell = null;
1638
1606
  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,6 +1656,10 @@ var Calendar = (props) => {
1688
1656
  weekStartsOn = 0,
1689
1657
  hideWeekends = false,
1690
1658
  maxEventsPerDay = DEFAULT_MAX_EVENTS_PER_DAY,
1659
+ // month-grid event token style: "statusTag" (dot + text, default) | "tag" (pill)
1660
+ monthEventStyle = "statusTag",
1661
+ // max characters for a month-cell label before "…" (default derived per style)
1662
+ monthEventMaxChars,
1691
1663
  // time grid (week / day)
1692
1664
  dayStartHour = DEFAULT_DAY_START_HOUR,
1693
1665
  dayEndHour = DEFAULT_DAY_END_HOUR,
@@ -1907,7 +1879,15 @@ var Calendar = (props) => {
1907
1879
  }
1908
1880
  return formatMonthTitle(focusedDate);
1909
1881
  }, [view, focusedDate, weekStartsOn, hideWeekends]);
1910
- const chipProps = { overlayMode, renderEventDetail, onEventClick, labels };
1882
+ const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
1883
+ const chipProps = {
1884
+ overlayMode,
1885
+ renderEventDetail,
1886
+ onEventClick,
1887
+ labels,
1888
+ monthEventStyle: safeMonthEventStyle,
1889
+ monthEventMaxChars
1890
+ };
1911
1891
  const timeZoneOptions = (0, import_react5.useMemo)(() => {
1912
1892
  if (!showTimeZoneSelect) return null;
1913
1893
  const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
package/dist/calendar.mjs CHANGED
@@ -2,6 +2,7 @@
2
2
  import React5, { useCallback, useEffect, useMemo, useState as useState2 } from "react";
3
3
  import {
4
4
  Alert,
5
+ AutoGrid,
5
6
  Box as Box2,
6
7
  Button as Button3,
7
8
  Divider,
@@ -91,7 +92,6 @@ import React2 from "react";
91
92
  import { Image, Icon as HsIcon, Link } from "@hubspot/ui-extensions";
92
93
 
93
94
  // src/common-components/svgDefaults.js
94
- var HS_FONT_FAMILY = '"Lexend Deca", Helvetica, Arial, sans-serif';
95
95
  var HS_TEXT_COLOR = "#33475b";
96
96
 
97
97
  // src/common-components/icons.generated.js
@@ -1213,21 +1213,6 @@ var formatTimeZoneLabel = (tz, atDate = /* @__PURE__ */ new Date()) => {
1213
1213
 
1214
1214
  // src/calendar/svgChips.js
1215
1215
  var toDataUri = (svg) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
1216
- var escapeXml = (s) => String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1217
- var truncateLabel = (value, max) => {
1218
- const s = String(value == null ? "" : value);
1219
- if (!max || s.length <= max) return s;
1220
- return s.slice(0, Math.max(1, max - 1)).trimEnd() + "\u2026";
1221
- };
1222
- var CHIP_PALETTE = {
1223
- default: { fill: "#7FD1DE", text: HS_TEXT_COLOR },
1224
- info: { fill: "#00A4BD", text: "#FFFFFF" },
1225
- success: { fill: "#00BDA5", text: "#FFFFFF" },
1226
- warning: { fill: "#F5C26B", text: HS_TEXT_COLOR },
1227
- error: { fill: "#F2545B", text: "#FFFFFF" },
1228
- danger: { fill: "#F2545B", text: "#FFFFFF" }
1229
- // StatusTag spells red "danger"; accept both
1230
- };
1231
1216
  var DOT_FILL = {
1232
1217
  default: "#7C98B6",
1233
1218
  info: "#00A4BD",
@@ -1242,23 +1227,6 @@ var makeDotDataUri = (variant = "default", size = 8) => {
1242
1227
  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>`;
1243
1228
  return { src: toDataUri(svg), width: size, height: size };
1244
1229
  };
1245
- var makeEventChipDataUri = (opts) => {
1246
- const { label, width, height = 24, variant = "default" } = opts;
1247
- const palette = CHIP_PALETTE[variant] || CHIP_PALETTE.default;
1248
- const accentX = 5;
1249
- const accentW = 3;
1250
- const textX = accentX + accentW + 6;
1251
- const rightPad = 8;
1252
- const maxChars = Math.max(1, Math.floor((width - textX - rightPad) / 6));
1253
- const text = truncateLabel(label, maxChars);
1254
- 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>`;
1255
- return { src: toDataUri(svg), width, height };
1256
- };
1257
- var makeMoreDataUri = (opts) => {
1258
- const { label, width, height = 24 } = opts;
1259
- 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>`;
1260
- return { src: toDataUri(svg), width, height };
1261
- };
1262
1230
  var makeSpacerDataUri = (height = 24, width = 4) => {
1263
1231
  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>`;
1264
1232
  return { src: toDataUri(svg), width, height };
@@ -1354,6 +1322,24 @@ var STATUS_VARIANT = {
1354
1322
  error: "danger",
1355
1323
  danger: "danger"
1356
1324
  };
1325
+ var TAG_VARIANT = {
1326
+ default: "default",
1327
+ info: "info",
1328
+ success: "success",
1329
+ warning: "warning",
1330
+ error: "error",
1331
+ danger: "error"
1332
+ };
1333
+ var MONTH_EVENT_STYLES = /* @__PURE__ */ new Set(["statusTag", "tag"]);
1334
+ var monthLabelMaxChars = (style) => {
1335
+ const overhead = style === "tag" ? 34 : 46;
1336
+ return Math.max(6, Math.floor((MONTH_COL_WIDTH - overhead) / 6.6));
1337
+ };
1338
+ var truncateMonthLabel = (value, max) => {
1339
+ const s = String(value == null ? "" : value);
1340
+ if (!max || s.length <= max) return s;
1341
+ return s.slice(0, max).trimEnd();
1342
+ };
1357
1343
  var MONTH_COL_WIDTH = 160;
1358
1344
  var TIMEGRID_DAY_COL = 150;
1359
1345
  var TIMEGRID_DAY_COL_SINGLE = 560;
@@ -1372,7 +1358,7 @@ var EventDetail = ({ event, labels }) => {
1372
1358
  when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
1373
1359
  }
1374
1360
  }
1375
- return /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, /* @__PURE__ */ React5.createElement(Text, { format: { fontWeight: "demibold" } }, title || "--"), when ? /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy" }, when) : null, subtitle ? /* @__PURE__ */ React5.createElement(Text, null, subtitle) : null, href ? /* @__PURE__ */ React5.createElement(Link2, { href: href.url, external: href.external }, labels.open) : null);
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);
1376
1362
  };
1377
1363
  var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1378
1364
  if (mode === "none") return void 0;
@@ -1384,7 +1370,7 @@ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
1384
1370
  if (mode === "panel") {
1385
1371
  return /* @__PURE__ */ React5.createElement(Panel, { id, title: event.title || labels.open, width: "small", variant: "modal" }, /* @__PURE__ */ React5.createElement(PanelBody, null, body));
1386
1372
  }
1387
- return /* @__PURE__ */ React5.createElement(Popover, { id, placement: "bottom" }, /* @__PURE__ */ React5.createElement(Tile, { compact: true }, body));
1373
+ return /* @__PURE__ */ React5.createElement(Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ React5.createElement(Tile, { compact: true }, body));
1388
1374
  };
1389
1375
  var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1390
1376
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
@@ -1393,9 +1379,8 @@ var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick
1393
1379
  const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
1394
1380
  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);
1395
1381
  };
1396
- var MONTH_CHIP_WIDTH = MONTH_COL_WIDTH - 8;
1397
- var MONTH_CHIP_HEIGHT = 24;
1398
- var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1382
+ var MONTH_SLOT_HEIGHT = 24;
1383
+ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars }) => {
1399
1384
  const isStartDay = !day || !event.start || isSameDay(event.start, day);
1400
1385
  const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
1401
1386
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -1403,23 +1388,12 @@ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, lab
1403
1388
  const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
1404
1389
  const time = isStartDay && startHasTime ? `${formatTime(event.start)} ` : "";
1405
1390
  const prefix = isStartDay ? "" : "\u2192 ";
1406
- const chip = makeEventChipDataUri({
1407
- label: `${prefix}${time}${event.title || "--"}`,
1408
- width: MONTH_CHIP_WIDTH,
1409
- height: MONTH_CHIP_HEIGHT,
1410
- variant
1411
- });
1412
- return /* @__PURE__ */ React5.createElement(
1413
- Image2,
1414
- {
1415
- src: chip.src,
1416
- width: chip.width,
1417
- height: chip.height,
1418
- alt: event.title || "",
1419
- overlay,
1420
- onClick: handleClick
1421
- }
1422
- );
1391
+ const maxChars = monthEventMaxChars != null ? monthEventMaxChars : monthLabelMaxChars(monthEventStyle);
1392
+ const label = truncateMonthLabel(`${prefix}${time}${event.title || "--"}`, maxChars);
1393
+ if (monthEventStyle === "tag") {
1394
+ return /* @__PURE__ */ React5.createElement(Tag2, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
1395
+ }
1396
+ return /* @__PURE__ */ React5.createElement(Link2, { overlay, onClick: handleClick }, /* @__PURE__ */ React5.createElement(StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, label));
1423
1397
  };
1424
1398
  var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
1425
1399
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -1502,7 +1476,7 @@ var MonthView = ({
1502
1476
  }) => {
1503
1477
  const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
1504
1478
  const today = now || /* @__PURE__ */ new Date();
1505
- const spacer24 = makeSpacerDataUri(MONTH_CHIP_HEIGHT, MONTH_COL_WIDTH);
1479
+ const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
1506
1480
  const renderCell = (day) => {
1507
1481
  const dayEvents = eventsForDay(day);
1508
1482
  const inMonth = isSameMonth(day, refDate);
@@ -1510,41 +1484,35 @@ var MonthView = ({
1510
1484
  if (renderDayCell) return renderDayCell(day, dayEvents);
1511
1485
  const shown = dayEvents.slice(0, maxEventsPerDay);
1512
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);
1513
1489
  const slots = [];
1514
1490
  for (let i = 0; i < maxEventsPerDay; i++) {
1515
1491
  if (i < shown.length) {
1516
- slots.push(/* @__PURE__ */ React5.createElement(MonthChip, { key: shown[i].key, event: shown[i], day, ...chipProps }));
1492
+ slots.push(slotRow(shown[i].key, /* @__PURE__ */ React5.createElement(MonthChip, { event: shown[i], day, ...chipProps })));
1517
1493
  } else {
1518
- slots.push(
1519
- /* @__PURE__ */ React5.createElement(Image2, { key: `sp-${i}`, src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
1520
- );
1494
+ slots.push(/* @__PURE__ */ React5.createElement(Image2, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
1521
1495
  }
1522
1496
  }
1523
1497
  if (hasOverflow) {
1524
- const more = makeMoreDataUri({
1525
- label: labels.more(dayEvents.length - maxEventsPerDay),
1526
- width: MONTH_COL_WIDTH,
1527
- height: MONTH_CHIP_HEIGHT
1528
- });
1529
1498
  slots.push(
1530
- /* @__PURE__ */ React5.createElement(
1531
- Image2,
1532
- {
1533
- key: "more",
1534
- src: more.src,
1535
- width: more.width,
1536
- height: more.height,
1537
- alt: labels.more(dayEvents.length - maxEventsPerDay),
1538
- 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", align: "center", gap: "sm" }, /* @__PURE__ */ React5.createElement(Heading, null, 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)))))
1539
- }
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
+ )
1540
1508
  )
1541
1509
  );
1542
1510
  } else {
1543
1511
  slots.push(
1544
- /* @__PURE__ */ React5.createElement(Image2, { key: "more-sp", src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
1512
+ /* @__PURE__ */ React5.createElement(Image2, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
1545
1513
  );
1546
1514
  }
1547
- return /* @__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);
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));
1548
1516
  };
1549
1517
  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) => {
1550
1518
  const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
@@ -1587,6 +1555,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1587
1555
  const today = now || /* @__PURE__ */ new Date();
1588
1556
  const centerDays = days.length === 1;
1589
1557
  const dayColWidth = days.length === 1 ? TIMEGRID_DAY_COL_SINGLE : TIMEGRID_DAY_COL;
1558
+ const weekTitleMaxChars = Math.max(6, Math.floor((TIMEGRID_DAY_COL - 46) / 6.6));
1590
1559
  const todayInView = days.some((d) => isSameDay(d, today));
1591
1560
  const nowHour = today.getHours();
1592
1561
  const dayData = days.map((day) => {
@@ -1631,10 +1600,10 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
1631
1600
  } else if (mode === "cont") {
1632
1601
  sub = `\u2191 cont. through ${endLabel}`;
1633
1602
  }
1634
- return /* @__PURE__ */ React5.createElement(Flex4, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ React5.createElement(Link2, { overlay, onClick: handleClick }, /* @__PURE__ */ React5.createElement(StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, e.title || "--")), sub ? /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy" }, sub) : null);
1603
+ const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
1604
+ return /* @__PURE__ */ React5.createElement(Flex4, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ React5.createElement(Link2, { overlay, onClick: handleClick }, /* @__PURE__ */ React5.createElement(StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, titleLabel)), sub ? /* @__PURE__ */ React5.createElement(Text, { variant: "microcopy" }, sub) : null);
1635
1605
  };
1636
- const daySpacer = makeSpacerDataUri(1, dayColWidth);
1637
- const dayCell = (key, content) => /* @__PURE__ */ React5.createElement(TableCell, { key, width: centerDays ? "max" : "min", align: "left" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, content, centerDays ? null : /* @__PURE__ */ React5.createElement(Image2, { src: daySpacer.src, width: daySpacer.width, height: daySpacer.height, alt: "" })));
1606
+ const dayCell = (key, content) => /* @__PURE__ */ React5.createElement(TableCell, { key, width: centerDays ? "max" : "min", align: "left" }, centerDays ? /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, content) : /* @__PURE__ */ React5.createElement(AutoGrid, { columnWidth: dayColWidth, gap: "flush" }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "column", gap: "xs" }, content)));
1638
1607
  const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
1639
1608
  const emptyCell = null;
1640
1609
  return /* @__PURE__ */ React5.createElement(Table, { bordered: true, flush: true, density: "compact" }, /* @__PURE__ */ React5.createElement(TableHead, null, /* @__PURE__ */ React5.createElement(TableRow, null, /* @__PURE__ */ React5.createElement(TableHeader, { width: "min" }, "TIME"), dayData.map(({ day }) => {
@@ -1690,6 +1659,10 @@ var Calendar = (props) => {
1690
1659
  weekStartsOn = 0,
1691
1660
  hideWeekends = false,
1692
1661
  maxEventsPerDay = DEFAULT_MAX_EVENTS_PER_DAY,
1662
+ // month-grid event token style: "statusTag" (dot + text, default) | "tag" (pill)
1663
+ monthEventStyle = "statusTag",
1664
+ // max characters for a month-cell label before "…" (default derived per style)
1665
+ monthEventMaxChars,
1693
1666
  // time grid (week / day)
1694
1667
  dayStartHour = DEFAULT_DAY_START_HOUR,
1695
1668
  dayEndHour = DEFAULT_DAY_END_HOUR,
@@ -1909,7 +1882,15 @@ var Calendar = (props) => {
1909
1882
  }
1910
1883
  return formatMonthTitle(focusedDate);
1911
1884
  }, [view, focusedDate, weekStartsOn, hideWeekends]);
1912
- const chipProps = { overlayMode, renderEventDetail, onEventClick, labels };
1885
+ const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
1886
+ const chipProps = {
1887
+ overlayMode,
1888
+ renderEventDetail,
1889
+ onEventClick,
1890
+ labels,
1891
+ monthEventStyle: safeMonthEventStyle,
1892
+ monthEventMaxChars
1893
+ };
1913
1894
  const timeZoneOptions = useMemo(() => {
1914
1895
  if (!showTimeZoneSelect) return null;
1915
1896
  const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
package/dist/index.js CHANGED
@@ -6957,21 +6957,6 @@ var formatTimeZoneLabel = (tz, atDate = /* @__PURE__ */ new Date()) => {
6957
6957
 
6958
6958
  // src/calendar/svgChips.js
6959
6959
  var toDataUri = (svg) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
6960
- var escapeXml = (s) => String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6961
- var truncateLabel = (value, max) => {
6962
- const s = String(value == null ? "" : value);
6963
- if (!max || s.length <= max) return s;
6964
- return s.slice(0, Math.max(1, max - 1)).trimEnd() + "\u2026";
6965
- };
6966
- var CHIP_PALETTE = {
6967
- default: { fill: "#7FD1DE", text: HS_TEXT_COLOR },
6968
- info: { fill: "#00A4BD", text: "#FFFFFF" },
6969
- success: { fill: "#00BDA5", text: "#FFFFFF" },
6970
- warning: { fill: "#F5C26B", text: HS_TEXT_COLOR },
6971
- error: { fill: "#F2545B", text: "#FFFFFF" },
6972
- danger: { fill: "#F2545B", text: "#FFFFFF" }
6973
- // StatusTag spells red "danger"; accept both
6974
- };
6975
6960
  var DOT_FILL = {
6976
6961
  default: "#7C98B6",
6977
6962
  info: "#00A4BD",
@@ -6986,23 +6971,6 @@ var makeDotDataUri = (variant = "default", size = 8) => {
6986
6971
  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>`;
6987
6972
  return { src: toDataUri(svg), width: size, height: size };
6988
6973
  };
6989
- var makeEventChipDataUri = (opts) => {
6990
- const { label, width, height = 24, variant = "default" } = opts;
6991
- const palette = CHIP_PALETTE[variant] || CHIP_PALETTE.default;
6992
- const accentX = 5;
6993
- const accentW = 3;
6994
- const textX = accentX + accentW + 6;
6995
- const rightPad = 8;
6996
- const maxChars = Math.max(1, Math.floor((width - textX - rightPad) / 6));
6997
- const text = truncateLabel(label, maxChars);
6998
- 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>`;
6999
- return { src: toDataUri(svg), width, height };
7000
- };
7001
- var makeMoreDataUri = (opts) => {
7002
- const { label, width, height = 24 } = opts;
7003
- 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>`;
7004
- return { src: toDataUri(svg), width, height };
7005
- };
7006
6974
  var makeSpacerDataUri = (height = 24, width = 4) => {
7007
6975
  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>`;
7008
6976
  return { src: toDataUri(svg), width, height };
@@ -7098,6 +7066,24 @@ var STATUS_VARIANT = {
7098
7066
  error: "danger",
7099
7067
  danger: "danger"
7100
7068
  };
7069
+ var TAG_VARIANT = {
7070
+ default: "default",
7071
+ info: "info",
7072
+ success: "success",
7073
+ warning: "warning",
7074
+ error: "error",
7075
+ danger: "error"
7076
+ };
7077
+ var MONTH_EVENT_STYLES = /* @__PURE__ */ new Set(["statusTag", "tag"]);
7078
+ var monthLabelMaxChars = (style) => {
7079
+ const overhead = style === "tag" ? 34 : 46;
7080
+ return Math.max(6, Math.floor((MONTH_COL_WIDTH - overhead) / 6.6));
7081
+ };
7082
+ var truncateMonthLabel = (value, max) => {
7083
+ const s = String(value == null ? "" : value);
7084
+ if (!max || s.length <= max) return s;
7085
+ return s.slice(0, max).trimEnd();
7086
+ };
7101
7087
  var MONTH_COL_WIDTH = 160;
7102
7088
  var TIMEGRID_DAY_COL = 150;
7103
7089
  var TIMEGRID_DAY_COL_SINGLE = 560;
@@ -7116,7 +7102,7 @@ var EventDetail = ({ event, labels }) => {
7116
7102
  when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
7117
7103
  }
7118
7104
  }
7119
- return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { format: { fontWeight: "demibold" } }, title || "--"), when ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy" }, when) : null, subtitle ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, null, subtitle) : null, href ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Link, { href: href.url, external: href.external }, labels.open) : null);
7105
+ return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { format: { fontWeight: "demibold" }, truncate: true }, title || "--"), when ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy", truncate: true }, when) : null, subtitle ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { truncate: true }, subtitle) : null, href ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Link, { href: href.url, external: href.external }, labels.open) : null);
7120
7106
  };
7121
7107
  var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
7122
7108
  if (mode === "none") return void 0;
@@ -7128,7 +7114,7 @@ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
7128
7114
  if (mode === "panel") {
7129
7115
  return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Panel, { id, title: event.title || labels.open, width: "small", variant: "modal" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.PanelBody, null, body));
7130
7116
  }
7131
- return /* @__PURE__ */ import_react15.default.createElement(import_experimental.Popover, { id, placement: "bottom" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Tile, { compact: true }, body));
7117
+ return /* @__PURE__ */ import_react15.default.createElement(import_experimental.Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Tile, { compact: true }, body));
7132
7118
  };
7133
7119
  var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
7134
7120
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
@@ -7137,9 +7123,8 @@ var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick
7137
7123
  const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
7138
7124
  return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Box, { flex: 2 }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy", format: { fontWeight: "demibold" } }, timeLabel))), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Box, { flex: 11 }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react15.default.createElement(ColorDot, { variant }), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { truncate: true }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Link, { overlay, onClick: handleClick }, event.title || "--")))), event.subtitle ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Box, { flex: 4 }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy", truncate: true }, event.subtitle))) : null);
7139
7125
  };
7140
- var MONTH_CHIP_WIDTH = MONTH_COL_WIDTH - 8;
7141
- var MONTH_CHIP_HEIGHT = 24;
7142
- var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
7126
+ var MONTH_SLOT_HEIGHT = 24;
7127
+ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars }) => {
7143
7128
  const isStartDay = !day || !event.start || isSameDay(event.start, day);
7144
7129
  const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
7145
7130
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -7147,23 +7132,12 @@ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, lab
7147
7132
  const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
7148
7133
  const time = isStartDay && startHasTime ? `${formatTime(event.start)} ` : "";
7149
7134
  const prefix = isStartDay ? "" : "\u2192 ";
7150
- const chip = makeEventChipDataUri({
7151
- label: `${prefix}${time}${event.title || "--"}`,
7152
- width: MONTH_CHIP_WIDTH,
7153
- height: MONTH_CHIP_HEIGHT,
7154
- variant
7155
- });
7156
- return /* @__PURE__ */ import_react15.default.createElement(
7157
- import_ui_extensions15.Image,
7158
- {
7159
- src: chip.src,
7160
- width: chip.width,
7161
- height: chip.height,
7162
- alt: event.title || "",
7163
- overlay,
7164
- onClick: handleClick
7165
- }
7166
- );
7135
+ const maxChars = monthEventMaxChars != null ? monthEventMaxChars : monthLabelMaxChars(monthEventStyle);
7136
+ const label = truncateMonthLabel(`${prefix}${time}${event.title || "--"}`, maxChars);
7137
+ if (monthEventStyle === "tag") {
7138
+ return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Tag, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
7139
+ }
7140
+ return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Link, { overlay, onClick: handleClick }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, label));
7167
7141
  };
7168
7142
  var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
7169
7143
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -7246,7 +7220,7 @@ var MonthView = ({
7246
7220
  }) => {
7247
7221
  const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
7248
7222
  const today = now || /* @__PURE__ */ new Date();
7249
- const spacer24 = makeSpacerDataUri(MONTH_CHIP_HEIGHT, MONTH_COL_WIDTH);
7223
+ const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
7250
7224
  const renderCell = (day) => {
7251
7225
  const dayEvents = eventsForDay(day);
7252
7226
  const inMonth = isSameMonth(day, refDate);
@@ -7254,41 +7228,35 @@ var MonthView = ({
7254
7228
  if (renderDayCell) return renderDayCell(day, dayEvents);
7255
7229
  const shown = dayEvents.slice(0, maxEventsPerDay);
7256
7230
  const hasOverflow = dayEvents.length > maxEventsPerDay;
7231
+ const heightSpacer = /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Image, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
7232
+ const slotRow = (key, content) => /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
7257
7233
  const slots = [];
7258
7234
  for (let i = 0; i < maxEventsPerDay; i++) {
7259
7235
  if (i < shown.length) {
7260
- slots.push(/* @__PURE__ */ import_react15.default.createElement(MonthChip, { key: shown[i].key, event: shown[i], day, ...chipProps }));
7236
+ slots.push(slotRow(shown[i].key, /* @__PURE__ */ import_react15.default.createElement(MonthChip, { event: shown[i], day, ...chipProps })));
7261
7237
  } else {
7262
- slots.push(
7263
- /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Image, { key: `sp-${i}`, src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
7264
- );
7238
+ slots.push(/* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Image, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
7265
7239
  }
7266
7240
  }
7267
7241
  if (hasOverflow) {
7268
- const more = makeMoreDataUri({
7269
- label: labels.more(dayEvents.length - maxEventsPerDay),
7270
- width: MONTH_COL_WIDTH,
7271
- height: MONTH_CHIP_HEIGHT
7272
- });
7273
7242
  slots.push(
7274
- /* @__PURE__ */ import_react15.default.createElement(
7275
- import_ui_extensions15.Image,
7276
- {
7277
- key: "more",
7278
- src: more.src,
7279
- width: more.width,
7280
- height: more.height,
7281
- alt: labels.more(dayEvents.length - maxEventsPerDay),
7282
- overlay: /* @__PURE__ */ import_react15.default.createElement(import_experimental.Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Tile, { compact: true }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Heading, null, String(dayEvents.length)), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Divider, null), dayEvents.map((event, i) => /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, { key: event.key }, /* @__PURE__ */ import_react15.default.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Divider, null) : null)))))
7283
- }
7243
+ slotRow(
7244
+ "more",
7245
+ /* @__PURE__ */ import_react15.default.createElement(
7246
+ import_ui_extensions15.Link,
7247
+ {
7248
+ overlay: /* @__PURE__ */ import_react15.default.createElement(import_experimental.Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Tile, { compact: true }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { format: { fontWeight: "bold" } }, String(dayEvents.length)), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Divider, null), dayEvents.map((event, i) => /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, { key: event.key }, /* @__PURE__ */ import_react15.default.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Divider, null) : null)))))
7249
+ },
7250
+ labels.more(dayEvents.length - maxEventsPerDay)
7251
+ )
7284
7252
  )
7285
7253
  );
7286
7254
  } else {
7287
7255
  slots.push(
7288
- /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Image, { key: "more-sp", src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
7256
+ /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Image, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
7289
7257
  );
7290
7258
  }
7291
- return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ import_react15.default.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots);
7259
+ return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.AutoGrid, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ import_react15.default.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots));
7292
7260
  };
7293
7261
  return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Table, { bordered: true, flush: true }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableHead, null, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableRow, null, headers.map((h6) => /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableHeader, { key: h6, width: "min", align: "center" }, h6.toUpperCase())))), /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableBody, null, weeks.map((week, wi) => {
7294
7262
  const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
@@ -7331,6 +7299,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
7331
7299
  const today = now || /* @__PURE__ */ new Date();
7332
7300
  const centerDays = days.length === 1;
7333
7301
  const dayColWidth = days.length === 1 ? TIMEGRID_DAY_COL_SINGLE : TIMEGRID_DAY_COL;
7302
+ const weekTitleMaxChars = Math.max(6, Math.floor((TIMEGRID_DAY_COL - 46) / 6.6));
7334
7303
  const todayInView = days.some((d) => isSameDay(d, today));
7335
7304
  const nowHour = today.getHours();
7336
7305
  const dayData = days.map((day) => {
@@ -7375,10 +7344,10 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
7375
7344
  } else if (mode === "cont") {
7376
7345
  sub = `\u2191 cont. through ${endLabel}`;
7377
7346
  }
7378
- return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Link, { overlay, onClick: handleClick }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, e.title || "--")), sub ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy" }, sub) : null);
7347
+ const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
7348
+ return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Link, { overlay, onClick: handleClick }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.StatusTag, { variant: STATUS_VARIANT[variant] || "default" }, titleLabel)), sub ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Text, { variant: "microcopy" }, sub) : null);
7379
7349
  };
7380
- const daySpacer = makeSpacerDataUri(1, dayColWidth);
7381
- const dayCell = (key, content) => /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableCell, { key, width: centerDays ? "max" : "min", align: "left" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, content, centerDays ? null : /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Image, { src: daySpacer.src, width: daySpacer.width, height: daySpacer.height, alt: "" })));
7350
+ const dayCell = (key, content) => /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableCell, { key, width: centerDays ? "max" : "min", align: "left" }, centerDays ? /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, content) : /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.AutoGrid, { columnWidth: dayColWidth, gap: "flush" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Flex, { direction: "column", gap: "xs" }, content)));
7382
7351
  const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
7383
7352
  const emptyCell = null;
7384
7353
  return /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.Table, { bordered: true, flush: true, density: "compact" }, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableHead, null, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableRow, null, /* @__PURE__ */ import_react15.default.createElement(import_ui_extensions15.TableHeader, { width: "min" }, "TIME"), dayData.map(({ day }) => {
@@ -7434,6 +7403,10 @@ var Calendar = (props) => {
7434
7403
  weekStartsOn = 0,
7435
7404
  hideWeekends = false,
7436
7405
  maxEventsPerDay = DEFAULT_MAX_EVENTS_PER_DAY,
7406
+ // month-grid event token style: "statusTag" (dot + text, default) | "tag" (pill)
7407
+ monthEventStyle = "statusTag",
7408
+ // max characters for a month-cell label before "…" (default derived per style)
7409
+ monthEventMaxChars,
7437
7410
  // time grid (week / day)
7438
7411
  dayStartHour = DEFAULT_DAY_START_HOUR,
7439
7412
  dayEndHour = DEFAULT_DAY_END_HOUR,
@@ -7653,7 +7626,15 @@ var Calendar = (props) => {
7653
7626
  }
7654
7627
  return formatMonthTitle(focusedDate);
7655
7628
  }, [view, focusedDate, weekStartsOn, hideWeekends]);
7656
- const chipProps = { overlayMode, renderEventDetail, onEventClick, labels };
7629
+ const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
7630
+ const chipProps = {
7631
+ overlayMode,
7632
+ renderEventDetail,
7633
+ onEventClick,
7634
+ labels,
7635
+ monthEventStyle: safeMonthEventStyle,
7636
+ monthEventMaxChars
7637
+ };
7657
7638
  const timeZoneOptions = (0, import_react15.useMemo)(() => {
7658
7639
  if (!showTimeZoneSelect) return null;
7659
7640
  const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
package/dist/index.mjs CHANGED
@@ -6778,6 +6778,7 @@ var Feed = ({
6778
6778
  import React14, { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo5, useState as useState6 } from "react";
6779
6779
  import {
6780
6780
  Alert as Alert4,
6781
+ AutoGrid as AutoGrid3,
6781
6782
  Box as Box6,
6782
6783
  Button as Button8,
6783
6784
  Divider as Divider4,
@@ -7009,21 +7010,6 @@ var formatTimeZoneLabel = (tz, atDate = /* @__PURE__ */ new Date()) => {
7009
7010
 
7010
7011
  // src/calendar/svgChips.js
7011
7012
  var toDataUri = (svg) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
7012
- var escapeXml = (s) => String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7013
- var truncateLabel = (value, max) => {
7014
- const s = String(value == null ? "" : value);
7015
- if (!max || s.length <= max) return s;
7016
- return s.slice(0, Math.max(1, max - 1)).trimEnd() + "\u2026";
7017
- };
7018
- var CHIP_PALETTE = {
7019
- default: { fill: "#7FD1DE", text: HS_TEXT_COLOR },
7020
- info: { fill: "#00A4BD", text: "#FFFFFF" },
7021
- success: { fill: "#00BDA5", text: "#FFFFFF" },
7022
- warning: { fill: "#F5C26B", text: HS_TEXT_COLOR },
7023
- error: { fill: "#F2545B", text: "#FFFFFF" },
7024
- danger: { fill: "#F2545B", text: "#FFFFFF" }
7025
- // StatusTag spells red "danger"; accept both
7026
- };
7027
7013
  var DOT_FILL = {
7028
7014
  default: "#7C98B6",
7029
7015
  info: "#00A4BD",
@@ -7038,23 +7024,6 @@ var makeDotDataUri = (variant = "default", size = 8) => {
7038
7024
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}"><circle cx="${r}" cy="${r}" r="${r}" fill="${fill}" /></svg>`;
7039
7025
  return { src: toDataUri(svg), width: size, height: size };
7040
7026
  };
7041
- var makeEventChipDataUri = (opts) => {
7042
- const { label, width, height = 24, variant = "default" } = opts;
7043
- const palette = CHIP_PALETTE[variant] || CHIP_PALETTE.default;
7044
- const accentX = 5;
7045
- const accentW = 3;
7046
- const textX = accentX + accentW + 6;
7047
- const rightPad = 8;
7048
- const maxChars = Math.max(1, Math.floor((width - textX - rightPad) / 6));
7049
- const text = truncateLabel(label, maxChars);
7050
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect x="0.5" y="0.5" width="${width - 1}" height="${height - 1}" rx="4" ry="4" fill="#FFFFFF" stroke="#CBD6E2" stroke-width="1" /><rect x="${accentX}" y="5" width="${accentW}" height="${height - 10}" rx="1.5" ry="1.5" fill="${palette.fill}" /><text x="${textX}" y="${height / 2}" font-family='${HS_FONT_FAMILY}' font-size="12" font-weight="500" fill="${HS_TEXT_COLOR}" text-anchor="start" dominant-baseline="central">${escapeXml(text)}</text></svg>`;
7051
- return { src: toDataUri(svg), width, height };
7052
- };
7053
- var makeMoreDataUri = (opts) => {
7054
- const { label, width, height = 24 } = opts;
7055
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><text x="3" y="${height / 2}" font-family='${HS_FONT_FAMILY}' font-size="13" font-weight="700" fill="#0091AE" text-anchor="start" dominant-baseline="central">${escapeXml(label)}</text></svg>`;
7056
- return { src: toDataUri(svg), width, height };
7057
- };
7058
7027
  var makeSpacerDataUri = (height = 24, width = 4) => {
7059
7028
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect x="0" y="0" width="${width}" height="${height}" fill="#FFFFFF" fill-opacity="0" /></svg>`;
7060
7029
  return { src: toDataUri(svg), width, height };
@@ -7150,6 +7119,24 @@ var STATUS_VARIANT = {
7150
7119
  error: "danger",
7151
7120
  danger: "danger"
7152
7121
  };
7122
+ var TAG_VARIANT = {
7123
+ default: "default",
7124
+ info: "info",
7125
+ success: "success",
7126
+ warning: "warning",
7127
+ error: "error",
7128
+ danger: "error"
7129
+ };
7130
+ var MONTH_EVENT_STYLES = /* @__PURE__ */ new Set(["statusTag", "tag"]);
7131
+ var monthLabelMaxChars = (style) => {
7132
+ const overhead = style === "tag" ? 34 : 46;
7133
+ return Math.max(6, Math.floor((MONTH_COL_WIDTH - overhead) / 6.6));
7134
+ };
7135
+ var truncateMonthLabel = (value, max) => {
7136
+ const s = String(value == null ? "" : value);
7137
+ if (!max || s.length <= max) return s;
7138
+ return s.slice(0, max).trimEnd();
7139
+ };
7153
7140
  var MONTH_COL_WIDTH = 160;
7154
7141
  var TIMEGRID_DAY_COL = 150;
7155
7142
  var TIMEGRID_DAY_COL_SINGLE = 560;
@@ -7168,7 +7155,7 @@ var EventDetail = ({ event, labels }) => {
7168
7155
  when = `${formatDayTitle(start)} \u2013 ${formatDayTitle(end)}`;
7169
7156
  }
7170
7157
  }
7171
- return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, title || "--"), when ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, when) : null, subtitle ? /* @__PURE__ */ React14.createElement(Text7, null, subtitle) : null, href ? /* @__PURE__ */ React14.createElement(Link6, { href: href.url, external: href.external }, labels.open) : null);
7158
+ return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" }, truncate: true }, title || "--"), when ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", truncate: true }, when) : null, subtitle ? /* @__PURE__ */ React14.createElement(Text7, { truncate: true }, subtitle) : null, href ? /* @__PURE__ */ React14.createElement(Link6, { href: href.url, external: href.external }, labels.open) : null);
7172
7159
  };
7173
7160
  var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
7174
7161
  if (mode === "none") return void 0;
@@ -7180,7 +7167,7 @@ var buildOverlay = (event, mode, renderEventDetail, labels, idSuffix = "") => {
7180
7167
  if (mode === "panel") {
7181
7168
  return /* @__PURE__ */ React14.createElement(Panel, { id, title: event.title || labels.open, width: "small", variant: "modal" }, /* @__PURE__ */ React14.createElement(PanelBody, null, body));
7182
7169
  }
7183
- return /* @__PURE__ */ React14.createElement(Popover, { id, placement: "bottom" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, body));
7170
+ return /* @__PURE__ */ React14.createElement(Popover, { id, placement: "bottom", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, body));
7184
7171
  };
7185
7172
  var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
7186
7173
  const variant = VALID_VARIANTS.has(event.color) ? event.color : "default";
@@ -7189,9 +7176,8 @@ var AgendaEventRow = ({ event, day, overlayMode, renderEventDetail, onEventClick
7189
7176
  const timeLabel = isAllDayEvent(event) ? labels.allDay : formatTime(event.start);
7190
7177
  return /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ React14.createElement(Box6, { flex: 2 }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center" }, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: "demibold" } }, timeLabel))), /* @__PURE__ */ React14.createElement(Box6, { flex: 11 }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React14.createElement(ColorDot, { variant }), /* @__PURE__ */ React14.createElement(Text7, { truncate: true }, /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, event.title || "--")))), event.subtitle ? /* @__PURE__ */ React14.createElement(Box6, { flex: 4 }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center" }, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", truncate: true }, event.subtitle))) : null);
7191
7178
  };
7192
- var MONTH_CHIP_WIDTH = MONTH_COL_WIDTH - 8;
7193
- var MONTH_CHIP_HEIGHT = 24;
7194
- var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
7179
+ var MONTH_SLOT_HEIGHT = 24;
7180
+ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels, monthEventStyle, monthEventMaxChars }) => {
7195
7181
  const isStartDay = !day || !event.start || isSameDay(event.start, day);
7196
7182
  const overlay = buildOverlay(event, overlayMode, renderEventDetail, labels, day ? `-m${day.getTime()}` : "");
7197
7183
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -7199,23 +7185,12 @@ var MonthChip = ({ event, day, overlayMode, renderEventDetail, onEventClick, lab
7199
7185
  const startHasTime = event.start && (event.start.getHours() !== 0 || event.start.getMinutes() !== 0);
7200
7186
  const time = isStartDay && startHasTime ? `${formatTime(event.start)} ` : "";
7201
7187
  const prefix = isStartDay ? "" : "\u2192 ";
7202
- const chip = makeEventChipDataUri({
7203
- label: `${prefix}${time}${event.title || "--"}`,
7204
- width: MONTH_CHIP_WIDTH,
7205
- height: MONTH_CHIP_HEIGHT,
7206
- variant
7207
- });
7208
- return /* @__PURE__ */ React14.createElement(
7209
- Image5,
7210
- {
7211
- src: chip.src,
7212
- width: chip.width,
7213
- height: chip.height,
7214
- alt: event.title || "",
7215
- overlay,
7216
- onClick: handleClick
7217
- }
7218
- );
7188
+ const maxChars = monthEventMaxChars != null ? monthEventMaxChars : monthLabelMaxChars(monthEventStyle);
7189
+ const label = truncateMonthLabel(`${prefix}${time}${event.title || "--"}`, maxChars);
7190
+ if (monthEventStyle === "tag") {
7191
+ return /* @__PURE__ */ React14.createElement(Tag5, { variant: TAG_VARIANT[variant] || "default", overlay, onClick: handleClick }, label);
7192
+ }
7193
+ return /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(StatusTag2, { variant: STATUS_VARIANT[variant] || "default" }, label));
7219
7194
  };
7220
7195
  var DayListItem = ({ event, day, overlayMode, renderEventDetail, onEventClick, labels }) => {
7221
7196
  const handleClick = onEventClick ? () => onEventClick(event.raw, event) : void 0;
@@ -7298,7 +7273,7 @@ var MonthView = ({
7298
7273
  }) => {
7299
7274
  const headers = weekdayLabels(weekStartsOn, hideWeekends, true);
7300
7275
  const today = now || /* @__PURE__ */ new Date();
7301
- const spacer24 = makeSpacerDataUri(MONTH_CHIP_HEIGHT, MONTH_COL_WIDTH);
7276
+ const slotSpacer = makeSpacerDataUri(MONTH_SLOT_HEIGHT, 1);
7302
7277
  const renderCell = (day) => {
7303
7278
  const dayEvents = eventsForDay(day);
7304
7279
  const inMonth = isSameMonth(day, refDate);
@@ -7306,41 +7281,35 @@ var MonthView = ({
7306
7281
  if (renderDayCell) return renderDayCell(day, dayEvents);
7307
7282
  const shown = dayEvents.slice(0, maxEventsPerDay);
7308
7283
  const hasOverflow = dayEvents.length > maxEventsPerDay;
7284
+ const heightSpacer = /* @__PURE__ */ React14.createElement(Image5, { src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" });
7285
+ const slotRow = (key, content) => /* @__PURE__ */ React14.createElement(Flex9, { key, direction: "row", align: "center", gap: "flush" }, heightSpacer, content);
7309
7286
  const slots = [];
7310
7287
  for (let i = 0; i < maxEventsPerDay; i++) {
7311
7288
  if (i < shown.length) {
7312
- slots.push(/* @__PURE__ */ React14.createElement(MonthChip, { key: shown[i].key, event: shown[i], day, ...chipProps }));
7289
+ slots.push(slotRow(shown[i].key, /* @__PURE__ */ React14.createElement(MonthChip, { event: shown[i], day, ...chipProps })));
7313
7290
  } else {
7314
- slots.push(
7315
- /* @__PURE__ */ React14.createElement(Image5, { key: `sp-${i}`, src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
7316
- );
7291
+ slots.push(/* @__PURE__ */ React14.createElement(Image5, { key: `sp-${i}`, src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" }));
7317
7292
  }
7318
7293
  }
7319
7294
  if (hasOverflow) {
7320
- const more = makeMoreDataUri({
7321
- label: labels.more(dayEvents.length - maxEventsPerDay),
7322
- width: MONTH_COL_WIDTH,
7323
- height: MONTH_CHIP_HEIGHT
7324
- });
7325
7295
  slots.push(
7326
- /* @__PURE__ */ React14.createElement(
7327
- Image5,
7328
- {
7329
- key: "more",
7330
- src: more.src,
7331
- width: more.width,
7332
- height: more.height,
7333
- alt: labels.more(dayEvents.length - maxEventsPerDay),
7334
- overlay: /* @__PURE__ */ React14.createElement(Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "sm" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "sm" }, /* @__PURE__ */ React14.createElement(Heading, null, String(dayEvents.length)), /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ React14.createElement(Divider4, null), dayEvents.map((event, i) => /* @__PURE__ */ React14.createElement(React14.Fragment, { key: event.key }, /* @__PURE__ */ React14.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ React14.createElement(Divider4, null) : null)))))
7335
- }
7296
+ slotRow(
7297
+ "more",
7298
+ /* @__PURE__ */ React14.createElement(
7299
+ Link6,
7300
+ {
7301
+ overlay: /* @__PURE__ */ React14.createElement(Popover, { id: `cal-day-${day.getTime()}`, placement: "top", variant: "longform" }, /* @__PURE__ */ React14.createElement(Tile5, { compact: true }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "sm" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", justify: "center", align: "center", gap: "xs" }, /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "bold" } }, String(dayEvents.length)), /* @__PURE__ */ React14.createElement(Text7, { format: { fontWeight: "demibold" } }, labels.onThisDate)), /* @__PURE__ */ React14.createElement(Divider4, null), dayEvents.map((event, i) => /* @__PURE__ */ React14.createElement(React14.Fragment, { key: event.key }, /* @__PURE__ */ React14.createElement(DayListItem, { event, day, ...chipProps }), i < dayEvents.length - 1 ? /* @__PURE__ */ React14.createElement(Divider4, null) : null)))))
7302
+ },
7303
+ labels.more(dayEvents.length - maxEventsPerDay)
7304
+ )
7336
7305
  )
7337
7306
  );
7338
7307
  } else {
7339
7308
  slots.push(
7340
- /* @__PURE__ */ React14.createElement(Image5, { key: "more-sp", src: spacer24.src, width: spacer24.width, height: spacer24.height, alt: "" })
7309
+ /* @__PURE__ */ React14.createElement(Image5, { key: "more-sp", src: slotSpacer.src, width: slotSpacer.width, height: slotSpacer.height, alt: "" })
7341
7310
  );
7342
7311
  }
7343
- return /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ React14.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots);
7312
+ return /* @__PURE__ */ React14.createElement(AutoGrid3, { columnWidth: MONTH_COL_WIDTH, gap: "flush" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "row", align: "center", gap: "xs" }, isToday ? /* @__PURE__ */ React14.createElement(ColorDot, { variant: "info" }) : null, /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy", format: { fontWeight: inMonth ? "demibold" : "regular" } }, String(day.getDate()))), slots));
7344
7313
  };
7345
7314
  return /* @__PURE__ */ React14.createElement(Table2, { bordered: true, flush: true }, /* @__PURE__ */ React14.createElement(TableHead2, null, /* @__PURE__ */ React14.createElement(TableRow2, null, headers.map((h6) => /* @__PURE__ */ React14.createElement(TableHeader2, { key: h6, width: "min", align: "center" }, h6.toUpperCase())))), /* @__PURE__ */ React14.createElement(TableBody2, null, weeks.map((week, wi) => {
7346
7315
  const days = hideWeekends ? week.filter((d) => d.getDay() !== 0 && d.getDay() !== 6) : week;
@@ -7383,6 +7352,7 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
7383
7352
  const today = now || /* @__PURE__ */ new Date();
7384
7353
  const centerDays = days.length === 1;
7385
7354
  const dayColWidth = days.length === 1 ? TIMEGRID_DAY_COL_SINGLE : TIMEGRID_DAY_COL;
7355
+ const weekTitleMaxChars = Math.max(6, Math.floor((TIMEGRID_DAY_COL - 46) / 6.6));
7386
7356
  const todayInView = days.some((d) => isSameDay(d, today));
7387
7357
  const nowHour = today.getHours();
7388
7358
  const dayData = days.map((day) => {
@@ -7427,10 +7397,10 @@ var TimeGridView = ({ days, now, hours, dayStartHour, dayEndHour, eventsForDay,
7427
7397
  } else if (mode === "cont") {
7428
7398
  sub = `\u2191 cont. through ${endLabel}`;
7429
7399
  }
7430
- return /* @__PURE__ */ React14.createElement(Flex9, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(StatusTag2, { variant: STATUS_VARIANT[variant] || "default" }, e.title || "--")), sub ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, sub) : null);
7400
+ const titleLabel = centerDays ? e.title || "--" : truncateMonthLabel(e.title || "--", weekTitleMaxChars);
7401
+ return /* @__PURE__ */ React14.createElement(Flex9, { key: `${e.key}-${mode}-${hour}`, direction: "column", gap: "flush" }, /* @__PURE__ */ React14.createElement(Link6, { overlay, onClick: handleClick }, /* @__PURE__ */ React14.createElement(StatusTag2, { variant: STATUS_VARIANT[variant] || "default" }, titleLabel)), sub ? /* @__PURE__ */ React14.createElement(Text7, { variant: "microcopy" }, sub) : null);
7431
7402
  };
7432
- const daySpacer = makeSpacerDataUri(1, dayColWidth);
7433
- const dayCell = (key, content) => /* @__PURE__ */ React14.createElement(TableCell2, { key, width: centerDays ? "max" : "min", align: "left" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, content, centerDays ? null : /* @__PURE__ */ React14.createElement(Image5, { src: daySpacer.src, width: daySpacer.width, height: daySpacer.height, alt: "" })));
7403
+ const dayCell = (key, content) => /* @__PURE__ */ React14.createElement(TableCell2, { key, width: centerDays ? "max" : "min", align: "left" }, centerDays ? /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, content) : /* @__PURE__ */ React14.createElement(AutoGrid3, { columnWidth: dayColWidth, gap: "flush" }, /* @__PURE__ */ React14.createElement(Flex9, { direction: "column", gap: "xs" }, content)));
7434
7404
  const slotSpacer = makeSpacerDataUri(HOUR_SLOT_HEIGHT, 1);
7435
7405
  const emptyCell = null;
7436
7406
  return /* @__PURE__ */ React14.createElement(Table2, { bordered: true, flush: true, density: "compact" }, /* @__PURE__ */ React14.createElement(TableHead2, null, /* @__PURE__ */ React14.createElement(TableRow2, null, /* @__PURE__ */ React14.createElement(TableHeader2, { width: "min" }, "TIME"), dayData.map(({ day }) => {
@@ -7486,6 +7456,10 @@ var Calendar = (props) => {
7486
7456
  weekStartsOn = 0,
7487
7457
  hideWeekends = false,
7488
7458
  maxEventsPerDay = DEFAULT_MAX_EVENTS_PER_DAY,
7459
+ // month-grid event token style: "statusTag" (dot + text, default) | "tag" (pill)
7460
+ monthEventStyle = "statusTag",
7461
+ // max characters for a month-cell label before "…" (default derived per style)
7462
+ monthEventMaxChars,
7489
7463
  // time grid (week / day)
7490
7464
  dayStartHour = DEFAULT_DAY_START_HOUR,
7491
7465
  dayEndHour = DEFAULT_DAY_END_HOUR,
@@ -7705,7 +7679,15 @@ var Calendar = (props) => {
7705
7679
  }
7706
7680
  return formatMonthTitle(focusedDate);
7707
7681
  }, [view, focusedDate, weekStartsOn, hideWeekends]);
7708
- const chipProps = { overlayMode, renderEventDetail, onEventClick, labels };
7682
+ const safeMonthEventStyle = MONTH_EVENT_STYLES.has(monthEventStyle) ? monthEventStyle : "statusTag";
7683
+ const chipProps = {
7684
+ overlayMode,
7685
+ renderEventDetail,
7686
+ onEventClick,
7687
+ labels,
7688
+ monthEventStyle: safeMonthEventStyle,
7689
+ monthEventMaxChars
7690
+ };
7709
7691
  const timeZoneOptions = useMemo5(() => {
7710
7692
  if (!showTimeZoneSelect) return null;
7711
7693
  const base = (timeZoneOptionsProp && timeZoneOptionsProp.length ? timeZoneOptionsProp : DEFAULT_TIME_ZONES).map(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hs-uix",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Production-ready UI components for HubSpot UI Extensions — DataTable, FormBuilder, and more",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -230,6 +230,8 @@ How it works: each event instant is converted to a "wall-clock" `Date` in the ac
230
230
  | `weekStartsOn` | `0 \| 1` | `0` | 0 = Sunday, 1 = Monday. |
231
231
  | `hideWeekends` | `boolean` | `false` | Hide Sat/Sun in month + week views. |
232
232
  | `maxEventsPerDay` | `number` | `3` | Chips per month cell before "+N more". |
233
+ | `monthEventStyle` | `"statusTag" \| "tag"` | `"statusTag"` | Month-cell event token: `statusTag` (colored dot + text, matches week/day) or `tag` (bordered pill). Both truncate and hold a fixed height as columns narrow. |
234
+ | `monthEventMaxChars` | `number` | per-style, from column width | Max characters for a month-cell label before "…". Labels are truncated up front so every token truncates consistently (the tag's native truncation measures unreliably while the table is still sizing columns). |
233
235
  | `dayStartHour` | `number` | `8` | First hour row in week/day (0–23). |
234
236
  | `dayEndHour` | `number` | `20` | Last hour row in week/day (0–23). |
235
237
  | `timeZone` | `string` | — | Controlled IANA zone (e.g. `"America/Chicago"`). Setting it opts into the tz layer; all times/placement/grouping resolve here. |
@@ -125,6 +125,19 @@ export interface CalendarProps<Event = Record<string, unknown>> {
125
125
  hideWeekends?: boolean;
126
126
  /** Max chips per day cell in month view before collapsing to "N more". */
127
127
  maxEventsPerDay?: number;
128
+ /**
129
+ * Month-cell event token style. `"statusTag"` (default) renders a colored dot +
130
+ * text matching the week/day grid; `"tag"` renders a bordered pill. Both
131
+ * truncate and hold a fixed height as columns narrow.
132
+ */
133
+ monthEventStyle?: "statusTag" | "tag";
134
+ /**
135
+ * Max characters for a month-cell event label before it's truncated with "…".
136
+ * Defaults to a per-style budget derived from the column width. Labels are
137
+ * truncated up front (not just via the tag's native TruncateString) so every
138
+ * token truncates consistently regardless of the table's column-sizing timing.
139
+ */
140
+ monthEventMaxChars?: number;
128
141
 
129
142
  // Time grid (week / day views)
130
143
  /** First hour row in week/day view (0–23). Default 8. */