@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.js CHANGED
@@ -21,16 +21,179 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- const require_MonthList = require("./MonthList-Aczb2RSY.js");
24
+ const require_MonthList = require("./MonthList-Gu6p64fE.js");
25
25
  let date_fns = require("date-fns");
26
26
  let react = require("react");
27
27
  let react_native_reanimated = require("react-native-reanimated");
28
28
  react_native_reanimated = __toESM(react_native_reanimated);
29
29
  let _super_calendar_core = require("@super-calendar/core");
30
- let _legendapp_list_react_native = require("@legendapp/list/react-native");
31
30
  let react_native = require("react-native");
32
31
  let react_jsx_runtime = require("react/jsx-runtime");
32
+ let _legendapp_list_react_native = require("@legendapp/list/react-native");
33
33
  let react_native_gesture_handler = require("react-native-gesture-handler");
34
+ //#region src/components/YearView.tsx
35
+ const FALLBACK_COLUMNS = 3;
36
+ function YearViewInner({ date, events, weekStartsOn, locale, activeDate, minMonthWidth = 150, onPressDay, onPressMonth, classNames, styles: styleOverrides }) {
37
+ const theme = require_MonthList.useCalendarTheme();
38
+ const slot = require_MonthList.createSlots({
39
+ classNames,
40
+ styles: styleOverrides
41
+ });
42
+ const [columns, setColumns] = (0, react.useState)(FALLBACK_COLUMNS);
43
+ const handleLayout = (event) => {
44
+ const width = event.nativeEvent.layout.width;
45
+ if (width <= 0) return;
46
+ setColumns(Math.max(2, Math.min(4, Math.floor(width / minMonthWidth))));
47
+ };
48
+ const months = (0, react.useMemo)(() => (0, _super_calendar_core.getYearMonths)(date), [date]);
49
+ const weekdayLabels = (0, react.useMemo)(() => (0, _super_calendar_core.getWeekDays)(date, weekStartsOn).map((d) => (0, date_fns.format)(d, (0, _super_calendar_core.weekdayFormatToken)("narrow"), { locale })), [
50
+ date,
51
+ weekStartsOn,
52
+ locale
53
+ ]);
54
+ const eventDays = (0, react.useMemo)(() => new Set(events && events.length > 0 ? (0, _super_calendar_core.groupEventsByDay)(events).keys() : []), [events]);
55
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.ScrollView, {
56
+ onLayout: handleLayout,
57
+ showsVerticalScrollIndicator: true,
58
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
59
+ ...slot("grid", { base: styles$5.grid }),
60
+ children: months.map((month) => {
61
+ const weeks = (0, _super_calendar_core.buildMonthWeeks)(month, weekStartsOn);
62
+ const title = (0, date_fns.format)(month, "MMMM", { locale });
63
+ const monthLabel = (0, date_fns.format)(month, "MMMM yyyy", { locale });
64
+ const titleText = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
65
+ ...slot("monthTitle", {
66
+ base: styles$5.monthTitle,
67
+ themed: [styles$5.monthTitleText, { color: theme.colors.todayBackground }]
68
+ }),
69
+ allowFontScaling: false,
70
+ children: title
71
+ });
72
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
73
+ ...slot("month", { base: [styles$5.month, { width: `${100 / columns}%` }] }),
74
+ children: [
75
+ onPressMonth ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
76
+ onPress: () => onPressMonth(month),
77
+ accessibilityRole: "button",
78
+ accessibilityLabel: monthLabel,
79
+ children: titleText
80
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
81
+ accessible: true,
82
+ accessibilityRole: "header",
83
+ accessibilityLabel: monthLabel,
84
+ children: titleText
85
+ }),
86
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
87
+ ...slot("weekdays", { base: styles$5.weekdays }),
88
+ accessibilityElementsHidden: true,
89
+ importantForAccessibility: "no-hide-descendants",
90
+ children: weekdayLabels.map((label, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
91
+ ...slot("weekday", {
92
+ base: styles$5.weekday,
93
+ themed: [styles$5.weekdayText, { color: theme.colors.textMuted }]
94
+ }),
95
+ allowFontScaling: false,
96
+ children: label
97
+ }, i))
98
+ }),
99
+ weeks.map((week) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
100
+ ...slot("week", { base: styles$5.week }),
101
+ children: week.map((day) => {
102
+ if (day.getMonth() !== month.getMonth()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { style: styles$5.day }, day.toISOString());
103
+ const isHighlighted = activeDate ? (0, _super_calendar_core.isSameCalendarDay)(day, activeDate) : (0, _super_calendar_core.getIsToday)(day);
104
+ const hasEvents = eventDays.has((0, date_fns.startOfDay)(day).toISOString());
105
+ const label = `${(0, date_fns.format)(day, "EEEE, d LLLL yyyy", { locale })}${(0, _super_calendar_core.getIsToday)(day) ? ", today" : ""}${hasEvents ? ", has events" : ""}`;
106
+ const daySlot = slot("day", {
107
+ base: styles$5.day,
108
+ themed: isHighlighted ? {
109
+ backgroundColor: theme.colors.todayBackground,
110
+ borderRadius: theme.todayBadgeRadius
111
+ } : void 0
112
+ });
113
+ const content = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
114
+ ...slot("dayText", { themed: [styles$5.dayText, { color: isHighlighted ? theme.colors.todayText : theme.colors.text }] }),
115
+ allowFontScaling: false,
116
+ children: day.getDate()
117
+ }), hasEvents ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { ...slot("eventDot", {
118
+ base: styles$5.eventDot,
119
+ themed: { backgroundColor: isHighlighted ? theme.colors.todayText : theme.colors.todayBackground }
120
+ }) }) : null] });
121
+ return onPressDay ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
122
+ onPress: () => onPressDay(day),
123
+ accessibilityRole: "button",
124
+ accessibilityLabel: label,
125
+ ...daySlot,
126
+ children: content
127
+ }, day.toISOString()) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
128
+ accessible: true,
129
+ accessibilityLabel: label,
130
+ ...daySlot,
131
+ children: content
132
+ }, day.toISOString());
133
+ })
134
+ }, week[0].toISOString()))
135
+ ]
136
+ }, month.toISOString());
137
+ })
138
+ })
139
+ });
140
+ }
141
+ /**
142
+ * A year at a glance: the twelve months as compact mini grids, with today
143
+ * highlighted and a dot under days that hold events. Tap a day or a month
144
+ * title to drill into a denser view. It's the view `Calendar` renders for
145
+ * `mode="year"`.
146
+ *
147
+ * @example
148
+ * ```tsx
149
+ * import { YearView } from "@super-calendar/native";
150
+ *
151
+ * <YearView
152
+ * date={new Date()}
153
+ * events={events}
154
+ * weekStartsOn={1}
155
+ * onPressDay={(day) => console.log(day)}
156
+ * />
157
+ * ```
158
+ */
159
+ const YearView = (0, react.memo)(YearViewInner);
160
+ const styles$5 = react_native.StyleSheet.create({
161
+ grid: {
162
+ flexDirection: "row",
163
+ flexWrap: "wrap"
164
+ },
165
+ month: { padding: 8 },
166
+ monthTitle: { paddingBottom: 4 },
167
+ monthTitleText: {
168
+ fontSize: 13,
169
+ fontWeight: "700"
170
+ },
171
+ weekdays: { flexDirection: "row" },
172
+ weekday: {
173
+ flex: 1,
174
+ textAlign: "center"
175
+ },
176
+ weekdayText: {
177
+ fontSize: 9,
178
+ fontWeight: "600"
179
+ },
180
+ week: { flexDirection: "row" },
181
+ day: {
182
+ flex: 1,
183
+ aspectRatio: 1,
184
+ alignItems: "center",
185
+ justifyContent: "center"
186
+ },
187
+ dayText: { fontSize: 11 },
188
+ eventDot: {
189
+ position: "absolute",
190
+ bottom: 1,
191
+ width: 3,
192
+ height: 3,
193
+ borderRadius: 2
194
+ }
195
+ });
196
+ //#endregion
34
197
  //#region src/components/Agenda.tsx
