Enables all-day event to timed event conversion

Implements drag-and-drop conversion from all-day events to timed events in day columns. This change introduces a new event type (`drag:mouseenter-column`) and delegates rendering logic to the `DateEventRenderer` to maintain architectural consistency.
This commit is contained in:
Janus C. H. Knudsen 2025-10-10 19:37:10 +02:00
parent 78ca23c07a
commit 0a3d274164
3 changed files with 615 additions and 2 deletions

View file

@ -5,7 +5,7 @@ import { calendarConfig } from '../core/CalendarConfig';
import { SwpEventElement } from '../elements/SwpEventElement';
import { PositionUtils } from '../utils/PositionUtils';
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
import { DragColumnChangeEventPayload, DragMoveEventPayload, DragStartEventPayload } from '../types/EventTypes';
import { DragColumnChangeEventPayload, DragMoveEventPayload, DragStartEventPayload, DragMouseEnterColumnEventPayload } from '../types/EventTypes';
import { DateService } from '../utils/DateService';
import { EventStackManager } from '../managers/EventStackManager';
import { EventLayoutCoordinator, GridGroupLayout, StackedEventLayout } from '../managers/EventLayoutCoordinator';
@ -23,6 +23,7 @@ export interface EventRendererStrategy {
handleEventClick?(eventId: string, originalElement: HTMLElement): void;
handleColumnChange?(payload: DragColumnChangeEventPayload): void;
handleNavigationCompleted?(): void;
handleConvertAllDayToTimed?(payload: DragMouseEnterColumnEventPayload): void;
}
/**
@ -128,6 +129,64 @@ export class DateEventRenderer implements EventRendererStrategy {
}
}
/**
* Handle conversion of all-day event to timed event
*/
public handleConvertAllDayToTimed(payload: DragMouseEnterColumnEventPayload): void {
const { calendarEvent, targetColumn, snappedY, replaceClone } = payload;
console.log('🎯 DateEventRenderer: Converting all-day to timed event', {
eventId: calendarEvent.id,
targetColumn: targetColumn.date,
snappedY
});
// Create timed event element from CalendarEvent
const timedClone = SwpEventElement.fromCalendarEvent(calendarEvent);
// Calculate proper height from event duration
const position = this.calculateEventPosition(calendarEvent);
// Calculate actual duration in minutes from CalendarEvent (important for all-day conversions)
const durationMinutes = (calendarEvent.end.getTime() - calendarEvent.start.getTime()) / (1000 * 60);
timedClone.dataset.duration = durationMinutes.toString();
timedClone.dataset.originalDuration = durationMinutes.toString();
// Set position at snapped Y
timedClone.style.top = `${snappedY}px`;
// Set complete styling for dragged clone (matching normal event rendering)
timedClone.style.height = `${position.height - 3}px`;
timedClone.style.left = '2px';
timedClone.style.right = '2px';
timedClone.style.width = 'auto';
timedClone.style.pointerEvents = 'none';
// Apply drag styling
this.applyDragStyling(timedClone);
// Find the events layer in the target column
const eventsLayer = targetColumn.element.querySelector('swp-events-layer');
if (!eventsLayer) {
console.warn('DateEventRenderer: Events layer not found in column');
return;
}
// Append new timed clone to events layer
eventsLayer.appendChild(timedClone);
// Update instance state
this.draggedClone = timedClone;
// Update DragDropManager's reference to the new clone
replaceClone(timedClone);
console.log('✅ DateEventRenderer: Converted all-day to timed event', {
eventId: calendarEvent.id,
position: snappedY
});
}
/**
* Handle drag end event
*/

View file

@ -6,7 +6,7 @@ import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { EventManager } from '../managers/EventManager';
import { EventRendererStrategy } from './EventRenderer';
import { SwpEventElement } from '../elements/SwpEventElement';
import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, DragColumnChangeEventPayload, HeaderReadyEventPayload, ResizeEndEventPayload } from '../types/EventTypes';
import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, DragMouseEnterColumnEventPayload, DragColumnChangeEventPayload, HeaderReadyEventPayload, ResizeEndEventPayload } from '../types/EventTypes';
import { DateService } from '../utils/DateService';
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
/**
@ -130,6 +130,7 @@ export class EventRenderingService {
this.setupDragEndListener();
this.setupDragColumnChangeListener();
this.setupDragMouseLeaveHeaderListener();
this.setupDragMouseEnterColumnListener();
this.setupResizeEndListener();
this.setupNavigationCompletedListener();
}
@ -250,6 +251,31 @@ export class EventRenderingService {
this.eventBus.on('drag:mouseleave-header', this.dragMouseLeaveHeaderListener);
}
private setupDragMouseEnterColumnListener(): void {
this.eventBus.on('drag:mouseenter-column', (event: Event) => {
const payload = (event as CustomEvent<DragMouseEnterColumnEventPayload>).detail;
// Only handle if clone is an all-day event
if (!payload.draggedClone.hasAttribute('data-allday')) {
return;
}
console.log('🎯 EventRendererManager: Received drag:mouseenter-column', {
targetColumn: payload.targetColumn,
snappedY: payload.snappedY,
calendarEvent: payload.calendarEvent
});
// Remove the old all-day clone from header
payload.draggedClone.remove();
// Delegate to strategy for conversion
if (this.strategy.handleConvertAllDayToTimed) {
this.strategy.handleConvertAllDayToTimed(payload);
}
});
}
private setupResizeEndListener(): void {
this.eventBus.on('resize:end', (event: Event) => {
const { eventId, element } = (event as CustomEvent<ResizeEndEventPayload>).detail;