Improves drag-and-drop event handling

Enhances the drag-and-drop experience for event elements by
introducing snapping to a configurable time interval and fading out
the original element upon successful drop.

This change allows users to configure the snap interval in minutes,
making it easier to align events to specific time slots.  It also
adds visual feedback by making the original event transparent during
the drag and fading it out on drop, providing a clearer indication
of the event's movement.
This commit is contained in:
Janus Knudsen 2025-08-24 21:31:52 +02:00
parent 906616fe7b
commit 457e222262

View file

@ -8,12 +8,55 @@ export class ColumnDetector {
private lastMousePosition = { x: 0, y: 0 }; private lastMousePosition = { x: 0, y: 0 };
private lastLoggedPosition = { x: 0, y: 0 }; private lastLoggedPosition = { x: 0, y: 0 };
private draggedClone: HTMLElement | null = null; private draggedClone: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null;
private mouseOffset = { x: 0, y: 0 }; private mouseOffset = { x: 0, y: 0 };
// Konfiguration for snap interval
private snapIntervalMinutes = 15; // 15 minutter
private hourHeightPx = 60; // Fra CSS --hour-height
private get snapDistancePx(): number {
return (this.snapIntervalMinutes / 60) * this.hourHeightPx; // 15/60 * 60 = 15px
}
constructor() { constructor() {
this.init(); this.init();
} }
/**
* Konfigurer snap interval
*/
public setSnapInterval(minutes: number): void {
this.snapIntervalMinutes = minutes;
console.log(`Snap interval set to ${minutes} minutes (${this.snapDistancePx}px)`);
}
/**
* Fade out og fjern element fra DOM
*/
private fadeOutAndRemove(element: HTMLElement): void {
element.style.transition = 'opacity 0.3s ease-out';
element.style.opacity = '0';
setTimeout(() => {
element.remove();
}, 300);
}
/**
* Fjern "clone-" prefix fra event ID og gendan pointer events
*/
private removeClonePrefix(clone: HTMLElement): void {
const cloneId = clone.dataset.eventId;
if (cloneId && cloneId.startsWith('clone-')) {
const originalId = cloneId.replace('clone-', '');
clone.dataset.eventId = originalId;
console.log(`Removed clone prefix: ${cloneId} -> ${originalId}`);
}
// Gendan pointer events så klonen kan dragges igen
clone.style.pointerEvents = '';
}
private init(): void { private init(): void {
// Lyt til mouse move på hele body // Lyt til mouse move på hele body
document.body.addEventListener('mousemove', this.handleMouseMove.bind(this)); document.body.addEventListener('mousemove', this.handleMouseMove.bind(this));
@ -27,19 +70,20 @@ export class ColumnDetector {
} }
private handleMouseMove(event: MouseEvent): void { private handleMouseMove(event: MouseEvent): void {
// Hvis musen er holdt nede, tjek for 25px vertikal bevægelse // Hvis musen er holdt nede, tjek for snap interval vertikal bevægelse
if (this.isMouseDown) { if (this.isMouseDown) {
const deltaY = Math.abs(event.clientY - this.lastLoggedPosition.y); const deltaY = Math.abs(event.clientY - this.lastLoggedPosition.y);
if (deltaY >= 25) { if (deltaY >= this.snapDistancePx) {
console.log('Mouse dragged 25px vertically:', { console.log(`Mouse dragged ${this.snapIntervalMinutes} minutes (${this.snapDistancePx}px) vertically:`, {
from: this.lastLoggedPosition, from: this.lastLoggedPosition,
to: { x: event.clientX, y: event.clientY }, to: { x: event.clientX, y: event.clientY },
verticalDistance: Math.round(deltaY) verticalDistance: Math.round(deltaY),
snapInterval: `${this.snapIntervalMinutes} minutes`
}); });
this.lastLoggedPosition = { x: event.clientX, y: event.clientY }; this.lastLoggedPosition = { x: event.clientX, y: event.clientY };
// Opdater klonens Y-position ved 25px snap (relativt til kolonne) // Opdater klonens Y-position ved snap interval (relativt til kolonne)
if (this.draggedClone && this.draggedClone.parentElement) { if (this.draggedClone && this.draggedClone.parentElement) {
const columnRect = this.draggedClone.parentElement.getBoundingClientRect(); const columnRect = this.draggedClone.parentElement.getBoundingClientRect();
const relativeY = event.clientY - columnRect.top - this.mouseOffset.y; const relativeY = event.clientY - columnRect.top - this.mouseOffset.y;
@ -149,7 +193,12 @@ export class ColumnDetector {
// Hvis vi fandt et event, lav en clone // Hvis vi fandt et event, lav en clone
if (eventElement) { if (eventElement) {
// Gem reference til original event
this.originalEvent = eventElement;
this.cloneEvent(eventElement, event); this.cloneEvent(eventElement, event);
// Sæt originalen til gennemsigtig og forhindre text selection mens der trækkes
eventElement.style.opacity = '0.6';
eventElement.style.userSelect = 'none';
} }
} }
@ -208,12 +257,27 @@ export class ColumnDetector {
this.isMouseDown = false; this.isMouseDown = false;
console.log('Mouse up at:', { x: event.clientX, y: event.clientY }); console.log('Mouse up at:', { x: event.clientX, y: event.clientY });
// Ryd op - fjern klonen (DISABLED FOR DEBUGGING) // Drop operationen: fade out original og remove clone suffix
// if (this.draggedClone) { if (this.originalEvent && this.draggedClone) {
// this.draggedClone.remove(); // Fade out og fjern originalen
// this.draggedClone = null; this.fadeOutAndRemove(this.originalEvent);
// console.log('Removed clone');
// } // Fjern clone suffix fra klonen
this.removeClonePrefix(this.draggedClone);
// Ryd op
this.originalEvent = null;
this.draggedClone = null;
console.log('Drop completed: original faded out and removed, clone became permanent');
}
// Cleanup hvis ingen drop (ingen clone var aktiv)
if (this.originalEvent && !this.draggedClone) {
this.originalEvent.style.opacity = '';
this.originalEvent.style.userSelect = '';
this.originalEvent = null;
}
} }
public destroy(): void { public destroy(): void {