Files
2026-06-13 01:30:18 +05:30

142 lines
4.3 KiB
TypeScript

"use client";
import * as React from "react";
import { Drawer as DrawerPrimitive } from "vaul";
import { cn } from "@/lib/utils";
const Drawer = ({
shouldScaleBackground = true,
// On iOS vaul freezes <body> (position:fixed) while the drawer is open and
// restores the old scroll offset on close. When a link inside the drawer
// navigates, that restore fires AFTER the route change and drags the new
// page to the previous page's scroll position — from the bottom of a long
// page it looks like the link simply didn't work. This flag makes vaul
// skip the restore when the URL changed; plain dismissals still restore.
preventScrollRestoration = true,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
<DrawerPrimitive.Root
shouldScaleBackground={shouldScaleBackground}
preventScrollRestoration={preventScrollRestoration}
{...props}
/>
);
Drawer.displayName = "Drawer";
const DrawerTrigger = DrawerPrimitive.Trigger;
const DrawerPortal = DrawerPrimitive.Portal;
const DrawerClose = DrawerPrimitive.Close;
const DrawerOverlay = React.forwardRef<
React.ComponentRef<typeof DrawerPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DrawerPrimitive.Overlay
ref={ref}
className={cn("fixed inset-0 z-90 bg-black/80", className)}
{...props}
/>
));
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
const DrawerContent = React.forwardRef<
React.ComponentRef<typeof DrawerPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {
direction?: "top" | "bottom" | "left" | "right";
/** Override the scrim styling — e.g. "bg-transparent" for a see-through
* overlay that keeps the content behind the drawer visible while still
* catching outside clicks to dismiss. */
overlayClassName?: string;
}
>(({ className, children, direction = "bottom", overlayClassName, ...props }, ref) => (
<DrawerPortal>
<DrawerOverlay className={overlayClassName} />
<DrawerPrimitive.Content
ref={ref}
className={cn(
"fixed z-90 flex flex-col border bg-background",
direction === "left" &&
"inset-y-0 left-0 h-full w-[80%] max-w-sm rounded-r-[10px]",
direction === "right" &&
"inset-y-0 right-0 h-full w-[80%] max-w-sm rounded-l-[10px]",
direction === "bottom" &&
"inset-x-0 bottom-0 mt-24 h-auto rounded-t-[10px]",
direction === "top" && "inset-x-0 top-0 mb-24 h-auto rounded-b-[10px]",
className,
)}
{...props}
>
{(direction === "bottom" || direction === "top") && (
<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.ComponentRef<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.ComponentRef<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,
};