264 lines
11 KiB
JavaScript
264 lines
11 KiB
JavaScript
|
|
import { CoreEvents } from '../constants/CoreEvents';
|
||
|
|
import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||
|
|
/**
|
||
|
|
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
||
|
|
* Håndterer event positioning og overlap detection
|
||
|
|
*/
|
||
|
|
export class EventRenderingService {
|
||
|
|
constructor(eventBus, eventManager, strategy, dateService) {
|
||
|
|
this.dragMouseLeaveHeaderListener = null;
|
||
|
|
this.eventBus = eventBus;
|
||
|
|
this.eventManager = eventManager;
|
||
|
|
this.strategy = strategy;
|
||
|
|
this.dateService = dateService;
|
||
|
|
this.setupEventListeners();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Render events in a specific container for a given period
|
||
|
|
*/
|
||
|
|
async renderEvents(context) {
|
||
|
|
// Clear existing events in the specific container first
|
||
|
|
this.strategy.clearEvents(context.container);
|
||
|
|
// Get events from EventManager for the period
|
||
|
|
const events = await this.eventManager.getEventsForPeriod(context.startDate, context.endDate);
|
||
|
|
if (events.length === 0) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Filter events by type - only render timed events here
|
||
|
|
const timedEvents = events.filter(event => !event.allDay);
|
||
|
|
console.log('🎯 EventRenderingService: Event filtering', {
|
||
|
|
totalEvents: events.length,
|
||
|
|
timedEvents: timedEvents.length,
|
||
|
|
allDayEvents: events.length - timedEvents.length
|
||
|
|
});
|
||
|
|
// Render timed events using existing strategy
|
||
|
|
if (timedEvents.length > 0) {
|
||
|
|
this.strategy.renderEvents(timedEvents, context.container);
|
||
|
|
}
|
||
|
|
// Emit EVENTS_RENDERED event for filtering system
|
||
|
|
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
|
||
|
|
events: events,
|
||
|
|
container: context.container
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupEventListeners() {
|
||
|
|
this.eventBus.on(CoreEvents.GRID_RENDERED, (event) => {
|
||
|
|
this.handleGridRendered(event);
|
||
|
|
});
|
||
|
|
this.eventBus.on(CoreEvents.VIEW_CHANGED, (event) => {
|
||
|
|
this.handleViewChanged(event);
|
||
|
|
});
|
||
|
|
// Handle all drag events and delegate to appropriate renderer
|
||
|
|
this.setupDragEventListeners();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Handle GRID_RENDERED event - render events in the current grid
|
||
|
|
*/
|
||
|
|
handleGridRendered(event) {
|
||
|
|
const { container, dates } = event.detail;
|
||
|
|
if (!container || !dates || dates.length === 0) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Calculate startDate and endDate from dates array
|
||
|
|
const startDate = dates[0];
|
||
|
|
const endDate = dates[dates.length - 1];
|
||
|
|
this.renderEvents({
|
||
|
|
container,
|
||
|
|
startDate,
|
||
|
|
endDate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Handle VIEW_CHANGED event - clear and re-render for new view
|
||
|
|
*/
|
||
|
|
handleViewChanged(event) {
|
||
|
|
// Clear all existing events since view structure may have changed
|
||
|
|
this.clearEvents();
|
||
|
|
// New rendering will be triggered by subsequent GRID_RENDERED event
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Setup all drag event listeners - moved from EventRenderer for better separation of concerns
|
||
|
|
*/
|
||
|
|
setupDragEventListeners() {
|
||
|
|
this.setupDragStartListener();
|
||
|
|
this.setupDragMoveListener();
|
||
|
|
this.setupDragEndListener();
|
||
|
|
this.setupDragColumnChangeListener();
|
||
|
|
this.setupDragMouseLeaveHeaderListener();
|
||
|
|
this.setupDragMouseEnterColumnListener();
|
||
|
|
this.setupResizeEndListener();
|
||
|
|
this.setupNavigationCompletedListener();
|
||
|
|
}
|
||
|
|
setupDragStartListener() {
|
||
|
|
this.eventBus.on('drag:start', (event) => {
|
||
|
|
const dragStartPayload = event.detail;
|
||
|
|
if (dragStartPayload.originalElement.hasAttribute('data-allday')) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (dragStartPayload.originalElement && this.strategy.handleDragStart && dragStartPayload.columnBounds) {
|
||
|
|
this.strategy.handleDragStart(dragStartPayload);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupDragMoveListener() {
|
||
|
|
this.eventBus.on('drag:move', (event) => {
|
||
|
|
let dragEvent = event.detail;
|
||
|
|
if (dragEvent.draggedClone.hasAttribute('data-allday')) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this.strategy.handleDragMove) {
|
||
|
|
this.strategy.handleDragMove(dragEvent);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupDragEndListener() {
|
||
|
|
this.eventBus.on('drag:end', async (event) => {
|
||
|
|
const { originalElement, draggedClone, originalSourceColumn, finalPosition, target } = event.detail;
|
||
|
|
const finalColumn = finalPosition.column;
|
||
|
|
const finalY = finalPosition.snappedY;
|
||
|
|
let element = draggedClone;
|
||
|
|
// Only handle day column drops for EventRenderer
|
||
|
|
if (target === 'swp-day-column' && finalColumn) {
|
||
|
|
if (originalElement && draggedClone && this.strategy.handleDragEnd) {
|
||
|
|
this.strategy.handleDragEnd(originalElement, draggedClone, finalColumn, finalY);
|
||
|
|
}
|
||
|
|
await this.eventManager.updateEvent(element.eventId, {
|
||
|
|
start: element.start,
|
||
|
|
end: element.end,
|
||
|
|
allDay: false
|
||
|
|
});
|
||
|
|
// Re-render affected columns for stacking/grouping (now with updated data)
|
||
|
|
await this.reRenderAffectedColumns(originalSourceColumn, finalColumn);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupDragColumnChangeListener() {
|
||
|
|
this.eventBus.on('drag:column-change', (event) => {
|
||
|
|
let columnChangeEvent = event.detail;
|
||
|
|
// Filter: Only handle events where clone is NOT an all-day event (normal timed events)
|
||
|
|
if (columnChangeEvent.draggedClone && columnChangeEvent.draggedClone.hasAttribute('data-allday')) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this.strategy.handleColumnChange) {
|
||
|
|
this.strategy.handleColumnChange(columnChangeEvent);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupDragMouseLeaveHeaderListener() {
|
||
|
|
this.dragMouseLeaveHeaderListener = (event) => {
|
||
|
|
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = event.detail;
|
||
|
|
if (cloneElement)
|
||
|
|
cloneElement.style.display = '';
|
||
|
|
console.log('🚪 EventRendererManager: Received drag:mouseleave-header', {
|
||
|
|
targetDate,
|
||
|
|
originalElement: originalElement,
|
||
|
|
cloneElement: cloneElement
|
||
|
|
});
|
||
|
|
};
|
||
|
|
this.eventBus.on('drag:mouseleave-header', this.dragMouseLeaveHeaderListener);
|
||
|
|
}
|
||
|
|
setupDragMouseEnterColumnListener() {
|
||
|
|
this.eventBus.on('drag:mouseenter-column', (event) => {
|
||
|
|
const payload = event.detail;
|
||
|
|
// Only handle if clone is an all-day event
|
||
|
|
if (!payload.draggedClone.hasAttribute('data-allday')) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
console.log('🎯 EventRendererManager: Received drag:mouseenter-column', {
|
||
|
|
targetColumn: payload.targetColumn,
|
||
|
|
snappedY: payload.snappedY,
|
||
|
|
calendarEvent: payload.calendarEvent
|
||
|
|
});
|
||
|
|
// Delegate to strategy for conversion
|
||
|
|
if (this.strategy.handleConvertAllDayToTimed) {
|
||
|
|
this.strategy.handleConvertAllDayToTimed(payload);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupResizeEndListener() {
|
||
|
|
this.eventBus.on('resize:end', async (event) => {
|
||
|
|
const { eventId, element } = event.detail;
|
||
|
|
// Update event data in EventManager with new end time from resized element
|
||
|
|
const swpEvent = element;
|
||
|
|
const newStart = swpEvent.start;
|
||
|
|
const newEnd = swpEvent.end;
|
||
|
|
await this.eventManager.updateEvent(eventId, {
|
||
|
|
start: newStart,
|
||
|
|
end: newEnd
|
||
|
|
});
|
||
|
|
console.log('📝 EventRendererManager: Updated event after resize', {
|
||
|
|
eventId,
|
||
|
|
newStart,
|
||
|
|
newEnd
|
||
|
|
});
|
||
|
|
let columnBounds = ColumnDetectionUtils.getColumnBoundsByDate(newStart);
|
||
|
|
if (columnBounds)
|
||
|
|
await this.renderSingleColumn(columnBounds);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setupNavigationCompletedListener() {
|
||
|
|
this.eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => {
|
||
|
|
// Delegate to strategy if it handles navigation
|
||
|
|
if (this.strategy.handleNavigationCompleted) {
|
||
|
|
this.strategy.handleNavigationCompleted();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Re-render affected columns after drag to recalculate stacking/grouping
|
||
|
|
*/
|
||
|
|
async reRenderAffectedColumns(originalSourceColumn, targetColumn) {
|
||
|
|
// Re-render original source column if exists
|
||
|
|
if (originalSourceColumn) {
|
||
|
|
await this.renderSingleColumn(originalSourceColumn);
|
||
|
|
}
|
||
|
|
// Re-render target column if exists and different from source
|
||
|
|
if (targetColumn && targetColumn.date !== originalSourceColumn?.date) {
|
||
|
|
await this.renderSingleColumn(targetColumn);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Clear events in a single column's events layer
|
||
|
|
*/
|
||
|
|
clearColumnEvents(eventsLayer) {
|
||
|
|
const existingEvents = eventsLayer.querySelectorAll('swp-event');
|
||
|
|
const existingGroups = eventsLayer.querySelectorAll('swp-event-group');
|
||
|
|
existingEvents.forEach(event => event.remove());
|
||
|
|
existingGroups.forEach(group => group.remove());
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Render events for a single column
|
||
|
|
*/
|
||
|
|
async renderSingleColumn(column) {
|
||
|
|
// Get events for just this column's date
|
||
|
|
const columnStart = this.dateService.parseISO(`${column.date}T00:00:00`);
|
||
|
|
const columnEnd = this.dateService.parseISO(`${column.date}T23:59:59.999`);
|
||
|
|
// Get events from EventManager for this single date
|
||
|
|
const events = await this.eventManager.getEventsForPeriod(columnStart, columnEnd);
|
||
|
|
// Filter to timed events only
|
||
|
|
const timedEvents = events.filter(event => !event.allDay);
|
||
|
|
// Get events layer within this specific column
|
||
|
|
const eventsLayer = column.element.querySelector('swp-events-layer');
|
||
|
|
if (!eventsLayer) {
|
||
|
|
console.warn('EventRendererManager: Events layer not found in column');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Clear only this column's events
|
||
|
|
this.clearColumnEvents(eventsLayer);
|
||
|
|
// Render events for this column using strategy
|
||
|
|
if (this.strategy.renderSingleColumnEvents) {
|
||
|
|
this.strategy.renderSingleColumnEvents(column, timedEvents);
|
||
|
|
}
|
||
|
|
console.log('🔄 EventRendererManager: Re-rendered single column', {
|
||
|
|
columnDate: column.date,
|
||
|
|
eventsCount: timedEvents.length
|
||
|
|
});
|
||
|
|
}
|
||
|
|
clearEvents(container) {
|
||
|
|
this.strategy.clearEvents(container);
|
||
|
|
}
|
||
|
|
refresh(container) {
|
||
|
|
this.clearEvents(container);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=EventRendererManager.js.map
|