35
198
  /**
36
199
  * A vertical, day-grouped list of events (no time grid). Events are sorted by
@@ -402,7 +565,7 @@ const styles$2 = react_native.StyleSheet.create({
402
565
  });
403
566
  //#endregion
404
567
  //#region src/components/TimeGrid.tsx
405
- const isWeb = react_native.Platform.OS === "web";
568
+ const isWeb$1 = react_native.Platform.OS === "web";
406
569
  const MINUTES_PER_HOUR$1 = 60;
407
570
  const HOURS_PER_DAY = 24;
408
571
  const MINUTES_PER_DAY = MINUTES_PER_HOUR$1 * HOURS_PER_DAY;
@@ -417,7 +580,7 @@ const MIN_EVENT_HEIGHT = 32;
417
580
  const EVENT_GAP = 2;
418
581
  const DRAG_ACTIVATE_MS = 300;
419
582
  const MOVE_ACTIVATE_MS$1 = 500;
420
- const DRAG_ACTIVATE_PX = 8;
583
+ const DRAG_ACTIVATE_PX$1 = 8;
421
584
  const RESIZE_HANDLE_HEIGHT = 14;
422
585
  const DEFAULT_DRAG_STEP_MINUTES = 15;
423
586
  const HOUR_LABEL_TOP_INSET = 12;
@@ -516,7 +679,7 @@ function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, dayWid
516
679
  const totalDelta = minuteDelta + dayDelta * MINUTES_PER_DAY;
517
680
  (0, react_native_reanimated.runOnJS)(commitDrag)(totalDelta, totalDelta);
518
681
  });
519
- return isWeb ? pan.activeOffsetX([-8, DRAG_ACTIVATE_PX]).activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS$1);
682
+ return isWeb$1 ? pan.activeOffsetX([-8, DRAG_ACTIVATE_PX$1]).activeOffsetY([-8, DRAG_ACTIVATE_PX$1]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS$1);
520
683
  }, [
521
684
  draggable,
522
685
  snapMinutes,
@@ -732,14 +895,14 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
732
895
  },
733
896
  onEndDrag: (event) => {
734
897
  isDragging.value = false;
735
- if (!isWeb && isActiveShared.value) {
898
+ if (!isWeb$1 && isActiveShared.value) {
736
899
  scrollY.value = event.contentOffset.y;
737
900
  (0, react_native_reanimated.runOnJS)(onSettleOffset)(event.contentOffset.y);
738
901
  }
739
902
  },
740
903
  onMomentumEnd: (event) => {
741
904
  isDragging.value = false;
742
- if (!isWeb && isActiveShared.value) {
905
+ if (!isWeb$1 && isActiveShared.value) {
743
906
  scrollY.value = event.contentOffset.y;
744
907
  (0, react_native_reanimated.runOnJS)(onSettleOffset)(event.contentOffset.y);
745
908
  }
@@ -749,7 +912,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
749
912
  if (!isActiveShared.value && current !== previous) (0, react_native_reanimated.scrollTo)(scrollRef, 0, current, false);
750
913
  });
751
914
  (0, react_native_reanimated.useAnimatedReaction)(() => isActiveShared.value, (active, previous) => {
752
- if (isWeb || !active || active === previous) return;
915
+ if (isWeb$1 || !active || active === previous) return;
753
916
  (0, react_native_reanimated.scrollTo)(scrollRef, 0, scrollY.value, false);
754
917
  });
755
918
  const days = (0, react.useMemo)(() => (0, _super_calendar_core.getViewDays)(mode, date, weekStartsOn, numberOfDays, isRTL, weekEndsOn), [
@@ -872,7 +1035,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
872
1035
  if (createCancelled.value) return;
873
1036
  (0, react_native_reanimated.runOnJS)(commitCreate)(createStartY.value, createStartY.value + event.translationY, createDayIndex.value);
874
1037
  });
875
- return isWeb ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(DRAG_ACTIVATE_MS);
1038
+ return isWeb$1 ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX$1]) : pan.activateAfterLongPress(DRAG_ACTIVATE_MS);
876
1039
  }, [
877
1040
  createEnabled,
878
1041
  dayCount,
@@ -891,7 +1054,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
891
1054
  createCancelled
892
1055
  ]);
893
1056
  const backgroundGesture = (0, react.useMemo)(() => {
894
- const tap = isWeb && onPressCell != null ? react_native_gesture_handler.Gesture.Tap().onEnd((event) => {
1057
+ const tap = isWeb$1 && onPressCell != null ? react_native_gesture_handler.Gesture.Tap().onEnd((event) => {
895
1058
  (0, react_native_reanimated.runOnJS)(tapCell)(event.x, event.y);
896
1059
  }) : null;
897
1060
  if (createEnabled && tap) return react_native_gesture_handler.Gesture.Exclusive(createGesture, tap);
@@ -904,7 +1067,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
904
1067
  tapCell
905
1068
  ]);
906
1069
  (0, react.useEffect)(() => {
907
- if (!isWeb || !createEnabled) return;
1070
+ if (!isWeb$1 || !createEnabled) return;
908
1071
  const doc = globalThis.document;
909
1072
  if (!doc) return;
910
1073
  const handler = (event) => {
@@ -1110,7 +1273,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1110
1273
  offsetSeedRef.current = y;
1111
1274
  }, []);
1112
1275
  const committedCellHeight = (0, react_native_reanimated.useSharedValue)(hourHeight);
1113
- useWebGridZoom(isWeb, containerRef, cellHeight, committedCellHeight, minHourHeight, maxHourHeight);
1276
+ useWebGridZoom(isWeb$1, containerRef, cellHeight, committedCellHeight, minHourHeight, maxHourHeight);
1114
1277
  const [anchorDate] = (0, react.useState)(date);
1115
1278
  const anchor = (0, react.useMemo)(() => weekAnchored ? (0, date_fns.startOfWeek)(anchorDate, { weekStartsOn }) : (0, date_fns.startOfDay)(anchorDate), [
1116
1279
  weekAnchored,
@@ -1140,7 +1303,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1140
1303
  weekEndsOn
1141
1304
  ]);
1142
1305
  const handleViewableItemsChanged = (0, react.useCallback)((info) => {
1143
- if (isWeb) return;
1306
+ if (isWeb$1) return;
1144
1307
  const settled = info.viewableItems.find((token) => token.isViewable);
1145
1308
  if (settled?.index == null) return;
1146
1309
  if (pendingScrollIndexRef.current != null) {
@@ -1164,7 +1327,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1164
1327
  });
1165
1328
  }, [activeIndex]);
1166
1329
  (0, react.useEffect)(() => {
1167
- if (!isWeb) return;
1330
+ if (!isWeb$1) return;
1168
1331
  const root = containerRef.current;
1169
1332
  if (!root) return;
1170
1333
  const restoreVisiblePage = () => {
@@ -1191,7 +1354,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1191
1354
  };
1192
1355
  }, [activeIndex, pageHeight]);
1193
1356
  (0, react.useEffect)(() => {
1194
- if (!isWeb) return;
1357
+ if (!isWeb$1) return;
1195
1358
  const root = containerRef.current;
1196
1359
  if (!root) return;
1197
1360
  const onScrollCapture = (event) => {
@@ -1208,7 +1371,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1208
1371
  return () => root.removeEventListener("scroll", onScrollCapture, true);
1209
1372
  }, []);
1210
1373
  (0, react.useEffect)(() => {
1211
- if (!isWeb) return;
1374
+ if (!isWeb$1) return;
1212
1375
  const root = containerRef.current;
1213
1376
  if (!root) return;
1214
1377
  const lockHorizontal = () => {
@@ -1374,14 +1537,14 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1374
1537
  },
1375
1538
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react_native.LegendList, {
1376
1539
  ref: listRef,
1377
- style: isWeb ? [styles$1.pagerList, styles$1.webNoScroll] : styles$1.pagerList,
1540
+ style: isWeb$1 ? [styles$1.pagerList, styles$1.webNoScroll] : styles$1.pagerList,
1378
1541
  data: pageDates,
1379
1542
  extraData: listExtraData,
1380
1543
  horizontal: true,
1381
1544
  recycleItems: false,
1382
1545
  keyExtractor: keyExtractorList,
1383
1546
  getFixedItemSize,
1384
- ...isWeb ? null : {
1547
+ ...isWeb$1 ? null : {
1385
1548
  scrollEnabled: swipeEnabled,
1386
1549
  pagingEnabled: !freeSwipe,
1387
1550
  snapToIndices: freeSwipe ? snapToIndices : void 0
@@ -1589,11 +1752,13 @@ const styles$1 = react_native.StyleSheet.create({
1589
1752
  const defaultKeyExtractor = (event) => `${event.start.toISOString()}|${event.end.toISOString()}|${event.title ?? ""}`;
1590
1753
  function visibleRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
1591
1754
  if (mode === "month") return [(0, date_fns.startOfWeek)((0, date_fns.startOfMonth)(date), { weekStartsOn }), (0, date_fns.endOfWeek)((0, date_fns.endOfMonth)(date), { weekStartsOn })];
1755
+ if (mode === "year") return [(0, date_fns.startOfWeek)((0, date_fns.startOfYear)(date), { weekStartsOn }), (0, date_fns.endOfWeek)((0, date_fns.endOfYear)(date), { weekStartsOn })];
1592
1756
  const days = (0, _super_calendar_core.getViewDays)(mode, date, weekStartsOn, numberOfDays, false, weekEndsOn);
1593
1757
  return [(0, date_fns.startOfDay)(days[0]), (0, date_fns.endOfDay)(days[days.length - 1])];
1594
1758
  }
1595
1759
  function expansionRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
1596
1760
  if (mode === "month") return [(0, date_fns.startOfWeek)((0, date_fns.startOfMonth)((0, date_fns.addMonths)(date, -1)), { weekStartsOn }), (0, date_fns.addWeeks)((0, date_fns.endOfWeek)((0, date_fns.endOfMonth)((0, date_fns.addMonths)(date, 1)), { weekStartsOn }), 1)];
1761
+ if (mode === "year") return [(0, date_fns.startOfWeek)((0, date_fns.startOfYear)(date), { weekStartsOn }), (0, date_fns.endOfWeek)((0, date_fns.endOfYear)(date), { weekStartsOn })];
1597
1762
  if (mode === "schedule") return [(0, date_fns.startOfDay)(date), (0, date_fns.endOfDay)((0, date_fns.addMonths)(date, 3))];
1598
1763
  const days = (0, _super_calendar_core.getViewDays)(mode, date, weekStartsOn, numberOfDays, false, weekEndsOn);
1599
1764
  const span = days.length;
@@ -1625,7 +1790,7 @@ function expansionRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
1625
1790
  * }
1626
1791
  * ```
1627
1792
  */
