- 添加 ESLint 和 Prettier 配置以统一代码风格 - 配置项目级 TypeScript 设置 - 更新前后端依赖版本 - 修复代码格式问题(引号、分号、尾随逗号等) - 优化文件结构和导入路径
130 lines
4.3 KiB
TypeScript
130 lines
4.3 KiB
TypeScript
import { Button, Avatar, Dropdown, Label } from '@heroui/react'
|
|
import { buttonVariants } from '@heroui/styles'
|
|
import { useEffect, useState } from 'react'
|
|
import { useSession, signOut } from '../lib/auth-client'
|
|
import { SunIcon, MoonIcon } from '@heroicons/react/24/outline'
|
|
import { Link as RouterLink } from 'react-router-dom'
|
|
|
|
export function SiteNavbar() {
|
|
const { data: session } = useSession()
|
|
const [theme, setTheme] = useState<'light' | 'dark'>(() => {
|
|
if (typeof window !== 'undefined') {
|
|
return document.documentElement.classList.contains('dark')
|
|
? 'dark'
|
|
: 'light'
|
|
}
|
|
return 'light'
|
|
})
|
|
|
|
useEffect(() => {
|
|
// Sync theme on mount if needed, but the state is already initialized
|
|
}, [])
|
|
|
|
const toggleTheme = () => {
|
|
const nextTheme = theme === 'dark' ? 'light' : 'dark'
|
|
document.documentElement.classList.remove('light', 'dark')
|
|
document.documentElement.classList.add(nextTheme)
|
|
document.documentElement.setAttribute('data-theme', nextTheme)
|
|
localStorage.setItem('theme', nextTheme)
|
|
setTheme(nextTheme)
|
|
}
|
|
|
|
return (
|
|
<nav className="bg-background/70 border-default-100 sticky top-0 z-50 flex w-full items-center justify-between border-b px-8 py-4 backdrop-blur-md">
|
|
<div className="flex items-center gap-4">
|
|
<RouterLink
|
|
to="/"
|
|
className="text-foreground text-xl font-bold text-inherit no-underline"
|
|
>
|
|
HLAE中文站
|
|
</RouterLink>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-8">
|
|
<div className="hidden items-center gap-8 sm:flex">
|
|
<RouterLink
|
|
to="/"
|
|
className="text-foreground/80 hover:text-foreground font-medium transition-colors"
|
|
>
|
|
主页
|
|
</RouterLink>
|
|
<RouterLink
|
|
to="/demo"
|
|
className="text-foreground/80 hover:text-foreground font-medium transition-colors"
|
|
>
|
|
击杀生成
|
|
</RouterLink>
|
|
<RouterLink
|
|
to="/about"
|
|
className="text-foreground/80 hover:text-foreground font-medium transition-colors"
|
|
>
|
|
关于
|
|
</RouterLink>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
isIconOnly
|
|
variant="ghost"
|
|
onPress={toggleTheme}
|
|
className="text-foreground/80"
|
|
>
|
|
{theme === 'dark' ? (
|
|
<SunIcon className="h-5 w-5" />
|
|
) : (
|
|
<MoonIcon className="h-5 w-5" />
|
|
)}
|
|
</Button>
|
|
|
|
{session ? (
|
|
<Dropdown>
|
|
<Dropdown.Trigger>
|
|
<Button isIconOnly variant="tertiary" className="rounded-full">
|
|
<Avatar size="sm" color="accent">
|
|
<Avatar.Image
|
|
alt={session.user.name || '用户头像'}
|
|
src={session.user.image || ''}
|
|
/>
|
|
<Avatar.Fallback>
|
|
{(session.user.name || 'U').slice(0, 2).toUpperCase()}
|
|
</Avatar.Fallback>
|
|
</Avatar>
|
|
</Button>
|
|
</Dropdown.Trigger>
|
|
<Dropdown.Popover>
|
|
<Dropdown.Menu aria-label="Profile Actions">
|
|
<Dropdown.Item id="profile" textValue="Profile">
|
|
<Label className="font-semibold">
|
|
登录为 {session.user.email}
|
|
</Label>
|
|
</Dropdown.Item>
|
|
<Dropdown.Item
|
|
id="logout"
|
|
textValue="Logout"
|
|
variant="danger"
|
|
onPress={() => signOut()}
|
|
>
|
|
<Label className="text-danger">退出登录</Label>
|
|
</Dropdown.Item>
|
|
</Dropdown.Menu>
|
|
</Dropdown.Popover>
|
|
</Dropdown>
|
|
) : (
|
|
<div className="flex items-center gap-4">
|
|
<RouterLink
|
|
to="/login"
|
|
className={buttonVariants({
|
|
variant: 'ghost',
|
|
className: 'text-foreground font-medium',
|
|
})}
|
|
>
|
|
登录
|
|
</RouterLink>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
)
|
|
}
|