An interactive demonstration of sitemap megamenu header dropdown panels. Explore columns, tabs, search panels, and dashboard widgets.
Sitemap Dropdown Laboratory
Compare sitemap dropdown/popover styles: mega menu grids, tab panels, icon tile grids, spotlight searchable dropdowns, and role dashboard cards.
Acme Pest Control
Dashboard
Help
Variant1_MegaMenuGrid.tsx (Widget Implementation)
import { useState, useRef } from "react";
import { Text, Group, Box, SimpleGrid, UnstyledButton, Badge, Paper } from "@mantine/core";
import { HiMap } from "react-icons/hi2";
import type { SitemapDropdownProps, NavLink } from "./types";
export function Variant1_MegaMenuGrid({
categories,
onLinkClick,
triggerLabel = "Sitemap",
}: SitemapDropdownProps) {
const [open, setOpen] = useState(false);
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const handleMouseEnter = () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
setOpen(true);
};
const handleMouseLeave = () => {
timeoutRef.current = setTimeout(() => setOpen(false), 200);
};
const handleClick = (link: NavLink) => {
onLinkClick?.(link);
setOpen(false);
};
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{ position: "relative" }}
>
{/* Trigger */}
<UnstyledButton>
<Group gap={6}>
<HiMap size={16} color="#4fc3f7" />
<Text size="sm" fw={500} style={{ color: "#4fc3f7" }}>
{triggerLabel}
</Text>
</Group>
</UnstyledButton>
{/* Dropdown */}
{open && (
<Paper
shadow="xl"
radius="md"
p="lg"
style={{
position: "fixed",
top: "auto",
left: 0,
right: 0,
marginTop: 8,
zIndex: 1000,
border: "1px solid #e9ecef",
maxWidth: 960,
marginLeft: "auto",
marginRight: "auto",
}}
>
<SimpleGrid cols={{ base: 1, xs: 2, sm: 3, md: 4 }} spacing="lg">
{categories.map((category) => (
<div key={category.id}>
<Group gap={6} mb="sm">
<Box c="blue.6">{category.icon}</Box>
<Text fw={700} size="sm" c="blue.7" tt="uppercase">
{category.label}
</Text>
</Group>
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
{category.links.map((link) => (
<UnstyledButton
key={link.id}
onClick={() => handleClick(link)}
px="sm"
py={6}
style={{
borderRadius: 6,
transition: "background-color 150ms ease",
}}
onMouseOver={(e) => {
e.currentTarget.style.backgroundColor = "#f1f3f5";
}}
onMouseOut={(e) => {
e.currentTarget.style.backgroundColor = "transparent";
}}
>
<Group gap="sm" justify="space-between" wrap="nowrap">
<Group gap="xs" wrap="nowrap">
<Box c="gray.6">{link.icon}</Box>
<div>
<Text size="sm" fw={500}>
{link.label}
</Text>
{link.description && (
<Text size="xs" c="dimmed" lineClamp={1}>
{link.description}
</Text>
)}
</div>
</Group>
{link.badge && (
<Badge size="xs" variant="light" color="orange">
{link.badge}
</Badge>
)}
</Group>
</UnstyledButton>
))}
</div>
</div>
))}
</SimpleGrid>
</Paper>
)}
</div>
);
}