From 3bc748d2d36d96aab0e250a2044b26bd27f58da2 Mon Sep 17 00:00:00 2001 From: Sushil Sharma Date: Sat, 11 Oct 2025 21:51:28 +0545 Subject: [PATCH 1/2] feat: expand shop menu for improved UX --- src/components/Header/MainHeader.jsx | 436 +++++++++++++++++++++++---- 1 file changed, 376 insertions(+), 60 deletions(-) diff --git a/src/components/Header/MainHeader.jsx b/src/components/Header/MainHeader.jsx index c5873c14..5c541ab6 100644 --- a/src/components/Header/MainHeader.jsx +++ b/src/components/Header/MainHeader.jsx @@ -1,48 +1,164 @@ -import React, { useState, useEffect, useRef } from "react"; -import { Link } from "react-router-dom"; +import React, { useState, useRef, useEffect } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; import { Menu, X, ChevronDown, + ChevronUp, Search, Heart, User, ShoppingCart, -} from "lucide-react"; -import SearchBox from "../Search/SearchBox"; + ArrowRight, +} from 'lucide-react'; +import SearchBox from '../Search/SearchBox'; import TopHeader from "../TopHeader/TopHeader"; export default function Header() { const [mobileOpen, setMobileOpen] = useState(false); const [shopOpen, setShopOpen] = useState(false); const [search, setSearch] = useState(false); - const mobileMenuRef = useRef(null); + const [focusedIndex, setFocusedIndex] = useState(-1); + const shopMenuRef = useRef(null); + const shopButtonRef = useRef(null); + const menuItemsRef = useRef([]); + const navigate = useNavigate(); - // Close mobile menu on outside click + // Close shop menu when clicking outside useEffect(() => { - const handleClickOutside = (e) => { - if (mobileMenuRef.current && !mobileMenuRef.current.contains(e.target)) { - setMobileOpen(false); + const handleClickOutside = (event) => { + if (shopMenuRef.current && !shopMenuRef.current.contains(event.target)) { + setShopOpen(false); + setFocusedIndex(-1); } }; - document.addEventListener("mousedown", handleClickOutside); + + document.addEventListener('mousedown', handleClickOutside); return () => { - document.removeEventListener("mousedown", handleClickOutside); + document.removeEventListener('mousedown', handleClickOutside); }; }, []); - // Close mobile menu on Escape key + // Keyboard accessibility useEffect(() => { - const handleEscape = (e) => { - if (e.key === "Escape") { - setMobileOpen(false); + const handleKeyDown = (event) => { + if (!shopOpen) return; + + switch (event.key) { + case 'Escape': + setShopOpen(false); + setFocusedIndex(-1); + shopButtonRef.current?.focus(); + break; + case 'ArrowDown': + event.preventDefault(); + setFocusedIndex(prev => { + const nextIndex = prev + 1; + const totalItems = menuItemsRef.current.length; + return nextIndex >= totalItems ? 0 : nextIndex; + }); + break; + case 'ArrowUp': + event.preventDefault(); + setFocusedIndex(prev => { + const prevIndex = prev - 1; + const totalItems = menuItemsRef.current.length; + return prevIndex < 0 ? totalItems - 1 : prevIndex; + }); + break; + case 'ArrowRight': + event.preventDefault(); + setFocusedIndex(prev => { + // Move to next column (every 4 items per column) + const itemsPerColumn = 4; + const currentColumn = Math.floor(prev / itemsPerColumn); + const nextColumn = (currentColumn + 1) % 8; // 8 total columns + const nextIndex = nextColumn * itemsPerColumn + (prev % itemsPerColumn); + return nextIndex < menuItemsRef.current.length ? nextIndex : prev; + }); + break; + case 'ArrowLeft': + event.preventDefault(); + setFocusedIndex(prev => { + // Move to previous column (every 4 items per column) + const itemsPerColumn = 4; + const currentColumn = Math.floor(prev / itemsPerColumn); + const prevColumn = currentColumn === 0 ? 7 : currentColumn - 1; // 8 total columns + const prevIndex = prevColumn * itemsPerColumn + (prev % itemsPerColumn); + return prevIndex < menuItemsRef.current.length ? prevIndex : prev; + }); + break; + case 'Enter': + case ' ': + if (focusedIndex >= 0) { + event.preventDefault(); + const focusedItem = menuItemsRef.current[focusedIndex]; + if (focusedItem) { + focusedItem.click(); + } + } + break; } }; - document.addEventListener("keydown", handleEscape); + + document.addEventListener('keydown', handleKeyDown); return () => { - document.removeEventListener("keydown", handleEscape); + document.removeEventListener('keydown', handleKeyDown); }; - }, []); + }, [shopOpen, focusedIndex]); + + // Focus management + useEffect(() => { + if (focusedIndex >= 0 && menuItemsRef.current[focusedIndex]) { + menuItemsRef.current[focusedIndex].focus(); + } + }, [focusedIndex]); + + // Handle shop button click + const handleShopClick = () => { + setShopOpen(!shopOpen); + if (!shopOpen) { + setTimeout(() => setFocusedIndex(0), 100); + } else { + setFocusedIndex(-1); + } + }; + + // Handle shop button keyboard events + const handleShopButtonKeyDown = (event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + handleShopClick(); + } + }; + + // Handle navigation to collection + const handleCollectionClick = (collectionName) => { + const url = `https://corexshoptest.onrender.com/api/collections/${collectionName.toLowerCase()}`; + window.open(url, '_blank'); + setShopOpen(false); + setFocusedIndex(-1); + }; + + // Helper function to create menu item with accessibility + const createMenuItem = (collectionName, displayName, index) => ( + + ); return ( <> @@ -63,63 +179,263 @@ export default function Header() { - {/* Desktop Navigation */} -