diff --git a/demos/custom-icon.html b/demos/custom-icon.html new file mode 100644 index 0000000..076e88e --- /dev/null +++ b/demos/custom-icon.html @@ -0,0 +1,317 @@ + + + + + + Leaflet Components Demo + + + +

Leaflet Web Components

+ + + + + + + + I am a green leaf. + + + + + I am a red leaf. + + + + + I am a orange leaf. + + + + + + + + + diff --git a/src/components/leaflet-icon.ts b/src/components/leaflet-icon.ts new file mode 100644 index 0000000..42023b0 --- /dev/null +++ b/src/components/leaflet-icon.ts @@ -0,0 +1,87 @@ +import { Icon, icon, divIcon, IconOptions, DivIconOptions } from 'leaflet'; +import { defineProps, str, on } from '../core/props.ts'; +import { WithProps } from '../core/utils.ts'; + +const PROPS = defineProps({ + iconUrl: str(), + iconRetinaUrl: str(), + iconSize: str(), + iconAnchor: str(), + popupAnchor: str(), + tooltipAnchor: str(), + shadowUrl: str(), + shadowRetinaUrl: str(), + shadowSize: str(), + shadowAnchor: str(), + className: str(), + div: on(), + html: str(), + bgPos: str(), +}); + +const JSON_KEYS = new Set([ + 'iconSize', + 'iconAnchor', + 'popupAnchor', + 'tooltipAnchor', + 'shadowSize', + 'shadowAnchor', + 'bgPos', +]); + +export class LeafletIcon extends WithProps(HTMLElement, PROPS) { + #obj?: Icon; + + connectedCallback() { + this.#applyIcon(); + this.dispatchEvent( + new CustomEvent('icon-changed', { + bubbles: true, + detail: { icon: this.#obj }, + }), + ); + } + + disconnectedCallback() { + this.dispatchEvent( + new CustomEvent('icon-changed', { + bubbles: true, + detail: { icon: null }, + }), + ); + } + + attributeChangedCallback() { + this.#applyIcon(); + if (!this.#obj) return; + this.dispatchEvent( + new CustomEvent('icon-changed', { + bubbles: true, + detail: { icon: this.#obj }, + }), + ); + } + + get leafletObject() { + return this.#obj; + } + + #applyIcon() { + const opts: Record = {}; + for (const [key, spec] of Object.entries(PROPS)) { + if (key === 'div') continue; + const v = this.getAttribute(spec.attr); + if (v === null) continue; + opts[key] = JSON_KEYS.has(key) ? JSON.parse(v) : v; + } + if (this.hasAttribute('div')) { + this.#obj = divIcon(opts as unknown as DivIconOptions) as unknown as Icon; + } else if (opts.iconUrl) { + this.#obj = icon(opts as unknown as IconOptions) as unknown as Icon; + } else { + this.#obj = undefined; + } + } +} + +customElements.define('leaflet-icon', LeafletIcon); diff --git a/src/components/leaflet-marker.ts b/src/components/leaflet-marker.ts index af19b4c..3454555 100644 --- a/src/components/leaflet-marker.ts +++ b/src/components/leaflet-marker.ts @@ -1,4 +1,4 @@ -import { Marker } from 'leaflet'; +import { Icon, Marker } from 'leaflet'; import { defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { WithProps, buildAttrMap, buildOptions, numAttr, setLayerAttr } from '../core/utils.ts'; @@ -26,10 +26,16 @@ export class LeafletMarker extends WithProps(HTMLElement, PROPS) { ); this.#obj.on('dragend move', this.#onChange, this); registerChildren(this, this.#obj); + this.addEventListener('icon-changed', this.#onIconChanged); + const iconEl = this.querySelector(':scope > leaflet-icon') as unknown as { + leafletObject: Icon; + } | null; + if (iconEl?.leafletObject) this.#obj.setIcon(iconEl.leafletObject); } disconnectedCallback() { this.#obj?.off('dragend move', this.#onChange, this); + this.removeEventListener('icon-changed', this.#onIconChanged); unregisterChildren(this); this.#obj?.remove(); this.#obj = undefined; @@ -65,6 +71,13 @@ export class LeafletMarker extends WithProps(HTMLElement, PROPS) { this.setAttribute('lng', `${pos.lng}`); this.#syncing = false; } + + #onIconChanged(e: Event) { + if (!this.#obj) return; + const detail = (e as CustomEvent).detail; + if (detail?.icon) this.#obj.setIcon(detail.icon); + else this.#obj.setIcon(new Icon.Default()); + } } customElements.define('leaflet-marker', LeafletMarker); diff --git a/src/index.ts b/src/index.ts index 6e48ada..4dab2d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,3 +22,4 @@ export { LeafletControlAttribution } from './components/leaflet-control-attribut export { LeafletControlScale } from './components/leaflet-control-scale.ts'; export { LeafletPopup } from './components/leaflet-popup.ts'; export { LeafletTooltip } from './components/leaflet-tooltip.ts'; +export { LeafletIcon } from './components/leaflet-icon.ts';