Enhances event layout engine with advanced rendering logic
Introduces sophisticated event layout algorithm for handling complex scheduling scenarios Adds support for: - Grid and stacked event rendering - Automatic column allocation - Nested event stacking - Threshold-based event grouping Improves visual representation of overlapping and concurrent events
This commit is contained in:
parent
4e22fbc948
commit
70172e8f10
26 changed files with 2108 additions and 44 deletions
|
|
@ -25,6 +25,8 @@ interface DragState {
|
|||
targetY: number;
|
||||
currentY: number;
|
||||
animationId: number;
|
||||
sourceDateKey: string; // Source column date (where drag started)
|
||||
sourceResourceId?: string; // Source column resource (where drag started)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -144,7 +146,7 @@ export class DragDropManager {
|
|||
// Remove ghost
|
||||
this.dragState.ghostElement.remove();
|
||||
|
||||
// Get column data
|
||||
// Get column data (target = current column, source = where drag started)
|
||||
const dateKey = this.dragState.columnElement.dataset.date || '';
|
||||
const resourceId = this.dragState.columnElement.dataset.resourceId;
|
||||
|
||||
|
|
@ -155,7 +157,9 @@ export class DragDropManager {
|
|||
snappedY,
|
||||
columnElement: this.dragState.columnElement,
|
||||
dateKey,
|
||||
resourceId
|
||||
resourceId,
|
||||
sourceDateKey: this.dragState.sourceDateKey,
|
||||
sourceResourceId: this.dragState.sourceResourceId
|
||||
};
|
||||
|
||||
this.eventBus.emit(CoreEvents.EVENT_DRAG_END, payload);
|
||||
|
|
@ -200,7 +204,9 @@ export class DragDropManager {
|
|||
currentColumn: columnElement,
|
||||
targetY: Math.max(0, targetY),
|
||||
currentY: startY,
|
||||
animationId: 0
|
||||
animationId: 0,
|
||||
sourceDateKey: columnElement.dataset.date || '',
|
||||
sourceResourceId: columnElement.dataset.resourceId
|
||||
};
|
||||
|
||||
// Emit drag:start
|
||||
|
|
|
|||
116
src/v2/managers/EventPersistenceManager.ts
Normal file
116
src/v2/managers/EventPersistenceManager.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* EventPersistenceManager - Persists event changes to IndexedDB
|
||||
*
|
||||
* Listens to drag/resize events and updates IndexedDB via EventService.
|
||||
* This bridges the gap between UI interactions and data persistence.
|
||||
*/
|
||||
|
||||
import { ICalendarEvent, IEventBus, IEventUpdatedPayload } from '../types/CalendarTypes';
|
||||
import { EventService } from '../storage/events/EventService';
|
||||
import { IGridConfig } from '../core/IGridConfig';
|
||||
import { DateService } from '../core/DateService';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { IDragEndPayload } from '../types/DragTypes';
|
||||
import { IResizeEndPayload } from '../types/ResizeTypes';
|
||||
import { pixelsToMinutes } from '../utils/PositionUtils';
|
||||
|
||||
export class EventPersistenceManager {
|
||||
constructor(
|
||||
private eventService: EventService,
|
||||
private eventBus: IEventBus,
|
||||
private gridConfig: IGridConfig,
|
||||
private dateService: DateService
|
||||
) {
|
||||
this.setupListeners();
|
||||
}
|
||||
|
||||
private setupListeners(): void {
|
||||
this.eventBus.on(CoreEvents.EVENT_DRAG_END, this.handleDragEnd);
|
||||
this.eventBus.on(CoreEvents.EVENT_RESIZE_END, this.handleResizeEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle drag end - update event position in IndexedDB
|
||||
*/
|
||||
private handleDragEnd = async (e: Event): Promise<void> => {
|
||||
const payload = (e as CustomEvent<IDragEndPayload>).detail;
|
||||
|
||||
// Get existing event
|
||||
const event = await this.eventService.get(payload.eventId);
|
||||
if (!event) {
|
||||
console.warn(`EventPersistenceManager: Event ${payload.eventId} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate new start time from snappedY
|
||||
const minutesFromDayStart = pixelsToMinutes(payload.snappedY, this.gridConfig);
|
||||
const totalMinutes = (this.gridConfig.dayStartHour * 60) + minutesFromDayStart;
|
||||
|
||||
// Preserve duration
|
||||
const durationMs = event.end.getTime() - event.start.getTime();
|
||||
|
||||
// Create new dates with correct day from dateKey
|
||||
const newStart = new Date(payload.dateKey);
|
||||
newStart.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0);
|
||||
const newEnd = new Date(newStart.getTime() + durationMs);
|
||||
|
||||
// Update and save
|
||||
const updatedEvent: ICalendarEvent = {
|
||||
...event,
|
||||
start: newStart,
|
||||
end: newEnd,
|
||||
resourceId: payload.resourceId ?? event.resourceId,
|
||||
syncStatus: 'pending'
|
||||
};
|
||||
|
||||
await this.eventService.save(updatedEvent);
|
||||
|
||||
// Emit EVENT_UPDATED for EventRenderer to re-render affected columns
|
||||
const updatePayload: IEventUpdatedPayload = {
|
||||
eventId: updatedEvent.id,
|
||||
sourceDateKey: payload.sourceDateKey,
|
||||
sourceResourceId: payload.sourceResourceId,
|
||||
targetDateKey: payload.dateKey,
|
||||
targetResourceId: payload.resourceId
|
||||
};
|
||||
this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle resize end - update event duration in IndexedDB
|
||||
*/
|
||||
private handleResizeEnd = async (e: Event): Promise<void> => {
|
||||
const payload = (e as CustomEvent<IResizeEndPayload>).detail;
|
||||
|
||||
// Get existing event
|
||||
const event = await this.eventService.get(payload.eventId);
|
||||
if (!event) {
|
||||
console.warn(`EventPersistenceManager: Event ${payload.eventId} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate new end time
|
||||
const newEnd = new Date(event.start.getTime() + payload.newDurationMinutes * 60 * 1000);
|
||||
|
||||
// Update and save
|
||||
const updatedEvent: ICalendarEvent = {
|
||||
...event,
|
||||
end: newEnd,
|
||||
syncStatus: 'pending'
|
||||
};
|
||||
|
||||
await this.eventService.save(updatedEvent);
|
||||
|
||||
// Emit EVENT_UPDATED for EventRenderer to re-render the column
|
||||
// Resize stays in same column, so source and target are the same
|
||||
const dateKey = this.dateService.getDateKey(event.start);
|
||||
const updatePayload: IEventUpdatedPayload = {
|
||||
eventId: updatedEvent.id,
|
||||
sourceDateKey: dateKey,
|
||||
sourceResourceId: event.resourceId,
|
||||
targetDateKey: dateKey,
|
||||
targetResourceId: event.resourceId
|
||||
};
|
||||
this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload);
|
||||
};
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ export class ResizeManager {
|
|||
const element = handle.parentElement as HTMLElement;
|
||||
if (!element) return;
|
||||
|
||||
const eventId = element.dataset.id || '';
|
||||
const eventId = element.dataset.eventId || '';
|
||||
const startHeight = element.offsetHeight;
|
||||
const startDurationMinutes = pixelsToMinutes(startHeight, this.gridConfig);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue