@super-calendar/dom 2.3.2 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { LegendList } from "@legendapp/list/react";
2
- import { addDays, addMinutes, addMonths, endOfMonth, format, isSameDay, isSameMonth, startOfDay, startOfMonth } from "date-fns";
2
+ import { addDays, addMinutes, addMonths, endOfDay, endOfMonth, endOfWeek, format, getISOWeek, isSameDay, isSameMonth, startOfDay, startOfMonth, startOfWeek } from "date-fns";
3
3
  import { useEffect, useId, useMemo, useRef, useState } from "react";
4
- import { bandRounding, buildMonthGrid, buildMonthGrid as buildMonthGrid$1, cellRangeFromDrag, closedHourBands, compareDayEvents, darkColors, dayBadgeKind, daySelectionState, eventAccessibilityLabel, eventChipLayout, eventTimeLabel, expandRecurringEvents, formatHour, getIsToday, getViewDays, getViewDays as getViewDays$1, groupEventsByDay, isAllDayEvent, isAllDayEvent as isAllDayEvent$1, isDateSelectable, isRangeEndpoint, isSameCalendarDay, isWeekend, isWithinDateRange, layoutDayEvents, layoutDayEvents as layoutDayEvents$1, lightColors, monthVisibleCount, nextDateRange, nextDateRange as nextDateRange$1, parseICalendar, rangeBandKind, titleNumberOfLines, toICalendar, useDateRange, useMonthGrid, weekdayFormatToken, weekdayFormatToken as weekdayFormatToken$1 } from "@super-calendar/core";
5
- import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { bandRounding, buildMonthGrid, buildMonthGrid as buildMonthGrid$1, cellRangeFromDrag, closedHourBands, compareDayEvents, darkColors, dayBadgeKind, daySelectionState, eventAccessibilityLabel, eventChipLayout, eventTimeLabel, eventsInTimeZone, expandRecurringEvents, expandRecurringEvents as expandRecurringEvents$1, formatHour, getIsToday, getViewDays, getViewDays as getViewDays$1, groupEventsByDay, isAllDayEvent, isAllDayEvent as isAllDayEvent$1, isDateSelectable, isRangeEndpoint, isSameCalendarDay, isWeekend, isWithinDateRange, layoutDayEvents, layoutDayEvents as layoutDayEvents$1, lightColors, monthVisibleCount, nextDateRange, nextDateRange as nextDateRange$1, parseICalendar, rangeBandKind, titleNumberOfLines, toICalendar, useDateRange, useMonthGrid, weekdayFormatToken, weekdayFormatToken as weekdayFormatToken$1 } from "@super-calendar/core";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/slots.ts
7
7
  /** Join truthy class names (a dependency-free clsx-lite). */
8
8
  function cn(...parts) {
@@ -727,9 +727,20 @@ function MonthView({ date, weekStartsOn = 0, weekdayFormat = "short", events, ev
727
727
  }
728
728
  //#endregion
729
729
  //#region src/TimeGrid.tsx
730
- const HOURS = Array.from({ length: 24 }, (_, h) => h);
731
- const GUTTER_WIDTH = 56;
730
+ const GUTTER_WIDTH$1 = 56;
732
731
  const clamp$1 = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
732
+ const VISUALLY_HIDDEN = {
733
+ position: "absolute",
734
+ width: 1,
735
+ height: 1,
736
+ padding: 0,
737
+ margin: -1,
738
+ overflow: "hidden",
739
+ clip: "rect(0 0 0 0)",
740
+ clipPath: "inset(50%)",
741
+ whiteSpace: "nowrap",
742
+ border: 0
743
+ };
733
744
  const DOM_TITLE_LINE_HEIGHT = 16;
734
745
  const DOM_TIME_LINE_HEIGHT = 30;
735
746
  const DOM_BOX_PADDING_V = 2;
@@ -800,6 +811,17 @@ function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme
800
811
  }) : null]
801
812
  });
802
813
  }
814
+ const NOW_TICK_MS = 6e4;
815
+ function useNow(enabled) {
816
+ const [now, setNow] = useState(() => /* @__PURE__ */ new Date());
817
+ useEffect(() => {
818
+ if (!enabled) return;
819
+ setNow(/* @__PURE__ */ new Date());
820
+ const id = setInterval(() => setNow(/* @__PURE__ */ new Date()), NOW_TICK_MS);
821
+ return () => clearInterval(id);
822
+ }, [enabled]);
823
+ return now;
824
+ }
803
825
  /**
804
826
  * A day / week / N-day time grid rendered with plain DOM elements. Events are
805
827
  * positioned with the library's pure `layoutDayEvents`, so overlap columns and
@@ -811,7 +833,7 @@ function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme
811
833
  * <TimeGrid mode="week" date={new Date()} events={events} />
812
834
  * ```
813
835
  */
814
- function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStartsOn = 0, weekdayFormat = "short", hourHeight: initialHourHeight = 48, scrollOffsetMinutes = 480, zoomable = true, minHourHeight = 24, maxHourHeight = 160, dragStepMinutes = 15, ampm = false, timeslots = 1, businessHours, showNowIndicator = true, showAllDayEventCell = true, locale, theme: themeOverrides, height = 600, renderEvent, eventAccessibilityLabel: eventAccessibilityLabel$1, hourComponent, onPressEvent, onPressDateHeader, onPressCell, onCreateEvent, onDragStart, onDragEvent, className, style, classNames, styles }) {
836
+ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStartsOn = 0, weekdayFormat = "short", hourHeight: initialHourHeight = 48, scrollOffsetMinutes = 480, zoomable = true, minHourHeight = 24, maxHourHeight = 160, dragStepMinutes = 15, ampm = false, timeslots = 1, minHour = 0, maxHour = 24, hideHours = false, showWeekNumber = false, weekNumberPrefix = "W", keyboardEventNavigation = false, businessHours, showNowIndicator = true, showAllDayEventCell = true, locale, theme: themeOverrides, height = 600, renderEvent, eventAccessibilityLabel: eventAccessibilityLabel$1, hourComponent, onPressEvent, onPressDateHeader, onPressCell, onCreateEvent, onDragStart, onDragEvent, className, style, classNames, styles }) {
815
837
  const theme = useMemo(() => mergeDomTheme(themeOverrides), [themeOverrides]);
816
838
  const slot = createSlots({
817
839
  classNames,
@@ -820,6 +842,12 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
820
842
  const scrollRef = useRef(null);
821
843
  const dfns = locale ? { locale } : void 0;
822
844
  const snapHours = dragStepMinutes / 60;
845
+ const windowStart = Math.max(0, Math.min(minHour, 23));
846
+ const windowEnd = Math.max(windowStart + 1, Math.min(maxHour, 24));
847
+ const windowHours = windowEnd - windowStart;
848
+ const gutterWidth = hideHours ? 0 : GUTTER_WIDTH$1;
849
+ const visibleHours = useMemo(() => Array.from({ length: windowHours }, (_, i) => windowStart + i), [windowStart, windowHours]);
850
+ const now = useNow(showNowIndicator);
823
851
  const [hourHeight, setHourHeight] = useState(initialHourHeight);
824
852
  useEffect(() => setHourHeight(initialHourHeight), [initialHourHeight]);
825
853
  const hourHeightRef = useRef(hourHeight);
@@ -847,8 +875,8 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
847
875
  }), [days, events]);
848
876
  const hasAllDay = allDayByDay.some((list) => list.length > 0);
849
877
  useEffect(() => {
850
- if (scrollRef.current) scrollRef.current.scrollTop = scrollOffsetMinutes / 60 * hourHeightRef.current;
851
- }, [scrollOffsetMinutes]);
878
+ if (scrollRef.current) scrollRef.current.scrollTop = Math.max(0, (scrollOffsetMinutes / 60 - windowStart) * hourHeightRef.current);
879
+ }, [scrollOffsetMinutes, windowStart]);
852
880
  useEffect(() => {
853
881
  const el = scrollRef.current;
854
882
  if (!el || !zoomable) return;
@@ -891,23 +919,30 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
891
919
  if (pinch.current.size < 2) pinchBase.current = null;
892
920
  };
893
921
  const Renderer = renderEvent;
894
- const totalHeight = 24 * hourHeight;
895
- const beginDrag = (e, event, key, kind, startHours, durationHours) => {
922
+ const totalHeight = windowHours * hourHeight;
923
+ const beginDrag = (e, event, key, kind, startHours, durationHours, dayIndex) => {
896
924
  if (!onDragEvent) return;
897
925
  e.stopPropagation();
898
926
  try {
899
927
  e.target.setPointerCapture?.(e.pointerId);
900
928
  } catch {}
929
+ const column = kind === "move" ? e.currentTarget.parentElement : null;
930
+ const dayWidth = column ? column.getBoundingClientRect().width : 0;
901
931
  dragOrigin.current = {
932
+ pointerX: e.clientX,
902
933
  pointerY: e.clientY,
903
934
  startHours,
904
- durationHours
935
+ durationHours,
936
+ dayIndex,
937
+ dayWidth
905
938
  };
906
939
  applyDrag({
907
940
  key,
908
941
  kind,
909
942
  startHours,
910
943
  durationHours,
944
+ dayDelta: 0,
945
+ dayOffsetPx: 0,
911
946
  moved: false
912
947
  });
913
948
  onDragStart?.(event);
@@ -922,14 +957,19 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
922
957
  const dHours = (e.clientY - dragOrigin.current.pointerY) / hourHeightRef.current;
923
958
  const snap = (v) => Math.round(v / snapHours) * snapHours;
924
959
  if (d.kind === "move") {
925
- const startHours = clamp$1(snap(dragOrigin.current.startHours + dHours), 0, 24 - d.durationHours);
960
+ const startHours = clamp$1(snap(dragOrigin.current.startHours + dHours), windowStart, Math.max(windowStart, windowEnd - d.durationHours));
961
+ const o = dragOrigin.current;
962
+ const rawDayDelta = o.dayWidth > 0 ? Math.round((e.clientX - o.pointerX) / o.dayWidth) : 0;
963
+ const dayDelta = clamp$1(o.dayIndex + rawDayDelta, 0, days.length - 1) - o.dayIndex;
926
964
  applyDrag({
927
965
  ...d,
928
966
  startHours,
967
+ dayDelta,
968
+ dayOffsetPx: dayDelta * o.dayWidth,
929
969
  moved: true
930
970
  });
931
971
  } else {
932
- const durationHours = clamp$1(snap(dragOrigin.current.durationHours + dHours), snapHours, 24 - d.startHours);
972
+ const durationHours = clamp$1(snap(dragOrigin.current.durationHours + dHours), snapHours, Math.max(snapHours, windowEnd - d.startHours));
933
973
  applyDrag({
934
974
  ...d,
935
975
  durationHours,
@@ -948,7 +988,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
948
988
  onPress();
949
989
  return;
950
990
  }
951
- const base = startOfDay(day);
991
+ const base = startOfDay(addDays(day, d.dayDelta));
952
992
  const start = addMinutes(base, Math.round(d.startHours * 60));
953
993
  const end = addMinutes(base, Math.round((d.startHours + d.durationHours) * 60));
954
994
  onDragEvent?.(event, start, end);
@@ -996,10 +1036,10 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
996
1036
  const moved = Math.abs(endPx - o.startPx) > 4;
997
1037
  const h = hourHeightRef.current;
998
1038
  if (moved && onCreateEvent) {
999
- const range = cellRangeFromDrag(day, o.startPx, endPx, h, 0, dragStepMinutes);
1039
+ const range = cellRangeFromDrag(day, o.startPx, endPx, h, windowStart, dragStepMinutes);
1000
1040
  if (range) onCreateEvent(range.start, range.end);
1001
1041
  } else if (onPressCell) {
1002
- const at = cellRangeFromDrag(day, o.startPx, o.startPx, h, 0, dragStepMinutes);
1042
+ const at = cellRangeFromDrag(day, o.startPx, o.startPx, h, windowStart, dragStepMinutes);
1003
1043
  if (at) onPressCell(at.start);
1004
1044
  }
1005
1045
  createOrigin.current = null;
@@ -1010,7 +1050,72 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1010
1050
  setCreateBox(null);
1011
1051
  };
1012
1052
  const positionedByDay = useMemo(() => days.map((day) => layoutDayEvents$1(events, day)), [days, events]);
1013
- const bandsByDay = useMemo(() => days.map((day) => closedHourBands(day, businessHours)), [days, businessHours]);
1053
+ const navByDay = useMemo(() => positionedByDay.map((list, day) => list.map((pe, idx) => ({
1054
+ key: `${day}:${idx}`,
1055
+ start: pe.startHours,
1056
+ dur: pe.durationHours
1057
+ })).filter((n) => !(n.start >= windowEnd || n.start + n.dur <= windowStart)).sort((a, b) => a.start - b.start)), [
1058
+ positionedByDay,
1059
+ windowStart,
1060
+ windowEnd
1061
+ ]);
1062
+ const nextEventKey = (currentKey, arrowKey) => {
1063
+ let day = -1;
1064
+ let pos = -1;
1065
+ for (let d = 0; d < navByDay.length; d++) {
1066
+ const p = navByDay[d].findIndex((n) => n.key === currentKey);
1067
+ if (p !== -1) {
1068
+ day = d;
1069
+ pos = p;
1070
+ break;
1071
+ }
1072
+ }
1073
+ if (day === -1) return null;
1074
+ const start = navByDay[day][pos].start;
1075
+ const nearestInDay = (d) => {
1076
+ let best = null;
1077
+ let bestDiff = Number.POSITIVE_INFINITY;
1078
+ for (const n of navByDay[d]) {
1079
+ const diff = Math.abs(n.start - start);
1080
+ if (diff < bestDiff) {
1081
+ bestDiff = diff;
1082
+ best = n.key;
1083
+ }
1084
+ }
1085
+ return best;
1086
+ };
1087
+ switch (arrowKey) {
1088
+ case "ArrowDown": return navByDay[day][Math.min(pos + 1, navByDay[day].length - 1)].key;
1089
+ case "ArrowUp": return navByDay[day][Math.max(pos - 1, 0)].key;
1090
+ case "ArrowRight":
1091
+ for (let d = day + 1; d < navByDay.length; d++) {
1092
+ const k = nearestInDay(d);
1093
+ if (k) return k;
1094
+ }
1095
+ return null;
1096
+ case "ArrowLeft":
1097
+ for (let d = day - 1; d >= 0; d--) {
1098
+ const k = nearestInDay(d);
1099
+ if (k) return k;
1100
+ }
1101
+ return null;
1102
+ case "Home": return navByDay[day][0].key;
1103
+ case "End": return navByDay[day][navByDay[day].length - 1].key;
1104
+ default: return null;
1105
+ }
1106
+ };
1107
+ const onEventKeyDown = (currentKey, e) => {
1108
+ const next = nextEventKey(currentKey, e.key);
1109
+ if (!next) return;
1110
+ e.preventDefault();
1111
+ scrollRef.current?.querySelector(`[data-event-key="${next}"]`)?.focus();
1112
+ };
1113
+ const bandsByDay = useMemo(() => days.map((day) => closedHourBands(day, businessHours, windowStart, windowEnd)), [
1114
+ days,
1115
+ businessHours,
1116
+ windowStart,
1117
+ windowEnd
1118
+ ]);
1014
1119
  const gridLines = useMemo(() => {
1015
1120
  const hourLines = `repeating-linear-gradient(to bottom, transparent 0, transparent ${hourHeight - 1}px, ${theme.gridLine} ${hourHeight - 1}px, ${theme.gridLine} ${hourHeight}px)`;
1016
1121
  if (timeslots <= 1) return hourLines;
@@ -1036,41 +1141,57 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1036
1141
  base: { display: "flex" },
1037
1142
  themed: { borderBottom: `1px solid ${theme.gridLine}` }
1038
1143
  }),
1039
- children: [/* @__PURE__ */ jsx("div", { style: {
1040
- width: GUTTER_WIDTH,
1041
- flex: "none"
1042
- } }), days.map((day) => {
1144
+ children: [/* @__PURE__ */ jsx("div", {
1145
+ ...slot("weekNumber", {
1146
+ base: {
1147
+ width: gutterWidth,
1148
+ flex: "none",
1149
+ display: "flex",
1150
+ alignItems: "center",
1151
+ justifyContent: "center",
1152
+ overflow: "hidden"
1153
+ },
1154
+ themed: {
1155
+ fontSize: 10,
1156
+ color: theme.textMuted
1157
+ }
1158
+ }),
1159
+ children: showWeekNumber && gutterWidth > 0 && days[0] ? `${weekNumberPrefix}${getISOWeek(days.find((d) => d.getDay() === 4) ?? days[0])}` : null
1160
+ }), days.map((day) => {
1043
1161
  const today = getIsToday(day);
1044
- return /* @__PURE__ */ jsxs("button", {
1045
- type: "button",
1046
- tabIndex: onPressDateHeader ? 0 : -1,
1047
- "aria-hidden": onPressDateHeader ? void 0 : true,
1048
- onClick: onPressDateHeader ? () => onPressDateHeader(day) : void 0,
1049
- ...dataState({ "data-today": today }),
1050
- ...slot("columnHeader", {
1051
- base: {
1052
- flex: 1,
1053
- border: "none",
1054
- background: "transparent",
1055
- font: "inherit",
1056
- cursor: onPressDateHeader ? "pointer" : "default",
1057
- display: "flex",
1058
- flexDirection: "column",
1059
- alignItems: "center",
1060
- gap: 2
1061
- },
1062
- themed: {
1063
- color: theme.textMuted,
1064
- padding: "6px 0"
1065
- }
1162
+ const dateLabel = format(day, "EEEE, d MMMM yyyy", dfns);
1163
+ const headerProps = slot("columnHeader", {
1164
+ base: {
1165
+ flex: 1,
1166
+ border: "none",
1167
+ background: "transparent",
1168
+ font: "inherit",
1169
+ cursor: onPressDateHeader ? "pointer" : "default",
1170
+ display: "flex",
1171
+ flexDirection: "column",
1172
+ alignItems: "center",
1173
+ gap: 2
1174
+ },
1175
+ themed: {
1176
+ color: theme.textMuted,
1177
+ padding: "6px 0"
1178
+ }
1179
+ });
1180
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
1181
+ /* @__PURE__ */ jsx("span", {
1182
+ style: VISUALLY_HIDDEN,
1183
+ children: dateLabel
1066
1184
  }),
1067
- children: [/* @__PURE__ */ jsx("span", {
1185
+ /* @__PURE__ */ jsx("span", {
1186
+ "aria-hidden": true,
1068
1187
  ...slot("columnHeaderWeekday", { themed: {
1069
1188
  fontSize: 11,
1070
1189
  fontWeight: 600
1071
1190
  } }),
1072
1191
  children: format(day, weekdayFormatToken$1(weekdayFormat), dfns)
1073
- }), /* @__PURE__ */ jsx("span", {
1192
+ }),
1193
+ /* @__PURE__ */ jsx("span", {
1194
+ "aria-hidden": true,
1074
1195
  ...dataState({ "data-today": today }),
1075
1196
  ...slot("columnHeaderDate", {
1076
1197
  base: {
@@ -1089,7 +1210,18 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1089
1210
  }
1090
1211
  }),
1091
1212
  children: format(day, "d", dfns)
1092
- })]
1213
+ })
1214
+ ] });
1215
+ return onPressDateHeader ? /* @__PURE__ */ jsx("button", {
1216
+ type: "button",
1217
+ onClick: () => onPressDateHeader(day),
1218
+ ...dataState({ "data-today": today }),
1219
+ ...headerProps,
1220
+ children: inner
1221
+ }, day.toISOString()) : /* @__PURE__ */ jsx("div", {
1222
+ ...dataState({ "data-today": today }),
1223
+ ...headerProps,
1224
+ children: inner
1093
1225
  }, day.toISOString());
1094
1226
  })]
1095
1227
  }),
@@ -1101,8 +1233,10 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1101
1233
  children: [/* @__PURE__ */ jsx("div", {
1102
1234
  ...slot("allDayLabel", {
1103
1235
  base: {
1104
- width: GUTTER_WIDTH,
1236
+ width: gutterWidth,
1105
1237
  flex: "none",
1238
+ minWidth: 0,
1239
+ overflow: "hidden",
1106
1240
  textAlign: "right"
1107
1241
  },
1108
1242
  themed: {
@@ -1185,17 +1319,17 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1185
1319
  height: totalHeight,
1186
1320
  position: "relative"
1187
1321
  },
1188
- children: [/* @__PURE__ */ jsx("div", {
1322
+ children: [hideHours ? null : /* @__PURE__ */ jsx("div", {
1189
1323
  ...slot("hourGutter", { base: {
1190
- width: GUTTER_WIDTH,
1324
+ width: gutterWidth,
1191
1325
  flex: "none",
1192
1326
  position: "relative"
1193
1327
  } }),
1194
- children: HOURS.map((h) => /* @__PURE__ */ jsx("div", {
1328
+ children: visibleHours.map((h) => /* @__PURE__ */ jsx("div", {
1195
1329
  ...slot("hourLabel", {
1196
1330
  base: {
1197
1331
  position: "absolute",
1198
- top: h * hourHeight - 6,
1332
+ top: Math.max(0, (h - windowStart) * hourHeight - 6),
1199
1333
  right: 6
1200
1334
  },
1201
1335
  themed: {
@@ -1207,9 +1341,9 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1207
1341
  }, h))
1208
1342
  }), days.map((day, dayIndex) => {
1209
1343
  const positioned = positionedByDay[dayIndex];
1210
- const showNow = showNowIndicator && isSameCalendarDay(day, /* @__PURE__ */ new Date());
1211
- const nowDate = /* @__PURE__ */ new Date();
1212
- const nowTop = (nowDate.getHours() * 60 + nowDate.getMinutes()) / 60 * hourHeight;
1344
+ const nowHours = (now.getHours() * 60 + now.getMinutes()) / 60;
1345
+ const showNow = showNowIndicator && isSameCalendarDay(day, now) && nowHours >= windowStart && nowHours <= windowEnd;
1346
+ const nowTop = (nowHours - windowStart) * hourHeight;
1213
1347
  const bands = bandsByDay[dayIndex];
1214
1348
  const ghost = createBox?.dayIndex === dayIndex ? createBox : null;
1215
1349
  return /* @__PURE__ */ jsxs("div", {
@@ -1239,7 +1373,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1239
1373
  position: "absolute",
1240
1374
  left: 0,
1241
1375
  right: 0,
1242
- top: b.start * hourHeight,
1376
+ top: (b.start - windowStart) * hourHeight,
1243
1377
  height: (b.end - b.start) * hourHeight,
1244
1378
  pointerEvents: "none",
1245
1379
  zIndex: 0
@@ -1264,7 +1398,8 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1264
1398
  const active = drag?.key === key ? drag : null;
1265
1399
  const startHours = active ? active.startHours : pe.startHours;
1266
1400
  const durationHours = active ? active.durationHours : pe.durationHours;
1267
- const top = startHours * hourHeight;
1401
+ if (!active && (pe.startHours >= windowEnd || pe.startHours + pe.durationHours <= windowStart)) return null;
1402
+ const top = (startHours - windowStart) * hourHeight;
1268
1403
  const boxHeight = Math.max(durationHours * hourHeight, 14);
1269
1404
  const widthPct = 100 / pe.columns;
1270
1405
  const onPress = () => onPressEvent?.(pe.event);
@@ -1282,6 +1417,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1282
1417
  return /* @__PURE__ */ jsxs("div", {
1283
1418
  role: "button",
1284
1419
  tabIndex: 0,
1420
+ "data-event-key": keyboardEventNavigation ? key : void 0,
1285
1421
  "aria-label": eventAccessibilityLabel$1 ? eventAccessibilityLabel$1(pe.event, {
1286
1422
  mode,
1287
1423
  isAllDay: false,
@@ -1297,9 +1433,11 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1297
1433
  if (e.key === "Enter" || e.key === " ") {
1298
1434
  e.preventDefault();
1299
1435
  onPress();
1436
+ return;
1300
1437
  }
1438
+ if (keyboardEventNavigation) onEventKeyDown(key, e);
1301
1439
  },
1302
- onPointerDown: draggable ? (e) => beginDrag(e, pe.event, key, "move", pe.startHours, pe.durationHours) : void 0,
1440
+ onPointerDown: draggable ? (e) => beginDrag(e, pe.event, key, "move", pe.startHours, pe.durationHours, dayIndex) : void 0,
1303
1441
  onPointerMove: draggable ? moveDrag : void 0,
1304
1442
  onPointerUp: draggable ? (e) => endDrag(e, day, pe.event, onPress) : void 0,
1305
1443
  onPointerCancel: draggable ? cancelDrag : void 0,
@@ -1314,7 +1452,8 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1314
1452
  cursor: draggable ? "grab" : "pointer",
1315
1453
  touchAction: draggable ? "none" : "auto",
1316
1454
  zIndex: active ? 3 : 1,
1317
- opacity: active ? .85 : 1
1455
+ opacity: active ? .85 : 1,
1456
+ ...active && active.dayOffsetPx !== 0 ? { transform: `translateX(${active.dayOffsetPx}px)` } : null
1318
1457
  } }),
1319
1458
  children: [Renderer ? /* @__PURE__ */ jsx(Renderer, { ...args }) : /* @__PURE__ */ jsx(DefaultDomEvent, {
1320
1459
  ...args,
@@ -1323,7 +1462,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1323
1462
  }), draggable ? /* @__PURE__ */ jsx("div", {
1324
1463
  onPointerDown: (e) => {
1325
1464
  e.stopPropagation();
1326
- beginDrag(e, pe.event, key, "resize", pe.startHours, pe.durationHours);
1465
+ beginDrag(e, pe.event, key, "resize", pe.startHours, pe.durationHours, dayIndex);
1327
1466
  },
1328
1467
  style: {
1329
1468
  position: "absolute",
@@ -1390,6 +1529,18 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1390
1529
  }
1391
1530
  //#endregion
1392
1531
  //#region src/Calendar.tsx
1532
+ const EMPTY_EVENTS = [];
1533
+ function expansionRange(mode, date, weekStartsOn, numberOfDays) {
1534
+ if (mode === "month") return [startOfWeek(startOfMonth(date), { weekStartsOn }), endOfWeek(endOfMonth(date), { weekStartsOn })];
1535
+ if (mode === "schedule") return [startOfDay(date), endOfDay(addMonths(date, 3))];
1536
+ const days = getViewDays$1(mode, date, weekStartsOn, numberOfDays ?? 1);
1537
+ return [startOfDay(days[0]), endOfDay(days[days.length - 1])];
1538
+ }
1539
+ function pageStep(mode, date, direction, weekStartsOn, numberOfDays) {
1540
+ if (mode === "month") return addMonths(date, direction);
1541
+ if (mode === "schedule") return addDays(date, direction * 7);
1542
+ return addDays(date, direction * getViewDays$1(mode, date, weekStartsOn, numberOfDays ?? 1).length);
1543
+ }
1393
1544
  /**
1394
1545
  * Batteries-included entry point for the react-dom renderer: it picks the right
1395
1546
  * view for `mode`. `month` renders a single {@link MonthView}; `schedule` renders
@@ -1401,9 +1552,37 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1401
1552
  * <Calendar mode="week" date={new Date()} events={events} />
1402
1553
  * ```
1403
1554
  */
1404
- function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays, locale, theme, height, className, style, onPressEvent, eventAccessibilityLabel, ampm, hourHeight, scrollOffsetMinutes, timeslots, businessHours, showNowIndicator, showAllDayEventCell, dragStepMinutes, onPressCell, onCreateEvent, onDragStart, onDragEvent, onPressDateHeader, renderTimeEvent, hourComponent, maxVisibleEventCount, moreLabel, showAdjacentMonths, weekdayFormat, fillCellOnSelection, selectedRange, selectedDates, minDate, maxDate, isDateDisabled, keyboardDayNavigation, onPressDay, onPressMore, renderMonthEvent, renderScheduleEvent, classNames, styles }) {
1405
- if (mode === "schedule") return /* @__PURE__ */ jsx(Agenda, {
1406
- events: events ?? [],
1555
+ function Calendar({ mode = "week", date, events, onChangeDate, weekStartsOn = 0, numberOfDays, locale, timeZone, theme, height, className, style, onPressEvent, eventAccessibilityLabel, ampm, hourHeight, scrollOffsetMinutes, timeslots, minHour, maxHour, hideHours, showWeekNumber, weekNumberPrefix, businessHours, showNowIndicator, showAllDayEventCell, dragStepMinutes, onPressCell, onCreateEvent, onDragStart, onDragEvent, onPressDateHeader, renderTimeEvent, hourComponent, keyboardEventNavigation, maxVisibleEventCount, moreLabel, showAdjacentMonths, weekdayFormat, fillCellOnSelection, selectedRange, selectedDates, minDate, maxDate, isDateDisabled, keyboardDayNavigation, onPressDay, onPressMore, renderMonthEvent, renderScheduleEvent, classNames, styles }) {
1556
+ const displayEvents = useMemo(() => {
1557
+ let out = events ?? EMPTY_EVENTS;
1558
+ if (out.some((e) => e.recurrence)) {
1559
+ let [start, end] = expansionRange(mode, date, weekStartsOn, numberOfDays);
1560
+ if (timeZone) {
1561
+ start = addDays(start, -1);
1562
+ end = addDays(end, 1);
1563
+ }
1564
+ out = expandRecurringEvents$1(out, start, end);
1565
+ }
1566
+ if (timeZone) out = eventsInTimeZone(out, timeZone);
1567
+ return out;
1568
+ }, [
1569
+ events,
1570
+ mode,
1571
+ date.getTime(),
1572
+ weekStartsOn,
1573
+ numberOfDays,
1574
+ timeZone
1575
+ ]);
1576
+ const handlePageKeys = (e) => {
1577
+ if (!onChangeDate) return;
1578
+ if (e.key === "PageDown") onChangeDate(pageStep(mode, date, 1, weekStartsOn, numberOfDays));
1579
+ else if (e.key === "PageUp") onChangeDate(pageStep(mode, date, -1, weekStartsOn, numberOfDays));
1580
+ else return;
1581
+ e.preventDefault();
1582
+ };
1583
+ let view;
1584
+ if (mode === "schedule") view = /* @__PURE__ */ jsx(Agenda, {
1585
+ events: displayEvents,
1407
1586
  locale,
1408
1587
  ampm,
1409
1588
  theme,
@@ -1418,9 +1597,9 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1418
1597
  onPressEvent,
1419
1598
  onPressDay
1420
1599
  });
1421
- if (mode === "month") return /* @__PURE__ */ jsx(MonthView, {
1600
+ else if (mode === "month") view = /* @__PURE__ */ jsx(MonthView, {
1422
1601
  date,
1423
- events: events ?? [],
1602
+ events: displayEvents,
1424
1603
  weekStartsOn,
1425
1604
  weekdayFormat,
1426
1605
  locale,
@@ -1446,10 +1625,10 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1446
1625
  renderEvent: renderMonthEvent,
1447
1626
  eventAccessibilityLabel
1448
1627
  });
1449
- return /* @__PURE__ */ jsx(TimeGrid, {
1628
+ else view = /* @__PURE__ */ jsx(TimeGrid, {
1450
1629
  date,
1451
1630
  mode,
1452
- events,
1631
+ events: displayEvents,
1453
1632
  weekStartsOn,
1454
1633
  weekdayFormat,
1455
1634
  numberOfDays,
@@ -1464,6 +1643,11 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1464
1643
  hourHeight,
1465
1644
  scrollOffsetMinutes,
1466
1645
  timeslots,
1646
+ minHour,
1647
+ maxHour,
1648
+ hideHours,
1649
+ showWeekNumber,
1650
+ weekNumberPrefix,
1467
1651
  businessHours,
1468
1652
  showNowIndicator,
1469
1653
  showAllDayEventCell,
@@ -1476,7 +1660,13 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1476
1660
  onPressDateHeader,
1477
1661
  renderEvent: renderTimeEvent,
1478
1662
  eventAccessibilityLabel,
1479
- hourComponent
1663
+ hourComponent,
1664
+ keyboardEventNavigation
1665
+ });
1666
+ return /* @__PURE__ */ jsx("div", {
1667
+ style: { display: "contents" },
1668
+ onKeyDown: handlePageKeys,
1669
+ children: view
1480
1670
  });
1481
1671
  }
1482
1672
  //#endregion
@@ -1834,7 +2024,9 @@ function DateRangePicker({ value, onChange, formatLabel = defaultRangeLabel, wee
1834
2024
  //#endregion
1835
2025
  //#region src/ResourceTimeline.tsx
1836
2026
  const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
1837
- function DefaultBar({ event, boxProps, theme }) {
2027
+ const GUTTER_WIDTH = 56;
2028
+ function DefaultBar({ event, height, boxProps, theme }) {
2029
+ const showTime = height != null ? height > 34 : true;
1838
2030
  const time = eventTimeLabel({
1839
2031
  mode: "day",
1840
2032
  isAllDay: false,
@@ -1874,7 +2066,7 @@ function DefaultBar({ event, boxProps, theme }) {
1874
2066
  textOverflow: "ellipsis"
1875
2067
  },
1876
2068
  children: event.title
1877
- }), time ? /* @__PURE__ */ jsx("div", {
2069
+ }), time && showTime ? /* @__PURE__ */ jsx("div", {
1878
2070
  style: {
1879
2071
  opacity: .75,
1880
2072
  fontSize: 11
@@ -1899,7 +2091,7 @@ function DefaultBar({ event, boxProps, theme }) {
1899
2091
  * />
1900
2092
  * ```
1901
2093
  */
1902
- function ResourceTimeline({ date, resources, events, resourceId = (event) => event.resourceId, startHour = 0, endHour = 24, hourWidth = 80, rowHeight = 56, labelWidth = 140, ampm = false, theme: themeOverrides, renderEvent, onPressEvent, onDragEvent, dragStepMinutes = 15, className, style, classNames, styles }) {
2094
+ function ResourceTimeline({ date, orientation = "horizontal", resources, events, resourceId = (event) => event.resourceId, startHour = 0, endHour = 24, hourWidth = 80, hourHeight = 48, rowHeight = 56, labelWidth = 140, ampm = false, theme: themeOverrides, renderEvent, onPressEvent, onDragEvent, onPressCell, onCreateEvent, businessHours, dragStepMinutes = 15, className, style, classNames, styles }) {
1903
2095
  const theme = useMemo(() => mergeDomTheme(themeOverrides), [themeOverrides]);
1904
2096
  const slot = createSlots({
1905
2097
  classNames,
@@ -1907,6 +2099,8 @@ function ResourceTimeline({ date, resources, events, resourceId = (event) => eve
1907
2099
  });
1908
2100
  const Renderer = renderEvent;
1909
2101
  const snapHours = dragStepMinutes / 60;
2102
+ const vertical = orientation === "vertical";
2103
+ const hourSize = vertical ? hourHeight : hourWidth;
1910
2104
  const [drag, setDrag] = useState(null);
1911
2105
  const dragRef = useRef(null);
1912
2106
  const origin = useRef(null);
@@ -1921,7 +2115,7 @@ function ResourceTimeline({ date, resources, events, resourceId = (event) => eve
1921
2115
  e.target.setPointerCapture?.(e.pointerId);
1922
2116
  } catch {}
1923
2117
  origin.current = {
1924
- x: e.clientX,
2118
+ x: vertical ? e.clientY : e.clientX,
1925
2119
  startHours,
1926
2120
  durationHours
1927
2121
  };
@@ -1936,7 +2130,7 @@ function ResourceTimeline({ date, resources, events, resourceId = (event) => eve
1936
2130
  const moveDrag = (e) => {
1937
2131
  const d = dragRef.current;
1938
2132
  if (!d || !origin.current) return;
1939
- const dHours = (e.clientX - origin.current.x) / hourWidth;
2133
+ const dHours = ((vertical ? e.clientY : e.clientX) - origin.current.x) / hourSize;
1940
2134
  const snap = (v) => Math.round(v / snapHours) * snapHours;
1941
2135
  if (d.kind === "move") {
1942
2136
  const startHours = clamp(snap(origin.current.startHours + dHours), startHour, endHour - d.durationHours);
@@ -1976,6 +2170,69 @@ function ResourceTimeline({ date, resources, events, resourceId = (event) => eve
1976
2170
  applyDrag(null);
1977
2171
  origin.current = null;
1978
2172
  };
2173
+ const [createBox, setCreateBox] = useState(null);
2174
+ const createOrigin = useRef(null);
2175
+ const cellEnabled = !!onPressCell || !!onCreateEvent;
2176
+ const pxAlongAxis = (el, e) => {
2177
+ const rect = el.getBoundingClientRect();
2178
+ return vertical ? e.clientY - rect.top : e.clientX - rect.left;
2179
+ };
2180
+ const beginCreate = (e, resource) => {
2181
+ if (!cellEnabled || e.pointerType === "touch") return;
2182
+ if (e.target !== e.currentTarget || e.button > 0) return;
2183
+ const el = e.currentTarget;
2184
+ const startPx = pxAlongAxis(el, e);
2185
+ try {
2186
+ el.setPointerCapture?.(e.pointerId);
2187
+ } catch {}
2188
+ createOrigin.current = {
2189
+ el,
2190
+ resource,
2191
+ startPx
2192
+ };
2193
+ if (onCreateEvent) setCreateBox({
2194
+ resourceKey: resource.id,
2195
+ startPx,
2196
+ curPx: startPx
2197
+ });
2198
+ };
2199
+ const moveCreate = (e) => {
2200
+ const o = createOrigin.current;
2201
+ if (!o || !onCreateEvent) return;
2202
+ setCreateBox({
2203
+ resourceKey: o.resource.id,
2204
+ startPx: o.startPx,
2205
+ curPx: pxAlongAxis(o.el, e)
2206
+ });
2207
+ };
2208
+ const endCreate = (e) => {
2209
+ const o = createOrigin.current;
2210
+ if (!o) return;
2211
+ try {
2212
+ o.el.releasePointerCapture?.(e.pointerId);
2213
+ } catch {}
2214
+ const endPx = pxAlongAxis(o.el, e);
2215
+ if (Math.abs(endPx - o.startPx) > 4 && onCreateEvent) {
2216
+ const range = cellRangeFromDrag(date, o.startPx, endPx, hourSize, startHour, dragStepMinutes);
2217
+ if (range) onCreateEvent(range.start, range.end, o.resource);
2218
+ } else if (onPressCell) {
2219
+ const at = cellRangeFromDrag(date, o.startPx, o.startPx, hourSize, startHour, dragStepMinutes);
2220
+ if (at) onPressCell(at.start, o.resource);
2221
+ }
2222
+ createOrigin.current = null;
2223
+ setCreateBox(null);
2224
+ };
2225
+ const cancelCreate = () => {
2226
+ createOrigin.current = null;
2227
+ setCreateBox(null);
2228
+ };
2229
+ const trackInteractionProps = (resource) => cellEnabled ? {
2230
+ onPointerDown: (e) => beginCreate(e, resource),
2231
+ onPointerMove: moveCreate,
2232
+ onPointerUp: endCreate,
2233
+ onPointerCancel: cancelCreate
2234
+ } : null;
2235
+ const bandsFor = (resource) => businessHours ? closedHourBands(date, (d) => businessHours(d, resource), startHour, endHour) : [];
1979
2236
  const hours = useMemo(() => Array.from({ length: Math.max(0, endHour - startHour) }, (_, i) => startHour + i), [startHour, endHour]);
1980
2237
  const trackWidth = (endHour - startHour) * hourWidth;
1981
2238
  const byResource = useMemo(() => {
@@ -1991,6 +2248,211 @@ function ResourceTimeline({ date, resources, events, resourceId = (event) => eve
1991
2248
  resourceId,
1992
2249
  date
1993
2250
  ]);
2251
+ if (vertical) {
2252
+ const trackHeight = (endHour - startHour) * hourHeight;
2253
+ const vGridLines = `repeating-linear-gradient(to bottom, transparent 0, transparent ${hourHeight - 1}px, ${theme.gridLine} ${hourHeight - 1}px, ${theme.gridLine} ${hourHeight}px)`;
2254
+ return /* @__PURE__ */ jsxs("div", {
2255
+ className,
2256
+ style: {
2257
+ fontFamily: theme.fontFamily,
2258
+ color: theme.text,
2259
+ overflowY: "auto",
2260
+ ...style
2261
+ },
2262
+ children: [/* @__PURE__ */ jsxs("div", {
2263
+ ...slot("header", {
2264
+ base: {
2265
+ display: "flex",
2266
+ position: "sticky",
2267
+ top: 0,
2268
+ zIndex: 2
2269
+ },
2270
+ themed: {
2271
+ borderBottom: `1px solid ${theme.gridLine}`,
2272
+ background: theme.surface
2273
+ }
2274
+ }),
2275
+ children: [/* @__PURE__ */ jsx("div", { ...slot("corner", { base: {
2276
+ width: GUTTER_WIDTH,
2277
+ flex: "none"
2278
+ } }) }), resources.map((resource) => /* @__PURE__ */ jsx("div", {
2279
+ ...slot("resourceLabel", {
2280
+ base: {
2281
+ flex: 1,
2282
+ minWidth: 0,
2283
+ padding: "6px 4px",
2284
+ textAlign: "center",
2285
+ overflow: "hidden",
2286
+ textOverflow: "ellipsis",
2287
+ whiteSpace: "nowrap",
2288
+ boxSizing: "border-box"
2289
+ },
2290
+ themed: {
2291
+ fontSize: 13,
2292
+ fontWeight: 600,
2293
+ borderLeft: `1px solid ${theme.gridLine}`
2294
+ }
2295
+ }),
2296
+ children: resource.title ?? resource.id
2297
+ }, resource.id))]
2298
+ }), /* @__PURE__ */ jsxs("div", {
2299
+ style: { display: "flex" },
2300
+ children: [/* @__PURE__ */ jsx("div", {
2301
+ ...slot("timeAxis", { base: {
2302
+ position: "relative",
2303
+ width: GUTTER_WIDTH,
2304
+ flex: "none",
2305
+ height: trackHeight
2306
+ } }),
2307
+ children: hours.map((h) => /* @__PURE__ */ jsx("div", {
2308
+ ...slot("hourTick", {
2309
+ base: {
2310
+ position: "absolute",
2311
+ top: Math.max(0, (h - startHour) * hourHeight - 6),
2312
+ right: 6
2313
+ },
2314
+ themed: {
2315
+ fontSize: 10,
2316
+ color: theme.textMuted
2317
+ }
2318
+ }),
2319
+ children: formatHour(h, { ampm })
2320
+ }, h))
2321
+ }), resources.map((resource) => {
2322
+ const positioned = byResource.get(resource.id) ?? [];
2323
+ return /* @__PURE__ */ jsx("div", {
2324
+ ...slot("row", {
2325
+ base: {
2326
+ flex: 1,
2327
+ minWidth: 0
2328
+ },
2329
+ themed: { borderLeft: `1px solid ${theme.gridLine}` }
2330
+ }),
2331
+ children: /* @__PURE__ */ jsxs("div", {
2332
+ ...trackInteractionProps(resource),
2333
+ ...slot("track", { base: {
2334
+ position: "relative",
2335
+ height: trackHeight
2336
+ } }),
2337
+ children: [
2338
+ bandsFor(resource).map((b) => /* @__PURE__ */ jsx("div", {
2339
+ "aria-hidden": true,
2340
+ ...slot("businessHours", {
2341
+ base: {
2342
+ position: "absolute",
2343
+ left: 0,
2344
+ right: 0,
2345
+ top: (b.start - startHour) * hourHeight,
2346
+ height: (b.end - b.start) * hourHeight,
2347
+ pointerEvents: "none",
2348
+ zIndex: 0
2349
+ },
2350
+ themed: { background: theme.outsideHoursBackground }
2351
+ })
2352
+ }, b.start)),
2353
+ /* @__PURE__ */ jsx("div", {
2354
+ "aria-hidden": true,
2355
+ ...slot("gridLines", {
2356
+ base: {
2357
+ position: "absolute",
2358
+ inset: 0,
2359
+ pointerEvents: "none"
2360
+ },
2361
+ themed: { backgroundImage: vGridLines }
2362
+ })
2363
+ }),
2364
+ positioned.map((pe, idx) => {
2365
+ const key = `${resource.id}:${idx}`;
2366
+ const active = drag?.key === key ? drag : null;
2367
+ const startH = active ? active.startHours : pe.startHours;
2368
+ const durH = active ? active.durationHours : pe.durationHours;
2369
+ const top = clamp(startH - startHour, 0, endHour - startHour) * hourHeight;
2370
+ const bottom = clamp(startH + durH - startHour, 0, endHour - startHour) * hourHeight;
2371
+ const height = Math.max(bottom - top, 2);
2372
+ const lanePct = 100 / pe.columns;
2373
+ const onPress = () => onPressEvent?.(pe.event);
2374
+ const args = {
2375
+ event: pe.event,
2376
+ width: 0,
2377
+ height,
2378
+ onPress
2379
+ };
2380
+ const draggable = !!onDragEvent;
2381
+ return /* @__PURE__ */ jsxs("button", {
2382
+ type: "button",
2383
+ onClick: draggable ? void 0 : onPress,
2384
+ "aria-label": pe.event.title,
2385
+ ...dataState({ "data-dragging": !!active }),
2386
+ onPointerDown: draggable ? (e) => beginDrag(e, key, "move", pe.startHours, pe.durationHours) : void 0,
2387
+ onPointerMove: draggable ? moveDrag : void 0,
2388
+ onPointerUp: draggable ? (e) => endDrag(e, pe.event, onPress) : void 0,
2389
+ onPointerCancel: draggable ? cancelDrag : void 0,
2390
+ ...slot("event", { base: {
2391
+ position: "absolute",
2392
+ top,
2393
+ height,
2394
+ left: `${pe.column * lanePct}%`,
2395
+ width: `${lanePct}%`,
2396
+ padding: 1,
2397
+ border: "none",
2398
+ background: "transparent",
2399
+ cursor: draggable ? "grab" : "pointer",
2400
+ touchAction: draggable ? "none" : "auto",
2401
+ font: "inherit",
2402
+ textAlign: "left",
2403
+ boxSizing: "border-box",
2404
+ zIndex: active ? 3 : 1,
2405
+ opacity: active ? .85 : 1
2406
+ } }),
2407
+ children: [Renderer ? /* @__PURE__ */ jsx(Renderer, { ...args }) : /* @__PURE__ */ jsx(DefaultBar, {
2408
+ ...args,
2409
+ theme,
2410
+ boxProps: slot("eventBox")
2411
+ }), draggable ? /* @__PURE__ */ jsx("span", {
2412
+ "aria-hidden": true,
2413
+ onPointerDown: (e) => {
2414
+ e.stopPropagation();
2415
+ beginDrag(e, key, "resize", pe.startHours, pe.durationHours);
2416
+ },
2417
+ style: {
2418
+ position: "absolute",
2419
+ left: 0,
2420
+ right: 0,
2421
+ bottom: 0,
2422
+ height: 8,
2423
+ cursor: "ns-resize",
2424
+ touchAction: "none"
2425
+ }
2426
+ }) : null]
2427
+ }, idx);
2428
+ }),
2429
+ createBox?.resourceKey === resource.id ? /* @__PURE__ */ jsx("div", {
2430
+ "aria-hidden": true,
2431
+ ...slot("createGhost", {
2432
+ base: {
2433
+ position: "absolute",
2434
+ left: 1,
2435
+ right: 1,
2436
+ top: Math.min(createBox.startPx, createBox.curPx),
2437
+ height: Math.max(Math.abs(createBox.curPx - createBox.startPx), 2),
2438
+ opacity: .7,
2439
+ pointerEvents: "none",
2440
+ zIndex: 2
2441
+ },
2442
+ themed: {
2443
+ background: theme.rangeBackground,
2444
+ border: `1px solid ${theme.selectedBackground}`,
2445
+ borderRadius: 6
2446
+ }
2447
+ })
2448
+ }) : null
2449
+ ]
2450
+ })
2451
+ }, resource.id);
2452
+ })]
2453
+ })]
2454
+ });
2455
+ }
1994
2456
  const gridLines = `repeating-linear-gradient(to right, transparent 0, transparent ${hourWidth - 1}px, ${theme.gridLine} ${hourWidth - 1}px, ${theme.gridLine} ${hourWidth}px)`;
1995
2457
  return /* @__PURE__ */ jsx("div", {
1996
2458
  className,
@@ -2059,84 +2521,123 @@ function ResourceTimeline({ date, resources, events, resourceId = (event) => eve
2059
2521
  }),
2060
2522
  children: resource.title ?? resource.id
2061
2523
  }), /* @__PURE__ */ jsxs("div", {
2524
+ ...trackInteractionProps(resource),
2062
2525
  ...slot("track", { base: {
2063
2526
  position: "relative",
2064
2527
  width: trackWidth
2065
2528
  } }),
2066
- children: [/* @__PURE__ */ jsx("div", {
2067
- "aria-hidden": true,
2068
- ...slot("gridLines", {
2069
- base: {
2070
- position: "absolute",
2071
- inset: 0,
2072
- pointerEvents: "none"
2073
- },
2074
- themed: { backgroundImage: gridLines }
2075
- })
2076
- }), positioned.map((pe, idx) => {
2077
- const key = `${resource.id}:${idx}`;
2078
- const active = drag?.key === key ? drag : null;
2079
- const startH = active ? active.startHours : pe.startHours;
2080
- const durH = active ? active.durationHours : pe.durationHours;
2081
- const left = clamp(startH - startHour, 0, endHour - startHour) * hourWidth;
2082
- const right = clamp(startH + durH - startHour, 0, endHour - startHour) * hourWidth;
2083
- const width = Math.max(right - left, 2);
2084
- const laneHeight = rowHeight / pe.columns;
2085
- const onPress = () => onPressEvent?.(pe.event);
2086
- const args = {
2087
- event: pe.event,
2088
- width,
2089
- onPress
2090
- };
2091
- const draggable = !!onDragEvent;
2092
- return /* @__PURE__ */ jsxs("button", {
2093
- type: "button",
2094
- onClick: draggable ? void 0 : onPress,
2095
- "aria-label": pe.event.title,
2096
- ...dataState({ "data-dragging": !!active }),
2097
- onPointerDown: draggable ? (e) => beginDrag(e, key, "move", pe.startHours, pe.durationHours) : void 0,
2098
- onPointerMove: draggable ? moveDrag : void 0,
2099
- onPointerUp: draggable ? (e) => endDrag(e, pe.event, onPress) : void 0,
2100
- onPointerCancel: draggable ? cancelDrag : void 0,
2101
- ...slot("event", { base: {
2102
- position: "absolute",
2103
- left,
2104
- width,
2105
- top: pe.column * laneHeight,
2106
- height: laneHeight,
2107
- padding: 1,
2108
- border: "none",
2109
- background: "transparent",
2110
- cursor: draggable ? "grab" : "pointer",
2111
- touchAction: draggable ? "none" : "auto",
2112
- font: "inherit",
2113
- textAlign: "left",
2114
- boxSizing: "border-box",
2115
- zIndex: active ? 3 : 1,
2116
- opacity: active ? .85 : 1
2117
- } }),
2118
- children: [Renderer ? /* @__PURE__ */ jsx(Renderer, { ...args }) : /* @__PURE__ */ jsx(DefaultBar, {
2119
- ...args,
2120
- theme,
2121
- boxProps: slot("eventBox")
2122
- }), draggable ? /* @__PURE__ */ jsx("span", {
2123
- "aria-hidden": true,
2124
- onPointerDown: (e) => {
2125
- e.stopPropagation();
2126
- beginDrag(e, key, "resize", pe.startHours, pe.durationHours);
2127
- },
2128
- style: {
2529
+ children: [
2530
+ bandsFor(resource).map((b) => /* @__PURE__ */ jsx("div", {
2531
+ "aria-hidden": true,
2532
+ ...slot("businessHours", {
2533
+ base: {
2129
2534
  position: "absolute",
2130
2535
  top: 0,
2131
2536
  bottom: 0,
2132
- right: 0,
2133
- width: 8,
2134
- cursor: "ew-resize",
2135
- touchAction: "none"
2537
+ left: (b.start - startHour) * hourWidth,
2538
+ width: (b.end - b.start) * hourWidth,
2539
+ pointerEvents: "none",
2540
+ zIndex: 0
2541
+ },
2542
+ themed: { background: theme.outsideHoursBackground }
2543
+ })
2544
+ }, b.start)),
2545
+ /* @__PURE__ */ jsx("div", {
2546
+ "aria-hidden": true,
2547
+ ...slot("gridLines", {
2548
+ base: {
2549
+ position: "absolute",
2550
+ inset: 0,
2551
+ pointerEvents: "none"
2552
+ },
2553
+ themed: { backgroundImage: gridLines }
2554
+ })
2555
+ }),
2556
+ positioned.map((pe, idx) => {
2557
+ const key = `${resource.id}:${idx}`;
2558
+ const active = drag?.key === key ? drag : null;
2559
+ const startH = active ? active.startHours : pe.startHours;
2560
+ const durH = active ? active.durationHours : pe.durationHours;
2561
+ const left = clamp(startH - startHour, 0, endHour - startHour) * hourWidth;
2562
+ const right = clamp(startH + durH - startHour, 0, endHour - startHour) * hourWidth;
2563
+ const width = Math.max(right - left, 2);
2564
+ const laneHeight = rowHeight / pe.columns;
2565
+ const onPress = () => onPressEvent?.(pe.event);
2566
+ const args = {
2567
+ event: pe.event,
2568
+ width,
2569
+ onPress
2570
+ };
2571
+ const draggable = !!onDragEvent;
2572
+ return /* @__PURE__ */ jsxs("button", {
2573
+ type: "button",
2574
+ onClick: draggable ? void 0 : onPress,
2575
+ "aria-label": pe.event.title,
2576
+ ...dataState({ "data-dragging": !!active }),
2577
+ onPointerDown: draggable ? (e) => beginDrag(e, key, "move", pe.startHours, pe.durationHours) : void 0,
2578
+ onPointerMove: draggable ? moveDrag : void 0,
2579
+ onPointerUp: draggable ? (e) => endDrag(e, pe.event, onPress) : void 0,
2580
+ onPointerCancel: draggable ? cancelDrag : void 0,
2581
+ ...slot("event", { base: {
2582
+ position: "absolute",
2583
+ left,
2584
+ width,
2585
+ top: pe.column * laneHeight,
2586
+ height: laneHeight,
2587
+ padding: 1,
2588
+ border: "none",
2589
+ background: "transparent",
2590
+ cursor: draggable ? "grab" : "pointer",
2591
+ touchAction: draggable ? "none" : "auto",
2592
+ font: "inherit",
2593
+ textAlign: "left",
2594
+ boxSizing: "border-box",
2595
+ zIndex: active ? 3 : 1,
2596
+ opacity: active ? .85 : 1
2597
+ } }),
2598
+ children: [Renderer ? /* @__PURE__ */ jsx(Renderer, { ...args }) : /* @__PURE__ */ jsx(DefaultBar, {
2599
+ ...args,
2600
+ theme,
2601
+ boxProps: slot("eventBox")
2602
+ }), draggable ? /* @__PURE__ */ jsx("span", {
2603
+ "aria-hidden": true,
2604
+ onPointerDown: (e) => {
2605
+ e.stopPropagation();
2606
+ beginDrag(e, key, "resize", pe.startHours, pe.durationHours);
2607
+ },
2608
+ style: {
2609
+ position: "absolute",
2610
+ top: 0,
2611
+ bottom: 0,
2612
+ right: 0,
2613
+ width: 8,
2614
+ cursor: "ew-resize",
2615
+ touchAction: "none"
2616
+ }
2617
+ }) : null]
2618
+ }, idx);
2619
+ }),
2620
+ createBox?.resourceKey === resource.id ? /* @__PURE__ */ jsx("div", {
2621
+ "aria-hidden": true,
2622
+ ...slot("createGhost", {
2623
+ base: {
2624
+ position: "absolute",
2625
+ top: 1,
2626
+ bottom: 1,
2627
+ left: Math.min(createBox.startPx, createBox.curPx),
2628
+ width: Math.max(Math.abs(createBox.curPx - createBox.startPx), 2),
2629
+ opacity: .7,
2630
+ pointerEvents: "none",
2631
+ zIndex: 2
2632
+ },
2633
+ themed: {
2634
+ background: theme.rangeBackground,
2635
+ border: `1px solid ${theme.selectedBackground}`,
2636
+ borderRadius: 6
2136
2637
  }
2137
- }) : null]
2138
- }, idx);
2139
- })]
2638
+ })
2639
+ }) : null
2640
+ ]
2140
2641
  })]
2141
2642
  }, resource.id);
2142
2643
  })]