@super-calendar/native 2.4.0 → 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 CHANGED
@@ -465,12 +465,38 @@ interface ResourceTimelineProps<T = unknown> {
465
465
  renderEvent?: ComponentType<ResourceEventArgs<T>>;
466
466
  /** Tap an event. */
467
467
  onPressEvent?: (event: CalendarEvent$1<T>) => void;
468
+ /**
469
+ * Long-press an event. When `onDragEvent` is also set, a long-press picks the
470
+ * bar up to move instead, so this fires only for non-draggable bars.
471
+ */
472
+ onLongPressEvent?: (event: CalendarEvent$1<T>) => void;
468
473
  /**
469
474
  * Enables drag-to-move and edge-resize along the time axis: long-press a bar to
470
475
  * move it, or drag its right edge to resize. Called with the proposed new
471
476
  * start/end; return `false` to reject the drop (it snaps back).
472
477
  */
473
478
  onDragEvent?: (event: CalendarEvent$1<T>, start: Date, end: Date) => void | boolean;
479
+ /** Tap empty lane space; called with the snapped time and the lane's resource. */
480
+ onPressCell?: (at: Date, resource: Resource) => void;
481
+ /**
482
+ * Long-press empty lane space. When `onCreateEvent` is also set, the long-press
483
+ * starts the create drag instead, so this fires only without it.
484
+ */
485
+ onLongPressCell?: (at: Date, resource: Resource) => void;
486
+ /**
487
+ * Long-press empty lane space, then drag along the time axis to sweep out a new
488
+ * event; called with the swept start/end and the lane's resource.
489
+ */
490
+ onCreateEvent?: (start: Date, end: Date, resource: Resource) => void;
491
+ /**
492
+ * Shade the hours outside this window, per lane. Same shape as the Calendar's
493
+ * `businessHours` plus the lane's resource, so per-resource opening hours work
494
+ * (return `null` for a fully closed lane). A date-only function is accepted.
495
+ */
496
+ businessHours?: (date: Date, resource: Resource) => {
497
+ start: number;
498
+ end: number;
499
+ } | null;
474
500
  /** Snap dragged events to this many minutes (default 15). */
475
501
  dragStepMinutes?: number;
476
502
  }
