UI: fix references problem of sidebar tabs

This commit is contained in:
Fuma Nama
2024-12-04 18:05:34 +08:00
committed by Fuma Nama
parent 6ad870c814
commit b7745f463a
3 changed files with 50 additions and 27 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-ui': patch
---
Fix references problem of sidebar tabs
@@ -7,8 +7,6 @@ import { cn } from '@/utils/cn';
import { isActive } from '@/utils/is-active';
import { useSidebar } from '@/contexts/sidebar';
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import type { PageTree } from 'fumadocs-core/server';
import { useTreePath } from '@/contexts/tree';
export interface Option {
/**
@@ -21,50 +19,54 @@ export interface Option {
description?: ReactNode;
/**
* Detect from page tree nodes
* Detect from a list of urls
*/
folder?: PageTree.Folder;
urls?: string[];
props?: HTMLAttributes<HTMLElement>;
}
export function RootToggle({
options,
placeholder,
...props
}: {
placeholder?: ReactNode;
options: Option[];
} & HTMLAttributes<HTMLButtonElement>) {
const [open, setOpen] = useState(false);
const { closeOnRedirect } = useSidebar();
const pathname = usePathname();
const path = useTreePath();
const selected = useMemo(() => {
return options.findLast((item) =>
item.folder
? path.includes(item.folder)
item.urls
? item.urls.includes(pathname)
: isActive(item.url, pathname, true),
);
}, [path, options, pathname]);
}, [options, pathname]);
const onClick = () => {
closeOnRedirect.current = false;
setOpen(false);
};
const item = selected ? <Item {...selected} /> : placeholder;
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger
{...props}
className={cn(
'flex flex-row items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-fd-accent/50 hover:text-fd-accent-foreground',
props.className,
)}
>
{selected ? <Item {...selected} /> : null}
<ChevronDown className="me-1.5 size-4 text-fd-muted-foreground" />
</PopoverTrigger>
{item ? (
<PopoverTrigger
{...props}
className={cn(
'flex flex-row items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-fd-accent/50 hover:text-fd-accent-foreground',
props.className,
)}
>
{item}
<ChevronDown className="me-1.5 size-4 text-fd-muted-foreground" />
</PopoverTrigger>
) : null}
<PopoverContent className="w-[var(--radix-popover-trigger-width)] overflow-hidden p-0">
{options.map((item) => (
<Link
+24 -8
View File
@@ -9,10 +9,10 @@ export function getSidebarTabs(
pageTree: PageTree.Root,
{ transform }: TabOptions = {},
): Option[] {
const options: Option[] = [];
function findOptions(node: PageTree.Folder): Option[] {
const results: Option[] = [];
function traverse(node: PageTree.Node) {
if (node.type === 'folder' && node.root) {
if (node.root) {
const index = node.index ?? node.children.at(0);
if (index?.type === 'page') {
@@ -20,18 +20,34 @@ export function getSidebarTabs(
url: index.url,
title: node.name,
icon: node.icon,
folder: node,
description: node.description,
urls: getFolderUrls(node),
};
const mapped = transform ? transform(option, node) : option;
if (mapped) options.push(mapped);
if (mapped) results.push(mapped);
}
}
if (node.type === 'folder') node.children.forEach(traverse);
for (const child of node.children) {
if (child.type === 'folder') results.push(...findOptions(child));
}
return results;
}
pageTree.children.forEach(traverse);
return options;
return findOptions(pageTree as PageTree.Folder);
}
function getFolderUrls(folder: PageTree.Folder): string[] {
const results: string[] = [];
if (folder.index) results.push(folder.index.url);
for (const child of folder.children) {
if (child.type === 'page') results.push(child.url);
if (child.type === 'folder') results.push(...getFolderUrls(child));
}
return results;
}