diff --git a/src/renderers/EventRenderer.ts b/src/renderers/EventRenderer.ts index 07700b4..f3ff195 100644 --- a/src/renderers/EventRenderer.ts +++ b/src/renderers/EventRenderer.ts @@ -80,9 +80,11 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { return; } - // Clear any existing all-day containers first + // Clear content of existing all-day containers (but keep the containers) const existingContainers = calendarHeader.querySelectorAll('swp-allday-container'); - existingContainers.forEach(container => container.remove()); + existingContainers.forEach(container => { + container.innerHTML = ''; // Clear content, keep container + }); // Track maximum number of stacked events to calculate row height let maxStackHeight = 0; @@ -142,20 +144,31 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { } const columnSpan = endColumn - startColumn + 1; - - // Create or find container for this column span - const containerKey = `${startColumn}-${columnSpan}`; - let allDayContainer = calendarHeader.querySelector(`swp-allday-container[data-container-key="${containerKey}"]`); - + let allDayContainer: Element | null = null; + + if (columnSpan === 1) { + // Single day event - use existing single-day container + const containerKey = `${startColumn}-1`; + allDayContainer = calendarHeader.querySelector(`swp-allday-container[data-container-key="${containerKey}"]`); + } else { + // Multi-day event - create or find spanning container + const containerKey = `${startColumn}-${columnSpan}`; + allDayContainer = calendarHeader.querySelector(`swp-allday-container[data-container-key="${containerKey}"]`); + + if (!allDayContainer) { + // Create container that spans the appropriate columns + allDayContainer = document.createElement('swp-allday-container'); + allDayContainer.setAttribute('data-container-key', containerKey); + allDayContainer.setAttribute('data-date', this.dateCalculator.formatISODate(startDate)); + (allDayContainer as HTMLElement).style.gridColumn = `${startColumn} / span ${columnSpan}`; + (allDayContainer as HTMLElement).style.gridRow = '2'; + calendarHeader.appendChild(allDayContainer); + } + } + if (!allDayContainer) { - // Create container that spans the appropriate columns - allDayContainer = document.createElement('swp-allday-container'); - allDayContainer.setAttribute('data-container-key', containerKey); - (allDayContainer as HTMLElement).style.gridColumn = columnSpan > 1 - ? `${startColumn} / span ${columnSpan}` - : `${startColumn}`; - (allDayContainer as HTMLElement).style.gridRow = '2'; - calendarHeader.appendChild(allDayContainer); + console.warn(`BaseEventRenderer: No container found for event "${event.title}"`); + return; } // Create the all-day event element inside container diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index de6811f..de9c521 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -51,7 +51,6 @@ export class EventRenderingService { // Use cached strategy to render events in the specific container this.strategy.renderEvents(events, context.container, calendarConfig); - console.log(` ✅ Rendered ${events.length} events successfully`); // Emit EVENTS_RENDERED event for filtering system diff --git a/src/renderers/GridRenderer.ts b/src/renderers/GridRenderer.ts index 24b2f12..7bffc23 100644 --- a/src/renderers/GridRenderer.ts +++ b/src/renderers/GridRenderer.ts @@ -10,6 +10,7 @@ import { eventBus } from '../core/EventBus'; */ export class GridRenderer { private config: CalendarConfig; + private headerEventListener: ((event: Event) => void) | null = null; constructor(config: CalendarConfig) { this.config = config; @@ -135,31 +136,11 @@ export class GridRenderer { headerRenderer.render(calendarHeader, context); - // Use event delegation for mouseover detection on entire header - calendarHeader.addEventListener('mouseover', (event) => { - const target = event.target as HTMLElement; - - // Check what was hovered - could be day-header OR all-day-container - const dayHeader = target.closest('swp-day-header'); - const allDayContainer = target.closest('swp-allday-container'); - - if (dayHeader || allDayContainer) { - const hoveredElement = dayHeader || allDayContainer; - const targetDate = (hoveredElement as HTMLElement).dataset.date; - - console.log('GridRenderer: Detected hover over:', { - elementType: dayHeader ? 'day-header' : 'all-day-container', - targetDate, - element: hoveredElement - }); - - eventBus.emit('header:mouseover', { - element: hoveredElement, - targetDate, - headerRenderer - }); - } - }); + // Always ensure all-day containers exist for all days + headerRenderer.ensureAllDayContainers(calendarHeader); + + // Setup event listener for mouseover detection + this.setupHeaderEventListener(calendarHeader); } /** @@ -200,4 +181,47 @@ export class GridRenderer { // Re-render headers using Strategy Pattern - this will also re-attach the event listener this.renderCalendarHeader(calendarHeader as HTMLElement, currentWeek, resourceData); } + + /** + * Setup or re-setup event delegation listener on calendar header + */ + private setupHeaderEventListener(calendarHeader: HTMLElement): void { + // Remove existing listener if any (stored reference approach) + if (this.headerEventListener) { + calendarHeader.removeEventListener('mouseover', this.headerEventListener); + } + + // Create new listener function + this.headerEventListener = (event) => { + const target = event.target as HTMLElement; + + // Check what was hovered - could be day-header OR all-day-container + const dayHeader = target.closest('swp-day-header'); + const allDayContainer = target.closest('swp-allday-container'); + + if (dayHeader || allDayContainer) { + const hoveredElement = dayHeader || allDayContainer; + const targetDate = (hoveredElement as HTMLElement).dataset.date; + + console.log('GridRenderer: Detected hover over:', { + elementType: dayHeader ? 'day-header' : 'all-day-container', + targetDate, + element: hoveredElement + }); + + // Get the header renderer for addToAllDay functionality + const calendarType = this.config.getCalendarMode(); + const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); + + eventBus.emit('header:mouseover', { + element: hoveredElement, + targetDate, + headerRenderer + }); + } + }; + + // Add the new listener + calendarHeader.addEventListener('mouseover', this.headerEventListener); + } } \ No newline at end of file diff --git a/src/renderers/HeaderRenderer.ts b/src/renderers/HeaderRenderer.ts index 2ac0f87..469e797 100644 --- a/src/renderers/HeaderRenderer.ts +++ b/src/renderers/HeaderRenderer.ts @@ -11,6 +11,7 @@ import { DateCalculator } from '../utils/DateCalculator'; export interface HeaderRenderer { render(calendarHeader: HTMLElement, context: HeaderRenderContext): void; addToAllDay(dayHeader: HTMLElement): void; + ensureAllDayContainers(calendarHeader: HTMLElement): void; } /** @@ -36,6 +37,24 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { } } + /** + * Ensure all-day containers exist for all days (called automatically after header render) + */ + ensureAllDayContainers(calendarHeader: HTMLElement): void { + const root = document.documentElement; + const currentHeight = parseInt(getComputedStyle(root).getPropertyValue('--all-day-row-height') || '0'); + + // If all-day row doesn't exist yet, set it up without animation + if (currentHeight === 0) { + root.style.setProperty('--all-day-row-height', `${ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT}px`); + } + + // Always ensure containers exist for all days + this.createEmptyAllDayContainers(calendarHeader); + + console.log('BaseHeaderRenderer: Ensured all-day containers exist for all days'); + } + private animateHeaderExpansion(calendarHeader: HTMLElement): void { const root = document.documentElement; const currentHeaderHeight = parseInt(getComputedStyle(root).getPropertyValue('--header-height')); @@ -126,7 +145,7 @@ export class DateHeaderRenderer extends BaseHeaderRenderer { private dateCalculator!: DateCalculator; render(calendarHeader: HTMLElement, context: HeaderRenderContext): void { - const { currentWeek, config, allDayEvents = [] } = context; + const { currentWeek, config } = context; // Initialize date calculator with config this.dateCalculator = new DateCalculator(config); diff --git a/src/renderers/NavigationRenderer.ts b/src/renderers/NavigationRenderer.ts index e0ddd9e..3810af1 100644 --- a/src/renderers/NavigationRenderer.ts +++ b/src/renderers/NavigationRenderer.ts @@ -3,6 +3,8 @@ import { CoreEvents } from '../constants/CoreEvents'; import { CalendarConfig } from '../core/CalendarConfig'; import { DateCalculator } from '../utils/DateCalculator'; import { EventRenderingService } from './EventRendererManager'; +import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; +import { eventBus } from '../core/EventBus'; /** * NavigationRenderer - Handles DOM rendering for navigation containers @@ -170,6 +172,13 @@ export class NavigationRenderer { header.appendChild(headerElement); }); + + // Always ensure all-day containers exist for all days + const headerRenderer = CalendarTypeFactory.getHeaderRenderer(this.config.getCalendarMode()); + headerRenderer.ensureAllDayContainers(header as HTMLElement); + + // Add event delegation listener for drag & drop functionality + this.setupHeaderEventListener(header as HTMLElement); // Render day columns for target week dates.forEach(date => { @@ -183,4 +192,38 @@ export class NavigationRenderer { }); } + /** + * Setup event delegation listener for header mouseover (same logic as GridRenderer) + */ + private setupHeaderEventListener(calendarHeader: HTMLElement): void { + calendarHeader.addEventListener('mouseover', (event) => { + const target = event.target as HTMLElement; + + // Check what was hovered - could be day-header OR all-day-container + const dayHeader = target.closest('swp-day-header'); + const allDayContainer = target.closest('swp-allday-container'); + + if (dayHeader || allDayContainer) { + const hoveredElement = dayHeader || allDayContainer; + const targetDate = (hoveredElement as HTMLElement).dataset.date; + + console.log('NavigationRenderer: Detected hover over:', { + elementType: dayHeader ? 'day-header' : 'all-day-container', + targetDate, + element: hoveredElement + }); + + // Get the header renderer for addToAllDay functionality + const calendarType = this.config.getCalendarMode(); + const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); + + eventBus.emit('header:mouseover', { + element: hoveredElement, + targetDate, + headerRenderer + }); + } + }); + } + } \ No newline at end of file diff --git a/src/strategies/MonthViewStrategy.ts b/src/strategies/MonthViewStrategy.ts index dd30043..b9cb1df 100644 --- a/src/strategies/MonthViewStrategy.ts +++ b/src/strategies/MonthViewStrategy.ts @@ -50,8 +50,8 @@ export class MonthViewStrategy implements ViewStrategy { // Add 6 weeks of day cells this.createDayCells(monthGrid, context.currentDate); - // Render events in day cells - this.renderMonthEvents(monthGrid, context.allDayEvents); + // Render events in day cells (will be handled by EventRendererManager) + // this.renderMonthEvents(monthGrid, context.allDayEvents); context.container.appendChild(monthGrid); }