@@ -492,6 +518,6 @@ interface ResourceTimelineProps<T = unknown> {
492
518
  * />
493
519
  * ```
494
520
  */
495
- declare function ResourceTimeline<T = unknown>({ date, orientation, resources, events, resourceId, startHour, endHour, hourWidth, hourHeight, rowHeight, labelWidth, ampm, renderEvent, onPressEvent, onDragEvent, dragStepMinutes }: ResourceTimelineProps<T>): ReactElement;
521
+ declare function ResourceTimeline<T = unknown>({ date, orientation, resources, events, resourceId, startHour, endHour, hourWidth, hourHeight, rowHeight, labelWidth, ampm, renderEvent, onPressEvent, onLongPressEvent, onDragEvent, onPressCell, onLongPressCell, onCreateEvent, businessHours, dragStepMinutes }: ResourceTimelineProps<T>): ReactElement;
496
522
  //#endregion
497
523
  export { Agenda, type AgendaProps, type AgendaSlot, type BusinessHours, Calendar, type CalendarEvent, type CalendarMode, type CalendarProps, type CalendarSlot, type CalendarTheme, CalendarThemeProvider, DEFAULT_HOUR_HEIGHT, type DateRange, type DateSelectionConstraints, type DaySelectionState, DefaultEvent, DefaultMonthEvent, type EventAccessibilityLabelContext, type EventAccessibilityLabeler, type EventDragHandler, type EventDragStartHandler, type EventKeyExtractor, type HourRenderer, type ICalEvent, type ICalendarEvent, type MonthGrid, type MonthGridDay, type MonthGridWeek, type MonthGridWeekday, MonthList, type MonthListProps, type MonthListSlot, MonthPager, type MonthPagerProps, MonthView, type MonthViewProps, type MonthViewSlot, type PartialCalendarTheme, type PositionedEvent, type RecurrenceFrequency, type RecurrenceRule, type RenderEvent, type RenderEventArgs, type ResolvedSlot, type Resource, type ResourceEventArgs, ResourceTimeline, type ResourceTimelineProps, type SlotDefault, type SlotStyleProps, TimeGrid, type TimeGridMode, type TimeGridProps, type TimeGridSlot, type ToICalendarOptions, type UseDateRangeOptions, type UseMonthGridOptions, type WeekStartsOn, type WeekdayFormat, 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 };
package/dist/index.d.ts CHANGED
@@ -465,12 +465,38 @@ interface ResourceTimelineProps<T = unknown> {
465
465
  renderEvent?: ComponentType<ResourceEventArgs<T>>;
466
466
  /** Tap an event. */
467
467
  onPressEvent?: (event: CalendarEvent$1<T>) => void;
468
+ /**
469
+ * Long-press an event. When `onDragEvent` is also set, a long-press picks the
470
+ * bar up to move instead, so this fires only for non-draggable bars.
471
+ */
472
+ onLongPressEvent?: (event: CalendarEvent$1<T>) => void;
468
473
  /**
469
474
  * Enables drag-to-move and edge-resize along the time axis: long-press a bar to
470
475
  * move it, or drag its right edge to resize. Called with the proposed new
471
476
  * start/end; return `false` to reject the drop (it snaps back).
472
477
  */
473
478
  onDragEvent?: (event: CalendarEvent$1<T>, start: Date, end: Date) => void | boolean;
479
+ /** Tap empty lane space; called with the snapped time and the lane's resource. */
480
+ onPressCell?: (at: Date, resource: Resource) => void;
481
+ /**
482
+ * Long-press empty lane space. When `onCreateEvent` is also set, the long-press
483
+ * starts the create drag instead, so this fires only without it.
484
+ */
485
+ onLongPressCell?: (at: Date, resource: Resource) => void;
486
+ /**
487
+ * Long-press empty lane space, then drag along the time axis to sweep out a new
488
+ * event; called with the swept start/end and the lane's resource.
489
+ */
490
+ onCreateEvent?: (start: Date, end: Date, resource: Resource) => void;
491
+ /**
492
+ * Shade the hours outside this window, per lane. Same shape as the Calendar's
493
+ * `businessHours` plus the lane's resource, so per-resource opening hours work
494
+ * (return `null` for a fully closed lane). A date-only function is accepted.
495
+ */
496
+ businessHours?: (date: Date, resource: Resource) => {
497
+ start: number;
498
+ end: number;
499
+ } | null;
474
500
  /** Snap dragged events to this many minutes (default 15). */
475
501
  dragStepMinutes?: number;
476
502
  }
@@ -492,6 +518,6 @@ interface ResourceTimelineProps<T = unknown> {
492
518
  * />
493
519
  * ```
494
520
  */
495
- declare function ResourceTimeline<T = unknown>({ date, orientation, resources, events, resourceId, startHour, endHour, hourWidth, hourHeight, rowHeight, labelWidth, ampm, renderEvent, onPressEvent, onDragEvent, dragStepMinutes }: ResourceTimelineProps<T>): ReactElement;
521
+ declare function ResourceTimeline<T = unknown>({ date, orientation, resources, events, resourceId, startHour, endHour, hourWidth, hourHeight, rowHeight, labelWidth, ampm, renderEvent, onPressEvent, onLongPressEvent, onDragEvent, onPressCell, onLongPressCell, onCreateEvent, businessHours, dragStepMinutes }: ResourceTimelineProps<T>): ReactElement;
496
522
  //#endregion
497
523
  export { Agenda, type AgendaProps, type AgendaSlot, type BusinessHours, Calendar, type CalendarEvent, type CalendarMode, type CalendarProps, type CalendarSlot, type CalendarTheme, CalendarThemeProvider, DEFAULT_HOUR_HEIGHT, type DateRange, type DateSelectionConstraints, type DaySelectionState, DefaultEvent, DefaultMonthEvent, type EventAccessibilityLabelContext, type EventAccessibilityLabeler, type EventDragHandler, type EventDragStartHandler, type EventKeyExtractor, type HourRenderer, type ICalEvent, type ICalendarEvent, type MonthGrid, type MonthGridDay, type MonthGridWeek, type MonthGridWeekday, MonthList, type MonthListProps, type MonthListSlot, MonthPager, type MonthPagerProps, MonthView, type MonthViewProps, type MonthViewSlot, type PartialCalendarTheme, type PositionedEvent, type RecurrenceFrequency, type RecurrenceRule, type RenderEvent, type RenderEventArgs, type ResolvedSlot, type Resource, type ResourceEventArgs, ResourceTimeline, type ResourceTimelineProps, type SlotDefault, type SlotStyleProps, TimeGrid, type TimeGridMode, type TimeGridProps, type TimeGridSlot, type ToICalendarOptions, type UseDateRangeOptions, type UseMonthGridOptions, type WeekStartsOn, type WeekdayFormat, 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 };
package/dist/index.js CHANGED
@@ -402,7 +402,7 @@ const styles$2 = react_native.StyleSheet.create({
402
402
  });
403
403
  //#endregion
404
404
  //#region src/components/TimeGrid.tsx
405
- const isWeb = react_native.Platform.OS === "web";
405
+ const isWeb$1 = react_native.Platform.OS === "web";
406
406
  const MINUTES_PER_HOUR$1 = 60;
407
407
  const HOURS_PER_DAY = 24;
408
408
  const MINUTES_PER_DAY = MINUTES_PER_HOUR$1 * HOURS_PER_DAY;
@@ -417,7 +417,7 @@ const MIN_EVENT_HEIGHT = 32;
417
417
  const EVENT_GAP = 2;
418
418
  const DRAG_ACTIVATE_MS = 300;
419
419
  const MOVE_ACTIVATE_MS$1 = 500;
420
- const DRAG_ACTIVATE_PX = 8;
420
+ const DRAG_ACTIVATE_PX$1 = 8;
421
421
  const RESIZE_HANDLE_HEIGHT = 14;
422
422
  const DEFAULT_DRAG_STEP_MINUTES = 15;
423
423
  const HOUR_LABEL_TOP_INSET = 12;
@@ -516,7 +516,7 @@ function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, dayWid
516
516
  const totalDelta = minuteDelta + dayDelta * MINUTES_PER_DAY;
517
517
  (0, react_native_reanimated.runOnJS)(commitDrag)(totalDelta, totalDelta);
518
518
  });
