fumadocs-ui
Version:
The Radix UI version of Fumadocs UI
159 lines (156 loc) • 6.58 kB
JavaScript
'use client';
import { useSidebar } from "../../components/sidebar/base.js";
import { Popover, PopoverContent, PopoverTrigger } from "../../components/ui/popover.js";
import { isTabActive } from "../../components/sidebar/tabs/dropdown.js";
import Link from "fumadocs-core/link";
import { usePathname } from "fumadocs-core/framework";
import { cn } from "@fumadocs/ui/cn";
import { jsx, jsxs } from "react/jsx-runtime";
import { ChevronDown } from "lucide-react";
import { Fragment as Fragment$1, createContext, use, useMemo, useRef, useState } from "react";
import { useIsScrollTop } from "@fumadocs/ui/hooks/use-is-scroll-top";
import { LinkItem } from "@fumadocs/ui/link-item";
//#region src/layouts/notebook/client.tsx
const LayoutContext = createContext(null);
function LayoutContextProvider({ navTransparentMode = "none", navMode, tabMode, children }) {
const isTop = useIsScrollTop({ enabled: navTransparentMode === "top" }) ?? true;
const isNavTransparent = navTransparentMode === "top" ? isTop : navTransparentMode === "always";
return /* @__PURE__ */ jsx(LayoutContext, {
value: useMemo(() => ({
isNavTransparent,
navMode,
tabMode
}), [
isNavTransparent,
navMode,
tabMode
]),
children
});
}
function LayoutHeader(props) {
const { open } = useSidebar();
const { isNavTransparent } = use(LayoutContext);
return /* @__PURE__ */ jsx("header", {
"data-transparent": isNavTransparent && !open,
...props,
children: props.children
});
}
function LayoutBody({ className, style, children, ...props }) {
const { navMode } = use(LayoutContext);
const { collapsed } = useSidebar();
const pageCol = "calc(var(--fd-layout-width,97rem) - var(--fd-sidebar-col) - var(--fd-toc-width))";
return /* @__PURE__ */ jsx("div", {
id: "nd-notebook-layout",
className: cn("grid overflow-x-clip min-h-(--fd-docs-height) transition-[grid-template-columns] auto-cols-auto auto-rows-auto [--fd-docs-height:100dvh] [--fd-header-height:0px] [--fd-toc-popover-height:0px] [--fd-sidebar-width:0px] [--fd-toc-width:0px]", className),
style: {
gridTemplate: navMode === "top" ? `". header header header ."
"sidebar sidebar toc-popover toc-popover ."
"sidebar sidebar main toc ." 1fr / minmax(min-content, 1fr) var(--fd-sidebar-col) minmax(0, ${pageCol}) var(--fd-toc-width) minmax(min-content, 1fr)` : `"sidebar sidebar header header ."
"sidebar sidebar toc-popover toc-popover ."
"sidebar sidebar main toc ." 1fr / minmax(min-content, 1fr) var(--fd-sidebar-col) minmax(0, ${pageCol}) var(--fd-toc-width) minmax(min-content, 1fr)`,
"--fd-docs-row-1": "var(--fd-banner-height, 0px)",
"--fd-docs-row-2": "calc(var(--fd-docs-row-1) + var(--fd-header-height))",
"--fd-docs-row-3": "calc(var(--fd-docs-row-2) + var(--fd-toc-popover-height))",
"--fd-sidebar-col": collapsed ? "0px" : "var(--fd-sidebar-width)",
...style
},
...props,
children
});
}
function LayoutHeaderTabs({ options, className, ...props }) {
const pathname = usePathname();
const selectedIdx = useMemo(() => {
return options.findLastIndex((option) => isTabActive(option, pathname));
}, [options, pathname]);
return /* @__PURE__ */ jsx("div", {
className: cn("flex flex-row items-end gap-6", className),
...props,
children: options.map((option, i) => {
const { title, url, unlisted, props: { className: className$1, ...rest } = {} } = option;
const isSelected = selectedIdx === i;
return /* @__PURE__ */ jsx(Link, {
href: url,
className: cn("inline-flex border-b-2 border-transparent transition-colors items-center pb-1.5 font-medium gap-2 text-fd-muted-foreground text-sm text-nowrap hover:text-fd-accent-foreground", unlisted && !isSelected && "hidden", isSelected && "border-fd-primary text-fd-primary", className$1),
...rest,
children: title
}, i);
})
});
}
function NavbarLinkItem({ item, className, ...props }) {
if (item.type === "custom") return item.children;
if (item.type === "menu") return /* @__PURE__ */ jsx(NavbarLinkItemMenu, {
item,
className,
...props
});
return /* @__PURE__ */ jsx(LinkItem, {
item,
className: cn("text-sm text-fd-muted-foreground transition-colors hover:text-fd-accent-foreground data-[active=true]:text-fd-primary", className),
...props,
children: item.text
});
}
function NavbarLinkItemMenu({ item, hoverDelay = 50, className, ...props }) {
const [open, setOpen] = useState(false);
const timeoutRef = useRef(null);
const freezeUntil = useRef(null);
const delaySetOpen = (value) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
timeoutRef.current = window.setTimeout(() => {
setOpen(value);
freezeUntil.current = Date.now() + 300;
}, hoverDelay);
};
const onPointerEnter = (e) => {
if (e.pointerType === "touch") return;
delaySetOpen(true);
};
const onPointerLeave = (e) => {
if (e.pointerType === "touch") return;
delaySetOpen(false);
};
function isTouchDevice() {
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
}
return /* @__PURE__ */ jsxs(Popover, {
open,
onOpenChange: (value) => {
if (freezeUntil.current === null || Date.now() >= freezeUntil.current) setOpen(value);
},
children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
className: cn("inline-flex items-center gap-1.5 p-1 text-sm text-fd-muted-foreground transition-colors has-data-[active=true]:text-fd-primary data-[state=open]:text-fd-accent-foreground focus-visible:outline-none", className),
onPointerEnter,
onPointerLeave,
...props,
children: [item.url ? /* @__PURE__ */ jsx(LinkItem, {
item,
children: item.text
}) : item.text, /* @__PURE__ */ jsx(ChevronDown, { className: "size-3" })]
}), /* @__PURE__ */ jsx(PopoverContent, {
className: "flex flex-col p-1 text-fd-muted-foreground text-start",
onPointerEnter,
onPointerLeave,
children: item.items.map((child, i) => {
if (child.type === "custom") return /* @__PURE__ */ jsx(Fragment$1, { children: child.children }, i);
return /* @__PURE__ */ jsxs(LinkItem, {
item: child,
className: "inline-flex items-center gap-2 rounded-md p-2 transition-colors hover:bg-fd-accent hover:text-fd-accent-foreground data-[active=true]:text-fd-primary [&_svg]:size-4",
onClick: () => {
if (isTouchDevice()) setOpen(false);
},
children: [child.icon, child.text]
}, i);
})
})]
});
}
//#endregion
export { LayoutBody, LayoutContext, LayoutContextProvider, LayoutHeader, LayoutHeaderTabs, NavbarLinkItem };
//# sourceMappingURL=client.js.map