import { GeoJSON, PathOptions, Layer } from 'leaflet'; import { defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts'; import { WithProps, buildOptions } from '../core/utils.ts'; const PROPS = defineProps({ data: str(), stroke: str(), color: str('#3388ff'), weight: num(3), opacity: num(1.0), lineCap: str('round'), lineJoin: str('round'), dashArray: str(), dashOffset: str(), fill: on(), fillColor: str('#3388ff'), fillOpacity: num(0.2), fillRule: str('evenodd'), }); const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); export class LeafletGeoJSON extends WithProps(HTMLElement, PROPS) { #obj?: GeoJSON; connectedCallback() { const raw = this.getAttribute('data'); const data = raw ? JSON.parse(raw) : undefined; const styleOpts = Object.fromEntries( Object.entries(buildOptions(this, PROPS, ['data'])).filter(([, v]) => v !== ''), ); this.#obj = new GeoJSON(data, { style: styleOpts as PathOptions }); registerChildren(this, this.#obj); } disconnectedCallback() { for (const [el, entry] of getChildren(this) ?? []) { if (entry.type === 'popup') this.#obj?.unbindPopup(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); else if (entry.type === 'layer') this.#obj?.removeLayer(el as unknown as Layer); } 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 (name === 'data') { this.#obj.clearLayers(); if (val) this.#obj.addData(JSON.parse(val)); } else { const propName = PROP_BY_ATTR.get(name); if (!propName) return; const spec = PROPS[propName as keyof typeof PROPS]; const styleVal = spec.kind === 'bool-on' ? val !== null : val; this.#obj.setStyle({ [propName]: styleVal } as PathOptions); } } } customElements.define('leaflet-geojson', LeafletGeoJSON);