| 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); |
|
|
| |
| 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> |
| ); |
| } |
|
|