Refactors all-day event layout tracking logic

Removes redundant state tracking for all-day event layouts

Shifts from maintaining internal layout state to reading directly from DOM elements
Simplifies event handling and updates by using DOM as the source of truth
Improves performance by reducing unnecessary state management
This commit is contained in:
Janus C. H. Knudsen 2025-11-11 18:08:48 +01:00
parent 4cc110d9f2
commit 2656bae054
3 changed files with 83 additions and 67 deletions

View file

@ -33,11 +33,9 @@ export class AllDayManager {
private layoutEngine: AllDayLayoutEngine | null = null;
// State tracking for differential updates
private currentLayouts: IEventLayout[] = [];
// State tracking for layout calculation
private currentAllDayEvents: ICalendarEvent[] = [];
private currentWeekDates: IColumnBounds[] = [];
private newLayouts: IEventLayout[] = [];
// Expand/collapse state
private isExpanded: boolean = false;
@ -128,23 +126,12 @@ export class AllDayManager {
if (dragEndPayload.target === 'swp-day-column' && dragEndPayload.originalElement?.hasAttribute('data-allday')) {
const eventId = dragEndPayload.originalElement.dataset.eventId;
console.log('🔄 AllDayManager: All-day → timed conversion', {
eventId,
currentLayoutsCount: this.currentLayouts.length,
layoutsBeforeFilter: this.currentLayouts.map(l => l.calenderEvent.id)
});
console.log('🔄 AllDayManager: All-day → timed conversion', { eventId });
// Remove event from currentLayouts since it's now a timed event
this.currentLayouts = this.currentLayouts.filter(
layout => layout.calenderEvent.id !== eventId
);
// No need to filter currentLayouts - DOM is source of truth!
// The element is already removed from all-day container by DragDropManager
console.log('📊 AllDayManager: After filter', {
currentLayoutsCount: this.currentLayouts.length,
layoutsAfterFilter: this.currentLayouts.map(l => l.calenderEvent.id)
});
// Recalculate and animate header height
// Recalculate and animate header height based on remaining DOM elements
this.checkAndAnimateAllDayHeight();
}
});
@ -171,9 +158,9 @@ export class AllDayManager {
// Filter for all-day events
const allDayEvents = events.filter(event => event.allDay);
this.currentLayouts = this.calculateAllDayEventsLayout(allDayEvents, headerReadyEventPayload.headerElements)
const layouts = this.calculateAllDayEventsLayout(allDayEvents, headerReadyEventPayload.headerElements);
this.allDayEventRenderer.renderAllDayEventsForPeriod(this.currentLayouts);
this.allDayEventRenderer.renderAllDayEventsForPeriod(layouts);
this.checkAndAnimateAllDayHeight();
});
@ -194,6 +181,65 @@ export class AllDayManager {
return document.querySelector('swp-header-spacer');
}
/**
* Read current max row from DOM elements
*/
private getMaxRowFromDOM(): number {
const container = this.getAllDayContainer();
if (!container) return 0;
let maxRow = 0;
const allDayEvents = container.querySelectorAll('swp-allday-event:not(.max-event-indicator)');
allDayEvents.forEach((element: Element) => {
const htmlElement = element as HTMLElement;
const row = parseInt(htmlElement.style.gridRow) || 1;
maxRow = Math.max(maxRow, row);
});
return maxRow;
}
/**
* Get current gridArea for an event from DOM
*/
private getGridAreaFromDOM(eventId: string): string | null {
const container = this.getAllDayContainer();
if (!container) return null;
const element = container.querySelector(`[data-event-id="${eventId}"]`) as HTMLElement;
return element?.style.gridArea || null;
}
/**
* Count events in a specific column by reading DOM
*/
private countEventsInColumnFromDOM(columnIndex: number): number {
const container = this.getAllDayContainer();
if (!container) return 0;
let count = 0;
const allDayEvents = container.querySelectorAll('swp-allday-event:not(.max-event-indicator)');
allDayEvents.forEach((element: Element) => {
const htmlElement = element as HTMLElement;
const gridColumn = htmlElement.style.gridColumn;
// Parse "1 / 3" format
const match = gridColumn.match(/(\d+)\s*\/\s*(\d+)/);
if (match) {
const startCol = parseInt(match[1]);
const endCol = parseInt(match[2]) - 1; // End is exclusive in CSS
if (startCol <= columnIndex && endCol >= columnIndex) {
count++;
}
}
});
return count;
}
/**
* Calculate all-day height based on number of rows
*/
@ -221,36 +267,14 @@ export class AllDayManager {
/**
* Check current all-day events and animate to correct height
* Reads max row directly from DOM elements
*/
public checkAndAnimateAllDayHeight(): void {
console.log('📏 AllDayManager: checkAndAnimateAllDayHeight called', {
currentLayoutsCount: this.currentLayouts.length,
layouts: this.currentLayouts.map(l => ({
id: l.calenderEvent.id,
row: l.row,
title: l.calenderEvent.title
}))
});
// Calculate required rows - 0 if no events (will collapse)
let maxRows = 0;
if (this.currentLayouts.length > 0) {
// Find the HIGHEST row number in use from currentLayouts
let highestRow = 0;
this.currentLayouts.forEach((layout) => {
highestRow = Math.max(highestRow, layout.row);
});
// Max rows = highest row number (e.g. if row 3 is used, height = 3 rows)
maxRows = highestRow;
}
// Read max row directly from DOM
const maxRows = this.getMaxRowFromDOM();
console.log('📊 AllDayManager: Height calculation', {
maxRows,
currentLayoutsLength: this.currentLayouts.length,
isExpanded: this.isExpanded
});
@ -508,17 +532,15 @@ export class AllDayManager {
];
// 4. Calculate new layouts for ALL events
this.newLayouts = this.calculateAllDayEventsLayout(tempEvents, this.currentWeekDates);
const newLayouts = this.calculateAllDayEventsLayout(tempEvents, this.currentWeekDates);
// 5. Apply differential updates - only update events that changed
let changedCount = 0;
// 5. Apply differential updates - compare with DOM instead of currentLayouts
let container = this.getAllDayContainer();
this.newLayouts.forEach((layout) => {
// Find current layout for this event
let currentLayout = this.currentLayouts.find(old => old.calenderEvent.id === layout.calenderEvent.id);
newLayouts.forEach((layout) => {
// Get current gridArea from DOM
const currentGridArea = this.getGridAreaFromDOM(layout.calenderEvent.id);
if (currentLayout?.gridArea !== layout.gridArea) {
changedCount++;
if (currentGridArea !== layout.gridArea) {
let element = container?.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement;
if (element) {
@ -542,9 +564,6 @@ 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 = '';
@ -625,18 +644,10 @@ export class AllDayManager {
}
/**
* Count number of events in a specific column using IColumnBounds
* Reads directly from DOM elements
*/
private countEventsInColumn(columnBounds: IColumnBounds): number {
let columnIndex = columnBounds.index;
let count = 0;
this.currentLayouts.forEach((layout) => {
// Check if event spans this column
if (layout.startColumn <= columnIndex && layout.endColumn >= columnIndex) {
count++;
}
});
return count;
return this.countEventsInColumnFromDOM(columnBounds.index);
}
/**