519
- return isWeb ? pan.activeOffsetX([-8, DRAG_ACTIVATE_PX]).activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS$1);
519
+ return isWeb$1 ? pan.activeOffsetX([-8, DRAG_ACTIVATE_PX$1]).activeOffsetY([-8, DRAG_ACTIVATE_PX$1]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS$1);
520
520
  }, [
521
521
  draggable,
522
522
  snapMinutes,
@@ -732,14 +732,14 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
732
732
  },
733
733
  onEndDrag: (event) => {
734
734
  isDragging.value = false;
735
- if (!isWeb && isActiveShared.value) {
735
+ if (!isWeb$1 && isActiveShared.value) {
736
736
  scrollY.value = event.contentOffset.y;
737
737
  (0, react_native_reanimated.runOnJS)(onSettleOffset)(event.contentOffset.y);
738
738
  }
739
739
  },
740
740
  onMomentumEnd: (event) => {
741
741
  isDragging.value = false;
742
- if (!isWeb && isActiveShared.value) {
742
+ if (!isWeb$1 && isActiveShared.value) {
743
743
  scrollY.value = event.contentOffset.y;
744
744
  (0, react_native_reanimated.runOnJS)(onSettleOffset)(event.contentOffset.y);
745
745
  }
@@ -749,7 +749,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
749
749
  if (!isActiveShared.value && current !== previous) (0, react_native_reanimated.scrollTo)(scrollRef, 0, current, false);
750
750
  });
751
751
  (0, react_native_reanimated.useAnimatedReaction)(() => isActiveShared.value, (active, previous) => {
752
- if (isWeb || !active || active === previous) return;
752
+ if (isWeb$1 || !active || active === previous) return;
753
753
  (0, react_native_reanimated.scrollTo)(scrollRef, 0, scrollY.value, false);
754
754
  });
755
755
  const days = (0, react.useMemo)(() => (0, _super_calendar_core.getViewDays)(mode, date, weekStartsOn, numberOfDays, isRTL, weekEndsOn), [
@@ -872,7 +872,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
872
872
  if (createCancelled.value) return;
873
873
  (0, react_native_reanimated.runOnJS)(commitCreate)(createStartY.value, createStartY.value + event.translationY, createDayIndex.value);
874
874
  });
875
- return isWeb ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(DRAG_ACTIVATE_MS);
875
+ return isWeb$1 ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX$1]) : pan.activateAfterLongPress(DRAG_ACTIVATE_MS);
876
876
  }, [
877
877
  createEnabled,
878
878
  dayCount,
@@ -891,7 +891,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
891
891
  createCancelled
892
892
  ]);
893
893
  const backgroundGesture = (0, react.useMemo)(() => {
894
- const tap = isWeb && onPressCell != null ? react_native_gesture_handler.Gesture.Tap().onEnd((event) => {
894
+ const tap = isWeb$1 && onPressCell != null ? react_native_gesture_handler.Gesture.Tap().onEnd((event) => {
895
895
  (0, react_native_reanimated.runOnJS)(tapCell)(event.x, event.y);
896
896
  }) : null;
897
897
  if (createEnabled && tap) return react_native_gesture_handler.Gesture.Exclusive(createGesture, tap);
@@ -904,7 +904,7 @@ function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hour
904
904
  tapCell
905
905
  ]);
906
906
  (0, react.useEffect)(() => {
907
- if (!isWeb || !createEnabled) return;
907
+ if (!isWeb$1 || !createEnabled) return;
908
908
  const doc = globalThis.document;
909
909
  if (!doc) return;
910
910
  const handler = (event) => {
@@ -1110,7 +1110,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1110
1110
  offsetSeedRef.current = y;
1111
1111
  }, []);
1112
1112
  const committedCellHeight = (0, react_native_reanimated.useSharedValue)(hourHeight);
1113
- useWebGridZoom(isWeb, containerRef, cellHeight, committedCellHeight, minHourHeight, maxHourHeight);
1113
+ useWebGridZoom(isWeb$1, containerRef, cellHeight, committedCellHeight, minHourHeight, maxHourHeight);
1114
1114
  const [anchorDate] = (0, react.useState)(date);
1115
1115
  const anchor = (0, react.useMemo)(() => weekAnchored ? (0, date_fns.startOfWeek)(anchorDate, { weekStartsOn }) : (0, date_fns.startOfDay)(anchorDate), [
1116
1116
  weekAnchored,
@@ -1140,7 +1140,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1140
1140
  weekEndsOn
1141
1141
  ]);
1142
1142
  const handleViewableItemsChanged = (0, react.useCallback)((info) => {
1143
- if (isWeb) return;
1143
+ if (isWeb$1) return;
1144
1144
  const settled = info.viewableItems.find((token) => token.isViewable);
1145
1145
  if (settled?.index == null) return;
1146
1146
  if (pendingScrollIndexRef.current != null) {
@@ -1164,7 +1164,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1164
1164
  });
1165
1165
  }, [activeIndex]);
1166
1166
  (0, react.useEffect)(() => {
1167
- if (!isWeb) return;
1167
+ if (!isWeb$1) return;
1168
1168
  const root = containerRef.current;
1169
1169
  if (!root) return;
1170
1170
  const restoreVisiblePage = () => {
@@ -1191,7 +1191,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1191
1191
  };
1192
1192
  }, [activeIndex, pageHeight]);
1193
1193
  (0, react.useEffect)(() => {
1194
- if (!isWeb) return;
1194
+ if (!isWeb$1) return;
1195
1195
  const root = containerRef.current;
1196
1196
  if (!root) return;
1197
1197
  const onScrollCapture = (event) => {
@@ -1208,7 +1208,7 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1208
1208
  return () => root.removeEventListener("scroll", onScrollCapture, true);
1209
1209
  }, []);
1210
1210
  (0, react.useEffect)(() => {
1211
- if (!isWeb) return;
1211
+ if (!isWeb$1) return;
1212
1212
  const root = containerRef.current;
1213
1213
  if (!root) return;
1214
1214
  const lockHorizontal = () => {
@@ -1374,14 +1374,14 @@ function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellH
1374
1374
  },
1375
1375
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react_native.LegendList, {
1376
1376
  ref: listRef,
1377
- style: isWeb ? [styles$1.pagerList, styles$1.webNoScroll] : styles$1.pagerList,
1377
+ style: isWeb$1 ? [styles$1.pagerList, styles$1.webNoScroll] : styles$1.pagerList,
1378
1378
  data: pageDates,
1379
1379
  extraData: listExtraData,
1380
1380
  horizontal: true,
1381
1381
  recycleItems: false,
1382
1382
  keyExtractor: keyExtractorList,
1383
1383
  getFixedItemSize,
1384
- ...isWeb ? null : {
1384
+ ...isWeb$1 ? null : {
1385
1385
  scrollEnabled: swipeEnabled,
1386
1386
  pagingEnabled: !freeSwipe,
1387
1387
  snapToIndices: freeSwipe ? snapToIndices : void 0
@@ -1795,7 +1795,9 @@ function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPress
1795
1795
  //#endregion
1796
1796
  //#region src/components/ResourceTimeline.tsx
1797
1797
  const MOVE_ACTIVATE_MS = 250;
1798
+ const DRAG_ACTIVATE_PX = 8;
1798
1799
  const MINUTES_PER_HOUR = 60;
1800
+ const isWeb = react_native.Platform.OS === "web";
1799
1801
  const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
1800
1802
  const GUTTER_WIDTH = 56;
1801
1803
  function DefaultBar({ event, width, height }) {
@@ -1828,7 +1830,7 @@ function DefaultBar({ event, width, height }) {
1828
1830
  }) : null]
1829
1831
  });
1830
1832
  }
1831
- function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendererSize, snapMinutes, Renderer, onPress, onDragEvent, theme }) {
1833
+ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendererSize, snapMinutes, Renderer, onPress, onLongPress, onDragEvent, theme }) {
1832
1834
  const moveX = (0, react_native_reanimated.useSharedValue)(0);
1833
1835
  const resizeW = (0, react_native_reanimated.useSharedValue)(0);
1834
1836
  const latest = (0, react.useRef)({
@@ -1959,6 +1961,7 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1959
1961
  gesture: moveGesture,
1960
1962
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
1961
1963
  onPress,
1964
+ onLongPress,
1962
1965
  accessibilityRole: "button",
1963
1966
  accessibilityLabel: pe.event.title,
1964
1967
  accessibilityActions: barActions,
@@ -1982,6 +1985,115 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
1982
1985
  })]
1983
1986
  });
