@super-calendar/native 2.4.0 → 2.6.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,12 +1,175 @@
1
- import { a as withEventAccessibilityLabel, c as createSlots, d as darkTheme, f as defaultTheme, i as MonthView, l as useSlots, m as useCalendarTheme, n as DefaultMonthEvent, o as useWebPagerKeys, p as mergeTheme, r as MonthPager, s as SlotStylesProvider, t as MonthList, u as CalendarThemeProvider } from "./MonthList-zPLklHAY.mjs";
2
- import { addDays, addMonths, addWeeks, differenceInCalendarDays, endOfDay, endOfMonth, endOfWeek, format, getHours, getISOWeek, getMinutes, isSameDay, startOfDay, startOfMonth, startOfWeek } from "date-fns";
1
+ import { a as withEventAccessibilityLabel, c as createSlots, d as darkTheme, f as defaultTheme, i as MonthView, l as useSlots, m as useCalendarTheme, n as DefaultMonthEvent, o as useWebPagerKeys, p as mergeTheme, r as MonthPager, s as SlotStylesProvider, t as MonthList, u as CalendarThemeProvider } from "./MonthList-CMzYn9T0.mjs";
2
+ import { addDays, addMonths, addWeeks, differenceInCalendarDays, endOfDay, endOfMonth, endOfWeek, endOfYear, format, getHours, getISOWeek, getMinutes, isSameDay, startOfDay, startOfMonth, startOfWeek, startOfYear } from "date-fns";
3
3
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import Animated, { runOnJS, scrollTo, useAnimatedReaction, useAnimatedRef, useAnimatedScrollHandler, useAnimatedStyle, useDerivedValue, useReducedMotion, useSharedValue } from "react-native-reanimated";
5
- import { buildMonthGrid, buildMonthWeeks, cellRangeFromDrag, closedHourBands, daySelectionState, eventAccessibilityLabel, eventChipLayout, eventTimeLabel, eventsInTimeZone, eventsInTimeZone as eventsInTimeZone$1, expandRecurringEvents, expandRecurringEvents as expandRecurringEvents$1, formatHour, getIsToday, getIsToday as getIsToday$1, getViewDays, getViewDays as getViewDays$1, getWeekDays, isAllDayEvent, isAllDayEvent as isAllDayEvent$1, isDateSelectable, isRangeEndpoint, isSameCalendarDay, isSameCalendarDay as isSameCalendarDay$1, isWeekend, isWeekend as isWeekend$1, isWithinDateRange, layoutDayEvents, layoutDayEvents as layoutDayEvents$1, minutesIntoDay, nextDateRange, parseICalendar, resolveDraggedBounds, snapDeltaMinutes, titleEllipsizeMode, titleNumberOfLines, toICalendar, toZonedTime, useDateRange, useMonthGrid, viewDayCount, weekdayFormatToken, weekdayFormatToken as weekdayFormatToken$1 } from "@super-calendar/core";
6
- import { LegendList } from "@legendapp/list/react-native";
5
+ import { buildMonthGrid, buildMonthWeeks, buildMonthWeeks as buildMonthWeeks$1, cellRangeFromDrag, closedHourBands, daySelectionState, eventAccessibilityLabel, eventChipLayout, eventTimeLabel, eventsInTimeZone, eventsInTimeZone as eventsInTimeZone$1, expandRecurringEvents, expandRecurringEvents as expandRecurringEvents$1, formatHour, getIsToday, getIsToday as getIsToday$1, getViewDays, getViewDays as getViewDays$1, getWeekDays, getWeekDays as getWeekDays$1, getYearMonths, groupEventsByDay, isAllDayEvent, isAllDayEvent as isAllDayEvent$1, isDateSelectable, isRangeEndpoint, isSameCalendarDay, isSameCalendarDay as isSameCalendarDay$1, isWeekend, isWeekend as isWeekend$1, isWithinDateRange, layoutDayEvents, layoutDayEvents as layoutDayEvents$1, minutesIntoDay, nextDateRange, parseICalendar, resolveDraggedBounds, snapDeltaMinutes, titleEllipsizeMode, titleNumberOfLines, toICalendar, toZonedTime, useDateRange, useMonthGrid, viewDayCount, weekdayFormatToken, weekdayFormatToken as weekdayFormatToken$1 } from "@super-calendar/core";
7
6
  import { Platform, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View, useWindowDimensions } from "react-native";
8
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { LegendList } from "@legendapp/list/react-native";
9
9
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
10
+ //#region src/components/YearView.tsx
11
+ const FALLBACK_COLUMNS = 3;
12
+ function YearViewInner({ date, events, weekStartsOn, locale, activeDate, minMonthWidth = 150, onPressDay, onPressMonth, classNames, styles: styleOverrides }) {
13
+ const theme = useCalendarTheme();
14
+ const slot = createSlots({
15
+ classNames,
16
+ styles: styleOverrides
17
+ });
18
+ const [columns, setColumns] = useState(FALLBACK_COLUMNS);
19
+ const handleLayout = (event) => {
20
+ const width = event.nativeEvent.layout.width;
21
+ if (width <= 0) return;
22
+ setColumns(Math.max(2, Math.min(4, Math.floor(width / minMonthWidth))));
23
+ };
24
+ const months = useMemo(() => getYearMonths(date), [date]);
25
+ const weekdayLabels = useMemo(() => getWeekDays$1(date, weekStartsOn).map((d) => format(d, weekdayFormatToken$1("narrow"), { locale })), [
26
+ date,
27
+ weekStartsOn,
28
+ locale
29
+ ]);
30
+ const eventDays = useMemo(() => new Set(events && events.length > 0 ? groupEventsByDay(events).keys() : []), [events]);
31
+ return /* @__PURE__ */ jsx(ScrollView, {
32
+ onLayout: handleLayout,
33
+ showsVerticalScrollIndicator: true,
34
+ children: /* @__PURE__ */ jsx(View, {
35
+ ...slot("grid", { base: styles$5.grid }),
36
+ children: months.map((month) => {
37
+ const weeks = buildMonthWeeks$1(month, weekStartsOn);
38
+ const title = format(month, "MMMM", { locale });
39
+ const monthLabel = format(month, "MMMM yyyy", { locale });
40
+ const titleText = /* @__PURE__ */ jsx(Text, {
41
+ ...slot("monthTitle", {
42
+ base: styles$5.monthTitle,
43
+ themed: [styles$5.monthTitleText, { color: theme.colors.todayBackground }]
44
+ }),
45
+ allowFontScaling: false,
46
+ children: title
47
+ });
48
+ return /* @__PURE__ */ jsxs(View, {
49
+ ...slot("month", { base: [styles$5.month, { width: `${100 / columns}%` }] }),
50
+ children: [
51
+ onPressMonth ? /* @__PURE__ */ jsx(Pressable, {
52
+ onPress: () => onPressMonth(month),
53
+ accessibilityRole: "button",
54
+ accessibilityLabel: monthLabel,
55
+ children: titleText
56
+ }) : /* @__PURE__ */ jsx(View, {
57
+ accessible: true,
58
+ accessibilityRole: "header",
59
+ accessibilityLabel: monthLabel,
60
+ children: titleText
61
+ }),
62
+ /* @__PURE__ */ jsx(View, {
63
+ ...slot("weekdays", { base: styles$5.weekdays }),
64
+ accessibilityElementsHidden: true,
65
+ importantForAccessibility: "no-hide-descendants",
66
+ children: weekdayLabels.map((label, i) => /* @__PURE__ */ jsx(Text, {
67
+ ...slot("weekday", {
68
+ base: styles$5.weekday,
69
+ themed: [styles$5.weekdayText, { color: theme.colors.textMuted }]
70
+ }),
71
+ allowFontScaling: false,
72
+ children: label
73
+ }, i))
74
+ }),
75
+ weeks.map((week) => /* @__PURE__ */ jsx(View, {
76
+ ...slot("week", { base: styles$5.week }),
77
+ children: week.map((day) => {
78
+ if (day.getMonth() !== month.getMonth()) return /* @__PURE__ */ jsx(View, { style: styles$5.day }, day.toISOString());
79
+ const isHighlighted = activeDate ? isSameCalendarDay$1(day, activeDate) : getIsToday$1(day);
80
+ const hasEvents = eventDays.has(startOfDay(day).toISOString());
81
+ const label = `${format(day, "EEEE, d LLLL yyyy", { locale })}${getIsToday$1(day) ? ", today" : ""}${hasEvents ? ", has events" : ""}`;
82
+ const daySlot = slot("day", {
83
+ base: styles$5.day,
84
+ themed: isHighlighted ? {
85
+ backgroundColor: theme.colors.todayBackground,
86
+ borderRadius: theme.todayBadgeRadius
87
+ } : void 0
88
+ });
89
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, {
90
+ ...slot("dayText", { themed: [styles$5.dayText, { color: isHighlighted ? theme.colors.todayText : theme.colors.text }] }),
91
+ allowFontScaling: false,
92
+ children: day.getDate()
93
+ }), hasEvents ? /* @__PURE__ */ jsx(View, { ...slot("eventDot", {
94
+ base: styles$5.eventDot,
95
+ themed: { backgroundColor: isHighlighted ? theme.colors.todayText : theme.colors.todayBackground }
96
+ }) }) : null] });
97
+ return onPressDay ? /* @__PURE__ */ jsx(Pressable, {
98
+ onPress: () => onPressDay(day),
99
+ accessibilityRole: "button",
100
+ accessibilityLabel: label,
101
+ ...daySlot,
102
+ children: content
103
+ }, day.toISOString()) : /* @__PURE__ */ jsx(View, {
104
+ accessible: true,
105
+ accessibilityLabel: label,
106
+ ...daySlot,
107
+ children: content
108
+ }, day.toISOString());
109
+ })
110
+ }, week[0].toISOString()))
111
+ ]
112
+ }, month.toISOString());
113
+ })
114
+ })
115
+ });
116
+ }
117
+ /**
118
+ * A year at a glance: the twelve months as compact mini grids, with today
119
+ * highlighted and a dot under days that hold events. Tap a day or a month
120
+ * title to drill into a denser view. It's the view `Calendar` renders for
121
+ * `mode="year"`.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * import { YearView } from "@super-calendar/native";
126
+ *
127
+ * <YearView
128
+ * date={new Date()}
129
+ * events={events}
130
+ * weekStartsOn={1}
131
+ * onPressDay={(day) => console.log(day)}
132
+ * />
133
+ * ```
134
+ */
135
+ const YearView = memo(YearViewInner);
136
+ const styles$5 = StyleSheet.create({
137
+ grid: {
138
+ flexDirection: "row",
139
+ flexWrap: "wrap"
140
+ },
141
+ month: { padding: 8 },
142
+ monthTitle: { paddingBottom: 4 },
143
+ monthTitleText: {
144
+ fontSize: 13,
145
+ fontWeight: "700"
146
+ },
147
+ weekdays: { flexDirection: "row" },
148
+ weekday: {
149
+ flex: 1,
150
+ textAlign: "center"
151
+ },
152
+ weekdayText: {
153
+ fontSize: 9,
154
+ fontWeight: "600"
155
+ },
156
+ week: { flexDirection: "row" },
157
+ day: {
158
+ flex: 1,
159
+ aspectRatio: 1,
160
+ alignItems: "center",
161
+ justifyContent: "center"
162
+ },
163
+ dayText: { fontSize: 11 },
164
+ eventDot: {
165
+ position: "absolute",
166
+ bottom: 1,
167
+ width: 3,
168
+ height: 3,
169
+ borderRadius: 2
170
+ }
171
+ });
172
+ //#endregion
10
173
  //#region src/components/Agenda.tsx
11
174
  /**
12
175
  * A vertical, day-grouped list of events (no time grid). Events are sorted by
@@ -378,7 +541,7 @@ const styles$2 = StyleSheet.create({
378
541
  });
379
542
  //#endregion
380
543
  //#region src/components/TimeGrid.tsx
381
- const isWeb = Platform.OS === "web";
544
+ const isWeb$1 = Platform.OS === "web";
382
545
  const MINUTES_PER_HOUR$1 = 60;
383
546
  const HOURS_PER_DAY = 24;
384
547
  const MINUTES_PER_DAY = MINUTES_PER_HOUR$1 * HOURS_PER_DAY;
@@ -393,7 +556,7 @@ const MIN_EVENT_HEIGHT = 32;
393
556
  const EVENT_GAP = 2;
394
557
  const DRAG_ACTIVATE_MS = 300;
395
558
  const MOVE_ACTIVATE_MS$1 = 500;
396
- const DRAG_ACTIVATE_PX = 8;
559
+ const DRAG_ACTIVATE_PX$1 = 8;
397
560
  const RESIZE_HANDLE_HEIGHT = 14;
398
561
  const DEFAULT_DRAG_STEP_MINUTES = 15;
399
562
  const HOUR_LABEL_TOP_INSET = 12;
@@ -492,7 +655,7 @@ function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, dayWid
492
655
  const totalDelta = minuteDelta + dayDelta * MINUTES_PER_DAY;
493
656
  runOnJS(commitDrag)(totalDelta, totalDelta);
494
657
  });
495
- return isWeb ? pan.activeOffsetX([-8, DRAG_ACTIVATE_PX]).activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS$1);
658
+ return isWeb$1 ? pan.activeOffsetX([-8, DRAG_ACTIVATE_PX$1]).activeOffsetY([-8, DRAG_ACTIVATE_PX$1]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS$1);
496
659
  }, [
497
660
  draggable,
498
661
  snapMinutes,
@@ -708,14 +871,14 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
708
871
  },
709
872
  onEndDrag: (event) => {
710
873
  isDragging.value = false;
711
- if (!isWeb && isActiveShared.value) {
874
+ if (!isWeb$1 && isActiveShared.value) {
712
875
  scrollY.value = event.contentOffset.y;
713
876
  runOnJS(onSettleOffset)(event.contentOffset.y);
714
877
  }
715
878
  },
716
879
  onMomentumEnd: (event) => {
717
880
  isDragging.value = false;
718
- if (!isWeb && isActiveShared.value) {
881
+ if (!isWeb$1 && isActiveShared.value) {
719
882
  scrollY.value = event.contentOffset.y;
720
883
  runOnJS(onSettleOffset)(event.contentOffset.y);
721
884
  }
@@ -725,7 +888,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
725
888
  if (!isActiveShared.value && current !== previous) scrollTo(scrollRef, 0, current, false);
726
889
  });
727
890
  useAnimatedReaction(() => isActiveShared.value, (active, previous) => {
728
- if (isWeb || !active || active === previous) return;
891
+ if (isWeb$1 || !active || active === previous) return;
729
892
  scrollTo(scrollRef, 0, scrollY.value, false);
730
893
  });
731
894
  const days = useMemo(() => getViewDays$1(mode, date, weekStartsOn, numberOfDays, isRTL, weekEndsOn), [
@@ -848,7 +1011,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
848
1011
  if (createCancelled.value) return;
849
1012
  runOnJS(commitCreate)(createStartY.value, createStartY.value + event.translationY, createDayIndex.value);
850
1013
  });
851
- return isWeb ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(DRAG_ACTIVATE_MS);
1014
+ return isWeb$1 ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX$1]) : pan.activateAfterLongPress(DRAG_ACTIVATE_MS);
852
1015
  }, [
853
1016
  createEnabled,
854
1017
  dayCount,
@@ -867,7 +1030,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
867
1030
  createCancelled
868
1031
  ]);
869
1032
  const backgroundGesture = useMemo(() => {
870
- const tap = isWeb && onPressCell != null ? Gesture.Tap().onEnd((event) => {
1033
+ const tap = isWeb$1 && onPressCell != null ? Gesture.Tap().onEnd((event) => {
871
1034
  runOnJS(tapCell)(event.x, event.y);
872
1035
  }) : null;
873
1036
  if (createEnabled && tap) return Gesture.Exclusive(createGesture, tap);
@@ -880,7 +1043,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
880
1043
  tapCell
881
1044
  ]);
882
1045
  useEffect(() => {
883
- if (!isWeb || !createEnabled) return;
1046
+ if (!isWeb$1 || !createEnabled) return;
884
1047
  const doc = globalThis.document;
885
1048
  if (!doc) return;
886
1049
  const handler = (event) => {
@@ -1086,7 +1249,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1086
1249
  offsetSeedRef.current = y;
1087
1250
  }, []);
1088
1251
  const committedCellHeight = useSharedValue(hourHeight);
1089
- useWebGridZoom(isWeb, containerRef, cellHeight, committedCellHeight, minHourHeight, maxHourHeight);
1252
+ useWebGridZoom(isWeb$1, containerRef, cellHeight, committedCellHeight, minHourHeight, maxHourHeight);
1090
1253
  const [anchorDate] = useState(date);
1091
1254
  const anchor = useMemo(() => weekAnchored ? startOfWeek(anchorDate, { weekStartsOn }) : startOfDay(anchorDate), [
1092
1255
  weekAnchored,
@@ -1116,7 +1279,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1116
1279
  weekEndsOn
1117
1280
  ]);
1118
1281
  const handleViewableItemsChanged = useCallback((info) => {
1119
- if (isWeb) return;
1282
+ if (isWeb$1) return;
1120
1283
  const settled = info.viewableItems.find((token) => token.isViewable);
1121
1284
  if (settled?.index == null) return;
1122
1285
  if (pendingScrollIndexRef.current != null) {
@@ -1140,7 +1303,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1140
1303
  });
1141
1304
  }, [activeIndex]);
1142
1305
  useEffect(() => {
1143
- if (!isWeb) return;
1306
+ if (!isWeb$1) return;
1144
1307
  const root = containerRef.current;
1145
1308
  if (!root) return;
1146
1309
  const restoreVisiblePage = () => {
@@ -1167,7 +1330,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1167
1330
  };
1168
1331
  }, [activeIndex, pageHeight]);
1169
1332
  useEffect(() => {
1170
- if (!isWeb) return;
1333
+ if (!isWeb$1) return;
1171
1334
  const root = containerRef.current;
1172
1335
  if (!root) return;
1173
1336
  const onScrollCapture = (event) => {
@@ -1184,7 +1347,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1184
1347
  return () => root.removeEventListener("scroll", onScrollCapture, true);
1185
1348
  }, []);
1186
1349
  useEffect(() => {
1187
- if (!isWeb) return;
1350
+ if (!isWeb$1) return;
1188
1351
  const root = containerRef.current;
1189
1352
  if (!root) return;
1190
1353
  const lockHorizontal = () => {
@@ -1350,14 +1513,14 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1350
1513
  },
1351
1514
  children: /* @__PURE__ */ jsx(LegendList, {
1352
1515
  ref: listRef,
1353
- style: isWeb ? [styles$1.pagerList, styles$1.webNoScroll] : styles$1.pagerList,
1516
+ style: isWeb$1 ? [styles$1.pagerList, styles$1.webNoScroll] : styles$1.pagerList,
1354
1517
  data: pageDates,
1355
1518
  extraData: listExtraData,
1356
1519
  horizontal: true,
1357
1520
  recycleItems: false,
1358
1521
  keyExtractor: keyExtractorList,
1359
1522
  getFixedItemSize,
1360
- ...isWeb ? null : {
1523
+ ...isWeb$1 ? null : {
1361
1524
  scrollEnabled: swipeEnabled,
1362
1525
  pagingEnabled: !freeSwipe,
1363
1526
  snapToIndices: freeSwipe ? snapToIndices : void 0
@@ -1565,11 +1728,13 @@ const styles$1 = StyleSheet.create({
1565
1728
  const defaultKeyExtractor = (event) => `${event.start.toISOString()}|${event.end.toISOString()}|${event.title ?? ""}`;
1566
1729
  function visibleRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
1567
1730
  if (mode === "month") return [startOfWeek(startOfMonth(date), { weekStartsOn }), endOfWeek(endOfMonth(date), { weekStartsOn })];
1731
+ if (mode === "year") return [startOfWeek(startOfYear(date), { weekStartsOn }), endOfWeek(endOfYear(date), { weekStartsOn })];
1568
1732
  const days = getViewDays$1(mode, date, weekStartsOn, numberOfDays, false, weekEndsOn);
1569
1733
  return [startOfDay(days[0]), endOfDay(days[days.length - 1])];
1570
1734
  }
1571
1735
  function expansionRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
1572
1736
  if (mode === "month") return [startOfWeek(startOfMonth(addMonths(date, -1)), { weekStartsOn }), addWeeks(endOfWeek(endOfMonth(addMonths(date, 1)), { weekStartsOn }), 1)];
1737
+ if (mode === "year") return [startOfWeek(startOfYear(date), { weekStartsOn }), endOfWeek(endOfYear(date), { weekStartsOn })];
1573
1738
  if (mode === "schedule") return [startOfDay(date), endOfDay(addMonths(date, 3))];
1574
1739
  const days = getViewDays$1(mode, date, weekStartsOn, numberOfDays, false, weekEndsOn);
1575
1740
  const span = days.length;
@@ -1601,7 +1766,7 @@ function expansionRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
1601
1766
  * }
1602
1767
  * ```
1603
1768
  */
1604
- function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPressEvent, onLongPressEvent, onDragEvent, onDragStart, dragStepMinutes, showDragHandle, onPressDay, onLongPressDay, onPressMore, onPressCell, resetPageOnPressCell, onLongPressCell, onCreateEvent, onPressDateHeader, maxVisibleEventCount, sortedMonthView, moreLabel, showAdjacentMonths, disableMonthEventCellPress, weekStartsOn = 0, weekdayFormat, numberOfDays, weekEndsOn, renderEvent = DefaultEvent, eventAccessibilityLabel, eventCellStyle, calendarCellStyle, businessHours, keyExtractor = defaultKeyExtractor, theme, cellHeight: cellHeightProp, hourHeight = 48, minHourHeight, maxHourHeight, hourColumnWidth, hideHours, timeslots, showAllDayEventCell, showWeekNumber, weekNumberPrefix, hourComponent, showSixWeeks, swipeEnabled, showVerticalScrollIndicator, verticalScrollEnabled, headerComponent, minHour, maxHour, ampm, showTime, ellipsizeTitle, allDayLabel, scrollOffsetMinutes, showNowIndicator, locale, timeZone, activeDate, isRTL, freeSwipe, renderTimeGridHeader, renderHeaderForMonthView, renderCustomDateForMonth, itemSeparatorComponent, classNames, styles: styleOverrides }) {
1769
+ function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPressEvent, onLongPressEvent, onDragEvent, onDragStart, dragStepMinutes, showDragHandle, onPressDay, onPressMonth, onLongPressDay, onPressMore, onPressCell, resetPageOnPressCell, onLongPressCell, onCreateEvent, onPressDateHeader, maxVisibleEventCount, sortedMonthView, moreLabel, showAdjacentMonths, disableMonthEventCellPress, weekStartsOn = 0, weekdayFormat, numberOfDays, weekEndsOn, renderEvent = DefaultEvent, eventAccessibilityLabel, eventCellStyle, calendarCellStyle, businessHours, keyExtractor = defaultKeyExtractor, theme, cellHeight: cellHeightProp, hourHeight = 48, minHourHeight, maxHourHeight, hourColumnWidth, hideHours, timeslots, showAllDayEventCell, showWeekNumber, weekNumberPrefix, hourComponent, showSixWeeks, swipeEnabled, showVerticalScrollIndicator, verticalScrollEnabled, headerComponent, minHour, maxHour, ampm, showTime, ellipsizeTitle, allDayLabel, scrollOffsetMinutes, showNowIndicator, locale, timeZone, activeDate, isRTL, freeSwipe, renderTimeGridHeader, renderHeaderForMonthView, renderCustomDateForMonth, itemSeparatorComponent, classNames, styles: styleOverrides }) {
1605
1770
  const mergedTheme = useMemo(() => mergeTheme(theme), [theme]);
1606
1771
  const internalCellHeight = useSharedValue(hourHeight);
1607
1772
  const cellHeight = cellHeightProp ?? internalCellHeight;
@@ -1702,6 +1867,16 @@ function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPress
1702
1867
  swipeEnabled,
1703
1868
  classNames,
1704
1869
  styles: styleOverrides
1870
+ }) : mode === "year" ? /* @__PURE__ */ jsx(YearView, {
1871
+ date,
1872
+ events: displayEvents,
1873
+ weekStartsOn,
1874
+ locale,
1875
+ activeDate,
1876
+ onPressDay,
1877
+ onPressMonth,
1878
+ classNames,
1879
+ styles: styleOverrides
1705
1880
  }) : mode === "schedule" ? /* @__PURE__ */ jsx(Agenda, {
1706
1881
  events: displayEvents,
1707
1882
  locale,
@@ -1771,7 +1946,9 @@ function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPress
1771
1946
  //#endregion
1772
1947
  //#region src/components/ResourceTimeline.tsx
1773
1948
  const MOVE_ACTIVATE_MS = 250;
1949
+ const DRAG_ACTIVATE_PX = 8;
1774
1950
  const MINUTES_PER_HOUR = 60;
1951
+ const isWeb = Platform.OS === "web";
1775
1952
  const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
1776
1953
  const GUTTER_WIDTH = 56;
1777
1954
  function DefaultBar({ event, width, height }) {
@@ -1804,7 +1981,7 @@ function DefaultBar({ event, width, height }) {
1804
1981
  }) : null]
1805
1982
  });
1806
1983
  }
1807
- function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendererSize, snapMinutes, Renderer, onPress, onDragEvent, theme }) {
1984
+ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendererSize, snapMinutes, Renderer, onPress, onLongPress, onDragEvent, theme }) {
1808
1985
  const moveX = useSharedValue(0);
1809
1986
  const resizeW = useSharedValue(0);
1810
1987
  const latest = useRef({
@@ -1935,6 +2112,7 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1935
2112
  gesture: moveGesture,
1936
2113
  children: /* @__PURE__ */ jsx(Pressable, {
1937
2114
  onPress,
2115
+ onLongPress,
1938
2116
  accessibilityRole: "button",
1939
2117
  accessibilityLabel: pe.event.title,
1940
2118
  accessibilityActions: barActions,
@@ -1958,6 +2136,115 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1958
2136
  })]
1959
2137
  });
1960
2138
  }
2139
+ function LaneInteractionLayer({ resource, vertical, hourSize, startHour, date, snapMinutes, onPressCell, onLongPressCell, onCreateEvent, theme }) {
2140
+ const ghostStart = useSharedValue(0);
2141
+ const ghostSize = useSharedValue(0);
2142
+ const ghostVisible = useSharedValue(0);
2143
+ const timeAt = useCallback((px) => cellRangeFromDrag(date, px, px, hourSize, startHour, snapMinutes)?.start, [
2144
+ date,
2145
+ hourSize,
2146
+ startHour,
2147
+ snapMinutes
2148
+ ]);
2149
+ const commitCreate = useCallback((fromPx, toPx) => {
2150
+ const range = cellRangeFromDrag(date, fromPx, toPx, hourSize, startHour, snapMinutes);
2151
+ if (range) onCreateEvent?.(range.start, range.end, resource);
2152
+ }, [
2153
+ date,
2154
+ hourSize,
2155
+ startHour,
2156
+ snapMinutes,
2157
+ onCreateEvent,
2158
+ resource
2159
+ ]);
2160
+ const pressAt = useCallback((px) => {
2161
+ const at = timeAt(px);
2162
+ if (at) onPressCell?.(at, resource);
2163
+ }, [
2164
+ timeAt,
2165
+ onPressCell,
2166
+ resource
2167
+ ]);
2168
+ const laneGesture = useMemo(() => {
2169
+ const pan = onCreateEvent ? Gesture.Pan().onStart((e) => {
2170
+ ghostStart.value = vertical ? e.y : e.x;
2171
+ ghostSize.value = 0;
2172
+ ghostVisible.value = 1;
2173
+ }).onUpdate((e) => {
2174
+ ghostSize.value = (vertical ? e.y : e.x) - ghostStart.value;
2175
+ }).onEnd((e, success) => {
2176
+ ghostVisible.value = 0;
2177
+ if (success) runOnJS(commitCreate)(ghostStart.value, vertical ? e.y : e.x);
2178
+ }).onFinalize(() => {
2179
+ ghostVisible.value = 0;
2180
+ }) : null;
2181
+ const activatedPan = pan ? isWeb ? vertical ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activeOffsetX([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS) : null;
2182
+ const tap = isWeb && onPressCell ? Gesture.Tap().onEnd((e) => {
2183
+ runOnJS(pressAt)(vertical ? e.y : e.x);
2184
+ }) : null;
2185
+ if (activatedPan && tap) return Gesture.Exclusive(activatedPan, tap);
2186
+ return activatedPan ?? tap;
2187
+ }, [
2188
+ onCreateEvent,
2189
+ onPressCell,
2190
+ vertical,
2191
+ commitCreate,
2192
+ pressAt,
2193
+ ghostStart,
2194
+ ghostSize,
2195
+ ghostVisible
2196
+ ]);
2197
+ const ghostStyle = useAnimatedStyle(() => {
2198
+ const stepPx = snapMinutes / MINUTES_PER_HOUR * hourSize;
2199
+ const snap = (v) => stepPx > 0 ? Math.round(v / stepPx) * stepPx : v;
2200
+ const a = snap(ghostStart.value);
2201
+ const b = snap(ghostStart.value + ghostSize.value);
2202
+ const from = Math.min(a, b);
2203
+ const size = Math.max(Math.abs(b - a), 2);
2204
+ return vertical ? {
2205
+ opacity: ghostVisible.value,
2206
+ top: from,
2207
+ height: size
2208
+ } : {
2209
+ opacity: ghostVisible.value,
2210
+ left: from,
2211
+ width: size
2212
+ };
2213
+ }, [
2214
+ vertical,
2215
+ hourSize,
2216
+ snapMinutes
2217
+ ]);
2218
+ const pxOf = (e) => vertical ? e.nativeEvent.locationY : e.nativeEvent.locationX;
2219
+ const handlePress = onPressCell ? (e) => pressAt(pxOf(e)) : void 0;
2220
+ const handleLongPress = onLongPressCell && !onCreateEvent ? (e) => {
2221
+ const at = timeAt(pxOf(e));
2222
+ if (at) onLongPressCell(at, resource);
2223
+ } : void 0;
2224
+ const layer = /* @__PURE__ */ jsx(Pressable, {
2225
+ testID: "resource-cell-layer",
2226
+ style: StyleSheet.absoluteFill,
2227
+ onPress: handlePress,
2228
+ onLongPress: handleLongPress,
2229
+ accessibilityElementsHidden: true,
2230
+ importantForAccessibility: "no"
2231
+ });
2232
+ return /* @__PURE__ */ jsxs(Fragment, { children: [laneGesture ? /* @__PURE__ */ jsx(GestureDetector, {
2233
+ gesture: laneGesture,
2234
+ children: layer
2235
+ }) : layer, onCreateEvent ? /* @__PURE__ */ jsx(Animated.View, {
2236
+ testID: "resource-create-ghost",
2237
+ pointerEvents: "none",
2238
+ style: [
2239
+ vertical ? styles.vcreateGhost : styles.hcreateGhost,
2240
+ {
2241
+ backgroundColor: theme.colors.eventBackground,
2242
+ borderColor: theme.colors.todayBackground
2243
+ },
2244
+ ghostStyle
2245
+ ]
2246
+ }) : null] });
2247
+ }
1961
2248
  /**
1962
2249
  * A horizontal resource timeline: rows are resources (rooms, people, machines)
1963
2250
  * and the x-axis is one day's hours. Events sit in their resource's row, and
@@ -1976,9 +2263,11 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1976
2263
  * />
1977
2264
  * ```
1978
2265
  */
1979
- 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, renderEvent, onPressEvent, onDragEvent, dragStepMinutes = 15 }) {
2266
+ 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, renderEvent, onPressEvent, onLongPressEvent, onDragEvent, onPressCell, onLongPressCell, onCreateEvent, businessHours, dragStepMinutes = 15 }) {
1980
2267
  const theme = useCalendarTheme();
1981
2268
  const Renderer = renderEvent ?? DefaultBar;
2269
+ const cellEnabled = !!onPressCell || !!onLongPressCell || !!onCreateEvent;
2270
+ const bandsFor = (resource) => businessHours ? closedHourBands(date, (d) => businessHours(d, resource), startHour, endHour) : [];
1982
2271
  const hours = useMemo(() => Array.from({ length: Math.max(0, endHour - startHour) }, (_, i) => startHour + i), [startHour, endHour]);
1983
2272
  const trackWidth = (endHour - startHour) * hourWidth;
1984
2273
  const byResource = useMemo(() => {
@@ -2031,58 +2320,84 @@ function ResourceTimeline({ date, orientation = "horizontal", resources, events,
2031
2320
  { borderLeftColor: theme.colors.gridLine },
2032
2321
  theme.containers.resourceRow
2033
2322
  ],
2034
- children: [hours.slice(1).map((h) => /* @__PURE__ */ jsx(View, {
2035
- pointerEvents: "none",
2036
- style: [styles.vgridLine, {
2037
- top: (h - startHour) * hourHeight,
2038
- backgroundColor: theme.colors.gridLine
2039
- }]
2040
- }, h)), positioned.map((pe, idx) => {
2041
- const top = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourHeight;
2042
- const bottomPx = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourHeight;
2043
- const height = Math.max(bottomPx - top, 2);
2044
- const lanePct = 100 / pe.columns;
2045
- const left = `${pe.column * lanePct}%`;
2046
- const width = `${lanePct}%`;
2047
- const onPress = () => onPressEvent?.(pe.event);
2048
- if (onDragEvent) return /* @__PURE__ */ jsx(ResourceBar, {
2049
- pe,
2323
+ children: [
2324
+ bandsFor(resource).map((b) => /* @__PURE__ */ jsx(View, {
2325
+ pointerEvents: "none",
2326
+ testID: "resource-hours-shade",
2327
+ style: [styles.vshadeBand, {
2328
+ top: (b.start - startHour) * hourHeight,
2329
+ height: (b.end - b.start) * hourHeight,
2330
+ backgroundColor: theme.colors.outsideHoursBackground
2331
+ }]
2332
+ }, `shade-${b.start}`)),
2333
+ hours.slice(1).map((h) => /* @__PURE__ */ jsx(View, {
2334
+ pointerEvents: "none",
2335
+ style: [styles.vgridLine, {
2336
+ top: (h - startHour) * hourHeight,
2337
+ backgroundColor: theme.colors.gridLine
2338
+ }]
2339
+ }, h)),
2340
+ cellEnabled ? /* @__PURE__ */ jsx(LaneInteractionLayer, {
2341
+ resource,
2050
2342
  vertical: true,
2051
2343
  hourSize: hourHeight,
2052
- left,
2053
- top,
2054
- width,
2055
- height,
2056
- rendererSize: {
2057
- width: 0,
2058
- height
2059
- },
2344
+ startHour,
2345
+ date,
2060
2346
  snapMinutes: dragStepMinutes,
2061
- Renderer,
2062
- onPress,
2063
- onDragEvent,
2347
+ onPressCell,
2348
+ onLongPressCell,
2349
+ onCreateEvent,
2064
2350
  theme
2065
- }, idx);
2066
- return /* @__PURE__ */ jsx(Pressable, {
2067
- onPress,
2068
- accessibilityRole: "button",
2069
- accessibilityLabel: pe.event.title,
2070
- style: {
2071
- position: "absolute",
2351
+ }) : null,
2352
+ positioned.map((pe, idx) => {
2353
+ const top = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourHeight;
2354
+ const bottomPx = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourHeight;
2355
+ const height = Math.max(bottomPx - top, 2);
2356
+ const lanePct = 100 / pe.columns;
2357
+ const left = `${pe.column * lanePct}%`;
2358
+ const width = `${lanePct}%`;
2359
+ const onPress = () => onPressEvent?.(pe.event);
2360
+ if (onDragEvent) return /* @__PURE__ */ jsx(ResourceBar, {
2361
+ pe,
2362
+ vertical: true,
2363
+ hourSize: hourHeight,
2072
2364
  left,
2073
- width,
2074
2365
  top,
2366
+ width,
2075
2367
  height,
2076
- padding: 1
2077
- },
2078
- children: /* @__PURE__ */ jsx(Renderer, {
2079
- event: pe.event,
2080
- width: 0,
2081
- height,
2082
- onPress
2083
- })
2084
- }, idx);
2085
- })]
2368
+ rendererSize: {
2369
+ width: 0,
2370
+ height
2371
+ },
2372
+ snapMinutes: dragStepMinutes,
2373
+ Renderer,
2374
+ onPress,
2375
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2376
+ onDragEvent,
2377
+ theme
2378
+ }, idx);
2379
+ return /* @__PURE__ */ jsx(Pressable, {
2380
+ onPress,
2381
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2382
+ accessibilityRole: "button",
2383
+ accessibilityLabel: pe.event.title,
2384
+ style: {
2385
+ position: "absolute",
2386
+ left,
2387
+ width,
2388
+ top,
2389
+ height,
2390
+ padding: 1
2391
+ },
2392
+ children: /* @__PURE__ */ jsx(Renderer, {
2393
+ event: pe.event,
2394
+ width: 0,
2395
+ height,
2396
+ onPress
2397
+ })
2398
+ }, idx);
2399
+ })
2400
+ ]
2086
2401
  }, resource.id);
2087
2402
  })]
