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:
1 parent
41272bd9ce
commit
f2a33a9164
4 files changed
+83
-2
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
define('APP_NAME', 'hyprcrd');
|
define('APP_NAME', 'hyprcrd');
|
||||||
define('APP_VERSION', '1.9.6');
|
define('APP_VERSION', '1.9.7');
|
||||||
|
|
||||||
// Database path — writable directory, outside webroot
|
// Database path — writable directory, outside webroot
|
||||||
// DB_PATH / UPLOAD_DIR can be pre-defined by the Electron router for app isolation
|
// DB_PATH / UPLOAD_DIR can be pre-defined by the Electron router for app isolation
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hyprcrd",
|
"name": "hyprcrd",
|
||||||
"version": "1.9.6",
|
"version": "1.9.7",
|
||||||
"description": "hyprcrd — self-hosted card manager",
|
"description": "hyprcrd — self-hosted card manager",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -780,6 +780,50 @@ body.detail-image-fs-open { overflow: hidden; }
|
|||||||
background: color-mix(in srgb, var(--t-accent) 12%, transparent) !important;
|
background: color-mix(in srgb, var(--t-accent) 12%, transparent) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bucket bar: no visible scrollbar, soft edge fades when the row
|
||||||
|
overflows so the user knows there's more off-screen. Fade is
|
||||||
|
implemented via absolutely-positioned overlay pseudo-elements on
|
||||||
|
#bucket-row (parent) rather than mask-image on the bar itself,
|
||||||
|
because mask-image would also clip super-bucket popovers rendered
|
||||||
|
inside the bar. Overlays use pointer-events:none so clicks pass
|
||||||
|
through to the tabs underneath. */
|
||||||
|
#bucket-bar {
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
#bucket-bar::-webkit-scrollbar { display: none; }
|
||||||
|
|
||||||
|
#bucket-row::before,
|
||||||
|
#bucket-row::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 32px;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
#bucket-row::before {
|
||||||
|
left: 24px; /* matches .px-6 = 24px on #bucket-row */
|
||||||
|
background: linear-gradient(to right, var(--t-bg) 0%, transparent 100%);
|
||||||
|
}
|
||||||
|
#bucket-row::after {
|
||||||
|
/* Stop at the right edge of #bucket-bar (before the toolbar
|
||||||
|
toggles). The toolbar is flex-0, bar is flex-1 — the easiest
|
||||||
|
way is to inset the overlay by the total width of the toolbar
|
||||||
|
via a right offset that the JS writes. Defaults to 24px (the
|
||||||
|
.px-6 pad) and the JS overrides with the real value. */
|
||||||
|
right: var(--bucket-row-right-inset, 24px);
|
||||||
|
background: linear-gradient(to left, var(--t-bg) 0%, transparent 100%);
|
||||||
|
}
|
||||||
|
#bucket-row.fade-left::before,
|
||||||
|
#bucket-row.fade-both::before { opacity: 1; }
|
||||||
|
#bucket-row.fade-right::after,
|
||||||
|
#bucket-row.fade-both::after { opacity: 1; }
|
||||||
|
|
||||||
/* Unread dot on the fixed `all` bucket-tab — appears when new cards
|
/* Unread dot on the fixed `all` bucket-tab — appears when new cards
|
||||||
arrive while the user is viewing a different bucket. Cleared the
|
arrive while the user is viewing a different bucket. Cleared the
|
||||||
moment the user navigates back to all (renderBoard drains the
|
moment the user navigates back to all (renderBoard drains the
|
||||||
|
|||||||
@@ -186,6 +186,43 @@ const inpBucket = $('inp-bucket');
|
|||||||
const searchFilter = $('search-bucket-filter');
|
const searchFilter = $('search-bucket-filter');
|
||||||
const tasksFilter = $('tasks-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 —
|
/** Switch the active bucket without going through the click path —
|
||||||
* used by the number-key hotkeys (and any other code that wants to
|
* used by the number-key hotkeys (and any other code that wants to
|
||||||
* activate a bucket programmatically without triggering the super-
|
* activate a bucket programmatically without triggering the super-
|
||||||
|
|||||||
Reference in new issue
Block a user