👁️
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 136 lines 4.0 kB view raw
1import { AlertTriangle } from "lucide-react"; 2import { useEffect, useId, useState } from "react"; 3 4interface DeleteDeckDialogProps { 5 deckName: string; 6 isOpen: boolean; 7 onClose: () => void; 8 onConfirm: () => void; 9 isDeleting?: boolean; 10} 11 12export function DeleteDeckDialog({ 13 deckName, 14 isOpen, 15 onClose, 16 onConfirm, 17 isDeleting = false, 18}: DeleteDeckDialogProps) { 19 const [confirmText, setConfirmText] = useState(""); 20 const titleId = useId(); 21 const inputId = useId(); 22 23 const isMatch = confirmText === deckName; 24 25 useEffect(() => { 26 if (!isOpen) { 27 setConfirmText(""); 28 } 29 }, [isOpen]); 30 31 useEffect(() => { 32 const handleKeyDown = (e: KeyboardEvent) => { 33 if (e.key === "Escape" && !isDeleting) { 34 onClose(); 35 } 36 }; 37 38 if (isOpen) { 39 document.addEventListener("keydown", handleKeyDown); 40 return () => document.removeEventListener("keydown", handleKeyDown); 41 } 42 }, [isOpen, isDeleting, onClose]); 43 44 if (!isOpen) return null; 45 46 const handleSubmit = (e: React.FormEvent) => { 47 e.preventDefault(); 48 if (isMatch && !isDeleting) { 49 onConfirm(); 50 } 51 }; 52 53 return ( 54 <> 55 {/* Backdrop */} 56 <div 57 className="fixed inset-0 bg-black/50 z-40" 58 onClick={isDeleting ? undefined : onClose} 59 aria-hidden="true" 60 /> 61 62 {/* Dialog */} 63 <div className="fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-none"> 64 <div 65 role="alertdialog" 66 aria-modal="true" 67 aria-labelledby={titleId} 68 className="bg-white dark:bg-zinc-900 rounded-lg shadow-2xl max-w-md w-full pointer-events-auto border border-gray-300 dark:border-zinc-600" 69 > 70 {/* Header */} 71 <div className="flex items-center gap-3 p-6 border-b border-gray-200 dark:border-zinc-700"> 72 <div className="p-2 bg-red-100 dark:bg-red-900/30 rounded-full"> 73 <AlertTriangle className="w-5 h-5 text-red-600 dark:text-red-400" /> 74 </div> 75 <h2 76 id={titleId} 77 className="text-xl font-bold text-gray-900 dark:text-white" 78 > 79 Delete deck 80 </h2> 81 </div> 82 83 {/* Body */} 84 <form onSubmit={handleSubmit} className="p-6 space-y-4"> 85 <p className="text-gray-600 dark:text-zinc-300"> 86 This action <strong>cannot</strong> be undone. This will 87 permanently delete the deck and all its cards. 88 </p> 89 90 <div> 91 <label 92 htmlFor={inputId} 93 className="block text-sm text-gray-700 dark:text-zinc-300 mb-2" 94 > 95 Please type{" "} 96 <span className="font-mono font-semibold text-gray-900 dark:text-white bg-gray-100 dark:bg-zinc-800 px-1.5 py-0.5 rounded"> 97 {deckName} 98 </span>{" "} 99 to confirm. 100 </label> 101 <input 102 id={inputId} 103 type="text" 104 value={confirmText} 105 onChange={(e) => setConfirmText(e.target.value)} 106 disabled={isDeleting} 107 autoComplete="off" 108 className="w-full px-4 py-2 bg-white dark:bg-zinc-800 border border-gray-300 dark:border-zinc-600 rounded-lg text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed" 109 placeholder="Deck name" 110 /> 111 </div> 112 113 {/* Footer */} 114 <div className="flex items-center justify-end gap-3 pt-2"> 115 <button 116 type="button" 117 onClick={onClose} 118 disabled={isDeleting} 119 className="px-4 py-2 bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700 text-gray-900 dark:text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" 120 > 121 Cancel 122 </button> 123 <button 124 type="submit" 125 disabled={!isMatch || isDeleting} 126 className="px-4 py-2 bg-red-600 hover:bg-red-700 disabled:bg-red-600/50 disabled:cursor-not-allowed text-white rounded-lg transition-colors" 127 > 128 {isDeleting ? "Deleting..." : "Delete this deck"} 129 </button> 130 </div> 131 </form> 132 </div> 133 </div> 134 </> 135 ); 136}