Calendar/src/managers/HeaderManager.ts

139 lines
4.3 KiB
TypeScript
Raw Normal View History

import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
/**
* HeaderManager - Handles all header-related event logic
* Separates event handling from rendering concerns
*/
export class HeaderManager {
private headerEventListener: ((event: Event) => void) | null = null;
private headerMouseLeaveListener: ((event: Event) => void) | null = null;
private cachedCalendarHeader: HTMLElement | null = null;
constructor() {
// Bind methods for event listeners
this.setupHeaderDragListeners = this.setupHeaderDragListeners.bind(this);
this.destroy = this.destroy.bind(this);
}
/**
* Get cached calendar header element
*/
private getCalendarHeader(): HTMLElement | null {
if (!this.cachedCalendarHeader) {
this.cachedCalendarHeader = document.querySelector('swp-calendar-header');
}
return this.cachedCalendarHeader;
}
/**
* Setup header drag event listeners
*/
public setupHeaderDragListeners(): void {
const calendarHeader = this.getCalendarHeader();
if (!calendarHeader) return;
// Clean up existing listeners first
this.removeEventListeners();
// Throttle for better performance
let lastEmitTime = 0;
const throttleDelay = 16; // ~60fps
this.headerEventListener = (event: Event) => {
const now = Date.now();
if (now - lastEmitTime < throttleDelay) {
return; // Throttle events for better performance
}
lastEmitTime = now;
const target = event.target as HTMLElement;
// 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;
// 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;
}
// Get header renderer for coordination
const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
eventBus.emit('header:mouseover', {
element: hoveredElement,
targetDate,
headerRenderer
});
}
};
// Header mouseleave listener
this.headerMouseLeaveListener = (event: Event) => {
eventBus.emit('header:mouseleave', {
element: event.target as HTMLElement
});
};
// Add event listeners
calendarHeader.addEventListener('mouseover', this.headerEventListener);
calendarHeader.addEventListener('mouseleave', this.headerMouseLeaveListener);
}
/**
* Remove event listeners from header
*/
private removeEventListeners(): void {
const calendarHeader = this.getCalendarHeader();
if (!calendarHeader) return;
if (this.headerEventListener) {
calendarHeader.removeEventListener('mouseover', this.headerEventListener);
}
if (this.headerMouseLeaveListener) {
calendarHeader.removeEventListener('mouseleave', this.headerMouseLeaveListener);
}
}
/**
* Clear cached header reference
*/
public clearCache(): void {
this.cachedCalendarHeader = null;
}
/**
* Clean up resources and event listeners
*/
public destroy(): void {
this.removeEventListeners();
// Clear references
this.headerEventListener = null;
this.headerMouseLeaveListener = null;
this.clearCache();
}
}