Refactor event rendering with column-based event management
Improves event rendering by integrating event filtering directly into column data sources Key changes: - Moves event filtering responsibility to IColumnDataSource - Simplifies event rendering pipeline by pre-filtering events per column - Supports both date and resource-based calendar modes - Enhances drag and drop event update mechanism Optimizes calendar rendering performance and flexibility
This commit is contained in:
parent
eeaeddeef8
commit
17909696ed
9 changed files with 179 additions and 250 deletions
|
|
@ -2,6 +2,7 @@ import { IColumnDataSource, IColumnInfo } from '../types/ColumnDataSource';
|
||||||
import { DateService } from '../utils/DateService';
|
import { DateService } from '../utils/DateService';
|
||||||
import { Configuration } from '../configurations/CalendarConfig';
|
import { Configuration } from '../configurations/CalendarConfig';
|
||||||
import { CalendarView } from '../types/CalendarTypes';
|
import { CalendarView } from '../types/CalendarTypes';
|
||||||
|
import { EventService } from '../storage/events/EventService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DateColumnDataSource - Provides date-based columns
|
* DateColumnDataSource - Provides date-based columns
|
||||||
|
|
@ -10,25 +11,31 @@ import { CalendarView } from '../types/CalendarTypes';
|
||||||
* - Current date
|
* - Current date
|
||||||
* - Current view (day/week/month)
|
* - Current view (day/week/month)
|
||||||
* - Workweek settings
|
* - Workweek settings
|
||||||
|
*
|
||||||
|
* Also fetches and filters events per column using EventService.
|
||||||
*/
|
*/
|
||||||
export class DateColumnDataSource implements IColumnDataSource {
|
export class DateColumnDataSource implements IColumnDataSource {
|
||||||
private dateService: DateService;
|
private dateService: DateService;
|
||||||
private config: Configuration;
|
private config: Configuration;
|
||||||
|
private eventService: EventService;
|
||||||
private currentDate: Date;
|
private currentDate: Date;
|
||||||
private currentView: CalendarView;
|
private currentView: CalendarView;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
dateService: DateService,
|
dateService: DateService,
|
||||||
config: Configuration
|
config: Configuration,
|
||||||
|
eventService: EventService
|
||||||
) {
|
) {
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.eventService = eventService;
|
||||||
this.currentDate = new Date();
|
this.currentDate = new Date();
|
||||||
this.currentView = this.config.currentView;
|
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<IColumnInfo[]> {
|
public async getColumns(): Promise<IColumnInfo[]> {
|
||||||
let dates: Date[];
|
let dates: Date[];
|
||||||
|
|
@ -47,11 +54,19 @@ export class DateColumnDataSource implements IColumnDataSource {
|
||||||
dates = this.getWeekDates();
|
dates = this.getWeekDates();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Date[] to IColumnInfo[]
|
// Fetch events for each column directly from EventService
|
||||||
return dates.map(date => ({
|
const columnsWithEvents = await Promise.all(
|
||||||
|
dates.map(async date => ({
|
||||||
identifier: this.dateService.formatISODate(date),
|
identifier: this.dateService.formatISODate(date),
|
||||||
data: 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';
|
return 'date';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this datasource is in resource mode
|
||||||
|
*/
|
||||||
|
public isResource(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update current date
|
* Update current date
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,52 @@
|
||||||
import { IColumnDataSource, IColumnInfo } from '../types/ColumnDataSource';
|
import { IColumnDataSource, IColumnInfo } from '../types/ColumnDataSource';
|
||||||
import { CalendarView } from '../types/CalendarTypes';
|
import { CalendarView } from '../types/CalendarTypes';
|
||||||
import { ResourceService } from '../storage/resources/ResourceService';
|
import { ResourceService } from '../storage/resources/ResourceService';
|
||||||
|
import { EventService } from '../storage/events/EventService';
|
||||||
|
import { DateService } from '../utils/DateService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResourceColumnDataSource - Provides resource-based columns
|
* ResourceColumnDataSource - Provides resource-based columns
|
||||||
*
|
*
|
||||||
* In resource mode, columns represent resources (people, rooms, etc.)
|
* In resource mode, columns represent resources (people, rooms, etc.)
|
||||||
* instead of dates. Events are still filtered by current date,
|
* instead of dates. Events are filtered by current date AND resourceId.
|
||||||
* but grouped by resourceId.
|
|
||||||
*/
|
*/
|
||||||
export class ResourceColumnDataSource implements IColumnDataSource {
|
export class ResourceColumnDataSource implements IColumnDataSource {
|
||||||
private resourceService: ResourceService;
|
private resourceService: ResourceService;
|
||||||
|
private eventService: EventService;
|
||||||
|
private dateService: DateService;
|
||||||
private currentDate: Date;
|
private currentDate: Date;
|
||||||
private currentView: CalendarView;
|
private currentView: CalendarView;
|
||||||
|
|
||||||
constructor(resourceService: ResourceService) {
|
constructor(
|
||||||
|
resourceService: ResourceService,
|
||||||
|
eventService: EventService,
|
||||||
|
dateService: DateService
|
||||||
|
) {
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
|
this.eventService = eventService;
|
||||||
|
this.dateService = dateService;
|
||||||
this.currentDate = new Date();
|
this.currentDate = new Date();
|
||||||
this.currentView = 'day';
|
this.currentView = 'day';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get columns (resources) to display
|
* Get columns (resources) to display with their events
|
||||||
*/
|
*/
|
||||||
public async getColumns(): Promise<IColumnInfo[]> {
|
public async getColumns(): Promise<IColumnInfo[]> {
|
||||||
const resources = await this.resourceService.getActive();
|
const resources = await this.resourceService.getActive();
|
||||||
return resources.map(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,
|
identifier: resource.id,
|
||||||
data: resource
|
data: resource,
|
||||||
}));
|
events: await this.eventService.getByResourceAndDateRange(resource.id, startDate, endDate)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
return columnsWithEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,6 +56,13 @@ export class ResourceColumnDataSource implements IColumnDataSource {
|
||||||
return 'resource';
|
return 'resource';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this datasource is in resource mode
|
||||||
|
*/
|
||||||
|
public isResource(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update current date (for event filtering)
|
* Update current date (for event filtering)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -112,19 +112,20 @@ export class SwpEventElement extends BaseSwpEventElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update event position during drag
|
* 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
|
* @param snappedY - The Y position in pixels
|
||||||
*/
|
*/
|
||||||
public updatePosition(columnDate: Date, snappedY: number): void {
|
public updatePosition(snappedY: number): void {
|
||||||
// 1. Update visual position
|
// 1. Update visual position
|
||||||
this.style.top = `${snappedY + 1}px`;
|
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);
|
const { startMinutes, endMinutes } = this.calculateTimesFromPosition(snappedY);
|
||||||
|
|
||||||
// 3. Update data attributes (triggers attributeChangedCallback)
|
// 3. Update data attributes (triggers attributeChangedCallback)
|
||||||
const startDate = this.dateService.createDateAtTime(columnDate, startMinutes);
|
const startDate = this.dateService.createDateAtTime(existingDate, startMinutes);
|
||||||
let endDate = this.dateService.createDateAtTime(columnDate, endMinutes);
|
let endDate = this.dateService.createDateAtTime(existingDate, endMinutes);
|
||||||
|
|
||||||
// Handle cross-midnight events
|
// Handle cross-midnight events
|
||||||
if (endMinutes >= 1440) {
|
if (endMinutes >= 1440) {
|
||||||
|
|
|
||||||
|
|
@ -140,9 +140,8 @@ async function initializeCalendar(): Promise<void> {
|
||||||
builder.registerType(MockResourceRepository).as<IApiRepository<IResource>>();
|
builder.registerType(MockResourceRepository).as<IApiRepository<IResource>>();
|
||||||
builder.registerType(MockAuditRepository).as<IApiRepository<IAuditEntry>>();
|
builder.registerType(MockAuditRepository).as<IApiRepository<IAuditEntry>>();
|
||||||
|
|
||||||
// Calendar mode: 'date' or 'resource' (default to resource)
|
|
||||||
const calendarMode: 'date' | 'resource' = 'resource';
|
|
||||||
|
|
||||||
|
let calendarMode = 'resource' ;
|
||||||
// Register DataSource and HeaderRenderer based on mode
|
// Register DataSource and HeaderRenderer based on mode
|
||||||
if (calendarMode === 'resource') {
|
if (calendarMode === 'resource') {
|
||||||
builder.registerType(ResourceColumnDataSource).as<IColumnDataSource>();
|
builder.registerType(ResourceColumnDataSource).as<IColumnDataSource>();
|
||||||
|
|
@ -155,6 +154,7 @@ async function initializeCalendar(): Promise<void> {
|
||||||
// Register entity services (sync status management)
|
// Register entity services (sync status management)
|
||||||
// Open/Closed Principle: Adding new entity only requires adding one line here
|
// Open/Closed Principle: Adding new entity only requires adding one line here
|
||||||
builder.registerType(EventService).as<IEntityService<ICalendarEvent>>();
|
builder.registerType(EventService).as<IEntityService<ICalendarEvent>>();
|
||||||
|
builder.registerType(EventService).as<EventService>();
|
||||||
builder.registerType(BookingService).as<IEntityService<IBooking>>();
|
builder.registerType(BookingService).as<IEntityService<IBooking>>();
|
||||||
builder.registerType(CustomerService).as<IEntityService<ICustomer>>();
|
builder.registerType(CustomerService).as<IEntityService<ICustomer>>();
|
||||||
builder.registerType(ResourceService).as<IEntityService<IResource>>();
|
builder.registerType(ResourceService).as<IEntityService<IResource>>();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* GridManager - Simplified grid manager using centralized GridRenderer
|
* GridManager - Simplified grid manager using centralized GridRenderer
|
||||||
* Delegates DOM rendering to GridRenderer, focuses on coordination
|
* 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';
|
import { eventBus } from '../core/EventBus';
|
||||||
|
|
@ -10,7 +12,6 @@ import { GridRenderer } from '../renderers/GridRenderer';
|
||||||
import { DateService } from '../utils/DateService';
|
import { DateService } from '../utils/DateService';
|
||||||
import { IColumnDataSource } from '../types/ColumnDataSource';
|
import { IColumnDataSource } from '../types/ColumnDataSource';
|
||||||
import { Configuration } from '../configurations/CalendarConfig';
|
import { Configuration } from '../configurations/CalendarConfig';
|
||||||
import { EventManager } from './EventManager';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplified GridManager focused on coordination, delegates rendering to GridRenderer
|
* Simplified GridManager focused on coordination, delegates rendering to GridRenderer
|
||||||
|
|
@ -23,19 +24,16 @@ export class GridManager {
|
||||||
private dateService: DateService;
|
private dateService: DateService;
|
||||||
private config: Configuration;
|
private config: Configuration;
|
||||||
private dataSource: IColumnDataSource;
|
private dataSource: IColumnDataSource;
|
||||||
private eventManager: EventManager;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
gridRenderer: GridRenderer,
|
gridRenderer: GridRenderer,
|
||||||
dateService: DateService,
|
dateService: DateService,
|
||||||
config: Configuration,
|
config: Configuration,
|
||||||
eventManager: EventManager,
|
|
||||||
dataSource: IColumnDataSource
|
dataSource: IColumnDataSource
|
||||||
) {
|
) {
|
||||||
this.gridRenderer = gridRenderer;
|
this.gridRenderer = gridRenderer;
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.eventManager = eventManager;
|
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
@ -82,31 +80,25 @@ export class GridManager {
|
||||||
/**
|
/**
|
||||||
* Main render method - delegates to GridRenderer
|
* Main render method - delegates to GridRenderer
|
||||||
* Note: CSS variables are automatically updated by ConfigManager when config changes
|
* Note: CSS variables are automatically updated by ConfigManager when config changes
|
||||||
|
* Note: Events are included in columns from IColumnDataSource
|
||||||
*/
|
*/
|
||||||
public async render(): Promise<void> {
|
public async render(): Promise<void> {
|
||||||
if (!this.container) {
|
if (!this.container) {
|
||||||
return;
|
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();
|
const columns = await this.dataSource.getColumns();
|
||||||
|
|
||||||
// Set grid columns CSS variable based on actual column count
|
// Set grid columns CSS variable based on actual column count
|
||||||
document.documentElement.style.setProperty('--grid-columns', columns.length.toString());
|
document.documentElement.style.setProperty('--grid-columns', columns.length.toString());
|
||||||
|
|
||||||
// Extract dates for EventManager query
|
// Delegate to GridRenderer with columns (events are inside each column)
|
||||||
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
|
|
||||||
this.gridRenderer.renderGrid(
|
this.gridRenderer.renderGrid(
|
||||||
this.container,
|
this.container,
|
||||||
this.currentDate,
|
this.currentDate,
|
||||||
this.currentView,
|
this.currentView,
|
||||||
columns,
|
columns
|
||||||
events
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Emit grid rendered event
|
// Emit grid rendered event
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Event rendering strategy interface and implementations
|
// Event rendering strategy interface and implementations
|
||||||
|
|
||||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||||
|
import { IColumnInfo } from '../types/ColumnDataSource';
|
||||||
import { Configuration } from '../configurations/CalendarConfig';
|
import { Configuration } from '../configurations/CalendarConfig';
|
||||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||||
import { PositionUtils } from '../utils/PositionUtils';
|
import { PositionUtils } from '../utils/PositionUtils';
|
||||||
|
|
@ -12,9 +13,12 @@ import { EventLayoutCoordinator, IGridGroupLayout, IStackedEventLayout } from '.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for event rendering strategies
|
* 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 {
|
export interface IEventRenderer {
|
||||||
renderEvents(events: ICalendarEvent[], container: HTMLElement): void;
|
renderEvents(columns: IColumnInfo[], container: HTMLElement): void;
|
||||||
clearEvents(container?: HTMLElement): void;
|
clearEvents(container?: HTMLElement): void;
|
||||||
renderSingleColumnEvents?(column: IColumnBounds, events: ICalendarEvent[]): void;
|
renderSingleColumnEvents?(column: IColumnBounds, events: ICalendarEvent[]): void;
|
||||||
handleDragStart?(payload: IDragStartEventPayload): void;
|
handleDragStart?(payload: IDragStartEventPayload): void;
|
||||||
|
|
@ -98,28 +102,22 @@ export class DateEventRenderer implements IEventRenderer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle drag move event
|
* Handle drag move event
|
||||||
|
* Only updates visual position and time - date stays the same
|
||||||
*/
|
*/
|
||||||
public handleDragMove(payload: IDragMoveEventPayload): void {
|
public handleDragMove(payload: IDragMoveEventPayload): void {
|
||||||
|
|
||||||
const swpEvent = payload.draggedClone as SwpEventElement;
|
const swpEvent = payload.draggedClone as SwpEventElement;
|
||||||
const columnDate = this.dateService.parseISO(payload.columnBounds!!.identifier);
|
swpEvent.updatePosition(payload.snappedY);
|
||||||
swpEvent.updatePosition(columnDate, payload.snappedY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle column change during drag
|
* 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 {
|
public handleColumnChange(payload: IDragColumnChangeEventPayload): void {
|
||||||
|
|
||||||
const eventsLayer = payload.newColumn.element.querySelector('swp-events-layer');
|
const eventsLayer = payload.newColumn.element.querySelector('swp-events-layer');
|
||||||
if (eventsLayer && payload.draggedClone.parentElement !== eventsLayer) {
|
if (eventsLayer && payload.draggedClone.parentElement !== eventsLayer) {
|
||||||
eventsLayer.appendChild(payload.draggedClone);
|
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 {
|
renderEvents(columns: IColumnInfo[], container: HTMLElement): void {
|
||||||
|
// Find column DOM elements in the container
|
||||||
|
const columnElements = 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;
|
||||||
|
|
||||||
// Filter out all-day events - they should be handled by AllDayEventRenderer
|
// Filter out all-day events - they should be handled by AllDayEventRenderer
|
||||||
const timedEvents = events.filter(event => !event.allDay);
|
const timedEvents = columnInfo.events.filter(event => !event.allDay);
|
||||||
|
|
||||||
// Find columns in the specific container for regular events
|
const eventsLayer = columnElement.querySelector('swp-events-layer') as HTMLElement;
|
||||||
const columns = this.getColumns(container);
|
if (eventsLayer && timedEvents.length > 0) {
|
||||||
|
this.renderColumnEvents(timedEvents, eventsLayer);
|
||||||
columns.forEach(column => {
|
|
||||||
const columnEvents = this.getEventsForColumn(column, timedEvents);
|
|
||||||
const eventsLayer = column.querySelector('swp-events-layer') as HTMLElement;
|
|
||||||
|
|
||||||
if (eventsLayer) {
|
|
||||||
this.renderColumnEvents(columnEvents, eventsLayer);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render events for a single column
|
* Render events for a single column
|
||||||
|
* Note: events are already filtered for this column
|
||||||
*/
|
*/
|
||||||
public renderSingleColumnEvents(column: IColumnBounds, events: ICalendarEvent[]): void {
|
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;
|
const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement;
|
||||||
|
|
||||||
if (eventsLayer) {
|
if (eventsLayer && timedEvents.length > 0) {
|
||||||
this.renderColumnEvents(columnEvents, eventsLayer);
|
this.renderColumnEvents(timedEvents, eventsLayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,24 +390,4 @@ export class DateEventRenderer implements IEventRenderer {
|
||||||
const columns = container.querySelectorAll('swp-day-column');
|
const columns = container.querySelectorAll('swp-day-column');
|
||||||
return Array.from(columns) as HTMLElement[];
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { CoreEvents } from '../constants/CoreEvents';
|
||||||
import { EventManager } from '../managers/EventManager';
|
import { EventManager } from '../managers/EventManager';
|
||||||
import { IEventRenderer } from './EventRenderer';
|
import { IEventRenderer } from './EventRenderer';
|
||||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
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 { DateService } from '../utils/DateService';
|
||||||
import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
|
||||||
/**
|
/**
|
||||||
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
||||||
* Håndterer event positioning og overlap detection
|
* Håndterer event positioning og overlap detection
|
||||||
|
|
@ -14,6 +15,7 @@ export class EventRenderingService {
|
||||||
private eventBus: IEventBus;
|
private eventBus: IEventBus;
|
||||||
private eventManager: EventManager;
|
private eventManager: EventManager;
|
||||||
private strategy: IEventRenderer;
|
private strategy: IEventRenderer;
|
||||||
|
private dataSource: IColumnDataSource;
|
||||||
private dateService: DateService;
|
private dateService: DateService;
|
||||||
|
|
||||||
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
|
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
|
||||||
|
|
@ -22,54 +24,18 @@ export class EventRenderingService {
|
||||||
eventBus: IEventBus,
|
eventBus: IEventBus,
|
||||||
eventManager: EventManager,
|
eventManager: EventManager,
|
||||||
strategy: IEventRenderer,
|
strategy: IEventRenderer,
|
||||||
|
dataSource: IColumnDataSource,
|
||||||
dateService: DateService
|
dateService: DateService
|
||||||
) {
|
) {
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.eventManager = eventManager;
|
this.eventManager = eventManager;
|
||||||
this.strategy = strategy;
|
this.strategy = strategy;
|
||||||
|
this.dataSource = dataSource;
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
|
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render events in a specific container for a given period
|
|
||||||
*/
|
|
||||||
public async renderEvents(context: IRenderContext): Promise<void> {
|
|
||||||
// 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 {
|
private setupEventListeners(): void {
|
||||||
|
|
||||||
this.eventBus.on(CoreEvents.GRID_RENDERED, (event: Event) => {
|
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
|
* Handle GRID_RENDERED event - render events in the current grid
|
||||||
|
* Events are now pre-filtered per column by IColumnDataSource
|
||||||
*/
|
*/
|
||||||
private handleGridRendered(event: CustomEvent): void {
|
private handleGridRendered(event: CustomEvent): void {
|
||||||
const { container, columns } = event.detail;
|
const { container, columns } = event.detail;
|
||||||
|
|
@ -97,17 +64,23 @@ export class EventRenderingService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract dates from columns
|
// Render events directly from columns (pre-filtered by IColumnDataSource)
|
||||||
const dates = columns.map((col: any) => col.data as Date);
|
this.renderEventsFromColumns(container, columns);
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate startDate and endDate from dates array
|
/**
|
||||||
const startDate = dates[0];
|
* Render events from pre-filtered columns
|
||||||
const endDate = dates[dates.length - 1];
|
* 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({
|
// Emit EVENTS_RENDERED for filtering system
|
||||||
container,
|
const allEvents = columns.flatMap(col => col.events);
|
||||||
startDate,
|
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
|
||||||
endDate
|
events: allEvents,
|
||||||
|
container: container
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,29 +139,42 @@ export class EventRenderingService {
|
||||||
|
|
||||||
private setupDragEndListener(): void {
|
private setupDragEndListener(): void {
|
||||||
this.eventBus.on('drag:end', async (event: Event) => {
|
this.eventBus.on('drag:end', async (event: Event) => {
|
||||||
|
const { originalElement, draggedClone, finalPosition, target } = (event as CustomEvent<IDragEndEventPayload>).detail;
|
||||||
const { originalElement, draggedClone, originalSourceColumn, finalPosition, target } = (event as CustomEvent<IDragEndEventPayload>).detail;
|
|
||||||
const finalColumn = finalPosition.column;
|
const finalColumn = finalPosition.column;
|
||||||
const finalY = finalPosition.snappedY;
|
const finalY = finalPosition.snappedY;
|
||||||
|
|
||||||
let element = draggedClone as SwpEventElement;
|
// Only handle day column drops
|
||||||
// Only handle day column drops for EventRenderer
|
|
||||||
if (target === 'swp-day-column' && finalColumn) {
|
if (target === 'swp-day-column' && finalColumn) {
|
||||||
|
const element = draggedClone as SwpEventElement;
|
||||||
|
|
||||||
if (originalElement && draggedClone && this.strategy.handleDragEnd) {
|
if (originalElement && draggedClone && this.strategy.handleDragEnd) {
|
||||||
this.strategy.handleDragEnd(originalElement, draggedClone, finalColumn, finalY);
|
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,
|
start: element.start,
|
||||||
end: element.end,
|
end: element.end,
|
||||||
allDay: false
|
allDay: false
|
||||||
});
|
};
|
||||||
|
|
||||||
// Re-render affected columns for stacking/grouping (now with updated data)
|
if (this.dataSource.isResource()) {
|
||||||
await this.reRenderAffectedColumns(originalSourceColumn, finalColumn);
|
// 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) => {
|
this.eventBus.on('resize:end', async (event: Event) => {
|
||||||
const { eventId, element } = (event as CustomEvent<IResizeEndEventPayload>).detail;
|
const { eventId, element } = (event as CustomEvent<IResizeEndEventPayload>).detail;
|
||||||
|
|
||||||
// Update event data in EventManager with new end time from resized element
|
|
||||||
const swpEvent = element as SwpEventElement;
|
const swpEvent = element as SwpEventElement;
|
||||||
const newStart = swpEvent.start;
|
|
||||||
const newEnd = swpEvent.end;
|
|
||||||
|
|
||||||
await this.eventManager.updateEvent(eventId, {
|
await this.eventManager.updateEvent(eventId, {
|
||||||
start: newStart,
|
start: swpEvent.start,
|
||||||
end: newEnd
|
end: swpEvent.end
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('📝 EventRendererManager: Updated event after resize', {
|
// Trigger full refresh to re-render with updated data
|
||||||
eventId,
|
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {});
|
||||||
newStart,
|
|
||||||
newEnd
|
|
||||||
});
|
|
||||||
|
|
||||||
const dateIdentifier = newStart.toISOString().split('T')[0];
|
|
||||||
let columnBounds = ColumnDetectionUtils.getColumnBoundsByIdentifier(dateIdentifier);
|
|
||||||
if (columnBounds)
|
|
||||||
await this.renderSingleColumn(columnBounds);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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<void> {
|
|
||||||
// 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<void> {
|
|
||||||
// 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 {
|
private clearEvents(container?: HTMLElement): void {
|
||||||
this.strategy.clearEvents(container);
|
this.strategy.clearEvents(container);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Configuration } from '../configurations/CalendarConfig';
|
import { Configuration } from '../configurations/CalendarConfig';
|
||||||
import { CalendarView, ICalendarEvent } from '../types/CalendarTypes';
|
import { CalendarView } from '../types/CalendarTypes';
|
||||||
import { IColumnRenderer, IColumnRenderContext } from './ColumnRenderer';
|
import { IColumnRenderer, IColumnRenderContext } from './ColumnRenderer';
|
||||||
import { eventBus } from '../core/EventBus';
|
import { eventBus } from '../core/EventBus';
|
||||||
import { DateService } from '../utils/DateService';
|
import { DateService } from '../utils/DateService';
|
||||||
|
|
@ -105,15 +105,13 @@ export class GridRenderer {
|
||||||
* @param grid - Container element where grid will be rendered
|
* @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 currentDate - Base date for the current view (e.g., any date in the week)
|
||||||
* @param view - Calendar view type (day/week/month)
|
* @param view - Calendar view type (day/week/month)
|
||||||
* @param dates - Array of dates to render as columns
|
* @param columns - Array of columns to render (each column contains its events)
|
||||||
* @param events - All events for the period
|
|
||||||
*/
|
*/
|
||||||
public renderGrid(
|
public renderGrid(
|
||||||
grid: HTMLElement,
|
grid: HTMLElement,
|
||||||
currentDate: Date,
|
currentDate: Date,
|
||||||
view: CalendarView = 'week',
|
view: CalendarView = 'week',
|
||||||
columns: IColumnInfo[] = [],
|
columns: IColumnInfo[] = []
|
||||||
events: ICalendarEvent[] = []
|
|
||||||
): void {
|
): void {
|
||||||
|
|
||||||
if (!grid || !currentDate) {
|
if (!grid || !currentDate) {
|
||||||
|
|
@ -125,10 +123,10 @@ export class GridRenderer {
|
||||||
|
|
||||||
// Only clear and rebuild if grid is empty (first render)
|
// Only clear and rebuild if grid is empty (first render)
|
||||||
if (grid.children.length === 0) {
|
if (grid.children.length === 0) {
|
||||||
this.createCompleteGridStructure(grid, currentDate, view, columns, events);
|
this.createCompleteGridStructure(grid, currentDate, view, columns);
|
||||||
} else {
|
} else {
|
||||||
// Optimized update - only refresh dynamic content
|
// 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 grid - Parent container
|
||||||
* @param currentDate - Current view date
|
* @param currentDate - Current view date
|
||||||
* @param view - View type
|
* @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(
|
private createCompleteGridStructure(
|
||||||
grid: HTMLElement,
|
grid: HTMLElement,
|
||||||
currentDate: Date,
|
currentDate: Date,
|
||||||
view: CalendarView,
|
view: CalendarView,
|
||||||
columns: IColumnInfo[],
|
columns: IColumnInfo[]
|
||||||
events: ICalendarEvent[]
|
|
||||||
): void {
|
): void {
|
||||||
// Create all elements in memory first for better performance
|
// Create all elements in memory first for better performance
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
@ -168,7 +165,7 @@ export class GridRenderer {
|
||||||
fragment.appendChild(timeAxis);
|
fragment.appendChild(timeAxis);
|
||||||
|
|
||||||
// Create grid container with caching
|
// Create grid container with caching
|
||||||
const gridContainer = this.createOptimizedGridContainer(columns, events);
|
const gridContainer = this.createOptimizedGridContainer(columns);
|
||||||
this.cachedGridContainer = gridContainer;
|
this.cachedGridContainer = gridContainer;
|
||||||
fragment.appendChild(gridContainer);
|
fragment.appendChild(gridContainer);
|
||||||
|
|
||||||
|
|
@ -213,14 +210,11 @@ export class GridRenderer {
|
||||||
* - Time grid (grid lines + day columns) - structure created here
|
* - Time grid (grid lines + day columns) - structure created here
|
||||||
* - Column container - created here, populated by ColumnRenderer
|
* - Column container - created here, populated by ColumnRenderer
|
||||||
*
|
*
|
||||||
* @param currentDate - Current view date
|
* @param columns - Array of columns to render (each column contains its events)
|
||||||
* @param view - View type
|
|
||||||
* @param dates - Array of dates to render
|
|
||||||
* @returns Complete grid container element
|
* @returns Complete grid container element
|
||||||
*/
|
*/
|
||||||
private createOptimizedGridContainer(
|
private createOptimizedGridContainer(
|
||||||
columns: IColumnInfo[],
|
columns: IColumnInfo[]
|
||||||
events: ICalendarEvent[]
|
|
||||||
): HTMLElement {
|
): HTMLElement {
|
||||||
const gridContainer = document.createElement('swp-grid-container');
|
const gridContainer = document.createElement('swp-grid-container');
|
||||||
|
|
||||||
|
|
@ -238,7 +232,7 @@ export class GridRenderer {
|
||||||
|
|
||||||
// Create column container
|
// Create column container
|
||||||
const columnContainer = document.createElement('swp-day-columns');
|
const columnContainer = document.createElement('swp-day-columns');
|
||||||
this.renderColumnContainer(columnContainer, columns, events);
|
this.renderColumnContainer(columnContainer, columns);
|
||||||
timeGrid.appendChild(columnContainer);
|
timeGrid.appendChild(columnContainer);
|
||||||
|
|
||||||
scrollableContent.appendChild(timeGrid);
|
scrollableContent.appendChild(timeGrid);
|
||||||
|
|
@ -255,13 +249,11 @@ export class GridRenderer {
|
||||||
* Event rendering is handled by EventRenderingService listening to GRID_RENDERED.
|
* Event rendering is handled by EventRenderingService listening to GRID_RENDERED.
|
||||||
*
|
*
|
||||||
* @param columnContainer - Empty container to populate
|
* @param columnContainer - Empty container to populate
|
||||||
* @param dates - Array of dates to render
|
* @param columns - Array of columns to render (each column contains its events)
|
||||||
* @param events - All events for the period (passed through, not used here)
|
|
||||||
*/
|
*/
|
||||||
private renderColumnContainer(
|
private renderColumnContainer(
|
||||||
columnContainer: HTMLElement,
|
columnContainer: HTMLElement,
|
||||||
columns: IColumnInfo[],
|
columns: IColumnInfo[]
|
||||||
events: ICalendarEvent[]
|
|
||||||
): void {
|
): void {
|
||||||
// Delegate to ColumnRenderer
|
// Delegate to ColumnRenderer
|
||||||
this.columnRenderer.render(columnContainer, {
|
this.columnRenderer.render(columnContainer, {
|
||||||
|
|
@ -279,21 +271,19 @@ export class GridRenderer {
|
||||||
* @param grid - Existing grid element
|
* @param grid - Existing grid element
|
||||||
* @param currentDate - New view date
|
* @param currentDate - New view date
|
||||||
* @param view - View type
|
* @param view - View type
|
||||||
* @param dates - Array of dates to render
|
* @param columns - Array of columns to render (each column contains its events)
|
||||||
* @param events - All events for the period
|
|
||||||
*/
|
*/
|
||||||
private updateGridContent(
|
private updateGridContent(
|
||||||
grid: HTMLElement,
|
grid: HTMLElement,
|
||||||
currentDate: Date,
|
currentDate: Date,
|
||||||
view: CalendarView,
|
view: CalendarView,
|
||||||
columns: IColumnInfo[],
|
columns: IColumnInfo[]
|
||||||
events: ICalendarEvent[]
|
|
||||||
): void {
|
): void {
|
||||||
// Update column container if needed
|
// Update column container if needed
|
||||||
const columnContainer = grid.querySelector('swp-day-columns');
|
const columnContainer = grid.querySelector('swp-day-columns');
|
||||||
if (columnContainer) {
|
if (columnContainer) {
|
||||||
columnContainer.innerHTML = '';
|
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
|
* @returns New grid element ready for animation
|
||||||
*/
|
*/
|
||||||
public createNavigationGrid(parentContainer: HTMLElement, columns: IColumnInfo[]): HTMLElement {
|
public createNavigationGrid(parentContainer: HTMLElement, columns: IColumnInfo[]): HTMLElement {
|
||||||
// Create grid structure without events (events rendered by EventRenderingService)
|
// Create grid structure (events are in columns, rendered by EventRenderingService)
|
||||||
const newGrid = this.createOptimizedGridContainer(columns, []);
|
const newGrid = this.createOptimizedGridContainer(columns);
|
||||||
|
|
||||||
// Position new grid for animation - NO transform here, let Animation API handle it
|
// Position new grid for animation - NO transform here, let Animation API handle it
|
||||||
newGrid.style.position = 'absolute';
|
newGrid.style.position = 'absolute';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { IResource } from './ResourceTypes';
|
import { IResource } from './ResourceTypes';
|
||||||
import { CalendarView } from './CalendarTypes';
|
import { CalendarView, ICalendarEvent } from './CalendarTypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Column information container
|
* Column information container
|
||||||
|
|
@ -8,6 +8,7 @@ import { CalendarView } from './CalendarTypes';
|
||||||
export interface IColumnInfo {
|
export interface IColumnInfo {
|
||||||
identifier: string; // "2024-11-13" (date mode) or "person-1" (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
|
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';
|
getType(): 'date' | 'resource';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this datasource is in resource mode
|
||||||
|
*/
|
||||||
|
isResource(): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current date for column calculations
|
* Update the current date for column calculations
|
||||||
* @param date - The new current date
|
* @param date - The new current date
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue