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

@ -15,7 +15,7 @@ export interface IColumnRenderer {
* Context for column rendering
*/
export interface IColumnRenderContext {
currentWeek: Date;
dates: Date[];
config: Configuration;
}
@ -35,24 +35,18 @@ export class DateColumnRenderer implements IColumnRenderer {
}
render(columnContainer: HTMLElement, context: IColumnRenderContext): void {
const { currentWeek, config } = context;
const { dates } = context;
const workWeekSettings = config.getWorkWeekSettings();
const dates = this.dateService.getWorkWeekDates(currentWeek, workWeekSettings.workDays);
const dateSettings = config.dateViewSettings;
const daysToShow = dates.slice(0, dateSettings.weekDays);
daysToShow.forEach((date) => {
dates.forEach((date) => {
const column = document.createElement('swp-day-column');
(column as any).dataset.date = this.dateService.formatISODate(date);
// Apply work hours styling
this.applyWorkHoursToColumn(column, date);
const eventsLayer = document.createElement('swp-events-layer');
column.appendChild(eventsLayer);
columnContainer.appendChild(column);
});
}

View file

@ -1,5 +1,5 @@
import { Configuration } from '../configurations/CalendarConfig';
import { CalendarView } from '../types/CalendarTypes';
import { CalendarView, ICalendarEvent } from '../types/CalendarTypes';
import { IColumnRenderer, IColumnRenderContext } from './ColumnRenderer';
import { eventBus } from '../core/EventBus';
import { DateService } from '../utils/DateService';
@ -104,11 +104,15 @@ export class GridRenderer {
* @param grid - Container element where grid will be rendered
* @param currentDate - Base date for the current view (e.g., any date in the week)
* @param view - Calendar view type (day/week/month)
* @param dates - Array of dates to render as columns
* @param events - All events for the period
*/
public renderGrid(
grid: HTMLElement,
currentDate: Date,
view: CalendarView = 'week'
view: CalendarView = 'week',
dates: Date[] = [],
events: ICalendarEvent[] = []
): void {
if (!grid || !currentDate) {
@ -120,10 +124,10 @@ export class GridRenderer {
// Only clear and rebuild if grid is empty (first render)
if (grid.children.length === 0) {
this.createCompleteGridStructure(grid, currentDate, view);
this.createCompleteGridStructure(grid, currentDate, view, dates, events);
} else {
// Optimized update - only refresh dynamic content
this.updateGridContent(grid, currentDate, view);
this.updateGridContent(grid, currentDate, view, dates, events);
}
}
@ -141,11 +145,14 @@ export class GridRenderer {
* @param grid - Parent container
* @param currentDate - Current view date
* @param view - View type
* @param dates - Array of dates to render
*/
private createCompleteGridStructure(
grid: HTMLElement,
currentDate: Date,
view: CalendarView
view: CalendarView,
dates: Date[],
events: ICalendarEvent[]
): void {
// Create all elements in memory first for better performance
const fragment = document.createDocumentFragment();
@ -160,7 +167,7 @@ export class GridRenderer {
fragment.appendChild(timeAxis);
// Create grid container with caching
const gridContainer = this.createOptimizedGridContainer(currentDate, view);
const gridContainer = this.createOptimizedGridContainer(dates, events);
this.cachedGridContainer = gridContainer;
fragment.appendChild(gridContainer);
@ -207,11 +214,12 @@ export class GridRenderer {
*
* @param currentDate - Current view date
* @param view - View type
* @param dates - Array of dates to render
* @returns Complete grid container element
*/
private createOptimizedGridContainer(
currentDate: Date,
view: CalendarView
dates: Date[],
events: ICalendarEvent[]
): HTMLElement {
const gridContainer = document.createElement('swp-grid-container');
@ -229,7 +237,7 @@ export class GridRenderer {
// Create column container
const columnContainer = document.createElement('swp-day-columns');
this.renderColumnContainer(columnContainer, currentDate, view);
this.renderColumnContainer(columnContainer, dates, events);
timeGrid.appendChild(columnContainer);
scrollableContent.appendChild(timeGrid);
@ -240,27 +248,25 @@ export class GridRenderer {
/**
* Delegates column rendering to the injected ColumnRenderer strategy
* Renders columns by delegating to ColumnRenderer
*
* This is where the Strategy Pattern is applied:
* - DateColumnRenderer iterates dates and creates day columns
* - Could be swapped with other implementations (e.g., ResourceColumnRenderer)
* GridRenderer delegates column creation to ColumnRenderer.
* Event rendering is handled by EventRenderingService listening to GRID_RENDERED.
*
* @param columnContainer - Empty container to populate
* @param currentDate - Current view date
* @param view - View type
* @param dates - Array of dates to render
* @param events - All events for the period (passed through, not used here)
*/
private renderColumnContainer(
columnContainer: HTMLElement,
currentDate: Date,
view: CalendarView
dates: Date[],
events: ICalendarEvent[]
): void {
const context: IColumnRenderContext = {
currentWeek: currentDate, // ColumnRenderer expects currentWeek property
// Delegate to ColumnRenderer
this.columnRenderer.render(columnContainer, {
dates: dates,
config: this.config
};
this.columnRenderer.render(columnContainer, context);
});
}
/**
@ -272,17 +278,21 @@ export class GridRenderer {
* @param grid - Existing grid element
* @param currentDate - New view date
* @param view - View type
* @param dates - Array of dates to render
* @param events - All events for the period
*/
private updateGridContent(
grid: HTMLElement,
currentDate: Date,
view: CalendarView
view: CalendarView,
dates: Date[],
events: ICalendarEvent[]
): void {
// Update column container if needed
const columnContainer = grid.querySelector('swp-day-columns');
if (columnContainer) {
columnContainer.innerHTML = '';
this.renderColumnContainer(columnContainer as HTMLElement, currentDate, view);
this.renderColumnContainer(columnContainer as HTMLElement, dates, events);
}
}
/**
@ -292,14 +302,15 @@ export class GridRenderer {
* Creates a complete grid positioned absolutely for animation.
*
* Note: Positioning is handled by Animation API, not here.
* Events will be rendered by EventRenderingService when GRID_RENDERED emits.
*
* @param parentContainer - Container for the new grid
* @param weekStart - Start date of the new week
* @param dates - Array of dates to render
* @returns New grid element ready for animation
*/
public createNavigationGrid(parentContainer: HTMLElement, weekStart: Date): HTMLElement {
// Use SAME method as initial load - respects workweek settings
const newGrid = this.createOptimizedGridContainer(weekStart, 'week');
public createNavigationGrid(parentContainer: HTMLElement, dates: Date[]): HTMLElement {
// Create grid structure without events (events rendered by EventRenderingService)
const newGrid = this.createOptimizedGridContainer(dates, []);
// Position new grid for animation - NO transform here, let Animation API handle it
newGrid.style.position = 'absolute';