Extracts event layout calculations
Moves complex layout determination logic (grid grouping, stack levels, positioning) from `EventRenderer` to a new `EventLayoutCoordinator` class. Delegates layout responsibilities to the coordinator, significantly simplifying the `EventRenderer`'s `renderColumnEvents` method. Refines `EventStackManager` by removing deprecated layout methods, consolidating its role to event grouping and core stack level management. Improves modularity and separation of concerns within the rendering pipeline.
This commit is contained in:
parent
2f58ceccd4
commit
c788a1695e
5 changed files with 166 additions and 248 deletions
|
|
@ -7,7 +7,8 @@ import { PositionUtils } from '../utils/PositionUtils';
|
|||
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
import { DragColumnChangeEventPayload, DragMoveEventPayload, DragStartEventPayload } from '../types/EventTypes';
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { EventStackManager, EventGroup, StackLink } from '../managers/EventStackManager';
|
||||
import { EventStackManager } from '../managers/EventStackManager';
|
||||
import { EventLayoutCoordinator, GridGroupLayout, StackedEventLayout } from '../managers/EventLayoutCoordinator';
|
||||
|
||||
/**
|
||||
* Interface for event rendering strategies
|
||||
|
|
@ -31,6 +32,7 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
|
||||
private dateService: DateService;
|
||||
private stackManager: EventStackManager;
|
||||
private layoutCoordinator: EventLayoutCoordinator;
|
||||
private draggedClone: HTMLElement | null = null;
|
||||
private originalEvent: HTMLElement | null = null;
|
||||
|
||||
|
|
@ -38,6 +40,7 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
const timezone = calendarConfig.getTimezone?.();
|
||||
this.dateService = new DateService(timezone);
|
||||
this.stackManager = new EventStackManager();
|
||||
this.layoutCoordinator = new EventLayoutCoordinator();
|
||||
}
|
||||
|
||||
private applyDragStyling(element: HTMLElement): void {
|
||||
|
|
@ -186,138 +189,51 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
private renderColumnEvents(columnEvents: CalendarEvent[], eventsLayer: HTMLElement): void {
|
||||
if (columnEvents.length === 0) return;
|
||||
|
||||
console.log('[EventRenderer] Rendering column with', columnEvents.length, 'events');
|
||||
// Get layout from coordinator
|
||||
const layout = this.layoutCoordinator.calculateColumnLayout(columnEvents);
|
||||
|
||||
// Step 1: Calculate stack levels for ALL events first (to understand overlaps)
|
||||
const allStackLinks = this.stackManager.createOptimizedStackLinks(columnEvents);
|
||||
|
||||
console.log('[EventRenderer] All stack links:');
|
||||
columnEvents.forEach(event => {
|
||||
const link = allStackLinks.get(event.id);
|
||||
console.log(` Event ${event.id} (${event.title}): stackLevel=${link?.stackLevel ?? 'none'}`);
|
||||
// Render grid groups
|
||||
layout.gridGroups.forEach(gridGroup => {
|
||||
this.renderGridGroup(gridGroup, eventsLayer);
|
||||
});
|
||||
|
||||
// Step 2: Find grid candidates (start together ±15 min)
|
||||
const groups = this.stackManager.groupEventsByStartTime(columnEvents);
|
||||
const gridGroups = groups.filter(group => {
|
||||
if (group.events.length <= 1) return false;
|
||||
group.containerType = this.stackManager.decideContainerType(group);
|
||||
return group.containerType === 'GRID';
|
||||
});
|
||||
|
||||
console.log('[EventRenderer] Grid groups:', gridGroups.length);
|
||||
gridGroups.forEach((g, i) => {
|
||||
console.log(` Grid group ${i}:`, g.events.map(e => e.id));
|
||||
});
|
||||
|
||||
// Step 3: Render grid groups and track which events have been rendered
|
||||
const renderedIds = new Set<string>();
|
||||
|
||||
gridGroups.forEach((group, index) => {
|
||||
console.log(`[EventRenderer] Rendering grid group ${index} with ${group.events.length} events:`, group.events.map(e => e.id));
|
||||
|
||||
// Calculate grid group stack level by finding what it overlaps OUTSIDE the group
|
||||
const gridStackLevel = this.calculateGridGroupStackLevel(group, columnEvents, allStackLinks);
|
||||
|
||||
console.log(` Grid group stack level: ${gridStackLevel}`);
|
||||
|
||||
this.renderGridGroup(group, eventsLayer, gridStackLevel);
|
||||
group.events.forEach(e => renderedIds.add(e.id));
|
||||
});
|
||||
|
||||
// Step 4: Get remaining events (not in grid)
|
||||
const remainingEvents = columnEvents.filter(e => !renderedIds.has(e.id));
|
||||
|
||||
console.log('[EventRenderer] Remaining events for stacking:');
|
||||
remainingEvents.forEach(event => {
|
||||
const link = allStackLinks.get(event.id);
|
||||
console.log(` Event ${event.id} (${event.title}): stackLevel=${link?.stackLevel ?? 'none'}`);
|
||||
});
|
||||
|
||||
// Step 5: Render remaining stacked/single events
|
||||
remainingEvents.forEach(event => {
|
||||
const element = this.renderEvent(event);
|
||||
const stackLink = allStackLinks.get(event.id);
|
||||
|
||||
console.log(`[EventRenderer] Rendering stacked event ${event.id}, stackLink:`, stackLink);
|
||||
|
||||
if (stackLink) {
|
||||
// Apply stack link to element (for drag-drop)
|
||||
this.stackManager.applyStackLinkToElement(element, stackLink);
|
||||
|
||||
// Apply visual styling
|
||||
this.stackManager.applyVisualStyling(element, stackLink.stackLevel);
|
||||
console.log(` Applied margin-left: ${stackLink.stackLevel * 15}px, stack-link:`, stackLink);
|
||||
}
|
||||
|
||||
// Render stacked events
|
||||
layout.stackedEvents.forEach(stackedEvent => {
|
||||
const element = this.renderEvent(stackedEvent.event);
|
||||
this.stackManager.applyStackLinkToElement(element, stackedEvent.stackLink);
|
||||
this.stackManager.applyVisualStyling(element, stackedEvent.stackLink.stackLevel);
|
||||
eventsLayer.appendChild(element);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate stack level for a grid group based on what it overlaps OUTSIDE the group
|
||||
*/
|
||||
private calculateGridGroupStackLevel(
|
||||
group: EventGroup,
|
||||
allEvents: CalendarEvent[],
|
||||
stackLinks: Map<string, StackLink>
|
||||
): number {
|
||||
const groupEventIds = new Set(group.events.map(e => e.id));
|
||||
|
||||
// Find all events OUTSIDE this group
|
||||
const outsideEvents = allEvents.filter(e => !groupEventIds.has(e.id));
|
||||
|
||||
// Find the highest stackLevel of any event that overlaps with ANY event in the grid group
|
||||
let maxOverlappingLevel = -1;
|
||||
|
||||
for (const gridEvent of group.events) {
|
||||
for (const outsideEvent of outsideEvents) {
|
||||
if (this.stackManager.doEventsOverlap(gridEvent, outsideEvent)) {
|
||||
const outsideLink = stackLinks.get(outsideEvent.id);
|
||||
if (outsideLink) {
|
||||
maxOverlappingLevel = Math.max(maxOverlappingLevel, outsideLink.stackLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grid group should be one level above the highest overlapping event
|
||||
return maxOverlappingLevel + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render events in a grid container (side-by-side)
|
||||
*/
|
||||
private renderGridGroup(group: EventGroup, eventsLayer: HTMLElement, stackLevel: number): void {
|
||||
private renderGridGroup(gridGroup: GridGroupLayout, eventsLayer: HTMLElement): void {
|
||||
const groupElement = document.createElement('swp-event-group');
|
||||
|
||||
// Add grid column class based on event count
|
||||
const colCount = group.events.length;
|
||||
const colCount = gridGroup.events.length;
|
||||
groupElement.classList.add(`cols-${colCount}`);
|
||||
|
||||
// Add stack level class for margin-left offset
|
||||
groupElement.classList.add(`stack-level-${stackLevel}`);
|
||||
groupElement.classList.add(`stack-level-${gridGroup.stackLevel}`);
|
||||
|
||||
// Position based on earliest event
|
||||
const earliestEvent = group.events[0];
|
||||
const position = this.calculateEventPosition(earliestEvent);
|
||||
groupElement.style.top = `${position.top + 1}px`;
|
||||
// Position from layout
|
||||
groupElement.style.top = `${gridGroup.position.top}px`;
|
||||
|
||||
// Add z-index based on stack level
|
||||
groupElement.style.zIndex = `${this.stackManager.calculateZIndex(stackLevel)}`;
|
||||
// Add inline styles for margin-left and z-index (guaranteed to work)
|
||||
groupElement.style.marginLeft = `${gridGroup.stackLevel * 15}px`;
|
||||
groupElement.style.zIndex = `${this.stackManager.calculateZIndex(gridGroup.stackLevel)}`;
|
||||
|
||||
// Add stack-link attribute for drag-drop (group acts as a stacked item)
|
||||
const stackLink: StackLink = {
|
||||
stackLevel: stackLevel
|
||||
// prev/next will be handled by drag-drop manager if needed
|
||||
const stackLink = {
|
||||
stackLevel: gridGroup.stackLevel
|
||||
};
|
||||
this.stackManager.applyStackLinkToElement(groupElement, stackLink);
|
||||
|
||||
// NO height on the group - it should auto-size based on children
|
||||
|
||||
// Render each event within the grid
|
||||
group.events.forEach(event => {
|
||||
const earliestEvent = gridGroup.events[0];
|
||||
gridGroup.events.forEach(event => {
|
||||
const element = this.renderEventInGrid(event, earliestEvent.start);
|
||||
groupElement.appendChild(element);
|
||||
});
|
||||
|
|
@ -363,12 +279,19 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
}
|
||||
|
||||
clearEvents(container?: HTMLElement): void {
|
||||
const selector = 'swp-event';
|
||||
const eventSelector = 'swp-event';
|
||||
const groupSelector = 'swp-event-group';
|
||||
|
||||
const existingEvents = container
|
||||
? container.querySelectorAll(selector)
|
||||
: document.querySelectorAll(selector);
|
||||
? container.querySelectorAll(eventSelector)
|
||||
: document.querySelectorAll(eventSelector);
|
||||
|
||||
const existingGroups = container
|
||||
? container.querySelectorAll(groupSelector)
|
||||
: document.querySelectorAll(groupSelector);
|
||||
|
||||
existingEvents.forEach(event => event.remove());
|
||||
existingGroups.forEach(group => group.remove());
|
||||
}
|
||||
|
||||
protected getColumns(container: HTMLElement): HTMLElement[] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue