Dialog
Um Dialog com animação de expansão
Código:
'use client'; import { forwardRef, useEffect, useRef } from 'react'; import { useOutsideClick } from '@/hook/useOutsideClick'; import { cn } from '@/lib/utils'; import { X } from 'lucide-react'; import { AnimatePresence, motion } from 'motion/react'; interface DialogProps extends React.HTMLAttributes<HTMLDivElement> { open: boolean; onClose: () => void; closeButton?: boolean; } export default function Dialog({ closeButton, open, onClose, children, ...props }: DialogProps) { const modalRef = useRef<HTMLDivElement>(null) as React.RefObject<HTMLDivElement>; useOutsideClick(modalRef, () => onClose()); useEffect(() => { if (open) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = 'auto'; } }, [open]); return ( <> {open && ( <div className='fixed inset-0 z-[9999] flex items-center justify-center overflow-y-auto bg-black/30 p-4 transition-all'> <AnimatePresence> {open && ( <Wrapper ref={modalRef} className='bg-card text-card-foreground border-border relative w-full max-w-lg rounded-lg border p-4 shadow-2xl' > {closeButton && ( <div className='text-foreground absolute right-4 top-4 cursor-pointer rounded-full p-1 transition-colors'> <X size={16} onClick={onClose} /> </div> )} {children} </Wrapper> )} </AnimatePresence> </div> )} </> ); } const Title = ({ children }: { children: React.ReactNode }) => { return ( <> <h3 className='text-lg font-semibold leading-none tracking-tight'>{children}</h3> </> ); }; const Header = ({ children }: { children: React.ReactNode }) => { return ( <> <div className='flex items-center pb-4'>{children}</div> </> ); }; const Body = ({ children }: { children: React.ReactNode }) => { return <div className='block py-2'>{children}</div>; }; const Footer = ({ children }: { children: React.ReactNode }) => { return ( <> <div className='flex items-center justify-end pt-4'>{children}</div> </> ); }; Dialog.Header = Header; Dialog.Title = Title; Dialog.Body = Body; Dialog.Footer = Footer; interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {} const Wrapper = forwardRef<HTMLDivElement, ContainerProps>(({ children, ...props }, ref) => { return ( <> <motion.div ref={ref} className={cn('', props.className)} initial={{ opacity: 0.3, scaleX: 0.9, scaleY: 0.2 }} viewport={{ once: true }} animate={{ opacity: 1, scaleX: 1, scaleY: 1 }} exit={{ opacity: 0.3, scaleX: 0.9, scaleY: 0.5 }} transition={{ type: 'spring', bounce: 0.4, duration: 0.5, }} > {children} </motion.div> </> ); });
yarn add motion lucide-react