Files
LiquidCode_Frontend/src/components/modal/Modal.tsx
Виталий Лавшонок 106510e3c9 Main branch changes
2025-12-25 17:35:56 +03:00

83 lines
2.6 KiB
TypeScript

import React from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { cn } from '../../lib/cn';
import { useClickOutside } from '../../hooks/useClickOutside';
type ModalBackdrop = 'opaque' | 'blur';
interface ModalProps {
className?: string;
children?: React.ReactNode;
backdrop?: ModalBackdrop;
open: boolean;
defaultOpen?: boolean;
onOpenChange: (state: boolean) => void;
}
const modalbgVariants = {
closed: { opacity: 0, backdropFilter: 'blur(0px)' },
open: { opacity: 1, backdropFilter: 'blur(6px)' },
};
const modalVariants = {
closed: { opacity: 0, scale: 0.9 },
open: { opacity: 1, scale: 1 },
};
export const Modal: React.FC<ModalProps> = ({
children,
open,
backdrop,
className,
onOpenChange,
}) => {
const ref = React.useRef<HTMLDivElement>(null);
useClickOutside(ref, () => {
onOpenChange(false);
});
return (
<div>
<AnimatePresence>
{open && (
<motion.div
initial={modalbgVariants.closed}
animate={modalbgVariants.open}
exit={modalbgVariants.closed}
transition={{ duration: 0.15 }}
className={cn(
'fixed top-0 left-0 h-svh w-svw z-50',
backdrop === 'opaque' && 'bg-[#00000055]',
)}
style={
backdrop === 'blur'
? undefined
: { backdropFilter: 'none' }
}
/>
)}
</AnimatePresence>
<div className="fixed top-0 left-0 h-svh w-svw flex items-center justify-center pointer-events-none z-50">
<AnimatePresence>
{open && (
<motion.div
ref={ref}
className={cn(
'h-fit w-fit rounded-md pointer-events-auto',
className,
)}
initial={modalVariants.closed}
animate={modalVariants.open}
exit={modalVariants.closed}
transition={{ duration: 0.15 }}
>
{children}
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
};