@theguild/components
Version:
180 lines (179 loc) • 7.43 kB
JavaScript
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
};