Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
396 changes: 198 additions & 198 deletions apps/web/src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,198 +1,198 @@
"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, SITE_URL } from "@/site/brand";
import { SOCIAL_LINKS } from "@/site/social";
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, SITE_URL } from "@/site/brand";
import { SOCIAL_LINKS } from "@/site/social";
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" />
48k+
</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>
);
}
Loading