168 lines
7.8 KiB
JavaScript
168 lines
7.8 KiB
JavaScript
|
|
/**
|
||
|
|
* AllDayCoordinator - Orchestrates all-day event functionality
|
||
|
|
*
|
||
|
|
* NO STATE - Only coordinates between services
|
||
|
|
* - Listens to EventBus events
|
||
|
|
* - Delegates to specialized services
|
||
|
|
* - Manages service lifecycle
|
||
|
|
*/
|
||
|
|
import { eventBus } from '../../core/EventBus';
|
||
|
|
import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig';
|
||
|
|
import { AllDayLayoutEngine } from '../../utils/AllDayLayoutEngine';
|
||
|
|
import { CoreEvents } from '../../constants/CoreEvents';
|
||
|
|
import { AllDayDomReader } from './AllDayDomReader';
|
||
|
|
import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils';
|
||
|
|
/**
|
||
|
|
* AllDayCoordinator - Orchestrates all-day event functionality
|
||
|
|
* Replaces the monolithic AllDayManager with a coordinated service architecture
|
||
|
|
*/
|
||
|
|
export class AllDayCoordinator {
|
||
|
|
constructor(eventManager, allDayEventRenderer, dateService, heightService, collapseService, dragService) {
|
||
|
|
this.eventManager = eventManager;
|
||
|
|
this.allDayEventRenderer = allDayEventRenderer;
|
||
|
|
this.dateService = dateService;
|
||
|
|
this.heightService = heightService;
|
||
|
|
this.collapseService = collapseService;
|
||
|
|
this.dragService = dragService;
|
||
|
|
// Sync CSS variable with TypeScript constant
|
||
|
|
document.documentElement.style.setProperty('--single-row-height', `${ALL_DAY_CONSTANTS.EVENT_HEIGHT}px`);
|
||
|
|
this.setupEventListeners();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Setup event listeners and delegate to services
|
||
|
|
*/
|
||
|
|
setupEventListeners() {
|
||
|
|
// Timed → All-day conversion
|
||
|
|
eventBus.on('drag:mouseenter-header', (event) => {
|
||
|
|
const payload = event.detail;
|
||
|
|
if (payload.draggedClone.hasAttribute('data-allday'))
|
||
|
|
return;
|
||
|
|
console.log('🔄 AllDayCoordinator: Received drag:mouseenter-header', {
|
||
|
|
targetDate: payload.targetColumn,
|
||
|
|
originalElementId: payload.originalElement?.dataset?.eventId,
|
||
|
|
originalElementTag: payload.originalElement?.tagName
|
||
|
|
});
|
||
|
|
this.dragService.handleConvertToAllDay(payload);
|
||
|
|
// Recalculate layouts and height after timed → all-day conversion
|
||
|
|
this.recalculateLayoutsAndHeight();
|
||
|
|
});
|
||
|
|
eventBus.on('drag:mouseleave-header', (event) => {
|
||
|
|
const { originalElement } = event.detail;
|
||
|
|
console.log('🚪 AllDayCoordinator: Received drag:mouseleave-header', {
|
||
|
|
originalElementId: originalElement?.dataset?.eventId
|
||
|
|
});
|
||
|
|
});
|
||
|
|
// All-day drag start
|
||
|
|
eventBus.on('drag:start', (event) => {
|
||
|
|
const payload = event.detail;
|
||
|
|
if (!payload.draggedClone?.hasAttribute('data-allday'))
|
||
|
|
return;
|
||
|
|
this.allDayEventRenderer.handleDragStart(payload);
|
||
|
|
});
|
||
|
|
// All-day column change
|
||
|
|
eventBus.on('drag:column-change', (event) => {
|
||
|
|
const payload = event.detail;
|
||
|
|
if (!payload.draggedClone?.hasAttribute('data-allday'))
|
||
|
|
return;
|
||
|
|
this.dragService.handleColumnChange(payload);
|
||
|
|
});
|
||
|
|
// Drag end
|
||
|
|
eventBus.on('drag:end', (event) => {
|
||
|
|
const dragEndPayload = event.detail;
|
||
|
|
console.log('🎯 AllDayCoordinator: drag:end received', {
|
||
|
|
target: dragEndPayload.target,
|
||
|
|
originalElementTag: dragEndPayload.originalElement?.tagName,
|
||
|
|
hasAllDayAttribute: dragEndPayload.originalElement?.hasAttribute('data-allday'),
|
||
|
|
eventId: dragEndPayload.originalElement?.dataset.eventId
|
||
|
|
});
|
||
|
|
// Handle all-day → all-day drops (within header)
|
||
|
|
if (dragEndPayload.target === 'swp-day-header') {
|
||
|
|
console.log('✅ AllDayCoordinator: Handling all-day → all-day drop');
|
||
|
|
this.dragService.handleDragEnd(dragEndPayload);
|
||
|
|
// Recalculate layouts and height after all-day → all-day repositioning
|
||
|
|
this.recalculateLayoutsAndHeight();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Handle all-day → timed conversion (dropped in column)
|
||
|
|
if (dragEndPayload.target === 'swp-day-column' &&
|
||
|
|
dragEndPayload.originalElement?.hasAttribute('data-allday')) {
|
||
|
|
const eventId = dragEndPayload.originalElement.dataset.eventId;
|
||
|
|
console.log('🔄 AllDayCoordinator: All-day → timed conversion', {
|
||
|
|
eventId
|
||
|
|
});
|
||
|
|
// Remove event element from DOM
|
||
|
|
const container = AllDayDomReader.getAllDayContainer();
|
||
|
|
const eventElement = container?.querySelector(`[data-event-id="${eventId}"]`);
|
||
|
|
if (eventElement) {
|
||
|
|
eventElement.remove();
|
||
|
|
}
|
||
|
|
// Recalculate layouts and height after event removal
|
||
|
|
this.recalculateLayoutsAndHeight();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
// Drag cancelled
|
||
|
|
eventBus.on('drag:cancelled', (event) => {
|
||
|
|
const { draggedElement, reason } = event.detail;
|
||
|
|
console.log('🚫 AllDayCoordinator: Drag cancelled', {
|
||
|
|
eventId: draggedElement?.dataset?.eventId,
|
||
|
|
reason
|
||
|
|
});
|
||
|
|
});
|
||
|
|
// Header ready - render all-day events
|
||
|
|
eventBus.on('header:ready', async (event) => {
|
||
|
|
const headerReadyEventPayload = event.detail;
|
||
|
|
const startDate = new Date(headerReadyEventPayload.headerElements.at(0).date);
|
||
|
|
const endDate = new Date(headerReadyEventPayload.headerElements.at(-1).date);
|
||
|
|
const events = await this.eventManager.getEventsForPeriod(startDate, endDate);
|
||
|
|
// Filter for all-day events
|
||
|
|
const allDayEvents = events.filter(event => event.allDay);
|
||
|
|
// Calculate layouts
|
||
|
|
const layouts = this.calculateAllDayEventsLayout(allDayEvents, headerReadyEventPayload.headerElements);
|
||
|
|
// Render events
|
||
|
|
this.allDayEventRenderer.renderAllDayEventsForPeriod(layouts);
|
||
|
|
// Initialize collapse/expand UI and calculate height
|
||
|
|
this.collapseService.initializeUI();
|
||
|
|
});
|
||
|
|
// View changed
|
||
|
|
eventBus.on(CoreEvents.VIEW_CHANGED, (event) => {
|
||
|
|
this.allDayEventRenderer.handleViewChanged(event);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Calculate layout for ALL all-day events using AllDayLayoutEngine
|
||
|
|
*/
|
||
|
|
calculateAllDayEventsLayout(events, dayHeaders) {
|
||
|
|
// Initialize layout engine with provided week dates
|
||
|
|
const layoutEngine = new AllDayLayoutEngine(dayHeaders.map(column => column.date));
|
||
|
|
// Calculate layout for all events together
|
||
|
|
return layoutEngine.calculateLayout(events);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Recalculate layouts and update height
|
||
|
|
* Called after events are added/removed/moved in all-day area
|
||
|
|
* Uses AllDayLayoutEngine to optimally reorganize all events
|
||
|
|
*/
|
||
|
|
recalculateLayoutsAndHeight() {
|
||
|
|
// 1. Read current events from DOM
|
||
|
|
const events = AllDayDomReader.getEventsAsData();
|
||
|
|
const weekDates = ColumnDetectionUtils.getColumns();
|
||
|
|
// 2. Calculate optimal layouts using greedy algorithm
|
||
|
|
const layouts = this.calculateAllDayEventsLayout(events, weekDates);
|
||
|
|
// 3. Apply layouts to DOM
|
||
|
|
this.dragService.applyLayoutUpdates(layouts);
|
||
|
|
// 4. Calculate max row from NEW layouts
|
||
|
|
const maxRow = layouts.length > 0 ? Math.max(...layouts.map(l => l.row)) : 0;
|
||
|
|
// 5. Check if collapsed state should be maintained
|
||
|
|
const isExpanded = AllDayDomReader.isExpanded();
|
||
|
|
const targetRows = isExpanded ? maxRow : Math.min(maxRow, ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS);
|
||
|
|
// 6. Animate height to target
|
||
|
|
this.heightService.animateToRows(targetRows);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Public API for collapsing all-day row
|
||
|
|
*/
|
||
|
|
collapseAllDayRow() {
|
||
|
|
this.heightService.collapseAllDayRow();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=AllDayCoordinator.js.map
|