diff --git a/frontend/src/components/SwipeTabs.tsx b/frontend/src/components/SwipeTabs.tsx index 6edda47..b4589e9 100644 --- a/frontend/src/components/SwipeTabs.tsx +++ b/frontend/src/components/SwipeTabs.tsx @@ -21,6 +21,7 @@ type SwipeTabsProps = { swipeDisabled?: boolean; threshold?: number; renderWindow?: number; + scrollToTopOnChange?: boolean; }; const DRAG_START_DISTANCE = 3; @@ -55,7 +56,8 @@ export function SwipeTabs({ panelClassName, swipeDisabled = false, threshold = DEFAULT_THRESHOLD, - renderWindow = DEFAULT_RENDER_WINDOW + renderWindow = DEFAULT_RENDER_WINDOW, + scrollToTopOnChange = true }: SwipeTabsProps) { const containerRef = useRef(null); const trackRef = useRef(null); @@ -77,6 +79,7 @@ export function SwipeTabs({ const pendingIndexRef = useRef(null); const isAnimatingRef = useRef(false); const needsResetRef = useRef(false); + const hasScrolledRef = useRef(false); const dragRef = useRef({ pointerId: null as number | null, touchId: null as number | null, @@ -164,6 +167,16 @@ export function SwipeTabs({ }; }, []); + useEffect(() => { + if (!scrollToTopOnChange) return; + if (!hasScrolledRef.current) { + hasScrolledRef.current = true; + return; + } + if (typeof window === 'undefined') return; + window.scrollTo({ top: 0, left: 0, behavior: 'auto' }); + }, [activeTab, scrollToTopOnChange]); + const resetToIndex = useCallback( (index: number) => { displayIndexRef.current = index; diff --git a/frontend/src/components/TabsScroller.tsx b/frontend/src/components/TabsScroller.tsx index 96931c1..e5b4091 100644 --- a/frontend/src/components/TabsScroller.tsx +++ b/frontend/src/components/TabsScroller.tsx @@ -16,6 +16,7 @@ type TabsScrollerProps = { scrollStep?: number; ariaLabelLeft?: string; ariaLabelRight?: string; + showArrows?: boolean; }; const SCROLL_EDGE_THRESHOLD = 8; @@ -30,7 +31,8 @@ const TabsScroller = forwardRef( children, scrollStep, ariaLabelLeft = 'Scroll tabs left', - ariaLabelRight = 'Scroll tabs right' + ariaLabelRight = 'Scroll tabs right', + showArrows = false }, ref ) => { @@ -63,10 +65,21 @@ const TabsScroller = forwardRef( const updateOverflow = useCallback(() => { const node = sliderRef.current; if (!node) return; + if (!showArrows) { + setShowLeft(false); + setShowRight(false); + return; + } + const hasTabs = Boolean(node.querySelector('.page-tab-btn, .admin-tab-btn')); + if (!hasTabs) { + setShowLeft(false); + setShowRight(false); + return; + } const maxScroll = node.scrollWidth - node.clientWidth; setShowLeft(node.scrollLeft > SCROLL_EDGE_THRESHOLD); setShowRight(node.scrollLeft < maxScroll - SCROLL_EDGE_THRESHOLD); - }, []); + }, [showArrows]); const scheduleOverflowUpdate = useCallback(() => { if (rafRef.current) return; diff --git a/frontend/src/pages/AdminPanel.tsx b/frontend/src/pages/AdminPanel.tsx index bd3a328..4263bf5 100644 --- a/frontend/src/pages/AdminPanel.tsx +++ b/frontend/src/pages/AdminPanel.tsx @@ -25,7 +25,7 @@ export default function AdminPanel({ initialTab = 'general' }: { initialTab?: Ta return (
- + diff --git a/frontend/src/pages/admin/Features.tsx b/frontend/src/pages/admin/Features.tsx index 454d091..37c7610 100644 --- a/frontend/src/pages/admin/Features.tsx +++ b/frontend/src/pages/admin/Features.tsx @@ -744,7 +744,7 @@ export default function Features() { return (
- + diff --git a/frontend/src/pages/admin/ThemeSettings.tsx b/frontend/src/pages/admin/ThemeSettings.tsx index 2229e17..61dd729 100644 --- a/frontend/src/pages/admin/ThemeSettings.tsx +++ b/frontend/src/pages/admin/ThemeSettings.tsx @@ -661,7 +661,7 @@ export default function ThemeSettings() { )} {/* Modern Tab Navigation */}
- + diff --git a/frontend/src/styles/Layout.css b/frontend/src/styles/Layout.css index 754edfb..61ee82c 100644 --- a/frontend/src/styles/Layout.css +++ b/frontend/src/styles/Layout.css @@ -464,6 +464,10 @@ body { min-height: 48px; } + .tabs-scroll-shell { + width: 100%; + } + .page-title-section, .admin-title-section { display: flex; @@ -609,6 +613,8 @@ body { width: 100%; max-width: none; margin: 0; + flex: 1 1 auto; + min-height: 0; } /* Swipeable tab panels */