Calendar/src/managers/DragHoverManager.ts

117 lines
3.5 KiB
TypeScript
Raw Normal View History

/**
* 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<HTMLElement>('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();
}
}