Addresses two key issues related to dragging events to the header: the premature removal of the original event element and the creation of duplicate all-day events. The original event is no longer removed when dragging to the header; it is now only removed upon a successful drop. Also, it prevents the creation of duplicate all-day events by checking for existing all-day events before creating new ones, using DOM queries to ensure accurate state.
244 lines
No EOL
7.3 KiB
TypeScript
244 lines
No EOL
7.3 KiB
TypeScript
import { EventBus } from '../core/EventBus';
|
|
import { CalendarView, IEventBus } from '../types/CalendarTypes';
|
|
import { calendarConfig } from '../core/CalendarConfig';
|
|
import { CoreEvents } from '../constants/CoreEvents';
|
|
|
|
/**
|
|
* 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 {
|
|
// 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();
|
|
})
|
|
);
|
|
|
|
// 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();
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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 value = button.getAttribute(attribute);
|
|
if (value) {
|
|
handler(value);
|
|
}
|
|
};
|
|
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.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.updateAllButtons();
|
|
|
|
this.eventBus.emit(CoreEvents.VIEW_CHANGED, {
|
|
previousView,
|
|
currentView: newView
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Optimized workweek change with consolidated updates
|
|
*/
|
|
private changeWorkweek(workweekId: string): void {
|
|
// Update the calendar config
|
|
calendarConfig.setWorkWeek(workweekId);
|
|
|
|
// Update button states using cached elements
|
|
this.updateAllButtons();
|
|
|
|
// Trigger a workweek change to apply the new workweek
|
|
this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED);
|
|
}
|
|
|
|
/**
|
|
* 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');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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());
|
|
this.eventCleanup = [];
|
|
|
|
// Clean up button listeners
|
|
this.buttonListeners.forEach((handler, button) => {
|
|
button.removeEventListener('click', handler);
|
|
});
|
|
this.buttonListeners.clear();
|
|
|
|
// Clear cached elements
|
|
this.cachedViewButtons = null;
|
|
this.cachedWorkweekButtons = null;
|
|
this.lastButtonCacheTime = 0;
|
|
}
|
|
} |