diff --git a/src/datasources/DateColumnDataSource.ts b/src/datasources/DateColumnDataSource.ts index 1e7fd51..42af446 100644 --- a/src/datasources/DateColumnDataSource.ts +++ b/src/datasources/DateColumnDataSource.ts @@ -2,6 +2,7 @@ import { IColumnDataSource, IColumnInfo } from '../types/ColumnDataSource'; import { DateService } from '../utils/DateService'; import { Configuration } from '../configurations/CalendarConfig'; import { CalendarView } from '../types/CalendarTypes'; +import { EventService } from '../storage/events/EventService'; /** * DateColumnDataSource - Provides date-based columns @@ -10,25 +11,31 @@ import { CalendarView } from '../types/CalendarTypes'; * - Current date * - Current view (day/week/month) * - Workweek settings + * + * Also fetches and filters events per column using EventService. */ export class DateColumnDataSource implements IColumnDataSource { private dateService: DateService; private config: Configuration; + private eventService: EventService; private currentDate: Date; private currentView: CalendarView; constructor( dateService: DateService, - config: Configuration + config: Configuration, + eventService: EventService ) { this.dateService = dateService; this.config = config; + this.eventService = eventService; this.currentDate = new Date(); this.currentView = this.config.currentView; } /** - * Get columns (dates) to display + * Get columns (dates) to display with their events + * Each column fetches its own events directly from EventService */ public async getColumns(): Promise { let dates: Date[]; @@ -47,11 +54,19 @@ export class DateColumnDataSource implements IColumnDataSource { dates = this.getWeekDates(); } - // Convert Date[] to IColumnInfo[] - return dates.map(date => ({ - identifier: this.dateService.formatISODate(date), - data: date - })); + // Fetch events for each column directly from EventService + const columnsWithEvents = await Promise.all( + dates.map(async date => ({ + identifier: this.dateService.formatISODate(date), + data: date, + events: await this.eventService.getByDateRange( + this.dateService.startOfDay(date), + this.dateService.endOfDay(date) + ) + })) + ); + + return columnsWithEvents; } /** @@ -61,6 +76,13 @@ export class DateColumnDataSource implements IColumnDataSource { return 'date'; } + /** + * Check if this datasource is in resource mode + */ + public isResource(): boolean { + return false; + } + /** * Update current date */ diff --git a/src/datasources/ResourceColumnDataSource.ts b/src/datasources/ResourceColumnDataSource.ts index 9e340ed..96d0b62 100644 --- a/src/datasources/ResourceColumnDataSource.ts +++ b/src/datasources/ResourceColumnDataSource.ts @@ -1,34 +1,52 @@ import { IColumnDataSource, IColumnInfo } from '../types/ColumnDataSource'; import { CalendarView } from '../types/CalendarTypes'; import { ResourceService } from '../storage/resources/ResourceService'; +import { EventService } from '../storage/events/EventService'; +import { DateService } from '../utils/DateService'; /** * ResourceColumnDataSource - Provides resource-based columns * * In resource mode, columns represent resources (people, rooms, etc.) - * instead of dates. Events are still filtered by current date, - * but grouped by resourceId. + * instead of dates. Events are filtered by current date AND resourceId. */ export class ResourceColumnDataSource implements IColumnDataSource { private resourceService: ResourceService; + private eventService: EventService; + private dateService: DateService; private currentDate: Date; private currentView: CalendarView; - constructor(resourceService: ResourceService) { + constructor( + resourceService: ResourceService, + eventService: EventService, + dateService: DateService + ) { this.resourceService = resourceService; + this.eventService = eventService; + this.dateService = dateService; this.currentDate = new Date(); this.currentView = 'day'; } /** - * Get columns (resources) to display + * Get columns (resources) to display with their events */ public async getColumns(): Promise { const resources = await this.resourceService.getActive(); - return resources.map(resource => ({ - identifier: resource.id, - data: resource - })); + const startDate = this.dateService.startOfDay(this.currentDate); + const endDate = this.dateService.endOfDay(this.currentDate); + + // Fetch events for each resource in parallel + const columnsWithEvents = await Promise.all( + resources.map(async resource => ({ + identifier: resource.id, + data: resource, + events: await this.eventService.getByResourceAndDateRange(resource.id, startDate, endDate) + })) + ); + + return columnsWithEvents; } /** @@ -38,6 +56,13 @@ export class ResourceColumnDataSource implements IColumnDataSource { return 'resource'; } + /** + * Check if this datasource is in resource mode + */ + public isResource(): boolean { + return true; + } + /** * Update current date (for event filtering) */ diff --git a/src/elements/SwpEventElement.ts b/src/elements/SwpEventElement.ts index 4b90898..7705925 100644 --- a/src/elements/SwpEventElement.ts +++ b/src/elements/SwpEventElement.ts @@ -112,19 +112,20 @@ export class SwpEventElement extends BaseSwpEventElement { /** * Update event position during drag - * @param columnDate - The date of the column + * Uses the event's existing date, only updates the time based on Y position * @param snappedY - The Y position in pixels */ - public updatePosition(columnDate: Date, snappedY: number): void { + public updatePosition(snappedY: number): void { // 1. Update visual position this.style.top = `${snappedY + 1}px`; - // 2. Calculate new timestamps + // 2. Calculate new timestamps (keep existing date, only change time) + const existingDate = this.start; const { startMinutes, endMinutes } = this.calculateTimesFromPosition(snappedY); // 3. Update data attributes (triggers attributeChangedCallback) - const startDate = this.dateService.createDateAtTime(columnDate, startMinutes); - let endDate = this.dateService.createDateAtTime(columnDate, endMinutes); + const startDate = this.dateService.createDateAtTime(existingDate, startMinutes); + let endDate = this.dateService.createDateAtTime(existingDate, endMinutes); // Handle cross-midnight events if (endMinutes >= 1440) { diff --git a/src/index.ts b/src/index.ts index 1992d83..5757592 100644 --- a/src/index.ts +++ b/src/index.ts @@ -140,9 +140,8 @@ async function initializeCalendar(): Promise { builder.registerType(MockResourceRepository).as>(); builder.registerType(MockAuditRepository).as>(); - // Calendar mode: 'date' or 'resource' (default to resource) - const calendarMode: 'date' | 'resource' = 'resource'; - + + let calendarMode = 'resource' ; // Register DataSource and HeaderRenderer based on mode if (calendarMode === 'resource') { builder.registerType(ResourceColumnDataSource).as(); @@ -155,6 +154,7 @@ async function initializeCalendar(): Promise { // Register entity services (sync status management) // Open/Closed Principle: Adding new entity only requires adding one line here builder.registerType(EventService).as>(); + builder.registerType(EventService).as(); builder.registerType(BookingService).as>(); builder.registerType(CustomerService).as>(); builder.registerType(ResourceService).as>(); diff --git a/src/managers/GridManager.ts b/src/managers/GridManager.ts index 042da5f..ad02681 100644 --- a/src/managers/GridManager.ts +++ b/src/managers/GridManager.ts @@ -1,6 +1,8 @@ /** * GridManager - Simplified grid manager using centralized GridRenderer * Delegates DOM rendering to GridRenderer, focuses on coordination + * + * Note: Events are now provided by IColumnDataSource (each column has its own events) */ import { eventBus } from '../core/EventBus'; @@ -10,7 +12,6 @@ import { GridRenderer } from '../renderers/GridRenderer'; import { DateService } from '../utils/DateService'; import { IColumnDataSource } from '../types/ColumnDataSource'; import { Configuration } from '../configurations/CalendarConfig'; -import { EventManager } from './EventManager'; /** * Simplified GridManager focused on coordination, delegates rendering to GridRenderer @@ -23,19 +24,16 @@ export class GridManager { private dateService: DateService; private config: Configuration; private dataSource: IColumnDataSource; - private eventManager: EventManager; constructor( gridRenderer: GridRenderer, dateService: DateService, config: Configuration, - eventManager: EventManager, dataSource: IColumnDataSource ) { this.gridRenderer = gridRenderer; this.dateService = dateService; this.config = config; - this.eventManager = eventManager; this.dataSource = dataSource; this.init(); } @@ -82,31 +80,25 @@ export class GridManager { /** * Main render method - delegates to GridRenderer * Note: CSS variables are automatically updated by ConfigManager when config changes + * Note: Events are included in columns from IColumnDataSource */ public async render(): Promise { if (!this.container) { return; } - // Get columns from datasource - single source of truth + // Get columns from datasource - single source of truth (includes events per column) const columns = await this.dataSource.getColumns(); // Set grid columns CSS variable based on actual column count document.documentElement.style.setProperty('--grid-columns', columns.length.toString()); - // Extract dates for EventManager query - const dates = columns.map(col => col.data as Date); - const startDate = dates[0]; - const endDate = dates[dates.length - 1]; - const events = await this.eventManager.getEventsForPeriod(startDate, endDate); - - // Delegate to GridRenderer with columns and events + // Delegate to GridRenderer with columns (events are inside each column) this.gridRenderer.renderGrid( this.container, this.currentDate, this.currentView, - columns, - events + columns ); // Emit grid rendered event diff --git a/src/renderers/EventRenderer.ts b/src/renderers/EventRenderer.ts index 045ffc6..853a982 100644 --- a/src/renderers/EventRenderer.ts +++ b/src/renderers/EventRenderer.ts @@ -1,6 +1,7 @@ // Event rendering strategy interface and implementations import { ICalendarEvent } from '../types/CalendarTypes'; +import { IColumnInfo } from '../types/ColumnDataSource'; import { Configuration } from '../configurations/CalendarConfig'; import { SwpEventElement } from '../elements/SwpEventElement'; import { PositionUtils } from '../utils/PositionUtils'; @@ -12,9 +13,12 @@ import { EventLayoutCoordinator, IGridGroupLayout, IStackedEventLayout } from '. /** * Interface for event rendering strategies + * + * Note: renderEvents now receives columns with pre-filtered events, + * not a flat array of events. Each column contains its own events. */ export interface IEventRenderer { - renderEvents(events: ICalendarEvent[], container: HTMLElement): void; + renderEvents(columns: IColumnInfo[], container: HTMLElement): void; clearEvents(container?: HTMLElement): void; renderSingleColumnEvents?(column: IColumnBounds, events: ICalendarEvent[]): void; handleDragStart?(payload: IDragStartEventPayload): void; @@ -98,28 +102,22 @@ export class DateEventRenderer implements IEventRenderer { /** * Handle drag move event + * Only updates visual position and time - date stays the same */ public handleDragMove(payload: IDragMoveEventPayload): void { - const swpEvent = payload.draggedClone as SwpEventElement; - const columnDate = this.dateService.parseISO(payload.columnBounds!!.identifier); - swpEvent.updatePosition(columnDate, payload.snappedY); + swpEvent.updatePosition(payload.snappedY); } /** * Handle column change during drag + * Only moves the element visually - no data updates here + * Data updates happen on drag:end in EventRenderingService */ public handleColumnChange(payload: IDragColumnChangeEventPayload): void { - const eventsLayer = payload.newColumn.element.querySelector('swp-events-layer'); if (eventsLayer && payload.draggedClone.parentElement !== eventsLayer) { eventsLayer.appendChild(payload.draggedClone); - - // Recalculate timestamps with new column date - const currentTop = parseFloat(payload.draggedClone.style.top) || 0; - const swpEvent = payload.draggedClone as SwpEventElement; - const columnDate = this.dateService.parseISO(payload.newColumn.identifier); - swpEvent.updatePosition(columnDate, currentTop); } } @@ -220,32 +218,36 @@ export class DateEventRenderer implements IEventRenderer { } - renderEvents(events: ICalendarEvent[], container: HTMLElement): void { - // Filter out all-day events - they should be handled by AllDayEventRenderer - const timedEvents = events.filter(event => !event.allDay); + renderEvents(columns: IColumnInfo[], container: HTMLElement): void { + // Find column DOM elements in the container + const columnElements = this.getColumns(container); - // Find columns in the specific container for regular events - const columns = this.getColumns(container); + // Render events for each column using pre-filtered events from IColumnInfo + columns.forEach((columnInfo, index) => { + const columnElement = columnElements[index]; + if (!columnElement) return; - columns.forEach(column => { - const columnEvents = this.getEventsForColumn(column, timedEvents); - const eventsLayer = column.querySelector('swp-events-layer') as HTMLElement; + // Filter out all-day events - they should be handled by AllDayEventRenderer + const timedEvents = columnInfo.events.filter(event => !event.allDay); - if (eventsLayer) { - this.renderColumnEvents(columnEvents, eventsLayer); + const eventsLayer = columnElement.querySelector('swp-events-layer') as HTMLElement; + if (eventsLayer && timedEvents.length > 0) { + this.renderColumnEvents(timedEvents, eventsLayer); } }); } /** * Render events for a single column + * Note: events are already filtered for this column */ public renderSingleColumnEvents(column: IColumnBounds, events: ICalendarEvent[]): void { - const columnEvents = this.getEventsForColumn(column.element, events); + // Filter out all-day events + const timedEvents = events.filter(event => !event.allDay); const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement; - if (eventsLayer) { - this.renderColumnEvents(columnEvents, eventsLayer); + if (eventsLayer && timedEvents.length > 0) { + this.renderColumnEvents(timedEvents, eventsLayer); } } @@ -388,24 +390,4 @@ export class DateEventRenderer implements IEventRenderer { const columns = container.querySelectorAll('swp-day-column'); return Array.from(columns) as HTMLElement[]; } - - protected getEventsForColumn(column: HTMLElement, events: ICalendarEvent[]): ICalendarEvent[] { - const columnId = column.dataset.columnId; - if (!columnId) { - return []; - } - - // Create start and end of day for interval overlap check - // In date-mode, columnId is ISO date string like "2024-11-13" - const columnStart = this.dateService.parseISO(`${columnId}T00:00:00`); - const columnEnd = this.dateService.parseISO(`${columnId}T23:59:59.999`); - - const columnEvents = events.filter(event => { - // Interval overlap: event overlaps with column day if event.start < columnEnd AND event.end > columnStart - const overlaps = event.start < columnEnd && event.end > columnStart; - return overlaps; - }); - - return columnEvents; - } } diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index 7d2f0fd..17862c0 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -1,11 +1,12 @@ -import { IEventBus, ICalendarEvent, IRenderContext } from '../types/CalendarTypes'; +import { IEventBus } from '../types/CalendarTypes'; +import { IColumnInfo, IColumnDataSource } from '../types/ColumnDataSource'; import { CoreEvents } from '../constants/CoreEvents'; import { EventManager } from '../managers/EventManager'; import { IEventRenderer } from './EventRenderer'; import { SwpEventElement } from '../elements/SwpEventElement'; -import { IDragStartEventPayload, IDragMoveEventPayload, IDragEndEventPayload, IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IDragMouseEnterColumnEventPayload, IDragColumnChangeEventPayload, IHeaderReadyEventPayload, IResizeEndEventPayload } from '../types/EventTypes'; +import { IDragStartEventPayload, IDragMoveEventPayload, IDragEndEventPayload, IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IDragMouseEnterColumnEventPayload, IDragColumnChangeEventPayload, IResizeEndEventPayload } from '../types/EventTypes'; import { DateService } from '../utils/DateService'; -import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; + /** * EventRenderingService - Render events i DOM med positionering using Strategy Pattern * Håndterer event positioning og overlap detection @@ -14,6 +15,7 @@ export class EventRenderingService { private eventBus: IEventBus; private eventManager: EventManager; private strategy: IEventRenderer; + private dataSource: IColumnDataSource; private dateService: DateService; private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null; @@ -22,54 +24,18 @@ export class EventRenderingService { eventBus: IEventBus, eventManager: EventManager, strategy: IEventRenderer, + dataSource: IColumnDataSource, dateService: DateService ) { this.eventBus = eventBus; this.eventManager = eventManager; this.strategy = strategy; + this.dataSource = dataSource; this.dateService = dateService; this.setupEventListeners(); } - /** - * Render events in a specific container for a given period - */ - public async renderEvents(context: IRenderContext): Promise { - // Clear existing events in the specific container first - this.strategy.clearEvents(context.container); - - // Get events from EventManager for the period - const events = await this.eventManager.getEventsForPeriod( - context.startDate, - context.endDate - ); - - if (events.length === 0) { - return; - } - - // Filter events by type - only render timed events here - const timedEvents = events.filter(event => !event.allDay); - - console.log('🎯 EventRenderingService: Event filtering', { - totalEvents: events.length, - timedEvents: timedEvents.length, - allDayEvents: events.length - timedEvents.length - }); - - // Render timed events using existing strategy - if (timedEvents.length > 0) { - this.strategy.renderEvents(timedEvents, context.container); - } - - // Emit EVENTS_RENDERED event for filtering system - this.eventBus.emit(CoreEvents.EVENTS_RENDERED, { - events: events, - container: context.container - }); - } - private setupEventListeners(): void { this.eventBus.on(CoreEvents.GRID_RENDERED, (event: Event) => { @@ -89,6 +55,7 @@ export class EventRenderingService { /** * Handle GRID_RENDERED event - render events in the current grid + * Events are now pre-filtered per column by IColumnDataSource */ private handleGridRendered(event: CustomEvent): void { const { container, columns } = event.detail; @@ -97,17 +64,23 @@ export class EventRenderingService { return; } - // Extract dates from columns - const dates = columns.map((col: any) => col.data as Date); + // Render events directly from columns (pre-filtered by IColumnDataSource) + this.renderEventsFromColumns(container, columns); + } - // Calculate startDate and endDate from dates array - const startDate = dates[0]; - const endDate = dates[dates.length - 1]; + /** + * Render events from pre-filtered columns + * Each column already contains its events (filtered by IColumnDataSource) + */ + private renderEventsFromColumns(container: HTMLElement, columns: IColumnInfo[]): void { + this.strategy.clearEvents(container); + this.strategy.renderEvents(columns, container); - this.renderEvents({ - container, - startDate, - endDate + // Emit EVENTS_RENDERED for filtering system + const allEvents = columns.flatMap(col => col.events); + this.eventBus.emit(CoreEvents.EVENTS_RENDERED, { + events: allEvents, + container: container }); } @@ -166,29 +139,42 @@ export class EventRenderingService { private setupDragEndListener(): void { this.eventBus.on('drag:end', async (event: Event) => { - - const { originalElement, draggedClone, originalSourceColumn, finalPosition, target } = (event as CustomEvent).detail; + const { originalElement, draggedClone, finalPosition, target } = (event as CustomEvent).detail; const finalColumn = finalPosition.column; const finalY = finalPosition.snappedY; - let element = draggedClone as SwpEventElement; - // Only handle day column drops for EventRenderer + // Only handle day column drops if (target === 'swp-day-column' && finalColumn) { + const element = draggedClone as SwpEventElement; if (originalElement && draggedClone && this.strategy.handleDragEnd) { this.strategy.handleDragEnd(originalElement, draggedClone, finalColumn, finalY); } - await this.eventManager.updateEvent(element.eventId, { + // Build update payload based on mode + const updatePayload: { start: Date; end: Date; allDay: boolean; resourceId?: string } = { start: element.start, end: element.end, allDay: false - }); + }; - // Re-render affected columns for stacking/grouping (now with updated data) - await this.reRenderAffectedColumns(originalSourceColumn, finalColumn); + if (this.dataSource.isResource()) { + // Resource mode: update resourceId, keep existing date + updatePayload.resourceId = finalColumn.identifier; + } else { + // Date mode: update date from column, keep existing time + const newDate = this.dateService.parseISO(finalColumn.identifier); + const startTimeMinutes = this.dateService.getMinutesSinceMidnight(element.start); + const endTimeMinutes = this.dateService.getMinutesSinceMidnight(element.end); + updatePayload.start = this.dateService.createDateAtTime(newDate, startTimeMinutes); + updatePayload.end = this.dateService.createDateAtTime(newDate, endTimeMinutes); + } + + await this.eventManager.updateEvent(element.eventId, updatePayload); + + // Trigger full refresh to re-render with updated data + this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {}); } - }); } @@ -252,27 +238,14 @@ export class EventRenderingService { this.eventBus.on('resize:end', async (event: Event) => { const { eventId, element } = (event as CustomEvent).detail; - // Update event data in EventManager with new end time from resized element const swpEvent = element as SwpEventElement; - const newStart = swpEvent.start; - const newEnd = swpEvent.end; - await this.eventManager.updateEvent(eventId, { - start: newStart, - end: newEnd + start: swpEvent.start, + end: swpEvent.end }); - console.log('📝 EventRendererManager: Updated event after resize', { - eventId, - newStart, - newEnd - }); - - const dateIdentifier = newStart.toISOString().split('T')[0]; - let columnBounds = ColumnDetectionUtils.getColumnBoundsByIdentifier(dateIdentifier); - if (columnBounds) - await this.renderSingleColumn(columnBounds); - + // Trigger full refresh to re-render with updated data + this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {}); }); } @@ -286,68 +259,6 @@ export class EventRenderingService { } - /** - * Re-render affected columns after drag to recalculate stacking/grouping - */ - private async reRenderAffectedColumns(originalSourceColumn: IColumnBounds | null, targetColumn: IColumnBounds | null): Promise { - // Re-render original source column if exists - if (originalSourceColumn) { - await this.renderSingleColumn(originalSourceColumn); - } - - // Re-render target column if exists and different from source - if (targetColumn && targetColumn.identifier !== originalSourceColumn?.identifier) { - await this.renderSingleColumn(targetColumn); - } - } - - /** - * Clear events in a single column's events layer - */ - private clearColumnEvents(eventsLayer: HTMLElement): void { - const existingEvents = eventsLayer.querySelectorAll('swp-event'); - const existingGroups = eventsLayer.querySelectorAll('swp-event-group'); - - existingEvents.forEach(event => event.remove()); - existingGroups.forEach(group => group.remove()); - } - - /** - * Render events for a single column - */ - private async renderSingleColumn(column: IColumnBounds): Promise { - // Get events for just this column's date - const dateString = column.identifier; - const columnStart = this.dateService.parseISO(`${dateString}T00:00:00`); - const columnEnd = this.dateService.parseISO(`${dateString}T23:59:59.999`); - - // Get events from EventManager for this single date - const events = await this.eventManager.getEventsForPeriod(columnStart, columnEnd); - - // Filter to timed events only - const timedEvents = events.filter(event => !event.allDay); - - // Get events layer within this specific column - const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement; - if (!eventsLayer) { - console.warn('EventRendererManager: Events layer not found in column'); - return; - } - - // Clear only this column's events - this.clearColumnEvents(eventsLayer); - - // Render events for this column using strategy - if (this.strategy.renderSingleColumnEvents) { - this.strategy.renderSingleColumnEvents(column, timedEvents); - } - - console.log('🔄 EventRendererManager: Re-rendered single column', { - columnDate: column.identifier, - eventsCount: timedEvents.length - }); - } - private clearEvents(container?: HTMLElement): void { this.strategy.clearEvents(container); } diff --git a/src/renderers/GridRenderer.ts b/src/renderers/GridRenderer.ts index 3929c49..81bc324 100644 --- a/src/renderers/GridRenderer.ts +++ b/src/renderers/GridRenderer.ts @@ -1,5 +1,5 @@ import { Configuration } from '../configurations/CalendarConfig'; -import { CalendarView, ICalendarEvent } from '../types/CalendarTypes'; +import { CalendarView } from '../types/CalendarTypes'; import { IColumnRenderer, IColumnRenderContext } from './ColumnRenderer'; import { eventBus } from '../core/EventBus'; import { DateService } from '../utils/DateService'; @@ -105,15 +105,13 @@ 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 + * @param columns - Array of columns to render (each column contains its events) */ public renderGrid( grid: HTMLElement, currentDate: Date, view: CalendarView = 'week', - columns: IColumnInfo[] = [], - events: ICalendarEvent[] = [] + columns: IColumnInfo[] = [] ): void { if (!grid || !currentDate) { @@ -125,10 +123,10 @@ export class GridRenderer { // Only clear and rebuild if grid is empty (first render) if (grid.children.length === 0) { - this.createCompleteGridStructure(grid, currentDate, view, columns, events); + this.createCompleteGridStructure(grid, currentDate, view, columns); } else { // Optimized update - only refresh dynamic content - this.updateGridContent(grid, currentDate, view, columns, events); + this.updateGridContent(grid, currentDate, view, columns); } } @@ -146,14 +144,13 @@ export class GridRenderer { * @param grid - Parent container * @param currentDate - Current view date * @param view - View type - * @param dates - Array of dates to render + * @param columns - Array of columns to render (each column contains its events) */ private createCompleteGridStructure( grid: HTMLElement, currentDate: Date, view: CalendarView, - columns: IColumnInfo[], - events: ICalendarEvent[] + columns: IColumnInfo[] ): void { // Create all elements in memory first for better performance const fragment = document.createDocumentFragment(); @@ -168,7 +165,7 @@ export class GridRenderer { fragment.appendChild(timeAxis); // Create grid container with caching - const gridContainer = this.createOptimizedGridContainer(columns, events); + const gridContainer = this.createOptimizedGridContainer(columns); this.cachedGridContainer = gridContainer; fragment.appendChild(gridContainer); @@ -213,14 +210,11 @@ export class GridRenderer { * - Time grid (grid lines + day columns) - structure created here * - Column container - created here, populated by ColumnRenderer * - * @param currentDate - Current view date - * @param view - View type - * @param dates - Array of dates to render + * @param columns - Array of columns to render (each column contains its events) * @returns Complete grid container element */ private createOptimizedGridContainer( - columns: IColumnInfo[], - events: ICalendarEvent[] + columns: IColumnInfo[] ): HTMLElement { const gridContainer = document.createElement('swp-grid-container'); @@ -238,7 +232,7 @@ export class GridRenderer { // Create column container const columnContainer = document.createElement('swp-day-columns'); - this.renderColumnContainer(columnContainer, columns, events); + this.renderColumnContainer(columnContainer, columns); timeGrid.appendChild(columnContainer); scrollableContent.appendChild(timeGrid); @@ -255,13 +249,11 @@ export class GridRenderer { * Event rendering is handled by EventRenderingService listening to GRID_RENDERED. * * @param columnContainer - Empty container to populate - * @param dates - Array of dates to render - * @param events - All events for the period (passed through, not used here) + * @param columns - Array of columns to render (each column contains its events) */ private renderColumnContainer( columnContainer: HTMLElement, - columns: IColumnInfo[], - events: ICalendarEvent[] + columns: IColumnInfo[] ): void { // Delegate to ColumnRenderer this.columnRenderer.render(columnContainer, { @@ -279,21 +271,19 @@ 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 + * @param columns - Array of columns to render (each column contains its events) */ private updateGridContent( grid: HTMLElement, currentDate: Date, view: CalendarView, - columns: IColumnInfo[], - events: ICalendarEvent[] + columns: IColumnInfo[] ): void { // Update column container if needed const columnContainer = grid.querySelector('swp-day-columns'); if (columnContainer) { columnContainer.innerHTML = ''; - this.renderColumnContainer(columnContainer as HTMLElement, columns, events); + this.renderColumnContainer(columnContainer as HTMLElement, columns); } } /** @@ -310,8 +300,8 @@ export class GridRenderer { * @returns New grid element ready for animation */ public createNavigationGrid(parentContainer: HTMLElement, columns: IColumnInfo[]): HTMLElement { - // Create grid structure without events (events rendered by EventRenderingService) - const newGrid = this.createOptimizedGridContainer(columns, []); + // Create grid structure (events are in columns, rendered by EventRenderingService) + const newGrid = this.createOptimizedGridContainer(columns); // Position new grid for animation - NO transform here, let Animation API handle it newGrid.style.position = 'absolute'; diff --git a/src/types/ColumnDataSource.ts b/src/types/ColumnDataSource.ts index 1b38a7d..6ecb563 100644 --- a/src/types/ColumnDataSource.ts +++ b/src/types/ColumnDataSource.ts @@ -1,13 +1,14 @@ import { IResource } from './ResourceTypes'; -import { CalendarView } from './CalendarTypes'; +import { CalendarView, ICalendarEvent } from './CalendarTypes'; /** * Column information container * Contains both identifier and actual data for a column */ export interface IColumnInfo { - identifier: string; // "2024-11-13" (date mode) or "person-1" (resource mode) - data: Date | IResource; // Date for date-mode, IResource for resource-mode + identifier: string; // "2024-11-13" (date mode) or "person-1" (resource mode) + data: Date | IResource; // Date for date-mode, IResource for resource-mode + events: ICalendarEvent[]; // Events for this column (pre-filtered by datasource) } /** @@ -28,6 +29,11 @@ export interface IColumnDataSource { */ getType(): 'date' | 'resource'; + /** + * Check if this datasource is in resource mode + */ + isResource(): boolean; + /** * Update the current date for column calculations * @param date - The new current date