Revert "refactor sticky scroll handler"

This reverts commit da9d7f8eb3.
This commit is contained in:
2025-10-09 00:20:39 +03:00
parent 5a9973d5b3
commit dea9a59127
4 changed files with 78 additions and 214 deletions

View File

@@ -15,28 +15,10 @@ export function FloatingButton() {
const { showScrollTop } = useScrollHandler();
const scrollToTop = useCallback(() => {
// Use a more stable approach to find the scroll container
const findScrollContainer = () => {
const antApp = document.querySelector('.ant-app') as HTMLElement;
if (antApp && antApp.scrollHeight > antApp.clientHeight) {
return antApp;
}
const appContainer = document.querySelector('[class*="App"]') as HTMLElement;
if (appContainer && appContainer.scrollHeight > appContainer.clientHeight) {
return appContainer;
}
return document.documentElement;
};
const scrollContainer = findScrollContainer();
if (scrollContainer) {
scrollContainer.scrollTo({
window.scrollTo({
top: 0,
behavior: "smooth",
});
}
}, []);
return (

View File

@@ -41,36 +41,10 @@ export function ScrollHandlerProvider({ children }: { children: ReactNode }) {
// Function to scroll to a specific category
const scrollToCategory = useCallback((categoryId: number) => {
const categoryRef = categoryRefs.current?.[categoryId];
// Use a more stable approach to find the scroll container
const findScrollContainer = () => {
const antApp = document.querySelector('.ant-app') as HTMLElement;
if (antApp && antApp.scrollHeight > antApp.clientHeight) {
return antApp;
}
const appContainer = document.querySelector('[class*="App"]') as HTMLElement;
if (appContainer && appContainer.scrollHeight > appContainer.clientHeight) {
return appContainer;
}
return document.documentElement;
};
const scrollContainer = findScrollContainer();
if (categoryRef && scrollContainer) {
// Get the position of the category relative to the scroll container
const categoryRect = categoryRef.getBoundingClientRect();
const containerRect = scrollContainer.getBoundingClientRect();
// Calculate the target scroll position
const targetScrollTop = scrollContainer.scrollTop + categoryRect.top - containerRect.top;
// Smooth scroll to the target position
scrollContainer.scrollTo({
top: targetScrollTop,
behavior: "smooth"
if (categoryRef) {
categoryRef.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
}, []);

View File

@@ -1,5 +1,5 @@
import { useScrollHandler } from "contexts/ScrollHandlerContext";
import { useCallback, useEffect, useRef } from "react";
import { useEffect, useRef } from "react";
export default function ScrollEventHandler() {
const {
@@ -7,52 +7,15 @@ export default function ScrollEventHandler() {
setIsCategoriesSticky,
categoriesContainerRef,
categoryRefs,
setActiveCategory,
activeCategory,
setActiveCategory
} = useScrollHandler();
const hasSetInitialCategory = useRef(false);
const scrollContainerRef = useRef<HTMLElement | null>(null);
const lastStickyState = useRef(false);
const originalCategoriesTop = useRef<number | null>(null);
// Find the main scrollable container - use a more stable approach
useEffect(() => {
// Use a more stable approach - find the container once and cache it
const findScrollContainer = () => {
// Try to find the Ant Design app container first
const antApp = document.querySelector(".ant-app") as HTMLElement;
if (antApp && antApp.scrollHeight > antApp.clientHeight) {
return antApp;
}
// Fallback to any container with App in the class name
const appContainer = document.querySelector(
'[class*="App"]',
) as HTMLElement;
if (
appContainer &&
appContainer.scrollHeight > appContainer.clientHeight
) {
return appContainer;
}
// Last resort - use document element
return document.documentElement;
};
const scrollContainer = findScrollContainer();
if (scrollContainer) {
scrollContainerRef.current = scrollContainer;
}
}, []);
// Set initial active category when categories are available
useEffect(() => {
if (
categoryRefs.current &&
Object.keys(categoryRefs.current).length > 0 &&
!hasSetInitialCategory.current
) {
if (categoryRefs.current && Object.keys(categoryRefs.current).length > 0 && !hasSetInitialCategory.current) {
// Set the first category as active initially
const firstCategoryId = parseInt(Object.keys(categoryRefs.current)[0]);
setActiveCategory(firstCategoryId);
@@ -60,84 +23,59 @@ export default function ScrollEventHandler() {
}
}, [categoryRefs, setActiveCategory]);
// Store the original position of categories container once
useEffect(() => {
if (
categoriesContainerRef.current &&
originalCategoriesTop.current === null
) {
const rect = categoriesContainerRef.current.getBoundingClientRect();
originalCategoriesTop.current =
rect.top + (scrollContainerRef.current?.scrollTop || 0);
}
}, [categoriesContainerRef]);
const handleScroll = useCallback(() => {
// Use the scrollable container instead of window
const scrollTop = scrollContainerRef.current?.scrollTop || 0;
const handleScroll = () => {
const scrollTop =
window.pageYOffset || document.documentElement.scrollTop;
setShowScrollTop(scrollTop > 300); // Show button after scrolling 300px
// Check if we should make categories sticky
if (
categoriesContainerRef.current &&
scrollContainerRef.current &&
originalCategoriesTop.current !== null
) {
// Use the cached original position to prevent flickering
const threshold = 50; // Increased threshold to prevent flickering
const shouldBeSticky =
scrollTop > originalCategoriesTop.current + threshold;
if (categoriesContainerRef.current) {
// Get the original position of the categories container
const originalTop = categoriesContainerRef.current.offsetTop;
// Only update state if it actually changed to prevent unnecessary re-renders
if (shouldBeSticky !== lastStickyState.current) {
// Only make sticky when we've scrolled past the original position
// and return to normal when we scroll back above it
// Add a small threshold (10px) to prevent flickering
const shouldBeSticky = scrollTop > originalTop + 10;
setIsCategoriesSticky(shouldBeSticky);
lastStickyState.current = shouldBeSticky;
}
}
// Find the most visible category based on scroll position
if (categoryRefs.current && scrollContainerRef.current) {
if (categoryRefs.current) {
let mostVisibleCategory: { id: number; visibility: number } | null = null;
Object.entries(categoryRefs.current).forEach(([categoryId, element]) => {
if (element) {
const rect = element.getBoundingClientRect();
const containerRect =
scrollContainerRef.current!.getBoundingClientRect();
const viewportHeight = window.innerHeight;
// Calculate visibility ratio relative to the scroll container
const elementTop = rect.top - containerRect.top;
const elementBottom = elementTop + rect.height;
const containerHeight = containerRect.height;
// Calculate visibility ratio
const elementTop = rect.top;
const elementBottom = rect.bottom;
const elementHeight = rect.height;
// Calculate how much of the element is visible within the container
// Calculate how much of the element is visible
let visibleHeight = 0;
if (elementTop >= 0 && elementBottom <= containerHeight) {
if (elementTop >= 0 && elementBottom <= viewportHeight) {
// Element is fully visible
visibleHeight = elementHeight;
} else if (elementTop < 0 && elementBottom > 0) {
// Element is partially visible from top
visibleHeight = elementBottom;
} else if (
elementTop < containerHeight &&
elementBottom > containerHeight
) {
} else if (elementTop < viewportHeight && elementBottom > viewportHeight) {
// Element is partially visible from bottom
visibleHeight = containerHeight - elementTop;
visibleHeight = viewportHeight - elementTop;
}
const visibility = visibleHeight / elementHeight;
// Only consider elements that are at least 30% visible
if (visibility > 0.3) {
if (
!mostVisibleCategory ||
visibility > mostVisibleCategory.visibility
) {
if (!mostVisibleCategory || visibility > mostVisibleCategory.visibility) {
mostVisibleCategory = {
id: parseInt(categoryId),
visibility,
visibility
};
}
}
@@ -146,26 +84,14 @@ export default function ScrollEventHandler() {
// Update active category if we found a visible one
if (mostVisibleCategory) {
setActiveCategory(
(mostVisibleCategory as { id: number; visibility: number }).id,
);
setActiveCategory((mostVisibleCategory as { id: number; visibility: number }).id);
}
}
}, [
setShowScrollTop,
categoriesContainerRef,
categoryRefs,
setIsCategoriesSticky,
setActiveCategory,
]); // categoryRefs and categoriesContainerRef are refs, don't need to be in deps
};
useEffect(() => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
scrollContainer.addEventListener("scroll", handleScroll);
return () => scrollContainer.removeEventListener("scroll", handleScroll);
}
}, [handleScroll]);
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [categoriesContainerRef, setIsCategoriesSticky, setShowScrollTop, categoryRefs, setActiveCategory, activeCategory]);
return null;
}

View File

@@ -27,29 +27,11 @@ export const ScrollToTop: React.FC = () => {
const { pathname } = useLocation();
useEffect(() => {
// Use a more stable approach to find the scroll container
const findScrollContainer = () => {
const antApp = document.querySelector('.ant-app') as HTMLElement;
if (antApp && antApp.scrollHeight > antApp.clientHeight) {
return antApp;
}
const appContainer = document.querySelector('[class*="App"]') as HTMLElement;
if (appContainer && appContainer.scrollHeight > appContainer.clientHeight) {
return appContainer;
}
return document.documentElement;
};
const scrollContainer = findScrollContainer();
if (scrollContainer) {
scrollContainer.scrollTo({
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
}); // Scroll to the top when the location changes
}
}, [pathname]);
return null; // This component doesn't render anything