From 2083c6921ec7f39cf73516445b3c5789fb4f08e3 Mon Sep 17 00:00:00 2001 From: Janus Knudsen Date: Wed, 3 Sep 2025 20:04:47 +0200 Subject: [PATCH] Refactors calendar configuration and event handling Streamlines calendar configuration by adopting a singleton pattern for consistent access and simplifies event handling. - Removes direct `CalendarConfig` dependency injection in favor of the `calendarConfig` singleton, reducing code complexity. - Replaces specific event emissions for grid, date, and resource settings updates with a general `REFRESH_REQUESTED` event. - Updates event names to be more descriptive and consistent ("NAVIGATION_COMPLETED", "PERIOD_INFO_UPDATE"). - Removes the need to pass the calendar config to renderers since it is now a singleton. This improves code maintainability and simplifies the event emission process. --- src/constants/CoreEvents.ts | 8 +- src/core/CalendarConfig.ts | 15 ++-- src/factories/CalendarTypeFactory.ts | 4 +- src/factories/ManagerFactory.ts | 14 ++- src/index.ts | 2 +- src/managers/CalendarManager.ts | 21 ++--- src/managers/DragDropManager.ts | 10 +-- src/managers/GridManager.ts | 6 +- src/managers/NavigationManager.ts | 6 +- src/managers/ScrollManager.ts | 2 +- src/managers/WorkHoursManager.ts | 12 ++- src/renderers/ColumnRenderer.ts | 2 +- src/renderers/EventRenderer.ts | 36 ++++---- src/renderers/EventRendererManager.ts | 2 +- src/renderers/GridRenderer.ts | 18 ++-- src/renderers/GridStyleManager.ts | 17 ++-- src/renderers/NavigationRenderer.ts | 14 ++- src/strategies/WeekViewStrategy.ts | 4 +- src/utils/PositionUtils.ts | 119 ++++++++++++-------------- 19 files changed, 139 insertions(+), 173 deletions(-) diff --git a/src/constants/CoreEvents.ts b/src/constants/CoreEvents.ts index f73bcd7..1a54ff7 100644 --- a/src/constants/CoreEvents.ts +++ b/src/constants/CoreEvents.ts @@ -15,8 +15,8 @@ export const CoreEvents = { // Navigation events (3) DATE_CHANGED: 'nav:date-changed', - PERIOD_CHANGED: 'nav:period-changed', - WEEK_CHANGED: 'nav:week-changed', + NAVIGATION_COMPLETED: 'nav:navigation-completed', + PERIOD_INFO_UPDATE: 'nav:period-info-update', // Data events (4) DATA_LOADING: 'data:loading', @@ -65,8 +65,8 @@ export const EVENT_MIGRATION_MAP: Record = { // Navigation 'calendar:datechanged': CoreEvents.DATE_CHANGED, - 'calendar:periodchange': CoreEvents.PERIOD_CHANGED, - 'calendar:weekchanged': CoreEvents.WEEK_CHANGED, + 'calendar:navigationcompleted': CoreEvents.NAVIGATION_COMPLETED, + 'calendar:periodinfoUpdate': CoreEvents.PERIOD_INFO_UPDATE, // Data 'calendar:datafetchstart': CoreEvents.DATA_LOADING, diff --git a/src/core/CalendarConfig.ts b/src/core/CalendarConfig.ts index 904f575..4bb9560 100644 --- a/src/core/CalendarConfig.ts +++ b/src/core/CalendarConfig.ts @@ -298,11 +298,10 @@ export class CalendarConfig { this.config.minEventDuration = updates.snapInterval; } - // Emit grid settings update event + // Grid settings changes trigger general refresh - avoid specific event eventBus.emit(CoreEvents.REFRESH_REQUESTED, { key: 'gridSettings', - value: this.gridSettings, - oldValue: this.gridSettings + value: this.gridSettings }); } @@ -319,11 +318,10 @@ export class CalendarConfig { updateDateViewSettings(updates: Partial): void { this.dateViewSettings = { ...this.dateViewSettings, ...updates }; - // Emit date view settings update event + // Date view settings changes trigger general refresh - avoid specific event eventBus.emit(CoreEvents.REFRESH_REQUESTED, { key: 'dateViewSettings', - value: this.dateViewSettings, - oldValue: this.dateViewSettings + value: this.dateViewSettings }); } @@ -340,11 +338,10 @@ export class CalendarConfig { updateResourceViewSettings(updates: Partial): void { this.resourceViewSettings = { ...this.resourceViewSettings, ...updates }; - // Emit resource view settings update event + // Resource view settings changes trigger general refresh - avoid specific event eventBus.emit(CoreEvents.REFRESH_REQUESTED, { key: 'resourceViewSettings', - value: this.resourceViewSettings, - oldValue: this.resourceViewSettings + value: this.resourceViewSettings }); } diff --git a/src/factories/CalendarTypeFactory.ts b/src/factories/CalendarTypeFactory.ts index 311a0de..952be43 100644 --- a/src/factories/CalendarTypeFactory.ts +++ b/src/factories/CalendarTypeFactory.ts @@ -34,13 +34,13 @@ export class CalendarTypeFactory { this.registerRenderers('date', { headerRenderer: new DateHeaderRenderer(), columnRenderer: new DateColumnRenderer(), - eventRenderer: new DateEventRenderer(calendarConfig) + eventRenderer: new DateEventRenderer() }); this.registerRenderers('resource', { headerRenderer: new ResourceHeaderRenderer(), columnRenderer: new ResourceColumnRenderer(), - eventRenderer: new ResourceEventRenderer(calendarConfig) + eventRenderer: new ResourceEventRenderer() }); this.isInitialized = true; diff --git a/src/factories/ManagerFactory.ts b/src/factories/ManagerFactory.ts index 8a08ce6..a48ea7e 100644 --- a/src/factories/ManagerFactory.ts +++ b/src/factories/ManagerFactory.ts @@ -1,5 +1,4 @@ import { IEventBus } from '../types/CalendarTypes'; -import { CalendarConfig } from '../core/CalendarConfig'; import { EventManager } from '../managers/EventManager'; import { EventRenderingService } from '../renderers/EventRendererManager'; import { GridManager } from '../managers/GridManager'; @@ -27,7 +26,7 @@ export class ManagerFactory { /** * Create all managers with proper dependency injection */ - public createManagers(eventBus: IEventBus, config: CalendarConfig): { + public createManagers(eventBus: IEventBus): { eventManager: EventManager; eventRenderer: EventRenderingService; gridManager: GridManager; @@ -45,15 +44,14 @@ export class ManagerFactory { const scrollManager = new ScrollManager(); const navigationManager = new NavigationManager(eventBus, eventRenderer); const viewManager = new ViewManager(eventBus); - const dragDropManager = new DragDropManager(eventBus, config); + const dragDropManager = new DragDropManager(eventBus); // CalendarManager depends on all other managers const calendarManager = new CalendarManager( - eventBus, - config, - eventManager, - gridManager, - eventRenderer, + eventBus, + eventManager, + gridManager, + eventRenderer, scrollManager ); diff --git a/src/index.ts b/src/index.ts index d6d1b5b..99fe5b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,7 @@ async function initializeCalendar(): Promise { // Create managers using factory pattern const managerFactory = ManagerFactory.getInstance(); - const managers = managerFactory.createManagers(eventBus, config); + const managers = managerFactory.createManagers(eventBus); // Enable debug mode for development eventBus.setDebug(true); diff --git a/src/managers/CalendarManager.ts b/src/managers/CalendarManager.ts index 0200175..baec945 100644 --- a/src/managers/CalendarManager.ts +++ b/src/managers/CalendarManager.ts @@ -1,6 +1,6 @@ import { EventBus } from '../core/EventBus.js'; import { CoreEvents } from '../constants/CoreEvents.js'; -import { CalendarConfig } from '../core/CalendarConfig.js'; +import { calendarConfig } from '../core/CalendarConfig.js'; import { CalendarEvent, CalendarView, IEventBus } from '../types/CalendarTypes.js'; import { EventManager } from './EventManager.js'; import { GridManager } from './GridManager.js'; @@ -11,11 +11,10 @@ import { EventFilterManager } from './EventFilterManager.js'; /** * CalendarManager - Main coordinator for all calendar managers - * Uses simple direct method calls instead of complex state management + * Uses singleton calendarConfig for consistent configuration access */ export class CalendarManager { private eventBus: IEventBus; - private config: CalendarConfig; private eventManager: EventManager; private gridManager: GridManager; private eventRenderer: EventRenderingService; @@ -28,20 +27,18 @@ export class CalendarManager { constructor( eventBus: IEventBus, - config: CalendarConfig, eventManager: EventManager, gridManager: GridManager, eventRenderer: EventRenderingService, scrollManager: ScrollManager ) { this.eventBus = eventBus; - this.config = config; this.eventManager = eventManager; this.gridManager = gridManager; this.eventRenderer = eventRenderer; this.scrollManager = scrollManager; this.eventFilterManager = new EventFilterManager(); - DateCalculator.initialize(config); + DateCalculator.initialize(calendarConfig); this.dateCalculator = new DateCalculator(); this.setupEventListeners(); } @@ -57,7 +54,7 @@ export class CalendarManager { try { // Debug: Check calendar type - const calendarType = this.config.getCalendarMode(); + const calendarType = calendarConfig.getCalendarMode(); // Step 1: Load data await this.eventManager.loadData(); @@ -194,8 +191,8 @@ export class CalendarManager { /** * Hent calendar konfiguration */ - public getConfig(): CalendarConfig { - return this.config; + public getConfig() { + return calendarConfig; } /** @@ -267,7 +264,7 @@ export class CalendarManager { nextDate.setDate(nextDate.getDate() + 1); break; case 'week': - const workWeekSettings = this.config.getWorkWeekSettings(); + const workWeekSettings = calendarConfig.getWorkWeekSettings(); nextDate.setDate(nextDate.getDate() + 7); // Move to next calendar week regardless of work days break; case 'month': @@ -289,7 +286,7 @@ export class CalendarManager { previousDate.setDate(previousDate.getDate() - 1); break; case 'week': - const workWeekSettings = this.config.getWorkWeekSettings(); + const workWeekSettings = calendarConfig.getWorkWeekSettings(); previousDate.setDate(previousDate.getDate() - 7); // Move to previous calendar week regardless of work days break; case 'month': @@ -441,7 +438,7 @@ export class CalendarManager { const dateRange = DateCalculator.formatDateRange(firstDate, lastDate); // Emit week info update - this.eventBus.emit(CoreEvents.WEEK_CHANGED, { + this.eventBus.emit(CoreEvents.PERIOD_INFO_UPDATE, { weekNumber, dateRange, weekStart: firstDate, diff --git a/src/managers/DragDropManager.ts b/src/managers/DragDropManager.ts index fbbe9b4..91631cb 100644 --- a/src/managers/DragDropManager.ts +++ b/src/managers/DragDropManager.ts @@ -4,7 +4,7 @@ */ import { IEventBus } from '../types/CalendarTypes'; -import { CalendarConfig } from '../core/CalendarConfig'; +import { calendarConfig } from '../core/CalendarConfig'; import { DateCalculator } from '../utils/DateCalculator'; interface CachedElements { @@ -20,7 +20,6 @@ interface Position { export class DragDropManager { private eventBus: IEventBus; - private config: CalendarConfig; // Mouse tracking with optimized state private isMouseDown = false; @@ -61,12 +60,11 @@ export class DragDropManager { return (this.snapIntervalMinutes / 60) * this.hourHeightPx; } - constructor(eventBus: IEventBus, config: CalendarConfig) { + constructor(eventBus: IEventBus) { this.eventBus = eventBus; - this.config = config; - + // Get config values - const gridSettings = config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); this.hourHeightPx = gridSettings.hourHeight; this.init(); diff --git a/src/managers/GridManager.ts b/src/managers/GridManager.ts index 3411f00..1b2c92f 100644 --- a/src/managers/GridManager.ts +++ b/src/managers/GridManager.ts @@ -23,7 +23,7 @@ export class GridManager { constructor() { // Initialize GridRenderer with config - this.gridRenderer = new GridRenderer(calendarConfig); + this.gridRenderer = new GridRenderer(); this.init(); } @@ -151,7 +151,7 @@ export class GridManager { this.currentDate = nextDate; - eventBus.emit(CoreEvents.PERIOD_CHANGED, { + eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { direction: 'next', newDate: nextDate, periodLabel: this.getCurrentPeriodLabel() @@ -182,7 +182,7 @@ export class GridManager { this.currentDate = prevDate; - eventBus.emit(CoreEvents.PERIOD_CHANGED, { + eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { direction: 'previous', newDate: prevDate, periodLabel: this.getCurrentPeriodLabel() diff --git a/src/managers/NavigationManager.ts b/src/managers/NavigationManager.ts index 90e8cc2..a7966fc 100644 --- a/src/managers/NavigationManager.ts +++ b/src/managers/NavigationManager.ts @@ -25,7 +25,7 @@ export class NavigationManager { this.eventBus = eventBus; DateCalculator.initialize(calendarConfig); this.dateCalculator = new DateCalculator(); - this.navigationRenderer = new NavigationRenderer(eventBus, calendarConfig, eventRenderer); + this.navigationRenderer = new NavigationRenderer(eventBus, eventRenderer); this.currentWeek = DateCalculator.getISOWeekStart(new Date()); this.targetWeek = new Date(this.currentWeek); this.init(); @@ -239,7 +239,7 @@ export class NavigationManager { this.updateWeekInfo(); // Emit period change event for ScrollManager - this.eventBus.emit(CoreEvents.PERIOD_CHANGED, { + this.eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { direction, weekStart: this.currentWeek }); @@ -253,7 +253,7 @@ export class NavigationManager { const dateRange = DateCalculator.formatDateRange(this.currentWeek, weekEnd); // Notify other managers about week info update - DOM manipulation should happen via events - this.eventBus.emit(CoreEvents.WEEK_CHANGED, { + this.eventBus.emit(CoreEvents.PERIOD_INFO_UPDATE, { weekNumber, dateRange, weekStart: this.currentWeek, diff --git a/src/managers/ScrollManager.ts b/src/managers/ScrollManager.ts index 971ca62..e09c362 100644 --- a/src/managers/ScrollManager.ts +++ b/src/managers/ScrollManager.ts @@ -31,7 +31,7 @@ export class ScrollManager { private subscribeToEvents(): void { // Handle navigation animation completion - sync time axis position - eventBus.on(CoreEvents.PERIOD_CHANGED, () => { + eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => { this.syncTimeAxisPosition(); this.setupScrolling(); }); diff --git a/src/managers/WorkHoursManager.ts b/src/managers/WorkHoursManager.ts index e379a67..e53089d 100644 --- a/src/managers/WorkHoursManager.ts +++ b/src/managers/WorkHoursManager.ts @@ -1,7 +1,7 @@ // Work hours management for per-column scheduling import { DateCalculator } from '../utils/DateCalculator'; -import { CalendarConfig } from '../core/CalendarConfig'; +import { calendarConfig } from '../core/CalendarConfig'; /** * Work hours for a specific day @@ -33,13 +33,11 @@ export interface WorkScheduleConfig { * Manages work hours scheduling with weekly defaults and date-specific overrides */ export class WorkHoursManager { - private config: CalendarConfig; private dateCalculator: DateCalculator; private workSchedule: WorkScheduleConfig; - constructor(config: CalendarConfig) { - this.config = config; - DateCalculator.initialize(config); + constructor() { + DateCalculator.initialize(calendarConfig); this.dateCalculator = new DateCalculator(); // Default work schedule - will be loaded from JSON later @@ -100,7 +98,7 @@ export class WorkHoursManager { return null; // Full day will be colored via CSS background } - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const dayStartHour = gridSettings.dayStartHour; const dayEndHour = gridSettings.dayEndHour; const hourHeight = gridSettings.hourHeight; @@ -125,7 +123,7 @@ export class WorkHoursManager { return null; } - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const dayStartHour = gridSettings.dayStartHour; const hourHeight = gridSettings.hourHeight; diff --git a/src/renderers/ColumnRenderer.ts b/src/renderers/ColumnRenderer.ts index f9fae00..58a75de 100644 --- a/src/renderers/ColumnRenderer.ts +++ b/src/renderers/ColumnRenderer.ts @@ -34,7 +34,7 @@ export class DateColumnRenderer implements ColumnRenderer { // Initialize date calculator and work hours manager DateCalculator.initialize(config); this.dateCalculator = new DateCalculator(); - this.workHoursManager = new WorkHoursManager(config); + this.workHoursManager = new WorkHoursManager(); const dates = DateCalculator.getWorkWeekDates(currentWeek); const dateSettings = config.getDateViewSettings(); diff --git a/src/renderers/EventRenderer.ts b/src/renderers/EventRenderer.ts index aeac023..6d70709 100644 --- a/src/renderers/EventRenderer.ts +++ b/src/renderers/EventRenderer.ts @@ -2,7 +2,7 @@ import { CalendarEvent } from '../types/CalendarTypes'; import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig'; -import { CalendarConfig } from '../core/CalendarConfig'; +import { calendarConfig } from '../core/CalendarConfig'; import { DateCalculator } from '../utils/DateCalculator'; import { eventBus } from '../core/EventBus'; import { CoreEvents } from '../constants/CoreEvents'; @@ -11,7 +11,7 @@ import { CoreEvents } from '../constants/CoreEvents'; * Interface for event rendering strategies */ export interface EventRendererStrategy { - renderEvents(events: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void; + renderEvents(events: CalendarEvent[], container: HTMLElement): void; clearEvents(container?: HTMLElement): void; } @@ -20,16 +20,14 @@ export interface EventRendererStrategy { */ export abstract class BaseEventRenderer implements EventRendererStrategy { protected dateCalculator: DateCalculator; - protected config: CalendarConfig; // Drag and drop state private draggedClone: HTMLElement | null = null; private originalEvent: HTMLElement | null = null; - constructor(config: CalendarConfig, dateCalculator?: DateCalculator) { - this.config = config; + constructor(dateCalculator?: DateCalculator) { if (!dateCalculator) { - DateCalculator.initialize(config); + DateCalculator.initialize(calendarConfig); } this.dateCalculator = dateCalculator || new DateCalculator(); } @@ -69,7 +67,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { }); // Handle navigation period change (when slide animation completes) - eventBus.on(CoreEvents.PERIOD_CHANGED, () => { + eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => { // Animate all-day height after navigation completes this.triggerAllDayHeightAnimation(); }); @@ -146,7 +144,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { * Update clone timestamp based on new position */ private updateCloneTimestamp(clone: HTMLElement, snappedY: number): void { - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const hourHeight = gridSettings.hourHeight; const dayStartHour = gridSettings.dayStartHour; const snapInterval = 15; // TODO: Get from config @@ -175,7 +173,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { * Calculate event duration in minutes from element height */ private getEventDuration(element: HTMLElement): number { - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const hourHeight = gridSettings.hourHeight; // Get height from style or computed @@ -424,7 +422,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { } } - renderEvents(events: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void { + renderEvents(events: CalendarEvent[], container: HTMLElement): void { // NOTE: Removed clearEvents() to support sliding animation // With sliding animation, multiple grid containers exist simultaneously @@ -437,7 +435,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { // Always call renderAllDayEvents to ensure height is set correctly (even to 0) - this.renderAllDayEvents(allDayEvents, container, config); + this.renderAllDayEvents(allDayEvents, container); // Find columns in the specific container for regular events const columns = this.getColumns(container); @@ -448,7 +446,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { const eventsLayer = column.querySelector('swp-events-layer'); if (eventsLayer) { columnEvents.forEach(event => { - this.renderEvent(event, eventsLayer, config); + this.renderEvent(event, eventsLayer); }); // Debug: Verify events were actually added @@ -465,7 +463,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { /** * Render all-day events in the header row 2 */ - protected renderAllDayEvents(allDayEvents: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void { + protected renderAllDayEvents(allDayEvents: CalendarEvent[], container: HTMLElement): void { // Find the calendar header const calendarHeader = container.querySelector('swp-calendar-header'); @@ -565,7 +563,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { } - protected renderEvent(event: CalendarEvent, container: Element, config: CalendarConfig): void { + protected renderEvent(event: CalendarEvent, container: Element): void { const eventElement = document.createElement('swp-event'); eventElement.dataset.eventId = event.id; eventElement.dataset.title = event.title; @@ -575,7 +573,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { eventElement.dataset.duration = event.metadata?.duration?.toString() || '60'; // Calculate position based on time - const position = this.calculateEventPosition(event, config); + const position = this.calculateEventPosition(event); eventElement.style.position = 'absolute'; eventElement.style.top = `${position.top + 1}px`; eventElement.style.height = `${position.height - 3}px`; //adjusted so bottom does not cover horizontal time lines. @@ -601,11 +599,11 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { container.appendChild(eventElement); } - protected calculateEventPosition(event: CalendarEvent, config: CalendarConfig): { top: number; height: number } { + protected calculateEventPosition(event: CalendarEvent): { top: number; height: number } { const startDate = new Date(event.start); const endDate = new Date(event.end); - const gridSettings = config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const dayStartHour = gridSettings.dayStartHour; const hourHeight = gridSettings.hourHeight; @@ -681,8 +679,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy { * Date-based event renderer */ export class DateEventRenderer extends BaseEventRenderer { - constructor(config: CalendarConfig, dateCalculator?: DateCalculator) { - super(config, dateCalculator); + constructor(dateCalculator?: DateCalculator) { + super(dateCalculator); this.setupDragEventListeners(); } diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index fa3b4f2..09395c8 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -46,7 +46,7 @@ export class EventRenderingService { } // Use cached strategy to render events in the specific container - this.strategy.renderEvents(events, context.container, calendarConfig); + this.strategy.renderEvents(events, context.container); // Emit EVENTS_RENDERED event for filtering system this.eventBus.emit(CoreEvents.EVENTS_RENDERED, { diff --git a/src/renderers/GridRenderer.ts b/src/renderers/GridRenderer.ts index 8d306df..b3848b4 100644 --- a/src/renderers/GridRenderer.ts +++ b/src/renderers/GridRenderer.ts @@ -1,4 +1,4 @@ -import { CalendarConfig } from '../core/CalendarConfig'; +import { calendarConfig } from '../core/CalendarConfig'; import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { HeaderRenderContext } from './HeaderRenderer'; @@ -11,14 +11,12 @@ import { DateCalculator } from '../utils/DateCalculator'; * Optimized to reduce redundant DOM operations and improve performance */ export class GridRenderer { - private config: CalendarConfig; private headerEventListener: ((event: Event) => void) | null = null; private cachedGridContainer: HTMLElement | null = null; private cachedCalendarHeader: HTMLElement | null = null; private cachedTimeAxis: HTMLElement | null = null; - constructor(config: CalendarConfig) { - this.config = config; + constructor() { } /** @@ -83,7 +81,7 @@ export class GridRenderer { private createOptimizedTimeAxis(): HTMLElement { const timeAxis = document.createElement('swp-time-axis'); const timeAxisContent = document.createElement('swp-time-axis-content'); - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const startHour = gridSettings.dayStartHour; const endHour = gridSettings.dayEndHour; @@ -146,12 +144,12 @@ export class GridRenderer { resourceData: ResourceCalendarData | null, view: CalendarView ): void { - const calendarType = this.config.getCalendarMode(); + const calendarType = calendarConfig.getCalendarMode(); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); const context: HeaderRenderContext = { currentWeek: currentDate, // HeaderRenderer expects currentWeek property - config: this.config, + config: calendarConfig, resourceData: resourceData }; @@ -173,12 +171,12 @@ export class GridRenderer { resourceData: ResourceCalendarData | null, view: CalendarView ): void { - const calendarType = this.config.getCalendarMode(); + const calendarType = calendarConfig.getCalendarMode(); const columnRenderer = CalendarTypeFactory.getColumnRenderer(calendarType); const context: ColumnRenderContext = { currentWeek: currentDate, // ColumnRenderer expects currentWeek property - config: this.config, + config: calendarConfig, resourceData: resourceData }; @@ -260,7 +258,7 @@ export class GridRenderer { } // Get header renderer once and cache - const calendarType = this.config.getCalendarMode(); + const calendarType = calendarConfig.getCalendarMode(); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); eventBus.emit('header:mouseover', { diff --git a/src/renderers/GridStyleManager.ts b/src/renderers/GridStyleManager.ts index c60e0d2..bc97604 100644 --- a/src/renderers/GridStyleManager.ts +++ b/src/renderers/GridStyleManager.ts @@ -1,4 +1,4 @@ -import { CalendarConfig } from '../core/CalendarConfig'; +import { calendarConfig } from '../core/CalendarConfig'; import { ResourceCalendarData } from '../types/CalendarTypes'; /** @@ -6,10 +6,7 @@ import { ResourceCalendarData } from '../types/CalendarTypes'; * Separated from GridManager to follow Single Responsibility Principle */ export class GridStyleManager { - private config: CalendarConfig; - - constructor(config: CalendarConfig) { - this.config = config; + constructor() { } /** @@ -17,9 +14,9 @@ export class GridStyleManager { */ public updateGridStyles(resourceData: ResourceCalendarData | null = null): void { const root = document.documentElement; - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const calendar = document.querySelector('swp-calendar') as HTMLElement; - const calendarType = this.config.getCalendarMode(); + const calendarType = calendarConfig.getCalendarMode(); // Set CSS variables for time and grid measurements this.setTimeVariables(root, gridSettings); @@ -58,8 +55,8 @@ export class GridStyleManager { if (calendarType === 'resource' && resourceData) { return resourceData.resources.length; } else if (calendarType === 'date') { - const dateSettings = this.config.getDateViewSettings(); - const workWeekSettings = this.config.getWorkWeekSettings(); + const dateSettings = calendarConfig.getDateViewSettings(); + const workWeekSettings = calendarConfig.getWorkWeekSettings(); switch (dateSettings.period) { case 'day': @@ -73,7 +70,7 @@ export class GridStyleManager { } } - return this.config.getWorkWeekSettings().totalDays; // Default to work week + return calendarConfig.getWorkWeekSettings().totalDays; // Default to work week } /** diff --git a/src/renderers/NavigationRenderer.ts b/src/renderers/NavigationRenderer.ts index 19f6edb..300de31 100644 --- a/src/renderers/NavigationRenderer.ts +++ b/src/renderers/NavigationRenderer.ts @@ -1,6 +1,6 @@ import { IEventBus } from '../types/CalendarTypes'; import { CoreEvents } from '../constants/CoreEvents'; -import { CalendarConfig } from '../core/CalendarConfig'; +import { calendarConfig } from '../core/CalendarConfig'; import { DateCalculator } from '../utils/DateCalculator'; import { EventRenderingService } from './EventRendererManager'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; @@ -12,7 +12,6 @@ import { eventBus } from '../core/EventBus'; */ export class NavigationRenderer { private eventBus: IEventBus; - private config: CalendarConfig; private dateCalculator: DateCalculator; private eventRenderer: EventRenderingService; @@ -20,11 +19,10 @@ export class NavigationRenderer { private cachedWeekNumberElement: HTMLElement | null = null; private cachedDateRangeElement: HTMLElement | null = null; - constructor(eventBus: IEventBus, config: CalendarConfig, eventRenderer: EventRenderingService) { + constructor(eventBus: IEventBus, eventRenderer: EventRenderingService) { this.eventBus = eventBus; - this.config = config; this.eventRenderer = eventRenderer; - DateCalculator.initialize(config); + DateCalculator.initialize(calendarConfig); this.dateCalculator = new DateCalculator(); this.setupEventListeners(); } @@ -61,7 +59,7 @@ export class NavigationRenderer { * Setup event listeners for DOM updates */ private setupEventListeners(): void { - this.eventBus.on(CoreEvents.WEEK_CHANGED, (event: Event) => { + this.eventBus.on(CoreEvents.PERIOD_INFO_UPDATE, (event: Event) => { const customEvent = event as CustomEvent; const { weekNumber, dateRange } = customEvent.detail; this.updateWeekInfoInDOM(weekNumber, dateRange); @@ -201,7 +199,7 @@ export class NavigationRenderer { }); // Always ensure all-day containers exist for all days - const headerRenderer = CalendarTypeFactory.getHeaderRenderer(this.config.getCalendarMode()); + const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarConfig.getCalendarMode()); headerRenderer.ensureAllDayContainers(header as HTMLElement); // Add event delegation listener for drag & drop functionality @@ -256,7 +254,7 @@ export class NavigationRenderer { // Get the header renderer for addToAllDay functionality - const calendarType = this.config.getCalendarMode(); + const calendarType = calendarConfig.getCalendarMode(); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); eventBus.emit('header:mouseover', { diff --git a/src/strategies/WeekViewStrategy.ts b/src/strategies/WeekViewStrategy.ts index ce22e28..fddb9ae 100644 --- a/src/strategies/WeekViewStrategy.ts +++ b/src/strategies/WeekViewStrategy.ts @@ -17,8 +17,8 @@ export class WeekViewStrategy implements ViewStrategy { constructor() { DateCalculator.initialize(calendarConfig); this.dateCalculator = new DateCalculator(); - this.gridRenderer = new GridRenderer(calendarConfig); - this.styleManager = new GridStyleManager(calendarConfig); + this.gridRenderer = new GridRenderer(); + this.styleManager = new GridStyleManager(); } getLayoutConfig(): ViewLayoutConfig { diff --git a/src/utils/PositionUtils.ts b/src/utils/PositionUtils.ts index 3e1f708..1718033 100644 --- a/src/utils/PositionUtils.ts +++ b/src/utils/PositionUtils.ts @@ -1,22 +1,16 @@ -import { CalendarConfig } from '../core/CalendarConfig.js'; +import { calendarConfig } from '../core/CalendarConfig.js'; import { DateCalculator } from './DateCalculator.js'; /** - * PositionUtils - Optimized positioning utilities using DateCalculator + * PositionUtils - Static positioning utilities using singleton calendarConfig * Focuses on pixel/position calculations while delegating date operations */ export class PositionUtils { - private config: CalendarConfig; - - constructor(config: CalendarConfig) { - this.config = config; - } - /** * Convert minutes to pixels */ - public minutesToPixels(minutes: number): number { - const gridSettings = this.config.getGridSettings(); + public static minutesToPixels(minutes: number): number { + const gridSettings = calendarConfig.getGridSettings(); const pixelsPerHour = gridSettings.hourHeight; return (minutes / 60) * pixelsPerHour; } @@ -24,8 +18,8 @@ export class PositionUtils { /** * Convert pixels to minutes */ - public pixelsToMinutes(pixels: number): number { - const gridSettings = this.config.getGridSettings(); + public static pixelsToMinutes(pixels: number): number { + const gridSettings = calendarConfig.getGridSettings(); const pixelsPerHour = gridSettings.hourHeight; return (pixels / pixelsPerHour) * 60; } @@ -33,33 +27,33 @@ export class PositionUtils { /** * Convert time (HH:MM) to pixels from day start using DateCalculator */ - public timeToPixels(timeString: string): number { + public static timeToPixels(timeString: string): number { const totalMinutes = DateCalculator.timeToMinutes(timeString); - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const dayStartMinutes = gridSettings.dayStartHour * 60; const minutesFromDayStart = totalMinutes - dayStartMinutes; - return this.minutesToPixels(minutesFromDayStart); + return PositionUtils.minutesToPixels(minutesFromDayStart); } /** * Convert Date object to pixels from day start using DateCalculator */ - public dateToPixels(date: Date): number { + public static dateToPixels(date: Date): number { const totalMinutes = DateCalculator.getMinutesSinceMidnight(date); - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const dayStartMinutes = gridSettings.dayStartHour * 60; const minutesFromDayStart = totalMinutes - dayStartMinutes; - return this.minutesToPixels(minutesFromDayStart); + return PositionUtils.minutesToPixels(minutesFromDayStart); } /** * Convert pixels to time using DateCalculator */ - public pixelsToTime(pixels: number): string { - const minutes = this.pixelsToMinutes(pixels); - const gridSettings = this.config.getGridSettings(); + public static pixelsToTime(pixels: number): string { + const minutes = PositionUtils.pixelsToMinutes(pixels); + const gridSettings = calendarConfig.getGridSettings(); const dayStartMinutes = gridSettings.dayStartHour * 60; const totalMinutes = dayStartMinutes + minutes; @@ -69,7 +63,7 @@ export class PositionUtils { /** * Beregn event position og størrelse */ - public calculateEventPosition(startTime: string | Date, endTime: string | Date): { + public static calculateEventPosition(startTime: string | Date, endTime: string | Date): { top: number; height: number; duration: number; @@ -78,19 +72,19 @@ export class PositionUtils { let endPixels: number; if (typeof startTime === 'string') { - startPixels = this.timeToPixels(startTime); + startPixels = PositionUtils.timeToPixels(startTime); } else { - startPixels = this.dateToPixels(startTime); + startPixels = PositionUtils.dateToPixels(startTime); } if (typeof endTime === 'string') { - endPixels = this.timeToPixels(endTime); + endPixels = PositionUtils.timeToPixels(endTime); } else { - endPixels = this.dateToPixels(endTime); + endPixels = PositionUtils.dateToPixels(endTime); } - const height = Math.max(endPixels - startPixels, this.getMinimumEventHeight()); - const duration = this.pixelsToMinutes(height); + const height = Math.max(endPixels - startPixels, PositionUtils.getMinimumEventHeight()); + const duration = PositionUtils.pixelsToMinutes(height); return { top: startPixels, @@ -102,10 +96,10 @@ export class PositionUtils { /** * Snap position til grid interval */ - public snapToGrid(pixels: number): number { - const gridSettings = this.config.getGridSettings(); + public static snapToGrid(pixels: number): number { + const gridSettings = calendarConfig.getGridSettings(); const snapInterval = gridSettings.snapInterval; - const snapPixels = this.minutesToPixels(snapInterval); + const snapPixels = PositionUtils.minutesToPixels(snapInterval); return Math.round(pixels / snapPixels) * snapPixels; } @@ -113,9 +107,9 @@ export class PositionUtils { /** * Snap time to interval using DateCalculator */ - public snapTimeToInterval(timeString: string): string { + public static snapTimeToInterval(timeString: string): string { const totalMinutes = DateCalculator.timeToMinutes(timeString); - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); const snapInterval = gridSettings.snapInterval; const snappedMinutes = Math.round(totalMinutes / snapInterval) * snapInterval; @@ -125,7 +119,7 @@ export class PositionUtils { /** * Beregn kolonne position for overlappende events */ - public calculateColumnPosition(eventIndex: number, totalColumns: number, containerWidth: number): { + public static calculateColumnPosition(eventIndex: number, totalColumns: number, containerWidth: number): { left: number; width: number; } { @@ -145,14 +139,14 @@ export class PositionUtils { /** * Check om to events overlapper i tid */ - public eventsOverlap( - start1: string | Date, - end1: string | Date, - start2: string | Date, + public static eventsOverlap( + start1: string | Date, + end1: string | Date, + start2: string | Date, end2: string | Date ): boolean { - const pos1 = this.calculateEventPosition(start1, end1); - const pos2 = this.calculateEventPosition(start2, end2); + const pos1 = PositionUtils.calculateEventPosition(start1, end1); + const pos2 = PositionUtils.calculateEventPosition(start2, end2); const event1End = pos1.top + pos1.height; const event2End = pos2.top + pos2.height; @@ -163,53 +157,53 @@ export class PositionUtils { /** * Beregn Y position fra mouse/touch koordinat */ - public getPositionFromCoordinate(clientY: number, containerElement: HTMLElement): number { + public static getPositionFromCoordinate(clientY: number, containerElement: HTMLElement): number { const rect = containerElement.getBoundingClientRect(); const relativeY = clientY - rect.top; // Snap til grid - return this.snapToGrid(relativeY); + return PositionUtils.snapToGrid(relativeY); } /** * Beregn tid fra mouse/touch koordinat */ - public getTimeFromCoordinate(clientY: number, containerElement: HTMLElement): string { - const position = this.getPositionFromCoordinate(clientY, containerElement); - return this.pixelsToTime(position); + public static getTimeFromCoordinate(clientY: number, containerElement: HTMLElement): string { + const position = PositionUtils.getPositionFromCoordinate(clientY, containerElement); + return PositionUtils.pixelsToTime(position); } /** * Valider at tid er inden for arbejdstimer */ - public isWithinWorkHours(timeString: string): boolean { + public static isWithinWorkHours(timeString: string): boolean { const [hours] = timeString.split(':').map(Number); - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); return hours >= gridSettings.workStartHour && hours < gridSettings.workEndHour; } /** * Valider at tid er inden for dag grænser */ - public isWithinDayBounds(timeString: string): boolean { + public static isWithinDayBounds(timeString: string): boolean { const [hours] = timeString.split(':').map(Number); - const gridSettings = this.config.getGridSettings(); + const gridSettings = calendarConfig.getGridSettings(); return hours >= gridSettings.dayStartHour && hours < gridSettings.dayEndHour; } /** * Hent minimum event højde i pixels */ - public getMinimumEventHeight(): number { + public static getMinimumEventHeight(): number { // Minimum 15 minutter - return this.minutesToPixels(15); + return PositionUtils.minutesToPixels(15); } /** * Hent maksimum event højde i pixels (hele dagen) */ - public getMaximumEventHeight(): number { - const gridSettings = this.config.getGridSettings(); + public static getMaximumEventHeight(): number { + const gridSettings = calendarConfig.getGridSettings(); const dayDurationHours = gridSettings.dayEndHour - gridSettings.dayStartHour; return dayDurationHours * gridSettings.hourHeight; } @@ -217,14 +211,14 @@ export class PositionUtils { /** * Beregn total kalender højde */ - public getTotalCalendarHeight(): number { - return this.getMaximumEventHeight(); + public static getTotalCalendarHeight(): number { + return PositionUtils.getMaximumEventHeight(); } /** * Convert ISO datetime to time string using DateCalculator */ - public isoToTimeString(isoString: string): string { + public static isoToTimeString(isoString: string): string { const date = new Date(isoString); return DateCalculator.formatTime(date); } @@ -232,7 +226,7 @@ export class PositionUtils { /** * Convert time string to ISO datetime using DateCalculator */ - public timeStringToIso(timeString: string, date: Date = new Date()): string { + public static timeStringToIso(timeString: string, date: Date = new Date()): string { const totalMinutes = DateCalculator.timeToMinutes(timeString); const hours = Math.floor(totalMinutes / 60); const minutes = totalMinutes % 60; @@ -246,14 +240,14 @@ export class PositionUtils { /** * Calculate event duration using DateCalculator */ - public calculateDuration(startTime: string | Date, endTime: string | Date): number { + public static calculateDuration(startTime: string | Date, endTime: string | Date): number { return DateCalculator.getDurationMinutes(startTime, endTime); } /** * Format duration to readable text (Danish) */ - public formatDuration(minutes: number): string { + public static formatDuration(minutes: number): string { if (minutes < 60) { return `${minutes} min`; } @@ -267,11 +261,4 @@ export class PositionUtils { return `${hours}t ${remainingMinutes}m`; } - - /** - * Opdater konfiguration - */ - public updateConfig(newConfig: CalendarConfig): void { - this.config = newConfig; - } } \ No newline at end of file