summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-09-07 07:07:32 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-09-07 07:07:32 +0000
commit86f1486b77cb345f3cfdacecbc676692cd0d89f7 (patch)
treed202ec51599a0b7c7a0827a9b7c4867688c888a7
parent97ca0d218f8c9a983b6e6d02e01325f266baac32 (diff)
Split timeline width across lanes instead of stacking overlapping events
The CSS gives every .calendar-event `left: 8px; right: 8px`, and the positioning code only ever overrode `left` (by 16px per column). So two events occupying the same slot both stayed full-width to the right edge and the later one simply covered the earlier one, leaving a 16px sliver. The column assignment was computing a correct answer that the layout then threw away. Reported as two yard-work chores rendering on top of each other. They sit at byte-identical timestamps because a bucket cycle stamps every item it activates with the same due_date, which makes this the maximally-bad case (identical top and height, so the overlap is total). Events now split the available width across the number of lanes in their overlap cluster, and a lone event keeps the full-width behaviour it had. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
-rw-r--r--web/static/js/app.js27
1 files changed, 25 insertions, 2 deletions
diff --git a/web/static/js/app.js b/web/static/js/app.js
index 6071ce3..0a74b46 100644
--- a/web/static/js/app.js
+++ b/web/static/js/app.js
@@ -54,7 +54,21 @@ function initTimelineCalendar(calendarId, nowLineId, untimedSectionId) {
ev.column = col;
}
- // Position events with column-based indentation
+ // How many lanes each event has to share its slot with. The CSS gives every
+ // event `left: 8px; right: 8px` (full width); previously only `left` was
+ // overridden, by 16px per column, so two events at the SAME time stayed
+ // full-width and the later one simply covered the earlier one except for a
+ // 16px sliver -- reported as two bucket chores "rendered on top of each
+ // other". They land on identical timestamps because a bucket cycle stamps
+ // every item it activates with the same due_date.
+ eventData.forEach(function(ev) {
+ const overlapping = eventData.filter(function(other) {
+ return other.endMin > ev.startMin && other.startMin < ev.endMin;
+ });
+ ev.lanes = Math.max.apply(null, overlapping.map(function(o) { return o.column; })) + 1;
+ });
+
+ // Position events, splitting the available width across lanes
eventData.forEach(function(ev) {
const el = ev.el;
const hour = parseInt(el.dataset.hour);
@@ -65,7 +79,16 @@ function initTimelineCalendar(calendarId, nowLineId, untimedSectionId) {
el.style.top = top + 'px';
el.style.height = height + 'px';
- el.style.left = (8 + ev.column * 16) + 'px';
+ if (ev.lanes > 1) {
+ const lane = '((100% - 16px) / ' + ev.lanes + ')';
+ el.style.left = 'calc(8px + ' + ev.column + ' * ' + lane + ')';
+ el.style.width = 'calc(' + lane + ' - 4px)';
+ el.style.right = 'auto';
+ } else {
+ el.style.left = '8px';
+ el.style.right = '8px';
+ el.style.width = '';
+ }
el.style.display = 'block';
// Debounced hover effect (100ms delay)