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.
This commit is contained in:
parent
6223bcd176
commit
cf463cc691
1 changed files with 33 additions and 74 deletions
|
|
@ -24,15 +24,17 @@ export class AllDayManager {
|
|||
private layoutEngine: AllDayLayoutEngine | null = null;
|
||||
|
||||
// State tracking for differential updates
|
||||
private currentLayouts: Map<string, string> = 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 = '';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue