Calendar/src/managers/ColumnDetector.ts

289 lines
10 KiB
TypeScript
Raw Normal View History

/**
* ColumnDetector - Bare detect hvilken kolonne musen er over
*/
export class ColumnDetector {
private currentColumn: string | null = null;
private isMouseDown = false;
private lastMousePosition = { x: 0, y: 0 };
private lastLoggedPosition = { x: 0, y: 0 };
private draggedClone: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null;
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() {
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 {
// Lyt til mouse move på hele body
document.body.addEventListener('mousemove', this.handleMouseMove.bind(this));
// Lyt til click på hele body
document.body.addEventListener('click', this.handleClick.bind(this));
// Lyt til mouse down og up
document.body.addEventListener('mousedown', this.handleMouseDown.bind(this));
document.body.addEventListener('mouseup', this.handleMouseUp.bind(this));
}
private handleMouseMove(event: MouseEvent): void {
// Hvis musen er holdt nede, tjek for snap interval vertikal bevægelse
if (this.isMouseDown) {
const deltaY = Math.abs(event.clientY - this.lastLoggedPosition.y);
if (deltaY >= this.snapDistancePx) {
console.log(`Mouse dragged ${this.snapIntervalMinutes} minutes (${this.snapDistancePx}px) vertically:`, {
from: this.lastLoggedPosition,
to: { x: event.clientX, y: event.clientY },
verticalDistance: Math.round(deltaY),
snapInterval: `${this.snapIntervalMinutes} minutes`
});
this.lastLoggedPosition = { x: event.clientX, y: event.clientY };
// Opdater klonens Y-position ved snap interval (relativt til kolonne)
if (this.draggedClone && this.draggedClone.parentElement) {
const columnRect = this.draggedClone.parentElement.getBoundingClientRect();
const relativeY = event.clientY - columnRect.top - this.mouseOffset.y;
this.draggedClone.style.top = relativeY + 'px';
}
}
}
// Find hvilket element musen er over (altid)
{
const elementUnder = document.elementFromPoint(event.clientX, event.clientY);
if (!elementUnder) {
// Ingen element under musen
if (this.currentColumn !== null) {
console.log('Left all columns');
this.currentColumn = null;
}
return;
}
// Gå op gennem DOM træet for at finde swp-day-column
let element = elementUnder as HTMLElement;
while (element && element.tagName !== 'SWP-DAY-COLUMN') {
element = element.parentElement as HTMLElement;
if (!element) {
// Ikke i en kolonne
if (this.currentColumn !== null) {
console.log('Left all columns');
this.currentColumn = null;
}
return;
}
}
// Vi fandt en kolonne
const date = element.dataset.date;
if (date && date !== this.currentColumn) {
console.log('Entered column:', date);
this.currentColumn = date;
// Flyt klonen til ny kolonne ved kolonneskift
if (this.draggedClone && this.isMouseDown) {
// Flyt klonen til den nye kolonne
const newColumnElement = document.querySelector(`swp-day-column[data-date="${date}"]`);
if (newColumnElement) {
newColumnElement.appendChild(this.draggedClone);
// Opdater Y-position relativt til den nye kolonne
const columnRect = newColumnElement.getBoundingClientRect();
const relativeY = event.clientY - columnRect.top - this.mouseOffset.y;
this.draggedClone.style.top = relativeY + 'px';
}
}
}
}
}
private handleClick(event: MouseEvent): void {
const target = event.target as HTMLElement;
// Find event element
let eventElement = target;
while (eventElement && eventElement.tagName !== 'SWP-EVENTS-LAYER') {
if (eventElement.tagName === 'SWP-EVENT' || eventElement.tagName === 'SWP-ALLDAY-EVENT') {
break;
}
eventElement = eventElement.parentElement as HTMLElement;
if (!eventElement) return;
}
// Hvis vi nåede til SWP-EVENTS-LAYER uden at finde et event, så return
if (!eventElement || eventElement.tagName === 'SWP-EVENTS-LAYER') {
return;
}
// Log event info
const eventId = eventElement.dataset.eventId;
const eventType = eventElement.dataset.type;
console.log('Clicked event:', {
id: eventId,
type: eventType,
element: eventElement,
title: eventElement.textContent
});
}
private handleMouseDown(event: MouseEvent): void {
this.isMouseDown = true;
this.lastMousePosition = { x: event.clientX, y: event.clientY };
this.lastLoggedPosition = { x: event.clientX, y: event.clientY };
console.log('Mouse down at:', this.lastMousePosition);
// Tjek om mousedown er på et event
const target = event.target as HTMLElement;
let eventElement = target;
while (eventElement && eventElement.tagName !== 'SWP-EVENTS-LAYER') {
if (eventElement.tagName === 'SWP-EVENT' || eventElement.tagName === 'SWP-ALLDAY-EVENT') {
break;
}
eventElement = eventElement.parentElement as HTMLElement;
if (!eventElement) return;
}
// Hvis vi nåede til SWP-EVENTS-LAYER uden at finde et event, så return
if (!eventElement || eventElement.tagName === 'SWP-EVENTS-LAYER') {
return;
}
// Hvis vi fandt et event, lav en clone
if (eventElement) {
// Gem reference til original event
this.originalEvent = eventElement;
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';
}
}
private cloneEvent(originalEvent: HTMLElement, mouseEvent: MouseEvent): void {
// Lav en clone
const clone = originalEvent.cloneNode(true) as HTMLElement;
// Præfiks ID med "clone-"
const originalId = originalEvent.dataset.eventId;
if (originalId) {
clone.dataset.eventId = `clone-${originalId}`;
}
// Beregn hvor på event'et musen klikkede
const eventRect = originalEvent.getBoundingClientRect();
this.mouseOffset = {
x: mouseEvent.clientX - eventRect.left, // Stadig nødvendig for cursor placering
y: mouseEvent.clientY - eventRect.top
};
// Gør klonen ready til at blive trukket
clone.style.position = 'absolute';
clone.style.zIndex = '999999';
clone.style.pointerEvents = 'none';
// Sæt størrelse fra det originale event
clone.style.width = eventRect.width + 'px';
clone.style.height = eventRect.height + 'px';
// Find den aktuelle kolonne og placer klonen der
const currentColumnElement = document.querySelector(`swp-day-column[data-date="${this.currentColumn}"]`);
if (currentColumnElement) {
// Sæt initial position relativt til kolonnen
const columnRect = currentColumnElement.getBoundingClientRect();
const relativeY = mouseEvent.clientY - columnRect.top - this.mouseOffset.y;
clone.style.top = relativeY + 'px';
currentColumnElement.appendChild(clone);
} else {
console.error('Could not find current column element:', this.currentColumn);
// Fallback til original placering
originalEvent.parentNode?.insertBefore(clone, originalEvent.nextSibling);
}
// Gem reference til klonen
this.draggedClone = clone;
console.log('Cloned event:', {
original: originalId,
clone: clone.dataset.eventId,
offset: this.mouseOffset
});
}
private handleMouseUp(event: MouseEvent): void {
this.isMouseDown = false;
console.log('Mouse up at:', { x: event.clientX, y: event.clientY });
// Drop operationen: fade out original og remove clone suffix
if (this.originalEvent && this.draggedClone) {
// Fade out og fjern originalen
this.fadeOutAndRemove(this.originalEvent);
// 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 {
document.body.removeEventListener('mousemove', this.handleMouseMove.bind(this));
document.body.removeEventListener('click', this.handleClick.bind(this));
document.body.removeEventListener('mousedown', this.handleMouseDown.bind(this));
document.body.removeEventListener('mouseup', this.handleMouseUp.bind(this));
}
}