crush-level-web/src/components/layout/Sidebar.tsx

156 lines
5.0 KiB
TypeScript
Raw Normal View History

2025-11-13 08:38:25 +00:00
"use client"
import React, { useState, useEffect } from "react"
import { MenuItem } from "@/types/global"
import Image from "next/image"
import { cn } from "@/lib/utils"
import ChatSidebar from "./components/ChatSidebar"
import { Badge } from "../ui/badge"
import Link from "next/link"
import { usePathname, useRouter } from "next/navigation"
import { useMainLayout } from "@/context/mainLayout"
import { useCurrentUser } from "@/hooks/auth"
import Notice from "./components/Notice"
import useCreatorNavigation from "@/hooks/useCreatorNavigation"
// 菜单项接口
interface IMenuItem {
id: MenuItem
icon: string
selectedIcon: string
label: string
link: string
isSelected: boolean
}
// 主侧边栏组件
function Sidebar() {
const pathname = usePathname()
const router = useRouter()
const { isSidebarExpanded, dispatch } = useMainLayout();
const { data: user } = useCurrentUser();
const { routerToCreate } = useCreatorNavigation()
// 在 /create/image 页面时强制收起侧边栏
const isImageCreatePage = ['/generate/image', '/generate/image-2-image', '/generate/image-edit', '/generate/image-2-background'].includes(pathname)
const actualIsExpanded = isImageCreatePage ? false : isSidebarExpanded
const menuItems: IMenuItem[] = [
{
id: MenuItem.FOR_YOU,
icon: "/icons/explore.svg",
selectedIcon: "/icons/explore_selected.svg",
label: "Home",
link: '/',
isSelected: pathname === '/',
},
{
id: MenuItem.CREATE,
icon: "/icons/create.svg",
selectedIcon: "/icons/create_selected.svg",
label: "Create a Character",
link: '/create',
isSelected: pathname.startsWith('/create'),
},
// {
// id: MenuItem.EXPLORE,
// icon: "/icons/explore.svg",
// selectedIcon: "/icons/explore_selected.svg",
// label: "Explore",
// link: '/explore',
// isSelected: pathname.startsWith('/explore'),
// },
{
id: MenuItem.CHAT,
icon: "/icons/contact.svg",
selectedIcon: "/icons/contact_selected.svg",
label: "My Crushes",
link: '/contact',
isSelected: pathname.startsWith('/contact'),
},
]
const toggleExpanded = () => {
// 在 /create/image 页面时禁用展开功能
if (isImageCreatePage) return
dispatch({
type: "updateState",
payload: {
isSidebarExpanded: !isSidebarExpanded,
},
})
}
return (
<aside className={cn(
"sticky top-0 left-0 bottom-0 z-10 h-screen flex-shrink-0 bg-background-default border-r border-outline-normal transition-all duration-300 ease-in-out",
isImageCreatePage ? "w-0 overflow-hidden" : (actualIsExpanded ? "w-80" : "w-20")
)}>
<div className="flex flex-col h-full py-4">
{/* 顶部导航 */}
<div className="flex flex-col gap-2 px-4">
{/* 展开/收起按钮 */}
<div className={cn(
"flex items-center justify-start p-2 select-none",
isImageCreatePage ? "cursor-default" : "cursor-pointer"
)} onClick={toggleExpanded}>
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
{actualIsExpanded ? (
<Image src="/icons/fold.svg" alt="Fold" width={32} height={32} />
) : (
<Image src="/icons/expand.svg" alt="Expand" width={32} height={32} />
)}
</div>
</div>
{/* 菜单项 */}
{menuItems.map((item) => {
const menuItemContent = (
<div
className={cn(
"flex items-center gap-2 p-2 rounded-full cursor-pointer transition-colors hover:bg-surface-element-hover",
item.isSelected && "bg-surface-element-normal"
)}
>
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
<Image
src={item.isSelected ? item.selectedIcon : item.icon}
alt={item.label}
width={32}
height={32}
className="w-full h-full"
/>
</div>
{actualIsExpanded && (
<span className="txt-label-m text-txt-primary-normal flex-1 truncate">
{item.label}
</span>
)}
</div>
);
// 对于 create 链接,使用 div + onClick 避免触发 ProgressProvider
if (item.link.startsWith('/create')) {
return (
<div key={item.id} onClick={routerToCreate}>
{menuItemContent}
</div>
);
}
// 其他链接使用 Link 组件以便 SEO
return (
<Link href={item.link} key={item.id}>
{menuItemContent}
</Link>
);
})}
</div>
<ChatSidebar isExpanded={actualIsExpanded} />
<Notice actualIsExpanded={actualIsExpanded} />
</div>
</aside>
)
}
export default Sidebar