From 0b7499521e5c963dc8bffa86e4270e284f1f2ce1 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Fri, 19 Sep 2025 00:20:30 +0200 Subject: [PATCH] Enables all-day event drag and drop Implements comprehensive drag and drop for all-day events, allowing movement within the header and conversion to timed events when dragged into the calendar grid. Optimizes column detection with a cached bounding box strategy, improving performance and accuracy. Refactors event conversion logic and renames related event bus events for clarity. --- docs/drag-drop-header-bug-analysis.md | 4 +- .../drag-drop-header-complete-bug-analysis.md | 2 +- refactored-header-manager.md | 2 +- src/managers/AllDayManager.ts | 251 ++++++++++++------ src/managers/DragDropManager.ts | 102 +++++-- src/renderers/EventRendererManager.ts | 83 ++++++ 6 files changed, 338 insertions(+), 106 deletions(-) diff --git a/docs/drag-drop-header-bug-analysis.md b/docs/drag-drop-header-bug-analysis.md index 02e78b7..e2aef1b 100644 --- a/docs/drag-drop-header-bug-analysis.md +++ b/docs/drag-drop-header-bug-analysis.md @@ -17,7 +17,7 @@ Når en day event dragges op til headeren (for at konvertere til all-day) og der ### Trin 3: Mouse enters Header ⚠️ PROBLEM STARTER HER - **DragDropManager** (linje 95-112): Lytter til `header:mouseover` -- Emitter `drag:convert-to-allday` event +- Emitter `drag:convert-to-allday_event` event - **AllDayManager** (linje 232-285): `handleConvertToAllDay()`: - Opretter all-day event i header - **FJERNER original timed event permanent** (linje 274: `originalElement.remove()`) @@ -25,7 +25,7 @@ Når en day event dragges op til headeren (for at konvertere til all-day) og der ### Trin 4: Mouse leaves Header (tilbage til grid) ⚠️ PROBLEM FORTSÆTTER - **DragDropManager** (linje 128-136): Lytter til `header:mouseleave` -- Emitter `drag:convert-from-allday` event +- Emitter `drag:convert-to-time_event` event - **AllDayManager** (linje 290-311): `handleConvertFromAllDay()`: - Fjerner all-day event fra container - Viser drag clone igen diff --git a/docs/drag-drop-header-complete-bug-analysis.md b/docs/drag-drop-header-complete-bug-analysis.md index 54ed011..85796a1 100644 --- a/docs/drag-drop-header-complete-bug-analysis.md +++ b/docs/drag-drop-header-complete-bug-analysis.md @@ -24,7 +24,7 @@ sequenceDiagram Note over Mouse: Dragger over header loop Hver mouseover event Mouse->>Header: mouseover - Header->>AllDay: drag:convert-to-allday + Header->>AllDay: drag:convert-to-allday_event AllDay->>AllDay: Opretter NYT all-day event ❌ Note over AllDay: Ingen check for eksisterende! end diff --git a/refactored-header-manager.md b/refactored-header-manager.md index 73aebbb..0d77314 100644 --- a/refactored-header-manager.md +++ b/refactored-header-manager.md @@ -133,7 +133,7 @@ eventBus.on('header:mouseover', (event) => { if (draggedElement) { console.log('🎯 Converting to all-day for date:', targetDate); - this.eventBus.emit('drag:convert-to-allday', { + this.eventBus.emit('drag:convert-to-allday_event', { targetDate, originalElement: draggedElement, headerRenderer: (event as CustomEvent).detail.headerRenderer diff --git a/src/managers/AllDayManager.ts b/src/managers/AllDayManager.ts index 7f0df13..6c632f6 100644 --- a/src/managers/AllDayManager.ts +++ b/src/managers/AllDayManager.ts @@ -19,7 +19,7 @@ export class AllDayManager { // Bind methods for event listeners this.checkAndAnimateAllDayHeight = this.checkAndAnimateAllDayHeight.bind(this); this.allDayEventRenderer = new AllDayEventRenderer(); - + // Listen for drag-to-allday conversions this.setupEventListeners(); } @@ -28,23 +28,16 @@ export class AllDayManager { * Setup event listeners for drag conversions */ private setupEventListeners(): void { - eventBus.on('drag:convert-to-allday', (event) => { + eventBus.on('drag:convert-to-allday_event', (event) => { const { targetDate, originalElement } = (event as CustomEvent).detail; - console.log('🔄 AllDayManager: Received drag:convert-to-allday', { + console.log('🔄 AllDayManager: Received drag:convert-to-allday_event', { targetDate, originalElementId: originalElement?.dataset?.eventId, originalElementTag: originalElement?.tagName }); this.handleConvertToAllDay(targetDate, originalElement); }); - - eventBus.on('drag:convert-from-allday', (event) => { - const { draggedEventId } = (event as CustomEvent).detail; - console.log('🔄 AllDayManager: Received drag:convert-from-allday', { - draggedEventId - }); - this.handleConvertFromAllDay(draggedEventId); - }); + // Listen for requests to ensure all-day container exists eventBus.on('allday:ensure-container', () => { @@ -57,6 +50,39 @@ export class AllDayManager { console.log('🔄 AllDayManager: Received header:mouseleave, recalculating height'); this.checkAndAnimateAllDayHeight(); }); + + // Listen for drag operations on all-day events + eventBus.on('drag:start', (event) => { + const { eventId, mouseOffset } = (event as CustomEvent).detail; + + // Check if this is an all-day event + const originalElement = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${eventId}"]`); + if (!originalElement) return; // Not an all-day event + + console.log('🎯 AllDayManager: Starting drag for all-day event', { eventId }); + this.handleDragStart(originalElement as HTMLElement, eventId, mouseOffset); + }); + + eventBus.on('drag:move', (event) => { + const { eventId, mousePosition } = (event as CustomEvent).detail; + + // Only handle for all-day events + const dragClone = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${eventId}"]`); + if (dragClone) { + this.handleDragMove(dragClone as HTMLElement, mousePosition); + } + }); + + eventBus.on('drag:end', (event) => { + const { eventId, finalPosition } = (event as CustomEvent).detail; + + // Check if this was an all-day event + const originalElement = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${eventId}"]`); + const dragClone = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${eventId}"]`); + + console.log('🎯 AllDayManager: Ending drag for all-day event', { eventId }); + this.handleDragEnd(originalElement as HTMLElement, dragClone as HTMLElement, finalPosition); + }); } /** @@ -104,7 +130,7 @@ export class AllDayManager { const targetHeight = targetRows * ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT; const currentHeight = parseInt(getComputedStyle(root).getPropertyValue('--all-day-row-height') || '0'); const heightDifference = targetHeight - currentHeight; - + return { targetHeight, currentHeight, heightDifference }; } @@ -122,7 +148,7 @@ export class AllDayManager { */ public expandAllDayRow(): void { const { currentHeight } = this.calculateAllDayHeight(0); - + if (currentHeight === 0) { this.checkAndAnimateAllDayHeight(); } @@ -141,49 +167,49 @@ export class AllDayManager { public checkAndAnimateAllDayHeight(): void { const container = this.getAllDayContainer(); if (!container) return; - + const allDayEvents = container.querySelectorAll('swp-allday-event'); - + // Calculate required rows - 0 if no events (will collapse) let maxRows = 0; - + if (allDayEvents.length > 0) { // Expand events to all dates they span and group by date const expandedEventsByDate: Record = {}; - + (Array.from(allDayEvents) as HTMLElement[]).forEach((event: HTMLElement) => { const startISO = event.dataset.start || ''; const endISO = event.dataset.end || startISO; const eventId = event.dataset.eventId || ''; - + // Extract dates from ISO strings const startDate = startISO.split('T')[0]; // YYYY-MM-DD const endDate = endISO.split('T')[0]; // YYYY-MM-DD - + // Loop through all dates from start to end let current = new Date(startDate); const end = new Date(endDate); - + while (current <= end) { const dateStr = current.toISOString().split('T')[0]; // YYYY-MM-DD format - + if (!expandedEventsByDate[dateStr]) { expandedEventsByDate[dateStr] = []; } expandedEventsByDate[dateStr].push(eventId); - + // Move to next day current.setDate(current.getDate() + 1); } }); - + // Find max rows needed maxRows = Math.max( ...Object.values(expandedEventsByDate).map(ids => ids?.length || 0), 0 ); } - + // Animate to required rows (0 = collapse, >0 = expand) this.animateToRows(maxRows); } @@ -193,22 +219,22 @@ export class AllDayManager { */ public animateToRows(targetRows: number): void { const { targetHeight, currentHeight, heightDifference } = this.calculateAllDayHeight(targetRows); - + if (targetHeight === currentHeight) return; // No animation needed - + console.log(`🎬 All-day height animation: ${currentHeight}px → ${targetHeight}px (${Math.ceil(currentHeight / ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT)} → ${targetRows} rows)`); - + // Get cached elements const calendarHeader = this.getCalendarHeader(); const headerSpacer = this.getHeaderSpacer(); const allDayContainer = this.getAllDayContainer(); - + if (!calendarHeader || !allDayContainer) return; - + // Get current parent height for animation const currentParentHeight = parseFloat(getComputedStyle(calendarHeader).height); const targetParentHeight = currentParentHeight + heightDifference; - + const animations = [ calendarHeader.animate([ { height: `${currentParentHeight}px` }, @@ -219,13 +245,13 @@ export class AllDayManager { fill: 'forwards' }) ]; - + // Add spacer animation if spacer exists if (headerSpacer) { const root = document.documentElement; const currentSpacerHeight = parseInt(getComputedStyle(root).getPropertyValue('--header-height')) + currentHeight; const targetSpacerHeight = parseInt(getComputedStyle(root).getPropertyValue('--header-height')) + targetHeight; - + animations.push( headerSpacer.animate([ { height: `${currentSpacerHeight}px` }, @@ -237,7 +263,7 @@ export class AllDayManager { }) ); } - + // Update CSS variable after animation Promise.all(animations.map(anim => anim.finished)).then(() => { const root = document.documentElement; @@ -265,16 +291,16 @@ export class AllDayManager { // 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, + id: `clone-${eventId}`, title: title, start: targetStart, end: targetEnd, @@ -286,8 +312,8 @@ export class AllDayManager { } }; - // Check if all-day event already exists for this event ID - const existingAllDayEvent = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${eventId}"]`); + // Check if all-day clone already exists for this event ID + const existingAllDayEvent = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${eventId}"]`); if (existingAllDayEvent) { // All-day event already exists, just ensure clone is hidden const dragClone = document.querySelector(`swp-event[data-event-id="clone-${eventId}"]`); @@ -299,44 +325,19 @@ export class AllDayManager { // Use renderer to create and add all-day event const allDayElement = this.allDayEventRenderer.renderAllDayEvent(calendarEvent, targetDate); - + if (allDayElement) { // Hide drag clone completely const dragClone = document.querySelector(`swp-event[data-event-id="clone-${eventId}"]`); if (dragClone) { (dragClone as HTMLElement).style.display = 'none'; } - + // Animate height change this.checkAndAnimateAllDayHeight(); } } - /** - * Handle conversion from all-day event back to day event - */ - private handleConvertFromAllDay(draggedEventId: string): void { - // Find and remove all-day event specifically in the container - const allDayEvent = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${draggedEventId}"]`); - if (allDayEvent) { - allDayEvent.remove(); - } - - // Show drag clone again with reset styles - const dragClone = document.querySelector(`swp-event[data-event-id="clone-${draggedEventId}"]`); - if (dragClone) { - const clone = dragClone as HTMLElement; - - // Reset to standard day event styles - clone.style.display = 'block'; - clone.style.zIndex = ''; // Fjern drag z-index - clone.style.cursor = ''; // Fjern drag cursor - clone.style.opacity = ''; // Fjern evt. opacity - clone.style.transform = ''; // Fjern evt. transforms - - // Position styles (top, height, left, right) bevares - } - } /** * Update row height when all-day events change @@ -350,36 +351,114 @@ export class AllDayManager { */ public ensureAllDayContainer(): HTMLElement | null { console.log('🔍 AllDayManager: Checking if all-day container exists...'); - + // Try to get existing container first let container = this.getAllDayContainer(); - + if (!container) { - console.log('🏗️ AllDayManager: Container not found, creating via AllDayEventRenderer...'); - - // Use the renderer to create container (which will call getContainer internally) + this.allDayEventRenderer.clearCache(); // Clear cache to force re-check - - // The renderer's getContainer method will create the container if it doesn't exist - // We can trigger this by trying to get the container + const header = this.getCalendarHeader(); - if (header) { - container = document.createElement('swp-allday-container'); - header.appendChild(container); - console.log('✅ AllDayManager: Created all-day container'); - - // Update our cache - this.cachedAllDayContainer = container; - } else { - console.log('❌ AllDayManager: No calendar header found, cannot create container'); - } - } else { - console.log('✅ AllDayManager: All-day container already exists'); + container = document.createElement('swp-allday-container'); + header?.appendChild(container); + + this.cachedAllDayContainer = container; + } - + return container; } + /** + * Handle drag start for all-day events + */ + private handleDragStart(originalElement: HTMLElement, eventId: string, mouseOffset: any): void { + // Create clone + const clone = originalElement.cloneNode(true) as HTMLElement; + clone.dataset.eventId = `clone-${eventId}`; + + // Get container + const container = this.getAllDayContainer(); + if (!container) return; + + // Add clone to container + container.appendChild(clone); + + // Copy positioning from original + clone.style.gridColumn = originalElement.style.gridColumn; + clone.style.gridRow = originalElement.style.gridRow; + + // Add dragging style + clone.classList.add('dragging'); + clone.style.zIndex = '1000'; + clone.style.cursor = 'grabbing'; + + // Make original semi-transparent + originalElement.style.opacity = '0.3'; + + console.log('✅ AllDayManager: Created drag clone for all-day event', { + eventId, + cloneId: clone.dataset.eventId, + gridColumn: clone.style.gridColumn, + gridRow: clone.style.gridRow + }); + } + + /** + * Handle drag move for all-day events + */ + private handleDragMove(dragClone: HTMLElement, mousePosition: any): void { + // Calculate grid column based on mouse position + const dayHeaders = document.querySelectorAll('swp-day-header'); + let targetColumn = 1; + + dayHeaders.forEach((header, index) => { + const rect = header.getBoundingClientRect(); + if (mousePosition.x >= rect.left && mousePosition.x <= rect.right) { + targetColumn = index + 1; + } + }); + + // Update clone position + dragClone.style.gridColumn = targetColumn.toString(); + + console.log('🔄 AllDayManager: Updated drag clone position', { + eventId: dragClone.dataset.eventId, + targetColumn, + mouseX: mousePosition.x + }); + } + + /** + * Handle drag end for all-day events + */ + private handleDragEnd(originalElement: HTMLElement, dragClone: HTMLElement, finalPosition: any): void { + // Remove original element + originalElement?.remove(); + + // Normalize clone + const cloneId = dragClone.dataset.eventId; + if (cloneId?.startsWith('clone-')) { + dragClone.dataset.eventId = cloneId.replace('clone-', ''); + } + + // Remove dragging styles + dragClone.classList.remove('dragging'); + dragClone.style.zIndex = ''; + dragClone.style.cursor = ''; + dragClone.style.opacity = ''; + + // Recalculate all-day container height + this.checkAndAnimateAllDayHeight(); + + console.log('✅ AllDayManager: Completed drag operation for all-day event', { + eventId: dragClone.dataset.eventId, + finalColumn: dragClone.style.gridColumn + }); + } + + /** * Clean up cached elements and resources */ diff --git a/src/managers/DragDropManager.ts b/src/managers/DragDropManager.ts index 3bfab65..c9f6d62 100644 --- a/src/managers/DragDropManager.ts +++ b/src/managers/DragDropManager.ts @@ -19,6 +19,12 @@ interface Position { y: number; } +interface ColumnBounds { + date: string; + left: number; + right: number; +} + export class DragDropManager { private eventBus: IEventBus; @@ -45,6 +51,9 @@ export class DragDropManager { lastColumnDate: null }; + // Column bounds cache for coordinate-based column detection + private columnBoundsCache: ColumnBounds[] = []; + // Auto-scroll properties private autoScrollAnimationId: number | null = null; private readonly scrollSpeed = 10; // pixels per frame @@ -92,6 +101,19 @@ export class DragDropManager { document.body.addEventListener('mousedown', this.boundHandlers.mouseDown); document.body.addEventListener('mouseup', this.boundHandlers.mouseUp); + // Initialize column bounds cache + this.updateColumnBoundsCache(); + + // Listen to resize events to update cache + window.addEventListener('resize', () => { + this.updateColumnBoundsCache(); + }); + + // Listen to navigation events to update cache + this.eventBus.on('navigation:completed', () => { + this.updateColumnBoundsCache(); + }); + // Listen for header mouseover events this.eventBus.on('header:mouseover', (event) => { const { targetDate, headerRenderer } = (event as CustomEvent).detail; @@ -116,7 +138,7 @@ export class DragDropManager { console.log('✅ DragDropManager: Converting to all-day for date:', targetDate); // Element findes stadig som day-event, så konverter - this.eventBus.emit('drag:convert-to-allday', { + this.eventBus.emit('drag:convert-to-allday_event', { targetDate, originalElement: draggedElement, headerRenderer @@ -147,8 +169,13 @@ export class DragDropManager { this.eventBus.on('header:mouseleave', (event) => { // Check if we're dragging ANY event if (this.draggedEventId) { - this.eventBus.emit('drag:convert-from-allday', { - draggedEventId: this.draggedEventId + const mousePosition = { x: this.lastMousePosition.x, y: this.lastMousePosition.y }; + const column = this.getColumnDateFromX(mousePosition.x); + + this.eventBus.emit('drag:convert-to-time_event', { + draggedEventId: this.draggedEventId, + mousePosition: mousePosition, + column: column }); } }); @@ -358,25 +385,68 @@ export class DragDropManager { } /** - * Optimized column detection with caching + * Update column bounds cache for coordinate-based column detection */ - private detectColumn(mouseX: number, mouseY: number): string | null { - const element = document.elementFromPoint(mouseX, mouseY); - if (!element) return null; + private updateColumnBoundsCache(): void { + // Reset cache + this.columnBoundsCache = []; - // Walk up DOM tree to find swp-day-column - let current = element as HTMLElement; - while (current && current.tagName !== 'SWP-DAY-COLUMN') { - current = current.parentElement as HTMLElement; - if (!current) return null; + // Find alle kolonner + const columns = document.querySelectorAll('swp-day-column'); + + // Cache hver kolonnes x-grænser + columns.forEach(column => { + const rect = column.getBoundingClientRect(); + const date = (column as HTMLElement).dataset.date; + + if (date) { + this.columnBoundsCache.push({ + date, + left: rect.left, + right: rect.right + }); + } + }); + + // Sorter efter x-position (fra venstre til højre) + this.columnBoundsCache.sort((a, b) => a.left - b.left); + + console.log('📏 DragDropManager: Updated column bounds cache', { + columns: this.columnBoundsCache.length + }); + } + + /** + * Get column date from X coordinate using cached bounds + */ + private getColumnDateFromX(x: number): string | null { + // Opdater cache hvis tom + if (this.columnBoundsCache.length === 0) { + this.updateColumnBoundsCache(); } - const columnDate = current.dataset.date || null; + // Find den kolonne hvor x-koordinaten er indenfor grænserne + const column = this.columnBoundsCache.find(col => + x >= col.left && x <= col.right + ); - // Update cache if we found a new column + return column ? column.date : null; + } + + /** + * Coordinate-based column detection (replaces DOM traversal) + */ + private detectColumn(mouseX: number, mouseY: number): string | null { + // Brug den koordinatbaserede metode direkte + const columnDate = this.getColumnDateFromX(mouseX); + + // Opdater stadig den eksisterende cache hvis vi finder en kolonne if (columnDate && columnDate !== this.cachedElements.lastColumnDate) { - this.cachedElements.currentColumn = current; - this.cachedElements.lastColumnDate = columnDate; + const columnElement = document.querySelector(`swp-day-column[data-date="${columnDate}"]`) as HTMLElement; + if (columnElement) { + this.cachedElements.currentColumn = columnElement; + this.cachedElements.lastColumnDate = columnDate; + } } return columnDate; diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index 09395c8..2e65e8b 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -5,6 +5,7 @@ import { calendarConfig } from '../core/CalendarConfig'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { EventManager } from '../managers/EventManager'; import { EventRendererStrategy } from './EventRenderer'; +import { SwpEventElement } from '../elements/SwpEventElement'; /** * EventRenderingService - Render events i DOM med positionering using Strategy Pattern @@ -69,6 +70,28 @@ export class EventRenderingService { this.eventBus.on(CoreEvents.VIEW_CHANGED, (event: Event) => { this.handleViewChanged(event as CustomEvent); }); + + // Simple drag:end listener to clean up day event clones + this.eventBus.on('drag:end', (event: Event) => { + const { eventId } = (event as CustomEvent).detail; + const dayEventClone = document.querySelector(`swp-event[data-event-id="clone-${eventId}"]`); + + if (dayEventClone) { + dayEventClone.remove(); + } + }); + + // Listen for conversion from all-day event to time event + this.eventBus.on('drag:convert-to-time_event', (event: Event) => { + const { draggedEventId, mousePosition, column } = (event as CustomEvent).detail; + console.log('🔄 EventRendererManager: Received drag:convert-to-time_event', { + draggedEventId, + mousePosition, + column + }); + this.handleConvertToTimeEvent(draggedEventId, mousePosition, column); + }); + } @@ -128,6 +151,66 @@ export class EventRenderingService { // New rendering will be triggered by subsequent GRID_RENDERED event } + + + /** + * Handle conversion from all-day event to time event + */ + private handleConvertToTimeEvent(draggedEventId: string, mousePosition: any, column: string): void { + // Find all-day event clone + const allDayClone = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${draggedEventId}"]`); + + if (!allDayClone) { + console.warn('EventRendererManager: All-day clone not found - drag may not have started properly', { draggedEventId }); + return; + } + + // Use SwpEventElement factory to create day event from all-day event + const dayEventElement = SwpEventElement.fromAllDayElement(allDayClone as HTMLElement); + const dayElement = dayEventElement.getElement(); + + // Remove the all-day clone - it's no longer needed since we're converting to day event + allDayClone.remove(); + + // Set clone ID + dayElement.dataset.eventId = `clone-${draggedEventId}`; + + // Find target column + const columnElement = document.querySelector(`swp-day-column[data-date="${column}"]`); + if (!columnElement) { + console.warn('EventRendererManager: Target column not found', { column }); + return; + } + + // Find events layer in the column + const eventsLayer = columnElement.querySelector('swp-events-layer'); + if (!eventsLayer) { + console.warn('EventRendererManager: Events layer not found in column'); + return; + } + + // Add to events layer + eventsLayer.appendChild(dayElement); + + // Position based on mouse Y coordinate + const columnRect = columnElement.getBoundingClientRect(); + const relativeY = Math.max(0, mousePosition.y - columnRect.top); + dayElement.style.top = `${relativeY}px`; + + // Set drag styling + dayElement.style.zIndex = '1000'; + dayElement.style.cursor = 'grabbing'; + dayElement.style.opacity = ''; + dayElement.style.transform = ''; + + console.log('✅ EventRendererManager: Converted all-day event to time event', { + draggedEventId, + column, + mousePosition, + relativeY + }); + } + private clearEvents(container?: HTMLElement): void { this.strategy.clearEvents(container); }