/** * EdgeScrollManager - Auto-scroll when dragging near edges * Uses time-based scrolling with 2-zone system for variable speed */ export class EdgeScrollManager { constructor(eventBus) { this.eventBus = eventBus; this.scrollableContent = null; this.timeGrid = null; this.draggedClone = null; this.scrollRAF = null; this.mouseY = 0; this.isDragging = false; this.isScrolling = false; // Track if edge-scroll is active this.lastTs = 0; this.rect = null; this.initialScrollTop = 0; this.scrollListener = null; // Constants - fixed values as per requirements this.OUTER_ZONE = 100; // px from edge (slow zone) this.INNER_ZONE = 50; // px from edge (fast zone) this.SLOW_SPEED_PXS = 140; // px/sec in outer zone this.FAST_SPEED_PXS = 640; // px/sec in inner zone this.init(); } init() { // Wait for DOM to be ready setTimeout(() => { this.scrollableContent = document.querySelector('swp-scrollable-content'); this.timeGrid = document.querySelector('swp-time-grid'); if (this.scrollableContent) { // Disable smooth scroll for instant auto-scroll this.scrollableContent.style.scrollBehavior = 'auto'; // Add scroll listener to detect actual scrolling this.scrollListener = this.handleScroll.bind(this); this.scrollableContent.addEventListener('scroll', this.scrollListener, { passive: true }); } }, 100); // Listen to mousemove directly from document to always get mouse coords document.body.addEventListener('mousemove', (e) => { if (this.isDragging) { this.mouseY = e.clientY; } }); this.subscribeToEvents(); } subscribeToEvents() { // Listen to drag events from DragDropManager this.eventBus.on('drag:start', (event) => { const payload = event.detail; this.draggedClone = payload.draggedClone; this.startDrag(); }); this.eventBus.on('drag:end', () => this.stopDrag()); this.eventBus.on('drag:cancelled', () => this.stopDrag()); // Stop scrolling when event converts to/from all-day this.eventBus.on('drag:mouseenter-header', () => { console.log('🔄 EdgeScrollManager: Event converting to all-day - stopping scroll'); this.stopDrag(); }); this.eventBus.on('drag:mouseenter-column', () => { this.startDrag(); }); } startDrag() { console.log('🎬 EdgeScrollManager: Starting drag'); this.isDragging = true; this.isScrolling = false; // Reset scroll state this.lastTs = performance.now(); // Save initial scroll position if (this.scrollableContent) { this.initialScrollTop = this.scrollableContent.scrollTop; } if (this.scrollRAF === null) { this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); } } stopDrag() { this.isDragging = false; // Emit stopped event if we were scrolling if (this.isScrolling) { this.isScrolling = false; console.log('🛑 EdgeScrollManager: Edge-scroll stopped (drag ended)'); this.eventBus.emit('edgescroll:stopped', {}); } if (this.scrollRAF !== null) { cancelAnimationFrame(this.scrollRAF); this.scrollRAF = null; } this.rect = null; this.lastTs = 0; this.initialScrollTop = 0; } handleScroll() { if (!this.isDragging || !this.scrollableContent) return; const currentScrollTop = this.scrollableContent.scrollTop; const scrollDelta = Math.abs(currentScrollTop - this.initialScrollTop); // Only emit started event if we've actually scrolled more than 1px if (scrollDelta > 1 && !this.isScrolling) { this.isScrolling = true; console.log('💾 EdgeScrollManager: Edge-scroll started (actual scroll detected)', { initialScrollTop: this.initialScrollTop, currentScrollTop, scrollDelta }); this.eventBus.emit('edgescroll:started', {}); } } scrollTick(ts) { const dt = this.lastTs ? (ts - this.lastTs) / 1000 : 0; this.lastTs = ts; if (!this.scrollableContent) { this.stopDrag(); return; } // Cache rect for performance (only measure once per frame) if (!this.rect) { this.rect = this.scrollableContent.getBoundingClientRect(); } let vy = 0; if (this.isDragging) { const distTop = this.mouseY - this.rect.top; const distBot = this.rect.bottom - this.mouseY; // Check top edge if (distTop < this.INNER_ZONE) { vy = -this.FAST_SPEED_PXS; } else if (distTop < this.OUTER_ZONE) { vy = -this.SLOW_SPEED_PXS; } // Check bottom edge else if (distBot < this.INNER_ZONE) { vy = this.FAST_SPEED_PXS; } else if (distBot < this.OUTER_ZONE) { vy = this.SLOW_SPEED_PXS; } } if (vy !== 0 && this.isDragging && this.timeGrid && this.draggedClone) { // Check if we can scroll in the requested direction const currentScrollTop = this.scrollableContent.scrollTop; const scrollableHeight = this.scrollableContent.clientHeight; const timeGridHeight = this.timeGrid.clientHeight; // Get dragged element position and height const cloneRect = this.draggedClone.getBoundingClientRect(); const cloneBottom = cloneRect.bottom; const timeGridRect = this.timeGrid.getBoundingClientRect(); const timeGridBottom = timeGridRect.bottom; // Check boundaries const atTop = currentScrollTop <= 0 && vy < 0; const atBottom = (cloneBottom >= timeGridBottom) && vy > 0; if (atTop || atBottom) { // At boundary - stop scrolling if (this.isScrolling) { this.isScrolling = false; this.initialScrollTop = this.scrollableContent.scrollTop; console.log('🛑 EdgeScrollManager: Edge-scroll stopped (reached boundary)'); this.eventBus.emit('edgescroll:stopped', {}); } // Continue RAF loop to detect when mouse moves away from boundary if (this.isDragging) { this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); } } else { // Not at boundary - apply scroll this.scrollableContent.scrollTop += vy * dt; this.rect = null; // Invalidate cache for next frame this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); } } else { // Mouse moved away from edge - stop scrolling if (this.isScrolling) { this.isScrolling = false; this.initialScrollTop = this.scrollableContent.scrollTop; // Reset for next scroll console.log('🛑 EdgeScrollManager: Edge-scroll stopped (mouse left edge)'); this.eventBus.emit('edgescroll:stopped', {}); } // Continue RAF loop even if not scrolling, to detect edge entry if (this.isDragging) { this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); } else { this.stopDrag(); } } } } //# sourceMappingURL=EdgeScrollManager.js.map