import { IEventBus, CalendarView } from '../types/CalendarTypes'; import { CoreEvents } from '../constants/CoreEvents'; import { DateService } from '../utils/DateService'; import { Configuration } from '../configurations/CalendarConfig'; /** * NavigationButtons - Manages navigation button UI and navigation logic * * RESPONSIBILITY: * =============== * This manager owns all logic related to the UI element * and performs the actual navigation calculations. * * RESPONSIBILITIES: * - Handles button clicks on swp-nav-button elements * - Validates navigation actions (prev, next, today) * - Calculates next/previous dates based on current view * - Emits NAVIGATION_COMPLETED events with new date * - Manages button UI listeners * * EVENT FLOW: * =========== * User clicks button → calculateNewDate() → emit NAVIGATION_COMPLETED → GridManager re-renders */ export class NavigationButtons { private eventBus: IEventBus; private buttonListeners: Map = new Map(); private dateService: DateService; private config: Configuration; private currentDate: Date = new Date(); private currentView: CalendarView = 'week'; constructor( eventBus: IEventBus, dateService: DateService, config: Configuration ) { this.eventBus = eventBus; this.dateService = dateService; this.config = config; this.setupButtonListeners(); this.subscribeToEvents(); } /** * Subscribe to events */ private subscribeToEvents(): void { // Listen for view changes this.eventBus.on(CoreEvents.VIEW_CHANGED, (e: Event) => { const detail = (e as CustomEvent).detail; this.currentView = detail.currentView; }); } /** * Setup click listeners on all navigation buttons */ private setupButtonListeners(): void { const buttons = document.querySelectorAll('swp-nav-button[data-action]'); buttons.forEach(button => { const clickHandler = (event: Event) => { event.preventDefault(); const action = button.getAttribute('data-action'); if (action && this.isValidAction(action)) { this.handleNavigation(action); } }; button.addEventListener('click', clickHandler); this.buttonListeners.set(button, clickHandler); }); } /** * Handle navigation action */ private handleNavigation(action: string): void { switch (action) { case 'prev': this.navigatePrevious(); break; case 'next': this.navigateNext(); break; case 'today': this.navigateToday(); break; } } /** * Navigate in specified direction */ private navigate(direction: 'next' | 'prev'): void { const offset = direction === 'next' ? 1 : -1; let newDate: Date; switch (this.currentView) { case 'week': newDate = this.dateService.addWeeks(this.currentDate, offset); break; case 'month': newDate = this.dateService.addMonths(this.currentDate, offset); break; case 'day': newDate = this.dateService.addDays(this.currentDate, offset); break; default: newDate = this.dateService.addWeeks(this.currentDate, offset); } this.currentDate = newDate; this.eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { direction: direction === 'next' ? 'next' : 'previous', newDate: newDate, periodLabel: this.getCurrentPeriodLabel() }); } /** * Navigate to next period */ private navigateNext(): void { this.navigate('next'); } /** * Navigate to previous period */ private navigatePrevious(): void { this.navigate('prev'); } /** * Navigate to today */ private navigateToday(): void { this.currentDate = new Date(); this.eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { direction: 'today', newDate: this.currentDate, periodLabel: this.getCurrentPeriodLabel() }); } /** * Get ISO week start (Monday) */ private getISOWeekStart(date: Date): Date { const weekBounds = this.dateService.getWeekBounds(date); return this.dateService.startOfDay(weekBounds.start); } /** * Get week end (Sunday) */ private getWeekEnd(date: Date): Date { const weekBounds = this.dateService.getWeekBounds(date); return this.dateService.endOfDay(weekBounds.end); } /** * Get current period label */ private getCurrentPeriodLabel(): string { switch (this.currentView) { case 'week': case 'day': const weekStart = this.getISOWeekStart(this.currentDate); const weekEnd = this.getWeekEnd(this.currentDate); return this.dateService.formatDateRange(weekStart, weekEnd); case 'month': return this.dateService.formatMonthYear(this.currentDate); default: const defaultWeekStart = this.getISOWeekStart(this.currentDate); const defaultWeekEnd = this.getWeekEnd(this.currentDate); return this.dateService.formatDateRange(defaultWeekStart, defaultWeekEnd); } } /** * Validate if string is a valid navigation action */ private isValidAction(action: string): boolean { return ['prev', 'next', 'today'].includes(action); } }