Repository navigation
Conversation
…ontent settles Positioning is not the end of settling: chrome that mounts once the content is placed (scroll buttons in the flex flow, a header, a footer) shrinks the viewport under the open-time alignment and left a far-down selection below the fold. The content now watches the viewport with a ResizeObserver and realigns on every resize until the user scrolls, which retires the scroll down button's mount-time realign — the code huntabyte#2109 had to guard — and moves the wheel/touchmove latch onto the content so a select without scroll buttons has it too.
Contributor
Author
|
Run evidence (macOS, chromium + webkit via New tests against unpatched main (chromium,
Branch: select + combobox, chromium + webkit —
Control: branch with only the
svelte-check —
|
🦋 Changeset detectedLatest commit: 82a6a33 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
built with Refined Cloudflare Pages Action⚡ Cloudflare Pages Deployment
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
When a
SelectorComboboxopens,SelectContentStatescrolls the highlighted item into view once the content is positioned (and again whenever the highlight changes):Positioning is not the end of settling. Chrome that mounts after the content is placed —
ScrollDownButtonin the flex flow (it renders under{#if canScrollDown}, which is only known once the viewport has been measured), a header, a footer, a "loading" row that resolves — takes its height from the viewport, and the alignment that just put the selection on screen is undone by the resize. Open a select whose selected item sits far down the list, with a footer that appears after placement, and the selection is left below the fold by the footer's height.The scroll buttons paper over their own case with a second alignment in
SelectScrollDownButtonState, run 5 ms after the button mounts. That is the code #2109 had to guard: the button remounts every time the viewport leaves the bottom, so the mount-time realign fired into the user's scroll gesture. The guard (userHasScrolled) fixed the symptom; the mount event was never the right trigger. What the alignment actually needs to follow is the viewport's size.The fix
The content watches the viewport with a
ResizeObserverfrom the moment it is positioned, and realigns the highlighted item on every resize until the user takes the scroll position over — theuserHasScrolledlatch from #2109, now set by the content itself fromwheel/touchmoveon the viewport (and, as before, by pressing or moving over a scroll button). Once latched, the observer stops calling the alignment; keyboard navigation's own scroll-into-view is untouched.That makes the scroll down button's mount-time realign redundant, so it is deleted along with its timer; the button's
scrolllistener forcanScrollDownstays. Thewheel/touchmovelatch listeners move fromSelectScrollDownButtonState— where they existed only if the app rendered<Select.ScrollDownButton>at all (the state is constructed outside the button's{#if}) — to the content, so a select without scroll buttons now has the latch too.One behavioural difference worth naming: a scroll button remount that does not change the viewport's size (overlaid buttons) no longer triggers an alignment. Nothing needed that alignment — the highlighted item was already aligned at positioning — and it is the case #2109's regression test covers.
Not latched, as before: programmatic
scrollTopwrites, and scrollbar drags (the viewport hides its scrollbar).Tests
New
select-settling-content-test.svelte— 60 items in a 200 px content, no scroll buttons, and a footer inside the content that starts at 0 px — with two tests inselect.browser.test.ts:should keep the selected item in view while chrome mounts after the content is positioned— opens on the last item, waits for it to be in view and for the observer's initial notification to be behind us, then grows the footer to 48 px and, after that settles, to 96 px, expecting the item back in view after each. Two separately settled resizes so that a single delayed alignment (or the observer's initial notification alone) cannot pass it. Against unpatchedmainit fails on the first resize: the item is left 48 px below the viewport.should stop realigning once the user has scrolled— same setup, then awheelon the viewport and a scroll to the top, then the footer grows; after the observer has delivered, the viewport must still be at 0. As a control, with only theuserHasScrolledguard removed from the observer callback this test fails (the viewport is realigned onto the last item), so it does exercise the observer, not just the absence of one.The #2109 tests still pass:
should keep the user's scroll position when the scroll down button remountsandshould still scroll the selected item into view when opening(in-flow buttons — this is the case the deleted mount-time realign existed for, and the resize observer now covers it).pnpm -F tests test:browser --run src/tests/select src/tests/comboboxpasses on chromium and webkit (282 tests). SyntheticwheelandscrollTopwrites stand in for the gesture; they test the latch wiring, not native scrolling.