Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -1,23 +1,121 @@
|
||||
// modules/ui/UIManager.js
|
||||
import { Modal } from './components/Modal.js';
|
||||
import { Lightbox } from './components/Lightbox.js';
|
||||
import { Logger } from '../../core/logger.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} UIComponentProps
|
||||
* @property {string} [content] - HTML content for the component
|
||||
* @property {Function} [onClose] - Callback when component closes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Available UI components
|
||||
* @type {Object<string, Function>}
|
||||
*/
|
||||
const components = {
|
||||
modal: Modal,
|
||||
lightbox: Lightbox,
|
||||
};
|
||||
|
||||
/**
|
||||
* Active component instances (singletons)
|
||||
* @type {Object<string, Object>}
|
||||
*/
|
||||
const activeInstances = {};
|
||||
|
||||
/**
|
||||
* UI Manager for creating and managing UI components
|
||||
* @namespace UIManager
|
||||
*/
|
||||
export const UIManager = {
|
||||
/**
|
||||
* Open a UI component (reuses existing instance for singletons)
|
||||
* @param {string} type - Component type (e.g., 'modal', 'lightbox')
|
||||
* @param {UIComponentProps} [props={}] - Component properties
|
||||
* @returns {Object|null} Component instance or null if type unknown
|
||||
*/
|
||||
open(type, props = {}) {
|
||||
const Component = components[type];
|
||||
if (!Component) {
|
||||
console.warn(`[UIManager] Unknown type: ${type}`);
|
||||
Logger.warn(`[UIManager] Unknown type: ${type}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// For lightbox, reuse existing instance for efficiency
|
||||
if (type === 'lightbox') {
|
||||
if (activeInstances.lightbox) {
|
||||
Logger.info('[UIManager] Reusing existing lightbox instance');
|
||||
|
||||
// Update content and reopen
|
||||
activeInstances.lightbox.updateContent(props.content || '');
|
||||
|
||||
// Always call open() to ensure it's shown, regardless of current state
|
||||
activeInstances.lightbox.open();
|
||||
|
||||
return activeInstances.lightbox;
|
||||
} else {
|
||||
Logger.info('[UIManager] Creating new lightbox instance');
|
||||
|
||||
// Create new instance and store it
|
||||
const instance = new Component({
|
||||
...props,
|
||||
onClose: () => {
|
||||
// Don't delete the instance, just close it for reuse
|
||||
Logger.info('[UIManager] Lightbox closed, instance kept for reuse');
|
||||
if (props.onClose) props.onClose();
|
||||
}
|
||||
});
|
||||
activeInstances.lightbox = instance;
|
||||
instance.open();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
// For other components, create new instances
|
||||
const instance = new Component(props);
|
||||
instance.open();
|
||||
return instance;
|
||||
},
|
||||
|
||||
/**
|
||||
* Close a UI component instance
|
||||
* @param {Object} instance - Component instance to close
|
||||
*/
|
||||
close(instance) {
|
||||
if (instance?.close) instance.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Close a component by type
|
||||
* @param {string} type - Component type to close
|
||||
*/
|
||||
closeByType(type) {
|
||||
if (activeInstances[type]) {
|
||||
this.close(activeInstances[type]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a component is currently open
|
||||
* @param {string} type - Component type to check
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isOpen(type) {
|
||||
return activeInstances[type]?.isOpen() || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy all active instances (cleanup)
|
||||
*/
|
||||
destroyAll() {
|
||||
Object.values(activeInstances).forEach(instance => {
|
||||
if (instance?.destroy) {
|
||||
instance.destroy();
|
||||
}
|
||||
});
|
||||
Object.keys(activeInstances).forEach(key => {
|
||||
delete activeInstances[key];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,39 +3,125 @@ 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.dialog.innerHTML = `
|
||||
<form method="dialog" class="${className}-content">
|
||||
${content}
|
||||
<button class="${className}-close" value="close">×</button>
|
||||
</form>
|
||||
`;
|
||||
this.eventCleanup = []; // Track cleanup functions
|
||||
|
||||
this.updateContent(content);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
useEvent(this.dialog, 'click', (e) => {
|
||||
const isOutside = !e.target.closest(className+'-content');
|
||||
/**
|
||||
* 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();
|
||||
})
|
||||
};
|
||||
|
||||
useEvent(this.dialog, 'cancel', (e) => {
|
||||
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)
|
||||
];
|
||||
}
|
||||
|
||||
open()
|
||||
{
|
||||
document.body.appendChild(this.dialog);
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
this.dialog.close?.() || this.dialog.removeAttribute('open');
|
||||
this.dialog.remove();
|
||||
delete document.documentElement.dataset[`${this.dialog.className}Open`];
|
||||
this.onClose?.();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { UIManager } from './UIManager.js';
|
||||
import { Logger } from '../../core/logger.js';
|
||||
|
||||
export function init() {
|
||||
|
||||
/*UIManager.open('modal', {
|
||||
content: '<p>Hallo!</p><button class="modal-close">OK</button>',
|
||||
onClose: () => console.log('Modal wurde geschlossen')
|
||||
onClose: () => Logger.info('Modal wurde geschlossen')
|
||||
});*/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user