feat(mobile): add Drawer (vaul wrapper) for native-feel bottom sheets

This commit is contained in:
Matt Ciaccio
2026-04-29 14:14:18 +02:00
parent 210360738d
commit 0330be1312

View File

@@ -0,0 +1,92 @@
'use client';
import * as React from 'react';
import { Drawer as VaulDrawer } from 'vaul';
import { cn } from '@/lib/utils';
const Drawer = ({
shouldScaleBackground = true,
...props
}: React.ComponentProps<typeof VaulDrawer.Root>) => (
<VaulDrawer.Root shouldScaleBackground={shouldScaleBackground} {...props} />
);
Drawer.displayName = 'Drawer';
const DrawerTrigger = VaulDrawer.Trigger;
const DrawerPortal = VaulDrawer.Portal;
const DrawerClose = VaulDrawer.Close;
const DrawerOverlay = React.forwardRef<
React.ElementRef<typeof VaulDrawer.Overlay>,
React.ComponentPropsWithoutRef<typeof VaulDrawer.Overlay>
>(({ className, ...props }, ref) => (
<VaulDrawer.Overlay
ref={ref}
className={cn('fixed inset-0 z-50 bg-black/60', className)}
{...props}
/>
));
DrawerOverlay.displayName = 'DrawerOverlay';
const DrawerContent = React.forwardRef<
React.ElementRef<typeof VaulDrawer.Content>,
React.ComponentPropsWithoutRef<typeof VaulDrawer.Content>
>(({ className, children, ...props }, ref) => (
<DrawerPortal>
<DrawerOverlay />
<VaulDrawer.Content
ref={ref}
className={cn(
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-xl border bg-background pb-safe-bottom',
className,
)}
{...props}
>
<div className="mx-auto mt-2 h-1.5 w-12 rounded-full bg-muted" aria-hidden />
{children}
</VaulDrawer.Content>
</DrawerPortal>
));
DrawerContent.displayName = 'DrawerContent';
const DrawerHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn('grid gap-1.5 p-4 text-left', className)} {...props} />
);
DrawerHeader.displayName = 'DrawerHeader';
const DrawerTitle = React.forwardRef<
React.ElementRef<typeof VaulDrawer.Title>,
React.ComponentPropsWithoutRef<typeof VaulDrawer.Title>
>(({ className, ...props }, ref) => (
<VaulDrawer.Title
ref={ref}
className={cn('text-lg font-semibold leading-none tracking-tight', className)}
{...props}
/>
));
DrawerTitle.displayName = 'DrawerTitle';
const DrawerDescription = React.forwardRef<
React.ElementRef<typeof VaulDrawer.Description>,
React.ComponentPropsWithoutRef<typeof VaulDrawer.Description>
>(({ className, ...props }, ref) => (
<VaulDrawer.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
));
DrawerDescription.displayName = 'DrawerDescription';
export {
Drawer,
DrawerPortal,
DrawerOverlay,
DrawerTrigger,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerDescription,
};