diff --git a/src/managers/SimpleEventOverlapManager.ts b/src/managers/SimpleEventOverlapManager.ts index f670527..bfca267 100644 --- a/src/managers/SimpleEventOverlapManager.ts +++ b/src/managers/SimpleEventOverlapManager.ts @@ -203,23 +203,58 @@ export class SimpleEventOverlapManager { const prevLink = this.getStackLink(prevElement); const nextLink = this.getStackLink(nextElement); - this.setStackLink(prevElement, { - ...prevLink!, - next: link.next - }); + // CRITICAL: Check if prev and next actually overlap without the middle element + const actuallyOverlap = this.checkPixelOverlap(prevElement, nextElement); - // FIXED: Use prev's stackLevel + 1 instead of subtracting 1 - const correctStackLevel = (prevLink?.stackLevel ?? 0) + 1; - this.setStackLink(nextElement, { - ...nextLink!, - prev: link.prev, - stackLevel: correctStackLevel - }); - - // CRITICAL: 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}`; + 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 + }); + + const correctStackLevel = (prevLink?.stackLevel ?? 0) + 1; + this.setStackLink(nextElement, { + ...nextLink!, + prev: link.prev, + stackLevel: correctStackLevel + }); + + // 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 @@ -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)); + } } \ No newline at end of file