feat: add leafletObject escape hatch + move event wiring

Every component now exposes a uniform get leafletObject() returning
the underlying Leaflet instance (undefined before connected).

Event wiring:
- Marker: listen for 'dragend move' via onChange, syncing lat/lng
  back to attributes. Converted from arrow property to regular
  method using Leaflet's 3rd context arg pattern.
- Circle, CircleMarker: listen for 'move' via onMove with syncing
  guard (same pattern).
- Popup, Tooltip: listen for 'move' via onChange with syncing
  guard and null-check on getLatLng().

Also:
- Update README with escape hatch section
- Change map getter from get map() to get leafletObject() for
  consistency
- index.html: add importmap for leaflet
- All component exports changed from default to named
- index.ts re-exports updated accordingly
main
Buddy 3 months ago
parent 11cae8c89f
commit c283632262

@ -161,13 +161,27 @@ Boolean options that default to `false` are enabled by adding the attribute:
```js
const el = document.querySelector('leaflet-map');
el.map; // L.Map instance (undefined before connected)
el.zoom; // current zoom (reads live from map, falls back to attribute)
el.leafletObject; // L.Map instance (undefined before connected)
el.zoom; // current zoom (reads live from map, falls back to attribute)
el.scrollWheelZoom = false; // disable at runtime
```
All `leaflet-map` properties are two-way: reading returns the live map value; writing updates both the attribute and the map.
## Escape hatch
Every component exposes a `leafletObject` getter that returns the underlying Leaflet instance. This is useful when you need to call Leaflet API methods directly:
```js
const marker = document.querySelector('leaflet-marker');
marker.leafletObject?.setLatLng([51.5, -0.09]);
const map = document.querySelector('leaflet-map');
map.leafletObject?.flyTo([48.86, 2.35], 13);
```
Returns `undefined` while the element isn't connected to the DOM.
### CSS
Leaflet's stylesheet is loaded from a CDN via a `<link>` in the shadow DOM. Marker icon paths are derived from the CSS URL automatically.

@ -320,6 +320,13 @@
<leaflet-control-scale position="bottomleft"></leaflet-control-scale>
</leaflet-map>
<script type="importmap">
{
"imports": {
"leaflet": "/node_modules/leaflet/dist/leaflet-src.esm.js"
}
}
</script>
<script type="module" src="/dist/index.js"></script>
<script type="module">
// ── Component metadata ──────────────────────────────────────────────────

