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.
This commit is contained in:
Janus Knudsen 2025-09-03 20:04:47 +02:00
parent d0936d1838
commit 2083c6921e
19 changed files with 139 additions and 173 deletions

View file

@ -15,8 +15,8 @@ export const CoreEvents = {
// Navigation events (3) // Navigation events (3)
DATE_CHANGED: 'nav:date-changed', DATE_CHANGED: 'nav:date-changed',
PERIOD_CHANGED: 'nav:period-changed', NAVIGATION_COMPLETED: 'nav:navigation-completed',
WEEK_CHANGED: 'nav:week-changed', PERIOD_INFO_UPDATE: 'nav:period-info-update',
// Data events (4) // Data events (4)
DATA_LOADING: 'data:loading', DATA_LOADING: 'data:loading',
@ -65,8 +65,8 @@ export const EVENT_MIGRATION_MAP: Record<string, string> = {
// Navigation // Navigation
'calendar:datechanged': CoreEvents.DATE_CHANGED, 'calendar:datechanged': CoreEvents.DATE_CHANGED,
'calendar:periodchange': CoreEvents.PERIOD_CHANGED, 'calendar:navigationcompleted': CoreEvents.NAVIGATION_COMPLETED,
'calendar:weekchanged': CoreEvents.WEEK_CHANGED, 'calendar:periodinfoUpdate': CoreEvents.PERIOD_INFO_UPDATE,
// Data // Data
'calendar:datafetchstart': CoreEvents.DATA_LOADING, 'calendar:datafetchstart': CoreEvents.DATA_LOADING,

View file

@ -298,11 +298,10 @@ export class CalendarConfig {
this.config.minEventDuration = updates.snapInterval; this.config.minEventDuration = updates.snapInterval;
} }
// Emit grid settings update event // Grid settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, { eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'gridSettings', key: 'gridSettings',
value: this.gridSettings, value: this.gridSettings
oldValue: this.gridSettings
}); });
} }
@ -319,11 +318,10 @@ export class CalendarConfig {
updateDateViewSettings(updates: Partial<DateViewSettings>): void { updateDateViewSettings(updates: Partial<DateViewSettings>): void {
this.dateViewSettings = { ...this.dateViewSettings, ...updates }; 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, { eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'dateViewSettings', key: 'dateViewSettings',
value: this.dateViewSettings, value: this.dateViewSettings
oldValue: this.dateViewSettings
}); });
} }
@ -340,11 +338,10 @@ export class CalendarConfig {
updateResourceViewSettings(updates: Partial<ResourceViewSettings>): void { updateResourceViewSettings(updates: Partial<ResourceViewSettings>): void {
this.resourceViewSettings = { ...this.resourceViewSettings, ...updates }; 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, { eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'resourceViewSettings', key: 'resourceViewSettings',
value: this.resourceViewSettings, value: this.resourceViewSettings
oldValue: this.resourceViewSettings
}); });
} }

View file

