5 Commits

Author SHA1 Message Date
typist
55207beff5 0.0.18
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m39s
2025-10-29 08:33:42 +08:00
typist
e98a344b95 feat: enhance sidebar component with recursive submenu rendering
- Implemented recursive rendering of submenu items in the sidebar for better navigation.
- Added support for collapsible submenus, improving user experience with nested tools.
- Refactored menu item rendering logic to utilize a new renderSubMenuContent function for cleaner code.
2025-10-29 08:33:22 +08:00
typist
d553c3e04c 0.0.17
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m1s
2025-10-29 08:27:15 +08:00
typist
e2da2758cc chore: simplify build script in package.json
- Removed sitemap generation step from the build script for a more streamlined build process.
2025-10-29 08:27:00 +08:00
typist
3ab70498e6 feat: add ModeToggle component to layout and sidebar
- Integrated ModeToggle component into the layout for theme switching.
- Replaced the existing button in the sidebar footer with SidebarMenuButton for improved styling and functionality.
2025-10-29 08:26:26 +08:00
3 changed files with 59 additions and 31 deletions

View File

@@ -1,11 +1,11 @@
{ {
"name": "litek", "name": "litek",
"private": true, "private": true,
"version": "0.0.16", "version": "0.0.18",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "npm run generate:sitemap && tsc -b && vite build", "build": "tsc -b && vite build",
"generate:sitemap": "tsx scripts/generate-sitemap.ts", "generate:sitemap": "tsx scripts/generate-sitemap.ts",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview", "preview": "vite preview",

View File

@@ -1,11 +1,11 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { Link } from "react-router-dom";
import { ChevronRight } from "lucide-react";
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenuButton, SidebarMenuItem, SidebarMenu, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton } from "@/components/ui/sidebar"; 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 { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible";
import { tools, type Tool } from "@/components/tool"; import { tools, type Tool } from "@/components/tool";
import { Link } from "react-router-dom";
import { ModeToggle } from "@/components/theme/toggle";
import { Button } from "../ui/button";
import { ChevronRight } from "lucide-react";
export const AppSidebar = () => { export const AppSidebar = () => {
// 递归构建完整路径 // 递归构建完整路径
@@ -13,6 +13,46 @@ export const AppSidebar = () => {
return `/tool/${pathSegments.join("/")}`; return `/tool/${pathSegments.join("/")}`;
}; };
// 递归渲染子菜单内容
const renderSubMenuContent = (child: Tool, currentPaths: string[]): ReactNode => {
if (child.children) {
// 子菜单内的可折叠项
return (
<Collapsible defaultOpen={false} className="group/collapsible">
<CollapsibleTrigger asChild>
<SidebarMenuSubButton>
{child.icon}
<span>{child.name}</span>
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuSubButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{child.children.map((subChild) => (
<SidebarMenuSubItem key={subChild.name}>
{renderSubMenuContent(subChild, [...currentPaths, child.path])}
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</Collapsible>
);
}
// 叶子节点
return (
<SidebarMenuSubButton asChild>
<Link
to={buildFullPath([...currentPaths, child.path])}
title={child.description}
>
{child.icon}
<span>{child.name}</span>
</Link>
</SidebarMenuSubButton>
);
};
// 递归渲染菜单项 // 递归渲染菜单项
const renderMenuItem = (tool: Tool, parentPaths: string[] = []): ReactNode => { const renderMenuItem = (tool: Tool, parentPaths: string[] = []): ReactNode => {
const currentPaths = [...parentPaths, tool.path]; const currentPaths = [...parentPaths, tool.path];
@@ -20,12 +60,11 @@ export const AppSidebar = () => {
if (tool.children) { if (tool.children) {
// 有子菜单的项目 // 有子菜单的项目
return ( return (
<SidebarMenuItem key={tool.name}>
<Collapsible <Collapsible
key={tool.name}
defaultOpen={false} defaultOpen={false}
className="group/collapsible" className="group/collapsible"
> >
<SidebarMenuItem>
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={tool.description}> <SidebarMenuButton tooltip={tool.description}>
{tool.icon} {tool.icon}
@@ -37,25 +76,13 @@ export const AppSidebar = () => {
<SidebarMenuSub> <SidebarMenuSub>
{tool.children.map((child) => ( {tool.children.map((child) => (
<SidebarMenuSubItem key={child.name}> <SidebarMenuSubItem key={child.name}>
{child.children ? ( {renderSubMenuContent(child, currentPaths)}
renderMenuItem(child, currentPaths)
) : (
<SidebarMenuSubButton asChild>
<Link
to={buildFullPath([...currentPaths, child.path])}
title={child.description}
>
{child.icon}
<span>{child.name}</span>
</Link>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem> </SidebarMenuSubItem>
))} ))}
</SidebarMenuSub> </SidebarMenuSub>
</CollapsibleContent> </CollapsibleContent>
</SidebarMenuItem>
</Collapsible> </Collapsible>
</SidebarMenuItem>
); );
} }
@@ -88,10 +115,9 @@ export const AppSidebar = () => {
</SidebarGroup> </SidebarGroup>
</SidebarContent> </SidebarContent>
<SidebarFooter className="flex flex-row justify-between items-center gap-2"> <SidebarFooter className="flex flex-row justify-between items-center gap-2">
<Button variant="link"> <SidebarMenuButton variant="outline" asChild>
<a href="mailto:litek@mail.typist.cc">need more tools?</a> <a href="mailto:litek@mail.typist.cc">need more tools?</a>
</Button> </SidebarMenuButton>
<ModeToggle />
</SidebarFooter> </SidebarFooter>
</Sidebar> </Sidebar>
); );

View File

@@ -4,6 +4,8 @@ import { Outlet } from "react-router-dom";
import { ThemeProvider } from "@/components/theme/provider" import { ThemeProvider } from "@/components/theme/provider"
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar" import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/sidebar"; import { AppSidebar } from "@/components/sidebar";
import { ModeToggle } from "@/components/theme/toggle";
import { useSEO } from "@/hooks/use-seo"; import { useSEO } from "@/hooks/use-seo";
export const Layout: FC = () => { export const Layout: FC = () => {
@@ -17,7 +19,7 @@ export const Layout: FC = () => {
<div className="p-4 flex flex-col w-full h-[100vh] overflow-hidden"> <div className="p-4 flex flex-col w-full h-[100vh] overflow-hidden">
<nav className="flex items-center justify-between"> <nav className="flex items-center justify-between">
<SidebarTrigger className="size-10" /> <SidebarTrigger className="size-10" />
<div role="actions" /> <ModeToggle />
</nav> </nav>
<main className="flex-1 overflow-auto p-4 overflow-hidden"> <main className="flex-1 overflow-auto p-4 overflow-hidden">
<Outlet /> <Outlet />