Spaces:
Build error
Build error
| 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> | |
| </> | |
| ); | |
| } | |