From fc27786c8832a79bd8d8803e2f3a33ba7ae7b21b Mon Sep 17 00:00:00 2001 From: matteoscrugli Date: Tue, 13 Jan 2026 01:29:53 +0100 Subject: [PATCH] Add automatic tab centering to TabsScroller - Clicked tabs now scroll to center of container automatically - Behavior works on all pages using TabsScroller - Removed duplicate scroll logic from Features.tsx Co-Authored-By: Claude Opus 4.5 --- frontend/src/components/TabsScroller.tsx | 15 +++++++++++++++ frontend/src/pages/admin/Features.tsx | 24 +++--------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/TabsScroller.tsx b/frontend/src/components/TabsScroller.tsx index 335ada1..f9e70f1 100644 --- a/frontend/src/components/TabsScroller.tsx +++ b/frontend/src/components/TabsScroller.tsx @@ -168,8 +168,22 @@ const TabsScroller = forwardRef( }, 150); }; const handleScrollEnd = () => updateOverflow(); + + // Scroll clicked tab to center + const handleTabClick = (event: Event) => { + const target = event.target as HTMLElement; + const tabButton = target.closest('.page-tab-btn, .admin-tab-btn') as HTMLElement; + if (!tabButton) return; + const nodeWidth = node.clientWidth; + const buttonLeft = tabButton.offsetLeft; + const buttonWidth = tabButton.offsetWidth; + const scrollLeft = buttonLeft - (nodeWidth / 2) + (buttonWidth / 2); + node.scrollTo({ left: Math.max(0, scrollLeft), behavior: 'smooth' }); + }; + node.addEventListener('scroll', handleScroll, { passive: true }); node.addEventListener('scrollend', handleScrollEnd, { passive: true }); + node.addEventListener('click', handleTabClick); let resizeObserver: ResizeObserver | null = null; if (typeof ResizeObserver !== 'undefined') { resizeObserver = new ResizeObserver(() => scheduleOverflowUpdate()); @@ -189,6 +203,7 @@ const TabsScroller = forwardRef( return () => { node.removeEventListener('scroll', handleScroll); node.removeEventListener('scrollend', handleScrollEnd); + node.removeEventListener('click', handleTabClick); if (scrollEndTimeoutRef.current) { clearTimeout(scrollEndTimeoutRef.current); } diff --git a/frontend/src/pages/admin/Features.tsx b/frontend/src/pages/admin/Features.tsx index 1de965a..f4f8193 100644 --- a/frontend/src/pages/admin/Features.tsx +++ b/frontend/src/pages/admin/Features.tsx @@ -51,7 +51,6 @@ export default function Features() { const [localOrder, setLocalOrder] = useState([]); const [localPositions, setLocalPositions] = useState>({}); const [hasOrderChanges, setHasOrderChanges] = useState(false); - const tabsContainerRef = useRef(null); const isUserEditing = useRef(false); // Track if user is actively editing const initialOrderRef = useRef([]); const initialPositionsRef = useRef>({}); @@ -155,27 +154,10 @@ export default function Features() { localPositionsRef.current = localPositions; }, [localPositions]); - // Scroll active tab to center of container - const scrollActiveTabIntoView = useCallback((tabId: string) => { - if (tabsContainerRef.current) { - const container = tabsContainerRef.current; - const activeButton = container.querySelector(`[data-tab-id="${tabId}"]`) as HTMLElement; - if (activeButton) { - const containerWidth = container.clientWidth; - const buttonLeft = activeButton.offsetLeft; - const buttonWidth = activeButton.offsetWidth; - // Calculate scroll position to center the button - const scrollLeft = buttonLeft - (containerWidth / 2) + (buttonWidth / 2); - container.scrollTo({ left: Math.max(0, scrollLeft), behavior: 'smooth' }); - } - } - }, []); - - // Handle tab change with scroll + // Handle tab change (centering is handled by TabsScroller) const handleTabChange = useCallback((tabId: TabId) => { setActiveTab(tabId); - setTimeout(() => scrollActiveTabIntoView(tabId), 50); - }, [scrollActiveTabIntoView]); + }, []); // Keep saveRef updated with latest function useEffect(() => { saveRef.current = saveModulesToBackend; @@ -787,7 +769,7 @@ export default function Features() { onMenuClick={toggleMobileMenu} />
- +
{t.featuresPage.title}