view v2 update
This commit is contained in:
149
web/src/components/TopBandBar.tsx
Normal file
149
web/src/components/TopBandBar.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
import { useNavigate, useLocation, matchPath } from "react-router-dom";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { listBands } from "../api/bands";
|
||||
import { getInitials } from "../utils";
|
||||
import { useBandStore } from "../stores/bandStore";
|
||||
|
||||
export function TopBandBar() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { data: bands } = useQuery({ queryKey: ["bands"], queryFn: listBands });
|
||||
const { activeBandId, setActiveBandId } = useBandStore();
|
||||
|
||||
// Sync store from URL when on a band page
|
||||
const urlMatch =
|
||||
matchPath("/bands/:bandId/*", location.pathname) ??
|
||||
matchPath("/bands/:bandId", location.pathname);
|
||||
const urlBandId = urlMatch?.params?.bandId ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
if (urlBandId) setActiveBandId(urlBandId);
|
||||
}, [urlBandId, setActiveBandId]);
|
||||
|
||||
const currentBandId = urlBandId ?? activeBandId;
|
||||
const activeBand = bands?.find((b) => b.id === currentBandId) ?? null;
|
||||
|
||||
// Close on outside click
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handler = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
|
||||
};
|
||||
document.addEventListener("mousedown", handler);
|
||||
return () => document.removeEventListener("mousedown", handler);
|
||||
}, [open]);
|
||||
|
||||
const border = "rgba(255,255,255,0.06)";
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
height: 44,
|
||||
flexShrink: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
padding: "0 20px",
|
||||
borderBottom: `1px solid ${border}`,
|
||||
background: "#10131f",
|
||||
zIndex: 10,
|
||||
}}>
|
||||
{/* Band switcher */}
|
||||
<div ref={ref} style={{ position: "relative" }}>
|
||||
<button
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", gap: 8,
|
||||
padding: "5px 10px",
|
||||
background: open ? "rgba(255,255,255,0.06)" : "transparent",
|
||||
border: `1px solid ${open ? "rgba(255,255,255,0.12)" : "transparent"}`,
|
||||
borderRadius: 8,
|
||||
cursor: "pointer", color: "#e8e9f0",
|
||||
fontFamily: "inherit", fontSize: 13,
|
||||
transition: "background 0.12s, border-color 0.12s",
|
||||
}}
|
||||
onMouseEnter={(e) => { if (!open) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
|
||||
onMouseLeave={(e) => { if (!open) e.currentTarget.style.background = "transparent"; }}
|
||||
>
|
||||
{activeBand ? (
|
||||
<>
|
||||
<div style={{
|
||||
width: 22, height: 22, borderRadius: 6, flexShrink: 0,
|
||||
background: "rgba(139,92,246,0.15)",
|
||||
border: "1px solid rgba(139,92,246,0.3)",
|
||||
display: "flex", alignItems: "center", justifyContent: "center",
|
||||
fontSize: 9, fontWeight: 800, color: "#a78bfa",
|
||||
}}>
|
||||
{getInitials(activeBand.name)}
|
||||
</div>
|
||||
<span style={{ fontWeight: 600, fontSize: 13 }}>{activeBand.name}</span>
|
||||
</>
|
||||
) : (
|
||||
<span style={{ color: "rgba(232,233,240,0.35)", fontSize: 13 }}>Select a band</span>
|
||||
)}
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" style={{ color: "rgba(232,233,240,0.3)", marginLeft: 2 }}>
|
||||
<path d="M3 5l3 3 3-3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div style={{
|
||||
position: "absolute", top: "calc(100% + 6px)", left: 0,
|
||||
minWidth: 220,
|
||||
background: "#1a1e30",
|
||||
border: "1px solid rgba(255,255,255,0.1)",
|
||||
borderRadius: 10, padding: 6, zIndex: 100,
|
||||
boxShadow: "0 8px 32px rgba(0,0,0,0.5)",
|
||||
}}>
|
||||
{bands?.map((band) => (
|
||||
<button
|
||||
key={band.id}
|
||||
onClick={() => {
|
||||
setActiveBandId(band.id);
|
||||
navigate(`/bands/${band.id}`);
|
||||
setOpen(false);
|
||||
}}
|
||||
style={{
|
||||
width: "100%", display: "flex", alignItems: "center", gap: 8,
|
||||
padding: "7px 9px", marginBottom: 1,
|
||||
background: band.id === currentBandId ? "rgba(139,92,246,0.1)" : "transparent",
|
||||
border: "none", borderRadius: 6,
|
||||
cursor: "pointer", color: "#e8e9f0",
|
||||
textAlign: "left", fontFamily: "inherit",
|
||||
transition: "background 0.12s",
|
||||
}}
|
||||
onMouseEnter={(e) => { if (band.id !== currentBandId) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
|
||||
onMouseLeave={(e) => { if (band.id !== currentBandId) e.currentTarget.style.background = "transparent"; }}
|
||||
>
|
||||
<div style={{ width: 22, height: 22, borderRadius: 6, background: "rgba(139,92,246,0.15)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 9, fontWeight: 700, color: "#a78bfa", flexShrink: 0 }}>
|
||||
{getInitials(band.name)}
|
||||
</div>
|
||||
<span style={{ flex: 1, fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
{band.name}
|
||||
</span>
|
||||
{band.id === currentBandId && (
|
||||
<span style={{ fontSize: 10, color: "#a78bfa", flexShrink: 0 }}>✓</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
|
||||
<div style={{ borderTop: "1px solid rgba(255,255,255,0.06)", marginTop: 4, paddingTop: 4 }}>
|
||||
<button
|
||||
onClick={() => { navigate("/"); setOpen(false); }}
|
||||
style={{ width: "100%", display: "flex", alignItems: "center", gap: 8, padding: "7px 9px", background: "transparent", border: "none", borderRadius: 6, cursor: "pointer", color: "rgba(232,233,240,0.35)", fontSize: 12, textAlign: "left", fontFamily: "inherit" }}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.color = "rgba(232,233,240,0.7)")}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.color = "rgba(232,233,240,0.35)")}
|
||||
>
|
||||
<span style={{ fontSize: 14, opacity: 0.6 }}>+</span>
|
||||
New band
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user