From 5e5ef57a007bc2c38e4dd4fce72c0345f48547ac Mon Sep 17 00:00:00 2001 From: Chad Curtis Date: Tue, 3 Mar 2026 11:13:36 -0600 Subject: [PATCH] Stop auto-scroll at list limits, clean up interval on unmount --- src/components/SidebarMoreMenu.tsx | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/components/SidebarMoreMenu.tsx b/src/components/SidebarMoreMenu.tsx index e0148c90..57169cf3 100644 --- a/src/components/SidebarMoreMenu.tsx +++ b/src/components/SidebarMoreMenu.tsx @@ -35,15 +35,25 @@ function useScrollCarets(deps: unknown[]) { // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { const el = scrollRef.current; if (!el) return; const ro = new ResizeObserver(update); ro.observe(el); update(); return () => ro.disconnect(); }, deps); - const startScroll = useCallback((direction: 'up' | 'down') => { - if (intervalRef.current) clearInterval(intervalRef.current); - intervalRef.current = setInterval(() => { scrollRef.current?.scrollBy({ top: direction === 'up' ? -8 : 8 }); update(); }, 16); - }, [update]); - const stopScroll = useCallback(() => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } - update(); - }, [update]); + }, []); + + const startScroll = useCallback((direction: 'up' | 'down') => { + stopScroll(); + intervalRef.current = setInterval(() => { + const el = scrollRef.current; + if (!el) return stopScroll(); + el.scrollBy({ top: direction === 'up' ? -8 : 8 }); + update(); + // stop automatically when the limit is reached + const atLimit = direction === 'up' ? el.scrollTop <= 0 : el.scrollTop + el.clientHeight >= el.scrollHeight - 1; + if (atLimit) stopScroll(); + }, 16); + }, [update, stopScroll]); + + // clean up interval on unmount + useEffect(() => stopScroll, [stopScroll]); return { scrollRef, canScrollUp, canScrollDown, onScroll: update, startScroll, stopScroll }; }