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.
This commit is contained in:
Janus C. H. Knudsen 2025-10-15 00:40:51 +02:00
parent f7c67e6253
commit bf4b9b5064
4 changed files with 58 additions and 17 deletions

View file

@ -6,6 +6,7 @@
import { Container } from 'brandi'; import { Container } from 'brandi';
import { TOKENS } from './tokens'; import { TOKENS } from './tokens';
import { eventBus } from '../core/EventBus'; import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig';
// Import all managers // Import all managers
import { EventManager } from '../managers/EventManager'; import { EventManager } from '../managers/EventManager';
@ -22,6 +23,11 @@ import { EdgeScrollManager } from '../managers/EdgeScrollManager';
import { DragHoverManager } from '../managers/DragHoverManager'; import { DragHoverManager } from '../managers/DragHoverManager';
import { HeaderManager } from '../managers/HeaderManager'; import { HeaderManager } from '../managers/HeaderManager';
// Import renderers
import { DateHeaderRenderer, ResourceHeaderRenderer } from '../renderers/HeaderRenderer';
import { DateColumnRenderer, ResourceColumnRenderer } from '../renderers/ColumnRenderer';
import { DateEventRenderer } from '../renderers/EventRenderer';
/** /**
* Create and configure the DI container * Create and configure the DI container
* Using manual instantiation instead of automatic injection * Using manual instantiation instead of automatic injection
@ -32,6 +38,24 @@ export function createContainer(): Container {
// Bind core services as constant // Bind core services as constant
container.bind(TOKENS.eventBus).toConstant(eventBus); container.bind(TOKENS.eventBus).toConstant(eventBus);
// Determine calendar mode at startup and bind appropriate renderers
const calendarMode = calendarConfig.getCalendarMode();
console.log('🔧 DI Container: Calendar mode detected:', calendarMode);
if (calendarMode === 'resource') {
// Resource mode renderers
console.log('🔧 DI Container: Binding DateHeaderRenderer for resource mode (TEST)');
container.bind(TOKENS.headerRenderer).toConstant(new DateHeaderRenderer());
container.bind(TOKENS.columnRenderer).toConstant(new DateColumnRenderer());
container.bind(TOKENS.eventRendererStrategy).toConstant(new DateEventRenderer()); // TODO: ResourceEventRenderer
} else {
// Date mode renderers (default)
console.log('🔧 DI Container: Binding DateHeaderRenderer for date mode');
container.bind(TOKENS.headerRenderer).toConstant(new DateHeaderRenderer());
container.bind(TOKENS.columnRenderer).toConstant(new DateColumnRenderer());
container.bind(TOKENS.eventRendererStrategy).toConstant(new DateEventRenderer());
}
// Create instances manually and bind as constants (singletons) // Create instances manually and bind as constants (singletons)
// This is simpler than using decorators or complex factory setup // This is simpler than using decorators or complex factory setup
@ -68,7 +92,7 @@ export function createContainer(): Container {
const dragHoverManager = new DragHoverManager(eventBus); const dragHoverManager = new DragHoverManager(eventBus);
container.bind(TOKENS.dragHoverManager).toConstant(dragHoverManager); container.bind(TOKENS.dragHoverManager).toConstant(dragHoverManager);
const headerManager = new HeaderManager(); const headerManager = new HeaderManager(container.get(TOKENS.headerRenderer));
container.bind(TOKENS.headerManager).toConstant(headerManager); container.bind(TOKENS.headerManager).toConstant(headerManager);
// CalendarManager depends on multiple managers // CalendarManager depends on multiple managers

View file

@ -18,6 +18,9 @@ import { ResizeHandleManager } from '../managers/ResizeHandleManager';
import { EdgeScrollManager } from '../managers/EdgeScrollManager'; import { EdgeScrollManager } from '../managers/EdgeScrollManager';
import { DragHoverManager } from '../managers/DragHoverManager'; import { DragHoverManager } from '../managers/DragHoverManager';
import { HeaderManager } from '../managers/HeaderManager'; import { HeaderManager } from '../managers/HeaderManager';
import { HeaderRenderer } from '../renderers/HeaderRenderer';
import { ColumnRenderer } from '../renderers/ColumnRenderer';
import { EventRendererStrategy } from '../renderers/EventRenderer';
/** /**
* DI Tokens - Type-safe identifiers for dependency injection * DI Tokens - Type-safe identifiers for dependency injection
@ -26,6 +29,11 @@ export const TOKENS = {
// Core services // Core services
eventBus: token<IEventBus>('eventBus'), eventBus: token<IEventBus>('eventBus'),
// Renderers (polymorphic - resolved based on calendar mode)
headerRenderer: token<HeaderRenderer>('headerRenderer'),
columnRenderer: token<ColumnRenderer>('columnRenderer'),
eventRendererStrategy: token<EventRendererStrategy>('eventRendererStrategy'),
// Managers // Managers
eventManager: token<EventManager>('eventManager'), eventManager: token<EventManager>('eventManager'),
eventRenderer: token<EventRenderingService>('eventRenderer'), eventRenderer: token<EventRenderingService>('eventRenderer'),

View file

@ -46,7 +46,8 @@ export class ManagerFactory {
const resizeHandleManager = new ResizeHandleManager(); const resizeHandleManager = new ResizeHandleManager();
const edgeScrollManager = new EdgeScrollManager(eventBus); const edgeScrollManager = new EdgeScrollManager(eventBus);
const dragHoverManager = new DragHoverManager(eventBus); const dragHoverManager = new DragHoverManager(eventBus);
const headerManager = new HeaderManager(); // HeaderManager now requires HeaderRenderer via DI - will be created in Brandi container
// const headerManager = new HeaderManager();
// CalendarManager depends on all other managers // CalendarManager depends on all other managers
const calendarManager = new CalendarManager( const calendarManager = new CalendarManager(
@ -71,7 +72,7 @@ export class ManagerFactory {
resizeHandleManager, resizeHandleManager,
edgeScrollManager, edgeScrollManager,
dragHoverManager, dragHoverManager,
headerManager headerManager: null as any // HeaderManager created in Brandi container
}; };
} }

View file

@ -1,8 +1,7 @@
import { eventBus } from '../core/EventBus'; import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { CoreEvents } from '../constants/CoreEvents'; import { CoreEvents } from '../constants/CoreEvents';
import { HeaderRenderContext } from '../renderers/HeaderRenderer'; import { HeaderRenderer, HeaderRenderContext } from '../renderers/HeaderRenderer';
import { ResourceCalendarData } from '../types/CalendarTypes'; import { ResourceCalendarData } from '../types/CalendarTypes';
import { DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, HeaderReadyEventPayload } from '../types/EventTypes'; import { DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, HeaderReadyEventPayload } from '../types/EventTypes';
import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
@ -10,10 +9,14 @@ import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
/** /**
* HeaderManager - Handles all header-related event logic * HeaderManager - Handles all header-related event logic
* Separates event handling from rendering concerns * Separates event handling from rendering concerns
* Uses dependency injection for renderer strategy
*/ */
export class HeaderManager { export class HeaderManager {
private headerRenderer: HeaderRenderer;
constructor(headerRenderer: HeaderRenderer) {
this.headerRenderer = headerRenderer;
constructor() {
// Bind handler methods for event listeners // Bind handler methods for event listeners
this.handleDragMouseEnterHeader = this.handleDragMouseEnterHeader.bind(this); this.handleDragMouseEnterHeader = this.handleDragMouseEnterHeader.bind(this);
this.handleDragMouseLeaveHeader = this.handleDragMouseLeaveHeader.bind(this); this.handleDragMouseLeaveHeader = this.handleDragMouseLeaveHeader.bind(this);
@ -62,10 +65,7 @@ export class HeaderManager {
cloneElement: !!cloneElement cloneElement: !!cloneElement
}); });
if (targetDate) { // Header renderer already injected - ready to use if needed
const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
}
} }
/** /**
@ -109,23 +109,31 @@ export class HeaderManager {
* Update header content for navigation * Update header content for navigation
*/ */
private updateHeader(currentDate: Date, resourceData: ResourceCalendarData | null = null): void { 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(); const calendarHeader = this.getOrCreateCalendarHeader();
if (!calendarHeader) return; if (!calendarHeader) {
console.warn('❌ HeaderManager: No calendar header found!');
return;
}
// Clear existing content // Clear existing content
calendarHeader.innerHTML = ''; calendarHeader.innerHTML = '';
// Render new header content // Render new header content using injected renderer
const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
const context: HeaderRenderContext = { const context: HeaderRenderContext = {
currentWeek: currentDate, currentWeek: currentDate,
config: calendarConfig, config: calendarConfig,
resourceData: resourceData resourceData: resourceData
}; };
headerRenderer.render(calendarHeader, context); console.log('🎨 HeaderManager: Calling renderer.render()', context);
this.headerRenderer.render(calendarHeader, context);
console.log('✅ HeaderManager: Renderer completed');
// Setup event listeners on the new content // Setup event listeners on the new content
this.setupHeaderDragListeners(); this.setupHeaderDragListeners();