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:
parent
77592278d3
commit
0da875a224
3 changed files with 126 additions and 37 deletions
|
|
@ -17,6 +17,10 @@ export class NavigationManager {
|
||||||
private targetWeek: Date;
|
private targetWeek: Date;
|
||||||
private animationQueue: number = 0;
|
private animationQueue: number = 0;
|
||||||
|
|
||||||
|
// Cached DOM elements to avoid redundant queries
|
||||||
|
private cachedCalendarContainer: HTMLElement | null = null;
|
||||||
|
private cachedCurrentGrid: HTMLElement | null = null;
|
||||||
|
|
||||||
constructor(eventBus: IEventBus, eventRenderer: EventRenderingService) {
|
constructor(eventBus: IEventBus, eventRenderer: EventRenderingService) {
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.dateCalculator = new DateCalculator(calendarConfig);
|
this.dateCalculator = new DateCalculator(calendarConfig);
|
||||||
|
|
@ -31,6 +35,37 @@ export class NavigationManager {
|
||||||
// Don't update week info immediately - wait for DOM to be ready
|
// Don't update week info immediately - wait for DOM to be ready
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached calendar container element
|
||||||
|
*/
|
||||||
|
private getCalendarContainer(): HTMLElement | null {
|
||||||
|
if (!this.cachedCalendarContainer) {
|
||||||
|
this.cachedCalendarContainer = document.querySelector('swp-calendar-container');
|
||||||
|
}
|
||||||
|
return this.cachedCalendarContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached current grid element
|
||||||
|
*/
|
||||||
|
private getCurrentGrid(): HTMLElement | null {
|
||||||
|
const container = this.getCalendarContainer();
|
||||||
|
if (!container) return null;
|
||||||
|
|
||||||
|
if (!this.cachedCurrentGrid) {
|
||||||
|
this.cachedCurrentGrid = container.querySelector('swp-grid-container:not([data-prerendered])');
|
||||||
|
}
|
||||||
|
return this.cachedCurrentGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cached DOM elements (call when DOM structure changes)
|
||||||
|
*/
|
||||||
|
private clearCache(): void {
|
||||||
|
this.cachedCalendarContainer = null;
|
||||||
|
this.cachedCurrentGrid = null;
|
||||||
|
}
|
||||||
|
|
||||||
private setupEventListeners(): void {
|
private setupEventListeners(): void {
|
||||||
// Initial DOM update when calendar is initialized
|
// Initial DOM update when calendar is initialized
|
||||||
this.eventBus.on(CoreEvents.INITIALIZED, () => {
|
this.eventBus.on(CoreEvents.INITIALIZED, () => {
|
||||||
|
|
@ -137,8 +172,8 @@ export class NavigationManager {
|
||||||
* Animation transition using pre-rendered containers when available
|
* 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 = this.getCalendarContainer();
|
||||||
const currentGrid = container?.querySelector('swp-grid-container:not([data-prerendered])');
|
const currentGrid = this.getCurrentGrid();
|
||||||
|
|
||||||
if (!container || !currentGrid) {
|
if (!container || !currentGrid) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -148,7 +183,7 @@ export class NavigationManager {
|
||||||
let newGrid: HTMLElement;
|
let newGrid: HTMLElement;
|
||||||
|
|
||||||
// Always create a fresh container for consistent behavior
|
// Always create a fresh container for consistent behavior
|
||||||
newGrid = this.navigationRenderer.renderContainer(container as HTMLElement, targetWeek);
|
newGrid = this.navigationRenderer.renderContainer(container, targetWeek);
|
||||||
|
|
||||||
|
|
||||||
// Clear any existing transforms before animation
|
// Clear any existing transforms before animation
|
||||||
|
|
@ -187,6 +222,9 @@ export class NavigationManager {
|
||||||
newGrid.style.position = 'relative';
|
newGrid.style.position = 'relative';
|
||||||
newGrid.removeAttribute('data-prerendered');
|
newGrid.removeAttribute('data-prerendered');
|
||||||
|
|
||||||
|
// Clear cache since DOM structure changed
|
||||||
|
this.clearCache();
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
this.currentWeek = new Date(targetWeek);
|
this.currentWeek = new Date(targetWeek);
|
||||||
this.animationQueue--;
|
this.animationQueue--;
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
private draggedClone: HTMLElement | null = null;
|
private draggedClone: HTMLElement | null = null;
|
||||||
private originalEvent: HTMLElement | null = null;
|
private originalEvent: HTMLElement | null = null;
|
||||||
|
|
||||||
constructor(config: CalendarConfig) {
|
constructor(config: CalendarConfig, dateCalculator?: DateCalculator) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.dateCalculator = new DateCalculator(config);
|
this.dateCalculator = dateCalculator || new DateCalculator(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,13 +68,28 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
// Handle navigation period change (when slide animation completes)
|
// Handle navigation period change (when slide animation completes)
|
||||||
eventBus.on(CoreEvents.PERIOD_CHANGED, () => {
|
eventBus.on(CoreEvents.PERIOD_CHANGED, () => {
|
||||||
// Animate all-day height after navigation completes
|
// Animate all-day height after navigation completes
|
||||||
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
|
this.triggerAllDayHeightAnimation();
|
||||||
const headerRenderer = new DateHeaderRenderer();
|
|
||||||
headerRenderer.checkAndAnimateAllDayHeight();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* 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 {
|
private formatTime(input: number | Date | string): string {
|
||||||
const hours = Math.floor(totalMinutes / 60) % 24;
|
let hours: number, minutes: number;
|
||||||
const minutes = totalMinutes % 60;
|
|
||||||
|
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 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}`;
|
return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,10 +357,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
this.draggedClone = allDayEvent;
|
this.draggedClone = allDayEvent;
|
||||||
|
|
||||||
// Check if height animation is needed
|
// Check if height animation is needed
|
||||||
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
|
this.triggerAllDayHeightAnimation();
|
||||||
const headerRenderer = new DateHeaderRenderer();
|
|
||||||
headerRenderer.checkAndAnimateAllDayHeight();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -556,9 +579,9 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
|
|
||||||
// Color is now handled by CSS classes based on data-type attribute
|
// Color is now handled by CSS classes based on data-type attribute
|
||||||
|
|
||||||
// Format time for display
|
// Format time for display using unified method
|
||||||
const startTime = this.formatTimeFromISOString(event.start);
|
const startTime = this.formatTime(event.start);
|
||||||
const endTime = this.formatTimeFromISOString(event.end);
|
const endTime = this.formatTime(event.end);
|
||||||
|
|
||||||
// Calculate duration in minutes
|
// Calculate duration in minutes
|
||||||
const startDate = new Date(event.start);
|
const startDate = new Date(event.start);
|
||||||
|
|
@ -599,17 +622,6 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
return { top, height };
|
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
|
* Calculate grid column span for event
|
||||||
*/
|
*/
|
||||||
|
|
@ -666,8 +678,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
* Date-based event renderer
|
* Date-based event renderer
|
||||||
*/
|
*/
|
||||||
export class DateEventRenderer extends BaseEventRenderer {
|
export class DateEventRenderer extends BaseEventRenderer {
|
||||||
constructor(config: CalendarConfig) {
|
constructor(config: CalendarConfig, dateCalculator?: DateCalculator) {
|
||||||
super(config);
|
super(config, dateCalculator);
|
||||||
this.setupDragEventListeners();
|
this.setupDragEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ export class NavigationRenderer {
|
||||||
private dateCalculator: DateCalculator;
|
private dateCalculator: DateCalculator;
|
||||||
private eventRenderer: EventRenderingService;
|
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) {
|
constructor(eventBus: IEventBus, config: CalendarConfig, eventRenderer: EventRenderingService) {
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
@ -24,6 +28,34 @@ export class NavigationRenderer {
|
||||||
this.setupEventListeners();
|
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
|
* 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 {
|
private updateWeekInfoInDOM(weekNumber: number, dateRange: string): void {
|
||||||
const weekNumberElement = document.querySelector('swp-week-number');
|
const weekNumberElement = this.getWeekNumberElement();
|
||||||
const dateRangeElement = document.querySelector('swp-date-range');
|
const dateRangeElement = this.getDateRangeElement();
|
||||||
|
|
||||||
if (weekNumberElement) {
|
if (weekNumberElement) {
|
||||||
weekNumberElement.textContent = `Week ${weekNumber}`;
|
weekNumberElement.textContent = `Week ${weekNumber}`;
|
||||||
|
|
@ -235,4 +267,11 @@ export class NavigationRenderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public cleanup method for cached elements
|
||||||
|
*/
|
||||||
|
public destroy(): void {
|
||||||
|
this.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue