Introduces event element classes

Creates `SwpEventElement` and `SwpAllDayEventElement` classes for handling event rendering.

Refactors event creation logic in `EventRenderer` to utilize these classes, improving code organization and reusability.

Adds factory methods for creating event elements from `CalendarEvent` objects, simplifying event instantiation and data management.
This commit is contained in:
Janus Knudsen 2025-09-10 22:36:11 +02:00
parent 3bd74d6f4e
commit e9298934c6
2 changed files with 240 additions and 51 deletions

View file

@ -7,6 +7,7 @@ import { eventBus } from '../core/EventBus';
import { CoreEvents } from '../constants/CoreEvents';
import { OverlapDetector, OverlapResult, EventId } from '../utils/OverlapDetector';
import { ResizeManager } from '../managers/ResizeManager';
import { SwpEventElement, SwpAllDayEventElement } from '../elements/SwpEventElement';
/**
* Interface for event rendering strategies
@ -146,6 +147,12 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
this.handleConvertToTimed(eventId, targetColumn, targetY);
});
// Handle simple all-day duration adjustment (when leaving header)
eventBus.on('drag:adjust-allday-duration', (event) => {
const { eventId, durationMinutes } = (event as CustomEvent).detail;
this.handleAdjustAllDayDuration(eventId, durationMinutes);
});
// Handle navigation period change (when slide animation completes)
eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => {
// Animate all-day height after navigation completes
@ -723,21 +730,25 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}
});
// Create all-day event with standardized data attributes
const allDayEvent = document.createElement('swp-allday-event');
allDayEvent.dataset.eventId = clone.dataset.eventId || '';
allDayEvent.dataset.title = eventTitle;
allDayEvent.dataset.start = `${targetDate}T00:00:00`;
allDayEvent.dataset.end = `${targetDate}T23:59:59`;
allDayEvent.dataset.type = clone.dataset.type || 'work';
allDayEvent.dataset.duration = eventDuration;
allDayEvent.dataset.allDay = "true";
// Create CalendarEvent object for the factory
const tempEvent: CalendarEvent = {
id: clone.dataset.eventId || '',
title: eventTitle,
start: new Date(`${targetDate}T00:00:00`),
end: new Date(`${targetDate}T23:59:59`),
type: clone.dataset.type || 'work',
allDay: true,
syncStatus: 'synced',
metadata: {
duration: eventDuration
}
};
allDayEvent.textContent = eventTitle;
// Create all-day event using factory
const swpAllDayEvent = SwpAllDayEventElement.fromCalendarEvent(tempEvent, targetDate);
const allDayEvent = swpAllDayEvent.getElement();
console.log("allDayEvent", allDayEvent.dataset);
// Position in grid
(allDayEvent as HTMLElement).style.gridColumn = columnIndex.toString();
// grid-row will be set by checkAndAnimateAllDayHeight() based on actual position
// Remove original clone
@ -768,6 +779,33 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
this.transformAllDayToTimed(this.draggedClone, targetColumn, targetY);
}
/**
* Handle simple all-day duration adjustment (when leaving header)
*/
private handleAdjustAllDayDuration(eventId: string, durationMinutes: number): void {
if (!this.draggedClone) return;
// Only adjust if it's an all-day event
if (this.draggedClone.tagName !== 'SWP-ALLDAY-EVENT') return;
// Simply adjust the duration and height - keep all other data intact
this.draggedClone.dataset.duration = durationMinutes.toString();
// Calculate new height based on duration
const gridSettings = calendarConfig.getGridSettings();
const hourHeight = gridSettings.hourHeight;
const newHeight = (durationMinutes / 60) * hourHeight;
this.draggedClone.style.height = `${newHeight}px`;
// Remove all-day specific styling to make it behave like a timed event
this.draggedClone.style.gridColumn = '';
this.draggedClone.style.gridRow = '';
this.draggedClone.dataset.allDay = "false";
// Apply basic timed event positioning
this.applyDragStyling(this.draggedClone);
}
/**
* Transform clone from all-day to timed event
*/
@ -821,18 +859,12 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}
};
// Create timed event element
const timedEvent = document.createElement('swp-event');
timedEvent.dataset.eventId = eventId;
timedEvent.dataset.title = eventTitle;
timedEvent.dataset.type = eventType;
timedEvent.dataset.start = startDate.toISOString();
timedEvent.dataset.end = endDate.toISOString();
timedEvent.dataset.duration = duration.toString();
timedEvent.dataset.originalDuration = duration.toString();
// Create timed event using factory
const swpTimedEvent = SwpEventElement.fromCalendarEvent(tempEvent);
const timedEvent = swpTimedEvent.getElement();
// Create inner structure using helper method
timedEvent.innerHTML = this.createEventInnerStructure(tempEvent);
// Set additional drag-specific attributes
timedEvent.dataset.originalDuration = duration.toString();
// Apply drag styling and positioning
this.applyDragStyling(timedEvent);
@ -970,19 +1002,12 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Place events directly in the single container
eventPlacements.forEach(({ event, span, row }) => {
// Create the all-day event element
const allDayEvent = document.createElement('swp-allday-event');
allDayEvent.textContent = event.title;
// Create all-day event using factory
const eventDateStr = DateCalculator.formatISODate(event.start);
const swpAllDayEvent = SwpAllDayEventElement.fromCalendarEvent(event, eventDateStr);
const allDayEvent = swpAllDayEvent.getElement();
// Set data attributes directly from CalendarEvent
allDayEvent.dataset.eventId = event.id;
allDayEvent.dataset.title = event.title;
allDayEvent.dataset.start = event.start.toISOString();
allDayEvent.dataset.end = event.end.toISOString();
allDayEvent.dataset.type = event.type;
allDayEvent.dataset.duration = event.metadata?.duration?.toString() || '60';
// Set grid position (column and row)
// Override grid position for spanning events
(allDayEvent as HTMLElement).style.gridColumn = span.columnSpan > 1
? `${span.startColumn} / span ${span.columnSpan}`
: `${span.startColumn}`;
@ -1000,22 +1025,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}
protected renderEvent(event: CalendarEvent): HTMLElement {
const eventElement = document.createElement('swp-event');
eventElement.dataset.eventId = event.id;
eventElement.dataset.title = event.title;
eventElement.dataset.start = event.start.toISOString();
eventElement.dataset.end = event.end.toISOString();
eventElement.dataset.type = event.type;
eventElement.dataset.duration = event.metadata?.duration?.toString() || '60';
// Calculate and apply position based on time
const position = this.calculateEventPosition(event);
this.applyEventPositioning(eventElement, position.top + 1, position.height - 3);
// Color is now handled by CSS classes based on data-type attribute
// Create event content using helper method
eventElement.innerHTML = this.createEventInnerStructure(event);
const swpEvent = SwpEventElement.fromCalendarEvent(event);
const eventElement = swpEvent.getElement();
// Setup resize handles on first mouseover only
eventElement.addEventListener('mouseover', () => {