From cf463cc691c867e7f3fdceea84554092e0c42d3e Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Tue, 30 Sep 2025 22:35:07 +0200 Subject: [PATCH] Refactors all-day event layout calculation Improves all-day event drag and drop by recalculating layouts and applying differential updates to minimize DOM manipulations. This change optimizes the update process by comparing current and new layouts, only updating elements with changed grid areas, leading to smoother transitions. Removes obsolete code. --- src/managers/AllDayManager.ts | 107 +++++++++++----------------------- 1 file changed, 33 insertions(+), 74 deletions(-) diff --git a/src/managers/AllDayManager.ts b/src/managers/AllDayManager.ts index 89d62e9..cb93988 100644 --- a/src/managers/AllDayManager.ts +++ b/src/managers/AllDayManager.ts @@ -24,15 +24,17 @@ export class AllDayManager { private layoutEngine: AllDayLayoutEngine | null = null; // State tracking for differential updates - private currentLayouts: Map = new Map(); + private currentLayouts: EventLayout[] = []; private currentAllDayEvents: CalendarEvent[] = []; private currentWeekDates: string[] = []; + private newLayouts: EventLayout[] = []; // Expand/collapse state private isExpanded: boolean = false; private actualRowCount: number = 0; private readonly MAX_COLLAPSED_ROWS = 4; // Show 4 rows when collapsed (3 events + 1 indicator row) + constructor() { this.allDayEventRenderer = new AllDayEventRenderer(); this.setupEventListeners(); @@ -168,13 +170,11 @@ export class AllDayManager { */ public checkAndAnimateAllDayHeight(): void { const container = this.getAllDayContainer(); - if (!container) { - this.animateToRows(0); - this.updateChevronButton(false); - return; - } - const allDayEvents = container.querySelectorAll('swp-event'); + const allDayEvents = container?.querySelectorAll('swp-event'); + //use currentLayouts here instead of the queryselector + + // Calculate required rows - 0 if no events (will collapse) let maxRows = 0; @@ -191,11 +191,6 @@ export class AllDayManager { // Max rows = highest row number (e.g. if row 3 is used, height = 3 rows) maxRows = highestRow; - console.log('🔍 AllDayManager: Height calculation FIXED', { - totalEvents: allDayEvents.length, - highestRowFound: highestRow, - maxRows - }); } // Store actual row count @@ -203,11 +198,11 @@ export class AllDayManager { // Determine what to display let displayRows = maxRows; - + if (maxRows > this.MAX_COLLAPSED_ROWS) { // Show chevron button this.updateChevronButton(true); - + // Show 4 rows when collapsed (3 events + indicators) if (!this.isExpanded) { displayRows = this.MAX_COLLAPSED_ROWS; @@ -285,28 +280,6 @@ export class AllDayManager { }); } - /** - * Store current layouts from DOM for comparison - */ - private storeCurrentLayouts(): void { - this.currentLayouts.clear(); - const container = this.getAllDayContainer(); - if (!container) return; - - container.querySelectorAll('swp-event').forEach(element => { - const htmlElement = element as HTMLElement; - const eventId = htmlElement.dataset.eventId; - const gridArea = htmlElement.style.gridArea; - if (eventId && gridArea) { - this.currentLayouts.set(eventId, gridArea); - } - }); - - console.log('📋 AllDayManager: Stored current layouts', { - count: this.currentLayouts.size, - layouts: Array.from(this.currentLayouts.entries()) - }); - } /** * Set current events and week dates (called by EventRendererManager) @@ -332,10 +305,10 @@ export class AllDayManager { this.currentWeekDates = weekDates; // Initialize layout engine with provided week dates - this.layoutEngine = new AllDayLayoutEngine(weekDates); + var layoutEngine = new AllDayLayoutEngine(weekDates); // Calculate layout for all events together - AllDayLayoutEngine handles CalendarEvents directly - return this.layoutEngine.calculateLayout(events); + return layoutEngine.calculateLayout(events); } @@ -346,7 +319,7 @@ export class AllDayManager { */ private handleConvertToAllDay(payload: DragMouseEnterHeaderEventPayload): void { - if(payload.draggedClone?.dataset == null) + if (payload.draggedClone?.dataset == null) console.error("payload.cloneElement.dataset.eventId is null"); @@ -366,14 +339,9 @@ export class AllDayManager { // Add to container allDayContainer?.appendChild(payload.draggedClone); - + ColumnDetectionUtils.updateColumnBoundsCache(); - console.log('✅ AllDayManager: Converted to all-day style (simple row 1)', { - eventId: payload.draggedClone.dataset.eventId, - gridColumn: payload.targetColumn, - gridRow: 1 - }); } @@ -432,28 +400,16 @@ export class AllDayManager { dragClone.style.gridRow = '1'; // Force row 1 during drag dragClone.style.gridArea = `1 / ${targetColumn.index} / 2 / ${targetColumn.index + 1}`; - console.log('🔄 AllDayManager: Updated all-day drag clone position', { - eventId: dragClone.dataset.eventId, - targetColumn, - gridRow: 1, - gridArea: dragClone.style.gridArea, - mouseX: mousePosition.x - }); } /** * Handle drag end for all-day events - WITH DIFFERENTIAL UPDATES */ private handleDragEnd(dragEndEvent: DragEndEventPayload): void { - console.log('🎯 AllDayManager: Starting drag end with differential updates', { - dragEndEvent - }); if (dragEndEvent.draggedClone == null) return; - // 1. Store current layouts BEFORE any changes - this.storeCurrentLayouts(); // 2. Normalize clone ID const cloneId = dragEndEvent.draggedClone?.dataset.eventId; @@ -484,19 +440,20 @@ export class AllDayManager { const tempEvents = [...this.currentAllDayEvents, droppedEvent]; // 4. Calculate new layouts for ALL events - const newLayouts = this.calculateAllDayEventsLayout(tempEvents, this.currentWeekDates); + this.newLayouts = this.calculateAllDayEventsLayout(tempEvents, this.currentWeekDates); // 5. Apply differential updates - only update events that changed let changedCount = 0; - newLayouts.forEach((layout) => { - const oldGridArea = this.currentLayouts.get(layout.calenderEvent.id); - const newGridArea = layout.gridArea; + this.newLayouts.forEach((layout) => { + // Find current layout for this event + var currentLayout = this.currentLayouts.find(old => old.calenderEvent.id === layout.calenderEvent.id); + var currentGridArea = currentLayout?.gridArea; + var newGridArea = layout.gridArea; - if (oldGridArea !== newGridArea) { + if (currentGridArea !== newGridArea) { changedCount++; - const element = dragEndEvent.draggedClone; //:end document.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement; + const element = dragEndEvent.draggedClone; if (element) { - // Add transition class for smooth animation element.classList.add('transitioning'); element.style.gridArea = newGridArea; @@ -509,6 +466,9 @@ export class AllDayManager { } }); + if (changedCount > 0) + this.currentLayouts = this.newLayouts; + // 6. Clean up drag styles from the dropped clone dragEndEvent.draggedClone.classList.remove('dragging'); dragEndEvent.draggedClone.style.zIndex = ''; @@ -516,8 +476,7 @@ export class AllDayManager { dragEndEvent.draggedClone.style.opacity = ''; // 7. Restore original element opacity - dragEndEvent.originalElement.remove(); - //originalElement.style.opacity = ''; + dragEndEvent.originalElement.remove(); //TODO: this should be an event that only fade and remove if confirmed dragdrop // 8. Check if height adjustment is needed this.checkAndAnimateAllDayHeight(); @@ -530,9 +489,9 @@ export class AllDayManager { private updateChevronButton(show: boolean): void { const headerSpacer = this.getHeaderSpacer(); if (!headerSpacer) return; - + let chevron = headerSpacer.querySelector('.allday-chevron') as HTMLElement; - + if (show && !chevron) { // Create chevron button chevron = document.createElement('button'); @@ -568,17 +527,17 @@ export class AllDayManager { private updateOverflowIndicators(): void { const container = this.getAllDayContainer(); if (!container) return; - + container.querySelectorAll('swp-event').forEach((element) => { const event = element as HTMLElement; const gridRow = parseInt(event.style.gridRow) || 1; - + if (gridRow === 4) { // Store original content before converting to indicator if (!event.dataset.originalTitle) { event.dataset.originalTitle = event.dataset.title || event.innerHTML; } - + // Convert row 4 events to indicators const overflowCount = this.actualRowCount - 3; // Total overflow rows event.classList.add('max-event-overflow'); @@ -600,12 +559,12 @@ export class AllDayManager { private clearOverflowIndicators(): void { const container = this.getAllDayContainer(); if (!container) return; - + container.querySelectorAll('.max-event-overflow').forEach((event) => { const htmlEvent = event as HTMLElement; htmlEvent.classList.remove('max-event-overflow'); htmlEvent.onclick = null; - + // Restore original title from data-title if (htmlEvent.dataset.title) { htmlEvent.innerHTML = htmlEvent.dataset.title; @@ -613,7 +572,7 @@ export class AllDayManager { htmlEvent.innerHTML = htmlEvent.dataset.originalTitle; } }); - + // Show all hidden events container.querySelectorAll('swp-event[style*="display: none"]').forEach((event) => { (event as HTMLElement).style.display = '';