Refactors calendar managers to components
Reorganizes navigation, view selector, and workweek preset managers into separate component files Improves code organization by moving specialized manager classes to a more descriptive components directory Adds enhanced logging for all-day event management and drag-and-drop interactions
This commit is contained in:
parent
bd8f5ae6c6
commit
b566aafb19
5 changed files with 73 additions and 15 deletions
|
|
@ -9,6 +9,7 @@ import { ICalendarEvent } from '../types/CalendarTypes';
|
|||
import { SwpAllDayEventElement } from '../elements/SwpEventElement';
|
||||
import {
|
||||
IDragMouseEnterHeaderEventPayload,
|
||||
IDragMouseEnterColumnEventPayload,
|
||||
IDragStartEventPayload,
|
||||
IDragMoveEventPayload,
|
||||
IDragEndEventPayload,
|
||||
|
|
@ -107,12 +108,45 @@ export class AllDayManager {
|
|||
});
|
||||
|
||||
eventBus.on('drag:end', (event) => {
|
||||
let draggedElement: IDragEndEventPayload = (event as CustomEvent<IDragEndEventPayload>).detail;
|
||||
let dragEndPayload: IDragEndEventPayload = (event as CustomEvent<IDragEndEventPayload>).detail;
|
||||
|
||||
if (draggedElement.target != 'swp-day-header') // we are not inside the swp-day-header, so just ignore.
|
||||
console.log('🎯 AllDayManager: drag:end received', {
|
||||
target: dragEndPayload.target,
|
||||
originalElementTag: dragEndPayload.originalElement?.tagName,
|
||||
hasAllDayAttribute: dragEndPayload.originalElement?.hasAttribute('data-allday'),
|
||||
eventId: dragEndPayload.originalElement?.dataset.eventId
|
||||
});
|
||||
|
||||
// Handle all-day → all-day drops (within header)
|
||||
if (dragEndPayload.target === 'swp-day-header') {
|
||||
console.log('✅ AllDayManager: Handling all-day → all-day drop');
|
||||
this.handleDragEnd(dragEndPayload);
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleDragEnd(draggedElement);
|
||||
// Handle all-day → timed conversion (dropped in column)
|
||||
if (dragEndPayload.target === 'swp-day-column' && dragEndPayload.originalElement?.hasAttribute('data-allday')) {
|
||||
const eventId = dragEndPayload.originalElement.dataset.eventId;
|
||||
|
||||
console.log('🔄 AllDayManager: All-day → timed conversion', {
|
||||
eventId,
|
||||
currentLayoutsCount: this.currentLayouts.length,
|
||||
layoutsBeforeFilter: this.currentLayouts.map(l => l.calenderEvent.id)
|
||||
});
|
||||
|
||||
// Remove event from currentLayouts since it's now a timed event
|
||||
this.currentLayouts = this.currentLayouts.filter(
|
||||
layout => layout.calenderEvent.id !== eventId
|
||||
);
|
||||
|
||||
console.log('📊 AllDayManager: After filter', {
|
||||
currentLayoutsCount: this.currentLayouts.length,
|
||||
layoutsAfterFilter: this.currentLayouts.map(l => l.calenderEvent.id)
|
||||
});
|
||||
|
||||
// Recalculate and animate header height
|
||||
this.checkAndAnimateAllDayHeight();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for drag cancellation to recalculate height
|
||||
|
|
@ -189,6 +223,15 @@ export class AllDayManager {
|
|||
* Check current all-day events and animate to correct height
|
||||
*/
|
||||
public checkAndAnimateAllDayHeight(): void {
|
||||
console.log('📏 AllDayManager: checkAndAnimateAllDayHeight called', {
|
||||
currentLayoutsCount: this.currentLayouts.length,
|
||||
layouts: this.currentLayouts.map(l => ({
|
||||
id: l.calenderEvent.id,
|
||||
row: l.row,
|
||||
title: l.calenderEvent.title
|
||||
}))
|
||||
});
|
||||
|
||||
// Calculate required rows - 0 if no events (will collapse)
|
||||
let maxRows = 0;
|
||||
|
||||
|
|
@ -205,6 +248,12 @@ export class AllDayManager {
|
|||
|
||||
}
|
||||
|
||||
console.log('📊 AllDayManager: Height calculation', {
|
||||
maxRows,
|
||||
currentLayoutsLength: this.currentLayouts.length,
|
||||
isExpanded: this.isExpanded
|
||||
});
|
||||
|
||||
// Store actual row count
|
||||
this.actualRowCount = maxRows;
|
||||
|
||||
|
|
@ -233,6 +282,12 @@ export class AllDayManager {
|
|||
this.clearOverflowIndicators();
|
||||
}
|
||||
|
||||
console.log('🎬 AllDayManager: Will animate to', {
|
||||
displayRows,
|
||||
maxRows,
|
||||
willAnimate: displayRows !== this.actualRowCount
|
||||
});
|
||||
|
||||
// Animate to required rows (0 = collapse, >0 = expand)
|
||||
this.animateToRows(displayRows);
|
||||
}
|
||||
|
|
@ -339,6 +394,9 @@ export class AllDayManager {
|
|||
|
||||
ColumnDetectionUtils.updateColumnBoundsCache();
|
||||
|
||||
// Recalculate height after adding all-day event
|
||||
this.checkAndAnimateAllDayHeight();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
import { IEventBus } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
|
||||
/**
|
||||
* NavigationButtonsManager - Manages navigation button UI and state
|
||||
*
|
||||
* RESPONSIBILITY:
|
||||
* ===============
|
||||
* This manager owns all logic related to the <swp-nav-group> UI element.
|
||||
* It follows the principle that each functional UI element has its own manager.
|
||||
*
|
||||
* RESPONSIBILITIES:
|
||||
* - Handles button clicks on swp-nav-button elements
|
||||
* - Validates navigation actions (prev, next, today)
|
||||
* - Emits NAV_BUTTON_CLICKED events
|
||||
* - Manages button UI listeners
|
||||
*
|
||||
* EVENT FLOW:
|
||||
* ===========
|
||||
* User clicks button → validateAction() → emit event → NavigationManager handles navigation
|
||||
*
|
||||
* SUBSCRIBERS:
|
||||
* ============
|
||||
* - NavigationManager: Performs actual navigation logic (animations, grid updates, week calculations)
|
||||
*/
|
||||
export class NavigationButtonsManager {
|
||||
private eventBus: IEventBus;
|
||||
private buttonListeners: Map<Element, EventListener> = new Map();
|
||||
|
||||
constructor(eventBus: IEventBus) {
|
||||
this.eventBus = eventBus;
|
||||
this.setupButtonListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup click listeners on all navigation buttons
|
||||
*/
|
||||
private setupButtonListeners(): void {
|
||||
const buttons = document.querySelectorAll('swp-nav-button[data-action]');
|
||||
|
||||
buttons.forEach(button => {
|
||||
const clickHandler = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const action = button.getAttribute('data-action');
|
||||
if (action && this.isValidAction(action)) {
|
||||
this.handleNavigation(action);
|
||||
}
|
||||
};
|
||||
|
||||
button.addEventListener('click', clickHandler);
|
||||
this.buttonListeners.set(button, clickHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle navigation action
|
||||
*/
|
||||
private handleNavigation(action: string): void {
|
||||
// Emit navigation button clicked event
|
||||
this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, {
|
||||
action: action
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if string is a valid navigation action
|
||||
*/
|
||||
private isValidAction(action: string): boolean {
|
||||
return ['prev', 'next', 'today'].includes(action);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
import { CalendarView, IEventBus } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { Configuration } from '../configurations/CalendarConfig';
|
||||
|
||||
/**
|
||||
* ViewSelectorManager - Manages view selector UI and state
|
||||
*
|
||||
* RESPONSIBILITY:
|
||||
* ===============
|
||||
* This manager owns all logic related to the <swp-view-selector> UI element.
|
||||
* It follows the principle that each functional UI element has its own manager.
|
||||
*
|
||||
* RESPONSIBILITIES:
|
||||
* - Handles button clicks on swp-view-button elements
|
||||
* - Manages current view state (day/week/month)
|
||||
* - Validates view values
|
||||
* - Emits VIEW_CHANGED and VIEW_RENDERED events
|
||||
* - Updates button UI states (data-active attributes)
|
||||
*
|
||||
* EVENT FLOW:
|
||||
* ===========
|
||||
* User clicks button → changeView() → validate → update state → emit event → update UI
|
||||
*
|
||||
* IMPLEMENTATION STATUS:
|
||||
* ======================
|
||||
* - Week view: FULLY IMPLEMENTED
|
||||
* - Day view: NOT IMPLEMENTED (button exists but no rendering)
|
||||
* - Month view: NOT IMPLEMENTED (button exists but no rendering)
|
||||
*
|
||||
* SUBSCRIBERS:
|
||||
* ============
|
||||
* - GridRenderer: Uses view parameter (currently only supports 'week')
|
||||
* - Future: DayRenderer, MonthRenderer when implemented
|
||||
*/
|
||||
export class ViewSelectorManager {
|
||||
private eventBus: IEventBus;
|
||||
private config: Configuration;
|
||||
private buttonListeners: Map<Element, EventListener> = new Map();
|
||||
|
||||
constructor(eventBus: IEventBus, config: Configuration) {
|
||||
this.eventBus = eventBus;
|
||||
this.config = config;
|
||||
|
||||
this.setupButtonListeners();
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup click listeners on all view selector buttons
|
||||
*/
|
||||
private setupButtonListeners(): void {
|
||||
const buttons = document.querySelectorAll('swp-view-button[data-view]');
|
||||
|
||||
buttons.forEach(button => {
|
||||
const clickHandler = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const view = button.getAttribute('data-view');
|
||||
if (view && this.isValidView(view)) {
|
||||
this.changeView(view as CalendarView);
|
||||
}
|
||||
};
|
||||
|
||||
button.addEventListener('click', clickHandler);
|
||||
this.buttonListeners.set(button, clickHandler);
|
||||
});
|
||||
|
||||
// Initialize button states
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup event bus listeners
|
||||
*/
|
||||
private setupEventListeners(): void {
|
||||
this.eventBus.on(CoreEvents.INITIALIZED, () => {
|
||||
this.initializeView();
|
||||
});
|
||||
|
||||
this.eventBus.on(CoreEvents.DATE_CHANGED, () => {
|
||||
this.refreshCurrentView();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the active view
|
||||
*/
|
||||
private changeView(newView: CalendarView): void {
|
||||
if (newView === this.config.currentView) {
|
||||
return; // No change
|
||||
}
|
||||
|
||||
const previousView = this.config.currentView;
|
||||
this.config.currentView = newView;
|
||||
|
||||
// Update button UI states
|
||||
this.updateButtonStates();
|
||||
|
||||
// Emit event for subscribers
|
||||
this.eventBus.emit(CoreEvents.VIEW_CHANGED, {
|
||||
previousView,
|
||||
currentView: newView
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update button states (data-active attributes)
|
||||
*/
|
||||
private updateButtonStates(): void {
|
||||
const buttons = document.querySelectorAll('swp-view-button[data-view]');
|
||||
|
||||
buttons.forEach(button => {
|
||||
const buttonView = button.getAttribute('data-view');
|
||||
|
||||
if (buttonView === this.config.currentView) {
|
||||
button.setAttribute('data-active', 'true');
|
||||
} else {
|
||||
button.removeAttribute('data-active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize view on INITIALIZED event
|
||||
*/
|
||||
private initializeView(): void {
|
||||
this.updateButtonStates();
|
||||
this.emitViewRendered();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit VIEW_RENDERED event
|
||||
*/
|
||||
private emitViewRendered(): void {
|
||||
this.eventBus.emit(CoreEvents.VIEW_RENDERED, {
|
||||
view: this.config.currentView
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh current view on DATE_CHANGED event
|
||||
*/
|
||||
private refreshCurrentView(): void {
|
||||
this.emitViewRendered();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if string is a valid CalendarView type
|
||||
*/
|
||||
private isValidView(view: string): view is CalendarView {
|
||||
return ['day', 'week', 'month'].includes(view);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
import { IEventBus } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { IWorkWeekSettings } from '../configurations/WorkWeekSettings';
|
||||
import { WORK_WEEK_PRESETS, Configuration } from '../configurations/CalendarConfig';
|
||||
|
||||
/**
|
||||
* WorkweekPresetsManager - Manages workweek preset UI and state
|
||||
*
|
||||
* RESPONSIBILITY:
|
||||
* ===============
|
||||
* This manager owns all logic related to the <swp-workweek-presets> UI element.
|
||||
* It follows the principle that each functional UI element has its own manager.
|
||||
*
|
||||
* RESPONSIBILITIES:
|
||||
* - Owns WORK_WEEK_PRESETS data
|
||||
* - Handles button clicks on swp-preset-button elements
|
||||
* - Manages current workweek preset state
|
||||
* - Validates preset IDs
|
||||
* - Emits WORKWEEK_CHANGED events
|
||||
* - Updates button UI states (data-active attributes)
|
||||
*
|
||||
* EVENT FLOW:
|
||||
* ===========
|
||||
* User clicks button → changePreset() → validate → update state → emit event → update UI
|
||||
*
|
||||
* SUBSCRIBERS:
|
||||
* ============
|
||||
* - ConfigManager: Updates CSS variables (--grid-columns)
|
||||
* - GridManager: Re-renders grid with new column count
|
||||
* - CalendarManager: Relays to header update (via workweek:header-update)
|
||||
* - HeaderManager: Updates date headers
|
||||
*/
|
||||
export class WorkweekPresetsManager {
|
||||
private eventBus: IEventBus;
|
||||
private config: Configuration;
|
||||
private buttonListeners: Map<Element, EventListener> = new Map();
|
||||
|
||||
constructor(eventBus: IEventBus, config: Configuration) {
|
||||
this.eventBus = eventBus;
|
||||
this.config = config;
|
||||
|
||||
this.setupButtonListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup click listeners on all workweek preset buttons
|
||||
*/
|
||||
private setupButtonListeners(): void {
|
||||
const buttons = document.querySelectorAll('swp-preset-button[data-workweek]');
|
||||
|
||||
buttons.forEach(button => {
|
||||
const clickHandler = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const presetId = button.getAttribute('data-workweek');
|
||||
if (presetId) {
|
||||
this.changePreset(presetId);
|
||||
}
|
||||
};
|
||||
|
||||
button.addEventListener('click', clickHandler);
|
||||
this.buttonListeners.set(button, clickHandler);
|
||||
});
|
||||
|
||||
// Initialize button states
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the active workweek preset
|
||||
*/
|
||||
private changePreset(presetId: string): void {
|
||||
if (!WORK_WEEK_PRESETS[presetId]) {
|
||||
console.warn(`Invalid preset ID "${presetId}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (presetId === this.config.currentWorkWeek) {
|
||||
return; // No change
|
||||
}
|
||||
|
||||
const previousPresetId = this.config.currentWorkWeek;
|
||||
this.config.currentWorkWeek = presetId;
|
||||
|
||||
const settings = WORK_WEEK_PRESETS[presetId];
|
||||
|
||||
// Update button UI states
|
||||
this.updateButtonStates();
|
||||
|
||||
// Emit event for subscribers
|
||||
this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED, {
|
||||
workWeekId: presetId,
|
||||
previousWorkWeekId: previousPresetId,
|
||||
settings: settings
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update button states (data-active attributes)
|
||||
*/
|
||||
private updateButtonStates(): void {
|
||||
const buttons = document.querySelectorAll('swp-preset-button[data-workweek]');
|
||||
|
||||
buttons.forEach(button => {
|
||||
const buttonPresetId = button.getAttribute('data-workweek');
|
||||
|
||||
if (buttonPresetId === this.config.currentWorkWeek) {
|
||||
button.setAttribute('data-active', 'true');
|
||||
} else {
|
||||
button.removeAttribute('data-active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue