Group Home/zoom/base-map into one top-left stack, NPS-style
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:
1 parent
be6d61e136
commit
215957382a
2 files changed
+70
-3
No files matched your search
+14
-1
@@ -139,7 +139,20 @@ back to the app's actual default is worth having: a "Home" button
|
|||||||
`home-button` MapLibre control — see `docs/third-party-notices.adoc`)
|
`home-button` MapLibre control — see `docs/third-party-notices.adoc`)
|
||||||
sits top-left on the map, pinned to `CENTER`/`ZOOM` explicitly rather
|
sits top-left on the map, pinned to `CENTER`/`ZOOM` explicitly rather
|
||||||
than its own fallback (the view at construction time, which would just
|
than its own fallback (the view at construction time, which would just
|
||||||
be wherever a loaded permalink happened to point).
|
be wherever a loaded permalink happened to point). It resets to the
|
||||||
|
app's default view, not the visitor's own location — that's
|
||||||
|
`GeolocateControl`, a separate button at bottom-right.
|
||||||
|
|
||||||
|
That Home button is the top of a small top-left stack — Home, zoom
|
||||||
|
in/out (`NavigationControl`, `showCompass: false` since this map never
|
||||||
|
rotates), and a "Base map" button — matching the National Park
|
||||||
|
Service's own map control layout (grouped together on the map itself,
|
||||||
|
rather than split between the map and a side panel). The last of
|
||||||
|
those, `MapLayersButton` (`public/js/map.js`), is a second entry point
|
||||||
|
into the exact same Online/Offline panel `LayerToggleControl` already
|
||||||
|
manages in the page's own sidebar — both call the same
|
||||||
|
`LayerToggleControl#toggle()`, so there's one source of truth for the
|
||||||
|
panel's open/closed state either way.
|
||||||
|
|
||||||
== Weather radar overlay
|
== Weather radar overlay
|
||||||
|
|
||||||
|
|||||||
+56
-2
@@ -127,6 +127,13 @@ class LayerToggleControl {
|
|||||||
if (open) notifySidePanelOpen(this._panel);
|
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) {
|
setActive(name) {
|
||||||
this._panel.querySelectorAll(".basemap-panel__option").forEach((button) => {
|
this._panel.querySelectorAll(".basemap-panel__option").forEach((button) => {
|
||||||
button.classList.toggle("active", button.dataset.style === name);
|
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({
|
const map = new MaplibreMap({
|
||||||
container: "map",
|
container: "map",
|
||||||
style: ONLINE_STYLE,
|
style: ONLINE_STYLE,
|
||||||
@@ -155,7 +194,6 @@ const map = new MaplibreMap({
|
|||||||
hash: "map",
|
hash: "map",
|
||||||
});
|
});
|
||||||
|
|
||||||
map.addControl(new NavigationControl(), "bottom-right");
|
|
||||||
map.addControl(
|
map.addControl(
|
||||||
new GeolocateControl({
|
new GeolocateControl({
|
||||||
positionOptions: { enableHighAccuracy: true },
|
positionOptions: { enableHighAccuracy: true },
|
||||||
@@ -164,19 +202,34 @@ map.addControl(
|
|||||||
}),
|
}),
|
||||||
"bottom-right",
|
"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
|
// 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
|
// 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),
|
// 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
|
// "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
|
// 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");
|
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 sidebar = document.getElementById("sidebar");
|
||||||
|
|
||||||
const layerToggle = new LayerToggleControl(applyStyle);
|
const layerToggle = new LayerToggleControl(applyStyle);
|
||||||
layerToggle.mount(sidebar);
|
layerToggle.mount(sidebar);
|
||||||
layerToggle.setActive("online");
|
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);
|
const profilePanel = new TrackProfilePanel(map);
|
||||||
profilePanel.mount();
|
profilePanel.mount();
|
||||||
@@ -202,6 +255,7 @@ fetch("/api/offline-tiles")
|
|||||||
applyStyle("offline");
|
applyStyle("offline");
|
||||||
} else {
|
} else {
|
||||||
layerToggle.remove();
|
layerToggle.remove();
|
||||||
|
map.removeControl(mapLayersButton);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|||||||
Reference in new issue
Block a user