1628
- 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 }) {
1793
+ 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 }) {
1629
1794
  const mergedTheme = (0, react.useMemo)(() => require_MonthList.mergeTheme(theme), [theme]);
1630
1795
  const internalCellHeight = (0, react_native_reanimated.useSharedValue)(hourHeight);
1631
1796
  const cellHeight = cellHeightProp ?? internalCellHeight;
@@ -1726,6 +1891,16 @@ function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPress
1726
1891
  swipeEnabled,
1727
1892
  classNames,
1728
1893
  styles: styleOverrides
1894
+ }) : mode === "year" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(YearView, {
1895
+ date,
1896
+ events: displayEvents,
1897
+ weekStartsOn,
1898
+ locale,
1899
+ activeDate,
1900
+ onPressDay,
1901
+ onPressMonth,
1902
+ classNames,
1903
+ styles: styleOverrides
1729
1904
  }) : mode === "schedule" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Agenda, {
1730
1905
  events: displayEvents,
1731
1906
  locale,
@@ -1795,7 +1970,9 @@ function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPress
1795
1970
  //#endregion
1796
1971
  //#region src/components/ResourceTimeline.tsx
1797
1972
  const MOVE_ACTIVATE_MS = 250;
1973
+ const DRAG_ACTIVATE_PX = 8;
1798
1974
  const MINUTES_PER_HOUR = 60;
1975
+ const isWeb = react_native.Platform.OS === "web";
1799
1976
  const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
1800
1977
  const GUTTER_WIDTH = 56;
1801
1978
  function DefaultBar({ event, width, height }) {
@@ -1828,7 +2005,7 @@ function DefaultBar({ event, width, height }) {
1828
2005
  }) : null]
