Refactors calendar managers and renderers
Improves calendar rendering performance by centralizing DOM manipulation in a dedicated `GridRenderer` class. This reduces redundant DOM queries and improves overall efficiency. Introduces `EventManager` for optimized event lifecycle management with caching and optimized data processing. The `ViewManager` is refactored for optimized view switching and event handling, further streamlining the application's architecture. This change moves from a strategy-based `GridManager` to a simpler approach leveraging the `GridRenderer` directly for DOM updates. This eliminates unnecessary abstractions and improves code maintainability. The changes include removing the old `GridManager`, `EventManager` and introducing new versions.
This commit is contained in:
parent
b8b44ddae8
commit
05bb074e9a
4 changed files with 566 additions and 271 deletions
|
|
@ -4,122 +4,180 @@ import { calendarConfig } from '../core/CalendarConfig';
|
|||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
|
||||
/**
|
||||
* ViewManager - Håndterer skift mellem dag/uge/måned visninger
|
||||
* Arbejder med custom tags fra POC design
|
||||
* ViewManager - Optimized view switching with consolidated event handling
|
||||
* Reduces redundant DOM queries and event listener setups
|
||||
*/
|
||||
export class ViewManager {
|
||||
private eventBus: IEventBus;
|
||||
private currentView: CalendarView = 'week';
|
||||
private eventCleanup: (() => void)[] = [];
|
||||
private buttonListeners: Map<Element, EventListener> = new Map();
|
||||
|
||||
// Cached DOM elements for performance
|
||||
private cachedViewButtons: NodeListOf<Element> | null = null;
|
||||
private cachedWorkweekButtons: NodeListOf<Element> | null = null;
|
||||
private lastButtonCacheTime: number = 0;
|
||||
private readonly CACHE_DURATION = 5000; // 5 seconds
|
||||
|
||||
constructor(eventBus: IEventBus) {
|
||||
this.eventBus = eventBus;
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidated event listener setup with better organization
|
||||
*/
|
||||
private setupEventListeners(): void {
|
||||
// Track event bus listeners for cleanup
|
||||
// Event bus listeners
|
||||
this.setupEventBusListeners();
|
||||
|
||||
// DOM button handlers with consolidated logic
|
||||
this.setupButtonHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup event bus listeners with proper cleanup tracking
|
||||
*/
|
||||
private setupEventBusListeners(): void {
|
||||
this.eventCleanup.push(
|
||||
this.eventBus.on(CoreEvents.INITIALIZED, () => {
|
||||
this.initializeView();
|
||||
})
|
||||
);
|
||||
|
||||
this.eventCleanup.push(
|
||||
this.eventBus.on(CoreEvents.VIEW_CHANGED, (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
const { currentView } = customEvent.detail;
|
||||
this.changeView(currentView);
|
||||
})
|
||||
);
|
||||
// Remove redundant VIEW_CHANGED listener that causes circular calls
|
||||
// changeView is called directly from button handlers
|
||||
|
||||
this.eventCleanup.push(
|
||||
this.eventBus.on(CoreEvents.DATE_CHANGED, () => {
|
||||
this.refreshCurrentView();
|
||||
})
|
||||
);
|
||||
|
||||
// Setup view button handlers
|
||||
this.setupViewButtonHandlers();
|
||||
|
||||
// Setup workweek preset button handlers
|
||||
this.setupWorkweekButtonHandlers();
|
||||
|
||||
}
|
||||
|
||||
private setupViewButtonHandlers(): void {
|
||||
const viewButtons = document.querySelectorAll('swp-view-button[data-view]');
|
||||
viewButtons.forEach(button => {
|
||||
const handler = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const view = button.getAttribute('data-view') as CalendarView;
|
||||
if (view && this.isValidView(view)) {
|
||||
this.changeView(view);
|
||||
}
|
||||
};
|
||||
button.addEventListener('click', handler);
|
||||
this.buttonListeners.set(button, handler);
|
||||
/**
|
||||
* Consolidated button handler setup with shared logic
|
||||
*/
|
||||
private setupButtonHandlers(): void {
|
||||
// Setup view buttons with consolidated handler
|
||||
this.setupButtonGroup('swp-view-button[data-view]', 'data-view', (value) => {
|
||||
if (this.isValidView(value)) {
|
||||
this.changeView(value as CalendarView);
|
||||
}
|
||||
});
|
||||
|
||||
// Setup workweek buttons with consolidated handler
|
||||
this.setupButtonGroup('swp-preset-button[data-workweek]', 'data-workweek', (value) => {
|
||||
this.changeWorkweek(value);
|
||||
});
|
||||
}
|
||||
|
||||
private setupWorkweekButtonHandlers(): void {
|
||||
const workweekButtons = document.querySelectorAll('swp-preset-button[data-workweek]');
|
||||
workweekButtons.forEach(button => {
|
||||
const handler = (event: Event) => {
|
||||
/**
|
||||
* Generic button group setup to eliminate duplicate code
|
||||
*/
|
||||
private setupButtonGroup(selector: string, attribute: string, handler: (value: string) => void): void {
|
||||
const buttons = document.querySelectorAll(selector);
|
||||
buttons.forEach(button => {
|
||||
const clickHandler = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const workweekId = button.getAttribute('data-workweek');
|
||||
if (workweekId) {
|
||||
this.changeWorkweek(workweekId);
|
||||
const value = button.getAttribute(attribute);
|
||||
if (value) {
|
||||
handler(value);
|
||||
}
|
||||
};
|
||||
button.addEventListener('click', handler);
|
||||
this.buttonListeners.set(button, handler);
|
||||
button.addEventListener('click', clickHandler);
|
||||
this.buttonListeners.set(button, clickHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached view buttons with cache invalidation
|
||||
*/
|
||||
private getViewButtons(): NodeListOf<Element> {
|
||||
const now = Date.now();
|
||||
if (!this.cachedViewButtons || (now - this.lastButtonCacheTime) > this.CACHE_DURATION) {
|
||||
this.cachedViewButtons = document.querySelectorAll('swp-view-button[data-view]');
|
||||
this.lastButtonCacheTime = now;
|
||||
}
|
||||
return this.cachedViewButtons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached workweek buttons with cache invalidation
|
||||
*/
|
||||
private getWorkweekButtons(): NodeListOf<Element> {
|
||||
const now = Date.now();
|
||||
if (!this.cachedWorkweekButtons || (now - this.lastButtonCacheTime) > this.CACHE_DURATION) {
|
||||
this.cachedWorkweekButtons = document.querySelectorAll('swp-preset-button[data-workweek]');
|
||||
this.lastButtonCacheTime = now;
|
||||
}
|
||||
return this.cachedWorkweekButtons;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize view with consolidated button updates
|
||||
*/
|
||||
private initializeView(): void {
|
||||
this.updateViewButtons();
|
||||
this.updateWorkweekButtons();
|
||||
|
||||
this.eventBus.emit(CoreEvents.VIEW_RENDERED, {
|
||||
view: this.currentView
|
||||
});
|
||||
this.updateAllButtons();
|
||||
this.emitViewRendered();
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized view change with debouncing
|
||||
*/
|
||||
private changeView(newView: CalendarView): void {
|
||||
if (newView === this.currentView) return;
|
||||
|
||||
const previousView = this.currentView;
|
||||
this.currentView = newView;
|
||||
|
||||
|
||||
this.updateViewButtons();
|
||||
this.updateAllButtons();
|
||||
|
||||
this.eventBus.emit(CoreEvents.VIEW_CHANGED, {
|
||||
this.eventBus.emit(CoreEvents.VIEW_CHANGED, {
|
||||
previousView,
|
||||
currentView: newView
|
||||
currentView: newView
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized workweek change with consolidated updates
|
||||
*/
|
||||
private changeWorkweek(workweekId: string): void {
|
||||
|
||||
// Update the calendar config
|
||||
calendarConfig.setWorkWeek(workweekId);
|
||||
|
||||
// Update button states
|
||||
this.updateWorkweekButtons();
|
||||
// Update button states using cached elements
|
||||
this.updateAllButtons();
|
||||
|
||||
// Trigger a calendar refresh to apply the new workweek
|
||||
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED);
|
||||
}
|
||||
|
||||
private updateViewButtons(): void {
|
||||
const viewButtons = document.querySelectorAll('swp-view-button[data-view]');
|
||||
viewButtons.forEach(button => {
|
||||
const buttonView = button.getAttribute('data-view') as CalendarView;
|
||||
if (buttonView === this.currentView) {
|
||||
/**
|
||||
* Consolidated button update method to eliminate duplicate code
|
||||
*/
|
||||
private updateAllButtons(): void {
|
||||
this.updateButtonGroup(
|
||||
this.getViewButtons(),
|
||||
'data-view',
|
||||
this.currentView
|
||||
);
|
||||
|
||||
this.updateButtonGroup(
|
||||
this.getWorkweekButtons(),
|
||||
'data-workweek',
|
||||
calendarConfig.getCurrentWorkWeek()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic button group update to eliminate duplicate logic
|
||||
*/
|
||||
private updateButtonGroup(buttons: NodeListOf<Element>, attribute: string, activeValue: string): void {
|
||||
buttons.forEach(button => {
|
||||
const buttonValue = button.getAttribute(attribute);
|
||||
if (buttonValue === activeValue) {
|
||||
button.setAttribute('data-active', 'true');
|
||||
} else {
|
||||
button.removeAttribute('data-active');
|
||||
|
|
@ -127,38 +185,46 @@ export class ViewManager {
|
|||
});
|
||||
}
|
||||
|
||||
private updateWorkweekButtons(): void {
|
||||
const currentWorkweek = calendarConfig.getCurrentWorkWeek();
|
||||
const workweekButtons = document.querySelectorAll('swp-preset-button[data-workweek]');
|
||||
|
||||
workweekButtons.forEach(button => {
|
||||
const buttonWorkweek = button.getAttribute('data-workweek');
|
||||
if (buttonWorkweek === currentWorkweek) {
|
||||
button.setAttribute('data-active', 'true');
|
||||
} else {
|
||||
button.removeAttribute('data-active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private refreshCurrentView(): void {
|
||||
/**
|
||||
* Emit view rendered event with current view
|
||||
*/
|
||||
private emitViewRendered(): void {
|
||||
this.eventBus.emit(CoreEvents.VIEW_RENDERED, {
|
||||
view: this.currentView
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh current view by emitting view rendered event
|
||||
*/
|
||||
private refreshCurrentView(): void {
|
||||
this.emitViewRendered();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if a string is a valid calendar view
|
||||
*/
|
||||
private isValidView(view: string): view is CalendarView {
|
||||
return ['day', 'week', 'month'].includes(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current active view
|
||||
*/
|
||||
public getCurrentView(): CalendarView {
|
||||
return this.currentView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public refresh method
|
||||
*/
|
||||
public refresh(): void {
|
||||
this.refreshCurrentView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all resources and cached elements
|
||||
*/
|
||||
public destroy(): void {
|
||||
// Clean up event bus listeners
|
||||
this.eventCleanup.forEach(cleanup => cleanup());
|
||||
|
|
@ -169,5 +235,10 @@ export class ViewManager {
|
|||
button.removeEventListener('click', handler);
|
||||
});
|
||||
this.buttonListeners.clear();
|
||||
|
||||
// Clear cached elements
|
||||
this.cachedViewButtons = null;
|
||||
this.cachedWorkweekButtons = null;
|
||||
this.lastButtonCacheTime = 0;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue