import Swal from "sweetalert2"; import ModernButton from '../ui/ModernButton'; /** * A custom SweetAlert2 wrapper that enforces a "Hold to Delete" confirmation. * Resolves with { isConfirmed: true } only if the user holds the button for the full duration. * * @param {Object} options * @param {string} options.title - The title of the modal (default: "Are you sure?") * @param {string} options.text - The warning text (default: "You won't be able to revert this!") * @param {string} options.holdText - The text inside the hold button (default: "Hold to Delete") * @param {number} options.holdTimeMs - How long the user must hold the button in milliseconds (default: 1750) * @returns {Promise<{isConfirmed: boolean, isDismissed?: boolean}>} */ export const confirmHoldToDelete = ({ title = "Are you sure?", text = "You won't be able to revert this!", holdText = "Hold to Delete", holdTimeMs = 1750, // 1.75 seconds default } = {}) => { return new Promise((resolve) => { let isHolding = false; let progress = 0; let animationFrame; let startTime; Swal.fire({ title: title, text: text, icon: "warning", showConfirmButton: false, showCancelButton: false, // We use custom HTML buttons for both html: `
${text ? `

${text}

` : ""}
Cancel
${holdText}
`, didOpen: () => { const btn = document.getElementById("hold-btn-container"); const progressBar = document.getElementById("hold-progress"); const textEl = document.getElementById("hold-text"); const cancelBtn = document.getElementById("cancel-btn"); // Custom cancel handler cancelBtn.addEventListener("click", () => { Swal.close(); }); const updateProgress = (timestamp) => { if (!isHolding) return; if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; progress = Math.min((elapsed / holdTimeMs) * 100, 100); progressBar.style.width = `${progress}%`; if (progress >= 100) { Swal.close(); resolve({ isConfirmed: true }); return; } animationFrame = requestAnimationFrame(updateProgress); }; const startHold = (e) => { // Prevent text selection or long-press context menus on mobile if (e.cancelable) e.preventDefault(); isHolding = true; startTime = null; textEl.style.color = "#ffffff"; animationFrame = requestAnimationFrame(updateProgress); }; const stopHold = () => { isHolding = false; progress = 0; progressBar.style.width = "0%"; textEl.style.color = "#991b1b"; if (animationFrame) cancelAnimationFrame(animationFrame); }; // Desktop events btn.addEventListener("mousedown", startHold); btn.addEventListener("mouseup", stopHold); btn.addEventListener("mouseleave", stopHold); // Mobile touch events btn.addEventListener("touchstart", startHold, { passive: false }); btn.addEventListener("touchend", stopHold); btn.addEventListener("touchcancel", stopHold); }, }).then((result) => { // If the modal was closed via cancel button, clicking outside, or Esc key if (result.isDismissed) { resolve({ isConfirmed: false, isDismissed: true }); } }); }); };