Files
SkyMoney/web/src/components/NavBar.tsx
Ricearoni1245 90131d61fc
All checks were successful
Deploy / deploy (push) Successful in 1m29s
Security Tests / security-non-db (push) Successful in 19s
Security Tests / security-db (push) Successful in 24s
fix rebalance feature get route, moved to top level nav
2026-03-11 21:39:11 -05:00

105 lines
4.3 KiB
TypeScript

import { NavLink, useLocation, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import ThemeToggle from "./ThemeToggle";
export default function NavBar({
hideOn = ["/onboarding", "/login", "/register"],
}: {
hideOn?: string[];
}) {
const navigate = useNavigate();
const { pathname } = useLocation();
const [menuOpen, setMenuOpen] = useState(false);
const linkClass = ({ isActive }: { isActive: boolean }) =>
"nav-link " + (isActive ? "nav-link-active" : "");
const mobileLinkClass = ({ isActive }: { isActive: boolean }) =>
"nav-link text-[--color-text] " +
(isActive ? "nav-link-active" : "hover:bg-[--color-ink]/20");
useEffect(() => {
setMenuOpen(false);
}, [pathname]);
if (hideOn.some((p) => pathname.startsWith(p))) return null;
return (
<header
className="topnav sticky top-0 z-40 border-b"
style={{ backdropFilter: "blur(8px)" }}
aria-label="Primary"
>
<div className="container h-14 min-h-14 flex items-center gap-2 flex-nowrap">
{/* Brand */}
<button
onClick={() => navigate("/")}
className="brand row items-center shrink-0 px-2 py-1 rounded-lg hover:bg-[--color-panel] transition-all"
aria-label="Go to dashboard"
>
<span className="font-bold text-xl tracking-wide">SkyMoney</span>
</button>
{/* Links */}
<nav className="ml-2 topnav-links items-center gap-1 flex-1 overflow-x-auto whitespace-nowrap hide-scrollbar">
<NavLink to="/" className={linkClass} end>Dashboard</NavLink>
<NavLink to="/spend" className={linkClass}>Transactions</NavLink>
<NavLink to="/income" className={linkClass}>Income</NavLink>
<NavLink to="/transactions" className={linkClass}>Records</NavLink>
<NavLink to="/rebalance" className={linkClass}>Rebalance</NavLink>
<NavLink to="/settings" className={linkClass}>Settings</NavLink>
</nav>
{/* Actions */}
<div className="ml-auto row gap-2 items-center shrink-0">
<div className="topnav-theme items-center gap-2">
<ThemeToggle size="sm" />
</div>
{/* Mobile menu */}
<div className="topnav-mobile relative">
<button
type="button"
className={
"rounded-xl border border-[--color-border] bg-[--color-panel] px-3 py-2 text-[--color-text] transition " +
(menuOpen ? "shadow-md" : "hover:bg-[--color-ink]/10")
}
aria-expanded={menuOpen}
aria-controls="mobile-menu"
onClick={() => setMenuOpen((open) => !open)}
>
<span className="sr-only">Open menu</span>
<span className="grid gap-1">
<span className="h-0.5 w-5 bg-current transition-all" />
<span className="h-0.5 w-5 bg-current transition-all" />
<span className="h-0.5 w-5 bg-current transition-all" />
</span>
</button>
<div
id="mobile-menu"
className={
"absolute right-0 top-full mt-2 w-64 origin-top-right rounded-2xl border bg-[--color-surface] p-3 shadow-lg transition-all duration-200 ease-out " +
(menuOpen
? "opacity-100 translate-y-0"
: "pointer-events-none opacity-0 -translate-y-2")
}
role="menu"
aria-hidden={!menuOpen}
>
<div className="flex flex-col gap-1">
<NavLink to="/" className={mobileLinkClass} end>Dashboard</NavLink>
<NavLink to="/spend" className={mobileLinkClass}>Transactions</NavLink>
<NavLink to="/income" className={mobileLinkClass}>Income</NavLink>
<NavLink to="/transactions" className={mobileLinkClass}>Records</NavLink>
<NavLink to="/rebalance" className={mobileLinkClass}>Rebalance</NavLink>
<NavLink to="/settings" className={mobileLinkClass}>Settings</NavLink>
</div>
<div className="mt-3 border-t border-[--color-border] pt-3 flex items-center justify-between">
<ThemeToggle size="sm" />
</div>
</div>
</div>
</div>
</div>
</header>
);
}