Refactors all-day event layout calculation

Simplifies all-day event rendering by streamlining the layout
calculation and event placement process, using the AllDayLayoutEngine
to determine the grid positions. This removes deprecated methods
and improves overall code clarity.
This commit is contained in:
Janus C. H. Knudsen 2025-09-27 15:01:22 +02:00
parent 9dfd4574d8
commit 4141bffca4
7 changed files with 76 additions and 321 deletions

View file

@ -2,6 +2,7 @@ import { CalendarEvent } from '../types/CalendarTypes';
import { calendarConfig } from '../core/CalendarConfig';
import { TimeFormatter } from '../utils/TimeFormatter';
import { PositionUtils } from '../utils/PositionUtils';
import { EventLayout } from '../utils/AllDayLayoutEngine';
/**
* Abstract base class for event DOM elements
@ -75,7 +76,7 @@ export class SwpEventElement extends BaseEventElement {
private createInnerStructure(): void {
const timeRange = TimeFormatter.formatTimeRange(this.event.start, this.event.end);
const durationMinutes = (this.event.end.getTime() - this.event.start.getTime()) / (1000 * 60);
this.element.innerHTML = `
<swp-event-time data-duration="${durationMinutes}">${timeRange}</swp-event-time>
<swp-event-title>${this.event.title}</swp-event-title>
@ -107,20 +108,20 @@ export class SwpEventElement extends BaseEventElement {
public createClone(): SwpEventElement {
// Clone the underlying DOM element
const clonedElement = this.element.cloneNode(true) as HTMLElement;
// Create new SwpEventElement instance from the cloned DOM
const clonedSwpEvent = SwpEventElement.fromExistingElement(clonedElement);
// Apply "clone-" prefix to ID
clonedSwpEvent.updateEventId(`clone-${this.event.id}`);
// Cache original duration for drag operations
const originalDuration = this.getOriginalEventDuration();
clonedSwpEvent.element.dataset.originalDuration = originalDuration.toString();
// Set height from original element
clonedSwpEvent.element.style.height = this.element.style.height || `${this.element.getBoundingClientRect().height}px`;
return clonedSwpEvent;
}
@ -130,11 +131,11 @@ export class SwpEventElement extends BaseEventElement {
public static fromExistingElement(element: HTMLElement): SwpEventElement {
// Extract CalendarEvent data from DOM element
const event = this.extractCalendarEventFromElement(element);
// Create new instance but replace the created element with the existing one
const swpEvent = new SwpEventElement(event);
swpEvent.element = element;
return swpEvent;
}
@ -202,7 +203,7 @@ export class SwpEventElement extends BaseEventElement {
const now = new Date();
const startDate = new Date(originalStart);
startDate.setHours(now.getHours() || 9, now.getMinutes() || 0, 0, 0);
const endDate = new Date(startDate);
endDate.setMinutes(endDate.getMinutes() + duration);
@ -228,14 +229,12 @@ export class SwpEventElement extends BaseEventElement {
* All-day event element (now using unified swp-event tag)
*/
export class SwpAllDayEventElement extends BaseEventElement {
private columnIndex: number;
private constructor(event: CalendarEvent, columnIndex: number) {
constructor(event: CalendarEvent) {
super(event);
this.columnIndex = columnIndex;
this.setAllDayAttributes();
this.createInnerStructure();
this.applyGridPositioning();
// this.applyGridPositioning();
}
protected createElement(): HTMLElement {
@ -264,128 +263,9 @@ export class SwpAllDayEventElement extends BaseEventElement {
/**
* Apply CSS grid positioning
*/
private applyGridPositioning(): void {
this.element.style.gridColumn = this.columnIndex.toString();
}
/**
* Set grid row for this all-day event
*/
public setGridRow(row: number): void {
this.element.style.gridRow = row.toString();
}
/**
* Set grid column span for this all-day event
*/
public setColumnSpan(startColumn: number, endColumn: number): void {
this.element.style.gridColumn = `${startColumn} / ${endColumn + 1}`;
}
/**
* Factory method to create from CalendarEvent and layout (provided by AllDayManager)
*/
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);
// Set complete grid-area instead of individual properties
public applyGridPositioning(layout: EventLayout): void {
const gridArea = `${layout.row} / ${layout.startColumn} / ${layout.row + 1} / ${layout.endColumn + 1}`;
element.element.style.gridArea = gridArea;
console.log('✅ SwpAllDayEventElement: Created all-day event with AllDayLayoutEngine', {
eventId: event.id,
title: event.title,
gridArea: gridArea,
layout: layout
});
return element;
this.element.style.gridArea = gridArea;
}
/**
* 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
*/
private static calculateColumnSpan(event: CalendarEvent): { startColumn: number; endColumn: number; columnSpan: number } {
const dayHeaders = document.querySelectorAll('swp-day-header');
// Extract dates from headers
const headerDates: string[] = [];
dayHeaders.forEach(header => {
const date = (header as HTMLElement).dataset.date;
if (date) {
headerDates.push(date);
}
});
// Format event dates for comparison (YYYY-MM-DD format)
const eventStartDate = event.start.toISOString().split('T')[0];
const eventEndDate = event.end.toISOString().split('T')[0];
// Find start and end column indices
let startColumn = 1;
let endColumn = headerDates.length;
headerDates.forEach((dateStr, index) => {
if (dateStr === eventStartDate) {
startColumn = index + 1;
}
if (dateStr === eventEndDate) {
endColumn = index + 1;
}
});
// Ensure end column is at least start column
if (endColumn < startColumn) {
endColumn = startColumn;
}
const columnSpan = endColumn - startColumn + 1;
return { startColumn, endColumn, columnSpan };
}
/**
* Get column index for a specific date
*/
private static getColumnIndexForDate(targetDate: string): number {
const dayHeaders = document.querySelectorAll('swp-day-header');
let columnIndex = 1;
dayHeaders.forEach((header, index) => {
if ((header as HTMLElement).dataset.date === targetDate) {
columnIndex = index + 1;
}
});
return columnIndex;
}
/**
* Check if two column ranges overlap
*/
private static columnsOverlap(startA: number, endA: number, startB: number, endB: number): boolean {
return !(endA < startB || endB < startA);
}
}