"use client"; import React, { useState, useCallback, useContext, createContext } from 'react'; import { createPortal } from 'react-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { cn } from '@/lib/utils'; interface SheetContextType { open: boolean; setOpen: (open: boolean) => void; side: 'top' | 'right' | 'bottom' | 'left'; } const SheetContext = createContext(undefined); function useSheet() { const context = useContext(SheetContext); if (!context) { throw new Error('Sheet components must be used within a Sheet'); } return context; } interface SheetProps { open?: boolean; onOpenChange?: (open: boolean) => void; children: React.ReactNode; side?: 'top' | 'right' | 'bottom' | 'left'; } function Sheet({ open: controlledOpen, onOpenChange, children, side = 'right', }: SheetProps) { const [internalOpen, setInternalOpen] = useState(false); const isControlled = controlledOpen !== undefined; const open = isControlled ? controlledOpen : internalOpen; const setOpen = useCallback( (newOpen: boolean) => { if (!isControlled) { setInternalOpen(newOpen); } onOpenChange?.(newOpen); }, [isControlled, onOpenChange] ); return ( {children} ); } function SheetTrigger( props: React.ButtonHTMLAttributes & { asChild?: boolean } ) { const { setOpen } = useSheet(); const { children, asChild, ...rest } = props; if (asChild && React.isValidElement(children)) { return React.cloneElement(children as React.ReactElement, { ...rest, onClick: (e: React.MouseEvent) => { setOpen(true); children.props.onClick?.(e); }, }); } return ( ); } function SheetPortal({ children }: { children: React.ReactNode }) { if (typeof document === 'undefined') return null; return createPortal(children, document.body); } function SheetOverlay({ className, onClick, ...props }: React.HTMLAttributes) { const { setOpen } = useSheet(); return ( { setOpen(false); onClick?.(e); }} {...props} /> ); } interface SheetContentProps extends React.HTMLAttributes { showCloseButton?: boolean; } function SheetContent({ className, children, showCloseButton = true, ...props }: SheetContentProps) { const { open, setOpen, side } = useSheet(); const sideClasses = { right: 'inset-y-0 right-0 h-full w-3/4 sm:max-w-sm border-l', left: 'inset-y-0 left-0 h-full w-3/4 sm:max-w-sm border-r', top: 'inset-x-0 top-0 h-auto border-b', bottom: 'inset-x-0 bottom-0 h-auto border-t', }; const slideVariants = { right: { initial: { x: 400, opacity: 0 }, animate: { x: 0, opacity: 1 }, exit: { x: 400, opacity: 0 }, }, left: { initial: { x: -400, opacity: 0 }, animate: { x: 0, opacity: 1 }, exit: { x: -400, opacity: 0 }, }, top: { initial: { y: -400, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: -400, opacity: 0 }, }, bottom: { initial: { y: 400, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: 400, opacity: 0 }, }, }; return ( {open ? ( <> {children} {showCloseButton && ( )} ) : null} ); } function SheetClose( props: React.ButtonHTMLAttributes & { asChild?: boolean } ) { const { setOpen } = useSheet(); const { children, asChild, ...rest } = props; if (asChild && React.isValidElement(children)) { return React.cloneElement(children as React.ReactElement, { ...rest, onClick: (e: React.MouseEvent) => { setOpen(false); children.props.onClick?.(e); }, }); } return ( ); } function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function SheetTitle({ className, ...props }: React.ComponentProps<'h2'>) { return (

); } function SheetDescription({ className, ...props }: React.ComponentProps<'p'>) { return (

); } interface SheetBodyProps extends React.HTMLAttributes { children: React.ReactNode; } function SheetBody({ className, children, ...props }: SheetBodyProps) { return (

{children}
); } export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, SheetBody, SheetPortal, SheetOverlay, AnimatePresence, };