返回 CodeWhale
nav-links.tsx
根目录 / web / components / nav-links.tsx
1 "use client";
2
3 import Link from "next/link";
4 import { usePathname } from "next/navigation";
5 import { currentNavHref, type ChromeLink } from "@/lib/i18n/links";
6
7 /**
8 * Desktop primary navigation. Labels come from the locale's chrome
9 * dictionary — no locale branch here, and no Han characters leaking into
10 * locales that never asked for them.
11 *
12 * Companion labels stay on the compact sheet (mobile-menu.tsx). On the
13 * 76rem desktop strip they do not fit: at 2xl, de/pt-BR/id secondaries
14 * collapsed the home wordmark to a zero-width hit target (#5290).
15 *
16 * Wrapping is the strip's escape valve — the container is capped at 76rem,
17 * so the six longest translated labels (de on a docs route, which also
18 * carries the theme control) can still exceed it. The row gap is tight so a
19 * wrapped second row does not double the height of the sticky header.
20 */
21 export function NavLinks({
22 links,
23 primaryAria,
24 }: {
25 links: ChromeLink[];
26 primaryAria: string;
27 }) {
28 const pathname = usePathname();
29 // One link is the page; ancestors are not. See currentNavHref.
30 const current = currentNavHref(links, pathname);
31
32 return (
33 <nav className="hidden xl:flex min-w-0 shrink items-center gap-x-5 gap-y-1 flex-wrap" aria-label={primaryAria}>
34 {links.map((l) => {
35 const isActive = l.href === current;
36 return (
37 <Link key={l.href} href={l.href} className="nav-link group inline-flex items-baseline" aria-current={isActive ? "page" : undefined}>
38 <span className="leading-none">{l.label}</span>
39 </Link>
40 );
41 })}
42 </nav>
43 );
44 }
45
45 lines Plain Text