Initial commit: Calendar Plantempus project setup with TypeScript, ASP.NET Core, and event-driven architecture
This commit is contained in:
commit
f06c02121c
38 changed files with 8233 additions and 0 deletions
239
src/managers/NavigationManager.ts
Normal file
239
src/managers/NavigationManager.ts
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
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)
|
||||
* and week transitions with smooth animations
|
||||
*/
|
||||
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 container = document.querySelector('swp-calendar-container');
|
||||
const currentWeekContainer = document.querySelector('swp-week-container');
|
||||
|
||||
if (!container || !currentWeekContainer) {
|
||||
console.warn('NavigationManager: Required DOM elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new week container
|
||||
const newWeekContainer = document.createElement('swp-week-container');
|
||||
newWeekContainer.innerHTML = `
|
||||
<swp-week-header></swp-week-header>
|
||||
<swp-scrollable-content>
|
||||
<swp-time-grid>
|
||||
<swp-grid-lines></swp-grid-lines>
|
||||
<swp-day-columns></swp-day-columns>
|
||||
</swp-time-grid>
|
||||
</swp-scrollable-content>
|
||||
`;
|
||||
|
||||
// Position new week off-screen
|
||||
newWeekContainer.style.position = 'absolute';
|
||||
newWeekContainer.style.top = '0';
|
||||
newWeekContainer.style.left = '0';
|
||||
newWeekContainer.style.width = '100%';
|
||||
newWeekContainer.style.height = '100%';
|
||||
newWeekContainer.style.transform = direction === 'next' ? 'translateX(100%)' : 'translateX(-100%)';
|
||||
|
||||
// Add to container
|
||||
container.appendChild(newWeekContainer);
|
||||
|
||||
// Notify other managers to render content for the new week
|
||||
this.eventBus.emit(EventTypes.WEEK_CONTAINER_CREATED, {
|
||||
container: newWeekContainer,
|
||||
weekStart: targetWeek
|
||||
});
|
||||
|
||||
// Animate transition
|
||||
requestAnimationFrame(() => {
|
||||
// Slide out current week
|
||||
(currentWeekContainer as HTMLElement).style.transform = direction === 'next' ? 'translateX(-100%)' : 'translateX(100%)';
|
||||
(currentWeekContainer as HTMLElement).style.opacity = '0.5';
|
||||
|
||||
// Slide in new week
|
||||
newWeekContainer.style.transform = 'translateX(0)';
|
||||
|
||||
// Clean up after animation
|
||||
setTimeout(() => {
|
||||
currentWeekContainer.remove();
|
||||
newWeekContainer.style.position = 'relative';
|
||||
|
||||
// Update currentWeek only after animation is complete
|
||||
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)
|
||||
});
|
||||
|
||||
}, 400); // Match CSS transition duration
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue