Map Sizing Across Sidebar Changes — Implementation Plan¶
Decisions¶
| Decision | Choice | Why |
|---|---|---|
| Transition scope | Cover every transition that changes the map layout: Edit, View, and Settings | The reported Edit-to-View failure shares the same layout boundary as the other mode changes. |
| Camera behavior | Preserve the current center and zoom | Resizing the Leaflet viewport must not undo the user's current map position. |
| Responsive scope | Desktop and mobile | The available map dimensions and sidebar presentation differ across responsive layouts. |
| Selection behavior | Preserve existing selection and detail-panel semantics | This task fixes geometry and rendering, not which object remains selected after a mode change. |
| Resize ownership | Keep the map mounted where possible and make the Leaflet boundary react to its actual container size | The route already avoids remounting for View/Edit, while a size observer handles conditional panels and CSS transitions without coupling Leaflet to sidebar internals. |
| Persistence/schema | No server, Directus, or schema change | Map sizing is client-side layout behavior; no directus/schema/*.json files exist and no persisted field is being read or written. |
Prerequisites¶
- Keep the route declaration in
app/routes.tsunchanged:project/:projectId/map/:mapIdcontinues to renderapp/routes/project-map.tsx. - Preserve the existing
MapViewStateflow anduseMapViewState; resize handling must not write a new center or zoom merely because the container changed size. - Keep Leaflet client-only and continue using the existing lazy map boundary. Do not import Leaflet into the server-rendered route path beyond the current client-only component.
- Treat
app/features/map-editor/edit-sidebar.tsxand the right-side panels inapp/routes/project-map.tsxas the actual mode-dependent layout owners. The generic shadcn sidebar component is not the controlling layout for this route. - No
directus/schema/*.jsonsnapshot exists in this workspace. No schema verification or persistence migration is required for this task. - There is no test suite. Verification must use
pnpm verify typecheck,pnpm verify format,pnpm build, and focused browser/manual checks.
Step 1 — Add a Leaflet container-resize boundary¶
Goal: Recalculate Leaflet's viewport whenever the map host's rendered dimensions change, without changing the current camera.
Scope
- In:
app/components/map.tsx, add a client-only map child or equivalent internal boundary that observes the rendered map container and invokes Leaflet's size invalidation after its dimensions settle. - In: use the map instance's existing center and zoom while invalidating size; do not call
fitBounds,setView, or any persistence callback as part of this response. - In: handle size changes caused by sidebar insertion/removal, flex reflow, viewport changes, and responsive transitions. Coalesce resize notifications when CSS transitions emit multiple intermediate sizes.
- In: clean up the observer and any scheduled callback when the map unmounts or the observed element changes.
- Out: Tile bounds, layer data, map object selection, camera persistence, and server actions.
Acceptance criteria
- A change to the map host's width or height causes Leaflet to recalculate its visible pixel bounds.
- The map center and zoom are unchanged by a size recalculation.
- Resize callbacks do not run after the map has unmounted.
- Existing upload and tile-set map variants use the same resize behavior.
- The map remains client-only and does not introduce server-rendering errors.
-
pnpm verify typecheckpasses.
Step 2 — Make the route's map host shrinkable and transition-safe¶
Goal: Ensure the flex layout gives the map a truthful, stable rectangle beside the edit and detail panels.
Scope
- In:
app/routes/project-map.tsx, review the flex containers around the map, edit sidebar, bottom edit sections, and right-side detail panels; add the smallest required min-width/min-height and overflow constraints so the central map column can shrink and expand instead of retaining an overflow-sized width. - In: keep the map at the same React tree position for View/Edit transitions, as the current route comment intends. Do not move map children into separate mode branches that would remount Leaflet.
- In: ensure the central map host has an explicit full-size relationship to its parent and that map controls remain positioned inside that host after the available width changes.
- In: account for the Settings branch, where the map is intentionally unmounted; preserve
mapViewstate so returning from Settings restores the existing camera rather than fitting or resetting it. - Out: Redesigning sidebar contents, changing panel widths as a product decision, changing the topbar, or changing map layer rendering logic.
Acceptance criteria
- View/Edit transitions do not leave the map column wider than the space between visible panels.
- The map host can shrink on narrow desktop and mobile viewports without forcing horizontal overflow or cutting off its right/left edge.
- The map remains mounted across View/Edit transitions.
- Entering and leaving Settings preserves the existing map view state when the map remounts.
- Map controls remain fully inside the visible map region after layout changes.
-
pnpm verify typecheckpasses.
Step 3 — Coordinate responsive sidebar changes with map resizing¶
Goal: Verify that the resize boundary responds to both desktop panel changes and mobile sidebar/viewport changes without changing selection behavior.
Scope
- In:
app/routes/project-map.tsxand, only if required by the actual rendered structure,app/features/map-editor/edit-sidebar.tsx, ensure sidebar visibility or size changes are reflected in the map host's observed geometry. - In: preserve the existing selected POI, line, area, dataset feature, and edit-detail panel behavior when switching modes; do not close or reassign selections as a sizing side effect.
- In: support viewport rotation/resizing and the mobile presentation currently provided by the route/components. If a mobile sidebar overlays rather than resizes the map, still invalidate the map when its open/closed state changes if the map host geometry or visible interaction area changes.
- Out: Introducing a new sidebar system, changing mobile navigation semantics, or adding new selection state.
Acceptance criteria
- On desktop, entering and leaving Edit updates the map's usable viewport for both left and right panel changes.
- On mobile, changing the viewport size or sidebar open/closed state does not leave stale clipped map content.
- Switching modes preserves current selections and existing detail-panel behavior.
- No panel or map control overlaps another incoherently at representative narrow and wide sizes.
-
pnpm verify typecheckpasses.
Step 4 — Verify map layers and camera preservation¶
Goal: Confirm the geometry fix resolves the observed blank or clipped left/right rendering for every supported map composition.
Acceptance criteria
-
pnpm verify typecheckpasses. -
pnpm verify formatcompletes successfully. -
pnpm buildpasses. - Manual check: load an uploaded-image map, pan/zoom to a non-default position, switch Edit to View, and confirm the center/zoom remain stable while the image fills the newly available viewport.
- Manual check: repeat with a tile-set map and confirm tile coverage reaches both former sidebar edges after switching modes.
- Manual check: with enabled image overlays, Avesmaps routes/features, POIs, lines, areas, and labels, switch View/Edit and confirm each layer renders across the complete map region without left/right clipping.
- Manual check: enter and leave Settings and confirm the map remounts with the saved in-browser view rather than an unintended refit.
- Manual check: test representative desktop and mobile viewport sizes, including resizing or rotating the viewport while the map is visible.
- Manual check: verify selected object and detail-panel behavior remains unchanged while switching modes.
- Confirm no server module, route loader/action, Directus field, or persisted map value was changed for the sizing fix.
Risks / open questions¶
- The route's Edit sidebar and detail panels are ordinary flex children, while the generic
app/components/ui/sidebar.tsxis not their owner. Implementation should avoid changing the unused abstraction unless a nearby call site proves otherwise. - Leaflet's size invalidation must occur after the flex layout has settled; an observer plus a scheduled callback should be preferred over a mode effect that guesses timing.
- Settings intentionally unmounts the map. Its remount path must continue to use
useMapViewStateand must not treat the remount as a request to fit bounds. - No
directus/schema/*.jsonsnapshot exists, and no Directus field is involved.