Initial commit
This commit is contained in:
97
src/pages/menu/components/ScrollEventHandler.tsx
Normal file
97
src/pages/menu/components/ScrollEventHandler.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user