@ -34,13 +34,13 @@ export class CalendarTypeFactory {
this.registerRenderers('date', { this.registerRenderers('date', {
headerRenderer: new DateHeaderRenderer(), headerRenderer: new DateHeaderRenderer(),
columnRenderer: new DateColumnRenderer(), columnRenderer: new DateColumnRenderer(),
eventRenderer: new DateEventRenderer(calendarConfig) eventRenderer: new DateEventRenderer()
}); });
this.registerRenderers('resource', { this.registerRenderers('resource', {
headerRenderer: new ResourceHeaderRenderer(), headerRenderer: new ResourceHeaderRenderer(),
columnRenderer: new ResourceColumnRenderer(), columnRenderer: new ResourceColumnRenderer(),
eventRenderer: new ResourceEventRenderer(calendarConfig) eventRenderer: new ResourceEventRenderer()
}); });
this.isInitialized = true; this.isInitialized = true;

View file

@ -1,5 +1,4 @@
import { IEventBus } from '../types/CalendarTypes'; import { IEventBus } from '../types/CalendarTypes';
import { CalendarConfig } from '../core/CalendarConfig';
import { EventManager } from '../managers/EventManager'; import { EventManager } from '../managers/EventManager';
import { EventRenderingService } from '../renderers/EventRendererManager'; import { EventRenderingService } from '../renderers/EventRendererManager';
import { GridManager } from '../managers/GridManager'; import { GridManager } from '../managers/GridManager';
@ -27,7 +26,7 @@ export class ManagerFactory {
/** /**
* Create all managers with proper dependency injection * Create all managers with proper dependency injection
*/ */
public createManagers(eventBus: IEventBus, config: CalendarConfig): { public createManagers(eventBus: IEventBus): {
eventManager: EventManager; eventManager: EventManager;
eventRenderer: EventRenderingService; eventRenderer: EventRenderingService;
gridManager: GridManager; gridManager: GridManager;
@ -45,15 +44,14 @@ export class ManagerFactory {
const scrollManager = new ScrollManager(); const scrollManager = new ScrollManager();
const navigationManager = new NavigationManager(eventBus, eventRenderer); const navigationManager = new NavigationManager(eventBus, eventRenderer);
const viewManager = new ViewManager(eventBus); const viewManager = new ViewManager(eventBus);
const dragDropManager = new DragDropManager(eventBus, config); const dragDropManager = new DragDropManager(eventBus);
// CalendarManager depends on all other managers // CalendarManager depends on all other managers
const calendarManager = new CalendarManager( const calendarManager = new CalendarManager(
eventBus, eventBus,
config, eventManager,
eventManager, gridManager,
gridManager, eventRenderer,
eventRenderer,
scrollManager scrollManager
); );

View file

@ -22,7 +22,7 @@ async function initializeCalendar(): Promise<void> {
// Create managers using factory pattern // Create managers using factory pattern
const managerFactory = ManagerFactory.getInstance(); const managerFactory = ManagerFactory.getInstance();
const managers = managerFactory.createManagers(eventBus, config); const managers = managerFactory.createManagers(eventBus);
// Enable debug mode for development // Enable debug mode for development
eventBus.setDebug(true); eventBus.setDebug(true);

View file

@ -1,6 +1,6 @@
import { EventBus } from '../core/EventBus.js'; import { EventBus } from '../core/EventBus.js';
import { CoreEvents } from '../constants/CoreEvents.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 { CalendarEvent, CalendarView, IEventBus } from '../types/CalendarTypes.js';
import { EventManager } from './EventManager.js'; import { EventManager } from './EventManager.js';
import { GridManager } from './GridManager.js'; import { GridManager } from './GridManager.js';
@ -11,11 +11,10 @@ import { EventFilterManager } from './EventFilterManager.js';
/** /**
* CalendarManager - Main coordinator for all calendar managers * 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 { export class CalendarManager {
private eventBus: IEventBus; private eventBus: IEventBus;
private config: CalendarConfig;
private eventManager: EventManager; private eventManager: EventManager;
private gridManager: GridManager; private gridManager: GridManager;
private eventRenderer: EventRenderingService; private eventRenderer: EventRenderingService;
@ -28,20 +27,18 @@ export class CalendarManager {
constructor( constructor(
eventBus: IEventBus, eventBus: IEventBus,
config: CalendarConfig,
eventManager: EventManager, eventManager: EventManager,
gridManager: GridManager, gridManager: GridManager,
eventRenderer: EventRenderingService, eventRenderer: EventRenderingService,
scrollManager: ScrollManager scrollManager: ScrollManager
) { ) {
this.eventBus = eventBus; this.eventBus = eventBus;
this.config = config;
this.eventManager = eventManager; this.eventManager = eventManager;
this.gridManager = gridManager; this.gridManager = gridManager;
this.eventRenderer = eventRenderer; this.eventRenderer = eventRenderer;
this.scrollManager = scrollManager; this.scrollManager = scrollManager;
this.eventFilterManager = new EventFilterManager(); this.eventFilterManager = new EventFilterManager();
DateCalculator.initialize(config); DateCalculator.initialize(calendarConfig);
this.dateCalculator = new DateCalculator(); this.dateCalculator = new DateCalculator();
this.setupEventListeners(); this.setupEventListeners();
} }
@ -57,7 +54,7 @@ export class CalendarManager {
try { try {
// Debug: Check calendar type // Debug: Check calendar type
const calendarType = this.config.getCalendarMode(); const calendarType = calendarConfig.getCalendarMode();
// Step 1: Load data // Step 1: Load data
await this.eventManager.loadData(); await this.eventManager.loadData();
@ -194,8 +191,8 @@ export class CalendarManager {
/** /**
* Hent calendar konfiguration * Hent calendar konfiguration
*/ */
public getConfig(): CalendarConfig { public getConfig() {
return this.config; return calendarConfig;
} }
/** /**
@ -267,7 +264,7 @@ export class CalendarManager {
nextDate.setDate(nextDate.getDate() + 1); nextDate.setDate(nextDate.getDate() + 1);
break; break;
case 'week': 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 nextDate.setDate(nextDate.getDate() + 7); // Move to next calendar week regardless of work days
break; break;
case 'month': case 'month':
@ -289,7 +286,7 @@ export class CalendarManager {
previousDate.setDate(previousDate.getDate() - 1); previousDate.setDate(previousDate.getDate() - 1);
break; break;
case 'week': 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 previousDate.setDate(previousDate.getDate() - 7); // Move to previous calendar week regardless of work days
break; break;
case 'month': case 'month':
@ -441,7 +438,7 @@ export class CalendarManager {
const dateRange = DateCalculator.formatDateRange(firstDate, lastDate); const dateRange = DateCalculator.formatDateRange(firstDate, lastDate);
// Emit week info update // Emit week info update
this.eventBus.emit(CoreEvents.WEEK_CHANGED, { this.eventBus.emit(CoreEvents.PERIOD_INFO_UPDATE, {
weekNumber, weekNumber,
dateRange, dateRange,
weekStart: firstDate, weekStart: firstDate,

View file

@ -4,7 +4,7 @@
*/ */
import { IEventBus } from '../types/CalendarTypes'; import { IEventBus } from '../types/CalendarTypes';
import { CalendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { DateCalculator } from '../utils/DateCalculator'; import { DateCalculator } from '../utils/DateCalculator';
interface CachedElements { interface CachedElements {
@ -20,7 +20,6 @@ interface Position {
export class DragDropManager { export class DragDropManager {
private eventBus: IEventBus; private eventBus: IEventBus;
private config: CalendarConfig;
// Mouse tracking with optimized state // Mouse tracking with optimized state
private isMouseDown = false; private isMouseDown = false;
@ -61,12 +60,11 @@ export class DragDropManager {
return (this.snapIntervalMinutes / 60) * this.hourHeightPx; return (this.snapIntervalMinutes / 60) * this.hourHeightPx;
} }
constructor(eventBus: IEventBus, config: CalendarConfig) { constructor(eventBus: IEventBus) {
this.eventBus = eventBus; this.eventBus = eventBus;
this.config = config;
// Get config values // Get config values
const gridSettings = config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
this.hourHeightPx = gridSettings.hourHeight; this.hourHeightPx = gridSettings.hourHeight;
this.init(); this.init();

View file

@ -23,7 +23,7 @@ export class GridManager {
constructor() { constructor() {
// Initialize GridRenderer with config // Initialize GridRenderer with config
this.gridRenderer = new GridRenderer(calendarConfig); this.gridRenderer = new GridRenderer();
this.init(); this.init();
} }
@ -151,7 +151,7 @@ export class GridManager {
this.currentDate = nextDate; this.currentDate = nextDate;
eventBus.emit(CoreEvents.PERIOD_CHANGED, { eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, {
direction: 'next', direction: 'next',
newDate: nextDate, newDate: nextDate,
periodLabel: this.getCurrentPeriodLabel() periodLabel: this.getCurrentPeriodLabel()
@ -182,7 +182,7 @@ export class GridManager {
this.currentDate = prevDate; this.currentDate = prevDate;
eventBus.emit(CoreEvents.PERIOD_CHANGED, { eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, {
direction: 'previous', direction: 'previous',
newDate: prevDate, newDate: prevDate,
periodLabel: this.getCurrentPeriodLabel() periodLabel: this.getCurrentPeriodLabel()

View file

@ -25,7 +25,7 @@ export class NavigationManager {
this.eventBus = eventBus; this.eventBus = eventBus;
DateCalculator.initialize(calendarConfig); DateCalculator.initialize(calendarConfig);
this.dateCalculator = new DateCalculator(); this.dateCalculator = new DateCalculator();
this.navigationRenderer = new NavigationRenderer(eventBus, calendarConfig, eventRenderer); this.navigationRenderer = new NavigationRenderer(eventBus, eventRenderer);
this.currentWeek = DateCalculator.getISOWeekStart(new Date()); this.currentWeek = DateCalculator.getISOWeekStart(new Date());
this.targetWeek = new Date(this.currentWeek); this.targetWeek = new Date(this.currentWeek);
this.init(); this.init();
@ -239,7 +239,7 @@ export class NavigationManager {
this.updateWeekInfo(); this.updateWeekInfo();
// Emit period change event for ScrollManager // Emit period change event for ScrollManager
this.eventBus.emit(CoreEvents.PERIOD_CHANGED, { this.eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, {
direction, direction,
weekStart: this.currentWeek weekStart: this.currentWeek
}); });
@ -253,7 +253,7 @@ export class NavigationManager {
const dateRange = DateCalculator.formatDateRange(this.currentWeek, weekEnd); const dateRange = DateCalculator.formatDateRange(this.currentWeek, weekEnd);
// Notify other managers about week info update - DOM manipulation should happen via events // 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, weekNumber,
dateRange, dateRange,
weekStart: this.currentWeek, weekStart: this.currentWeek,

View file

@ -31,7 +31,7 @@ export class ScrollManager {
private subscribeToEvents(): void { private subscribeToEvents(): void {
// Handle navigation animation completion - sync time axis position // Handle navigation animation completion - sync time axis position
eventBus.on(CoreEvents.PERIOD_CHANGED, () => { eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => {
this.syncTimeAxisPosition(); this.syncTimeAxisPosition();
this.setupScrolling(); this.setupScrolling();
}); });

View file

@ -1,7 +1,7 @@
// Work hours management for per-column scheduling // Work hours management for per-column scheduling
import { DateCalculator } from '../utils/DateCalculator'; import { DateCalculator } from '../utils/DateCalculator';
import { CalendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
/** /**
* Work hours for a specific day * Work hours for a specific day
@ -33,13 +33,11 @@ export interface WorkScheduleConfig {
* Manages work hours scheduling with weekly defaults and date-specific overrides * Manages work hours scheduling with weekly defaults and date-specific overrides
*/ */
export class WorkHoursManager { export class WorkHoursManager {
private config: CalendarConfig;
private dateCalculator: DateCalculator; private dateCalculator: DateCalculator;
private workSchedule: WorkScheduleConfig; private workSchedule: WorkScheduleConfig;
constructor(config: CalendarConfig) { constructor() {
this.config = config; DateCalculator.initialize(calendarConfig);
DateCalculator.initialize(config);
this.dateCalculator = new DateCalculator(); this.dateCalculator = new DateCalculator();
// Default work schedule - will be loaded from JSON later // 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 return null; // Full day will be colored via CSS background
} }
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayStartHour = gridSettings.dayStartHour; const dayStartHour = gridSettings.dayStartHour;
const dayEndHour = gridSettings.dayEndHour; const dayEndHour = gridSettings.dayEndHour;
const hourHeight = gridSettings.hourHeight; const hourHeight = gridSettings.hourHeight;
@ -125,7 +123,7 @@ export class WorkHoursManager {
return null; return null;
} }
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayStartHour = gridSettings.dayStartHour; const dayStartHour = gridSettings.dayStartHour;
const hourHeight = gridSettings.hourHeight; const hourHeight = gridSettings.hourHeight;

View file

@ -34,7 +34,7 @@ export class DateColumnRenderer implements ColumnRenderer {
// Initialize date calculator and work hours manager // Initialize date calculator and work hours manager
DateCalculator.initialize(config); DateCalculator.initialize(config);
this.dateCalculator = new DateCalculator(); this.dateCalculator = new DateCalculator();
this.workHoursManager = new WorkHoursManager(config); this.workHoursManager = new WorkHoursManager();
const dates = DateCalculator.getWorkWeekDates(currentWeek); const dates = DateCalculator.getWorkWeekDates(currentWeek);
const dateSettings = config.getDateViewSettings(); const dateSettings = config.getDateViewSettings();

View file

@ -2,7 +2,7 @@
import { CalendarEvent } from '../types/CalendarTypes'; import { CalendarEvent } from '../types/CalendarTypes';
import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig'; import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig';
import { CalendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { DateCalculator } from '../utils/DateCalculator'; import { DateCalculator } from '../utils/DateCalculator';
import { eventBus } from '../core/EventBus'; import { eventBus } from '../core/EventBus';
import { CoreEvents } from '../constants/CoreEvents'; import { CoreEvents } from '../constants/CoreEvents';
@ -11,7 +11,7 @@ import { CoreEvents } from '../constants/CoreEvents';
* Interface for event rendering strategies * Interface for event rendering strategies
*/ */
export interface EventRendererStrategy { export interface EventRendererStrategy {
renderEvents(events: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void; renderEvents(events: CalendarEvent[], container: HTMLElement): void;
clearEvents(container?: HTMLElement): void; clearEvents(container?: HTMLElement): void;
} }
@ -20,16 +20,14 @@ export interface EventRendererStrategy {
*/ */
export abstract class BaseEventRenderer implements EventRendererStrategy { export abstract class BaseEventRenderer implements EventRendererStrategy {
protected dateCalculator: DateCalculator; protected dateCalculator: DateCalculator;
protected config: CalendarConfig;
// Drag and drop state // Drag and drop state
private draggedClone: HTMLElement | null = null; private draggedClone: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null; private originalEvent: HTMLElement | null = null;
constructor(config: CalendarConfig, dateCalculator?: DateCalculator) { constructor(dateCalculator?: DateCalculator) {
this.config = config;
if (!dateCalculator) { if (!dateCalculator) {
DateCalculator.initialize(config); DateCalculator.initialize(calendarConfig);
} }
this.dateCalculator = dateCalculator || new DateCalculator(); this.dateCalculator = dateCalculator || new DateCalculator();
} }
@ -69,7 +67,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}); });
// Handle navigation period change (when slide animation completes) // 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 // Animate all-day height after navigation completes
this.triggerAllDayHeightAnimation(); this.triggerAllDayHeightAnimation();
}); });
@ -146,7 +144,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
* Update clone timestamp based on new position * Update clone timestamp based on new position
*/ */
private updateCloneTimestamp(clone: HTMLElement, snappedY: number): void { private updateCloneTimestamp(clone: HTMLElement, snappedY: number): void {
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const hourHeight = gridSettings.hourHeight; const hourHeight = gridSettings.hourHeight;
const dayStartHour = gridSettings.dayStartHour; const dayStartHour = gridSettings.dayStartHour;
const snapInterval = 15; // TODO: Get from config 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 * Calculate event duration in minutes from element height
*/ */
private getEventDuration(element: HTMLElement): number { private getEventDuration(element: HTMLElement): number {
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const hourHeight = gridSettings.hourHeight; const hourHeight = gridSettings.hourHeight;
// Get height from style or computed // 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 // NOTE: Removed clearEvents() to support sliding animation
// With sliding animation, multiple grid containers exist simultaneously // 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) // 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 // Find columns in the specific container for regular events
const columns = this.getColumns(container); const columns = this.getColumns(container);
@ -448,7 +446,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
const eventsLayer = column.querySelector('swp-events-layer'); const eventsLayer = column.querySelector('swp-events-layer');
if (eventsLayer) { if (eventsLayer) {
columnEvents.forEach(event => { columnEvents.forEach(event => {
this.renderEvent(event, eventsLayer, config); this.renderEvent(event, eventsLayer);
}); });
// Debug: Verify events were actually added // 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 * 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 // Find the calendar header
const calendarHeader = container.querySelector('swp-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'); const eventElement = document.createElement('swp-event');
eventElement.dataset.eventId = event.id; eventElement.dataset.eventId = event.id;
eventElement.dataset.title = event.title; eventElement.dataset.title = event.title;
@ -575,7 +573,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
eventElement.dataset.duration = event.metadata?.duration?.toString() || '60'; eventElement.dataset.duration = event.metadata?.duration?.toString() || '60';
// Calculate position based on time // Calculate position based on time
const position = this.calculateEventPosition(event, config); const position = this.calculateEventPosition(event);
eventElement.style.position = 'absolute'; eventElement.style.position = 'absolute';
eventElement.style.top = `${position.top + 1}px`; eventElement.style.top = `${position.top + 1}px`;
eventElement.style.height = `${position.height - 3}px`; //adjusted so bottom does not cover horizontal time lines. 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); 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 startDate = new Date(event.start);
const endDate = new Date(event.end); const endDate = new Date(event.end);
const gridSettings = config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayStartHour = gridSettings.dayStartHour; const dayStartHour = gridSettings.dayStartHour;
const hourHeight = gridSettings.hourHeight; const hourHeight = gridSettings.hourHeight;
@ -681,8 +679,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
* Date-based event renderer * Date-based event renderer
*/ */
export class DateEventRenderer extends BaseEventRenderer { export class DateEventRenderer extends BaseEventRenderer {
constructor(config: CalendarConfig, dateCalculator?: DateCalculator) { constructor(dateCalculator?: DateCalculator) {
super(config, dateCalculator); super(dateCalculator);
this.setupDragEventListeners(); this.setupDragEventListeners();
} }

View file

@ -46,7 +46,7 @@ export class EventRenderingService {
} }
// Use cached strategy to render events in the specific container // 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 // Emit EVENTS_RENDERED event for filtering system
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, { this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {

View file

@ -1,4 +1,4 @@
import { CalendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes'; import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { HeaderRenderContext } from './HeaderRenderer'; import { HeaderRenderContext } from './HeaderRenderer';
@ -11,14 +11,12 @@ import { DateCalculator } from '../utils/DateCalculator';
* Optimized to reduce redundant DOM operations and improve performance * Optimized to reduce redundant DOM operations and improve performance
*/ */
export class GridRenderer { export class GridRenderer {
private config: CalendarConfig;
private headerEventListener: ((event: Event) => void) | null = null; private headerEventListener: ((event: Event) => void) | null = null;
private cachedGridContainer: HTMLElement | null = null; private cachedGridContainer: HTMLElement | null = null;
private cachedCalendarHeader: HTMLElement | null = null; private cachedCalendarHeader: HTMLElement | null = null;
private cachedTimeAxis: HTMLElement | null = null; private cachedTimeAxis: HTMLElement | null = null;
constructor(config: CalendarConfig) { constructor() {
this.config = config;
} }
/** /**
@ -83,7 +81,7 @@ export class GridRenderer {
private createOptimizedTimeAxis(): HTMLElement { private createOptimizedTimeAxis(): HTMLElement {
const timeAxis = document.createElement('swp-time-axis'); const timeAxis = document.createElement('swp-time-axis');
const timeAxisContent = document.createElement('swp-time-axis-content'); const timeAxisContent = document.createElement('swp-time-axis-content');
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const startHour = gridSettings.dayStartHour; const startHour = gridSettings.dayStartHour;
const endHour = gridSettings.dayEndHour; const endHour = gridSettings.dayEndHour;
@ -146,12 +144,12 @@ export class GridRenderer {
resourceData: ResourceCalendarData | null, resourceData: ResourceCalendarData | null,
view: CalendarView view: CalendarView
): void { ): void {
const calendarType = this.config.getCalendarMode(); const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
const context: HeaderRenderContext = { const context: HeaderRenderContext = {
currentWeek: currentDate, // HeaderRenderer expects currentWeek property currentWeek: currentDate, // HeaderRenderer expects currentWeek property
config: this.config, config: calendarConfig,
resourceData: resourceData resourceData: resourceData
}; };
@ -173,12 +171,12 @@ export class GridRenderer {
resourceData: ResourceCalendarData | null, resourceData: ResourceCalendarData | null,
view: CalendarView view: CalendarView
): void { ): void {
const calendarType = this.config.getCalendarMode(); const calendarType = calendarConfig.getCalendarMode();
const columnRenderer = CalendarTypeFactory.getColumnRenderer(calendarType); const columnRenderer = CalendarTypeFactory.getColumnRenderer(calendarType);
const context: ColumnRenderContext = { const context: ColumnRenderContext = {
currentWeek: currentDate, // ColumnRenderer expects currentWeek property currentWeek: currentDate, // ColumnRenderer expects currentWeek property
config: this.config, config: calendarConfig,
resourceData: resourceData resourceData: resourceData
}; };
@ -260,7 +258,7 @@ export class GridRenderer {
} }
// Get header renderer once and cache // Get header renderer once and cache
const calendarType = this.config.getCalendarMode(); const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
eventBus.emit('header:mouseover', { eventBus.emit('header:mouseover', {

View file

@ -1,4 +1,4 @@
import { CalendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { ResourceCalendarData } from '../types/CalendarTypes'; import { ResourceCalendarData } from '../types/CalendarTypes';
/** /**
@ -6,10 +6,7 @@ import { ResourceCalendarData } from '../types/CalendarTypes';
* Separated from GridManager to follow Single Responsibility Principle * Separated from GridManager to follow Single Responsibility Principle
*/ */
export class GridStyleManager { export class GridStyleManager {
private config: CalendarConfig; constructor() {
constructor(config: CalendarConfig) {
this.config = config;
} }
/** /**
@ -17,9 +14,9 @@ export class GridStyleManager {
*/ */
public updateGridStyles(resourceData: ResourceCalendarData | null = null): void { public updateGridStyles(resourceData: ResourceCalendarData | null = null): void {
const root = document.documentElement; const root = document.documentElement;
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const calendar = document.querySelector('swp-calendar') as HTMLElement; 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 // Set CSS variables for time and grid measurements
this.setTimeVariables(root, gridSettings); this.setTimeVariables(root, gridSettings);
@ -58,8 +55,8 @@ export class GridStyleManager {
if (calendarType === 'resource' && resourceData) { if (calendarType === 'resource' && resourceData) {
return resourceData.resources.length; return resourceData.resources.length;
} else if (calendarType === 'date') { } else if (calendarType === 'date') {
const dateSettings = this.config.getDateViewSettings(); const dateSettings = calendarConfig.getDateViewSettings();
const workWeekSettings = this.config.getWorkWeekSettings(); const workWeekSettings = calendarConfig.getWorkWeekSettings();
switch (dateSettings.period) { switch (dateSettings.period) {
case 'day': 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
} }
/** /**

View file

@ -1,6 +1,6 @@
import { IEventBus } from '../types/CalendarTypes'; import { IEventBus } from '../types/CalendarTypes';
import { CoreEvents } from '../constants/CoreEvents'; import { CoreEvents } from '../constants/CoreEvents';
import { CalendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { DateCalculator } from '../utils/DateCalculator'; import { DateCalculator } from '../utils/DateCalculator';
import { EventRenderingService } from './EventRendererManager'; import { EventRenderingService } from './EventRendererManager';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
@ -12,7 +12,6 @@ import { eventBus } from '../core/EventBus';
*/ */
export class NavigationRenderer { export class NavigationRenderer {
private eventBus: IEventBus; private eventBus: IEventBus;
private config: CalendarConfig;
private dateCalculator: DateCalculator; private dateCalculator: DateCalculator;
private eventRenderer: EventRenderingService; private eventRenderer: EventRenderingService;
@ -20,11 +19,10 @@ export class NavigationRenderer {
private cachedWeekNumberElement: HTMLElement | null = null; private cachedWeekNumberElement: HTMLElement | null = null;
private cachedDateRangeElement: HTMLElement | null = null; private cachedDateRangeElement: HTMLElement | null = null;
constructor(eventBus: IEventBus, config: CalendarConfig, eventRenderer: EventRenderingService) { constructor(eventBus: IEventBus, eventRenderer: EventRenderingService) {
this.eventBus = eventBus; this.eventBus = eventBus;
this.config = config;
this.eventRenderer = eventRenderer; this.eventRenderer = eventRenderer;
DateCalculator.initialize(config); DateCalculator.initialize(calendarConfig);
this.dateCalculator = new DateCalculator(); this.dateCalculator = new DateCalculator();
this.setupEventListeners(); this.setupEventListeners();
} }
@ -61,7 +59,7 @@ export class NavigationRenderer {
* Setup event listeners for DOM updates * Setup event listeners for DOM updates
*/ */
private setupEventListeners(): void { 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 customEvent = event as CustomEvent;
const { weekNumber, dateRange } = customEvent.detail; const { weekNumber, dateRange } = customEvent.detail;
this.updateWeekInfoInDOM(weekNumber, dateRange); this.updateWeekInfoInDOM(weekNumber, dateRange);
@ -201,7 +199,7 @@ export class NavigationRenderer {
}); });
// Always ensure all-day containers exist for all days // 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); headerRenderer.ensureAllDayContainers(header as HTMLElement);
// Add event delegation listener for drag & drop functionality // Add event delegation listener for drag & drop functionality
@ -256,7 +254,7 @@ export class NavigationRenderer {
// Get the header renderer for addToAllDay functionality // Get the header renderer for addToAllDay functionality
const calendarType = this.config.getCalendarMode(); const calendarType = calendarConfig.getCalendarMode();
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType); const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
eventBus.emit('header:mouseover', { eventBus.emit('header:mouseover', {

View file

@ -17,8 +17,8 @@ export class WeekViewStrategy implements ViewStrategy {
constructor() { constructor() {
DateCalculator.initialize(calendarConfig); DateCalculator.initialize(calendarConfig);
this.dateCalculator = new DateCalculator(); this.dateCalculator = new DateCalculator();
this.gridRenderer = new GridRenderer(calendarConfig); this.gridRenderer = new GridRenderer();
this.styleManager = new GridStyleManager(calendarConfig); this.styleManager = new GridStyleManager();
} }
getLayoutConfig(): ViewLayoutConfig { getLayoutConfig(): ViewLayoutConfig {

View file

@ -1,22 +1,16 @@
import { CalendarConfig } from '../core/CalendarConfig.js'; import { calendarConfig } from '../core/CalendarConfig.js';
import { DateCalculator } from './DateCalculator.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 * Focuses on pixel/position calculations while delegating date operations
*/ */
export class PositionUtils { export class PositionUtils {
private config: CalendarConfig;
constructor(config: CalendarConfig) {
this.config = config;
}
/** /**
* Convert minutes to pixels * Convert minutes to pixels
*/ */
public minutesToPixels(minutes: number): number { public static minutesToPixels(minutes: number): number {
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const pixelsPerHour = gridSettings.hourHeight; const pixelsPerHour = gridSettings.hourHeight;
return (minutes / 60) * pixelsPerHour; return (minutes / 60) * pixelsPerHour;
} }
@ -24,8 +18,8 @@ export class PositionUtils {
/** /**
* Convert pixels to minutes * Convert pixels to minutes
*/ */
public pixelsToMinutes(pixels: number): number { public static pixelsToMinutes(pixels: number): number {
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const pixelsPerHour = gridSettings.hourHeight; const pixelsPerHour = gridSettings.hourHeight;
return (pixels / pixelsPerHour) * 60; return (pixels / pixelsPerHour) * 60;
} }
@ -33,33 +27,33 @@ export class PositionUtils {
/** /**
* Convert time (HH:MM) to pixels from day start using DateCalculator * 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 totalMinutes = DateCalculator.timeToMinutes(timeString);
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayStartMinutes = gridSettings.dayStartHour * 60; const dayStartMinutes = gridSettings.dayStartHour * 60;
const minutesFromDayStart = totalMinutes - dayStartMinutes; const minutesFromDayStart = totalMinutes - dayStartMinutes;
return this.minutesToPixels(minutesFromDayStart); return PositionUtils.minutesToPixels(minutesFromDayStart);
} }
/** /**
* Convert Date object to pixels from day start using DateCalculator * 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 totalMinutes = DateCalculator.getMinutesSinceMidnight(date);
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayStartMinutes = gridSettings.dayStartHour * 60; const dayStartMinutes = gridSettings.dayStartHour * 60;
const minutesFromDayStart = totalMinutes - dayStartMinutes; const minutesFromDayStart = totalMinutes - dayStartMinutes;
return this.minutesToPixels(minutesFromDayStart); return PositionUtils.minutesToPixels(minutesFromDayStart);
} }
/** /**
* Convert pixels to time using DateCalculator * Convert pixels to time using DateCalculator
*/ */
public pixelsToTime(pixels: number): string { public static pixelsToTime(pixels: number): string {
const minutes = this.pixelsToMinutes(pixels); const minutes = PositionUtils.pixelsToMinutes(pixels);
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayStartMinutes = gridSettings.dayStartHour * 60; const dayStartMinutes = gridSettings.dayStartHour * 60;
const totalMinutes = dayStartMinutes + minutes; const totalMinutes = dayStartMinutes + minutes;
@ -69,7 +63,7 @@ export class PositionUtils {
/** /**
* Beregn event position og størrelse * 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; top: number;
height: number; height: number;
duration: number; duration: number;
@ -78,19 +72,19 @@ export class PositionUtils {
let endPixels: number; let endPixels: number;
if (typeof startTime === 'string') { if (typeof startTime === 'string') {
startPixels = this.timeToPixels(startTime); startPixels = PositionUtils.timeToPixels(startTime);
} else { } else {
startPixels = this.dateToPixels(startTime); startPixels = PositionUtils.dateToPixels(startTime);
} }
if (typeof endTime === 'string') { if (typeof endTime === 'string') {
endPixels = this.timeToPixels(endTime); endPixels = PositionUtils.timeToPixels(endTime);
} else { } else {
endPixels = this.dateToPixels(endTime); endPixels = PositionUtils.dateToPixels(endTime);
} }
const height = Math.max(endPixels - startPixels, this.getMinimumEventHeight()); const height = Math.max(endPixels - startPixels, PositionUtils.getMinimumEventHeight());
const duration = this.pixelsToMinutes(height); const duration = PositionUtils.pixelsToMinutes(height);
return { return {
top: startPixels, top: startPixels,
@ -102,10 +96,10 @@ export class PositionUtils {
/** /**
* Snap position til grid interval * Snap position til grid interval
*/ */
public snapToGrid(pixels: number): number { public static snapToGrid(pixels: number): number {
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const snapInterval = gridSettings.snapInterval; const snapInterval = gridSettings.snapInterval;
const snapPixels = this.minutesToPixels(snapInterval); const snapPixels = PositionUtils.minutesToPixels(snapInterval);
return Math.round(pixels / snapPixels) * snapPixels; return Math.round(pixels / snapPixels) * snapPixels;
} }
@ -113,9 +107,9 @@ export class PositionUtils {
/** /**
* Snap time to interval using DateCalculator * Snap time to interval using DateCalculator
*/ */
public snapTimeToInterval(timeString: string): string { public static snapTimeToInterval(timeString: string): string {
const totalMinutes = DateCalculator.timeToMinutes(timeString); const totalMinutes = DateCalculator.timeToMinutes(timeString);
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const snapInterval = gridSettings.snapInterval; const snapInterval = gridSettings.snapInterval;
const snappedMinutes = Math.round(totalMinutes / snapInterval) * snapInterval; const snappedMinutes = Math.round(totalMinutes / snapInterval) * snapInterval;
@ -125,7 +119,7 @@ export class PositionUtils {
/** /**
* Beregn kolonne position for overlappende events * 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; left: number;
width: number; width: number;
} { } {
@ -145,14 +139,14 @@ export class PositionUtils {
/** /**
* Check om to events overlapper i tid * Check om to events overlapper i tid
*/ */
public eventsOverlap( public static eventsOverlap(
start1: string | Date, start1: string | Date,
end1: string | Date, end1: string | Date,
start2: string | Date, start2: string | Date,
end2: string | Date end2: string | Date
): boolean { ): boolean {
const pos1 = this.calculateEventPosition(start1, end1); const pos1 = PositionUtils.calculateEventPosition(start1, end1);
const pos2 = this.calculateEventPosition(start2, end2); const pos2 = PositionUtils.calculateEventPosition(start2, end2);
const event1End = pos1.top + pos1.height; const event1End = pos1.top + pos1.height;
const event2End = pos2.top + pos2.height; const event2End = pos2.top + pos2.height;
@ -163,53 +157,53 @@ export class PositionUtils {
/** /**
* Beregn Y position fra mouse/touch koordinat * 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 rect = containerElement.getBoundingClientRect();
const relativeY = clientY - rect.top; const relativeY = clientY - rect.top;
// Snap til grid // Snap til grid
return this.snapToGrid(relativeY); return PositionUtils.snapToGrid(relativeY);
} }
/** /**
* Beregn tid fra mouse/touch koordinat * Beregn tid fra mouse/touch koordinat
*/ */
public getTimeFromCoordinate(clientY: number, containerElement: HTMLElement): string { public static getTimeFromCoordinate(clientY: number, containerElement: HTMLElement): string {
const position = this.getPositionFromCoordinate(clientY, containerElement); const position = PositionUtils.getPositionFromCoordinate(clientY, containerElement);
return this.pixelsToTime(position); return PositionUtils.pixelsToTime(position);
} }
/** /**
* Valider at tid er inden for arbejdstimer * 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 [hours] = timeString.split(':').map(Number);
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
return hours >= gridSettings.workStartHour && hours < gridSettings.workEndHour; return hours >= gridSettings.workStartHour && hours < gridSettings.workEndHour;
} }
/** /**
* Valider at tid er inden for dag grænser * 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 [hours] = timeString.split(':').map(Number);
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
return hours >= gridSettings.dayStartHour && hours < gridSettings.dayEndHour; return hours >= gridSettings.dayStartHour && hours < gridSettings.dayEndHour;
} }
/** /**
* Hent minimum event højde i pixels * Hent minimum event højde i pixels
*/ */
public getMinimumEventHeight(): number { public static getMinimumEventHeight(): number {
// Minimum 15 minutter // Minimum 15 minutter
return this.minutesToPixels(15); return PositionUtils.minutesToPixels(15);
} }
/** /**
* Hent maksimum event højde i pixels (hele dagen) * Hent maksimum event højde i pixels (hele dagen)
*/ */
public getMaximumEventHeight(): number { public static getMaximumEventHeight(): number {
const gridSettings = this.config.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const dayDurationHours = gridSettings.dayEndHour - gridSettings.dayStartHour; const dayDurationHours = gridSettings.dayEndHour - gridSettings.dayStartHour;
return dayDurationHours * gridSettings.hourHeight; return dayDurationHours * gridSettings.hourHeight;
} }
@ -217,14 +211,14 @@ export class PositionUtils {
/** /**
* Beregn total kalender højde * Beregn total kalender højde
*/ */
public getTotalCalendarHeight(): number { public static getTotalCalendarHeight(): number {
return this.getMaximumEventHeight(); return PositionUtils.getMaximumEventHeight();
} }
/** /**
* Convert ISO datetime to time string using DateCalculator * Convert ISO datetime to time string using DateCalculator
*/ */
public isoToTimeString(isoString: string): string { public static isoToTimeString(isoString: string): string {
const date = new Date(isoString); const date = new Date(isoString);
return DateCalculator.formatTime(date); return DateCalculator.formatTime(date);
} }
@ -232,7 +226,7 @@ export class PositionUtils {
/** /**
* Convert time string to ISO datetime using DateCalculator * 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 totalMinutes = DateCalculator.timeToMinutes(timeString);
const hours = Math.floor(totalMinutes / 60); const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60; const minutes = totalMinutes % 60;
@ -246,14 +240,14 @@ export class PositionUtils {
/** /**
* Calculate event duration using DateCalculator * 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); return DateCalculator.getDurationMinutes(startTime, endTime);
} }
/** /**
* Format duration to readable text (Danish) * Format duration to readable text (Danish)
*/ */
public formatDuration(minutes: number): string { public static formatDuration(minutes: number): string {
if (minutes < 60) { if (minutes < 60) {
return `${minutes} min`; return `${minutes} min`;
} }
@ -267,11 +261,4 @@ export class PositionUtils {
return `${hours}t ${remainingMinutes}m`; return `${hours}t ${remainingMinutes}m`;
} }
/**
* Opdater konfiguration
*/
public updateConfig(newConfig: CalendarConfig): void {
this.config = newConfig;
}
} }