1829
2006
  });
1830
2007
  }
1831
- function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendererSize, snapMinutes, Renderer, onPress, onDragEvent, theme }) {
2008
+ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendererSize, snapMinutes, Renderer, onPress, onLongPress, onDragEvent, theme }) {
1832
2009
  const moveX = (0, react_native_reanimated.useSharedValue)(0);
1833
2010
  const resizeW = (0, react_native_reanimated.useSharedValue)(0);
1834
2011
  const latest = (0, react.useRef)({
@@ -1959,6 +2136,7 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1959
2136
  gesture: moveGesture,
1960
2137
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
1961
2138
  onPress,
2139
+ onLongPress,
1962
2140
  accessibilityRole: "button",
1963
2141
  accessibilityLabel: pe.event.title,
1964
2142
  accessibilityActions: barActions,
@@ -1982,6 +2160,115 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1982
2160
  })]
1983
2161
  });
1984
2162
  }
2163
+ function LaneInteractionLayer({ resource, vertical, hourSize, startHour, date, snapMinutes, onPressCell, onLongPressCell, onCreateEvent, theme }) {
2164
+ const ghostStart = (0, react_native_reanimated.useSharedValue)(0);
2165
+ const ghostSize = (0, react_native_reanimated.useSharedValue)(0);
2166
+ const ghostVisible = (0, react_native_reanimated.useSharedValue)(0);
2167
+ const timeAt = (0, react.useCallback)((px) => (0, _super_calendar_core.cellRangeFromDrag)(date, px, px, hourSize, startHour, snapMinutes)?.start, [
2168
+ date,
2169
+ hourSize,
2170
+ startHour,
2171
+ snapMinutes
2172
+ ]);
2173
+ const commitCreate = (0, react.useCallback)((fromPx, toPx) => {
2174
+ const range = (0, _super_calendar_core.cellRangeFromDrag)(date, fromPx, toPx, hourSize, startHour, snapMinutes);
2175
+ if (range) onCreateEvent?.(range.start, range.end, resource);
2176
+ }, [
2177
+ date,
2178
+ hourSize,
2179
+ startHour,
2180
+ snapMinutes,
2181
+ onCreateEvent,
2182
+ resource
2183
+ ]);
2184
+ const pressAt = (0, react.useCallback)((px) => {
2185
+ const at = timeAt(px);
2186
+ if (at) onPressCell?.(at, resource);
2187
+ }, [
2188
+ timeAt,
2189
+ onPressCell,
2190
+ resource
2191
+ ]);
2192
+ const laneGesture = (0, react.useMemo)(() => {
2193
+ const pan = onCreateEvent ? react_native_gesture_handler.Gesture.Pan().onStart((e) => {
2194
+ ghostStart.value = vertical ? e.y : e.x;
2195
+ ghostSize.value = 0;
2196
+ ghostVisible.value = 1;
2197
+ }).onUpdate((e) => {
2198
+ ghostSize.value = (vertical ? e.y : e.x) - ghostStart.value;
2199
+ }).onEnd((e, success) => {
2200
+ ghostVisible.value = 0;
2201
+ if (success) (0, react_native_reanimated.runOnJS)(commitCreate)(ghostStart.value, vertical ? e.y : e.x);
2202
+ }).onFinalize(() => {
2203
+ ghostVisible.value = 0;
2204
+ }) : null;
2205
+ const activatedPan = pan ? isWeb ? vertical ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activeOffsetX([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS) : null;
2206
+ const tap = isWeb && onPressCell ? react_native_gesture_handler.Gesture.Tap().onEnd((e) => {
2207
+ (0, react_native_reanimated.runOnJS)(pressAt)(vertical ? e.y : e.x);
2208
+ }) : null;
2209
+ if (activatedPan && tap) return react_native_gesture_handler.Gesture.Exclusive(activatedPan, tap);
2210
+ return activatedPan ?? tap;
2211
+ }, [
2212
+ onCreateEvent,
2213
+ onPressCell,
2214
+ vertical,
2215
+ commitCreate,
2216
+ pressAt,
2217
+ ghostStart,
2218
+ ghostSize,
2219
+ ghostVisible
2220
+ ]);
2221
+ const ghostStyle = (0, react_native_reanimated.useAnimatedStyle)(() => {
2222
+ const stepPx = snapMinutes / MINUTES_PER_HOUR * hourSize;
2223
+ const snap = (v) => stepPx > 0 ? Math.round(v / stepPx) * stepPx : v;
2224
+ const a = snap(ghostStart.value);
2225
+ const b = snap(ghostStart.value + ghostSize.value);
2226
+ const from = Math.min(a, b);
2227
+ const size = Math.max(Math.abs(b - a), 2);
2228
+ return vertical ? {
2229
+ opacity: ghostVisible.value,
2230
+ top: from,
2231
+ height: size
2232
+ } : {
2233
+ opacity: ghostVisible.value,
2234
+ left: from,
2235
+ width: size
2236
+ };
2237
+ }, [
2238
+ vertical,
2239
+ hourSize,
2240
+ snapMinutes
2241
+ ]);
2242
+ const pxOf = (e) => vertical ? e.nativeEvent.locationY : e.nativeEvent.locationX;
2243
+ const handlePress = onPressCell ? (e) => pressAt(pxOf(e)) : void 0;
2244
+ const handleLongPress = onLongPressCell && !onCreateEvent ? (e) => {
2245
+ const at = timeAt(pxOf(e));
2246
+ if (at) onLongPressCell(at, resource);
2247
+ } : void 0;
2248
+ const layer = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2249
+ testID: "resource-cell-layer",
2250
+ style: react_native.StyleSheet.absoluteFill,
2251
+ onPress: handlePress,
2252
+ onLongPress: handleLongPress,
2253
+ accessibilityElementsHidden: true,
2254
+ importantForAccessibility: "no"
2255
+ });
2256
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [laneGesture ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_gesture_handler.GestureDetector, {
2257
+ gesture: laneGesture,
2258
+ children: layer
2259
+ }) : layer, onCreateEvent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
2260
+ testID: "resource-create-ghost",
2261
+ pointerEvents: "none",
2262
+ style: [
2263
+ vertical ? styles.vcreateGhost : styles.hcreateGhost,
2264
+ {
2265
+ backgroundColor: theme.colors.eventBackground,
2266
+ borderColor: theme.colors.todayBackground
2267
+ },
2268
+ ghostStyle
2269
+ ]
2270
+ }) : null] });
2271
+ }
1985
2272
  /**
1986
2273
  * A horizontal resource timeline: rows are resources (rooms, people, machines)
1987
2274
  * and the x-axis is one day's hours. Events sit in their resource's row, and
@@ -2000,9 +2287,11 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
2000
2287
  * />
2001
2288
  * ```
2002
2289
  */
2003
- 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 }) {
2290
+ 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 }) {
2004
2291
  const theme = require_MonthList.useCalendarTheme();
2005
2292
  const Renderer = renderEvent ?? DefaultBar;
2293
+ const cellEnabled = !!onPressCell || !!onLongPressCell || !!onCreateEvent;
2294
+ const bandsFor = (resource) => businessHours ? (0, _super_calendar_core.closedHourBands)(date, (d) => businessHours(d, resource), startHour, endHour) : [];
2006
2295
  const hours = (0, react.useMemo)(() => Array.from({ length: Math.max(0, endHour - startHour) }, (_, i) => startHour + i), [startHour, endHour]);
2007
2296
  const trackWidth = (endHour - startHour) * hourWidth;
2008
2297
  const byResource = (0, react.useMemo)(() => {
@@ -2055,58 +2344,84 @@ function ResourceTimeline({ date, orientation = "horizontal", resources, events,
2055
2344
  { borderLeftColor: theme.colors.gridLine },
2056
2345
  theme.containers.resourceRow
2057
2346
  ],
2058
- children: [hours.slice(1).map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2059
- pointerEvents: "none",
2060
- style: [styles.vgridLine, {
2061
- top: (h - startHour) * hourHeight,
2062
- backgroundColor: theme.colors.gridLine
2063
- }]
2064
- }, h)), positioned.map((pe, idx) => {
2065
- const top = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourHeight;
2066
- const bottomPx = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourHeight;
2067
- const height = Math.max(bottomPx - top, 2);
2068
- const lanePct = 100 / pe.columns;
2069
- const left = `${pe.column * lanePct}%`;
2070
- const width = `${lanePct}%`;
2071
- const onPress = () => onPressEvent?.(pe.event);
2072
- if (onDragEvent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResourceBar, {
2073
- pe,
2347
+ children: [
2348
+ bandsFor(resource).map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2349
+ pointerEvents: "none",
2350
+ testID: "resource-hours-shade",
2351
+ style: [styles.vshadeBand, {
2352
+ top: (b.start - startHour) * hourHeight,
2353
+ height: (b.end - b.start) * hourHeight,
2354
+ backgroundColor: theme.colors.outsideHoursBackground
2355
+ }]
2356
+ }, `shade-${b.start}`)),
2357
+ hours.slice(1).map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2358
+ pointerEvents: "none",
2359
+ style: [styles.vgridLine, {
2360
+ top: (h - startHour) * hourHeight,
2361
+ backgroundColor: theme.colors.gridLine
2362
+ }]
2363
+ }, h)),
2364
+ cellEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LaneInteractionLayer, {
2365
+ resource,
2074
2366
  vertical: true,
2075
2367
  hourSize: hourHeight,
2076
- left,
2077
- top,
2078
- width,
2079
- height,
2080
- rendererSize: {
2081
- width: 0,
2082
- height
2083
- },
2368
+ startHour,
2369
+ date,
2084
2370
  snapMinutes: dragStepMinutes,
2085
- Renderer,
2086
- onPress,
2087
- onDragEvent,
2371
+ onPressCell,
2372
+ onLongPressCell,
2373
+ onCreateEvent,
2088
2374
  theme
2089
- }, idx);
2090
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2091
- onPress,
2092
- accessibilityRole: "button",
2093
- accessibilityLabel: pe.event.title,
2094
- style: {
2095
- position: "absolute",
2375
+ }) : null,
2376
+ positioned.map((pe, idx) => {
2377
+ const top = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourHeight;
2378
+ const bottomPx = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourHeight;
2379
+ const height = Math.max(bottomPx - top, 2);
2380
+ const lanePct = 100 / pe.columns;
2381
+ const left = `${pe.column * lanePct}%`;
2382
+ const width = `${lanePct}%`;
2383
+ const onPress = () => onPressEvent?.(pe.event);
2384
+ if (onDragEvent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResourceBar, {
2385
+ pe,
2386
+ vertical: true,
2387
+ hourSize: hourHeight,
2096
2388
  left,
2097
- width,
2098
2389
  top,
2390
+ width,
2099
2391
  height,
2100
- padding: 1
2101
- },
2102
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, {
2103
- event: pe.event,
2104
- width: 0,
2105
- height,
2106
- onPress
2107
- })
2108
- }, idx);
2109
- })]
2392
+ rendererSize: {
2393
+ width: 0,
2394
+ height
2395
+ },
2396
+ snapMinutes: dragStepMinutes,
2397
+ Renderer,
2398
+ onPress,
2399
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2400
+ onDragEvent,
2401
+ theme
2402
+ }, idx);
2403
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2404
+ onPress,
2405
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2406
+ accessibilityRole: "button",
2407
+ accessibilityLabel: pe.event.title,
2408
+ style: {
2409
+ position: "absolute",
2410
+ left,
2411
+ width,
2412
+ top,
2413
+ height,
2414
+ padding: 1
2415
+ },
2416
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, {
2417
+ event: pe.event,
2418
+ width: 0,
2419
+ height,
2420
+ onPress
2421
+ })
2422
+ }, idx);
2423
+ })
2424
+ ]
2110
2425
  }, resource.id);
