Skip to main content

The Map's Cockpit

Float over the map and you'll find its cockpit: a + FAB that fans out into a tray of actions — layers, basemap, measure, my-location, legend, recenter, lat/long finder — plus zoom buttons and the expand/close controls for the map panel. Two components own all of it:

  • MapButtonsFL — the container: layout, visibility rules, zoom buttons, the panels (measurement, lat/long), and the layers drawer.
  • MapButtonsOpenState — the tray itself: the circular buttons revealed by the +.

The interesting engineering here isn't the buttons — it's deciding when they exist at all.

The visibility algebra

Map real estate is precious, especially on a phone in landscape. Three rules govern what renders, and each is a judgment about screen space:

Rule 1 — buttons earn their pixels. On mobile/landscape the tray wraps in FadeAfterClickContainer (fades after a tap); on desktop, FadeAfterInactivityContainer (fades when the mouse goes idle). Either way, buttons melt away while the operator watches the map.

Rule 2 — no cockpit in a split view. When the layout shows image + map side by side on mobile, the action tray hides entirely (showActionBtns) — two panels are already fighting for a phone screen. Similarly, the Expand/X buttons only appear on mobile when the map is the only panel (showExpandAndX); on desktop they're always available. Both conditions are computed from visibleLocationViewPanels — the same image/map/wide triple you met in Rendering the camera view.

Rule 3 — a hidden map means no buttons at all. A ResizeObserver on the container unmounts everything when its width hits 0. Without this, the absolutely-positioned FABs would bleed out over whatever replaced the map panel.

Zoom in/out buttons are desktop-only — pinch owns that job on touch.

The tray

Each action delegates up to MapButtonsFL by name. The ones with logic worth knowing:

Layers opens the HamburgerMenu drawer — but LayersMenu only mounts inside it while btnRecentlyClicked === 'layers', since the same drawer serves other content. The Layers Menu is its own lesson.

Basemap cycles Topographic → Imagery → … by filtering out webmap-current entries, finding the active one, and advancing modulo the list. It then flips the associated AGOL layers through the same visibility pipeline as everything else. The button's icon always shows the current basemap; the tooltip names the next one.

Location asks the browser Geolocation API and pans there, falling back to defaultLocation on error. Recenter pans back to the selected camera at location-view zoom. Ruler and Legend toggle the corresponding ArcGIS widgets via useMapWidgets. Lat/Long Finder toggles an inline coordinate panel.

Two of the tray items hide behind feature flags — enable-map-ruler-button and enable-map-legend-button — and "Center on Camera" only renders outside tile view (there's no selected camera to center on in the grid). A third flag, enable-map-add-camera, gates a video-call FAB in tile view.

Going deeper: the quirks (all verified)

Wrapping up

A container that decides whether buttons deserve pixels — fade wrappers, panel algebra, a zero-width guard — and a tray that decides what they do, mostly by delegating upward. The basemap cycler and the layers drawer both funnel into the same layer-visibility pipeline the rest of the map uses.

Next: the Layers Menu that drawer opens.