Improves all-day event rendering and handling
Refactors all-day event container handling to improve rendering and event delegation. Ensures all-day containers are consistently created and managed, preventing issues with event display and drag-and-drop functionality. Moves event listener setup into dedicated methods for better organization and reusability.
This commit is contained in:
parent
f2763ad826
commit
07402a07d9
6 changed files with 142 additions and 44 deletions
|
|
@ -80,9 +80,11 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear any existing all-day containers first
|
// Clear content of existing all-day containers (but keep the containers)
|
||||||
const existingContainers = calendarHeader.querySelectorAll('swp-allday-container');
|
const existingContainers = calendarHeader.querySelectorAll('swp-allday-container');
|
||||||
existingContainers.forEach(container => container.remove());
|
existingContainers.forEach(container => {
|
||||||
|
container.innerHTML = ''; // Clear content, keep container
|
||||||
|
});
|
||||||
|
|
||||||
// Track maximum number of stacked events to calculate row height
|
// Track maximum number of stacked events to calculate row height
|
||||||
let maxStackHeight = 0;
|
let maxStackHeight = 0;
|
||||||
|
|
@ -142,20 +144,31 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnSpan = endColumn - startColumn + 1;
|
const columnSpan = endColumn - startColumn + 1;
|
||||||
|
let allDayContainer: Element | null = null;
|
||||||
|
|
||||||
// Create or find container for this column span
|
if (columnSpan === 1) {
|
||||||
const containerKey = `${startColumn}-${columnSpan}`;
|
// Single day event - use existing single-day container
|
||||||
let allDayContainer = calendarHeader.querySelector(`swp-allday-container[data-container-key="${containerKey}"]`);
|
const containerKey = `${startColumn}-1`;
|
||||||
|
allDayContainer = calendarHeader.querySelector(`swp-allday-container[data-container-key="${containerKey}"]`);
|
||||||
|
} else {
|
||||||
|
// Multi-day event - create or find spanning container
|
||||||
|
const containerKey = `${startColumn}-${columnSpan}`;
|
||||||
|
allDayContainer = calendarHeader.querySelector(`swp-allday-container[data-container-key="${containerKey}"]`);
|
||||||
|
|
||||||
|
if (!allDayContainer) {
|
||||||
|
// Create container that spans the appropriate columns
|
||||||
|
allDayContainer = document.createElement('swp-allday-container');
|
||||||
|
allDayContainer.setAttribute('data-container-key', containerKey);
|
||||||
|
allDayContainer.setAttribute('data-date', this.dateCalculator.formatISODate(startDate));
|
||||||
|
(allDayContainer as HTMLElement).style.gridColumn = `${startColumn} / span ${columnSpan}`;
|
||||||
|
(allDayContainer as HTMLElement).style.gridRow = '2';
|
||||||
|
calendarHeader.appendChild(allDayContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!allDayContainer) {
|
if (!allDayContainer) {
|
||||||
// Create container that spans the appropriate columns
|
console.warn(`BaseEventRenderer: No container found for event "${event.title}"`);
|
||||||
allDayContainer = document.createElement('swp-allday-container');
|
return;
|
||||||
allDayContainer.setAttribute('data-container-key', containerKey);
|
|
||||||
(allDayContainer as HTMLElement).style.gridColumn = columnSpan > 1
|
|
||||||
? `${startColumn} / span ${columnSpan}`
|
|
||||||
: `${startColumn}`;
|
|
||||||
(allDayContainer as HTMLElement).style.gridRow = '2';
|
|
||||||
calendarHeader.appendChild(allDayContainer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the all-day event element inside container
|
// Create the all-day event element inside container
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ export class EventRenderingService {
|
||||||
|
|
||||||
// Use cached strategy to render events in the specific container
|
// Use cached strategy to render events in the specific container
|
||||||
this.strategy.renderEvents(events, context.container, calendarConfig);
|
this.strategy.renderEvents(events, context.container, calendarConfig);
|
||||||
|
|
||||||
console.log(` ✅ Rendered ${events.length} events successfully`);
|
console.log(` ✅ Rendered ${events.length} events successfully`);
|
||||||
|
|
||||||
// Emit EVENTS_RENDERED event for filtering system
|
// Emit EVENTS_RENDERED event for filtering system
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { eventBus } from '../core/EventBus';
|
||||||
*/
|
*/
|
||||||
export class GridRenderer {
|
export class GridRenderer {
|
||||||
private config: CalendarConfig;
|
private config: CalendarConfig;
|
||||||
|
private headerEventListener: ((event: Event) => void) | null = null;
|
||||||
|
|
||||||
constructor(config: CalendarConfig) {
|
constructor(config: CalendarConfig) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
@ -135,31 +136,11 @@ export class GridRenderer {
|
||||||
|
|
||||||
headerRenderer.render(calendarHeader, context);
|
headerRenderer.render(calendarHeader, context);
|
||||||
|
|
||||||
// Use event delegation for mouseover detection on entire header
|
// Always ensure all-day containers exist for all days
|
||||||
calendarHeader.addEventListener('mouseover', (event) => {
|
headerRenderer.ensureAllDayContainers(calendarHeader);
|
||||||
const target = event.target as HTMLElement;
|
|
||||||
|
|
||||||
// Check what was hovered - could be day-header OR all-day-container
|
// Setup event listener for mouseover detection
|
||||||
const dayHeader = target.closest('swp-day-header');
|
this.setupHeaderEventListener(calendarHeader);
|
||||||
const allDayContainer = target.closest('swp-allday-container');
|
|
||||||
|
|
||||||
if (dayHeader || allDayContainer) {
|
|
||||||
const hoveredElement = dayHeader || allDayContainer;
|
|
||||||
const targetDate = (hoveredElement as HTMLElement).dataset.date;
|
|
||||||
|
|
||||||
console.log('GridRenderer: Detected hover over:', {
|
|
||||||
elementType: dayHeader ? 'day-header' : 'all-day-container',
|
|
||||||
targetDate,
|
|
||||||
element: hoveredElement
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.emit('header:mouseover', {
|
|
||||||
element: hoveredElement,
|
|
||||||
targetDate,
|
|
||||||
headerRenderer
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,4 +181,47 @@ export class GridRenderer {
|
||||||
// Re-render headers using Strategy Pattern - this will also re-attach the event listener
|
// Re-render headers using Strategy Pattern - this will also re-attach the event listener
|
||||||
this.renderCalendarHeader(calendarHeader as HTMLElement, currentWeek, resourceData);
|
this.renderCalendarHeader(calendarHeader as HTMLElement, currentWeek, resourceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup or re-setup event delegation listener on calendar header
|
||||||
|
*/
|
||||||
|
private setupHeaderEventListener(calendarHeader: HTMLElement): void {
|
||||||
|
// Remove existing listener if any (stored reference approach)
|
||||||
|
if (this.headerEventListener) {
|
||||||
|
calendarHeader.removeEventListener('mouseover', this.headerEventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new listener function
|
||||||
|
this.headerEventListener = (event) => {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
|
||||||
|
// Check what was hovered - could be day-header OR all-day-container
|
||||||
|
const dayHeader = target.closest('swp-day-header');
|
||||||
|
const allDayContainer = target.closest('swp-allday-container');
|
||||||
|
|
||||||
|
if (dayHeader || allDayContainer) {
|
||||||
|
const hoveredElement = dayHeader || allDayContainer;
|
||||||
|
const targetDate = (hoveredElement as HTMLElement).dataset.date;
|
||||||
|
|
||||||
|
console.log('GridRenderer: Detected hover over:', {
|
||||||
|
elementType: dayHeader ? 'day-header' : 'all-day-container',
|
||||||
|
targetDate,
|
||||||
|
element: hoveredElement
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the header renderer for addToAllDay functionality
|
||||||
|
const calendarType = this.config.getCalendarMode();
|
||||||
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
||||||
|
|
||||||
|
eventBus.emit('header:mouseover', {
|
||||||
|
element: hoveredElement,
|
||||||
|
targetDate,
|
||||||
|
headerRenderer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the new listener
|
||||||
|
calendarHeader.addEventListener('mouseover', this.headerEventListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import { DateCalculator } from '../utils/DateCalculator';
|
||||||
export interface HeaderRenderer {
|
export interface HeaderRenderer {
|
||||||
render(calendarHeader: HTMLElement, context: HeaderRenderContext): void;
|
render(calendarHeader: HTMLElement, context: HeaderRenderContext): void;
|
||||||
addToAllDay(dayHeader: HTMLElement): void;
|
addToAllDay(dayHeader: HTMLElement): void;
|
||||||
|
ensureAllDayContainers(calendarHeader: HTMLElement): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,6 +37,24 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure all-day containers exist for all days (called automatically after header render)
|
||||||
|
*/
|
||||||
|
ensureAllDayContainers(calendarHeader: HTMLElement): void {
|
||||||
|
const root = document.documentElement;
|
||||||
|
const currentHeight = parseInt(getComputedStyle(root).getPropertyValue('--all-day-row-height') || '0');
|
||||||
|
|
||||||
|
// If all-day row doesn't exist yet, set it up without animation
|
||||||
|
if (currentHeight === 0) {
|
||||||
|
root.style.setProperty('--all-day-row-height', `${ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT}px`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always ensure containers exist for all days
|
||||||
|
this.createEmptyAllDayContainers(calendarHeader);
|
||||||
|
|
||||||
|
console.log('BaseHeaderRenderer: Ensured all-day containers exist for all days');
|
||||||
|
}
|
||||||
|
|
||||||
private animateHeaderExpansion(calendarHeader: HTMLElement): void {
|
private animateHeaderExpansion(calendarHeader: HTMLElement): void {
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const currentHeaderHeight = parseInt(getComputedStyle(root).getPropertyValue('--header-height'));
|
const currentHeaderHeight = parseInt(getComputedStyle(root).getPropertyValue('--header-height'));
|
||||||
|
|
@ -126,7 +145,7 @@ export class DateHeaderRenderer extends BaseHeaderRenderer {
|
||||||
private dateCalculator!: DateCalculator;
|
private dateCalculator!: DateCalculator;
|
||||||
|
|
||||||
render(calendarHeader: HTMLElement, context: HeaderRenderContext): void {
|
render(calendarHeader: HTMLElement, context: HeaderRenderContext): void {
|
||||||
const { currentWeek, config, allDayEvents = [] } = context;
|
const { currentWeek, config } = context;
|
||||||
|
|
||||||
// Initialize date calculator with config
|
// Initialize date calculator with config
|
||||||
this.dateCalculator = new DateCalculator(config);
|
this.dateCalculator = new DateCalculator(config);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { CoreEvents } from '../constants/CoreEvents';
|
||||||
import { CalendarConfig } from '../core/CalendarConfig';
|
import { CalendarConfig } from '../core/CalendarConfig';
|
||||||
import { DateCalculator } from '../utils/DateCalculator';
|
import { DateCalculator } from '../utils/DateCalculator';
|
||||||
import { EventRenderingService } from './EventRendererManager';
|
import { EventRenderingService } from './EventRendererManager';
|
||||||
|
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
|
||||||
|
import { eventBus } from '../core/EventBus';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NavigationRenderer - Handles DOM rendering for navigation containers
|
* NavigationRenderer - Handles DOM rendering for navigation containers
|
||||||
|
|
@ -171,6 +173,13 @@ export class NavigationRenderer {
|
||||||
header.appendChild(headerElement);
|
header.appendChild(headerElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Always ensure all-day containers exist for all days
|
||||||
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(this.config.getCalendarMode());
|
||||||
|
headerRenderer.ensureAllDayContainers(header as HTMLElement);
|
||||||
|
|
||||||
|
// Add event delegation listener for drag & drop functionality
|
||||||
|
this.setupHeaderEventListener(header as HTMLElement);
|
||||||
|
|
||||||
// Render day columns for target week
|
// Render day columns for target week
|
||||||
dates.forEach(date => {
|
dates.forEach(date => {
|
||||||
const column = document.createElement('swp-day-column');
|
const column = document.createElement('swp-day-column');
|
||||||
|
|
@ -183,4 +192,38 @@ export class NavigationRenderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup event delegation listener for header mouseover (same logic as GridRenderer)
|
||||||
|
*/
|
||||||
|
private setupHeaderEventListener(calendarHeader: HTMLElement): void {
|
||||||
|
calendarHeader.addEventListener('mouseover', (event) => {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
|
||||||
|
// Check what was hovered - could be day-header OR all-day-container
|
||||||
|
const dayHeader = target.closest('swp-day-header');
|
||||||
|
const allDayContainer = target.closest('swp-allday-container');
|
||||||
|
|
||||||
|
if (dayHeader || allDayContainer) {
|
||||||
|
const hoveredElement = dayHeader || allDayContainer;
|
||||||
|
const targetDate = (hoveredElement as HTMLElement).dataset.date;
|
||||||
|
|
||||||
|
console.log('NavigationRenderer: Detected hover over:', {
|
||||||
|
elementType: dayHeader ? 'day-header' : 'all-day-container',
|
||||||
|
targetDate,
|
||||||
|
element: hoveredElement
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the header renderer for addToAllDay functionality
|
||||||
|
const calendarType = this.config.getCalendarMode();
|
||||||
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
||||||
|
|
||||||
|
eventBus.emit('header:mouseover', {
|
||||||
|
element: hoveredElement,
|
||||||
|
targetDate,
|
||||||
|
headerRenderer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -50,8 +50,8 @@ export class MonthViewStrategy implements ViewStrategy {
|
||||||
// Add 6 weeks of day cells
|
// Add 6 weeks of day cells
|
||||||
this.createDayCells(monthGrid, context.currentDate);
|
this.createDayCells(monthGrid, context.currentDate);
|
||||||
|
|
||||||
// Render events in day cells
|
// Render events in day cells (will be handled by EventRendererManager)
|
||||||
this.renderMonthEvents(monthGrid, context.allDayEvents);
|
// this.renderMonthEvents(monthGrid, context.allDayEvents);
|
||||||
|
|
||||||
context.container.appendChild(monthGrid);
|
context.container.appendChild(monthGrid);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue