Some checks failed
Build and Deploy / build (push) Has been cancelled
- Email capture popup with scroll (10%) and exit intent triggers - First name field and full tracking (UTM, device, time on page) - Mautic API integration for contact creation - GeoIP detection for country/region - 4 locale support (sr, en, de, fr) - Mautic tracking script in layout
63 lines
1.6 KiB
TypeScript
63 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import { ReactNode } from "react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import { X } from "lucide-react";
|
|
|
|
interface DrawerProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
children: ReactNode;
|
|
side?: "left" | "right";
|
|
width?: string;
|
|
}
|
|
|
|
export default function Drawer({
|
|
isOpen,
|
|
onClose,
|
|
children,
|
|
side = "left",
|
|
width = "max-w-[420px]",
|
|
}: DrawerProps) {
|
|
const slideAnimation = {
|
|
initial: { x: side === "left" ? "-100%" : "100%" },
|
|
animate: { x: 0 },
|
|
exit: { x: side === "left" ? "-100%" : "100%" },
|
|
};
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<>
|
|
<motion.div
|
|
className="fixed inset-0 bg-black/40 backdrop-blur-sm z-50"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
onClick={onClose}
|
|
/>
|
|
|
|
<motion.div
|
|
className={`fixed top-0 ${side}-0 bottom-0 ${width} w-full bg-white z-50 shadow-2xl`}
|
|
initial={slideAnimation.initial}
|
|
animate={slideAnimation.animate}
|
|
exit={slideAnimation.exit}
|
|
transition={{ type: "tween", duration: 0.3 }}
|
|
>
|
|
<button
|
|
onClick={onClose}
|
|
className="absolute top-4 right-4 p-2 rounded-full hover:bg-gray-100 transition-colors z-10"
|
|
aria-label="Close"
|
|
>
|
|
<X className="w-5 h-5 text-gray-500" />
|
|
</button>
|
|
|
|
<div className="h-full overflow-y-auto">{children}</div>
|
|
</motion.div>
|
|
</>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
}
|