/** * DragHoverManager - Handles event hover tracking * Fully autonomous - listens to mouse events and manages hover state independently */ import { IEventBus } from '../types/CalendarTypes'; export class DragHoverManager { private isHoverTrackingActive = false; private currentHoveredEvent: HTMLElement | null = null; private calendarContainer: HTMLElement | null = null; constructor(private eventBus: IEventBus) { this.init(); } private init(): void { // Wait for DOM to be ready setTimeout(() => { this.calendarContainer = document.querySelector('swp-calendar-container'); if (this.calendarContainer) { this.setupEventListeners(); } }, 100); // Listen to drag start to deactivate hover tracking this.eventBus.on('drag:start', () => { this.deactivateTracking(); }); } private setupEventListeners(): void { if (!this.calendarContainer) return; // Listen to mouseenter on events (using event delegation) this.calendarContainer.addEventListener('mouseenter', (e) => { const target = e.target as HTMLElement; const eventElement = target.closest('swp-event'); if (eventElement) { this.handleEventMouseEnter(e as MouseEvent, eventElement); } }, true); // Use capture phase // Listen to mousemove globally to track when mouse leaves event bounds document.body.addEventListener('mousemove', (e: MouseEvent) => { if (this.isHoverTrackingActive && e.buttons === 0) { this.checkEventHover(e); } }); } /** * Handle mouse enter on swp-event - activate hover tracking */ private handleEventMouseEnter(event: MouseEvent, eventElement: HTMLElement): void { // Only handle hover if mouse button is up if (event.buttons === 0) { // Clear any previous hover first if (this.currentHoveredEvent && this.currentHoveredEvent !== eventElement) { this.currentHoveredEvent.classList.remove('hover'); } this.isHoverTrackingActive = true; this.currentHoveredEvent = eventElement; eventElement.classList.add('hover'); this.eventBus.emit('event:hover:start', { element: eventElement }); } } /** * Check if mouse is still over the currently hovered event */ private checkEventHover(event: MouseEvent): void { // Only track hover when active and mouse button is up if (!this.isHoverTrackingActive || !this.currentHoveredEvent) return; const rect = this.currentHoveredEvent.getBoundingClientRect(); const mouseX = event.clientX; const mouseY = event.clientY; // Check if mouse is still within the current hovered event const isStillInside = mouseX >= rect.left && mouseX <= rect.right && mouseY >= rect.top && mouseY <= rect.bottom; // If mouse left the event if (!isStillInside) { // Only disable tracking and clear if mouse is NOT pressed (allow resize to work) if (event.buttons === 0) { this.isHoverTrackingActive = false; this.clearEventHover(); } } } /** * Clear hover state */ private clearEventHover(): void { if (this.currentHoveredEvent) { this.currentHoveredEvent.classList.remove('hover'); this.eventBus.emit('event:hover:end', { element: this.currentHoveredEvent }); this.currentHoveredEvent = null; } } /** * Deactivate hover tracking and clear any current hover * Called via event bus when drag starts */ private deactivateTracking(): void { this.isHoverTrackingActive = false; this.clearEventHover(); } }