From fc354ad6189f7432638ac9ed3f2846f6006da37d Mon Sep 17 00:00:00 2001 From: Janus Knudsen Date: Sun, 24 Aug 2025 23:41:32 +0200 Subject: [PATCH] Implements auto-scroll for drag and drop Adds auto-scrolling functionality to the column detector when dragging elements near the edges of the scrollable container. This enhances the drag and drop experience by automatically scrolling the content when the user drags an element close to the top or bottom of the visible area. The auto-scroll is triggered when the mouse cursor is within a defined threshold of the container's edges and stops when the mouse moves outside of the threshold. A check is also added to ensure auto-scroll stops when the user releases the mouse button. --- src/managers/ColumnDetector.ts | 98 ++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/managers/ColumnDetector.ts b/src/managers/ColumnDetector.ts index 29758c3..ce9be43 100644 --- a/src/managers/ColumnDetector.ts +++ b/src/managers/ColumnDetector.ts @@ -14,6 +14,12 @@ export class ColumnDetector { private originalEvent: HTMLElement | null = null; private mouseOffset = { x: 0, y: 0 }; + // Auto-scroll properties + private scrollContainer: HTMLElement | null = null; + private autoScrollAnimationId: number | null = null; + private scrollSpeed = 10; // pixels per frame + private scrollThreshold = 30; // pixels from edge + // Konfiguration for snap interval private snapIntervalMinutes = 15; // 15 minutter private hourHeightPx = 60; // Fra CSS --hour-height @@ -103,6 +109,12 @@ export class ColumnDetector { this.draggedClone.style.top = relativeY + 'px'; } } + + // Auto-scroll detection når der er en aktiv clone + if (this.draggedClone) { + console.log('ColumnDetector: Checking auto-scroll, mouse at:', event.clientY); + this.checkAutoScroll(event); + } } // Find hvilket element musen er over (altid) @@ -270,6 +282,9 @@ export class ColumnDetector { this.isMouseDown = false; console.log('Mouse up at:', { x: event.clientX, y: event.clientY }); + // Stop auto-scroll + this.stopAutoScroll(); + // Drop operationen: fade out original og remove clone suffix if (this.originalEvent && this.draggedClone) { // Fade out og fjern originalen @@ -281,6 +296,7 @@ export class ColumnDetector { // Ryd op this.originalEvent = null; this.draggedClone = null; + this.scrollContainer = null; console.log('Drop completed: original faded out and removed, clone became permanent'); } @@ -306,7 +322,89 @@ export class ColumnDetector { } } + /** + * Check if auto-scroll should be triggered based on mouse position + */ + private checkAutoScroll(event: MouseEvent): void { + // Find scrollable content if not cached + if (!this.scrollContainer) { + this.scrollContainer = document.querySelector('swp-scrollable-content') as HTMLElement; + if (!this.scrollContainer) { + console.warn('ColumnDetector: Could not find swp-scrollable-content for auto-scroll'); + return; + } + console.log('ColumnDetector: Found scroll container:', this.scrollContainer); + } + + const containerRect = this.scrollContainer.getBoundingClientRect(); + const mouseY = event.clientY; + + // Calculate distances from edges + const distanceFromTop = mouseY - containerRect.top; + const distanceFromBottom = containerRect.bottom - mouseY; + + console.log('ColumnDetector: Auto-scroll check:', { + mouseY, + containerTop: containerRect.top, + containerBottom: containerRect.bottom, + distanceFromTop: Math.round(distanceFromTop), + distanceFromBottom: Math.round(distanceFromBottom), + threshold: this.scrollThreshold + }); + + // Check if we need to scroll up + if (distanceFromTop <= this.scrollThreshold && distanceFromTop > 0) { + this.startAutoScroll('up'); + console.log(`Auto-scroll up triggered: ${Math.round(distanceFromTop)}px from top edge`); + } + // Check if we need to scroll down + else if (distanceFromBottom <= this.scrollThreshold && distanceFromBottom > 0) { + this.startAutoScroll('down'); + console.log(`Auto-scroll down triggered: ${Math.round(distanceFromBottom)}px from bottom edge`); + } + // Stop scrolling if not in threshold zone + else { + this.stopAutoScroll(); + } + } + + /** + * Start auto-scroll animation in specified direction + */ + private startAutoScroll(direction: 'up' | 'down'): void { + // Don't start if already scrolling in same direction + if (this.autoScrollAnimationId !== null) { + return; + } + + const scroll = () => { + if (!this.scrollContainer || !this.isMouseDown || !this.draggedClone) { + this.stopAutoScroll(); + return; + } + + const scrollAmount = direction === 'up' ? -this.scrollSpeed : this.scrollSpeed; + this.scrollContainer.scrollTop += scrollAmount; + + // Continue animation + 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; + } + } + public destroy(): void { + this.stopAutoScroll(); document.body.removeEventListener('mousemove', this.handleMouseMove.bind(this)); document.body.removeEventListener('click', this.handleClick.bind(this)); document.body.removeEventListener('mousedown', this.handleMouseDown.bind(this));