Separates all-day event rendering; handles header lifecycle
Event rendering strategies now exclusively handle timed events, while all-day events are managed by a dedicated renderer. Centralizes calendar header creation within `GridRenderer`, ensuring the header element is always present from initial DOM construction. `HeaderManager` and `ScrollManager` now react to a `header:ready` event, which signifies the header is fully initialized. Synchronizes all-day event rendering with header readiness, temporarily queuing events until the header is prepared. Emits an `allday:checkHeight` event to prompt all-day container height adjustments after rendering.
This commit is contained in:
parent
996459f226
commit
f5e9909935
6 changed files with 128 additions and 18 deletions
|
|
@ -120,6 +120,12 @@ export class AllDayManager {
|
||||||
// Recalculate all-day height since clones may have been removed
|
// Recalculate all-day height since clones may have been removed
|
||||||
this.checkAndAnimateAllDayHeight();
|
this.checkAndAnimateAllDayHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for height check requests from EventRendererManager
|
||||||
|
eventBus.on('allday:checkHeight', () => {
|
||||||
|
console.log('📏 AllDayManager: Received allday:checkHeight request');
|
||||||
|
this.checkAndAnimateAllDayHeight();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -172,27 +172,21 @@ export class HeaderManager {
|
||||||
// Setup event listeners on the new content
|
// Setup event listeners on the new content
|
||||||
this.setupHeaderDragListeners();
|
this.setupHeaderDragListeners();
|
||||||
|
|
||||||
// Notify other managers that header was rebuilt
|
// Notify other managers that header is ready
|
||||||
eventBus.emit('header:rebuilt', {
|
eventBus.emit('header:ready', {
|
||||||
headerElement: calendarHeader
|
headerElement: calendarHeader
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or create calendar header element
|
* Get calendar header element - header always exists now
|
||||||
*/
|
*/
|
||||||
private getOrCreateCalendarHeader(): HTMLElement | null {
|
private getOrCreateCalendarHeader(): HTMLElement | null {
|
||||||
let calendarHeader = this.getCalendarHeader();
|
const calendarHeader = this.getCalendarHeader();
|
||||||
|
|
||||||
if (!calendarHeader) {
|
if (!calendarHeader) {
|
||||||
// Find grid container and create header
|
console.warn('HeaderManager: Calendar header not found - should always exist now!');
|
||||||
const gridContainer = document.querySelector('swp-grid-container');
|
return null;
|
||||||
if (gridContainer) {
|
|
||||||
calendarHeader = document.createElement('swp-calendar-header');
|
|
||||||
// Insert header as first child
|
|
||||||
gridContainer.insertBefore(calendarHeader, gridContainer.firstChild);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return calendarHeader;
|
return calendarHeader;
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ export class ScrollManager {
|
||||||
this.updateScrollableHeight();
|
this.updateScrollableHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle header rebuild - refresh header reference and re-sync
|
// Handle header ready - refresh header reference and re-sync
|
||||||
eventBus.on('header:rebuilt', () => {
|
eventBus.on('header:ready', () => {
|
||||||
this.calendarHeader = document.querySelector('swp-calendar-header');
|
this.calendarHeader = document.querySelector('swp-calendar-header');
|
||||||
if (this.scrollableContent && this.calendarHeader) {
|
if (this.scrollableContent && this.calendarHeader) {
|
||||||
this.setupHorizontalScrollSynchronization();
|
this.setupHorizontalScrollSynchronization();
|
||||||
|
|
|
||||||
|
|
@ -542,11 +542,20 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
|
|
||||||
renderEvents(events: CalendarEvent[], container: HTMLElement): void {
|
renderEvents(events: CalendarEvent[], container: HTMLElement): void {
|
||||||
|
|
||||||
|
// Filter out all-day events - they should be handled by AllDayEventRenderer
|
||||||
|
const timedEvents = events.filter(event => !event.allDay);
|
||||||
|
|
||||||
|
console.log('🎯 EventRenderer: Filtering events', {
|
||||||
|
totalEvents: events.length,
|
||||||
|
timedEvents: timedEvents.length,
|
||||||
|
filteredOutAllDay: events.length - timedEvents.length
|
||||||
|
});
|
||||||
|
|
||||||
// Find columns in the specific container for regular events
|
// Find columns in the specific container for regular events
|
||||||
const columns = this.getColumns(container);
|
const columns = this.getColumns(container);
|
||||||
|
|
||||||
columns.forEach(column => {
|
columns.forEach(column => {
|
||||||
const columnEvents = this.getEventsForColumn(column, events);
|
const columnEvents = this.getEventsForColumn(column, timedEvents);
|
||||||
|
|
||||||
const eventsLayer = column.querySelector('swp-events-layer');
|
const eventsLayer = column.querySelector('swp-events-layer');
|
||||||
if (eventsLayer) {
|
if (eventsLayer) {
|
||||||
|
|
@ -659,6 +668,14 @@ export class DateEventRenderer extends BaseEventRenderer {
|
||||||
this.setupDragEventListeners();
|
this.setupDragEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup drag event listeners - placeholder method
|
||||||
|
*/
|
||||||
|
private setupDragEventListeners(): void {
|
||||||
|
// Drag event listeners are handled by EventRendererManager
|
||||||
|
// This method exists for compatibility
|
||||||
|
}
|
||||||
|
|
||||||
protected getColumns(container: HTMLElement): HTMLElement[] {
|
protected getColumns(container: HTMLElement): HTMLElement[] {
|
||||||
const columns = container.querySelectorAll('swp-day-column');
|
const columns = container.querySelectorAll('swp-day-column');
|
||||||
return Array.from(columns) as HTMLElement[];
|
return Array.from(columns) as HTMLElement[];
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ 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';
|
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||||
|
import { AllDayEventRenderer } from './AllDayEventRenderer';
|
||||||
import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload } from '../types/EventTypes';
|
import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload } from '../types/EventTypes';
|
||||||
/**
|
/**
|
||||||
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
||||||
|
|
@ -15,6 +16,11 @@ export class EventRenderingService {
|
||||||
private eventBus: IEventBus;
|
private eventBus: IEventBus;
|
||||||
private eventManager: EventManager;
|
private eventManager: EventManager;
|
||||||
private strategy: EventRendererStrategy;
|
private strategy: EventRendererStrategy;
|
||||||
|
private allDayEventRenderer: AllDayEventRenderer;
|
||||||
|
|
||||||
|
// Store all-day events until header is ready with dates
|
||||||
|
private pendingAllDayEvents: CalendarEvent[] = [];
|
||||||
|
private isHeaderReady: boolean = false;
|
||||||
|
|
||||||
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
|
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
|
||||||
|
|
||||||
|
|
@ -26,6 +32,9 @@ export class EventRenderingService {
|
||||||
const calendarType = calendarConfig.getCalendarMode();
|
const calendarType = calendarConfig.getCalendarMode();
|
||||||
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
|
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
|
||||||
|
|
||||||
|
// Initialize all-day event renderer
|
||||||
|
this.allDayEventRenderer = new AllDayEventRenderer();
|
||||||
|
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,13 +52,40 @@ export class EventRenderingService {
|
||||||
context.endDate
|
context.endDate
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (events.length === 0) {
|
if (events.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use cached strategy to render events in the specific container
|
// Filter events by type
|
||||||
this.strategy.renderEvents(events, context.container);
|
const timedEvents = events.filter(event => !event.allDay);
|
||||||
|
const allDayEvents = events.filter(event => event.allDay);
|
||||||
|
|
||||||
|
console.log('🎯 EventRenderingService: Event filtering', {
|
||||||
|
totalEvents: events.length,
|
||||||
|
timedEvents: timedEvents.length,
|
||||||
|
allDayEvents: allDayEvents.length,
|
||||||
|
allDayEventIds: allDayEvents.map(e => e.id)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render timed events using existing strategy
|
||||||
|
if (timedEvents.length > 0) {
|
||||||
|
this.strategy.renderEvents(timedEvents, context.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render all-day events - wait for header if not ready
|
||||||
|
if (allDayEvents.length > 0) {
|
||||||
|
if (this.isHeaderReady) {
|
||||||
|
this.renderAllDayEvents(allDayEvents);
|
||||||
|
// Check and adjust all-day container height after rendering
|
||||||
|
this.eventBus.emit('allday:checkHeight');
|
||||||
|
} else {
|
||||||
|
console.log('🕐 EventRendererManager: Header not ready, storing all-day events for later');
|
||||||
|
// Only store if we don't already have pending events to avoid duplicates
|
||||||
|
if (this.pendingAllDayEvents.length === 0) {
|
||||||
|
this.pendingAllDayEvents = [...allDayEvents];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Emit EVENTS_RENDERED event for filtering system
|
// Emit EVENTS_RENDERED event for filtering system
|
||||||
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
|
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
|
||||||
|
|
@ -68,6 +104,20 @@ export class EventRenderingService {
|
||||||
this.handleViewChanged(event as CustomEvent);
|
this.handleViewChanged(event as CustomEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for header ready - when dates are populated
|
||||||
|
this.eventBus.on('header:ready', () => {
|
||||||
|
console.log('🎯 EventRendererManager: Header ready, rendering pending all-day events');
|
||||||
|
this.isHeaderReady = true;
|
||||||
|
|
||||||
|
if (this.pendingAllDayEvents.length > 0) {
|
||||||
|
this.renderAllDayEvents(this.pendingAllDayEvents);
|
||||||
|
this.pendingAllDayEvents = []; // Clear after rendering
|
||||||
|
|
||||||
|
// Check and adjust all-day container height after rendering
|
||||||
|
this.eventBus.emit('allday:checkHeight');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Handle all drag events and delegate to appropriate renderer
|
// Handle all drag events and delegate to appropriate renderer
|
||||||
this.setupDragEventListeners();
|
this.setupDragEventListeners();
|
||||||
|
|
||||||
|
|
@ -299,8 +349,45 @@ export class EventRenderingService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render all-day events using AllDayEventRenderer
|
||||||
|
*/
|
||||||
|
private renderAllDayEvents(allDayEvents: CalendarEvent[]): void {
|
||||||
|
console.log('🏗️ EventRenderingService: Rendering all-day events', {
|
||||||
|
count: allDayEvents.length,
|
||||||
|
events: allDayEvents.map(e => ({ id: e.id, title: e.title }))
|
||||||
|
});
|
||||||
|
|
||||||
|
// Header always exists now, so we can render directly
|
||||||
|
allDayEvents.forEach(event => {
|
||||||
|
const renderedElement = this.allDayEventRenderer.renderAllDayEvent(event);
|
||||||
|
if (renderedElement) {
|
||||||
|
console.log('✅ EventRenderingService: Rendered all-day event', {
|
||||||
|
id: event.id,
|
||||||
|
title: event.title,
|
||||||
|
element: renderedElement.tagName
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('❌ EventRenderingService: Failed to render all-day event', {
|
||||||
|
id: event.id,
|
||||||
|
title: event.title
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private clearEvents(container?: HTMLElement): void {
|
private clearEvents(container?: HTMLElement): void {
|
||||||
this.strategy.clearEvents(container);
|
this.strategy.clearEvents(container);
|
||||||
|
|
||||||
|
// Also clear all-day events
|
||||||
|
const allDayContainer = document.querySelector('swp-allday-container');
|
||||||
|
if (allDayContainer) {
|
||||||
|
allDayContainer.querySelectorAll('swp-event').forEach(event => event.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear pending all-day events
|
||||||
|
this.pendingAllDayEvents = [];
|
||||||
|
this.isHeaderReady = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refresh(container?: HTMLElement): void {
|
public refresh(container?: HTMLElement): void {
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,10 @@ export class GridRenderer {
|
||||||
): HTMLElement {
|
): HTMLElement {
|
||||||
const gridContainer = document.createElement('swp-grid-container');
|
const gridContainer = document.createElement('swp-grid-container');
|
||||||
|
|
||||||
|
// Create calendar header as first child - always exists now!
|
||||||
|
const calendarHeader = document.createElement('swp-calendar-header');
|
||||||
|
gridContainer.appendChild(calendarHeader);
|
||||||
|
|
||||||
// Create scrollable content structure
|
// Create scrollable content structure
|
||||||
const scrollableContent = document.createElement('swp-scrollable-content');
|
const scrollableContent = document.createElement('swp-scrollable-content');
|
||||||
const timeGrid = document.createElement('swp-time-grid');
|
const timeGrid = document.createElement('swp-time-grid');
|
||||||
|
|
@ -124,6 +128,8 @@ export class GridRenderer {
|
||||||
scrollableContent.appendChild(timeGrid);
|
scrollableContent.appendChild(timeGrid);
|
||||||
gridContainer.appendChild(scrollableContent);
|
gridContainer.appendChild(scrollableContent);
|
||||||
|
|
||||||
|
console.log('✅ GridRenderer: Created grid container with header');
|
||||||
|
|
||||||
return gridContainer;
|
return gridContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue