fix: scope the "Show in folder" reveal scroll to the tree body
A reveal on a deep tree shifted the whole panel (tab bar included) out of the viewport: the reveal used native scrollIntoView, which scrolls every scrollable ancestor, and the fixed panel host ([data-dsh-panel-host], overflow hidden) is still a programmatically scrollable container — the host scrolled instead of (or besides) the tree, with no way to scroll back. Reported on DSH 0.1.2-alpha.1. Two layers: - FileTree: compute the centered, clamped target and scrollTo the tree body only; scrollIntoView is never called. - panel host: overflow hidden -> clip. A hidden box remains programmatically scrollable; clip keeps the same viewport-edge clipping (collapsed panels sliding off-screen) while making the host impossible to scroll, killing the whole leak class (md-toc jumps, future consumer scrollIntoView calls).
This commit is contained in:
+13
-2
@@ -280,10 +280,21 @@ export function FileTree(props: {
|
||||
// (revealPaths), but the row may not be scrolled into sight — a reveal on
|
||||
// a long tree should surface the highlighted file. Re-runs when the tree
|
||||
// data or reveal set changes (the row appears after its level loads).
|
||||
// Scrolls ONLY this tree's body: scrollIntoView would scroll every
|
||||
// scrollable ancestor, and the clipping panel host
|
||||
// ([data-dsh-panel-host]) is still programmatically scrollable — a deep
|
||||
// reveal shifted the whole panel, tab bar included, out of the viewport.
|
||||
useEffect(() => {
|
||||
if (revealed.length === 0) return
|
||||
const row = bodyRef.current?.querySelector('[data-dsh-revealed]')
|
||||
row?.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
||||
const body = bodyRef.current
|
||||
if (body === null) return
|
||||
const row = body.querySelector<HTMLElement>('[data-dsh-revealed]')
|
||||
if (row === null) return
|
||||
const bodyTop = body.getBoundingClientRect().top
|
||||
const rowRect = row.getBoundingClientRect()
|
||||
const target = body.scrollTop + (rowRect.top + rowRect.height / 2) - (bodyTop + body.clientHeight / 2)
|
||||
const max = Math.max(body.scrollHeight - body.clientHeight, 0)
|
||||
body.scrollTo({ top: Math.min(Math.max(target, 0), max), behavior: 'smooth' })
|
||||
}, [revealed, data])
|
||||
|
||||
/** Copy `text`; on success flip the row's copied label for a moment. */
|
||||
|
||||
@@ -44,8 +44,12 @@
|
||||
page draggable left-right and up-down while any panel was collapsed.
|
||||
The host is exactly viewport-sized (inset: 0), so the clip is invisible
|
||||
until a panel slides in from the edge; internal scroll containers and
|
||||
the app's fixed overlay stack are unaffected. */
|
||||
overflow: hidden;
|
||||
the app's fixed overlay stack are unaffected. `clip`, not `hidden`: a
|
||||
hidden box stays a PROGRAMMATICALLY scrollable container, and a
|
||||
scrollIntoView fired from panel content (the file tree's "Show in
|
||||
folder" reveal) scrolled the host itself — shifting the whole panel,
|
||||
tab bar included, out of the viewport with no way to scroll back. */
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
:global([data-dsh-panel-host][data-dsh-panel-host-degraded]) {
|
||||
|
||||
Reference in New Issue
Block a user