Improves event overlap stacking logic

Ensures event stacking only occurs when events actually overlap in pixel space, preventing visual artifacts. Breaks stacking chains when events no longer overlap due to middle element removal, and correctly resets styling.
This commit is contained in:
Janus Knudsen 2025-09-04 23:51:39 +02:00
parent 5bdb2f578d
commit c7716d1b8f

View file

@ -203,12 +203,46 @@ export class SimpleEventOverlapManager {
const prevLink = this.getStackLink(prevElement);
const nextLink = this.getStackLink(nextElement);
// CRITICAL: Check if prev and next actually overlap without the middle element
const actuallyOverlap = this.checkPixelOverlap(prevElement, nextElement);
if (!actuallyOverlap) {
// CHAIN BREAKING: prev and next don't overlap - break the chain
console.log('Breaking stack chain - events do not overlap directly');
// Prev element: remove next link (becomes end of its own chain)
this.setStackLink(prevElement, {
...prevLink!,
next: undefined
});
// Next element: becomes standalone (remove all stack links and styling)
this.setStackLink(nextElement, null);
nextElement.style.marginLeft = '';
nextElement.style.zIndex = '';
// If next element had subsequent events, they also become standalone
if (nextLink?.next) {
let subsequentId: string | undefined = nextLink.next;
while (subsequentId) {
const subsequentElement = this.findElementById(subsequentId);
if (!subsequentElement) break;
const subsequentLink = this.getStackLink(subsequentElement);
this.setStackLink(subsequentElement, null);
subsequentElement.style.marginLeft = '';
subsequentElement.style.zIndex = '';
subsequentId = subsequentLink?.next;
}
}
} else {
// NORMAL STACKING: they overlap, maintain the chain
this.setStackLink(prevElement, {
...prevLink!,
next: link.next
});
// FIXED: Use prev's stackLevel + 1 instead of subtracting 1
const correctStackLevel = (prevLink?.stackLevel ?? 0) + 1;
this.setStackLink(nextElement, {
...nextLink!,
@ -216,11 +250,12 @@ export class SimpleEventOverlapManager {
stackLevel: correctStackLevel
});
// CRITICAL: Update visual styling to match new stackLevel
// Update visual styling to match new stackLevel
const marginLeft = correctStackLevel * SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX;
nextElement.style.marginLeft = `${marginLeft}px`;
nextElement.style.zIndex = `${100 + correctStackLevel}`;
}
}
} else if (link.prev) {
// Last element - remove next link from prev
const prevElement = this.findElementById(link.prev);
@ -244,15 +279,16 @@ export class SimpleEventOverlapManager {
}
}
// For middle element removal, we've already set the correct stackLevel for next element
// Only update subsequent elements after the next one
// Only update subsequent stack levels if we didn't break the chain
if (link.prev && link.next) {
// Middle removal - update elements after the next one
const nextElement = this.findElementById(link.next);
const nextLink = nextElement ? this.getStackLink(nextElement) : null;
if (nextLink?.next) {
// If next element still has a stack link, the chain wasn't broken
if (nextLink && nextLink.next) {
this.updateSubsequentStackLevels(nextLink.next, -1);
}
// If nextLink is null, chain was broken - no subsequent updates needed
} else {
// First or last removal - update all subsequent
this.updateSubsequentStackLevels(link.next, -1);
@ -500,4 +536,23 @@ export class SimpleEventOverlapManager {
private findElementById(eventId: string): HTMLElement | null {
return document.querySelector(`swp-event[data-event-id="${eventId}"]`) as HTMLElement;
}
/**
* Check if two elements overlap in pixel space
*/
private checkPixelOverlap(element1: HTMLElement, element2: HTMLElement): boolean {
if (!element1 || !element2) return false;
const top1 = parseFloat(element1.style.top) || 0;
const height1 = parseFloat(element1.style.height) || 0;
const bottom1 = top1 + height1;
const top2 = parseFloat(element2.style.top) || 0;
const height2 = parseFloat(element2.style.height) || 0;
const bottom2 = top2 + height2;
// Add tolerance for small gaps (borders, etc)
const tolerance = 2;
return !(bottom1 <= (top2 + tolerance) || bottom2 <= (top1 + tolerance));
}
}