95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { signOut, useSession } from 'next-auth/react'
|
|
import { ChevronDown, LogOut, UserRound } from 'lucide-react'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
|
|
type DepartmentAccountMenuProps = {
|
|
collapsed: boolean
|
|
onNavigate?: () => void
|
|
}
|
|
|
|
export default function DepartmentAccountMenu({ collapsed, onNavigate }: DepartmentAccountMenuProps) {
|
|
const { data: session } = useSession()
|
|
const [open, setOpen] = useState(false)
|
|
const menuRef = useRef<HTMLDivElement>(null)
|
|
const triggerRef = useRef<HTMLButtonElement>(null)
|
|
const userName = session?.user?.name || session?.user?.email || '当前用户'
|
|
const initials = userName.slice(0, 1).toUpperCase()
|
|
|
|
useEffect(() => {
|
|
const closeMenu = (event: MouseEvent) => {
|
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) setOpen(false)
|
|
}
|
|
const closeWithEscape = (event: KeyboardEvent) => {
|
|
if (event.key !== 'Escape' || !open) return
|
|
event.stopPropagation()
|
|
setOpen(false)
|
|
triggerRef.current?.focus()
|
|
}
|
|
|
|
document.addEventListener('mousedown', closeMenu)
|
|
document.addEventListener('keydown', closeWithEscape)
|
|
return () => {
|
|
document.removeEventListener('mousedown', closeMenu)
|
|
document.removeEventListener('keydown', closeWithEscape)
|
|
}
|
|
}, [open])
|
|
|
|
return (
|
|
<div ref={menuRef} className="relative">
|
|
{open && (
|
|
<div
|
|
role="menu"
|
|
className={`absolute bottom-full z-50 mb-2 w-56 rounded-lg bg-white p-2 text-[#344054] shadow-[0_14px_38px_rgba(15,23,42,0.22)] ring-1 ring-[#e2e8f0] ${collapsed ? 'left-0' : 'inset-x-0 w-auto'}`}
|
|
>
|
|
<Link
|
|
href="/profile"
|
|
role="menuitem"
|
|
onClick={() => {
|
|
setOpen(false)
|
|
onNavigate?.()
|
|
}}
|
|
className="flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium hover:bg-[#f2f5f8] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]"
|
|
>
|
|
<UserRound className="h-4 w-4" aria-hidden="true" />
|
|
个人资料
|
|
</Link>
|
|
<button
|
|
type="button"
|
|
role="menuitem"
|
|
onClick={() => signOut({ callbackUrl: '/login' })}
|
|
className="flex min-h-11 w-full items-center gap-3 rounded-lg px-3 text-left text-sm font-medium text-[#b42318] hover:bg-[#fff1f0] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f97066]"
|
|
>
|
|
<LogOut className="h-4 w-4" aria-hidden="true" />
|
|
退出登录
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
ref={triggerRef}
|
|
type="button"
|
|
aria-expanded={open}
|
|
aria-haspopup="menu"
|
|
aria-label="打开账户菜单"
|
|
title={collapsed ? userName : undefined}
|
|
onClick={() => setOpen((current) => !current)}
|
|
className={`flex min-h-12 w-full items-center rounded-lg text-left text-[#dbe4ed] transition-colors hover:bg-white/[0.08] hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#69a9ff] ${collapsed ? 'justify-center px-2' : 'gap-3 px-2'}`}
|
|
>
|
|
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[#1677ff] text-sm font-bold text-white">{initials}</span>
|
|
{!collapsed && (
|
|
<>
|
|
<span className="min-w-0 flex-1">
|
|
<span className="block truncate text-sm font-semibold">{userName}</span>
|
|
<span className="mt-0.5 block text-xs text-[#7f93a8]">账户与退出</span>
|
|
</span>
|
|
<ChevronDown className={`h-4 w-4 shrink-0 text-[#7f93a8] transition-transform ${open ? 'rotate-180' : ''}`} aria-hidden="true" />
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|