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 config: CalendarConfig; private currentView: CalendarView = 'week'; 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, config: CalendarConfig) { this.eventBus = eventBus; this.config = config; 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.eventBus.on(CoreEvents.INITIALIZED, () => { this.initializeView(); }); // Remove redundant VIEW_CHANGED listener that causes circular calls // changeView is called directly from button handlers 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 (does not emit events) this.config.setWorkWeek(workweekId); // Update button states using cached elements this.updateAllButtons(); // Emit workweek change event with full payload const settings = this.config.getWorkWeekSettings(); this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED, { workWeekId: workweekId, settings: settings }); } /** * 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', this.config.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(); } }