diff --git a/src/components/SideBySideView/SideBySideView.tsx b/src/components/SideBySideView/SideBySideView.tsx index d1dc9e7..e2c27a1 100644 --- a/src/components/SideBySideView/SideBySideView.tsx +++ b/src/components/SideBySideView/SideBySideView.tsx @@ -2,6 +2,7 @@ import { useState, useMemo, useEffect, + useRef, Dispatch, SetStateAction, useCallback, @@ -384,6 +385,39 @@ export default function SideBySideView({ lastPosition: null, }); + // Shared pagination and scroll sync + const [sharedPage, setSharedPage] = useState(0); + const leftScrollRef = useRef(null); + const rightScrollRef = useRef(null); + /** Prevents ping-pong when programmatic scrollTop fires a scroll event on the peer after the sync tick. */ + const scrollSyncSource = useRef<'left' | 'right' | null>(null); + + const handleLeftScroll = useCallback((scrollTop: number) => { + if (scrollSyncSource.current === 'right') return; + const peer = rightScrollRef.current; + if (!peer || Math.abs(peer.scrollTop - scrollTop) < 1) return; + scrollSyncSource.current = 'left'; + peer.scrollTop = scrollTop; + requestAnimationFrame(() => { + requestAnimationFrame(() => { + if (scrollSyncSource.current === 'left') scrollSyncSource.current = null; + }); + }); + }, []); + + const handleRightScroll = useCallback((scrollTop: number) => { + if (scrollSyncSource.current === 'left') return; + const peer = leftScrollRef.current; + if (!peer || Math.abs(peer.scrollTop - scrollTop) < 1) return; + scrollSyncSource.current = 'right'; + peer.scrollTop = scrollTop; + requestAnimationFrame(() => { + requestAnimationFrame(() => { + if (scrollSyncSource.current === 'right') scrollSyncSource.current = null; + }); + }); + }, []); + // Filter charted users based on people status const filteredChartedUsers = useMemo( () => ({ @@ -540,6 +574,10 @@ export default function SideBySideView({ isGeneralDays={isGeneralDays} setCalendarHeight={setCalendarHeight} calendarLabel="Your Availability" + currentStartPage={sharedPage} + onPageChange={setSharedPage} + scrollRef={leftScrollRef} + onScroll={handleLeftScroll} /> @@ -603,6 +641,10 @@ export default function SideBySideView({ setChartedUsers={setChartedUsers} chartedUsers={chartedUsers} calendarLabel="Group Availability" + currentStartPage={sharedPage} + onPageChange={setSharedPage} + scrollRef={rightScrollRef} + onScroll={handleRightScroll} /> diff --git a/src/components/selectCalendarComponents/CalendarApp.tsx b/src/components/selectCalendarComponents/CalendarApp.tsx index 78e2148..1766b41 100644 --- a/src/components/selectCalendarComponents/CalendarApp.tsx +++ b/src/components/selectCalendarComponents/CalendarApp.tsx @@ -45,6 +45,10 @@ interface CalendarProps { setCalendarHeight?: Dispatch>; compactMode?: boolean; calendarLabel?: string; + currentStartPage?: number; + onPageChange?: (page: number) => void; + scrollRef?: React.RefObject; + onScroll?: (scrollTop: number) => void; } export default function Calendar({ @@ -64,6 +68,10 @@ export default function Calendar({ setCalendarHeight, compactMode = false, calendarLabel, + currentStartPage: controlledPage, + onPageChange, + scrollRef, + onScroll, }: CalendarProps) { const [calendarFramework, setCalendarFramework] = theCalendarFramework; const [calendarState, setCalendarState] = theCalendarState; @@ -133,18 +141,21 @@ export default function Calendar({ }; }, [compactMode]); - const [currentStartPage, setCurrentStartPage] = React.useState(0); + const [internalStartPage, setInternalStartPage] = React.useState(0); + const currentStartPage = controlledPage ?? internalStartPage; const handlePrev = () => { - setCurrentStartPage(Math.max(currentStartPage - 1, 0)); + const next = Math.max(currentStartPage - numberOfColumnsPerPage, 0); + onPageChange ? onPageChange(next) : setInternalStartPage(next); }; const handleNext = () => { - if ( - currentStartPage + numberOfColumnsPerPage < - calendarFramework.dates.flat().length - ) { - setCurrentStartPage(currentStartPage + 1); + if (currentStartPage + numberOfColumnsPerPage < calendarFramework.dates.flat().length) { + const next = Math.min( + currentStartPage + numberOfColumnsPerPage, + calendarFramework.dates.flat().length - numberOfColumnsPerPage + ); + onPageChange ? onPageChange(next) : setInternalStartPage(next); } }; @@ -194,7 +205,7 @@ export default function Calendar({
onScroll((e.target as HTMLDivElement).scrollTop) : undefined} >
diff --git a/src/components/selectCalendarComponents/SelectCalendar.tsx b/src/components/selectCalendarComponents/SelectCalendar.tsx index 6888506..00ed840 100644 --- a/src/components/selectCalendarComponents/SelectCalendar.tsx +++ b/src/components/selectCalendarComponents/SelectCalendar.tsx @@ -128,14 +128,10 @@ const handleDisappear = useCallback(() => { }, [handleMouseLeave]); return ( -
+
-
-
+
+