61 Commits (875a374f05a5f313aaf45b6b8e749edb583d0e67)
 

Author SHA1 Message Date
Buddy 875a374f05 chore: npm upgrade 3 weeks ago
Buddy ef7828128b feat: make WMS crs extensible via a nested provider element, not a registry
Replaces the mutable registerCRS() registry (never released beyond this
branch) with a web-components-first design: any custom element, no base
class required, can provide a CRS Leaflet doesn't ship by nesting inside
<leaflet-tile-layer-wms> and firing a bubbling leaflet-crs-changed event
(detail: { crs: CRS | null }, mirroring icon-changed's { icon: null }
pattern for "revert to default"). The plain `crs="EPSG4326"` attribute
stays as a convenience shortcut for the 4 CRSes Leaflet itself ships; a
nested provider takes priority over it when both are present.

This surfaced a real, previously-undiscovered bug in with-props.ts:
recreateLeafletObject() was declared on the public LeafletElement
interface but only ever implemented as a private #recreateLeafletObject()
-- calling it would throw "is not a function" at runtime despite
type-checking cleanly. Fixed by making it public, and it now also
re-dispatches registerWithParent() for attach !== 'none' components,
which it never did before (previously only safe for attach: 'none'
components like icons, since recreate would otherwise rebuild a layer
without ever re-adding it to whatever registered it the first time --
exactly what leaflet-tile-layer-wms needs when a nested provider's crs
arrives after it already constructed itself with the default).
4 weeks ago
Buddy b90616d3c5 docs: add plugin authoring guide to README
Documents the toolkit third-party components are built from, now that
it's exported from the package root: the WithProps + PROPS table
pattern, the prop builder reference, reusing the shared fragments
(pathProps/latLngProps/tileLayerProps/urlProp), how nesting works
(attach modes, and why Leaflet plugin classes register correctly without
any special-casing on either side), typing events with
LeafletAddEventListener and the event-types.ts fragments, the
emitIconChanged protocol for custom marker icons, and the
customElements.define ordering pitfall for a plugin with multiple
interdependent custom elements of its own.
4 weeks ago
Buddy 1d91b44372 feat: export core primitives for third-party plugin components
WithProps and its types (ElementOptions, Attach, LeafletElement,
LeafletElementConstructor, LeafletAddEventListener/LeafletRemoveEventListener),
the shared-props.ts fragments (pathProps, latLngProps, tileLayerProps,
urlProp, style, getBounds, Positioned/Styleable/Sourced/Bounded), and all
of event-types.ts are now part of the public API surface, not just
internal to this package's own components.

