Skip to main content

CnMapWidget

Leaflet-backed map primitive for declarative manifest-driven maps. Renders a configurable layer stack (tile / WMS / WFS / inline GeoJSON) plus an optional marker set sourced from inline features OR an HTTP dataSource.url. Marker clustering is opt-in (lazy-loads leaflet.markercluster only when first toggled). Spec: REQ-MMW-* (manifest-map-widget).

Try it

Loading CnMapWidget playground…

Usage

<!-- Standalone — embed a map inside a custom component -->
<CnMapWidget
:center="[52.1326, 5.2913]"
:zoom="7"
:layers="[
{ type: 'tile',
url: 'https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/standaard/EPSG:3857/{z}/{x}/{y}.png',
options: { attribution: '© Kadaster', maxZoom: 19 } },
{ type: 'wms',
url: 'https://service.pdok.nl/lv/bag/wms/v2_0',
options: { layers: 'pand', transparent: true, opacity: 0.6 } },
]"
:markers="{
dataSource: { url: '/index.php/apps/procest/api/cases/geo' },
latField: 'lat',
lngField: 'lng',
popupField: 'title',
clustering: true,
}"
height="500px"
@marker-click="onMarkerClick" />

The layers[] array dispatches by type:

layer.typeLeaflet factory
tileL.tileLayer(url, options)
wmsL.tileLayer.wms(url, options)
wfsfetch(url) → L.geoJSON(features, options)
geojsoninline dataL.geoJSON; OR fetched URL

Unknown types log a warning and are skipped — manifests stay forward-compatible.

Markers

Pick one of:

  • markers.features — inline GeoJSON Feature[] (static maps).
  • markers.dataSource.url — fetched on mount; the response may be a GeoJSON FeatureCollection OR a flat array of rows (in which case latField, lngField, popupField drive the conversion).
  • markers.dataSource.{register, schema} — RESERVED. Round-tripped by validators today, resolver lands in a follow-up change.

markers.clustering: true (or the widget-level clustering prop) opts in to leaflet.markercluster. The cluster bundle lazy-loads only when first enabled — consumers without clustering pay zero extra bytes.

Manifest mounting

Don't mount this component directly from a manifest. Use CnMapPage (the type: "map" page renderer) which wraps this primitive with a header, filter slot, and event pass-through.

Fallback

When Leaflet cannot be loaded (test envs, CSP-blocked CDNs), the #fallback slot renders instead. Default text uses unavailableLabel.

<CnMapWidget :center="[52, 5]">
<template #fallback>
<p>Map view unavailable.</p>
</template>
</CnMapWidget>

Reference

Props

NameTypeRequiredDefaultDescription
center[number, number]Initial map center as [latitude, longitude].
zoomnumber7Initial zoom level.
layersArray<object>[]Layer definitions. Each entry: { type: 'tile'|'wms'|'wfs'|'geojson', url, options }. geojson MAY supply inline data (FeatureCollection) instead of url. Unknown types log a console.warn and are skipped.
markersunionnullMarker config. { features?, dataSource?, latField?, lngField?, popupField?, clustering?, iconColor?, iconUrl? }. features[] is inline; dataSource.url is HTTP-fetched on mount; dataSource.{register, schema} is reserved (resolver deferred).
clusteringbooleanfalseEnable marker clustering. When true, lazy-loads leaflet.markercluster on first mount. markers.clustering overrides this prop when set.
heightunion'500px'Container height. Forwarded to the wrapper div's style.height.
autoFitbooleantrueAuto-fit map bounds to all loaded features after first load.
ariaLabelstring() =&gt; t('nextcloud-vue', 'Map')Aria-label for the map application region.
unavailableLabelstring() =&gt; t('nextcloud-vue', 'Map library not available')Label shown when Leaflet is not available.

Events

NamePayloadDescription
map-readyMap ready event. Fired once after Leaflet has loaded and the map is mounted.
marker-clickMarker click event. Fired when a marker is clicked.
bounds-changeViewport bounds change event. Fired (debounced) after pan / zoom settles.
clickMap background click event. Fired when the user clicks the map outside any marker.

Slots

NameBindingsDescription
fallbackfallback
legendlayers, markerslegend