import React, { useEffect, useRef } from 'react'; import { X, AlertCircle, Trash2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; interface ConfirmDialogProps { isOpen: boolean; onClose: () => void; onConfirm: () => void; title: string; description?: string | React.ReactNode; confirmText?: string; cancelText?: string; variant?: 'destructive' | 'default' | 'outline' | 'secondary' | 'ghost' | 'link'; isLoading?: boolean; type?: 'delete' | 'warning' | 'info'; } const ConfirmDialog: React.FC = ({ isOpen, onClose, onConfirm, title, description, confirmText = 'Confirm', cancelText = 'Cancel', variant = 'destructive', isLoading = false, type = 'warning' }) => { const dialogRef = useRef(null); const cancelButtonRef = useRef(null); const confirmButtonRef = useRef(null); // Focus management useEffect(() => { if (isOpen) { // Focus the cancel button by default for safety cancelButtonRef.current?.focus(); // Trap focus within the dialog const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && !isLoading) { onClose(); } if (e.key === 'Tab') { const focusableElements = dialogRef.current?.querySelectorAll( 'button:not([disabled]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); if (focusableElements && focusableElements.length > 0) { const firstElement = focusableElements[0] as HTMLElement; const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement; if (e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); } }, [isOpen, isLoading, onClose]); if (!isOpen) return null; const handleOverlayClick = (e: React.MouseEvent) => { // Only allow closing if not in loading state if (!isLoading && e.target === e.currentTarget) { onClose(); } }; const getIcon = () => { switch (type) { case 'delete': return ; case 'warning': case 'info': default: return ; } }; const getIconBgColor = () => { switch (type) { case 'delete': return 'bg-destructive/20 text-destructive'; case 'warning': return 'bg-yellow-500/20 text-yellow-600'; case 'info': return 'bg-blue-500/20 text-blue-600'; default: return 'bg-destructive/20 text-destructive'; } }; return (
{/* Overlay */} ); }; export default ConfirmDialog;