Refactors event element handling with web components

Introduces web components for event elements, separating timed and all-day events into distinct components for better organization and reusability.

This change also simplifies event rendering and drag-and-drop operations by leveraging the properties and lifecycle methods of web components.
This commit is contained in:
Janus C. H. Knudsen 2025-10-04 15:35:09 +02:00
parent 1a47214831
commit a9d6d14c93
6 changed files with 253 additions and 274 deletions

View file

@ -76,10 +76,10 @@ export class AllDayEventRenderer {
const container = this.getContainer();
if (!container) return null;
let dayEvent = new SwpAllDayEventElement(event);
dayEvent.applyGridPositioning(layout);
const dayEvent = SwpAllDayEventElement.fromCalendarEvent(event);
dayEvent.applyGridPositioning(layout.row, layout.startColumn, layout.endColumn);
container.appendChild(dayEvent.getElement());
container.appendChild(dayEvent);
}

View file

@ -43,86 +43,6 @@ export class DateEventRenderer implements EventRendererStrategy {
}
/**
* Update clone timestamp based on new position
*/
private updateCloneTimestamp(payload: DragMoveEventPayload): void {
if (payload.draggedClone.dataset.allDay === "true" || !payload.columnBounds) return;
const gridSettings = calendarConfig.getGridSettings();
const { hourHeight, dayStartHour, snapInterval } = gridSettings;
if (!payload.draggedClone.dataset.originalDuration) {
throw new DOMException("missing clone.dataset.originalDuration");
}
// Calculate snapped start minutes
const minutesFromGridStart = (payload.snappedY / hourHeight) * 60;
const snappedStartMinutes = this.calculateSnappedMinutes(
minutesFromGridStart, dayStartHour, snapInterval
);
// Calculate end minutes
const originalDuration = parseInt(payload.draggedClone.dataset.originalDuration);
const endTotalMinutes = snappedStartMinutes + originalDuration;
// Update UI
this.updateTimeDisplay(payload.draggedClone, snappedStartMinutes, endTotalMinutes);
// Update data attributes
this.updateDateTimeAttributes(
payload.draggedClone,
new Date(payload.columnBounds.date),
snappedStartMinutes,
endTotalMinutes
);
}
/**
* Calculate snapped minutes from grid start
*/
private calculateSnappedMinutes(minutesFromGridStart: number, dayStartHour: number, snapInterval: number): number {
const actualStartMinutes = (dayStartHour * 60) + minutesFromGridStart;
return Math.round(actualStartMinutes / snapInterval) * snapInterval;
}
/**
* Update time display in the UI
*/
private updateTimeDisplay(element: HTMLElement, startMinutes: number, endMinutes: number): void {
const timeElement = element.querySelector('swp-event-time');
if (!timeElement) return;
const startTime = this.formatTimeFromMinutes(startMinutes);
const endTime = this.formatTimeFromMinutes(endMinutes);
timeElement.textContent = `${startTime} - ${endTime}`;
}
/**
* Update data-start and data-end attributes with ISO timestamps
*/
private updateDateTimeAttributes(element: HTMLElement, columnDate: Date, startMinutes: number, endMinutes: number): void {
const startDate = this.dateService.createDateAtTime(columnDate, startMinutes);
let endDate = this.dateService.createDateAtTime(columnDate, endMinutes);
// Handle cross-midnight events
if (endMinutes >= 1440) {
const extraDays = Math.floor(endMinutes / 1440);
endDate = this.dateService.addDays(endDate, extraDays);
}
// Convert to UTC before storing as ISO string
element.dataset.start = this.dateService.toUTC(startDate);
element.dataset.end = this.dateService.toUTC(endDate);
}
/**
* Format minutes since midnight to time string
*/
private formatTimeFromMinutes(totalMinutes: number): string {
return this.dateService.minutesToTime(totalMinutes);
}
/**
* Handle drag start event
@ -155,15 +75,12 @@ export class DateEventRenderer implements EventRendererStrategy {
* Handle drag move event
*/
public handleDragMove(payload: DragMoveEventPayload): void {
if (!this.draggedClone) return;
// Update position - snappedY is already the event top position
// Add +1px to match the initial positioning offset from SwpEventElement
this.draggedClone.style.top = (payload.snappedY + 1) + 'px';
// Update timestamp display
this.updateCloneTimestamp(payload);
if (!this.draggedClone || !payload.columnBounds) return;
// Delegate to SwpEventElement to update position and timestamps
const swpEvent = this.draggedClone as SwpEventElement;
const columnDate = new Date(payload.columnBounds.date);
swpEvent.updatePosition(columnDate, payload.snappedY);
}
/**
@ -191,16 +108,9 @@ export class DateEventRenderer implements EventRendererStrategy {
// Recalculate timestamps with new column date
const currentTop = parseFloat(this.draggedClone.style.top) || 0;
const mockPayload: DragMoveEventPayload = {
draggedElement: dragColumnChangeEvent.originalElement,
draggedClone: this.draggedClone,
mousePosition: dragColumnChangeEvent.mousePosition,
mouseOffset: { x: 0, y: 0 },
columnBounds: dragColumnChangeEvent.newColumn,
snappedY: currentTop
};
this.updateCloneTimestamp(mockPayload);
const swpEvent = this.draggedClone as SwpEventElement;
const columnDate = new Date(dragColumnChangeEvent.newColumn.date);
swpEvent.updatePosition(columnDate, currentTop);
}
}
@ -272,8 +182,7 @@ export class DateEventRenderer implements EventRendererStrategy {
}
private renderEvent(event: CalendarEvent): HTMLElement {
const swpEvent = SwpEventElement.fromCalendarEvent(event);
return swpEvent.getElement();
return SwpEventElement.fromCalendarEvent(event);
}
protected calculateEventPosition(event: CalendarEvent): { top: number; height: number } {

View file

@ -247,8 +247,7 @@ export class EventRenderingService {
// Use SwpEventElement factory to create day event from all-day event
const dayEventElement = SwpEventElement.fromAllDayElement(allDayClone as HTMLElement);
const dayElement = dayEventElement.getElement();
const dayElement = SwpEventElement.fromAllDayElement(allDayClone as HTMLElement);
// Remove the all-day clone - it's no longer needed since we're converting to day event
allDayClone.remove();