Calendar/src/managers/HeaderManager.ts
Janus C. H. Knudsen bf4b9b5064 Implements dynamic renderer injection
Uses dependency injection to dynamically resolve renderers based on the calendar mode.

This change decouples the HeaderManager from specific renderer implementations,
allowing for more flexible configuration and easier addition of new calendar types.
The appropriate renderer is bound to a token in the DI container at startup.
2025-10-15 00:40:51 +02:00

161 lines
No EOL
5.3 KiB
TypeScript

import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig';
import { CoreEvents } from '../constants/CoreEvents';
import { HeaderRenderer, HeaderRenderContext } from '../renderers/HeaderRenderer';
import { ResourceCalendarData } from '../types/CalendarTypes';
import { DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, HeaderReadyEventPayload } from '../types/EventTypes';
import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
/**
* HeaderManager - Handles all header-related event logic
* Separates event handling from rendering concerns
* Uses dependency injection for renderer strategy
*/
export class HeaderManager {
private headerRenderer: HeaderRenderer;
constructor(headerRenderer: HeaderRenderer) {
this.headerRenderer = headerRenderer;
// Bind handler methods for event listeners
this.handleDragMouseEnterHeader = this.handleDragMouseEnterHeader.bind(this);
this.handleDragMouseLeaveHeader = this.handleDragMouseLeaveHeader.bind(this);
// Listen for navigation events to update header
this.setupNavigationListener();
}
/**
* Initialize header with initial date
*/
public initializeHeader(currentDate: Date, resourceData: ResourceCalendarData | null = null): void {
this.updateHeader(currentDate, resourceData);
}
/**
* Get cached calendar header element
*/
private getCalendarHeader(): HTMLElement | null {
return document.querySelector('swp-calendar-header');
}
/**
* Setup header drag event listeners - Listen to DragDropManager events
*/
public setupHeaderDragListeners(): void {
console.log('🎯 HeaderManager: Setting up drag event listeners');
// Subscribe to drag events from DragDropManager
eventBus.on('drag:mouseenter-header', this.handleDragMouseEnterHeader);
eventBus.on('drag:mouseleave-header', this.handleDragMouseLeaveHeader);
console.log('✅ HeaderManager: Drag event listeners attached');
}
/**
* Handle drag mouse enter header event
*/
private handleDragMouseEnterHeader(event: Event): void {
const { targetColumn: targetDate, mousePosition, originalElement, draggedClone: cloneElement } =
(event as CustomEvent<DragMouseEnterHeaderEventPayload>).detail;
console.log('🎯 HeaderManager: Received drag:mouseenter-header', {
targetDate,
originalElement: !!originalElement,
cloneElement: !!cloneElement
});
// Header renderer already injected - ready to use if needed
}
/**
* Handle drag mouse leave header event
*/
private handleDragMouseLeaveHeader(event: Event): void {
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } =
(event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
console.log('🚪 HeaderManager: Received drag:mouseleave-header', {
targetDate,
originalElement: !!originalElement,
cloneElement: !!cloneElement
});
}
/**
* Setup navigation event listener
*/
private setupNavigationListener(): void {
eventBus.on(CoreEvents.NAVIGATION_COMPLETED, (event) => {
const { currentDate, resourceData } = (event as CustomEvent).detail;
this.updateHeader(currentDate, resourceData);
});
// Also listen for date changes (including initial setup)
eventBus.on(CoreEvents.DATE_CHANGED, (event) => {
const { currentDate } = (event as CustomEvent).detail;
this.updateHeader(currentDate, null);
});
// Listen for workweek header updates after grid rebuild
eventBus.on('workweek:header-update', (event) => {
const { currentDate } = (event as CustomEvent).detail;
this.updateHeader(currentDate, null);
});
}
/**
* Update header content for navigation
*/
private updateHeader(currentDate: Date, resourceData: ResourceCalendarData | null = null): void {
console.log('🎯 HeaderManager.updateHeader called', {
currentDate,
hasResourceData: !!resourceData,
rendererType: this.headerRenderer.constructor.name
});
const calendarHeader = this.getOrCreateCalendarHeader();
if (!calendarHeader) {
console.warn('❌ HeaderManager: No calendar header found!');
return;
}
// Clear existing content
calendarHeader.innerHTML = '';
// Render new header content using injected renderer
const context: HeaderRenderContext = {
currentWeek: currentDate,
config: calendarConfig,
resourceData: resourceData
};
console.log('🎨 HeaderManager: Calling renderer.render()', context);
this.headerRenderer.render(calendarHeader, context);
console.log('✅ HeaderManager: Renderer completed');
// Setup event listeners on the new content
this.setupHeaderDragListeners();
// Notify other managers that header is ready with period data
const payload: HeaderReadyEventPayload = {
headerElements: ColumnDetectionUtils.getHeaderColumns(),
};
eventBus.emit('header:ready', payload);
}
/**
* Get calendar header element - header always exists now
*/
private getOrCreateCalendarHeader(): HTMLElement | null {
const calendarHeader = this.getCalendarHeader();
if (!calendarHeader) {
console.warn('HeaderManager: Calendar header not found - should always exist now!');
return null;
}
return calendarHeader;
}
}