Group Home/zoom/base-map into one top-left stack, NPS-style
CI / check (push) Successful in 42s
CI / deploy (push) Successful in 16s

Home was alone at top-left and zoom (NavigationControl) was down at
bottom-right with GeolocateControl — split across two corners instead of
grouped together like the National Park Service's own map controls
(https://www.nps.gov/, screenshot: Home, zoom in/out, base map, all
stacked in one place).

Moved NavigationControl up to join Home (showCompass: false — this map
never rotates, so a bearing-reset button would be dead weight), and added
MapLayersButton, a small map-corner control that's a second entry point
into the same Online/Offline panel LayerToggleControl already manages in
the page's sidebar (both go through the same toggle() now, so there's one
source of truth for whether the panel's open). GeolocateControl stays
alone at bottom-right — it's a different action (go to my actual location)
from Home (reset to the app's default view), which was worth clarifying
since the two are easy to conflate.
This commit is contained in:
randy committed 2026-09-07 20:23:37 +02:00
1 parent be6d61e136
commit 215957382a
2 files changed
+70 -3

No files matched your search

+56 -2
View File
@@ -127,6 +127,13 @@ class LayerToggleControl {
if (open) notifySidePanelOpen(this._panel);
}
// Public wrapper so a second entry point (MapLayersButton, a map corner
// control) can open/close the same panel without reaching into a
// "private" method by convention only.
toggle() {
this._togglePanel();
}
setActive(name) {
this._panel.querySelectorAll(".basemap-panel__option").forEach((button) => {
button.classList.toggle("active", button.dataset.style === name);
@@ -142,6 +149,38 @@ class LayerToggleControl {
}
}
// A small map-corner button (grouped with Home/zoom, see below) that's a
// second entry point to the same panel LayerToggleControl already manages
// in the page's own sidebar — matching the National Park Service's own
// map control layout, which keeps zoom/home/base-map together on the map
// rather than split between the map and a separate side rail.
class MapLayersButton {
constructor(layerToggle) {
this._layerToggle = layerToggle;
}
onAdd() {
this._container = document.createElement("div");
this._container.className = "maplibregl-ctrl maplibregl-ctrl-group";
const button = document.createElement("button");
button.type = "button";
button.title = "Base map";
button.setAttribute("aria-label", "Base map");
button.innerHTML = LAYERS_ICON;
button.addEventListener("click", () => this._layerToggle.toggle());
this._container.appendChild(button);
return this._container;
}
onRemove() {
this._container.remove();
}
getDefaultPosition() {
return "top-left";
}
}
const map = new MaplibreMap({
container: "map",
style: ONLINE_STYLE,
@@ -155,7 +194,6 @@ const map = new MaplibreMap({
hash: "map",
});
map.addControl(new NavigationControl(), "bottom-right");
map.addControl(
new GeolocateControl({
positionOptions: { enableHighAccuracy: true },
@@ -164,19 +202,34 @@ map.addControl(
}),
"bottom-right",
);
// A compact top-left stack — Home, zoom, base map — matching the National
// Park Service's own map control layout. Added in this order since
// MapLibre stacks same-position controls in add order.
//
// homePosition/zoom pinned to the app's own default rather than left to the
// button's own fallback (current center/zoom at construction) — now that
// the URL hash can restore any view on load (see the Hash option above),
// "current view" and "the default view" are no longer the same thing, and
// a home button that resets to wherever you happened to load the page
// wouldn't be a home button.
// wouldn't be a home button. This is *not* "go to my location" — that's
// GeolocateControl, above.
map.addControl(new HomeButton({ homePosition: CENTER, zoom: ZOOM }), "top-left");
// showCompass: false — this map never rotates, so a bearing-reset button
// (NavigationControl's default third button) would just be dead weight.
map.addControl(new NavigationControl({ showCompass: false }), "top-left");
const sidebar = document.getElementById("sidebar");
const layerToggle = new LayerToggleControl(applyStyle);
layerToggle.mount(sidebar);
layerToggle.setActive("online");
// The sidebar already has a "Base map" icon (opens the same panel) —
// this is a second entry point for it, grouped with Home/zoom on the map
// itself rather than off in the page's own side rail, matching NPS's
// layout. Added after mount() since it reuses that panel/toggle state.
const mapLayersButton = new MapLayersButton(layerToggle);
map.addControl(mapLayersButton, "top-left");
const profilePanel = new TrackProfilePanel(map);
profilePanel.mount();
@@ -202,6 +255,7 @@ fetch("/api/offline-tiles")
applyStyle("offline");
} else {
layerToggle.remove();
map.removeControl(mapLayersButton);
}
})
.catch(() => {