From 6fd81b7d7d2c7ed5e11b709498fba3682dbeb9fe Mon Sep 17 00:00:00 2001 From: Adrian Lam Date: Thu, 23 Oct 2025 03:11:05 -0700 Subject: [PATCH] update docs component sidebar to auto expand --- docs/components/layout/docs.tsx | 55 +++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/docs/components/layout/docs.tsx b/docs/components/layout/docs.tsx index fa0ee635eb..ed2fee417c 100644 --- a/docs/components/layout/docs.tsx +++ b/docs/components/layout/docs.tsx @@ -8,11 +8,39 @@ import { } from 'fumadocs-ui/components/ui/collapsible'; import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; import { TreeContextProvider, useTreeContext } from 'fumadocs-ui/contexts/tree'; -import { ChevronDown, ChevronRight, FlaskConical } from 'lucide-react'; +import { ChevronDown, ChevronRight } from 'lucide-react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import { type ComponentProps, type ReactNode, useMemo, useState } from 'react'; +import { + type ComponentProps, + type ReactNode, + useEffect, + useMemo, + useState, +} from 'react'; import { cn } from '../../lib/cn'; +import { isActive } from '../../lib/is-active'; + +/** + * Recursively checks if the current pathname matches any page within a folder + */ +function isPathInFolder(folder: PageTree.Folder, pathname: string): boolean { + // Check if folder has an index page that matches + if (folder.index && isActive(folder.index.url, pathname, true)) { + return true; + } + + // Check all children + return folder.children.some((child) => { + if (child.type === 'page') { + return isActive(child.url, pathname, true); + } + if (child.type === 'folder') { + return isPathInFolder(child, pathname); + } + return false; + }); +} export interface DocsLayoutProps { tree: PageTree.Root; @@ -123,9 +151,24 @@ function SidebarItem({ }) { const pathname = usePathname(); const { setOpen: setSidebarOpen } = useSidebar(); - const [isOpen, setIsOpen] = useState( - item.type === 'folder' && 'defaultOpen' in item && item.defaultOpen === true - ); + const [isOpen, setIsOpen] = useState(() => { + if (item.type !== 'folder') return false; + + // Open if explicitly set to defaultOpen + if ('defaultOpen' in item && item.defaultOpen === true) { + return true; + } + + // Open if current path is within this folder + return isPathInFolder(item, pathname); + }); + + // Expand folder when navigating to any page within it + useEffect(() => { + if (item.type === 'folder' && isPathInFolder(item, pathname)) { + setIsOpen(true); + } + }, [pathname, item]); // Close sidebar on mobile when link is clicked const handleLinkClick = () => { @@ -200,7 +243,7 @@ function SidebarItem({