fix(frontend): make the grid actually follow keyboard focus
deploy / deploy (push) Successful in 20s
deploy / deploy (push) Successful in 20s
The previous attempt wrote scrollContainer.scrollTop, but <main> isn't the
scroll element here (the window/document scrolls, as getScroller and the
infinite-scroll listeners assume) — so it was a no-op and the grid stopped
following the focus. Move back to scrollIntoView({block:'nearest'}), which
scrolls whatever element actually scrolls, and give the card
scroll-margin-top/-bottom so it clears the sticky header and the fixed
navbar instead of sliding under them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -174,6 +174,10 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
/* Keyboard scrollIntoView leaves room for the sticky header above and the
|
||||||
|
fixed bottom navbar below, so the focused card never hides under them. */
|
||||||
|
scroll-margin-top: 52px;
|
||||||
|
scroll-margin-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
|
|||||||
@@ -71,29 +71,16 @@
|
|||||||
focusedId = files[next]?.id ?? null;
|
focusedId = files[next]?.id ?? null;
|
||||||
if (next >= files.length - gridCols() * 2 && hasMore && !loading) void loadMore();
|
if (next >= files.length - gridCols() * 2 && hasMore && !loading) void loadMore();
|
||||||
const id = focusedId;
|
const id = focusedId;
|
||||||
requestAnimationFrame(() => keepFocusedInView(id));
|
// scrollIntoView scrolls whichever element actually scrolls (the window
|
||||||
}
|
// here, not <main>), so the grid follows the focus. The card's
|
||||||
|
// scroll-margin-bottom leaves room for the fixed navbar so it doesn't slide
|
||||||
// Keep the focused card within the scroller, leaving a margin at the bottom for
|
// underneath.
|
||||||
// the fixed navbar (which overlaps the scroll area and otherwise hides the row
|
requestAnimationFrame(() => {
|
||||||
// the focus moves onto). scrollIntoView can't account for that overlay.
|
|
||||||
const FOCUS_MARGIN_TOP = 8;
|
|
||||||
const FOCUS_MARGIN_BOTTOM = 72; // ~navbar height + gap
|
|
||||||
|
|
||||||
function keepFocusedInView(id: string | null) {
|
|
||||||
if (!id || !scrollContainer) return;
|
|
||||||
const idx = files.findIndex((f) => f.id === id);
|
const idx = files.findIndex((f) => f.id === id);
|
||||||
const card = scrollContainer.querySelector<HTMLElement>(`[data-file-index="${idx}"]`);
|
scrollContainer
|
||||||
if (!card) return;
|
?.querySelector<HTMLElement>(`[data-file-index="${idx}"]`)
|
||||||
const cardRect = card.getBoundingClientRect();
|
?.scrollIntoView({ block: 'nearest' });
|
||||||
const scRect = scrollContainer.getBoundingClientRect();
|
});
|
||||||
const top = cardRect.top - scRect.top;
|
|
||||||
const bottom = cardRect.bottom - scRect.top;
|
|
||||||
if (top < FOCUS_MARGIN_TOP) {
|
|
||||||
scrollContainer.scrollTop += top - FOCUS_MARGIN_TOP;
|
|
||||||
} else if (bottom > scRect.height - FOCUS_MARGIN_BOTTOM) {
|
|
||||||
scrollContainer.scrollTop += bottom - (scRect.height - FOCUS_MARGIN_BOTTOM);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action keys operate on the selection; with nothing selected they fall back to
|
// Action keys operate on the selection; with nothing selected they fall back to
|
||||||
|
|||||||
Reference in New Issue
Block a user