The registration protocol (instanceof Layer/Popup/Tooltip against
Leaflet's own base classes) and the icon-changed protocol were already
plugin-friendly by design -- a third-party layer, control, or icon
component built on Leaflet's own class hierarchy integrates with what's
here without any special-casing. This closes the one real gap: those
same primitives weren't reachable from the package root, only via
undocumented dist/core/* deep imports. A plugin author can now build a
component the same way this package's own components are built.
4 weeks ago
Buddy 3845e53329 fix: remove parent-reaches-into-child patterns, fix a real load-order bug
Two parent-looks-for-child patterns found and removed, per the
child-emits-events-parent-never-reaches-in principle:

- leaflet-control-layers: #childLayers() queried children directly via
  querySelectorAll + reading their leafletObject/attributes, in two call
  sites. Verified empirically that a parent's connectedCallback always
  completes before a freshly-connected child's does (even for an
  already-built subtree attached in one shot), which means both call
  sites always ran against unpopulated children -- dead code. Deleted;
  #onChildRegister (already event-driven) was doing all the real work.

- leaflet-polygon/leaflet-polyline: #coords() queried <leaflet-line>
  children via querySelectorAll + read their .latlng property directly,
  using a MutationObserver + a payload-less event only as a "something
  changed, rescan everyone" signal. Rewritten to be purely event-driven:
  <leaflet-line> now fires leaflet-line-sync (connect + every lat/lng
  change, carrying its own position) and leaflet-line-remove (disconnect)
  on itself; a new shared VertexTracker (src/core/vertex-tracker.ts)
  turns that event stream into an ordered coordinate list, using
  compareDocumentPosition only to place a newly-registered vertex at its
  real document position rather than assuming registration order matches
  DOM order. No querySelectorAll, no MutationObserver, no reading a
  child's property.

Testing the polygon rewrite in an actual browser (not jsdom) surfaced two
real bugs, both fixed:

1. disconnectedCallback fires *after* a node is already detached from its
   parent, so leaflet-line's removal event had nowhere to bubble to.
   Fixed by caching parentNode while still connected and dispatching from
   that cached reference instead of from the (by-then-detached) node.

2. A load-order hazard affecting every "parent listens for a specific
   child tag's announcement" relationship in this codebase:
   customElements.define() upgrades every matching element already
   parsed into the page immediately and synchronously, so whichever tag
   gets defined first wins a race -- a child tag defined before its
   listening parent fires its one-shot connect-time announcement into a
   parent that doesn't exist yet, and it's lost for good. This broke
   polygon/polyline (introduced by this rewrite, since leaflet-line was
   exported before leaflet-polygon) and, independently, was already
   silently broken for leaflet-control-layers (a base/overlay layer
   leaked directly onto the map, bypassing the control entirely) and
   latently for leaflet-layer-group/leaflet-feature-group. src/index.ts's
   export order now encodes and documents the real dependency hierarchy
   (map -> containers that listen for child announcements -> concrete
   layer types -> their own children).

   Added test/load-order.test.ts, which is structurally different from
   every other test file: it never statically imports a component
   module, building the DOM with plain undefined elements first and only
   dynamically import()ing src/index.ts afterward -- reproducing a real
   page's actual load order, which every other test's "define everything
   first" pattern cannot catch. Confirmed it fails against the old
   ordering and passes against the fix.
4 weeks ago
Buddy 255d10499b docs: bring README and CLAUDE.md up to date
README.md:
- Import options: drop CJS/UMD/minified-bundle mentions, none exist since
  the build simplified to ESM-only.
- New Events + TypeScript sections documenting the leaflet:<type> event
  forwarding mechanism and the HTMLElementTagNameMap / per-component
  addEventListener typing added this session.
- leaflet-tile-layer: 6 -> 23 attributes (full tileLayerProps), fixed
  z-index default (0 -> 1).
- leaflet-tile-layer-wms: now documents that it inherits all tile-layer
  attributes (previously listed none, matching the bug fixed earlier) +
  the new crs attribute.
- leaflet-video-overlay: added the 4 attributes added this session.
- leaflet-polyline: fixed a wrong "excludes fill*" claim and added
  smooth-factor/no-clip.
- leaflet-geojson: was documenting a stale hand-picked subset of path
  attributes (with stroke mis-described as a color); now says all
  path-style attributes apply, matching the dedup against pathProps.
- Path style table: added stroke/interactive/bubbling-mouse-events/
  class-name/pane, fixed fill's default.
- Added leaflet-icon/leaflet-div-icon sections (existed but were
  undocumented).
- Nesting rules and Development section brought in line with reality.

CLAUDE.md: the WithProps mixin description referenced an API from before
this session that no longer exists (WithProps(Base, PROPS), definePropAccessors,
initOptions(), updateLeafletObject()) and claimed LeafletMap extends
HTMLElement directly when it extends WithProps(...) like everything else.
Rewrote to match the real internals, fixed the child-component-pattern
steps, and documented how to type a new component's events.

Also added '*.md' to the format script's glob -- root markdown files
weren't covered, so this formatting could have silently drifted again.
4 weeks ago
Buddy 0419ab781d feat: type leaflet-* components and their leaflet: events
Two augmentations, so TypeScript actually knows about the custom elements:

- HTMLElementTagNameMap (src/index.ts): createElement/querySelector now
  infer the exact component class for all 24 tags instead of HTMLElement.
- Per-component addEventListener/removeEventListener overrides, typing
  `leaflet:<name>` events against the real Leaflet event payload
  (PopupEvent, DragEndEvent, LeafletMouseEvent, etc.) while still accepting
  ordinary DOM events normally, and rejecting event names that component
  doesn't fire.

src/core/event-types.ts holds reusable event-name -> payload-type
fragments (mirroring shared-props.ts's fragment reuse), composed per
family: MapEvents, MarkerEvents, PathEvents, TileLayerEvents,
DivOverlayLayerEvents, GroupEvents. LeafletAddEventListener<T>/
LeafletRemoveEventListener<T> in with-props.ts are type-only
intersection-of-overloads helpers applied via `declare addEventListener:
...`, the same pattern every component already uses for `declare readonly
leafletObject?: X` -- zero runtime cost. Deliberately no generic `string`
fallback overload: a fallback would silently accept unrecognized
`leaflet:*` names too, defeating the point.

Fixed a real bug found while building this: #forwardEvents was
dispatching the raw pre-merge `data` Leaflet passes to fire(), missing
type/target/sourceTarget that Leaflet's own fire() merges in before
notifying real .on() listeners. Typing `detail` against Leaflet's actual
event interfaces would have been dishonest otherwise, so the merge now
matches Leaflet's own Evented#fire.

Also added 'line-updated' to the existing internal-event HTMLElementEventMap
augmentation in register.ts (needed once addEventListener got overridden
on polygon/polyline, which use it internally) and cleaned up ~35 now-
redundant `as HTMLElement & {...}` casts across the test suite that the
tag name map makes unnecessary.
4 weeks ago
Buddy 9b6c7ca598 test: add a Vitest + jsdom test suite
No test coverage existed before this. Adds vitest + jsdom (2 new
devDependencies) and a suite that runs entirely without a browser: real
Leaflet objects work fine under jsdom for everything this library needs to
verify (option/attribute wiring, event forwarding), given a ResizeObserver
stub in test/setup.ts (jsdom's only real gap here).

- test/core/with-props.test.ts: the WithProps mixin itself, against a fake
  Leaflet-like class -- attribute<->setter dispatch, get/attribute/default
  fallback order, event-driven attribute sync-back, positional/recreate/
  attach modes, and the generic fire()-patch event forwarding. Child
  registration (popup/tooltip/layer binding) uses real Popup/Tooltip/Marker
  instances since #onChildRegister discriminates by instanceof.
- test/core/props.test.ts: the codec functions in isolation.
- test/components/*.test.ts: grouped smoke tests across all components.
- test/integration.test.ts: full tree wiring (map + tile-layer +
  feature-group + marker + popup).

Caught and fixed one real bug along the way: urlProp's "live url getter"
from the last refactor was dead code -- neither ImageOverlay nor
VideoOverlay actually expose getUrl(). Removed it and the now-pointless
getUrl?() from the Sourced interface in shared-props.ts.

tsconfig.test.json keeps test/ out of the tsc build (dist/ stays
test-free) while still typechecking it; oxlint.config.ts now covers test/
too, with max-classes-per-file relaxed there since testing a class
factory means many small one-off element classes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4 weeks ago
Buddy c339ce1d06 chore: switch oxlint config from .oxlintrc.json to oxlint.config.ts
Same config, just the TS form (defineConfig from 'oxlint'). Verified with
--print-config that it's actually picked up over the JSON default.
4 weeks ago
Buddy 14e35846d5 refactor: complete WithProps mixin, generic event forwarding, and full prop coverage
Finishes the WithProps(PROPS, options) mixin (src/core/with-props.ts): every
component gets reactive attributes, live-value property getters that read
through to the Leaflet object where possible, and every Leaflet event
re-emitted on the element as `leaflet:<type>` via a generic fire() patch (no
per-component or per-event registration needed).

Prop coverage:
- Centralized pathProps in shared-props.ts (stroke, lineCap, lineJoin,
  dashArray, dashOffset, fillRule, interactive, className,
  bubblingMouseEvents, pane), deduped leaflet-geojson against it.
- Added live getters backed by Leaflet's own accessors: radius (circle,
  circle-marker), bounds (rectangle, image/video/svg-overlay), url
  (image/video-overlay).
- Filled gaps: smoothFactor/noClip on polyline; a shared tileLayerProps
  fragment now used by both tile-layer and tile-layer-wms (WMS previously
  exposed none of its base tile options); crs on WMS; zIndex/className/
  keepAspectRatio/errorOverlayUrl on video-overlay; fixed tile-layer's
  zIndex default and div-icon's className default to match Leaflet.

Tooling:
- Removed dead src/core/events.ts (broken, unused, superseded by the
  generic event forwarding above) and src/core/attributes.ts (emptied by
  an earlier rename, nothing imported it).
- Swapped ESLint + @typescript-eslint for oxlint: no released
  @typescript-eslint version supports the pinned typescript@7, even for
  parsing alone. oxlint has its own parser and lints clean.
- Dropped the Rollup CJS/UMD bundle step; dist/ is ESM-only from tsc now.
  Updated package.json's main/module/exports/unpkg accordingly.
- Updated CLAUDE.md to match: build/lint commands, ESM-only output, the
  event-forwarding mechanism, and layer-group/feature-group now going
  through WithProps({}) instead of raw HTMLElement.
4 weeks ago
Buddy bdf5bd56f3 chore: rename core files and move functions
Removed the utils.ts file. The functions in there either should have
been a part of the register.ts or props.ts files. The only functions
left related to attributes so utils.ts was renamed to attributes.ts
3 months ago
Buddy e2382f1117 refactor: enforce strict TypeScript linting rules
Upgrade ESLint config to use flat config with @typescript-eslint/strict-type-checked and stylistic-type-checked presets.

- Use defineConfig helper, scope to .ts files only
- Replace || with ??, convert type to interface, remove redundant type assertions and non-null assertions
- Annotate intentional violations (unbound-method, non-null-assertion) with eslint-disable
- Use querySelectorAll<T> for typed selection, Map over globalThis.Map
- Add @eslint/js dev dependency
3 months ago
Buddy 5b0c3ecdb7 style: format with `npm run format` 3 months ago
Buddy 3854c62896 refactor: move WithProps mixin from utils.ts to props.ts
WithProps is tightly coupled to PropDef types, so it belongs in
props.ts alongside defineProps and the type definitions.

- Move WithProps, definePropAccessors, and Ctor to props.ts
- Import PropTypesFromTable in utils.ts (used by buildOptions)
- Update all 18 component imports to source WithProps from props.ts
3 months ago
Buddy d6f7304313 refactor: use defineProps with str() in icon components
Switch leaflet-icon and leaflet-div-icon from bare { attr: '...' }
objects to defineProps(...) with str() factories, consistent with
all other components.
3 months ago
Buddy 301f223938 refactor: simplify ChildEntry from object to union type
Change ChildEntry from { type: 'layer'|'popup'|'tooltip' } to just
the union string, eliminating the wrapper object. Update Map.set calls
in createChildRegisterHandler and destructuring in leaflet-layer-group,
leaflet-feature-group, and leaflet-geojson disconnectedCallback.
3 months ago
Buddy 2a1b56c6e3 refactor: remove leaflet-request-icon event protocol
The discovery handshake (marker dispatching leaflet-request-icon on
children, icons listening and responding with icon-changed) existed
to handle the edge case where icon definitions register before marker
definitions. Since bundled imports register all components in deterministic
order and deep imports are an advanced use case left to the consumer,
remove the event entirely. icon-changed bubbling alone covers all
practical timing scenarios.

- Remove LeafletRequestIconEvent type and HTMLElementEventMap entry
- Remove #onRequest handler and addEventListener/removeEventListener
  calls from both leaflet-icon and leaflet-div-icon
- Remove child-iteration dispatch loop from leaflet-marker
3 months ago
Buddy 35190bacc5 feat: split leaflet-icon into image Icon and DivIcon, event-only marker↔icon handshake
- Strip leaflet-icon.ts to image-only (new Icon(), remove div/html/bgPos)
- Create leaflet-div-icon.ts with DivIcon, innerHTML content, MutationObserver
- Convert marker↔icon from querySelector to event-only protocol:
  marker iterates children dispatching leaflet-request-icon on each,
  icons listen on themselves and respond with icon-changed
- Extract emitIconChanged() helper into register.ts to eliminate
  repeated CustomEvent construction across both icon components
- Type icon-changed and leaflet-request-icon in HTMLElementEventMap
- Update marker's #onIconChanged to use typed event instead of cast
- Export LeafletDivIcon from index.ts
- Add DivIcon demo markers to custom-icon.html and index.html
3 months ago
Buddy 0855851504 docs: document leaflet-control-layers component and active attribute 3 months ago
Buddy f85e67547d refactor: use events for control-layers layer add/remove instead of DOM traversal
Replace the #map getter (which used closest("leaflet-map") + leafletObject)
with two new custom events: leaflet-add-layer and leaflet-remove-layer.
The map listens for them and delegates to map.addLayer/removeLayer.

This removes the coupling between leaflet-control-layers and the map
element, aligning with the existing leaflet-register event protocol.
3 months ago
Buddy eba6b2e6ad refactor: use type="base" instead of slot="base" for control-layers
slot is a reserved HTML global attribute with shadow-DOM semantics;
using it was misleading even though it happened to work. Switch to
type="base" / type="overlay", which is conventional and carries no
side effects.
3 months ago
Buddy e45162b0be feat: add leaflet-control-layers component
New <leaflet-control-layers> wraps Control.Layers. Children with
slot="base" or slot="overlay" and a name attribute are automatically
registered as base layers or overlays. The control intercepts child
leaflet-register events and stops propagation so layers are managed
by the layers control rather than added directly to the map.

Unchecked layers that connected before the control are removed from
the map before the control initialises, so Control.Layers._addItem
correctly reflects the unchecked state on first render.
3 months ago
Buddy f64b4e70de feat: add leaflet-icon component for custom marker icons
New <leaflet-icon> element wraps L.Icon / L.DivIcon and dispatches
icon-changed custom events to parent markers. Marker listens for
the event and queries for existing icon children on connect.

Fix: use kebab-case attribute names (via PROPS table spec.attr) in
applyIcon() instead of camelCase, and guard against empty iconUrl
to avoid L.icon({}) throwing.
3 months ago
Buddy c283632262 feat: add leafletObject escape hatch + move event wiring
Every component now exposes a uniform get leafletObject() returning
the underlying Leaflet instance (undefined before connected).

Event wiring:
- Marker: listen for 'dragend move' via onChange, syncing lat/lng
  back to attributes. Converted from arrow property to regular
  method using Leaflet's 3rd context arg pattern.
- Circle, CircleMarker: listen for 'move' via onMove with syncing
  guard (same pattern).
- Popup, Tooltip: listen for 'move' via onChange with syncing
  guard and null-check on getLatLng().

Also:
- Update README with escape hatch section
- Change map getter from get map() to get leafletObject() for
  consistency
- index.html: add importmap for leaflet
- All component exports changed from default to named
- index.ts re-exports updated accordingly
3 months ago
Buddy 11cae8c89f style: replace Number/String/parseInt/parseFloat with shorter equivalents
Replace Number(x) with +x, String(x) with template literal,
parseInt(x, 10) with +, and parseFloat(x || "0") with +(x ?? 0).
Fix operator precedence with parentheses around ?? expressions.
3 months ago
Buddy 7ae092ac98 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.
3 months ago
Buddy d8bdc2dcfa refactor: replace any with generics in NumProp and BoolOffProp
Make NumProp and BoolOffProp generic (NumProp<T>, BoolOffProp<T>)
instead of using any, with T inferred from callback parameters at
call sites via the num<T>() and off<T>() factory helpers. Remove
unused num import from leaflet-tile-layer-wms. Fix prettier
formatting issues.
3 months ago
Buddy f95cc3b1f6 refactor: switch leaflet-map to defineProps with factory helpers
Extend num() factory to accept optional extra fields (mapGet, mapSet,
viewState, event). Add off() factory and BoolOffPropInput type. Update
defineProps to derive disable- prefix for bool-off attrs. Remove
~40 lines of local type definitions from leaflet-map.ts.
3 months ago
Buddy 10027c9fe7 refactor: rename withProps to WithProps
Capitalize the mixin factory name since it's used in the extends
position (class Foo extends WithProps(...)). Update CLAUDE.md to
match.
3 months ago
Buddy f615dceea3 refactor: replace PROPS literal tables with factory helpers
Add num(), str(), on() factory functions and defineProps() wrapper
to core/props.ts. Each helper returns a PropDef fragment without
'attr'; defineProps fills it in via camelToKebab(key), with an
optional override for edge cases like playsInline->playsinline.

Input types are derived from their canonical counterparts via
OptionalAttr<T> = Omit<T, 'attr'> & { attr?: string }, keeping
definitions in sync automatically.

Net effect: ~3800 chars removed across 18 component files, no
behavior change, full type inference preserved.
3 months ago
Buddy 96037f22b6 refactor: switch component exports from named to default
All 21 component files export exactly one class. Change to
export default so consumers can import them without named braces.
Update src/index.ts re-exports from "export *" to
"export { default as LeafletXxx }". Fix the two type-only
imports of LeafletLine to use default import syntax.
3 months ago
Buddy 85f0fbe216 refactor: use .ts extension for relative imports in source
Change all import specifiers from ".js" to ".ts" in TypeScript source
files. Add allowImportingTsExtensions and rewriteRelativeImportExtensions
to tsconfig so tsc strips them to ".js" in the dist output — no build
pipeline changes needed. Update CLAUDE.md to reflect the new extension policy.
3 months ago
Buddy edecb44533 docs: update README and CLAUDE.md for current build and architecture
Remove stale JSR reference, add import-options section (formats, deep
imports, leaflet as external dep), fix build command (esbuild -> Rollup).
Rewrite CLAUDE.md architecture to reflect withProps mixin, register.ts
helpers, feature-group, and the full rollup output.
3 months ago
Buddy 5d0d15fba1 refactor: move props.ts from src/types/ to src/core/
The types/ directory now only had a single file. The PropDef and
PropTypesFromTable types are foundational to the module infrastructure
(used by utils.ts, with-props.ts, and every component), so core/ is a
better home. Deleted the empty types/ directory.
3 months ago
Buddy 565b79a89c chore: remove unused exports and dead css.d.ts
- camelCase removed entirely (never imported anywhere)
- PATH_STYLE_ATTRS, ChildEntry, createChildRegisterHandler made private
  (used only internally within their modules)
- src/types/css.d.ts deleted (leftover from an earlier
  approach — no .css imports exist in the source)
3 months ago
Buddy 3ec2d07115 feat: replace esbuild with Rollup, add CJS/UMD bundles and deep import support
Build pipeline now consists of two passes:
  1. tsc --outDir dist  — individual ESM modules for deep imports
  2. rollup -c           — bundled ESM, CJS, and UMD (minified + unminified)

Package.json changes:
  - main/dist/index.cjs (CJS for require())
  - module/dist/index.js (ESM for bundlers)
  - types/dist/index.d.ts (bundled declarations via rollup-plugin-dts)
  - unpkg/dist/index.umd.js (CDN entry)
  - exports map with ./dist/* wildcard for deep imports
  - sideEffects: true since every component calls customElements.define
  - files: [dist, README.md]

tsconfig.json: adds declarationMap: true for Go-to-Definition to source.

Dependencies: removed esbuild, added rollup, @rollup/plugin-typescript,
@rollup/plugin-terser, rollup-plugin-dts, tslib.
3 months ago
Buddy 6906505a53 fix: move leaflet from devDependencies to dependencies
Consumers need leaflet at runtime since it is imported by the
bundled output.
3 months ago
Buddy 18db84e9a4 feat: derive buildOptions return type from PROPS table via Omit<PropTypesFromTable<TProps>, TExclude[number]>
Use a const type parameter on the exclude array so literal tuples narrow
correctly. The return type now reflects the actual properties and their
kinds (number/string/boolean) instead of Record<string, unknown>.

Four call sites needed local casts where PROPS declares str but Leaflet
expects a narrower type (crossOrigin, offset) — these are design-level
mismatches between HTML attribute types and Leaflet's option types.
3 months ago
Buddy 348e865dae docs: add why/how comments to all src/core modules
Explain design rationale for every exported utility, mixin, registration
helper, and path-style handler
3 months ago
Buddy 319b7bdb0c docs: remove vite mentions from README and CLAUDE.md 3 months ago
Buddy c69f9c8503 refactor: extract PROP_BY_ATTR + dynamic setter dispatch into helpers
Adds buildAttrMap(props) and setLayerAttr(obj, props, attrMap,
name, val) to utils.ts. The former replaces the PROP_BY_ATTR Map
construction (2 fewer lines per component), the latter replaces
the 6-line dynamic setter dispatch pattern used as the fallback
else block in 6 components.

For WMS, setLayerAttr returns false when no setter exists, so the
caller can fall back to setParams for non-method props.
3 months ago
Buddy 96b48a6ae2 refactor: extract #parsedBounds into parseBoundsAttr helper
Replaces the identical private #parsedBounds method in rectangle,
image-overlay, video-overlay, and svg-overlay with a call to
parseBoundsAttr(el) from utils.ts. Removes 4 x 5 = 20 lines
of method definitions.
3 months ago
Buddy e2752aa7e0 refactor: extract child-registration boilerplate into WeakMap helpers
Adds registerChildren / unregisterChildren / getChildren to
register.ts using module-scoped WeakMaps, removing the duplicated
#children / #childHandler fields + lifecycle wiring from 15
components.

registerChildren(el, layer) creates the children Map + handler,
stores both in WeakMaps, wires the event listener, and calls
registerWithParent — all in one call. unregisterChildren(el)
tears it down. getChildren(el) gives access to the map for
components that need custom child iteration in disconnectedCallback
(geojson, layer-group, feature-group).

Net: -101 lines, 15 components lose 2-4 fields + 4-8 lifecycle
lines each. Components with extra lifecycle work (marker -> dragend,
polygon/polyline -> observer + syncCoords) keep their custom
additions inline, just the register boilerplate is replaced.
3 months ago
Buddy c18829a0e3 refactor: extract duplicate #num helper into standalone numAttr()
Removes the identical private #num method from 5 components
(leaflet-circle, leaflet-circle-marker, leaflet-marker,
leaflet-popup, leaflet-tooltip). Replaces with numAttr(this, PROPS, name)
call to the new helper in utils.ts.
3 months ago
Buddy 566b8ea43c fix: handle runtime attribute changes for marker title/alt and media props
Marker title/alt now set via getElement() on the icon element.
VideoOverlay loop/autoplay/muted/playsInline set via getElement()
on the video element. ImageOverlay alt set via getElement() on
the img element. These props have no Leaflet set* method but
can be updated through the underlying DOM element.
3 months ago
Buddy 3846cad38e fix: use dragging.enable/disable instead of non-existent setDraggable
Leaflet Marker has no setDraggable method. Toggling dragging at
runtime requires marker.dragging.enable() / .disable(). Adds a
special case for the 'draggable' attribute in the
attributeChangedCallback before the generic setter lookup.
3 months ago
Buddy be84e61ad0 fix: bool-on props broken in generic setter dispatcher
parseAttributeValue('') returns '' (falsy) for boolean-present
attributes, making setDraggable(false), setInteractive(''), etc.
not work. Now looks up the prop spec from PROPS and passes
val !== null (true/false) when kind is 'bool-on'.

Fixes: leaflet-marker, leaflet-tile-layer-wms, leaflet-image-overlay,
leaflet-video-overlay, leaflet-svg-overlay, leaflet-geojson
3 months ago
Buddy 78c39f33a3 fix: sync lat/lng attributes on marker dragend
LeafletMarker now listens for Leaflet's 'dragend' event and writes
the final position back to the lat/lng attributes, using a #syncing
guard to prevent re-entrant setLatLng calls.
3 months ago
Buddy 80c87468b6 refactor: extract withProps mixin to eliminate TypedBase/observedAttributes boilerplate
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.
3 months ago
Buddy 26e110d0b4 feat: group child tags by type in add-child wizard
When adding a child to leaflet-map, the tag select is now grouped
into categories: Tile Layers, Markers, Shapes, Data, Overlays,
Groups, and Controls, using <optgroup> elements.
3 months ago