Improves all-day event layout calculation

Refactors all-day event rendering to use a layout engine
for overlap detection and positioning, ensuring events
are placed in available rows and columns.

Removes deprecated method and adds unit tests.
This commit is contained in:
Janus C. H. Knudsen 2025-09-25 23:38:17 +02:00
parent 274753936e
commit a624394ffb
11 changed files with 2898 additions and 145 deletions

View file

@ -4,6 +4,7 @@ import { SwpAllDayEventElement } from '../elements/SwpEventElement';
/**
* AllDayEventRenderer - Simple rendering of all-day events
* Handles adding and removing all-day events from the header container
* NOTE: Layout calculation is now handled by AllDayManager
*/
export class AllDayEventRenderer {
private container: HTMLElement | null = null;
@ -34,19 +35,23 @@ export class AllDayEventRenderer {
// REMOVED: createGhostColumns() method - no longer needed!
/**
* Render an all-day event using factory pattern
* Render an all-day event with pre-calculated layout
*/
public renderAllDayEvent(event: CalendarEvent, targetDate?: string): HTMLElement | null {
public renderAllDayEventWithLayout(
event: CalendarEvent,
layout: { startColumn: number; endColumn: number; row: number; columnSpan: number }
): HTMLElement | null {
const container = this.getContainer();
if (!container) return null;
const allDayElement = SwpAllDayEventElement.fromCalendarEvent(event, targetDate);
const allDayElement = SwpAllDayEventElement.fromCalendarEventWithLayout(event, layout);
const element = allDayElement.getElement();
container.appendChild(element);
return element;
}
/**
* Remove an all-day event by ID
*/

View file

@ -4,6 +4,7 @@ import { CoreEvents } from '../constants/CoreEvents';
import { calendarConfig } from '../core/CalendarConfig';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { EventManager } from '../managers/EventManager';
import { AllDayManager } from '../managers/AllDayManager';
import { EventRendererStrategy } from './EventRenderer';
import { SwpEventElement } from '../elements/SwpEventElement';
import { AllDayEventRenderer } from './AllDayEventRenderer';
@ -17,7 +18,7 @@ export class EventRenderingService {
private eventManager: EventManager;
private strategy: EventRendererStrategy;
private allDayEventRenderer: AllDayEventRenderer;
private allDayManager: AllDayManager;
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
@ -29,8 +30,9 @@ export class EventRenderingService {
const calendarType = calendarConfig.getCalendarMode();
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
// Initialize all-day event renderer
// Initialize all-day event renderer and manager
this.allDayEventRenderer = new AllDayEventRenderer();
this.allDayManager = new AllDayManager();
this.setupEventListeners();
}
@ -349,13 +351,35 @@ export class EventRenderingService {
// Clear existing all-day events first
this.clearAllDayEvents();
// Render each all-day event
// Get actual visible dates from DOM headers instead of generating them
const weekDates = this.getVisibleDatesFromDOM();
console.log('🔍 EventRenderingService: Using visible dates from DOM', {
weekDates,
count: weekDates.length
});
// Calculate layout for ALL all-day events together using AllDayLayoutEngine
const layouts = this.allDayManager.calculateAllDayEventsLayout(allDayEvents, weekDates);
// Render each all-day event with pre-calculated layout
allDayEvents.forEach(event => {
const renderedElement = this.allDayEventRenderer.renderAllDayEvent(event);
const layout = layouts.get(event.id);
if (!layout) {
console.warn('❌ EventRenderingService: No layout found for all-day event', {
id: event.id,
title: event.title
});
return;
}
// Render with pre-calculated layout
const renderedElement = this.allDayEventRenderer.renderAllDayEventWithLayout(event, layout);
if (renderedElement) {
console.log('✅ EventRenderingService: Rendered all-day event', {
console.log('✅ EventRenderingService: Rendered all-day event with AllDayLayoutEngine', {
id: event.id,
title: event.title,
gridArea: layout.gridArea,
element: renderedElement.tagName
});
} else {
@ -392,6 +416,26 @@ export class EventRenderingService {
this.clearEvents(container);
}
/**
* Get visible dates from DOM headers - only the dates that are actually displayed
*/
private getVisibleDatesFromDOM(): string[] {
const dayHeaders = document.querySelectorAll('swp-calendar-header swp-day-header');
const weekDates: string[] = [];
dayHeaders.forEach(header => {
const dateAttr = header.getAttribute('data-date');
if (dateAttr) {
weekDates.push(dateAttr);
}
});
return weekDates;
}
public destroy(): void {
this.clearEvents();
}