Enhance drag and drop interactions across calendar views
Adds support for dragging events between header and grid views Improves drag-and-drop state management and event persistence Enables converting all-day events to timed events when dropped in grid Refactors drag handling to support more flexible event interactions
This commit is contained in:
parent
f7f1f8afe0
commit
bc5854e09a
5 changed files with 259 additions and 67 deletions
|
|
@ -4,7 +4,7 @@ import { DateService } from '../../core/DateService';
|
|||
import { IGridConfig } from '../../core/IGridConfig';
|
||||
import { calculateEventPosition, snapToGrid, pixelsToMinutes } from '../../utils/PositionUtils';
|
||||
import { CoreEvents } from '../../constants/CoreEvents';
|
||||
import { IDragColumnChangePayload, IDragMovePayload, IDragEndPayload } from '../../types/DragTypes';
|
||||
import { IDragColumnChangePayload, IDragMovePayload, IDragEndPayload, IDragLeaveHeaderPayload } from '../../types/DragTypes';
|
||||
import { calculateColumnLayout } from './EventLayoutEngine';
|
||||
import { IGridGroupLayout } from './EventLayoutTypes';
|
||||
|
||||
|
|
@ -51,6 +51,11 @@ export class EventRenderer {
|
|||
const payload = (e as CustomEvent<IDragEndPayload>).detail;
|
||||
this.handleDragEnd(payload);
|
||||
});
|
||||
|
||||
this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => {
|
||||
const payload = (e as CustomEvent<IDragLeaveHeaderPayload>).detail;
|
||||
this.handleDragLeaveHeader(payload);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,6 +69,48 @@ export class EventRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle header item leaving header - create swp-event in grid
|
||||
*/
|
||||
private handleDragLeaveHeader(payload: IDragLeaveHeaderPayload): void {
|
||||
// Only handle when source is header (header item dragged to grid)
|
||||
if (payload.source !== 'header') return;
|
||||
if (!payload.targetColumn || !payload.start || !payload.end) return;
|
||||
|
||||
// Turn header item into ghost (stays visible but faded)
|
||||
if (payload.element) {
|
||||
payload.element.classList.add('drag-ghost');
|
||||
payload.element.style.opacity = '0.3';
|
||||
payload.element.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
// Create event object from header item data
|
||||
const event: ICalendarEvent = {
|
||||
id: payload.eventId,
|
||||
title: payload.title || '',
|
||||
description: '',
|
||||
start: payload.start,
|
||||
end: payload.end,
|
||||
type: 'customer',
|
||||
allDay: false,
|
||||
syncStatus: 'pending'
|
||||
};
|
||||
|
||||
// Create swp-event element using existing method
|
||||
const element = this.createEventElement(event);
|
||||
|
||||
// Add to target column
|
||||
let eventsLayer = payload.targetColumn.querySelector('swp-events-layer');
|
||||
if (!eventsLayer) {
|
||||
eventsLayer = document.createElement('swp-events-layer');
|
||||
payload.targetColumn.appendChild(eventsLayer);
|
||||
}
|
||||
eventsLayer.appendChild(element);
|
||||
|
||||
// Mark as dragging so DragDropManager can continue with it
|
||||
element.classList.add('dragging');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle EVENT_UPDATED - re-render affected columns
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import { DateService } from '../../core/DateService';
|
|||
import {
|
||||
IDragEnterHeaderPayload,
|
||||
IDragMoveHeaderPayload,
|
||||
IDragLeaveHeaderPayload
|
||||
IDragLeaveHeaderPayload,
|
||||
IDragEndPayload
|
||||
} from '../../types/DragTypes';
|
||||
|
||||
/**
|
||||
|
|
@ -199,8 +200,9 @@ export class HeaderDrawerRenderer {
|
|||
this.handleDragLeave(payload);
|
||||
});
|
||||
|
||||
this.eventBus.on(CoreEvents.EVENT_DRAG_END, () => {
|
||||
this.handleDragEnd();
|
||||
this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => {
|
||||
const payload = (e as CustomEvent<IDragEndPayload>).detail;
|
||||
this.handleDragEnd(payload);
|
||||
});
|
||||
|
||||
this.eventBus.on(CoreEvents.EVENT_DRAG_CANCEL, () => {
|
||||
|
|
@ -290,22 +292,23 @@ export class HeaderDrawerRenderer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Handle drag end - finalize the item (it stays in header)
|
||||
* Note: EventRenderer handles removing the original element from the grid
|
||||
* via EVENT_DRAG_END with target === 'header'
|
||||
* Handle drag end - finalize based on drop target
|
||||
*/
|
||||
private handleDragEnd(): void {
|
||||
if (!this.currentItem) return;
|
||||
|
||||
// Remove dragging state
|
||||
this.currentItem.classList.remove('dragging');
|
||||
|
||||
// Recalculate layout for all items in drawer
|
||||
this.recalculateDrawerLayout();
|
||||
|
||||
// Clear references
|
||||
this.currentItem = null;
|
||||
this.sourceElement = null;
|
||||
private handleDragEnd(payload: IDragEndPayload): void {
|
||||
if (payload.target === 'header') {
|
||||
// Grid→Header: Finalize the header item (it stays in header)
|
||||
if (this.currentItem) {
|
||||
this.currentItem.classList.remove('dragging');
|
||||
this.recalculateDrawerLayout();
|
||||
this.currentItem = null;
|
||||
this.sourceElement = null;
|
||||
}
|
||||
} else {
|
||||
// Header→Grid: Remove ghost header item and recalculate
|
||||
const ghost = document.querySelector(`swp-header-item.drag-ghost[data-event-id="${payload.swpEvent.eventId}"]`);
|
||||
ghost?.remove();
|
||||
this.recalculateDrawerLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue