128 lines
5.0 KiB
TypeScript
128 lines
5.0 KiB
TypeScript
'use client';
|
|
|
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
import Link from 'next/link';
|
|
import { useState } from 'react';
|
|
import { avatarInitials, useUserProfile } from '@/lib/hooks/use-user-profile';
|
|
import { useMenuNavigation } from '@/lib/hooks/use-menu-navigation';
|
|
import { useAuthStore } from '@/stores/auth-store';
|
|
import { useLayoutStore } from '@/stores/layout-store';
|
|
import { useTabStore } from '@/stores/tab-store';
|
|
|
|
export function UserMenu() {
|
|
const logout = useAuthStore((s) => s.logout);
|
|
const sidebarMode = useLayoutStore((s) => s.sidebarMode);
|
|
const setSidebarMode = useLayoutStore((s) => s.setSidebarMode);
|
|
const { profile, userSub, loading, label } = useUserProfile();
|
|
const [open, setOpen] = useState(false);
|
|
const onMenuNavigate = useMenuNavigation();
|
|
const initials = avatarInitials(profile, userSub);
|
|
|
|
return (
|
|
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
|
<DropdownMenu.Trigger asChild>
|
|
<button
|
|
type="button"
|
|
className="flex max-w-[min(100vw-8rem,14rem)] items-center gap-2 rounded-lg border-0 bg-white px-2 py-1.5 text-left text-sm text-neutral-800 outline-none hover:bg-neutral-50 data-[state=open]:bg-neutral-50"
|
|
aria-label="用户菜单"
|
|
>
|
|
{profile?.avatar ? (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img src={profile.avatar} alt="" className="h-8 w-8 shrink-0 rounded-full object-cover" />
|
|
) : (
|
|
<span
|
|
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-neutral-200 text-xs font-medium text-neutral-700"
|
|
aria-hidden
|
|
>
|
|
{initials}
|
|
</span>
|
|
)}
|
|
<span className="min-w-0 flex-1 truncate font-medium">{label}</span>
|
|
<span className="shrink-0 text-neutral-400" aria-hidden>
|
|
{open ? '▴' : '▾'}
|
|
</span>
|
|
</button>
|
|
</DropdownMenu.Trigger>
|
|
|
|
<DropdownMenu.Portal>
|
|
<DropdownMenu.Content
|
|
className="z-50 w-60 rounded-lg border border-neutral-200 bg-white py-1 shadow-lg outline-none"
|
|
sideOffset={4}
|
|
align="end"
|
|
collisionPadding={8}
|
|
>
|
|
<div className="border-b border-neutral-100 px-3 py-2">
|
|
<p className="truncate text-sm font-medium text-neutral-900">{label}</p>
|
|
{profile?.email ? (
|
|
<p className="mt-0.5 truncate text-xs text-neutral-500">{profile.email}</p>
|
|
) : null}
|
|
{!profile?.email && userSub ? (
|
|
<p className="mt-0.5 truncate font-mono text-xs text-neutral-400" title={userSub}>
|
|
ID {userSub}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
|
|
<DropdownMenu.Item asChild>
|
|
<Link
|
|
href="/dashboard"
|
|
className="block cursor-pointer px-3 py-2 text-sm text-neutral-800 outline-none data-highlighted:bg-neutral-50"
|
|
onClick={() => onMenuNavigate('/dashboard', '概览')}
|
|
>
|
|
工作台
|
|
</Link>
|
|
</DropdownMenu.Item>
|
|
<DropdownMenu.Item asChild>
|
|
<Link
|
|
href="/dashboard/account"
|
|
className="block cursor-pointer px-3 py-2 text-sm text-neutral-800 outline-none data-highlighted:bg-neutral-50"
|
|
onClick={() => onMenuNavigate('/dashboard/account', '个人中心')}
|
|
>
|
|
个人中心
|
|
</Link>
|
|
</DropdownMenu.Item>
|
|
|
|
<DropdownMenu.Separator className="my-1 h-px bg-neutral-100" />
|
|
|
|
<div className="border-b border-neutral-100 px-3 py-2">
|
|
<p className="mb-1 text-xs text-neutral-500">侧栏布局</p>
|
|
<div className="flex gap-1">
|
|
<button
|
|
type="button"
|
|
className={`flex-1 rounded border px-2 py-1 text-xs ${
|
|
sidebarMode === 'classic'
|
|
? 'border-neutral-900 bg-neutral-900 text-white'
|
|
: 'border-neutral-200 bg-white text-neutral-700'
|
|
}`}
|
|
onClick={() => setSidebarMode('classic')}
|
|
>
|
|
经典
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`flex-1 rounded border px-2 py-1 text-xs ${
|
|
sidebarMode === 'icon'
|
|
? 'border-neutral-900 bg-neutral-900 text-white'
|
|
: 'border-neutral-200 bg-white text-neutral-700'
|
|
}`}
|
|
onClick={() => setSidebarMode('icon')}
|
|
>
|
|
图标
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<DropdownMenu.Item
|
|
className="cursor-pointer px-3 py-2 text-left text-sm text-red-600 outline-none data-highlighted:bg-red-50"
|
|
onSelect={() => {
|
|
void logout();
|
|
}}
|
|
>
|
|
退出登录
|
|
</DropdownMenu.Item>
|
|
</DropdownMenu.Content>
|
|
</DropdownMenu.Portal>
|
|
</DropdownMenu.Root>
|
|
);
|
|
}
|