react-native-ll-calendar 0.1.1

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 (50) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +13 -0
  3. package/lib/module/calendar/month-calendar/MonthCalendar.js +79 -0
  4. package/lib/module/calendar/month-calendar/MonthCalendar.js.map +1 -0
  5. package/lib/module/calendar/month-calendar/logic/useEvents.js +31 -0
  6. package/lib/module/calendar/month-calendar/logic/useEvents.js.map +1 -0
  7. package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js +101 -0
  8. package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js.map +1 -0
  9. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js +174 -0
  10. package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js.map +1 -0
  11. package/lib/module/constants/size.js +5 -0
  12. package/lib/module/constants/size.js.map +1 -0
  13. package/lib/module/index.js +4 -0
  14. package/lib/module/index.js.map +1 -0
  15. package/lib/module/package.json +1 -0
  16. package/lib/module/types/month-calendar.js +2 -0
  17. package/lib/module/types/month-calendar.js.map +1 -0
  18. package/lib/module/utils/functions.js +76 -0
  19. package/lib/module/utils/functions.js.map +1 -0
  20. package/lib/module/utils/month-calendar-event-position.js +68 -0
  21. package/lib/module/utils/month-calendar-event-position.js.map +1 -0
  22. package/lib/typescript/package.json +1 -0
  23. package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts +10 -0
  24. package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts.map +1 -0
  25. package/lib/typescript/src/calendar/month-calendar/logic/useEvents.d.ts +8 -0
  26. package/lib/typescript/src/calendar/month-calendar/logic/useEvents.d.ts.map +1 -0
  27. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts +9 -0
  28. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts.map +1 -0
  29. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts +12 -0
  30. package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts.map +1 -0
  31. package/lib/typescript/src/constants/size.d.ts +3 -0
  32. package/lib/typescript/src/constants/size.d.ts.map +1 -0
  33. package/lib/typescript/src/index.d.ts +3 -0
  34. package/lib/typescript/src/index.d.ts.map +1 -0
  35. package/lib/typescript/src/types/month-calendar.d.ts +11 -0
  36. package/lib/typescript/src/types/month-calendar.d.ts.map +1 -0
  37. package/lib/typescript/src/utils/functions.d.ts +15 -0
  38. package/lib/typescript/src/utils/functions.d.ts.map +1 -0
  39. package/lib/typescript/src/utils/month-calendar-event-position.d.ts +22 -0
  40. package/lib/typescript/src/utils/month-calendar-event-position.d.ts.map +1 -0
  41. package/package.json +157 -0
  42. package/src/calendar/month-calendar/MonthCalendar.tsx +94 -0
  43. package/src/calendar/month-calendar/logic/useEvents.tsx +35 -0
  44. package/src/calendar/month-calendar/view/MonthCalendarViewItem.tsx +88 -0
  45. package/src/calendar/month-calendar/view/MonthCalendarWeekRow.tsx +206 -0
  46. package/src/constants/size.ts +2 -0
  47. package/src/index.tsx +2 -0
  48. package/src/types/month-calendar.ts +11 -0
  49. package/src/utils/functions.ts +79 -0
  50. package/src/utils/month-calendar-event-position.ts +68 -0
