# Refactored HeaderManager - Fjern Ghost Columns ## 1. HeaderManager Ændringer ```typescript // src/managers/HeaderManager.ts /** * Setup header drag event listeners - REFACTORED VERSION */ public setupHeaderDragListeners(): void { const calendarHeader = this.getCalendarHeader(); if (!calendarHeader) return; // Use mouseenter instead of mouseover to avoid continuous firing this.headerEventListener = (event: Event) => { const target = event.target as HTMLElement; // Check if we're entering the all-day container const allDayContainer = target.closest('swp-allday-container'); if (allDayContainer) { // Calculate target date from mouse X coordinate const targetDate = this.calculateTargetDateFromMouseX(event as MouseEvent); if (targetDate) { const calendarType = calendarConfig.getCalendarMode(); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); eventBus.emit('header:mouseover', { element: allDayContainer, targetDate, headerRenderer }); } } }; // Header mouseleave listener - unchanged this.headerMouseLeaveListener = (event: Event) => { eventBus.emit('header:mouseleave', { element: event.target as HTMLElement }); }; // Use mouseenter instead of mouseover calendarHeader.addEventListener('mouseenter', this.headerEventListener, true); calendarHeader.addEventListener('mouseleave', this.headerMouseLeaveListener); } /** * Calculate target date from mouse X coordinate */ private calculateTargetDateFromMouseX(event: MouseEvent): string | null { const dayHeaders = document.querySelectorAll('swp-day-header'); const mouseX = event.clientX; for (const header of dayHeaders) { const headerElement = header as HTMLElement; const rect = headerElement.getBoundingClientRect(); // Check if mouse X is within this header's bounds if (mouseX >= rect.left && mouseX <= rect.right) { return headerElement.dataset.date || null; } } return null; } /** * Remove event listeners from header - UPDATED */ private removeEventListeners(): void { const calendarHeader = this.getCalendarHeader(); if (!calendarHeader) return; if (this.headerEventListener) { // Remove mouseenter listener calendarHeader.removeEventListener('mouseenter', this.headerEventListener, true); } if (this.headerMouseLeaveListener) { calendarHeader.removeEventListener('mouseleave', this.headerMouseLeaveListener); } } ``` ## 2. AllDayEventRenderer Ændringer ```typescript // src/renderers/AllDayEventRenderer.ts /** * Get or cache all-day container, create if it doesn't exist - SIMPLIFIED */ private getContainer(): HTMLElement | null { if (!this.container) { const header = document.querySelector('swp-calendar-header'); if (header) { // Try to find existing container this.container = header.querySelector('swp-allday-container'); // If not found, create it if (!this.container) { this.container = document.createElement('swp-allday-container'); header.appendChild(this.container); // NO MORE GHOST COLUMNS! 🎉 // Mouse detection handled by HeaderManager coordinate calculation } } } return this.container; } // REMOVE this method entirely: // private createGhostColumns(): void { ... } ``` ## 3. DragDropManager Ændringer ```typescript // src/managers/DragDropManager.ts // In constructor, update the header:mouseover listener eventBus.on('header:mouseover', (event) => { const { targetDate, element } = (event as CustomEvent).detail; if (this.draggedEventId && targetDate) { // Only proceed if we're actually dragging and have a valid target date const draggedElement = document.querySelector(`swp-event[data-event-id="${this.draggedEventId}"]`); if (draggedElement) { console.log('🎯 Converting to all-day for date:', targetDate); this.eventBus.emit('drag:convert-to-allday', { targetDate, originalElement: draggedElement, headerRenderer: (event as CustomEvent).detail.headerRenderer }); } } }); ``` ## 4. CSS Ændringer (hvis nødvendigt) ```css /* Ensure all-day container is properly positioned for mouse events */ swp-allday-container { position: relative; width: 100%; min-height: var(--all-day-row-height, 0px); display: grid; grid-template-columns: repeat(7, 1fr); /* Match day columns */ pointer-events: all; /* Ensure mouse events work */ } /* Remove any ghost column styles */ /* swp-allday-column styles can be removed if they were only for ghosts */ ``` ## 5. Fordele ved denne løsning: ✅ **Performance**: Ingen kontinuerlige mouseover events ✅ **Simplicity**: Fjerner ghost column kompleksitet ✅ **Accuracy**: Direkte coordinate-baseret detection ✅ **Maintainability**: Mindre kode at vedligeholde ✅ **Debugging**: Lettere at følge event flow ## 6. Potentielle udfordringer: ⚠️ **Event Bubbling**: `mouseenter` med `capture: true` for at fange events tidligt ⚠️ **Coordinate Precision**: Skal teste at coordinate beregning er præcis ⚠️ **Multi-day Events**: Skal stadig håndteres korrekt ved drop ## 7. Test Scenarie: 1. Drag et day-event 2. Træk musen ind i all-day området 3. `mouseenter` fyrer én gang og beregner target date 4. Event konverteres til all-day 5. Træk musen ud af all-day området 6. `mouseleave` fyrer og konverterer tilbage