/** * ColumnDetector - Bare detect hvilken kolonne musen er over */ import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig'; import { eventBus } from '../core/EventBus'; 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)); // Listen for header mouseover events eventBus.on('header:mouseover', (event) => { const { dayHeader, headerRenderer } = (event as CustomEvent).detail; if (this.isMouseDown && this.draggedClone) { console.log('Dragging clone over header - calling addToAllDay'); headerRenderer.addToAllDay(dayHeader); } }); } 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; } } /** * Expand header to show all-day row when clone is dragged into header */ private expandHeaderForAllDay(): void { const root = document.documentElement; const currentHeight = parseInt(getComputedStyle(root).getPropertyValue('--all-day-row-height') || '0'); if (currentHeight === 0) { root.style.setProperty('--all-day-row-height', `${ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT}px`); console.log('Header expanded for all-day row'); } } 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)); } }