Files
DorkNet/DorkNet.Server/site/src/components/Header.tsx
T
Alexa bb04782e11 chore(deps): clear Dependabot alerts — React 19, react-router 8, Vite 7
Bumps both frontend apps (site, admin-ui):
- vite 5.4 -> 7.3.6 (pulls esbuild 0.28, patched dev-server CVEs)
- @vitejs/plugin-react 4 -> 5 (babel 7.29.7)
- postcss 8.4 -> 8.5.25
- react-router-dom 6.27 -> react-router 8.3.0 (package renamed
  upstream in v8; imports swapped in src). Fixes the open-redirect
  advisories with no patched 6.x, plus the RSC CSRF advisory
  (GHSA-qwww-vcr4-c8h2) that only 8.3.0 patches.
- react/react-dom 18.3 -> 19.2 (required by react-router 8)

npm audit: 0 vulnerabilities in both apps. tsc + vite build clean.
2026-08-02 00:57:34 +03:00

106 lines
3.3 KiB
TypeScript

import { NavLink, Link, useLocation, useNavigate } from 'react-router';
import { useEffect, useRef, useState } from 'react';
import { BrandMark } from './BrandMark';
const NAV = [
{ to: '/', label: 'Home', exact: true },
{ to: '/feed', label: 'Feed' },
{ to: '/players', label: 'Players' },
{ to: '/rooms', label: 'Rooms' },
{ to: '/join', label: 'Join' },
];
export function Header() {
const [open, setOpen] = useState(false);
const [q, setQ] = useState('');
const nav = useNavigate();
const loc = useLocation();
const inputRef = useRef<HTMLInputElement>(null);
// Close the mobile drawer on route change so tapping a nav item
// doesn't leave a half-open overlay covering the page.
useEffect(() => { setOpen(false); }, [loc.pathname]);
const submitSearch = (e: React.FormEvent) => {
e.preventDefault();
const v = q.trim();
if (v) nav(`/players?q=${encodeURIComponent(v)}`);
};
return (
<header className="sticky top-0 z-40 border-b border-ink-800 bg-ink-950/85 backdrop-blur">
<div className="mx-auto max-w-6xl flex items-center gap-4 px-4 sm:px-6 py-3">
<Link to="/" className="flex items-center gap-2 shrink-0">
<BrandMark className="size-7" />
<span className="font-semibold text-ink-50">DorkNet</span>
</Link>
<nav className="hidden md:flex items-center gap-1 ml-2">
{NAV.map(n => (
<NavLink
key={n.to}
to={n.to}
end={n.exact}
className={({ isActive }) =>
`px-3 py-1.5 rounded-md text-sm transition-colors ${
isActive ? 'bg-ink-800 text-ink-50' : 'text-ink-300 hover:bg-ink-800/60 hover:text-ink-100'
}`
}
>
{n.label}
</NavLink>
))}
</nav>
<form onSubmit={submitSearch} className="ml-auto hidden sm:flex flex-1 max-w-xs">
<input
ref={inputRef}
value={q}
onChange={e => setQ(e.target.value)}
placeholder="Search players…"
className="input"
/>
</form>
<button
type="button"
onClick={() => setOpen(o => !o)}
aria-label="Toggle menu"
className="md:hidden btn-ghost text-sm ml-auto"
>
{open ? 'Close' : 'Menu'}
</button>
</div>
{open && (
<div className="md:hidden border-t border-ink-800 bg-ink-900">
<div className="mx-auto max-w-6xl px-4 sm:px-6 py-3 flex flex-col gap-2">
<form onSubmit={submitSearch} className="flex">
<input
value={q}
onChange={e => setQ(e.target.value)}
placeholder="Search players…"
className="input"
/>
</form>
<nav className="flex flex-col gap-1">
{NAV.map(n => (
<NavLink
key={n.to}
to={n.to}
end={n.exact}
className={({ isActive }) =>
`px-3 py-2 rounded-md text-sm ${isActive ? 'bg-ink-800 text-ink-50' : 'text-ink-200 hover:bg-ink-800/60'}`
}
>
{n.label}
</NavLink>
))}
</nav>
</div>
</div>
)}
</header>
);
}