2025-09-12 00:36:02 +02:00
|
|
|
// All-day row height management and animations
|
|
|
|
|
|
|
|
|
|
import { eventBus } from '../core/EventBus';
|
|
|
|
|
import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig';
|
2025-09-13 00:39:56 +02:00
|
|
|
import { AllDayEventRenderer } from '../renderers/AllDayEventRenderer';
|
2025-09-27 15:01:22 +02:00
|
|
|
import { AllDayLayoutEngine, EventLayout } from '../utils/AllDayLayoutEngine';
|
2025-09-28 13:25:09 +02:00
|
|
|
import { ColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
2025-09-13 00:39:56 +02:00
|
|
|
import { CalendarEvent } from '../types/CalendarTypes';
|
2025-09-21 15:48:13 +02:00
|
|
|
import {
|
|
|
|
|
DragMouseEnterHeaderEventPayload,
|
|
|
|
|
DragStartEventPayload,
|
|
|
|
|
DragMoveEventPayload,
|
2025-09-26 22:11:57 +02:00
|
|
|
DragEndEventPayload,
|
|
|
|
|
DragColumnChangeEventPayload
|
2025-09-21 15:48:13 +02:00
|
|
|
} from '../types/EventTypes';
|
2025-09-23 20:44:15 +02:00
|
|
|
import { DragOffset, MousePosition } from '../types/DragDropTypes';
|
2025-09-12 00:36:02 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* AllDayManager - Handles all-day row height animations and management
|
2025-09-25 23:38:17 +02:00
|
|
|
* Uses AllDayLayoutEngine for all overlap detection and layout calculation
|
2025-09-12 00:36:02 +02:00
|
|
|
*/
|
|
|
|
|
export class AllDayManager {
|
2025-09-13 00:39:56 +02:00
|
|
|
private allDayEventRenderer: AllDayEventRenderer;
|
2025-09-25 23:38:17 +02:00
|
|
|
private layoutEngine: AllDayLayoutEngine | null = null;
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// State tracking for differential updates
|
|
|
|
|
private currentLayouts: Map<string, string> = new Map();
|
|
|
|
|
private currentAllDayEvents: CalendarEvent[] = [];
|
|
|
|
|
private currentWeekDates: string[] = [];
|
2025-09-12 00:36:02 +02:00
|
|
|
|
|
|
|
|
constructor() {
|
2025-09-13 00:39:56 +02:00
|
|
|
this.allDayEventRenderer = new AllDayEventRenderer();
|
|
|
|
|
this.setupEventListeners();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Setup event listeners for drag conversions
|
|
|
|
|
*/
|
|
|
|
|
private setupEventListeners(): void {
|
2025-09-21 15:48:13 +02:00
|
|
|
eventBus.on('drag:mouseenter-header', (event) => {
|
2025-09-29 20:50:52 +02:00
|
|
|
const payload = (event as CustomEvent<DragMouseEnterHeaderEventPayload>).detail;
|
2025-09-21 21:30:51 +02:00
|
|
|
|
2025-09-21 15:48:13 +02:00
|
|
|
console.log('🔄 AllDayManager: Received drag:mouseenter-header', {
|
2025-09-29 20:50:52 +02:00
|
|
|
targetDate: payload.targetColumn,
|
|
|
|
|
originalElementId: payload.originalElement?.dataset?.eventId,
|
|
|
|
|
originalElementTag: payload.originalElement?.tagName
|
2025-09-18 17:55:52 +02:00
|
|
|
});
|
2025-09-22 23:37:43 +02:00
|
|
|
|
2025-09-29 20:50:52 +02:00
|
|
|
this.handleConvertToAllDay(payload);
|
2025-09-22 23:37:43 +02:00
|
|
|
this.checkAndAnimateAllDayHeight();
|
2025-09-13 00:39:56 +02:00
|
|
|
});
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-21 21:30:51 +02:00
|
|
|
eventBus.on('drag:mouseleave-header', (event) => {
|
|
|
|
|
const { originalElement, cloneElement } = (event as CustomEvent).detail;
|
2025-09-18 17:55:52 +02:00
|
|
|
|
2025-09-21 21:30:51 +02:00
|
|
|
console.log('🚪 AllDayManager: Received drag:mouseleave-header', {
|
|
|
|
|
originalElementId: originalElement?.dataset?.eventId
|
|
|
|
|
});
|
|
|
|
|
|
2025-09-22 23:37:43 +02:00
|
|
|
this.checkAndAnimateAllDayHeight();
|
2025-09-18 19:26:00 +02:00
|
|
|
});
|
2025-09-19 00:20:30 +02:00
|
|
|
|
|
|
|
|
// Listen for drag operations on all-day events
|
|
|
|
|
eventBus.on('drag:start', (event) => {
|
2025-09-26 22:53:49 +02:00
|
|
|
const { draggedElement, draggedClone, mouseOffset } = (event as CustomEvent<DragStartEventPayload>).detail;
|
2025-09-21 21:30:51 +02:00
|
|
|
|
2025-09-21 15:48:13 +02:00
|
|
|
// Check if this is an all-day event by checking if it's in all-day container
|
|
|
|
|
const isAllDayEvent = draggedElement.closest('swp-allday-container');
|
|
|
|
|
if (!isAllDayEvent) return; // Not an all-day event
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-21 15:48:13 +02:00
|
|
|
const eventId = draggedElement.dataset.eventId;
|
2025-09-19 00:20:30 +02:00
|
|
|
console.log('🎯 AllDayManager: Starting drag for all-day event', { eventId });
|
2025-09-21 15:48:13 +02:00
|
|
|
this.handleDragStart(draggedElement, eventId || '', mouseOffset);
|
2025-09-19 00:20:30 +02:00
|
|
|
});
|
|
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
eventBus.on('drag:column-change', (event) => {
|
2025-09-30 00:13:52 +02:00
|
|
|
const { originalElement: draggedElement, draggedClone, mousePosition } = (event as CustomEvent<DragColumnChangeEventPayload>).detail;
|
2025-09-28 13:25:09 +02:00
|
|
|
|
|
|
|
|
if (draggedClone == null)
|
2025-09-26 22:53:49 +02:00
|
|
|
return;
|
2025-09-21 15:48:13 +02:00
|
|
|
|
2025-09-26 22:53:49 +02:00
|
|
|
// Filter: Only handle events where clone IS an all-day event
|
|
|
|
|
if (!draggedClone.hasAttribute('data-allday')) {
|
|
|
|
|
return; // This is not an all-day event, let EventRendererManager handle it
|
2025-09-26 22:11:57 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-26 22:53:49 +02:00
|
|
|
console.log('🔄 AllDayManager: Handling drag:column-change for all-day event', {
|
2025-09-28 13:25:09 +02:00
|
|
|
eventId: draggedElement.dataset.eventId,
|
2025-09-26 22:53:49 +02:00
|
|
|
cloneId: draggedClone.dataset.eventId
|
|
|
|
|
});
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-26 22:53:49 +02:00
|
|
|
this.handleColumnChange(draggedClone, mousePosition);
|
2025-09-19 00:20:30 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
eventBus.on('drag:end', (event) => {
|
2025-09-28 13:25:09 +02:00
|
|
|
let draggedElement: DragEndEventPayload = (event as CustomEvent<DragEndEventPayload>).detail;
|
2025-09-20 09:40:56 +02:00
|
|
|
|
2025-09-28 13:25:09 +02:00
|
|
|
if (draggedElement.target != 'swp-day-header') // we are not inside the swp-day-header, so just ignore.
|
2025-09-20 09:40:56 +02:00
|
|
|
return;
|
|
|
|
|
|
2025-09-29 20:50:52 +02:00
|
|
|
this.handleDragEnd(draggedElement);
|
2025-09-19 00:20:30 +02:00
|
|
|
});
|
2025-09-22 17:51:24 +02:00
|
|
|
|
|
|
|
|
// Listen for drag cancellation to recalculate height
|
|
|
|
|
eventBus.on('drag:cancelled', (event) => {
|
|
|
|
|
const { draggedElement, reason } = (event as CustomEvent).detail;
|
2025-09-22 23:37:43 +02:00
|
|
|
|
2025-09-22 17:51:24 +02:00
|
|
|
console.log('🚫 AllDayManager: Drag cancelled', {
|
|
|
|
|
eventId: draggedElement?.dataset?.eventId,
|
|
|
|
|
reason
|
|
|
|
|
});
|
2025-09-22 23:37:43 +02:00
|
|
|
|
2025-09-22 17:51:24 +02:00
|
|
|
// Recalculate all-day height since clones may have been removed
|
|
|
|
|
this.checkAndAnimateAllDayHeight();
|
|
|
|
|
});
|
2025-09-22 21:53:18 +02:00
|
|
|
|
|
|
|
|
// Listen for height check requests from EventRendererManager
|
|
|
|
|
eventBus.on('allday:checkHeight', () => {
|
|
|
|
|
console.log('📏 AllDayManager: Received allday:checkHeight request');
|
|
|
|
|
this.checkAndAnimateAllDayHeight();
|
|
|
|
|
});
|
2025-09-12 00:36:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getAllDayContainer(): HTMLElement | null {
|
2025-09-22 23:37:43 +02:00
|
|
|
return document.querySelector('swp-calendar-header swp-allday-container');
|
2025-09-12 00:36:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getCalendarHeader(): HTMLElement | null {
|
2025-09-22 23:37:43 +02:00
|
|
|
return document.querySelector('swp-calendar-header');
|
2025-09-12 00:36:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getHeaderSpacer(): HTMLElement | null {
|
2025-09-22 23:37:43 +02:00
|
|
|
return document.querySelector('swp-header-spacer');
|
2025-09-12 00:36:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate all-day height based on number of rows
|
|
|
|
|
*/
|
|
|
|
|
private calculateAllDayHeight(targetRows: number): {
|
|
|
|
|
targetHeight: number;
|
|
|
|
|
currentHeight: number;
|
|
|
|
|
heightDifference: number;
|
|
|
|
|
} {
|
|
|
|
|
const root = document.documentElement;
|
|
|
|
|
const targetHeight = targetRows * ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT;
|
2025-09-25 23:38:17 +02:00
|
|
|
// Read CSS variable directly from style property or default to 0
|
|
|
|
|
const currentHeightStr = root.style.getPropertyValue('--all-day-row-height') || '0px';
|
|
|
|
|
const currentHeight = parseInt(currentHeightStr) || 0;
|
2025-09-12 00:36:02 +02:00
|
|
|
const heightDifference = targetHeight - currentHeight;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
return { targetHeight, currentHeight, heightDifference };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Collapse all-day row when no events
|
|
|
|
|
*/
|
|
|
|
|
public collapseAllDayRow(): void {
|
|
|
|
|
this.animateToRows(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check current all-day events and animate to correct height
|
|
|
|
|
*/
|
|
|
|
|
public checkAndAnimateAllDayHeight(): void {
|
|
|
|
|
const container = this.getAllDayContainer();
|
2025-09-22 23:37:43 +02:00
|
|
|
if (!container) {
|
|
|
|
|
this.animateToRows(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-09-21 21:30:51 +02:00
|
|
|
|
|
|
|
|
const allDayEvents = container.querySelectorAll('swp-event');
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
// Calculate required rows - 0 if no events (will collapse)
|
|
|
|
|
let maxRows = 0;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
if (allDayEvents.length > 0) {
|
2025-09-25 18:17:37 +02:00
|
|
|
// Find the HIGHEST row number in use (not count of unique rows)
|
|
|
|
|
let highestRow = 0;
|
2025-09-22 23:37:43 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
(Array.from(allDayEvents) as HTMLElement[]).forEach((event: HTMLElement) => {
|
2025-09-25 23:38:17 +02:00
|
|
|
const gridRow = parseInt(event.style.gridRow) || 1;
|
2025-09-25 18:17:37 +02:00
|
|
|
highestRow = Math.max(highestRow, gridRow);
|
2025-09-21 22:13:31 +02:00
|
|
|
});
|
2025-09-22 23:37:43 +02:00
|
|
|
|
2025-09-25 18:17:37 +02:00
|
|
|
// Max rows = highest row number (e.g. if row 3 is used, height = 3 rows)
|
|
|
|
|
maxRows = highestRow;
|
2025-09-22 23:37:43 +02:00
|
|
|
|
2025-09-25 18:17:37 +02:00
|
|
|
console.log('🔍 AllDayManager: Height calculation FIXED', {
|
2025-09-21 22:13:31 +02:00
|
|
|
totalEvents: allDayEvents.length,
|
2025-09-25 18:17:37 +02:00
|
|
|
highestRowFound: highestRow,
|
2025-09-21 22:13:31 +02:00
|
|
|
maxRows
|
2025-09-12 00:36:02 +02:00
|
|
|
});
|
|
|
|
|
}
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
// Animate to required rows (0 = collapse, >0 = expand)
|
|
|
|
|
this.animateToRows(maxRows);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Animate all-day container to specific number of rows
|
|
|
|
|
*/
|
|
|
|
|
public animateToRows(targetRows: number): void {
|
|
|
|
|
const { targetHeight, currentHeight, heightDifference } = this.calculateAllDayHeight(targetRows);
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
if (targetHeight === currentHeight) return; // No animation needed
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
console.log(`🎬 All-day height animation: ${currentHeight}px → ${targetHeight}px (${Math.ceil(currentHeight / ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT)} → ${targetRows} rows)`);
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
// Get cached elements
|
|
|
|
|
const calendarHeader = this.getCalendarHeader();
|
|
|
|
|
const headerSpacer = this.getHeaderSpacer();
|
|
|
|
|
const allDayContainer = this.getAllDayContainer();
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
if (!calendarHeader || !allDayContainer) return;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
// Get current parent height for animation
|
|
|
|
|
const currentParentHeight = parseFloat(getComputedStyle(calendarHeader).height);
|
|
|
|
|
const targetParentHeight = currentParentHeight + heightDifference;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
const animations = [
|
|
|
|
|
calendarHeader.animate([
|
|
|
|
|
{ height: `${currentParentHeight}px` },
|
|
|
|
|
{ height: `${targetParentHeight}px` }
|
|
|
|
|
], {
|
2025-09-22 23:37:43 +02:00
|
|
|
duration: 150,
|
2025-09-12 00:36:02 +02:00
|
|
|
easing: 'ease-out',
|
|
|
|
|
fill: 'forwards'
|
|
|
|
|
})
|
|
|
|
|
];
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-23 16:30:30 +02:00
|
|
|
// Add spacer animation if spacer exists, but don't use fill: 'forwards'
|
2025-09-12 00:36:02 +02:00
|
|
|
if (headerSpacer) {
|
|
|
|
|
const root = document.documentElement;
|
2025-09-25 23:38:17 +02:00
|
|
|
const headerHeightStr = root.style.getPropertyValue('--header-height');
|
|
|
|
|
const headerHeight = parseInt(headerHeightStr);
|
|
|
|
|
const currentSpacerHeight = headerHeight + currentHeight;
|
|
|
|
|
const targetSpacerHeight = headerHeight + targetHeight;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
animations.push(
|
|
|
|
|
headerSpacer.animate([
|
|
|
|
|
{ height: `${currentSpacerHeight}px` },
|
|
|
|
|
{ height: `${targetSpacerHeight}px` }
|
|
|
|
|
], {
|
2025-09-23 09:46:47 +02:00
|
|
|
duration: 150,
|
2025-09-23 16:30:30 +02:00
|
|
|
easing: 'ease-out'
|
|
|
|
|
// No fill: 'forwards' - let CSS calc() take over after animation
|
2025-09-12 00:36:02 +02:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
// Update CSS variable after animation
|
|
|
|
|
Promise.all(animations.map(anim => anim.finished)).then(() => {
|
|
|
|
|
const root = document.documentElement;
|
|
|
|
|
root.style.setProperty('--all-day-row-height', `${targetHeight}px`);
|
|
|
|
|
eventBus.emit('header:height-changed');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
/**
|
|
|
|
|
* Store current layouts from DOM for comparison
|
|
|
|
|
*/
|
|
|
|
|
private storeCurrentLayouts(): void {
|
|
|
|
|
this.currentLayouts.clear();
|
|
|
|
|
const container = this.getAllDayContainer();
|
|
|
|
|
if (!container) return;
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
container.querySelectorAll('swp-event').forEach(element => {
|
|
|
|
|
const htmlElement = element as HTMLElement;
|
|
|
|
|
const eventId = htmlElement.dataset.eventId;
|
|
|
|
|
const gridArea = htmlElement.style.gridArea;
|
|
|
|
|
if (eventId && gridArea) {
|
|
|
|
|
this.currentLayouts.set(eventId, gridArea);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
console.log('📋 AllDayManager: Stored current layouts', {
|
|
|
|
|
count: this.currentLayouts.size,
|
|
|
|
|
layouts: Array.from(this.currentLayouts.entries())
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set current events and week dates (called by EventRendererManager)
|
|
|
|
|
*/
|
|
|
|
|
public setCurrentEvents(events: CalendarEvent[], weekDates: string[]): void {
|
|
|
|
|
this.currentAllDayEvents = events;
|
|
|
|
|
this.currentWeekDates = weekDates;
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
console.log('📝 AllDayManager: Set current events', {
|
|
|
|
|
eventCount: events.length,
|
|
|
|
|
weekDatesCount: weekDates.length
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-25 23:38:17 +02:00
|
|
|
/**
|
|
|
|
|
* Calculate layout for ALL all-day events using AllDayLayoutEngine
|
|
|
|
|
* This is the correct method that processes all events together for proper overlap detection
|
|
|
|
|
*/
|
2025-09-27 15:01:22 +02:00
|
|
|
public calculateAllDayEventsLayout(events: CalendarEvent[], weekDates: string[]): EventLayout[] {
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// Store current state
|
|
|
|
|
this.currentAllDayEvents = events;
|
|
|
|
|
this.currentWeekDates = weekDates;
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-25 23:38:17 +02:00
|
|
|
// Initialize layout engine with provided week dates
|
|
|
|
|
this.layoutEngine = new AllDayLayoutEngine(weekDates);
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-25 23:38:17 +02:00
|
|
|
// Calculate layout for all events together - AllDayLayoutEngine handles CalendarEvents directly
|
2025-09-27 15:01:22 +02:00
|
|
|
return this.layoutEngine.calculateLayout(events);
|
2025-09-28 13:25:09 +02:00
|
|
|
|
2025-09-25 23:38:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-09-13 00:39:56 +02:00
|
|
|
/**
|
2025-09-26 22:11:57 +02:00
|
|
|
* Handle conversion of timed event to all-day event - SIMPLIFIED
|
|
|
|
|
* During drag: Place in row 1 only, calculate column from targetDate
|
2025-09-13 00:39:56 +02:00
|
|
|
*/
|
2025-09-29 20:50:52 +02:00
|
|
|
private handleConvertToAllDay(payload: DragMouseEnterHeaderEventPayload): void {
|
2025-09-30 00:13:52 +02:00
|
|
|
|
|
|
|
|
if(payload.draggedClone?.dataset == null)
|
|
|
|
|
console.error("payload.cloneElement.dataset.eventId is null");
|
|
|
|
|
|
|
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
console.log('🔄 AllDayManager: Converting to all-day (row 1 only during drag)', {
|
2025-09-30 00:13:52 +02:00
|
|
|
eventId: payload.draggedClone.dataset.eventId,
|
2025-09-29 20:50:52 +02:00
|
|
|
targetDate: payload.targetColumn
|
2025-09-21 21:30:51 +02:00
|
|
|
});
|
2025-09-13 00:39:56 +02:00
|
|
|
|
2025-09-21 21:30:51 +02:00
|
|
|
// Get all-day container, request creation if needed
|
|
|
|
|
let allDayContainer = this.getAllDayContainer();
|
2025-09-16 23:09:56 +02:00
|
|
|
|
2025-09-25 23:38:17 +02:00
|
|
|
|
2025-09-30 00:13:52 +02:00
|
|
|
payload.draggedClone.removeAttribute('style');
|
|
|
|
|
payload.draggedClone.style.gridRow = '1';
|
|
|
|
|
payload.draggedClone.style.gridColumn = payload.targetColumn.index.toString();
|
|
|
|
|
payload.draggedClone.dataset.allday = 'true'; // Set the all-day attribute for filtering
|
2025-09-21 21:30:51 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// Add to container
|
2025-09-30 00:13:52 +02:00
|
|
|
allDayContainer?.appendChild(payload.draggedClone);
|
|
|
|
|
|
|
|
|
|
ColumnDetectionUtils.updateColumnBoundsCache();
|
2025-09-21 22:17:24 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
console.log('✅ AllDayManager: Converted to all-day style (simple row 1)', {
|
2025-09-30 00:13:52 +02:00
|
|
|
eventId: payload.draggedClone.dataset.eventId,
|
2025-09-29 20:50:52 +02:00
|
|
|
gridColumn: payload.targetColumn,
|
2025-09-26 22:11:57 +02:00
|
|
|
gridRow: 1
|
2025-09-21 21:30:51 +02:00
|
|
|
});
|
|
|
|
|
}
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-18 17:55:52 +02:00
|
|
|
|
2025-09-19 00:20:30 +02:00
|
|
|
/**
|
|
|
|
|
* Handle drag start for all-day events
|
|
|
|
|
*/
|
2025-09-23 20:44:15 +02:00
|
|
|
private handleDragStart(originalElement: HTMLElement, eventId: string, mouseOffset: DragOffset): void {
|
2025-09-19 00:20:30 +02:00
|
|
|
// Create clone
|
|
|
|
|
const clone = originalElement.cloneNode(true) as HTMLElement;
|
|
|
|
|
clone.dataset.eventId = `clone-${eventId}`;
|
|
|
|
|
|
|
|
|
|
// Get container
|
|
|
|
|
const container = this.getAllDayContainer();
|
|
|
|
|
if (!container) return;
|
|
|
|
|
|
|
|
|
|
// Add clone to container
|
|
|
|
|
container.appendChild(clone);
|
|
|
|
|
|
|
|
|
|
// Copy positioning from original
|
|
|
|
|
clone.style.gridColumn = originalElement.style.gridColumn;
|
|
|
|
|
clone.style.gridRow = originalElement.style.gridRow;
|
|
|
|
|
|
|
|
|
|
// Add dragging style
|
|
|
|
|
clone.classList.add('dragging');
|
|
|
|
|
clone.style.zIndex = '1000';
|
|
|
|
|
clone.style.cursor = 'grabbing';
|
|
|
|
|
|
|
|
|
|
// Make original semi-transparent
|
|
|
|
|
originalElement.style.opacity = '0.3';
|
|
|
|
|
|
|
|
|
|
console.log('✅ AllDayManager: Created drag clone for all-day event', {
|
|
|
|
|
eventId,
|
|
|
|
|
cloneId: clone.dataset.eventId,
|
|
|
|
|
gridColumn: clone.style.gridColumn,
|
|
|
|
|
gridRow: clone.style.gridRow
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-26 22:11:57 +02:00
|
|
|
* Handle drag move for all-day events - SPECIALIZED FOR ALL-DAY CONTAINER
|
2025-09-19 00:20:30 +02:00
|
|
|
*/
|
2025-09-26 22:11:57 +02:00
|
|
|
private handleColumnChange(dragClone: HTMLElement, mousePosition: MousePosition): void {
|
|
|
|
|
// Get the all-day container to understand its grid structure
|
|
|
|
|
const allDayContainer = this.getAllDayContainer();
|
|
|
|
|
if (!allDayContainer) return;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// Calculate target column using ColumnDetectionUtils
|
2025-09-28 13:25:09 +02:00
|
|
|
const targetColumn = ColumnDetectionUtils.getColumnBounds(mousePosition);
|
|
|
|
|
|
|
|
|
|
if (targetColumn == null)
|
|
|
|
|
return;
|
2025-09-26 22:11:57 +02:00
|
|
|
|
|
|
|
|
// Update clone position - ALWAYS keep in row 1 during drag
|
|
|
|
|
// Use simple grid positioning that matches all-day container structure
|
2025-09-28 13:25:09 +02:00
|
|
|
dragClone.style.gridColumn = targetColumn.index.toString();
|
2025-09-26 22:11:57 +02:00
|
|
|
dragClone.style.gridRow = '1'; // Force row 1 during drag
|
2025-09-28 13:25:09 +02:00
|
|
|
dragClone.style.gridArea = `1 / ${targetColumn.index} / 2 / ${targetColumn.index + 1}`;
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
console.log('🔄 AllDayManager: Updated all-day drag clone position', {
|
2025-09-19 00:20:30 +02:00
|
|
|
eventId: dragClone.dataset.eventId,
|
|
|
|
|
targetColumn,
|
2025-09-26 22:11:57 +02:00
|
|
|
gridRow: 1,
|
|
|
|
|
gridArea: dragClone.style.gridArea,
|
2025-09-19 00:20:30 +02:00
|
|
|
mouseX: mousePosition.x
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-26 22:11:57 +02:00
|
|
|
* Handle drag end for all-day events - WITH DIFFERENTIAL UPDATES
|
2025-09-19 00:20:30 +02:00
|
|
|
*/
|
2025-09-28 13:25:09 +02:00
|
|
|
private handleDragEnd(dragEndEvent: DragEndEventPayload): void {
|
2025-09-26 22:11:57 +02:00
|
|
|
console.log('🎯 AllDayManager: Starting drag end with differential updates', {
|
2025-09-28 13:25:09 +02:00
|
|
|
dragEndEvent
|
2025-09-26 22:11:57 +02:00
|
|
|
});
|
|
|
|
|
|
2025-09-28 13:25:09 +02:00
|
|
|
if (dragEndEvent.draggedClone == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// 1. Store current layouts BEFORE any changes
|
|
|
|
|
this.storeCurrentLayouts();
|
|
|
|
|
|
|
|
|
|
// 2. Normalize clone ID
|
2025-09-28 13:25:09 +02:00
|
|
|
const cloneId = dragEndEvent.draggedClone?.dataset.eventId;
|
2025-09-19 00:20:30 +02:00
|
|
|
if (cloneId?.startsWith('clone-')) {
|
2025-09-28 13:25:09 +02:00
|
|
|
dragEndEvent.draggedClone.dataset.eventId = cloneId.replace('clone-', '');
|
2025-09-19 00:20:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// 3. Create temporary array with existing events + the dropped event
|
2025-09-28 13:25:09 +02:00
|
|
|
let eventId = dragEndEvent.draggedClone.dataset.eventId;
|
|
|
|
|
let eventDate = dragEndEvent.finalPosition.column?.date;
|
|
|
|
|
let eventType = dragEndEvent.draggedClone.dataset.type;
|
|
|
|
|
|
|
|
|
|
if (eventDate == null || eventId == null || eventType == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
const droppedEvent: CalendarEvent = {
|
2025-09-28 13:25:09 +02:00
|
|
|
id: eventId,
|
|
|
|
|
title: dragEndEvent.draggedClone.dataset.title || dragEndEvent.draggedClone.textContent || '',
|
|
|
|
|
start: new Date(eventDate),
|
|
|
|
|
end: new Date(eventDate),
|
|
|
|
|
type: eventType,
|
2025-09-26 22:11:57 +02:00
|
|
|
allDay: true,
|
|
|
|
|
syncStatus: 'synced'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Use current events + dropped event for calculation
|
|
|
|
|
const tempEvents = [...this.currentAllDayEvents, droppedEvent];
|
|
|
|
|
|
|
|
|
|
// 4. Calculate new layouts for ALL events
|
|
|
|
|
const newLayouts = this.calculateAllDayEventsLayout(tempEvents, this.currentWeekDates);
|
|
|
|
|
|
|
|
|
|
// 5. Apply differential updates - only update events that changed
|
|
|
|
|
let changedCount = 0;
|
2025-09-27 15:01:22 +02:00
|
|
|
newLayouts.forEach((layout) => {
|
|
|
|
|
const oldGridArea = this.currentLayouts.get(layout.calenderEvent.id);
|
2025-09-26 22:11:57 +02:00
|
|
|
const newGridArea = layout.gridArea;
|
|
|
|
|
|
|
|
|
|
if (oldGridArea !== newGridArea) {
|
|
|
|
|
changedCount++;
|
2025-09-30 00:13:52 +02:00
|
|
|
const element = dragEndEvent.draggedClone; //:end document.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement;
|
2025-09-26 22:11:57 +02:00
|
|
|
if (element) {
|
|
|
|
|
|
|
|
|
|
// Add transition class for smooth animation
|
|
|
|
|
element.classList.add('transitioning');
|
|
|
|
|
element.style.gridArea = newGridArea;
|
|
|
|
|
element.style.gridRow = layout.row.toString();
|
|
|
|
|
element.style.gridColumn = `${layout.startColumn} / ${layout.endColumn + 1}`;
|
|
|
|
|
|
|
|
|
|
// Remove transition class after animation
|
|
|
|
|
setTimeout(() => element.classList.remove('transitioning'), 200);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 6. Clean up drag styles from the dropped clone
|
2025-09-28 13:25:09 +02:00
|
|
|
dragEndEvent.draggedClone.classList.remove('dragging');
|
|
|
|
|
dragEndEvent.draggedClone.style.zIndex = '';
|
|
|
|
|
dragEndEvent.draggedClone.style.cursor = '';
|
|
|
|
|
dragEndEvent.draggedClone.style.opacity = '';
|
2025-09-19 00:20:30 +02:00
|
|
|
|
2025-09-26 22:11:57 +02:00
|
|
|
// 7. Restore original element opacity
|
2025-09-30 00:13:52 +02:00
|
|
|
dragEndEvent.originalElement.remove();
|
2025-09-28 13:25:09 +02:00
|
|
|
//originalElement.style.opacity = '';
|
2025-09-26 22:11:57 +02:00
|
|
|
|
|
|
|
|
// 8. Check if height adjustment is needed
|
|
|
|
|
this.checkAndAnimateAllDayHeight();
|
|
|
|
|
|
2025-09-25 23:38:17 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-12 00:36:02 +02:00
|
|
|
}
|