import { CalendarView, IEventBus } from '../types/CalendarTypes'; import { CoreEvents } from '../constants/CoreEvents'; import { Configuration } from '../configurations/CalendarConfig'; /** * ViewSelectorManager - Manages view selector UI and state * * RESPONSIBILITY: * =============== * This manager owns all logic related to the UI element. * It follows the principle that each functional UI element has its own manager. * * RESPONSIBILITIES: * - Handles button clicks on swp-view-button elements * - Manages current view state (day/week/month) * - Validates view values * - Emits VIEW_CHANGED and VIEW_RENDERED events * - Updates button UI states (data-active attributes) * * EVENT FLOW: * =========== * User clicks button → changeView() → validate → update state → emit event → update UI * * IMPLEMENTATION STATUS: * ====================== * - Week view: FULLY IMPLEMENTED * - Day view: NOT IMPLEMENTED (button exists but no rendering) * - Month view: NOT IMPLEMENTED (button exists but no rendering) * * SUBSCRIBERS: * ============ * - GridRenderer: Uses view parameter (currently only supports 'week') * - Future: DayRenderer, MonthRenderer when implemented */ export class ViewSelectorManager { private eventBus: IEventBus; private config: Configuration; private buttonListeners: Map = new Map(); constructor(eventBus: IEventBus, config: Configuration) { this.eventBus = eventBus; this.config = config; this.setupButtonListeners(); this.setupEventListeners(); } /** * Setup click listeners on all view selector buttons */ private setupButtonListeners(): void { const buttons = document.querySelectorAll('swp-view-button[data-view]'); buttons.forEach(button => { const clickHandler = (event: Event) => { event.preventDefault(); const view = button.getAttribute('data-view'); if (view && this.isValidView(view)) { this.changeView(view as CalendarView); } }; button.addEventListener('click', clickHandler); this.buttonListeners.set(button, clickHandler); }); // Initialize button states this.updateButtonStates(); } /** * Setup event bus listeners */ private setupEventListeners(): void { this.eventBus.on(CoreEvents.INITIALIZED, () => { this.initializeView(); }); this.eventBus.on(CoreEvents.DATE_CHANGED, () => { this.refreshCurrentView(); }); } /** * Change the active view */ private changeView(newView: CalendarView): void { if (newView === this.config.currentView) { return; // No change } const previousView = this.config.currentView; this.config.currentView = newView; // Update button UI states this.updateButtonStates(); // Emit event for subscribers this.eventBus.emit(CoreEvents.VIEW_CHANGED, { previousView, currentView: newView }); } /** * Update button states (data-active attributes) */ private updateButtonStates(): void { const buttons = document.querySelectorAll('swp-view-button[data-view]'); buttons.forEach(button => { const buttonView = button.getAttribute('data-view'); if (buttonView === this.config.currentView) { button.setAttribute('data-active', 'true'); } else { button.removeAttribute('data-active'); } }); } /** * Initialize view on INITIALIZED event */ private initializeView(): void { this.updateButtonStates(); this.emitViewRendered(); } /** * Emit VIEW_RENDERED event */ private emitViewRendered(): void { this.eventBus.emit(CoreEvents.VIEW_RENDERED, { view: this.config.currentView }); } /** * Refresh current view on DATE_CHANGED event */ private refreshCurrentView(): void { this.emitViewRendered(); } /** * Validate if string is a valid CalendarView type */ private isValidView(view: string): view is CalendarView { return ['day', 'week', 'month'].includes(view); } }