fix(calendar): lay out overlapping events in side-by-side lanes - #296
Merged
Conversation
Before this change, day and week views rendered every timed event at full column width with identical z-index. When events overlapped in time, the later DOM sibling captured every click across the full column — the earlier chip was visible (poking above/below the overlay) but completely unclickable for real users. Fix applies standard interval graph coloring / greedy lane packing: sort events by start (tiebreak longer-first), scan through assigning each to the first lane whose last event has already ended. Within an overlap cluster every event renders at width = 100/laneCount with a lane-indexed left offset, so each chip has a dedicated hit region. - apps/desktop/src/renderer/src/components/calendar/overlap-layout.ts: new pure function assignLanes() with 11 unit tests covering empty, single, non-overlapping, boundary-touching, two-overlap, chain overlap, multi-cluster, three-way, null-endAt, input-order stability, and nested events. - apps/desktop/src/renderer/src/components/calendar/calendar-day-view.tsx, calendar-week-view.tsx: wired to assignLanes with percentage-based left/width and px-0.5 (@XL:px-1) inner padding for lane gutters. - apps/desktop/src/main/test-hooks.ts: optional overlapMemryTitle field on CalendarProjectionSeedInput that seeds a 9:00–10:00 Memry event overlapping the existing 9:30–10:30 external seed. - apps/desktop/tests/e2e/calendar-overlap.e2e.ts: Playwright regression coverage for day and week views — asserts chips have disjoint horizontal bounding boxes and that clicking the external chip opens the promote-external dialog without force/dispatch tricks.
h4yfans
force-pushed
the
funny-wescoff-37863a
branch
from
April 20, 2026 09:51
82b6670 to
1089878
Compare
h4yfans
added a commit
that referenced
this pull request
May 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix day and week view rendering so overlapping events get dedicated lanes instead of stacking at full column width with identical z-index.
The bug: when two events overlapped in time, the later DOM sibling captured every click across the whole column — the earlier chip was visually peeking above/below but completely unclickable for real users. E2E tests had been working around this with `.first()` and `{ force: true }`.
The fix: standard interval graph coloring / greedy lane packing. Sort events by start (longer-first as tiebreak), greedy-assign each to the first lane whose last event has already ended. Within each overlap cluster, render at `width = 100/laneCount` with a lane-indexed `left` offset. Single events still render at full column width — no regression for the common case.
What's in the diff
Why this approach
Column lane packing is what Google Calendar, Apple Calendar, and Outlook all do — it's the standard UX. The alternative (stack indicator with "+N events" popover) would be inferior on busy days because titles become invisible at a glance.
Cluster-wide `laneCount` (rather than per-event widths based on personal overlap set) keeps the column grid visually consistent — chips align vertically like a mini CSS grid instead of jumping widths.
Test plan
Notes for the reviewer