react-native-ll-calendar 0.15.1 → 0.17.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.
Files changed (42) hide show
  1. package/README.md +102 -12
  2. package/lib/module/calendar/month-calendar/MonthCalendar.js +3 -1
  3. package/lib/module/calendar/month-calendar/MonthCalendar.js.map +1 -1
  4. package/lib/module/calendar/month-calendar/view/MonthCalendarEventOverlay.js +21 -0
  5. package/lib/module/calendar/month-calendar/view/MonthCalendarEventOverlay.js.map +1 -0
  6. package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js +2 -0
  7. package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js.map +1 -1
  8. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js +118 -66
  9. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js.map +1 -1
  10. package/lib/module/calendar/resources-calendar/ResourcesCalendar.js +102 -50
  11. package/lib/module/calendar/resources-calendar/ResourcesCalendar.js.map +1 -1
  12. package/lib/module/calendar/week-resources-calendar/WeekPanel.js +402 -0
  13. package/lib/module/calendar/week-resources-calendar/WeekPanel.js.map +1 -0
  14. package/lib/module/calendar/week-resources-calendar/WeekResourcesCalendar.js +119 -0
  15. package/lib/module/calendar/week-resources-calendar/WeekResourcesCalendar.js.map +1 -0
  16. package/lib/module/index.js +2 -0
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts +8 -0
  19. package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts.map +1 -1
  20. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarEventOverlay.d.ts +16 -0
  21. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarEventOverlay.d.ts.map +1 -0
  22. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts +3 -0
  23. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts.map +1 -1
  24. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts +3 -0
  25. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts.map +1 -1
  26. package/lib/typescript/src/calendar/resources-calendar/ResourcesCalendar.d.ts +8 -1
  27. package/lib/typescript/src/calendar/resources-calendar/ResourcesCalendar.d.ts.map +1 -1
  28. package/lib/typescript/src/calendar/week-resources-calendar/WeekPanel.d.ts +33 -0
  29. package/lib/typescript/src/calendar/week-resources-calendar/WeekPanel.d.ts.map +1 -0
  30. package/lib/typescript/src/calendar/week-resources-calendar/WeekResourcesCalendar.d.ts +39 -0
  31. package/lib/typescript/src/calendar/week-resources-calendar/WeekResourcesCalendar.d.ts.map +1 -0
  32. package/lib/typescript/src/index.d.ts +3 -0
  33. package/lib/typescript/src/index.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/calendar/month-calendar/MonthCalendar.tsx +16 -1
  36. package/src/calendar/month-calendar/view/MonthCalendarEventOverlay.tsx +37 -0
  37. package/src/calendar/month-calendar/view/MonthCalendarViewItem.tsx +5 -0
  38. package/src/calendar/month-calendar/view/MonthCalendarWeekRow.tsx +186 -113
  39. package/src/calendar/resources-calendar/ResourcesCalendar.tsx +181 -96
  40. package/src/calendar/week-resources-calendar/WeekPanel.tsx +562 -0
  41. package/src/calendar/week-resources-calendar/WeekResourcesCalendar.tsx +174 -0
  42. package/src/index.tsx +4 -0
@@ -1,4 +1,10 @@
1
- import React, { useCallback, useMemo, useRef, useState } from 'react';
1
+ import React, {
2
+ useCallback,
3
+ useMemo,
4
+ useRef,
5
+ useState,
6
+ type ReactNode,
7
+ } from 'react';
2
8
  import type {
3
9
  CalendarEvent,
4
10
  CalendarResource,
@@ -8,6 +14,7 @@ import {
8
14
  type NativeSyntheticEvent,
9
15
  type TextStyle,
10
16
  type ViewStyle,
17
+ Platform,
11
18
  RefreshControl,
12
19
  ScrollView,
13
20
  StyleSheet,
@@ -43,11 +50,18 @@ type ResourcesCalendarProps = {
43
50
  bottomSpacing?: number;
44
51
  eventTextStyle?: (event: CalendarEvent) => TextStyle;
45
52
  eventEllipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
53
+ renderEventOverlay?: (event: CalendarEvent) => ReactNode;
46
54
  dateCellContainerStyle?: (date: Date) => ViewStyle;
47
55
  cellContainerStyle?: (resource: CalendarResource, date: Date) => ViewStyle;
48
56
  hiddenMonth?: boolean;
49
57
  allowFontScaling?: boolean;
50
58
  resourceNameLayout?: 'fixed-column' | 'inline-band';
59
+ /**
60
+ * When true, a transparent layer is placed above events in each cell so
61
+ * `onPressCell` / `onLongPressCell` receive touches for the full cell area.
62
+ * Event taps are disabled while this is on (overlay captures the gesture).
63
+ */
64
+ prioritizeCellInteraction?: boolean;
51
65
  };
52
66
 
53
67
  const DEFAULT_DATE_COLUMN_WIDTH = 60;
@@ -69,6 +83,7 @@ type ResourceRowProps = {
69
83
  eventHeight: number;
70
84
  eventTextStyle?: (event: CalendarEvent) => TextStyle;
71
85
  eventEllipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
86
+ renderEventOverlay?: (event: CalendarEvent) => ReactNode;
72
87
  cellContainerStyle?: (resource: CalendarResource, date: Date) => ViewStyle;
73
88
  allowFontScaling?: boolean;
74
89
  onLayout?: (height: number) => void;
@@ -76,6 +91,7 @@ type ResourceRowProps = {
76
91
  scrollOffset: number;
77
92
  renderResourceNameLabel?: (resource: CalendarResource) => React.JSX.Element;
78
93
  };
94
+ prioritizeCellInteraction?: boolean;
79
95
  };
80
96
 
81
97
  function ResourceRow({
@@ -92,10 +108,12 @@ function ResourceRow({
92
108
  eventHeight,
93
109
  eventTextStyle,
94
110
  eventEllipsizeMode,
111
+ renderEventOverlay,
95
112
  cellContainerStyle,
96
113
  allowFontScaling,
97
114
  onLayout,
98
115
  inlineBand,
116
+ prioritizeCellInteraction,
99
117
  }: ResourceRowProps) {
100
118
  const resourceEvents = eventsByResourceId.get(resource.id) ?? [];
101
119
  const eventPosition = new ResourcesCalendarEventPosition();
@@ -169,106 +187,152 @@ function ResourceRow({
169
187
  }
170
188
  }
171
189
 
172
- return (
190
+ const showPrioritizedCellOverlay =
191
+ prioritizeCellInteraction === true &&
192
+ (onPressCell != null || onLongPressCell != null);
193
+
194
+ const cellWrapperStyle = [
195
+ styles.contentCellContainer,
196
+ { width: dateColumnWidth },
197
+ { zIndex: dates.length - dateIndex },
198
+ ];
199
+
200
+ const eventRows = cellEvents.map((event, rowIndex) => {
201
+ if (typeof event === 'number') {
202
+ return (
203
+ <View
204
+ key={`spacer-${rowIndex}`}
205
+ style={{
206
+ height: eventHeight,
207
+ marginBottom: EVENT_GAP,
208
+ }}
209
+ />
210
+ );
211
+ }
212
+
213
+ const rawStartDjs = dayjs(event.start);
214
+ const startDjs = dateIndex === 0 ? djs : dayjs(event.start);
215
+ const endDjs = dayjs(event.end);
216
+ const diffDays = endDjs
217
+ .startOf('day')
218
+ .diff(startDjs.startOf('day'), 'day');
219
+ const isPrevDateEvent =
220
+ dateIndex === 0 && rawStartDjs.isBefore(djs);
221
+
222
+ // Calculate event width based on the number of days it spans
223
+ let width =
224
+ (diffDays + 1) * dateColumnWidth -
225
+ EVENT_GAP * 2 -
226
+ CELL_BORDER_WIDTH * 2;
227
+ if (isPrevDateEvent) {
228
+ width += EVENT_GAP + 1;
229
+ }
230
+
231
+ // Record position info
232
+ eventPosition.push({
233
+ resourceId: resource.id,
234
+ startDate: startDjs.toDate(),
235
+ days: diffDays + 1,
236
+ rowNum: rowIndex + 1,
237
+ });
238
+
239
+ const eventOverlayNode = renderEventOverlay?.(event);
240
+ const showEventOverlay =
241
+ renderEventOverlay != null &&
242
+ eventOverlayNode != null &&
243
+ eventOverlayNode !== false;
244
+
245
+ return (
246
+ <View
247
+ key={event.id}
248
+ pointerEvents={showPrioritizedCellOverlay ? 'none' : 'auto'}
249
+ style={[
250
+ styles.eventOuter,
251
+ { width, height: eventHeight },
252
+ isPrevDateEvent ? styles.prevDateEvent : {},
253
+ ]}
254
+ >
255
+ <TouchableOpacity
256
+ data-component-name="resources-calendar-event"
257
+ style={[
258
+ styles.event,
259
+ {
260
+ backgroundColor: event.backgroundColor,
261
+ borderColor: event.borderColor,
262
+ ...(event.borderStyle !== undefined && {
263
+ borderStyle: event.borderStyle,
264
+ }),
265
+ ...(event.borderWidth !== undefined && {
266
+ borderWidth: event.borderWidth,
267
+ }),
268
+ ...(event.borderRadius !== undefined && {
269
+ borderRadius: event.borderRadius,
270
+ }),
271
+ },
272
+ ]}
273
+ onPress={() => onPressEvent?.(event)}
274
+ onLongPress={() => onLongPressEvent?.(event)}
275
+ delayLongPress={delayLongPressEvent}
276
+ >
277
+ <Text
278
+ numberOfLines={1}
279
+ ellipsizeMode={eventEllipsizeMode ?? 'tail'}
280
+ allowFontScaling={allowFontScaling}
281
+ style={[
282
+ styles.eventTitle,
283
+ { color: event.color },
284
+ eventTextStyle?.(event),
285
+ ]}
286
+ >
287
+ {event.title}
288
+ </Text>
289
+ </TouchableOpacity>
290
+ {showEventOverlay ? (
291
+ <View
292
+ style={styles.eventOverlayHost}
293
+ pointerEvents="box-none"
294
+ >
295
+ {eventOverlayNode}
296
+ </View>
297
+ ) : null}
298
+ </View>
299
+ );
300
+ });
301
+
302
+ const cellInner = (
303
+ <>
304
+ <View
305
+ style={[
306
+ styles.contentCellContainerInner,
307
+ cellContainerStyle?.(resource, date),
308
+ ]}
309
+ />
310
+ {eventRows}
311
+ </>
312
+ );
313
+
314
+ return showPrioritizedCellOverlay ? (
315
+ <View key={date.getTime()} style={cellWrapperStyle}>
316
+ {cellInner}
317
+ <TouchableOpacity
318
+ accessible={false}
319
+ style={styles.cellInteractionOverlay}
320
+ activeOpacity={1}
321
+ onPress={() => onPressCell?.(resource, date)}
322
+ onLongPress={() => onLongPressCell?.(resource, date)}
323
+ delayLongPress={delayLongPressCell}
324
+ />
325
+ </View>
326
+ ) : (
173
327
  <TouchableOpacity
174
328
  key={date.getTime()}
175
- style={[
176
- styles.contentCellContainer,
177
- { width: dateColumnWidth },
178
- { zIndex: dates.length - dateIndex },
179
- ]}
329
+ style={cellWrapperStyle}
180
330
  onPress={() => onPressCell?.(resource, date)}
181
331
  onLongPress={() => onLongPressCell?.(resource, date)}
182
332
  delayLongPress={delayLongPressCell}
183
333
  activeOpacity={1}
184
334
  >
185
- <View
186
- style={[
187
- styles.contentCellContainerInner,
188
- cellContainerStyle?.(resource, date),
189
- ]}
190
- />
191
- {cellEvents.map((event, rowIndex) => {
192
- if (typeof event === 'number') {
193
- return (
194
- <View
195
- key={`spacer-${rowIndex}`}
196
- style={{
197
- height: eventHeight,
198
- marginBottom: EVENT_GAP,
199
- }}
200
- />
201
- );
202
- }
203
-
204
- const rawStartDjs = dayjs(event.start);
205
- const startDjs = dateIndex === 0 ? djs : dayjs(event.start);
206
- const endDjs = dayjs(event.end);
207
- const diffDays = endDjs
208
- .startOf('day')
209
- .diff(startDjs.startOf('day'), 'day');
210
- const isPrevDateEvent =
211
- dateIndex === 0 && rawStartDjs.isBefore(djs);
212
-
213
- // Calculate event width based on the number of days it spans
214
- let width =
215
- (diffDays + 1) * dateColumnWidth -
216
- EVENT_GAP * 2 -
217
- CELL_BORDER_WIDTH * 2;
218
- if (isPrevDateEvent) {
219
- width += EVENT_GAP + 1;
220
- }
221
-
222
- // Record position info
223
- eventPosition.push({
224
- resourceId: resource.id,
225
- startDate: startDjs.toDate(),
226
- days: diffDays + 1,
227
- rowNum: rowIndex + 1,
228
- });
229
-
230
- return (
231
- <TouchableOpacity
232
- data-component-name="resources-calendar-event"
233
- key={event.id}
234
- style={[
235
- styles.event,
236
- {
237
- backgroundColor: event.backgroundColor,
238
- borderColor: event.borderColor,
239
- width,
240
- height: eventHeight,
241
- ...(event.borderStyle !== undefined && {
242
- borderStyle: event.borderStyle,
243
- }),
244
- ...(event.borderWidth !== undefined && {
245
- borderWidth: event.borderWidth,
246
- }),
247
- ...(event.borderRadius !== undefined && {
248
- borderRadius: event.borderRadius,
249
- }),
250
- },
251
- isPrevDateEvent ? styles.prevDateEvent : {},
252
- ]}
253
- onPress={() => onPressEvent?.(event)}
254
- onLongPress={() => onLongPressEvent?.(event)}
255
- delayLongPress={delayLongPressEvent}
256
- >
257
- <Text
258
- numberOfLines={1}
259
- ellipsizeMode={eventEllipsizeMode ?? 'tail'}
260
- allowFontScaling={allowFontScaling}
261
- style={[
262
- styles.eventTitle,
263
- { color: event.color },
264
- eventTextStyle?.(event),
265
- ]}
266
- >
267
- {event.title}
268
- </Text>
269
- </TouchableOpacity>
270
- );
271
- })}
335
+ {cellInner}
272
336
  </TouchableOpacity>
273
337
  );
274
338
  })}
@@ -421,6 +485,7 @@ export function ResourcesCalendar(props: ResourcesCalendarProps) {
421
485
  eventHeight: props.eventHeight ?? DEFAULT_EVENT_HEIGHT,
422
486
  eventTextStyle: props.eventTextStyle,
423
487
  eventEllipsizeMode: props.eventEllipsizeMode,
488
+ renderEventOverlay: props.renderEventOverlay,
424
489
  cellContainerStyle: props.cellContainerStyle,
425
490
  allowFontScaling: props.allowFontScaling,
426
491
  inlineBand: isInlineBand
@@ -429,6 +494,7 @@ export function ResourcesCalendar(props: ResourcesCalendarProps) {
429
494
  renderResourceNameLabel: props.renderResourceNameLabel,
430
495
  }
431
496
  : undefined,
497
+ prioritizeCellInteraction: props.prioritizeCellInteraction,
432
498
  };
433
499
 
434
500
  const resourceNameColumn = !isInlineBand ? (
@@ -696,6 +762,15 @@ const styles = StyleSheet.create({
696
762
  right: 0,
697
763
  bottom: 0,
698
764
  },
765
+ cellInteractionOverlay: {
766
+ ...StyleSheet.absoluteFillObject,
767
+ zIndex: 1000,
768
+ backgroundColor: 'transparent',
769
+ ...Platform.select({
770
+ android: { elevation: 12 },
771
+ default: {},
772
+ }),
773
+ },
699
774
  container: {
700
775
  flex: 1,
701
776
  flexDirection: 'row',
@@ -739,14 +814,24 @@ const styles = StyleSheet.create({
739
814
  fontSize: 12,
740
815
  color: 'black',
741
816
  },
817
+ eventOuter: {
818
+ position: 'relative',
819
+ marginTop: EVENT_GAP,
820
+ marginLeft: EVENT_GAP,
821
+ },
742
822
  event: {
823
+ flex: 1,
824
+ width: '100%',
825
+ height: '100%',
743
826
  borderWidth: 0.5,
744
827
  borderRadius: 4,
745
828
  paddingHorizontal: 4,
746
829
  flexDirection: 'row',
747
830
  alignItems: 'center',
748
- marginTop: EVENT_GAP,
749
- marginLeft: EVENT_GAP,
831
+ },
832
+ eventOverlayHost: {
833
+ ...StyleSheet.absoluteFillObject,
834
+ pointerEvents: 'box-none',
750
835
  },
751
836
  prevDateEvent: {
752
837
  marginLeft: -1,