diff --git a/src/managers/DragDropManager.ts b/src/managers/DragDropManager.ts index 178ef5c..5d52d1f 100644 --- a/src/managers/DragDropManager.ts +++ b/src/managers/DragDropManager.ts @@ -23,16 +23,14 @@ export class DragDropManager { private eventBus: IEventBus; // Mouse tracking with optimized state - private lastMousePosition: MousePosition = { x: 0, y: 0 }; - private currentMouseY = 0; + private mouseDownPosition: MousePosition = { x: 0, y: 0 }; private mouseOffset: MousePosition = { x: 0, y: 0 }; - private initialMousePosition: MousePosition = { x: 0, y: 0 }; // Drag state - private draggedElement!: HTMLElement | null; + private originalElement!: HTMLElement | null; private draggedClone!: HTMLElement | null; - private currentColumnBounds: ColumnBounds | null = null; - private initialColumnBounds: ColumnBounds | null = null; // Track source column + private currentColumn: ColumnBounds | null = null; + private previousColumn: ColumnBounds | null = null; private isDragStarted = false; // Hover state @@ -70,7 +68,7 @@ export class DragDropManager { if (calendarContainer) { calendarContainer.addEventListener('mouseleave', () => { - if (this.draggedElement && this.isDragStarted) { + if (this.originalElement && this.isDragStarted) { this.cancelDrag(); } }); @@ -116,11 +114,13 @@ export class DragDropManager { // Clean up drag state first this.cleanupDragState(); ColumnDetectionUtils.updateColumnBoundsCache(); - this.lastMousePosition = { x: event.clientX, y: event.clientY }; - this.initialMousePosition = { x: event.clientX, y: event.clientY }; + //this.lastMousePosition = { x: event.clientX, y: event.clientY }; + //this.initialMousePosition = { x: event.clientX, y: event.clientY }; // Check if mousedown is on an event const target = event.target as HTMLElement; + if (target.closest('swp-resize-handle')) return; + let eventElement = target; while (eventElement && eventElement.tagName !== 'SWP-GRID-CONTAINER') { @@ -131,21 +131,18 @@ export class DragDropManager { if (!eventElement) return; } - - // Found an event - check if clicking on resize handle first if (eventElement) { - // Check if click is on resize handle - if (target.closest('swp-resize-handle')) { - return; // Exit early - this is a resize operation, let ResizeHandleManager handle it - } + // Normal drag - prepare for potential dragging - this.draggedElement = eventElement; + this.originalElement = eventElement; // Calculate mouse offset within event const eventRect = eventElement.getBoundingClientRect(); this.mouseOffset = { x: event.clientX - eventRect.left, y: event.clientY - eventRect.top }; + this.mouseDownPosition = { x: event.clientX, y: event.clientY }; + } } @@ -153,8 +150,8 @@ export class DragDropManager { * Optimized mouse move handler with consolidated position calculations */ private handleMouseMove(event: MouseEvent): void { - this.currentMouseY = event.clientY; - this.lastMousePosition = { x: event.clientX, y: event.clientY }; + //this.currentMouseY = event.clientY; + // this.lastMousePosition = { x: event.clientX, y: event.clientY }; // Check for event hover (coordinate-based) - only when mouse button is up if (this.isHoverTrackingActive && event.buttons === 0) { @@ -165,17 +162,17 @@ export class DragDropManager { const currentPosition: MousePosition = { x: event.clientX, y: event.clientY }; // Try to initialize drag if not started - if (!this.isDragStarted && this.draggedElement) { - if (!this.tryInitializeDrag(currentPosition)) { + if (!this.isDragStarted && this.originalElement) { + if (!this.initializeDrag(currentPosition)) { return; // Not enough movement yet } } // Continue drag if started - if (this.isDragStarted && this.draggedElement && this.draggedClone) { + if (this.isDragStarted && this.originalElement && this.draggedClone) { console.log("Continue drag if started", this.draggedClone); this.continueDrag(currentPosition); - this.detectAndEmitColumnChange(currentPosition); + this.detectColumnChange(currentPosition); } } } @@ -184,9 +181,9 @@ export class DragDropManager { * Try to initialize drag based on movement threshold * Returns true if drag was initialized, false if not enough movement */ - private tryInitializeDrag(currentPosition: MousePosition): boolean { - const deltaX = Math.abs(currentPosition.x - this.initialMousePosition.x); - const deltaY = Math.abs(currentPosition.y - this.initialMousePosition.y); + private initializeDrag(currentPosition: MousePosition): boolean { + const deltaX = Math.abs(currentPosition.x - this.mouseDownPosition.x); + const deltaY = Math.abs(currentPosition.y - this.mouseDownPosition.y); const totalMovement = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (totalMovement < this.dragThreshold) { @@ -197,27 +194,23 @@ export class DragDropManager { this.isDragStarted = true; // Set high z-index on event-group if exists, otherwise on event itself - const eventGroup = this.draggedElement!.closest('swp-event-group'); + const eventGroup = this.originalElement!.closest('swp-event-group'); if (eventGroup) { eventGroup.style.zIndex = '9999'; } else { - this.draggedElement!.style.zIndex = '9999'; + this.originalElement!.style.zIndex = '9999'; } - // Detect current column and save as initial source column - this.currentColumnBounds = ColumnDetectionUtils.getColumnBounds(currentPosition); - this.initialColumnBounds = this.currentColumnBounds; - - // Cast to BaseSwpEventElement and create clone - const originalElement = this.draggedElement as BaseSwpEventElement; + const originalElement = this.originalElement as BaseSwpEventElement; + this.currentColumn = ColumnDetectionUtils.getColumnBounds(currentPosition); this.draggedClone = originalElement.createClone(); const dragStartPayload: DragStartEventPayload = { - draggedElement: this.draggedElement!, - draggedClone: this.draggedClone, - mousePosition: this.initialMousePosition, + originalElement: this.originalElement!, + draggedClone: this.draggedClone, + mousePosition: this.mouseDownPosition, mouseOffset: this.mouseOffset, - columnBounds: this.currentColumnBounds + columnBounds: this.currentColumn }; this.eventBus.emit('drag:start', dragStartPayload); @@ -250,18 +243,18 @@ export class DragDropManager { /** * Detect column change and emit event */ - private detectAndEmitColumnChange(currentPosition: MousePosition): void { + private detectColumnChange(currentPosition: MousePosition): void { const newColumn = ColumnDetectionUtils.getColumnBounds(currentPosition); if (newColumn == null) return; - if (newColumn.index !== this.currentColumnBounds?.index) { - const previousColumn = this.currentColumnBounds; - this.currentColumnBounds = newColumn; + if (newColumn.index !== this.currentColumn?.index) { + this.previousColumn = this.currentColumn; + this.currentColumn = newColumn; const dragColumnChangePayload: DragColumnChangeEventPayload = { - originalElement: this.draggedElement!, + originalElement: this.originalElement!, draggedClone: this.draggedClone!, - previousColumn, + previousColumn: this.previousColumn, newColumn, mousePosition: currentPosition }; @@ -275,7 +268,7 @@ export class DragDropManager { private handleMouseUp(event: MouseEvent): void { this.stopDragAnimation(); - if (this.draggedElement) { + if (this.originalElement) { // Only emit drag:end if drag was actually started if (this.isDragStarted) { @@ -284,13 +277,9 @@ export class DragDropManager { // Snap to grid on mouse up (like ResizeHandleManager) const column = ColumnDetectionUtils.getColumnBounds(mousePosition); - if (!column) { - console.warn('No column detected on mouseUp'); - return; - } + if (!column) return; // Get current position and snap it to grid - const currentY = parseFloat(this.draggedClone?.style.top || '0'); const snappedY = this.calculateSnapPosition(mousePosition.y, column); // Update clone to snapped position immediately @@ -304,22 +293,17 @@ export class DragDropManager { if (!dropTarget) throw "dropTarget is null"; - console.log('🎯 DragDropManager: Emitting drag:end', { - draggedElement: this.draggedElement.dataset.eventId, - finalColumn: column, - finalY: snappedY, - dropTarget: dropTarget, - isDragStarted: this.isDragStarted - }); - const dragEndPayload: DragEndEventPayload = { - originalElement: this.draggedElement, + originalElement: this.originalElement, draggedClone: this.draggedClone, - sourceColumn: this.initialColumnBounds, // Where drag started mousePosition, + sourceColumn: this.previousColumn!!, finalPosition: { column, snappedY }, // Where drag ended target: dropTarget }; + + console.log('DragEndEventPayload', dragEndPayload); + this.eventBus.emit('drag:end', dragEndPayload); this.cleanupDragState(); @@ -327,7 +311,7 @@ export class DragDropManager { } else { // This was just a click - emit click event instead this.eventBus.emit('event:click', { - draggedElement: this.draggedElement, + clickedElement: this.originalElement, mousePosition: { x: event.clientX, y: event.clientY } }); } @@ -348,46 +332,24 @@ export class DragDropManager { * Cancel drag operation when mouse leaves grid container */ private cancelDrag(): void { - if (!this.draggedElement) return; + if (!this.originalElement) return; console.log('🚫 DragDropManager: Cancelling drag - mouse left grid container'); - const draggedElement = this.draggedElement; - - // 1. Remove all clones this.cleanupAllClones(); - // 2. Restore original element - if (draggedElement) { - draggedElement.style.opacity = ''; - draggedElement.style.cursor = ''; - } + this.originalElement.style.opacity = ''; + this.originalElement.style.cursor = ''; - // 3. Emit cancellation event this.eventBus.emit('drag:cancelled', { - draggedElement: draggedElement, + originalElement: this.originalElement, reason: 'mouse-left-grid' }); - // 4. Clean up state this.cleanupDragState(); this.stopDragAnimation(); } - /** - * Consolidated position calculation method using PositionUtils - */ - private calculateDragPosition(mousePosition: MousePosition): { column: ColumnBounds | null; snappedY: number } { - let column = ColumnDetectionUtils.getColumnBounds(mousePosition); - let snappedY = 0; - if (column) { - snappedY = this.calculateSnapPosition(mousePosition.y, column); - return { column, snappedY }; - } - - return { column, snappedY }; - } - /** * Optimized snap position calculation using PositionUtils */ @@ -405,7 +367,8 @@ export class DragDropManager { * Smooth drag animation using requestAnimationFrame * Emits drag:move events with current draggedClone reference on each frame */ - private animateDrag(): void { + private animateDrag(): void { //TODO: this can be optimized... WAIT !!! + if (!this.isDragStarted || !this.draggedClone || !this.targetColumn) { this.dragAnimationId = null; return; @@ -421,9 +384,9 @@ export class DragDropManager { // Emit drag:move event with current draggedClone reference const dragMovePayload: DragMoveEventPayload = { - draggedElement: this.draggedElement!, + originalElement: this.originalElement!, draggedClone: this.draggedClone, // Always uses current reference - mousePosition: this.lastMousePosition, + mousePosition: this.mouseDownPosition, snappedY: this.currentY, columnBounds: this.targetColumn, mouseOffset: this.mouseOffset @@ -437,9 +400,9 @@ export class DragDropManager { // Emit final position const dragMovePayload: DragMoveEventPayload = { - draggedElement: this.draggedElement!, + originalElement: this.originalElement!, draggedClone: this.draggedClone, - mousePosition: this.lastMousePosition, + mousePosition: this.mouseDownPosition, snappedY: this.currentY, columnBounds: this.targetColumn, mouseOffset: this.mouseOffset @@ -465,8 +428,10 @@ export class DragDropManager { * Clean up drag state */ private cleanupDragState(): void { - this.draggedElement = null; + this.previousColumn = null; + this.originalElement = null; this.draggedClone = null; + this.currentColumn = null; this.isDragStarted = false; } @@ -523,23 +488,20 @@ export class DragDropManager { const targetColumn = ColumnDetectionUtils.getColumnBounds(position); if (targetColumn) { - console.log('🎯 DragDropManager: Mouse entered header', { targetDate: targetColumn }); - - // Extract CalendarEvent from the dragged clone const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone); const dragMouseEnterPayload: DragMouseEnterHeaderEventPayload = { targetColumn: targetColumn, mousePosition: position, - originalElement: this.draggedElement, + originalElement: this.originalElement, draggedClone: this.draggedClone, calendarEvent: calendarEvent, - // Delegate pattern - allows AllDayManager to replace the clone replaceClone: (newClone: HTMLElement) => { this.draggedClone = newClone; this.dragAnimationId === null; } }; + console.log('DragMouseEnterHeaderEventPayload', dragMouseEnterPayload); this.eventBus.emit('drag:mouseenter-header', dragMouseEnterPayload); } } @@ -573,15 +535,13 @@ export class DragDropManager { targetColumn: targetColumn, mousePosition: position, snappedY: snappedY, - originalElement: this.draggedElement, + originalElement: this.originalElement, draggedClone: this.draggedClone, calendarEvent: calendarEvent, - // Delegate pattern - allows EventRenderer to replace the clone replaceClone: (newClone: HTMLElement) => { this.draggedClone = newClone; this.dragAnimationId === null; this.stopDragAnimation(); - console.log("replacing clone with", newClone) } }; this.eventBus.emit('drag:mouseenter-column', dragMouseEnterPayload); @@ -609,7 +569,7 @@ export class DragDropManager { const dragMouseLeavePayload: DragMouseLeaveHeaderEventPayload = { targetDate: targetColumn.date, mousePosition: position, - originalElement: this.draggedElement, + originalElement: this.originalElement, draggedClone: this.draggedClone }; this.eventBus.emit('drag:mouseleave-header', dragMouseLeavePayload); diff --git a/src/managers/EdgeScrollManager.ts b/src/managers/EdgeScrollManager.ts index bf82c12..82858de 100644 --- a/src/managers/EdgeScrollManager.ts +++ b/src/managers/EdgeScrollManager.ts @@ -4,7 +4,7 @@ */ import { IEventBus } from '../types/CalendarTypes'; -import { DragMoveEventPayload } from '../types/EventTypes'; +import { DragMoveEventPayload, DragStartEventPayload } from '../types/EventTypes'; export class EdgeScrollManager { private scrollableContent: HTMLElement | null = null; @@ -13,6 +13,10 @@ export class EdgeScrollManager { private isDragging = false; private lastTs = 0; private rect: DOMRect | null = null; + private draggedClone: HTMLElement | null = null; + private initialScrollTop = 0; + private initialCloneTop = 0; + private scrollListener: ((e: Event) => void) | null = null; // Constants - fixed values as per requirements private readonly OUTER_ZONE = 100; // px from edge (slow zone) @@ -31,6 +35,10 @@ export class EdgeScrollManager { if (this.scrollableContent) { // Disable smooth scroll for instant auto-scroll this.scrollableContent.style.scrollBehavior = 'auto'; + + // Add scroll listener + this.scrollListener = this.handleScroll.bind(this); + this.scrollableContent.addEventListener('scroll', this.scrollListener, { passive: true }); } }, 100); @@ -38,12 +46,20 @@ export class EdgeScrollManager { } private subscribeToEvents(): void { + // Listen to drag events from DragDropManager - this.eventBus.on('drag:start', () => this.startDrag()); + this.eventBus.on('drag:start', (event: Event) => { + let customEvent = event as CustomEvent; + this.draggedClone = customEvent.detail.draggedClone; + this.startDrag(); + }); + this.eventBus.on('drag:move', (event: Event) => { - const customEvent = event as CustomEvent; + let customEvent = event as CustomEvent; + this.draggedClone = customEvent.detail.draggedClone; this.updateMouseY(customEvent.detail.mousePosition.y); }); + this.eventBus.on('drag:end', () => this.stopDrag()); this.eventBus.on('drag:cancelled', () => this.stopDrag()); } @@ -52,6 +68,16 @@ export class EdgeScrollManager { console.log('🎬 EdgeScrollManager: Starting drag'); this.isDragging = true; this.lastTs = performance.now(); + + // Gem initial scroll position OG clone position + this.initialScrollTop = this.scrollableContent?.scrollTop || 0; + this.initialCloneTop = parseFloat(this.draggedClone?.style.top || '0'); + + console.log('💾 EdgeScrollManager: Saved initial state', { + initialScrollTop: this.initialScrollTop, + initialCloneTop: this.initialCloneTop + }); + if (this.scrollRAF === null) { this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); } @@ -74,6 +100,28 @@ export class EdgeScrollManager { } this.rect = null; this.lastTs = 0; + this.draggedClone = null; + this.initialScrollTop = 0; + this.initialCloneTop = 0; + } + + private handleScroll(): void { + if (!this.isDragging || !this.draggedClone || !this.scrollableContent) return; + + const currentScrollTop = this.scrollableContent.scrollTop; + const totalScrollDelta = currentScrollTop - this.initialScrollTop; + + // Beregn ny position baseret på initial position + total scroll delta + const newTop = this.initialCloneTop + totalScrollDelta; + this.draggedClone.style.top = `${newTop}px`; + + console.log('📜 EdgeScrollManager: Scroll event - updated clone', { + initialScrollTop: this.initialScrollTop, + currentScrollTop, + totalScrollDelta, + initialCloneTop: this.initialCloneTop, + newTop + }); } private scrollTick(ts: number): void { @@ -97,23 +145,15 @@ export class EdgeScrollManager { // Check top edge if (distTop < this.INNER_ZONE) { - // Inner zone (0-50px) - fast speed vy = -this.FAST_SPEED_PXS; - console.log('⬆️ EdgeScrollManager: Top FAST', { distTop, vy }); } else if (distTop < this.OUTER_ZONE) { - // Outer zone (50-100px) - slow speed vy = -this.SLOW_SPEED_PXS; - console.log('⬆️ EdgeScrollManager: Top SLOW', { distTop, vy }); } // Check bottom edge else if (distBot < this.INNER_ZONE) { - // Inner zone (0-50px) - fast speed vy = this.FAST_SPEED_PXS; - console.log('⬇️ EdgeScrollManager: Bottom FAST', { distBot, vy }); } else if (distBot < this.OUTER_ZONE) { - // Outer zone (50-100px) - slow speed vy = this.SLOW_SPEED_PXS; - console.log('⬇️ EdgeScrollManager: Bottom SLOW', { distBot, vy }); } } diff --git a/src/renderers/AllDayEventRenderer.ts b/src/renderers/AllDayEventRenderer.ts index 6803d02..9548b54 100644 --- a/src/renderers/AllDayEventRenderer.ts +++ b/src/renderers/AllDayEventRenderer.ts @@ -40,7 +40,7 @@ export class AllDayEventRenderer { */ public handleDragStart(payload: DragStartEventPayload): void { - this.originalEvent = payload.draggedElement;; + this.originalEvent = payload.originalElement;; this.draggedClone = payload.draggedClone; if (this.draggedClone) { diff --git a/src/renderers/EventRenderer.ts b/src/renderers/EventRenderer.ts index 42596a4..01af06a 100644 --- a/src/renderers/EventRenderer.ts +++ b/src/renderers/EventRenderer.ts @@ -56,7 +56,7 @@ export class DateEventRenderer implements EventRendererStrategy { */ public handleDragStart(payload: DragStartEventPayload): void { - this.originalEvent = payload.draggedElement;; + this.originalEvent = payload.originalElement;; // Use the clone from the payload instead of creating a new one this.draggedClone = payload.draggedClone; diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index a37cfa1..5c73e6c 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -138,11 +138,11 @@ export class EventRenderingService { this.eventBus.on('drag:start', (event: Event) => { const dragStartPayload = (event as CustomEvent).detail; - if (dragStartPayload.draggedElement.hasAttribute('data-allday')) { + if (dragStartPayload.originalElement.hasAttribute('data-allday')) { return; } - if (dragStartPayload.draggedElement && this.strategy.handleDragStart && dragStartPayload.columnBounds) { + if (dragStartPayload.originalElement && this.strategy.handleDragStart && dragStartPayload.columnBounds) { this.strategy.handleDragStart(dragStartPayload); } }); @@ -163,6 +163,7 @@ export class EventRenderingService { private setupDragEndListener(): void { this.eventBus.on('drag:end', (event: Event) => { + const { originalElement: draggedElement, sourceColumn, finalPosition, target } = (event as CustomEvent).detail; const finalColumn = finalPosition.column; const finalY = finalPosition.snappedY; diff --git a/src/types/EventTypes.ts b/src/types/EventTypes.ts index 7552543..2a9a5aa 100644 --- a/src/types/EventTypes.ts +++ b/src/types/EventTypes.ts @@ -18,7 +18,7 @@ export interface MousePosition { // Drag start event payload export interface DragStartEventPayload { - draggedElement: HTMLElement; + originalElement: HTMLElement; draggedClone: HTMLElement | null; mousePosition: MousePosition; mouseOffset: MousePosition; @@ -27,7 +27,7 @@ export interface DragStartEventPayload { // Drag move event payload export interface DragMoveEventPayload { - draggedElement: HTMLElement; + originalElement: HTMLElement; draggedClone: HTMLElement; mousePosition: MousePosition; mouseOffset: MousePosition; @@ -39,8 +39,8 @@ export interface DragMoveEventPayload { export interface DragEndEventPayload { originalElement: HTMLElement; draggedClone: HTMLElement | null; - sourceColumn: ColumnBounds | null; // Where drag started mousePosition: MousePosition; + sourceColumn: ColumnBounds; finalPosition: { column: ColumnBounds | null; // Where drag ended snappedY: number; @@ -55,7 +55,6 @@ export interface DragMouseEnterHeaderEventPayload { originalElement: HTMLElement | null; draggedClone: HTMLElement; calendarEvent: CalendarEvent; - // Delegate pattern - allows subscriber to replace the dragged clone replaceClone: (newClone: HTMLElement) => void; } @@ -75,7 +74,6 @@ export interface DragMouseEnterColumnEventPayload { originalElement: HTMLElement | null; draggedClone: HTMLElement; calendarEvent: CalendarEvent; - // Delegate pattern - allows subscriber to replace the dragged clone replaceClone: (newClone: HTMLElement) => void; }