Refactor calendar datasource and event management
Enhances calendar flexibility by introducing group-based column spanning and improving cross-mode event handling Adds support for: - Dynamic column grouping in date and resource modes - Consistent event drag-and-drop across different calendar views - More robust all-day event layout calculations Improves event management logic to handle resource and date mode transitions more elegantly
This commit is contained in:
parent
17909696ed
commit
d8b9f6dabd
16 changed files with 192 additions and 79 deletions
|
|
@ -5,6 +5,7 @@ import { ALL_DAY_CONSTANTS } from '../configurations/CalendarConfig';
|
|||
import { AllDayEventRenderer } from '../renderers/AllDayEventRenderer';
|
||||
import { AllDayLayoutEngine, IEventLayout } from '../utils/AllDayLayoutEngine';
|
||||
import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
import { IColumnDataSource } from '../types/ColumnDataSource';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { CalendarEventType } from '../types/BookingTypes';
|
||||
import { SwpAllDayEventElement } from '../elements/SwpEventElement';
|
||||
|
|
@ -30,12 +31,13 @@ export class AllDayManager {
|
|||
private allDayEventRenderer: AllDayEventRenderer;
|
||||
private eventManager: EventManager;
|
||||
private dateService: DateService;
|
||||
private dataSource: IColumnDataSource;
|
||||
|
||||
private layoutEngine: AllDayLayoutEngine | null = null;
|
||||
|
||||
// State tracking for layout calculation
|
||||
private currentAllDayEvents: ICalendarEvent[] = [];
|
||||
private currentWeekDates: IColumnBounds[] = [];
|
||||
private currentColumns: IColumnBounds[] = [];
|
||||
|
||||
// Expand/collapse state
|
||||
private isExpanded: boolean = false;
|
||||
|
|
@ -45,11 +47,13 @@ export class AllDayManager {
|
|||
constructor(
|
||||
eventManager: EventManager,
|
||||
allDayEventRenderer: AllDayEventRenderer,
|
||||
dateService: DateService
|
||||
dateService: DateService,
|
||||
dataSource: IColumnDataSource
|
||||
) {
|
||||
this.eventManager = eventManager;
|
||||
this.allDayEventRenderer = allDayEventRenderer;
|
||||
this.dateService = dateService;
|
||||
this.dataSource = dataSource;
|
||||
|
||||
// Sync CSS variable with TypeScript constant to ensure consistency
|
||||
document.documentElement.style.setProperty('--single-row-height', `${ALL_DAY_CONSTANTS.EVENT_HEIGHT}px`);
|
||||
|
|
@ -140,7 +144,7 @@ export class AllDayManager {
|
|||
|
||||
// Recalculate layout WITHOUT the removed event to compress gaps
|
||||
const remainingEvents = this.currentAllDayEvents.filter(e => e.id !== eventId);
|
||||
const newLayouts = this.calculateAllDayEventsLayout(remainingEvents, this.currentWeekDates);
|
||||
const newLayouts = this.calculateAllDayEventsLayout(remainingEvents, this.currentColumns);
|
||||
|
||||
// Re-render all-day events with compressed layout
|
||||
this.allDayEventRenderer.renderAllDayEventsForPeriod(newLayouts);
|
||||
|
|
@ -395,10 +399,18 @@ export class AllDayManager {
|
|||
|
||||
// Store current state
|
||||
this.currentAllDayEvents = events;
|
||||
this.currentWeekDates = dayHeaders;
|
||||
this.currentColumns = dayHeaders;
|
||||
|
||||
// Initialize layout engine with provided week dates
|
||||
let layoutEngine = new AllDayLayoutEngine(dayHeaders.map(column => column.identifier));
|
||||
// Map IColumnBounds to IColumnInfo structure (identifier + groupId)
|
||||
const columns = dayHeaders.map(column => ({
|
||||
identifier: column.identifier,
|
||||
groupId: column.element.dataset.groupId || column.identifier,
|
||||
data: new Date(), // Not used by AllDayLayoutEngine
|
||||
events: [] // Not used by AllDayLayoutEngine
|
||||
}));
|
||||
|
||||
// Initialize layout engine with column info including groupId
|
||||
let layoutEngine = new AllDayLayoutEngine(columns);
|
||||
|
||||
// Calculate layout for all events together - AllDayLayoutEngine handles CalendarEvents directly
|
||||
return layoutEngine.calculateLayout(events);
|
||||
|
|
@ -489,23 +501,43 @@ export class AllDayManager {
|
|||
|
||||
const clone = dragEndEvent.draggedClone as SwpAllDayEventElement;
|
||||
const eventId = clone.eventId.replace('clone-', '');
|
||||
const targetDate = this.dateService.parseISO(dragEndEvent.finalPosition.column.identifier);
|
||||
const columnIdentifier = dragEndEvent.finalPosition.column.identifier;
|
||||
|
||||
// Determine target date based on mode
|
||||
let targetDate: Date;
|
||||
let resourceId: string | undefined;
|
||||
|
||||
if (this.dataSource.isResource()) {
|
||||
// Resource mode: keep event's existing date, set resourceId
|
||||
targetDate = clone.start;
|
||||
resourceId = columnIdentifier;
|
||||
} else {
|
||||
// Date mode: parse date from column identifier
|
||||
targetDate = this.dateService.parseISO(columnIdentifier);
|
||||
}
|
||||
|
||||
console.log('🔄 AllDayManager: Converting timed event to all-day', { eventId, targetDate, resourceId });
|
||||
|
||||
console.log('🔄 AllDayManager: Converting timed event to all-day', { eventId, targetDate });
|
||||
|
||||
// Create new dates preserving time
|
||||
const newStart = new Date(targetDate);
|
||||
newStart.setHours(clone.start.getHours(), clone.start.getMinutes(), 0, 0);
|
||||
|
||||
|
||||
const newEnd = new Date(targetDate);
|
||||
newEnd.setHours(clone.end.getHours(), clone.end.getMinutes(), 0, 0);
|
||||
|
||||
// Update event in repository
|
||||
await this.eventManager.updateEvent(eventId, {
|
||||
|
||||
// Build update payload
|
||||
const updatePayload: { start: Date; end: Date; allDay: boolean; resourceId?: string } = {
|
||||
start: newStart,
|
||||
end: newEnd,
|
||||
allDay: true
|
||||
});
|
||||
};
|
||||
|
||||
if (resourceId) {
|
||||
updatePayload.resourceId = resourceId;
|
||||
}
|
||||
|
||||
// Update event in repository
|
||||
await this.eventManager.updateEvent(eventId, updatePayload);
|
||||
|
||||
// Remove original timed event
|
||||
this.fadeOutAndRemove(dragEndEvent.originalElement);
|
||||
|
|
@ -522,7 +554,7 @@ export class AllDayManager {
|
|||
};
|
||||
|
||||
const updatedEvents = [...this.currentAllDayEvents, newEvent];
|
||||
const newLayouts = this.calculateAllDayEventsLayout(updatedEvents, this.currentWeekDates);
|
||||
const newLayouts = this.calculateAllDayEventsLayout(updatedEvents, this.currentColumns);
|
||||
this.allDayEventRenderer.renderAllDayEventsForPeriod(newLayouts);
|
||||
|
||||
// Animate height
|
||||
|
|
@ -537,25 +569,45 @@ export class AllDayManager {
|
|||
|
||||
const clone = dragEndEvent.draggedClone as SwpAllDayEventElement;
|
||||
const eventId = clone.eventId.replace('clone-', '');
|
||||
const targetDate = this.dateService.parseISO(dragEndEvent.finalPosition.column.identifier);
|
||||
const columnIdentifier = dragEndEvent.finalPosition.column.identifier;
|
||||
|
||||
// Determine target date based on mode
|
||||
let targetDate: Date;
|
||||
let resourceId: string | undefined;
|
||||
|
||||
if (this.dataSource.isResource()) {
|
||||
// Resource mode: keep event's existing date, set resourceId
|
||||
targetDate = clone.start;
|
||||
resourceId = columnIdentifier;
|
||||
} else {
|
||||
// Date mode: parse date from column identifier
|
||||
targetDate = this.dateService.parseISO(columnIdentifier);
|
||||
}
|
||||
|
||||
// Calculate duration in days
|
||||
const durationDays = this.dateService.differenceInCalendarDays(clone.end, clone.start);
|
||||
|
||||
|
||||
// Create new dates preserving time
|
||||
const newStart = new Date(targetDate);
|
||||
newStart.setHours(clone.start.getHours(), clone.start.getMinutes(), 0, 0);
|
||||
|
||||
|
||||
const newEnd = new Date(targetDate);
|
||||
newEnd.setDate(newEnd.getDate() + durationDays);
|
||||
newEnd.setHours(clone.end.getHours(), clone.end.getMinutes(), 0, 0);
|
||||
|
||||
// Update event in repository
|
||||
await this.eventManager.updateEvent(eventId, {
|
||||
|
||||
// Build update payload
|
||||
const updatePayload: { start: Date; end: Date; allDay: boolean; resourceId?: string } = {
|
||||
start: newStart,
|
||||
end: newEnd,
|
||||
allDay: true
|
||||
});
|
||||
};
|
||||
|
||||
if (resourceId) {
|
||||
updatePayload.resourceId = resourceId;
|
||||
}
|
||||
|
||||
// Update event in repository
|
||||
await this.eventManager.updateEvent(eventId, updatePayload);
|
||||
|
||||
// Remove original and fade out
|
||||
this.fadeOutAndRemove(dragEndEvent.originalElement);
|
||||
|
|
@ -564,7 +616,7 @@ export class AllDayManager {
|
|||
const updatedEvents = this.currentAllDayEvents.map(e =>
|
||||
e.id === eventId ? { ...e, start: newStart, end: newEnd } : e
|
||||
);
|
||||
const newLayouts = this.calculateAllDayEventsLayout(updatedEvents, this.currentWeekDates);
|
||||
const newLayouts = this.calculateAllDayEventsLayout(updatedEvents, this.currentColumns);
|
||||
this.allDayEventRenderer.renderAllDayEventsForPeriod(newLayouts);
|
||||
|
||||
// Animate height - this also handles overflow classes!
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue