import { EventBus } from '../core/EventBus'; import { CalendarView, IEventBus } from '../types/CalendarTypes'; import { calendarConfig } from '../core/CalendarConfig'; import { CoreEvents } from '../constants/CoreEvents'; /** * ViewManager - Optimized view switching with consolidated event handling * Reduces redundant DOM queries and event listener setups */ export class ViewManager { private eventBus: IEventBus; private currentView: CalendarView = 'week'; private eventCleanup: (() => void)[] = []; private buttonListeners: Map = new Map(); // Cached DOM elements for performance private cachedViewButtons: NodeListOf | null = null; private cachedWorkweekButtons: NodeListOf | null = null; private lastButtonCacheTime: number = 0; private readonly CACHE_DURATION = 5000; // 5 seconds constructor(eventBus: IEventBus) { this.eventBus = eventBus; this.setupEventListeners(); } /** * Consolidated event listener setup with better organization */ private setupEventListeners(): void { // Event bus listeners this.setupEventBusListeners(); // DOM button handlers with consolidated logic this.setupButtonHandlers(); } /** * Setup event bus listeners with proper cleanup tracking */ private setupEventBusListeners(): void { this.eventCleanup.push( this.eventBus.on(CoreEvents.INITIALIZED, () => { this.initializeView(); }) ); // Remove redundant VIEW_CHANGED listener that causes circular calls // changeView is called directly from button handlers this.eventCleanup.push( this.eventBus.on(CoreEvents.DATE_CHANGED, () => { this.refreshCurrentView(); }) ); } /** * Consolidated button handler setup with shared logic */ private setupButtonHandlers(): void { // Setup view buttons with consolidated handler this.setupButtonGroup('swp-view-button[data-view]', 'data-view', (value) => { if (this.isValidView(value)) { this.changeView(value as CalendarView); } }); // Setup workweek buttons with consolidated handler this.setupButtonGroup('swp-preset-button[data-workweek]', 'data-workweek', (value) => { this.changeWorkweek(value); }); } /** * Generic button group setup to eliminate duplicate code */ private setupButtonGroup(selector: string, attribute: string, handler: (value: string) => void): void { const buttons = document.querySelectorAll(selector); buttons.forEach(button => { const clickHandler = (event: Event) => { event.preventDefault(); const value = button.getAttribute(attribute); if (value) { handler(value); } }; button.addEventListener('click', clickHandler); this.buttonListeners.set(button, clickHandler); }); } /** * Get cached view buttons with cache invalidation */ private getViewButtons(): NodeListOf { const now = Date.now(); if (!this.cachedViewButtons || (now - this.lastButtonCacheTime) > this.CACHE_DURATION) { this.cachedViewButtons = document.querySelectorAll('swp-view-button[data-view]'); this.lastButtonCacheTime = now; } return this.cachedViewButtons; } /** * Get cached workweek buttons with cache invalidation */ private getWorkweekButtons(): NodeListOf { const now = Date.now(); if (!this.cachedWorkweekButtons || (now - this.lastButtonCacheTime) > this.CACHE_DURATION) { this.cachedWorkweekButtons = document.querySelectorAll('swp-preset-button[data-workweek]'); this.lastButtonCacheTime = now; } return this.cachedWorkweekButtons; } /** * Initialize view with consolidated button updates */ private initializeView(): void { this.updateAllButtons(); this.emitViewRendered(); } /** * Optimized view change with debouncing */ private changeView(newView: CalendarView): void { if (newView === this.currentView) return; const previousView = this.currentView; this.currentView = newView; this.updateAllButtons(); this.eventBus.emit(CoreEvents.VIEW_CHANGED, { previousView, currentView: newView }); } /** * Optimized workweek change with consolidated updates */ private changeWorkweek(workweekId: string): void { // Update the calendar config calendarConfig.setWorkWeek(workweekId); // Update button states using cached elements this.updateAllButtons(); // Trigger a calendar refresh to apply the new workweek this.eventBus.emit(CoreEvents.REFRESH_REQUESTED); } /** * Consolidated button update method to eliminate duplicate code */ private updateAllButtons(): void { this.updateButtonGroup( this.getViewButtons(), 'data-view', this.currentView ); this.updateButtonGroup( this.getWorkweekButtons(), 'data-workweek', calendarConfig.getCurrentWorkWeek() ); } /** * Generic button group update to eliminate duplicate logic */ private updateButtonGroup(buttons: NodeListOf, attribute: string, activeValue: string): void { buttons.forEach(button => { const buttonValue = button.getAttribute(attribute); if (buttonValue === activeValue) { button.setAttribute('data-active', 'true'); } else { button.removeAttribute('data-active'); } }); } /** * Emit view rendered event with current view */ private emitViewRendered(): void { this.eventBus.emit(CoreEvents.VIEW_RENDERED, { view: this.currentView }); } /** * Refresh current view by emitting view rendered event */ private refreshCurrentView(): void { this.emitViewRendered(); } /** * Validate if a string is a valid calendar view */ private isValidView(view: string): view is CalendarView { return ['day', 'week', 'month'].includes(view); } /** * Get current active view */ public getCurrentView(): CalendarView { return this.currentView; } /** * Public refresh method */ public refresh(): void { this.refreshCurrentView(); } /** * Clean up all resources and cached elements */ public destroy(): void { // Clean up event bus listeners this.eventCleanup.forEach(cleanup => cleanup()); this.eventCleanup = []; // Clean up button listeners this.buttonListeners.forEach((handler, button) => { button.removeEventListener('click', handler); }); this.buttonListeners.clear(); // Clear cached elements this.cachedViewButtons = null; this.cachedWorkweekButtons = null; this.lastButtonCacheTime = 0; } }