"use client"; import * as React from "react"; import { createPortal } from "react-dom"; import { cn } from "../../lib/utils"; import { X } from "lucide-react"; import { motion, AnimatePresence, HTMLMotionProps } from "framer-motion"; // Import HTMLMotionProps interface DrawerContextValue { open: boolean; setOpen: React.Dispatch>; } const DrawerContext = React.createContext( undefined ); function useDrawerContext() { const context = React.useContext(DrawerContext); if (!context) { throw new Error("useDrawerContext must be used within a Drawer"); } return context; } interface DrawerProps { children: React.ReactNode; defaultOpen?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; } const Drawer = ({ children, defaultOpen = false, open: controlledOpen, onOpenChange, }: DrawerProps) => { const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen); const isControlled = controlledOpen !== undefined; const open = isControlled ? controlledOpen : uncontrolledOpen; const setOpen = React.useCallback( (value: React.SetStateAction) => { if (!isControlled) { setUncontrolledOpen(value); } if (onOpenChange) { const nextValue = typeof value === "function" ? value(open) : value; onOpenChange(nextValue); } }, [isControlled, onOpenChange, open] ); return ( {children} ); }; interface DrawerTriggerProps extends React.ButtonHTMLAttributes { asChild?: boolean; } const DrawerTrigger = React.forwardRef( ({ children, ...props }, ref) => { const { setOpen } = useDrawerContext(); return ( ); } ); DrawerTrigger.displayName = "DrawerTrigger"; // Define a type that omits conflicting HTML attributes for Framer Motion type OmittedDrawerContentHTMLAttributes = Omit< React.HTMLAttributes, | "onAnimationStart" | "onAnimationEnd" | "onAnimationIteration" | "onTransitionEnd" | "onDrag" | "onDragEnd" | "onDragEnter" | "onDragExit" | "onDragLeave" | "onDragOver" | "onDragStart" | "onDrop" | "onMouseDown" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseOut" | "onMouseOver" | "onMouseUp" | "onTouchCancel" | "onTouchEnd" | "onTouchMove" | "onTouchStart" | "onPointerDown" | "onPointerMove" | "onPointerUp" | "onPointerCancel" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOut" | "onGotPointerCapture" | "onLostPointerCapture" >; interface DrawerContentProps extends OmittedDrawerContentHTMLAttributes { // Use the new type here className?: string; } const DrawerContent = React.forwardRef( ({ children, className, ...props }, ref) => { const { open, setOpen } = useDrawerContext(); // Use AnimatePresence to handle mount/unmount animations return createPortal( {open && ( // Conditionally render the motion components based on `open` state
{/* Backdrop Overlay */} setOpen(false)} // Close drawer when clicking overlay aria-hidden="true" // Hide from screen readers as it's purely visual /> {/* Drawer Content */} )} // Cast props to HTMLMotionProps<'div'> >
{children}
)} , document.body ); } ); DrawerContent.displayName = "DrawerContent"; // Add the missing exports for the Drawer components const DrawerClose = React.forwardRef< HTMLButtonElement, React.ButtonHTMLAttributes >(({ children, ...props }, ref) => { const { setOpen } = useDrawerContext(); return ( ); }); DrawerClose.displayName = "DrawerClose"; const DrawerHeader = ({ className, ...props }: React.HTMLAttributes) => (
); DrawerHeader.displayName = "DrawerHeader"; const DrawerFooter = ({ className, ...props }: React.HTMLAttributes) => (
); DrawerFooter.displayName = "DrawerFooter"; const DrawerTitle = React.forwardRef< HTMLHeadingElement, React.HTMLAttributes >(({ className, ...props }, ref) => (

)); DrawerTitle.displayName = "DrawerTitle"; const DrawerDescription = React.forwardRef< HTMLParagraphElement, React.HTMLAttributes >(({ className, ...props }, ref) => (

)); DrawerDescription.displayName = "DrawerDescription"; export { Drawer, DrawerTrigger, DrawerContent, DrawerClose, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, };