diff --git a/src/components/leaflet-circle-marker.ts b/src/components/leaflet-circle-marker.ts index e12bd45..9e42993 100644 --- a/src/components/leaflet-circle-marker.ts +++ b/src/components/leaflet-circle-marker.ts @@ -1,6 +1,11 @@ import { CircleMarker } from 'leaflet'; -import { registerWithParent, buildOptions } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; + +import { + createChildRegisterHandler, + type ChildEntry, + LeafletRegisterEvent, +} from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.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 { #obj?: CircleMarker; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletCircleMarker.prototype, PROPS); } connectedCallback() { @@ -52,13 +42,14 @@ export class LeafletCircleMarker extends TypedBase { [this.#num('lat'), this.#num('lng')], buildOptions(this, PROPS, ['lat', 'lng']), ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-circle.ts b/src/components/leaflet-circle.ts index ecbf73f..84d7627 100644 --- a/src/components/leaflet-circle.ts +++ b/src/components/leaflet-circle.ts @@ -1,6 +1,11 @@ import { Circle } from 'leaflet'; -import { registerWithParent, buildOptions } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; + +import { + createChildRegisterHandler, + type ChildEntry, + type LeafletRegisterEvent, +} from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.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 { #obj?: Circle; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletCircle.prototype, PROPS); } connectedCallback() { @@ -52,13 +42,14 @@ export class LeafletCircle extends TypedBase { [this.#num('lat'), this.#num('lng')], buildOptions(this, PROPS, ['lat', 'lng']), ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-control-attribution.ts b/src/components/leaflet-control-attribution.ts index 9f726ee..817452b 100644 --- a/src/components/leaflet-control-attribution.ts +++ b/src/components/leaflet-control-attribution.ts @@ -1,5 +1,6 @@ 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'; const PROPS = { @@ -18,18 +19,7 @@ export class LeafletControlAttribution extends TypedBase { } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletControlAttribution.prototype, PROPS); } connectedCallback() { diff --git a/src/components/leaflet-control-scale.ts b/src/components/leaflet-control-scale.ts index ded2b62..900544b 100644 --- a/src/components/leaflet-control-scale.ts +++ b/src/components/leaflet-control-scale.ts @@ -1,5 +1,6 @@ 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'; const PROPS = { @@ -21,22 +22,7 @@ export class LeafletControlScale extends TypedBase { } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletControlScale.prototype, PROPS); } connectedCallback() { diff --git a/src/components/leaflet-control-zoom.ts b/src/components/leaflet-control-zoom.ts index df2ff14..02a68db 100644 --- a/src/components/leaflet-control-zoom.ts +++ b/src/components/leaflet-control-zoom.ts @@ -1,5 +1,6 @@ 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'; const PROPS = { @@ -21,19 +22,7 @@ export class LeafletControlZoom extends TypedBase { } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletControlZoom.prototype, PROPS); } connectedCallback() { diff --git a/src/components/leaflet-feature-group.ts b/src/components/leaflet-feature-group.ts index 1a60136..f5e6c68 100644 --- a/src/components/leaflet-feature-group.ts +++ b/src/components/leaflet-feature-group.ts @@ -1,11 +1,15 @@ import { FeatureGroup, Layer } from 'leaflet'; 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 { #obj?: FeatureGroup; #children = new Map(); - #childHandler?: (e: Event) => void; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return []; @@ -13,13 +17,14 @@ export class LeafletFeatureGroup extends HTMLElement { connectedCallback() { this.#obj = new FeatureGroup([], buildOptions(this, {})); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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) { if (entry.type === 'popup') this.#obj?.unbindPopup(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); diff --git a/src/components/leaflet-geojson.ts b/src/components/leaflet-geojson.ts index b2a8c18..91ad33f 100644 --- a/src/components/leaflet-geojson.ts +++ b/src/components/leaflet-geojson.ts @@ -1,6 +1,11 @@ import { GeoJSON, PathOptions, Layer } from 'leaflet'; -import { buildOptions, registerWithParent } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { buildOptions, registerWithParent, definePropAccessors } from '../core/utils.js'; + +import { + createChildRegisterHandler, + type ChildEntry, + LeafletRegisterEvent, +} from '../core/register.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js'; const PROPS = { @@ -29,28 +34,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletGeoJSON extends TypedBase { #obj?: GeoJSON; #children = new Map(); - #childHandler?: (e: Event) => void; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletGeoJSON.prototype, PROPS); } connectedCallback() { @@ -60,13 +51,14 @@ export class LeafletGeoJSON extends TypedBase { Object.entries(buildOptions(this, PROPS, ['data'])).filter(([, v]) => v !== ''), ); this.#obj = new GeoJSON(data, { style: styleOpts as PathOptions }); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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) { if (entry.type === 'popup') this.#obj?.unbindPopup(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); diff --git a/src/components/leaflet-image-overlay.ts b/src/components/leaflet-image-overlay.ts index b1e5ca8..eeaa0bd 100644 --- a/src/components/leaflet-image-overlay.ts +++ b/src/components/leaflet-image-overlay.ts @@ -1,6 +1,16 @@ import { ImageOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; -import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { + 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'; const PROPS = { @@ -25,29 +35,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletImageOverlay extends TypedBase { #obj?: ImageOverlay; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletImageOverlay.prototype, PROPS); } connectedCallback() { @@ -57,13 +52,14 @@ export class LeafletImageOverlay extends TypedBase { this.#parsedBounds(), buildOptions(this, PROPS, ['url', 'bounds']), ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-layer-group.ts b/src/components/leaflet-layer-group.ts index 05b5442..724f69a 100644 --- a/src/components/leaflet-layer-group.ts +++ b/src/components/leaflet-layer-group.ts @@ -1,11 +1,15 @@ import { LayerGroup, Layer } from 'leaflet'; 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 { #obj?: LayerGroup; #children = new Map(); - #childHandler?: (e: Event) => void; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return []; @@ -13,13 +17,14 @@ export class LeafletLayerGroup extends HTMLElement { connectedCallback() { this.#obj = new LayerGroup([], buildOptions(this, {})); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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) { if (entry.type === 'popup') this.#obj?.unbindPopup(); else if (entry.type === 'tooltip') this.#obj?.unbindTooltip(); diff --git a/src/components/leaflet-marker.ts b/src/components/leaflet-marker.ts index 635e804..17ded2b 100644 --- a/src/components/leaflet-marker.ts +++ b/src/components/leaflet-marker.ts @@ -1,6 +1,16 @@ import { Marker } from 'leaflet'; -import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { + 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'; const PROPS = { @@ -23,29 +33,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletMarker extends TypedBase { #obj?: Marker; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletMarker.prototype, PROPS); } connectedCallback() { @@ -53,13 +48,14 @@ export class LeafletMarker extends TypedBase { [this.#num('lat'), this.#num('lng')], buildOptions(this, PROPS, ['lat', 'lng']), ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-polygon.ts b/src/components/leaflet-polygon.ts index 119c394..48b6010 100644 --- a/src/components/leaflet-polygon.ts +++ b/src/components/leaflet-polygon.ts @@ -1,6 +1,11 @@ import { Polygon } from 'leaflet'; -import { registerWithParent, buildOptions } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; + +import { + createChildRegisterHandler, + type ChildEntry, + LeafletRegisterEvent, +} from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { LeafletLine } from './leaflet-line.js'; @@ -21,35 +26,20 @@ export class LeafletPolygon extends TypedBase { #obj?: Polygon; #observer?: MutationObserver; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletPolygon.prototype, PROPS); } connectedCallback() { this.#obj = new Polygon(this.#getCoords(), buildOptions(this, PROPS)); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); this.addEventListener('line-updated', this.#syncCoords); @@ -61,7 +51,8 @@ export class LeafletPolygon extends TypedBase { this.#observer?.disconnect(); this.#observer = undefined; 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-polyline.ts b/src/components/leaflet-polyline.ts index d55d1fa..b93f3c3 100644 --- a/src/components/leaflet-polyline.ts +++ b/src/components/leaflet-polyline.ts @@ -1,6 +1,11 @@ import { Polyline } from 'leaflet'; -import { registerWithParent, buildOptions } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; + +import { + createChildRegisterHandler, + type ChildEntry, + LeafletRegisterEvent, +} from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; import type { PropDef, PropTypesFromTable } from '../types/props.js'; import type { LeafletLine } from './leaflet-line.js'; @@ -21,35 +26,20 @@ export class LeafletPolyline extends TypedBase { #obj?: Polyline; #observer?: MutationObserver; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletPolyline.prototype, PROPS); } connectedCallback() { this.#obj = new Polyline(this.#getCoords(), buildOptions(this, PROPS)); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); this.addEventListener('line-updated', this.#syncCoords); @@ -61,7 +51,8 @@ export class LeafletPolyline extends TypedBase { this.#observer?.disconnect(); this.#observer = undefined; 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-popup.ts b/src/components/leaflet-popup.ts index 15cfd96..886d74b 100644 --- a/src/components/leaflet-popup.ts +++ b/src/components/leaflet-popup.ts @@ -1,5 +1,6 @@ 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'; const PROPS = { @@ -25,23 +26,7 @@ export class LeafletPopup extends TypedBase { } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletPopup.prototype, PROPS); } connectedCallback() { diff --git a/src/components/leaflet-rectangle.ts b/src/components/leaflet-rectangle.ts index 673aa4e..58dda86 100644 --- a/src/components/leaflet-rectangle.ts +++ b/src/components/leaflet-rectangle.ts @@ -1,6 +1,11 @@ import { Rectangle, LatLngBoundsExpression } from 'leaflet'; -import { registerWithParent, buildOptions } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; + +import { + createChildRegisterHandler, + type ChildEntry, + LeafletRegisterEvent, +} from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.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 { #obj?: Rectangle; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletRectangle.prototype, PROPS); } connectedCallback() { this.#obj = new Rectangle(this.#parsedBounds(), buildOptions(this, PROPS, ['bounds'])); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-svg-overlay.ts b/src/components/leaflet-svg-overlay.ts index 02c788c..126e101 100644 --- a/src/components/leaflet-svg-overlay.ts +++ b/src/components/leaflet-svg-overlay.ts @@ -1,6 +1,16 @@ import { SVGOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; -import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { + 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'; const PROPS = { @@ -22,29 +32,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletSVGOverlay extends TypedBase { #obj?: SVGOverlay; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletSVGOverlay.prototype, PROPS); } connectedCallback() { @@ -55,13 +50,14 @@ export class LeafletSVGOverlay extends TypedBase { this.#parsedBounds(), buildOptions(this, PROPS, ['bounds']), ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-tile-layer-wms.ts b/src/components/leaflet-tile-layer-wms.ts index 2cf1a67..58d49a5 100644 --- a/src/components/leaflet-tile-layer-wms.ts +++ b/src/components/leaflet-tile-layer-wms.ts @@ -1,6 +1,16 @@ import { TileLayer } from 'leaflet'; -import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { + 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'; const PROPS = { @@ -23,31 +33,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletTileLayerWMS extends TypedBase { #obj?: TileLayer.WMS; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletTileLayerWMS.prototype, PROPS); } connectedCallback() { @@ -56,13 +49,14 @@ export class LeafletTileLayerWMS extends TypedBase { url, buildOptions(this, PROPS, ['url']) as Record, ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-tile-layer.ts b/src/components/leaflet-tile-layer.ts index f880a83..74b33c2 100644 --- a/src/components/leaflet-tile-layer.ts +++ b/src/components/leaflet-tile-layer.ts @@ -1,6 +1,16 @@ import { TileLayer } from 'leaflet'; -import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { + 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'; const PROPS = { @@ -22,39 +32,27 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletTileLayer extends TypedBase { #obj?: TileLayer; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletTileLayer.prototype, PROPS); } connectedCallback() { const url = this.getAttribute('url') || ''; this.#obj = new TileLayer(url, buildOptions(this, PROPS, ['url'])); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/components/leaflet-tooltip.ts b/src/components/leaflet-tooltip.ts index f18aa98..5b28571 100644 --- a/src/components/leaflet-tooltip.ts +++ b/src/components/leaflet-tooltip.ts @@ -1,5 +1,6 @@ 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'; const PROPS = { @@ -25,22 +26,7 @@ export class LeafletTooltip extends TypedBase { } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletTooltip.prototype, PROPS); } connectedCallback() { diff --git a/src/components/leaflet-video-overlay.ts b/src/components/leaflet-video-overlay.ts index 1d5d8d3..45f0953 100644 --- a/src/components/leaflet-video-overlay.ts +++ b/src/components/leaflet-video-overlay.ts @@ -1,6 +1,16 @@ import { VideoOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; -import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; -import { createChildRegisterHandler, type ChildEntry } from '../core/register.js'; +import { + 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'; const PROPS = { @@ -26,29 +36,14 @@ const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; export class LeafletVideoOverlay extends TypedBase { #obj?: VideoOverlay; #children = new Map(); - #childHandler?: EventListener; + #childHandler?: (e: LeafletRegisterEvent) => void; static get observedAttributes() { return Object.values(PROPS).map((s) => s.attr); } static { - for (const [name, spec] of Object.entries(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, - }); - } + definePropAccessors(LeafletVideoOverlay.prototype, PROPS); } connectedCallback() { @@ -58,13 +53,14 @@ export class LeafletVideoOverlay extends TypedBase { this.#parsedBounds(), buildOptions(this, PROPS, ['url', 'bounds']), ); - this.#childHandler = createChildRegisterHandler(this.#obj, this.#children) as EventListener; - this.addEventListener('leaflet-register', this.#childHandler); + this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); + this.addEventListener('leaflet-register', this.#childHandler as EventListener); registerWithParent(this, this.#obj); } 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.#obj?.remove(); this.#obj = undefined; diff --git a/src/core/register.ts b/src/core/register.ts index 2c5a62c..21ab8d5 100644 --- a/src/core/register.ts +++ b/src/core/register.ts @@ -1,21 +1,16 @@ import { Layer, LayerGroup, Popup, Tooltip } from 'leaflet'; -export interface LeafletRegisterEvent extends CustomEvent { - detail: { - leafletObject: Layer; - element: HTMLElement; - }; -} +export type LeafletRegisterEvent = CustomEvent<{ + leafletObject: Layer; + element: HTMLElement; +}>; export type ChildEntry = { type: 'layer' | 'popup' | 'tooltip'; }; -export function createChildRegisterHandler( - layer: Layer, - children: globalThis.Map, -) { - return (e: LeafletRegisterEvent) => { +export function createChildRegisterHandler(layer: Layer, children: Map) { + return function (e: LeafletRegisterEvent) { const obj = e.detail.leafletObject; const el = e.detail.element; if (obj instanceof Popup) { diff --git a/src/core/utils.ts b/src/core/utils.ts index 472c9d1..e275115 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -17,6 +17,27 @@ export function parseAttributeValue(value: string | null): unknown { } } +export function definePropAccessors(proto: object, props: Record) { + 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( el: HTMLElement, props: Record, diff --git a/src/types/props.ts b/src/types/props.ts index 297a692..0769333 100644 --- a/src/types/props.ts +++ b/src/types/props.ts @@ -31,13 +31,3 @@ export type PropTypeOf = T extends NumProp export type PropTypesFromTable> = { [K in keyof T]: PropTypeOf; }; - -export function attrToPropName

>( - 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; -}