diff --git a/src/components/ShopMenu/ShopMenu.jsx b/src/components/ShopMenu/ShopMenu.jsx index 850cb54c..a69f434e 100644 --- a/src/components/ShopMenu/ShopMenu.jsx +++ b/src/components/ShopMenu/ShopMenu.jsx @@ -1,20 +1,77 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useEffect, useCallback } from 'react'; import { ChevronDown, ChevronUp, ArrowRight } from 'lucide-react'; const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { const [focusedIndex, setFocusedIndex] = useState(-1); + const [animationState, setAnimationState] = useState('closed'); // 'closed', 'opening', 'open', 'closing' + const [showScrollToTop, setShowScrollToTop] = useState(false); const menuItemsRef = useRef([]); const shopButtonRef = useRef(null); + // Handle opening animation + const handleOpenMenu = () => { + setAnimationState('opening'); + setTimeout(() => { + setAnimationState('open'); + }, 500); // Match animation duration + }; + + // Handle closing animation + const handleCloseMenu = useCallback(() => { + if (animationState === 'closing') return; // Prevent multiple close calls + setAnimationState('closing'); + setTimeout(() => { + setShopOpen(false); + setAnimationState('closed'); + setFocusedIndex(-1); + }, 500); // Match animation duration + }, [animationState, setShopOpen]); + + // Prevent background scrolling when menu is open + useEffect(() => { + if (shopOpen || animationState === 'opening' || animationState === 'open' || animationState === 'closing') { + // Add overflow-hidden to body + document.body.style.overflow = 'hidden'; + + // Make main content inert for screen readers + const mainContent = document.querySelector('main'); + if (mainContent) { + mainContent.setAttribute('inert', 'true'); + mainContent.setAttribute('aria-hidden', 'true'); + } + } else { + // Restore scrolling + document.body.style.overflow = ''; + + // Restore main content accessibility + const mainContent = document.querySelector('main'); + if (mainContent) { + mainContent.removeAttribute('inert'); + mainContent.removeAttribute('aria-hidden'); + } + } + + // Cleanup on unmount + return () => { + document.body.style.overflow = ''; + const mainContent = document.querySelector('main'); + if (mainContent) { + mainContent.removeAttribute('inert'); + mainContent.removeAttribute('aria-hidden'); + } + }; + }, [shopOpen, animationState]); + // Close shop menu when clicking outside useEffect(() => { const handleClickOutside = (event) => { if ( shopButtonRef.current && - !shopButtonRef.current.contains(event.target) + !shopButtonRef.current.contains(event.target) && + shopOpen && + animationState !== 'closing' ) { - setShopOpen(false); - setFocusedIndex(-1); + handleCloseMenu(); } }; @@ -22,7 +79,32 @@ const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { return () => { document.removeEventListener('mousedown', handleClickOutside); }; - }, [setShopOpen]); + }, [setShopOpen, shopOpen, animationState, handleCloseMenu]); + + // Handle opening animation when shopOpen changes + useEffect(() => { + if (shopOpen && animationState === 'closed') { + handleOpenMenu(); + } + }, [shopOpen, animationState]); + + // Handle scroll to top functionality + useEffect(() => { + const handleScroll = () => { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + setShowScrollToTop(scrollTop > 300); // Show arrow when scrolled down 300px + }; + + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + const scrollToTop = () => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + }; // Keyboard accessibility useEffect(() => { @@ -31,8 +113,7 @@ const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { switch (event.key) { case 'Escape': - setShopOpen(false); - setFocusedIndex(-1); + handleCloseMenu(); shopButtonRef.current?.focus(); break; case 'ArrowDown': @@ -92,7 +173,7 @@ const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { return () => { document.removeEventListener('keydown', handleKeyDown); }; - }, [shopOpen, focusedIndex, setShopOpen, shopButtonRef]); + }, [shopOpen, focusedIndex, setShopOpen, shopButtonRef, handleCloseMenu]); // Focus management useEffect(() => { @@ -103,10 +184,10 @@ const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { // Handle navigation to collection const handleCollectionClick = (collectionName) => { - const url = `https://corexshoptest.onrender.com/api/collections/${collectionName.toLowerCase()}`; + const encodedName = encodeURIComponent(collectionName.toLowerCase()); + const url = `https://corexshoptest.onrender.com/api/collections/${encodedName}`; window.open(url, '_blank'); - setShopOpen(false); - setFocusedIndex(-1); + handleCloseMenu(); }; // Helper function to create menu item with accessibility @@ -137,7 +218,11 @@ const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - onShopClick(); + if (shopOpen || animationState === 'opening' || animationState === 'open') { + handleCloseMenu(); + } else { + onShopClick(); + } }} onKeyDown={onShopKeyDown} className={`px-5 py-2 rounded-full font-medium transition-all duration-300 cursor-pointer flex items-center ${ @@ -157,9 +242,11 @@ const ShopMenu = ({ shopOpen, setShopOpen, onShopClick, onShopKeyDown }) => { {/* Mega Menu */} - {shopOpen && ( + {(shopOpen || animationState === 'opening' || animationState === 'open' || animationState === 'closing') && (