feat: add mobile drawer component for user button menu
This commit is contained in:
parent
161cd45fa1
commit
fe1bf59d6f
@ -57,6 +57,7 @@
|
||||
"tailwind-merge": "^3.0.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@ -152,6 +152,9 @@ importers:
|
||||
unist-util-visit:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
vaul:
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
@ -3581,6 +3584,12 @@ packages:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
hasBin: true
|
||||
|
||||
vaul@1.1.2:
|
||||
resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
vfile-location@5.0.3:
|
||||
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
|
||||
|
||||
@ -7203,6 +7212,15 @@ snapshots:
|
||||
|
||||
uuid@9.0.1: {}
|
||||
|
||||
vaul@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@radix-ui/react-dialog': 1.1.6(@types/react-dom@19.0.3(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
|
||||
vfile-location@5.0.3:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
@ -17,7 +17,15 @@ import { LogOutIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
// import { Drawer } from "vaul";
|
||||
import {
|
||||
Drawer,
|
||||
DrawerTrigger,
|
||||
DrawerContent,
|
||||
DrawerOverlay,
|
||||
DrawerPortal,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from "@/components/ui/drawer";
|
||||
|
||||
export function UserButton() {
|
||||
const router = useRouter();
|
||||
@ -43,83 +51,83 @@ export function UserButton() {
|
||||
// }
|
||||
|
||||
// Mobile View, use Drawer
|
||||
// if (isMobile) {
|
||||
// return (
|
||||
// <Drawer.Root open={open} onClose={closeDrawer}>
|
||||
// <Drawer.Trigger onClick={() => setOpen(true)}>
|
||||
// <UserAvatar
|
||||
// name={user.name || null}
|
||||
// image={user.image || null}
|
||||
// className="size-8 border"
|
||||
// />
|
||||
// </Drawer.Trigger>
|
||||
// <Drawer.Portal>
|
||||
// <Drawer.Overlay
|
||||
// className="fixed inset-0 z-40 h-full bg-background/80 backdrop-blur-sm"
|
||||
// onClick={closeDrawer}
|
||||
// />
|
||||
// <Drawer.Content className="fixed inset-x-0 bottom-0 z-50 mt-24 overflow-hidden rounded-t-[10px] border bg-background px-3 text-sm">
|
||||
// <div className="sticky top-0 z-20 flex w-full items-center justify-center bg-inherit">
|
||||
// <div className="my-3 h-1.5 w-16 rounded-full bg-muted-foreground/20" />
|
||||
// </div>
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Drawer open={open} onClose={closeDrawer}>
|
||||
<DrawerTrigger onClick={() => setOpen(true)}>
|
||||
<UserAvatar
|
||||
name={user.name || undefined}
|
||||
image={user.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
</DrawerTrigger>
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay className="fixed inset-0 z-40 bg-background/50" />
|
||||
<DrawerContent className="fixed inset-x-0 bottom-0 z-50 mt-24 overflow-hidden rounded-t-[10px] border bg-background px-3 text-sm">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle></DrawerTitle>
|
||||
</DrawerHeader>
|
||||
<div className="flex items-center justify-start gap-4 p-2">
|
||||
<UserAvatar
|
||||
name={user.name || undefined}
|
||||
image={user.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
{user.name && <p className="font-medium">{user.name}</p>}
|
||||
{user.email && (
|
||||
<p className="w-[200px] truncate text-muted-foreground">
|
||||
{user?.email}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// <div className="flex items-center justify-start gap-2 p-2">
|
||||
// <div className="flex flex-col">
|
||||
// {user.name && <p className="font-medium">{user.name}</p>}
|
||||
// {user.email && (
|
||||
// <p className="w-[200px] truncate text-muted-foreground">
|
||||
// {user?.email}
|
||||
// </p>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <ul className="mb-14 mt-1 w-full text-muted-foreground">
|
||||
// {userButtonConfig.menus.map((item) => {
|
||||
// const Icon = Icons[item.icon || "arrowRight"];
|
||||
// return (
|
||||
// <li
|
||||
// key={item.href}
|
||||
// className="rounded-lg text-foreground hover:bg-muted"
|
||||
// >
|
||||
// <Link
|
||||
// href={item.href}
|
||||
// onClick={closeDrawer}
|
||||
// className="flex w-full items-center gap-3 px-2.5 py-2"
|
||||
// >
|
||||
// <Icon className="size-4" />
|
||||
// <p className="text-sm">{item.title}</p>
|
||||
// </Link>
|
||||
// </li>
|
||||
// );
|
||||
// })}
|
||||
// <li
|
||||
// key="logout"
|
||||
// className="rounded-lg text-foreground hover:bg-muted"
|
||||
// >
|
||||
// <Link
|
||||
// href="#"
|
||||
// onClick={(event) => {
|
||||
// event.preventDefault();
|
||||
// closeDrawer();
|
||||
// // signOut({
|
||||
// // callbackUrl: `${window.location.origin}/`,
|
||||
// // redirect: true,
|
||||
// // });
|
||||
// }}
|
||||
// className="flex w-full items-center gap-3 px-2.5 py-2"
|
||||
// >
|
||||
// <LogOutIcon className="size-4" />
|
||||
// <p className="text-sm">Log out</p>
|
||||
// </Link>
|
||||
// </li>
|
||||
// </ul>
|
||||
// </Drawer.Content>
|
||||
// <Drawer.Overlay />
|
||||
// </Drawer.Portal>
|
||||
// </Drawer.Root>
|
||||
// );
|
||||
// }
|
||||
<ul className="mb-14 mt-1 w-full text-muted-foreground">
|
||||
{userButtonConfig.menus.map((item) => {
|
||||
const Icon = Icons[item.icon || "arrowRight"];
|
||||
return (
|
||||
<li
|
||||
key={item.href}
|
||||
className="rounded-lg text-foreground hover:bg-muted"
|
||||
>
|
||||
<Link
|
||||
href={item.href}
|
||||
onClick={closeDrawer}
|
||||
className="flex w-full items-center gap-3 px-2.5 py-2"
|
||||
>
|
||||
<Icon className="size-4" />
|
||||
<p className="text-sm">{item.title}</p>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
<li
|
||||
key="logout"
|
||||
className="rounded-lg text-foreground hover:bg-muted"
|
||||
>
|
||||
<Link
|
||||
href="#"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
closeDrawer();
|
||||
// signOut({
|
||||
// callbackUrl: `${window.location.origin}/`,
|
||||
// redirect: true,
|
||||
// });
|
||||
}}
|
||||
className="flex w-full items-center gap-3 px-2.5 py-2"
|
||||
>
|
||||
<LogOutIcon className="size-4" />
|
||||
<p className="text-sm">Log out</p>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</DrawerContent>
|
||||
</DrawerPortal>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
// Desktop View, use DropdownMenu
|
||||
return (
|
||||
|
118
src/components/ui/drawer.tsx
Normal file
118
src/components/ui/drawer.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Drawer as DrawerPrimitive } from "vaul"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Drawer = ({
|
||||
shouldScaleBackground = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
||||
<DrawerPrimitive.Root
|
||||
shouldScaleBackground={shouldScaleBackground}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
Drawer.displayName = "Drawer"
|
||||
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger
|
||||
|
||||
const DrawerPortal = DrawerPrimitive.Portal
|
||||
|
||||
const DrawerClose = DrawerPrimitive.Close
|
||||
|
||||
const DrawerOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn("fixed inset-0 z-50 bg-black/80", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
|
||||
|
||||
const DrawerContent = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
))
|
||||
DrawerContent.displayName = "DrawerContent"
|
||||
|
||||
const DrawerHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DrawerHeader.displayName = "DrawerHeader"
|
||||
|
||||
const DrawerFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DrawerFooter.displayName = "DrawerFooter"
|
||||
|
||||
const DrawerTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DrawerTitle.displayName = DrawerPrimitive.Title.displayName
|
||||
|
||||
const DrawerDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DrawerDescription.displayName = DrawerPrimitive.Description.displayName
|
||||
|
||||
export {
|
||||
Drawer,
|
||||
DrawerPortal,
|
||||
DrawerOverlay,
|
||||
DrawerTrigger,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerFooter,
|
||||
DrawerTitle,
|
||||
DrawerDescription,
|
||||
}
|
Loading…
Reference in New Issue
Block a user