import { FolkElement } from '@lib'; import { css, unsafeCSS } from '@lit/reactive-element'; import { LatLng, LatLngExpression, LeafletEvent, map, tileLayer } from 'leaflet'; // @ts-ignore // Vite specific import :( import leafletCSS from 'leaflet/dist/leaflet.css?inline'; export class RecenterEvent extends Event { constructor() { super('recenter', { bubbles: true }); } } export class FolkMap extends FolkElement { static override tagName = 'folk-map'; static override styles = css` ${unsafeCSS(leafletCSS)} :host { display: block; } :host > div { height: 100%; width: 100%; } `; #container = document.createElement('div'); #map = map(this.#container); override createRenderRoot() { const root = super.createRenderRoot(); root.appendChild(this.#container); this.#map.addLayer( tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap', }), ); this.#map.setView( (this.getAttribute('coordinates') || '0, 0').split(',').map(Number) as LatLngExpression, Number(this.getAttribute('zoom') || 13), ); return root; } connectedCallback(): void { super.connectedCallback(); // Move end includes changes to zoom this.#map.on('moveend', this.handleEvent); } disconnectedCallback(): void { super.disconnectedCallback(); // Move end includes changes to zoom this.#map.off('moveend', this.handleEvent); } get lat() { return this.coordinates.lat; } set lat(lat) { this.coordinates = [lat, this.lng]; } get lng() { return this.coordinates.lng; } set lng(lng) { this.coordinates = [this.lat, lng]; } get coordinates(): LatLng { return this.#map.getCenter(); } set coordinates(coordinates: LatLngExpression) { this.#map.setView(coordinates); } get zoom() { return this.#map.getZoom(); } set zoom(zoom) { this.#map.setZoom(zoom); } handleEvent = (event: LeafletEvent) => { switch (event.type) { case 'moveend': { this.dispatchEvent(new RecenterEvent()); break; } } }; }