2088
2403
  })
@@ -2139,52 +2454,78 @@ function ResourceTimeline({ date, orientation = "horizontal", resources, events,
2139
2454
  })
2140
2455
  }), /* @__PURE__ */ jsxs(View, {
2141
2456
  style: { width: trackWidth },
2142
- children: [hours.slice(1).map((h) => /* @__PURE__ */ jsx(View, {
2143
- pointerEvents: "none",
2144
- style: [styles.gridLine, {
2145
- left: (h - startHour) * hourWidth,
2146
- backgroundColor: theme.colors.gridLine
2147
- }]
2148
- }, h)), positioned.map((pe, idx) => {
2149
- const left = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourWidth;
2150
- const right = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourWidth;
2151
- const width = Math.max(right - left, 2);
2152
- const laneHeight = rowHeight / pe.columns;
2153
- const onPress = () => onPressEvent?.(pe.event);
2154
- if (onDragEvent) return /* @__PURE__ */ jsx(ResourceBar, {
2155
- pe,
2457
+ children: [
2458
+ bandsFor(resource).map((b) => /* @__PURE__ */ jsx(View, {
2459
+ pointerEvents: "none",
2460
+ testID: "resource-hours-shade",
2461
+ style: [styles.hshadeBand, {
2462
+ left: (b.start - startHour) * hourWidth,
2463
+ width: (b.end - b.start) * hourWidth,
2464
+ backgroundColor: theme.colors.outsideHoursBackground
2465
+ }]
2466
+ }, `shade-${b.start}`)),
2467
+ hours.slice(1).map((h) => /* @__PURE__ */ jsx(View, {
2468
+ pointerEvents: "none",
2469
+ style: [styles.gridLine, {
2470
+ left: (h - startHour) * hourWidth,
2471
+ backgroundColor: theme.colors.gridLine
2472
+ }]
2473
+ }, h)),
2474
+ cellEnabled ? /* @__PURE__ */ jsx(LaneInteractionLayer, {
2475
+ resource,
2156
2476
  vertical: false,
2157
2477
  hourSize: hourWidth,
2158
- left,
2159
- top: pe.column * laneHeight,
2160
- width,
2161
- height: laneHeight,
2162
- rendererSize: { width },
2478
+ startHour,
2479
+ date,
2163
2480
  snapMinutes: dragStepMinutes,
2164
- Renderer,
2165
- onPress,
2166
- onDragEvent,
2481
+ onPressCell,
2482
+ onLongPressCell,
2483
+ onCreateEvent,
2167
2484
  theme
2168
- }, idx);
2169
- return /* @__PURE__ */ jsx(Pressable, {
2170
- onPress,
2171
- accessibilityRole: "button",
2172
- accessibilityLabel: pe.event.title,
2173
- style: {
2174
- position: "absolute",
2485
+ }) : null,
2486
+ positioned.map((pe, idx) => {
2487
+ const left = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourWidth;
2488
+ const right = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourWidth;
2489
+ const width = Math.max(right - left, 2);
2490
+ const laneHeight = rowHeight / pe.columns;
2491
+ const onPress = () => onPressEvent?.(pe.event);
2492
+ if (onDragEvent) return /* @__PURE__ */ jsx(ResourceBar, {
2493
+ pe,
2494
+ vertical: false,
2495
+ hourSize: hourWidth,
2175
2496
  left,
2176
- width,
2177
2497
  top: pe.column * laneHeight,
2178
- height: laneHeight,
2179
- padding: 1
2180
- },
2181
- children: /* @__PURE__ */ jsx(Renderer, {
2182
- event: pe.event,
2183
2498
  width,
2184
- onPress
2185
- })
2186
- }, idx);
2187
- })]
2499
+ height: laneHeight,
2500
+ rendererSize: { width },
2501
+ snapMinutes: dragStepMinutes,
2502
+ Renderer,
2503
+ onPress,
2504
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2505
+ onDragEvent,
2506
+ theme
2507
+ }, idx);
2508
+ return /* @__PURE__ */ jsx(Pressable, {
2509
+ onPress,
2510
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2511
+ accessibilityRole: "button",
2512
+ accessibilityLabel: pe.event.title,
2513
+ style: {
2514
+ position: "absolute",
2515
+ left,
2516
+ width,
2517
+ top: pe.column * laneHeight,
2518
+ height: laneHeight,
2519
+ padding: 1
2520
+ },
2521
+ children: /* @__PURE__ */ jsx(Renderer, {
2522
+ event: pe.event,
2523
+ width,
2524
+ onPress
2525
+ })
2526
+ }, idx);
2527
+ })
2528
+ ]
2188
2529
  })]
