diff --git a/src/managers/DragDropManager.ts b/src/managers/DragDropManager.ts index 2b2b6f9..6eda62e 100644 --- a/src/managers/DragDropManager.ts +++ b/src/managers/DragDropManager.ts @@ -40,6 +40,7 @@ export class DragDropManager { private draggedElement!: HTMLElement | null; private draggedClone!: HTMLElement | null; private currentColumnBounds: ColumnBounds | null = null; + private initialColumnBounds: ColumnBounds | null = null; // Track source column private isDragStarted = false; // Hover state @@ -231,8 +232,9 @@ export class DragDropManager { this.draggedElement.style.zIndex = '9999'; } - // Detect current column + // Detect current column and save as initial source column this.currentColumnBounds = ColumnDetectionUtils.getColumnBounds(currentPosition); + this.initialColumnBounds = this.currentColumnBounds; // Save source column // Cast to BaseSwpEventElement and create clone (works for both SwpEventElement and SwpAllDayEventElement) const originalElement = this.draggedElement as BaseSwpEventElement; @@ -344,8 +346,9 @@ export class DragDropManager { const dragEndPayload: DragEndEventPayload = { originalElement: this.draggedElement, draggedClone: this.draggedClone, + sourceColumn: this.initialColumnBounds, // Where drag started mousePosition, - finalPosition: { column, snappedY }, + finalPosition: { column, snappedY }, // Where drag ended target: dropTarget }; this.eventBus.emit('drag:end', dragEndPayload); diff --git a/src/managers/ResizeHandleManager.ts b/src/managers/ResizeHandleManager.ts index 62944c2..ead6ca3 100644 --- a/src/managers/ResizeHandleManager.ts +++ b/src/managers/ResizeHandleManager.ts @@ -1,6 +1,7 @@ import { eventBus } from '../core/EventBus'; import { CoreEvents } from '../constants/CoreEvents'; import { calendarConfig } from '../core/CalendarConfig'; +import { ResizeEndEventPayload } from '../types/EventTypes'; type SwpEventEl = HTMLElement & { updateHeight?: (h: number) => void }; @@ -33,7 +34,7 @@ export class ResizeHandleManager { const grid = calendarConfig.getGridSettings(); this.hourHeightPx = grid.hourHeight; this.snapMin = grid.snapInterval; - this.minDurationMin = grid.minEventDuration ?? this.snapMin; + this.minDurationMin = this.snapMin; // Use snap interval as minimum duration } public initialize(): void { @@ -232,6 +233,15 @@ export class ResizeHandleManager { this.targetEl.updateHeight?.(finalHeight); + // Emit resize:end event for re-stacking + const eventId = this.targetEl.dataset.eventId || ''; + const resizeEndPayload: ResizeEndEventPayload = { + eventId, + element: this.targetEl, + finalHeight + }; + eventBus.emit('resize:end', resizeEndPayload); + const group = this.targetEl.closest('swp-event-group') ?? this.targetEl; group.style.zIndex = this.prevZ ?? ''; this.prevZ = undefined; diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index 959564b..8866b0f 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -6,7 +6,9 @@ import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { EventManager } from '../managers/EventManager'; import { EventRendererStrategy } from './EventRenderer'; import { SwpEventElement } from '../elements/SwpEventElement'; -import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, DragColumnChangeEventPayload, HeaderReadyEventPayload } from '../types/EventTypes'; +import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, DragColumnChangeEventPayload, HeaderReadyEventPayload, ResizeEndEventPayload } from '../types/EventTypes'; +import { DateService } from '../utils/DateService'; +import { ColumnBounds } from '../utils/ColumnDetectionUtils'; /** * EventRenderingService - Render events i DOM med positionering using Strategy Pattern * Håndterer event positioning og overlap detection @@ -15,6 +17,7 @@ export class EventRenderingService { private eventBus: IEventBus; private eventManager: EventManager; private strategy: EventRendererStrategy; + private dateService: DateService; private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null; @@ -26,6 +29,10 @@ export class EventRenderingService { const calendarType = calendarConfig.getCalendarMode(); this.strategy = CalendarTypeFactory.getEventRenderer(calendarType); + // Initialize DateService + const timezone = calendarConfig.getTimezone?.(); + this.dateService = new DateService(timezone); + this.setupEventListeners(); } @@ -175,7 +182,7 @@ export class EventRenderingService { // Handle drag end events and delegate to appropriate renderer this.eventBus.on('drag:end', (event: Event) => { - const { originalElement: draggedElement, finalPosition, target } = (event as CustomEvent).detail; + const { originalElement: draggedElement, sourceColumn, finalPosition, target } = (event as CustomEvent).detail; const finalColumn = finalPosition.column; const finalY = finalPosition.snappedY; const eventId = draggedElement.dataset.eventId || ''; @@ -188,6 +195,27 @@ export class EventRenderingService { if (draggedElement && draggedClone && this.strategy.handleDragEnd) { this.strategy.handleDragEnd(eventId, draggedElement, draggedClone, finalColumn, finalY); } + + // Update event data in EventManager with new position from clone + if (draggedClone) { + const swpEvent = draggedClone as SwpEventElement; + const newStart = swpEvent.start; + const newEnd = swpEvent.end; + + this.eventManager.updateEvent(eventId, { + start: newStart, + end: newEnd + }); + + console.log('📝 EventRendererManager: Updated event in EventManager', { + eventId, + newStart, + newEnd + }); + } + + // Re-render affected columns for stacking/grouping (now with updated data) + this.reRenderAffectedColumns(sourceColumn, finalColumn); } // Clean up any remaining day event clones @@ -228,6 +256,37 @@ export class EventRenderingService { this.eventBus.on('drag:mouseleave-header', this.dragMouseLeaveHeaderListener); + // Handle resize end events + this.eventBus.on('resize:end', (event: Event) => { + const { eventId, element } = (event as CustomEvent).detail; + + // Update event data in EventManager with new end time from resized element + const swpEvent = element as SwpEventElement; + const newStart = swpEvent.start; + const newEnd = swpEvent.end; + + this.eventManager.updateEvent(eventId, { + start: newStart, + end: newEnd + }); + + console.log('📝 EventRendererManager: Updated event after resize', { + eventId, + newStart, + newEnd + }); + + // Find the column for this event + const columnElement = element.closest('swp-day-column') as HTMLElement; + if (columnElement) { + const columnDate = columnElement.dataset.date; + if (columnDate) { + // Re-render the column to recalculate stacking/grouping + this.renderSingleColumn(columnDate); + } + } + }); + // Handle navigation period change this.eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => { // Delegate to strategy if it handles navigation @@ -291,6 +350,73 @@ export class EventRenderingService { }); } + /** + * Re-render affected columns after drag to recalculate stacking/grouping + */ + private reRenderAffectedColumns(sourceColumn: ColumnBounds | null, targetColumn: ColumnBounds | null): void { + const columnsToRender = new Set(); + + // Add source column if exists + if (sourceColumn) { + columnsToRender.add(sourceColumn.date); + } + + // Add target column if exists and different from source + if (targetColumn && targetColumn.date !== sourceColumn?.date) { + columnsToRender.add(targetColumn.date); + } + + // Re-render each affected column + columnsToRender.forEach(columnDate => { + this.renderSingleColumn(columnDate); + }); + } + + /** + * Render events for a single column by re-rendering entire container + */ + private renderSingleColumn(columnDate: string): void { + // Find the column element + const columnElement = document.querySelector(`swp-day-column[data-date="${columnDate}"]`) as HTMLElement; + if (!columnElement) { + console.warn('EventRendererManager: Column not found', { columnDate }); + return; + } + + // Find the parent container (swp-day-columns) + const container = columnElement.closest('swp-day-columns') as HTMLElement; + if (!container) { + console.warn('EventRendererManager: Container not found'); + return; + } + + // Get all columns in container to determine date range + const allColumns = Array.from(container.querySelectorAll('swp-day-column')); + if (allColumns.length === 0) return; + + // Get date range from first and last column + const firstColumnDate = allColumns[0].dataset.date; + const lastColumnDate = allColumns[allColumns.length - 1].dataset.date; + + if (!firstColumnDate || !lastColumnDate) return; + + const startDate = this.dateService.parseISO(`${firstColumnDate}T00:00:00`); + const endDate = this.dateService.parseISO(`${lastColumnDate}T23:59:59.999`); + + // Re-render entire container (this will recalculate stacking for all columns) + this.renderEvents({ + container, + startDate, + endDate + }); + + console.log('🔄 EventRendererManager: Re-rendered container for column', { + columnDate, + startDate: firstColumnDate, + endDate: lastColumnDate + }); + } + private clearEvents(container?: HTMLElement): void { this.strategy.clearEvents(container); } diff --git a/src/types/EventTypes.ts b/src/types/EventTypes.ts index e5e5b09..73f88ab 100644 --- a/src/types/EventTypes.ts +++ b/src/types/EventTypes.ts @@ -39,9 +39,10 @@ export interface DragMoveEventPayload { export interface DragEndEventPayload { originalElement: HTMLElement; draggedClone: HTMLElement | null; + sourceColumn: ColumnBounds | null; // Where drag started mousePosition: MousePosition; finalPosition: { - column: ColumnBounds | null; + column: ColumnBounds | null; // Where drag ended snappedY: number; }; target: 'swp-day-column' | 'swp-day-header' | null; @@ -78,5 +79,12 @@ export interface DragColumnChangeEventPayload { // Header ready event payload export interface HeaderReadyEventPayload { headerElements: ColumnBounds[]; - + +} + +// Resize end event payload +export interface ResizeEndEventPayload { + eventId: string; + element: HTMLElement; + finalHeight: number; } \ No newline at end of file