Improves performance by caching DOM elements

Caches frequently accessed DOM elements in NavigationManager and
EventRenderer to reduce redundant queries, improving performance.

Updates the event renderer to trigger all-day height animations and
introduces a destroy method for resource management.

Refactors time formatting in EventRenderer to handle both total
minutes and Date objects using a unified method.
This commit is contained in:
Janus Knudsen 2025-09-03 18:15:33 +02:00
parent 77592278d3
commit 0da875a224
3 changed files with 126 additions and 37 deletions

View file

@ -26,9 +26,9 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
private draggedClone: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null;
constructor(config: CalendarConfig) {
constructor(config: CalendarConfig, dateCalculator?: DateCalculator) {
this.config = config;
this.dateCalculator = new DateCalculator(config);
this.dateCalculator = dateCalculator || new DateCalculator(config);
}
/**
@ -68,12 +68,27 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Handle navigation period change (when slide animation completes)
eventBus.on(CoreEvents.PERIOD_CHANGED, () => {
// Animate all-day height after navigation completes
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
const headerRenderer = new DateHeaderRenderer();
headerRenderer.checkAndAnimateAllDayHeight();
});
this.triggerAllDayHeightAnimation();
});
}
/**
* Trigger all-day height animation without creating new renderer instance
*/
private triggerAllDayHeightAnimation(): void {
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
const headerRenderer = new DateHeaderRenderer();
headerRenderer.checkAndAnimateAllDayHeight();
});
}
/**
* Cleanup method for proper resource management
*/
public destroy(): void {
this.draggedClone = null;
this.originalEvent = null;
}
/**
* Get original event duration from data-duration attribute
@ -171,13 +186,24 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}
/**
* Format time from total minutes
* Unified time formatting method - handles both total minutes and Date objects
*/
private formatTime(totalMinutes: number): string {
const hours = Math.floor(totalMinutes / 60) % 24;
const minutes = totalMinutes % 60;
private formatTime(input: number | Date | string): string {
let hours: number, minutes: number;
if (typeof input === 'number') {
// Total minutes input
hours = Math.floor(input / 60) % 24;
minutes = input % 60;
} else {
// Date or ISO string input
const date = typeof input === 'string' ? new Date(input) : input;
hours = date.getHours();
minutes = date.getMinutes();
}
const period = hours >= 12 ? 'PM' : 'AM';
const displayHours = hours % 12 || 12;
const displayHours = hours > 12 ? hours - 12 : (hours === 0 ? 12 : hours);
return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;
}
@ -331,10 +357,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
this.draggedClone = allDayEvent;
// Check if height animation is needed
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
const headerRenderer = new DateHeaderRenderer();
headerRenderer.checkAndAnimateAllDayHeight();
});
this.triggerAllDayHeightAnimation();
}
@ -556,9 +579,9 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Color is now handled by CSS classes based on data-type attribute
// Format time for display
const startTime = this.formatTimeFromISOString(event.start);
const endTime = this.formatTimeFromISOString(event.end);
// Format time for display using unified method
const startTime = this.formatTime(event.start);
const endTime = this.formatTime(event.end);
// Calculate duration in minutes
const startDate = new Date(event.start);
@ -599,17 +622,6 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
return { top, height };
}
protected formatTimeFromISOString(isoString: string): string {
const date = new Date(isoString);
const hours = date.getHours();
const minutes = date.getMinutes();
const period = hours >= 12 ? 'PM' : 'AM';
const displayHour = hours > 12 ? hours - 12 : (hours === 0 ? 12 : hours);
return `${displayHour}:${minutes.toString().padStart(2, '0')} ${period}`;
}
/**
* Calculate grid column span for event
*/
@ -666,8 +678,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
* Date-based event renderer
*/
export class DateEventRenderer extends BaseEventRenderer {
constructor(config: CalendarConfig) {
super(config);
constructor(config: CalendarConfig, dateCalculator?: DateCalculator) {
super(config, dateCalculator);
this.setupDragEventListeners();
}

View file

@ -15,6 +15,10 @@ export class NavigationRenderer {
private config: CalendarConfig;
private dateCalculator: DateCalculator;
private eventRenderer: EventRenderingService;
// Cached DOM elements to avoid redundant queries
private cachedWeekNumberElement: HTMLElement | null = null;
private cachedDateRangeElement: HTMLElement | null = null;
constructor(eventBus: IEventBus, config: CalendarConfig, eventRenderer: EventRenderingService) {
this.eventBus = eventBus;
@ -24,6 +28,34 @@ export class NavigationRenderer {
this.setupEventListeners();
}
/**
* Get cached week number element
*/
private getWeekNumberElement(): HTMLElement | null {
if (!this.cachedWeekNumberElement) {
this.cachedWeekNumberElement = document.querySelector('swp-week-number');
}
return this.cachedWeekNumberElement;
}
/**
* Get cached date range element
*/
private getDateRangeElement(): HTMLElement | null {
if (!this.cachedDateRangeElement) {
this.cachedDateRangeElement = document.querySelector('swp-date-range');
}
return this.cachedDateRangeElement;
}
/**
* Clear cached DOM elements (call when DOM structure changes)
*/
private clearCache(): void {
this.cachedWeekNumberElement = null;
this.cachedDateRangeElement = null;
}
/**
* Setup event listeners for DOM updates
*/
@ -36,11 +68,11 @@ export class NavigationRenderer {
}
/**
* Update week info in DOM elements
* Update week info in DOM elements using cached references
*/
private updateWeekInfoInDOM(weekNumber: number, dateRange: string): void {
const weekNumberElement = document.querySelector('swp-week-number');
const dateRangeElement = document.querySelector('swp-date-range');
const weekNumberElement = this.getWeekNumberElement();
const dateRangeElement = this.getDateRangeElement();
if (weekNumberElement) {
weekNumberElement.textContent = `Week ${weekNumber}`;
@ -235,4 +267,11 @@ export class NavigationRenderer {
});
}
/**
* Public cleanup method for cached elements
*/
public destroy(): void {
this.clearCache();
}
}