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(); } }