From 3b6f0407fbc2ca4ed8b303105ebf294683faae60 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Thu, 6 Nov 2025 21:11:22 +0100 Subject: [PATCH] Refactors event rendering and drag-and-drop logic Simplifies event drag handling by removing redundant clone management Optimizes single column event rendering and cleanup process Removes unnecessary logging and console output Improves event update and re-rendering strategies Enhances performance and reduces complexity in event manipulation --- src/index.ts | 10 +- src/managers/EdgeScrollManager.ts | 10 -- src/managers/ResizeHandleManager.ts | 2 +- src/renderers/EventRenderer.ts | 26 ++++- src/renderers/EventRendererManager.ts | 139 +++++++++++--------------- 5 files changed, 89 insertions(+), 98 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8f89177..8d309bf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -157,7 +157,11 @@ async function initializeCalendar(): Promise { await resizeHandleManager.initialize?.(); // Resolve SyncManager (starts automatically in constructor) - const syncManager = app.resolveType(); + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + //const syncManager = app.resolveType(); // Handle deep linking after managers are initialized await handleDeepLinking(eventManager, urlManager); @@ -169,14 +173,14 @@ async function initializeCalendar(): Promise { app: typeof app; calendarManager: typeof calendarManager; eventManager: typeof eventManager; - syncManager: typeof syncManager; + //syncManager: typeof syncManager; }; }).calendarDebug = { eventBus, app, calendarManager, eventManager, - syncManager, + //syncManager, }; } catch (error) { diff --git a/src/managers/EdgeScrollManager.ts b/src/managers/EdgeScrollManager.ts index a9b45ab..9170ed5 100644 --- a/src/managers/EdgeScrollManager.ts +++ b/src/managers/EdgeScrollManager.ts @@ -180,16 +180,6 @@ export class EdgeScrollManager { const atTop = currentScrollTop <= 0 && vy < 0; const atBottom = (cloneBottom >= timeGridBottom) && vy > 0; - console.log('๐Ÿ“Š Scroll check:', { - currentScrollTop, - scrollableHeight, - timeGridHeight, - cloneBottom, - timeGridBottom, - atTop, - atBottom, - vy - }); if (atTop || atBottom) { // At boundary - stop scrolling diff --git a/src/managers/ResizeHandleManager.ts b/src/managers/ResizeHandleManager.ts index 42576f1..82c34ab 100644 --- a/src/managers/ResizeHandleManager.ts +++ b/src/managers/ResizeHandleManager.ts @@ -244,6 +244,6 @@ export class ResizeHandleManager { this.pointerCaptured = false; } document.documentElement.classList.remove('swp--resizing'); - this.refreshEventCache(); + this.refreshEventCache(); //TODO: We should avoid this caching. }; } diff --git a/src/renderers/EventRenderer.ts b/src/renderers/EventRenderer.ts index 0fe7978..5d21b31 100644 --- a/src/renderers/EventRenderer.ts +++ b/src/renderers/EventRenderer.ts @@ -16,10 +16,11 @@ import { EventLayoutCoordinator, IGridGroupLayout, IStackedEventLayout } from '. export interface IEventRenderer { renderEvents(events: ICalendarEvent[], container: HTMLElement): void; clearEvents(container?: HTMLElement): void; + renderSingleColumnEvents?(column: IColumnBounds, events: ICalendarEvent[]): void; handleDragStart?(payload: IDragStartEventPayload): void; handleDragMove?(payload: IDragMoveEventPayload): void; handleDragAutoScroll?(eventId: string, snappedY: number): void; - handleDragEnd?(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void; + handleDragEnd?(originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void; handleEventClick?(eventId: string, originalElement: HTMLElement): void; handleColumnChange?(payload: IDragColumnChangeEventPayload): void; handleNavigationCompleted?(): void; @@ -153,7 +154,7 @@ export class DateEventRenderer implements IEventRenderer { let eventsLayer = payload.targetColumn.element.querySelector('swp-events-layer'); // Add "clone-" prefix to match clone ID pattern - timedClone.dataset.eventId = `clone-${payload.calendarEvent.id}`; + //timedClone.dataset.eventId = `clone-${payload.calendarEvent.id}`; // Remove old all-day clone and replace with new timed clone payload.draggedClone.remove(); @@ -165,7 +166,7 @@ export class DateEventRenderer implements IEventRenderer { /** * Handle drag end event */ - public handleDragEnd(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void { + public handleDragEnd(originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void { if (!draggedClone || !originalElement) { console.warn('Missing draggedClone or originalElement'); return; @@ -187,6 +188,13 @@ export class DateEventRenderer implements IEventRenderer { // Clean up instance state this.draggedClone = null; this.originalEvent = null; + + + // Clean up any remaining day event clones + const dayEventClone = document.querySelector(`swp-event[data-event-id="clone-${cloneId}"]`); + if (dayEventClone) { + dayEventClone.remove(); + } } /** @@ -226,6 +234,18 @@ export class DateEventRenderer implements IEventRenderer { }); } + /** + * Render events for a single column + */ + public renderSingleColumnEvents(column: IColumnBounds, events: ICalendarEvent[]): void { + const columnEvents = this.getEventsForColumn(column.element, events); + const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement; + + if (eventsLayer) { + this.renderColumnEvents(columnEvents, eventsLayer); + } + } + /** * Render events in a column using combined stacking + grid algorithm */ diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index 6fb043d..db9ca59 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -6,7 +6,7 @@ import { IEventRenderer } from './EventRenderer'; import { SwpEventElement } from '../elements/SwpEventElement'; import { IDragStartEventPayload, IDragMoveEventPayload, IDragEndEventPayload, IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IDragMouseEnterColumnEventPayload, IDragColumnChangeEventPayload, IHeaderReadyEventPayload, IResizeEndEventPayload } from '../types/EventTypes'; import { DateService } from '../utils/DateService'; -import { IColumnBounds } from '../utils/ColumnDetectionUtils'; +import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; /** * EventRenderingService - Render events i DOM med positionering using Strategy Pattern * Hรฅndterer event positioning og overlap detection @@ -161,49 +161,28 @@ export class EventRenderingService { private setupDragEndListener(): void { this.eventBus.on('drag:end', async (event: Event) => { - const { originalElement: draggedElement, originalSourceColumn, finalPosition, target } = (event as CustomEvent).detail; + const { originalElement, draggedClone, originalSourceColumn, finalPosition, target } = (event as CustomEvent).detail; const finalColumn = finalPosition.column; const finalY = finalPosition.snappedY; - const eventId = draggedElement.dataset.eventId || ''; + let element = draggedClone as SwpEventElement; // Only handle day column drops for EventRenderer if (target === 'swp-day-column' && finalColumn) { - // Find dragged clone - use draggedElement as original - const draggedClone = document.querySelector(`swp-day-column swp-event[data-event-id="clone-${eventId}"]`) as HTMLElement; - if (draggedElement && draggedClone && this.strategy.handleDragEnd) { - this.strategy.handleDragEnd(eventId, draggedElement, draggedClone, finalColumn, finalY); + if (originalElement && draggedClone && this.strategy.handleDragEnd) { + this.strategy.handleDragEnd(originalElement, draggedClone, finalColumn, finalY); } - // Update event data in EventManager with new position from clone - if (draggedClone) { - const swpEvent = draggedClone as SwpEventElement; - const newStart = swpEvent.start; - const newEnd = swpEvent.end; - - await this.eventManager.updateEvent(eventId, { - start: newStart, - end: newEnd, - allDay: false - }); - - console.log('๐Ÿ“ EventRendererManager: Updated event in EventManager', { - eventId, - newStart, - newEnd, - allDay: false - }); - } + await this.eventManager.updateEvent(element.eventId, { + start: element.start, + end: element.end, + allDay: false + }); // Re-render affected columns for stacking/grouping (now with updated data) await this.reRenderAffectedColumns(originalSourceColumn, finalColumn); } - // Clean up any remaining day event clones - const dayEventClone = document.querySelector(`swp-day-column swp-event[data-event-id="clone-${eventId}"]`); - if (dayEventClone) { - dayEventClone.remove(); - } }); } @@ -223,7 +202,7 @@ export class EventRenderingService { } private setupDragMouseLeaveHeaderListener(): void { - + this.dragMouseLeaveHeaderListener = (event: Event) => { const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent).detail; @@ -288,8 +267,12 @@ export class EventRenderingService { if (columnElement) { const columnDate = columnElement.dataset.date; if (columnDate) { - // Re-render the column to recalculate stacking/grouping - this.renderSingleColumn(columnDate); + // Get column bounds and re-render the column to recalculate stacking/grouping + const columnDateObj = this.dateService.parseISO(`${columnDate}T00:00:00`); + const columnBounds = ColumnDetectionUtils.getColumnBoundsByDate(columnDateObj); + if (columnBounds) { + await this.renderSingleColumn(columnBounds); + } } } }); @@ -303,72 +286,66 @@ export class EventRenderingService { } }); } - + /** * Re-render affected columns after drag to recalculate stacking/grouping */ private async reRenderAffectedColumns(originalSourceColumn: IColumnBounds | null, targetColumn: IColumnBounds | null): Promise { - const columnsToRender = new Set(); - - // Add original source column if exists + // Re-render original source column if exists if (originalSourceColumn) { - columnsToRender.add(originalSourceColumn.date); + await this.renderSingleColumn(originalSourceColumn); } - // Add target column if exists and different from source + // Re-render target column if exists and different from source if (targetColumn && targetColumn.date !== originalSourceColumn?.date) { - columnsToRender.add(targetColumn.date); - } - - // Re-render each affected column - for (const columnDate of columnsToRender) { - await this.renderSingleColumn(columnDate); + await this.renderSingleColumn(targetColumn); } } /** - * Render events for a single column by re-rendering entire container + * Clear events in a single column's events layer */ - private async renderSingleColumn(columnDate: string): Promise { - // Find the column element - const columnElement = document.querySelector(`swp-day-column[data-date="${columnDate}"]`) as HTMLElement; - if (!columnElement) { - console.warn('EventRendererManager: Column not found', { columnDate }); + private clearColumnEvents(eventsLayer: HTMLElement): void { + const existingEvents = eventsLayer.querySelectorAll('swp-event'); + const existingGroups = eventsLayer.querySelectorAll('swp-event-group'); + + existingEvents.forEach(event => event.remove()); + existingGroups.forEach(group => group.remove()); + } + + /** + * Render events for a single column + */ + private async renderSingleColumn(column: IColumnBounds): Promise { + // Get events for just this column's date + const columnStart = this.dateService.parseISO(`${column.date}T00:00:00`); + const columnEnd = this.dateService.parseISO(`${column.date}T23:59:59.999`); + + // Get events from EventManager for this single date + const events = await this.eventManager.getEventsForPeriod(columnStart, columnEnd); + + // Filter to timed events only + const timedEvents = events.filter(event => !event.allDay); + + // Get events layer within this specific column + const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement; + if (!eventsLayer) { + console.warn('EventRendererManager: Events layer not found in column'); return; } - // Find the parent container (swp-day-columns) - const container = columnElement.closest('swp-day-columns') as HTMLElement; - if (!container) { - console.warn('EventRendererManager: Container not found'); - return; + // Clear only this column's events + this.clearColumnEvents(eventsLayer); + + // Render events for this column using strategy + if (this.strategy.renderSingleColumnEvents) { + this.strategy.renderSingleColumnEvents(column, timedEvents); } - // Get all columns in container to determine date range - const allColumns = Array.from(container.querySelectorAll('swp-day-column')); - if (allColumns.length === 0) return; - - // Get date range from first and last column - const firstColumnDate = allColumns[0].dataset.date; - const lastColumnDate = allColumns[allColumns.length - 1].dataset.date; - - if (!firstColumnDate || !lastColumnDate) return; - - const startDate = this.dateService.parseISO(`${firstColumnDate}T00:00:00`); - const endDate = this.dateService.parseISO(`${lastColumnDate}T23:59:59.999`); - - // Re-render entire container (this will recalculate stacking for all columns) - await this.renderEvents({ - container, - startDate, - endDate - }); - - console.log('๐Ÿ”„ EventRendererManager: Re-rendered container for column', { - columnDate, - startDate: firstColumnDate, - endDate: lastColumnDate + console.log('๐Ÿ”„ EventRendererManager: Re-rendered single column', { + columnDate: column.date, + eventsCount: timedEvents.length }); }