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 prevLink = this.getStackLink(prevElement);
const nextLink = this.getStackLink(nextElement); 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, { this.setStackLink(prevElement, {
...prevLink!, ...prevLink!,
next: link.next next: link.next
}); });
// FIXED: Use prev's stackLevel + 1 instead of subtracting 1
const correctStackLevel = (prevLink?.stackLevel ?? 0) + 1; const correctStackLevel = (prevLink?.stackLevel ?? 0) + 1;
this.setStackLink(nextElement, { this.setStackLink(nextElement, {
...nextLink!, ...nextLink!,
@ -216,11 +250,12 @@ export class SimpleEventOverlapManager {
stackLevel: correctStackLevel 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; const marginLeft = correctStackLevel * SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX;
nextElement.style.marginLeft = `${marginLeft}px`; nextElement.style.marginLeft = `${marginLeft}px`;
nextElement.style.zIndex = `${100 + correctStackLevel}`; nextElement.style.zIndex = `${100 + correctStackLevel}`;
} }
}
} else if (link.prev) { } else if (link.prev) {
// Last element - remove next link from prev // Last element - remove next link from prev
const prevElement = this.findElementById(link.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 stack levels if we didn't break the chain
// Only update subsequent elements after the next one
if (link.prev && link.next) { if (link.prev && link.next) {
// Middle removal - update elements after the next one
const nextElement = this.findElementById(link.next); const nextElement = this.findElementById(link.next);
const nextLink = nextElement ? this.getStackLink(nextElement) : null; 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); this.updateSubsequentStackLevels(nextLink.next, -1);
} }
// If nextLink is null, chain was broken - no subsequent updates needed
} else { } else {
// First or last removal - update all subsequent // First or last removal - update all subsequent
this.updateSubsequentStackLevels(link.next, -1); this.updateSubsequentStackLevels(link.next, -1);
@ -500,4 +536,23 @@ export class SimpleEventOverlapManager {
private findElementById(eventId: string): HTMLElement | null { private findElementById(eventId: string): HTMLElement | null {
return document.querySelector(`swp-event[data-event-id="${eventId}"]`) as HTMLElement; 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));
}
} }