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:
parent
4cc110d9f2
commit
2656bae054
3 changed files with 83 additions and 67 deletions
|
|
@ -33,11 +33,9 @@ export class AllDayManager {
|
||||||
|
|
||||||
private layoutEngine: AllDayLayoutEngine | null = null;
|
private layoutEngine: AllDayLayoutEngine | null = null;
|
||||||
|
|
||||||
// State tracking for differential updates
|
// State tracking for layout calculation
|
||||||
private currentLayouts: IEventLayout[] = [];
|
|
||||||
private currentAllDayEvents: ICalendarEvent[] = [];
|
private currentAllDayEvents: ICalendarEvent[] = [];
|
||||||
private currentWeekDates: IColumnBounds[] = [];
|
private currentWeekDates: IColumnBounds[] = [];
|
||||||
private newLayouts: IEventLayout[] = [];
|
|
||||||
|
|
||||||
// Expand/collapse state
|
// Expand/collapse state
|
||||||
private isExpanded: boolean = false;
|
private isExpanded: boolean = false;
|
||||||
|
|
@ -128,23 +126,12 @@ export class AllDayManager {
|
||||||
if (dragEndPayload.target === 'swp-day-column' && dragEndPayload.originalElement?.hasAttribute('data-allday')) {
|
if (dragEndPayload.target === 'swp-day-column' && dragEndPayload.originalElement?.hasAttribute('data-allday')) {
|
||||||
const eventId = dragEndPayload.originalElement.dataset.eventId;
|
const eventId = dragEndPayload.originalElement.dataset.eventId;
|
||||||
|
|
||||||
console.log('🔄 AllDayManager: All-day → timed conversion', {
|
console.log('🔄 AllDayManager: All-day → timed conversion', { eventId });
|
||||||
eventId,
|
|
||||||
currentLayoutsCount: this.currentLayouts.length,
|
|
||||||
layoutsBeforeFilter: this.currentLayouts.map(l => l.calenderEvent.id)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove event from currentLayouts since it's now a timed event
|
// No need to filter currentLayouts - DOM is source of truth!
|
||||||
this.currentLayouts = this.currentLayouts.filter(
|
// The element is already removed from all-day container by DragDropManager
|
||||||
layout => layout.calenderEvent.id !== eventId
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('📊 AllDayManager: After filter', {
|
// Recalculate and animate header height based on remaining DOM elements
|
||||||
currentLayoutsCount: this.currentLayouts.length,
|
|
||||||
layoutsAfterFilter: this.currentLayouts.map(l => l.calenderEvent.id)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Recalculate and animate header height
|
|
||||||
this.checkAndAnimateAllDayHeight();
|
this.checkAndAnimateAllDayHeight();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -171,9 +158,9 @@ export class AllDayManager {
|
||||||
// Filter for all-day events
|
// Filter for all-day events
|
||||||
const allDayEvents = events.filter(event => event.allDay);
|
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();
|
this.checkAndAnimateAllDayHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -194,6 +181,65 @@ export class AllDayManager {
|
||||||
return document.querySelector('swp-header-spacer');
|
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
|
* 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
|
* Check current all-day events and animate to correct height
|
||||||
|
* Reads max row directly from DOM elements
|
||||||
*/
|
*/
|
||||||
public checkAndAnimateAllDayHeight(): void {
|
public checkAndAnimateAllDayHeight(): void {
|
||||||
console.log('📏 AllDayManager: checkAndAnimateAllDayHeight called', {
|
// Read max row directly from DOM
|
||||||
currentLayoutsCount: this.currentLayouts.length,
|
const maxRows = this.getMaxRowFromDOM();
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('📊 AllDayManager: Height calculation', {
|
console.log('📊 AllDayManager: Height calculation', {
|
||||||
maxRows,
|
maxRows,
|
||||||
currentLayoutsLength: this.currentLayouts.length,
|
|
||||||
isExpanded: this.isExpanded
|
isExpanded: this.isExpanded
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -508,17 +532,15 @@ export class AllDayManager {
|
||||||
];
|
];
|
||||||
|
|
||||||
// 4. Calculate new layouts for ALL events
|
// 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
|
// 5. Apply differential updates - compare with DOM instead of currentLayouts
|
||||||
let changedCount = 0;
|
|
||||||
let container = this.getAllDayContainer();
|
let container = this.getAllDayContainer();
|
||||||
this.newLayouts.forEach((layout) => {
|
newLayouts.forEach((layout) => {
|
||||||
// Find current layout for this event
|
// Get current gridArea from DOM
|
||||||
let currentLayout = this.currentLayouts.find(old => old.calenderEvent.id === layout.calenderEvent.id);
|
const currentGridArea = this.getGridAreaFromDOM(layout.calenderEvent.id);
|
||||||
|
|
||||||
if (currentLayout?.gridArea !== layout.gridArea) {
|
if (currentGridArea !== layout.gridArea) {
|
||||||
changedCount++;
|
|
||||||
let element = container?.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement;
|
let element = container?.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement;
|
||||||
if (element) {
|
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
|
// 6. Clean up drag styles from the dropped clone
|
||||||
dragEndEvent.draggedClone.classList.remove('dragging');
|
dragEndEvent.draggedClone.classList.remove('dragging');
|
||||||
dragEndEvent.draggedClone.style.zIndex = '';
|
dragEndEvent.draggedClone.style.zIndex = '';
|
||||||
|
|
@ -625,18 +644,10 @@ export class AllDayManager {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Count number of events in a specific column using IColumnBounds
|
* Count number of events in a specific column using IColumnBounds
|
||||||
|
* Reads directly from DOM elements
|
||||||
*/
|
*/
|
||||||
private countEventsInColumn(columnBounds: IColumnBounds): number {
|
private countEventsInColumn(columnBounds: IColumnBounds): number {
|
||||||
let columnIndex = columnBounds.index;
|
return this.countEventsInColumnFromDOM(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -197,6 +197,11 @@ swp-calendar-header {
|
||||||
gap: 2px 0px;
|
gap: 2px 0px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
/* Border only when events exist */
|
||||||
|
&:has(swp-allday-event) {
|
||||||
|
border-bottom: 1px solid var(--color-grid-line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue