From a666a632bd42f6dd58c48a08e8f8d3ef33e37c3f Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Thu, 13 Nov 2025 20:12:05 +0100 Subject: [PATCH] Refactors navigation buttons with comprehensive logic Enhances navigation buttons management with advanced date calculation and view-specific navigation Separates navigation logic from GridManager into dedicated NavigationButtons component Adds support for multiple calendar views (week, month, day) Implements robust event-driven navigation mechanism Improves date navigation with dynamic period label generation --- src/components/NavigationButtons.ts | 148 +++++++++++++++++++++++++--- src/managers/GridManager.ts | 87 ++-------------- 2 files changed, 141 insertions(+), 94 deletions(-) diff --git a/src/components/NavigationButtons.ts b/src/components/NavigationButtons.ts index 9901000..2fe0817 100644 --- a/src/components/NavigationButtons.ts +++ b/src/components/NavigationButtons.ts @@ -1,35 +1,56 @@ -import { IEventBus } from '../types/CalendarTypes'; +import { IEventBus, CalendarView } from '../types/CalendarTypes'; import { CoreEvents } from '../constants/CoreEvents'; +import { DateService } from '../utils/DateService'; +import { Configuration } from '../configurations/CalendarConfig'; /** - * NavigationButtonsManager - Manages navigation button UI and state + * NavigationButtons - Manages navigation button UI and navigation logic * * RESPONSIBILITY: * =============== - * This manager owns all logic related to the UI element. - * It follows the principle that each functional UI element has its own manager. + * 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) - * - Emits NAV_BUTTON_CLICKED events + * - Calculates next/previous dates based on current view + * - Emits NAVIGATION_COMPLETED events with new date * - Manages button UI listeners * * EVENT FLOW: * =========== - * User clicks button → validateAction() → emit event → NavigationManager handles navigation - * - * SUBSCRIBERS: - * ============ - * - NavigationManager: Performs actual navigation logic (animations, grid updates, week calculations) + * 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) { + 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; + }); } /** @@ -56,12 +77,111 @@ export class NavigationButtons { * Handle navigation action */ private handleNavigation(action: string): void { - // Emit navigation button clicked event - this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, { - action: action + 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 */ diff --git a/src/managers/GridManager.ts b/src/managers/GridManager.ts index 1185302..db01dad 100644 --- a/src/managers/GridManager.ts +++ b/src/managers/GridManager.ts @@ -61,6 +61,13 @@ export class GridManager { this.render(); }); + // Listen for navigation events from NavigationButtons + eventBus.on(CoreEvents.NAVIGATION_COMPLETED, (e: Event) => { + const detail = (e as CustomEvent).detail; + this.currentDate = detail.newDate; + this.render(); + }); + // Listen for config changes that affect rendering eventBus.on(CoreEvents.REFRESH_REQUESTED, (e: Event) => { this.render(); @@ -105,86 +112,6 @@ export class GridManager { } - /** - * Get current period label - */ - public 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); - } - } - - /** - * Navigate to next period - */ - public navigateNext(): void { - let nextDate: Date; - - switch (this.currentView) { - case 'week': - nextDate = this.dateService.addWeeks(this.currentDate, 1); - break; - case 'month': - nextDate = this.dateService.addMonths(this.currentDate, 1); - break; - case 'day': - nextDate = this.dateService.addDays(this.currentDate, 1); - break; - default: - nextDate = this.dateService.addWeeks(this.currentDate, 1); - } - - this.currentDate = nextDate; - - eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { - direction: 'next', - newDate: nextDate, - periodLabel: this.getCurrentPeriodLabel() - }); - - this.render(); - } - - /** - * Navigate to previous period - */ - public navigatePrevious(): void { - let prevDate: Date; - - switch (this.currentView) { - case 'week': - prevDate = this.dateService.addWeeks(this.currentDate, -1); - break; - case 'month': - prevDate = this.dateService.addMonths(this.currentDate, -1); - break; - case 'day': - prevDate = this.dateService.addDays(this.currentDate, -1); - break; - default: - prevDate = this.dateService.addWeeks(this.currentDate, -1); - } - - this.currentDate = prevDate; - - eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { - direction: 'previous', - newDate: prevDate, - periodLabel: this.getCurrentPeriodLabel() - }); - - this.render(); - } /** * Get current view's display dates