Refactor GridManager with new DateColumnDataSource

Introduces DateColumnDataSource to centralize date column generation logic
Simplifies GridManager by delegating date calculations to dedicated data source
Enhances flexibility for different calendar views and date rendering strategies

Improves separation of concerns and makes calendar view management more modular
This commit is contained in:
Janus C. H. Knudsen 2025-11-13 23:35:29 +01:00
parent 284c85b2f8
commit 75a2d4913e
6 changed files with 229 additions and 157 deletions

View file

@ -8,6 +8,9 @@ import { CoreEvents } from '../constants/CoreEvents';
import { CalendarView } from '../types/CalendarTypes';
import { GridRenderer } from '../renderers/GridRenderer';
import { DateService } from '../utils/DateService';
import { DateColumnDataSource } from '../datasources/DateColumnDataSource';
import { Configuration } from '../configurations/CalendarConfig';
import { EventManager } from './EventManager';
/**
* Simplified GridManager focused on coordination, delegates rendering to GridRenderer
@ -18,13 +21,21 @@ export class GridManager {
private currentView: CalendarView = 'week';
private gridRenderer: GridRenderer;
private dateService: DateService;
private config: Configuration;
private dataSource: DateColumnDataSource;
private eventManager: EventManager;
constructor(
gridRenderer: GridRenderer,
dateService: DateService
dateService: DateService,
config: Configuration,
eventManager: EventManager
) {
this.gridRenderer = gridRenderer;
this.dateService = dateService;
this.config = config;
this.eventManager = eventManager;
this.dataSource = new DateColumnDataSource(dateService, config, this.currentDate, this.currentView);
this.init();
}
@ -33,22 +44,6 @@ export class GridManager {
this.subscribeToEvents();
}
/**
* Get the start of the ISO week (Monday) for a given date
*/
private getISOWeekStart(date: Date): Date {
const weekBounds = this.dateService.getWeekBounds(date);
return this.dateService.startOfDay(weekBounds.start);
}
/**
* Get the end of the ISO week (Sunday) for a given date
*/
private getWeekEnd(date: Date): Date {
const weekBounds = this.dateService.getWeekBounds(date);
return this.dateService.endOfDay(weekBounds.end);
}
private findElements(): void {
this.container = document.querySelector('swp-calendar-container');
}
@ -58,14 +53,18 @@ export class GridManager {
eventBus.on(CoreEvents.VIEW_CHANGED, (e: Event) => {
const detail = (e as CustomEvent).detail;
this.currentView = detail.currentView;
this.dataSource.setCurrentView(this.currentView);
this.render();
});
// Listen for navigation events from NavigationButtons
// Listen for navigation events from NavigationManager
// NavigationManager has already created new grid with animation
// GridManager only needs to update state, NOT re-render
eventBus.on(CoreEvents.NAVIGATION_COMPLETED, (e: Event) => {
const detail = (e as CustomEvent).detail;
this.currentDate = detail.newDate;
this.render();
this.dataSource.setCurrentDate(this.currentDate);
// Do NOT call render() - NavigationManager already created new grid
});
// Listen for config changes that affect rendering
@ -88,109 +87,28 @@ export class GridManager {
return;
}
// Delegate to GridRenderer with current view context
// Get dates from datasource - single source of truth
const dates = this.dataSource.getColumns();
// Get events for the period from EventManager
const startDate = dates[0];
const endDate = dates[dates.length - 1];
const events = await this.eventManager.getEventsForPeriod(startDate, endDate);
// Delegate to GridRenderer with dates and events
this.gridRenderer.renderGrid(
this.container,
this.currentDate
this.currentDate,
this.currentView,
dates,
events
);
// Get display dates for current view
const dates = this.getDisplayDates();
// Get layout config based on current view
const layoutConfig = this.getLayoutConfig();
// Emit grid rendered event
eventBus.emit(CoreEvents.GRID_RENDERED, {
container: this.container,
currentDate: this.currentDate,
dates: dates,
layoutConfig: layoutConfig,
columnCount: layoutConfig.columnCount
dates: dates
});
}
/**
* Get current view's display dates
*/
public getDisplayDates(): Date[] {
switch (this.currentView) {
case 'week':
const weekStart = this.getISOWeekStart(this.currentDate);
return this.dateService.getFullWeekDates(weekStart);
case 'month':
return this.getMonthDates(this.currentDate);
case 'day':
return [this.currentDate];
default:
const defaultWeekStart = this.getISOWeekStart(this.currentDate);
return this.dateService.getFullWeekDates(defaultWeekStart);
}
}
/**
* Get layout config for current view
*/
private getLayoutConfig(): { columnCount: number; type: string } {
switch (this.currentView) {
case 'week':
return {
columnCount: 7,
type: 'week'
};
case 'month':
return {
columnCount: 7,
type: 'month'
};
case 'day':
return {
columnCount: 1,
type: 'day'
};
default:
return {
columnCount: 7,
type: 'week'
};
}
}
/**
* Helper method to get month start
*/
private getMonthStart(date: Date): Date {
const year = date.getFullYear();
const month = date.getMonth();
return this.dateService.startOfDay(new Date(year, month, 1));
}
/**
* Helper method to get month end
*/
private getMonthEnd(date: Date): Date {
const nextMonth = this.dateService.addMonths(date, 1);
const firstOfNextMonth = this.getMonthStart(nextMonth);
return this.dateService.endOfDay(this.dateService.addDays(firstOfNextMonth, -1));
}
/**
* Helper method to get all dates in a month
*/
private getMonthDates(date: Date): Date[] {
const dates: Date[] = [];
const monthStart = this.getMonthStart(date);
const monthEnd = this.getMonthEnd(date);
const totalDays = Math.ceil((monthEnd.getTime() - monthStart.getTime()) / (1000 * 60 * 60 * 24)) + 1;
for (let i = 0; i < totalDays; i++) {
dates.push(this.dateService.addDays(monthStart, i));
}
return dates;
}
}

