Refactors event rendering and drag-and-drop logic
Simplifies event drag handling by removing redundant clone management Optimizes single column event rendering and cleanup process Removes unnecessary logging and console output Improves event update and re-rendering strategies Enhances performance and reduces complexity in event manipulation
This commit is contained in:
parent
fba85094d7
commit
3b6f0407fb
5 changed files with 89 additions and 98 deletions
10
src/index.ts
10
src/index.ts
|
|
@ -157,7 +157,11 @@ async function initializeCalendar(): Promise<void> {
|
||||||
await resizeHandleManager.initialize?.();
|
await resizeHandleManager.initialize?.();
|
||||||
|
|
||||||
// Resolve SyncManager (starts automatically in constructor)
|
// Resolve SyncManager (starts automatically in constructor)
|
||||||
const syncManager = app.resolveType<SyncManager>();
|
// Resolve SyncManager (starts automatically in constructor)
|
||||||
|
// Resolve SyncManager (starts automatically in constructor)
|
||||||
|
// Resolve SyncManager (starts automatically in constructor)
|
||||||
|
// Resolve SyncManager (starts automatically in constructor)
|
||||||
|
//const syncManager = app.resolveType<SyncManager>();
|
||||||
|
|
||||||
// Handle deep linking after managers are initialized
|
// Handle deep linking after managers are initialized
|
||||||
await handleDeepLinking(eventManager, urlManager);
|
await handleDeepLinking(eventManager, urlManager);
|
||||||
|
|
@ -169,14 +173,14 @@ async function initializeCalendar(): Promise<void> {
|
||||||
app: typeof app;
|
app: typeof app;
|
||||||
calendarManager: typeof calendarManager;
|
calendarManager: typeof calendarManager;
|
||||||
eventManager: typeof eventManager;
|
eventManager: typeof eventManager;
|
||||||
syncManager: typeof syncManager;
|
//syncManager: typeof syncManager;
|
||||||
};
|
};
|
||||||
}).calendarDebug = {
|
}).calendarDebug = {
|
||||||
eventBus,
|
eventBus,
|
||||||
app,
|
app,
|
||||||
calendarManager,
|
calendarManager,
|
||||||
eventManager,
|
eventManager,
|
||||||
syncManager,
|
//syncManager,
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -180,16 +180,6 @@ export class EdgeScrollManager {
|
||||||
const atTop = currentScrollTop <= 0 && vy < 0;
|
const atTop = currentScrollTop <= 0 && vy < 0;
|
||||||
const atBottom = (cloneBottom >= timeGridBottom) && vy > 0;
|
const atBottom = (cloneBottom >= timeGridBottom) && vy > 0;
|
||||||
|
|
||||||
console.log('📊 Scroll check:', {
|
|
||||||
currentScrollTop,
|
|
||||||
scrollableHeight,
|
|
||||||
timeGridHeight,
|
|
||||||
cloneBottom,
|
|
||||||
timeGridBottom,
|
|
||||||
atTop,
|
|
||||||
atBottom,
|
|
||||||
vy
|
|
||||||
});
|
|
||||||
|
|
||||||
if (atTop || atBottom) {
|
if (atTop || atBottom) {
|
||||||
// At boundary - stop scrolling
|
// At boundary - stop scrolling
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,6 @@ export class ResizeHandleManager {
|
||||||
this.pointerCaptured = false;
|
this.pointerCaptured = false;
|
||||||
}
|
}
|
||||||
document.documentElement.classList.remove('swp--resizing');
|
document.documentElement.classList.remove('swp--resizing');
|
||||||
this.refreshEventCache();
|
this.refreshEventCache(); //TODO: We should avoid this caching.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@ import { EventLayoutCoordinator, IGridGroupLayout, IStackedEventLayout } from '.
|
||||||
export interface IEventRenderer {
|
export interface IEventRenderer {
|
||||||
renderEvents(events: ICalendarEvent[], container: HTMLElement): void;
|
renderEvents(events: ICalendarEvent[], container: HTMLElement): void;
|
||||||
clearEvents(container?: HTMLElement): void;
|
clearEvents(container?: HTMLElement): void;
|
||||||
|
renderSingleColumnEvents?(column: IColumnBounds, events: ICalendarEvent[]): void;
|
||||||
handleDragStart?(payload: IDragStartEventPayload): void;
|
handleDragStart?(payload: IDragStartEventPayload): void;
|
||||||
handleDragMove?(payload: IDragMoveEventPayload): void;
|
handleDragMove?(payload: IDragMoveEventPayload): void;
|
||||||
handleDragAutoScroll?(eventId: string, snappedY: number): void;
|
handleDragAutoScroll?(eventId: string, snappedY: number): void;
|
||||||
handleDragEnd?(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void;
|
handleDragEnd?(originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void;
|
||||||
handleEventClick?(eventId: string, originalElement: HTMLElement): void;
|
handleEventClick?(eventId: string, originalElement: HTMLElement): void;
|
||||||
handleColumnChange?(payload: IDragColumnChangeEventPayload): void;
|
handleColumnChange?(payload: IDragColumnChangeEventPayload): void;
|
||||||
handleNavigationCompleted?(): void;
|
handleNavigationCompleted?(): void;
|
||||||
|
|
@ -153,7 +154,7 @@ export class DateEventRenderer implements IEventRenderer {
|
||||||
let eventsLayer = payload.targetColumn.element.querySelector('swp-events-layer');
|
let eventsLayer = payload.targetColumn.element.querySelector('swp-events-layer');
|
||||||
|
|
||||||
// Add "clone-" prefix to match clone ID pattern
|
// Add "clone-" prefix to match clone ID pattern
|
||||||
timedClone.dataset.eventId = `clone-${payload.calendarEvent.id}`;
|
//timedClone.dataset.eventId = `clone-${payload.calendarEvent.id}`;
|
||||||
|
|
||||||
// Remove old all-day clone and replace with new timed clone
|
// Remove old all-day clone and replace with new timed clone
|
||||||
payload.draggedClone.remove();
|
payload.draggedClone.remove();
|
||||||
|
|
@ -165,7 +166,7 @@ export class DateEventRenderer implements IEventRenderer {
|
||||||
/**
|
/**
|
||||||
* Handle drag end event
|
* Handle drag end event
|
||||||
*/
|
*/
|
||||||
public handleDragEnd(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void {
|
public handleDragEnd(originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void {
|
||||||
if (!draggedClone || !originalElement) {
|
if (!draggedClone || !originalElement) {
|
||||||
console.warn('Missing draggedClone or originalElement');
|
console.warn('Missing draggedClone or originalElement');
|
||||||
return;
|
return;
|
||||||
|
|
@ -187,6 +188,13 @@ export class DateEventRenderer implements IEventRenderer {
|
||||||
// Clean up instance state
|
// Clean up instance state
|
||||||
this.draggedClone = null;
|
this.draggedClone = null;
|
||||||
this.originalEvent = null;
|
this.originalEvent = null;
|
||||||
|
|
||||||
|
|
||||||
|
// Clean up any remaining day event clones
|
||||||
|
const dayEventClone = document.querySelector(`swp-event[data-event-id="clone-${cloneId}"]`);
|
||||||
|
if (dayEventClone) {
|
||||||
|
dayEventClone.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -226,6 +234,18 @@ export class DateEventRenderer implements IEventRenderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render events for a single column
|
||||||
|
*/
|
||||||
|
public renderSingleColumnEvents(column: IColumnBounds, events: ICalendarEvent[]): void {
|
||||||
|
const columnEvents = this.getEventsForColumn(column.element, events);
|
||||||
|
const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement;
|
||||||
|
|
||||||
|
if (eventsLayer) {
|
||||||
|
this.renderColumnEvents(columnEvents, eventsLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render events in a column using combined stacking + grid algorithm
|
* Render events in a column using combined stacking + grid algorithm
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { IEventRenderer } from './EventRenderer';
|
||||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||||
import { IDragStartEventPayload, IDragMoveEventPayload, IDragEndEventPayload, IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IDragMouseEnterColumnEventPayload, IDragColumnChangeEventPayload, IHeaderReadyEventPayload, IResizeEndEventPayload } from '../types/EventTypes';
|
import { IDragStartEventPayload, IDragMoveEventPayload, IDragEndEventPayload, IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IDragMouseEnterColumnEventPayload, IDragColumnChangeEventPayload, IHeaderReadyEventPayload, IResizeEndEventPayload } from '../types/EventTypes';
|
||||||
import { DateService } from '../utils/DateService';
|
import { DateService } from '../utils/DateService';
|
||||||
import { IColumnBounds } from '../utils/ColumnDetectionUtils';
|
import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||||
/**
|
/**
|
||||||
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
||||||
* Håndterer event positioning og overlap detection
|
* Håndterer event positioning og overlap detection
|
||||||
|
|
@ -161,49 +161,28 @@ export class EventRenderingService {
|
||||||
private setupDragEndListener(): void {
|
private setupDragEndListener(): void {
|
||||||
this.eventBus.on('drag:end', async (event: Event) => {
|
this.eventBus.on('drag:end', async (event: Event) => {
|
||||||
|
|
||||||
const { originalElement: draggedElement, originalSourceColumn, finalPosition, target } = (event as CustomEvent<IDragEndEventPayload>).detail;
|
const { originalElement, draggedClone, originalSourceColumn, finalPosition, target } = (event as CustomEvent<IDragEndEventPayload>).detail;
|
||||||
const finalColumn = finalPosition.column;
|
const finalColumn = finalPosition.column;
|
||||||
const finalY = finalPosition.snappedY;
|
const finalY = finalPosition.snappedY;
|
||||||
const eventId = draggedElement.dataset.eventId || '';
|
|
||||||
|
|
||||||
|
let element = draggedClone as SwpEventElement;
|
||||||
// Only handle day column drops for EventRenderer
|
// Only handle day column drops for EventRenderer
|
||||||
if (target === 'swp-day-column' && finalColumn) {
|
if (target === 'swp-day-column' && finalColumn) {
|
||||||
// Find dragged clone - use draggedElement as original
|
|
||||||
const draggedClone = document.querySelector(`swp-day-column swp-event[data-event-id="clone-${eventId}"]`) as HTMLElement;
|
|
||||||
|
|
||||||
if (draggedElement && draggedClone && this.strategy.handleDragEnd) {
|
if (originalElement && draggedClone && this.strategy.handleDragEnd) {
|
||||||
this.strategy.handleDragEnd(eventId, draggedElement, draggedClone, finalColumn, finalY);
|
this.strategy.handleDragEnd(originalElement, draggedClone, finalColumn, finalY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update event data in EventManager with new position from clone
|
await this.eventManager.updateEvent(element.eventId, {
|
||||||
if (draggedClone) {
|
start: element.start,
|
||||||
const swpEvent = draggedClone as SwpEventElement;
|
end: element.end,
|
||||||
const newStart = swpEvent.start;
|
|
||||||
const newEnd = swpEvent.end;
|
|
||||||
|
|
||||||
await this.eventManager.updateEvent(eventId, {
|
|
||||||
start: newStart,
|
|
||||||
end: newEnd,
|
|
||||||
allDay: false
|
allDay: false
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('📝 EventRendererManager: Updated event in EventManager', {
|
|
||||||
eventId,
|
|
||||||
newStart,
|
|
||||||
newEnd,
|
|
||||||
allDay: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-render affected columns for stacking/grouping (now with updated data)
|
// Re-render affected columns for stacking/grouping (now with updated data)
|
||||||
await this.reRenderAffectedColumns(originalSourceColumn, finalColumn);
|
await this.reRenderAffectedColumns(originalSourceColumn, finalColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up any remaining day event clones
|
|
||||||
const dayEventClone = document.querySelector(`swp-day-column swp-event[data-event-id="clone-${eventId}"]`);
|
|
||||||
if (dayEventClone) {
|
|
||||||
dayEventClone.remove();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,8 +267,12 @@ export class EventRenderingService {
|
||||||
if (columnElement) {
|
if (columnElement) {
|
||||||
const columnDate = columnElement.dataset.date;
|
const columnDate = columnElement.dataset.date;
|
||||||
if (columnDate) {
|
if (columnDate) {
|
||||||
// Re-render the column to recalculate stacking/grouping
|
// Get column bounds and re-render the column to recalculate stacking/grouping
|
||||||
this.renderSingleColumn(columnDate);
|
const columnDateObj = this.dateService.parseISO(`${columnDate}T00:00:00`);
|
||||||
|
const columnBounds = ColumnDetectionUtils.getColumnBoundsByDate(columnDateObj);
|
||||||
|
if (columnBounds) {
|
||||||
|
await this.renderSingleColumn(columnBounds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -309,66 +292,60 @@ export class EventRenderingService {
|
||||||
* Re-render affected columns after drag to recalculate stacking/grouping
|
* Re-render affected columns after drag to recalculate stacking/grouping
|
||||||
*/
|
*/
|
||||||
private async reRenderAffectedColumns(originalSourceColumn: IColumnBounds | null, targetColumn: IColumnBounds | null): Promise<void> {
|
private async reRenderAffectedColumns(originalSourceColumn: IColumnBounds | null, targetColumn: IColumnBounds | null): Promise<void> {
|
||||||
const columnsToRender = new Set<string>();
|
// Re-render original source column if exists
|
||||||
|
|
||||||
// Add original source column if exists
|
|
||||||
if (originalSourceColumn) {
|
if (originalSourceColumn) {
|
||||||
columnsToRender.add(originalSourceColumn.date);
|
await this.renderSingleColumn(originalSourceColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add target column if exists and different from source
|
// Re-render target column if exists and different from source
|
||||||
if (targetColumn && targetColumn.date !== originalSourceColumn?.date) {
|
if (targetColumn && targetColumn.date !== originalSourceColumn?.date) {
|
||||||
columnsToRender.add(targetColumn.date);
|
await this.renderSingleColumn(targetColumn);
|
||||||
}
|
|
||||||
|
|
||||||
// Re-render each affected column
|
|
||||||
for (const columnDate of columnsToRender) {
|
|
||||||
await this.renderSingleColumn(columnDate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render events for a single column by re-rendering entire container
|
* Clear events in a single column's events layer
|
||||||
*/
|
*/
|
||||||
private async renderSingleColumn(columnDate: string): Promise<void> {
|
private clearColumnEvents(eventsLayer: HTMLElement): void {
|
||||||
// Find the column element
|
const existingEvents = eventsLayer.querySelectorAll('swp-event');
|
||||||
const columnElement = document.querySelector(`swp-day-column[data-date="${columnDate}"]`) as HTMLElement;
|
const existingGroups = eventsLayer.querySelectorAll('swp-event-group');
|
||||||
if (!columnElement) {
|
|
||||||
console.warn('EventRendererManager: Column not found', { columnDate });
|
existingEvents.forEach(event => event.remove());
|
||||||
|
existingGroups.forEach(group => group.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render events for a single column
|
||||||
|
*/
|
||||||
|
private async renderSingleColumn(column: IColumnBounds): Promise<void> {
|
||||||
|
// Get events for just this column's date
|
||||||
|
const columnStart = this.dateService.parseISO(`${column.date}T00:00:00`);
|
||||||
|
const columnEnd = this.dateService.parseISO(`${column.date}T23:59:59.999`);
|
||||||
|
|
||||||
|
// Get events from EventManager for this single date
|
||||||
|
const events = await this.eventManager.getEventsForPeriod(columnStart, columnEnd);
|
||||||
|
|
||||||
|
// Filter to timed events only
|
||||||
|
const timedEvents = events.filter(event => !event.allDay);
|
||||||
|
|
||||||
|
// Get events layer within this specific column
|
||||||
|
const eventsLayer = column.element.querySelector('swp-events-layer') as HTMLElement;
|
||||||
|
if (!eventsLayer) {
|
||||||
|
console.warn('EventRendererManager: Events layer not found in column');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the parent container (swp-day-columns)
|
// Clear only this column's events
|
||||||
const container = columnElement.closest('swp-day-columns') as HTMLElement;
|
this.clearColumnEvents(eventsLayer);
|
||||||
if (!container) {
|
|
||||||
console.warn('EventRendererManager: Container not found');
|
// Render events for this column using strategy
|
||||||
return;
|
if (this.strategy.renderSingleColumnEvents) {
|
||||||
|
this.strategy.renderSingleColumnEvents(column, timedEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all columns in container to determine date range
|
console.log('🔄 EventRendererManager: Re-rendered single column', {
|
||||||
const allColumns = Array.from(container.querySelectorAll<HTMLElement>('swp-day-column'));
|
columnDate: column.date,
|
||||||
if (allColumns.length === 0) return;
|
eventsCount: timedEvents.length
|
||||||
|
|
||||||
// Get date range from first and last column
|
|
||||||
const firstColumnDate = allColumns[0].dataset.date;
|
|
||||||
const lastColumnDate = allColumns[allColumns.length - 1].dataset.date;
|
|
||||||
|
|
||||||
if (!firstColumnDate || !lastColumnDate) return;
|
|
||||||
|
|
||||||
const startDate = this.dateService.parseISO(`${firstColumnDate}T00:00:00`);
|
|
||||||
const endDate = this.dateService.parseISO(`${lastColumnDate}T23:59:59.999`);
|
|
||||||
|
|
||||||
// Re-render entire container (this will recalculate stacking for all columns)
|
|
||||||
await this.renderEvents({
|
|
||||||
container,
|
|
||||||
startDate,
|
|
||||||
endDate
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('🔄 EventRendererManager: Re-rendered container for column', {
|
|
||||||
columnDate,
|
|
||||||
startDate: firstColumnDate,
|
|
||||||
endDate: lastColumnDate
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue