You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
import type { LatLngBoundsExpression } from 'leaflet';
|
|
import { Path } from 'leaflet';
|
|
import type { PropDef } from './props.ts';
|
|
|
|
// 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.
|
|
export function numAttr(el: HTMLElement, props: Record<string, PropDef>, name: string): number {
|
|
const v = el.getAttribute(name);
|
|
if (v !== null) return +v;
|
|
return (props[name] as { default: number }).default;
|
|
}
|
|
|
|
// Reads the `bounds` attribute from an element and parses it as JSON
|
|
// into a LatLngBoundsExpression. Returns an empty array when no
|
|
// attribute is set. Shared by rectangle, image-overlay, video-overlay,
|
|
// and svg-overlay, all of which accept a `bounds` attribute.
|
|
export function parseBoundsAttr(el: HTMLElement): LatLngBoundsExpression {
|
|
const raw = el.getAttribute('bounds');
|
|
return raw ? (JSON.parse(raw) as LatLngBoundsExpression) : [];
|
|
}
|
|
|
|
// Builds a reverse lookup map from HTML attribute name → PROP key.
|
|
// For example, { fillColor: { attr: 'fill-color' } } becomes
|
|
// { 'fill-color' → 'fillColor' }. Used by setLayerAttr to find the
|
|
// Leaflet setter name when an attribute changes at runtime.
|
|
export function buildAttrMap<T extends Record<string, PropDef>>(props: T): Map<string, keyof T> {
|
|
return new Map(Object.entries(props).map(([name, spec]) => [spec.attr, name]));
|
|
}
|
|
|
|
// Generic dispatcher for runtime attribute changes on Leaflet objects.
|
|
// Given the attribute name, it looks up the PROP key, constructs the
|
|
// matching Leaflet setter name (e.g. "opacity" → "setOpacity"), and
|
|
// calls it with the parsed value. Returns true if a setter was found
|
|
// and called, false otherwise (caller can fall back, as WMS does with
|
|
// setParams). Handles bool-on props by passing the presence/absence of
|
|
// the attribute rather than its string value.
|
|
export function setLayerAttr(
|
|
obj: object,
|
|
props: Record<string, PropDef>,
|
|
attrMap: Map<string, string>,
|
|
name: string,
|
|
val: string | null,
|
|
): boolean {
|
|
const propName = attrMap.get(name);
|
|
if (!propName) return false;
|
|
const spec = props[propName];
|
|
const setter = `set${propName.charAt(0).toUpperCase()}${propName.slice(1)}`;
|
|
const fn = (obj as Record<string, unknown>)[setter];
|
|
if (typeof fn === 'function') {
|
|
const value = spec.kind === 'bool-on' ? val !== null : parseAttributeValue(val);
|
|
(fn as (v: unknown) => void)(value);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parses an HTML attribute string into a typed JS value:
|
|
// null → null, "true"/"false" → boolean, numeric strings → number,
|
|
// valid JSON → parsed object/array, otherwise the raw string.
|
|
// This is the bridge between HTML attribute strings and Leaflet options.
|
|
export function parseAttributeValue(value: string | null): unknown {
|
|
if (value === null) return null;
|
|
if (value === 'true') return true;
|
|
if (value === 'false') return false;
|
|
const num = +value;
|
|
if (!isNaN(num) && value !== '') return num;
|
|
try {
|
|
return JSON.parse(value);
|
|
} catch {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
const PATH_STYLE_ATTRS = new Set([
|
|
'color',
|
|
'weight',
|
|
'opacity',
|
|
'fill',
|
|
'fill-color',
|
|
'fill-opacity',
|
|
'stroke',
|
|
'dash-array',
|
|
'dash-offset',
|
|
'line-cap',
|
|
'line-join',
|
|
'fill-rule',
|
|
]);
|
|
|
|
export function isPathStyleAttr(name: string): boolean {
|
|
return PATH_STYLE_ATTRS.has(name);
|
|
}
|
|
|
|
export function updatePathStyle(obj: Path, name: string, value: string | null) {
|
|
const key = name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
obj.setStyle({ [key]: parseAttributeValue(value) });
|
|
}
|