2111
2426
  })]
2112
2427
  })
@@ -2163,52 +2478,78 @@ function ResourceTimeline({ date, orientation = "horizontal", resources, events,
2163
2478
  })
2164
2479
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
2165
2480
  style: { width: trackWidth },
2166
- children: [hours.slice(1).map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2167
- pointerEvents: "none",
2168
- style: [styles.gridLine, {
2169
- left: (h - startHour) * hourWidth,
2170
- backgroundColor: theme.colors.gridLine
2171
- }]
2172
- }, h)), positioned.map((pe, idx) => {
2173
- const left = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourWidth;
2174
- const right = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourWidth;
2175
- const width = Math.max(right - left, 2);
2176
- const laneHeight = rowHeight / pe.columns;
2177
- const onPress = () => onPressEvent?.(pe.event);
2178
- if (onDragEvent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResourceBar, {
2179
- pe,
2481
+ children: [
2482
+ bandsFor(resource).map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2483
+ pointerEvents: "none",
2484
+ testID: "resource-hours-shade",
2485
+ style: [styles.hshadeBand, {
2486
+ left: (b.start - startHour) * hourWidth,
2487
+ width: (b.end - b.start) * hourWidth,
2488
+ backgroundColor: theme.colors.outsideHoursBackground
2489
+ }]
2490
+ }, `shade-${b.start}`)),
2491
+ hours.slice(1).map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2492
+ pointerEvents: "none",
2493
+ style: [styles.gridLine, {
2494
+ left: (h - startHour) * hourWidth,
2495
+ backgroundColor: theme.colors.gridLine
2496
+ }]
2497
+ }, h)),
2498
+ cellEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LaneInteractionLayer, {
2499
+ resource,
2180
2500
  vertical: false,
2181
2501
  hourSize: hourWidth,
2182
- left,
2183
- top: pe.column * laneHeight,
2184
- width,
2185
- height: laneHeight,
2186
- rendererSize: { width },
2502
+ startHour,
2503
+ date,
2187
2504
  snapMinutes: dragStepMinutes,
2188
- Renderer,
2189
- onPress,
2190
- onDragEvent,
2505
+ onPressCell,
2506
+ onLongPressCell,
2507
+ onCreateEvent,
2191
2508
  theme
2192
- }, idx);
2193
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2194
- onPress,
2195
- accessibilityRole: "button",
2196
- accessibilityLabel: pe.event.title,
2197
- style: {
2198
- position: "absolute",
2509
+ }) : null,
2510
+ positioned.map((pe, idx) => {
2511
+ const left = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourWidth;
2512
+ const right = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourWidth;
2513
+ const width = Math.max(right - left, 2);
2514
+ const laneHeight = rowHeight / pe.columns;
2515
+ const onPress = () => onPressEvent?.(pe.event);
2516
+ if (onDragEvent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResourceBar, {
2517
+ pe,
2518
+ vertical: false,
2519
+ hourSize: hourWidth,
2199
2520
  left,
2200
- width,
2201
2521
  top: pe.column * laneHeight,
2202
- height: laneHeight,
2203
- padding: 1
2204
- },
2205
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, {
2206
- event: pe.event,
2207
2522
  width,
2208
- onPress
2209
- })
2210
- }, idx);
2211
- })]
2523
+ height: laneHeight,
2524
+ rendererSize: { width },
2525
+ snapMinutes: dragStepMinutes,
2526
+ Renderer,
2527
+ onPress,
2528
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2529
+ onDragEvent,
2530
+ theme
2531
+ }, idx);
2532
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2533
+ onPress,
2534
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2535
+ accessibilityRole: "button",
2536
+ accessibilityLabel: pe.event.title,
2537
+ style: {
2538
+ position: "absolute",
2539
+ left,
2540
+ width,
2541
+ top: pe.column * laneHeight,
2542
+ height: laneHeight,
2543
+ padding: 1
2544
+ },
2545
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, {
2546
+ event: pe.event,
2547
+ width,
2548
+ onPress
2549
+ })
2550
+ }, idx);
2551
+ })
2552
+ ]
2212
2553
  })]
