Commit ·
a4bdc89
1
Parent(s): d87c1fb
Collapse header nav into a hamburger menu on mobile
Browse filesCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
frontend/src/components/layout/Header.tsx
CHANGED
|
@@ -1,8 +1,20 @@
|
|
| 1 |
-
import {
|
|
|
|
| 2 |
import { Link, useLocation } from 'react-router-dom';
|
| 3 |
|
| 4 |
export function Header() {
|
| 5 |
const location = useLocation();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
return (
|
| 8 |
<header className="border-b border-gray-200 bg-white sticky top-0 z-50 shadow-sm">
|
|
@@ -17,34 +29,58 @@ export function Header() {
|
|
| 17 |
</div>
|
| 18 |
</Link>
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
className={`flex items-center gap-1.5 px-2.5 sm:px-3 py-2 text-sm rounded-lg transition-colors ${
|
| 24 |
-
location.pathname === '/' ? 'bg-slate-100 text-slate-900 font-semibold' : 'text-slate-600 hover:bg-slate-50'
|
| 25 |
-
}`}
|
| 26 |
-
>
|
| 27 |
<Database className="w-4 h-4 shrink-0" />
|
| 28 |
-
|
| 29 |
</Link>
|
| 30 |
-
<Link
|
| 31 |
-
to="/docs"
|
| 32 |
-
className={`flex items-center gap-1.5 px-2.5 sm:px-3 py-2 text-sm rounded-lg transition-colors ${
|
| 33 |
-
location.pathname === '/docs' ? 'bg-slate-100 text-slate-900 font-semibold' : 'text-slate-600 hover:bg-slate-50'
|
| 34 |
-
}`}
|
| 35 |
-
>
|
| 36 |
<BookOpen className="w-4 h-4 shrink-0" />
|
| 37 |
-
|
| 38 |
</Link>
|
| 39 |
<a
|
| 40 |
href="/api/protein/download/db"
|
| 41 |
-
className="flex items-center gap-1.5 px-
|
| 42 |
>
|
| 43 |
<Download className="w-4 h-4 shrink-0" />
|
| 44 |
-
|
| 45 |
</a>
|
| 46 |
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
</header>
|
| 49 |
);
|
| 50 |
}
|
|
|
|
| 1 |
+
import { useEffect, useState } from 'react';
|
| 2 |
+
import { BookOpen, Database, Download, Menu, X } from 'lucide-react';
|
| 3 |
import { Link, useLocation } from 'react-router-dom';
|
| 4 |
|
| 5 |
export function Header() {
|
| 6 |
const location = useLocation();
|
| 7 |
+
const [menuOpen, setMenuOpen] = useState(false);
|
| 8 |
+
|
| 9 |
+
// Close the mobile menu whenever the route changes
|
| 10 |
+
useEffect(() => {
|
| 11 |
+
setMenuOpen(false);
|
| 12 |
+
}, [location.pathname]);
|
| 13 |
+
|
| 14 |
+
const navItemClass = (active: boolean) =>
|
| 15 |
+
`flex items-center gap-1.5 px-3 py-2 text-sm rounded-lg transition-colors ${
|
| 16 |
+
active ? 'bg-slate-100 text-slate-900 font-semibold' : 'text-slate-600 hover:bg-slate-50'
|
| 17 |
+
}`;
|
| 18 |
|
| 19 |
return (
|
| 20 |
<header className="border-b border-gray-200 bg-white sticky top-0 z-50 shadow-sm">
|
|
|
|
| 29 |
</div>
|
| 30 |
</Link>
|
| 31 |
|
| 32 |
+
{/* Desktop nav */}
|
| 33 |
+
<nav className="hidden sm:flex items-center gap-2">
|
| 34 |
+
<Link to="/" className={navItemClass(location.pathname === '/')}>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
<Database className="w-4 h-4 shrink-0" />
|
| 36 |
+
Explorer
|
| 37 |
</Link>
|
| 38 |
+
<Link to="/docs" className={navItemClass(location.pathname === '/docs')}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
<BookOpen className="w-4 h-4 shrink-0" />
|
| 40 |
+
Docs
|
| 41 |
</Link>
|
| 42 |
<a
|
| 43 |
href="/api/protein/download/db"
|
| 44 |
+
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"
|
| 45 |
>
|
| 46 |
<Download className="w-4 h-4 shrink-0" />
|
| 47 |
+
Download DB
|
| 48 |
</a>
|
| 49 |
</nav>
|
| 50 |
+
|
| 51 |
+
{/* Mobile menu toggle */}
|
| 52 |
+
<button
|
| 53 |
+
type="button"
|
| 54 |
+
aria-label="Toggle navigation menu"
|
| 55 |
+
aria-expanded={menuOpen}
|
| 56 |
+
onClick={() => setMenuOpen((open) => !open)}
|
| 57 |
+
className="sm:hidden inline-flex items-center justify-center p-2 rounded-lg text-slate-700 hover:bg-slate-100 transition-colors shrink-0"
|
| 58 |
+
>
|
| 59 |
+
{menuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
| 60 |
+
</button>
|
| 61 |
</div>
|
| 62 |
+
|
| 63 |
+
{/* Mobile dropdown menu */}
|
| 64 |
+
{menuOpen && (
|
| 65 |
+
<nav className="sm:hidden border-t border-gray-200 bg-white px-4 py-3 flex flex-col gap-1">
|
| 66 |
+
<Link to="/" className={navItemClass(location.pathname === '/')}>
|
| 67 |
+
<Database className="w-4 h-4 shrink-0" />
|
| 68 |
+
Explorer
|
| 69 |
+
</Link>
|
| 70 |
+
<Link to="/docs" className={navItemClass(location.pathname === '/docs')}>
|
| 71 |
+
<BookOpen className="w-4 h-4 shrink-0" />
|
| 72 |
+
Docs
|
| 73 |
+
</Link>
|
| 74 |
+
<a
|
| 75 |
+
href="/api/protein/download/db"
|
| 76 |
+
onClick={() => setMenuOpen(false)}
|
| 77 |
+
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"
|
| 78 |
+
>
|
| 79 |
+
<Download className="w-4 h-4 shrink-0" />
|
| 80 |
+
Download DB
|
| 81 |
+
</a>
|
| 82 |
+
</nav>
|
| 83 |
+
)}
|
| 84 |
</header>
|
| 85 |
);
|
| 86 |
}
|