fumadocs-ui
Version:
The Radix UI version of Fumadocs UI
240 lines (239 loc) • 9.86 kB
JavaScript
"use client";
import { cn } from "../../../utils/cn.js";
import { buttonVariants } from "../../../components/ui/button.js";
import { LinkItem } from "../../shared/client.js";
import "../../shared/index.js";
import { useIsScrollTop } from "../../../utils/use-is-scroll-top.js";
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport } from "../../../components/ui/navigation-menu.js";
import { useHomeLayout } from "../index.js";
import Link from "fumadocs-core/link";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { ChevronDown, Languages } from "lucide-react";
import { cva } from "class-variance-authority";
import { Fragment as Fragment$1, useState } from "react";
import { useTranslations } from "@fuma-translate/react";
//#region src/layouts/home/slots/header.tsx
const navItemVariants = cva("[&_svg]:size-4", {
variants: { variant: {
main: "inline-flex items-center gap-1 p-2 text-fd-muted-foreground transition-colors hover:text-fd-accent-foreground data-[active=true]:text-fd-primary",
button: buttonVariants({
color: "secondary",
className: "gap-1.5"
}),
icon: buttonVariants({
color: "ghost",
size: "icon"
})
} },
defaultVariants: { variant: "main" }
});
function Header(props) {
const { navItems, menuItems, slots, props: { nav } } = useHomeLayout();
const t = useTranslations({ note: "mobile menu" });
if (nav?.component) return nav.component;
return /* @__PURE__ */ jsxs(HeaderNavigationMenu, {
transparentMode: nav?.transparentMode,
...props,
children: [
slots.navTitle && /* @__PURE__ */ jsx(slots.navTitle, { className: "inline-flex items-center gap-2.5 font-semibold" }),
nav?.children,
/* @__PURE__ */ jsx("ul", {
className: "flex flex-row items-center gap-2 px-6 max-sm:hidden",
children: navItems.filter((item) => !isSecondary(item)).map((item, i) => /* @__PURE__ */ jsx(NavigationMenuLinkItem, {
item,
className: "text-sm"
}, i))
}),
/* @__PURE__ */ jsxs("div", {
className: "flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden",
children: [
slots.searchTrigger && /* @__PURE__ */ jsx(slots.searchTrigger.full, {
hideIfDisabled: true,
className: "w-full rounded-full ps-2.5 max-w-[240px]"
}),
slots.themeSwitch && /* @__PURE__ */ jsx(slots.themeSwitch, {}),
slots.languageSelect && /* @__PURE__ */ jsx(slots.languageSelect.root, { children: /* @__PURE__ */ jsx(Languages, { className: "size-5" }) }),
/* @__PURE__ */ jsx("ul", {
className: "flex flex-row gap-2 items-center empty:hidden",
children: navItems.filter(isSecondary).map((item, i) => /* @__PURE__ */ jsx(NavigationMenuLinkItem, {
className: cn(item.type === "icon" && "-mx-1 first:ms-0 last:me-0"),
item
}, i))
})
]
}),
/* @__PURE__ */ jsxs("div", {
className: "flex flex-row items-center ms-auto -me-1.5 lg:hidden",
children: [slots.searchTrigger && /* @__PURE__ */ jsx(slots.searchTrigger.sm, {
hideIfDisabled: true,
className: "p-2"
}), /* @__PURE__ */ jsx(NavigationMenuItem, {
asChild: true,
children: /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(NavigationMenuTrigger, {
"aria-label": t("Toggle Menu", { note: "aria-label" }),
className: cn(buttonVariants({
size: "icon",
color: "ghost",
className: "group [&_svg]:size-5.5"
})),
onPointerMove: nav?.enableHoverToOpen ? void 0 : (e) => e.preventDefault(),
children: /* @__PURE__ */ jsx(ChevronDown, { className: "transition-transform duration-300 group-data-[state=open]:rotate-180" })
}), /* @__PURE__ */ jsxs(NavigationMenuContent, {
className: "flex flex-col p-4 sm:flex-row sm:items-center sm:justify-end",
children: [menuItems.filter((item) => !isSecondary(item)).map((item, i) => /* @__PURE__ */ jsx(MobileNavigationMenuLinkItem, {
item,
className: "sm:hidden"
}, i)), /* @__PURE__ */ jsxs("div", {
className: "-ms-1.5 flex flex-row items-center gap-2 max-sm:mt-2",
children: [
menuItems.filter(isSecondary).map((item, i) => /* @__PURE__ */ jsx(MobileNavigationMenuLinkItem, {
item,
className: cn(item.type === "icon" && "-mx-1 first:ms-0")
}, i)),
/* @__PURE__ */ jsx("div", {
role: "separator",
className: "flex-1"
}),
slots.languageSelect && /* @__PURE__ */ jsxs(slots.languageSelect.root, { children: [
/* @__PURE__ */ jsx(Languages, { className: "size-5" }),
slots.languageSelect.text && /* @__PURE__ */ jsx(slots.languageSelect.text, {}),
/* @__PURE__ */ jsx(ChevronDown, { className: "size-3 text-fd-muted-foreground" })
] }),
slots.themeSwitch && /* @__PURE__ */ jsx(slots.themeSwitch, {})
]
})]
})] })
})]
})
]
});
}
function isSecondary(item) {
if ("secondary" in item && item.secondary != null) return item.secondary;
return item.type === "icon";
}
function HeaderNavigationMenu({ transparentMode = "none", ...props }) {
const [value, setValue] = useState("");
const isTop = useIsScrollTop({ enabled: transparentMode === "top" }) ?? true;
const isTransparent = transparentMode === "top" ? isTop : transparentMode === "always";
return /* @__PURE__ */ jsx(NavigationMenu, {
value,
onValueChange: setValue,
asChild: true,
children: /* @__PURE__ */ jsx("header", {
id: "nd-nav",
...props,
className: cn("sticky h-14 top-0 z-40", props.className),
children: /* @__PURE__ */ jsxs("div", {
className: cn("backdrop-blur-lg border-b transition-colors *:mx-auto *:max-w-(--fd-layout-width)", value.length > 0 && "max-lg:shadow-lg max-lg:rounded-b-2xl", (!isTransparent || value.length > 0) && "bg-fd-background/80"),
children: [/* @__PURE__ */ jsx(NavigationMenuList, {
className: "flex h-14 w-full items-center px-4",
asChild: true,
children: /* @__PURE__ */ jsx("nav", { children: props.children })
}), /* @__PURE__ */ jsx(NavigationMenuViewport, {})]
})
})
});
}
function NavigationMenuLinkItem({ item, ...props }) {
if (item.type === "custom") return item.children;
if (item.type === "menu") {
const children = item.items.map((child, j) => {
if (child.type === "custom") return /* @__PURE__ */ jsx(Fragment$1, { children: child.children }, j);
const { banner = child.icon ? /* @__PURE__ */ jsx("div", {
className: "w-fit rounded-md border bg-fd-muted p-1 [&_svg]:size-4",
children: child.icon
}) : null, ...rest } = child.menu ?? {};
return /* @__PURE__ */ jsx(NavigationMenuLink, {
asChild: true,
children: /* @__PURE__ */ jsx(Link, {
href: child.url,
external: child.external,
...rest,
className: cn("flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground", rest.className),
children: rest.children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
banner,
/* @__PURE__ */ jsx("p", {
className: "text-base font-medium",
children: child.text
}),
/* @__PURE__ */ jsx("p", {
className: "text-sm text-fd-muted-foreground empty:hidden",
children: child.description
})
] })
})
}, `${j}-${child.url}`);
});
return /* @__PURE__ */ jsxs(NavigationMenuItem, {
...props,
children: [/* @__PURE__ */ jsx(NavigationMenuTrigger, {
className: cn(navItemVariants(), "rounded-md"),
children: item.url ? /* @__PURE__ */ jsx(Link, {
href: item.url,
external: item.external,
children: item.text
}) : item.text
}), /* @__PURE__ */ jsx(NavigationMenuContent, {
className: "grid grid-cols-1 gap-2 p-4 md:grid-cols-2 lg:grid-cols-3",
children
})]
});
}
return /* @__PURE__ */ jsx(NavigationMenuItem, {
...props,
children: /* @__PURE__ */ jsx(NavigationMenuLink, {
asChild: true,
children: /* @__PURE__ */ jsx(LinkItem, {
item,
"aria-label": item.type === "icon" ? item.label : void 0,
className: cn(navItemVariants({ variant: item.type })),
children: item.type === "icon" ? item.icon : item.text
})
})
});
}
function MobileNavigationMenuLinkItem({ item, ...props }) {
if (item.type === "custom") return /* @__PURE__ */ jsx("div", {
className: cn("grid", props.className),
children: item.children
});
if (item.type === "menu") {
const header = /* @__PURE__ */ jsxs(Fragment, { children: [item.icon, item.text] });
return /* @__PURE__ */ jsxs("div", {
className: cn("mb-4 flex flex-col", props.className),
children: [/* @__PURE__ */ jsx("p", {
className: "mb-1 text-sm text-fd-muted-foreground",
children: item.url ? /* @__PURE__ */ jsx(NavigationMenuLink, {
asChild: true,
children: /* @__PURE__ */ jsx(Link, {
href: item.url,
external: item.external,
children: header
})
}) : header
}), item.items.map((child, i) => /* @__PURE__ */ jsx(MobileNavigationMenuLinkItem, { item: child }, i))]
});
}
return /* @__PURE__ */ jsx(NavigationMenuLink, {
asChild: true,
children: /* @__PURE__ */ jsxs(LinkItem, {
item,
className: cn({
main: "inline-flex items-center gap-2 py-1.5 transition-colors hover:text-fd-popover-foreground/50 data-[active=true]:font-medium data-[active=true]:text-fd-primary [&_svg]:size-4",
icon: buttonVariants({
size: "icon",
color: "ghost"
}),
button: buttonVariants({
color: "secondary",
className: "gap-1.5 [&_svg]:size-4"
})
}[item.type ?? "main"], props.className),
"aria-label": item.type === "icon" ? item.label : void 0,
children: [item.icon, item.type === "icon" ? void 0 : item.text]
})
});
}
//#endregion
export { Header, navItemVariants };