From 80c87468b6bf0d9004e61ba68be33e9941b9ed4f Mon Sep 17 00:00:00 2001 From: Buddy Date: Mon, 8 Jun 2026 22:09:16 -0700 Subject: [PATCH] refactor: extract withProps mixin to eliminate TypedBase/observedAttributes boilerplate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 18 components now use withProps(HTMLElement, PROPS) instead of repeating TypedBase cast, static get observedAttributes(), and static { definePropAccessors(...) } in each file. Adds src/core/with-props.ts — a mixin factory that derives PropTypesFromTable, defines property accessors, and sets up observedAttributes from a PROPS table in a single expression. --- src/components/leaflet-circle-marker.ts | 18 ++++----------- src/components/leaflet-circle.ts | 18 ++++----------- src/components/leaflet-control-attribution.ts | 18 ++++----------- src/components/leaflet-control-scale.ts | 18 ++++----------- src/components/leaflet-control-zoom.ts | 18 ++++----------- src/components/leaflet-geojson.ts | 18 ++++----------- src/components/leaflet-image-overlay.ts | 23 ++++--------------- src/components/leaflet-marker.ts | 23 ++++--------------- src/components/leaflet-polygon.ts | 18 ++++----------- src/components/leaflet-polyline.ts | 18 ++++----------- src/components/leaflet-popup.ts | 18 ++++----------- src/components/leaflet-rectangle.ts | 18 ++++----------- src/components/leaflet-svg-overlay.ts | 23 ++++--------------- src/components/leaflet-tile-layer-wms.ts | 23 ++++--------------- src/components/leaflet-tile-layer.ts | 23 ++++--------------- src/components/leaflet-tooltip.ts | 18 ++++----------- src/components/leaflet-video-overlay.ts | 23 ++++--------------- src/core/with-props.ts | 21 +++++++++++++++++ src/index.ts | 1 + 19 files changed, 90 insertions(+), 268 deletions(-) create mode 100644 src/core/with-props.ts diff --git a/src/components/leaflet-circle-marker.ts b/src/components/leaflet-circle-marker.ts index caed33b..1818447 100644 --- a/src/components/leaflet-circle-marker.ts +++ b/src/components/leaflet-circle-marker.ts @@ -1,5 +1,6 @@ import { CircleMarker } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, @@ -7,7 +8,7 @@ import { LeafletRegisterEvent, } from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { lat: { kind: 'num', attr: 'lat', default: 0 }, @@ -21,22 +22,11 @@ const PROPS = { fillOpacity: { kind: 'num', attr: 'fill-opacity', default: 0.2 }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletCircleMarker extends TypedBase { +export class LeafletCircleMarker extends withProps(HTMLElement, PROPS) { #obj?: CircleMarker; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletCircleMarker.prototype, PROPS); - } - connectedCallback() { this.#obj = new CircleMarker( [this.#num('lat'), this.#num('lng')], diff --git a/src/components/leaflet-circle.ts b/src/components/leaflet-circle.ts index a696c57..6fea28d 100644 --- a/src/components/leaflet-circle.ts +++ b/src/components/leaflet-circle.ts @@ -1,5 +1,6 @@ import { Circle } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, @@ -7,7 +8,7 @@ import { type LeafletRegisterEvent, } from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { lat: { kind: 'num', attr: 'lat', default: 0 }, @@ -21,22 +22,11 @@ const PROPS = { fillOpacity: { kind: 'num', attr: 'fill-opacity', default: 0.2 }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletCircle extends TypedBase { +export class LeafletCircle extends withProps(HTMLElement, PROPS) { #obj?: Circle; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletCircle.prototype, PROPS); - } - connectedCallback() { this.#obj = new Circle( [this.#num('lat'), this.#num('lng')], diff --git a/src/components/leaflet-control-attribution.ts b/src/components/leaflet-control-attribution.ts index 817452b..b7b4a46 100644 --- a/src/components/leaflet-control-attribution.ts +++ b/src/components/leaflet-control-attribution.ts @@ -1,27 +1,17 @@ import { Control, ControlPosition } from 'leaflet'; -import { registerWithParent, definePropAccessors } from '../core/utils.js'; +import { registerWithParent } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { position: { kind: 'str', attr: 'position', default: 'bottomright' }, prefix: { kind: 'str', attr: 'prefix', default: '' }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletControlAttribution extends TypedBase { +export class LeafletControlAttribution extends withProps(HTMLElement, PROPS) { #obj?: Control.Attribution; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletControlAttribution.prototype, PROPS); - } - connectedCallback() { this.#obj = new Control.Attribution({ position: this.getAttribute('position') as ControlPosition | undefined, diff --git a/src/components/leaflet-control-scale.ts b/src/components/leaflet-control-scale.ts index 900544b..3119153 100644 --- a/src/components/leaflet-control-scale.ts +++ b/src/components/leaflet-control-scale.ts @@ -1,7 +1,8 @@ import { Control, ControlPosition } from 'leaflet'; -import { registerWithParent, definePropAccessors } from '../core/utils.js'; +import { registerWithParent } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { position: { kind: 'str', attr: 'position', default: 'bottomleft' }, @@ -11,20 +12,9 @@ const PROPS = { updateWhenIdle: { kind: 'bool-on', attr: 'update-when-idle' }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletControlScale extends TypedBase { +export class LeafletControlScale extends withProps(HTMLElement, PROPS) { #obj?: Control.Scale; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletControlScale.prototype, PROPS); - } - connectedCallback() { this.#obj = new Control.Scale({ position: this.getAttribute('position') as ControlPosition | undefined, diff --git a/src/components/leaflet-control-zoom.ts b/src/components/leaflet-control-zoom.ts index 02a68db..f038314 100644 --- a/src/components/leaflet-control-zoom.ts +++ b/src/components/leaflet-control-zoom.ts @@ -1,7 +1,8 @@ import { Control, ControlPosition } from 'leaflet'; -import { registerWithParent, definePropAccessors } from '../core/utils.js'; +import { registerWithParent } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { position: { kind: 'str', attr: 'position', default: 'topleft' }, @@ -11,20 +12,9 @@ const PROPS = { zoomOutTitle: { kind: 'str', attr: 'zoom-out-title', default: 'Zoom out' }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletControlZoom extends TypedBase { +export class LeafletControlZoom extends withProps(HTMLElement, PROPS) { #obj?: Control.Zoom; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletControlZoom.prototype, PROPS); - } - connectedCallback() { this.#obj = new Control.Zoom({ position: this.getAttribute('position') as ControlPosition | undefined, diff --git a/src/components/leaflet-geojson.ts b/src/components/leaflet-geojson.ts index 0127242..faef253 100644 --- a/src/components/leaflet-geojson.ts +++ b/src/components/leaflet-geojson.ts @@ -1,12 +1,13 @@ import { GeoJSON, PathOptions, Layer } from 'leaflet'; -import { buildOptions, registerWithParent, definePropAccessors } from '../core/utils.js'; +import { buildOptions, registerWithParent } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { data: { kind: 'str', attr: 'data', default: '' }, @@ -28,22 +29,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletGeoJSON extends TypedBase { +export class LeafletGeoJSON extends withProps(HTMLElement, PROPS) { #obj?: GeoJSON; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletGeoJSON.prototype, PROPS); - } - connectedCallback() { const raw = this.getAttribute('data'); const data = raw ? JSON.parse(raw) : undefined; diff --git a/src/components/leaflet-image-overlay.ts b/src/components/leaflet-image-overlay.ts index 554267a..5dd72b2 100644 --- a/src/components/leaflet-image-overlay.ts +++ b/src/components/leaflet-image-overlay.ts @@ -1,17 +1,13 @@ import { ImageOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; -import { - registerWithParent, - buildOptions, - parseAttributeValue, - definePropAccessors, -} from '../core/utils.js'; +import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { url: { kind: 'str', attr: 'url', default: '' }, @@ -29,22 +25,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletImageOverlay extends TypedBase { +export class LeafletImageOverlay extends withProps(HTMLElement, PROPS) { #obj?: ImageOverlay; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletImageOverlay.prototype, PROPS); - } - connectedCallback() { const url = this.getAttribute('url') || ''; this.#obj = new ImageOverlay( diff --git a/src/components/leaflet-marker.ts b/src/components/leaflet-marker.ts index edc4672..8fd3ae1 100644 --- a/src/components/leaflet-marker.ts +++ b/src/components/leaflet-marker.ts @@ -1,17 +1,13 @@ import { Marker } from 'leaflet'; -import { - registerWithParent, - buildOptions, - parseAttributeValue, - definePropAccessors, -} from '../core/utils.js'; +import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { lat: { kind: 'num', attr: 'lat', default: 0 }, @@ -27,22 +23,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletMarker extends TypedBase { +export class LeafletMarker extends withProps(HTMLElement, PROPS) { #obj?: Marker; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletMarker.prototype, PROPS); - } - connectedCallback() { this.#obj = new Marker( [this.#num('lat'), this.#num('lng')], diff --git a/src/components/leaflet-polygon.ts b/src/components/leaflet-polygon.ts index 89ca903..2f71bb3 100644 --- a/src/components/leaflet-polygon.ts +++ b/src/components/leaflet-polygon.ts @@ -1,5 +1,6 @@ import { Polygon } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, @@ -7,7 +8,7 @@ import { LeafletRegisterEvent, } from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; import type { LeafletLine } from './leaflet-line.js'; const PROPS = { @@ -19,23 +20,12 @@ const PROPS = { fillOpacity: { kind: 'num', attr: 'fill-opacity', default: 0.2 }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletPolygon extends TypedBase { +export class LeafletPolygon extends withProps(HTMLElement, PROPS) { #obj?: Polygon; #observer?: MutationObserver; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletPolygon.prototype, PROPS); - } - connectedCallback() { this.#obj = new Polygon(this.#getCoords(), buildOptions(this, PROPS)); this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); diff --git a/src/components/leaflet-polyline.ts b/src/components/leaflet-polyline.ts index b1bb1c9..09936eb 100644 --- a/src/components/leaflet-polyline.ts +++ b/src/components/leaflet-polyline.ts @@ -1,5 +1,6 @@ import { Polyline } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, @@ -7,7 +8,7 @@ import { LeafletRegisterEvent, } from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; import type { LeafletLine } from './leaflet-line.js'; const PROPS = { @@ -19,23 +20,12 @@ const PROPS = { fillOpacity: { kind: 'num', attr: 'fill-opacity', default: 0.2 }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletPolyline extends TypedBase { +export class LeafletPolyline extends withProps(HTMLElement, PROPS) { #obj?: Polyline; #observer?: MutationObserver; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletPolyline.prototype, PROPS); - } - connectedCallback() { this.#obj = new Polyline(this.#getCoords(), buildOptions(this, PROPS)); this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); diff --git a/src/components/leaflet-popup.ts b/src/components/leaflet-popup.ts index 886d74b..7eee2d0 100644 --- a/src/components/leaflet-popup.ts +++ b/src/components/leaflet-popup.ts @@ -1,7 +1,8 @@ import { Popup } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { lat: { kind: 'num', attr: 'lat', default: 0 }, @@ -14,21 +15,10 @@ const PROPS = { autoClose: { kind: 'bool-on', attr: 'auto-close' }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletPopup extends TypedBase { +export class LeafletPopup extends withProps(HTMLElement, PROPS) { #obj?: Popup; #observer?: MutationObserver; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletPopup.prototype, PROPS); - } - connectedCallback() { this.#obj = new Popup({ ...buildOptions(this, PROPS, ['lat', 'lng']), diff --git a/src/components/leaflet-rectangle.ts b/src/components/leaflet-rectangle.ts index a4e93d4..63eb197 100644 --- a/src/components/leaflet-rectangle.ts +++ b/src/components/leaflet-rectangle.ts @@ -1,5 +1,6 @@ import { Rectangle, LatLngBoundsExpression } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, @@ -7,7 +8,7 @@ import { LeafletRegisterEvent, } from '../core/register.js'; import { isPathStyleAttr, updatePathStyle } from '../core/path-style.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { bounds: { kind: 'str', attr: 'bounds', default: '' }, @@ -19,22 +20,11 @@ const PROPS = { fillOpacity: { kind: 'num', attr: 'fill-opacity', default: 0.2 }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletRectangle extends TypedBase { +export class LeafletRectangle extends withProps(HTMLElement, PROPS) { #obj?: Rectangle; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletRectangle.prototype, PROPS); - } - connectedCallback() { this.#obj = new Rectangle(this.#parsedBounds(), buildOptions(this, PROPS, ['bounds'])); this.#childHandler = createChildRegisterHandler(this.#obj, this.#children); diff --git a/src/components/leaflet-svg-overlay.ts b/src/components/leaflet-svg-overlay.ts index 050f96d..f8dbb59 100644 --- a/src/components/leaflet-svg-overlay.ts +++ b/src/components/leaflet-svg-overlay.ts @@ -1,17 +1,13 @@ import { SVGOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; -import { - registerWithParent, - buildOptions, - parseAttributeValue, - definePropAccessors, -} from '../core/utils.js'; +import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { bounds: { kind: 'str', attr: 'bounds', default: '' }, @@ -26,22 +22,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletSVGOverlay extends TypedBase { +export class LeafletSVGOverlay extends withProps(HTMLElement, PROPS) { #obj?: SVGOverlay; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletSVGOverlay.prototype, PROPS); - } - connectedCallback() { const svg = this.querySelector('svg'); const dummy = !svg ? document.createElementNS('http://www.w3.org/2000/svg', 'svg') : undefined; diff --git a/src/components/leaflet-tile-layer-wms.ts b/src/components/leaflet-tile-layer-wms.ts index 667ce2a..a731b16 100644 --- a/src/components/leaflet-tile-layer-wms.ts +++ b/src/components/leaflet-tile-layer-wms.ts @@ -1,17 +1,13 @@ import { TileLayer } from 'leaflet'; -import { - registerWithParent, - buildOptions, - parseAttributeValue, - definePropAccessors, -} from '../core/utils.js'; +import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { url: { kind: 'str', attr: 'url', default: '' }, @@ -27,22 +23,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletTileLayerWMS extends TypedBase { +export class LeafletTileLayerWMS extends withProps(HTMLElement, PROPS) { #obj?: TileLayer.WMS; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletTileLayerWMS.prototype, PROPS); - } - connectedCallback() { const url = this.getAttribute('url') || ''; this.#obj = new TileLayer.WMS( diff --git a/src/components/leaflet-tile-layer.ts b/src/components/leaflet-tile-layer.ts index 1ece531..9058bfe 100644 --- a/src/components/leaflet-tile-layer.ts +++ b/src/components/leaflet-tile-layer.ts @@ -1,17 +1,13 @@ import { TileLayer } from 'leaflet'; -import { - registerWithParent, - buildOptions, - parseAttributeValue, - definePropAccessors, -} from '../core/utils.js'; +import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { url: { kind: 'str', attr: 'url', default: '' }, @@ -26,22 +22,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletTileLayer extends TypedBase { +export class LeafletTileLayer extends withProps(HTMLElement, PROPS) { #obj?: TileLayer; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletTileLayer.prototype, PROPS); - } - connectedCallback() { const url = this.getAttribute('url') || ''; this.#obj = new TileLayer(url, buildOptions(this, PROPS, ['url'])); diff --git a/src/components/leaflet-tooltip.ts b/src/components/leaflet-tooltip.ts index 5b28571..4132616 100644 --- a/src/components/leaflet-tooltip.ts +++ b/src/components/leaflet-tooltip.ts @@ -1,7 +1,8 @@ import { Tooltip } from 'leaflet'; -import { registerWithParent, buildOptions, definePropAccessors } from '../core/utils.js'; +import { registerWithParent, buildOptions } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { lat: { kind: 'num', attr: 'lat', default: 0 }, @@ -14,21 +15,10 @@ const PROPS = { opacity: { kind: 'num', attr: 'opacity', default: 1.0 }, } satisfies Record; -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletTooltip extends TypedBase { +export class LeafletTooltip extends withProps(HTMLElement, PROPS) { #obj?: Tooltip; #observer?: MutationObserver; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletTooltip.prototype, PROPS); - } - connectedCallback() { this.#obj = new Tooltip({ ...buildOptions(this, PROPS, ['lat', 'lng']), diff --git a/src/components/leaflet-video-overlay.ts b/src/components/leaflet-video-overlay.ts index 268f8e0..0102328 100644 --- a/src/components/leaflet-video-overlay.ts +++ b/src/components/leaflet-video-overlay.ts @@ -1,17 +1,13 @@ import { VideoOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; -import { - registerWithParent, - buildOptions, - parseAttributeValue, - definePropAccessors, -} from '../core/utils.js'; +import { registerWithParent, buildOptions, parseAttributeValue } from '../core/utils.js'; +import { withProps } from '../core/with-props.js'; import { createChildRegisterHandler, type ChildEntry, LeafletRegisterEvent, } from '../core/register.js'; -import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import type { PropDef } from '../types/props.js'; const PROPS = { url: { kind: 'str', attr: 'url', default: '' }, @@ -30,22 +26,11 @@ const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); -type PropTypes = PropTypesFromTable; -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; - -export class LeafletVideoOverlay extends TypedBase { +export class LeafletVideoOverlay extends withProps(HTMLElement, PROPS) { #obj?: VideoOverlay; #children = new Map(); #childHandler?: (e: LeafletRegisterEvent) => void; - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - static { - definePropAccessors(LeafletVideoOverlay.prototype, PROPS); - } - connectedCallback() { const url = this.getAttribute('url') || ''; this.#obj = new VideoOverlay( diff --git a/src/core/with-props.ts b/src/core/with-props.ts new file mode 100644 index 0000000..5358b08 --- /dev/null +++ b/src/core/with-props.ts @@ -0,0 +1,21 @@ +import type { PropDef, PropTypesFromTable } from '../types/props.js'; +import { definePropAccessors } from './utils.js'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Ctor = new (...args: any[]) => T; + +export function withProps, TProps extends Record>( + Base: TBase, + props: TProps, +): TBase & Ctor> { + class WithProps extends Base { + static { + definePropAccessors(WithProps.prototype, props); + } + static get observedAttributes(): string[] { + return Object.values(props).map((s) => s.attr); + } + } + + return WithProps as TBase & Ctor>; +} diff --git a/src/index.ts b/src/index.ts index 7a2dc1f..2c39dae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export * from './core/utils.js'; +export * from './core/with-props.js'; export * from './core/path-style.js'; export * from './core/register.js'; export * from './types/props.js';