152 lines
4.5 KiB
TypeScript
152 lines
4.5 KiB
TypeScript
|
|
import { IEventBus } from '../../types/CalendarTypes';
|
||
|
|
import { IGridConfig } from '../../core/IGridConfig';
|
||
|
|
import { CoreEvents } from '../../constants/CoreEvents';
|
||
|
|
import {
|
||
|
|
IDragEnterHeaderPayload,
|
||
|
|
IDragMoveHeaderPayload,
|
||
|
|
IDragLeaveHeaderPayload
|
||
|
|
} from '../../types/DragTypes';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* HeaderDrawerRenderer - Handles rendering of items in the header drawer
|
||
|
|
*
|
||
|
|
* Listens to drag events from DragDropManager and creates/manages
|
||
|
|
* swp-header-item elements in the header drawer.
|
||
|
|
*
|
||
|
|
* Uses subgrid for column alignment with parent swp-calendar-header.
|
||
|
|
* Position items via gridArea for explicit row/column placement.
|
||
|
|
*/
|
||
|
|
export class HeaderDrawerRenderer {
|
||
|
|
private currentItem: HTMLElement | null = null;
|
||
|
|
private container: HTMLElement | null = null;
|
||
|
|
private sourceElement: HTMLElement | null = null;
|
||
|
|
|
||
|
|
constructor(
|
||
|
|
private eventBus: IEventBus,
|
||
|
|
private gridConfig: IGridConfig
|
||
|
|
) {
|
||
|
|
this.setupListeners();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Setup event listeners for drag events
|
||
|
|
*/
|
||
|
|
private setupListeners(): void {
|
||
|
|
this.eventBus.on(CoreEvents.EVENT_DRAG_ENTER_HEADER, (e) => {
|
||
|
|
const payload = (e as CustomEvent<IDragEnterHeaderPayload>).detail;
|
||
|
|
this.handleDragEnter(payload);
|
||
|
|
});
|
||
|
|
|
||
|
|
this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE_HEADER, (e) => {
|
||
|
|
const payload = (e as CustomEvent<IDragMoveHeaderPayload>).detail;
|
||
|
|
this.handleDragMove(payload);
|
||
|
|
});
|
||
|
|
|
||
|
|
this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => {
|
||
|
|
const payload = (e as CustomEvent<IDragLeaveHeaderPayload>).detail;
|
||
|
|
this.handleDragLeave(payload);
|
||
|
|
});
|
||
|
|
|
||
|
|
this.eventBus.on(CoreEvents.EVENT_DRAG_END, () => {
|
||
|
|
this.handleDragEnd();
|
||
|
|
});
|
||
|
|
|
||
|
|
this.eventBus.on(CoreEvents.EVENT_DRAG_CANCEL, () => {
|
||
|
|
this.cleanup();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle drag entering header zone - create preview item
|
||
|
|
*/
|
||
|
|
private handleDragEnter(payload: IDragEnterHeaderPayload): void {
|
||
|
|
this.container = document.querySelector('swp-header-drawer');
|
||
|
|
if (!this.container) return;
|
||
|
|
|
||
|
|
// Store reference to source element
|
||
|
|
this.sourceElement = payload.element;
|
||
|
|
|
||
|
|
// Create header item
|
||
|
|
const item = document.createElement('swp-header-item');
|
||
|
|
item.dataset.id = payload.eventId;
|
||
|
|
item.dataset.itemType = payload.itemType;
|
||
|
|
item.dataset.date = payload.sourceDate;
|
||
|
|
item.dataset.duration = String(payload.duration);
|
||
|
|
item.textContent = payload.title;
|
||
|
|
|
||
|
|
// Apply color class if present
|
||
|
|
if (payload.colorClass) {
|
||
|
|
item.classList.add(payload.colorClass);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add dragging state
|
||
|
|
item.classList.add('dragging');
|
||
|
|
|
||
|
|
// Initial placement (duration determines column span)
|
||
|
|
// gridArea format: "row / col-start / row+1 / col-end"
|
||
|
|
const col = payload.sourceColumnIndex + 1;
|
||
|
|
const endCol = col + payload.duration;
|
||
|
|
item.style.gridArea = `1 / ${col} / 2 / ${endCol}`;
|
||
|
|
|
||
|
|
this.container.appendChild(item);
|
||
|
|
this.currentItem = item;
|
||
|
|
|
||
|
|
// Hide original element while in header
|
||
|
|
payload.element.style.visibility = 'hidden';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle drag moving within header - update column position
|
||
|
|
*/
|
||
|
|
private handleDragMove(payload: IDragMoveHeaderPayload): void {
|
||
|
|
if (!this.currentItem) return;
|
||
|
|
|
||
|
|
// Update column position (duration=1 for now)
|
||
|
|
const col = payload.columnIndex + 1;
|
||
|
|
const duration = parseInt(this.currentItem.dataset.duration || '1', 10);
|
||
|
|
const endCol = col + duration;
|
||
|
|
|
||
|
|
this.currentItem.style.gridArea = `1 / ${col} / 2 / ${endCol}`;
|
||
|
|
this.currentItem.dataset.date = payload.dateKey;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle drag leaving header - remove preview and restore source
|
||
|
|
*/
|
||
|
|
private handleDragLeave(_payload: IDragLeaveHeaderPayload): void {
|
||
|
|
this.cleanup();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle drag end - finalize the item (it stays in header)
|
||
|
|
*/
|
||
|
|
private handleDragEnd(): void {
|
||
|
|
if (!this.currentItem) return;
|
||
|
|
|
||
|
|
// Remove dragging state
|
||
|
|
this.currentItem.classList.remove('dragging');
|
||
|
|
|
||
|
|
// Item stays - it's now permanent
|
||
|
|
// TODO: Emit event to persist allDay=true change
|
||
|
|
|
||
|
|
// Clear references but leave item in DOM
|
||
|
|
this.currentItem = null;
|
||
|
|
this.sourceElement = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Cleanup preview item and restore source visibility
|
||
|
|
*/
|
||
|
|
private cleanup(): void {
|
||
|
|
// Remove preview item
|
||
|
|
this.currentItem?.remove();
|
||
|
|
this.currentItem = null;
|
||
|
|
|
||
|
|
// Restore source element visibility
|
||
|
|
if (this.sourceElement) {
|
||
|
|
this.sourceElement.style.visibility = '';
|
||
|
|
this.sourceElement = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|