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; }