Fixes drag and drop to header issues

Addresses two key issues related to dragging events to the header: the premature removal of the original event element and the creation of duplicate all-day events.

The original event is no longer removed when dragging to the header; it is now only removed upon a successful drop.

Also, it prevents the creation of duplicate all-day events by checking for existing all-day events before creating new ones, using DOM queries to ensure accurate state.
This commit is contained in:
Janus Knudsen 2025-09-17 23:39:29 +02:00
parent b4af5a9211
commit 46b8bf9fb5
10 changed files with 684 additions and 61 deletions

View file

@ -170,10 +170,11 @@ export class SwpAllDayEventElement extends BaseEventElement {
*/
private setAllDayAttributes(): void {
this.element.dataset.allDay = "true";
// Override start/end times to be full day
const dateStr = this.event.start.toISOString().split('T')[0];
this.element.dataset.start = `${dateStr}T00:00:00`;
this.element.dataset.end = `${dateStr}T23:59:59`;
// For all-day events, preserve original start/end dates but set to full day times
const startDateStr = this.event.start.toISOString().split('T')[0];
const endDateStr = this.event.end.toISOString().split('T')[0];
this.element.dataset.start = `${startDateStr}T00:00:00`;
this.element.dataset.end = `${endDateStr}T23:59:59`;
}
/**
@ -197,28 +198,36 @@ export class SwpAllDayEventElement extends BaseEventElement {
this.element.style.gridRow = row.toString();
}
/**
* Set grid column span for this all-day event
*/
public setColumnSpan(startColumn: number, endColumn: number): void {
this.element.style.gridColumn = `${startColumn} / ${endColumn + 1}`;
}
/**
* Factory method to create from CalendarEvent and target date
*/
public static fromCalendarEvent(event: CalendarEvent, targetDate: string): SwpAllDayEventElement {
// Calculate column index
const dayHeaders = document.querySelectorAll('swp-day-header');
let columnIndex = 1;
dayHeaders.forEach((header, index) => {
if ((header as HTMLElement).dataset.date === targetDate) {
columnIndex = index + 1;
}
});
public static fromCalendarEvent(event: CalendarEvent, targetDate?: string): SwpAllDayEventElement {
// Calculate column span based on event start and end dates
const { startColumn, endColumn, columnSpan } = this.calculateColumnSpan(event);
// For backwards compatibility, use targetDate if provided, otherwise use calculated start column
const finalStartColumn = targetDate ? this.getColumnIndexForDate(targetDate) : startColumn;
const finalEndColumn = targetDate ? finalStartColumn : endColumn;
const finalColumnSpan = targetDate ? 1 : columnSpan;
// Find occupied rows in this column using computedStyle
// Find occupied rows in the spanned columns using computedStyle
const existingEvents = document.querySelectorAll('swp-allday-event');
const occupiedRows = new Set<number>();
existingEvents.forEach(existingEvent => {
const style = getComputedStyle(existingEvent);
const eventCol = parseInt(style.gridColumnStart);
const eventStartCol = parseInt(style.gridColumnStart);
const eventEndCol = parseInt(style.gridColumnEnd);
if (eventCol === columnIndex) {
// Check if this existing event overlaps with our column span
if (this.columnsOverlap(eventStartCol, eventEndCol, finalStartColumn, finalEndColumn)) {
const eventRow = parseInt(style.gridRowStart) || 1;
occupiedRows.add(eventRow);
}
@ -230,9 +239,73 @@ export class SwpAllDayEventElement extends BaseEventElement {
targetRow++;
}
// Create element with both column and row
const element = new SwpAllDayEventElement(event, columnIndex);
// Create element with calculated column span
const element = new SwpAllDayEventElement(event, finalStartColumn);
element.setGridRow(targetRow);
element.setColumnSpan(finalStartColumn, finalEndColumn);
return element;
}
/**
* Calculate column span based on event start and end dates
*/
private static calculateColumnSpan(event: CalendarEvent): { startColumn: number; endColumn: number; columnSpan: number } {
const dayHeaders = document.querySelectorAll('swp-day-header');
// Extract dates from headers
const headerDates: string[] = [];
dayHeaders.forEach(header => {
const date = (header as HTMLElement).dataset.date;
if (date) {
headerDates.push(date);
}
});
// Format event dates for comparison (YYYY-MM-DD format)
const eventStartDate = event.start.toISOString().split('T')[0];
const eventEndDate = event.end.toISOString().split('T')[0];
// Find start and end column indices
let startColumn = 1;
let endColumn = headerDates.length;
headerDates.forEach((dateStr, index) => {
if (dateStr === eventStartDate) {
startColumn = index + 1;
}
if (dateStr === eventEndDate) {
endColumn = index + 1;
}
});
// Ensure end column is at least start column
if (endColumn < startColumn) {
endColumn = startColumn;
}
const columnSpan = endColumn - startColumn + 1;
return { startColumn, endColumn, columnSpan };
}
/**
* Get column index for a specific date
*/
private static getColumnIndexForDate(targetDate: string): number {
const dayHeaders = document.querySelectorAll('swp-day-header');
let columnIndex = 1;
dayHeaders.forEach((header, index) => {
if ((header as HTMLElement).dataset.date === targetDate) {
columnIndex = index + 1;
}
});
return columnIndex;
}
/**
* Check if two column ranges overlap
*/
private static columnsOverlap(startA: number, endA: number, startB: number, endB: number): boolean {
return !(endA < startB || endB < startA);
}
}