Skip to content

v0.10.1

Choose a tag to compare

@github-actions github-actions released this 11 Sep 20:25
· 33 commits to dev since this release
9c91934

Security

  • maplibre-gl is now required at ^6.9.0, which closes CVE-2026-85061 (CVSS 9.6).
    DOM.sanitize() in every version up to and including 6.4.0 walked the live NamedNodeMap
    returned by elem.attributes while removing entries from it, so the indexes shifted and an
    adjacent dangerous attribute was skipped. Two consecutive ones let an onload or ontoggle
    survive sanitization and run when the attribution control wrote the string into innerHTML.
    Attribution text comes from the style document, so any deployment that renders a third-party
    or user-supplied style was one map load away from script execution in the viewer's session.
    Fixed upstream in 6.4.1.

Added

  • Overlapping map features now open one popup with a switcher instead of only the topmost.
    Clicking where several interactive layers overlap collects every hit into a popupStack
    on the map store (SET_POPUP_STACK, SET_POPUP_INDEX) and renders them through a single
    MapPopupStack, with a ‹ 2 of 5 · Building › header when there is more than one. Entries
    are ordered by MapLayerClickPriority, deduplicated by id and capped at 10 per click.
    Clusters contribute one entry listing their contents with a zoom-to-expand button. New
    MapPopupStack message namespace in en, es and fr.
  • PopupEntry and PopupStack in …/types/map, the descriptor a layer returns from its
    click resolver.
  • An optional header slot on Sensor, Comment and MapFeaturePopoverMenu, so the popup
    switcher renders inside the body's own card. The slot also carries the popup's single close
    button: a body that receives a header hides its own, so every map popup closes the same way.
  • A 3D-buildings toggle in the map settings panel. New show3dBuildings on the map store
    (default true) with an UPDATE_SHOW_3D_BUILDINGS action, a BuildingVisibility control
    under Map Style, and a MapCustomization.buildings3d message in en, es and fr.
    Switching it off hides the layer, which also takes it out of hit-testing.
  • TERRAIN_SPEC from …/map/utils/mapStyleSpec and SUBDIVISION_LINE_OPACITY from
    …/map/src/MapLayers/src/CountryLayer, so a consumer overriding either can reuse the same values.
  • writeModelMatrix in @collabdt/core/core/components/viewers/map/utils/modelMatrix builds the
    model-to-mercator matrix for a three.js custom layer, replacing the
    map.transform.getMatrixForModel that maplibre 6 removed. It writes into a caller-owned
    Matrix4 so a render loop allocates nothing.

Changed

  • resolveBounds (…/viewers/map/utils/validateBounds) now returns flat bounds
    [minLng, minLat, maxLng, maxLat] | undefined instead of LngLatBoundsLike, normalising a
    nested [[minLng, minLat], [maxLng, maxLat]] input to that shape. Nested bounds and
    LngLatBounds instances are not assignable to the map's maxBounds prop under
    maplibre-gl@6. Its fallbackBounds parameter takes the same flat tuple.
  • SiteContextMenu is renamed SiteMenu, along with its file, its props type and its
    i18n namespace. It is anchored by the map popup rather than the cursor, so the
    "context menu" name was misleading; its x / y props are gone with the fixed
    positioning they drove. See Migration.
  • MapClickManager.register now takes a PopupResolver that returns PopupEntry[], not a
    side-effecting ClickCallback.
    Clicks collect entries from every layer that was hit rather
    than stopping at the highest-priority one, so MapLayerClickPriority orders the stack instead
    of deciding which single layer wins. registerLegacy keeps the old callback contract for
    handlers that own their own UI, and MapLayerClickPriority.ActiveTool is still exclusive:
    a click while a tool is active opens nothing. See Migration.
  • maplibre 6 is ESM-only and no longer inlines its tile-parsing worker the way 5 did; the
    worker now ships as a separate maplibre-gl-worker.mjs chunk that has to be resolvable at
    runtime. See Migration.
  • maplibre-gl moves from ^5.0.0 to ^6.9.0 in peerDependencies. A consumer has to
    upgrade in lockstep; the new range cannot be satisfied by any release still carrying the
    advisory above.
  • react-map-gl moves from ^8.0.0 to ^8.1.3 in peerDependencies. maplibre 6 removed
    the internal map.transform property — Map now composes a Camera instead of extending it —
    and react-map-gl read it on every camera event. Versions below 8.1.3 therefore throw
    Cannot read properties of undefined (reading 'center') on any pan, zoom or flyTo; 8.1.3
    reads the public getters instead.
  • The country-subdivision border fades out above zoom 9 and stops drawing at 10. The
    subdivision fill is unchanged and still hit-tests at every zoom, so camera tracking and hover
    keep resolving countrySubdivision with no border on screen.
  • Map-level pointer listeners use mouseout. In maplibre 6 mouseenter and mouseleave are
    layer-scoped events and require a layer id, so they never fired when bound to the map itself.

