fumadocs-ui
Version:
The Radix UI version of Fumadocs UI
262 lines (259 loc) • 10.4 kB
JavaScript
'use client';
import { buttonVariants } from "../../components/ui/button.js";
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport } from "../../components/ui/navigation-menu.js";
import { LargeSearchToggle, SearchToggle } from "../shared/search-toggle.js";
import { renderTitleNav, resolveLinkItems } from "../shared/index.js";
import { LanguageToggle, LanguageToggleText } from "../shared/language-toggle.js";
import { ThemeToggle } from "../shared/theme-toggle.js";
import Link from "fumadocs-core/link";
import { cn } from "@fumadocs/ui/cn";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { ChevronDown, Languages } from "lucide-react";
import { Fragment as Fragment$1, useMemo, useState } from "react";
import { cva } from "class-variance-authority";
import { useIsScrollTop } from "@fumadocs/ui/hooks/use-is-scroll-top";
import { LinkItem } from "@fumadocs/ui/link-item";
//#region src/layouts/home/client.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({ nav = {}, i18n = false, links, githubUrl, themeSwitch = {}, searchToggle = {} }) {
const { navItems, menuItems } = useMemo(() => {
const navItems$1 = [];
const menuItems$1 = [];
for (const item of resolveLinkItems({
links,
githubUrl
})) switch (item.on ?? "all") {
case "menu":
menuItems$1.push(item);
break;
case "nav":
navItems$1.push(item);
break;
default:
navItems$1.push(item);
menuItems$1.push(item);
}
return {
navItems: navItems$1,
menuItems: menuItems$1
};
}, [links, githubUrl]);
return /* @__PURE__ */ jsxs(HeaderNavigationMenu, {
transparentMode: nav.transparentMode,
children: [
renderTitleNav(nav, { 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: [
searchToggle.enabled !== false && (searchToggle.components?.lg ?? /* @__PURE__ */ jsx(LargeSearchToggle, {
className: "w-full rounded-full ps-2.5 max-w-[240px]",
hideIfDisabled: true
})),
themeSwitch.enabled !== false && (themeSwitch.component ?? /* @__PURE__ */ jsx(ThemeToggle, { mode: themeSwitch?.mode })),
i18n && /* @__PURE__ */ jsx(LanguageToggle, { 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("ul", {
className: "flex flex-row items-center ms-auto -me-1.5 lg:hidden",
children: [searchToggle.enabled !== false && (searchToggle.components?.sm ?? /* @__PURE__ */ jsx(SearchToggle, {
className: "p-2",
hideIfDisabled: true
})), /* @__PURE__ */ jsxs(NavigationMenuItem, { children: [/* @__PURE__ */ jsx(NavigationMenuTrigger, {
"aria-label": "Toggle Menu",
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"
}),
i18n && /* @__PURE__ */ jsxs(LanguageToggle, { children: [
/* @__PURE__ */ jsx(Languages, { className: "size-5" }),
/* @__PURE__ */ jsx(LanguageToggleText, {}),
/* @__PURE__ */ jsx(ChevronDown, { className: "size-3 text-fd-muted-foreground" })
] }),
themeSwitch.enabled !== false && (themeSwitch.component ?? /* @__PURE__ */ jsx(ThemeToggle, { mode: themeSwitch?.mode }))
]
})]
})] })]
})
]
});
}
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 /* @__PURE__ */ jsx("div", {
...props,
children: 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 };
//# sourceMappingURL=client.js.map