Some checks failed
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Has been cancelled
Centralize primary, secondary, tertiary, and neutral tokens and apply them across theme variables and UI components. Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { Menu, X } from "lucide-react";
|
|
import { useEffect } from "react";
|
|
import {
|
|
Accordion,
|
|
AccordionContent,
|
|
AccordionItem,
|
|
AccordionTrigger,
|
|
} from "@/components/ui/accordion";
|
|
import { programDays } from "@/content/program";
|
|
import { site } from "@/content/site";
|
|
import { TicketsMarqueeCta } from "@/components/layout/NavTicketsCta";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const navLinks = [
|
|
{ href: "/speakers", label: "Lineup" },
|
|
{ href: "/pitch-competition", label: "Pitch", badge: "Grants" },
|
|
{ href: "/partners", label: "Partners" },
|
|
{ href: "/exhibit", label: "Exhibit" },
|
|
] as const;
|
|
|
|
type TriggerProps = {
|
|
open: boolean;
|
|
onToggle: () => void;
|
|
};
|
|
|
|
export function MobileNavTrigger({ open, onToggle }: TriggerProps) {
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={onToggle}
|
|
className={cn(
|
|
"inline-flex items-center gap-2 rounded-full px-4 py-2.5 text-sm font-semibold transition-colors lg:hidden",
|
|
open
|
|
? "bg-[#141414] text-white"
|
|
: "bg-[#141414] text-white hover:bg-[#1f1f1f]"
|
|
)}
|
|
aria-expanded={open}
|
|
aria-controls="mobile-nav-panel"
|
|
aria-label={open ? "Close menu" : "Open menu"}
|
|
>
|
|
{open ? "Close" : "Menu"}
|
|
{open ? <X className="size-4" strokeWidth={2} /> : <Menu className="size-4" strokeWidth={2} />}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
type DropdownProps = {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
};
|
|
|
|
export function MobileNavDropdown({ open, onClose }: DropdownProps) {
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape") onClose();
|
|
};
|
|
document.body.style.overflow = "hidden";
|
|
window.addEventListener("keydown", onKey);
|
|
return () => {
|
|
document.body.style.overflow = "";
|
|
window.removeEventListener("keydown", onKey);
|
|
};
|
|
}, [open, onClose]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
type="button"
|
|
className="fixed inset-0 top-16 z-40 bg-black/30 lg:hidden"
|
|
aria-label="Close menu"
|
|
onClick={onClose}
|
|
/>
|
|
<nav
|
|
id="mobile-nav-panel"
|
|
className={cn(
|
|
"absolute left-0 right-0 top-[calc(100%+0.5rem)] z-50 overflow-hidden rounded-[1.75rem]",
|
|
"bg-[#141414] text-white shadow-2xl shadow-black/40",
|
|
"animate-in fade-in slide-in-from-top-3 duration-200"
|
|
)}
|
|
aria-label="Mobile navigation"
|
|
>
|
|
<div className="divide-y divide-white/10">
|
|
<Accordion type="single" collapsible className="w-full">
|
|
<AccordionItem value="program" className="border-0">
|
|
<AccordionTrigger
|
|
className={cn(
|
|
"px-5 py-5 text-lg font-medium text-white hover:no-underline",
|
|
"[&[data-state=open]>svg]:rotate-180",
|
|
"[&>svg]:size-5 [&>svg]:text-white/50"
|
|
)}
|
|
>
|
|
Program
|
|
</AccordionTrigger>
|
|
<AccordionContent className="px-5 pb-4 pt-0">
|
|
<ul className="space-y-1 border-t border-white/10 pt-3">
|
|
{programDays.map((day) => (
|
|
<li key={day.id}>
|
|
<Link
|
|
href="/program"
|
|
onClick={onClose}
|
|
className="block rounded-xl px-3 py-3 transition-colors hover:bg-white/5"
|
|
>
|
|
<span className="text-[10px] font-semibold uppercase tracking-wider text-white/45">
|
|
{day.date}
|
|
</span>
|
|
<span className="mt-0.5 block text-sm font-medium text-white/90">
|
|
{day.title}
|
|
</span>
|
|
</Link>
|
|
</li>
|
|
))}
|
|
<li>
|
|
<Link
|
|
href="/program"
|
|
onClick={onClose}
|
|
className="mt-1 block rounded-xl px-3 py-2.5 text-sm font-semibold text-[#b9d8c9] hover:bg-white/5"
|
|
>
|
|
View full program
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
|
|
{navLinks.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.href}
|
|
onClick={onClose}
|
|
className="flex items-center justify-between px-5 py-5 text-lg font-medium transition-colors hover:bg-white/5"
|
|
>
|
|
<span>{link.label}</span>
|
|
{"badge" in link && link.badge && (
|
|
<span className="rounded-md bg-[#37a47a] px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-[#ffffff]">
|
|
{link.badge}
|
|
</span>
|
|
)}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3 border-t border-white/10 p-4">
|
|
<Link
|
|
href={site.links.pitchApplyUrl}
|
|
onClick={onClose}
|
|
className={cn(
|
|
"flex h-12 min-w-0 flex-1 items-center justify-center rounded-full",
|
|
"bg-white px-4 text-sm font-bold text-[#ffffff] transition-transform active:scale-[0.98]"
|
|
)}
|
|
>
|
|
Apply to pitch
|
|
</Link>
|
|
<TicketsMarqueeCta
|
|
onNavigate={onClose}
|
|
className={cn(
|
|
"h-12 min-w-[8.5rem] max-w-[9.5rem] shrink-0",
|
|
"focus-visible:ring-offset-[#141414]"
|
|
)}
|
|
/>
|
|
</div>
|
|
</nav>
|
|
</>
|
|
);
|
|
}
|