UNPKG

@theguild/components

Version:
180 lines (179 loc) • 7.43 kB
import { jsx, jsxs } from "react/jsx-runtime"; import { forwardRef, useEffect, useLayoutEffect } from "react"; import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"; import { cn } from "../../cn"; import { Anchor } from "../anchor"; import { ArrowIcon } from "../icons"; const CONTAINER_ID = "h-navmenu-container"; const VIEWPORT_ID = "h-navmenu-viewport"; const NavigationMenu = forwardRef(({ className, children, forceMount, ...rest }, ref) => /* @__PURE__ */ jsxs( NavigationMenuPrimitive.Root, { id: CONTAINER_ID, ref, className: cn("relative z-10 flex flex-1 items-center", className), "aria-label": "Navigation Menu", ...rest, children: [ children, /* @__PURE__ */ jsx(NavigationMenuViewport, { forceMount }), /* @__PURE__ */ jsx(RemoveMotionIfPreferred, {}) ] } )); NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName; const NavigationMenuList = forwardRef(({ className, ...rest }, ref) => /* @__PURE__ */ jsx( NavigationMenuPrimitive.List, { ref, className: cn( "group flex flex-1 list-none items-center rounded-lg border-beige-200 px-1.5 lg:border lg:px-3 dark:border-neutral-700", className ), ...rest } )); NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName; const NavigationMenuItem = NavigationMenuPrimitive.Item; const NavigationMenuTrigger = forwardRef(({ className, children, ...rest }, ref) => /* @__PURE__ */ jsx( NavigationMenuPrimitive.Trigger, { ref, className: cn( "hive-focus cursor-default rounded p-3 font-medium leading-normal text-green-800 aria-expanded:text-green-1000 dark:text-neutral-300 dark:aria-expanded:text-neutral-100", className ), onPointerOver: (event) => { rest.onPointerOver?.(event); const container = document.getElementById(CONTAINER_ID); const viewport = document.getElementById(VIEWPORT_ID); if (!viewport || !(viewport instanceof HTMLElement) || !container) return; const newX = getTransformX(event.currentTarget, viewport, container); if (!viewport.style.transition) { setTimeout(() => { viewport.style.transition = "transform 0.5s"; }, 0); } viewport.style.transform = `translateX(${newX}px)`; }, ...rest, children } )); NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName; const NavigationMenuContent = forwardRef(({ className, ...rest }, ref) => /* @__PURE__ */ jsx( NavigationMenuPrimitive.Content, { ref, className: cn( "absolute left-0 top-0 w-auto bg-white data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 dark:bg-neutral-900 [&>:first-child]:p-6", className ), ...rest, style: { animationDuration: "0.4s", ...rest.style } } )); NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName; const NavigationMenuLink = forwardRef(({ className, arrow, children, href, ...rest }, ref) => { const isActive = false; return /* @__PURE__ */ jsx(NavigationMenuPrimitive.Link, { active: isActive, ...rest, asChild: true, ref, children: /* @__PURE__ */ jsxs( Anchor, { href, className: cn( 'hive-focus [data-active="true"]:text-green-1000 rounded p-3 leading-normal text-green-800 transition-colors hover:bg-beige-100 hover:text-green-1000 dark:text-neutral-300 dark:hover:bg-neutral-800/50 dark:hover:text-neutral-100', arrow && "flex items-center", className ), ...href.startsWith("http") ? { target: "_blank", rel: "noopener noreferrer" } : {}, children: [ children, arrow && /* @__PURE__ */ jsx(ArrowIcon, { className: "ml-auto size-6 shrink-0 opacity-0 transition-all" }) ] } ) }); }); NavigationMenuLink.displayName = NavigationMenuPrimitive.Link.displayName; const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect; const NavigationMenuViewport = forwardRef(({ className, ...rest }, ref) => { useIsomorphicLayoutEffect(() => { const viewport = document.getElementById(VIEWPORT_ID); const container = document.getElementById(CONTAINER_ID); if (!(container && container instanceof HTMLElement && viewport && viewport instanceof HTMLElement)) { return; } const firstCollectionItem = container.querySelector("[data-radix-collection-item]"); if (firstCollectionItem && firstCollectionItem instanceof HTMLElement) { viewport.style.transform = `translateX(${getTransformX(firstCollectionItem, viewport, container)}px)`; } }, []); return /* @__PURE__ */ jsx( "div", { id: VIEWPORT_ID, className: "absolute left-0 top-full flex", style: { perspective: "2000px" }, children: /* @__PURE__ */ jsx( NavigationMenuPrimitive.Viewport, { className: cn( "relative mt-1.5 h-[--radix-navigation-menu-viewport-height] w-[--radix-navigation-menu-viewport-width] origin-[top_center] overflow-hidden rounded-xl border border-beige-200 bg-white shadow-[0px_16px_32px_-12px_rgba(14,18,27,0.10)] ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-90 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 dark:border-neutral-800 dark:bg-neutral-900", className ), style: { transition: "width 450ms, height 450ms, transform 250ms, opacity 250ms" }, ref, ...rest } ) } ); }); NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName; const NavigationMenuIndicator = forwardRef(({ className, ...rest }, ref) => /* @__PURE__ */ jsx( NavigationMenuPrimitive.Indicator, { ref, className: cn( "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in", className ), ...rest, children: /* @__PURE__ */ jsx("div", { className: "relative top-[60%] size-2 rotate-45 rounded-tl-sm bg-beige-200 shadow-md" }) } )); NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName; function getTransformX(element, viewport, container) { const boundingBox = element.getBoundingClientRect(); const containerLeft = container.getBoundingClientRect().left; const left = boundingBox.left - containerLeft; const offsetX = -32; let newX = left + offsetX; if (newX + viewport.offsetWidth > window.innerWidth) { newX = window.innerWidth - viewport.offsetWidth + offsetX; } return newX; } function RemoveMotionIfPreferred() { return /* @__PURE__ */ jsx("style", { children: `@media (prefers-reduced-motion: reduce) { #${CONTAINER_ID} * { animation-duration: 0ms !important; transition-duration: 0ms !important; } }` }); } export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport };