"use client"; import { useCallback, useEffect, useRef, useState, type CSSProperties, type ReactNode, } from "react"; import * as Dialog from "@radix-ui/react-dialog"; import { useGSAP } from "@gsap/react"; import gsap from "gsap"; import { cn } from "@/lib/utils"; import { AsciiGlitchText, type AsciiGlitchTextHandle, } from "./ascii-hover-button"; gsap.registerPlugin(useGSAP); export interface CurtainNavbarItem { label: string; href: string; category?: string; detail?: string; description?: string; } export interface CurtainNavbarLink { label: string; href: string; } export interface CurtainNavbarProps { brand?: string; brandHref?: string; logo?: ReactNode; items?: readonly CurtainNavbarItem[]; quickLinks?: readonly CurtainNavbarLink[]; footerLinks?: readonly CurtainNavbarLink[]; leftNote?: string; rightNote?: string; footerText?: string; background?: string; foreground?: string; headerBackground?: string; headerForeground?: string; duration?: number; stagger?: number; hoverDuration?: number; closeSpeed?: number; glitchDuration?: number; hoverScroll?: boolean; scrollDuration?: number; contained?: boolean; open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean) => void; className?: string; } export const CURTAIN_NAVBAR_ITEMS: readonly CurtainNavbarItem[] = [ { label: "Build", href: "/components", category: "Components", detail: "UI", description: "Find your next component.", }, { label: "Learn", href: "/docs/introduction", category: "Documentation", detail: "01", description: "Make motion part of your craft.", }, { label: "Club", href: "https://github.com/devclub-nstru/component_library", category: "Community", detail: "Open", description: "Built together. Shared with everyone.", }, { label: "Hello", href: "https://github.com/devclub-nstru/component_library/issues", category: "Connect", detail: "↗", description: "Bring an idea. Start a conversation.", }, { label: "Source", href: "https://github.com/devclub-nstru/component_library", category: "Open source", detail: "MIT", description: "Explore the code behind the motion.", }, ]; const DEFAULT_QUICK_LINKS: readonly CurtainNavbarLink[] = [ { label: "Build", href: "/components" }, { label: "Learn", href: "/docs/introduction" }, { label: "Hello", href: "https://github.com/devclub-nstru/component_library/issues", }, ]; const DEFAULT_FOOTER_LINKS: readonly CurtainNavbarLink[] = [ { label: "GitHub", href: "https://github.com/devclub-nstru/component_library", }, { label: "Installation", href: "/docs/installation" }, { label: "Dev Club", href: "https://devclub.co" }, ]; const clamp = (value: number, min: number, max: number, fallback: number) => Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback; function MenuMark({ open }: { open: boolean }) { const rootRef = useRef(null); const animateRef = useRef<((next: boolean) => void) | null>(null); const openRef = useRef(open); useEffect(() => { openRef.current = open; }, [open]); useGSAP( () => { const media = gsap.matchMedia(); media.add( { reduced: "(prefers-reduced-motion: reduce)", all: "all" }, (context) => { const animate = context.add("animate", (next: boolean) => gsap.to(rootRef.current, { rotation: next ? 135 : 0, duration: context.conditions?.reduced ? 0 : 0.45, ease: "power3.inOut", overwrite: "auto", }), ); animateRef.current = (next) => animate(next); animate(openRef.current); return () => { animateRef.current = null; }; }, ); return () => media.revert(); }, { scope: rootRef }, ); useGSAP( () => { animateRef.current?.(open); }, { scope: rootRef, dependencies: [open] }, ); return (