Calendar/src/managers/NavigationManager.ts

209 lines
6 KiB
TypeScript
Raw Normal View History

import { IEventBus } from '../types/CalendarTypes.js';
import { DateUtils } from '../utils/DateUtils.js';
import { EventTypes } from '../constants/EventTypes.js';
/**
* NavigationManager handles calendar navigation (prev/next/today buttons)
2025-07-25 23:31:25 +02:00
* with simplified CSS Grid approach
*/
export class NavigationManager {
private eventBus: IEventBus;
private currentWeek: Date;
private targetWeek: Date;
private animationQueue: number = 0;
constructor(eventBus: IEventBus) {
this.eventBus = eventBus;
this.currentWeek = DateUtils.getWeekStart(new Date(), 0); // Sunday start like POC
this.targetWeek = new Date(this.currentWeek);
this.init();
}
private init(): void {
this.setupEventListeners();
this.updateWeekInfo();
}
private setupEventListeners(): void {
// Listen for navigation button clicks
document.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
const navButton = target.closest('[data-action]') as HTMLElement;
if (!navButton) return;
const action = navButton.dataset.action;
switch (action) {
case 'prev':
this.navigateToPreviousWeek();
break;
case 'next':
this.navigateToNextWeek();
break;
case 'today':
this.navigateToToday();
break;
}
});
// Listen for external navigation requests
this.eventBus.on(EventTypes.NAVIGATE_TO_DATE, (event: Event) => {
const customEvent = event as CustomEvent;
const targetDate = new Date(customEvent.detail.date);
this.navigateToDate(targetDate);
});
}
private navigateToPreviousWeek(): void {
this.targetWeek.setDate(this.targetWeek.getDate() - 7);
const weekToShow = new Date(this.targetWeek);
this.animationQueue++;
this.animateTransition('prev', weekToShow);
}
private navigateToNextWeek(): void {
this.targetWeek.setDate(this.targetWeek.getDate() + 7);
const weekToShow = new Date(this.targetWeek);
this.animationQueue++;
this.animateTransition('next', weekToShow);
}
private navigateToToday(): void {
const today = new Date();
const todayWeekStart = DateUtils.getWeekStart(today, 0);
// Reset to today
this.targetWeek = new Date(todayWeekStart);
const currentTime = this.currentWeek.getTime();
const targetTime = todayWeekStart.getTime();
if (currentTime < targetTime) {
this.animationQueue++;
this.animateTransition('next', todayWeekStart);
} else if (currentTime > targetTime) {
this.animationQueue++;
this.animateTransition('prev', todayWeekStart);
}
}
private navigateToDate(date: Date): void {
const weekStart = DateUtils.getWeekStart(date, 0);
this.targetWeek = new Date(weekStart);
const currentTime = this.currentWeek.getTime();
const targetTime = weekStart.getTime();
if (currentTime < targetTime) {
this.animationQueue++;
this.animateTransition('next', weekStart);
} else if (currentTime > targetTime) {
this.animationQueue++;
this.animateTransition('prev', weekStart);
}
}
private animateTransition(direction: 'prev' | 'next', targetWeek: Date): void {
const calendarContainer = document.querySelector('swp-calendar-container');
2025-07-25 23:31:25 +02:00
if (!calendarContainer) {
console.warn('NavigationManager: Calendar container not found');
return;
}
2025-07-25 23:31:25 +02:00
// Add transition class for visual feedback
calendarContainer.classList.add('week-transition');
2025-07-25 23:31:25 +02:00
// Brief fade effect
setTimeout(() => {
calendarContainer.classList.add('week-transition-out');
2025-07-25 23:31:25 +02:00
// Update the week after fade starts
setTimeout(() => {
2025-07-25 23:31:25 +02:00
// Update currentWeek
this.currentWeek = new Date(targetWeek);
this.animationQueue--;
// If this was the last queued animation, ensure we're in sync
if (this.animationQueue === 0) {
this.currentWeek = new Date(this.targetWeek);
}
// Update week info and notify other managers
this.updateWeekInfo();
this.eventBus.emit(EventTypes.WEEK_CHANGED, {
weekStart: this.currentWeek,
weekEnd: DateUtils.addDays(this.currentWeek, 6)
});
2025-07-25 23:31:25 +02:00
// Remove transition classes
setTimeout(() => {
calendarContainer.classList.remove('week-transition', 'week-transition-out');
}, 150);
}, 150); // Half of transition duration
}, 50);
}
private updateWeekInfo(): void {
const weekNumber = DateUtils.getWeekNumber(this.currentWeek);
const weekEnd = DateUtils.addDays(this.currentWeek, 6);
const dateRange = DateUtils.formatDateRange(this.currentWeek, weekEnd);
// Update week info in DOM
const weekNumberElement = document.querySelector('swp-week-number');
const dateRangeElement = document.querySelector('swp-date-range');
if (weekNumberElement) {
weekNumberElement.textContent = `Week ${weekNumber}`;
}
if (dateRangeElement) {
dateRangeElement.textContent = dateRange;
}
// Notify other managers about week info update
this.eventBus.emit(EventTypes.WEEK_INFO_UPDATED, {
weekNumber,
dateRange,
weekStart: this.currentWeek,
weekEnd
});
}
/**
* Get current week start date
*/
getCurrentWeek(): Date {
return new Date(this.currentWeek);
}
/**
* Get target week (where navigation is heading)
*/
getTargetWeek(): Date {
return new Date(this.targetWeek);
}
/**
* Check if navigation animation is in progress
*/
isAnimating(): boolean {
return this.animationQueue > 0;
}
/**
* Force navigation to specific week without animation
*/
setWeek(weekStart: Date): void {
this.currentWeek = new Date(weekStart);
this.targetWeek = new Date(weekStart);
this.updateWeekInfo();
this.eventBus.emit(EventTypes.WEEK_CHANGED, {
weekStart: this.currentWeek,
weekEnd: DateUtils.addDays(this.currentWeek, 6)
});
}
}