2189
2530
  }, resource.id);
2190
2531
  })]
@@ -2274,7 +2615,35 @@ const styles = StyleSheet.create({
2274
2615
  left: 0,
2275
2616
  right: 0,
2276
2617
  height: StyleSheet.hairlineWidth
2618
+ },
2619
+ vcreateGhost: {
2620
+ position: "absolute",
2621
+ left: 2,
2622
+ right: 2,
2623
+ borderWidth: 1,
2624
+ borderRadius: 6,
2625
+ opacity: 0,
2626
+ zIndex: 2
2627
+ },
2628
+ hcreateGhost: {
2629
+ position: "absolute",
2630
+ top: 2,
2631
+ bottom: 2,
2632
+ borderWidth: 1,
2633
+ borderRadius: 6,
2634
+ opacity: 0,
2635
+ zIndex: 2
2636
+ },
2637
+ hshadeBand: {
2638
+ position: "absolute",
2639
+ top: 0,
2640
+ bottom: 0
2641
+ },
2642
+ vshadeBand: {
2643
+ position: "absolute",
2644
+ left: 0,
2645
+ right: 0
2277
2646
  }
2278
2647
  });
2279
2648
  //#endregion
2280
- export { Agenda, Calendar, CalendarThemeProvider, DEFAULT_HOUR_HEIGHT, DefaultEvent, DefaultMonthEvent, MonthList, MonthPager, MonthView, ResourceTimeline, TimeGrid, buildMonthGrid, buildMonthWeeks, darkTheme, daySelectionState, defaultTheme, eventsInTimeZone, expandRecurringEvents, getIsToday, getViewDays, getWeekDays, isAllDayEvent, isDateSelectable, isRangeEndpoint, isSameCalendarDay, isWeekend, isWithinDateRange, layoutDayEvents, mergeTheme, minutesIntoDay, nextDateRange, parseICalendar, toICalendar, toZonedTime, useCalendarTheme, useDateRange, useMonthGrid, weekdayFormatToken };
2649
+ export { Agenda, Calendar, CalendarThemeProvider, DEFAULT_HOUR_HEIGHT, DefaultEvent, DefaultMonthEvent, MonthList, MonthPager, MonthView, ResourceTimeline, TimeGrid, YearView, buildMonthGrid, buildMonthWeeks, darkTheme, daySelectionState, defaultTheme, eventsInTimeZone, expandRecurringEvents, getIsToday, getViewDays, getWeekDays, isAllDayEvent, isDateSelectable, isRangeEndpoint, isSameCalendarDay, isWeekend, isWithinDateRange, layoutDayEvents, mergeTheme, minutesIntoDay, nextDateRange, parseICalendar, toICalendar, toZonedTime, useCalendarTheme, useDateRange, useMonthGrid, weekdayFormatToken };