refactor: extract static PROP accessor blocks into definePropAccessors helper

All 17 TypedBase components now share a single definePropAccessors(proto, PROPS)
call in their static block instead of duplicating the getter/setter loop.

Also includes pre-existing cleanup:
- Refine LeafletRegisterEvent type (interface -> generic CustomEvent)
- Drop unused attrToPropName helper
- Fix createChildRegisterHandler return type in layer-group/feature-group
main
Buddy 3 months ago
parent 4d27c8eaac
commit 4e1abdcc3d

@ -1,6 +1,11 @@
import { CircleMarker } from 'leaflet'; import { CircleMarker } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
@ -22,29 +27,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletCircleMarker extends TypedBase { export class LeafletCircleMarker extends TypedBase {
#obj?: CircleMarker; #obj?: CircleMarker;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletCircleMarker.prototype, PROPS);
Object.defineProperty(LeafletCircleMarker.prototype, name, {
get(this: LeafletCircleMarker) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletCircleMarker, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -52,13 +42,14 @@ export class LeafletCircleMarker extends TypedBase {
[this.#num('lat'), this.#num('lng')], [this.#num('lat'), this.#num('lng')],
buildOptions(this, PROPS, ['lat', 'lng']), buildOptions(this, PROPS, ['lat', 'lng']),
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,6 +1,11 @@
import { Circle } from 'leaflet'; import { Circle } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js';
import {
createChildRegisterHandler,
type ChildEntry,
type LeafletRegisterEvent,
} from '../core/register.js';
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
@ -22,29 +27,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletCircle extends TypedBase { export class LeafletCircle extends TypedBase {
#obj?: Circle; #obj?: Circle;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletCircle.prototype, PROPS);
Object.defineProperty(LeafletCircle.prototype, name, {
get(this: LeafletCircle) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletCircle, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -52,13 +42,14 @@ export class LeafletCircle extends TypedBase {
[this.#num('lat'), this.#num('lng')], [this.#num('lat'), this.#num('lng')],
buildOptions(this, PROPS, ['lat', 'lng']), buildOptions(this, PROPS, ['lat', 'lng']),
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,5 +1,6 @@
import { Control, ControlPosition } from 'leaflet'; import { Control, ControlPosition } from 'leaflet';
import { registerWithParent } from '../core/utils.js'; import { registerWithParent, definePropAccessors } from '../core/utils.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -18,18 +19,7 @@ export class LeafletControlAttribution extends TypedBase {
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletControlAttribution.prototype, PROPS);
Object.defineProperty(LeafletControlAttribution.prototype, name, {
get(this: LeafletControlAttribution) {
return this.getAttribute(spec.attr) ?? spec.default;
},
set(this: LeafletControlAttribution, v: string) {
this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {

@ -1,5 +1,6 @@
import { Control, ControlPosition } from 'leaflet'; import { Control, ControlPosition } from 'leaflet';
import { registerWithParent } from '../core/utils.js'; import { registerWithParent, definePropAccessors } from '../core/utils.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -21,22 +22,7 @@ export class LeafletControlScale extends TypedBase {
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletControlScale.prototype, PROPS);
Object.defineProperty(LeafletControlScale.prototype, name, {
get(this: LeafletControlScale) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletControlScale, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {

@ -1,5 +1,6 @@
import { Control, ControlPosition } from 'leaflet'; import { Control, ControlPosition } from 'leaflet';
import { registerWithParent } from '../core/utils.js'; import { registerWithParent, definePropAccessors } from '../core/utils.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -21,19 +22,7 @@ export class LeafletControlZoom extends TypedBase {
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletControlZoom.prototype, PROPS);
Object.defineProperty(LeafletControlZoom.prototype, name, {
get(this: LeafletControlZoom) {
const val = this.getAttribute(spec.attr);
return val ?? (spec as { default: string }).default;
},
set(this: LeafletControlZoom, v: number | string) {
this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {

@ -1,11 +1,15 @@
import { FeatureGroup, Layer } from 'leaflet'; import { FeatureGroup, Layer } from 'leaflet';
import { buildOptions, registerWithParent } from '../core/utils.js'; import { buildOptions, registerWithParent } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; import {
createChildRegisterHandler,
type ChildEntry,
type LeafletRegisterEvent,
} from '../core/register.js';
export class LeafletFeatureGroup extends HTMLElement { export class LeafletFeatureGroup extends HTMLElement {
#obj?: FeatureGroup; #obj?: FeatureGroup;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: (e: Event) => void; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return []; return [];
@ -13,13 +17,14 @@ export class LeafletFeatureGroup extends HTMLElement {
connectedCallback() { connectedCallback() {
this.#obj = new FeatureGroup([], buildOptions(this, {})); this.#obj = new FeatureGroup([], buildOptions(this, {}));
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
for (const [el, entry] of this.#children) { for (const [el, entry] of this.#children) {
if (entry.type === 'popup') this.#obj?.unbindPopup(); if (entry.type === 'popup') this.#obj?.unbindPopup();
else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip();

@ -1,6 +1,11 @@
import { GeoJSON, PathOptions, Layer } from 'leaflet'; import { GeoJSON, PathOptions, Layer } from 'leaflet';
import { buildOptions, registerWithParent } from '../core/utils.js'; import { buildOptions, registerWithParent, definePropAccessors } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -29,28 +34,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletGeoJSON extends TypedBase { export class LeafletGeoJSON extends TypedBase {
#obj?: GeoJSON; #obj?: GeoJSON;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: (e: Event) => void; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletGeoJSON.prototype, PROPS);
Object.defineProperty(LeafletGeoJSON.prototype, name, {
get(this: LeafletGeoJSON) {
if (spec.kind === 'num') return Number(this.getAttribute(spec.attr) ?? spec.default);
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return this.getAttribute(spec.attr) ?? spec.default;
},
set(this: LeafletGeoJSON, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -60,13 +51,14 @@ export class LeafletGeoJSON extends TypedBase {
Object.entries(buildOptions(this, PROPS, ['data'])).filter(([, v]) => v !== ''), Object.entries(buildOptions(this, PROPS, ['data'])).filter(([, v]) => v !== ''),
); );
this.#obj = new GeoJSON(data, { style: styleOpts as PathOptions }); this.#obj = new GeoJSON(data, { style: styleOpts as PathOptions });
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
for (const [el, entry] of this.#children) { for (const [el, entry] of this.#children) {
if (entry.type === 'popup') this.#obj?.unbindPopup(); if (entry.type === 'popup') this.#obj?.unbindPopup();
else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip();

@ -1,6 +1,16 @@
import { ImageOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; import { ImageOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet';
import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; import {
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; registerWithParent,
buildOptions,
parseAttributeValue,
definePropAccessors,
} from '../core/utils.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -25,29 +35,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletImageOverlay extends TypedBase { export class LeafletImageOverlay extends TypedBase {
#obj?: ImageOverlay; #obj?: ImageOverlay;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletImageOverlay.prototype, PROPS);
Object.defineProperty(LeafletImageOverlay.prototype, name, {
get(this: LeafletImageOverlay) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletImageOverlay, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -57,13 +52,14 @@ export class LeafletImageOverlay extends TypedBase {
this.#parsedBounds(), this.#parsedBounds(),
buildOptions(this, PROPS, ['url', 'bounds']), buildOptions(this, PROPS, ['url', 'bounds']),
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,11 +1,15 @@
import { LayerGroup, Layer } from 'leaflet'; import { LayerGroup, Layer } from 'leaflet';
import { buildOptions, registerWithParent } from '../core/utils.js'; import { buildOptions, registerWithParent } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
export class LeafletLayerGroup extends HTMLElement { export class LeafletLayerGroup extends HTMLElement {
#obj?: LayerGroup; #obj?: LayerGroup;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: (e: Event) => void; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return []; return [];
@ -13,13 +17,14 @@ export class LeafletLayerGroup extends HTMLElement {
connectedCallback() { connectedCallback() {
this.#obj = new LayerGroup([], buildOptions(this, {})); this.#obj = new LayerGroup([], buildOptions(this, {}));
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
for (const [el, entry] of this.#children) { for (const [el, entry] of this.#children) {
if (entry.type === 'popup') this.#obj?.unbindPopup(); if (entry.type === 'popup') this.#obj?.unbindPopup();
else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip();

@ -1,6 +1,16 @@
import { Marker } from 'leaflet'; import { Marker } from 'leaflet';
import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; import {
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; registerWithParent,
buildOptions,
parseAttributeValue,
definePropAccessors,
} from '../core/utils.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -23,29 +33,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletMarker extends TypedBase { export class LeafletMarker extends TypedBase {
#obj?: Marker; #obj?: Marker;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletMarker.prototype, PROPS);
Object.defineProperty(LeafletMarker.prototype, name, {
get(this: LeafletMarker) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletMarker, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -53,13 +48,14 @@ export class LeafletMarker extends TypedBase {
[this.#num('lat'), this.#num('lng')], [this.#num('lat'), this.#num('lng')],
buildOptions(this, PROPS, ['lat', 'lng']), buildOptions(this, PROPS, ['lat', 'lng']),
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,6 +1,11 @@
import { Polygon } from 'leaflet'; import { Polygon } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
import type { LeafletLine } from './leaflet-line.js'; import type { LeafletLine } from './leaflet-line.js';
@ -21,35 +26,20 @@ export class LeafletPolygon extends TypedBase {
#obj?: Polygon; #obj?: Polygon;
#observer?: MutationObserver; #observer?: MutationObserver;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletPolygon.prototype, PROPS);
Object.defineProperty(LeafletPolygon.prototype, name, {
get(this: LeafletPolygon) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletPolygon, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
this.#obj = new Polygon(this.#getCoords(), buildOptions(this, PROPS)); this.#obj = new Polygon(this.#getCoords(), buildOptions(this, PROPS));
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
this.addEventListener('line-updated', this.#syncCoords); this.addEventListener('line-updated', this.#syncCoords);
@ -61,7 +51,8 @@ export class LeafletPolygon extends TypedBase {
this.#observer?.disconnect(); this.#observer?.disconnect();
this.#observer = undefined; this.#observer = undefined;
this.removeEventListener('line-updated', this.#syncCoords); this.removeEventListener('line-updated', this.#syncCoords);
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,6 +1,11 @@
import { Polyline } from 'leaflet'; import { Polyline } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
import type { LeafletLine } from './leaflet-line.js'; import type { LeafletLine } from './leaflet-line.js';
@ -21,35 +26,20 @@ export class LeafletPolyline extends TypedBase {
#obj?: Polyline; #obj?: Polyline;
#observer?: MutationObserver; #observer?: MutationObserver;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletPolyline.prototype, PROPS);
Object.defineProperty(LeafletPolyline.prototype, name, {
get(this: LeafletPolyline) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletPolyline, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
this.#obj = new Polyline(this.#getCoords(), buildOptions(this, PROPS)); this.#obj = new Polyline(this.#getCoords(), buildOptions(this, PROPS));
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
this.addEventListener('line-updated', this.#syncCoords); this.addEventListener('line-updated', this.#syncCoords);
@ -61,7 +51,8 @@ export class LeafletPolyline extends TypedBase {
this.#observer?.disconnect(); this.#observer?.disconnect();
this.#observer = undefined; this.#observer = undefined;
this.removeEventListener('line-updated', this.#syncCoords); this.removeEventListener('line-updated', this.#syncCoords);
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,5 +1,6 @@
import { Popup } from 'leaflet'; import { Popup } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -25,23 +26,7 @@ export class LeafletPopup extends TypedBase {
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletPopup.prototype, PROPS);
Object.defineProperty(LeafletPopup.prototype, name, {
get(this: LeafletPopup) {
const val = this.getAttribute(spec.attr);
if ('default' in spec && spec.kind === 'num')
return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? ('default' in spec ? (spec as { default: string }).default : '');
},
set(this: LeafletPopup, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {

@ -1,6 +1,11 @@
import { Rectangle, LatLngBoundsExpression } from 'leaflet'; import { Rectangle, LatLngBoundsExpression } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
@ -20,40 +25,26 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletRectangle extends TypedBase { export class LeafletRectangle extends TypedBase {
#obj?: Rectangle; #obj?: Rectangle;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletRectangle.prototype, PROPS);
Object.defineProperty(LeafletRectangle.prototype, name, {
get(this: LeafletRectangle) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletRectangle, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
this.#obj = new Rectangle(this.#parsedBounds(), buildOptions(this, PROPS, ['bounds'])); this.#obj = new Rectangle(this.#parsedBounds(), buildOptions(this, PROPS, ['bounds']));
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,6 +1,16 @@
import { SVGOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; import { SVGOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet';
import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; import {
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; registerWithParent,
buildOptions,
parseAttributeValue,
definePropAccessors,
} from '../core/utils.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -22,29 +32,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletSVGOverlay extends TypedBase { export class LeafletSVGOverlay extends TypedBase {
#obj?: SVGOverlay; #obj?: SVGOverlay;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletSVGOverlay.prototype, PROPS);
Object.defineProperty(LeafletSVGOverlay.prototype, name, {
get(this: LeafletSVGOverlay) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletSVGOverlay, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -55,13 +50,14 @@ export class LeafletSVGOverlay extends TypedBase {
this.#parsedBounds(), this.#parsedBounds(),
buildOptions(this, PROPS, ['bounds']), buildOptions(this, PROPS, ['bounds']),
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,6 +1,16 @@
import { TileLayer } from 'leaflet'; import { TileLayer } from 'leaflet';
import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; import {
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; registerWithParent,
buildOptions,
parseAttributeValue,
definePropAccessors,
} from '../core/utils.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -23,31 +33,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletTileLayerWMS extends TypedBase { export class LeafletTileLayerWMS extends TypedBase {
#obj?: TileLayer.WMS; #obj?: TileLayer.WMS;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletTileLayerWMS.prototype, PROPS);
Object.defineProperty(LeafletTileLayerWMS.prototype, name, {
get(this: LeafletTileLayerWMS) {
const val = this.getAttribute(spec.attr);
const s = spec as { kind: string; default: unknown };
if (s.kind === 'num') return val !== null ? Number(val) : (s.default as number);
if (s.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? (s.default as string);
},
set(this: LeafletTileLayerWMS, v: number | string | boolean) {
const s = spec as { kind: string };
if (s.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -56,13 +49,14 @@ export class LeafletTileLayerWMS extends TypedBase {
url, url,
buildOptions(this, PROPS, ['url']) as Record<string, string>, buildOptions(this, PROPS, ['url']) as Record<string, string>,
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,6 +1,16 @@
import { TileLayer } from 'leaflet'; import { TileLayer } from 'leaflet';
import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; import {
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; registerWithParent,
buildOptions,
parseAttributeValue,
definePropAccessors,
} from '../core/utils.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -22,39 +32,27 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletTileLayer extends TypedBase { export class LeafletTileLayer extends TypedBase {
#obj?: TileLayer; #obj?: TileLayer;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletTileLayer.prototype, PROPS);
Object.defineProperty(LeafletTileLayer.prototype, name, {
get(this: LeafletTileLayer) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
return val ?? spec.default;
},
set(this: LeafletTileLayer, v: number | string) {
this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
const url = this.getAttribute('url') || ''; const url = this.getAttribute('url') || '';
this.#obj = new TileLayer(url, buildOptions(this, PROPS, ['url'])); this.#obj = new TileLayer(url, buildOptions(this, PROPS, ['url']));
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,5 +1,6 @@
import { Tooltip } from 'leaflet'; import { Tooltip } from 'leaflet';
import { registerWithParent, buildOptions } from '../core/utils.js'; import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -25,22 +26,7 @@ export class LeafletTooltip extends TypedBase {
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletTooltip.prototype, PROPS);
Object.defineProperty(LeafletTooltip.prototype, name, {
get(this: LeafletTooltip) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletTooltip, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {

@ -1,6 +1,16 @@
import { VideoOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; import { VideoOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet';
import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; import {
import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; registerWithParent,
buildOptions,
parseAttributeValue,
definePropAccessors,
} from '../core/utils.js';
import {
createChildRegisterHandler,
type ChildEntry,
LeafletRegisterEvent,
} from '../core/register.js';
import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js';
const PROPS = { const PROPS = {
@ -26,29 +36,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes;
export class LeafletVideoOverlay extends TypedBase { export class LeafletVideoOverlay extends TypedBase {
#obj?: VideoOverlay; #obj?: VideoOverlay;
#children = new Map<HTMLElement, ChildEntry>(); #children = new Map<HTMLElement, ChildEntry>();
#childHandler?: EventListener; #childHandler?: (e: LeafletRegisterEvent) => void;
static get observedAttributes() { static get observedAttributes() {
return Object.values(PROPS).map((s) => s.attr); return Object.values(PROPS).map((s) => s.attr);
} }
static { static {
for (const [name, spec] of Object.entries(PROPS)) { definePropAccessors(LeafletVideoOverlay.prototype, PROPS);
Object.defineProperty(LeafletVideoOverlay.prototype, name, {
get(this: LeafletVideoOverlay) {
const val = this.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return this.hasAttribute(spec.attr);
return val ?? spec.default;
},
set(this: LeafletVideoOverlay, v: number | string | boolean) {
if (spec.kind === 'bool-on') this.toggleAttribute(spec.attr, !!v);
else this.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
} }
connectedCallback() { connectedCallback() {
@ -58,13 +53,14 @@ export class LeafletVideoOverlay extends TypedBase {
this.#parsedBounds(), this.#parsedBounds(),
buildOptions(this, PROPS, ['url', 'bounds']), buildOptions(this, PROPS, ['url', 'bounds']),
); );
this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; this.#childHandler = createChildRegisterHandler(this.#obj, this.#children);
this.addEventListener('leaflet-register', this.#childHandler); this.addEventListener('leaflet-register', this.#childHandler as EventListener);
registerWithParent(this, this.#obj); registerWithParent(this, this.#obj);
} }
disconnectedCallback() { disconnectedCallback() {
if (this.#childHandler) this.removeEventListener('leaflet-register', this.#childHandler); if (this.#childHandler)
this.removeEventListener('leaflet-register', this.#childHandler as EventListener);
this.#children.clear(); this.#children.clear();
this.#obj?.remove(); this.#obj?.remove();
this.#obj = undefined; this.#obj = undefined;

@ -1,21 +1,16 @@
import { Layer, LayerGroup, Popup, Tooltip } from 'leaflet'; import { Layer, LayerGroup, Popup, Tooltip } from 'leaflet';
export interface LeafletRegisterEvent extends CustomEvent { export type LeafletRegisterEvent = CustomEvent<{
detail: { leafletObject: Layer;
leafletObject: Layer; element: HTMLElement;
element: HTMLElement; }>;
};
}
export type ChildEntry = { export type ChildEntry = {
type: 'layer' | 'popup' | 'tooltip'; type: 'layer' | 'popup' | 'tooltip';
}; };
export function createChildRegisterHandler( export function createChildRegisterHandler(layer: Layer, children: Map<HTMLElement, ChildEntry>) {
layer: Layer, return function (e: LeafletRegisterEvent) {
children: globalThis.Map<HTMLElement, ChildEntry>,
) {
return (e: LeafletRegisterEvent) => {
const obj = e.detail.leafletObject; const obj = e.detail.leafletObject;
const el = e.detail.element; const el = e.detail.element;
if (obj instanceof Popup) { if (obj instanceof Popup) {

@ -17,6 +17,27 @@ export function parseAttributeValue(value: string | null): unknown {
} }
} }
export function definePropAccessors(proto: object, props: Record<string, PropDef>) {
for (const [name, spec] of Object.entries(props)) {
Object.defineProperty(proto, name, {
get() {
const el = this as HTMLElement;
const val = el.getAttribute(spec.attr);
if (spec.kind === 'num') return val !== null ? Number(val) : spec.default;
if (spec.kind === 'bool-on') return el.hasAttribute(spec.attr);
return val ?? (spec as { default: string }).default;
},
set(v: unknown) {
const el = this as HTMLElement;
if (spec.kind === 'bool-on') el.toggleAttribute(spec.attr, !!v);
else el.setAttribute(spec.attr, String(v));
},
configurable: true,
enumerable: true,
});
}
}
export function buildOptions( export function buildOptions(
el: HTMLElement, el: HTMLElement,
props: Record<string, PropDef>, props: Record<string, PropDef>,

@ -31,13 +31,3 @@ export type PropTypeOf<T extends PropDef> = T extends NumProp
export type PropTypesFromTable<T extends Record<string, PropDef>> = { export type PropTypesFromTable<T extends Record<string, PropDef>> = {
[K in keyof T]: PropTypeOf<T[K]>; [K in keyof T]: PropTypeOf<T[K]>;
}; };
export function attrToPropName<P extends Record<string, PropDef>>(
props: P,
attr: string,
): keyof P | undefined {
for (const [name, spec] of Object.entries(props) as [keyof P, PropDef][]) {
if (spec.attr === attr) return name;
}
return undefined;
}

Loading…
Cancel
Save