diff options
Diffstat (limited to 'apps/web/src/components/header.tsx')
| -rw-r--r-- | apps/web/src/components/header.tsx | 394 |
1 files changed, 197 insertions, 197 deletions
diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx index e24486b4..5986308e 100644 --- a/apps/web/src/components/header.tsx +++ b/apps/web/src/components/header.tsx @@ -1,197 +1,197 @@ -"use client"; - -import { useState } from "react"; -import Link from "next/link"; -import { motion } from "motion/react"; -import { Button } from "./ui/button"; -import { ArrowRight } from "lucide-react"; -import Image from "next/image"; -import { ThemeToggle } from "./theme-toggle"; -import { - Copy01Icon, - Download01Icon, - GithubIcon, - LinkSquare02Icon, - Menu02Icon, -} from "@hugeicons/core-free-icons"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { cn } from "@/utils/ui"; -import { DEFAULT_LOGO_URL, SOCIAL_LINKS } from "@/constants/site-constants"; -import { - ContextMenu, - ContextMenuContent, - ContextMenuItem, - ContextMenuTrigger, -} from "./ui/context-menu"; - -export function Header() { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const closeMenu = () => setIsMenuOpen(false); - - const links = [ - { - label: "Roadmap", - href: "/roadmap", - }, - { - label: "Contributors", - href: "/contributors", - }, - { - label: "Sponsors", - href: "/sponsors", - }, - { - label: "Blog", - href: "/blog", - }, - ]; - - return ( - <header className="bg-background shadow-background/85 sticky top-0 z-10 shadow-[0_30px_35px_15px_rgba(0,0,0,1)]"> - <div className="relative flex w-full items-center justify-between px-6 pt-4"> - <div className="relative z-10 flex items-center gap-6"> - <ContextMenu> - <ContextMenuTrigger asChild> - <Link href="/" className="flex items-center gap-3"> - <Image - src={DEFAULT_LOGO_URL} - alt="OpenCut Logo" - className="invert dark:invert-0" - width={32} - height={32} - /> - </Link> - </ContextMenuTrigger> - <ContextMenuContent> - <ContextMenuItem - onClick={async () => { - const res = await fetch(DEFAULT_LOGO_URL); - const svg = await res.text(); - await navigator.clipboard.writeText(svg); - }} - > - <HugeiconsIcon icon={Copy01Icon} /> - Copy SVG - </ContextMenuItem> - <ContextMenuItem - onClick={() => { - const a = document.createElement("a"); - a.href = DEFAULT_LOGO_URL; - a.download = "opencut-logo.svg"; - a.click(); - }} - > - <HugeiconsIcon icon={Download01Icon} /> - Download SVG - </ContextMenuItem> - <Link href="/brand"> - <ContextMenuItem> - <HugeiconsIcon icon={LinkSquare02Icon} /> - Brand assets - </ContextMenuItem> - </Link> - </ContextMenuContent> - </ContextMenu> - - <nav className="hidden items-center gap-4 md:flex"> - {links.map((link) => ( - <Link key={link.href} href={link.href}> - <Button variant="text" className="p-0 text-sm"> - {link.label} - </Button> - </Link> - ))} - </nav> - </div> - - <div className="relative z-10"> - <div className="flex items-center gap-3 md:hidden"> - <Button - variant="text" - size="icon" - className="flex items-center justify-center p-0" - onClick={() => setIsMenuOpen(!isMenuOpen)} - > - <HugeiconsIcon icon={Menu02Icon} size={30} /> - </Button> - </div> - <div className="hidden items-center gap-3 md:flex"> - <Link href={SOCIAL_LINKS.github}> - <Button className="bg-background text-sm" variant="outline"> - <HugeiconsIcon icon={GithubIcon} className="size-4" /> - 40k+ - </Button> - </Link> - <Link href="/projects"> - <Button className="text-sm"> - Projects - <ArrowRight className="size-4" /> - </Button> - </Link> - <ThemeToggle /> - </div> - </div> - <div - className={cn( - "bg-background/20 pointer-events-none fixed inset-0 opacity-0 backdrop-blur-3xl", - "transition-opacity duration-150", - isMenuOpen && "pointer-events-auto opacity-100", - )} - > - <div className="relative h-full"> - <button - type="button" - aria-label="Close menu" - className="absolute inset-0" - onClick={closeMenu} - onKeyDown={(event) => { - if ( - event.key === "Enter" || - event.key === " " || - event.key === "Escape" - ) { - event.preventDefault(); - closeMenu(); - } - }} - /> - <nav className="flex flex-col gap-3 px-6 pt-[5rem]"> - {links.map((link, index) => ( - <motion.div - key={link.href} - initial={{ scale: 0.98, opacity: 0 }} - animate={{ - scale: isMenuOpen ? 1 : 0.98, - opacity: isMenuOpen ? 1 : 0, - }} - transition={{ - duration: 0.4, - delay: isMenuOpen ? index * 0.1 : 0, - ease: [0.25, 0.46, 0.45, 0.94], - }} - > - <Link - href={link.href} - className="text-2xl font-semibold" - onClick={() => setIsMenuOpen(false)} - > - {link.label} - </Link> - </motion.div> - ))} - </nav> - <ThemeToggle - className="absolute right-8 bottom-8 size-10" - iconClassName="!size-[1.2rem]" - onToggle={(e) => { - e.preventDefault(); - e.stopPropagation(); - }} - /> - </div> - </div> - </div> - </header> - ); -} +"use client";
+
+import { useState } from "react";
+import Link from "next/link";
+import { motion } from "motion/react";
+import { Button } from "./ui/button";
+import { ArrowRight } from "lucide-react";
+import Image from "next/image";
+import { ThemeToggle } from "./theme-toggle";
+import {
+ Copy01Icon,
+ Download01Icon,
+ GithubIcon,
+ LinkSquare02Icon,
+ Menu02Icon,
+} from "@hugeicons/core-free-icons";
+import { HugeiconsIcon } from "@hugeicons/react";
+import { cn } from "@/utils/ui";
+import { DEFAULT_LOGO_URL, SOCIAL_LINKS } from "@/constants/site-constants";
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuTrigger,
+} from "./ui/context-menu";
+
+export function Header() {
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const closeMenu = () => setIsMenuOpen(false);
+
+ const links = [
+ {
+ label: "Roadmap",
+ href: "/roadmap",
+ },
+ {
+ label: "Contributors",
+ href: "/contributors",
+ },
+ {
+ label: "Sponsors",
+ href: "/sponsors",
+ },
+ {
+ label: "Blog",
+ href: "/blog",
+ },
+ ];
+
+ return (
+ <header className="bg-background shadow-background/85 sticky top-0 z-10 shadow-[0_30px_35px_15px_rgba(0,0,0,1)]">
+ <div className="relative flex w-full items-center justify-between px-6 pt-4">
+ <div className="relative z-10 flex items-center gap-6">
+ <ContextMenu>
+ <ContextMenuTrigger asChild>
+ <Link href="/" className="flex items-center gap-3">
+ <Image
+ src={DEFAULT_LOGO_URL}
+ alt="OpenCut Logo"
+ className="invert dark:invert-0"
+ width={32}
+ height={32}
+ />
+ </Link>
+ </ContextMenuTrigger>
+ <ContextMenuContent>
+ <ContextMenuItem
+ onClick={async () => {
+ const res = await fetch(DEFAULT_LOGO_URL);
+ const svg = await res.text();
+ await navigator.clipboard.writeText(svg);
+ }}
+ >
+ <HugeiconsIcon icon={Copy01Icon} />
+ Copy SVG
+ </ContextMenuItem>
+ <ContextMenuItem
+ onClick={() => {
+ const a = document.createElement("a");
+ a.href = DEFAULT_LOGO_URL;
+ a.download = "opencut-logo.svg";
+ a.click();
+ }}
+ >
+ <HugeiconsIcon icon={Download01Icon} />
+ Download SVG
+ </ContextMenuItem>
+ <Link href="/brand">
+ <ContextMenuItem>
+ <HugeiconsIcon icon={LinkSquare02Icon} />
+ Brand assets
+ </ContextMenuItem>
+ </Link>
+ </ContextMenuContent>
+ </ContextMenu>
+
+ <nav className="hidden items-center gap-4 md:flex">
+ {links.map((link) => (
+ <Link key={link.href} href={link.href}>
+ <Button variant="text" className="p-0 text-sm">
+ {link.label}
+ </Button>
+ </Link>
+ ))}
+ </nav>
+ </div>
+
+ <div className="relative z-10">
+ <div className="flex items-center gap-3 md:hidden">
+ <Button
+ variant="text"
+ size="icon"
+ className="flex items-center justify-center p-0"
+ onClick={() => setIsMenuOpen(!isMenuOpen)}
+ >
+ <HugeiconsIcon icon={Menu02Icon} size={30} />
+ </Button>
+ </div>
+ <div className="hidden items-center gap-3 md:flex">
+ <Link href={SOCIAL_LINKS.github}>
+ <Button className="bg-background text-sm" variant="outline">
+ <HugeiconsIcon icon={GithubIcon} className="size-4" />
+ 40k+
+ </Button>
+ </Link>
+ <Link href="/projects">
+ <Button className="text-sm">
+ Projects
+ <ArrowRight className="size-4" />
+ </Button>
+ </Link>
+ <ThemeToggle />
+ </div>
+ </div>
+ <div
+ className={cn(
+ "bg-background/20 pointer-events-none fixed inset-0 opacity-0 backdrop-blur-3xl",
+ "transition-opacity duration-150",
+ isMenuOpen && "pointer-events-auto opacity-100",
+ )}
+ >
+ <div className="relative h-full">
+ <button
+ type="button"
+ aria-label="Close menu"
+ className="absolute inset-0"
+ onClick={closeMenu}
+ onKeyDown={(event) => {
+ if (
+ event.key === "Enter" ||
+ event.key === " " ||
+ event.key === "Escape"
+ ) {
+ event.preventDefault();
+ closeMenu();
+ }
+ }}
+ />
+ <nav className="flex flex-col gap-3 px-6 pt-[5rem]">
+ {links.map((link, index) => (
+ <motion.div
+ key={link.href}
+ initial={{ scale: 0.98, opacity: 0 }}
+ animate={{
+ scale: isMenuOpen ? 1 : 0.98,
+ opacity: isMenuOpen ? 1 : 0,
+ }}
+ transition={{
+ duration: 0.4,
+ delay: isMenuOpen ? index * 0.1 : 0,
+ ease: [0.25, 0.46, 0.45, 0.94],
+ }}
+ >
+ <Link
+ href={link.href}
+ className="text-2xl font-semibold"
+ onClick={() => setIsMenuOpen(false)}
+ >
+ {link.label}
+ </Link>
+ </motion.div>
+ ))}
+ </nav>
+ <ThemeToggle
+ className="absolute right-8 bottom-8 size-10"
+ iconClassName="!size-[1.2rem]"
+ onToggle={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ }}
+ />
+ </div>
+ </div>
+ </div>
+ </header>
+ );
+}
|
