From bf4b9b5064082fa5d6f7d4a04fe673869f154cff Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Wed, 15 Oct 2025 00:40:51 +0200 Subject: [PATCH] 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. --- src/di/container.ts | 26 ++++++++++++++++++++++++- src/di/tokens.ts | 8 ++++++++ src/factories/ManagerFactory.ts | 7 ++++--- src/managers/HeaderManager.ts | 34 ++++++++++++++++++++------------- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/di/container.ts b/src/di/container.ts index 572e0a8..cdc5d63 100644 --- a/src/di/container.ts +++ b/src/di/container.ts @@ -6,6 +6,7 @@ import { Container } from 'brandi'; import { TOKENS } from './tokens'; import { eventBus } from '../core/EventBus'; +import { calendarConfig } from '../core/CalendarConfig'; // Import all managers import { EventManager } from '../managers/EventManager'; @@ -22,6 +23,11 @@ import { EdgeScrollManager } from '../managers/EdgeScrollManager'; import { DragHoverManager } from '../managers/DragHoverManager'; 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 * Using manual instantiation instead of automatic injection @@ -32,6 +38,24 @@ export function createContainer(): Container { // Bind core services as constant 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) // This is simpler than using decorators or complex factory setup @@ -68,7 +92,7 @@ export function createContainer(): Container { const dragHoverManager = new DragHoverManager(eventBus); container.bind(TOKENS.dragHoverManager).toConstant(dragHoverManager); - const headerManager = new HeaderManager(); + const headerManager = new HeaderManager(container.get(TOKENS.headerRenderer)); container.bind(TOKENS.headerManager).toConstant(headerManager); // CalendarManager depends on multiple managers diff --git a/src/di/tokens.ts b/src/di/tokens.ts index fd1db0b..29ab897 100644 --- a/src/di/tokens.ts +++ b/src/di/tokens.ts @@ -18,6 +18,9 @@ import { ResizeHandleManager } from '../managers/ResizeHandleManager'; import { EdgeScrollManager } from '../managers/EdgeScrollManager'; import { DragHoverManager } from '../managers/DragHoverManager'; 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 @@ -26,6 +29,11 @@ export const TOKENS = { // Core services eventBus: token('eventBus'), + // Renderers (polymorphic - resolved based on calendar mode) + headerRenderer: token('headerRenderer'), + columnRenderer: token('columnRenderer'), + eventRendererStrategy: token('eventRendererStrategy'), + // Managers eventManager: token('eventManager'), eventRenderer: token('eventRenderer'), diff --git a/src/factories/ManagerFactory.ts b/src/factories/ManagerFactory.ts index 0762969..858b010 100644 --- a/src/factories/ManagerFactory.ts +++ b/src/factories/ManagerFactory.ts @@ -46,7 +46,8 @@ export class ManagerFactory { const resizeHandleManager = new ResizeHandleManager(); const edgeScrollManager = new EdgeScrollManager(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 const calendarManager = new CalendarManager( @@ -70,8 +71,8 @@ export class ManagerFactory { allDayManager, resizeHandleManager, edgeScrollManager, - dragHoverManager, - headerManager + dragHoverManager, + headerManager: null as any // HeaderManager created in Brandi container }; } diff --git a/src/managers/HeaderManager.ts b/src/managers/HeaderManager.ts index d1a6530..d42b73d 100644 --- a/src/managers/HeaderManager.ts +++ b/src/managers/HeaderManager.ts @@ -1,8 +1,7 @@ import { eventBus } from '../core/EventBus'; import { calendarConfig } from '../core/CalendarConfig'; -import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { CoreEvents } from '../constants/CoreEvents'; -import { HeaderRenderContext } from '../renderers/HeaderRenderer'; +import { HeaderRenderer, HeaderRenderContext } from '../renderers/HeaderRenderer'; import { ResourceCalendarData } from '../types/CalendarTypes'; import { DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, HeaderReadyEventPayload } from '../types/EventTypes'; import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; @@ -10,10 +9,14 @@ 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; - constructor() { // Bind handler methods for event listeners this.handleDragMouseEnterHeader = this.handleDragMouseEnterHeader.bind(this); this.handleDragMouseLeaveHeader = this.handleDragMouseLeaveHeader.bind(this); @@ -62,10 +65,7 @@ export class HeaderManager { cloneElement: !!cloneElement }); - if (targetDate) { - const calendarType = calendarConfig.getCalendarMode(); - const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); - } + // Header renderer already injected - ready to use if needed } /** @@ -109,23 +109,31 @@ export class HeaderManager { * 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) return; + if (!calendarHeader) { + console.warn('❌ HeaderManager: No calendar header found!'); + return; + } // Clear existing content calendarHeader.innerHTML = ''; - // Render new header content - const calendarType = calendarConfig.getCalendarMode(); - const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); - + // Render new header content using injected renderer const context: HeaderRenderContext = { currentWeek: currentDate, config: calendarConfig, 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 this.setupHeaderDragListeners();