Retour au catalogue
Modal Confirm
Modale de confirmation avec icone, message et boutons confirmer/annuler.
modal-pagessimple Both Responsive a11y
minimalcorporatesaasuniversalcentered
Theme
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { AlertTriangle, X } from "lucide-react";
interface ModalConfirmProps {
title?: string;
message?: string;
confirmLabel?: string;
cancelLabel?: string;
variant?: "danger" | "warning" | "default";
}
const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];
export default function ModalConfirm({
title = "Confirmer l'action",
message = "Etes-vous sur de vouloir continuer ?",
confirmLabel = "Confirmer",
cancelLabel = "Annuler",
variant = "danger",
}: ModalConfirmProps) {
const [isOpen, setIsOpen] = useState(true);
return (
<div className="min-h-[400px] flex items-center justify-center px-6" style={{ background: "var(--color-background)" }}>
<button
onClick={() => setIsOpen(true)}
className="px-4 py-2 text-sm font-medium rounded-lg"
style={{ background: "var(--color-background-alt)", color: "var(--color-foreground)", border: "1px solid var(--color-border)" }}
>
Ouvrir la modale
</button>
<AnimatePresence>
{isOpen && (
<>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="fixed inset-0 z-40" style={{ background: "rgba(0,0,0,0.5)" }} onClick={() => setIsOpen(false)} />
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2, ease }}
className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 w-full max-w-md rounded-2xl p-6 shadow-2xl"
style={{ background: "var(--color-background)", border: "1px solid var(--color-border)" }}
>
<div className="flex items-start gap-4">
<div className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0" style={{ background: "var(--color-background-alt)" }}>
<AlertTriangle size={20} style={{ color: variant === "danger" ? "var(--color-accent)" : "var(--color-foreground-muted)" }} />
</div>
<div className="flex-1">
<div className="flex items-center justify-between">
<h3 className="text-base font-semibold" style={{ color: "var(--color-foreground)" }}>{title}</h3>
<button onClick={() => setIsOpen(false)}><X size={16} style={{ color: "var(--color-foreground-light)" }} /></button>
</div>
<p className="mt-2 text-sm" style={{ color: "var(--color-foreground-muted)" }}>{message}</p>
</div>
</div>
<div className="flex items-center justify-end gap-3 mt-6">
<button onClick={() => setIsOpen(false)} className="px-4 py-2 text-sm font-medium rounded-lg transition-colors" style={{ color: "var(--color-foreground-muted)", background: "var(--color-background-alt)" }}>
{cancelLabel}
</button>
<motion.button whileTap={{ scale: 0.97 }} onClick={() => setIsOpen(false)} className="px-4 py-2 text-sm font-medium rounded-lg" style={{ background: "var(--color-accent)", color: "var(--color-background)" }}>
{confirmLabel}
</motion.button>
</div>
</motion.div>
</>
)}
</AnimatePresence>
</div>
);
}