- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
128 lines
3.9 KiB
JavaScript
128 lines
3.9 KiB
JavaScript
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 = `
|
||
<form method="dialog" class="${this.className}-content">
|
||
${content}
|
||
<button class="${this.className}-close" value="close">×</button>
|
||
</form>
|
||
`;
|
||
|
||
// 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();
|
||
}
|
||
}
|
||
}
|