bucket-bar: hide scrollbar, soft edge fades, wheel-to-scroll

The horizontal scrollbar on the bucket row was ugly and
inconsistent with the rest of the app's scrollbar-less panels. Hide
it, redirect vertical wheel over the bar to horizontal scroll, and
surface overflow with soft bg-coloured gradients on whichever side
has more content off-screen: right only while anchored at the
start, both in the middle, left only at the end.

Fade overlays live on #bucket-row (parent) as pseudo-elements with
pointer-events:none, rather than mask-image on the bar itself — a
mask would also clip super-bucket popovers that are intentionally
rendered inside #bucket-bar for the collab MutationObserver.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-15 21:20:27 +02:00
1 parent 41272bd9ce
commit f2a33a9164
4 files changed
+83 -2

No files matched your search

+37
View File
@@ -186,6 +186,43 @@ const inpBucket = $('inp-bucket');
const searchFilter = $('search-bucket-filter');
const tasksFilter = $('tasks-bucket-filter');
// ── Overflow handling for the bucket bar ─────────────────────────
// Scrollbar is hidden (CSS). Mouse-wheel over the bar translates to
// horizontal scroll so long bucket rows stay usable without a bar.
// Edge fades kick in dynamically based on scroll position so the
// user sees there's more off-screen — both sides at once in the
// middle, one side at either extreme. Fade overlays live on the
// parent #bucket-row (see CSS) so they don't clip super-bucket
// popovers that render inside #bucket-bar itself.
const barRow = $('bucket-row');
function updateBucketBarFades() {
if (!bar || !barRow) return;
const canLeft = bar.scrollLeft > 2;
const canRight = bar.scrollLeft + bar.clientWidth < bar.scrollWidth - 2;
barRow.classList.toggle('fade-left', canLeft && !canRight);
barRow.classList.toggle('fade-right', !canLeft && canRight);
barRow.classList.toggle('fade-both', canLeft && canRight);
// Right-side fade overlay is anchored to the parent's right edge;
// the actual bar ends before the toolbar toggles. Compute the
// distance so the gradient lines up with the bar's right edge.
const rowRect = barRow.getBoundingClientRect();
const barRect = bar.getBoundingClientRect();
const rightInset = Math.max(0, rowRect.right - barRect.right);
barRow.style.setProperty('--bucket-row-right-inset', rightInset + 'px');
}
if (bar) {
bar.addEventListener('wheel', (e) => {
if (e.deltaY === 0) return;
if (bar.scrollWidth <= bar.clientWidth) return;
e.preventDefault();
bar.scrollLeft += e.deltaY;
}, { passive: false });
bar.addEventListener('scroll', updateBucketBarFades, { passive: true });
window.addEventListener('resize', updateBucketBarFades);
new MutationObserver(updateBucketBarFades).observe(bar, { childList: true, subtree: false });
updateBucketBarFades();
}
/** Switch the active bucket without going through the click path —
* used by the number-key hotkeys (and any other code that wants to
* activate a bucket programmatically without triggering the super-