1984
1987
  }
1988
+ function LaneInteractionLayer({ resource, vertical, hourSize, startHour, date, snapMinutes, onPressCell, onLongPressCell, onCreateEvent, theme }) {
1989
+ const ghostStart = (0, react_native_reanimated.useSharedValue)(0);
1990
+ const ghostSize = (0, react_native_reanimated.useSharedValue)(0);
1991
+ const ghostVisible = (0, react_native_reanimated.useSharedValue)(0);
1992
+ const timeAt = (0, react.useCallback)((px) => (0, _super_calendar_core.cellRangeFromDrag)(date, px, px, hourSize, startHour, snapMinutes)?.start, [
1993
+ date,
1994
+ hourSize,
1995
+ startHour,
1996
+ snapMinutes
1997
+ ]);
1998
+ const commitCreate = (0, react.useCallback)((fromPx, toPx) => {
1999
+ const range = (0, _super_calendar_core.cellRangeFromDrag)(date, fromPx, toPx, hourSize, startHour, snapMinutes);
2000
+ if (range) onCreateEvent?.(range.start, range.end, resource);
2001
+ }, [
2002
+ date,
2003
+ hourSize,
2004
+ startHour,
2005
+ snapMinutes,
2006
+ onCreateEvent,
2007
+ resource
2008
+ ]);
2009
+ const pressAt = (0, react.useCallback)((px) => {
2010
+ const at = timeAt(px);
2011
+ if (at) onPressCell?.(at, resource);
2012
+ }, [
2013
+ timeAt,
2014
+ onPressCell,
2015
+ resource
2016
+ ]);
2017
+ const laneGesture = (0, react.useMemo)(() => {
2018
+ const pan = onCreateEvent ? react_native_gesture_handler.Gesture.Pan().onStart((e) => {
2019
+ ghostStart.value = vertical ? e.y : e.x;
2020
+ ghostSize.value = 0;
2021
+ ghostVisible.value = 1;
2022
+ }).onUpdate((e) => {
2023
+ ghostSize.value = (vertical ? e.y : e.x) - ghostStart.value;
2024
+ }).onEnd((e, success) => {
2025
+ ghostVisible.value = 0;
2026
+ if (success) (0, react_native_reanimated.runOnJS)(commitCreate)(ghostStart.value, vertical ? e.y : e.x);
2027
+ }).onFinalize(() => {
2028
+ ghostVisible.value = 0;
2029
+ }) : null;
2030
+ const activatedPan = pan ? isWeb ? vertical ? pan.activeOffsetY([-8, DRAG_ACTIVATE_PX]) : pan.activeOffsetX([-8, DRAG_ACTIVATE_PX]) : pan.activateAfterLongPress(MOVE_ACTIVATE_MS) : null;
2031
+ const tap = isWeb && onPressCell ? react_native_gesture_handler.Gesture.Tap().onEnd((e) => {
2032
+ (0, react_native_reanimated.runOnJS)(pressAt)(vertical ? e.y : e.x);
2033
+ }) : null;
2034
+ if (activatedPan && tap) return react_native_gesture_handler.Gesture.Exclusive(activatedPan, tap);
2035
+ return activatedPan ?? tap;
2036
+ }, [
2037
+ onCreateEvent,
2038
+ onPressCell,
2039
+ vertical,
2040
+ commitCreate,
2041
+ pressAt,
2042
+ ghostStart,
2043
+ ghostSize,
2044
+ ghostVisible
2045
+ ]);
2046
+ const ghostStyle = (0, react_native_reanimated.useAnimatedStyle)(() => {
2047
+ const stepPx = snapMinutes / MINUTES_PER_HOUR * hourSize;
2048
+ const snap = (v) => stepPx > 0 ? Math.round(v / stepPx) * stepPx : v;
2049
+ const a = snap(ghostStart.value);
2050
+ const b = snap(ghostStart.value + ghostSize.value);
2051
+ const from = Math.min(a, b);
2052
+ const size = Math.max(Math.abs(b - a), 2);
2053
+ return vertical ? {
2054
+ opacity: ghostVisible.value,
2055
+ top: from,
2056
+ height: size
2057
+ } : {
2058
+ opacity: ghostVisible.value,
2059
+ left: from,
2060
+ width: size
2061
+ };
2062
+ }, [
2063
+ vertical,
2064
+ hourSize,
2065
+ snapMinutes
2066
+ ]);
2067
+ const pxOf = (e) => vertical ? e.nativeEvent.locationY : e.nativeEvent.locationX;
2068
+ const handlePress = onPressCell ? (e) => pressAt(pxOf(e)) : void 0;
2069
+ const handleLongPress = onLongPressCell && !onCreateEvent ? (e) => {
2070
+ const at = timeAt(pxOf(e));
2071
+ if (at) onLongPressCell(at, resource);
2072
+ } : void 0;
2073
+ const layer = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2074
+ testID: "resource-cell-layer",
2075
+ style: react_native.StyleSheet.absoluteFill,
2076
+ onPress: handlePress,
2077
+ onLongPress: handleLongPress,
2078
+ accessibilityElementsHidden: true,
2079
+ importantForAccessibility: "no"
2080
+ });
2081
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [laneGesture ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_gesture_handler.GestureDetector, {
2082
+ gesture: laneGesture,
2083
+ children: layer
2084
+ }) : layer, onCreateEvent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
2085
+ testID: "resource-create-ghost",
2086
+ pointerEvents: "none",
2087
+ style: [
2088
+ vertical ? styles.vcreateGhost : styles.hcreateGhost,
2089
+ {
2090
+ backgroundColor: theme.colors.eventBackground,
2091
+ borderColor: theme.colors.todayBackground
2092
+ },
2093
+ ghostStyle
2094
+ ]
2095
+ }) : null] });
2096
+ }
1985
2097
  /**
1986
2098
  * A horizontal resource timeline: rows are resources (rooms, people, machines)
1987
2099
  * and the x-axis is one day's hours. Events sit in their resource's row, and
@@ -2000,9 +2112,11 @@ function ResourceBar({ pe, vertical, hourSize, left, top, width, height, rendere
2000
2112
  * />
2001
2113
  * ```
2002
2114
  */
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 }) {
2115
+ 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
2116
  const theme = require_MonthList.useCalendarTheme();
2005
2117
  const Renderer = renderEvent ?? DefaultBar;
2118
+ const cellEnabled = !!onPressCell || !!onLongPressCell || !!onCreateEvent;
2119
+ const bandsFor = (resource) => businessHours ? (0, _super_calendar_core.closedHourBands)(date, (d) => businessHours(d, resource), startHour, endHour) : [];
2006
2120
  const hours = (0, react.useMemo)(() => Array.from({ length: Math.max(0, endHour - startHour) }, (_, i) => startHour + i), [startHour, endHour]);
2007
2121
  const trackWidth = (endHour - startHour) * hourWidth;
2008
2122
  const byResource = (0, react.useMemo)(() => {
@@ -2055,58 +2169,84 @@ function ResourceTimeline({ date, orientation = "horizontal", resources, events,
2055
2169
  { borderLeftColor: theme.colors.gridLine },
2056
2170
  theme.containers.resourceRow
2057
2171
  ],
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,
2172
+ children: [
2173
+ bandsFor(resource).map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2174
+ pointerEvents: "none",
2175
+ testID: "resource-hours-shade",
2176
+ style: [styles.vshadeBand, {
2177
+ top: (b.start - startHour) * hourHeight,
2178
+ height: (b.end - b.start) * hourHeight,
2179
+ backgroundColor: theme.colors.outsideHoursBackground
2180
+ }]
2181
+ }, `shade-${b.start}`)),
2182
+ hours.slice(1).map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2183
+ pointerEvents: "none",
2184
+ style: [styles.vgridLine, {
2185
+ top: (h - startHour) * hourHeight,
2186
+ backgroundColor: theme.colors.gridLine
2187
+ }]
2188
+ }, h)),
2189
+ cellEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LaneInteractionLayer, {
2190
+ resource,
2074
2191
  vertical: true,
2075
2192
  hourSize: hourHeight,
2076
- left,
2077
- top,
2078
- width,
2079
- height,
2080
- rendererSize: {
2081
- width: 0,
2082
- height
2083
- },
2193
+ startHour,
2194
+ date,
2084
2195
  snapMinutes: dragStepMinutes,
2085
- Renderer,
2086
- onPress,
2087
- onDragEvent,
2196
+ onPressCell,
2197
+ onLongPressCell,
2198
+ onCreateEvent,
2088
2199
  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",
2200
+ }) : null,
2201
+ positioned.map((pe, idx) => {
2202
+ const top = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourHeight;
2203
+ const bottomPx = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourHeight;
2204
+ const height = Math.max(bottomPx - top, 2);
2205
+ const lanePct = 100 / pe.columns;
2206
+ const left = `${pe.column * lanePct}%`;
2207
+ const width = `${lanePct}%`;
2208
+ const onPress = () => onPressEvent?.(pe.event);
2209
+ if (onDragEvent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResourceBar, {
2210
+ pe,
2211
+ vertical: true,
2212
+ hourSize: hourHeight,
2096
2213
  left,
2097
- width,
2098
2214
  top,
2215
+ width,
2099
2216
  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
- })]
2217
+ rendererSize: {
2218
+ width: 0,
2219
+ height
2220
+ },
2221
+ snapMinutes: dragStepMinutes,
2222
+ Renderer,
2223
+ onPress,
2224
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2225
+ onDragEvent,
2226
+ theme
2227
+ }, idx);
2228
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2229
+ onPress,
2230
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2231
+ accessibilityRole: "button",
2232
+ accessibilityLabel: pe.event.title,
2233
+ style: {
2234
+ position: "absolute",
2235
+ left,
2236
+ width,
2237
+ top,
2238
+ height,
2239
+ padding: 1
2240
+ },
2241
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, {
2242
+ event: pe.event,
2243
+ width: 0,
2244
+ height,
2245
+ onPress
2246
+ })
2247
+ }, idx);
2248
+ })
2249
+ ]
2110
2250
  }, resource.id);
