Enables all-day event drag and drop
Implements comprehensive drag and drop for all-day events, allowing movement within the header and conversion to timed events when dragged into the calendar grid. Optimizes column detection with a cached bounding box strategy, improving performance and accuracy. Refactors event conversion logic and renames related event bus events for clarity.
This commit is contained in:
parent
f1d04ae12e
commit
0b7499521e
6 changed files with 338 additions and 106 deletions
|
|
@ -17,7 +17,7 @@ Når en day event dragges op til headeren (for at konvertere til all-day) og der
|
||||||
|
|
||||||
### Trin 3: Mouse enters Header ⚠️ PROBLEM STARTER HER
|
### Trin 3: Mouse enters Header ⚠️ PROBLEM STARTER HER
|
||||||
- **DragDropManager** (linje 95-112): Lytter til `header:mouseover`
|
- **DragDropManager** (linje 95-112): Lytter til `header:mouseover`
|
||||||
- Emitter `drag:convert-to-allday` event
|
- Emitter `drag:convert-to-allday_event` event
|
||||||
- **AllDayManager** (linje 232-285): `handleConvertToAllDay()`:
|
- **AllDayManager** (linje 232-285): `handleConvertToAllDay()`:
|
||||||
- Opretter all-day event i header
|
- Opretter all-day event i header
|
||||||
- **FJERNER original timed event permanent** (linje 274: `originalElement.remove()`)
|
- **FJERNER original timed event permanent** (linje 274: `originalElement.remove()`)
|
||||||
|
|
@ -25,7 +25,7 @@ Når en day event dragges op til headeren (for at konvertere til all-day) og der
|
||||||
|
|
||||||
### Trin 4: Mouse leaves Header (tilbage til grid) ⚠️ PROBLEM FORTSÆTTER
|
### Trin 4: Mouse leaves Header (tilbage til grid) ⚠️ PROBLEM FORTSÆTTER
|
||||||
- **DragDropManager** (linje 128-136): Lytter til `header:mouseleave`
|
- **DragDropManager** (linje 128-136): Lytter til `header:mouseleave`
|
||||||
- Emitter `drag:convert-from-allday` event
|
- Emitter `drag:convert-to-time_event` event
|
||||||
- **AllDayManager** (linje 290-311): `handleConvertFromAllDay()`:
|
- **AllDayManager** (linje 290-311): `handleConvertFromAllDay()`:
|
||||||
- Fjerner all-day event fra container
|
- Fjerner all-day event fra container
|
||||||
- Viser drag clone igen
|
- Viser drag clone igen
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ sequenceDiagram
|
||||||
Note over Mouse: Dragger over header
|
Note over Mouse: Dragger over header
|
||||||
loop Hver mouseover event
|
loop Hver mouseover event
|
||||||
Mouse->>Header: mouseover
|
Mouse->>Header: mouseover
|
||||||
Header->>AllDay: drag:convert-to-allday
|
Header->>AllDay: drag:convert-to-allday_event
|
||||||
AllDay->>AllDay: Opretter NYT all-day event ❌
|
AllDay->>AllDay: Opretter NYT all-day event ❌
|
||||||
Note over AllDay: Ingen check for eksisterende!
|
Note over AllDay: Ingen check for eksisterende!
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ eventBus.on('header:mouseover', (event) => {
|
||||||
if (draggedElement) {
|
if (draggedElement) {
|
||||||
console.log('🎯 Converting to all-day for date:', targetDate);
|
console.log('🎯 Converting to all-day for date:', targetDate);
|
||||||
|
|
||||||
this.eventBus.emit('drag:convert-to-allday', {
|
this.eventBus.emit('drag:convert-to-allday_event', {
|
||||||
targetDate,
|
targetDate,
|
||||||
originalElement: draggedElement,
|
originalElement: draggedElement,
|
||||||
headerRenderer: (event as CustomEvent).detail.headerRenderer
|
headerRenderer: (event as CustomEvent).detail.headerRenderer
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ export class AllDayManager {
|
||||||
* Setup event listeners for drag conversions
|
* Setup event listeners for drag conversions
|
||||||
*/
|
*/
|
||||||
private setupEventListeners(): void {
|
private setupEventListeners(): void {
|
||||||
eventBus.on('drag:convert-to-allday', (event) => {
|
eventBus.on('drag:convert-to-allday_event', (event) => {
|
||||||
const { targetDate, originalElement } = (event as CustomEvent).detail;
|
const { targetDate, originalElement } = (event as CustomEvent).detail;
|
||||||
console.log('🔄 AllDayManager: Received drag:convert-to-allday', {
|
console.log('🔄 AllDayManager: Received drag:convert-to-allday_event', {
|
||||||
targetDate,
|
targetDate,
|
||||||
originalElementId: originalElement?.dataset?.eventId,
|
originalElementId: originalElement?.dataset?.eventId,
|
||||||
originalElementTag: originalElement?.tagName
|
originalElementTag: originalElement?.tagName
|
||||||
|
|
@ -38,13 +38,6 @@ export class AllDayManager {
|
||||||
this.handleConvertToAllDay(targetDate, originalElement);
|
this.handleConvertToAllDay(targetDate, originalElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.on('drag:convert-from-allday', (event) => {
|
|
||||||
const { draggedEventId } = (event as CustomEvent).detail;
|
|
||||||
console.log('🔄 AllDayManager: Received drag:convert-from-allday', {
|
|
||||||
draggedEventId
|
|
||||||
});
|
|
||||||
this.handleConvertFromAllDay(draggedEventId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for requests to ensure all-day container exists
|
// Listen for requests to ensure all-day container exists
|
||||||
eventBus.on('allday:ensure-container', () => {
|
eventBus.on('allday:ensure-container', () => {
|
||||||
|
|
@ -57,6 +50,39 @@ export class AllDayManager {
|
||||||
console.log('🔄 AllDayManager: Received header:mouseleave, recalculating height');
|
console.log('🔄 AllDayManager: Received header:mouseleave, recalculating height');
|
||||||
this.checkAndAnimateAllDayHeight();
|
this.checkAndAnimateAllDayHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for drag operations on all-day events
|
||||||
|
eventBus.on('drag:start', (event) => {
|
||||||
|
const { eventId, mouseOffset } = (event as CustomEvent).detail;
|
||||||
|
|
||||||
|
// Check if this is an all-day event
|
||||||
|
const originalElement = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${eventId}"]`);
|
||||||
|
if (!originalElement) return; // Not an all-day event
|
||||||
|
|
||||||
|
console.log('🎯 AllDayManager: Starting drag for all-day event', { eventId });
|
||||||
|
this.handleDragStart(originalElement as HTMLElement, eventId, mouseOffset);
|
||||||
|
});
|
||||||
|
|
||||||
|
eventBus.on('drag:move', (event) => {
|
||||||
|
const { eventId, mousePosition } = (event as CustomEvent).detail;
|
||||||
|
|
||||||
|
// Only handle for all-day events
|
||||||
|
const dragClone = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${eventId}"]`);
|
||||||
|
if (dragClone) {
|
||||||
|
this.handleDragMove(dragClone as HTMLElement, mousePosition);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventBus.on('drag:end', (event) => {
|
||||||
|
const { eventId, finalPosition } = (event as CustomEvent).detail;
|
||||||
|
|
||||||
|
// Check if this was an all-day event
|
||||||
|
const originalElement = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${eventId}"]`);
|
||||||
|
const dragClone = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${eventId}"]`);
|
||||||
|
|
||||||
|
console.log('🎯 AllDayManager: Ending drag for all-day event', { eventId });
|
||||||
|
this.handleDragEnd(originalElement as HTMLElement, dragClone as HTMLElement, finalPosition);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -274,7 +300,7 @@ export class AllDayManager {
|
||||||
targetEnd.setHours(originalEnd.getHours(), originalEnd.getMinutes(), originalEnd.getSeconds(), originalEnd.getMilliseconds());
|
targetEnd.setHours(originalEnd.getHours(), originalEnd.getMinutes(), originalEnd.getSeconds(), originalEnd.getMilliseconds());
|
||||||
|
|
||||||
const calendarEvent: CalendarEvent = {
|
const calendarEvent: CalendarEvent = {
|
||||||
id: eventId,
|
id: `clone-${eventId}`,
|
||||||
title: title,
|
title: title,
|
||||||
start: targetStart,
|
start: targetStart,
|
||||||
end: targetEnd,
|
end: targetEnd,
|
||||||
|
|
@ -286,8 +312,8 @@ export class AllDayManager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if all-day event already exists for this event ID
|
// Check if all-day clone already exists for this event ID
|
||||||
const existingAllDayEvent = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${eventId}"]`);
|
const existingAllDayEvent = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${eventId}"]`);
|
||||||
if (existingAllDayEvent) {
|
if (existingAllDayEvent) {
|
||||||
// All-day event already exists, just ensure clone is hidden
|
// All-day event already exists, just ensure clone is hidden
|
||||||
const dragClone = document.querySelector(`swp-event[data-event-id="clone-${eventId}"]`);
|
const dragClone = document.querySelector(`swp-event[data-event-id="clone-${eventId}"]`);
|
||||||
|
|
@ -312,31 +338,6 @@ export class AllDayManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle conversion from all-day event back to day event
|
|
||||||
*/
|
|
||||||
private handleConvertFromAllDay(draggedEventId: string): void {
|
|
||||||
// Find and remove all-day event specifically in the container
|
|
||||||
const allDayEvent = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="${draggedEventId}"]`);
|
|
||||||
if (allDayEvent) {
|
|
||||||
allDayEvent.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show drag clone again with reset styles
|
|
||||||
const dragClone = document.querySelector(`swp-event[data-event-id="clone-${draggedEventId}"]`);
|
|
||||||
if (dragClone) {
|
|
||||||
const clone = dragClone as HTMLElement;
|
|
||||||
|
|
||||||
// Reset to standard day event styles
|
|
||||||
clone.style.display = 'block';
|
|
||||||
clone.style.zIndex = ''; // Fjern drag z-index
|
|
||||||
clone.style.cursor = ''; // Fjern drag cursor
|
|
||||||
clone.style.opacity = ''; // Fjern evt. opacity
|
|
||||||
clone.style.transform = ''; // Fjern evt. transforms
|
|
||||||
|
|
||||||
// Position styles (top, height, left, right) bevares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update row height when all-day events change
|
* Update row height when all-day events change
|
||||||
|
|
@ -355,31 +356,109 @@ export class AllDayManager {
|
||||||
let container = this.getAllDayContainer();
|
let container = this.getAllDayContainer();
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
console.log('🏗️ AllDayManager: Container not found, creating via AllDayEventRenderer...');
|
|
||||||
|
|
||||||
// Use the renderer to create container (which will call getContainer internally)
|
|
||||||
this.allDayEventRenderer.clearCache(); // Clear cache to force re-check
|
this.allDayEventRenderer.clearCache(); // Clear cache to force re-check
|
||||||
|
|
||||||
// The renderer's getContainer method will create the container if it doesn't exist
|
|
||||||
// We can trigger this by trying to get the container
|
|
||||||
const header = this.getCalendarHeader();
|
const header = this.getCalendarHeader();
|
||||||
if (header) {
|
container = document.createElement('swp-allday-container');
|
||||||
container = document.createElement('swp-allday-container');
|
header?.appendChild(container);
|
||||||
header.appendChild(container);
|
|
||||||
console.log('✅ AllDayManager: Created all-day container');
|
this.cachedAllDayContainer = container;
|
||||||
|
|
||||||
// Update our cache
|
|
||||||
this.cachedAllDayContainer = container;
|
|
||||||
} else {
|
|
||||||
console.log('❌ AllDayManager: No calendar header found, cannot create container');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('✅ AllDayManager: All-day container already exists');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle drag start for all-day events
|
||||||
|
*/
|
||||||
|
private handleDragStart(originalElement: HTMLElement, eventId: string, mouseOffset: any): void {
|
||||||
|
// 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle drag move for all-day events
|
||||||
|
*/
|
||||||
|
private handleDragMove(dragClone: HTMLElement, mousePosition: any): void {
|
||||||
|
// Calculate grid column based on mouse position
|
||||||
|
const dayHeaders = document.querySelectorAll('swp-day-header');
|
||||||
|
let targetColumn = 1;
|
||||||
|
|
||||||
|
dayHeaders.forEach((header, index) => {
|
||||||
|
const rect = header.getBoundingClientRect();
|
||||||
|
if (mousePosition.x >= rect.left && mousePosition.x <= rect.right) {
|
||||||
|
targetColumn = index + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update clone position
|
||||||
|
dragClone.style.gridColumn = targetColumn.toString();
|
||||||
|
|
||||||
|
console.log('🔄 AllDayManager: Updated drag clone position', {
|
||||||
|
eventId: dragClone.dataset.eventId,
|
||||||
|
targetColumn,
|
||||||
|
mouseX: mousePosition.x
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle drag end for all-day events
|
||||||
|
*/
|
||||||
|
private handleDragEnd(originalElement: HTMLElement, dragClone: HTMLElement, finalPosition: any): void {
|
||||||
|
// Remove original element
|
||||||
|
originalElement?.remove();
|
||||||
|
|
||||||
|
// Normalize clone
|
||||||
|
const cloneId = dragClone.dataset.eventId;
|
||||||
|
if (cloneId?.startsWith('clone-')) {
|
||||||
|
dragClone.dataset.eventId = cloneId.replace('clone-', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove dragging styles
|
||||||
|
dragClone.classList.remove('dragging');
|
||||||
|
dragClone.style.zIndex = '';
|
||||||
|
dragClone.style.cursor = '';
|
||||||
|
dragClone.style.opacity = '';
|
||||||
|
|
||||||
|
// Recalculate all-day container height
|
||||||
|
this.checkAndAnimateAllDayHeight();
|
||||||
|
|
||||||
|
console.log('✅ AllDayManager: Completed drag operation for all-day event', {
|
||||||
|
eventId: dragClone.dataset.eventId,
|
||||||
|
finalColumn: dragClone.style.gridColumn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up cached elements and resources
|
* Clean up cached elements and resources
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,12 @@ interface Position {
|
||||||
y: number;
|
y: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ColumnBounds {
|
||||||
|
date: string;
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class DragDropManager {
|
export class DragDropManager {
|
||||||
private eventBus: IEventBus;
|
private eventBus: IEventBus;
|
||||||
|
|
||||||
|
|
@ -45,6 +51,9 @@ export class DragDropManager {
|
||||||
lastColumnDate: null
|
lastColumnDate: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Column bounds cache for coordinate-based column detection
|
||||||
|
private columnBoundsCache: ColumnBounds[] = [];
|
||||||
|
|
||||||
// Auto-scroll properties
|
// Auto-scroll properties
|
||||||
private autoScrollAnimationId: number | null = null;
|
private autoScrollAnimationId: number | null = null;
|
||||||
private readonly scrollSpeed = 10; // pixels per frame
|
private readonly scrollSpeed = 10; // pixels per frame
|
||||||
|
|
@ -92,6 +101,19 @@ export class DragDropManager {
|
||||||
document.body.addEventListener('mousedown', this.boundHandlers.mouseDown);
|
document.body.addEventListener('mousedown', this.boundHandlers.mouseDown);
|
||||||
document.body.addEventListener('mouseup', this.boundHandlers.mouseUp);
|
document.body.addEventListener('mouseup', this.boundHandlers.mouseUp);
|
||||||
|
|
||||||
|
// Initialize column bounds cache
|
||||||
|
this.updateColumnBoundsCache();
|
||||||
|
|
||||||
|
// Listen to resize events to update cache
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
this.updateColumnBoundsCache();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen to navigation events to update cache
|
||||||
|
this.eventBus.on('navigation:completed', () => {
|
||||||
|
this.updateColumnBoundsCache();
|
||||||
|
});
|
||||||
|
|
||||||
// Listen for header mouseover events
|
// Listen for header mouseover events
|
||||||
this.eventBus.on('header:mouseover', (event) => {
|
this.eventBus.on('header:mouseover', (event) => {
|
||||||
const { targetDate, headerRenderer } = (event as CustomEvent).detail;
|
const { targetDate, headerRenderer } = (event as CustomEvent).detail;
|
||||||
|
|
@ -116,7 +138,7 @@ export class DragDropManager {
|
||||||
console.log('✅ DragDropManager: Converting to all-day for date:', targetDate);
|
console.log('✅ DragDropManager: Converting to all-day for date:', targetDate);
|
||||||
|
|
||||||
// Element findes stadig som day-event, så konverter
|
// Element findes stadig som day-event, så konverter
|
||||||
this.eventBus.emit('drag:convert-to-allday', {
|
this.eventBus.emit('drag:convert-to-allday_event', {
|
||||||
targetDate,
|
targetDate,
|
||||||
originalElement: draggedElement,
|
originalElement: draggedElement,
|
||||||
headerRenderer
|
headerRenderer
|
||||||
|
|
@ -147,8 +169,13 @@ export class DragDropManager {
|
||||||
this.eventBus.on('header:mouseleave', (event) => {
|
this.eventBus.on('header:mouseleave', (event) => {
|
||||||
// Check if we're dragging ANY event
|
// Check if we're dragging ANY event
|
||||||
if (this.draggedEventId) {
|
if (this.draggedEventId) {
|
||||||
this.eventBus.emit('drag:convert-from-allday', {
|
const mousePosition = { x: this.lastMousePosition.x, y: this.lastMousePosition.y };
|
||||||
draggedEventId: this.draggedEventId
|
const column = this.getColumnDateFromX(mousePosition.x);
|
||||||
|
|
||||||
|
this.eventBus.emit('drag:convert-to-time_event', {
|
||||||
|
draggedEventId: this.draggedEventId,
|
||||||
|
mousePosition: mousePosition,
|
||||||
|
column: column
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -358,25 +385,68 @@ export class DragDropManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimized column detection with caching
|
* Update column bounds cache for coordinate-based column detection
|
||||||
*/
|
*/
|
||||||
private detectColumn(mouseX: number, mouseY: number): string | null {
|
private updateColumnBoundsCache(): void {
|
||||||
const element = document.elementFromPoint(mouseX, mouseY);
|
// Reset cache
|
||||||
if (!element) return null;
|
this.columnBoundsCache = [];
|
||||||
|
|
||||||
// Walk up DOM tree to find swp-day-column
|
// Find alle kolonner
|
||||||
let current = element as HTMLElement;
|
const columns = document.querySelectorAll('swp-day-column');
|
||||||
while (current && current.tagName !== 'SWP-DAY-COLUMN') {
|
|
||||||
current = current.parentElement as HTMLElement;
|
// Cache hver kolonnes x-grænser
|
||||||
if (!current) return null;
|
columns.forEach(column => {
|
||||||
|
const rect = column.getBoundingClientRect();
|
||||||
|
const date = (column as HTMLElement).dataset.date;
|
||||||
|
|
||||||
|
if (date) {
|
||||||
|
this.columnBoundsCache.push({
|
||||||
|
date,
|
||||||
|
left: rect.left,
|
||||||
|
right: rect.right
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sorter efter x-position (fra venstre til højre)
|
||||||
|
this.columnBoundsCache.sort((a, b) => a.left - b.left);
|
||||||
|
|
||||||
|
console.log('📏 DragDropManager: Updated column bounds cache', {
|
||||||
|
columns: this.columnBoundsCache.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get column date from X coordinate using cached bounds
|
||||||
|
*/
|
||||||
|
private getColumnDateFromX(x: number): string | null {
|
||||||
|
// Opdater cache hvis tom
|
||||||
|
if (this.columnBoundsCache.length === 0) {
|
||||||
|
this.updateColumnBoundsCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnDate = current.dataset.date || null;
|
// Find den kolonne hvor x-koordinaten er indenfor grænserne
|
||||||
|
const column = this.columnBoundsCache.find(col =>
|
||||||
|
x >= col.left && x <= col.right
|
||||||
|
);
|
||||||
|
|
||||||
// Update cache if we found a new column
|
return column ? column.date : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coordinate-based column detection (replaces DOM traversal)
|
||||||
|
*/
|
||||||
|
private detectColumn(mouseX: number, mouseY: number): string | null {
|
||||||
|
// Brug den koordinatbaserede metode direkte
|
||||||
|
const columnDate = this.getColumnDateFromX(mouseX);
|
||||||
|
|
||||||
|
// Opdater stadig den eksisterende cache hvis vi finder en kolonne
|
||||||
if (columnDate && columnDate !== this.cachedElements.lastColumnDate) {
|
if (columnDate && columnDate !== this.cachedElements.lastColumnDate) {
|
||||||
this.cachedElements.currentColumn = current;
|
const columnElement = document.querySelector(`swp-day-column[data-date="${columnDate}"]`) as HTMLElement;
|
||||||
this.cachedElements.lastColumnDate = columnDate;
|
if (columnElement) {
|
||||||
|
this.cachedElements.currentColumn = columnElement;
|
||||||
|
this.cachedElements.lastColumnDate = columnDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return columnDate;
|
return columnDate;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { calendarConfig } from '../core/CalendarConfig';
|
||||||
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
|
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
|
||||||
import { EventManager } from '../managers/EventManager';
|
import { EventManager } from '../managers/EventManager';
|
||||||
import { EventRendererStrategy } from './EventRenderer';
|
import { EventRendererStrategy } from './EventRenderer';
|
||||||
|
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
||||||
|
|
@ -69,6 +70,28 @@ export class EventRenderingService {
|
||||||
this.eventBus.on(CoreEvents.VIEW_CHANGED, (event: Event) => {
|
this.eventBus.on(CoreEvents.VIEW_CHANGED, (event: Event) => {
|
||||||
this.handleViewChanged(event as CustomEvent);
|
this.handleViewChanged(event as CustomEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Simple drag:end listener to clean up day event clones
|
||||||
|
this.eventBus.on('drag:end', (event: Event) => {
|
||||||
|
const { eventId } = (event as CustomEvent).detail;
|
||||||
|
const dayEventClone = document.querySelector(`swp-event[data-event-id="clone-${eventId}"]`);
|
||||||
|
|
||||||
|
if (dayEventClone) {
|
||||||
|
dayEventClone.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for conversion from all-day event to time event
|
||||||
|
this.eventBus.on('drag:convert-to-time_event', (event: Event) => {
|
||||||
|
const { draggedEventId, mousePosition, column } = (event as CustomEvent).detail;
|
||||||
|
console.log('🔄 EventRendererManager: Received drag:convert-to-time_event', {
|
||||||
|
draggedEventId,
|
||||||
|
mousePosition,
|
||||||
|
column
|
||||||
|
});
|
||||||
|
this.handleConvertToTimeEvent(draggedEventId, mousePosition, column);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -128,6 +151,66 @@ export class EventRenderingService {
|
||||||
|
|
||||||
// New rendering will be triggered by subsequent GRID_RENDERED event
|
// New rendering will be triggered by subsequent GRID_RENDERED event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle conversion from all-day event to time event
|
||||||
|
*/
|
||||||
|
private handleConvertToTimeEvent(draggedEventId: string, mousePosition: any, column: string): void {
|
||||||
|
// Find all-day event clone
|
||||||
|
const allDayClone = document.querySelector(`swp-allday-container swp-allday-event[data-event-id="clone-${draggedEventId}"]`);
|
||||||
|
|
||||||
|
if (!allDayClone) {
|
||||||
|
console.warn('EventRendererManager: All-day clone not found - drag may not have started properly', { draggedEventId });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use SwpEventElement factory to create day event from all-day event
|
||||||
|
const dayEventElement = SwpEventElement.fromAllDayElement(allDayClone as HTMLElement);
|
||||||
|
const dayElement = dayEventElement.getElement();
|
||||||
|
|
||||||
|
// Remove the all-day clone - it's no longer needed since we're converting to day event
|
||||||
|
allDayClone.remove();
|
||||||
|
|
||||||
|
// Set clone ID
|
||||||
|
dayElement.dataset.eventId = `clone-${draggedEventId}`;
|
||||||
|
|
||||||
|
// Find target column
|
||||||
|
const columnElement = document.querySelector(`swp-day-column[data-date="${column}"]`);
|
||||||
|
if (!columnElement) {
|
||||||
|
console.warn('EventRendererManager: Target column not found', { column });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find events layer in the column
|
||||||
|
const eventsLayer = columnElement.querySelector('swp-events-layer');
|
||||||
|
if (!eventsLayer) {
|
||||||
|
console.warn('EventRendererManager: Events layer not found in column');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to events layer
|
||||||
|
eventsLayer.appendChild(dayElement);
|
||||||
|
|
||||||
|
// Position based on mouse Y coordinate
|
||||||
|
const columnRect = columnElement.getBoundingClientRect();
|
||||||
|
const relativeY = Math.max(0, mousePosition.y - columnRect.top);
|
||||||
|
dayElement.style.top = `${relativeY}px`;
|
||||||
|
|
||||||
|
// Set drag styling
|
||||||
|
dayElement.style.zIndex = '1000';
|
||||||
|
dayElement.style.cursor = 'grabbing';
|
||||||
|
dayElement.style.opacity = '';
|
||||||
|
dayElement.style.transform = '';
|
||||||
|
|
||||||
|
console.log('✅ EventRendererManager: Converted all-day event to time event', {
|
||||||
|
draggedEventId,
|
||||||
|
column,
|
||||||
|
mousePosition,
|
||||||
|
relativeY
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private clearEvents(container?: HTMLElement): void {
|
private clearEvents(container?: HTMLElement): void {
|
||||||
this.strategy.clearEvents(container);
|
this.strategy.clearEvents(container);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue