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

@ -283,80 +283,49 @@ export class SwpAllDayEventElement extends BaseEventElement {
}
/**
* Factory method to create from CalendarEvent and target date
* Factory method to create from CalendarEvent and layout (provided by AllDayManager)
*/
public static fromCalendarEvent(event: CalendarEvent, targetDate?: string): SwpAllDayEventElement {
// Calculate column span based on event start and end dates
const { startColumn, endColumn, columnSpan } = this.calculateColumnSpan(event);
public static fromCalendarEventWithLayout(
event: CalendarEvent,
layout: { startColumn: number; endColumn: number; row: number; columnSpan: number }
): SwpAllDayEventElement {
// Create element with provided layout
const element = new SwpAllDayEventElement(event, layout.startColumn);
// For backwards compatibility, use targetDate if provided, otherwise use calculated start column
const finalStartColumn = targetDate ? this.getColumnIndexForDate(targetDate) : startColumn;
const finalEndColumn = targetDate ? finalStartColumn : endColumn;
const finalColumnSpan = targetDate ? 1 : columnSpan;
// Find occupied rows in the spanned columns using computedStyle
const existingEvents = document.querySelectorAll('swp-allday-container swp-event');
const occupiedRows = new Set<number>();
// Set complete grid-area instead of individual properties
const gridArea = `${layout.row} / ${layout.startColumn} / ${layout.row + 1} / ${layout.endColumn + 1}`;
element.element.style.gridArea = gridArea;
console.log('🔍 SwpAllDayEventElement: Checking grid row for new event', {
targetDate,
finalStartColumn,
finalEndColumn,
existingEventsCount: existingEvents.length
});
existingEvents.forEach(existingEvent => {
const style = getComputedStyle(existingEvent);
const eventStartCol = parseInt(style.gridColumnStart);
const eventEndCol = parseInt(style.gridColumnEnd);
const eventRow = parseInt(style.gridRowStart) || 1;
const eventId = (existingEvent as HTMLElement).dataset.eventId;
console.log('📊 SwpAllDayEventElement: Checking existing event', {
eventId,
eventStartCol,
eventEndCol,
eventRow,
newEventColumn: finalStartColumn
});
// FIXED: Only check events in the same column (not overlap detection)
if (eventStartCol === finalStartColumn) {
console.log('✅ SwpAllDayEventElement: Same column - adding occupied row', eventRow);
occupiedRows.add(eventRow);
} else {
console.log('⏭️ SwpAllDayEventElement: Different column - skipping');
}
});
// Find first available row
let targetRow = 1;
while (occupiedRows.has(targetRow)) {
targetRow++;
}
console.log('🎯 SwpAllDayEventElement: Final row assignment', {
targetDate,
finalStartColumn,
occupiedRows: Array.from(occupiedRows).sort(),
assignedRow: targetRow
});
// Create element with calculated column span
const element = new SwpAllDayEventElement(event, finalStartColumn);
element.setGridRow(targetRow);
element.setColumnSpan(finalStartColumn, finalEndColumn);
console.log('✅ SwpAllDayEventElement: Created all-day event', {
console.log('✅ SwpAllDayEventElement: Created all-day event with AllDayLayoutEngine', {
eventId: event.id,
title: event.title,
column: finalStartColumn,
row: targetRow
gridArea: gridArea,
layout: layout
});
return element;
}
/**
* Factory method to create from CalendarEvent and target date (DEPRECATED - use AllDayManager.calculateAllDayEventLayout)
* @deprecated Use AllDayManager.calculateAllDayEventLayout() and fromCalendarEventWithLayout() instead
*/
public static fromCalendarEvent(event: CalendarEvent, targetDate?: string): SwpAllDayEventElement {
console.warn('⚠️ SwpAllDayEventElement.fromCalendarEvent is deprecated. Use AllDayManager.calculateAllDayEventLayout() instead.');
// Fallback to simple column calculation without overlap detection
const { startColumn, endColumn } = this.calculateColumnSpan(event);
const finalStartColumn = targetDate ? this.getColumnIndexForDate(targetDate) : startColumn;
const finalEndColumn = targetDate ? finalStartColumn : endColumn;
// Create element with row 1 (no overlap detection)
const element = new SwpAllDayEventElement(event, finalStartColumn);
element.setGridRow(1);
element.setColumnSpan(finalStartColumn, finalEndColumn);
return element;
}
/**
* Calculate column span based on event start and end dates
*/