@@ -0,0 +1,206 @@
1
+ import dayjs from 'dayjs';
2
+ import { StyleSheet, useWindowDimensions } from 'react-native';
3
+ import { Text, TouchableOpacity, View } from 'react-native';
4
+ import type { CalendarEvent } from '../../../types/month-calendar';
5
+ import type MonthCalendarEventPosition from '../../../utils/month-calendar-event-position';
6
+ import { CELL_BORDER_WIDTH, EVENT_GAP } from '../../../constants/size';
7
+
8
+ export const MonthCalendarWeekRow = (props: {
9
+ dates: dayjs.Dayjs[];
10
+ isWeekdayHeader?: boolean;
11
+ events?: CalendarEvent[];
12
+ eventPosition?: MonthCalendarEventPosition;
13
+ onPressEvent?: (event: CalendarEvent) => void;
14
+ onPressCell?: (date: Date) => void;
15
+ }) => {
16
+ const {
17
+ dates,
18
+ isWeekdayHeader,
19
+ events = [],
20
+ eventPosition,
21
+ onPressEvent,
22
+ onPressCell,
23
+ } = props;
24
+ const eventHeight = 26;
25
+ const { width: screenWidth } = useWindowDimensions();
26
+ const dateColumnWidth = screenWidth / 7;
27
+ const weekId = dates[0]?.format('YYYY-MM-DD');
28
+ if (weekId && eventPosition) {
29
+ eventPosition.resetResource(weekId);
30
+ }
31
+
32
+ return (
33
+ <View style={styles.container}>
34
+ {dates.map((djs, dateIndex) => {
35
+ const text = isWeekdayHeader ? djs.format('ddd') : djs.format('D');
36
+ const filteredEvents = events
37
+ .filter((event) => {
38
+ const startDjs = dayjs(event.start);
39
+ return (
40
+ startDjs.format('YYYY-MM-DD') === djs.format('YYYY-MM-DD') ||
41
+ (dateIndex === 0 && startDjs.isBefore(djs))
42
+ );
43
+ })
44
+ .sort((a, b) => {
45
+ const aStartDjs = dateIndex === 0 ? djs : dayjs(a.start);
46
+ const bStartDjs = dateIndex === 0 ? djs : dayjs(b.start);
47
+ const aEndDjs = dayjs(a.end);
48
+ const bEndDjs = dayjs(b.end);
49
+ const aDiffDays = aEndDjs.diff(aStartDjs, 'day');
50
+ const bDiffDays = bEndDjs.diff(bStartDjs, 'day');
51
+
52
+ return bDiffDays - aDiffDays;
53
+ });
54
+
55
+ const rows: (CalendarEvent | number)[] = [];
56
+ if (weekId && eventPosition) {
57
+ const rowNums = eventPosition.getRowNums({
58
+ weekId,
59
+ date: djs.toDate(),
60
+ });
61
+ const rowsLength = rowNums.length + filteredEvents.length;
62
+ let eventIndex = 0;
63
+ for (let ii = 1; ii <= rowsLength; ii++) {
64
+ if (rowNums.includes(ii)) {
65
+ rows.push(ii);
66
+ } else {
67
+ const event = filteredEvents[eventIndex];
68
+ if (event) {
69
+ rows.push(event);
70
+ }
71
+ eventIndex++;
72
+ }
73
+ }
74
+ }
75
+ return (
76
+ <TouchableOpacity
77
+ key={isWeekdayHeader ? djs.get('d') : djs.get('date')}
78
+ style={[
79
+ styles.dayCellCountainer,
80
+ isWeekdayHeader ? { minHeight: undefined } : {},
81
+ { zIndex: 7 - dateIndex },
82
+ ]}
83
+ onPress={() => {
84
+ onPressCell?.(djs.toDate());
85
+ }}
86
+ >
87
+ <View style={styles.dayCellLabel}>
88
+ <Text style={styles.dayCellText}>{text}</Text>
89
+ </View>
90
+ {rows.map((eventRow, rowIndex) => {
91
+ if (typeof eventRow === 'number') {
92
+ return (
93
+ <View
94
+ key={eventRow}
95
+ style={{ height: eventHeight, marginBottom: EVENT_GAP }}
96
+ />
97
+ );
98
+ }
99
+
100
+ const rawStartDjs = dayjs(eventRow.start);
101
+ const startDjs = dateIndex === 0 ? djs : dayjs(eventRow.start);
102
+ const endDjs = dayjs(eventRow.end);
103
+ const diffDays = endDjs.diff(startDjs, 'day');
104
+ const isPrevDateEvent =
105
+ dateIndex === 0 && rawStartDjs.isBefore(djs);
106
+ let width =
107
+ (diffDays + 1) * dateColumnWidth -
108
+ EVENT_GAP * 2 -
109
+ CELL_BORDER_WIDTH * 2;
110
+
111
+ if (isPrevDateEvent) {
112
+ width += EVENT_GAP + 1;
113
+ }
114
+
115
+ const isLastRow = rowIndex === rows.length - 1;
116
+
117
+ if (eventPosition && weekId) {
118
+ eventPosition.push({
119
+ weekId,
120
+ startDate: eventRow.start,
121
+ days: diffDays + 1,
122
+ rowNum: rowIndex + 1,
123
+ });
124
+ }
125
+
126
+ return (
127
+ <TouchableOpacity
128
+ key={eventRow.id}
129
+ style={[
130
+ styles.event,
131
+ {
132
+ backgroundColor: eventRow.backgroundColor,
133
+ borderColor: eventRow.borderColor,
134
+ width: width,
135
+ height: eventHeight,
136
+ },
137
+ isPrevDateEvent ? styles.prevDateEvent : {},
138
+ isLastRow ? styles.lastRowEvent : {},
139
+ ]}
140
+ onPress={() => {
141
+ onPressEvent?.(eventRow);
142
+ }}
143
+ >
144
+ <Text
145
+ numberOfLines={1}
146
+ ellipsizeMode="tail"
147
+ style={[styles.eventTitle, { color: eventRow.color }]}
148
+ >
149
+ {eventRow.title}
150
+ </Text>
151
+ </TouchableOpacity>
152
+ );
153
+ })}
154
+ </TouchableOpacity>
155
+ );
156
+ })}
157
+ </View>
158
+ );
159
+ };
160
+
161
+ const styles = StyleSheet.create({
162
+ container: {
163
+ width: '100%',
164
+ display: 'flex',
165
+ flexDirection: 'row',
166
+ flex: 1,
167
+ backgroundColor: 'white',
168
+ },
169
+ dayCellCountainer: {
170
+ minHeight: 80,
171
+ flex: 1,
172
+ borderRightWidth: CELL_BORDER_WIDTH,
173
+ borderBottomWidth: CELL_BORDER_WIDTH,
174
+ borderColor: 'lightslategrey',
175
+ backgroundColor: 'white',
176
+ },
177
+ dayCellLabel: {
178
+ paddingVertical: 1,
179
+ paddingHorizontal: 2,
180
+ },
181
+ dayCellText: {
182
+ textAlign: 'center',
183
+ fontSize: 12,
184
+ },
185
+ event: {
186
+ borderWidth: 0.5,
187
+ borderRadius: 4,
188
+ paddingHorizontal: 4,
189
+ flexDirection: 'row',
190
+ alignItems: 'center',
191
+ boxShadow: '0 0 2px 0 rgba(0, 0, 0, 0.1)',
192
+ marginTop: EVENT_GAP,
193
+ marginLeft: EVENT_GAP,
194
+ },
195
+ prevDateEvent: {
196
+ marginLeft: -1,
197
+ borderTopStartRadius: 0,
198
+ borderBottomStartRadius: 0,
199
+ },
200
+ lastRowEvent: {
201
+ marginBottom: EVENT_GAP,
202
+ },
203
+ eventTitle: {
204
+ fontSize: 10,
205
+ },
206
+ });
@@ -0,0 +1,2 @@
1
+ export const CELL_BORDER_WIDTH = 0.2;
2
+ export const EVENT_GAP = 2;
package/src/index.tsx ADDED
@@ -0,0 +1,2 @@
1
+ export { MonthCalendar } from './calendar/month-calendar/MonthCalendar';
2
+ export type { CalendarEvent } from './types/month-calendar';
@@ -0,0 +1,11 @@
1
+ export type WeekStartsOn = 0 | 1;
2
+
3
+ export type CalendarEvent = {
4
+ id: string;
5
+ title: string;
6
+ start: Date;
7
+ end: Date;
8
+ backgroundColor: string;
9
+ borderColor: string;
10
+ color: string;
11
+ };
@@ -0,0 +1,79 @@
1
+ import dayjs from 'dayjs';
2
+ import type { WeekStartsOn } from '../types/month-calendar';
3
+
4
+ export function monthlyStartDate(args: {
5
+ date: Date;
6
+ weekStartsOn: WeekStartsOn;
7
+ }) {
8
+ const { date, weekStartsOn } = args;
9
+ const djs = dayjs(date);
10
+ if (weekStartsOn === 0) {
11
+ return djs.startOf('month').startOf('week').toDate();
12
+ } else {
13
+ const startOfMonth = djs.startOf('month');
14
+ if (startOfMonth.day() === 1) {
15
+ return startOfMonth.toDate();
16
+ } else if (startOfMonth.day() === 0) {
17
+ return startOfMonth.subtract(6, 'day').toDate();
18
+ } else {
19
+ return startOfMonth.subtract(startOfMonth.day() - 1, 'day').toDate();
20
+ }
21
+ }
22
+ }
23
+
24
+ export function monthlyEndDate(args: {
25
+ date: Date;
26
+ weekStartsOn: WeekStartsOn;
27
+ }) {
28
+ const { date, weekStartsOn } = args;
29
+ const djs = dayjs(date);
30
+ if (weekStartsOn === 0) {
31
+ return djs.endOf('month').endOf('week').toDate();
32
+ } else {
33
+ const endOfMonth = djs.endOf('month');
34
+ if (endOfMonth.day() === 0) {
35
+ return endOfMonth.toDate();
36
+ } else {
37
+ return endOfMonth.add(7 - endOfMonth.day(), 'day').toDate();
38
+ }
39
+ }
40
+ }
41
+
42
+ export function getWeekIds(args: {
43
+ start: Date;
44
+ end: Date;
45
+ weekStartsOn: WeekStartsOn;
46
+ }) {
47
+ const { start, end, weekStartsOn } = args;
48
+ const startDjs = dayjs(start);
49
+ const endDjs = dayjs(end);
50
+ const weekIds: string[] = [];
51
+ let current = startDjs;
52
+ if (weekStartsOn === 0) {
53
+ while (current.isSame(endDjs) || current.isBefore(endDjs)) {
54
+ const weekId = current.startOf('week').format('YYYY-MM-DD');
55
+ if (!weekIds.includes(weekId)) {
56
+ weekIds.push(weekId);
57
+ }
58
+ current = current.add(1, 'day');
59
+ }
60
+ } else {
61
+ while (current.isSame(endDjs) || current.isBefore(endDjs)) {
62
+ if (current.day() === 0) {
63
+ const weekId = current.subtract(6, 'day').format('YYYY-MM-DD');
64
+ if (!weekIds.includes(weekId)) {
65
+ weekIds.push(weekId);
66
+ }
67
+ } else {
68
+ const weekId = current
69
+ .subtract(current.day() - 1, 'day')
70
+ .format('YYYY-MM-DD');
71
+ if (!weekIds.includes(weekId)) {
72
+ weekIds.push(weekId);
73
+ }
74
+ }
75
+ current = current.add(1, 'day');
76
+ }
77
+ }
78
+ return weekIds;
79
+ }
@@ -0,0 +1,68 @@
1
+ class MonthCalendarEventPosition {
2
+ public record: Record<string, Record<string, number[]>> = {};
3
+ constructor() {}
4
+
5
+ private generateKey(date: Date): string {
6
+ const year = date.getFullYear();
7
+ const month = date.getMonth() + 1;
8
+ const day = date.getDate();
9
+ return `${year}-${month}-${day}`;
10
+ }
11
+
12
+ public push(arg: {
13
+ weekId: string;
14
+ startDate: Date;
15
+ days: number;
16
+ rowNum: number;
17
+ }) {
18
+ const { weekId, startDate, days, rowNum } = arg;
19
+
20
+ for (let i = 0; i < days; i++) {
21
+ const date = new Date(startDate);
22
+ date.setDate(date.getDate() + i);
23
+ const key = this.generateKey(date);
24
+ if (!this.record[weekId]) {
25
+ this.record[weekId] = {};
26
+ }
27
+ if (!this.record[weekId][key]) {
28
+ this.record[weekId][key] = [];
29
+ }
30
+ this.record[weekId][key]?.push(rowNum);
31
+ }
32
+ }
33
+
34
+ public getMaxRowNum(arg: { weekId: string; date: Date }): number {
35
+ const { weekId, date } = arg;
36
+ const key = this.generateKey(date);
37
+ const resourceRecord = this.record[weekId];
38
+ if (!resourceRecord) {
39
+ return 0;
40
+ }
41
+
42
+ const indexes = resourceRecord[key];
43
+
44
+ if (indexes === undefined || indexes.length === 0) {
45
+ return 0;
46
+ }
47
+
48
+ return Math.max(...indexes);
49
+ }
50
+
51
+ public getRowNums(arg: { weekId: string; date: Date }): number[] {
52
+ const { weekId, date } = arg;
53
+ const key = this.generateKey(date);
54
+ const resourceRecord = this.record[weekId];
55
+ if (!resourceRecord) {
56
+ return [];
57
+ }
58
+ if (!resourceRecord[key]) {
59
+ return [];
60
+ }
61
+ return resourceRecord[key];
62
+ }
63
+
64
+ public resetResource(weekId: string) {
65
+ this.record[weekId] = {};
66
+ }
67
+ }
68
+ export default MonthCalendarEventPosition;