react-native-bigger-calendar 0.1.0 → 0.2.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/README.md +82 -2
- package/dist/index.d.mts +289 -73
- package/dist/index.d.ts +289 -73
- package/dist/index.js +659 -150
- package/dist/index.mjs +658 -151
- package/package.json +2 -2
- package/src/components/Agenda.tsx +125 -0
- package/src/components/AllDayLane.tsx +83 -0
- package/src/components/Calendar.tsx +221 -11
- package/src/components/DefaultEvent.tsx +24 -5
- package/src/components/MonthPager.tsx +138 -26
- package/src/components/MonthView.tsx +82 -14
- package/src/components/TimeGrid.tsx +351 -62
- package/src/index.tsx +9 -2
- package/src/types.ts +23 -1
- package/src/utils/dates.ts +67 -2
- package/src/utils/layout.ts +18 -0
package/dist/index.js
CHANGED
|
@@ -21,13 +21,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
+
let date_fns = require("date-fns");
|
|
24
25
|
let react = require("react");
|
|
25
26
|
let react_native_reanimated = require("react-native-reanimated");
|
|
26
27
|
react_native_reanimated = __toESM(react_native_reanimated);
|
|
27
|
-
let
|
|
28
|
+
let _legendapp_list_react_native = require("@legendapp/list/react-native");
|
|
28
29
|
let react_native = require("react-native");
|
|
29
30
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
30
|
-
let _legendapp_list_react_native = require("@legendapp/list/react-native");
|
|
31
31
|
let react_native_gesture_handler = require("react-native-gesture-handler");
|
|
32
32
|
//#region src/theme.ts
|
|
33
33
|
const defaultTheme = {
|
|
@@ -87,18 +87,169 @@ const CalendarThemeContext = (0, react.createContext)(defaultTheme);
|
|
|
87
87
|
const CalendarThemeProvider = CalendarThemeContext.Provider;
|
|
88
88
|
const useCalendarTheme = () => (0, react.useContext)(CalendarThemeContext);
|
|
89
89
|
//#endregion
|
|
90
|
+
//#region src/utils/dates.ts
|
|
91
|
+
/** The seven dates of the week containing `date`, starting on `weekStartsOn`. */
|
|
92
|
+
const getWeekDays = (date, weekStartsOn) => {
|
|
93
|
+
const start = (0, date_fns.startOfWeek)(date, { weekStartsOn });
|
|
94
|
+
return Array.from({ length: 7 }, (_, index) => (0, date_fns.addDays)(start, index));
|
|
95
|
+
};
|
|
96
|
+
/** How many day columns a time-grid mode shows. `custom` uses `numberOfDays`. */
|
|
97
|
+
const viewDayCount = (mode, numberOfDays = 1) => {
|
|
98
|
+
switch (mode) {
|
|
99
|
+
case "week": return 7;
|
|
100
|
+
case "3days": return 3;
|
|
101
|
+
case "custom": return Math.max(1, Math.floor(numberOfDays));
|
|
102
|
+
default: return 1;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Days in the inclusive span from `weekStartsOn` to `weekEndsOn` (1–7),
|
|
107
|
+
* wrapping when the end precedes the start (e.g. Sat→Wed). Mirrors
|
|
108
|
+
* react-native-big-calendar's `weekDaysCount`.
|
|
109
|
+
*/
|
|
110
|
+
const weekDaysCount = (weekStartsOn, weekEndsOn) => {
|
|
111
|
+
if (weekEndsOn < weekStartsOn) {
|
|
112
|
+
let count = 1;
|
|
113
|
+
let i = weekStartsOn;
|
|
114
|
+
while (i !== weekEndsOn && count <= 7) {
|
|
115
|
+
i = (i + 1) % 7;
|
|
116
|
+
count++;
|
|
117
|
+
}
|
|
118
|
+
return count;
|
|
119
|
+
}
|
|
120
|
+
if (weekEndsOn > weekStartsOn) return weekEndsOn - weekStartsOn + 1;
|
|
121
|
+
return 1;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* The day columns to render for a time-grid page. `week` spans the calendar week
|
|
125
|
+
* (honouring `weekStartsOn`). `custom` with a `weekEndsOn` spans the partial week
|
|
126
|
+
* from `weekStartsOn` to `weekEndsOn` (anchored to `date`'s week, paging by week);
|
|
127
|
+
* otherwise every mode shows `viewDayCount` consecutive days starting at `date`.
|
|
128
|
+
*/
|
|
129
|
+
const getViewDays = (mode, date, weekStartsOn, numberOfDays = 1, isRTL = false, weekEndsOn) => {
|
|
130
|
+
let days;
|
|
131
|
+
if (mode === "week") days = getWeekDays(date, weekStartsOn);
|
|
132
|
+
else if (mode === "custom" && weekEndsOn != null) {
|
|
133
|
+
const subject = (0, date_fns.startOfDay)(date);
|
|
134
|
+
const offset = weekStartsOn - subject.getDay();
|
|
135
|
+
days = Array.from({ length: weekDaysCount(weekStartsOn, weekEndsOn) }, (_, index) => (0, date_fns.addDays)(subject, index + offset));
|
|
136
|
+
} else days = Array.from({ length: viewDayCount(mode, numberOfDays) }, (_, index) => (0, date_fns.addDays)((0, date_fns.startOfDay)(date), index));
|
|
137
|
+
return isRTL ? days.reverse() : days;
|
|
138
|
+
};
|
|
139
|
+
const isWeekend = (date) => {
|
|
140
|
+
const day = date.getDay();
|
|
141
|
+
return day === 0 || day === 6;
|
|
142
|
+
};
|
|
143
|
+
const getIsToday = (date) => (0, date_fns.isToday)(date);
|
|
144
|
+
const isSameCalendarDay = (a, b) => (0, date_fns.isSameDay)(a, b);
|
|
145
|
+
/** Minutes elapsed since midnight (0–1439). */
|
|
146
|
+
const minutesIntoDay = (date) => (0, date_fns.getHours)(date) * 60 + (0, date_fns.getMinutes)(date);
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/components/Agenda.tsx
|
|
149
|
+
/**
|
|
150
|
+
* A vertical, day-grouped list of events (no time grid). Events are sorted by
|
|
151
|
+
* start, grouped under a date header per day. The consumer controls which
|
|
152
|
+
* events (and therefore which date range) are shown.
|
|
153
|
+
*/
|
|
154
|
+
function Agenda({ events, locale, renderEvent, keyExtractor, onPressEvent, onLongPressEvent, onPressDay, activeDate, itemSeparatorComponent }) {
|
|
155
|
+
const theme = useCalendarTheme();
|
|
156
|
+
const RenderEventComponent = renderEvent;
|
|
157
|
+
const rows = (0, react.useMemo)(() => {
|
|
158
|
+
const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
159
|
+
const out = [];
|
|
160
|
+
let currentDay = null;
|
|
161
|
+
sorted.forEach((event, index) => {
|
|
162
|
+
if (!currentDay || !(0, date_fns.isSameDay)(event.start, currentDay)) {
|
|
163
|
+
currentDay = (0, date_fns.startOfDay)(event.start);
|
|
164
|
+
out.push({
|
|
165
|
+
kind: "header",
|
|
166
|
+
date: currentDay,
|
|
167
|
+
key: `h-${currentDay.toISOString()}`
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
out.push({
|
|
171
|
+
kind: "event",
|
|
172
|
+
event,
|
|
173
|
+
index,
|
|
174
|
+
key: `e-${keyExtractor(event, index)}`
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
return out;
|
|
178
|
+
}, [events, keyExtractor]);
|
|
179
|
+
const keyExtractorRow = (0, react.useCallback)((row) => row.key, []);
|
|
180
|
+
const renderItem = (0, react.useCallback)(({ item }) => {
|
|
181
|
+
if (item.kind === "header") {
|
|
182
|
+
const isHighlighted = activeDate ? (0, date_fns.isSameDay)(item.date, activeDate) : getIsToday(item.date);
|
|
183
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
184
|
+
style: [
|
|
185
|
+
theme.text.weekday,
|
|
186
|
+
styles$5.header,
|
|
187
|
+
{ color: isHighlighted ? theme.colors.todayBackground : theme.colors.textMuted }
|
|
188
|
+
],
|
|
189
|
+
onPress: onPressDay ? () => onPressDay(item.date) : void 0,
|
|
190
|
+
accessibilityRole: onPressDay ? "button" : "header",
|
|
191
|
+
children: (0, date_fns.format)(item.date, "EEEE, d LLLL", { locale })
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
195
|
+
style: styles$5.eventRow,
|
|
196
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderEventComponent, {
|
|
197
|
+
event: item.event,
|
|
198
|
+
mode: "schedule",
|
|
199
|
+
onPress: () => onPressEvent(item.event),
|
|
200
|
+
onLongPress: onLongPressEvent ? () => onLongPressEvent(item.event) : void 0
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
}, [
|
|
204
|
+
theme,
|
|
205
|
+
locale,
|
|
206
|
+
activeDate,
|
|
207
|
+
onPressDay,
|
|
208
|
+
onPressEvent,
|
|
209
|
+
onLongPressEvent,
|
|
210
|
+
RenderEventComponent
|
|
211
|
+
]);
|
|
212
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react_native.LegendList, {
|
|
213
|
+
style: styles$5.list,
|
|
214
|
+
data: rows,
|
|
215
|
+
keyExtractor: keyExtractorRow,
|
|
216
|
+
renderItem,
|
|
217
|
+
ItemSeparatorComponent: itemSeparatorComponent ?? void 0,
|
|
218
|
+
recycleItems: false
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const styles$5 = react_native.StyleSheet.create({
|
|
222
|
+
list: { flex: 1 },
|
|
223
|
+
header: {
|
|
224
|
+
paddingTop: 12,
|
|
225
|
+
paddingBottom: 4,
|
|
226
|
+
paddingHorizontal: 12
|
|
227
|
+
},
|
|
228
|
+
eventRow: {
|
|
229
|
+
paddingHorizontal: 12,
|
|
230
|
+
paddingVertical: 2
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
//#endregion
|
|
90
234
|
//#region src/components/DefaultEvent.tsx
|
|
91
235
|
/**
|
|
92
236
|
* The built-in event renderer: a filled, rounded box showing the event title
|
|
93
237
|
* and (on the day/week grid) its time range. Pass your own `renderEvent` to
|
|
94
238
|
* `<Calendar>` to replace it entirely.
|
|
95
239
|
*/
|
|
96
|
-
function DefaultEvent({ event, mode, onPress }) {
|
|
240
|
+
function DefaultEvent({ event, mode, isAllDay, ampm = false, showTime = true, cellStyle, onPress, onLongPress }) {
|
|
97
241
|
const theme = useCalendarTheme();
|
|
98
|
-
const
|
|
242
|
+
const timeFormat = ampm ? "h:mm a" : "HH:mm";
|
|
243
|
+
const shouldShowTime = mode !== "month" && !isAllDay && showTime;
|
|
99
244
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.TouchableOpacity, {
|
|
100
|
-
style: [
|
|
245
|
+
style: [
|
|
246
|
+
styles$4.box,
|
|
247
|
+
{ backgroundColor: theme.colors.eventBackground },
|
|
248
|
+
event.disabled && styles$4.disabled,
|
|
249
|
+
cellStyle
|
|
250
|
+
],
|
|
101
251
|
onPress,
|
|
252
|
+
onLongPress,
|
|
102
253
|
activeOpacity: .7,
|
|
103
254
|
accessibilityRole: "button",
|
|
104
255
|
accessibilityLabel: event.title,
|
|
@@ -108,15 +259,15 @@ function DefaultEvent({ event, mode, onPress }) {
|
|
|
108
259
|
ellipsizeMode: "tail",
|
|
109
260
|
allowFontScaling: false,
|
|
110
261
|
children: event.title
|
|
111
|
-
}) : null,
|
|
112
|
-
style: [styles$
|
|
262
|
+
}) : null, shouldShowTime ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
263
|
+
style: [styles$4.time, { color: theme.colors.eventText }],
|
|
113
264
|
numberOfLines: 1,
|
|
114
265
|
allowFontScaling: false,
|
|
115
|
-
children: `${(0, date_fns.format)(event.start,
|
|
266
|
+
children: `${(0, date_fns.format)(event.start, timeFormat)} - ${(0, date_fns.format)(event.end, timeFormat)}`
|
|
116
267
|
}) : null]
|
|
117
268
|
});
|
|
118
269
|
}
|
|
119
|
-
const styles$
|
|
270
|
+
const styles$4 = react_native.StyleSheet.create({
|
|
120
271
|
box: {
|
|
121
272
|
flex: 1,
|
|
122
273
|
borderRadius: 6,
|
|
@@ -124,24 +275,10 @@ const styles$3 = react_native.StyleSheet.create({
|
|
|
124
275
|
paddingHorizontal: 4,
|
|
125
276
|
overflow: "hidden"
|
|
126
277
|
},
|
|
127
|
-
time: { fontSize: 11 }
|
|
278
|
+
time: { fontSize: 11 },
|
|
279
|
+
disabled: { opacity: .5 }
|
|
128
280
|
});
|
|
129
281
|
//#endregion
|
|
130
|
-
//#region src/utils/dates.ts
|
|
131
|
-
/** The seven dates of the week containing `date`, starting on `weekStartsOn`. */
|
|
132
|
-
const getWeekDays = (date, weekStartsOn) => {
|
|
133
|
-
const start = (0, date_fns.startOfWeek)(date, { weekStartsOn });
|
|
134
|
-
return Array.from({ length: 7 }, (_, index) => (0, date_fns.addDays)(start, index));
|
|
135
|
-
};
|
|
136
|
-
const isWeekend = (date) => {
|
|
137
|
-
const day = date.getDay();
|
|
138
|
-
return day === 0 || day === 6;
|
|
139
|
-
};
|
|
140
|
-
const getIsToday = (date) => (0, date_fns.isToday)(date);
|
|
141
|
-
const isSameCalendarDay = (a, b) => (0, date_fns.isSameDay)(a, b);
|
|
142
|
-
/** Minutes elapsed since midnight (0–1439). */
|
|
143
|
-
const minutesIntoDay = (date) => (0, date_fns.getHours)(date) * 60 + (0, date_fns.getMinutes)(date);
|
|
144
|
-
//#endregion
|
|
145
282
|
//#region src/utils/layout.ts
|
|
146
283
|
const MINUTES_PER_HOUR$1 = 60;
|
|
147
284
|
const MIN_DURATION_HOURS = .25;
|
|
@@ -154,7 +291,7 @@ const MIN_DURATION_HOURS = .25;
|
|
|
154
291
|
function layoutDayEvents(events, day) {
|
|
155
292
|
const dayStart = (0, date_fns.startOfDay)(day);
|
|
156
293
|
const nextDayStart = (0, date_fns.addDays)(dayStart, 1);
|
|
157
|
-
const segments = events.filter((event) => event.start < nextDayStart && event.end > dayStart).map((event) => {
|
|
294
|
+
const segments = events.filter((event) => !isAllDayEvent(event)).filter((event) => event.start < nextDayStart && event.end > dayStart).map((event) => {
|
|
158
295
|
const segStart = (0, date_fns.max)([event.start, dayStart]);
|
|
159
296
|
const segEnd = (0, date_fns.min)([event.end, nextDayStart]);
|
|
160
297
|
return {
|
|
@@ -198,6 +335,16 @@ function layoutDayEvents(events, day) {
|
|
|
198
335
|
if (cluster.length > 0) flushCluster();
|
|
199
336
|
return positioned;
|
|
200
337
|
}
|
|
338
|
+
const atMidnight = (date) => date.getHours() === 0 && date.getMinutes() === 0 && date.getSeconds() === 0 && date.getMilliseconds() === 0;
|
|
339
|
+
/**
|
|
340
|
+
* Whether an event belongs in the all-day lane. An explicit `allDay` flag wins;
|
|
341
|
+
* otherwise it's inferred when the event spans whole days (both `start` and
|
|
342
|
+
* `end` land on midnight, e.g. an iCal-style all-day event). Pure.
|
|
343
|
+
*/
|
|
344
|
+
function isAllDayEvent(event) {
|
|
345
|
+
if (typeof event.allDay === "boolean") return event.allDay;
|
|
346
|
+
return event.end > event.start && atMidnight(event.start) && atMidnight(event.end);
|
|
347
|
+
}
|
|
201
348
|
/**
|
|
202
349
|
* The `startOfDay` ISO keys of every calendar day an event touches (inclusive).
|
|
203
350
|
* An event ending exactly at midnight does not count the following day. Used to
|
|
@@ -217,15 +364,23 @@ const chunkIntoWeeks = (days) => {
|
|
|
217
364
|
for (let index = 0; index < days.length; index += 7) weeks.push(days.slice(index, index + 7));
|
|
218
365
|
return weeks;
|
|
219
366
|
};
|
|
220
|
-
function MonthViewInner({ date, events, maxVisibleEventCount, weekStartsOn, renderEvent, keyExtractor, onPressDay, onPressEvent, onPressMore }) {
|
|
367
|
+
function MonthViewInner({ date, events, maxVisibleEventCount, weekStartsOn, locale, sortedMonthView = true, moreLabel = "{moreCount} More", showAdjacentMonths = true, disableMonthEventCellPress = false, isRTL = false, showSixWeeks = false, activeDate, calendarCellStyle, renderEvent, keyExtractor, onPressDay, onLongPressDay, onPressEvent, onLongPressEvent, onPressMore }) {
|
|
221
368
|
const theme = useCalendarTheme();
|
|
222
369
|
const RenderEventComponent = renderEvent;
|
|
223
370
|
const weeks = (0, react.useMemo)(() => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
371
|
+
const start = (0, date_fns.startOfWeek)((0, date_fns.startOfMonth)(date), { weekStartsOn });
|
|
372
|
+
const naturalEnd = (0, date_fns.endOfWeek)((0, date_fns.endOfMonth)(date), { weekStartsOn });
|
|
373
|
+
const chunked = chunkIntoWeeks((0, date_fns.eachDayOfInterval)({
|
|
374
|
+
start,
|
|
375
|
+
end: showSixWeeks ? (0, date_fns.addDays)(start, 41) : naturalEnd
|
|
227
376
|
}));
|
|
228
|
-
|
|
377
|
+
return isRTL ? chunked.map((week) => [...week].reverse()) : chunked;
|
|
378
|
+
}, [
|
|
379
|
+
date,
|
|
380
|
+
weekStartsOn,
|
|
381
|
+
isRTL,
|
|
382
|
+
showSixWeeks
|
|
383
|
+
]);
|
|
229
384
|
const eventsByDay = (0, react.useMemo)(() => {
|
|
230
385
|
const map = /* @__PURE__ */ new Map();
|
|
231
386
|
for (const event of events) for (const key of eventDayKeys(event)) {
|
|
@@ -233,29 +388,38 @@ function MonthViewInner({ date, events, maxVisibleEventCount, weekStartsOn, rend
|
|
|
233
388
|
if (existing) existing.push(event);
|
|
234
389
|
else map.set(key, [event]);
|
|
235
390
|
}
|
|
391
|
+
if (sortedMonthView) for (const list of map.values()) list.sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
236
392
|
return map;
|
|
237
|
-
}, [events]);
|
|
393
|
+
}, [events, sortedMonthView]);
|
|
238
394
|
const renderDay = (day) => {
|
|
239
|
-
const dayEvents = eventsByDay.get((0, date_fns.startOfDay)(day).toISOString()) ?? [];
|
|
240
395
|
const isCurrentMonth = (0, date_fns.isSameMonth)(day, date);
|
|
396
|
+
if (!isCurrentMonth && !showAdjacentMonths) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { style: [
|
|
397
|
+
styles$3.dayCell,
|
|
398
|
+
{ borderColor: theme.colors.gridLine },
|
|
399
|
+
isWeekend(day) && { backgroundColor: theme.colors.weekendBackground }
|
|
400
|
+
] }, day.toISOString());
|
|
401
|
+
const dayEvents = eventsByDay.get((0, date_fns.startOfDay)(day).toISOString()) ?? [];
|
|
241
402
|
const isToday = getIsToday(day);
|
|
403
|
+
const isHighlighted = activeDate ? isSameCalendarDay(day, activeDate) : isToday;
|
|
242
404
|
const hiddenCount = dayEvents.length - maxVisibleEventCount;
|
|
243
|
-
const dateColor =
|
|
405
|
+
const dateColor = isHighlighted ? theme.colors.todayText : isCurrentMonth ? theme.colors.text : theme.colors.textDisabled;
|
|
244
406
|
const eventCount = dayEvents.length;
|
|
245
|
-
const accessibilityLabel = `${(0, date_fns.format)(day, "EEEE, d LLLL yyyy")}${isToday ? ", today" : ""}, ${eventCount} ${eventCount === 1 ? "event" : "events"}`;
|
|
407
|
+
const accessibilityLabel = `${(0, date_fns.format)(day, "EEEE, d LLLL yyyy", { locale })}${isToday ? ", today" : ""}, ${eventCount} ${eventCount === 1 ? "event" : "events"}`;
|
|
246
408
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.TouchableOpacity, {
|
|
247
409
|
style: [
|
|
248
|
-
styles$
|
|
410
|
+
styles$3.dayCell,
|
|
249
411
|
{ borderColor: theme.colors.gridLine },
|
|
250
|
-
isWeekend(day) && { backgroundColor: theme.colors.weekendBackground }
|
|
412
|
+
isWeekend(day) && { backgroundColor: theme.colors.weekendBackground },
|
|
413
|
+
calendarCellStyle?.(day)
|
|
251
414
|
],
|
|
252
415
|
onPress: onPressDay ? () => onPressDay(day) : void 0,
|
|
253
|
-
|
|
416
|
+
onLongPress: onLongPressDay ? () => onLongPressDay(day) : void 0,
|
|
417
|
+
disabled: !onPressDay && !onLongPressDay,
|
|
254
418
|
accessibilityRole: onPressDay ? "button" : void 0,
|
|
255
419
|
accessibilityLabel,
|
|
256
420
|
children: [
|
|
257
421
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
258
|
-
style: [styles$
|
|
422
|
+
style: [styles$3.dateBadge, isHighlighted && {
|
|
259
423
|
backgroundColor: theme.colors.todayBackground,
|
|
260
424
|
borderRadius: theme.todayBadgeRadius
|
|
261
425
|
}],
|
|
@@ -266,38 +430,40 @@ function MonthViewInner({ date, events, maxVisibleEventCount, weekStartsOn, rend
|
|
|
266
430
|
})
|
|
267
431
|
}),
|
|
268
432
|
dayEvents.slice(0, maxVisibleEventCount).map((event, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
269
|
-
style: styles$
|
|
433
|
+
style: styles$3.monthEvent,
|
|
270
434
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderEventComponent, {
|
|
271
435
|
event,
|
|
272
436
|
mode: "month",
|
|
273
|
-
|
|
437
|
+
isAllDay: isAllDayEvent(event),
|
|
438
|
+
onPress: disableMonthEventCellPress ? () => {} : () => onPressEvent(event),
|
|
439
|
+
onLongPress: disableMonthEventCellPress || !onLongPressEvent ? void 0 : () => onLongPressEvent(event)
|
|
274
440
|
})
|
|
275
441
|
}, keyExtractor(event, index))),
|
|
276
442
|
hiddenCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
277
443
|
style: [
|
|
278
444
|
theme.text.more,
|
|
279
|
-
styles$
|
|
445
|
+
styles$3.moreLabel,
|
|
280
446
|
{ color: theme.colors.textMuted }
|
|
281
447
|
],
|
|
282
448
|
onPress: onPressMore ? () => onPressMore(dayEvents, day) : void 0,
|
|
283
449
|
accessibilityRole: "button",
|
|
284
450
|
accessibilityLabel: `Show ${hiddenCount} more events`,
|
|
285
451
|
allowFontScaling: false,
|
|
286
|
-
children:
|
|
452
|
+
children: moreLabel.replace("{moreCount}", String(hiddenCount))
|
|
287
453
|
}) : null
|
|
288
454
|
]
|
|
289
455
|
}, day.toISOString());
|
|
290
456
|
};
|
|
291
457
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
292
|
-
style: styles$
|
|
458
|
+
style: styles$3.container,
|
|
293
459
|
children: weeks.map((week) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
294
|
-
style: styles$
|
|
460
|
+
style: styles$3.weekRow,
|
|
295
461
|
children: week.map((day) => renderDay(day))
|
|
296
462
|
}, week[0].toISOString()))
|
|
297
463
|
});
|
|
298
464
|
}
|
|
299
465
|
const MonthView = (0, react.memo)(MonthViewInner);
|
|
300
|
-
const styles$
|
|
466
|
+
const styles$3 = react_native.StyleSheet.create({
|
|
301
467
|
container: { flex: 1 },
|
|
302
468
|
weekRow: {
|
|
303
469
|
flex: 1,
|
|
@@ -325,7 +491,7 @@ const styles$2 = react_native.StyleSheet.create({
|
|
|
325
491
|
//#region src/components/MonthPager.tsx
|
|
326
492
|
const PAGE_WINDOW$1 = 60;
|
|
327
493
|
const PAGE_VIEWABILITY$1 = { itemVisiblePercentThreshold: 90 };
|
|
328
|
-
function MonthPagerInner({ date, events, maxVisibleEventCount, weekStartsOn, renderEvent, keyExtractor, onPressDay, onPressEvent, onPressMore, onChangeDate, freeSwipe = false }) {
|
|
494
|
+
function MonthPagerInner({ date, events, maxVisibleEventCount, weekStartsOn, locale, sortedMonthView, moreLabel, showAdjacentMonths, disableMonthEventCellPress, isRTL, calendarCellStyle, renderEvent, keyExtractor, onPressDay, onLongPressDay, onPressEvent, onLongPressEvent, onPressMore, onChangeDate, freeSwipe = false, swipeEnabled = true, showSixWeeks = false, activeDate, renderHeaderForMonthView }) {
|
|
329
495
|
const { width, height } = (0, react_native.useWindowDimensions)();
|
|
330
496
|
const listRef = (0, react.useRef)(null);
|
|
331
497
|
const [pageHeight, setPageHeight] = (0, react.useState)(height);
|
|
@@ -348,6 +514,14 @@ function MonthPagerInner({ date, events, maxVisibleEventCount, weekStartsOn, ren
|
|
|
348
514
|
animated: false
|
|
349
515
|
});
|
|
350
516
|
}, [activeIndex]);
|
|
517
|
+
const weekDays = (0, react.useMemo)(() => {
|
|
518
|
+
const days = getWeekDays(anchor, weekStartsOn);
|
|
519
|
+
return isRTL ? days.reverse() : days;
|
|
520
|
+
}, [
|
|
521
|
+
anchor,
|
|
522
|
+
weekStartsOn,
|
|
523
|
+
isRTL
|
|
524
|
+
]);
|
|
351
525
|
const snapToIndices = (0, react.useMemo)(() => monthDates.map((_, index) => index), [monthDates]);
|
|
352
526
|
const keyExtractorList = (0, react.useCallback)((item) => item.toISOString(), []);
|
|
353
527
|
const getFixedItemSize = (0, react.useCallback)(() => width, [width]);
|
|
@@ -361,10 +535,21 @@ function MonthPagerInner({ date, events, maxVisibleEventCount, weekStartsOn, ren
|
|
|
361
535
|
events,
|
|
362
536
|
maxVisibleEventCount,
|
|
363
537
|
weekStartsOn,
|
|
538
|
+
locale,
|
|
539
|
+
sortedMonthView,
|
|
540
|
+
moreLabel,
|
|
541
|
+
showAdjacentMonths,
|
|
542
|
+
disableMonthEventCellPress,
|
|
543
|
+
isRTL,
|
|
544
|
+
showSixWeeks,
|
|
545
|
+
activeDate,
|
|
546
|
+
calendarCellStyle,
|
|
364
547
|
renderEvent,
|
|
365
548
|
keyExtractor,
|
|
366
549
|
onPressDay,
|
|
550
|
+
onLongPressDay,
|
|
367
551
|
onPressEvent,
|
|
552
|
+
onLongPressEvent,
|
|
368
553
|
onPressMore
|
|
369
554
|
})
|
|
370
555
|
}), [
|
|
@@ -373,44 +558,131 @@ function MonthPagerInner({ date, events, maxVisibleEventCount, weekStartsOn, ren
|
|
|
373
558
|
events,
|
|
374
559
|
maxVisibleEventCount,
|
|
375
560
|
weekStartsOn,
|
|
561
|
+
locale,
|
|
562
|
+
sortedMonthView,
|
|
563
|
+
moreLabel,
|
|
564
|
+
showAdjacentMonths,
|
|
565
|
+
disableMonthEventCellPress,
|
|
566
|
+
isRTL,
|
|
567
|
+
showSixWeeks,
|
|
568
|
+
activeDate,
|
|
569
|
+
calendarCellStyle,
|
|
376
570
|
renderEvent,
|
|
377
571
|
keyExtractor,
|
|
378
572
|
onPressDay,
|
|
573
|
+
onLongPressDay,
|
|
379
574
|
onPressEvent,
|
|
575
|
+
onLongPressEvent,
|
|
380
576
|
onPressMore
|
|
381
577
|
]);
|
|
382
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
383
|
-
style: styles$
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
578
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
|
|
579
|
+
style: styles$2.container,
|
|
580
|
+
children: [renderHeaderForMonthView ? renderHeaderForMonthView(weekDays) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthWeekdayHeader, {
|
|
581
|
+
weekDays,
|
|
582
|
+
locale
|
|
583
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
584
|
+
style: styles$2.pager,
|
|
585
|
+
onLayout: (event) => setPageHeight(event.nativeEvent.layout.height),
|
|
586
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react_native.LegendList, {
|
|
587
|
+
ref: listRef,
|
|
588
|
+
style: styles$2.pagerList,
|
|
589
|
+
data: monthDates,
|
|
590
|
+
horizontal: true,
|
|
591
|
+
recycleItems: false,
|
|
592
|
+
keyExtractor: keyExtractorList,
|
|
593
|
+
getFixedItemSize,
|
|
594
|
+
scrollEnabled: swipeEnabled,
|
|
595
|
+
pagingEnabled: !freeSwipe,
|
|
596
|
+
snapToIndices: freeSwipe ? snapToIndices : void 0,
|
|
597
|
+
initialScrollIndex: activeIndex,
|
|
598
|
+
showsHorizontalScrollIndicator: false,
|
|
599
|
+
viewabilityConfig: PAGE_VIEWABILITY$1,
|
|
600
|
+
onViewableItemsChanged: handleViewableItemsChanged,
|
|
601
|
+
renderItem
|
|
602
|
+
}, pageHeight)
|
|
603
|
+
})]
|
|
401
604
|
});
|
|
402
605
|
}
|
|
403
606
|
const MonthPager = (0, react.memo)(MonthPagerInner);
|
|
404
|
-
const
|
|
607
|
+
const MonthWeekdayHeader = ({ weekDays, locale }) => {
|
|
608
|
+
const theme = useCalendarTheme();
|
|
609
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
610
|
+
style: styles$2.weekdayHeader,
|
|
611
|
+
children: weekDays.map((day) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
612
|
+
style: [
|
|
613
|
+
theme.text.weekday,
|
|
614
|
+
styles$2.weekdayLabel,
|
|
615
|
+
{ color: theme.colors.textMuted }
|
|
616
|
+
],
|
|
617
|
+
allowFontScaling: false,
|
|
618
|
+
children: (0, date_fns.format)(day, "EEE", { locale })
|
|
619
|
+
}, day.toISOString()))
|
|
620
|
+
});
|
|
621
|
+
};
|
|
622
|
+
const styles$2 = react_native.StyleSheet.create({
|
|
623
|
+
container: { flex: 1 },
|
|
405
624
|
pager: { flex: 1 },
|
|
406
|
-
pagerList: { flex: 1 }
|
|
625
|
+
pagerList: { flex: 1 },
|
|
626
|
+
weekdayHeader: {
|
|
627
|
+
flexDirection: "row",
|
|
628
|
+
paddingBottom: 4
|
|
629
|
+
},
|
|
630
|
+
weekdayLabel: {
|
|
631
|
+
flex: 1,
|
|
632
|
+
textAlign: "center"
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/components/AllDayLane.tsx
|
|
637
|
+
/**
|
|
638
|
+
* The all-day lane that sits above the scrolling time grid. All-day events are
|
|
639
|
+
* excluded from the timed columns (see `layoutDayEvents`) and shown here,
|
|
640
|
+
* stacked under their day(s). Renders nothing when no day has an all-day event,
|
|
641
|
+
* so timed-only calendars are unaffected.
|
|
642
|
+
*/
|
|
643
|
+
function AllDayLane({ days, events, mode, hourColumnWidth, dayWidth, renderEvent, keyExtractor, onPressEvent, onLongPressEvent }) {
|
|
644
|
+
const theme = useCalendarTheme();
|
|
645
|
+
const RenderEventComponent = renderEvent;
|
|
646
|
+
const allDay = events.filter(isAllDayEvent);
|
|
647
|
+
const perDay = days.map((day) => {
|
|
648
|
+
const start = (0, date_fns.startOfDay)(day);
|
|
649
|
+
const next = (0, date_fns.addDays)(start, 1);
|
|
650
|
+
return allDay.filter((event) => event.start < next && event.end > start);
|
|
651
|
+
});
|
|
652
|
+
if (perDay.every((list) => list.length === 0)) return null;
|
|
653
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
|
|
654
|
+
style: [styles$1.lane, { borderBottomColor: theme.colors.gridLine }],
|
|
655
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { style: { width: hourColumnWidth } }), days.map((day, dayIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
656
|
+
style: [styles$1.column, { width: dayWidth }],
|
|
657
|
+
children: perDay[dayIndex].map((event, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
658
|
+
style: styles$1.chip,
|
|
659
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderEventComponent, {
|
|
660
|
+
event,
|
|
661
|
+
mode,
|
|
662
|
+
isAllDay: true,
|
|
663
|
+
onPress: () => onPressEvent(event),
|
|
664
|
+
onLongPress: onLongPressEvent ? () => onLongPressEvent(event) : void 0
|
|
665
|
+
})
|
|
666
|
+
}, keyExtractor(event, index)))
|
|
667
|
+
}, day.toISOString()))]
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
const styles$1 = react_native.StyleSheet.create({
|
|
671
|
+
lane: {
|
|
672
|
+
flexDirection: "row",
|
|
673
|
+
borderBottomWidth: react_native.StyleSheet.hairlineWidth,
|
|
674
|
+
paddingBottom: 2
|
|
675
|
+
},
|
|
676
|
+
column: {
|
|
677
|
+
paddingHorizontal: 1,
|
|
678
|
+
gap: 2
|
|
679
|
+
},
|
|
680
|
+
chip: { minHeight: 18 }
|
|
407
681
|
});
|
|
408
682
|
//#endregion
|
|
409
683
|
//#region src/components/TimeGrid.tsx
|
|
410
684
|
const MINUTES_PER_HOUR = 60;
|
|
411
685
|
const HOURS_PER_DAY = 24;
|
|
412
|
-
const DAY_VIEW_STEP = 1;
|
|
413
|
-
const WEEK_VIEW_STEP = 7;
|
|
414
686
|
const PAGE_WINDOW = 180;
|
|
415
687
|
const PAGE_VIEWABILITY = { itemVisiblePercentThreshold: 90 };
|
|
416
688
|
const DEFAULT_HOUR_HEIGHT = 64;
|
|
@@ -435,7 +707,7 @@ function formatHourLabel(hour, ampm) {
|
|
|
435
707
|
const period = hour < 12 ? "AM" : "PM";
|
|
436
708
|
return `${hour % 12 === 0 ? 12 : hour % 12} ${period}`;
|
|
437
709
|
}
|
|
438
|
-
function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, mode, renderEvent, onPress }) {
|
|
710
|
+
function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, mode, renderEvent, onPress, onLongPress }) {
|
|
439
711
|
const RenderEventComponent = renderEvent;
|
|
440
712
|
const boxHeight = (0, react_native_reanimated.useDerivedValue)(() => Math.max(positioned.durationHours * cellHeight.value, MIN_EVENT_HEIGHT), [positioned.durationHours]);
|
|
441
713
|
const boxStyle = (0, react_native_reanimated.useAnimatedStyle)(() => ({
|
|
@@ -447,6 +719,7 @@ function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, mode,
|
|
|
447
719
|
minHour
|
|
448
720
|
]);
|
|
449
721
|
const handlePress = () => onPress(positioned.event);
|
|
722
|
+
const handleLongPress = onLongPress ? () => onLongPress(positioned.event) : void 0;
|
|
450
723
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
|
|
451
724
|
style: [
|
|
452
725
|
styles.eventBox,
|
|
@@ -462,17 +735,21 @@ function AnimatedEventBox({ positioned, cellHeight, minHour, left, width, mode,
|
|
|
462
735
|
boxHeight,
|
|
463
736
|
continuesBefore: positioned.continuesBefore,
|
|
464
737
|
continuesAfter: positioned.continuesAfter,
|
|
465
|
-
onPress: handlePress
|
|
738
|
+
onPress: handlePress,
|
|
739
|
+
onLongPress: handleLongPress
|
|
466
740
|
})
|
|
467
741
|
});
|
|
468
742
|
}
|
|
469
|
-
const HourRow = ({ hour, minHour, cellHeight, hourColumnWidth, label }) => {
|
|
743
|
+
const HourRow = ({ hour, minHour, cellHeight, hourColumnWidth, label, ampm, hourComponent }) => {
|
|
470
744
|
const theme = useCalendarTheme();
|
|
471
745
|
const animatedStyle = (0, react_native_reanimated.useAnimatedStyle)(() => ({ top: (hour - minHour) * cellHeight.value }), [hour, minHour]);
|
|
472
746
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native_reanimated.default.View, {
|
|
473
747
|
style: [styles.hourRow, animatedStyle],
|
|
474
748
|
pointerEvents: "none",
|
|
475
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.
|
|
749
|
+
children: [hourComponent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
750
|
+
style: { width: hourColumnWidth },
|
|
751
|
+
children: hourComponent(hour, ampm)
|
|
752
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
476
753
|
style: [
|
|
477
754
|
theme.text.hourLabel,
|
|
478
755
|
styles.hourLabel,
|
|
@@ -486,6 +763,25 @@ const HourRow = ({ hour, minHour, cellHeight, hourColumnWidth, label }) => {
|
|
|
486
763
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { style: [styles.hourLine, { backgroundColor: theme.colors.gridLine }] })]
|
|
487
764
|
});
|
|
488
765
|
};
|
|
766
|
+
const TimeslotLine = ({ hour, minHour, fraction, cellHeight, hourColumnWidth }) => {
|
|
767
|
+
const theme = useCalendarTheme();
|
|
768
|
+
const animatedStyle = (0, react_native_reanimated.useAnimatedStyle)(() => ({ top: (hour - minHour + fraction) * cellHeight.value }), [
|
|
769
|
+
hour,
|
|
770
|
+
minHour,
|
|
771
|
+
fraction
|
|
772
|
+
]);
|
|
773
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
|
|
774
|
+
style: [
|
|
775
|
+
styles.timeslotLine,
|
|
776
|
+
{
|
|
777
|
+
left: hourColumnWidth,
|
|
778
|
+
backgroundColor: theme.colors.gridLine
|
|
779
|
+
},
|
|
780
|
+
animatedStyle
|
|
781
|
+
],
|
|
782
|
+
pointerEvents: "none"
|
|
783
|
+
});
|
|
784
|
+
};
|
|
489
785
|
const NowIndicator = ({ cellHeight, nowHours, minHour, left, color }) => {
|
|
490
786
|
const animatedStyle = (0, react_native_reanimated.useAnimatedStyle)(() => ({ top: (nowHours - minHour) * cellHeight.value }), [nowHours, minHour]);
|
|
491
787
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
|
|
@@ -500,7 +796,7 @@ const NowIndicator = ({ cellHeight, nowHours, minHour, left, color }) => {
|
|
|
500
796
|
pointerEvents: "none"
|
|
501
797
|
});
|
|
502
798
|
};
|
|
503
|
-
function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, committedCellHeight, scrollY, isActive, scrollOffsetMinutes, weekStartsOn, hourColumnWidth, minHour, maxHour, ampm, minHourHeight, maxHourHeight, showNowIndicator, renderEvent, keyExtractor, onPressEvent, onPressCell }) {
|
|
799
|
+
function TimetablePageInner({ mode, numberOfDays, date, events, cellHeight, hourHeight, committedCellHeight, scrollY, isActive, scrollOffsetMinutes, weekStartsOn, weekEndsOn, hourColumnWidth, minHour, maxHour, ampm, timeslots, isRTL, showVerticalScrollIndicator, verticalScrollEnabled, hourComponent, calendarCellStyle, minHourHeight, maxHourHeight, showNowIndicator, renderEvent, keyExtractor, onPressEvent, onLongPressEvent, onPressCell, onLongPressCell }) {
|
|
504
800
|
const theme = useCalendarTheme();
|
|
505
801
|
const { width } = (0, react_native.useWindowDimensions)();
|
|
506
802
|
const scrollRef = (0, react_native_reanimated.useAnimatedRef)();
|
|
@@ -511,24 +807,34 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
511
807
|
(0, react_native_reanimated.useAnimatedReaction)(() => scrollY.value, (current, previous) => {
|
|
512
808
|
if (!isActive && current !== previous) (0, react_native_reanimated.scrollTo)(scrollRef, 0, current, false);
|
|
513
809
|
});
|
|
514
|
-
const days = (0, react.useMemo)(() => mode
|
|
810
|
+
const days = (0, react.useMemo)(() => getViewDays(mode, date, weekStartsOn, numberOfDays, isRTL, weekEndsOn), [
|
|
515
811
|
mode,
|
|
516
812
|
date,
|
|
517
|
-
weekStartsOn
|
|
813
|
+
weekStartsOn,
|
|
814
|
+
numberOfDays,
|
|
815
|
+
isRTL,
|
|
816
|
+
weekEndsOn
|
|
518
817
|
]);
|
|
519
818
|
const dayWidth = (width - hourColumnWidth) / days.length;
|
|
520
819
|
const dayLeft = (dayIndex) => hourColumnWidth + dayIndex * dayWidth;
|
|
521
820
|
const dayLayouts = (0, react.useMemo)(() => days.map((day) => layoutDayEvents(events, day)), [days, events]);
|
|
522
|
-
const
|
|
523
|
-
if (!onPressCell) return;
|
|
821
|
+
const cellDateFromTouch = (event) => {
|
|
524
822
|
const { locationX, locationY } = event.nativeEvent;
|
|
525
823
|
const day = days[days.length === 1 ? 0 : Math.floor(locationX / dayWidth)];
|
|
526
|
-
if (!day) return;
|
|
824
|
+
if (!day) return null;
|
|
527
825
|
const minutes = Math.round((minHour + locationY / heightSource.value) * MINUTES_PER_HOUR);
|
|
528
826
|
const pressed = new Date(day);
|
|
529
827
|
pressed.setHours(0, 0, 0, 0);
|
|
530
828
|
pressed.setMinutes(minutes);
|
|
531
|
-
|
|
829
|
+
return pressed;
|
|
830
|
+
};
|
|
831
|
+
const handleBackgroundPress = (event) => {
|
|
832
|
+
const date = onPressCell && cellDateFromTouch(event);
|
|
833
|
+
if (date) onPressCell?.(date);
|
|
834
|
+
};
|
|
835
|
+
const handleBackgroundLongPress = (event) => {
|
|
836
|
+
const date = onLongPressCell && cellDateFromTouch(event);
|
|
837
|
+
if (date) onLongPressCell?.(date);
|
|
532
838
|
};
|
|
533
839
|
const hoursRange = (0, react.useMemo)(() => Array.from({ length: maxHour - minHour }, (_, index) => minHour + index), [minHour, maxHour]);
|
|
534
840
|
const now = useNow(showNowIndicator && isActive);
|
|
@@ -557,13 +863,24 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
557
863
|
minHourHeight,
|
|
558
864
|
maxHourHeight
|
|
559
865
|
]);
|
|
560
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
866
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
|
|
561
867
|
style: styles.container,
|
|
562
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
868
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AllDayLane, {
|
|
869
|
+
days,
|
|
870
|
+
events,
|
|
871
|
+
mode,
|
|
872
|
+
hourColumnWidth,
|
|
873
|
+
dayWidth,
|
|
874
|
+
renderEvent,
|
|
875
|
+
keyExtractor,
|
|
876
|
+
onPressEvent,
|
|
877
|
+
onLongPressEvent
|
|
878
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_gesture_handler.GestureDetector, {
|
|
563
879
|
gesture: zoomGesture,
|
|
564
880
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.ScrollView, {
|
|
565
881
|
ref: scrollRef,
|
|
566
|
-
showsVerticalScrollIndicator:
|
|
882
|
+
showsVerticalScrollIndicator: showVerticalScrollIndicator,
|
|
883
|
+
scrollEnabled: verticalScrollEnabled,
|
|
567
884
|
onScroll: scrollHandler,
|
|
568
885
|
scrollEventThrottle: 16,
|
|
569
886
|
contentContainerStyle: { paddingTop: HOUR_LABEL_TOP_INSET },
|
|
@@ -574,9 +891,10 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
574
891
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native_reanimated.default.View, {
|
|
575
892
|
style: [styles.content, fullHeightStyle],
|
|
576
893
|
children: [
|
|
577
|
-
onPressCell ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
|
|
894
|
+
onPressCell || onLongPressCell ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
|
|
578
895
|
style: [styles.cellPressLayer, { left: hourColumnWidth }],
|
|
579
|
-
onPress: handleBackgroundPress,
|
|
896
|
+
onPress: onPressCell ? handleBackgroundPress : void 0,
|
|
897
|
+
onLongPress: onLongPressCell ? handleBackgroundLongPress : void 0,
|
|
580
898
|
importantForAccessibility: "no",
|
|
581
899
|
accessibilityElementsHidden: true
|
|
582
900
|
}) : null,
|
|
@@ -592,6 +910,21 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
592
910
|
],
|
|
593
911
|
pointerEvents: "none"
|
|
594
912
|
}, `weekend-${day.toISOString()}`) : null),
|
|
913
|
+
calendarCellStyle ? days.map((day, dayIndex) => {
|
|
914
|
+
const cellStyle = calendarCellStyle(day);
|
|
915
|
+
return cellStyle ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
|
|
916
|
+
style: [
|
|
917
|
+
styles.weekendColumn,
|
|
918
|
+
{
|
|
919
|
+
left: dayLeft(dayIndex),
|
|
920
|
+
width: dayWidth
|
|
921
|
+
},
|
|
922
|
+
cellStyle,
|
|
923
|
+
fullHeightStyle
|
|
924
|
+
],
|
|
925
|
+
pointerEvents: "none"
|
|
926
|
+
}, `cell-${day.toISOString()}`) : null;
|
|
927
|
+
}) : null,
|
|
595
928
|
days.map((day, dayIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
|
|
596
929
|
style: [
|
|
597
930
|
styles.daySeparator,
|
|
@@ -606,8 +939,17 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
606
939
|
minHour,
|
|
607
940
|
cellHeight: heightSource,
|
|
608
941
|
hourColumnWidth,
|
|
609
|
-
label: formatHourLabel(hour, ampm)
|
|
942
|
+
label: formatHourLabel(hour, ampm),
|
|
943
|
+
ampm,
|
|
944
|
+
hourComponent
|
|
610
945
|
}, hour)),
|
|
946
|
+
timeslots > 1 ? hoursRange.flatMap((hour) => Array.from({ length: timeslots - 1 }, (_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeslotLine, {
|
|
947
|
+
hour,
|
|
948
|
+
minHour,
|
|
949
|
+
fraction: (i + 1) / timeslots,
|
|
950
|
+
cellHeight: heightSource,
|
|
951
|
+
hourColumnWidth
|
|
952
|
+
}, `slot-${hour}-${i}`))) : null,
|
|
611
953
|
dayLayouts.flatMap((layout, dayIndex) => layout.filter((p) => p.startHours < maxHour && p.startHours + p.durationHours > minHour).map((positioned, eventIndex) => {
|
|
612
954
|
const columnWidth = dayWidth / positioned.columns;
|
|
613
955
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnimatedEventBox, {
|
|
@@ -618,8 +960,9 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
618
960
|
width: columnWidth,
|
|
619
961
|
mode,
|
|
620
962
|
renderEvent,
|
|
621
|
-
onPress: onPressEvent
|
|
622
|
-
|
|
963
|
+
onPress: onPressEvent,
|
|
964
|
+
onLongPress: onLongPressEvent
|
|
965
|
+
}, `${dayIndex}:${keyExtractor(positioned.event, eventIndex)}`);
|
|
623
966
|
})),
|
|
624
967
|
showNowIndicator && nowDayIndex >= 0 && nowInWindow ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NowIndicator, {
|
|
625
968
|
cellHeight: heightSource,
|
|
@@ -631,40 +974,48 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
631
974
|
]
|
|
632
975
|
})
|
|
633
976
|
})
|
|
634
|
-
})
|
|
977
|
+
})]
|
|
635
978
|
});
|
|
636
979
|
}
|
|
637
980
|
const TimetablePage = (0, react.memo)(TimetablePageInner);
|
|
638
|
-
function TimeGridInner({ mode, date, events, cellHeight, hourHeight = 64, weekStartsOn, renderEvent, keyExtractor, scrollOffsetMinutes = 0, hourColumnWidth = DEFAULT_HOUR_COLUMN_WIDTH, minHour = 0, maxHour = HOURS_PER_DAY, ampm = false, minHourHeight = DEFAULT_MIN_HOUR_HEIGHT, maxHourHeight = DEFAULT_MAX_HOUR_HEIGHT, showNowIndicator = true, locale, freeSwipe = false, onPressEvent, onPressCell, onChangeDate, renderHeader }) {
|
|
981
|
+
function TimeGridInner({ mode, numberOfDays = 1, weekEndsOn, date, events, cellHeight, hourHeight = 64, weekStartsOn, renderEvent, keyExtractor, scrollOffsetMinutes = 0, hourColumnWidth: hourColumnWidthProp = DEFAULT_HOUR_COLUMN_WIDTH, hideHours = false, timeslots = 1, calendarCellStyle, showWeekNumber = false, headerComponent, minHour = 0, maxHour = HOURS_PER_DAY, ampm = false, isRTL = false, minHourHeight = DEFAULT_MIN_HOUR_HEIGHT, maxHourHeight = DEFAULT_MAX_HOUR_HEIGHT, showNowIndicator = true, locale, freeSwipe = false, swipeEnabled = true, showVerticalScrollIndicator = true, verticalScrollEnabled = true, weekNumberPrefix = "W", hourComponent, activeDate, resetPageOnPressCell = false, onPressEvent, onLongPressEvent, onPressCell, onLongPressCell, onPressDateHeader, onChangeDate, renderHeader }) {
|
|
639
982
|
const clampedMinHour = Math.max(0, Math.min(minHour, HOURS_PER_DAY - 1));
|
|
640
983
|
const clampedMaxHour = Math.max(clampedMinHour + 1, Math.min(maxHour, HOURS_PER_DAY));
|
|
984
|
+
const hourColumnWidth = hideHours ? 0 : hourColumnWidthProp;
|
|
641
985
|
const { width, height } = (0, react_native.useWindowDimensions)();
|
|
642
986
|
const listRef = (0, react.useRef)(null);
|
|
643
987
|
const [pageHeight, setPageHeight] = (0, react.useState)(height);
|
|
644
|
-
const
|
|
988
|
+
const [measured, setMeasured] = (0, react.useState)(false);
|
|
989
|
+
const weekAnchored = mode === "week" || mode === "custom" && weekEndsOn != null;
|
|
990
|
+
const step = weekAnchored ? 7 : viewDayCount(mode, numberOfDays);
|
|
645
991
|
const scrollY = (0, react_native_reanimated.useSharedValue)(Math.max(0, scrollOffsetMinutes / MINUTES_PER_HOUR - clampedMinHour) * hourHeight);
|
|
646
992
|
const committedCellHeight = (0, react_native_reanimated.useSharedValue)(hourHeight);
|
|
647
993
|
const [anchorDate] = (0, react.useState)(date);
|
|
648
|
-
const anchor = (0, react.useMemo)(() =>
|
|
649
|
-
|
|
994
|
+
const anchor = (0, react.useMemo)(() => weekAnchored ? (0, date_fns.startOfWeek)(anchorDate, { weekStartsOn }) : (0, date_fns.startOfDay)(anchorDate), [
|
|
995
|
+
weekAnchored,
|
|
650
996
|
anchorDate,
|
|
651
997
|
weekStartsOn
|
|
652
998
|
]);
|
|
653
999
|
const pageDates = (0, react.useMemo)(() => Array.from({ length: 361 }, (_, i) => (0, date_fns.addDays)(anchor, (i - PAGE_WINDOW) * step)), [anchor, step]);
|
|
654
1000
|
const activeIndex = (0, react.useCallback)((target) => {
|
|
655
|
-
const aligned =
|
|
656
|
-
return Math.
|
|
1001
|
+
const aligned = weekAnchored ? (0, date_fns.startOfWeek)(target, { weekStartsOn }) : (0, date_fns.startOfDay)(target);
|
|
1002
|
+
return Math.floor((0, date_fns.differenceInCalendarDays)(aligned, anchor) / step) + PAGE_WINDOW;
|
|
657
1003
|
}, [
|
|
658
1004
|
anchor,
|
|
659
|
-
|
|
1005
|
+
weekAnchored,
|
|
660
1006
|
step,
|
|
661
1007
|
weekStartsOn
|
|
662
1008
|
])(date);
|
|
663
1009
|
const viewedIndexRef = (0, react.useRef)(activeIndex);
|
|
664
|
-
const headerDays = (0, react.useMemo)(() => mode
|
|
1010
|
+
const headerDays = (0, react.useMemo)(() => getViewDays(mode, pageDates[activeIndex] ?? date, weekStartsOn, numberOfDays, isRTL, weekEndsOn), [
|
|
665
1011
|
mode,
|
|
1012
|
+
pageDates,
|
|
1013
|
+
activeIndex,
|
|
666
1014
|
date,
|
|
667
|
-
weekStartsOn
|
|
1015
|
+
weekStartsOn,
|
|
1016
|
+
numberOfDays,
|
|
1017
|
+
isRTL,
|
|
1018
|
+
weekEndsOn
|
|
668
1019
|
]);
|
|
669
1020
|
const handleViewableItemsChanged = (0, react.useCallback)((info) => {
|
|
670
1021
|
const settled = info.viewableItems.find((token) => token.isViewable);
|
|
@@ -680,6 +1031,21 @@ function TimeGridInner({ mode, date, events, cellHeight, hourHeight = 64, weekSt
|
|
|
680
1031
|
animated: false
|
|
681
1032
|
});
|
|
682
1033
|
}, [activeIndex]);
|
|
1034
|
+
const handlePressCell = (0, react.useMemo)(() => {
|
|
1035
|
+
if (!onPressCell) return void 0;
|
|
1036
|
+
if (!resetPageOnPressCell) return onPressCell;
|
|
1037
|
+
return (cellDate) => {
|
|
1038
|
+
onPressCell(cellDate);
|
|
1039
|
+
listRef.current?.scrollToIndex({
|
|
1040
|
+
index: activeIndex,
|
|
1041
|
+
animated: true
|
|
1042
|
+
});
|
|
1043
|
+
};
|
|
1044
|
+
}, [
|
|
1045
|
+
onPressCell,
|
|
1046
|
+
resetPageOnPressCell,
|
|
1047
|
+
activeIndex
|
|
1048
|
+
]);
|
|
683
1049
|
const snapToIndices = (0, react.useMemo)(() => pageDates.map((_, index) => index), [pageDates]);
|
|
684
1050
|
const keyExtractorList = (0, react.useCallback)((item) => item.toISOString(), []);
|
|
685
1051
|
const getFixedItemSize = (0, react.useCallback)(() => width, [width]);
|
|
@@ -690,6 +1056,7 @@ function TimeGridInner({ mode, date, events, cellHeight, hourHeight = 64, weekSt
|
|
|
690
1056
|
},
|
|
691
1057
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimetablePage, {
|
|
692
1058
|
mode,
|
|
1059
|
+
numberOfDays,
|
|
693
1060
|
date: item,
|
|
694
1061
|
events,
|
|
695
1062
|
cellHeight,
|
|
@@ -699,22 +1066,32 @@ function TimeGridInner({ mode, date, events, cellHeight, hourHeight = 64, weekSt
|
|
|
699
1066
|
isActive: index === activeIndex,
|
|
700
1067
|
scrollOffsetMinutes,
|
|
701
1068
|
weekStartsOn,
|
|
1069
|
+
weekEndsOn,
|
|
702
1070
|
hourColumnWidth,
|
|
703
1071
|
minHour: clampedMinHour,
|
|
704
1072
|
maxHour: clampedMaxHour,
|
|
705
1073
|
ampm,
|
|
1074
|
+
timeslots,
|
|
1075
|
+
isRTL,
|
|
1076
|
+
showVerticalScrollIndicator,
|
|
1077
|
+
verticalScrollEnabled,
|
|
1078
|
+
hourComponent,
|
|
1079
|
+
calendarCellStyle,
|
|
706
1080
|
minHourHeight,
|
|
707
1081
|
maxHourHeight,
|
|
708
1082
|
showNowIndicator,
|
|
709
1083
|
renderEvent,
|
|
710
1084
|
keyExtractor,
|
|
711
1085
|
onPressEvent,
|
|
712
|
-
|
|
1086
|
+
onLongPressEvent,
|
|
1087
|
+
onPressCell: handlePressCell,
|
|
1088
|
+
onLongPressCell
|
|
713
1089
|
})
|
|
714
1090
|
}), [
|
|
715
1091
|
width,
|
|
716
1092
|
pageHeight,
|
|
717
1093
|
mode,
|
|
1094
|
+
numberOfDays,
|
|
718
1095
|
events,
|
|
719
1096
|
cellHeight,
|
|
720
1097
|
hourHeight,
|
|
@@ -723,79 +1100,117 @@ function TimeGridInner({ mode, date, events, cellHeight, hourHeight = 64, weekSt
|
|
|
723
1100
|
activeIndex,
|
|
724
1101
|
scrollOffsetMinutes,
|
|
725
1102
|
weekStartsOn,
|
|
1103
|
+
weekEndsOn,
|
|
726
1104
|
hourColumnWidth,
|
|
727
1105
|
clampedMinHour,
|
|
728
1106
|
clampedMaxHour,
|
|
729
1107
|
ampm,
|
|
1108
|
+
timeslots,
|
|
1109
|
+
isRTL,
|
|
1110
|
+
showVerticalScrollIndicator,
|
|
1111
|
+
verticalScrollEnabled,
|
|
1112
|
+
hourComponent,
|
|
1113
|
+
calendarCellStyle,
|
|
730
1114
|
minHourHeight,
|
|
731
1115
|
maxHourHeight,
|
|
732
1116
|
showNowIndicator,
|
|
733
1117
|
renderEvent,
|
|
734
1118
|
keyExtractor,
|
|
735
1119
|
onPressEvent,
|
|
736
|
-
|
|
1120
|
+
onLongPressEvent,
|
|
1121
|
+
handlePressCell,
|
|
1122
|
+
onLongPressCell
|
|
737
1123
|
]);
|
|
738
1124
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
|
|
739
1125
|
style: styles.container,
|
|
740
|
-
children: [
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
1126
|
+
children: [
|
|
1127
|
+
renderHeader ? renderHeader(headerDays) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultHeader, {
|
|
1128
|
+
days: headerDays,
|
|
1129
|
+
mode,
|
|
1130
|
+
width,
|
|
1131
|
+
hourColumnWidth,
|
|
1132
|
+
showWeekNumber,
|
|
1133
|
+
weekNumberPrefix,
|
|
1134
|
+
locale,
|
|
1135
|
+
activeDate,
|
|
1136
|
+
onPressDateHeader
|
|
1137
|
+
}),
|
|
1138
|
+
headerComponent,
|
|
1139
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
1140
|
+
style: styles.pager,
|
|
1141
|
+
onLayout: (event) => {
|
|
1142
|
+
setPageHeight(event.nativeEvent.layout.height);
|
|
1143
|
+
setMeasured(true);
|
|
1144
|
+
},
|
|
1145
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react_native.LegendList, {
|
|
1146
|
+
ref: listRef,
|
|
1147
|
+
style: styles.pagerList,
|
|
1148
|
+
data: pageDates,
|
|
1149
|
+
horizontal: true,
|
|
1150
|
+
recycleItems: false,
|
|
1151
|
+
keyExtractor: keyExtractorList,
|
|
1152
|
+
getFixedItemSize,
|
|
1153
|
+
scrollEnabled: swipeEnabled,
|
|
1154
|
+
pagingEnabled: !freeSwipe,
|
|
1155
|
+
snapToIndices: freeSwipe ? snapToIndices : void 0,
|
|
1156
|
+
initialScrollIndex: activeIndex,
|
|
1157
|
+
showsHorizontalScrollIndicator: false,
|
|
1158
|
+
viewabilityConfig: PAGE_VIEWABILITY,
|
|
1159
|
+
onViewableItemsChanged: handleViewableItemsChanged,
|
|
1160
|
+
renderItem
|
|
1161
|
+
}, measured ? "grid" : "grid-seed")
|
|
1162
|
+
})
|
|
1163
|
+
]
|
|
766
1164
|
});
|
|
767
1165
|
}
|
|
768
1166
|
const TimeGrid = (0, react.memo)(TimeGridInner);
|
|
769
|
-
const DefaultHeader = ({ days, mode, width, hourColumnWidth, locale }) => {
|
|
770
|
-
const
|
|
1167
|
+
const DefaultHeader = ({ days, mode, width, hourColumnWidth, showWeekNumber, weekNumberPrefix = "W", locale, activeDate, onPressDateHeader }) => {
|
|
1168
|
+
const theme = useCalendarTheme();
|
|
1169
|
+
const dayWidth = (width - hourColumnWidth) / days.length;
|
|
771
1170
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
|
|
772
1171
|
style: styles.headerRow,
|
|
773
|
-
children: [
|
|
1172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
1173
|
+
style: [styles.weekNumberGutter, { width: hourColumnWidth }],
|
|
1174
|
+
children: showWeekNumber && hourColumnWidth > 0 && days[0] ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
1175
|
+
style: [theme.text.hourLabel, { color: theme.colors.textMuted }],
|
|
1176
|
+
allowFontScaling: false,
|
|
1177
|
+
children: `${weekNumberPrefix}${(0, date_fns.getISOWeek)(days[0])}`
|
|
1178
|
+
}) : null
|
|
1179
|
+
}), days.map((day) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DayHeader, {
|
|
774
1180
|
day,
|
|
775
1181
|
mode,
|
|
776
1182
|
width: dayWidth,
|
|
777
|
-
locale
|
|
1183
|
+
locale,
|
|
1184
|
+
activeDate,
|
|
1185
|
+
onPressDateHeader
|
|
778
1186
|
}, day.toISOString()))]
|
|
779
1187
|
});
|
|
780
1188
|
};
|
|
781
|
-
const DayHeader = ({ day, mode, width, locale }) => {
|
|
1189
|
+
const DayHeader = ({ day, mode, width, locale, activeDate, onPressDateHeader }) => {
|
|
782
1190
|
const theme = useCalendarTheme();
|
|
783
1191
|
const isToday = getIsToday(day);
|
|
1192
|
+
const isHighlighted = activeDate ? isSameCalendarDay(day, activeDate) : isToday;
|
|
784
1193
|
const badgeSize = mode === "day" ? 44 : 32;
|
|
785
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.
|
|
1194
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.Pressable, {
|
|
786
1195
|
style: [styles.dayHeader, {
|
|
787
1196
|
width,
|
|
788
1197
|
gap: mode === "day" ? 4 : 2
|
|
789
1198
|
}],
|
|
1199
|
+
onPress: onPressDateHeader ? () => onPressDateHeader(day) : void 0,
|
|
1200
|
+
disabled: !onPressDateHeader,
|
|
1201
|
+
accessibilityRole: onPressDateHeader ? "button" : void 0,
|
|
790
1202
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
791
|
-
style: [
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1203
|
+
style: [
|
|
1204
|
+
styles.dayHeaderBadge,
|
|
1205
|
+
{
|
|
1206
|
+
width: badgeSize,
|
|
1207
|
+
height: badgeSize,
|
|
1208
|
+
borderRadius: 999
|
|
1209
|
+
},
|
|
1210
|
+
isHighlighted && { backgroundColor: theme.colors.todayBackground }
|
|
1211
|
+
],
|
|
797
1212
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
798
|
-
style: [theme.text.dayNumber, { color:
|
|
1213
|
+
style: [theme.text.dayNumber, { color: isHighlighted ? theme.colors.todayText : theme.colors.text }],
|
|
799
1214
|
allowFontScaling: false,
|
|
800
1215
|
...isToday && { accessibilityLabel: `Today, ${day.getDate()}` },
|
|
801
1216
|
children: day.getDate()
|
|
@@ -803,7 +1218,7 @@ const DayHeader = ({ day, mode, width, locale }) => {
|
|
|
803
1218
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
804
1219
|
style: [theme.text.weekday, { color: theme.colors.text }],
|
|
805
1220
|
allowFontScaling: false,
|
|
806
|
-
children:
|
|
1221
|
+
children: (0, date_fns.format)(day, "EEE", { locale })
|
|
807
1222
|
})]
|
|
808
1223
|
});
|
|
809
1224
|
};
|
|
@@ -816,6 +1231,10 @@ const styles = react_native.StyleSheet.create({
|
|
|
816
1231
|
alignItems: "center",
|
|
817
1232
|
paddingBottom: 8
|
|
818
1233
|
},
|
|
1234
|
+
weekNumberGutter: {
|
|
1235
|
+
alignItems: "center",
|
|
1236
|
+
justifyContent: "flex-end"
|
|
1237
|
+
},
|
|
819
1238
|
dayHeader: { alignItems: "center" },
|
|
820
1239
|
dayHeaderBadge: {
|
|
821
1240
|
justifyContent: "center",
|
|
@@ -855,6 +1274,12 @@ const styles = react_native.StyleSheet.create({
|
|
|
855
1274
|
flex: 1,
|
|
856
1275
|
height: react_native.StyleSheet.hairlineWidth
|
|
857
1276
|
},
|
|
1277
|
+
timeslotLine: {
|
|
1278
|
+
position: "absolute",
|
|
1279
|
+
right: 0,
|
|
1280
|
+
height: react_native.StyleSheet.hairlineWidth,
|
|
1281
|
+
opacity: .5
|
|
1282
|
+
},
|
|
858
1283
|
eventBox: {
|
|
859
1284
|
position: "absolute",
|
|
860
1285
|
overflow: "hidden"
|
|
@@ -868,9 +1293,50 @@ const styles = react_native.StyleSheet.create({
|
|
|
868
1293
|
//#endregion
|
|
869
1294
|
//#region src/components/Calendar.tsx
|
|
870
1295
|
const defaultKeyExtractor = (event) => `${event.start.toISOString()}|${event.end.toISOString()}|${event.title ?? ""}`;
|
|
871
|
-
function
|
|
1296
|
+
function visibleRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
|
|
1297
|
+
if (mode === "month") return [(0, date_fns.startOfWeek)((0, date_fns.startOfMonth)(date), { weekStartsOn }), (0, date_fns.endOfWeek)((0, date_fns.endOfMonth)(date), { weekStartsOn })];
|
|
1298
|
+
const days = getViewDays(mode, date, weekStartsOn, numberOfDays, false, weekEndsOn);
|
|
1299
|
+
return [(0, date_fns.startOfDay)(days[0]), (0, date_fns.endOfDay)(days[days.length - 1])];
|
|
1300
|
+
}
|
|
1301
|
+
function Calendar({ events, mode, date, onChangeDate, onChangeDateRange, onPressEvent, onLongPressEvent, onPressDay, onLongPressDay, onPressMore, onPressCell, resetPageOnPressCell, onLongPressCell, onPressDateHeader, maxVisibleEventCount = 2, sortedMonthView, moreLabel, showAdjacentMonths, disableMonthEventCellPress, weekStartsOn = 0, numberOfDays, weekEndsOn, renderEvent = DefaultEvent, eventCellStyle, calendarCellStyle, keyExtractor = defaultKeyExtractor, theme, cellHeight: cellHeightProp, hourHeight = 64, minHourHeight, maxHourHeight, hourColumnWidth, hideHours, timeslots, showWeekNumber, weekNumberPrefix, hourComponent, showSixWeeks, swipeEnabled, showVerticalScrollIndicator, verticalScrollEnabled, headerComponent, minHour, maxHour, ampm, showTime, scrollOffsetMinutes, showNowIndicator, locale, activeDate, isRTL, freeSwipe, renderTimeGridHeader, renderHeaderForMonthView, itemSeparatorComponent }) {
|
|
872
1302
|
const mergedTheme = (0, react.useMemo)(() => mergeTheme(theme), [theme]);
|
|
873
1303
|
const internalCellHeight = (0, react_native_reanimated.useSharedValue)(hourHeight);
|
|
1304
|
+
const cellHeight = cellHeightProp ?? internalCellHeight;
|
|
1305
|
+
const handlePressEvent = (0, react.useCallback)((event) => {
|
|
1306
|
+
if (!event.disabled) onPressEvent(event);
|
|
1307
|
+
}, [onPressEvent]);
|
|
1308
|
+
const handleLongPressEvent = (0, react.useMemo)(() => onLongPressEvent ? (event) => {
|
|
1309
|
+
if (!event.disabled) onLongPressEvent(event);
|
|
1310
|
+
} : void 0, [onLongPressEvent]);
|
|
1311
|
+
const handleChangeDate = (0, react.useCallback)((next) => {
|
|
1312
|
+
onChangeDate(next);
|
|
1313
|
+
onChangeDateRange?.(visibleRange(mode, next, weekStartsOn, numberOfDays ?? 1, weekEndsOn));
|
|
1314
|
+
}, [
|
|
1315
|
+
onChangeDate,
|
|
1316
|
+
onChangeDateRange,
|
|
1317
|
+
mode,
|
|
1318
|
+
weekStartsOn,
|
|
1319
|
+
numberOfDays,
|
|
1320
|
+
weekEndsOn
|
|
1321
|
+
]);
|
|
1322
|
+
const resolvedRenderEvent = (0, react.useMemo)(() => {
|
|
1323
|
+
if (eventCellStyle == null && ampm == null && showTime == null) return renderEvent;
|
|
1324
|
+
const Base = renderEvent;
|
|
1325
|
+
return function StyledEvent(props) {
|
|
1326
|
+
const cellStyle = typeof eventCellStyle === "function" ? eventCellStyle(props.event) : eventCellStyle;
|
|
1327
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Base, {
|
|
1328
|
+
...props,
|
|
1329
|
+
cellStyle,
|
|
1330
|
+
ampm,
|
|
1331
|
+
showTime
|
|
1332
|
+
});
|
|
1333
|
+
};
|
|
1334
|
+
}, [
|
|
1335
|
+
renderEvent,
|
|
1336
|
+
eventCellStyle,
|
|
1337
|
+
ampm,
|
|
1338
|
+
showTime
|
|
1339
|
+
]);
|
|
874
1340
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarThemeProvider, {
|
|
875
1341
|
value: mergedTheme,
|
|
876
1342
|
children: mode === "month" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthPager, {
|
|
@@ -878,24 +1344,58 @@ function Calendar({ events, mode, date, onChangeDate, onPressEvent, onPressDay,
|
|
|
878
1344
|
events,
|
|
879
1345
|
maxVisibleEventCount,
|
|
880
1346
|
weekStartsOn,
|
|
881
|
-
|
|
1347
|
+
locale,
|
|
1348
|
+
sortedMonthView,
|
|
1349
|
+
moreLabel,
|
|
1350
|
+
showAdjacentMonths,
|
|
1351
|
+
disableMonthEventCellPress,
|
|
1352
|
+
isRTL,
|
|
1353
|
+
showSixWeeks,
|
|
1354
|
+
activeDate,
|
|
1355
|
+
renderHeaderForMonthView,
|
|
1356
|
+
calendarCellStyle,
|
|
1357
|
+
renderEvent: resolvedRenderEvent,
|
|
882
1358
|
keyExtractor,
|
|
883
1359
|
onPressDay,
|
|
884
|
-
|
|
1360
|
+
onLongPressDay,
|
|
1361
|
+
onPressEvent: handlePressEvent,
|
|
1362
|
+
onLongPressEvent: handleLongPressEvent,
|
|
885
1363
|
onPressMore,
|
|
886
|
-
onChangeDate,
|
|
887
|
-
freeSwipe
|
|
1364
|
+
onChangeDate: handleChangeDate,
|
|
1365
|
+
freeSwipe,
|
|
1366
|
+
swipeEnabled
|
|
1367
|
+
}) : mode === "schedule" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Agenda, {
|
|
1368
|
+
events,
|
|
1369
|
+
locale,
|
|
1370
|
+
renderEvent: resolvedRenderEvent,
|
|
1371
|
+
keyExtractor,
|
|
1372
|
+
onPressEvent: handlePressEvent,
|
|
1373
|
+
onLongPressEvent: handleLongPressEvent,
|
|
1374
|
+
onPressDay,
|
|
1375
|
+
activeDate,
|
|
1376
|
+
itemSeparatorComponent
|
|
888
1377
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeGrid, {
|
|
889
1378
|
mode,
|
|
1379
|
+
numberOfDays,
|
|
1380
|
+
weekEndsOn,
|
|
890
1381
|
date,
|
|
891
1382
|
events,
|
|
892
|
-
cellHeight
|
|
1383
|
+
cellHeight,
|
|
893
1384
|
hourHeight,
|
|
894
1385
|
weekStartsOn,
|
|
895
|
-
renderEvent,
|
|
1386
|
+
renderEvent: resolvedRenderEvent,
|
|
896
1387
|
keyExtractor,
|
|
897
1388
|
scrollOffsetMinutes,
|
|
898
1389
|
hourColumnWidth,
|
|
1390
|
+
hideHours,
|
|
1391
|
+
timeslots,
|
|
1392
|
+
calendarCellStyle,
|
|
1393
|
+
showWeekNumber,
|
|
1394
|
+
weekNumberPrefix,
|
|
1395
|
+
hourComponent,
|
|
1396
|
+
showVerticalScrollIndicator,
|
|
1397
|
+
verticalScrollEnabled,
|
|
1398
|
+
headerComponent,
|
|
899
1399
|
minHour,
|
|
900
1400
|
maxHour,
|
|
901
1401
|
ampm,
|
|
@@ -903,15 +1403,23 @@ function Calendar({ events, mode, date, onChangeDate, onPressEvent, onPressDay,
|
|
|
903
1403
|
maxHourHeight,
|
|
904
1404
|
showNowIndicator,
|
|
905
1405
|
locale,
|
|
1406
|
+
activeDate,
|
|
1407
|
+
isRTL,
|
|
906
1408
|
freeSwipe,
|
|
907
|
-
|
|
1409
|
+
swipeEnabled,
|
|
1410
|
+
onPressEvent: handlePressEvent,
|
|
1411
|
+
onLongPressEvent: handleLongPressEvent,
|
|
908
1412
|
onPressCell,
|
|
909
|
-
|
|
1413
|
+
resetPageOnPressCell,
|
|
1414
|
+
onLongPressCell,
|
|
1415
|
+
onPressDateHeader,
|
|
1416
|
+
onChangeDate: handleChangeDate,
|
|
910
1417
|
renderHeader: renderTimeGridHeader
|
|
911
1418
|
})
|
|
912
1419
|
});
|
|
913
1420
|
}
|
|
914
1421
|
//#endregion
|
|
1422
|
+
exports.Agenda = Agenda;
|
|
915
1423
|
exports.Calendar = Calendar;
|
|
916
1424
|
exports.CalendarThemeProvider = CalendarThemeProvider;
|
|
917
1425
|
exports.DEFAULT_HOUR_HEIGHT = DEFAULT_HOUR_HEIGHT;
|
|
@@ -922,6 +1430,7 @@ exports.TimeGrid = TimeGrid;
|
|
|
922
1430
|
exports.defaultTheme = defaultTheme;
|
|
923
1431
|
exports.getIsToday = getIsToday;
|
|
924
1432
|
exports.getWeekDays = getWeekDays;
|
|
1433
|
+
exports.isAllDayEvent = isAllDayEvent;
|
|
925
1434
|
exports.isSameCalendarDay = isSameCalendarDay;
|
|
926
1435
|
exports.isWeekend = isWeekend;
|
|
927
1436
|
exports.layoutDayEvents = layoutDayEvents;
|