Preserve browse scroll positions exactly when returning from details

Skip ensure-visible and synced-row animations on focus restore, and
snapshot/restore panel scrollTop plus synced-row scrollLeft around
detail navigation so the browse page comes back pixel-identical.
This commit is contained in:
2026-09-09 04:51:10 +00:00
parent 8f462b9e1d
commit 36e2fdd5ff
2 changed files with 74 additions and 9 deletions
@@ -702,7 +702,7 @@ function findNextElement(
}
}
function focusElement(element: HTMLElement | null) {
function focusElement(element: HTMLElement | null, options?: { preserveScroll?: boolean }) {
if (!element) return
if (!isElementInActiveScope(element)) return
@@ -714,8 +714,10 @@ function focusElement(element: HTMLElement | null) {
element.classList.add("nav-focused")
element.focus({ preventScroll: true })
ensureElementVisibleVertically(element)
syncRowsToElement(element)
if (!options?.preserveScroll) {
ensureElementVisibleVertically(element)
syncRowsToElement(element)
}
focusedElement.value = element
}
@@ -727,13 +729,16 @@ function getFocusState(): { row: number; col: number } | null {
return { row, col }
}
function restoreFocusState(state: { row: number; col: number } | null) {
function restoreFocusState(
state: { row: number; col: number } | null,
options?: { preserveScroll?: boolean },
) {
if (!state) return
const target = findElementAt(state.row, state.col)
if (target) {
setTimeout(() => {
focusElement(target.element)
focusElement(target.element, options)
}, 50)
}
}
@@ -893,6 +898,32 @@ export function installKeyboardNavigation() {
})
}
export interface SyncedRowScrollSnapshot {
rows: [HTMLElement, number][]
offset: number
targetOffset: number
}
function snapshotSyncedRowScroll(): SyncedRowScrollSnapshot {
return {
rows: getSyncedRows().map((row) => [row, row.scrollLeft]),
offset: syncedRowsCurrentOffset,
targetOffset: syncedRowsTargetOffset,
}
}
function restoreSyncedRowScroll(snapshot: SyncedRowScrollSnapshot) {
stopSyncedRowAnimation()
for (const [row, scrollLeft] of snapshot.rows) {
if (row.isConnected) {
row.scrollLeft = scrollLeft
}
}
syncedRowsCurrentOffset = snapshot.offset
syncedRowsTargetOffset = snapshot.targetOffset
lastSyncedRowsAnimationAt = null
}
export function useKeyboardNavigation() {
return {
focusedElement,
@@ -901,6 +932,8 @@ export function useKeyboardNavigation() {
focusAt,
getFocusState,
restoreFocusState,
snapshotSyncedRowScroll,
restoreSyncedRowScroll,
}
}