react-native-bigger-calendar 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -2
- package/dist/index.d.mts +289 -73
- package/dist/index.d.ts +289 -73
- package/dist/index.js +650 -144
- package/dist/index.mjs +649 -145
- package/package.json +1 -1
- 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 +344 -56
- 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,7 +960,8 @@ function TimetablePageInner({ mode, date, events, cellHeight, hourHeight, commit
|
|
|
618
960
|
width: columnWidth,
|
|
619
961
|
mode,
|
|
620
962
|
renderEvent,
|
|
621
|
-
onPress: onPressEvent
|
|
963
|
+
onPress: onPressEvent,
|
|
964
|
+
onLongPress: onLongPressEvent
|
|
622
965
|
}, keyExtractor(positioned.event, eventIndex));
|
|
623
966
|
})),
|
|
624
967
|
showNowIndicator && nowDayIndex >= 0 && nowInWindow ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NowIndicator, {
|
|
@@ -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,114 @@ 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: [styles.dayHeaderBadge,
|
|
1203
|
+
style: [styles.dayHeaderBadge, isHighlighted && {
|
|
792
1204
|
backgroundColor: theme.colors.todayBackground,
|
|
793
1205
|
borderRadius: 999,
|
|
794
1206
|
width: badgeSize,
|
|
795
1207
|
height: badgeSize
|
|
796
1208
|
}],
|
|
797
1209
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
798
|
-
style: [theme.text.dayNumber, { color:
|
|
1210
|
+
style: [theme.text.dayNumber, { color: isHighlighted ? theme.colors.todayText : theme.colors.text }],
|
|
799
1211
|
allowFontScaling: false,
|
|
800
1212
|
...isToday && { accessibilityLabel: `Today, ${day.getDate()}` },
|
|
801
1213
|
children: day.getDate()
|
|
@@ -803,7 +1215,7 @@ const DayHeader = ({ day, mode, width, locale }) => {
|
|
|
803
1215
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
804
1216
|
style: [theme.text.weekday, { color: theme.colors.text }],
|
|
805
1217
|
allowFontScaling: false,
|
|
806
|
-
children:
|
|
1218
|
+
children: (0, date_fns.format)(day, "EEE", { locale })
|
|
807
1219
|
})]
|
|
808
1220
|
});
|
|
809
1221
|
};
|
|
@@ -816,6 +1228,10 @@ const styles = react_native.StyleSheet.create({
|
|
|
816
1228
|
alignItems: "center",
|
|
817
1229
|
paddingBottom: 8
|
|
818
1230
|
},
|
|
1231
|
+
weekNumberGutter: {
|
|
1232
|
+
alignItems: "center",
|
|
1233
|
+
justifyContent: "flex-end"
|
|
1234
|
+
},
|
|
819
1235
|
dayHeader: { alignItems: "center" },
|
|
820
1236
|
dayHeaderBadge: {
|
|
821
1237
|
justifyContent: "center",
|
|
@@ -855,6 +1271,12 @@ const styles = react_native.StyleSheet.create({
|
|
|
855
1271
|
flex: 1,
|
|
856
1272
|
height: react_native.StyleSheet.hairlineWidth
|
|
857
1273
|
},
|
|
1274
|
+
timeslotLine: {
|
|
1275
|
+
position: "absolute",
|
|
1276
|
+
right: 0,
|
|
1277
|
+
height: react_native.StyleSheet.hairlineWidth,
|
|
1278
|
+
opacity: .5
|
|
1279
|
+
},
|
|
858
1280
|
eventBox: {
|
|
859
1281
|
position: "absolute",
|
|
860
1282
|
overflow: "hidden"
|
|
@@ -868,9 +1290,50 @@ const styles = react_native.StyleSheet.create({
|
|
|
868
1290
|
//#endregion
|
|
869
1291
|
//#region src/components/Calendar.tsx
|
|
870
1292
|
const defaultKeyExtractor = (event) => `${event.start.toISOString()}|${event.end.toISOString()}|${event.title ?? ""}`;
|
|
871
|
-
function
|
|
1293
|
+
function visibleRange(mode, date, weekStartsOn, numberOfDays, weekEndsOn) {
|
|
1294
|
+
if (mode === "month") return [(0, date_fns.startOfWeek)((0, date_fns.startOfMonth)(date), { weekStartsOn }), (0, date_fns.endOfWeek)((0, date_fns.endOfMonth)(date), { weekStartsOn })];
|
|
1295
|
+
const days = getViewDays(mode, date, weekStartsOn, numberOfDays, false, weekEndsOn);
|
|
1296
|
+
return [(0, date_fns.startOfDay)(days[0]), (0, date_fns.endOfDay)(days[days.length - 1])];
|
|
1297
|
+
}
|
|
1298
|
+
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
1299
|
const mergedTheme = (0, react.useMemo)(() => mergeTheme(theme), [theme]);
|
|
873
1300
|
const internalCellHeight = (0, react_native_reanimated.useSharedValue)(hourHeight);
|
|
1301
|
+
const cellHeight = cellHeightProp ?? internalCellHeight;
|
|
1302
|
+
const handlePressEvent = (0, react.useCallback)((event) => {
|
|
1303
|
+
if (!event.disabled) onPressEvent(event);
|
|
1304
|
+
}, [onPressEvent]);
|
|
1305
|
+
const handleLongPressEvent = (0, react.useMemo)(() => onLongPressEvent ? (event) => {
|
|
1306
|
+
if (!event.disabled) onLongPressEvent(event);
|
|
1307
|
+
} : void 0, [onLongPressEvent]);
|
|
1308
|
+
const handleChangeDate = (0, react.useCallback)((next) => {
|
|
1309
|
+
onChangeDate(next);
|
|
1310
|
+
onChangeDateRange?.(visibleRange(mode, next, weekStartsOn, numberOfDays ?? 1, weekEndsOn));
|
|
1311
|
+
}, [
|
|
1312
|
+
onChangeDate,
|
|
1313
|
+
onChangeDateRange,
|
|
1314
|
+
mode,
|
|
1315
|
+
weekStartsOn,
|
|
1316
|
+
numberOfDays,
|
|
1317
|
+
weekEndsOn
|
|
1318
|
+
]);
|
|
1319
|
+
const resolvedRenderEvent = (0, react.useMemo)(() => {
|
|
1320
|
+
if (eventCellStyle == null && ampm == null && showTime == null) return renderEvent;
|
|
1321
|
+
const Base = renderEvent;
|
|
1322
|
+
return function StyledEvent(props) {
|
|
1323
|
+
const cellStyle = typeof eventCellStyle === "function" ? eventCellStyle(props.event) : eventCellStyle;
|
|
1324
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Base, {
|
|
1325
|
+
...props,
|
|
1326
|
+
cellStyle,
|
|
1327
|
+
ampm,
|
|
1328
|
+
showTime
|
|
1329
|
+
});
|
|
1330
|
+
};
|
|
1331
|
+
}, [
|
|
1332
|
+
renderEvent,
|
|
1333
|
+
eventCellStyle,
|
|
1334
|
+
ampm,
|
|
1335
|
+
showTime
|
|
1336
|
+
]);
|
|
874
1337
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarThemeProvider, {
|
|
875
1338
|
value: mergedTheme,
|
|
876
1339
|
children: mode === "month" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthPager, {
|
|
@@ -878,24 +1341,58 @@ function Calendar({ events, mode, date, onChangeDate, onPressEvent, onPressDay,
|
|
|
878
1341
|
events,
|
|
879
1342
|
maxVisibleEventCount,
|
|
880
1343
|
weekStartsOn,
|
|
881
|
-
|
|
1344
|
+
locale,
|
|
1345
|
+
sortedMonthView,
|
|
1346
|
+
moreLabel,
|
|
1347
|
+
showAdjacentMonths,
|
|
1348
|
+
disableMonthEventCellPress,
|
|
1349
|
+
isRTL,
|
|
1350
|
+
showSixWeeks,
|
|
1351
|
+
activeDate,
|
|
1352
|
+
renderHeaderForMonthView,
|
|
1353
|
+
calendarCellStyle,
|
|
1354
|
+
renderEvent: resolvedRenderEvent,
|
|
882
1355
|
keyExtractor,
|
|
883
1356
|
onPressDay,
|
|
884
|
-
|
|
1357
|
+
onLongPressDay,
|
|
1358
|
+
onPressEvent: handlePressEvent,
|
|
1359
|
+
onLongPressEvent: handleLongPressEvent,
|
|
885
1360
|
onPressMore,
|
|
886
|
-
onChangeDate,
|
|
887
|
-
freeSwipe
|
|
1361
|
+
onChangeDate: handleChangeDate,
|
|
1362
|
+
freeSwipe,
|
|
1363
|
+
swipeEnabled
|
|
1364
|
+
}) : mode === "schedule" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Agenda, {
|
|
1365
|
+
events,
|
|
1366
|
+
locale,
|
|
1367
|
+
renderEvent: resolvedRenderEvent,
|
|
1368
|
+
keyExtractor,
|
|
1369
|
+
onPressEvent: handlePressEvent,
|
|
1370
|
+
onLongPressEvent: handleLongPressEvent,
|
|
1371
|
+
onPressDay,
|
|
1372
|
+
activeDate,
|
|
1373
|
+
itemSeparatorComponent
|
|
888
1374
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeGrid, {
|
|
889
1375
|
mode,
|
|
1376
|
+
numberOfDays,
|
|
1377
|
+
weekEndsOn,
|
|
890
1378
|
date,
|
|
891
1379
|
events,
|
|
892
|
-
cellHeight
|
|
1380
|
+
cellHeight,
|
|
893
1381
|
hourHeight,
|
|
894
1382
|
weekStartsOn,
|
|
895
|
-
renderEvent,
|
|
1383
|
+
renderEvent: resolvedRenderEvent,
|
|
896
1384
|
keyExtractor,
|
|
897
1385
|
scrollOffsetMinutes,
|
|
898
1386
|
hourColumnWidth,
|
|
1387
|
+
hideHours,
|
|
1388
|
+
timeslots,
|
|
1389
|
+
calendarCellStyle,
|
|
1390
|
+
showWeekNumber,
|
|
1391
|
+
weekNumberPrefix,
|
|
1392
|
+
hourComponent,
|
|
1393
|
+
showVerticalScrollIndicator,
|
|
1394
|
+
verticalScrollEnabled,
|
|
1395
|
+
headerComponent,
|
|
899
1396
|
minHour,
|
|
900
1397
|
maxHour,
|
|
901
1398
|
ampm,
|
|
@@ -903,15 +1400,23 @@ function Calendar({ events, mode, date, onChangeDate, onPressEvent, onPressDay,
|
|
|
903
1400
|
maxHourHeight,
|
|
904
1401
|
showNowIndicator,
|
|
905
1402
|
locale,
|
|
1403
|
+
activeDate,
|
|
1404
|
+
isRTL,
|
|
906
1405
|
freeSwipe,
|
|
907
|
-
|
|
1406
|
+
swipeEnabled,
|
|
1407
|
+
onPressEvent: handlePressEvent,
|
|
1408
|
+
onLongPressEvent: handleLongPressEvent,
|
|
908
1409
|
onPressCell,
|
|
909
|
-
|
|
1410
|
+
resetPageOnPressCell,
|
|
1411
|
+
onLongPressCell,
|
|
1412
|
+
onPressDateHeader,
|
|
1413
|
+
onChangeDate: handleChangeDate,
|
|
910
1414
|
renderHeader: renderTimeGridHeader
|
|
911
1415
|
})
|
|
912
1416
|
});
|
|
913
1417
|
}
|
|
914
1418
|
//#endregion
|
|
1419
|
+
exports.Agenda = Agenda;
|
|
915
1420
|
exports.Calendar = Calendar;
|
|
916
1421
|
exports.CalendarThemeProvider = CalendarThemeProvider;
|
|
917
1422
|
exports.DEFAULT_HOUR_HEIGHT = DEFAULT_HOUR_HEIGHT;
|
|
@@ -922,6 +1427,7 @@ exports.TimeGrid = TimeGrid;
|
|
|
922
1427
|
exports.defaultTheme = defaultTheme;
|
|
923
1428
|
exports.getIsToday = getIsToday;
|
|
924
1429
|
exports.getWeekDays = getWeekDays;
|
|
1430
|
+
exports.isAllDayEvent = isAllDayEvent;
|
|
925
1431
|
exports.isSameCalendarDay = isSameCalendarDay;
|
|
926
1432
|
exports.isWeekend = isWeekend;
|
|
927
1433
|
exports.layoutDayEvents = layoutDayEvents;
|