This commit is contained in:
Janus C. H. Knudsen 2025-10-02 23:11:26 +02:00
parent 88702e574a
commit 89e8a3f7b2
6 changed files with 137 additions and 198 deletions

View file

@ -3,21 +3,20 @@ import { SwpAllDayEventElement } from '../elements/SwpEventElement';
import { EventLayout } from '../utils/AllDayLayoutEngine';
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
import { EventManager } from '../managers/EventManager';
/**
* AllDayEventRenderer - Simple rendering of all-day events
* Handles adding and removing all-day events from the header container
* NOTE: Layout calculation is now handled by AllDayManager
*/
import { DragStartEventPayload } from '../types/EventTypes';
import { EventRendererStrategy } from './EventRenderer';
export class AllDayEventRenderer {
private container: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null;
private draggedClone: HTMLElement | null = null;
constructor() {
this.getContainer();
}
/**
* Get or cache all-day container, create if it doesn't exist - SIMPLIFIED (no ghost columns)
*/
private getContainer(): HTMLElement | null {
const header = document.querySelector('swp-calendar-header');
@ -27,14 +26,45 @@ export class AllDayEventRenderer {
if (!this.container) {
this.container = document.createElement('swp-allday-container');
header.appendChild(this.container);
}
}
return this.container;
}
// REMOVED: createGhostColumns() method - no longer needed!
private getAllDayContainer(): HTMLElement | null {
return document.querySelector('swp-calendar-header swp-allday-container');
}
/**
* Handle drag start for all-day events
*/
public handleDragStart(payload: DragStartEventPayload): void {
this.originalEvent = payload.draggedElement;;
this.draggedClone = payload.draggedClone;
if (this.draggedClone) {
const container = this.getAllDayContainer();
if (!container) return;
this.draggedClone.style.gridColumn = this.originalEvent.style.gridColumn;
this.draggedClone.style.gridRow = this.originalEvent.style.gridRow;
console.log('handleDragStart:this.draggedClone', this.draggedClone);
container.appendChild(this.draggedClone);
// Add dragging style
this.draggedClone.classList.add('dragging');
this.draggedClone.style.zIndex = '1000';
this.draggedClone.style.cursor = 'grabbing';
// Make original semi-transparent
this.originalEvent.style.opacity = '0.3';
this.originalEvent.style.userSelect = 'none';
}
}
/**
* Render an all-day event with pre-calculated layout
@ -77,28 +107,14 @@ export class AllDayEventRenderer {
* Render all-day events for specific period using AllDayEventRenderer
*/
public renderAllDayEventsForPeriod(eventLayouts: EventLayout[]): void {
// Get events from EventManager for the period
// const events = this.eventManager.getEventsForPeriod(startDate, endDate);
// Clear existing all-day events first
this.clearAllDayEvents();
// Get actual visible dates from DOM headers instead of generating them
// const layouts = this.allDayManager.initAllDayEventsLayout(allDayEvents, weekDates);
// Render each all-day event with pre-calculated layout
eventLayouts.forEach(layout => {
this.renderAllDayEventWithLayout(layout.calenderEvent, layout);
});
}
/**
* Clear only all-day events
*/
private clearAllDayEvents(): void {
const allDayContainer = document.querySelector('swp-allday-container');
if (allDayContainer) {

View file

@ -26,25 +26,32 @@ export interface EventRendererStrategy {
handleColumnChange?(payload: DragColumnChangeEventPayload): void;
handleNavigationCompleted?(): void;
}
// Abstract methods that subclasses must implement
// private getColumns(container: HTMLElement): HTMLElement[];
// private getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[];
/**
* Base class for event renderers with common functionality
* Date-based event renderer
*/
export abstract class BaseEventRenderer implements EventRendererStrategy {
protected dateCalculator: DateCalculator;
export class DateEventRenderer implements EventRendererStrategy {
// Drag and drop state
private draggedClone: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null;
// Resize manager
constructor(dateCalculator?: DateCalculator) {
if (!dateCalculator) {
DateCalculator.initialize(calendarConfig);
}
this.dateCalculator = dateCalculator || new DateCalculator();
this.setupDragEventListeners();
}
private dateCalculator: DateCalculator;
private draggedClone: HTMLElement | null = null;
private originalEvent: HTMLElement | null = null;
// ============================================
// NEW OVERLAP DETECTION SYSTEM
@ -96,10 +103,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
});
}
/**
* Apply common drag styling to an element
*/
private applyDragStyling(element: HTMLElement): void {
element.classList.add('dragging');
}
@ -127,8 +131,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Snap to interval
const snappedStartMinutes = Math.round(actualStartMinutes / snapInterval) * snapInterval;
if(!clone.dataset.originalDuration)
if (!clone.dataset.originalDuration)
throw new DOMException("missing clone.dataset.originalDuration")
const endTotalMinutes = snappedStartMinutes + parseInt(clone.dataset.originalDuration);
@ -153,7 +157,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
this.draggedClone = payload.draggedClone;
if (this.draggedClone) {
// Apply drag styling
// Apply drag styling
this.applyDragStyling(this.draggedClone);
// Add to current column's events layer (not directly to column)
@ -296,7 +300,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Fade out original
// TODO: this should be changed into a subscriber which only after a succesful placement is fired, not just mouseup as this can remove elements that are not placed.
this.fadeOutAndRemove(originalElement);
this.fadeOutAndRemove(originalElement);
// Remove clone prefix and normalize clone to be a regular event
const cloneId = draggedClone.dataset.eventId;
@ -452,17 +456,14 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
});
}
// Abstract methods that subclasses must implement
protected abstract getColumns(container: HTMLElement): HTMLElement[];
protected abstract getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[];
protected renderEvent(event: CalendarEvent): HTMLElement {
private renderEvent(event: CalendarEvent): HTMLElement {
const swpEvent = SwpEventElement.fromCalendarEvent(event);
const eventElement = swpEvent.getElement();
// Setup resize handles on first mouseover only
eventElement.addEventListener('mouseover', () => {
eventElement.addEventListener('mouseover', () => { // TODO: This is not the correct way... we should not add eventlistener on every event
if (eventElement.dataset.hasResizeHandlers !== 'true') {
eventElement.dataset.hasResizeHandlers = 'true';
}
@ -544,16 +545,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
element.style.minWidth = '50px';
});
}
}
/**
* Date-based event renderer
*/
export class DateEventRenderer extends BaseEventRenderer {
constructor(dateCalculator?: DateCalculator) {
super(dateCalculator);
this.setupDragEventListeners();
}
/**
* Setup drag event listeners - placeholder method
@ -585,32 +577,3 @@ export class DateEventRenderer extends BaseEventRenderer {
return columnEvents;
}
}
/**
* Resource-based event renderer
*/
export class ResourceEventRenderer extends BaseEventRenderer {
protected getColumns(container: HTMLElement): HTMLElement[] {
const columns = container.querySelectorAll('swp-resource-column');
return Array.from(columns) as HTMLElement[];
}
protected getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[] {
const resourceName = column.dataset.resource;
if (!resourceName) return [];
const columnEvents = events.filter(event => {
return event.resource?.name === resourceName;
});
return columnEvents;
}
// ============================================
// NEW OVERLAP DETECTION SYSTEM
// All new functions prefixed with new_
// ============================================
protected overlapDetector = new OverlapDetector();
}

View file

@ -142,29 +142,29 @@ export class EventRenderingService {
* Setup all drag event listeners - moved from EventRenderer for better separation of concerns
*/
private setupDragEventListeners(): void {
// Handle drag start
this.eventBus.on('drag:start', (event: Event) => {
const dragStartPayload = (event as CustomEvent<DragStartEventPayload>).detail;
// Use the draggedElement directly - no need for DOM query
if (dragStartPayload.draggedElement.hasAttribute('data-allday')) {
return;
}
if (dragStartPayload.draggedElement && this.strategy.handleDragStart && dragStartPayload.columnBounds) {
this.strategy.handleDragStart(dragStartPayload);
}
});
// Handle drag move
this.eventBus.on('drag:move', (event: Event) => {
let dragEvent = (event as CustomEvent<DragMoveEventPayload>).detail;
// Filter: Only handle events WITHOUT data-allday attribute (normal timed events)
if (dragEvent.draggedElement.hasAttribute('data-allday')) {
return; // This is an all-day event, let AllDayManager handle it
return;
}
if (this.strategy.handleDragMove) {
this.strategy.handleDragMove(dragEvent);
}
});
// Handle drag auto-scroll
this.eventBus.on('drag:auto-scroll', (event: Event) => {
const { draggedElement, snappedY } = (event as CustomEvent).detail;
if (this.strategy.handleDragAutoScroll) {