2025-08-27 22:50:13 +02:00
|
|
|
/**
|
2025-09-03 19:05:03 +02:00
|
|
|
* DragDropManager - Optimized drag and drop with consolidated position calculations
|
|
|
|
|
* Reduces redundant DOM queries and improves performance through caching
|
2025-08-27 22:50:13 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { IEventBus } from '../types/CalendarTypes';
|
2025-09-03 20:04:47 +02:00
|
|
|
import { calendarConfig } from '../core/CalendarConfig';
|
2025-09-03 19:05:03 +02:00
|
|
|
import { DateCalculator } from '../utils/DateCalculator';
|
|
|
|
|
|
|
|
|
|
interface CachedElements {
|
|
|
|
|
scrollContainer: HTMLElement | null;
|
|
|
|
|
currentColumn: HTMLElement | null;
|
|
|
|
|
lastColumnDate: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Position {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
}
|
2025-08-27 22:50:13 +02:00
|
|
|
|
|
|
|
|
export class DragDropManager {
|
|
|
|
|
private eventBus: IEventBus;
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Mouse tracking with optimized state
|
2025-08-27 22:50:13 +02:00
|
|
|
private isMouseDown = false;
|
2025-09-03 19:05:03 +02:00
|
|
|
private lastMousePosition: Position = { x: 0, y: 0 };
|
|
|
|
|
private lastLoggedPosition: Position = { x: 0, y: 0 };
|
2025-08-27 22:50:13 +02:00
|
|
|
private currentMouseY = 0;
|
2025-09-03 19:05:03 +02:00
|
|
|
private mouseOffset: Position = { x: 0, y: 0 };
|
2025-08-27 22:50:13 +02:00
|
|
|
|
|
|
|
|
// Drag state
|
|
|
|
|
private draggedEventId: string | null = null;
|
|
|
|
|
private originalElement: HTMLElement | null = null;
|
|
|
|
|
private currentColumn: string | null = null;
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Cached DOM elements for performance
|
|
|
|
|
private cachedElements: CachedElements = {
|
|
|
|
|
scrollContainer: null,
|
|
|
|
|
currentColumn: null,
|
|
|
|
|
lastColumnDate: null
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-27 22:50:13 +02:00
|
|
|
// Auto-scroll properties
|
|
|
|
|
private autoScrollAnimationId: number | null = null;
|
2025-09-03 19:05:03 +02:00
|
|
|
private readonly scrollSpeed = 10; // pixels per frame
|
|
|
|
|
private readonly scrollThreshold = 30; // pixels from edge
|
2025-08-27 22:50:13 +02:00
|
|
|
|
|
|
|
|
// Snap configuration
|
|
|
|
|
private snapIntervalMinutes = 15; // Default 15 minutes
|
|
|
|
|
private hourHeightPx = 60; // From CSS --hour-height
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Event listener references for proper cleanup
|
|
|
|
|
private boundHandlers = {
|
|
|
|
|
mouseMove: this.handleMouseMove.bind(this),
|
|
|
|
|
mouseDown: this.handleMouseDown.bind(this),
|
|
|
|
|
mouseUp: this.handleMouseUp.bind(this)
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-27 22:50:13 +02:00
|
|
|
private get snapDistancePx(): number {
|
|
|
|
|
return (this.snapIntervalMinutes / 60) * this.hourHeightPx;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 20:04:47 +02:00
|
|
|
constructor(eventBus: IEventBus) {
|
2025-08-27 22:50:13 +02:00
|
|
|
this.eventBus = eventBus;
|
2025-09-03 20:04:47 +02:00
|
|
|
|
2025-08-27 22:50:13 +02:00
|
|
|
// Get config values
|
2025-09-03 20:04:47 +02:00
|
|
|
const gridSettings = calendarConfig.getGridSettings();
|
2025-08-27 22:50:13 +02:00
|
|
|
this.hourHeightPx = gridSettings.hourHeight;
|
|
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure snap interval
|
|
|
|
|
*/
|
|
|
|
|
public setSnapInterval(minutes: number): void {
|
|
|
|
|
this.snapIntervalMinutes = minutes;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
/**
|
|
|
|
|
* Initialize with optimized event listener setup
|
|
|
|
|
*/
|
2025-08-27 22:50:13 +02:00
|
|
|
private init(): void {
|
2025-09-03 19:05:03 +02:00
|
|
|
// Use bound handlers for proper cleanup
|
|
|
|
|
document.body.addEventListener('mousemove', this.boundHandlers.mouseMove);
|
|
|
|
|
document.body.addEventListener('mousedown', this.boundHandlers.mouseDown);
|
|
|
|
|
document.body.addEventListener('mouseup', this.boundHandlers.mouseUp);
|
2025-08-27 22:50:13 +02:00
|
|
|
|
|
|
|
|
// Listen for header mouseover events
|
|
|
|
|
this.eventBus.on('header:mouseover', (event) => {
|
|
|
|
|
const { element, targetDate, headerRenderer } = (event as CustomEvent).detail;
|
|
|
|
|
|
|
|
|
|
if (this.isMouseDown && this.draggedEventId && targetDate) {
|
|
|
|
|
// Emit event to convert to all-day
|
|
|
|
|
this.eventBus.emit('drag:convert-to-allday', {
|
|
|
|
|
eventId: this.draggedEventId,
|
|
|
|
|
targetDate,
|
|
|
|
|
element,
|
|
|
|
|
headerRenderer
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private handleMouseDown(event: MouseEvent): void {
|
|
|
|
|
this.isMouseDown = true;
|
|
|
|
|
this.lastMousePosition = { x: event.clientX, y: event.clientY };
|
|
|
|
|
this.lastLoggedPosition = { x: event.clientX, y: event.clientY };
|
|
|
|
|
|
|
|
|
|
// Check if mousedown is on an event
|
|
|
|
|
const target = event.target as HTMLElement;
|
|
|
|
|
let eventElement = target;
|
|
|
|
|
|
|
|
|
|
while (eventElement && eventElement.tagName !== 'SWP-EVENTS-LAYER') {
|
|
|
|
|
if (eventElement.tagName === 'SWP-EVENT' || eventElement.tagName === 'SWP-ALLDAY-EVENT') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
eventElement = eventElement.parentElement as HTMLElement;
|
|
|
|
|
if (!eventElement) return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we reached SWP-EVENTS-LAYER without finding an event, return
|
|
|
|
|
if (!eventElement || eventElement.tagName === 'SWP-EVENTS-LAYER') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Found an event - start dragging
|
|
|
|
|
if (eventElement) {
|
|
|
|
|
this.originalElement = eventElement;
|
|
|
|
|
this.draggedEventId = eventElement.dataset.eventId || null;
|
|
|
|
|
|
|
|
|
|
// Calculate mouse offset within event
|
|
|
|
|
const eventRect = eventElement.getBoundingClientRect();
|
|
|
|
|
this.mouseOffset = {
|
|
|
|
|
x: event.clientX - eventRect.left,
|
|
|
|
|
y: event.clientY - eventRect.top
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Detect current column
|
|
|
|
|
const column = this.detectColumn(event.clientX, event.clientY);
|
|
|
|
|
if (column) {
|
|
|
|
|
this.currentColumn = column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit drag start event
|
|
|
|
|
this.eventBus.emit('drag:start', {
|
|
|
|
|
originalElement: eventElement,
|
|
|
|
|
eventId: this.draggedEventId,
|
|
|
|
|
mousePosition: { x: event.clientX, y: event.clientY },
|
|
|
|
|
mouseOffset: this.mouseOffset,
|
|
|
|
|
column: this.currentColumn
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
/**
|
|
|
|
|
* Optimized mouse move handler with consolidated position calculations
|
|
|
|
|
*/
|
2025-08-27 22:50:13 +02:00
|
|
|
private handleMouseMove(event: MouseEvent): void {
|
|
|
|
|
this.currentMouseY = event.clientY;
|
|
|
|
|
|
|
|
|
|
if (this.isMouseDown && this.draggedEventId) {
|
2025-09-03 19:05:03 +02:00
|
|
|
const currentPosition: Position = { x: event.clientX, y: event.clientY };
|
|
|
|
|
const deltaY = Math.abs(currentPosition.y - this.lastLoggedPosition.y);
|
2025-08-27 22:50:13 +02:00
|
|
|
|
|
|
|
|
// Check for snap interval vertical movement
|
|
|
|
|
if (deltaY >= this.snapDistancePx) {
|
2025-09-03 19:05:03 +02:00
|
|
|
this.lastLoggedPosition = currentPosition;
|
2025-08-27 22:50:13 +02:00
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Consolidated position calculations
|
|
|
|
|
const positionData = this.calculateDragPosition(currentPosition);
|
2025-08-27 22:50:13 +02:00
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Emit drag move event with consolidated data
|
2025-08-27 22:50:13 +02:00
|
|
|
this.eventBus.emit('drag:move', {
|
|
|
|
|
eventId: this.draggedEventId,
|
2025-09-03 19:05:03 +02:00
|
|
|
mousePosition: currentPosition,
|
|
|
|
|
snappedY: positionData.snappedY,
|
|
|
|
|
column: positionData.column,
|
2025-08-27 22:50:13 +02:00
|
|
|
mouseOffset: this.mouseOffset
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for auto-scroll
|
|
|
|
|
this.checkAutoScroll(event);
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Check for column change using cached data
|
|
|
|
|
const newColumn = this.getColumnFromCache(currentPosition);
|
2025-08-27 22:50:13 +02:00
|
|
|
if (newColumn && newColumn !== this.currentColumn) {
|
2025-09-03 19:05:03 +02:00
|
|
|
const previousColumn = this.currentColumn;
|
2025-08-27 22:50:13 +02:00
|
|
|
this.currentColumn = newColumn;
|
|
|
|
|
|
|
|
|
|
this.eventBus.emit('drag:column-change', {
|
|
|
|
|
eventId: this.draggedEventId,
|
2025-09-03 19:05:03 +02:00
|
|
|
previousColumn,
|
2025-08-27 22:50:13 +02:00
|
|
|
newColumn,
|
2025-09-03 19:05:03 +02:00
|
|
|
mousePosition: currentPosition
|
2025-08-27 22:50:13 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
/**
|
|
|
|
|
* Optimized mouse up handler with consolidated cleanup
|
|
|
|
|
*/
|
2025-08-27 22:50:13 +02:00
|
|
|
private handleMouseUp(event: MouseEvent): void {
|
|
|
|
|
if (!this.isMouseDown) return;
|
|
|
|
|
|
|
|
|
|
this.isMouseDown = false;
|
|
|
|
|
this.stopAutoScroll();
|
|
|
|
|
|
|
|
|
|
if (this.draggedEventId && this.originalElement) {
|
2025-09-03 19:05:03 +02:00
|
|
|
const finalPosition: Position = { x: event.clientX, y: event.clientY };
|
|
|
|
|
|
|
|
|
|
// Use consolidated position calculation
|
|
|
|
|
const positionData = this.calculateDragPosition(finalPosition);
|
2025-08-27 22:50:13 +02:00
|
|
|
|
|
|
|
|
// Emit drag end event
|
|
|
|
|
this.eventBus.emit('drag:end', {
|
|
|
|
|
eventId: this.draggedEventId,
|
|
|
|
|
originalElement: this.originalElement,
|
2025-09-03 19:05:03 +02:00
|
|
|
finalPosition,
|
|
|
|
|
finalColumn: positionData.column,
|
|
|
|
|
finalY: positionData.snappedY
|
2025-08-27 22:50:13 +02:00
|
|
|
});
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Clean up drag state
|
|
|
|
|
this.cleanupDragState();
|
2025-08-27 22:50:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-03 19:05:03 +02:00
|
|
|
* Consolidated position calculation method
|
2025-08-27 22:50:13 +02:00
|
|
|
*/
|
2025-09-03 19:05:03 +02:00
|
|
|
private calculateDragPosition(mousePosition: Position): { column: string | null; snappedY: number } {
|
|
|
|
|
const column = this.detectColumn(mousePosition.x, mousePosition.y);
|
|
|
|
|
const snappedY = this.calculateSnapPosition(mousePosition.y, column);
|
|
|
|
|
|
|
|
|
|
return { column, snappedY };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Optimized snap position calculation with caching
|
|
|
|
|
*/
|
|
|
|
|
private calculateSnapPosition(mouseY: number, column: string | null = null): number {
|
|
|
|
|
const targetColumn = column || this.currentColumn;
|
|
|
|
|
|
|
|
|
|
// Use cached column element if available
|
|
|
|
|
const columnElement = this.getCachedColumnElement(targetColumn);
|
2025-08-27 22:50:13 +02:00
|
|
|
if (!columnElement) return mouseY;
|
|
|
|
|
|
|
|
|
|
const columnRect = columnElement.getBoundingClientRect();
|
|
|
|
|
const relativeY = mouseY - columnRect.top - this.mouseOffset.y;
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
// Snap to nearest interval using DateCalculator precision
|
2025-08-27 22:50:13 +02:00
|
|
|
const snappedY = Math.round(relativeY / this.snapDistancePx) * this.snapDistancePx;
|
|
|
|
|
|
|
|
|
|
return Math.max(0, snappedY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-03 19:05:03 +02:00
|
|
|
* Optimized column detection with caching
|
2025-08-27 22:50:13 +02:00
|
|
|
*/
|
|
|
|
|
private detectColumn(mouseX: number, mouseY: number): string | null {
|
|
|
|
|
const element = document.elementFromPoint(mouseX, mouseY);
|
|
|
|
|
if (!element) return null;
|
|
|
|
|
|
|
|
|
|
// Walk up DOM tree to find swp-day-column
|
|
|
|
|
let current = element as HTMLElement;
|
|
|
|
|
while (current && current.tagName !== 'SWP-DAY-COLUMN') {
|
|
|
|
|
current = current.parentElement as HTMLElement;
|
|
|
|
|
if (!current) return null;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
const columnDate = current.dataset.date || null;
|
|
|
|
|
|
|
|
|
|
// Update cache if we found a new column
|
|
|
|
|
if (columnDate && columnDate !== this.cachedElements.lastColumnDate) {
|
|
|
|
|
this.cachedElements.currentColumn = current;
|
|
|
|
|
this.cachedElements.lastColumnDate = columnDate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return columnDate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get column from cache or detect new one
|
|
|
|
|
*/
|
|
|
|
|
private getColumnFromCache(mousePosition: Position): string | null {
|
|
|
|
|
// Try to use cached column first
|
|
|
|
|
if (this.cachedElements.currentColumn && this.cachedElements.lastColumnDate) {
|
|
|
|
|
const rect = this.cachedElements.currentColumn.getBoundingClientRect();
|
|
|
|
|
if (mousePosition.x >= rect.left && mousePosition.x <= rect.right) {
|
|
|
|
|
return this.cachedElements.lastColumnDate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cache miss - detect new column
|
|
|
|
|
return this.detectColumn(mousePosition.x, mousePosition.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get cached column element or query for new one
|
|
|
|
|
*/
|
|
|
|
|
private getCachedColumnElement(columnDate: string | null): HTMLElement | null {
|
|
|
|
|
if (!columnDate) return null;
|
|
|
|
|
|
|
|
|
|
// Return cached element if it matches
|
|
|
|
|
if (this.cachedElements.lastColumnDate === columnDate && this.cachedElements.currentColumn) {
|
|
|
|
|
return this.cachedElements.currentColumn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Query for new element and cache it
|
|
|
|
|
const element = document.querySelector(`swp-day-column[data-date="${columnDate}"]`) as HTMLElement;
|
|
|
|
|
if (element) {
|
|
|
|
|
this.cachedElements.currentColumn = element;
|
|
|
|
|
this.cachedElements.lastColumnDate = columnDate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return element;
|
2025-08-27 22:50:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-03 19:05:03 +02:00
|
|
|
* Optimized auto-scroll check with cached container
|
2025-08-27 22:50:13 +02:00
|
|
|
*/
|
|
|
|
|
private checkAutoScroll(event: MouseEvent): void {
|
2025-09-03 19:05:03 +02:00
|
|
|
// Use cached scroll container
|
|
|
|
|
if (!this.cachedElements.scrollContainer) {
|
|
|
|
|
this.cachedElements.scrollContainer = document.querySelector('swp-scrollable-content') as HTMLElement;
|
|
|
|
|
if (!this.cachedElements.scrollContainer) {
|
2025-08-27 22:50:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 19:05:03 +02:00
|
|
|
const containerRect = this.cachedElements.scrollContainer.getBoundingClientRect();
|
2025-08-27 22:50:13 +02:00
|
|
|
const mouseY = event.clientY;
|
|
|
|
|
|
|
|
|
|
// Calculate distances from edges
|
|
|
|
|
const distanceFromTop = mouseY - containerRect.top;
|
|
|
|
|
const distanceFromBottom = containerRect.bottom - mouseY;
|
|
|
|
|
|
|
|
|
|
// Check if we need to scroll
|
|
|
|
|
if (distanceFromTop <= this.scrollThreshold && distanceFromTop > 0) {
|
|
|
|
|
this.startAutoScroll('up');
|
|
|
|
|
} else if (distanceFromBottom <= this.scrollThreshold && distanceFromBottom > 0) {
|
|
|
|
|
this.startAutoScroll('down');
|
|
|
|
|
} else {
|
|
|
|
|
this.stopAutoScroll();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-03 19:05:03 +02:00
|
|
|
* Optimized auto-scroll with cached container reference
|
2025-08-27 22:50:13 +02:00
|
|
|
*/
|
|
|
|
|
private startAutoScroll(direction: 'up' | 'down'): void {
|
|
|
|
|
if (this.autoScrollAnimationId !== null) return;
|
|
|
|
|
|
|
|
|
|
const scroll = () => {
|
2025-09-03 19:05:03 +02:00
|
|
|
if (!this.cachedElements.scrollContainer || !this.isMouseDown) {
|
2025-08-27 22:50:13 +02:00
|
|
|
this.stopAutoScroll();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const scrollAmount = direction === 'up' ? -this.scrollSpeed : this.scrollSpeed;
|
2025-09-03 19:05:03 +02:00
|
|
|
this.cachedElements.scrollContainer.scrollTop += scrollAmount;
|
2025-08-27 22:50:13 +02:00
|
|
|
|
2025-09-03 20:13:56 +02:00
|
|
|
// Emit updated position during scroll - adjust for scroll movement
|
2025-08-27 22:50:13 +02:00
|
|
|
if (this.draggedEventId) {
|
2025-09-03 20:13:56 +02:00
|
|
|
// During autoscroll, we need to calculate position relative to the scrolled content
|
|
|
|
|
// The mouse hasn't moved, but the content has scrolled
|
|
|
|
|
const columnElement = this.getCachedColumnElement(this.currentColumn);
|
|
|
|
|
if (columnElement) {
|
|
|
|
|
const columnRect = columnElement.getBoundingClientRect();
|
|
|
|
|
// Calculate position relative to column, accounting for scroll movement
|
|
|
|
|
const relativeY = this.currentMouseY - columnRect.top - this.mouseOffset.y;
|
|
|
|
|
const snappedY = Math.round(relativeY / this.snapDistancePx) * this.snapDistancePx;
|
|
|
|
|
const finalSnappedY = Math.max(0, snappedY);
|
|
|
|
|
|
|
|
|
|
this.eventBus.emit('drag:auto-scroll', {
|
|
|
|
|
eventId: this.draggedEventId,
|
|
|
|
|
snappedY: finalSnappedY,
|
|
|
|
|
scrollTop: this.cachedElements.scrollContainer.scrollTop
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-08-27 22:50:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.autoScrollAnimationId = requestAnimationFrame(scroll);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.autoScrollAnimationId = requestAnimationFrame(scroll);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stop auto-scroll animation
|
|
|
|
|
*/
|
|
|
|
|
private stopAutoScroll(): void {
|
|
|
|
|
if (this.autoScrollAnimationId !== null) {
|
|
|
|
|
cancelAnimationFrame(this.autoScrollAnimationId);
|
|
|
|
|
this.autoScrollAnimationId = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-03 19:05:03 +02:00
|
|
|
* Clean up drag state
|
|
|
|
|
*/
|
|
|
|
|
private cleanupDragState(): void {
|
|
|
|
|
this.draggedEventId = null;
|
|
|
|
|
this.originalElement = null;
|
|
|
|
|
this.currentColumn = null;
|
|
|
|
|
|
|
|
|
|
// Clear cached elements
|
|
|
|
|
this.cachedElements.currentColumn = null;
|
|
|
|
|
this.cachedElements.lastColumnDate = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clean up all resources and event listeners
|
2025-08-27 22:50:13 +02:00
|
|
|
*/
|
|
|
|
|
public destroy(): void {
|
|
|
|
|
this.stopAutoScroll();
|
2025-09-03 19:05:03 +02:00
|
|
|
|
|
|
|
|
// Remove event listeners using bound references
|
|
|
|
|
document.body.removeEventListener('mousemove', this.boundHandlers.mouseMove);
|
|
|
|
|
document.body.removeEventListener('mousedown', this.boundHandlers.mouseDown);
|
|
|
|
|
document.body.removeEventListener('mouseup', this.boundHandlers.mouseUp);
|
|
|
|
|
|
|
|
|
|
// Clear all cached elements
|
|
|
|
|
this.cachedElements.scrollContainer = null;
|
|
|
|
|
this.cachedElements.currentColumn = null;
|
|
|
|
|
this.cachedElements.lastColumnDate = null;
|
|
|
|
|
|
|
|
|
|
// Clean up drag state
|
|
|
|
|
this.cleanupDragState();
|
2025-08-27 22:50:13 +02:00
|
|
|
}
|
|
|
|
|
}
|