2025-07-24 22:17:38 +02:00
|
|
|
// Core EventBus using pure DOM CustomEvents
|
|
|
|
|
import { EventLogEntry, ListenerEntry, IEventBus } from '../types/CalendarTypes';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Central event dispatcher for calendar using DOM CustomEvents
|
|
|
|
|
* Provides logging and debugging capabilities
|
|
|
|
|
*/
|
|
|
|
|
export class EventBus implements IEventBus {
|
|
|
|
|
private eventLog: EventLogEntry[] = [];
|
|
|
|
|
private debug: boolean = false;
|
|
|
|
|
private listeners: Set<ListenerEntry> = new Set();
|
2025-07-29 00:52:01 +02:00
|
|
|
|
|
|
|
|
// Log configuration for different categories
|
|
|
|
|
private logConfig: { [key: string]: boolean } = {
|
|
|
|
|
calendar: true,
|
|
|
|
|
grid: true,
|
|
|
|
|
event: true,
|
|
|
|
|
scroll: true,
|
|
|
|
|
navigation: true,
|
|
|
|
|
view: true,
|
|
|
|
|
default: true
|
|
|
|
|
};
|
2025-07-24 22:17:38 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Subscribe to an event via DOM addEventListener
|
|
|
|
|
*/
|
|
|
|
|
on(eventType: string, handler: EventListener, options?: AddEventListenerOptions): () => void {
|
|
|
|
|
document.addEventListener(eventType, handler, options);
|
|
|
|
|
|
|
|
|
|
// Track for cleanup
|
|
|
|
|
this.listeners.add({ eventType, handler, options });
|
|
|
|
|
|
|
|
|
|
// Return unsubscribe function
|
|
|
|
|
return () => this.off(eventType, handler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Subscribe to an event once
|
|
|
|
|
*/
|
|
|
|
|
once(eventType: string, handler: EventListener): () => void {
|
|
|
|
|
return this.on(eventType, handler, { once: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unsubscribe from an event
|
|
|
|
|
*/
|
|
|
|
|
off(eventType: string, handler: EventListener): void {
|
|
|
|
|
document.removeEventListener(eventType, handler);
|
|
|
|
|
|
|
|
|
|
// Remove from tracking
|
|
|
|
|
for (const listener of this.listeners) {
|
|
|
|
|
if (listener.eventType === eventType && listener.handler === handler) {
|
|
|
|
|
this.listeners.delete(listener);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Emit an event via DOM CustomEvent
|
|
|
|
|
*/
|
2025-09-23 20:44:15 +02:00
|
|
|
emit(eventType: string, detail: unknown = {}): boolean {
|
2025-08-20 20:22:51 +02:00
|
|
|
// Validate eventType
|
2025-09-23 20:44:15 +02:00
|
|
|
if (!eventType) {
|
2025-08-20 20:22:51 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-24 22:17:38 +02:00
|
|
|
const event = new CustomEvent(eventType, {
|
2025-09-23 20:44:15 +02:00
|
|
|
detail: detail ?? {},
|
2025-07-24 22:17:38 +02:00
|
|
|
bubbles: true,
|
|
|
|
|
cancelable: true
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-29 00:52:01 +02:00
|
|
|
// Log event with grouping
|
2025-07-24 22:17:38 +02:00
|
|
|
if (this.debug) {
|
2025-07-29 00:52:01 +02:00
|
|
|
this.logEventWithGrouping(eventType, detail);
|
2025-07-24 22:17:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.eventLog.push({
|
|
|
|
|
type: eventType,
|
2025-09-23 20:44:15 +02:00
|
|
|
detail: detail ?? {},
|
2025-07-24 22:17:38 +02:00
|
|
|
timestamp: Date.now()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Emit on document (only DOM events now)
|
|
|
|
|
return !document.dispatchEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-29 00:52:01 +02:00
|
|
|
/**
|
|
|
|
|
* Log event with console grouping
|
|
|
|
|
*/
|
2025-09-23 20:44:15 +02:00
|
|
|
private logEventWithGrouping(eventType: string, detail: unknown): void {
|
2025-07-29 00:52:01 +02:00
|
|
|
// Extract category from event type (e.g., 'calendar:datechanged' → 'calendar')
|
|
|
|
|
const category = this.extractCategory(eventType);
|
|
|
|
|
|
|
|
|
|
// Only log if category is enabled
|
|
|
|
|
if (!this.logConfig[category]) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get category emoji and color
|
|
|
|
|
const { emoji, color } = this.getCategoryStyle(category);
|
|
|
|
|
|
|
|
|
|
// Use collapsed group to reduce visual noise
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract category from event type
|
|
|
|
|
*/
|
|
|
|
|
private extractCategory(eventType: string): string {
|
2025-09-23 20:44:15 +02:00
|
|
|
if (!eventType) {
|
2025-08-20 20:22:51 +02:00
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-29 00:52:01 +02:00
|
|
|
if (eventType.includes(':')) {
|
|
|
|
|
return eventType.split(':')[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fallback: try to detect category from event name patterns
|
|
|
|
|
const lowerType = eventType.toLowerCase();
|
|
|
|
|
if (lowerType.includes('grid') || lowerType.includes('rendered')) return 'grid';
|
|
|
|
|
if (lowerType.includes('event') || lowerType.includes('sync')) return 'event';
|
|
|
|
|
if (lowerType.includes('scroll')) return 'scroll';
|
|
|
|
|
if (lowerType.includes('nav') || lowerType.includes('date')) return 'navigation';
|
|
|
|
|
if (lowerType.includes('view')) return 'view';
|
|
|
|
|
|
|
|
|
|
return 'default';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get styling for different categories
|
|
|
|
|
*/
|
|
|
|
|
private getCategoryStyle(category: string): { emoji: string; color: string } {
|
|
|
|
|
const styles: { [key: string]: { emoji: string; color: string } } = {
|
|
|
|
|
calendar: { emoji: '🗓️', color: '#2196F3' },
|
|
|
|
|
grid: { emoji: '📊', color: '#4CAF50' },
|
|
|
|
|
event: { emoji: '📅', color: '#FF9800' },
|
|
|
|
|
scroll: { emoji: '📜', color: '#9C27B0' },
|
|
|
|
|
navigation: { emoji: '🧭', color: '#F44336' },
|
|
|
|
|
view: { emoji: '👁️', color: '#00BCD4' },
|
|
|
|
|
default: { emoji: '📢', color: '#607D8B' }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return styles[category] || styles.default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure logging for specific categories
|
|
|
|
|
*/
|
|
|
|
|
setLogConfig(config: { [key: string]: boolean }): void {
|
|
|
|
|
this.logConfig = { ...this.logConfig, ...config };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get current log configuration
|
|
|
|
|
*/
|
|
|
|
|
getLogConfig(): { [key: string]: boolean } {
|
|
|
|
|
return { ...this.logConfig };
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-24 22:17:38 +02:00
|
|
|
/**
|
|
|
|
|
* Get event history
|
|
|
|
|
*/
|
|
|
|
|
getEventLog(eventType?: string): EventLogEntry[] {
|
|
|
|
|
if (eventType) {
|
|
|
|
|
return this.eventLog.filter(e => e.type === eventType);
|
|
|
|
|
}
|
|
|
|
|
return this.eventLog;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enable/disable debug mode
|
|
|
|
|
*/
|
|
|
|
|
setDebug(enabled: boolean): void {
|
|
|
|
|
this.debug = enabled;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create singleton instance
|
|
|
|
|
export const eventBus = new EventBus();
|