diff --git a/src/components/leaflet-circle-marker.ts b/src/components/leaflet-circle-marker.ts index f65dc81..887f5cc 100644 --- a/src/components/leaflet-circle-marker.ts +++ b/src/components/leaflet-circle-marker.ts @@ -1,8 +1,7 @@ import { CircleMarker } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildOptions, isPathStyleAttr, numAttr, diff --git a/src/components/leaflet-circle.ts b/src/components/leaflet-circle.ts index 76a1db5..8824f5d 100644 --- a/src/components/leaflet-circle.ts +++ b/src/components/leaflet-circle.ts @@ -1,8 +1,7 @@ import { Circle } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildOptions, isPathStyleAttr, numAttr, diff --git a/src/components/leaflet-control-attribution.ts b/src/components/leaflet-control-attribution.ts index 12ee41b..53b0c73 100644 --- a/src/components/leaflet-control-attribution.ts +++ b/src/components/leaflet-control-attribution.ts @@ -1,6 +1,6 @@ import { Control, ControlPosition } from 'leaflet'; -import { defineProps, str } from '../core/props.ts'; -import { WithProps, registerWithParent } from '../core/utils.ts'; +import { WithProps, defineProps, str } from '../core/props.ts'; +import { registerWithParent } from '../core/utils.ts'; const PROPS = defineProps({ position: str('bottomright'), diff --git a/src/components/leaflet-control-layers.ts b/src/components/leaflet-control-layers.ts index 0ecec1e..d757e97 100644 --- a/src/components/leaflet-control-layers.ts +++ b/src/components/leaflet-control-layers.ts @@ -1,6 +1,6 @@ import { Control, ControlPosition, Layer } from 'leaflet'; -import { defineProps, str } from '../core/props.ts'; -import { WithProps, registerWithParent } from '../core/utils.ts'; +import { WithProps, defineProps, str } from '../core/props.ts'; +import { registerWithParent } from '../core/utils.ts'; import { LeafletRegisterEvent } from '../core/register.ts'; const PROPS = defineProps({ diff --git a/src/components/leaflet-control-scale.ts b/src/components/leaflet-control-scale.ts index 8f119bb..1d8d7fe 100644 --- a/src/components/leaflet-control-scale.ts +++ b/src/components/leaflet-control-scale.ts @@ -1,6 +1,6 @@ import { Control, ControlPosition } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; -import { WithProps, registerWithParent } from '../core/utils.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; +import { registerWithParent } from '../core/utils.ts'; const PROPS = defineProps({ position: str('bottomleft'), diff --git a/src/components/leaflet-control-zoom.ts b/src/components/leaflet-control-zoom.ts index 7d86a7e..f15e5ac 100644 --- a/src/components/leaflet-control-zoom.ts +++ b/src/components/leaflet-control-zoom.ts @@ -1,6 +1,6 @@ import { Control, ControlPosition } from 'leaflet'; -import { defineProps, str } from '../core/props.ts'; -import { WithProps, registerWithParent } from '../core/utils.ts'; +import { WithProps, defineProps, str } from '../core/props.ts'; +import { registerWithParent } from '../core/utils.ts'; const PROPS = defineProps({ position: str('topleft'), diff --git a/src/components/leaflet-geojson.ts b/src/components/leaflet-geojson.ts index df234aa..ea425fe 100644 --- a/src/components/leaflet-geojson.ts +++ b/src/components/leaflet-geojson.ts @@ -1,7 +1,7 @@ import { GeoJSON, PathOptions, Layer } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts'; -import { WithProps, buildOptions } from '../core/utils.ts'; +import { buildOptions } from '../core/utils.ts'; const PROPS = defineProps({ data: str(), diff --git a/src/components/leaflet-image-overlay.ts b/src/components/leaflet-image-overlay.ts index 6afd788..d059db4 100644 --- a/src/components/leaflet-image-overlay.ts +++ b/src/components/leaflet-image-overlay.ts @@ -1,9 +1,8 @@ import { ImageOverlay, LatLngBounds, LatLngExpression } from 'leaflet'; import type { ImageOverlayOptions } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildAttrMap, buildOptions, parseBoundsAttr, diff --git a/src/components/leaflet-marker.ts b/src/components/leaflet-marker.ts index b9bb5e8..0aef326 100644 --- a/src/components/leaflet-marker.ts +++ b/src/components/leaflet-marker.ts @@ -1,7 +1,7 @@ import { Icon, Marker } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { LeafletIconChangedEvent, registerChildren, unregisterChildren } from '../core/register.ts'; -import { WithProps, buildAttrMap, buildOptions, numAttr, setLayerAttr } from '../core/utils.ts'; +import { buildAttrMap, buildOptions, numAttr, setLayerAttr } from '../core/utils.ts'; const PROPS = defineProps({ lat: num(), diff --git a/src/components/leaflet-polygon.ts b/src/components/leaflet-polygon.ts index 3e97129..ff00f78 100644 --- a/src/components/leaflet-polygon.ts +++ b/src/components/leaflet-polygon.ts @@ -1,7 +1,7 @@ import { Polygon } from 'leaflet'; import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { defineProps, num, str, on } from '../core/props.ts'; -import { WithProps, buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; +import { buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts'; import type { LeafletLine } from './leaflet-line.ts'; const PROPS = defineProps({ diff --git a/src/components/leaflet-polyline.ts b/src/components/leaflet-polyline.ts index 75d0283..37c1771 100644 --- a/src/components/leaflet-polyline.ts +++ b/src/components/leaflet-polyline.ts @@ -1,7 +1,7 @@ import { Polyline } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { WithProps, buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts'; +import { buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts'; import type { LeafletLine } from './leaflet-line.ts'; const PROPS = defineProps({ diff --git a/src/components/leaflet-popup.ts b/src/components/leaflet-popup.ts index 479c156..c91b488 100644 --- a/src/components/leaflet-popup.ts +++ b/src/components/leaflet-popup.ts @@ -1,6 +1,6 @@ import { Popup } from 'leaflet'; -import { defineProps, num, on } from '../core/props.ts'; -import { WithProps, buildOptions, numAttr, registerWithParent } from '../core/utils.ts'; +import { WithProps, defineProps, num, on } from '../core/props.ts'; +import { buildOptions, numAttr, registerWithParent } from '../core/utils.ts'; const PROPS = defineProps({ lat: num(), diff --git a/src/components/leaflet-rectangle.ts b/src/components/leaflet-rectangle.ts index 2ab4c28..341c5b9 100644 --- a/src/components/leaflet-rectangle.ts +++ b/src/components/leaflet-rectangle.ts @@ -1,8 +1,7 @@ import { Rectangle } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildOptions, isPathStyleAttr, parseBoundsAttr, diff --git a/src/components/leaflet-svg-overlay.ts b/src/components/leaflet-svg-overlay.ts index f40e48f..24512cb 100644 --- a/src/components/leaflet-svg-overlay.ts +++ b/src/components/leaflet-svg-overlay.ts @@ -1,9 +1,8 @@ import { SVGOverlay, LatLngBounds, LatLngExpression } from 'leaflet'; import type { ImageOverlayOptions } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildAttrMap, buildOptions, parseBoundsAttr, diff --git a/src/components/leaflet-tile-layer-wms.ts b/src/components/leaflet-tile-layer-wms.ts index 0b7f9b7..669b77a 100644 --- a/src/components/leaflet-tile-layer-wms.ts +++ b/src/components/leaflet-tile-layer-wms.ts @@ -1,8 +1,7 @@ import { TileLayer } from 'leaflet'; -import { defineProps, str, on } from '../core/props.ts'; +import { WithProps, defineProps, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildAttrMap, buildOptions, parseAttributeValue, diff --git a/src/components/leaflet-tile-layer.ts b/src/components/leaflet-tile-layer.ts index b4758e0..23ec492 100644 --- a/src/components/leaflet-tile-layer.ts +++ b/src/components/leaflet-tile-layer.ts @@ -1,7 +1,7 @@ import { TileLayer } from 'leaflet'; import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { defineProps, num, str } from '../core/props.ts'; -import { WithProps, buildAttrMap, buildOptions, setLayerAttr } from '../core/utils.ts'; +import { WithProps, defineProps, num, str } from '../core/props.ts'; +import { buildAttrMap, buildOptions, setLayerAttr } from '../core/utils.ts'; const PROPS = defineProps({ url: str(), diff --git a/src/components/leaflet-tooltip.ts b/src/components/leaflet-tooltip.ts index 0a0350d..73ec3fe 100644 --- a/src/components/leaflet-tooltip.ts +++ b/src/components/leaflet-tooltip.ts @@ -1,7 +1,7 @@ import { Tooltip } from 'leaflet'; import type { TooltipOptions } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; -import { WithProps, buildOptions, numAttr, registerWithParent } from '../core/utils.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; +import { buildOptions, numAttr, registerWithParent } from '../core/utils.ts'; const PROPS = defineProps({ lat: num(), diff --git a/src/components/leaflet-video-overlay.ts b/src/components/leaflet-video-overlay.ts index 68bce94..65be74f 100644 --- a/src/components/leaflet-video-overlay.ts +++ b/src/components/leaflet-video-overlay.ts @@ -1,9 +1,8 @@ import { VideoOverlay, LatLngBounds, LatLngExpression } from 'leaflet'; import type { VideoOverlayOptions } from 'leaflet'; -import { defineProps, num, str, on } from '../core/props.ts'; +import { WithProps, defineProps, num, str, on } from '../core/props.ts'; import { registerChildren, unregisterChildren } from '../core/register.ts'; import { - WithProps, buildAttrMap, buildOptions, parseBoundsAttr, diff --git a/src/core/props.ts b/src/core/props.ts index b59aee1..c857d27 100644 --- a/src/core/props.ts +++ b/src/core/props.ts @@ -37,8 +37,6 @@ export type PropTypesFromTable> = { [K in keyof T]: PropTypeOf; }; -// --- Factory helpers --- - type OptionalAttr = Omit & { attr?: string }; type NumPropInput = OptionalAttr>; @@ -98,3 +96,40 @@ export function defineProps>( } return output as unknown as { [K in keyof T]: PropDefFromInput }; } + +// 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 get observedAttributes(): string[] { + return Object.values(props).map((s) => s.attr); + } + } + definePropAccessors(WithProps.prototype, props); + return WithProps as TBase & Ctor>; +} + +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 ? +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, `${v}`); + }, + configurable: true, + enumerable: true, + }); + } +} diff --git a/src/core/utils.ts b/src/core/utils.ts index f432743..9552908 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -2,50 +2,6 @@ import { Path } from 'leaflet'; import type { PropDef, PropTypesFromTable } from './props.ts'; import type { LatLngBoundsExpression } from 'leaflet'; -// 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>; -} - -// Installs reactive getter/setter pairs on a prototype for every entry in -// a PROPS table. Each getter reads from the attribute (coerced to the -// correct type), each setter writes via setAttribute/toggleAttribute. -// Used by the WithProps() mixin so components have `el.lat = 51.5` sugar. -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 ? +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, `${v}`); - }, - configurable: true, - enumerable: true, - }); - } -} - // Reads a numeric attribute from an element, falling back to the default // value declared in the PROPS table. Handles the common pattern of // reading lat/lng/radius/opacity with a guaranteed number return.