WIP: Mobile optimizations - responsive layout with bottom nav

This commit is contained in:
Mistral Vibe
2026-04-07 12:27:32 +00:00
parent fdf9f52f6f
commit 21c1673fcc
11 changed files with 1088 additions and 633 deletions

View File

@@ -0,0 +1,130 @@
import { useNavigate, useLocation, matchPath } from "react-router-dom";
// ── Icons (inline SVG) ──────────────────────────────────────────────────────
function IconLibrary() {
return (
<svg width="20" height="20" viewBox="0 0 14 14" fill="currentColor">
<path d="M2 3.5h10v1.5H2zm0 3h10v1.5H2zm0 3h7v1.5H2z" />
</svg>
);
}
function IconPlay() {
return (
<svg width="20" height="20" viewBox="0 0 14 14" fill="currentColor">
<path d="M3 2l9 5-9 5V2z" />
</svg>
);
}
function IconSettings() {
return (
<svg width="20" height="20" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.3">
<circle cx="7" cy="7" r="2" />
<path d="M7 1v1.5M7 11.5V13M1 7h1.5M11.5 7H13" />
</svg>
);
}
function IconMembers() {
return (
<svg width="20" height="20" viewBox="0 0 14 14" fill="currentColor">
<circle cx="5" cy="4.5" r="2" />
<path d="M1 12c0-2.2 1.8-3.5 4-3.5s4 1.3 4 3.5H1z" />
<circle cx="10.5" cy="4.5" r="1.5" opacity=".6" />
<path d="M10.5 8.5c1.4 0 2.5 1 2.5 2.5H9.5" opacity=".6" />
</svg>
);
}
// ── NavItem ─────────────────────────────────────────────────────────────────
interface NavItemProps {
icon: React.ReactNode;
label: string;
active: boolean;
onClick: () => void;
disabled?: boolean;
}
function NavItem({ icon, label, active, onClick, disabled }: NavItemProps) {
const color = active ? "#e8a22a" : "rgba(255,255,255,0.5)";
return (
<button
onClick={onClick}
disabled={disabled}
style={{
flex: 1,
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 4,
padding: "8px 4px",
background: "transparent",
border: "none",
cursor: disabled ? "default" : "pointer",
color,
fontSize: 10,
transition: "color 0.12s",
fontFamily: "inherit",
}}
>
{icon}
<span style={{ fontSize: 10 }}>{label}</span>
</button>
);
}
// ── BottomNavBar ────────────────────────────────────────────────────────────
export function BottomNavBar() {
const navigate = useNavigate();
const location = useLocation();
// Derive active states
const isLibrary = !!matchPath("/bands/:bandId", location.pathname);
const isPlayer = !!matchPath("/bands/:bandId/songs/:songId", location.pathname);
const isSettings = location.pathname.startsWith("/settings");
return (
<nav
style={{
position: "fixed",
bottom: 0,
left: 0,
right: 0,
display: "flex",
background: "#0b0b0e",
borderTop: "1px solid rgba(255,255,255,0.06)",
zIndex: 1000,
padding: "8px 16px",
}}
>
<NavItem
icon={<IconLibrary />}
label="Library"
active={isLibrary}
onClick={() => navigate("/bands")}
/>
<NavItem
icon={<IconPlay />}
label="Player"
active={isPlayer}
onClick={() => {}}
disabled={!isPlayer}
/>
<NavItem
icon={<IconMembers />}
label="Members"
active={false}
onClick={() => navigate("/settings")}
/>
<NavItem
icon={<IconSettings />}
label="Settings"
active={isSettings}
onClick={() => navigate("/settings")}
/>
</nav>
);
}