Opaque action identifier from the button config.
Layer the feature belongs to.
Feature/POI identifier (null when the source has no stable id).
Whitelisted subset of feature properties, per the payloadFields option of the button.
⚠️ Without payloadFields, this is {} — the default goes to confidentiality, not
convenience, because this is a document event any script on the page can hear. There is
no "send everything" mode, and adding one is not a configuration gap.
OptionallngGeographic position of the popup, when available.
The button that was clicked, for host-owned visual state.
Live node, not a copy: mutating it mutates what the user sees. Prefer setBusy for the pending state — it keeps the busy convention in one place.
Toggles the button's pending state: disabled, aria-busy, and the
gl-poi-popup__action--busy modifier.
Safe to call after the surface has closed — it writes to a detached node, which is inert.
That is why a .finally(() => d.setBusy(false)) needs no guard.
true to enter the pending state, false to leave it.
Closes the surface the button was rendered in — the popup or the side panel, never both.
⚠️ This is deliberately narrower than GeoLeaf.FeatureInfo.close(), which closes both
surfaces unconditionally and emits geoleaf:poi:panel:close as it goes. A popup button
calling that would have closed an unrelated side panel and announced a panel close nobody
performed.
Idempotent: closing an already-closed surface does nothing.
Detail payload for
geoleaf:popup:action.Dispatched when a user clicks a popup or side-panel action button (
type: "action"inpopup.fields). Listen viaGeoLeaf.events.on("geoleaf:popup:action", …).The payload is NOT JSON — and that is the point
Five fields are plain data;
button,setBusyandcloseare a live DOM node and two closures. The key therefore lives in GeoLeafRawEventMap, not in the sanitising bus, which would have delivered them as{}andundefinedwithout any error.⚠️ Consequence for subscribers, and it is the only breaking part of this change:
JSON.stringify(e.detail)now throws (circular DOM reference), and passing the detail topostMessage/ aWorkerthrowsDataCloneError. Copy the fields you need instead:const { actionId, layerId, featureId, properties } = e.detail;Attack surface, stated rather than implied
This is a
documentevent: any script on the page can hear it, and therefore any script can now callclose()orsetBusy()on the popup. That is a real widening — and an acceptable one, because the same script can already dodocument.querySelector(".gl-poi-popup__action").click(). The confidentiality rule onpropertiesbelow is what actually guards this channel, and it is unchanged.Example: Handle an action, showing progress and closing on success. Subscribe with
GeoLeaf.Events.on("geoleaf:popup:action", (e) => onPopupAction(e.detail)).