Refactors event rendering to be event-driven

Moves event rendering logic into a dedicated EventRenderer class that uses a strategy pattern for different calendar types.

The rendering is now triggered by `GRID_RENDERED` and `CONTAINER_READY_FOR_EVENTS` events, emitted by the GridManager and NavigationManager respectively.

This change decouples the CalendarManager from direct event rendering and allows for more flexible and efficient event updates. The EventManager now has a method to fetch events for a given time period.

Removes direct calls to event rendering from CalendarManager. Improves animation transitions by using pre-rendered containers in the NavigationManager.
This commit is contained in:
Janus Knudsen 2025-08-16 00:51:12 +02:00
parent afe5b6b899
commit a03f314c4a
9 changed files with 271 additions and 166 deletions

View file

@ -81,6 +81,7 @@ export const EventTypes = {
CALENDAR_INITIALIZED: 'calendar:initialized', CALENDAR_INITIALIZED: 'calendar:initialized',
CALENDAR_DATA_LOADED: 'calendar:calendardataloaded', CALENDAR_DATA_LOADED: 'calendar:calendardataloaded',
GRID_RENDERED: 'calendar:gridrendered', GRID_RENDERED: 'calendar:gridrendered',
CONTAINER_READY_FOR_EVENTS: 'calendar:containerreadyforevents',
// Management events (legacy - prefer StateEvents) // Management events (legacy - prefer StateEvents)
REFRESH_REQUESTED: 'calendar:refreshrequested', REFRESH_REQUESTED: 'calendar:refreshrequested',

View file

@ -36,10 +36,10 @@ async function initializeCalendar(): Promise<void> {
// Create all managers // Create all managers
console.log('📋 Creating managers...'); console.log('📋 Creating managers...');
calendarManager = new CalendarManager(eventBus, config); calendarManager = new CalendarManager(eventBus, config);
navigationManager = new NavigationManager(eventBus); navigationManager = new NavigationManager(eventBus); // No EventRenderer dependency
viewManager = new ViewManager(eventBus); viewManager = new ViewManager(eventBus);
eventManager = new EventManager(eventBus); eventManager = new EventManager(eventBus);
eventRenderer = new EventRenderer(eventBus); eventRenderer = new EventRenderer(eventBus, eventManager); // Pass EventManager
gridManager = new GridManager(); gridManager = new GridManager();
scrollManager = new ScrollManager(); scrollManager = new ScrollManager();

View file

@ -76,14 +76,18 @@ export class CalendarManager {
this.setView(this.currentView); this.setView(this.currentView);
this.setCurrentDate(this.currentDate); this.setCurrentDate(this.currentDate);
// Step 5: Render events (after view is set) - only render events for current period // Step 5: Event rendering will be triggered by GRID_RENDERED event
console.log('🎨 Rendering events for current period...'); console.log('🎨 Event rendering will be triggered automatically by grid events...');
const events = this.getEventsForCurrentPeriod();
await this.eventRenderer.renderEvents(events);
this.isInitialized = true; this.isInitialized = true;
console.log('✅ CalendarManager: Simple initialization complete'); console.log('✅ CalendarManager: Simple initialization complete');
// Emit initialization complete event
this.eventBus.emit(EventTypes.CALENDAR_INITIALIZED, {
currentDate: this.currentDate,
currentView: this.currentView
});
} catch (error) { } catch (error) {
console.error('❌ CalendarManager initialization failed:', error); console.error('❌ CalendarManager initialization failed:', error);
throw error; throw error;
@ -110,10 +114,7 @@ export class CalendarManager {
date: this.currentDate date: this.currentDate
}); });
// Re-render events for new view if calendar is initialized // Grid re-rendering will trigger event rendering automatically via GRID_RENDERED event
if (this.isInitialized) {
this.rerenderEventsForCurrentPeriod();
}
} }
/** /**
@ -132,10 +133,7 @@ export class CalendarManager {
view: this.currentView view: this.currentView
}); });
// Re-render events for new period if calendar is initialized // Grid update for new date will trigger event rendering automatically via GRID_RENDERED event
if (this.isInitialized) {
this.rerenderEventsForCurrentPeriod();
}
} }
/** /**
@ -313,34 +311,6 @@ export class CalendarManager {
return previousDate; return previousDate;
} }
/**
* Get events filtered for the current period (week/month/day)
*/
private getEventsForCurrentPeriod(): CalendarEvent[] {
const allEvents = this.eventManager.getEvents();
// Calculate current period based on view
const period = this.calculateCurrentPeriod();
// Filter events to only include those in the current period
const filteredEvents = allEvents.filter(event => {
const eventStart = new Date(event.start);
const eventEnd = new Date(event.end);
const periodStart = new Date(period.start);
const periodEnd = new Date(period.end);
// Include event if it overlaps with the period
return eventStart <= periodEnd && eventEnd >= periodStart;
});
// Also filter out all-day events (handled by GridManager)
const nonAllDayEvents = filteredEvents.filter(event => !event.allDay);
console.log(`CalendarManager: Filtered ${allEvents.length} total events to ${nonAllDayEvents.length} non-all-day events for current period`);
return nonAllDayEvents;
}
/** /**
* Calculate the current period based on view and date * Calculate the current period based on view and date
*/ */
@ -399,12 +369,4 @@ export class CalendarManager {
} }
} }
/**
* Re-render events for the current period
*/
private async rerenderEventsForCurrentPeriod(): Promise<void> {
console.log('CalendarManager: Re-rendering events for current period');
const events = this.getEventsForCurrentPeriod();
await this.eventRenderer.renderEvents(events);
}
} }

View file

@ -119,6 +119,19 @@ export class EventManager {
return this.events.find(event => event.id === id); return this.events.find(event => event.id === id);
} }
/**
* Get events for a specific time period
*/
public getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[] {
return this.events.filter(event => {
const eventStart = new Date(event.start);
const eventEnd = new Date(event.end);
// Event overlaps period if it starts before period ends AND ends after period starts
return eventStart <= endDate && eventEnd >= startDate;
});
}
public addEvent(event: Omit<CalendarEvent, 'id'>): CalendarEvent { public addEvent(event: Omit<CalendarEvent, 'id'>): CalendarEvent {
const newEvent: CalendarEvent = { const newEvent: CalendarEvent = {
...event, ...event,

View file

@ -1,9 +1,11 @@
import { EventBus } from '../core/EventBus'; import { EventBus } from '../core/EventBus';
import { IEventBus, CalendarEvent } from '../types/CalendarTypes'; import { IEventBus, CalendarEvent, RenderContext } from '../types/CalendarTypes';
import { EventTypes } from '../constants/EventTypes'; import { EventTypes } from '../constants/EventTypes';
import { StateEvents } from '../types/CalendarState'; import { StateEvents } from '../types/CalendarState';
import { calendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { EventManager } from './EventManager';
import { EventRendererStrategy } from '../renderers/EventRenderer';
/** /**
* EventRenderer - Render events i DOM med positionering using Strategy Pattern * EventRenderer - Render events i DOM med positionering using Strategy Pattern
@ -11,73 +13,133 @@ import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
*/ */
export class EventRenderer { export class EventRenderer {
private eventBus: IEventBus; private eventBus: IEventBus;
private eventManager: EventManager;
private strategy: EventRendererStrategy;
constructor(eventBus: IEventBus) { constructor(eventBus: IEventBus, eventManager: EventManager) {
this.eventBus = eventBus; this.eventBus = eventBus;
this.eventManager = eventManager;
// Cache strategy at initialization
const calendarType = calendarConfig.getCalendarMode();
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
this.setupEventListeners(); this.setupEventListeners();
} }
/** /**
* Public method to render events - called directly by CalendarManager * Render events in a specific container for a given period
*/ */
public async renderEvents(events: CalendarEvent[]): Promise<void> { public renderEvents(context: RenderContext): void {
console.log('EventRenderer: Direct renderEvents called with', events.length, 'events'); console.log('EventRenderer: Rendering events for period', {
startDate: context.startDate,
endDate: context.endDate,
container: context.container
});
// Get events from EventManager for the period
const events = this.eventManager.getEventsForPeriod(
context.startDate,
context.endDate
);
console.log(`EventRenderer: Found ${events.length} events for period`);
// Debug: Check if we have any events
if (events.length === 0) { if (events.length === 0) {
console.warn('EventRenderer: No events to render'); console.log('EventRenderer: No events to render for this period');
return; return;
} }
// Debug: Log first event details // Use cached strategy to render events in the specific container
console.log('EventRenderer: First event details:', { this.strategy.renderEvents(events, context.container, calendarConfig);
title: events[0].title,
start: events[0].start,
end: events[0].end,
allDay: events[0].allDay
});
// Get the appropriate event renderer strategy
const calendarType = calendarConfig.getCalendarMode();
const eventRenderer = CalendarTypeFactory.getEventRenderer(calendarType);
console.log(`EventRenderer: Using ${calendarType} event renderer strategy`);
// Debug: Check if columns exist
const columns = document.querySelectorAll('swp-day-column');
console.log(`EventRenderer: Found ${columns.length} day columns in DOM`);
// Use strategy to render events
eventRenderer.renderEvents(events, calendarConfig);
console.log(`EventRenderer: Successfully rendered ${events.length} events`); console.log(`EventRenderer: Successfully rendered ${events.length} events`);
} }
private setupEventListeners(): void { private setupEventListeners(): void {
// Keep only UI-related event listeners // Event-driven rendering: React to grid and container events
this.eventBus.on(EventTypes.VIEW_RENDERED, () => { this.eventBus.on(EventTypes.GRID_RENDERED, (event: Event) => {
// Clear existing events when view changes console.log('EventRenderer: Received GRID_RENDERED event');
this.clearEvents(); this.handleGridRendered(event as CustomEvent);
}); });
// Handle calendar type changes this.eventBus.on(EventTypes.CONTAINER_READY_FOR_EVENTS, (event: Event) => {
console.log('EventRenderer: Received CONTAINER_READY_FOR_EVENTS event');
this.handleContainerReady(event as CustomEvent);
});
this.eventBus.on(EventTypes.VIEW_CHANGED, (event: Event) => {
console.log('EventRenderer: Received VIEW_CHANGED event');
this.handleViewChanged(event as CustomEvent);
});
// Handle calendar type changes - update cached strategy
this.eventBus.on(EventTypes.CALENDAR_TYPE_CHANGED, () => { this.eventBus.on(EventTypes.CALENDAR_TYPE_CHANGED, () => {
// Re-render would need to be triggered by CalendarManager now const calendarType = calendarConfig.getCalendarMode();
this.clearEvents(); this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
console.log(`EventRenderer: Updated strategy to ${calendarType}`);
}); });
} }
private clearEvents(): void { /**
console.warn(`🗑️ EventRenderer: clearEvents() called from EventRenderer manager`); * Handle GRID_RENDERED event - render events in the current grid
const calendarType = calendarConfig.getCalendarMode(); */
const eventRenderer = CalendarTypeFactory.getEventRenderer(calendarType); private handleGridRendered(event: CustomEvent): void {
eventRenderer.clearEvents(); const { container, startDate, endDate } = event.detail;
if (!container) {
console.error('EventRenderer: No container in GRID_RENDERED event', event.detail);
return;
}
// Use period from event or fallback to calculated period
const periodStart = startDate;
const periodEnd = endDate;
this.renderEvents({
container: container,
startDate: periodStart,
endDate: periodEnd
});
} }
public refresh(): void { /**
// Refresh would need to be coordinated by CalendarManager now * Handle CONTAINER_READY_FOR_EVENTS event - render events in pre-rendered container
*/
private handleContainerReady(event: CustomEvent): void {
const { container, startDate, endDate } = event.detail;
if (!container || !startDate || !endDate) {
console.error('EventRenderer: Invalid CONTAINER_READY_FOR_EVENTS event data', event.detail);
return;
}
this.renderEvents({
container: container,
startDate: new Date(startDate),
endDate: new Date(endDate)
});
}
/**
* Handle VIEW_CHANGED event - clear and re-render for new view
*/
private handleViewChanged(event: CustomEvent): void {
// Clear all existing events since view structure may have changed
this.clearEvents(); this.clearEvents();
// New rendering will be triggered by subsequent GRID_RENDERED event
console.log('EventRenderer: Cleared events for view change, waiting for GRID_RENDERED');
}
private clearEvents(container?: HTMLElement): void {
console.log(`EventRenderer: Clearing events`, container ? 'in container' : 'globally');
this.strategy.clearEvents(container);
}
public refresh(container?: HTMLElement): void {
// Clear events in specific container or globally
this.clearEvents(container);
} }
public destroy(): void { public destroy(): void {

View file

@ -140,6 +140,16 @@ export class GridManager {
const columnCount = this.getColumnCount(); const columnCount = this.getColumnCount();
console.log(`GridManager: Render complete - created ${columnCount} columns`); console.log(`GridManager: Render complete - created ${columnCount} columns`);
// Emit GRID_RENDERED event to trigger event rendering
const weekEnd = this.currentWeek ? new Date(this.currentWeek.getTime() + 6 * 24 * 60 * 60 * 1000) : null;
eventBus.emit(EventTypes.GRID_RENDERED, {
container: this.grid,
currentWeek: this.currentWeek,
startDate: this.currentWeek,
endDate: weekEnd,
columnCount: columnCount
});
} }
/** /**

View file

@ -113,11 +113,11 @@ export class NavigationManager {
} }
/** /**
* POC-style animation transition - creates new grid container and slides it in * Animation transition using pre-rendered containers when available
*/ */
private animateTransition(direction: 'prev' | 'next', targetWeek: Date): void { private animateTransition(direction: 'prev' | 'next', targetWeek: Date): void {
const container = document.querySelector('swp-calendar-container'); const container = document.querySelector('swp-calendar-container');
const currentGrid = container?.querySelector('swp-grid-container'); const currentGrid = container?.querySelector('swp-grid-container:not([data-prerendered])');
if (!container || !currentGrid) { if (!container || !currentGrid) {
console.warn('NavigationManager: Required DOM elements not found'); console.warn('NavigationManager: Required DOM elements not found');
@ -126,31 +126,15 @@ export class NavigationManager {
console.log(`NavigationManager: Starting ${direction} animation to ${targetWeek.toDateString()}`); console.log(`NavigationManager: Starting ${direction} animation to ${targetWeek.toDateString()}`);
// Create new grid container (POC approach) let newGrid: HTMLElement;
const newGrid = document.createElement('swp-grid-container');
newGrid.innerHTML = `
<swp-calendar-header></swp-calendar-header>
<swp-scrollable-content>
<swp-time-grid>
<swp-grid-lines></swp-grid-lines>
<swp-day-columns></swp-day-columns>
</swp-time-grid>
</swp-scrollable-content>
`;
// Position new grid off-screen (POC positioning) // Always create a fresh container for consistent behavior
newGrid.style.position = 'absolute'; console.log('NavigationManager: Creating new container');
newGrid.style.top = '0'; newGrid = this.renderContainer(container as HTMLElement, targetWeek);
newGrid.style.left = '0';
newGrid.style.width = '100%';
newGrid.style.height = '100%';
newGrid.style.transform = direction === 'next' ? 'translateX(100%)' : 'translateX(-100%)';
// Add to container // Clear any existing transforms before animation
container.appendChild(newGrid); newGrid.style.transform = '';
(currentGrid as HTMLElement).style.transform = '';
// Render new content for target week
this.renderWeekContent(newGrid, targetWeek);
// Animate transition using Web Animations API // Animate transition using Web Animations API
const slideOutAnimation = (currentGrid as HTMLElement).animate([ const slideOutAnimation = (currentGrid as HTMLElement).animate([
@ -181,6 +165,7 @@ export class NavigationManager {
// Reset positioning // Reset positioning
newGrid.style.position = 'relative'; newGrid.style.position = 'relative';
newGrid.removeAttribute('data-prerendered');
// Update state // Update state
this.currentWeek = new Date(targetWeek); this.currentWeek = new Date(targetWeek);
@ -198,6 +183,7 @@ export class NavigationManager {
weekEnd: DateUtils.addDays(this.currentWeek, 6) weekEnd: DateUtils.addDays(this.currentWeek, 6)
}); });
// Emit animation complete event for ScrollManager // Emit animation complete event for ScrollManager
this.eventBus.emit(EventTypes.NAVIGATION_ANIMATION_COMPLETE, { this.eventBus.emit(EventTypes.NAVIGATION_ANIMATION_COMPLETE, {
direction, direction,
@ -335,4 +321,97 @@ export class NavigationManager {
weekEnd: DateUtils.addDays(this.currentWeek, 6) weekEnd: DateUtils.addDays(this.currentWeek, 6)
}); });
} }
/**
* Render a complete container with content and events
*/
private renderContainer(parentContainer: HTMLElement, weekStart: Date): HTMLElement {
console.log('NavigationManager: Rendering new container for week:', weekStart.toDateString());
// Create new grid container
const newGrid = document.createElement('swp-grid-container');
newGrid.innerHTML = `
<swp-calendar-header></swp-calendar-header>
<swp-scrollable-content>
<swp-time-grid>
<swp-grid-lines></swp-grid-lines>
<swp-day-columns></swp-day-columns>
</swp-time-grid>
</swp-scrollable-content>
`;
// Position new grid - NO transform here, let Animation API handle it
newGrid.style.position = 'absolute';
newGrid.style.top = '0';
newGrid.style.left = '0';
newGrid.style.width = '100%';
newGrid.style.height = '100%';
// Add to parent container
parentContainer.appendChild(newGrid);
// Render week content (headers and columns)
this.renderWeekContentInContainer(newGrid, weekStart);
// Emit event to trigger event rendering
const weekEnd = DateUtils.addDays(weekStart, 6);
this.eventBus.emit(EventTypes.CONTAINER_READY_FOR_EVENTS, {
container: newGrid,
startDate: weekStart,
endDate: weekEnd
});
console.log('NavigationManager: Container rendered with content and events triggered');
return newGrid;
}
/**
* Render week content in specific container
*/
private renderWeekContentInContainer(gridContainer: HTMLElement, weekStart: Date): void {
const header = gridContainer.querySelector('swp-calendar-header');
const dayColumns = gridContainer.querySelector('swp-day-columns');
if (!header || !dayColumns) return;
// Clear existing content
header.innerHTML = '';
dayColumns.innerHTML = '';
// Render headers for target week
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
for (let i = 0; i < 7; i++) {
const date = new Date(weekStart);
date.setDate(date.getDate() + i);
const headerElement = document.createElement('swp-day-header');
if (this.isToday(date)) {
headerElement.dataset.today = 'true';
}
headerElement.innerHTML = `
<swp-day-name>${days[date.getDay()]}</swp-day-name>
<swp-day-date>${date.getDate()}</swp-day-date>
`;
headerElement.dataset.date = this.formatDate(date);
header.appendChild(headerElement);
}
// Render day columns for target week (with hardcoded test event)
for (let i = 0; i < 7; i++) {
const column = document.createElement('swp-day-column');
const date = new Date(weekStart);
date.setDate(date.getDate() + i);
column.dataset.date = this.formatDate(date);
const eventsLayer = document.createElement('swp-events-layer');
column.appendChild(eventsLayer);
dayColumns.appendChild(column);
}
}
} }

View file

@ -8,18 +8,15 @@ import { DateUtils } from '../utils/DateUtils';
* Interface for event rendering strategies * Interface for event rendering strategies
*/ */
export interface EventRendererStrategy { export interface EventRendererStrategy {
findColumn(event: CalendarEvent): HTMLElement | null; renderEvents(events: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void;
renderEvents(events: CalendarEvent[], config: CalendarConfig): void; clearEvents(container?: HTMLElement): void;
clearEvents(): void;
} }
/** /**
* Base class for event renderers with common functionality * Base class for event renderers with common functionality
*/ */
export abstract class BaseEventRenderer implements EventRendererStrategy { export abstract class BaseEventRenderer implements EventRendererStrategy {
abstract findColumn(event: CalendarEvent): HTMLElement | null; renderEvents(events: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void {
renderEvents(events: CalendarEvent[], config: CalendarConfig): void {
console.log('BaseEventRenderer: renderEvents called with', events.length, 'events'); console.log('BaseEventRenderer: renderEvents called with', events.length, 'events');
// NOTE: Removed clearEvents() to support sliding animation // NOTE: Removed clearEvents() to support sliding animation
@ -30,10 +27,9 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Events should already be filtered by DataManager - no need to filter here // Events should already be filtered by DataManager - no need to filter here
console.log('BaseEventRenderer: Rendering', events.length, 'pre-filtered events'); console.log('BaseEventRenderer: Rendering', events.length, 'pre-filtered events');
// OPTIMIZATION: Column-first rendering instead of event-first // Find columns in the specific container
// This is much more efficient when there are many events const columns = this.getColumns(container);
const columns = this.getColumns(); console.log(`BaseEventRenderer: Found ${columns.length} columns in container`);
console.log(`BaseEventRenderer: Found ${columns.length} columns to render events in`);
columns.forEach(column => { columns.forEach(column => {
const columnEvents = this.getEventsForColumn(column, events); const columnEvents = this.getEventsForColumn(column, events);
@ -55,8 +51,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}); });
} }
// Abstract methods that subclasses must implement for column-first rendering // Abstract methods that subclasses must implement
protected abstract getColumns(): HTMLElement[]; protected abstract getColumns(container: HTMLElement): HTMLElement[];
protected abstract getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[]; protected abstract getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[];
protected renderEvent(event: CalendarEvent, container: Element, config: CalendarConfig): void { protected renderEvent(event: CalendarEvent, container: Element, config: CalendarConfig): void {
@ -138,10 +134,15 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
return `${displayHour}:${minutes.toString().padStart(2, '0')} ${period}`; return `${displayHour}:${minutes.toString().padStart(2, '0')} ${period}`;
} }
clearEvents(): void { clearEvents(container?: HTMLElement): void {
const existingEvents = document.querySelectorAll('swp-event'); const selector = 'swp-event';
const existingEvents = container
? container.querySelectorAll(selector)
: document.querySelectorAll(selector);
if (existingEvents.length > 0) { if (existingEvents.length > 0) {
console.warn(`🗑️ BaseEventRenderer: REMOVING ${existingEvents.length} events from DOM! Stack trace:`, new Error().stack); console.log(`BaseEventRenderer: Clearing ${existingEvents.length} events`,
container ? 'from container' : 'globally');
} }
existingEvents.forEach(event => event.remove()); existingEvents.forEach(event => event.remove());
} }
@ -151,26 +152,9 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
* Date-based event renderer * Date-based event renderer
*/ */
export class DateEventRenderer extends BaseEventRenderer { export class DateEventRenderer extends BaseEventRenderer {
findColumn(event: CalendarEvent): HTMLElement | null { protected getColumns(container: HTMLElement): HTMLElement[] {
const eventDate = new Date(event.start); const columns = container.querySelectorAll('swp-day-column');
const dateStr = DateUtils.formatDate(eventDate); console.log('DateEventRenderer: Found', columns.length, 'day columns in container');
const dayColumn = document.querySelector(`swp-day-column[data-date="${dateStr}"]`) as HTMLElement;
// Debug: Check all available columns
const allColumns = document.querySelectorAll('swp-day-column');
const availableDates = Array.from(allColumns).map(col => (col as HTMLElement).dataset.date);
console.log('DateEventRenderer: Event', event.title, 'start:', event.start);
console.log('DateEventRenderer: Looking for date:', dateStr);
console.log('DateEventRenderer: Available columns with dates:', availableDates);
console.log('DateEventRenderer: Found column:', !!dayColumn);
return dayColumn;
}
protected getColumns(): HTMLElement[] {
const columns = document.querySelectorAll('swp-day-column');
console.log('DateEventRenderer: Found', columns.length, 'day columns in DOM');
return Array.from(columns) as HTMLElement[]; return Array.from(columns) as HTMLElement[];
} }
@ -193,21 +177,9 @@ export class DateEventRenderer extends BaseEventRenderer {
* Resource-based event renderer * Resource-based event renderer
*/ */
export class ResourceEventRenderer extends BaseEventRenderer { export class ResourceEventRenderer extends BaseEventRenderer {
findColumn(event: CalendarEvent): HTMLElement | null { protected getColumns(container: HTMLElement): HTMLElement[] {
const resourceName = event.resource?.name; const columns = container.querySelectorAll('swp-resource-column');
if (!resourceName) { console.log('ResourceEventRenderer: Found', columns.length, 'resource columns in container');
console.warn('ResourceEventRenderer: Event has no resource.name', event);
return null;
}
const resourceColumn = document.querySelector(`swp-resource-column[data-resource="${resourceName}"]`) as HTMLElement;
console.log('ResourceEventRenderer: Looking for resource column with name', resourceName, 'found:', !!resourceColumn);
return resourceColumn;
}
protected getColumns(): HTMLElement[] {
const columns = document.querySelectorAll('swp-resource-column');
console.log('ResourceEventRenderer: Found', columns.length, 'resource columns in DOM');
return Array.from(columns) as HTMLElement[]; return Array.from(columns) as HTMLElement[];
} }

View file

@ -27,6 +27,12 @@ export interface ResourceCalendarData {
resources: Resource[]; resources: Resource[];
} }
export interface RenderContext {
container: HTMLElement;
startDate: Date;
endDate: Date;
}
export interface CalendarEvent { export interface CalendarEvent {
id: string; id: string;
title: string; title: string;