Enhances drag and drop functionality

Improves drag and drop event handling, including conversion between all-day and timed events.

Introduces HeaderManager to handle header-related event logic and
centralizes header event handling for better code organization and
separation of concerns.

Optimizes event listeners and throttles events for improved performance.
Removes redundant code and improves the overall drag and drop
experience.
This commit is contained in:
Janus Knudsen 2025-09-10 22:07:40 +02:00
parent d087e333fe
commit 3bd74d6f4e
6 changed files with 418 additions and 170 deletions

View file

@ -11,7 +11,6 @@ import { DateCalculator } from '../utils/DateCalculator';
* Optimized to reduce redundant DOM operations and improve performance
*/
export class GridRenderer {
private headerEventListener: ((event: Event) => void) | null = null;
private cachedGridContainer: HTMLElement | null = null;
private cachedCalendarHeader: HTMLElement | null = null;
private cachedTimeAxis: HTMLElement | null = null;
@ -158,8 +157,8 @@ export class GridRenderer {
// Always ensure all-day containers exist for all days
headerRenderer.ensureAllDayContainers(calendarHeader);
// Setup optimized event listener
this.setupOptimizedHeaderEventListener(calendarHeader);
// Setup only grid-related event listeners
this.setupGridEventListeners();
}
/**
@ -209,83 +208,74 @@ export class GridRenderer {
}
/**
* Setup optimized event delegation listener with better performance
* Setup grid-only event listeners (column events)
*/
private setupOptimizedHeaderEventListener(calendarHeader: HTMLElement): void {
// Remove existing listener if any
if (this.headerEventListener) {
calendarHeader.removeEventListener('mouseover', this.headerEventListener);
}
private setupGridEventListeners(): void {
// Setup grid body mouseover listener for all-day to timed conversion
this.setupGridBodyMouseOver();
}
// Create optimized listener with throttling
/**
* Setup grid body mouseover listener for all-day to timed conversion
*/
private setupGridBodyMouseOver(): void {
const grid = this.cachedGridContainer;
if (!grid) return;
const columnContainer = grid.querySelector('swp-day-columns');
if (!columnContainer) return;
// Throttle for better performance
let lastEmitTime = 0;
const throttleDelay = 16; // ~60fps
this.headerEventListener = (event) => {
const gridBodyEventListener = (event: Event) => {
const now = Date.now();
if (now - lastEmitTime < throttleDelay) {
return; // Throttle events for better performance
return;
}
lastEmitTime = now;
const target = event.target as HTMLElement;
const dayColumn = target.closest('swp-day-column');
// Optimized element detection
const dayHeader = target.closest('swp-day-header');
const allDayContainer = target.closest('swp-allday-container');
if (dayHeader || allDayContainer) {
let hoveredElement: HTMLElement;
let targetDate: string | undefined;
if (dayHeader) {
hoveredElement = dayHeader as HTMLElement;
targetDate = hoveredElement.dataset.date;
} else if (allDayContainer) {
hoveredElement = allDayContainer as HTMLElement;
if (dayColumn) {
const targetColumn = (dayColumn as HTMLElement).dataset.date;
if (targetColumn) {
// Calculate Y position relative to the column
const columnRect = dayColumn.getBoundingClientRect();
const mouseY = (event as MouseEvent).clientY;
const targetY = mouseY - columnRect.top;
// Optimized day calculation using cached header rect
const headerRect = calendarHeader.getBoundingClientRect();
const dayHeaders = calendarHeader.querySelectorAll('swp-day-header');
const mouseX = (event as MouseEvent).clientX - headerRect.left;
const dayWidth = headerRect.width / dayHeaders.length;
const dayIndex = Math.max(0, Math.min(dayHeaders.length - 1, Math.floor(mouseX / dayWidth)));
const targetDayHeader = dayHeaders[dayIndex] as HTMLElement;
targetDate = targetDayHeader?.dataset.date;
} else {
return;
eventBus.emit('column:mouseover', {
targetColumn,
targetY
});
}
// Get header renderer once and cache
const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
eventBus.emit('header:mouseover', {
element: hoveredElement,
targetDate,
headerRenderer
});
}
};
// Add the optimized listener
calendarHeader.addEventListener('mouseover', this.headerEventListener);
columnContainer.addEventListener('mouseover', gridBodyEventListener);
// Store reference for cleanup
(this as any).gridBodyEventListener = gridBodyEventListener;
(this as any).cachedColumnContainer = columnContainer;
}
/**
* Clean up cached elements and event listeners
*/
public destroy(): void {
// Clean up event listeners
if (this.headerEventListener && this.cachedCalendarHeader) {
this.cachedCalendarHeader.removeEventListener('mouseover', this.headerEventListener);
// Clean up grid-only event listeners
if ((this as any).gridBodyEventListener && (this as any).cachedColumnContainer) {
(this as any).cachedColumnContainer.removeEventListener('mouseover', (this as any).gridBodyEventListener);
}
// Clear cached references
this.cachedGridContainer = null;
this.cachedCalendarHeader = null;
this.cachedTimeAxis = null;
this.headerEventListener = null;
(this as any).gridBodyEventListener = null;
(this as any).cachedColumnContainer = null;
}
}