feat: split leaflet-icon into image Icon and DivIcon, event-only marker↔icon handshake
- Strip leaflet-icon.ts to image-only (new Icon(), remove div/html/bgPos) - Create leaflet-div-icon.ts with DivIcon, innerHTML content, MutationObserver - Convert marker↔icon from querySelector to event-only protocol: marker iterates children dispatching leaflet-request-icon on each, icons listen on themselves and respond with icon-changed - Extract emitIconChanged() helper into register.ts to eliminate repeated CustomEvent construction across both icon components - Type icon-changed and leaflet-request-icon in HTMLElementEventMap - Update marker's #onIconChanged to use typed event instead of cast - Export LeafletDivIcon from index.ts - Add DivIcon demo markers to custom-icon.html and index.htmlmain
parent
0855851504
commit
35190bacc5
@ -0,0 +1,74 @@
|
||||
import { DivIcon, DivIconOptions, Icon } from 'leaflet';
|
||||
import { emitIconChanged } from '../core/register.ts';
|
||||
|
||||
const PROPS = {
|
||||
iconSize: { attr: 'icon-size' },
|
||||
iconAnchor: { attr: 'icon-anchor' },
|
||||
popupAnchor: { attr: 'popup-anchor' },
|
||||
tooltipAnchor: { attr: 'tooltip-anchor' },
|
||||
className: { attr: 'class-name' },
|
||||
html: { attr: 'html' },
|
||||
bgPos: { attr: 'bg-pos' },
|
||||
} as const;
|
||||
|
||||
const JSON_KEYS = new Set(['iconSize', 'iconAnchor', 'popupAnchor', 'tooltipAnchor', 'bgPos']);
|
||||
|
||||
export class LeafletDivIcon extends HTMLElement {
|
||||
#obj?: Icon;
|
||||
#observer?: MutationObserver;
|
||||
#onRequest = () => {
|
||||
if (!this.#obj) return;
|
||||
emitIconChanged(this, this.#obj);
|
||||
};
|
||||
|
||||
connectedCallback() {
|
||||
this.#applyIcon();
|
||||
emitIconChanged(this, this.#obj);
|
||||
this.addEventListener('leaflet-request-icon', this.#onRequest);
|
||||
|
||||
this.#observer = new MutationObserver(() => {
|
||||
this.#applyIcon();
|
||||
emitIconChanged(this, this.#obj);
|
||||
});
|
||||
this.#observer.observe(this, {
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener('leaflet-request-icon', this.#onRequest);
|
||||
this.#observer?.disconnect();
|
||||
this.#observer = undefined;
|
||||
emitIconChanged(this, null);
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return Object.values(PROPS).map((s) => s.attr);
|
||||
}
|
||||
|
||||
attributeChangedCallback() {
|
||||
this.#applyIcon();
|
||||
if (!this.#obj) return;
|
||||
emitIconChanged(this, this.#obj);
|
||||
}
|
||||
|
||||
get leafletObject() {
|
||||
return this.#obj;
|
||||
}
|
||||
|
||||
#applyIcon() {
|
||||
const opts: Record<string, unknown> = {};
|
||||
for (const [key, spec] of Object.entries(PROPS)) {
|
||||
const v = this.getAttribute(spec.attr);
|
||||
if (v === null) continue;
|
||||
opts[key] = JSON_KEYS.has(key) ? JSON.parse(v) : v;
|
||||
}
|
||||
const content = this.innerHTML;
|
||||
if (content) opts.html = content;
|
||||
this.#obj = new DivIcon(opts as DivIconOptions) as unknown as Icon;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('leaflet-div-icon', LeafletDivIcon);
|
||||
Loading…
Reference in New Issue