Refactors event rendering to be event-driven

Moves event rendering logic into a dedicated EventRenderer class that uses a strategy pattern for different calendar types.

The rendering is now triggered by `GRID_RENDERED` and `CONTAINER_READY_FOR_EVENTS` events, emitted by the GridManager and NavigationManager respectively.

This change decouples the CalendarManager from direct event rendering and allows for more flexible and efficient event updates. The EventManager now has a method to fetch events for a given time period.

Removes direct calls to event rendering from CalendarManager. Improves animation transitions by using pre-rendered containers in the NavigationManager.
This commit is contained in:
Janus Knudsen 2025-08-16 00:51:12 +02:00
parent afe5b6b899
commit a03f314c4a
9 changed files with 271 additions and 166 deletions

View file

@ -113,11 +113,11 @@ export class NavigationManager {
}
/**
* POC-style animation transition - creates new grid container and slides it in
* Animation transition using pre-rendered containers when available
*/
private animateTransition(direction: 'prev' | 'next', targetWeek: Date): void {
const container = document.querySelector('swp-calendar-container');
const currentGrid = container?.querySelector('swp-grid-container');
const currentGrid = container?.querySelector('swp-grid-container:not([data-prerendered])');
if (!container || !currentGrid) {
console.warn('NavigationManager: Required DOM elements not found');
@ -126,31 +126,15 @@ export class NavigationManager {
console.log(`NavigationManager: Starting ${direction} animation to ${targetWeek.toDateString()}`);
// Create new grid container (POC approach)
const newGrid = document.createElement('swp-grid-container');
newGrid.innerHTML = `
<swp-calendar-header></swp-calendar-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>
`;
let newGrid: HTMLElement;
// Position new grid off-screen (POC positioning)
newGrid.style.position = 'absolute';
newGrid.style.top = '0';
newGrid.style.left = '0';
newGrid.style.width = '100%';
newGrid.style.height = '100%';
newGrid.style.transform = direction === 'next' ? 'translateX(100%)' : 'translateX(-100%)';
// Always create a fresh container for consistent behavior
console.log('NavigationManager: Creating new container');
newGrid = this.renderContainer(container as HTMLElement, targetWeek);
// Add to container
container.appendChild(newGrid);
// Render new content for target week
this.renderWeekContent(newGrid, targetWeek);
// Clear any existing transforms before animation
newGrid.style.transform = '';
(currentGrid as HTMLElement).style.transform = '';
// Animate transition using Web Animations API
const slideOutAnimation = (currentGrid as HTMLElement).animate([
@ -181,6 +165,7 @@ export class NavigationManager {
// Reset positioning
newGrid.style.position = 'relative';
newGrid.removeAttribute('data-prerendered');
// Update state
this.currentWeek = new Date(targetWeek);
@ -198,6 +183,7 @@ export class NavigationManager {
weekEnd: DateUtils.addDays(this.currentWeek, 6)
});
// Emit animation complete event for ScrollManager
this.eventBus.emit(EventTypes.NAVIGATION_ANIMATION_COMPLETE, {
direction,
@ -335,4 +321,97 @@ export class NavigationManager {
weekEnd: DateUtils.addDays(this.currentWeek, 6)
});
}
/**
* Render a complete container with content and events
*/
private renderContainer(parentContainer: HTMLElement, weekStart: Date): HTMLElement {
console.log('NavigationManager: Rendering new container for week:', weekStart.toDateString());
// Create new grid container
const newGrid = document.createElement('swp-grid-container');
newGrid.innerHTML = `
<swp-calendar-header></swp-calendar-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 grid - NO transform here, let Animation API handle it
newGrid.style.position = 'absolute';
newGrid.style.top = '0';
newGrid.style.left = '0';
newGrid.style.width = '100%';
newGrid.style.height = '100%';
// Add to parent container
parentContainer.appendChild(newGrid);
// Render week content (headers and columns)
this.renderWeekContentInContainer(newGrid, weekStart);
// Emit event to trigger event rendering
const weekEnd = DateUtils.addDays(weekStart, 6);
this.eventBus.emit(EventTypes.CONTAINER_READY_FOR_EVENTS, {
container: newGrid,
startDate: weekStart,
endDate: weekEnd
});
console.log('NavigationManager: Container rendered with content and events triggered');
return newGrid;
}
/**
* Render week content in specific container
*/
private renderWeekContentInContainer(gridContainer: HTMLElement, weekStart: Date): void {
const header = gridContainer.querySelector('swp-calendar-header');
const dayColumns = gridContainer.querySelector('swp-day-columns');
if (!header || !dayColumns) return;
// Clear existing content
header.innerHTML = '';
dayColumns.innerHTML = '';
// Render headers for target week
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
for (let i = 0; i < 7; i++) {
const date = new Date(weekStart);
date.setDate(date.getDate() + i);
const headerElement = document.createElement('swp-day-header');
if (this.isToday(date)) {
headerElement.dataset.today = 'true';
}
headerElement.innerHTML = `
<swp-day-name>${days[date.getDay()]}</swp-day-name>
<swp-day-date>${date.getDate()}</swp-day-date>
`;
headerElement.dataset.date = this.formatDate(date);
header.appendChild(headerElement);
}
// Render day columns for target week (with hardcoded test event)
for (let i = 0; i < 7; i++) {
const column = document.createElement('swp-day-column');
const date = new Date(weekStart);
date.setDate(date.getDate() + i);
column.dataset.date = this.formatDate(date);
const eventsLayer = document.createElement('swp-events-layer');
column.appendChild(eventsLayer);
dayColumns.appendChild(column);
}
}
}