Adds drag column change event handling

Introduces support for moving events between columns during drag
Tracks column changes and updates event positioning dynamically
Enables smooth cross-column event dragging experience
This commit is contained in:
Janus C. H. Knudsen 2025-12-10 17:18:37 +01:00
parent 159b023f60
commit 8b95f2735f
4 changed files with 79 additions and 4 deletions

View file

@ -35,6 +35,7 @@ export const CoreEvents = {
EVENT_DRAG_MOVE: 'event:drag-move', EVENT_DRAG_MOVE: 'event:drag-move',
EVENT_DRAG_END: 'event:drag-end', EVENT_DRAG_END: 'event:drag-end',
EVENT_DRAG_CANCEL: 'event:drag-cancel', EVENT_DRAG_CANCEL: 'event:drag-cancel',
EVENT_DRAG_COLUMN_CHANGE: 'event:drag-column-change',
// System events // System events
ERROR: 'system:error', ERROR: 'system:error',

View file

@ -1,8 +1,10 @@
import { ICalendarEvent } from '../../types/CalendarTypes'; import { ICalendarEvent, IEventBus } from '../../types/CalendarTypes';
import { EventService } from '../../storage/events/EventService'; import { EventService } from '../../storage/events/EventService';
import { DateService } from '../../core/DateService'; import { DateService } from '../../core/DateService';
import { IGridConfig } from '../../core/IGridConfig'; import { IGridConfig } from '../../core/IGridConfig';
import { calculateEventPosition } from '../../utils/PositionUtils'; import { calculateEventPosition } from '../../utils/PositionUtils';
import { CoreEvents } from '../../constants/CoreEvents';
import { IDragColumnChangePayload } from '../../types/DragTypes';
/** /**
* EventRenderer - Renders calendar events to the DOM * EventRenderer - Renders calendar events to the DOM
@ -16,8 +18,35 @@ export class EventRenderer {
constructor( constructor(
private eventService: EventService, private eventService: EventService,
private dateService: DateService, private dateService: DateService,
private gridConfig: IGridConfig private gridConfig: IGridConfig,
) {} private eventBus: IEventBus
) {
this.setupDragListeners();
}
/**
* Setup listeners for drag-drop events
*/
private setupDragListeners(): void {
this.eventBus.on(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, (e) => {
const payload = (e as CustomEvent<IDragColumnChangePayload>).detail;
this.handleColumnChange(payload);
});
}
/**
* Handle event moving to a new column during drag
*/
private handleColumnChange(payload: IDragColumnChangePayload): void {
const eventsLayer = payload.newColumn.querySelector('swp-events-layer');
if (!eventsLayer) return;
// Move element to new column
eventsLayer.appendChild(payload.element);
// Preserve Y position
payload.element.style.top = `${payload.currentY}px`;
}
/** /**
* Render events for visible dates into day columns * Render events for visible dates into day columns

View file

@ -7,7 +7,8 @@ import {
IDragStartPayload, IDragStartPayload,
IDragMovePayload, IDragMovePayload,
IDragEndPayload, IDragEndPayload,
IDragCancelPayload IDragCancelPayload,
IDragColumnChangePayload
} from '../types/DragTypes'; } from '../types/DragTypes';
interface DragState { interface DragState {
@ -17,6 +18,7 @@ interface DragState {
startY: number; startY: number;
mouseOffset: IMousePosition; mouseOffset: IMousePosition;
columnElement: HTMLElement; columnElement: HTMLElement;
currentColumn: HTMLElement;
targetY: number; targetY: number;
currentY: number; currentY: number;
animationId: number; animationId: number;
@ -36,6 +38,7 @@ export class DragDropManager {
private mouseDownPosition: IMousePosition | null = null; private mouseDownPosition: IMousePosition | null = null;
private pendingElement: HTMLElement | null = null; private pendingElement: HTMLElement | null = null;
private pendingMouseOffset: IMousePosition | null = null; private pendingMouseOffset: IMousePosition | null = null;
private container: HTMLElement | null = null;
private readonly DRAG_THRESHOLD = 5; private readonly DRAG_THRESHOLD = 5;
private readonly INTERPOLATION_FACTOR = 0.3; private readonly INTERPOLATION_FACTOR = 0.3;
@ -49,6 +52,7 @@ export class DragDropManager {
* Initialize drag-drop on a container element * Initialize drag-drop on a container element
*/ */
init(container: HTMLElement): void { init(container: HTMLElement): void {
this.container = container;
container.addEventListener('pointerdown', this.handlePointerDown); container.addEventListener('pointerdown', this.handlePointerDown);
document.addEventListener('pointermove', this.handlePointerMove); document.addEventListener('pointermove', this.handlePointerMove);
document.addEventListener('pointerup', this.handlePointerUp); document.addEventListener('pointerup', this.handlePointerUp);
@ -169,6 +173,7 @@ export class DragDropManager {
startY, startY,
mouseOffset, mouseOffset,
columnElement, columnElement,
currentColumn: columnElement,
targetY: Math.max(0, targetY), targetY: Math.max(0, targetY),
currentY: startY, currentY: startY,
animationId: 0 animationId: 0
@ -193,6 +198,22 @@ export class DragDropManager {
private updateDragTarget(e: PointerEvent): void { private updateDragTarget(e: PointerEvent): void {
if (!this.dragState) return; if (!this.dragState) return;
// Check for column change
const columnAtPoint = this.getColumnAtPoint(e.clientX);
if (columnAtPoint && columnAtPoint !== this.dragState.currentColumn) {
const payload: IDragColumnChangePayload = {
eventId: this.dragState.eventId,
element: this.dragState.element,
previousColumn: this.dragState.currentColumn,
newColumn: columnAtPoint,
currentY: this.dragState.currentY
};
this.eventBus.emit(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, payload);
this.dragState.currentColumn = columnAtPoint;
this.dragState.columnElement = columnAtPoint;
}
const columnRect = this.dragState.columnElement.getBoundingClientRect(); const columnRect = this.dragState.columnElement.getBoundingClientRect();
const targetY = e.clientY - columnRect.top - this.dragState.mouseOffset.y; const targetY = e.clientY - columnRect.top - this.dragState.mouseOffset.y;
@ -204,6 +225,22 @@ export class DragDropManager {
} }
} }
/**
* Find column element at given X coordinate
*/
private getColumnAtPoint(clientX: number): HTMLElement | null {
if (!this.container) return null;
const columns = this.container.querySelectorAll('swp-day-column');
for (const col of columns) {
const rect = col.getBoundingClientRect();
if (clientX >= rect.left && clientX <= rect.right) {
return col as HTMLElement;
}
}
return null;
}
private animateDrag = (): void => { private animateDrag = (): void => {
if (!this.dragState) return; if (!this.dragState) return;

View file

@ -37,3 +37,11 @@ export interface IDragCancelPayload {
element: HTMLElement; element: HTMLElement;
startY: number; // Position to animate back to startY: number; // Position to animate back to
} }
export interface IDragColumnChangePayload {
eventId: string;
element: HTMLElement;
previousColumn: HTMLElement;
newColumn: HTMLElement;
currentY: number;
}