Calendar/src/renderers/EventRendererManager.ts
Janus Knudsen 12df6a9b06 Adds fuzzy search filter system
Implements a fuzzy search filter system using Fuse.js to enhance event searching.

This system allows users to quickly find events by typing partial matches of event titles or descriptions, providing visual feedback by dimming non-matching events. The filter persists during navigation and includes escape key support for quick clearing. It also includes performance optimizations like requestAnimationFrame debouncing.
2025-08-23 00:01:59 +02:00

158 lines
No EOL
5.7 KiB
TypeScript

import { EventBus } from '../core/EventBus';
import { IEventBus, CalendarEvent, RenderContext } from '../types/CalendarTypes';
import { CoreEvents } from '../constants/CoreEvents';
import { calendarConfig } from '../core/CalendarConfig';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { EventManager } from '../managers/EventManager';
import { EventRendererStrategy } from './EventRenderer';
/**
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
* Håndterer event positioning og overlap detection
*/
export class EventRenderingService {
private eventBus: IEventBus;
private eventManager: EventManager;
private strategy: EventRendererStrategy;
constructor(eventBus: IEventBus, eventManager: EventManager) {
this.eventBus = eventBus;
this.eventManager = eventManager;
// Cache strategy at initialization
const calendarType = calendarConfig.getCalendarMode();
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
this.setupEventListeners();
}
/**
* Render events in a specific container for a given period
*/
public renderEvents(context: RenderContext): void {
console.log(` 📅 Getting events for ${context.startDate.toDateString()} - ${context.endDate.toDateString()}`);
// Clear existing events in the specific container first
this.strategy.clearEvents(context.container);
console.log(` 🧹 Cleared existing events in container`);
// Get events from EventManager for the period
const events = this.eventManager.getEventsForPeriod(
context.startDate,
context.endDate
);
console.log(` 📊 Found ${events.length} events for period`);
if (events.length === 0) {
console.log(' ⚠️ No events to render for this period');
return;
}
// Use cached strategy to render events in the specific container
this.strategy.renderEvents(events, context.container, calendarConfig);
console.log(` ✅ Rendered ${events.length} events successfully`);
// Emit EVENTS_RENDERED event for filtering system
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
events: events,
container: context.container
});
}
private setupEventListeners(): void {
// Event-driven rendering: React to grid and container events
this.eventBus.on(CoreEvents.GRID_RENDERED, (event: Event) => {
console.log('EventRenderer: Received GRID_RENDERED event');
this.handleGridRendered(event as CustomEvent);
});
// CONTAINER_READY_FOR_EVENTS removed - events are now pre-rendered synchronously
// 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(CoreEvents.VIEW_CHANGED, (event: Event) => {
console.log('EventRenderer: Received VIEW_CHANGED event');
this.handleViewChanged(event as CustomEvent);
});
}
/**
* Handle GRID_RENDERED event - render events in the current grid
*/
private handleGridRendered(event: CustomEvent): void {
const { container, startDate, endDate, currentDate } = event.detail;
if (!container) {
console.error('EventRenderer: No container in GRID_RENDERED event', event.detail);
return;
}
let periodStart: Date;
let periodEnd: Date;
if (startDate && endDate) {
// Direct date format - use as provided
periodStart = startDate;
periodEnd = endDate;
} else if (currentDate) {
console.error('EventRenderer: GRID_RENDERED events must include explicit startDate and endDate', event.detail);
return;
} else {
console.error('EventRenderer: No date information in GRID_RENDERED event', event.detail);
return;
}
this.renderEvents({
container: container,
startDate: periodStart,
endDate: periodEnd
});
}
/**
* 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();
// 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 {
this.clearEvents();
}
}