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:
		| @@ -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> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
		Reference in New Issue
	
	Block a user
	 typist
					typist