1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
'use client';
import Link from 'next/link';
import { Icons } from '@/components/icons/icons';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Skeleton } from '@/components/ui/skeleton';
import { signOut, useSession } from '@/lib/auth-client';
import { cn } from '@/lib/utils';
import type { UserAvatarClassNames } from './user-avatar';
import { UserAvatar } from './user-avatar';
export interface UserButtonClassNames {
base?: string;
skeleton?: string;
trigger?: {
base?: string;
avatar?: UserAvatarClassNames;
skeleton?: string;
};
content?: {
base?: string;
avatar?: UserAvatarClassNames;
menuItem?: string;
separator?: string;
};
}
export interface UserButtonProps {
className?: string;
classNames?: UserButtonClassNames;
}
export function UserButton({ className, classNames }: UserButtonProps) {
const { data: sessionData, isPending } = useSession();
const user = sessionData?.user ?? null;
return (
<DropdownMenu>
<DropdownMenuTrigger
className={cn('rounded-md bg-transparent', classNames?.trigger?.base)}
asChild
>
<Button
variant='ghost'
className='size-auto rounded-md border-none bg-transparent p-1.5 hover:bg-accent dark:hover:bg-accent'
disabled={isPending}
>
{isPending ? (
<Skeleton
className={cn(
'size-8 rounded-md',
className,
classNames?.base,
classNames?.skeleton,
classNames?.trigger?.skeleton,
)}
/>
) : (
<UserAvatar
className={cn('size-5', className, classNames?.base)}
classNames={classNames?.trigger?.avatar}
user={user}
/>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className={cn('max-w-64', classNames?.content?.base)}
onCloseAutoFocus={(e) => e.preventDefault()}
align='end'
>
{user ? (
<div className='flex items-center gap-2 p-2'>
<UserAvatar classNames={classNames?.content?.avatar} user={user} />
<div className='flex flex-col truncate'>
<div className='truncate font-medium text-sm'>
{user.name || user.email}
</div>
{user.name && (
<div className='truncate text-muted-foreground text-xs'>
{user.email}
</div>
)}
</div>
</div>
) : (
<div className='px-2 py-1 text-muted-foreground text-xs'>Account</div>
)}
<DropdownMenuSeparator className={classNames?.content?.separator} />
{!user ? (
<>
<DropdownMenuItem className={classNames?.content?.menuItem} asChild>
<Link href={'/login'}>
<Icons.logIn className='size-4' />
Sign In
</Link>
</DropdownMenuItem>
</>
) : (
<>
<DropdownMenuItem
className={classNames?.content?.menuItem}
onClick={() => signOut()}
>
<Icons.logOut className='size-4' />
Log Out
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}
|