Template
47 lines
1.8 KiB
TypeScript
47 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import Link from 'next/link';
|
|
import { Menu, X } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
export type NavigationLink = { label: string; url: string };
|
|
|
|
export type HeaderProps = {
|
|
siteName?: string;
|
|
links?: NavigationLink[];
|
|
};
|
|
|
|
export default function Header({ siteName = 'Logo', links = [] }: HeaderProps) {
|
|
const [open, setOpen] = useState(false);
|
|
|
|
return (
|
|
<header className="relative z-50 h-16 bg-background">
|
|
<div className="page-shell flex h-full items-center justify-between">
|
|
<Link href="/" className="text-sm font-semibold tracking-tight">{siteName}</Link>
|
|
<nav aria-label="Primary navigation" className="hidden items-center gap-7 md:flex">
|
|
{links.map((link) => (
|
|
<Link key={`${link.label}-${link.url}`} href={link.url} className="text-sm text-muted-foreground transition-colors hover:text-foreground">
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
<Button type="button" variant="ghost" size="icon" className="md:hidden" aria-label={open ? 'Close navigation' : 'Open navigation'} aria-expanded={open} onClick={() => setOpen((value) => !value)}>
|
|
{open ? <X aria-hidden="true" /> : <Menu aria-hidden="true" />}
|
|
</Button>
|
|
</div>
|
|
{open && (
|
|
<nav aria-label="Mobile navigation" className="absolute inset-x-0 top-16 bg-background px-4 py-4 md:hidden">
|
|
<div className="page-shell flex flex-col gap-1">
|
|
{links.map((link) => (
|
|
<Link key={`${link.label}-${link.url}`} href={link.url} onClick={() => setOpen(false)} className="rounded-md px-3 py-2.5 text-sm hover:bg-muted">
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</nav>
|
|
)}
|
|
</header>
|
|
);
|
|
}
|