feat: Created AdminDropdown component with click-outside/Escape close,…
- "frontend/src/components/AdminDropdown.tsx" - "frontend/src/App.tsx" - "frontend/src/App.css" GSD-Task: S01/T01
This commit is contained in:
parent
93a643f1b8
commit
cd9dd6d8f9
3 changed files with 122 additions and 5 deletions
|
|
@ -776,6 +776,53 @@ body {
|
|||
color: var(--color-text-on-header-hover);
|
||||
}
|
||||
|
||||
/* ── Admin dropdown ───────────────────────────────────────────────────────── */
|
||||
|
||||
.admin-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.admin-dropdown__trigger {
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-on-header);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.admin-dropdown__trigger:hover {
|
||||
color: var(--color-text-on-header-hover);
|
||||
}
|
||||
|
||||
.admin-dropdown__menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.5rem);
|
||||
right: 0;
|
||||
min-width: 10rem;
|
||||
background: var(--color-bg-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 16px var(--color-shadow-heavy);
|
||||
z-index: 100;
|
||||
padding: 0.375rem 0;
|
||||
}
|
||||
|
||||
.admin-dropdown__item {
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
color: var(--color-text-primary);
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
transition: background 0.12s;
|
||||
}
|
||||
|
||||
.admin-dropdown__item:hover {
|
||||
background: var(--color-bg-surface-hover);
|
||||
}
|
||||
|
||||
/* ── Home / Hero ──────────────────────────────────────────────────────────── */
|
||||
|
||||
.home-hero {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import ReviewQueue from "./pages/ReviewQueue";
|
|||
import MomentDetail from "./pages/MomentDetail";
|
||||
import AdminReports from "./pages/AdminReports";
|
||||
import AdminPipeline from "./pages/AdminPipeline";
|
||||
import ModeToggle from "./components/ModeToggle";
|
||||
import AdminDropdown from "./components/AdminDropdown";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
|
@ -23,11 +23,8 @@ export default function App() {
|
|||
<Link to="/">Home</Link>
|
||||
<Link to="/topics">Topics</Link>
|
||||
<Link to="/creators">Creators</Link>
|
||||
<Link to="/admin/review">Review</Link>
|
||||
<Link to="/admin/reports">Reports</Link>
|
||||
<Link to="/admin/pipeline">Pipeline</Link>
|
||||
<AdminDropdown />
|
||||
</nav>
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
|
|||
73
frontend/src/components/AdminDropdown.tsx
Normal file
73
frontend/src/components/AdminDropdown.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function AdminDropdown() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Close on outside click
|
||||
useEffect(() => {
|
||||
function handler(e: MouseEvent) {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(e.target as Node)
|
||||
) {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handler);
|
||||
return () => document.removeEventListener("mousedown", handler);
|
||||
}, []);
|
||||
|
||||
// Close on Escape
|
||||
useEffect(() => {
|
||||
function handler(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") setOpen(false);
|
||||
}
|
||||
if (open) {
|
||||
document.addEventListener("keydown", handler);
|
||||
return () => document.removeEventListener("keydown", handler);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<div className="admin-dropdown" ref={dropdownRef}>
|
||||
<button
|
||||
className="admin-dropdown__trigger"
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
aria-expanded={open}
|
||||
aria-haspopup="true"
|
||||
>
|
||||
Admin ▾
|
||||
</button>
|
||||
{open && (
|
||||
<div className="admin-dropdown__menu" role="menu">
|
||||
<Link
|
||||
to="/admin/review"
|
||||
className="admin-dropdown__item"
|
||||
role="menuitem"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
Review
|
||||
</Link>
|
||||
<Link
|
||||
to="/admin/reports"
|
||||
className="admin-dropdown__item"
|
||||
role="menuitem"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
Reports
|
||||
</Link>
|
||||
<Link
|
||||
to="/admin/pipeline"
|
||||
className="admin-dropdown__item"
|
||||
role="menuitem"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
Pipeline
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue