From 97f6fb91d877cb3b0fbc8ec3b2338595488cbef1 Mon Sep 17 00:00:00 2001 From: Cristian Felipe Cruz Buitron Date: Tue, 25 Aug 2026 13:44:38 -0500 Subject: [PATCH] feat(ecommerce): mega-menu de Departamentos multi-columna en el navbar Reemplaza la fila horizontal de pildoras por categoria (ADR 0005 seccion 3, patron Walmart) por un trigger unico "Departamentos" que abre un panel multi-columna con las 6 categorias raiz y sus hijos, activado por hover (mouse) o tap (touch), con cierre por click-fuera y Escape. --- .../ecommerce/components/layout/navbar.tsx | 162 ++++++++++-------- 1 file changed, 94 insertions(+), 68 deletions(-) diff --git a/workloads/ecommerce/components/layout/navbar.tsx b/workloads/ecommerce/components/layout/navbar.tsx index 5e0df39..1ae0ecc 100644 --- a/workloads/ecommerce/components/layout/navbar.tsx +++ b/workloads/ecommerce/components/layout/navbar.tsx @@ -3,8 +3,8 @@ import Image from "next/image"; import Link from "next/link"; import { usePathname, useSearchParams } from "next/navigation"; -import { ChevronDown, Heart, Menu, MessageCircle, PackageCheck, ShoppingCart, UserRound } from "lucide-react"; -import { Suspense, useMemo, useState } from "react"; +import { ChevronDown, Heart, LayoutGrid, Menu, MessageCircle, PackageCheck, ShoppingCart, UserRound } from "lucide-react"; +import { Suspense, useEffect, useMemo, useRef, useState } from "react"; import { PredictiveSearch } from "@/components/search/predictive-search"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent } from "@/components/ui/dialog"; @@ -151,94 +151,120 @@ function mobileLinkClass(active: boolean): string { function DesktopCategoryNavigation({ tree, onOpenMenu }: { tree: CategoryNode[]; onOpenMenu: () => void }) { const { isActive, isAllActive } = useCategoryNavigationState(); + return ; +} +function DesktopCategoryNavigationFallback({ tree, onOpenMenu }: { tree: CategoryNode[]; onOpenMenu: () => void }) { + return false} isAllActive={false} />; +} + +function CategoryNavBar({ + tree, + onOpenMenu, + isActive, + isAllActive, +}: { + tree: CategoryNode[]; + onOpenMenu: () => void; + isActive: (name: string) => boolean; + isAllActive: boolean; +}) { return ( ); } -function DesktopCategoryNavigationFallback({ tree, onOpenMenu }: { tree: CategoryNode[]; onOpenMenu: () => void }) { +function DepartmentsMegaMenu({ tree, isActive }: { tree: CategoryNode[]; isActive: (name: string) => boolean }) { + const [open, setOpen] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + if (!open) return; + function handlePointerDown(event: PointerEvent) { + if (containerRef.current && !containerRef.current.contains(event.target as Node)) setOpen(false); + } + function handleKeyDown(event: KeyboardEvent) { + if (event.key === "Escape") setOpen(false); + } + document.addEventListener("pointerdown", handlePointerDown); + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("pointerdown", handlePointerDown); + document.removeEventListener("keydown", handleKeyDown); + }; + }, [open]); + return ( - + {open && ( +
+
+ {tree.map((root) => ( +
+ setOpen(false)} + className={`block text-sm font-black uppercase tracking-wide transition ${ + isActive(root.name) ? "text-fuchsia-400" : "text-white hover:text-fuchsia-400" + }`} + > + {root.name} + + {root.children.length > 0 && ( +
    + {root.children.map((child) => ( +
  • + setOpen(false)} + className={`block text-sm transition ${isActive(child.name) ? "font-bold text-violet-300" : "text-white/70 hover:text-white"}`} + > + {child.name} + +
  • + ))} +
+ )} +
+ ))} +
+
+ )} + ); } -- 2.54.0