Refactors event positioning and drag-and-drop

Centralizes event position calculations into `PositionUtils` for consistency and reusability across managers and renderers.

Improves drag-and-drop functionality by emitting events for all-day event conversion and streamlining position calculations during drag operations.

Introduces `AllDayManager` and `AllDayEventRenderer` to manage and render all-day events in the calendar header. This allows dragging events to the header to convert them to all-day events.
This commit is contained in:
Janus Knudsen 2025-09-13 00:39:56 +02:00
parent 8b96376d1f
commit 7054c0d40a
9 changed files with 404 additions and 72 deletions

View file

@ -2,6 +2,8 @@
import { eventBus } from '../core/EventBus';
import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig';
import { AllDayEventRenderer } from '../renderers/AllDayEventRenderer';
import { CalendarEvent } from '../types/CalendarTypes';
/**
* AllDayManager - Handles all-day row height animations and management
@ -11,10 +13,25 @@ export class AllDayManager {
private cachedAllDayContainer: HTMLElement | null = null;
private cachedCalendarHeader: HTMLElement | null = null;
private cachedHeaderSpacer: HTMLElement | null = null;
private allDayEventRenderer: AllDayEventRenderer;
constructor() {
// Bind methods for event listeners
this.checkAndAnimateAllDayHeight = this.checkAndAnimateAllDayHeight.bind(this);
this.allDayEventRenderer = new AllDayEventRenderer();
// Listen for drag-to-allday conversions
this.setupEventListeners();
}
/**
* Setup event listeners for drag conversions
*/
private setupEventListeners(): void {
eventBus.on('drag:convert-to-allday', (event) => {
const { targetDate, originalElement } = (event as CustomEvent).detail;
this.handleConvertToAllDay(targetDate, originalElement);
});
}
/**
@ -204,6 +221,58 @@ export class AllDayManager {
});
}
/**
* Handle conversion of timed event to all-day event
*/
private handleConvertToAllDay(targetDate: string, originalElement: HTMLElement): void {
// Extract event data from original element
const eventId = originalElement.dataset.eventId;
const title = originalElement.dataset.title || originalElement.textContent || 'Untitled';
const type = originalElement.dataset.type || 'work';
const startStr = originalElement.dataset.start;
const endStr = originalElement.dataset.end;
if (!eventId || !startStr || !endStr) {
console.error('Original element missing required data (eventId, start, end)');
return;
}
// Create CalendarEvent for all-day conversion - preserve original times
const originalStart = new Date(startStr);
const originalEnd = new Date(endStr);
// Set date to target date but keep original time
const targetStart = new Date(targetDate);
targetStart.setHours(originalStart.getHours(), originalStart.getMinutes(), originalStart.getSeconds(), originalStart.getMilliseconds());
const targetEnd = new Date(targetDate);
targetEnd.setHours(originalEnd.getHours(), originalEnd.getMinutes(), originalEnd.getSeconds(), originalEnd.getMilliseconds());
const calendarEvent: CalendarEvent = {
id: eventId,
title: title,
start: targetStart,
end: targetEnd,
type: type,
allDay: true,
syncStatus: 'synced',
metadata: {
duration: originalElement.dataset.duration || '60'
}
};
// Use renderer to create and add all-day event
const allDayElement = this.allDayEventRenderer.renderAllDayEvent(calendarEvent, targetDate);
if (allDayElement) {
// Remove original timed event
originalElement.remove();
// Animate height change
this.checkAndAnimateAllDayHeight();
}
}
/**
* Update row height when all-day events change
*/