Fixes event overlap detection and stacking logic
Updates the event overlap detection to accurately determine when events overlap in time, fixing incorrect stacking behavior. Implements column sharing for events starting within 30 minutes of each other. Applies stacking only when events truly overlap in time but start times differ by more than 30 minutes. Removes unnecessary data attributes and simplifies styling for stacked events, improving code cleanliness and performance.
This commit is contained in:
parent
ff067cfac3
commit
6afea2571b
4 changed files with 361 additions and 90 deletions
|
|
@ -26,24 +26,39 @@ export class EventOverlapManager {
|
|||
private nextZIndex = 100;
|
||||
|
||||
/**
|
||||
* Detect overlap mellem events baseret på start tid
|
||||
* Detect overlap mellem events baseret på faktisk time overlap og start tid forskel
|
||||
*/
|
||||
public detectOverlap(event1: CalendarEvent, event2: CalendarEvent): OverlapType {
|
||||
// Først: Tjek om events overlapper i tid
|
||||
if (!this.eventsOverlapInTime(event1, event2)) {
|
||||
return OverlapType.NONE;
|
||||
}
|
||||
|
||||
// Events overlapper i tid - nu tjek start tid forskel
|
||||
const start1 = new Date(event1.start).getTime();
|
||||
const start2 = new Date(event2.start).getTime();
|
||||
const timeDiffMinutes = Math.abs(start1 - start2) / (1000 * 60);
|
||||
|
||||
// Samme start tid = column sharing
|
||||
if (timeDiffMinutes === 0) {
|
||||
return OverlapType.COLUMN_SHARING;
|
||||
}
|
||||
|
||||
// Mere end 30 min forskel = stacking
|
||||
|
||||
// Over 30 min start forskel = stacking
|
||||
if (timeDiffMinutes > EventOverlapManager.STACKING_TIME_THRESHOLD_MINUTES) {
|
||||
return OverlapType.STACKING;
|
||||
}
|
||||
|
||||
// Indenfor 30 min start forskel = column sharing
|
||||
return OverlapType.COLUMN_SHARING;
|
||||
}
|
||||
|
||||
return OverlapType.NONE;
|
||||
/**
|
||||
* Tjek om to events faktisk overlapper i tid
|
||||
*/
|
||||
private eventsOverlapInTime(event1: CalendarEvent, event2: CalendarEvent): boolean {
|
||||
const start1 = new Date(event1.start).getTime();
|
||||
const end1 = new Date(event1.end).getTime();
|
||||
const start2 = new Date(event2.start).getTime();
|
||||
const end2 = new Date(event2.end).getTime();
|
||||
|
||||
// Events overlapper hvis de deler mindst ét tidspunkt
|
||||
return !(end1 <= start2 || end2 <= start1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -95,17 +110,12 @@ export class EventOverlapManager {
|
|||
* Opret flexbox container for column sharing events
|
||||
*/
|
||||
public createEventGroup(events: CalendarEvent[], position: { top: number; height: number }): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'event-group';
|
||||
const container = document.createElement('swp-event-group');
|
||||
container.style.position = 'absolute';
|
||||
container.style.top = `${position.top}px`;
|
||||
container.style.height = `${position.height}px`;
|
||||
// Ingen højde på gruppen - kun på individuelle events
|
||||
container.style.left = '2px';
|
||||
container.style.right = '2px';
|
||||
|
||||
// Data attributter for debugging og styling
|
||||
container.dataset.eventCount = events.length.toString();
|
||||
container.dataset.overlapType = OverlapType.COLUMN_SHARING;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
|
@ -114,17 +124,16 @@ export class EventOverlapManager {
|
|||
* Tilføj event til eksisterende event group
|
||||
*/
|
||||
public addToEventGroup(container: HTMLElement, eventElement: HTMLElement): void {
|
||||
// Fjern absolute positioning fra event da flexbox håndterer layout
|
||||
eventElement.style.position = 'relative';
|
||||
eventElement.style.top = '';
|
||||
eventElement.style.left = '';
|
||||
eventElement.style.right = '';
|
||||
// Sørg for at event har korrekt højde baseret på varighed
|
||||
const duration = eventElement.dataset.duration;
|
||||
if (duration) {
|
||||
const durationMinutes = parseInt(duration);
|
||||
const gridSettings = { hourHeight: 80 }; // Fra config
|
||||
const height = (durationMinutes / 60) * gridSettings.hourHeight;
|
||||
eventElement.style.height = `${height - 3}px`; // -3px som andre events
|
||||
}
|
||||
|
||||
container.appendChild(eventElement);
|
||||
|
||||
// Opdater event count
|
||||
const currentCount = parseInt(container.dataset.eventCount || '0');
|
||||
container.dataset.eventCount = (currentCount + 1).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -138,68 +147,57 @@ export class EventOverlapManager {
|
|||
eventElement.style.position = 'absolute';
|
||||
eventElement.remove();
|
||||
|
||||
// Opdater event count
|
||||
const currentCount = parseInt(container.dataset.eventCount || '0');
|
||||
const newCount = Math.max(0, currentCount - 1);
|
||||
container.dataset.eventCount = newCount.toString();
|
||||
// Tæl resterende events
|
||||
const remainingEvents = container.querySelectorAll('swp-event');
|
||||
const remainingCount = remainingEvents.length;
|
||||
|
||||
// Cleanup hvis tom container
|
||||
if (newCount === 0) {
|
||||
if (remainingCount === 0) {
|
||||
container.remove();
|
||||
return true; // Container blev fjernet
|
||||
}
|
||||
|
||||
// Hvis kun ét event tilbage, konvertér tilbage til normal event
|
||||
if (newCount === 1) {
|
||||
const remainingEvent = container.querySelector('swp-event') as HTMLElement;
|
||||
if (remainingEvent) {
|
||||
// Gendan normal event positioning
|
||||
remainingEvent.style.position = 'absolute';
|
||||
remainingEvent.style.top = container.style.top;
|
||||
remainingEvent.style.left = '2px';
|
||||
remainingEvent.style.right = '2px';
|
||||
|
||||
// Indsæt før container og fjern container
|
||||
container.parentElement?.insertBefore(remainingEvent, container);
|
||||
container.remove();
|
||||
return true; // Container blev fjernet
|
||||
}
|
||||
if (remainingCount === 1) {
|
||||
const remainingEvent = remainingEvents[0] as HTMLElement;
|
||||
// Gendan normal event positioning
|
||||
remainingEvent.style.position = 'absolute';
|
||||
remainingEvent.style.top = container.style.top;
|
||||
remainingEvent.style.left = '2px';
|
||||
remainingEvent.style.right = '2px';
|
||||
|
||||
// Indsæt før container og fjern container
|
||||
container.parentElement?.insertBefore(remainingEvent, container);
|
||||
container.remove();
|
||||
return true; // Container blev fjernet
|
||||
}
|
||||
|
||||
return false; // Container blev ikke fjernet
|
||||
}
|
||||
|
||||
/**
|
||||
* Opret stacked event med reduceret bredde
|
||||
* Opret stacked event med margin-left offset
|
||||
*/
|
||||
public createStackedEvent(eventElement: HTMLElement, underlyingElement: HTMLElement): void {
|
||||
// Beregn reduceret bredde baseret på swp-events-layer (som har den korrekte fulde bredde)
|
||||
// Underlying event skal beholde sin fulde bredde
|
||||
const eventsLayer = underlyingElement.parentElement;
|
||||
const columnWidth = eventsLayer ? eventsLayer.offsetWidth : 200; // fallback
|
||||
const stackedWidth = Math.max(50, columnWidth - EventOverlapManager.STACKING_WIDTH_REDUCTION_PX);
|
||||
public createStackedEvent(eventElement: HTMLElement, underlyingElement: HTMLElement, stackLevel: number = 1): void {
|
||||
// Brug margin-left i stedet for width manipulation
|
||||
const marginLeft = stackLevel * EventOverlapManager.STACKING_WIDTH_REDUCTION_PX;
|
||||
|
||||
eventElement.style.width = `${stackedWidth}px`;
|
||||
eventElement.style.marginLeft = `${marginLeft}px`;
|
||||
eventElement.style.left = '2px';
|
||||
eventElement.style.right = '2px';
|
||||
eventElement.style.left = 'auto';
|
||||
eventElement.style.width = '';
|
||||
eventElement.style.zIndex = this.getNextZIndex().toString();
|
||||
|
||||
// Data attributter
|
||||
eventElement.dataset.overlapType = OverlapType.STACKING;
|
||||
eventElement.dataset.stackedWidth = stackedWidth.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fjern stacking styling fra event
|
||||
*/
|
||||
public removeStackedStyling(eventElement: HTMLElement): void {
|
||||
eventElement.style.marginLeft = '';
|
||||
eventElement.style.width = '';
|
||||
eventElement.style.right = '';
|
||||
eventElement.style.left = '2px';
|
||||
eventElement.style.right = '2px';
|
||||
eventElement.style.zIndex = '';
|
||||
|
||||
delete eventElement.dataset.overlapType;
|
||||
delete eventElement.dataset.stackedWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -249,20 +247,20 @@ export class EventOverlapManager {
|
|||
* Check if element is part of an event group
|
||||
*/
|
||||
public isInEventGroup(element: HTMLElement): boolean {
|
||||
return element.closest('.event-group') !== null;
|
||||
return element.closest('swp-event-group') !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element is a stacked event
|
||||
*/
|
||||
public isStackedEvent(element: HTMLElement): boolean {
|
||||
return element.dataset.overlapType === OverlapType.STACKING;
|
||||
return element.style.marginLeft !== '' && element.style.marginLeft !== '0px';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event group container for an event element
|
||||
*/
|
||||
public getEventGroup(eventElement: HTMLElement): HTMLElement | null {
|
||||
return eventElement.closest('.event-group') as HTMLElement;
|
||||
return eventElement.closest('swp-event-group') as HTMLElement;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue