feat: refactor AppSidebar to support nested tool menus with collapsible items

- Enhanced the AppSidebar component to recursively render tools with potential submenus.
- Introduced a buildFullPath function for dynamic routing based on tool hierarchy.
- Utilized the Collapsible component for better organization of tools with children.
- Maintained existing footer and header structure for consistency.
This commit is contained in:
typist
2025-10-29 05:45:09 +08:00
parent e9cf714da9
commit 3f7c81e892

View File

@@ -1,40 +1,98 @@
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar"; import type { ReactNode } from "react";
import { tools } from "@/components/tool"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenuButton, SidebarMenuItem, SidebarMenu, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton } from "@/components/ui/sidebar";
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible";
import { tools, type Tool } from "@/components/tool";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { ModeToggle } from "@/components/theme/toggle"; import { ModeToggle } from "@/components/theme/toggle";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { ChevronRight } from "lucide-react";
export const AppSidebar = () => ( export const AppSidebar = () => {
<Sidebar> // 递归构建完整路径
<SidebarHeader className="text-2xl font-bold flex justify-center items-center"> const buildFullPath = (pathSegments: string[]): string => {
Lite Kit return `/tool/${pathSegments.join("/")}`;
</SidebarHeader> };
<SidebarContent>
<SidebarGroup> // 递归渲染菜单项
<SidebarGroupLabel> const renderMenuItem = (tool: Tool, parentPaths: string[] = []): ReactNode => {
Tools const currentPaths = [...parentPaths, tool.path];
</SidebarGroupLabel>
<SidebarGroupContent> if (tool.children) {
{ // 有子菜单的项目
tools.map((tool) => ( return (
<SidebarMenuItem key={tool.name}> <Collapsible
<SidebarMenuButton asChild> key={tool.name}
<Link to={`/tool/${tool.path}`} title={tool.description}> defaultOpen={false}
{tool.icon} className="group/collapsible"
{tool.name} >
</Link> <SidebarMenuItem>
</SidebarMenuButton> <CollapsibleTrigger asChild>
</SidebarMenuItem> <SidebarMenuButton tooltip={tool.description}>
)) {tool.icon}
} <span>{tool.name}</span>
</SidebarGroupContent> <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarGroup> </SidebarMenuButton>
</SidebarContent> </CollapsibleTrigger>
<SidebarFooter className="flex flex-row justify-between items-center gap-2"> <CollapsibleContent>
<Button variant="link"> <SidebarMenuSub>
<a href="mailto:litek@mail.typist.cc">need more tools?</a> {tool.children.map((child) => (
</Button> <SidebarMenuSubItem key={child.name}>
<ModeToggle /> {child.children ? (
</SidebarFooter> renderMenuItem(child, currentPaths)
</Sidebar> ) : (
) <SidebarMenuSubButton asChild>
<Link
to={buildFullPath([...currentPaths, child.path])}
title={child.description}
>
{child.icon}
<span>{child.name}</span>
</Link>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
);
}
// 没有子菜单的项目
return (
<SidebarMenuItem key={tool.name}>
<SidebarMenuButton asChild tooltip={tool.description}>
<Link to={buildFullPath(currentPaths)}>
{tool.icon}
<span>{tool.name}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
);
};
return (
<Sidebar>
<SidebarHeader className="text-2xl font-bold flex justify-center items-center">
Lite Kit
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Tools</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{tools.map((tool) => renderMenuItem(tool))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter className="flex flex-row justify-between items-center gap-2">
<Button variant="link">
<a href="mailto:litek@mail.typist.cc">need more tools?</a>
</Button>
<ModeToggle />
</SidebarFooter>
</Sidebar>
);
};