import {useEvent} from "../../../core/useEvent"; export class Dialog { constructor({content = '', className = '', onClose = null} = {}) { this.onClose = onClose; this.className = className; this.isOpenState = false; this.dialog = document.createElement('dialog'); this.dialog.className = className; this.eventCleanup = []; // Track cleanup functions this.updateContent(content); this.bindEvents(); } /** * Bind event handlers to the dialog */ bindEvents() { // Clean up any existing event listeners first this.cleanupEvents(); // Store references to bound event handlers for cleanup this.clickHandler = (e) => { const isOutside = !e.target.closest('.' + this.className + '-content'); if (isOutside) this.close(); }; this.cancelHandler = (e) => { e.preventDefault(); this.close(); }; // Use direct event listeners instead of useEvent to avoid module-based cleanup issues this.dialog.addEventListener('click', this.clickHandler); this.dialog.addEventListener('cancel', this.cancelHandler); // Store cleanup functions this.eventCleanup = [ () => this.dialog.removeEventListener('click', this.clickHandler), () => this.dialog.removeEventListener('cancel', this.cancelHandler) ]; } /** * Clean up existing event listeners */ cleanupEvents() { this.eventCleanup.forEach(cleanup => cleanup()); this.eventCleanup = []; } /** * Update the content of the dialog without recreating it * @param {string} content - New HTML content */ updateContent(content) { this.dialog.innerHTML = `
`; // No need to rebind events - they're attached to the dialog element itself // which doesn't get replaced by innerHTML } open() { // Always ensure dialog is in DOM if (!this.dialog.parentElement) { document.body.appendChild(this.dialog); } // Force close if somehow already open if (this.dialog.hasAttribute('open') || this.dialog.open) { this.dialog.close?.() || this.dialog.removeAttribute('open'); } // Now open fresh this.dialog.showModal?.() || this.dialog.setAttribute('open', ''); document.documentElement.dataset[`${this.dialog.className}Open`] = 'true'; this.isOpenState = true; } close() { if (this.isOpenState) { this.dialog.close?.() || this.dialog.removeAttribute('open'); delete document.documentElement.dataset[`${this.dialog.className}Open`]; this.isOpenState = false; // Keep dialog in DOM for reuse, just hide it // Don't remove from DOM to enable singleton pattern this.onClose?.(); } } /** * Check if dialog is currently open (checks both state and DOM) * @returns {boolean} */ isOpen() { // Check both our internal state and the actual DOM state const domIsOpen = this.dialog && (this.dialog.hasAttribute('open') || this.dialog.open); // Sync our internal state with DOM reality if (domIsOpen !== this.isOpenState) { this.isOpenState = domIsOpen; } return this.isOpenState; } /** * Destroy the dialog completely (for cleanup) */ destroy() { if (this.isOpenState) { this.close(); } this.cleanupEvents(); if (this.dialog.parentElement) { this.dialog.remove(); } } }