2111
2251
  })]
2112
2252
  })
@@ -2163,52 +2303,78 @@ function ResourceTimeline({ date, orientation = "horizontal", resources, events,
2163
2303
  })
2164
2304
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
2165
2305
  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,
2306
+ children: [
2307
+ bandsFor(resource).map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2308
+ pointerEvents: "none",
2309
+ testID: "resource-hours-shade",
2310
+ style: [styles.hshadeBand, {
2311
+ left: (b.start - startHour) * hourWidth,
2312
+ width: (b.end - b.start) * hourWidth,
2313
+ backgroundColor: theme.colors.outsideHoursBackground
2314
+ }]
2315
+ }, `shade-${b.start}`)),
2316
+ hours.slice(1).map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
2317
+ pointerEvents: "none",
2318
+ style: [styles.gridLine, {
2319
+ left: (h - startHour) * hourWidth,
2320
+ backgroundColor: theme.colors.gridLine
2321
+ }]
2322
+ }, h)),
2323
+ cellEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LaneInteractionLayer, {
2324
+ resource,
2180
2325
  vertical: false,
2181
2326
  hourSize: hourWidth,
2182
- left,
2183
- top: pe.column * laneHeight,
2184
- width,
2185
- height: laneHeight,
2186
- rendererSize: { width },
2327
+ startHour,
2328
+ date,
2187
2329
  snapMinutes: dragStepMinutes,
2188
- Renderer,
2189
- onPress,
2190
- onDragEvent,
2330
+ onPressCell,
2331
+ onLongPressCell,
2332
+ onCreateEvent,
2191
2333
  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",
2334
+ }) : null,
2335
+ positioned.map((pe, idx) => {
2336
+ const left = clamp(pe.startHours - startHour, 0, endHour - startHour) * hourWidth;
2337
+ const right = clamp(pe.startHours + pe.durationHours - startHour, 0, endHour - startHour) * hourWidth;
2338
+ const width = Math.max(right - left, 2);
2339
+ const laneHeight = rowHeight / pe.columns;
2340
+ const onPress = () => onPressEvent?.(pe.event);
2341
+ if (onDragEvent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResourceBar, {
2342
+ pe,
2343
+ vertical: false,
2344
+ hourSize: hourWidth,
2199
2345
  left,
2200
- width,
2201
2346
  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
2347
  width,
2208
- onPress
2209
- })
2210
- }, idx);
2211
- })]
2348
+ height: laneHeight,
2349
+ rendererSize: { width },
2350
+ snapMinutes: dragStepMinutes,
2351
+ Renderer,
2352
+ onPress,
2353
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2354
+ onDragEvent,
2355
+ theme
2356
+ }, idx);
2357
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
2358
+ onPress,
2359
+ onLongPress: onLongPressEvent ? () => onLongPressEvent(pe.event) : void 0,
2360
+ accessibilityRole: "button",
2361
+ accessibilityLabel: pe.event.title,
2362
+ style: {
2363
+ position: "absolute",
2364
+ left,
2365
+ width,
2366
+ top: pe.column * laneHeight,
2367
+ height: laneHeight,
2368
+ padding: 1
2369
+ },
2370
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, {
2371
+ event: pe.event,
2372
+ width,
2373
+ onPress
2374
+ })
2375
+ }, idx);
2376
+ })
2377
+ ]
2212
2378
  })]
2213
2379
  }, resource.id);
2214
2380
  })]
@@ -2298,6 +2464,34 @@ const styles = react_native.StyleSheet.create({
2298
2464
  left: 0,
2299
2465
  right: 0,
2300
2466
  height: react_native.StyleSheet.hairlineWidth
2467
+ },
2468
+ vcreateGhost: {
2469
+ position: "absolute",
2470
+ left: 2,
2471
+ right: 2,
2472
+ borderWidth: 1,
2473
+ borderRadius: 6,
2474
+ opacity: 0,
2475
+ zIndex: 2
2476
+ },
2477
+ hcreateGhost: {
2478
+ position: "absolute",
2479
+ top: 2,
2480
+ bottom: 2,
2481
+ borderWidth: 1,
2482
+ borderRadius: 6,
2483
+ opacity: 0,
2484
+ zIndex: 2
2485
+ },
2486
+ hshadeBand: {
2487
+ position: "absolute",
2488
+ top: 0,
2489
+ bottom: 0
2490
+ },
2491
+ vshadeBand: {
2492
+ position: "absolute",
2493
+ left: 0,
2494
+ right: 0
2301
2495
  }
2302
2496
  });
2303
2497
  //#endregion