Spaces:
Sleeping
Sleeping
"use client"; | |
import * as React from "react"; | |
import { Moon, Sun } from "lucide-react"; | |
import { useTheme } from "next-themes"; | |
import Link from "next/link"; | |
import { Button } from "@/components/ui/button"; | |
import { | |
DropdownMenu, | |
DropdownMenuContent, | |
DropdownMenuItem, | |
DropdownMenuTrigger, | |
} from "@/components/ui/dropdown-menu"; | |
/** | |
* TopNav - A responsive navigation header component with theme switching functionality | |
* Features: | |
* - Colorful brand logo/text | |
* - Navigation links for teachers and students | |
* - Theme switcher (light/dark/system) with animated icons | |
* - Client-side hydration handling | |
*/ | |
export function TopNav() { | |
const { setTheme } = useTheme(); | |
// Track component mounting to prevent hydration mismatch | |
const [mounted, setMounted] = React.useState(false); | |
// Enable theme switching only after client-side hydration | |
React.useEffect(() => { | |
setMounted(true); | |
}, []); | |
// Pre-hydration render - shows a simplified version without theme toggle | |
if (!mounted) { | |
return ( | |
<header className="fixed w-full bg-background-secondary/80 backdrop-blur-sm border-b border-border z-50"> | |
<div className="container mx-auto px-4 py-4 flex items-center"> | |
<Link href="/" className="text-3xl font-bold"> | |
<span className="text-[#FF6B6B]">P</span> | |
<span className="text-[#4ECDC4]">l</span> | |
<span className="text-[#45B7D1]">a</span> | |
<span className="text-[#FDCB6E]">y</span> | |
<span className="text-[#FF6B6B]">G</span> | |
<span className="text-[#4ECDC4]">o</span> | |
<span className="ml-2 text-[#45B7D1]">A</span> | |
<span className="text-[#FDCB6E]">I</span> | |
</Link> | |
<nav className="flex items-center justify-center flex-1 space-x-8"> | |
<Link | |
href="/for-teachers" | |
className="text-text-secondary hover:text-primary" | |
> | |
我是老師 | |
</Link> | |
<Link | |
href="/for-students" | |
className="text-text-secondary hover:text-primary" | |
> | |
我是學生 | |
</Link> | |
</nav> | |
<div className="w-9 h-9" />{" "} | |
{/* Placeholder maintaining layout consistency during hydration */} | |
</div> | |
</header> | |
); | |
} | |
// Post-hydration render - includes full functionality with theme toggle | |
return ( | |
<header className="fixed w-full bg-background-secondary/80 backdrop-blur-sm border-b border-border z-50"> | |
<div className="container mx-auto px-4 py-4 flex items-center"> | |
{/* Brand logo with multi-colored letters */} | |
<Link href="/" className="text-3xl font-bold"> | |
<span className="text-[#FF6B6B]">P</span> | |
<span className="text-[#4ECDC4]">l</span> | |
<span className="text-[#45B7D1]">a</span> | |
<span className="text-[#FDCB6E]">y</span> | |
<span className="text-[#FF6B6B]">G</span> | |
<span className="text-[#4ECDC4]">o</span> | |
<span className="ml-2 text-[#45B7D1]">A</span> | |
<span className="text-[#FDCB6E]">I</span> | |
</Link> | |
{/* Navigation links with language-specific text (Traditional Chinese) */} | |
<nav className="flex items-center justify-center flex-1 space-x-8"> | |
<Link | |
href="/for-teachers" | |
className="text-text-secondary hover:text-primary" | |
> | |
我是老師 | |
</Link> | |
<Link | |
href="/for-students" | |
className="text-text-secondary hover:text-primary" | |
> | |
我是學生 | |
</Link> | |
</nav> | |
{/* Theme switcher dropdown with animated sun/moon icons */} | |
<DropdownMenu> | |
<DropdownMenuTrigger asChild> | |
<Button> | |
{/* Animated sun/moon icons that rotate based on theme */} | |
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> | |
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /> | |
<span className="sr-only">Toggle theme</span> | |
</Button> | |
</DropdownMenuTrigger> | |
{/* Theme selection menu */} | |
<DropdownMenuContent align="end"> | |
<DropdownMenuItem onClick={() => setTheme("light")}> | |
Light | |
</DropdownMenuItem> | |
<DropdownMenuItem onClick={() => setTheme("dark")}> | |
Dark | |
</DropdownMenuItem> | |
<DropdownMenuItem onClick={() => setTheme("system")}> | |
System | |
</DropdownMenuItem> | |
</DropdownMenuContent> | |
</DropdownMenu> | |
</div> | |
</header> | |
); | |
} | |