feat: hover preview dim effect — overlay dims page, hovered tile + popover stand out
1.5s hover delay (up from 350ms) for deliberate preview. When popover appears, a semi-transparent dimmer covers the page (z-index 499). The hovered tile/sidebar-item gets z-index 500 (same as popover) so it appears above the dimmer alongside the preview. Both cleaned up on mouseleave via hidePreview().
This commit is contained in:
+22
-2
@@ -127,6 +127,8 @@ let _notificationPermission = 'default';
|
||||
let _pollFailCount = 0;
|
||||
let _previewPopover = null;
|
||||
let _previewTimer = null;
|
||||
var _previewDimmer = null;
|
||||
var _previewEl = null;
|
||||
|
||||
// ─── DOM helpers ──────────────────────────────────────────────────────────────
|
||||
function $(id) {
|
||||
@@ -526,6 +528,16 @@ function showPreview(el, name) {
|
||||
|
||||
hidePreview();
|
||||
|
||||
// Dim layer — behind popover, above everything else
|
||||
var dimmer = document.createElement('div');
|
||||
dimmer.className = 'preview-dimmer';
|
||||
document.body.appendChild(dimmer);
|
||||
_previewDimmer = dimmer;
|
||||
|
||||
// Lift the hovered element above the dimmer
|
||||
el.classList.add(el.classList.contains('sidebar-item') ? 'item--previewing' : 'tile--previewing');
|
||||
_previewEl = el; // track so we can remove the class later
|
||||
|
||||
var popover = document.createElement('div');
|
||||
popover.className = 'preview-popover';
|
||||
var pre = document.createElement('pre');
|
||||
@@ -592,6 +604,14 @@ function hidePreview() {
|
||||
_previewPopover.remove();
|
||||
_previewPopover = null;
|
||||
}
|
||||
if (_previewDimmer) {
|
||||
_previewDimmer.remove();
|
||||
_previewDimmer = null;
|
||||
}
|
||||
if (_previewEl) {
|
||||
_previewEl.classList.remove('tile--previewing', 'item--previewing');
|
||||
_previewEl = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Notification permission ────────────────────────────────────────────────
|
||||
@@ -1103,7 +1123,7 @@ function bindStaticEventListeners() {
|
||||
gridEl.addEventListener('mouseenter', function (e) {
|
||||
var tile = e.target.closest('.session-tile');
|
||||
if (!tile) return;
|
||||
_previewTimer = setTimeout(function () { showPreview(tile); }, 350);
|
||||
_previewTimer = setTimeout(function () { showPreview(tile); }, 1500);
|
||||
}, true); // useCapture: true for delegation with mouseenter
|
||||
|
||||
gridEl.addEventListener('mouseleave', function (e) {
|
||||
@@ -1121,7 +1141,7 @@ function bindStaticEventListeners() {
|
||||
if (!item) return;
|
||||
var name = item.dataset.session;
|
||||
if (!name) return;
|
||||
_previewTimer = setTimeout(function () { showPreview(item, name); }, 350);
|
||||
_previewTimer = setTimeout(function () { showPreview(item, name); }, 1500);
|
||||
}, true);
|
||||
|
||||
sidebarListEl.addEventListener('mouseleave', function (e) {
|
||||
|
||||
@@ -986,6 +986,20 @@ body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.preview-dimmer {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 499; /* behind popover (500), above everything else */
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
pointer-events: none; /* clicks pass through — don't block mouseleave */
|
||||
}
|
||||
|
||||
.session-tile.tile--previewing,
|
||||
.sidebar-item.item--previewing {
|
||||
position: relative;
|
||||
z-index: 500; /* same as popover — above dimmer */
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Responsive overlay sidebar at <960px
|
||||
============================================================ */
|
||||
|
||||
@@ -2074,4 +2074,21 @@ test('hover preview popover works for both grid tiles and sidebar items', () =>
|
||||
'must handle sidebar items too');
|
||||
});
|
||||
|
||||
test('hover preview popover has dim overlay and highlights hovered element', () => {
|
||||
const source = fs.readFileSync(
|
||||
new URL('../app.js', import.meta.url), 'utf8'
|
||||
);
|
||||
assert.ok(source.includes('preview-dimmer'), 'must create dimmer overlay');
|
||||
assert.ok(source.includes('tile--previewing'), 'must lift grid tile above dimmer');
|
||||
assert.ok(source.includes('item--previewing'), 'must lift sidebar item above dimmer');
|
||||
});
|
||||
|
||||
test('hover preview delay is 1500ms (not 350ms)', () => {
|
||||
const source = fs.readFileSync(
|
||||
new URL('../app.js', import.meta.url), 'utf8'
|
||||
);
|
||||
assert.ok(source.includes('1500'), 'hover delay must be 1500ms');
|
||||
assert.ok(!source.includes(', 350)'), 'old 350ms delay must be removed');
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -631,3 +631,9 @@ def test_preview_popover_css_exists():
|
||||
assert "position: fixed" in block or "position:fixed" in block, (
|
||||
".preview-popover must use position: fixed"
|
||||
)
|
||||
|
||||
|
||||
def test_preview_dimmer_css_exists():
|
||||
"""Dim overlay behind popover must have CSS rules."""
|
||||
css = read_css()
|
||||
assert ".preview-dimmer" in css
|
||||
|
||||
Reference in New Issue
Block a user