Improves drag-and-drop scroll compensation.

Enhances drag-and-drop functionality by accurately compensating for scroll during drag operations.
This ensures the dragged element remains correctly positioned relative to the mouse,
even when the user scrolls the content during the drag.

It achieves this by:
- Tracking whether scrolling has occurred during a drag operation.
- Factoring scroll delta into target position calculation.
- Updating targetY/currentY instead of directly manipulating the clone's style.
This commit is contained in:
Janus C. H. Knudsen 2025-10-13 20:24:19 +02:00
parent dbbd19de13
commit 9a7a90c124

View file

@ -47,6 +47,7 @@ export class DragDropManager {
private initialScrollTop = 0; private initialScrollTop = 0;
private initialCloneTop = 0; private initialCloneTop = 0;
private isScrollCompensating = false; // Track if scroll compensation is active private isScrollCompensating = false; // Track if scroll compensation is active
private hasScrolledDuringDrag = false; // Track if we have scrolled during this drag operation
private scrollListener: ((e: Event) => void) | null = null; private scrollListener: ((e: Event) => void) | null = null;
// Smooth drag animation // Smooth drag animation
@ -127,6 +128,7 @@ export class DragDropManager {
// Listen to edge-scroll events to control scroll compensation // Listen to edge-scroll events to control scroll compensation
this.eventBus.on('edgescroll:started', () => { this.eventBus.on('edgescroll:started', () => {
this.isScrollCompensating = true; this.isScrollCompensating = true;
this.hasScrolledDuringDrag = true;
console.log('🎬 DragDropManager: Edge-scroll started - disabling continueDrag()'); console.log('🎬 DragDropManager: Edge-scroll started - disabling continueDrag()');
}); });
@ -194,21 +196,20 @@ export class DragDropManager {
} }
if (event.buttons === 1) { if (event.buttons === 1) {
const currentPosition: MousePosition = { x: event.clientX, y: event.clientY }; // Always update mouse position from event
this.currentMousePosition = currentPosition; // Track current mouse position this.currentMousePosition = { x: event.clientX, y: event.clientY };
// Try to initialize drag if not started // Try to initialize drag if not started
if (!this.isDragStarted && this.originalElement) { if (!this.isDragStarted && this.originalElement) {
if (!this.initializeDrag(currentPosition)) { if (!this.initializeDrag(this.currentMousePosition)) {
return; // Not enough movement yet return; // Not enough movement yet
} }
} }
// Continue drag if started //TODO: This has to be fixed... it fires way too many events, we can do better // Continue drag if started
if (this.isDragStarted && this.originalElement && this.draggedClone) { if (this.isDragStarted && this.originalElement && this.draggedClone) {
//console.log("Continue drag if started", this.draggedClone); this.continueDrag(this.currentMousePosition);
this.continueDrag(currentPosition); this.detectColumnChange(this.currentMousePosition);
this.detectColumnChange(currentPosition);
} }
} }
} }
@ -265,7 +266,14 @@ export class DragDropManager {
if (column) { if (column) {
// Calculate raw Y position relative to column (accounting for mouse offset) // Calculate raw Y position relative to column (accounting for mouse offset)
const columnRect = column.boundingClientRect; const columnRect = column.boundingClientRect;
const eventTopY = currentPosition.y - columnRect.top - this.mouseOffset.y; let eventTopY = currentPosition.y - columnRect.top - this.mouseOffset.y;
// Kompenser for scroll bevægelse hvis vi har scrollet
if (this.scrollableContent && this.initialScrollTop > 0) {
const totalScrollDelta = this.scrollableContent.scrollTop - this.initialScrollTop;
eventTopY += totalScrollDelta;
}
this.targetY = Math.max(0, eventTopY); this.targetY = Math.max(0, eventTopY);
this.targetColumn = column; this.targetColumn = column;
@ -453,38 +461,47 @@ export class DragDropManager {
} }
/** /**
* Handle scroll during drag - compensate clone position * Handle scroll during drag - compensate clone position via targetY/currentY
*/ */
private handleScroll(): void { private handleScroll(): void {
if (!this.isDragStarted || !this.draggedClone || !this.scrollableContent || !this.isScrollCompensating) return; if (!this.isDragStarted || !this.draggedClone || !this.scrollableContent || !this.isScrollCompensating) return;
// First time scrolling - save initial positions NOW! // First time scrolling - save initial positions NOW!
if(this.initialScrollTop == 0) {
if(this.initialScrollTop == 0)
this.initialScrollTop = this.scrollableContent.scrollTop; this.initialScrollTop = this.scrollableContent.scrollTop;
if(this.initialCloneTop == 0) }
if(this.initialCloneTop == 0) {
this.initialCloneTop = parseFloat(this.draggedClone.style.top || '0'); this.initialCloneTop = parseFloat(this.draggedClone.style.top || '0');
console.log('💾 DragDropManager: Scroll compensation started', { console.log('💾 DragDropManager: Scroll compensation started', {
initialScrollTop: this.initialScrollTop, initialScrollTop: this.initialScrollTop,
initialCloneTop: this.initialCloneTop initialCloneTop: this.initialCloneTop
}); });
}
const currentScrollTop = this.scrollableContent.scrollTop; const currentScrollTop = this.scrollableContent.scrollTop;
const totalScrollDelta = currentScrollTop - this.initialScrollTop; const totalScrollDelta = currentScrollTop - this.initialScrollTop;
// Beregn ny position baseret på initial position + total scroll delta // Beregn ny position baseret på initial position + total scroll delta
const newTop = this.initialCloneTop + totalScrollDelta; const newTop = this.initialCloneTop + totalScrollDelta;
this.draggedClone.style.top = `${newTop}px`;
// Opdater targetY og currentY i stedet for direkte clone opdatering
this.targetY = newTop;
this.currentY = newTop;
// Kald animateDrag() hvis ikke allerede kører
if (this.dragAnimationId === null) {
this.animateDrag();
}
console.log('📜 DragDropManager: Scroll compensation', { console.log('📜 DragDropManager: Scroll compensation', {
initialScrollTop: this.initialScrollTop, initialScrollTop: this.initialScrollTop,
currentScrollTop, currentScrollTop,
totalScrollDelta, totalScrollDelta,
initialCloneTop: this.initialCloneTop, initialCloneTop: this.initialCloneTop,
newTop newTop,
targetY: this.targetY,
currentY: this.currentY
}); });
} }
@ -507,6 +524,9 @@ export class DragDropManager {
this.draggedClone = null; this.draggedClone = null;
this.currentColumn = null; this.currentColumn = null;
this.isDragStarted = false; this.isDragStarted = false;
this.hasScrolledDuringDrag = false;
this.initialScrollTop = 0;
this.initialCloneTop = 0;
} }
/** /**