"use client"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; import { useEffect, type ReactNode } from "react"; import { EASE_OUT, SPRING_PANEL } from "@/lib/ease"; import { cn } from "@/lib/utils"; export interface DrawerProps { open: boolean; onOpenChange: (open: boolean) => void; side?: "left" | "right"; children: ReactNode; /** Class for the panel surface. */ className?: string; /** Class for the backdrop. */ backdropClassName?: string; ariaLabel?: string; /** Close when the backdrop is clicked. Default true. */ dismissable?: boolean; } export function Drawer({ open, onOpenChange, side = "right", children, className, backdropClassName, ariaLabel, dismissable = true, }: DrawerProps) { const reduce = useReducedMotion(); useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onOpenChange(false); }; window.addEventListener("keydown", onKey); const prevOverflow = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { window.removeEventListener("keydown", onKey); document.body.style.overflow = prevOverflow; }; }, [open, onOpenChange]); const offscreen = side === "right" ? "100%" : "-100%"; return ( {open ? (
dismissable && onOpenChange(false)} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.25, ease: EASE_OUT }} className={cn( "absolute inset-0 h-full w-full cursor-default bg-black/40 backdrop-blur-sm", backdropClassName, )} /> {children}
) : null}
); }