2213
2554
  }, resource.id);
2214
2555
  })]
@@ -2298,6 +2639,34 @@ const styles = react_native.StyleSheet.create({
2298
2639
  left: 0,
2299
2640
  right: 0,
2300
2641
  height: react_native.StyleSheet.hairlineWidth
2642
+ },
2643
+ vcreateGhost: {
2644
+ position: "absolute",
2645
+ left: 2,
2646
+ right: 2,
2647
+ borderWidth: 1,
2648
+ borderRadius: 6,
2649
+ opacity: 0,
2650
+ zIndex: 2
2651
+ },
2652
+ hcreateGhost: {
2653
+ position: "absolute",
2654
+ top: 2,
2655
+ bottom: 2,
2656
+ borderWidth: 1,
2657
+ borderRadius: 6,
2658
+ opacity: 0,
2659
+ zIndex: 2
2660
+ },
2661
+ hshadeBand: {
2662
+ position: "absolute",
2663
+ top: 0,
2664
+ bottom: 0
2665
+ },
2666
+ vshadeBand: {
2667
+ position: "absolute",
2668
+ left: 0,
2669
+ right: 0
2301
2670
  }
2302
2671
  });
2303
2672
  //#endregion
@@ -2312,6 +2681,7 @@ exports.MonthPager = require_MonthList.MonthPager;
2312
2681
  exports.MonthView = require_MonthList.MonthView;
2313
2682
  exports.ResourceTimeline = ResourceTimeline;
2314
2683
  exports.TimeGrid = TimeGrid;
2684
+ exports.YearView = YearView;
2315
2685
  Object.defineProperty(exports, "buildMonthGrid", {
2316
2686
  enumerable: true,
2317
2687
  get: function() {