react-native-ll-calendar 0.3.0 → 0.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.
Files changed (29) hide show
  1. package/README.md +46 -32
  2. package/lib/module/calendar/month-calendar/MonthCalendar.js +21 -38
  3. package/lib/module/calendar/month-calendar/MonthCalendar.js.map +1 -1
  4. package/lib/module/calendar/month-calendar/logic/useEvents.js +3 -7
  5. package/lib/module/calendar/month-calendar/logic/useEvents.js.map +1 -1
  6. package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js +41 -53
  7. package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js.map +1 -1
  8. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekDayRow.js +62 -0
  9. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekDayRow.js.map +1 -0
  10. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js +47 -52
  11. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js.map +1 -1
  12. package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts +4 -1
  13. package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts.map +1 -1
  14. package/lib/typescript/src/calendar/month-calendar/logic/useEvents.d.ts.map +1 -1
  15. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts +4 -1
  16. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts.map +1 -1
  17. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekDayRow.d.ts +11 -0
  18. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekDayRow.d.ts.map +1 -0
  19. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts +4 -4
  20. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts.map +1 -1
  21. package/lib/typescript/src/types/month-calendar.d.ts +3 -0
  22. package/lib/typescript/src/types/month-calendar.d.ts.map +1 -1
  23. package/package.json +1 -1
  24. package/src/calendar/month-calendar/MonthCalendar.tsx +25 -39
  25. package/src/calendar/month-calendar/logic/useEvents.tsx +3 -5
  26. package/src/calendar/month-calendar/view/MonthCalendarViewItem.tsx +51 -48
  27. package/src/calendar/month-calendar/view/MonthCalendarWeekDayRow.tsx +79 -0
  28. package/src/calendar/month-calendar/view/MonthCalendarWeekRow.tsx +68 -81
  29. package/src/types/month-calendar.ts +3 -0
@@ -21,15 +21,18 @@ import { useEvents } from '../logic/useEvents';
21
21
  import { CELL_BORDER_WIDTH } from '../../../constants/size';
22
22
  import { RefreshControl } from 'react-native';
23
23
  import { useCallback, useMemo, useState } from 'react';
24
+ import { MonthCalendarWeekDayRow } from './MonthCalendarWeekDayRow';
24
25
 
25
26
  export const MonthCalendarViewItem = (props: {
26
27
  month: string;
27
28
  weekStartsOn: WeekStartsOn;
28
29
  events: CalendarEvent[];
29
30
  onPressEvent?: (event: CalendarEvent) => void;
31
+ onLongPressEvent?: (event: CalendarEvent) => void;
32
+ delayLongPressEvent?: number;
30
33
  onPressCell?: (date: Date) => void;
31
34
  onLongPressCell?: (date: Date) => void;
32
- delayLongPress?: number;
35
+ delayLongPressCell?: number;
33
36
  flatListIndex: number;
34
37
  onRefresh?: () => void;
35
38
  refreshing?: boolean;
@@ -41,33 +44,17 @@ export const MonthCalendarViewItem = (props: {
41
44
  todayCellTextStyle?: TextStyle;
42
45
  hiddenMonth?: boolean;
43
46
  monthFormat?: string;
47
+ stickyHeaderEnabled?: boolean;
44
48
  }) => {
45
- const {
46
- month,
47
- weekStartsOn,
48
- events,
49
- onPressEvent,
50
- onPressCell,
51
- onLongPressCell,
52
- delayLongPress,
53
- flatListIndex,
54
- onRefresh,
55
- refreshing,
56
- dayCellContainerStyle,
57
- dayCellTextStyle,
58
- locale,
59
- weekdayCellContainerStyle,
60
- weekdayCellTextStyle,
61
- todayCellTextStyle,
62
- hiddenMonth,
63
- monthFormat = 'YYYY/MM',
64
- } = props;
65
49
  const { width } = useWindowDimensions();
66
50
  const eventPosition = new MonthCalendarEventPosition();
67
- const date = new Date(month);
51
+ const date = new Date(props.month);
68
52
  const dateDjs = dayjs(date);
69
- const startDate = monthlyStartDate({ date, weekStartsOn });
70
- const endDate = monthlyEndDate({ date, weekStartsOn });
53
+ const startDate = monthlyStartDate({
54
+ date,
55
+ weekStartsOn: props.weekStartsOn,
56
+ });
57
+ const endDate = monthlyEndDate({ date, weekStartsOn: props.weekStartsOn });
71
58
  const endDjs = dayjs(endDate);
72
59
  const weeks: dayjs.Dayjs[][] = [];
73
60
  let currentDate = dayjs(startDate);
@@ -79,7 +66,10 @@ export const MonthCalendarViewItem = (props: {
79
66
  currentDate = currentDate.add(7, 'day');
80
67
  }
81
68
 
82
- const { eventsGroupByWeekId } = useEvents({ events, weekStartsOn });
69
+ const { eventsGroupByWeekId } = useEvents({
70
+ events: props.events,
71
+ weekStartsOn: props.weekStartsOn,
72
+ });
83
73
 
84
74
  const [bodyHeight, setBodyHeight] = useState(0);
85
75
  const onLayoutBody = useCallback((e: LayoutChangeEvent) => {
@@ -100,29 +90,40 @@ export const MonthCalendarViewItem = (props: {
100
90
  return (bodyHeight - monthRowHeight - weekdayRowHeight) / weeks.length;
101
91
  }, [bodyHeight, monthRowHeight, weekdayRowHeight, weeks.length]);
102
92
 
93
+ const stickyHeaderIndices = useMemo(() => {
94
+ return props.stickyHeaderEnabled ? [0] : [];
95
+ }, [props.stickyHeaderEnabled]);
96
+
103
97
  return (
104
98
  <ScrollView
105
- style={[styles.container, { width, zIndex: flatListIndex }]}
99
+ style={[styles.container, { width, zIndex: props.flatListIndex }]}
106
100
  refreshControl={
107
- <RefreshControl refreshing={!!refreshing} onRefresh={onRefresh} />
101
+ <RefreshControl
102
+ refreshing={!!props.refreshing}
103
+ onRefresh={props.onRefresh}
104
+ />
108
105
  }
109
106
  onLayout={onLayoutBody}
107
+ stickyHeaderIndices={stickyHeaderIndices}
110
108
  >
111
- {hiddenMonth ? (
112
- <View style={styles.blankMonthContainer} />
113
- ) : (
114
- <View style={styles.monthContainer} onLayout={onLayoutMonthRow}>
115
- <Text style={styles.monthText}>{dateDjs.format(monthFormat)}</Text>
109
+ <View>
110
+ {props.hiddenMonth ? (
111
+ <View style={styles.blankMonthContainer} />
112
+ ) : (
113
+ <View style={styles.monthContainer} onLayout={onLayoutMonthRow}>
114
+ <Text style={styles.monthText}>
115
+ {dateDjs.format(props.monthFormat ?? 'YYYY/MM')}
116
+ </Text>
117
+ </View>
118
+ )}
119
+ <View onLayout={onLayoutWeekdayRow}>
120
+ <MonthCalendarWeekDayRow
121
+ dates={weeks[0] ?? []}
122
+ locale={props.locale}
123
+ weekdayCellContainerStyle={props.weekdayCellContainerStyle}
124
+ weekdayCellTextStyle={props.weekdayCellTextStyle}
125
+ />
116
126
  </View>
117
- )}
118
- <View onLayout={onLayoutWeekdayRow}>
119
- <MonthCalendarWeekRow
120
- dates={weeks[0] ?? []}
121
- isWeekdayHeader={true}
122
- locale={locale}
123
- weekdayCellContainerStyle={weekdayCellContainerStyle}
124
- weekdayCellTextStyle={weekdayCellTextStyle}
125
- />
126
127
  </View>
127
128
  <View>
128
129
  {weeks.map((week, index) => {
@@ -138,14 +139,16 @@ export const MonthCalendarViewItem = (props: {
138
139
  dates={week}
139
140
  events={weekEvents}
140
141
  eventPosition={eventPosition}
141
- onPressEvent={onPressEvent}
142
- onPressCell={onPressCell}
143
- onLongPressCell={onLongPressCell}
144
- delayLongPress={delayLongPress}
145
- dayCellContainerStyle={dayCellContainerStyle}
146
- dayCellTextStyle={dayCellTextStyle}
142
+ onPressEvent={props.onPressEvent}
143
+ onLongPressEvent={props.onLongPressEvent}
144
+ delayLongPressEvent={props.delayLongPressEvent}
145
+ onPressCell={props.onPressCell}
146
+ onLongPressCell={props.onLongPressCell}
147
+ delayLongPressCell={props.delayLongPressCell}
148
+ dayCellContainerStyle={props.dayCellContainerStyle}
149
+ dayCellTextStyle={props.dayCellTextStyle}
147
150
  weekRowMinHeight={weekRowMinHeight}
148
- todayCellTextStyle={todayCellTextStyle}
151
+ todayCellTextStyle={props.todayCellTextStyle}
149
152
  />
150
153
  );
151
154
  })}
@@ -0,0 +1,79 @@
1
+ import dayjs from 'dayjs';
2
+ import en from 'dayjs/locale/en';
3
+ import { StyleSheet } from 'react-native';
4
+ import { Text, View, type ViewStyle } from 'react-native';
5
+ import type { WeekdayNum } from '../../../types/month-calendar';
6
+ import { CELL_BORDER_WIDTH } from '../../../constants/size';
7
+ import type { TextStyle } from 'react-native';
8
+
9
+ export const MonthCalendarWeekDayRow = (props: {
10
+ locale?: ILocale;
11
+ dates: dayjs.Dayjs[];
12
+ weekdayCellContainerStyle?: (weekDayNum: WeekdayNum) => ViewStyle;
13
+ weekdayCellTextStyle?: (weekDayNum: WeekdayNum) => TextStyle;
14
+ }) => {
15
+ return (
16
+ <View style={styles.container}>
17
+ {props.dates.map((djs, dateIndex) => {
18
+ const text = djs.locale(props.locale ?? en).format('ddd');
19
+
20
+ return (
21
+ <View
22
+ key={djs.get('d')}
23
+ style={[styles.dayCellContainer, { zIndex: 7 - dateIndex }]}
24
+ >
25
+ <View
26
+ style={[
27
+ styles.dayCellInner,
28
+ props.weekdayCellContainerStyle?.(djs.day()),
29
+ ]}
30
+ />
31
+ <View style={styles.dayCellLabel}>
32
+ <Text
33
+ style={[
34
+ styles.dayCellText,
35
+ props.weekdayCellTextStyle?.(djs.day()),
36
+ ]}
37
+ >
38
+ {text}
39
+ </Text>
40
+ </View>
41
+ </View>
42
+ );
43
+ })}
44
+ </View>
45
+ );
46
+ };
47
+
48
+ const styles = StyleSheet.create({
49
+ container: {
50
+ width: '100%',
51
+ display: 'flex',
52
+ flexDirection: 'row',
53
+ flex: 1,
54
+ backgroundColor: 'white',
55
+ },
56
+ dayCellContainer: {
57
+ flex: 1,
58
+ borderRightWidth: CELL_BORDER_WIDTH,
59
+ borderBottomWidth: CELL_BORDER_WIDTH,
60
+ borderColor: 'lightslategrey',
61
+ backgroundColor: 'white',
62
+ position: 'relative',
63
+ },
64
+ dayCellInner: {
65
+ position: 'absolute',
66
+ top: 0,
67
+ left: 0,
68
+ right: 0,
69
+ bottom: 0,
70
+ },
71
+ dayCellLabel: {
72
+ paddingVertical: 1,
73
+ paddingHorizontal: 2,
74
+ },
75
+ dayCellText: {
76
+ textAlign: 'center',
77
+ fontSize: 12,
78
+ },
79
+ });
@@ -1,5 +1,4 @@
1
1
  import dayjs from 'dayjs';
2
- import en from 'dayjs/locale/en';
3
2
  import { StyleSheet, useWindowDimensions } from 'react-native';
4
3
  import { Text, TouchableOpacity, View, type ViewStyle } from 'react-native';
5
4
  import type { CalendarEvent, WeekdayNum } from '../../../types/month-calendar';
@@ -9,76 +8,55 @@ import type { TextStyle } from 'react-native';
9
8
 
10
9
  export const MonthCalendarWeekRow = (props: {
11
10
  dates: dayjs.Dayjs[];
12
- isWeekdayHeader?: boolean;
13
- events?: CalendarEvent[];
11
+ events: CalendarEvent[];
14
12
  eventPosition?: MonthCalendarEventPosition;
15
13
  onPressEvent?: (event: CalendarEvent) => void;
14
+ onLongPressEvent?: (event: CalendarEvent) => void;
15
+ delayLongPressEvent?: number;
16
16
  onPressCell?: (date: Date) => void;
17
17
  onLongPressCell?: (date: Date) => void;
18
- delayLongPress?: number;
18
+ delayLongPressCell?: number;
19
19
  dayCellContainerStyle?: (date: Date) => ViewStyle;
20
20
  dayCellTextStyle?: (date: Date) => TextStyle;
21
- locale?: ILocale;
22
21
  weekdayCellContainerStyle?: (weekDayNum: WeekdayNum) => ViewStyle;
23
22
  weekdayCellTextStyle?: (weekDayNum: WeekdayNum) => TextStyle;
24
23
  weekRowMinHeight?: number;
25
24
  todayCellTextStyle?: TextStyle;
26
25
  }) => {
27
- const {
28
- dates,
29
- isWeekdayHeader,
30
- events = [],
31
- eventPosition,
32
- onPressEvent,
33
- onPressCell,
34
- onLongPressCell,
35
- delayLongPress = 500,
36
- dayCellContainerStyle,
37
- dayCellTextStyle,
38
- locale = en,
39
- weekdayCellContainerStyle,
40
- weekdayCellTextStyle,
41
- weekRowMinHeight,
42
- todayCellTextStyle,
43
- } = props;
44
26
  const eventHeight = 26;
45
27
  const { width: screenWidth } = useWindowDimensions();
46
28
  const dateColumnWidth = screenWidth / 7;
47
- const weekId = dates[0]?.format('YYYY-MM-DD');
48
- if (weekId && eventPosition) {
49
- eventPosition.resetResource(weekId);
29
+ const weekId = props.dates[0]?.format('YYYY-MM-DD');
30
+ if (weekId && props.eventPosition) {
31
+ props.eventPosition.resetResource(weekId);
50
32
  }
51
33
 
52
34
  return (
53
35
  <View style={styles.container}>
54
- {dates.map((djs, dateIndex) => {
55
- const text = isWeekdayHeader
56
- ? djs.locale(locale).format('ddd')
57
- : djs.format('D');
58
- const filteredEvents = isWeekdayHeader
59
- ? []
60
- : events
61
- .filter((event) => {
62
- const startDjs = dayjs(event.start);
63
- return (
64
- startDjs.format('YYYY-MM-DD') === djs.format('YYYY-MM-DD') ||
65
- (dateIndex === 0 && startDjs.isBefore(djs))
66
- );
67
- })
68
- .sort((a, b) => {
69
- const aStartDjs = dateIndex === 0 ? djs : dayjs(a.start);
70
- const bStartDjs = dateIndex === 0 ? djs : dayjs(b.start);
71
- const aEndDjs = dayjs(a.end);
72
- const bEndDjs = dayjs(b.end);
73
- const aDiffDays = aEndDjs.diff(aStartDjs, 'day');
74
- const bDiffDays = bEndDjs.diff(bStartDjs, 'day');
36
+ {props.dates.map((djs, dateIndex) => {
37
+ const text = djs.format('D');
38
+ const filteredEvents = props.events
39
+ ?.filter((event) => {
40
+ const startDjs = dayjs(event.start);
41
+ return (
42
+ startDjs.format('YYYY-MM-DD') === djs.format('YYYY-MM-DD') ||
43
+ (dateIndex === 0 && startDjs.isBefore(djs))
44
+ );
45
+ })
46
+ .sort((a, b) => {
47
+ const aStartDjs = dateIndex === 0 ? djs : dayjs(a.start);
48
+ const bStartDjs = dateIndex === 0 ? djs : dayjs(b.start);
49
+ const aEndDjs = dayjs(a.end);
50
+ const bEndDjs = dayjs(b.end);
51
+ const aDiffDays = aEndDjs.diff(aStartDjs, 'day');
52
+ const bDiffDays = bEndDjs.diff(bStartDjs, 'day');
75
53
 
76
- return bDiffDays - aDiffDays;
77
- });
54
+ return bDiffDays - aDiffDays;
55
+ });
78
56
 
79
- const rows: (CalendarEvent | number)[] = [];
80
- if (weekId && eventPosition) {
81
- const rowNums = eventPosition.getRowNums({
57
+ const events: (CalendarEvent | number)[] = [];
58
+ if (weekId && props.eventPosition) {
59
+ const rowNums = props.eventPosition.getRowNums({
82
60
  weekId,
83
61
  date: djs.toDate(),
84
62
  });
@@ -86,11 +64,11 @@ export const MonthCalendarWeekRow = (props: {
86
64
  let eventIndex = 0;
87
65
  for (let ii = 1; ii <= rowsLength; ii++) {
88
66
  if (rowNums.includes(ii)) {
89
- rows.push(ii);
67
+ events.push(ii);
90
68
  } else {
91
69
  const event = filteredEvents[eventIndex];
92
70
  if (event) {
93
- rows.push(event);
71
+ events.push(event);
94
72
  }
95
73
  eventIndex++;
96
74
  }
@@ -98,56 +76,52 @@ export const MonthCalendarWeekRow = (props: {
98
76
  }
99
77
  return (
100
78
  <TouchableOpacity
101
- key={isWeekdayHeader ? djs.get('d') : djs.get('date')}
79
+ key={djs.get('date')}
102
80
  style={[
103
81
  styles.dayCellCountainer,
104
- { minHeight: isWeekdayHeader ? undefined : weekRowMinHeight },
82
+ { minHeight: props.weekRowMinHeight },
105
83
  { zIndex: 7 - dateIndex },
106
84
  ]}
107
85
  onPress={() => {
108
- onPressCell?.(djs.toDate());
86
+ props.onPressCell?.(djs.toDate());
109
87
  }}
110
88
  onLongPress={() => {
111
- onLongPressCell?.(djs.toDate());
89
+ props.onLongPressCell?.(djs.toDate());
112
90
  }}
113
- delayLongPress={delayLongPress}
91
+ delayLongPress={props.delayLongPressCell}
114
92
  >
115
93
  <View
116
94
  style={[
117
95
  styles.dayCellInner,
118
- isWeekdayHeader
119
- ? weekdayCellContainerStyle?.(djs.day())
120
- : dayCellContainerStyle?.(djs.toDate()),
96
+ props.dayCellContainerStyle?.(djs.toDate()),
121
97
  ]}
122
98
  />
123
99
  <View style={styles.dayCellLabel}>
124
100
  <Text
125
101
  style={[
126
102
  styles.dayCellText,
127
- isWeekdayHeader
128
- ? weekdayCellTextStyle?.(djs.day())
129
- : dayCellTextStyle?.(djs.toDate()),
130
- !isWeekdayHeader && dayjs(djs).isSame(dayjs(), 'day')
131
- ? todayCellTextStyle
103
+ props.dayCellTextStyle?.(djs.toDate()),
104
+ dayjs(djs).isSame(dayjs(), 'day')
105
+ ? props.todayCellTextStyle
132
106
  : {},
133
107
  ]}
134
108
  >
135
109
  {text}
136
110
  </Text>
137
111
  </View>
138
- {rows.map((eventRow, rowIndex) => {
139
- if (typeof eventRow === 'number') {
112
+ {events.map((event, rowIndex) => {
113
+ if (typeof event === 'number') {
140
114
  return (
141
115
  <View
142
- key={eventRow}
116
+ key={event}
143
117
  style={{ height: eventHeight, marginBottom: EVENT_GAP }}
144
118
  />
145
119
  );
146
120
  }
147
121
 
148
- const rawStartDjs = dayjs(eventRow.start);
149
- const startDjs = dateIndex === 0 ? djs : dayjs(eventRow.start);
150
- const endDjs = dayjs(eventRow.end);
122
+ const rawStartDjs = dayjs(event.start);
123
+ const startDjs = dateIndex === 0 ? djs : dayjs(event.start);
124
+ const endDjs = dayjs(event.end);
151
125
  const diffDays = endDjs
152
126
  .startOf('day')
153
127
  .diff(startDjs.startOf('day'), 'day');
@@ -162,10 +136,10 @@ export const MonthCalendarWeekRow = (props: {
162
136
  width += EVENT_GAP + 1;
163
137
  }
164
138
 
165
- const isLastRow = rowIndex === rows.length - 1;
139
+ const isLastRow = rowIndex === events.length - 1;
166
140
 
167
- if (eventPosition && weekId) {
168
- eventPosition.push({
141
+ if (props.eventPosition && weekId) {
142
+ props.eventPosition.push({
169
143
  weekId,
170
144
  startDate: startDjs.toDate(),
171
145
  days: diffDays + 1,
@@ -175,28 +149,41 @@ export const MonthCalendarWeekRow = (props: {
175
149
 
176
150
  return (
177
151
  <TouchableOpacity
178
- key={eventRow.id}
152
+ key={event.id}
179
153
  style={[
180
154
  styles.event,
181
155
  {
182
- backgroundColor: eventRow.backgroundColor,
183
- borderColor: eventRow.borderColor,
156
+ backgroundColor: event.backgroundColor,
157
+ borderColor: event.borderColor,
184
158
  width: width,
185
159
  height: eventHeight,
160
+ ...(event.borderStyle !== undefined && {
161
+ borderStyle: event.borderStyle,
162
+ }),
163
+ ...(event.borderWidth !== undefined && {
164
+ borderWidth: event.borderWidth,
165
+ }),
166
+ ...(event.borderRadius !== undefined && {
167
+ borderRadius: event.borderRadius,
168
+ }),
186
169
  },
187
170
  isPrevDateEvent ? styles.prevDateEvent : {},
188
171
  isLastRow ? styles.lastRowEvent : {},
189
172
  ]}
190
173
  onPress={() => {
191
- onPressEvent?.(eventRow);
174
+ props.onPressEvent?.(event);
175
+ }}
176
+ onLongPress={() => {
177
+ props.onLongPressEvent?.(event);
192
178
  }}
179
+ delayLongPress={props.delayLongPressEvent}
193
180
  >
194
181
  <Text
195
182
  numberOfLines={1}
196
183
  ellipsizeMode="tail"
197
- style={[styles.eventTitle, { color: eventRow.color }]}
184
+ style={[styles.eventTitle, { color: event.color }]}
198
185
  >
199
- {eventRow.title}
186
+ {event.title}
200
187
  </Text>
201
188
  </TouchableOpacity>
202
189
  );
@@ -8,6 +8,9 @@ export type CalendarEvent = {
8
8
  backgroundColor: string;
9
9
  borderColor: string;
10
10
  color: string;
11
+ borderStyle?: 'solid' | 'dashed' | 'dotted';
12
+ borderWidth?: number;
13
+ borderRadius?: number;
11
14
  };
12
15
 
13
16
  export type WeekdayNum = 0 | 1 | 2 | 3 | 4 | 5 | 6;