feat(navbar): add modular navbar components and color variables
This commit is contained in:
parent
6dbc16f23d
commit
6c4ef371c1
7 changed files with 154 additions and 48 deletions
40
apps/web/src/components/navbar/NavAuth.tsx
Normal file
40
apps/web/src/components/navbar/NavAuth.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Link, useNavigate } from "@tanstack/react-router";
|
||||||
|
import { useSession, signOut } from "../../lib/auth-client";
|
||||||
|
|
||||||
|
const NavAuth = () => {
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleSignOut = () => {
|
||||||
|
void signOut()
|
||||||
|
.then(() => void navigate({ to: "/" }))
|
||||||
|
.catch((err) => console.error("Sign out error:", err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ml-auto">
|
||||||
|
{session ? (
|
||||||
|
<button
|
||||||
|
onClick={handleSignOut}
|
||||||
|
className="text-sm text-(--color-text-muted) transition-colors duration-200
|
||||||
|
hover:text-(--color-primary)"
|
||||||
|
>
|
||||||
|
Sign out{" "}
|
||||||
|
<span className="text-(--color-accent)">{session.user.name}</span>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
to="/login"
|
||||||
|
className="text-sm font-medium px-4 py-1.5 rounded-full
|
||||||
|
text-white bg-(--color-primary)
|
||||||
|
hover:bg-(--color-primary-dark)
|
||||||
|
transition-colors duration-200"
|
||||||
|
>
|
||||||
|
Sign in
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavAuth;
|
||||||
18
apps/web/src/components/navbar/NavBar.tsx
Normal file
18
apps/web/src/components/navbar/NavBar.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import NavAuth from "./NavAuth";
|
||||||
|
import NavLinks from "./NavLinks";
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
return (
|
||||||
|
<header className="sticky top-0 z-50 w-full bg-(--color-surface) border-b border-(--color-primary-light)">
|
||||||
|
<div className="max-w-5xl mx-auto px-6 h-14 flex items-center gap-8">
|
||||||
|
<span className="text-sm font-bold tracking-tight text-(--color-primary)">
|
||||||
|
lila
|
||||||
|
</span>
|
||||||
|
<NavLinks />
|
||||||
|
<NavAuth />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
||||||
26
apps/web/src/components/navbar/NavLink.tsx
Normal file
26
apps/web/src/components/navbar/NavLink.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
type NavLinkProps = { to: string; children: React.ReactNode };
|
||||||
|
|
||||||
|
const NavLink = ({ to, children }: NavLinkProps) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={to}
|
||||||
|
className="relative text-sm font-medium text-(--color-text-muted) transition-colors duration-200
|
||||||
|
hover:text-(--color-primary)
|
||||||
|
[&.active]:text-(--color-primary)
|
||||||
|
[&.active]:after:absolute
|
||||||
|
[&.active]:after:-bottom-1
|
||||||
|
[&.active]:after:left-0
|
||||||
|
[&.active]:after:w-full
|
||||||
|
[&.active]:after:h-0.5
|
||||||
|
[&.active]:after:bg-(--color-accent)
|
||||||
|
[&.active]:after:rounded-full
|
||||||
|
[&.active]:after:content-['']"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavLink;
|
||||||
21
apps/web/src/components/navbar/NavLinks.tsx
Normal file
21
apps/web/src/components/navbar/NavLinks.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import NavLink from "./NavLink";
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{ to: "/", label: "Home" },
|
||||||
|
{ to: "/play", label: "Play" },
|
||||||
|
{ to: "/multiplayer", label: "Multiplayer" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const NavLinks = () => {
|
||||||
|
return (
|
||||||
|
<nav className="flex items-center gap-6">
|
||||||
|
{links.map(({ to, label }) => (
|
||||||
|
<NavLink key={to} to={to}>
|
||||||
|
{label}
|
||||||
|
</NavLink>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavLinks;
|
||||||
17
apps/web/src/components/navbar/NavLogin.tsx
Normal file
17
apps/web/src/components/navbar/NavLogin.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
const NavLogin = () => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to="/login"
|
||||||
|
className="text-sm font-medium px-4 py-1.5 rounded-full
|
||||||
|
text-white bg-(--color-primary)
|
||||||
|
hover:bg-(--color-primary-dark)
|
||||||
|
transition-colors duration-200"
|
||||||
|
>
|
||||||
|
Sign in
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavLogin;
|
||||||
26
apps/web/src/components/navbar/NavLogout.tsx
Normal file
26
apps/web/src/components/navbar/NavLogout.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
import { signOut } from "../../lib/auth-client";
|
||||||
|
|
||||||
|
type NavLogoutProps = { name: string };
|
||||||
|
|
||||||
|
const NavLogout = ({ name }: NavLogoutProps) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
void signOut()
|
||||||
|
.then(() => void navigate({ to: "/" }))
|
||||||
|
.catch((err) => console.error("logout error:", err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleLogout}
|
||||||
|
className="text-sm text-(--color-text-muted) transition-colors duration-200
|
||||||
|
hover:text-(--color-primary)"
|
||||||
|
>
|
||||||
|
logout <span className="text-(--color-accent)">{name}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavLogout;
|
||||||
|
|
@ -1,56 +1,14 @@
|
||||||
import {
|
import { createRootRoute, Outlet } from "@tanstack/react-router";
|
||||||
createRootRoute,
|
|
||||||
Link,
|
|
||||||
Outlet,
|
|
||||||
useNavigate,
|
|
||||||
} from "@tanstack/react-router";
|
|
||||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||||
import { useSession, signOut } from "../lib/auth-client";
|
import Navbar from "../components/navbar/NavBar";
|
||||||
|
|
||||||
const RootLayout = () => {
|
const RootLayout = () => {
|
||||||
const { data: session } = useSession();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-2 flex gap-2 items-center">
|
<Navbar />
|
||||||
<Link to="/" className="[&.active]:font-bold">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
Home
|
<Outlet />
|
||||||
</Link>
|
</main>
|
||||||
<Link to="/play" className="[&.active]:font-bold">
|
|
||||||
Play
|
|
||||||
</Link>
|
|
||||||
<Link to="/multiplayer" className="[&.active]:font-bold">
|
|
||||||
Multiplayer
|
|
||||||
</Link>
|
|
||||||
<div className="ml-auto">
|
|
||||||
{session ? (
|
|
||||||
<button
|
|
||||||
className="text-sm text-gray-600 hover:text-gray-900"
|
|
||||||
onClick={() => {
|
|
||||||
void signOut()
|
|
||||||
.then(() => {
|
|
||||||
void navigate({ to: "/" });
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Sign out error:", err);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Sign out ({session.user.name})
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<Link
|
|
||||||
to="/login"
|
|
||||||
className="text-sm text-blue-600 hover:text-blue-800"
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<Outlet />
|
|
||||||
<TanStackRouterDevtools />
|
<TanStackRouterDevtools />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue