mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
- Enhanced EventManager class to support priority-based event handling, conditional execution, and automatic cleanup. - Integrated event management into BulkManager for global keyboard shortcuts and marquee selection events. - Migrated mouse tracking and node selector events to UIHelpers for better coordination. - Established global event handlers for context menu interactions and modal state management. - Added comprehensive documentation for event management implementation and usage. - Implemented initialization logic for event management, including error handling and cleanup on page unload.
7.5 KiB
7.5 KiB
Centralized Event Management System
This document describes the centralized event management system that coordinates event handling across the ComfyUI LoRA Manager application.
Overview
The EventManager class provides a centralized way to handle DOM events with priority-based execution, conditional execution based on application state, and proper cleanup mechanisms.
Features
- Priority-based execution: Handlers with higher priority run first
- Conditional execution: Handlers can be executed based on application state
- Element filtering: Handlers can target specific elements or exclude others
- Automatic cleanup: Cleanup functions are called when handlers are removed
- State tracking: Tracks application states like bulk mode, modal open, etc.
Basic Usage
Importing
import { eventManager } from './EventManager.js';
Adding Event Handlers
eventManager.addHandler('click', 'myComponent', (event) => {
console.log('Button clicked!');
return true; // Stop propagation to other handlers
}, {
priority: 100,
targetSelector: '.my-button',
skipWhenModalOpen: true
});
Removing Event Handlers
// Remove specific handler
eventManager.removeHandler('click', 'myComponent');
// Remove all handlers for a component
eventManager.removeAllHandlersForSource('myComponent');
Updating Application State
// Set state
eventManager.setState('bulkMode', true);
eventManager.setState('modalOpen', true);
// Get state
const isBulkMode = eventManager.getState('bulkMode');
Available States
bulkMode: Whether bulk selection mode is activemarqueeActive: Whether marquee selection is in progressmodalOpen: Whether any modal is currently opennodeSelectorActive: Whether the node selector popup is active
Handler Options
Priority
Higher numbers = higher priority. Handlers run in descending priority order.
{
priority: 100 // High priority
}
Conditional Execution
{
onlyInBulkMode: true, // Only run when bulk mode is active
onlyWhenMarqueeActive: true, // Only run when marquee selection is active
skipWhenModalOpen: true, // Skip when any modal is open
skipWhenNodeSelectorActive: true, // Skip when node selector is active
onlyWhenNodeSelectorActive: true // Only run when node selector is active
}
Element Filtering
{
targetSelector: '.model-card', // Only handle events on matching elements
excludeSelector: 'button, input', // Exclude events from these elements
button: 0 // Only handle specific mouse button (0=left, 1=middle, 2=right)
}
Cleanup Functions
{
cleanup: () => {
// Custom cleanup logic
console.log('Handler cleaned up');
}
}
Integration Examples
BulkManager Integration
class BulkManager {
registerEventHandlers() {
// High priority keyboard shortcuts
eventManager.addHandler('keydown', 'bulkManager-keyboard', (e) => {
return this.handleGlobalKeyboard(e);
}, {
priority: 100,
skipWhenModalOpen: true
});
// Marquee selection
eventManager.addHandler('mousedown', 'bulkManager-marquee-start', (e) => {
return this.handleMarqueeStart(e);
}, {
priority: 80,
skipWhenModalOpen: true,
targetSelector: '.models-container',
excludeSelector: '.model-card, button, input',
button: 0
});
}
cleanup() {
eventManager.removeAllHandlersForSource('bulkManager-keyboard');
eventManager.removeAllHandlersForSource('bulkManager-marquee-start');
}
}
Modal Integration
class ModalManager {
showModal(modalId) {
// Update state when modal opens
eventManager.setState('modalOpen', true);
this.displayModal(modalId);
}
closeModal(modalId) {
// Update state when modal closes
eventManager.setState('modalOpen', false);
this.hideModal(modalId);
}
}
Component Event Delegation
export function setupComponentEvents() {
eventManager.addHandler('click', 'myComponent-actions', (event) => {
const button = event.target.closest('.action-button');
if (!button) return false;
this.handleAction(button.dataset.action);
return true; // Stop propagation
}, {
priority: 60,
targetSelector: '.component-container'
});
}
Best Practices
1. Use Descriptive Source Names
Use the format componentName-purposeDescription:
// Good
'bulkManager-marqueeSelection'
'nodeSelector-clickOutside'
'modelCard-delegation'
// Avoid
'bulk'
'click'
'handler1'
2. Set Appropriate Priorities
- 200+: Critical system events (escape keys, critical modals)
- 100-199: High priority application events (keyboard shortcuts)
- 50-99: Normal UI interactions (buttons, cards)
- 1-49: Low priority events (tracking, analytics)
3. Use Conditional Execution
Instead of checking state inside handlers, use options:
// Good
eventManager.addHandler('click', 'bulk-action', handler, {
onlyInBulkMode: true
});
// Avoid
eventManager.addHandler('click', 'bulk-action', (e) => {
if (!state.bulkMode) return;
// handler logic
});
4. Clean Up Properly
Always clean up handlers when components are destroyed:
class MyComponent {
constructor() {
this.registerEvents();
}
destroy() {
eventManager.removeAllHandlersForSource('myComponent');
}
}
5. Return Values Matter
- Return
trueto stop event propagation to other handlers - Return
falseorundefinedto continue with other handlers
Migration Guide
From Direct Event Listeners
Before:
document.addEventListener('click', (e) => {
if (e.target.closest('.my-button')) {
this.handleClick(e);
}
});
After:
eventManager.addHandler('click', 'myComponent-button', (e) => {
this.handleClick(e);
}, {
targetSelector: '.my-button'
});
From Event Delegation
Before:
container.addEventListener('click', (e) => {
const card = e.target.closest('.model-card');
if (!card) return;
if (e.target.closest('.action-btn')) {
this.handleAction(e);
}
});
After:
eventManager.addHandler('click', 'container-actions', (e) => {
const card = e.target.closest('.model-card');
if (!card) return false;
if (e.target.closest('.action-btn')) {
this.handleAction(e);
return true;
}
}, {
targetSelector: '.container'
});
Performance Benefits
- Reduced DOM listeners: Single listener per event type instead of multiple
- Conditional execution: Handlers only run when conditions are met
- Priority ordering: Important handlers run first, avoiding unnecessary work
- Automatic cleanup: Prevents memory leaks from orphaned listeners
- Centralized debugging: All event handling flows through one system
Debugging
Enable debug logging to trace event handling:
// Add to EventManager.js for debugging
console.log(`Handling ${eventType} event with ${handlers.length} handlers`);
The event manager provides a foundation for coordinated, efficient event handling across the entire application.