Fixed

  • Streets no longer draws buildings twice. Basemaps such as maptiler streets-v2 extrude
    the building source-layer themselves, so the basemap's own extrusion is dropped before
    maptiler-3d-buildings is added and only ours renders.
  • Terrain is removed while the globe projection is active. The globe has no fog matrix, so
    maplibre logged calculateFogMatrix is not supported on globe projection and drew a terrain
    pass that produced nothing. Terrain is restored when the map returns to mercator.
  • MapHoverManager.destroy() removed a mouseenter listener it had never registered, leaving
    its mousemove handler bound to the map for the life of the page.

Removed

  • The global-borders-region layer. Its admin_level 3–4 lines drew a second province border
    on top of the subdivision outline; the subdivision layer is the org-coloured, hover-aware one,
    and global-borders-country still draws country outlines.

Migration

  • A caller of resolveBounds that relied on its nested input coming back unchanged now receives
    the flattened equivalent, and a fallbackBounds passed as a nested array or a LngLatBounds
    must be written flat.
  • Rename any import of SiteContextMenu to SiteMenu (the file moves from
    SiteContextMenu.tsx to SiteMenu.tsx, and SiteContextMenuProps becomes SiteMenuProps).
    Drop the x / y props. A message catalog that overrides the SiteContextMenu namespace
    must rename that key to SiteMenu, or its overrides fall back to the bundled strings.
  • A custom map layer that called mapClickManager.register must return PopupEntry[]
    instead of opening its own <Popup>.
    Build the descriptor in the layer so render keeps
    that layer's state and hooks:
    mapClickManager.register(LAYER_ID, MapLayerClickPriority.BuildingLayersClickPriority,
      (e, hits) => hits.map(feature => ({
        id: `${LAYER_ID}:${feature.id}`,
        layerId: LAYER_ID,
        priority: MapLayerClickPriority.BuildingLayersClickPriority,
        title: feature.properties.name ?? 'Feature',
        coordinates: [e.lngLat.lng, e.lngLat.lat],
        render: header => <MyPopupBody header={header} feature={feature} />,
      })))
    render receives the stack's switcher bar and must place it inside its own card, so
    per-card chrome (the sensor ring, a coloured border) wraps the bar too. Sensor, Comment
    and MapFeaturePopoverMenu take it as a new optional header prop.
    id is the dedup key, so a layer drawn as several maplibre layers (fill + outline) must
    produce the same id for the same feature. A body that reads live data should render a
    component rather than capture values in the closure, which is built once per click.
    Registrations that open their own UI and consume the click — an active tool, for example —
    move to registerLegacy with no other change.
  • Upgrade maplibre-gl to ^6.9.0 and regenerate your lockfile in the same commit. Bumping
    package.json alone leaves the vulnerable version resolved and the advisory open.
  • Upgrade react-map-gl to ^8.1.3 at the same time. The two are not independent: maplibre 6
    with react-map-gl 8.1.1 or earlier crashes on the first camera movement.
  • Call setWorkerUrl() once, at application startup. maplibre 6 loads tile parsing from a
    separate worker chunk, and under a bundler import.meta.url does not resolve into the module
    graph, so its own defaultWorkerUrl() returns an empty string and no tiles load. The URL is
    bundler-specific, so this package does not guess it. On Next.js, copy both
    maplibre-gl-worker.mjs and its sibling maplibre-gl-shared.mjs out of maplibre-gl/dist
    into public/maplibre/ from a pre-build script, then point at the served path:
    import { setWorkerUrl } from 'maplibre-gl'
    setWorkerUrl('/maplibre/maplibre-gl-worker.mjs')
    The worker imports that sibling on its first line, and Turbopack does not emit it beside a
    hashed worker asset, so the new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url) form that works under plain webpack fails there. Vite instead wants
    import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url'.
  • maplibre 6 drops its default export. import maplibregl from 'maplibre-gl' becomes
    import * as maplibregl from 'maplibre-gl', and the same for import type.
  • map.transform.getMatrixForModel is gone. A custom layer that builds its own model matrix
    should call writeModelMatrix(out, lngLat, altitude) instead.
  • A map-level map.on('mouseleave', fn) or map.on('mouseenter', fn) must become
    map.on('mouseout', fn) / map.on('mousemove', fn), or pass a layer id to keep the old event.