Fix mobile tab header and swipe behavior
This commit is contained in:
@@ -21,6 +21,7 @@ type SwipeTabsProps<T extends string | number> = {
|
||||
swipeDisabled?: boolean;
|
||||
threshold?: number;
|
||||
renderWindow?: number;
|
||||
scrollToTopOnChange?: boolean;
|
||||
};
|
||||
|
||||
const DRAG_START_DISTANCE = 3;
|
||||
@@ -55,7 +56,8 @@ export function SwipeTabs<T extends string | number>({
|
||||
panelClassName,
|
||||
swipeDisabled = false,
|
||||
threshold = DEFAULT_THRESHOLD,
|
||||
renderWindow = DEFAULT_RENDER_WINDOW
|
||||
renderWindow = DEFAULT_RENDER_WINDOW,
|
||||
scrollToTopOnChange = true
|
||||
}: SwipeTabsProps<T>) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const trackRef = useRef<HTMLDivElement>(null);
|
||||
@@ -77,6 +79,7 @@ export function SwipeTabs<T extends string | number>({
|
||||
const pendingIndexRef = useRef<number | null>(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<T extends string | number>({
|
||||
};
|
||||
}, []);
|
||||
|
||||
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;
|
||||
|
||||
@@ -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<HTMLDivElement, TabsScrollerProps>(
|
||||
children,
|
||||
scrollStep,
|
||||
ariaLabelLeft = 'Scroll tabs left',
|
||||
ariaLabelRight = 'Scroll tabs right'
|
||||
ariaLabelRight = 'Scroll tabs right',
|
||||
showArrows = false
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -63,10 +65,21 @@ const TabsScroller = forwardRef<HTMLDivElement, TabsScrollerProps>(
|
||||
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;
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function AdminPanel({ initialTab = 'general' }: { initialTab?: Ta
|
||||
return (
|
||||
<main className="main-content admin-panel-root">
|
||||
<div className="page-tabs-container">
|
||||
<TabsScroller className="page-tabs-slider">
|
||||
<TabsScroller className="page-tabs-slider" showArrows>
|
||||
<button className="mobile-menu-btn" onClick={toggleMobileMenu} aria-label={t.theme.toggleMenu}>
|
||||
<span className="material-symbols-outlined">menu</span>
|
||||
</button>
|
||||
|
||||
@@ -744,7 +744,7 @@ export default function Features() {
|
||||
return (
|
||||
<main className="main-content admin-panel-root">
|
||||
<div className="page-tabs-container">
|
||||
<TabsScroller className="page-tabs-slider" ref={tabsContainerRef}>
|
||||
<TabsScroller className="page-tabs-slider" ref={tabsContainerRef} showArrows>
|
||||
<button className="mobile-menu-btn" onClick={toggleMobileMenu} aria-label={t.theme.toggleMenu}>
|
||||
<span className="material-symbols-outlined">menu</span>
|
||||
</button>
|
||||
|
||||
@@ -661,7 +661,7 @@ export default function ThemeSettings() {
|
||||
)}
|
||||
{/* Modern Tab Navigation */}
|
||||
<div className="page-tabs-container">
|
||||
<TabsScroller className="page-tabs-slider">
|
||||
<TabsScroller className="page-tabs-slider" showArrows>
|
||||
<button className="mobile-menu-btn" onClick={toggleMobileMenu} aria-label={t.theme.toggleMenu}>
|
||||
<span className="material-symbols-outlined">menu</span>
|
||||
</button>
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user