WinslowFan's picture
Toggle header nav by orientation (hamburger only in portrait)
3acde0a
Raw
History Blame Contribute Delete
3.62 kB
import { useEffect, useState } from 'react';
import { BookOpen, Database, Download, Menu, X } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
export function Header() {
const location = useLocation();
const [menuOpen, setMenuOpen] = useState(false);
// Close the mobile menu whenever the route changes
useEffect(() => {
setMenuOpen(false);
}, [location.pathname]);
const navItemClass = (active: boolean) =>
`flex items-center gap-1.5 px-3 py-2 text-sm rounded-lg transition-colors ${
active ? 'bg-slate-100 text-slate-900 font-semibold' : 'text-slate-600 hover:bg-slate-50'
}`;
return (
<header className="border-b border-gray-200 bg-white sticky top-0 z-50 shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 py-4 flex items-center justify-between gap-3 sm:gap-6">
<Link to="/" className="flex items-center gap-3 hover:opacity-80 transition-opacity min-w-0">
<div className="w-9 h-9 bg-slate-900 rounded-lg flex items-center justify-center shrink-0">
<Database className="w-5 h-5 text-white" />
</div>
<div className="min-w-0">
<div className="text-xl font-semibold text-slate-900 truncate">MuSProt</div>
<div className="header-nav-subtitle text-xs text-slate-500 truncate">Multistate Protein Structure Database</div>
</div>
</Link>
{/* Full nav — shown in landscape (W >= H) */}
<nav className="header-nav-full items-center gap-2">
<Link to="/" className={navItemClass(location.pathname === '/')}>
<Database className="w-4 h-4 shrink-0" />
Explorer
</Link>
<Link to="/docs" className={navItemClass(location.pathname === '/docs')}>
<BookOpen className="w-4 h-4 shrink-0" />
Docs
</Link>
<a
href="/api/protein/download/db"
className="flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-slate-900 rounded-lg hover:bg-slate-700 transition-colors"
>
<Download className="w-4 h-4 shrink-0" />
Download DB
</a>
</nav>
{/* Menu toggle — shown only in portrait (W < H) */}
<button
type="button"
aria-label="Toggle navigation menu"
aria-expanded={menuOpen}
onClick={() => setMenuOpen((open) => !open)}
className="header-nav-toggle items-center justify-center p-2 rounded-lg text-slate-700 hover:bg-slate-100 transition-colors shrink-0"
>
{menuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
</div>
{/* Dropdown menu — portrait only */}
{menuOpen && (
<nav className="header-nav-dropdown border-t border-gray-200 bg-white px-4 py-3 flex-col gap-1">
<Link to="/" className={navItemClass(location.pathname === '/')}>
<Database className="w-4 h-4 shrink-0" />
Explorer
</Link>
<Link to="/docs" className={navItemClass(location.pathname === '/docs')}>
<BookOpen className="w-4 h-4 shrink-0" />
Docs
</Link>
<a
href="/api/protein/download/db"
onClick={() => setMenuOpen(false)}
className="flex items-center gap-1.5 px-3 py-2 text-sm font-medium text-white bg-slate-900 rounded-lg hover:bg-slate-700 transition-colors"
>
<Download className="w-4 h-4 shrink-0" />
Download DB
</a>
</nav>
)}
</header>
);
}