@ -21,25 +21,32 @@ const PROPS = defineProps({
fillOpacity: num(0.2),
});
export default class LeafletCircleMarker extends WithProps(HTMLElement, PROPS) {
export class LeafletCircleMarker extends WithProps(HTMLElement, PROPS) {
#obj?: CircleMarker;
#syncing = false;
connectedCallback() {
this.#obj = new CircleMarker(
[numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')],
buildOptions(this, PROPS, ['lat', 'lng']),
);
this.#obj.on('move', this.#onMove, this);
registerChildren(this, this.#obj);
}
disconnectedCallback() {
this.#obj?.off('move', this.#onMove, this);
unregisterChildren(this);
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (!this.#obj || this.#syncing) return;
if (name === 'lat' || name === 'lng') {
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
} else if (name === 'radius') {
@ -48,6 +55,15 @@ export default class LeafletCircleMarker extends WithProps(HTMLElement, PROPS) {
updatePathStyle(this.#obj, name, val);
}
}
#onMove() {
if (!this.#obj || this.#syncing) return;
this.#syncing = true;
const pos = this.#obj.getLatLng();
this.setAttribute('lat', `${pos.lat}`);
this.setAttribute('lng', `${pos.lng}`);
this.#syncing = false;
}
}
customElements.define('leaflet-circle-marker', LeafletCircleMarker);

@ -21,25 +21,32 @@ const PROPS = defineProps({
fillOpacity: num(0.2),
});
export default class LeafletCircle extends WithProps(HTMLElement, PROPS) {
export class LeafletCircle extends WithProps(HTMLElement, PROPS) {
#obj?: Circle;
#syncing = false;
connectedCallback() {
this.#obj = new Circle(
[numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')],
buildOptions(this, PROPS, ['lat', 'lng']),
);
this.#obj.on('move', this.#onMove, this);
registerChildren(this, this.#obj);
}
disconnectedCallback() {
this.#obj?.off('move', this.#onMove, this);
unregisterChildren(this);
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (!this.#obj || this.#syncing) return;
if (name === 'lat' || name === 'lng') {
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
} else if (name === 'radius') {
@ -48,6 +55,15 @@ export default class LeafletCircle extends WithProps(HTMLElement, PROPS) {
updatePathStyle(this.#obj, name, val);
}
}
#onMove() {
if (!this.#obj || this.#syncing) return;
this.#syncing = true;
const pos = this.#obj.getLatLng();
this.setAttribute('lat', `${pos.lat}`);
this.setAttribute('lng', `${pos.lng}`);
this.#syncing = false;
}
}
customElements.define('leaflet-circle', LeafletCircle);

@ -7,7 +7,7 @@ const PROPS = defineProps({
prefix: str(),
});
export default class LeafletControlAttribution extends WithProps(HTMLElement, PROPS) {
export class LeafletControlAttribution extends WithProps(HTMLElement, PROPS) {
#obj?: Control.Attribution;
connectedCallback() {
@ -22,6 +22,10 @@ export default class LeafletControlAttribution extends WithProps(HTMLElement, PR
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
}
customElements.define('leaflet-control-attribution', LeafletControlAttribution);

@ -10,7 +10,7 @@ const PROPS = defineProps({
updateWhenIdle: on(),
});
export default class LeafletControlScale extends WithProps(HTMLElement, PROPS) {
export class LeafletControlScale extends WithProps(HTMLElement, PROPS) {
#obj?: Control.Scale;
connectedCallback() {
@ -29,6 +29,10 @@ export default class LeafletControlScale extends WithProps(HTMLElement, PROPS) {
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
}
customElements.define('leaflet-control-scale', LeafletControlScale);

@ -10,7 +10,7 @@ const PROPS = defineProps({
zoomOutTitle: str('Zoom out'),
});
export default class LeafletControlZoom extends WithProps(HTMLElement, PROPS) {
export class LeafletControlZoom extends WithProps(HTMLElement, PROPS) {
#obj?: Control.Zoom;
connectedCallback() {
@ -28,6 +28,10 @@ export default class LeafletControlZoom extends WithProps(HTMLElement, PROPS) {
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
}
customElements.define('leaflet-control-zoom', LeafletControlZoom);

@ -2,7 +2,7 @@ import { FeatureGroup, Layer } from 'leaflet';
import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts';
import { buildOptions } from '../core/utils.ts';
export default class LeafletFeatureGroup extends HTMLElement {
export class LeafletFeatureGroup extends HTMLElement {
#obj?: FeatureGroup;
static get observedAttributes() {
@ -24,6 +24,10 @@ export default class LeafletFeatureGroup extends HTMLElement {
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
}
customElements.define('leaflet-feature-group', LeafletFeatureGroup);

@ -23,7 +23,7 @@ const PROP_BY_ATTR = new Map<string, string>(
Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]),
);
export default class LeafletGeoJSON extends WithProps(HTMLElement, PROPS) {
export class LeafletGeoJSON extends WithProps(HTMLElement, PROPS) {
#obj?: GeoJSON;
connectedCallback() {
@ -47,6 +47,10 @@ export default class LeafletGeoJSON extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'data') {

@ -24,7 +24,7 @@ const PROPS = defineProps({
const PROP_BY_ATTR = buildAttrMap(PROPS);
export default class LeafletImageOverlay extends WithProps(HTMLElement, PROPS) {
export class LeafletImageOverlay extends WithProps(HTMLElement, PROPS) {
#obj?: ImageOverlay;
connectedCallback() {
@ -43,6 +43,10 @@ export default class LeafletImageOverlay extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'url') {

@ -2,7 +2,7 @@ import { LayerGroup, Layer } from 'leaflet';
import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts';
import { buildOptions } from '../core/utils.ts';
export default class LeafletLayerGroup extends HTMLElement {
export class LeafletLayerGroup extends HTMLElement {
#obj?: LayerGroup;
static get observedAttributes() {
@ -24,6 +24,10 @@ export default class LeafletLayerGroup extends HTMLElement {
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
}
customElements.define('leaflet-layer-group', LeafletLayerGroup);

@ -1,4 +1,4 @@
export default class LeafletLine extends HTMLElement {
export class LeafletLine extends HTMLElement {
static get observedAttributes() {
return ['lat', 'lng'];
}

@ -93,7 +93,7 @@ type PropTypes = {
const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export default class LeafletMap extends TypedBase {
export class LeafletMap extends TypedBase {
#map?: LMap;
#container!: HTMLDivElement;
#cssLink?: HTMLLinkElement;
@ -309,7 +309,7 @@ export default class LeafletMap extends TypedBase {
}
};
get map(): LMap | undefined {
get leafletObject() {
return this.#map;
}
}

@ -15,7 +15,7 @@ const PROPS = defineProps({
const PROP_BY_ATTR = buildAttrMap(PROPS);
export default class LeafletMarker extends WithProps(HTMLElement, PROPS) {
export class LeafletMarker extends WithProps(HTMLElement, PROPS) {
#obj?: Marker;
#syncing = false;
@ -24,17 +24,21 @@ export default class LeafletMarker extends WithProps(HTMLElement, PROPS) {
[numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')],
buildOptions(this, PROPS, ['lat', 'lng']),
);
this.#obj.on('dragend', this.#onDragEnd);
this.#obj.on('dragend move', this.#onChange, this);
registerChildren(this, this.#obj);
}
disconnectedCallback() {
this.#obj?.off('dragend', this.#onDragEnd);
this.#obj?.off('dragend move', this.#onChange, this);
unregisterChildren(this);
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj || this.#syncing) return;
if (name === 'lat' || name === 'lng') {
@ -53,14 +57,14 @@ export default class LeafletMarker extends WithProps(HTMLElement, PROPS) {
}
}
#onDragEnd = () => {
#onChange() {
if (!this.#obj || this.#syncing) return;
this.#syncing = true;
const pos = this.#obj.getLatLng();
this.setAttribute('lat', `${pos.lat}`);
this.setAttribute('lng', `${pos.lng}`);
this.#syncing = false;
};
}
}
customElements.define('leaflet-marker', LeafletMarker);

@ -2,7 +2,7 @@ import { Polygon } from 'leaflet';
import { registerChildren, unregisterChildren } from '../core/register.ts';
import { defineProps, num, str, on } from '../core/props.ts';
import { WithProps, buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts';
import type LeafletLine from './leaflet-line.ts';
import type { LeafletLine } from './leaflet-line.ts';
const PROPS = defineProps({
color: str('#3388ff'),
@ -13,7 +13,7 @@ const PROPS = defineProps({
fillOpacity: num(0.2),
});
export default class LeafletPolygon extends WithProps(HTMLElement, PROPS) {
export class LeafletPolygon extends WithProps(HTMLElement, PROPS) {
#obj?: Polygon;
#observer?: MutationObserver;
@ -35,6 +35,10 @@ export default class LeafletPolygon extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (isPathStyleAttr(name)) {

@ -2,7 +2,7 @@ import { Polyline } from 'leaflet';
import { defineProps, num, str, on } from '../core/props.ts';
import { registerChildren, unregisterChildren } from '../core/register.ts';
import { WithProps, buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts';
import type LeafletLine from './leaflet-line.ts';
import type { LeafletLine } from './leaflet-line.ts';
const PROPS = defineProps({
color: str('#3388ff'),
@ -13,7 +13,7 @@ const PROPS = defineProps({
fillOpacity: num(0.2),
});
export default class LeafletPolyline extends WithProps(HTMLElement, PROPS) {
export class LeafletPolyline extends WithProps(HTMLElement, PROPS) {
#obj?: Polyline;
#observer?: MutationObserver;
@ -35,6 +35,10 @@ export default class LeafletPolyline extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (isPathStyleAttr(name)) {

@ -13,9 +13,10 @@ const PROPS = defineProps({
autoClose: on(),
});
export default class LeafletPopup extends WithProps(HTMLElement, PROPS) {
export class LeafletPopup extends WithProps(HTMLElement, PROPS) {
#obj?: Popup;
#observer?: MutationObserver;
#syncing = false;
connectedCallback() {
this.#obj = new Popup({
@ -25,6 +26,7 @@ export default class LeafletPopup extends WithProps(HTMLElement, PROPS) {
if (this.hasAttribute('lat') && this.hasAttribute('lng')) {
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
}
this.#obj.on('move', this.#onChange, this);
registerWithParent(this, this.#obj);
this.#observer = new MutationObserver(() => {
@ -38,18 +40,33 @@ export default class LeafletPopup extends WithProps(HTMLElement, PROPS) {
}
disconnectedCallback() {
this.#obj?.off('move', this.#onChange, this);
this.#observer?.disconnect();
this.#observer = undefined;
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string) {
if (!this.#obj) return;
if (!this.#obj || this.#syncing) return;
if (name === 'lat' || name === 'lng') {
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
}
}
#onChange() {
if (!this.#obj || this.#syncing) return;
const pos = this.#obj.getLatLng();
if (!pos) return;
this.#syncing = true;
this.setAttribute('lat', `${pos.lat}`);
this.setAttribute('lng', `${pos.lng}`);
this.#syncing = false;
}
}
customElements.define('leaflet-popup', LeafletPopup);

@ -19,7 +19,7 @@ const PROPS = defineProps({
fillOpacity: num(0.2),
});
export default class LeafletRectangle extends WithProps(HTMLElement, PROPS) {
export class LeafletRectangle extends WithProps(HTMLElement, PROPS) {
#obj?: Rectangle;
connectedCallback() {
@ -33,6 +33,10 @@ export default class LeafletRectangle extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'bounds') {

@ -21,7 +21,7 @@ const PROPS = defineProps({
const PROP_BY_ATTR = buildAttrMap(PROPS);
export default class LeafletSVGOverlay extends WithProps(HTMLElement, PROPS) {
export class LeafletSVGOverlay extends WithProps(HTMLElement, PROPS) {
#obj?: SVGOverlay;
connectedCallback() {
@ -41,6 +41,10 @@ export default class LeafletSVGOverlay extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'bounds') {

@ -21,7 +21,7 @@ const PROPS = defineProps({
const PROP_BY_ATTR = buildAttrMap(PROPS);
export default class LeafletTileLayerWMS extends WithProps(HTMLElement, PROPS) {
export class LeafletTileLayerWMS extends WithProps(HTMLElement, PROPS) {
#obj?: TileLayer.WMS;
connectedCallback() {
@ -36,6 +36,10 @@ export default class LeafletTileLayerWMS extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'url') {

@ -14,7 +14,7 @@ const PROPS = defineProps({
const PROP_BY_ATTR = buildAttrMap(PROPS);
export default class LeafletTileLayer extends WithProps(HTMLElement, PROPS) {
export class LeafletTileLayer extends WithProps(HTMLElement, PROPS) {
#obj?: TileLayer;
connectedCallback() {
@ -29,6 +29,10 @@ export default class LeafletTileLayer extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'url') {

@ -14,9 +14,10 @@ const PROPS = defineProps({
opacity: num(1.0),
});
export default class LeafletTooltip extends WithProps(HTMLElement, PROPS) {
export class LeafletTooltip extends WithProps(HTMLElement, PROPS) {
#obj?: Tooltip;
#observer?: MutationObserver;
#syncing = false;
connectedCallback() {
this.#obj = new Tooltip({
@ -26,6 +27,7 @@ export default class LeafletTooltip extends WithProps(HTMLElement, PROPS) {
if (this.hasAttribute('lat') && this.hasAttribute('lng')) {
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
}
this.#obj.on('move', this.#onChange, this);
registerWithParent(this, this.#obj);
this.#observer = new MutationObserver(() => {
@ -39,18 +41,33 @@ export default class LeafletTooltip extends WithProps(HTMLElement, PROPS) {
}
disconnectedCallback() {
this.#obj?.off('move', this.#onChange, this);
this.#observer?.disconnect();
this.#observer = undefined;
this.#obj?.remove();
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string) {
if (!this.#obj) return;
if (!this.#obj || this.#syncing) return;
if (name === 'lat' || name === 'lng') {
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
}
}
#onChange() {
if (!this.#obj || this.#syncing) return;
const pos = this.#obj.getLatLng();
if (!pos) return;
this.#syncing = true;
this.setAttribute('lat', `${pos.lat}`);
this.setAttribute('lng', `${pos.lng}`);
this.#syncing = false;
}
}
customElements.define('leaflet-tooltip', LeafletTooltip);

@ -25,7 +25,7 @@ const PROPS = defineProps({
const PROP_BY_ATTR = buildAttrMap(PROPS);
export default class LeafletVideoOverlay extends WithProps(HTMLElement, PROPS) {
export class LeafletVideoOverlay extends WithProps(HTMLElement, PROPS) {
#obj?: VideoOverlay;
connectedCallback() {
@ -44,6 +44,10 @@ export default class LeafletVideoOverlay extends WithProps(HTMLElement, PROPS) {
this.#obj = undefined;
}
get leafletObject() {
return this.#obj;
}
attributeChangedCallback(name: string, _old: string | null, val: string | null) {
if (!this.#obj) return;
if (name === 'url') {

@ -1,24 +1,24 @@
export * from './core/utils.ts';
export * from './core/register.ts';
export * from './core/props.ts';
export { default as LeafletMap } from './components/leaflet-map.ts';
export { default as LeafletMarker } from './components/leaflet-marker.ts';
export { default as LeafletCircle } from './components/leaflet-circle.ts';
export { default as LeafletCircleMarker } from './components/leaflet-circle-marker.ts';
export { default as LeafletLine } from './components/leaflet-line.ts';
export { default as LeafletPolygon } from './components/leaflet-polygon.ts';
export { default as LeafletPolyline } from './components/leaflet-polyline.ts';
export { default as LeafletRectangle } from './components/leaflet-rectangle.ts';
export { default as LeafletTileLayer } from './components/leaflet-tile-layer.ts';
export { default as LeafletTileLayerWMS } from './components/leaflet-tile-layer-wms.ts';
export { default as LeafletImageOverlay } from './components/leaflet-image-overlay.ts';
export { default as LeafletVideoOverlay } from './components/leaflet-video-overlay.ts';
export { default as LeafletSVGOverlay } from './components/leaflet-svg-overlay.ts';
export { default as LeafletLayerGroup } from './components/leaflet-layer-group.ts';
export { default as LeafletFeatureGroup } from './components/leaflet-feature-group.ts';
export { default as LeafletGeoJSON } from './components/leaflet-geojson.ts';
export { default as LeafletControlZoom } from './components/leaflet-control-zoom.ts';
export { default as LeafletControlAttribution } from './components/leaflet-control-attribution.ts';
export { default as LeafletControlScale } from './components/leaflet-control-scale.ts';
export { default as LeafletPopup } from './components/leaflet-popup.ts';
export { default as LeafletTooltip } from './components/leaflet-tooltip.ts';
export { LeafletMap } from './components/leaflet-map.ts';
export { LeafletMarker } from './components/leaflet-marker.ts';
export { LeafletCircle } from './components/leaflet-circle.ts';
export { LeafletCircleMarker } from './components/leaflet-circle-marker.ts';
export { LeafletLine } from './components/leaflet-line.ts';
export { LeafletPolygon } from './components/leaflet-polygon.ts';
export { LeafletPolyline } from './components/leaflet-polyline.ts';
export { LeafletRectangle } from './components/leaflet-rectangle.ts';
export { LeafletTileLayer } from './components/leaflet-tile-layer.ts';
export { LeafletTileLayerWMS } from './components/leaflet-tile-layer-wms.ts';
export { LeafletImageOverlay } from './components/leaflet-image-overlay.ts';
export { LeafletVideoOverlay } from './components/leaflet-video-overlay.ts';
export { LeafletSVGOverlay } from './components/leaflet-svg-overlay.ts';
export { LeafletLayerGroup } from './components/leaflet-layer-group.ts';
export { LeafletFeatureGroup } from './components/leaflet-feature-group.ts';
export { LeafletGeoJSON } from './components/leaflet-geojson.ts';
export { LeafletControlZoom } from './components/leaflet-control-zoom.ts';
export { LeafletControlAttribution } from './components/leaflet-control-attribution.ts';
export { LeafletControlScale } from './components/leaflet-control-scale.ts';
export { LeafletPopup } from './components/leaflet-popup.ts';
export { LeafletTooltip } from './components/leaflet-tooltip.ts';

Loading…
Cancel
Save