'use client'; import { ChevronDown, Search } from 'lucide-react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useEffect, useRef, useState } from 'react'; import { Wordmark } from '@/components/brand/logo'; import { cn } from '@/lib/cn'; import { moreGroups, primaryNav, routes } from '@/lib/site'; import { DensityToggle } from './density'; import { useOpenSearch } from './search-context'; import { ThemeToggle } from './theme'; const MORE_HREFS = moreGroups.flatMap((g) => g.items.map((i) => i.href)); /** * Header: `AI Atlas | Models · Frontier · Benchmarks · Prices · Research · Graph · Changes · More ▾ | Search ⌘K | theme | density`. * "More" is a keyboard-accessible menu panel (Enter/Space/↓ opens, ↑↓ move, Home/End, Esc closes, click outside closes). * Below lg the primary nav lives in the bottom tab bar (`MobileTabBar`); the header keeps search, theme and density. */ export function SiteHeader() { const pathname = usePathname(); const openSearch = useOpenSearch(); const [more, setMore] = useState(false); const moreRef = useRef(null); const moreBtn = useRef(null); useEffect(() => setMore(false), [pathname]); useEffect(() => { if (!more) return; const onDoc = (e: MouseEvent) => { if (!moreRef.current?.contains(e.target as Node)) setMore(false); }; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { setMore(false); moreBtn.current?.focus(); } }; document.addEventListener('mousedown', onDoc); document.addEventListener('keydown', onKey); const first = moreRef.current?.querySelector('[role="menuitem"]'); first?.focus(); return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); }; }, [more]); const onMenuKey = (e: React.KeyboardEvent) => { const items = [...(moreRef.current?.querySelectorAll('[role="menuitem"]') ?? [])]; const i = items.indexOf(document.activeElement as HTMLElement); if (e.key === 'ArrowDown') { e.preventDefault(); items[(i + 1) % items.length]?.focus(); } else if (e.key === 'ArrowUp') { e.preventDefault(); items[(i - 1 + items.length) % items.length]?.focus(); } else if (e.key === 'Home') { e.preventDefault(); items[0]?.focus(); } else if (e.key === 'End') { e.preventDefault(); items[items.length - 1]?.focus(); } else if (e.key === 'Tab') setMore(false); }; const isActive = (href: string) => pathname === href || pathname.startsWith(href + '/'); const moreActive = MORE_HREFS.some(isActive) && !primaryNav.some((n) => isActive(n.href)); const navCls = (active: boolean) => cn('flex h-9 items-center gap-1 rounded-sm px-2.5 text-[13.5px] transition-colors', active ? 'text-ink font-medium' : 'text-ink-2 hover:text-ink'); return (
); }