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.
- package/LICENSE +20 -0
- package/README.md +13 -0
- package/lib/module/calendar/month-calendar/MonthCalendar.js +79 -0
- package/lib/module/calendar/month-calendar/MonthCalendar.js.map +1 -0
- package/lib/module/calendar/month-calendar/logic/useEvents.js +31 -0
- package/lib/module/calendar/month-calendar/logic/useEvents.js.map +1 -0
- package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js +101 -0
- package/lib/module/calendar/month-calendar/view/MonthCalendarViewItem.js.map +1 -0
- package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js +174 -0
- package/lib/module/calendar/month-calendar/view/MonthCalendarWeekRow.js.map +1 -0
- package/lib/module/constants/size.js +5 -0
- package/lib/module/constants/size.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types/month-calendar.js +2 -0
- package/lib/module/types/month-calendar.js.map +1 -0
- package/lib/module/utils/functions.js +76 -0
- package/lib/module/utils/functions.js.map +1 -0
- package/lib/module/utils/month-calendar-event-position.js +68 -0
- package/lib/module/utils/month-calendar-event-position.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts +10 -0
- package/lib/typescript/src/calendar/month-calendar/MonthCalendar.d.ts.map +1 -0
- package/lib/typescript/src/calendar/month-calendar/logic/useEvents.d.ts +8 -0
- package/lib/typescript/src/calendar/month-calendar/logic/useEvents.d.ts.map +1 -0
- package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts +9 -0
- package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarViewItem.d.ts.map +1 -0
- package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts +12 -0
- package/lib/typescript/src/calendar/month-calendar/view/MonthCalendarWeekRow.d.ts.map +1 -0
- package/lib/typescript/src/constants/size.d.ts +3 -0
- package/lib/typescript/src/constants/size.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types/month-calendar.d.ts +11 -0
- package/lib/typescript/src/types/month-calendar.d.ts.map +1 -0
- package/lib/typescript/src/utils/functions.d.ts +15 -0
- package/lib/typescript/src/utils/functions.d.ts.map +1 -0
- package/lib/typescript/src/utils/month-calendar-event-position.d.ts +22 -0
- package/lib/typescript/src/utils/month-calendar-event-position.d.ts.map +1 -0
- package/package.json +157 -0
- package/src/calendar/month-calendar/MonthCalendar.tsx +94 -0
- package/src/calendar/month-calendar/logic/useEvents.tsx +35 -0
- package/src/calendar/month-calendar/view/MonthCalendarViewItem.tsx +88 -0
- package/src/calendar/month-calendar/view/MonthCalendarWeekRow.tsx +206 -0
- package/src/constants/size.ts +2 -0
- package/src/index.tsx +2 -0
- package/src/types/month-calendar.ts +11 -0
- package/src/utils/functions.ts +79 -0
- 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
|
+
});
|
package/src/index.tsx
ADDED
|
@@ -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;
|