/** * DragDropManager - Advanced drag-and-drop system with smooth animations and event type conversion * * ARCHITECTURE OVERVIEW: * ===================== * DragDropManager provides a sophisticated drag-and-drop system for calendar events that supports: * - Smooth animated dragging with requestAnimationFrame * - Automatic event type conversion (timed events ↔ all-day events) * - Scroll compensation during edge scrolling * - Grid snapping for precise event placement * - Column detection and change tracking * * KEY FEATURES: * ============= * 1. DRAG DETECTION * - Movement threshold (5px) to distinguish clicks from drags * - Immediate visual feedback with cloned element * - Mouse offset tracking for natural drag feel * * 2. SMOOTH ANIMATION * - Uses requestAnimationFrame for 60fps animations * - Interpolated movement (30% per frame) for smooth transitions * - Continuous drag:move events for real-time updates * * 3. EVENT TYPE CONVERSION * - Timed → All-day: When dragging into calendar header * - All-day → Timed: When dragging into day columns * - Automatic clone replacement with appropriate element type * * 4. SCROLL COMPENSATION * - Tracks scroll delta during edge-scrolling * - Compensates dragged element position during scroll * - Prevents visual "jumping" when scrolling while dragging * * 5. GRID SNAPPING * - Snaps to time grid on mouse up * - Uses PositionUtils for consistent positioning * - Accounts for mouse offset within event * * STATE MANAGEMENT: * ================= * Mouse Tracking: * - mouseDownPosition: Initial click position * - currentMousePosition: Latest mouse position * - mouseOffset: Click offset within event (for natural dragging) * * Drag State: * - originalElement: Source event being dragged * - draggedClone: Animated clone following mouse * - currentColumn: Column mouse is currently over * - previousColumn: Last column (for detecting changes) * - isDragStarted: Whether drag threshold exceeded * * Scroll State: * - scrollDeltaY: Accumulated scroll offset during drag * - lastScrollTop: Previous scroll position * - isScrollCompensating: Whether edge-scroll is active * * Animation State: * - dragAnimationId: requestAnimationFrame ID * - targetY: Desired position for smooth interpolation * - currentY: Current interpolated position * * EVENT FLOW: * =========== * 1. Mouse Down (handleMouseDown) * ├─ Store originalElement and mouse offset * └─ Wait for movement * * 2. Mouse Move (handleMouseMove) * ├─ Check movement threshold * ├─ Initialize drag if threshold exceeded (initializeDrag) * │ ├─ Create clone * │ ├─ Emit drag:start * │ └─ Start animation loop * ├─ Continue drag (continueDrag) * │ ├─ Calculate target position with scroll compensation * │ └─ Update animation target * └─ Detect column changes (detectColumnChange) * └─ Emit drag:column-change * * 3. Animation Loop (animateDrag) * ├─ Interpolate currentY toward targetY * ├─ Emit drag:move on each frame * └─ Schedule next frame until target reached * * 4. Event Type Conversion * ├─ Entering header (handleHeaderMouseEnter) * │ ├─ Emit drag:mouseenter-header * │ └─ AllDayManager creates all-day clone * └─ Entering column (handleColumnMouseEnter) * ├─ Emit drag:mouseenter-column * └─ EventRenderingService creates timed clone * * 5. Mouse Up (handleMouseUp) * ├─ Stop animation * ├─ Snap to grid * ├─ Detect drop target (header or column) * ├─ Emit drag:end with final position * └─ Cleanup drag state * * SCROLL COMPENSATION SYSTEM: * =========================== * Problem: When EdgeScrollManager scrolls the grid during drag, the dragged element * can appear to "jump" because the mouse position stays the same but the * coordinate system (scrollable content) has moved. * * Solution: Track cumulative scroll delta and add it to mouse position calculations * * Flow: * 1. EdgeScrollManager starts scrolling → emit edgescroll:started * 2. DragDropManager sets isScrollCompensating = true * 3. On each scroll event: * ├─ Calculate scrollDelta = currentScrollTop - lastScrollTop * ├─ Accumulate into scrollDeltaY * └─ Call continueDrag with adjusted position * 4. continueDrag adds scrollDeltaY to mouse Y coordinate * 5. On event conversion, reset scrollDeltaY (new clone, new coordinate system) * * PERFORMANCE OPTIMIZATIONS: * ========================== * - Uses ColumnDetectionUtils cache for fast column lookups * - Single requestAnimationFrame loop (not per-mousemove) * - Interpolated animation reduces update frequency * - Passive scroll listeners * - Event delegation for header/column detection * * USAGE: * ====== * const dragDropManager = new DragDropManager(eventBus, positionUtils); * // Automatically attaches event listeners and manages drag lifecycle * // Other managers listen to drag:start, drag:move, drag:end, etc. */ import { IEventBus } from '../types/CalendarTypes'; import { PositionUtils } from '../utils/PositionUtils'; export declare class DragDropManager { private eventBus; private mouseDownPosition; private currentMousePosition; private mouseOffset; private originalElement; private draggedClone; private currentColumn; private previousColumn; private originalSourceColumn; private isDragStarted; private readonly dragThreshold; private scrollableContent; private scrollDeltaY; private lastScrollTop; private isScrollCompensating; private dragAnimationId; private targetY; private currentY; private targetColumn; private positionUtils; constructor(eventBus: IEventBus, positionUtils: PositionUtils); /** * Initialize with optimized event listener setup */ private init; private handleGridRendered; private handleMouseDown; private handleMouseMove; /** * Try to initialize drag based on movement threshold * Returns true if drag was initialized, false if not enough movement */ private initializeDrag; private continueDrag; /** * Detect column change and emit event */ private detectColumnChange; /** * Optimized mouse up handler with consolidated cleanup */ private handleMouseUp; private cleanupAllClones; /** * Cancel drag operation when mouse leaves grid container * Animates clone back to original position before cleanup */ private cancelDrag; /** * Optimized snap position calculation using PositionUtils */ private calculateSnapPosition; /** * Smooth drag animation using requestAnimationFrame * Emits drag:move events with current draggedClone reference on each frame */ private animateDrag; /** * Handle scroll during drag - update scrollDeltaY and call continueDrag */ private handleScroll; /** * Stop drag animation */ private stopDragAnimation; /** * Clean up drag state */ private cleanupDragState; /** * Detect drop target - whether dropped in swp-day-column or swp-day-header */ private detectDropTarget; /** * Handle mouse enter on calendar header - simplified using native events */ private handleHeaderMouseEnter; /** * Handle mouse enter on day column - for converting all-day to timed events */ private handleColumnMouseEnter; /** * Handle mouse leave from calendar header - simplified using native events */ private handleHeaderMouseLeave; }