fumadocs-ui
Version:
The Radix UI version of Fumadocs UI
273 lines (272 loc) • 8.8 kB
JavaScript
"use client";
import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
import { cn } from "../../utils/cn.js";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
import { ScrollArea, ScrollViewport } from "../ui/scroll-area.js";
import Link from "fumadocs-core/link";
import { usePathname } from "fumadocs-core/framework";
import { jsx, jsxs } from "react/jsx-runtime";
import { ChevronDown, ExternalLink } from "lucide-react";
import { createContext, use, useEffect, useMemo, useRef, useState } from "react";
import { useTranslations } from "@fuma-translate/react";
import { useOnChange } from "fumadocs-core/utils/use-on-change";
import { useMediaQuery } from "fumadocs-core/utils/use-media-query";
import { Presence } from "@radix-ui/react-presence";
import scrollIntoView from "scroll-into-view-if-needed";
//#region src/components/sidebar/base.tsx
var base_exports = /* @__PURE__ */ __exportAll({
SidebarCollapseTrigger: () => SidebarCollapseTrigger,
SidebarContent: () => SidebarContent,
SidebarDrawerContent: () => SidebarDrawerContent,
SidebarDrawerOverlay: () => SidebarDrawerOverlay,
SidebarFolder: () => SidebarFolder,
SidebarFolderContent: () => SidebarFolderContent,
SidebarFolderLink: () => SidebarFolderLink,
SidebarFolderTrigger: () => SidebarFolderTrigger,
SidebarItem: () => SidebarItem,
SidebarProvider: () => SidebarProvider,
SidebarSeparator: () => SidebarSeparator,
SidebarTrigger: () => SidebarTrigger,
SidebarViewport: () => SidebarViewport,
useAutoScroll: () => useAutoScroll,
useFolder: () => useFolder,
useFolderDepth: () => useFolderDepth,
useSidebar: () => useSidebar
});
const SidebarContext = createContext(null);
const FolderContext = createContext(null);
function SidebarProvider({ defaultOpenLevel = 0, prefetch, children }) {
const closeOnRedirect = useRef(true);
const [open, setOpen] = useState(false);
const [collapsed, setCollapsed] = useState(false);
const pathname = usePathname();
const mode = useMediaQuery("(width < 768px)") ? "drawer" : "full";
useOnChange(pathname, () => {
if (closeOnRedirect.current) setOpen(false);
closeOnRedirect.current = true;
});
return /* @__PURE__ */ jsx(SidebarContext, {
value: useMemo(() => ({
open,
setOpen,
collapsed,
setCollapsed,
closeOnRedirect,
defaultOpenLevel,
prefetch,
mode
}), [
open,
collapsed,
defaultOpenLevel,
prefetch,
mode
]),
children
});
}
function useSidebar() {
const ctx = use(SidebarContext);
if (!ctx) throw new Error("Missing SidebarContext, make sure you have wrapped the component in <DocsLayout /> and the context is available.");
return ctx;
}
function useFolder() {
return use(FolderContext);
}
function useFolderDepth() {
return use(FolderContext)?.depth ?? 0;
}
function SidebarContent({ mode: allowedMode = "full", children }) {
const { collapsed, mode } = useSidebar();
const [hover, setHover] = useState(false);
const ref = useRef(null);
const timerRef = useRef(0);
useOnChange(collapsed, () => {
if (collapsed) setHover(false);
});
if (allowedMode !== true && allowedMode !== mode) return;
function shouldIgnoreHover(e) {
const element = ref.current;
if (!element) return true;
return !collapsed || e.pointerType === "touch" || element.getAnimations().length > 0;
}
return children({
ref,
collapsed,
hovered: hover,
onPointerEnter(e) {
if (shouldIgnoreHover(e)) return;
window.clearTimeout(timerRef.current);
setHover(true);
},
onPointerLeave(e) {
if (shouldIgnoreHover(e)) return;
window.clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(() => setHover(false), Math.min(e.clientX, document.body.clientWidth - e.clientX) > 100 ? 0 : 500);
}
});
}
function SidebarViewport({ area, viewport, children }) {
return /* @__PURE__ */ jsx(ScrollArea, {
...area,
className: cn("min-h-0 flex-1", area?.className),
children: /* @__PURE__ */ jsx(ScrollViewport, {
...viewport,
className: cn("*:flex! *:flex-col! *:gap-0.5! p-4 overscroll-contain mask-[linear-gradient(to_bottom,transparent,white_12px,white_calc(100%-12px),transparent)]", viewport?.className),
children
})
});
}
function SidebarDrawerOverlay(props) {
const { open, setOpen, mode } = useSidebar();
if (mode !== "drawer") return;
return /* @__PURE__ */ jsx(Presence, {
present: open,
children: /* @__PURE__ */ jsx("div", {
"data-state": open ? "open" : "closed",
onClick: () => setOpen(false),
...props
})
});
}
function SidebarDrawerContent({ className, children, ...props }) {
const { open, mode } = useSidebar();
const state = open ? "open" : "closed";
if (mode !== "drawer") return;
return /* @__PURE__ */ jsx(Presence, {
present: open,
children: ({ present }) => /* @__PURE__ */ jsx("aside", {
id: "nd-sidebar-mobile",
"data-state": state,
className: cn(!present && "invisible", className),
...props,
children
})
});
}
function SidebarSeparator(props) {
return /* @__PURE__ */ jsx("p", { ...props });
}
function SidebarItem({ icon, active = false, children, ...props }) {
const ref = useRef(null);
const { prefetch } = useSidebar();
useAutoScroll(active, ref);
return /* @__PURE__ */ jsxs(Link, {
ref,
"data-active": active,
prefetch,
...props,
children: [icon ?? (props.external ? /* @__PURE__ */ jsx(ExternalLink, {}) : null), children]
});
}
function SidebarFolder({ defaultOpen: defaultOpenProp, collapsible = true, active = false, children, ...props }) {
const { defaultOpenLevel } = useSidebar();
const depth = useFolderDepth() + 1;
const defaultOpen = collapsible === false || active || (defaultOpenProp ?? defaultOpenLevel >= depth);
const [open, setOpen] = useState(defaultOpen);
useOnChange(defaultOpen, (v) => {
if (v) setOpen(v);
});
return /* @__PURE__ */ jsx(Collapsible, {
open,
onOpenChange: setOpen,
disabled: !collapsible,
...props,
children: /* @__PURE__ */ jsx(FolderContext, {
value: useMemo(() => ({
open,
setOpen,
depth,
collapsible
}), [
collapsible,
depth,
open
]),
children
})
});
}
function SidebarFolderTrigger({ children, ...props }) {
const { open, collapsible } = use(FolderContext);
if (collapsible) return /* @__PURE__ */ jsxs(CollapsibleTrigger, {
...props,
children: [children, /* @__PURE__ */ jsx(ChevronDown, {
"data-icon": true,
className: cn("ms-auto transition-transform", !open && "-rotate-90 rtl:rotate-90")
})]
});
return /* @__PURE__ */ jsx("div", {
...props,
children
});
}
function SidebarFolderLink({ children, active = false, ...props }) {
const ref = useRef(null);
const { open, setOpen, collapsible } = use(FolderContext);
const { prefetch } = useSidebar();
useAutoScroll(active, ref);
return /* @__PURE__ */ jsxs(Link, {
ref,
"data-active": active,
onClick: (e) => {
if (!collapsible) return;
if (e.target instanceof Element && e.target.matches("[data-icon], [data-icon] *")) {
setOpen(!open);
e.preventDefault();
} else setOpen(active ? !open : true);
},
prefetch,
...props,
children: [children, collapsible && /* @__PURE__ */ jsx(ChevronDown, {
"data-icon": true,
className: cn("ms-auto transition-transform", !open && "-rotate-90 rtl:rotate-90")
})]
});
}
function SidebarFolderContent(props) {
return /* @__PURE__ */ jsx(CollapsibleContent, {
...props,
children: props.children
});
}
function SidebarTrigger({ children, ...props }) {
const { setOpen } = useSidebar();
return /* @__PURE__ */ jsx("button", {
"aria-label": useTranslations({ note: "sidebar" })("Open Sidebar", { note: "aria-label" }),
onClick: () => setOpen((prev) => !prev),
...props,
children
});
}
function SidebarCollapseTrigger(props) {
const { collapsed, setCollapsed } = useSidebar();
return /* @__PURE__ */ jsx("button", {
type: "button",
"aria-label": useTranslations({ note: "sidebar" })("Collapse Sidebar", { note: "aria-label" }),
"data-collapsed": collapsed,
onClick: () => {
setCollapsed((prev) => !prev);
},
...props,
children: props.children
});
}
/**
* scroll to the element if `active` is true
*/
function useAutoScroll(active, ref) {
const { mode } = useSidebar();
useEffect(() => {
if (active && ref.current) scrollIntoView(ref.current, {
boundary: document.getElementById(mode === "drawer" ? "nd-sidebar-mobile" : "nd-sidebar"),
scrollMode: "if-needed"
});
}, [
active,
mode,
ref
]);
}
//#endregion
export { SidebarCollapseTrigger, SidebarContent, SidebarDrawerContent, SidebarDrawerOverlay, SidebarFolder, SidebarFolderContent, SidebarFolderLink, SidebarFolderTrigger, SidebarItem, SidebarProvider, SidebarSeparator, SidebarTrigger, SidebarViewport, base_exports, useAutoScroll, useFolder, useFolderDepth, useSidebar };