View file

@ -1,16 +1,20 @@
import { IEventBus } from '../types/CalendarTypes';
import { IEventBus, CalendarView } from '../types/CalendarTypes';
import { EventRenderingService } from '../renderers/EventRendererManager';
import { DateService } from '../utils/DateService';
import { CoreEvents } from '../constants/CoreEvents';
import { WeekInfoRenderer } from '../renderers/WeekInfoRenderer';
import { GridRenderer } from '../renderers/GridRenderer';
import { INavButtonClickedEventPayload } from '../types/EventTypes';
import { DateColumnDataSource } from '../datasources/DateColumnDataSource';
import { Configuration } from '../configurations/CalendarConfig';
export class NavigationManager {
private eventBus: IEventBus;
private weekInfoRenderer: WeekInfoRenderer;
private gridRenderer: GridRenderer;
private dateService: DateService;
private config: Configuration;
private dataSource: DateColumnDataSource;
private currentWeek: Date;
private targetWeek: Date;
private animationQueue: number = 0;
@ -20,14 +24,17 @@ export class NavigationManager {
eventRenderer: EventRenderingService,
gridRenderer: GridRenderer,
dateService: DateService,
weekInfoRenderer: WeekInfoRenderer
weekInfoRenderer: WeekInfoRenderer,
config: Configuration
) {
this.eventBus = eventBus;
this.dateService = dateService;
this.weekInfoRenderer = weekInfoRenderer;
this.gridRenderer = gridRenderer;
this.config = config;
this.currentWeek = this.getISOWeekStart(new Date());
this.targetWeek = new Date(this.currentWeek);
this.dataSource = new DateColumnDataSource(dateService, config, this.currentWeek, 'week' as CalendarView);
this.init();
}
@ -184,8 +191,12 @@ export class NavigationManager {
console.log('Calling GridRenderer instead of NavigationRenderer');
console.log('Target week:', targetWeek);
// Update DataSource with target week and get dates
this.dataSource.setCurrentDate(targetWeek);
const dates = this.dataSource.getColumns();
// Always create a fresh container for consistent behavior
newGrid = this.gridRenderer.createNavigationGrid(container, targetWeek);
newGrid = this.gridRenderer.createNavigationGrid(container, dates);
console.groupEnd();