aniqove / frontend /src /components /Sidebar.jsx
mwask's picture
Upload 59 files
d571830 verified
Raw
History Blame Contribute Delete
3.57 kB
import { useEffect, useState } from "react";
import { Link, NavLink, useNavigate } from "react-router-dom";
const ICONS = {
home: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 10.5 12 3l9 7.5" />
<path d="M5 9.5V21h14V9.5" />
</svg>
),
search: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<circle cx="11" cy="11" r="7" />
<path d="m20 20-3.5-3.5" />
</svg>
),
book: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
</svg>
),
};
function NavItem({ to, end, label, icon, onClick }) {
return (
<NavLink
to={to}
end={end}
className={({ isActive }) => `nav-item${isActive ? " active" : ""}`}
onClick={onClick}
>
<span className="nav-icon">{ICONS[icon]}</span>
{label}
</NavLink>
);
}
export default function Sidebar() {
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const [q, setQ] = useState("");
useEffect(() => {
const onKey = (e) => {
if (e.key === "Escape") setOpen(false);
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
function submit(e) {
e.preventDefault();
if (!q.trim()) return;
close();
navigate(`/search?q=${encodeURIComponent(q.trim())}`);
}
return (
<>
<header className="topbar">
<span className="mark"></span>
<span className="topbar-title">anicove</span>
<button
className={`menu-btn${open ? " active" : ""}`}
onClick={() => setOpen((v) => !v)}
aria-label="Toggle menu"
>
<span />
<span />
<span />
</button>
</header>
<div className={`overlay${open ? " open" : ""}`} onClick={close} />
<aside className={`sidebar${open ? " open" : ""}`}>
<div className="logo">
<Link to="/home" onClick={close} style={{ display: "flex", alignItems: "center", gap: 10 }}>
<span className="mark"></span>
<span className="logo-text">anicove</span>
</Link>
<span className="logo-version">v0.1</span>
</div>
<form className="sidebar-search" onSubmit={submit}>
{ICONS.search}
<input
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="Search anime…"
aria-label="Search anime"
/>
</form>
<nav className="nav">
<div className="nav-section">
<div className="nav-label">Browse</div>
<NavItem to="/home" label="Home" icon="home" onClick={close} />
<NavItem to="/search" label="Search" icon="search" onClick={close} />
</div>
<div className="nav-section">
<div className="nav-label">Library</div>
<NavItem to="/manga" label="Manga" icon="book" onClick={close} />
</div>
</nav>
<div className="sidebar-foot">
Streaming via <b>Anicove</b> · 13 providers
<br />
Fallback: <b>Aniraku</b>
</div>
</aside>
</>
);
}