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 <noreply@anthropic.com>
This commit is contained in:
@@ -168,8 +168,22 @@ const TabsScroller = forwardRef<HTMLDivElement, TabsScrollerProps>(
|
|||||||
}, 150);
|
}, 150);
|
||||||
};
|
};
|
||||||
const handleScrollEnd = () => updateOverflow();
|
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('scroll', handleScroll, { passive: true });
|
||||||
node.addEventListener('scrollend', handleScrollEnd, { passive: true });
|
node.addEventListener('scrollend', handleScrollEnd, { passive: true });
|
||||||
|
node.addEventListener('click', handleTabClick);
|
||||||
let resizeObserver: ResizeObserver | null = null;
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
if (typeof ResizeObserver !== 'undefined') {
|
if (typeof ResizeObserver !== 'undefined') {
|
||||||
resizeObserver = new ResizeObserver(() => scheduleOverflowUpdate());
|
resizeObserver = new ResizeObserver(() => scheduleOverflowUpdate());
|
||||||
@@ -189,6 +203,7 @@ const TabsScroller = forwardRef<HTMLDivElement, TabsScrollerProps>(
|
|||||||
return () => {
|
return () => {
|
||||||
node.removeEventListener('scroll', handleScroll);
|
node.removeEventListener('scroll', handleScroll);
|
||||||
node.removeEventListener('scrollend', handleScrollEnd);
|
node.removeEventListener('scrollend', handleScrollEnd);
|
||||||
|
node.removeEventListener('click', handleTabClick);
|
||||||
if (scrollEndTimeoutRef.current) {
|
if (scrollEndTimeoutRef.current) {
|
||||||
clearTimeout(scrollEndTimeoutRef.current);
|
clearTimeout(scrollEndTimeoutRef.current);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export default function Features() {
|
|||||||
const [localOrder, setLocalOrder] = useState<string[]>([]);
|
const [localOrder, setLocalOrder] = useState<string[]>([]);
|
||||||
const [localPositions, setLocalPositions] = useState<Record<string, 'top' | 'bottom'>>({});
|
const [localPositions, setLocalPositions] = useState<Record<string, 'top' | 'bottom'>>({});
|
||||||
const [hasOrderChanges, setHasOrderChanges] = useState(false);
|
const [hasOrderChanges, setHasOrderChanges] = useState(false);
|
||||||
const tabsContainerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const isUserEditing = useRef(false); // Track if user is actively editing
|
const isUserEditing = useRef(false); // Track if user is actively editing
|
||||||
const initialOrderRef = useRef<string[]>([]);
|
const initialOrderRef = useRef<string[]>([]);
|
||||||
const initialPositionsRef = useRef<Record<string, 'top' | 'bottom'>>({});
|
const initialPositionsRef = useRef<Record<string, 'top' | 'bottom'>>({});
|
||||||
@@ -155,27 +154,10 @@ export default function Features() {
|
|||||||
localPositionsRef.current = localPositions;
|
localPositionsRef.current = localPositions;
|
||||||
}, [localPositions]);
|
}, [localPositions]);
|
||||||
|
|
||||||
// Scroll active tab to center of container
|
// Handle tab change (centering is handled by TabsScroller)
|
||||||
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
|
|
||||||
const handleTabChange = useCallback((tabId: TabId) => {
|
const handleTabChange = useCallback((tabId: TabId) => {
|
||||||
setActiveTab(tabId);
|
setActiveTab(tabId);
|
||||||
setTimeout(() => scrollActiveTabIntoView(tabId), 50);
|
}, []);
|
||||||
}, [scrollActiveTabIntoView]);
|
|
||||||
// Keep saveRef updated with latest function
|
// Keep saveRef updated with latest function
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
saveRef.current = saveModulesToBackend;
|
saveRef.current = saveModulesToBackend;
|
||||||
@@ -787,7 +769,7 @@ export default function Features() {
|
|||||||
onMenuClick={toggleMobileMenu}
|
onMenuClick={toggleMobileMenu}
|
||||||
/>
|
/>
|
||||||
<div className="page-tabs-container">
|
<div className="page-tabs-container">
|
||||||
<TabsScroller className="page-tabs-slider" ref={tabsContainerRef} showArrows>
|
<TabsScroller className="page-tabs-slider" showArrows>
|
||||||
<div className="page-title-section">
|
<div className="page-title-section">
|
||||||
<span className="page-title-text">{t.featuresPage.title}</span>
|
<span className="page-title-text">{t.featuresPage.title}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user