From 005526d5af40d916d0a62ea489e3e87b76664ab1 Mon Sep 17 00:00:00 2001 From: matteoscrugli Date: Tue, 13 Jan 2026 01:24:14 +0100 Subject: [PATCH] Fix tab scroll arrows not updating at scroll end - Add debounced scroll-end detection (150ms) for trackpad/touch scrolling - Add scrollend event listener for native browser support - Add 350ms timeout fallback after arrow button clicks - Arrows now update immediately when reaching scroll boundaries Co-Authored-By: Claude Opus 4.5 --- frontend/src/components/TabsScroller.tsx | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/TabsScroller.tsx b/frontend/src/components/TabsScroller.tsx index b08efc5..335ada1 100644 --- a/frontend/src/components/TabsScroller.tsx +++ b/frontend/src/components/TabsScroller.tsx @@ -40,6 +40,7 @@ const TabsScroller = forwardRef( const { tabBarPosition } = useTheme(); const sliderRef = useRef(null); const rafRef = useRef(0); + const scrollEndTimeoutRef = useRef | null>(null); const [showLeft, setShowLeft] = useState(false); const [showRight, setShowRight] = useState(false); const heightRef = useRef(0); @@ -156,8 +157,19 @@ const TabsScroller = forwardRef( if (!node) return undefined; const container = node.closest('.page-tabs-container, .admin-tabs-container'); - const handleScroll = () => scheduleOverflowUpdate(); + const handleScroll = () => { + scheduleOverflowUpdate(); + // Debounced scroll-end detection for trackpad/touch scrolling + if (scrollEndTimeoutRef.current) { + clearTimeout(scrollEndTimeoutRef.current); + } + scrollEndTimeoutRef.current = setTimeout(() => { + updateOverflow(); + }, 150); + }; + const handleScrollEnd = () => updateOverflow(); node.addEventListener('scroll', handleScroll, { passive: true }); + node.addEventListener('scrollend', handleScrollEnd, { passive: true }); let resizeObserver: ResizeObserver | null = null; if (typeof ResizeObserver !== 'undefined') { resizeObserver = new ResizeObserver(() => scheduleOverflowUpdate()); @@ -176,6 +188,10 @@ const TabsScroller = forwardRef( } return () => { node.removeEventListener('scroll', handleScroll); + node.removeEventListener('scrollend', handleScrollEnd); + if (scrollEndTimeoutRef.current) { + clearTimeout(scrollEndTimeoutRef.current); + } resizeObserver?.disconnect(); if (typeof window !== 'undefined') { window.removeEventListener('resize', scheduleOverflowUpdate); @@ -222,8 +238,12 @@ const TabsScroller = forwardRef( left: direction === 'left' ? -step : step, behavior: 'smooth' }); + // Force update after smooth scroll completes (fallback for browsers without scrollend) + setTimeout(() => { + updateOverflow(); + }, 350); }, - [scrollStep] + [scrollStep, updateOverflow] ); const handlePointerDown = useCallback((event: PointerEvent) => {