feat(shell): add shared app shell primitives
Add AppShell, AppNav components and refactor ThemeChanger for header use Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
c8cf9b1573
commit
e8d557b0d2
3 changed files with 93 additions and 11 deletions
44
src/components/core/AppNav.tsx
Normal file
44
src/components/core/AppNav.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { routes } from "@/lib/routes";
|
||||
import { ThemeChanger } from "./ThemeChanger";
|
||||
import { AuthNavActions } from "./AuthNavActions";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AppNavProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function AppNav({ className }: AppNavProps) {
|
||||
const t = useTranslations("Navigation");
|
||||
|
||||
return (
|
||||
<header
|
||||
data-testid="app-nav"
|
||||
className={cn(
|
||||
"border-b bg-background sticky top-0 z-50",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex h-16 items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<Link
|
||||
href={routes.public.home}
|
||||
className="text-xl font-bold text-foreground hover:text-foreground/80 transition-colors"
|
||||
>
|
||||
{t("Home")}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<AuthNavActions />
|
||||
<ThemeChanger />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
14
src/components/core/AppShell.tsx
Normal file
14
src/components/core/AppShell.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AppShellProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function AppShell({ children, className }: AppShellProps) {
|
||||
return (
|
||||
<div data-testid="app-shell" className={cn("min-h-screen flex flex-col", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,17 @@
|
|||
"use client";
|
||||
|
||||
import { useTheme } from "@wrksz/themes/client";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { Sun01Icon, Moon02Icon } from "@hugeicons/core-free-icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const ThemeChanger = () => {
|
||||
interface ThemeChangerProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ThemeChanger({ className }: ThemeChangerProps) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const mounted = useSyncExternalStore(
|
||||
() => () => undefined,
|
||||
|
|
@ -11,18 +20,33 @@ export const ThemeChanger = () => {
|
|||
);
|
||||
|
||||
if (!mounted) {
|
||||
return <p>Loading theme...</p>;
|
||||
return (
|
||||
<div data-testid="theme-switcher" className={cn("flex items-center gap-1", className)}>
|
||||
<Button variant="ghost" size="icon" disabled>
|
||||
<HugeiconsIcon icon={Sun01Icon} className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>The current theme is: {theme}</p>
|
||||
<button type="button" onClick={() => setTheme("light")}>
|
||||
Light Mode
|
||||
</button>
|
||||
<button type="button" onClick={() => setTheme("dark")}>
|
||||
Dark Mode
|
||||
</button>
|
||||
<div data-testid="theme-switcher" className={cn("flex items-center gap-1", className)}>
|
||||
<Button
|
||||
variant={theme === "light" ? "default" : "ghost"}
|
||||
size="icon"
|
||||
onClick={() => setTheme("light")}
|
||||
aria-label="Light mode"
|
||||
>
|
||||
<HugeiconsIcon icon={Sun01Icon} className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "dark" ? "default" : "ghost"}
|
||||
size="icon"
|
||||
onClick={() => setTheme("dark")}
|
||||
aria-label="Dark mode"
|
||||
>
|
||||
<HugeiconsIcon icon={Moon02Icon} className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue