@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.d.mts +94 -223
- package/dist/index.d.ts +94 -223
- package/dist/index.js +640 -139
- package/dist/index.mjs +643 -142
- package/package.json +2 -2
- package/src/Calendar.tsx +192 -50
- package/src/ResourceTimeline.tsx +406 -11
- package/src/TimeGrid.tsx +345 -72
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
|
|
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 =
|
|
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),
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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", {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
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
|
-
|
|
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
|
-
}),
|
|
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:
|
|
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:
|
|
1324
|
+
width: gutterWidth,
|
|
1191
1325
|
flex: "none",
|
|
1192
1326
|
position: "relative"
|
|
1193
1327
|
} }),
|
|
1194
|
-
children:
|
|
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
|
|
1211
|
-
const
|
|
1212
|
-
const nowTop = (
|
|
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
|
-
|
|
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
|
-
|
|
1406
|
-
|
|
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")
|
|
1600
|
+
else if (mode === "month") view = /* @__PURE__ */ jsx(MonthView, {
|
|
1422
1601
|
date,
|
|
1423
|
-
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
|
-
|
|
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
|
-
|
|
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) /
|
|
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: [
|
|
2067
|
-
"
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
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
|
-
|
|
2133
|
-
width:
|
|
2134
|
-
|
|
2135
|
-
|
|
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
|
-
})
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2638
|
+
})
|
|
2639
|
+
}) : null
|
|
2640
|
+
]
|
|
2140
2641
|
})]
|
|
2141
2642
|
}, resource.id);
|
|
2142
2643
|
})]
|