refactor: consolidate core modules into utils.ts
Merge path-style.ts and with-props.ts into utils.ts to reduce the number of core files from 5 to 3 (utils.ts, props.ts, register.ts). Unify duplicate ../core/utils.ts imports in all component files. Remove stale re-exports from src/index.ts.
This commit is contained in:
parent
d8bdc2dcfa
commit
7ae092ac98
@ -1,10 +1,13 @@
|
||||
import { CircleMarker } from 'leaflet';
|
||||
import { buildOptions, numAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildOptions,
|
||||
isPathStyleAttr,
|
||||
numAttr,
|
||||
updatePathStyle,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
lat: num(),
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { Circle } from 'leaflet';
|
||||
import { buildOptions, numAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildOptions,
|
||||
isPathStyleAttr,
|
||||
numAttr,
|
||||
updatePathStyle,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
lat: num(),
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { Control, ControlPosition } from 'leaflet';
|
||||
import { registerWithParent } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { defineProps, str } from '../core/props.ts';
|
||||
import { WithProps, registerWithParent } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
position: str('bottomright'),
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { Control, ControlPosition } from 'leaflet';
|
||||
import { registerWithParent } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { WithProps, registerWithParent } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
position: str('bottomleft'),
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { Control, ControlPosition } from 'leaflet';
|
||||
import { registerWithParent } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { defineProps, str } from '../core/props.ts';
|
||||
import { WithProps, registerWithParent } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
position: str('topleft'),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FeatureGroup, Layer } from 'leaflet';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
|
||||
export default class LeafletFeatureGroup extends HTMLElement {
|
||||
#obj?: FeatureGroup;
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { GeoJSON, PathOptions, Layer } from 'leaflet';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts';
|
||||
import { WithProps, buildOptions } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
data: str(),
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { ImageOverlay, LatLngBounds, LatLngExpression } from 'leaflet';
|
||||
import type { ImageOverlayOptions } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseBoundsAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildAttrMap,
|
||||
buildOptions,
|
||||
parseBoundsAttr,
|
||||
setLayerAttr,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
url: str(),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { LayerGroup, Layer } from 'leaflet';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
|
||||
export default class LeafletLayerGroup extends HTMLElement {
|
||||
#obj?: LayerGroup;
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { Icon, Map as LMap, MapOptions } from 'leaflet';
|
||||
import { LeafletRegisterEvent } from '../core/register.ts';
|
||||
import { defineProps, num, off, on, type NumProp, type PropDef } from '../core/props.ts';
|
||||
import { LeafletRegisterEvent } from '../core/register.ts';
|
||||
|
||||
const DEFAULT_CSS_URL = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
|
||||
const DEFAULT_CSS_INTEGRITY = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
|
||||
|
||||
const CSS_ATTRS = ['css-url', 'css-integrity', 'css-crossorigin'] as const;
|
||||
|
||||
// ── Property table ─────────────────────────────────────────────────────────
|
||||
|
||||
const PROPS = defineProps({
|
||||
// View state — excluded from #buildOptions, initialised via setView()
|
||||
lat: num(0, {
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { Marker } from 'leaflet';
|
||||
import { buildOptions, numAttr, buildAttrMap, setLayerAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { WithProps, buildAttrMap, buildOptions, numAttr, setLayerAttr } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
lat: num(),
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { Polygon } from 'leaflet';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { WithProps, buildOptions, isPathStyleAttr, updatePathStyle } from '../core/utils.ts';
|
||||
import type LeafletLine from './leaflet-line.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { Polyline } from 'leaflet';
|
||||
import { buildOptions } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.ts';
|
||||
import { 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 type LeafletLine from './leaflet-line.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { Popup } from 'leaflet';
|
||||
import { registerWithParent, buildOptions, numAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { defineProps, num, on } from '../core/props.ts';
|
||||
import { WithProps, buildOptions, numAttr, registerWithParent } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
lat: num(),
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { Rectangle } from 'leaflet';
|
||||
import { buildOptions, parseBoundsAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { isPathStyleAttr, updatePathStyle } from '../core/path-style.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildOptions,
|
||||
isPathStyleAttr,
|
||||
parseBoundsAttr,
|
||||
updatePathStyle,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
bounds: str(),
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { SVGOverlay, LatLngBounds, LatLngExpression } from 'leaflet';
|
||||
import type { ImageOverlayOptions } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseBoundsAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildAttrMap,
|
||||
buildOptions,
|
||||
parseBoundsAttr,
|
||||
setLayerAttr,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
bounds: str(),
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { TileLayer } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseAttributeValue } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { defineProps, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildAttrMap,
|
||||
buildOptions,
|
||||
parseAttributeValue,
|
||||
setLayerAttr,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
url: str(),
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { TileLayer } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { defineProps, num, str } from '../core/props.ts';
|
||||
import { WithProps, buildAttrMap, buildOptions, setLayerAttr } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
url: str(),
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { Tooltip } from 'leaflet';
|
||||
import type { TooltipOptions } from 'leaflet';
|
||||
import { registerWithParent, buildOptions, numAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { WithProps, buildOptions, numAttr, registerWithParent } from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
lat: num(),
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { VideoOverlay, LatLngBounds, LatLngExpression } from 'leaflet';
|
||||
import type { VideoOverlayOptions } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseBoundsAttr } from '../core/utils.ts';
|
||||
import { WithProps } from '../core/with-props.ts';
|
||||
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import { defineProps, num, str, on } from '../core/props.ts';
|
||||
import { registerChildren, unregisterChildren } from '../core/register.ts';
|
||||
import {
|
||||
WithProps,
|
||||
buildAttrMap,
|
||||
buildOptions,
|
||||
parseBoundsAttr,
|
||||
setLayerAttr,
|
||||
} from '../core/utils.ts';
|
||||
|
||||
const PROPS = defineProps({
|
||||
url: str(),
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
import { Path } from 'leaflet';
|
||||
import { parseAttributeValue } from './utils.ts';
|
||||
|
||||
// The set of HTML attribute names that map to Leaflet Path style options.
|
||||
// These are handled especially because Leaflet exposes them through
|
||||
// setStyle() rather than individual setter methods, and they are shared
|
||||
// across many vector components (polyline, polygon, circle, rectangle).
|
||||
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',
|
||||
]);
|
||||
|
||||
// Quick check for whether an attribute name is a path-style attribute.
|
||||
// Used in attributeChangedCallback to decide whether to route through
|
||||
// setLayerAttr or updatePathStyle.
|
||||
export function isPathStyleAttr(name: string): boolean {
|
||||
return PATH_STYLE_ATTRS.has(name);
|
||||
}
|
||||
|
||||
// Applies a single style change to a Leaflet Path by converting the
|
||||
// kebab-case attribute name to camelCase and calling setStyle() with
|
||||
// the parsed value. This is called instead of the generic setLayerAttr
|
||||
// dispatcher because Path.setStyle is a bulk-update method that merges
|
||||
// into the existing style object rather than replacing it.
|
||||
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) });
|
||||
}
|
||||
@ -1,28 +1,31 @@
|
||||
import { Path } from 'leaflet';
|
||||
import type { PropDef, PropTypesFromTable } from './props.ts';
|
||||
import type { LatLngBoundsExpression } from 'leaflet';
|
||||
|
||||
// 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 = Number(value);
|
||||
if (!isNaN(num) && value !== '') return num;
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return value;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Ctor<T = object> = new (...args: any[]) => T;
|
||||
|
||||
export function WithProps<TBase extends Ctor<HTMLElement>, TProps extends Record<string, PropDef>>(
|
||||
Base: TBase,
|
||||
props: TProps,
|
||||
): TBase & Ctor<HTMLElement & PropTypesFromTable<TProps>> {
|
||||
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<HTMLElement & PropTypesFromTable<TProps>>;
|
||||
}
|
||||
|
||||
// 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.
|
||||
export function definePropAccessors(proto: object, props: Record<string, PropDef>) {
|
||||
function definePropAccessors(proto: object, props: Record<string, PropDef>) {
|
||||
for (const [name, spec] of Object.entries(props)) {
|
||||
Object.defineProperty(proto, name, {
|
||||
get() {
|
||||
@ -96,6 +99,23 @@ export function setLayerAttr(
|
||||
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 = Number(value);
|
||||
if (!isNaN(num) && value !== '') return num;
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// Builds the initial Leaflet options object from the PROPS table and the
|
||||
// element's current attributes at connection time. Props listed in
|
||||
// `exclude` are skipped (they're passed separately to Leaflet
|
||||
@ -106,7 +126,7 @@ export function setLayerAttr(
|
||||
// The return type is derived from the PROPS table: each prop name maps
|
||||
// to its kind's value type (number for `num`, boolean for `bool-on`/`bool-off`,
|
||||
// string for `str`). The `const` type parameter makes literal exclude arrays
|
||||
// narrow correctly so excluded keys are stripped from the return type.
|
||||
// narrow correctly, so excluded keys are stripped from the return type.
|
||||
export function buildOptions<
|
||||
TProps extends Record<string, PropDef>,
|
||||
const TExclude extends readonly string[] = [],
|
||||
@ -146,3 +166,27 @@ export function registerWithParent(el: HTMLElement, obj: unknown) {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
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) });
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import type { PropDef, PropTypesFromTable } from './props.ts';
|
||||
import { definePropAccessors } from './utils.ts';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Ctor<T = object> = new (...args: any[]) => T;
|
||||
|
||||
// Mixin factory that turns a PROPS table into reactive property
|
||||
// accessors + observedAttributes in one shot. Every component that
|
||||
// follows the PROPS-table pattern uses this instead of manually
|
||||
// defining a static initialization block, getters/setters, and an
|
||||
// observedAttributes getter.
|
||||
//
|
||||
// Usage: `class LeafletFoo extends WithProps(HTMLElement, PROPS)`
|
||||
// Returns an intersection type so consumers see the generated
|
||||
// properties from PropTypesFromTable.
|
||||
export function WithProps<TBase extends Ctor<HTMLElement>, TProps extends Record<string, PropDef>>(
|
||||
Base: TBase,
|
||||
props: TProps,
|
||||
): TBase & Ctor<HTMLElement & PropTypesFromTable<TProps>> {
|
||||
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<HTMLElement & PropTypesFromTable<TProps>>;
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
export * from './core/utils.ts';
|
||||
export * from './core/with-props.ts';
|
||||
export * from './core/path-style.ts';
|
||||
export * from './core/register.ts';
|
||||
export * from './core/props.ts';
|
||||
export { default as LeafletMap } from './components/leaflet-map.ts';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user