@sunsama/event-calendar 0.13.2 → 0.13.5
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/lib/commonjs/components/all-day-events.js +1 -1
- package/lib/commonjs/components/all-day-events.js.map +1 -1
- package/lib/commonjs/components/edit-event-container.js +9 -1
- package/lib/commonjs/components/edit-event-container.js.map +1 -1
- package/lib/commonjs/components/event-container.js +2 -1
- package/lib/commonjs/components/event-container.js.map +1 -1
- package/lib/commonjs/components/timed-event-container.js +1 -1
- package/lib/commonjs/components/timed-event-container.js.map +1 -1
- package/lib/commonjs/hooks/use-auto-scroll.js +11 -1
- package/lib/commonjs/hooks/use-auto-scroll.js.map +1 -1
- package/lib/commonjs/hooks/use-events.js +7 -6
- package/lib/commonjs/hooks/use-events.js.map +1 -1
- package/lib/commonjs/index.js +33 -32
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/calendar-layout.js +15 -2
- package/lib/commonjs/utils/calendar-layout.js.map +1 -1
- package/lib/commonjs/utils/compute-positioning.js +1 -1
- package/lib/commonjs/utils/compute-positioning.js.map +1 -1
- package/lib/commonjs/utils/date-utils.js +59 -12
- package/lib/commonjs/utils/date-utils.js.map +1 -1
- package/lib/commonjs/utils/generate-event-layouts.js +13 -6
- package/lib/commonjs/utils/generate-event-layouts.js.map +1 -1
- package/lib/module/components/all-day-events.js +2 -2
- package/lib/module/components/all-day-events.js.map +1 -1
- package/lib/module/components/edit-event-container.js +9 -1
- package/lib/module/components/edit-event-container.js.map +1 -1
- package/lib/module/components/event-container.js +3 -2
- package/lib/module/components/event-container.js.map +1 -1
- package/lib/module/components/timed-event-container.js +1 -1
- package/lib/module/components/timed-event-container.js.map +1 -1
- package/lib/module/hooks/use-auto-scroll.js +11 -1
- package/lib/module/hooks/use-auto-scroll.js.map +1 -1
- package/lib/module/hooks/use-events.js +8 -7
- package/lib/module/hooks/use-events.js.map +1 -1
- package/lib/module/index.js +33 -32
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/calendar-layout.js +15 -2
- package/lib/module/utils/calendar-layout.js.map +1 -1
- package/lib/module/utils/compute-positioning.js +1 -1
- package/lib/module/utils/compute-positioning.js.map +1 -1
- package/lib/module/utils/date-utils.js +57 -11
- package/lib/module/utils/date-utils.js.map +1 -1
- package/lib/module/utils/generate-event-layouts.js +14 -7
- package/lib/module/utils/generate-event-layouts.js.map +1 -1
- package/lib/typescript/commonjs/components/all-day-events.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/edit-event-container.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/event-container.d.ts +2 -1
- package/lib/typescript/commonjs/components/event-container.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/timed-event-container.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/use-auto-scroll.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/use-events.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/calendar-layout.d.ts +3 -1
- package/lib/typescript/commonjs/utils/calendar-layout.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/compute-positioning.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/date-utils.d.ts +1 -0
- package/lib/typescript/commonjs/utils/date-utils.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/generate-event-layouts.d.ts.map +1 -1
- package/lib/typescript/module/components/all-day-events.d.ts.map +1 -1
- package/lib/typescript/module/components/edit-event-container.d.ts.map +1 -1
- package/lib/typescript/module/components/event-container.d.ts +2 -1
- package/lib/typescript/module/components/event-container.d.ts.map +1 -1
- package/lib/typescript/module/components/timed-event-container.d.ts.map +1 -1
- package/lib/typescript/module/hooks/use-auto-scroll.d.ts.map +1 -1
- package/lib/typescript/module/hooks/use-events.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/utils/calendar-layout.d.ts +3 -1
- package/lib/typescript/module/utils/calendar-layout.d.ts.map +1 -1
- package/lib/typescript/module/utils/compute-positioning.d.ts.map +1 -1
- package/lib/typescript/module/utils/date-utils.d.ts +1 -0
- package/lib/typescript/module/utils/date-utils.d.ts.map +1 -1
- package/lib/typescript/module/utils/generate-event-layouts.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/all-day-events.tsx +8 -4
- package/src/components/edit-event-container.tsx +11 -1
- package/src/components/event-container.tsx +14 -5
- package/src/components/timed-event-container.tsx +3 -0
- package/src/hooks/use-auto-scroll.ts +11 -1
- package/src/hooks/use-events.tsx +12 -8
- package/src/index.tsx +68 -34
- package/src/utils/__tests___/date-utils-extended.test.ts +34 -0
- package/src/utils/__tests___/generate-event-layout.test.ts +29 -0
- package/src/utils/calendar-layout.ts +15 -4
- package/src/utils/compute-positioning.ts +4 -3
- package/src/utils/date-utils.ts +70 -26
- package/src/utils/generate-event-layouts.ts +17 -19
package/src/utils/date-utils.ts
CHANGED
|
@@ -2,6 +2,46 @@ import moment, { type Moment } from "moment-timezone";
|
|
|
2
2
|
import { isDate, range, size } from "lodash";
|
|
3
3
|
import { CalendarEvent, PrefabHour } from "../types";
|
|
4
4
|
|
|
5
|
+
// Cached Intl.DateTimeFormat instances for fast timezone-aware day/hour extraction.
|
|
6
|
+
// Avoids creating Moment instances in hot paths like isAllDayOrSpansMidnight.
|
|
7
|
+
let _cachedTz = "";
|
|
8
|
+
let _dayFormatter: Intl.DateTimeFormat;
|
|
9
|
+
let _hourFormatter: Intl.DateTimeFormat;
|
|
10
|
+
|
|
11
|
+
function ensureFormatters(timezone: string) {
|
|
12
|
+
if (timezone !== _cachedTz) {
|
|
13
|
+
_cachedTz = timezone;
|
|
14
|
+
_dayFormatter = new Intl.DateTimeFormat("en-CA", { timeZone: timezone });
|
|
15
|
+
_hourFormatter = new Intl.DateTimeFormat("en-GB", {
|
|
16
|
+
hour: "2-digit",
|
|
17
|
+
hour12: false,
|
|
18
|
+
timeZone: timezone,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function toDate(date: Date | string): Date {
|
|
24
|
+
return typeof date === "string" ? new Date(date) : date;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Event layouts flow into Reanimated worklet closures (useAnimatedReaction,
|
|
28
|
+
// useAnimatedStyle, gesture handlers), which can't clone a `Date` instance
|
|
29
|
+
// across the JS/UI thread boundary ("[Worklets] Cannot copy value of type
|
|
30
|
+
// 'Date'."). Consumers may pass `start`/`end` as `Date` objects even though
|
|
31
|
+
// `CalendarEvent` types them as `string`, so normalize to ISO strings as
|
|
32
|
+
// soon as events enter the layout pipeline.
|
|
33
|
+
export const normalizeEventDates = <T extends CalendarEvent>(event: T): T => {
|
|
34
|
+
if (!isDate(event.start) && !isDate(event.end)) {
|
|
35
|
+
return event;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...event,
|
|
40
|
+
start: isDate(event.start) ? event.start.toISOString() : event.start,
|
|
41
|
+
end: isDate(event.end) ? event.end.toISOString() : event.end,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
5
45
|
export const generatePrefabHours = (
|
|
6
46
|
timeFormat: string = "HH:mm"
|
|
7
47
|
): PrefabHour[] => {
|
|
@@ -62,15 +102,19 @@ export const isAllDayOrSpansMidnight = <T extends CalendarEvent>(
|
|
|
62
102
|
return true;
|
|
63
103
|
}
|
|
64
104
|
|
|
65
|
-
|
|
66
|
-
const startMoment = moment.tz(start, timezone);
|
|
67
|
-
const endMoment = moment.tz(end, timezone);
|
|
105
|
+
ensureFormatters(timezone);
|
|
68
106
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
);
|
|
107
|
+
const endDateObj = toDate(end);
|
|
108
|
+
|
|
109
|
+
// If end hour is 0 in user's timezone, subtract 1 minute so events
|
|
110
|
+
// ending exactly at midnight aren't considered as spanning midnight
|
|
111
|
+
const endHour = parseInt(_hourFormatter.format(endDateObj), 10);
|
|
112
|
+
const effectiveEnd =
|
|
113
|
+
endHour === 0 ? new Date(endDateObj.getTime() - 60_000) : endDateObj;
|
|
114
|
+
|
|
115
|
+
const startDay = _dayFormatter.format(toDate(start));
|
|
116
|
+
const endDay = _dayFormatter.format(effectiveEnd);
|
|
117
|
+
return startDay !== endDay;
|
|
74
118
|
};
|
|
75
119
|
|
|
76
120
|
// Returns the count of unique dates in the provided timezone
|
|
@@ -79,17 +123,20 @@ export const getDurationInDays = <T extends CalendarEvent>(
|
|
|
79
123
|
timezone: string
|
|
80
124
|
) => {
|
|
81
125
|
// the event duration in days calculation depends on if the event is all day
|
|
82
|
-
|
|
83
|
-
|
|
126
|
+
if (calendarEvent.isAllDay) {
|
|
127
|
+
return (
|
|
128
|
+
moment
|
|
84
129
|
.tz(calendarEvent.end, timezone)
|
|
85
130
|
.diff(moment.tz(calendarEvent.start, timezone), "days") + 1
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return size(
|
|
134
|
+
daysInRange({
|
|
135
|
+
startDate: calendarEvent.start,
|
|
136
|
+
endDate: calendarEvent.end,
|
|
137
|
+
timezone,
|
|
138
|
+
})
|
|
139
|
+
);
|
|
93
140
|
};
|
|
94
141
|
|
|
95
142
|
// Returns an array of days (e.g. ['2022-01-02']) in a given date range.
|
|
@@ -102,23 +149,20 @@ export const daysInRange = ({
|
|
|
102
149
|
endDate: Date | string;
|
|
103
150
|
timezone: string;
|
|
104
151
|
}) => {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const startDay = moment.tz(startDate, timezone).format("YYYY-MM-DD");
|
|
152
|
+
const startMoment = moment.tz(startDate, timezone);
|
|
153
|
+
const endMoment = moment.tz(endDate, timezone);
|
|
154
|
+
const countOfDaysInRange = endMoment.diff(startMoment, "days");
|
|
109
155
|
const days = [];
|
|
156
|
+
const maxIterations = Math.min(30, Math.abs(countOfDaysInRange));
|
|
110
157
|
// Make sure we loop at a max of 30 times here as we had events that were scheduled for all day long for
|
|
111
158
|
// 1000 years in the future and this was causing the app to crash
|
|
112
159
|
for (
|
|
113
160
|
let countOfDaysAfterStart = 0;
|
|
114
|
-
countOfDaysAfterStart <=
|
|
161
|
+
countOfDaysAfterStart <= maxIterations;
|
|
115
162
|
countOfDaysAfterStart++
|
|
116
163
|
) {
|
|
117
164
|
days.push(
|
|
118
|
-
|
|
119
|
-
.tz(startDay, timezone)
|
|
120
|
-
.add(countOfDaysAfterStart, "day")
|
|
121
|
-
.format("YYYY-MM-DD")
|
|
165
|
+
startMoment.clone().add(countOfDaysAfterStart, "day").format("YYYY-MM-DD")
|
|
122
166
|
);
|
|
123
167
|
}
|
|
124
168
|
return days;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getDuration,
|
|
11
11
|
getDurationInDays,
|
|
12
12
|
isAllDayOrSpansMidnight,
|
|
13
|
+
normalizeEventDates,
|
|
13
14
|
startOfUserWeek,
|
|
14
15
|
} from "../utils/date-utils";
|
|
15
16
|
import computePositioning from "../utils/compute-positioning";
|
|
@@ -42,6 +43,10 @@ export const generateEventLayouts = <T extends CalendarEvent>({
|
|
|
42
43
|
startDayOfWeekOffset = 0,
|
|
43
44
|
timezone,
|
|
44
45
|
}: GenerateEventLayouts<T>) => {
|
|
46
|
+
// Normalize start/end to strings before events flow into any layout object,
|
|
47
|
+
// since layouts get closed over by Reanimated worklets that can't clone Dates.
|
|
48
|
+
events = events.map(normalizeEventDates);
|
|
49
|
+
|
|
45
50
|
// Calculate spacial layout for CalendarViewDay events
|
|
46
51
|
// in month view, midnight-spanning part-day events are rendered as all-day multi-day
|
|
47
52
|
const [allDayEvents, partDayEvents] = _partition([...events], (event) =>
|
|
@@ -76,16 +81,13 @@ export const generateEventLayouts = <T extends CalendarEvent>({
|
|
|
76
81
|
...(calendarViewInterval === "month" ? partDayEventsSorted : []),
|
|
77
82
|
];
|
|
78
83
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const startCalendarMoment = moment.tz(startCalendarDate, timezone);
|
|
85
|
+
const endCalendarMoment = moment.tz(endCalendarDate, timezone);
|
|
86
|
+
const visibleDayCount = endCalendarMoment.diff(startCalendarMoment, "days");
|
|
82
87
|
const visibleDays: string[] = [];
|
|
83
88
|
for (let dayCount = 0; dayCount <= visibleDayCount; dayCount++) {
|
|
84
89
|
visibleDays.push(
|
|
85
|
-
|
|
86
|
-
.tz(startCalendarDate, timezone)
|
|
87
|
-
.add(dayCount, "day")
|
|
88
|
-
.format("YYYY-MM-DD")
|
|
90
|
+
startCalendarMoment.clone().add(dayCount, "day").format("YYYY-MM-DD")
|
|
89
91
|
);
|
|
90
92
|
}
|
|
91
93
|
|
|
@@ -103,15 +105,14 @@ export const generateEventLayouts = <T extends CalendarEvent>({
|
|
|
103
105
|
} = {};
|
|
104
106
|
|
|
105
107
|
calendarViewDateRanges.forEach((date) => {
|
|
108
|
+
const basisMoment = moment.tz(date.basisDate, timezone);
|
|
106
109
|
const calendarLayoutOptions = {
|
|
107
110
|
visibleX: date.dayIndexes,
|
|
108
111
|
enableWeekBreaks: calendarViewInterval === "month",
|
|
109
|
-
startOfWeekXOffset:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
"days"
|
|
114
|
-
),
|
|
112
|
+
startOfWeekXOffset: basisMoment.diff(
|
|
113
|
+
startOfUserWeek(startDayOfWeekOffset, date.basisDate!, timezone),
|
|
114
|
+
"days"
|
|
115
|
+
),
|
|
115
116
|
};
|
|
116
117
|
const calendarViewDayLayout = _reduce(
|
|
117
118
|
calendarViewDayEvents,
|
|
@@ -119,7 +120,7 @@ export const generateEventLayouts = <T extends CalendarEvent>({
|
|
|
119
120
|
const eventStartIndex = moment
|
|
120
121
|
.tz(event.start, timezone)
|
|
121
122
|
.startOf("day")
|
|
122
|
-
.diff(
|
|
123
|
+
.diff(basisMoment, "days");
|
|
123
124
|
const eventDurationDays = getDurationInDays(event, timezone);
|
|
124
125
|
|
|
125
126
|
calendarLayout.findFitAndInsert(
|
|
@@ -139,16 +140,13 @@ export const generateEventLayouts = <T extends CalendarEvent>({
|
|
|
139
140
|
calendarDates.forEach((calendarDate) => {
|
|
140
141
|
const currentDayDate = moment.tz(calendarDate, timezone).startOf("day");
|
|
141
142
|
const startDate = currentDayDate.toDate();
|
|
142
|
-
const endDate =
|
|
143
|
+
const endDate = currentDayDate.clone().endOf("day").toDate();
|
|
143
144
|
const showHours = !["month"].includes(calendarViewInterval);
|
|
144
145
|
const dayId = calendarDate;
|
|
145
146
|
const allDayEventsLayout: AllDayEventLayoutType<T>[] = [];
|
|
146
147
|
let partDayEventsLayout: PartDayEventLayoutType<T>[] = [];
|
|
147
148
|
|
|
148
|
-
const x =
|
|
149
|
-
moment.tz(date.basisDate, timezone),
|
|
150
|
-
"days"
|
|
151
|
-
);
|
|
149
|
+
const x = currentDayDate.diff(basisMoment, "days");
|
|
152
150
|
for (let y = 0; y < calendarViewDayRowHeight; ++y) {
|
|
153
151
|
const {
|
|
154
152
|
event,
|