fix: fit an opened map to the window it is opened in - #1828
Merged
Merged
Conversation
1.152.0 clamped the viewport to the map's own extent and derived the zoom floor as max(zoomExtent.min, cover). Both halves of that broke opening a map made on a different screen: - a map smaller than the window was letterboxed - the #map svg was sized to the extent, leaving dead canvas right and bottom; - a map larger than the window could not zoom out to fit, because the default zoomExtent.min of 1 won over the cover scale of <1. Before 1.152 the svg followed the window and the floor was the derived cover value, which also overwrote the zoomExtentMin control. Restore both: - the viewport follows the window (or the viewport the user pinned); the extent no longer bounds it; - the zoom floor is max(viewport / extent), rounded up so the rounding cannot leave a hairline of canvas, written to app.zoomExtent.min and shown in the panel; - constrainZoom() re-applies the current transform through the behavior, since d3 enforces a new extent on gestures only; - an opened map opens at that floor - the fitted view - via resetZoom(0), which no longer goes through a transition; - restoreDefaultZoomExtent re-derives the floor instead of forcing 1, which would have re-broken the invariant. A hand-set floor still applies as typed and stands until the next fit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ Deploy Preview for afmg ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
#labels font-size is written by applyLabelsZoomSize from the current zoom, not by any preset. It baselined as 100px only because a loaded map used to open at scale 1; now that a map opens fitted to the window, it tracks the window size against the map's extent - 1.112.1.map is 1680x849, so at the suite's 1280x720 viewport the floor is 0.849 and the size lands at 108.89px. That is the same category as the scale bar layout the helper already strips, so drop it there and from both baselines.
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 bug
A map opened on a screen that is not the one it was made on no longer fills the window. 1.152.0 changed two things at once in
fitMapToScreen():Each half breaks one direction:
#mapsvg is sized to the map's extent, so the map sits in the top-left with dead canvas right and bottom.zoomExtent.minof1wins, so the map can never be zoomed out to fit.Reproduced with
1.108.12.map(extent 1440×778) at 1600×900 and at 1000×600, and confirmed against a build ofd00e3311^.What it used to do
public/modules/ui/options.jssized the svg from the user's canvas request (which follows the window), and the floor was the derived cover value, which also overwrote the control:The fix
canvas.ts— the viewport follows the window (or the viewport the user pinned); the extent no longer bounds it. The zoom floor ismax(viewport / extent), rounded up — rounding to nearest can leave a sub-pixel strip of canvas, which makes d3'sconstraincentre the map instead of pinning it. It is written toapp.zoomExtent.minand into the panel, so the control shows the limit the canvas enforces.zoom.ts— newconstrainZoom()re-applies the current transform through the behavior, because d3 enforcesscaleExtent/translateExtenton gestures only; a raised floor otherwise did nothing until the user touched the map.resetZoom()targets the floor rather than a bare identity, and skips the transition at duration 0 (Submap and Transform callresetZoom(0)right after a fit).load.ts—fitMapToScreen()runs beforefocusOn()(matching the generation path, so a?scale=/?burg=deep link still wins), followed byresetZoom(0)so an opened map opens at the fitted view.options-tab.ts— a hand-set floor applies as typed and stands until the next fit re-derives it, as before. "Restore default zoom extent" re-derives the floor instead of forcing1, which would have re-broken the invariant.Verified
1.108.12.map, extent 1440×778:#mapsvgsetMapZoom(0.1)clamps to the floor; zooming to 4 and then resizing keepsk=4and only re-derives the floor, so a resize never yanks the user's view; regenerating resets the extent to the window with floor1.942 unit tests,
tscandbiome check src/pass.tests/e2e/canvas-size.spec.tscovers both directions; per the repo rule Playwright was not run locally, so CI is the first run.Notes
app.zoomExtent.minis now a derived value the canvas writes rather than a stored preference — that is what 1.151 did, anddocs/architecture/configuration.mdis updated to say so, but it sits slightly against that file's "preferences are what the user asked for" framing. Worth a second opinion.sync-versionpre-commit hook pullspackage.jsonand the lockfile along with it.🤖 Generated with Claude Code