feat: 优化header组件语义化

This commit is contained in:
2025-12-15 14:17:02 +08:00
parent 578e7eeb4b
commit 50877448ab

View File

@@ -3,7 +3,7 @@
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import Link from "next/link"; import Link from "next/link";
import { usePathname, useRouter } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import { import {
Drawer, Drawer,
DrawerContent, DrawerContent,
@@ -29,81 +29,100 @@ export default function Header() {
]; ];
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>, path: string) => { const handleClick = (e: React.MouseEvent<HTMLAnchorElement>, path: string) => {
e.preventDefault();
if (path === '/console') { if (path === '/console') {
e.preventDefault();
const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null; const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null;
router.push(token ? '/console' : '/console/login'); router.push(token ? '/console' : '/console/login');
setShowMenu(false);
} else { } else {
router.push(path); setShowMenu(false);
} }
setShowMenu(false);
} }
const menuButtonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (!showMenu && menuButtonRef.current) {
menuButtonRef.current.focus();
}
}, [showMenu]);
return ( return (
<header className="sticky top-0 z-50 backdrop-blur-sm bg-white/40 shadow"> <>
<div className="flex items-center justify-between px-10 md:h-18 md:px-20 h-14 duration-300"> <header className="sticky top-0 z-50 backdrop-blur-sm bg-white/40 shadow" role="banner" aria-label="网站顶部导航栏">
<Link <div className="flex items-center justify-between px-10 md:h-18 md:px-20 h-14 duration-300" aria-label="主菜单">
href="/" <Link
className={cn( href="/"
"cursor-pointer font-medium text-zinc-500 hover:text-zinc-800 border-b-4 border-transparent duration-200", className={cn(
pathname === "/" && "text-zinc-800" "cursor-pointer font-medium text-zinc-500 hover:text-zinc-800 border-b-4 border-transparent duration-200",
)} pathname === "/" && "text-zinc-800"
> )}
{pathname === "/" aria-current={pathname === "/" ? "page" : undefined}
? <div className="text-2xl"> 🍭</div> >
: <div className="md:text-lg">(TONE)</div>} <span className="sr-only">(TONE)</span>
</Link> {pathname === "/"
? <span className="text-2xl" aria-hidden="true" >🍭</span>
: <span className="md:text-lg" aria-hidden="true">(TONE)</span>}
</Link>
<nav className={cn(
"items-center gap-12 hidden sm:flex",
)}>
{menuItems.slice(1).map((item) => (
<Link
key={item.name}
href={item.path}
className={cn(
"cursor-pointer md:text-lg font-medium text-zinc-500 hover:text-zinc-800 border-b-4 border-transparent duration-200",
pathname.startsWith(item.path) && "text-zinc-800 border-b-pink-500"
)}
onClick={e => handleClick(e, item.path)}
aria-current={pathname === item.path ? "page" : undefined}
>
{item.name}
</Link>
))}
</nav>
<Drawer direction="right" open={showMenu} onOpenChange={(state) => !state && setShowMenu(false)}> <button
<DrawerTrigger> ref={menuButtonRef}
<div className="sm:hidden cursor-pointer text-zinc-600" onClick={() => setShowMenu(true)}> className="sm:hidden text-zinc-600"
onClick={() => setShowMenu(true)}
</div> aria-label="打开主菜单"
</DrawerTrigger> ></button>
<DrawerContent>
<DrawerHeader>
<DrawerTitle className="flex justify-between">
<span></span>
<X className="cursor-pointer" onClick={() => setShowMenu(false)} />
</DrawerTitle>
<DrawerDescription></DrawerDescription>
</DrawerHeader>
<div className="w-full flex flex-col px-4 gap-2">
{menuItems.slice(1).map((item) => (
<Link
key={item.name}
href={item.path}
onClick={e => handleClick(e, item.path)}
>
<Button className="w-full" size='lg'
variant={pathname.startsWith(item.path) ? 'default' : 'outline'}
>{item.name}</Button>
</Link>
))}
</div>
</DrawerContent>
</Drawer>
<div className={cn(
"sm:flex items-center gap-12 hidden",
)}>
{menuItems.slice(1).map((item) => (
<Link
key={item.name}
href={item.path}
className={cn(
"cursor-pointer md:text-lg font-medium text-zinc-500 hover:text-zinc-800 border-b-4 border-transparent duration-200",
pathname.startsWith(item.path) && "text-zinc-800 border-b-pink-500"
)}
onClick={e => handleClick(e, item.path)}
>
{item.name}
</Link>
))}
</div> </div>
</div> </header >
</header >
<Drawer direction="right" open={showMenu} onOpenChange={setShowMenu}>
<DrawerContent>
<DrawerHeader>
<DrawerTitle className="flex justify-between">
<span></span>
<button
onClick={() => setShowMenu(false)}
aria-label="关闭菜单"
>
<X className="size-5" aria-hidden="true" />
</button>
</DrawerTitle>
<DrawerDescription></DrawerDescription>
</DrawerHeader>
<nav className="w-full flex flex-col px-4 gap-2" aria-label="移动设备主菜单">
{menuItems.slice(1).map((item) => (
<Link
key={item.name}
href={item.path}
onClick={e => handleClick(e, item.path)}
aria-current={pathname === item.path ? "page" : undefined}
>
<Button className="w-full" size='lg'
variant={pathname.startsWith(item.path) ? 'default' : 'outline'}
>{item.name}</Button>
</Link>
))}
</nav>
</DrawerContent>
</Drawer>
</>
) )
} }