@open-webapp/calendar 0.9.0 → 0.9.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/dist/react/CalendarPane.js +50 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { filterByRsvp, groupByDay, getOrganizerLabel, formatDayHeader, formatRibbonLabel, isEventCurrent, findNextUpcomingEvent, getCalendarEventKey, DEFAULT_RSVP_STATUSES, } from '../core/events.js';
|
|
4
4
|
import { GroupedCalendarSelector } from './CalendarSelector.js';
|
|
5
5
|
function toggleRsvpStatus(selection, status) {
|
|
@@ -144,6 +144,8 @@ export function CalendarPane(props) {
|
|
|
144
144
|
const chipsRef = useRef(null);
|
|
145
145
|
const scrollRef = useRef(null);
|
|
146
146
|
const popupRef = useRef(null);
|
|
147
|
+
const anchorSnapshotRef = useRef(null);
|
|
148
|
+
const didMountRsvpRef = useRef(false);
|
|
147
149
|
const [now, setNow] = useState(() => new Date());
|
|
148
150
|
const unacceptedCount = useMemo(() => events.filter((event) => event.selfResponseStatus === 'needsAction').length, [events]);
|
|
149
151
|
const filteredEvents = useMemo(() => filterByRsvp(events, selectedRsvpStatuses), [events, selectedRsvpStatuses]);
|
|
@@ -167,6 +169,53 @@ export function CalendarPane(props) {
|
|
|
167
169
|
const nextUpcomingEvent = findNextUpcomingEvent(filteredEvents, now);
|
|
168
170
|
const selectedEventKey = currentEventKeys.values().next().value ??
|
|
169
171
|
(nextUpcomingEvent ? getCalendarEventKey(nextUpcomingEvent) : undefined);
|
|
172
|
+
function captureAnchorSnapshot() {
|
|
173
|
+
const scrollEl = scrollRef.current;
|
|
174
|
+
if (!scrollEl)
|
|
175
|
+
return;
|
|
176
|
+
const scrollRect = scrollEl.getBoundingClientRect();
|
|
177
|
+
const offsets = new Map();
|
|
178
|
+
let anchor;
|
|
179
|
+
for (const [dateKey, header] of dayHeaderRefs.current) {
|
|
180
|
+
const rect = header.getBoundingClientRect();
|
|
181
|
+
offsets.set(dateKey, rect.top - scrollRect.top);
|
|
182
|
+
if (rect.bottom > scrollRect.top && (anchor === undefined || dateKey < anchor))
|
|
183
|
+
anchor = dateKey;
|
|
184
|
+
}
|
|
185
|
+
anchorSnapshotRef.current = anchor === undefined ? null : { anchor, offsets };
|
|
186
|
+
}
|
|
187
|
+
// Keep the visible date stable when RSVP filtering adds/removes day groups above the viewport.
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const scrollEl = scrollRef.current;
|
|
190
|
+
if (!scrollEl)
|
|
191
|
+
return;
|
|
192
|
+
const onScroll = () => captureAnchorSnapshot();
|
|
193
|
+
scrollEl.addEventListener('scroll', onScroll, { passive: true });
|
|
194
|
+
return () => scrollEl.removeEventListener('scroll', onScroll);
|
|
195
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
196
|
+
}, []);
|
|
197
|
+
useLayoutEffect(() => {
|
|
198
|
+
if (!didMountRsvpRef.current) {
|
|
199
|
+
didMountRsvpRef.current = true;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const scrollEl = scrollRef.current;
|
|
203
|
+
const snap = anchorSnapshotRef.current;
|
|
204
|
+
if (!scrollEl || !snap)
|
|
205
|
+
return;
|
|
206
|
+
const keys = Array.from(dayHeaderRefs.current.keys()).sort();
|
|
207
|
+
const target = (dayHeaderRefs.current.has(snap.anchor) ? snap.anchor : undefined) ??
|
|
208
|
+
keys.find((k) => k > snap.anchor && snap.offsets.has(k)) ??
|
|
209
|
+
keys.reverse().find((k) => k < snap.anchor && snap.offsets.has(k));
|
|
210
|
+
const header = target ? dayHeaderRefs.current.get(target) : undefined;
|
|
211
|
+
if (target && header) {
|
|
212
|
+
const delta = header.getBoundingClientRect().top - scrollEl.getBoundingClientRect().top - snap.offsets.get(target);
|
|
213
|
+
if (delta !== 0)
|
|
214
|
+
scrollEl.scrollTop += delta;
|
|
215
|
+
}
|
|
216
|
+
captureAnchorSnapshot();
|
|
217
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
218
|
+
}, [selectedRsvpStatuses]);
|
|
170
219
|
function handleToggle(status) {
|
|
171
220
|
props.onRsvpStatusesChange(toggleRsvpStatus(selectedRsvpStatuses, status));
|
|
172
221
|
}
|