Files
web-menu-react-version-/src/pages/menu/components/ScrollEventHandler.tsx
2025-10-04 18:22:24 +03:00

98 lines
3.7 KiB
TypeScript

import { useScrollHandler } from "contexts/ScrollHandlerContext";
import { useEffect, useRef } from "react";
export default function ScrollEventHandler() {
const {
setShowScrollTop,
setIsCategoriesSticky,
categoriesContainerRef,
categoryRefs,
activeCategory,
setActiveCategory
} = useScrollHandler();
const hasSetInitialCategory = useRef(false);
// Set initial active category when categories are available
useEffect(() => {
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);
hasSetInitialCategory.current = true;
}
}, [categoryRefs, setActiveCategory]);
useEffect(() => {
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) {
// Get the original position of the categories container
const originalTop = categoriesContainerRef.current.offsetTop;
// 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);
}
// Find the most visible category based on scroll position
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 viewportHeight = window.innerHeight;
// Calculate visibility ratio
const elementTop = rect.top;
const elementBottom = rect.bottom;
const elementHeight = rect.height;
// Calculate how much of the element is visible
let visibleHeight = 0;
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 < viewportHeight && elementBottom > viewportHeight) {
// Element is partially visible from bottom
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) {
mostVisibleCategory = {
id: parseInt(categoryId),
visibility
};
}
}
}
});
// Update active category if we found a visible one
if (mostVisibleCategory) {
setActiveCategory((mostVisibleCategory as { id: number; visibility: number }).id);
}
}
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [categoriesContainerRef, setIsCategoriesSticky, setShowScrollTop, categoryRefs, setActiveCategory, activeCategory]);
return null;
}