feat: peek column to the left of the file list, mirroring the info text on the right
replaces the last commits attempt (hover updating the main preview directly) with what was actually asked for: the main preview now stays on the locked file, untouched by hover; a separate 18rem-wide column to the left of the file list — same width and spot as .proj-info on the right — shows whichever OTHER image is under the mouse while something is locked, and disappears again once the mouse leaves the list or lands back on the locked row itself.\n\npositioned via plain absolute positioning against .filebrowser (not scroll-tracked — it scrolls together with the list naturally), sitting inside the padding .proj-layout already reserves to keep the file list anchored. only enabled above 1300px, with a safety margin over the ~1168px point where that reserved space stops being wide enough; caught and fixed a bug in the same pass where .proj-peek had no display:none fallback below that breakpoint and would otherwise render as a bare block in-flow.
This commit is contained in:
@@ -251,6 +251,40 @@ body:not(.home) .menu { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
/* small peek column — mirrors .proj-info's spot and width on the OTHER side
|
||||
of the file list, showing whichever image is under the mouse while
|
||||
something is locked. sits inside the space .proj-layout already reserves
|
||||
to keep the file list anchored (see above), so it needs that reserved
|
||||
padding to be at least 18rem + the gap wide to have room; 1300px keeps a
|
||||
safety margin above the ~1168px point where that stops being true. plain
|
||||
absolute positioning against .filebrowser — no scroll-tracking needed,
|
||||
it just scrolls together with the file list it's attached to. */
|
||||
.proj-peek { display: none; }
|
||||
@media (min-width: 1300px) {
|
||||
.filebrowser { position: relative; }
|
||||
.proj-peek {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 100%;
|
||||
margin-right: 1.5rem;
|
||||
width: 18rem;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.15s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
.proj-peek.is-visible { opacity: 1; visibility: visible; }
|
||||
.proj-peek img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 70vh;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* prev/next as shell "cd" commands — same vocabulary as the rest of the site */
|
||||
.proj-nav {
|
||||
font-family: var(--font-family-mono);
|
||||
|
||||
+31
-11
@@ -30,9 +30,10 @@
|
||||
if (img.complete) img.style.opacity = "1"; // already cached → no fade
|
||||
}
|
||||
|
||||
// renders row i into the preview pane. this runs on hover AND on lock —
|
||||
// hovering always drives what the pane shows, lock only decides what it
|
||||
// falls back to once the mouse leaves the list again.
|
||||
// renders row i into the MAIN preview pane. driven by lock (click) and
|
||||
// keyboard nav only — hovering never touches this, it drives the small
|
||||
// peek column instead (see bind()), so a locked file stays put while you
|
||||
// browse the others.
|
||||
function render(i, silent, scroll) {
|
||||
if (!rows.length) return;
|
||||
if (i < 0) i = 0;
|
||||
@@ -79,6 +80,27 @@
|
||||
cur = 0; locked = -1;
|
||||
if (!rows.length) return;
|
||||
|
||||
// small side-by-side peek — sits to the left of the file list (mirroring
|
||||
// the info text's spot on the right, same 18rem width) and shows
|
||||
// whichever OTHER image is under the mouse while something is locked,
|
||||
// without disturbing the main preview.
|
||||
var peek = null, peekImg = null;
|
||||
if (container) {
|
||||
peek = document.createElement("div");
|
||||
peek.className = "proj-peek";
|
||||
peekImg = document.createElement("img");
|
||||
peekImg.alt = "";
|
||||
peek.appendChild(peekImg);
|
||||
container.insertBefore(peek, container.firstChild);
|
||||
}
|
||||
function showPeek(a) {
|
||||
var src = a.getAttribute("data-img");
|
||||
if (!peek || !src) { if (peek) peek.classList.remove("is-visible"); return; }
|
||||
peekImg.src = src;
|
||||
peek.classList.add("is-visible");
|
||||
}
|
||||
function hidePeek() { if (peek) peek.classList.remove("is-visible"); }
|
||||
|
||||
rows.forEach(function (a, i) {
|
||||
// portfolio-index rows are real links (data-img + href together) — a
|
||||
// single click there used to jump straight into the project, which
|
||||
@@ -87,29 +109,27 @@
|
||||
// click on the already-locked row, or the preview image itself,
|
||||
// actually navigates.
|
||||
var isProjectLink = a.hasAttribute("href") && a.hasAttribute("data-img");
|
||||
a.addEventListener("mouseenter", function () { render(i); });
|
||||
a.addEventListener("mouseenter", function () {
|
||||
if (locked >= 0 && i !== locked) showPeek(a); else hidePeek();
|
||||
});
|
||||
a.addEventListener("click", function (e) {
|
||||
if (isProjectLink) {
|
||||
if (locked === i) return; // already locked — let the link through
|
||||
e.preventDefault();
|
||||
lock(i);
|
||||
render(i, true);
|
||||
hidePeek();
|
||||
return;
|
||||
}
|
||||
if (a.getAttribute("href")) return; // ".." navigates normally
|
||||
e.preventDefault();
|
||||
if (locked === i) { unlock(); ghostClear(); }
|
||||
else { lock(i); render(i, true); }
|
||||
hidePeek();
|
||||
});
|
||||
});
|
||||
|
||||
// leaving the list reverts the preview back to whatever is locked (or
|
||||
// to the initial file, if nothing is) — hover only ever peeks
|
||||
if (list) {
|
||||
list.addEventListener("mouseleave", function () {
|
||||
render(locked >= 0 ? locked : 0, true);
|
||||
});
|
||||
}
|
||||
if (list) list.addEventListener("mouseleave", hidePeek);
|
||||
|
||||
var start = rows.findIndex(function (a) { return a.classList.contains("is-selected"); });
|
||||
start = start < 0 ? 0 : start;
|
||||
|
||||
Reference in New Issue
Block a user