|
|
# leaflet-components
|
|
|
|
|
|
[Leaflet.js](https://leafletjs.com/) as native Web Components (Custom Elements). Each HTML element maps 1:1 to a Leaflet object with reactive attribute binding.
|
|
|
|
|
|
## Installation
|
|
|
|
|
|
```bash
|
|
|
npm install leaflet-components
|
|
|
```
|
|
|
|
|
|
## Import options
|
|
|
|
|
|
The package ships multiple bundle formats and supports deep imports for tree-shaking.
|
|
|
|
|
|
```js
|
|
|
// Default ESM bundle (recommended) — registers all components
|
|
|
import 'leaflet-components';
|
|
|
|
|
|
// Minified ESM bundle
|
|
|
import 'leaflet-components/dist/index.min.js';
|
|
|
|
|
|
// CommonJS
|
|
|
const lc = require('leaflet-components');
|
|
|
|
|
|
// Deep import — register only one component
|
|
|
import 'leaflet-components/dist/components/leaflet-marker.js';
|
|
|
|
|
|
// UMD (for script tags, expects Leaflet as window.L)
|
|
|
// <script src="node_modules/leaflet-components/dist/index.umd.js"></script>
|
|
|
```
|
|
|
|
|
|
`leaflet` is always an external dependency — you must install it yourself.
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
Import once to register all custom elements, then use them declaratively in HTML.
|
|
|
|
|
|
```html
|
|
|
<script type="module">
|
|
|
import 'leaflet-components';
|
|
|
</script>
|
|
|
|
|
|
<leaflet-map lat="51.505" lng="-0.09" zoom="13" style="height:400px">
|
|
|
<leaflet-tile-layer
|
|
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
|
attribution="© OpenStreetMap contributors"
|
|
|
></leaflet-tile-layer>
|
|
|
|
|
|
<leaflet-marker lat="51.505" lng="-0.09">
|
|
|
<leaflet-popup><b>Hello!</b></leaflet-popup>
|
|
|
<leaflet-tooltip>Hover me</leaflet-tooltip>
|
|
|
</leaflet-marker>
|
|
|
|
|
|
<leaflet-circle lat="51.508" lng="-0.11" radius="500" color="red" fill-color="#f03" fill-opacity="0.5">
|
|
|
<leaflet-popup>I am a circle</leaflet-popup>
|
|
|
</leaflet-circle>
|
|
|
|
|
|
<leaflet-polygon color="blue">
|
|
|
<leaflet-line lat="51.509" lng="-0.08"></leaflet-line>
|
|
|
<leaflet-line lat="51.503" lng="-0.06"></leaflet-line>
|
|
|
<leaflet-line lat="51.510" lng="-0.047"></leaflet-line>
|
|
|
</leaflet-polygon>
|
|
|
|
|
|
<leaflet-control-scale position="bottomleft"></leaflet-control-scale>
|
|
|
</leaflet-map>
|
|
|
```
|
|
|
|
|
|
## `leaflet-map`
|
|
|
|
|
|
The root component. All other components must be children of `<leaflet-map>`.
|
|
|
|
|
|
```html
|
|
|
<leaflet-map lat="51.505" lng="-0.09" zoom="13" disable-scroll-wheel-zoom>
|
|
|
```
|
|
|
|
|
|
### View state
|
|
|
|
|
|
These attributes stay in sync with the map as the user interacts with it — panning updates `lat`/`lng`, zooming updates `zoom`.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Center latitude |
|
|
|
| `lng` | `0` | Center longitude |
|
|
|
| `zoom` | `2` | Zoom level |
|
|
|
| `min-zoom` | `0` | Minimum zoom level |
|
|
|
| `max-zoom` | — | Maximum zoom level. When unset, Leaflet uses the tile layer's own max zoom. |
|
|
|
|
|
|
### Interaction
|
|
|
|
|
|
Boolean options that default to `true` are controlled by the presence of a `disable-*` attribute. Handler-based options can be toggled at any time; the rest only apply at construction.
|
|
|
|
|
|
| Attribute | Controls | Live? |
|
|
|
|---|---|---|
|
|
|
| `disable-dragging` | Mouse/touch panning | ✓ |
|
|
|
| `disable-scroll-wheel-zoom` | Scroll-wheel zoom | ✓ |
|
|
|
| `disable-double-click-zoom` | Double-click zoom | ✓ |
|
|
|
| `disable-touch-zoom` | Pinch-to-zoom | ✓ |
|
|
|
| `disable-box-zoom` | Shift-drag zoom box | ✓ |
|
|
|
| `disable-keyboard` | Keyboard pan/zoom | ✓ |
|
|
|
| `disable-zoom-control` | Built-in zoom control | — |
|
|
|
| `disable-attribution-control` | Built-in attribution | — |
|
|
|
| `disable-close-popup-on-click` | Close popup on map click | — |
|
|
|
| `disable-track-resize` | Auto-resize on window resize | — |
|
|
|
| `disable-bounce-at-zoom-limits` | Bounce animation at min/max zoom | — |
|
|
|
| `disable-tap-hold` | Long-press context menu (mobile) | — |
|
|
|
|
|
|
Boolean options that default to `false` are enabled by adding the attribute:
|
|
|
|
|
|
| Attribute | Description |
|
|
|
|---|---|
|
|
|
| `prefer-canvas` | Render vector layers on Canvas instead of SVG |
|
|
|
| `world-copy-jump` | Pan to the original world copy when crossing the antimeridian |
|
|
|
|
|
|
### Animation
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `disable-zoom-animation` | — | Disable CSS zoom animation |
|
|
|
| `disable-fade-animation` | — | Disable tile fade-in |
|
|
|
| `disable-marker-zoom-animation` | — | Disable marker zoom animation |
|
|
|
| `zoom-animation-threshold` | `4` | Max zoom delta for animated zoom |
|
|
|
|
|
|
### Inertia & panning
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `disable-inertia` | — | Disable inertial panning |
|
|
|
| `inertia-deceleration` | `3000` | Deceleration rate (px/s²) |
|
|
|
| `inertia-max-speed` | `Infinity` | Maximum inertia speed (px/s) |
|
|
|
| `ease-linearity` | `0.2` | Pan easing linearity |
|
|
|
|
|
|
### Zoom behaviour
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `zoom-snap` | `1` | Zoom snapping interval; `0` for continuous zoom |
|
|
|
| `zoom-delta` | `1` | Zoom step per keyboard/button press |
|
|
|
| `max-bounds-viscosity` | `0` | How much the bounds resist panning past them (`0`–`1`) |
|
|
|
|
|
|
### Scroll wheel
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `wheel-debounce-time` | `40` | Debounce delay for wheel events (ms) |
|
|
|
| `wheel-px-per-zoom-level` | `60` | Pixels of scroll per zoom level |
|
|
|
|
|
|
### Keyboard & touch
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `keyboard-pan-delta` | `80` | Pan distance per key press (px) |
|
|
|
| `tap-tolerance` | `15` | Max touch movement to trigger a tap (px) |
|
|
|
|
|
|
### Rendering
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `transform-3d-limit` | `8388608` | Max CSS `translate3d` component value before a layer reset |
|
|
|
|
|
|
### Accessing the underlying map
|
|
|
|
|
|
```js
|
|
|
const el = document.querySelector('leaflet-map');
|
|
|
el.map; // L.Map instance (undefined before connected)
|
|
|
el.zoom; // current zoom (reads live from map, falls back to attribute)
|
|
|
el.scrollWheelZoom = false; // disable at runtime
|
|
|
```
|
|
|
|
|
|
All `leaflet-map` properties are two-way: reading returns the live map value; writing updates both the attribute and the map.
|
|
|
|
|
|
### CSS
|
|
|
|
|
|
Leaflet's stylesheet is loaded from a CDN via a `<link>` in the shadow DOM. Marker icon paths are derived from the CSS URL automatically.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `css-url` | `https://unpkg.com/leaflet@1.9.4/dist/leaflet.css` | URL for Leaflet CSS |
|
|
|
| `css-integrity` | `sha256-...` | SRI hash (required with default URL; when `css-url` is custom, no default integrity is used unless explicitly set) |
|
|
|
| `css-crossorigin` | _(auto)_ | `"anonymous"` when integrity is active, otherwise absent. Set explicitly to override. |
|
|
|
|
|
|
### Resize
|
|
|
|
|
|
A `ResizeObserver` on the host element automatically calls `map.invalidateSize()` whenever the element's dimensions change — from CSS classes, inline styles, attribute changes, or parent layout.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Components
|
|
|
|
|
|
### `<leaflet-tile-layer>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `url` | `''` | Tile URL template (`https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`) |
|
|
|
| `attribution` | `''` | Attribution text |
|
|
|
| `min-zoom` | `0` | Minimum zoom level |
|
|
|
| `max-zoom` | `18` | Maximum zoom level |
|
|
|
| `opacity` | `1.0` | Tile layer opacity |
|
|
|
| `z-index` | `0` | Z-index |
|
|
|
|
|
|
### `<leaflet-tile-layer-wms>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `url` | `''` | WMS service URL |
|
|
|
| `layers` | `''` | Comma-separated layer names |
|
|
|
| `styles` | `''` | Comma-separated style names |
|
|
|
| `format` | `'image/jpeg'` | Image format |
|
|
|
| `transparent` | — | Request transparent tiles |
|
|
|
| `version` | `'1.1.1'` | WMS version |
|
|
|
| `uppercase` | — | Use uppercase WMS parameter names |
|
|
|
|
|
|
### `<leaflet-marker>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Latitude |
|
|
|
| `lng` | `0` | Longitude |
|
|
|
| `title` | `''` | Tooltip text on hover |
|
|
|
| `alt` | `''` | Alt text for the marker image |
|
|
|
| `draggable` | — | Allow dragging the marker |
|
|
|
| `opacity` | `1.0` | Marker opacity |
|
|
|
| `z-index-offset` | `0` | Z-index offset |
|
|
|
|
|
|
### `<leaflet-circle>`
|
|
|
|
|
|
All [path style](#path-style) attributes apply.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Center latitude |
|
|
|
| `lng` | `0` | Center longitude |
|
|
|
| `radius` | `1000` | Radius in meters |
|
|
|
|
|
|
### `<leaflet-circle-marker>`
|
|
|
|
|
|
All [path style](#path-style) attributes apply.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Center latitude |
|
|
|
| `lng` | `0` | Center longitude |
|
|
|
| `radius` | `10` | Radius in pixels |
|
|
|
|
|
|
### `<leaflet-polyline>`
|
|
|
|
|
|
Coordinates are taken from child `<leaflet-line>` elements, not from attributes. All [path style](#path-style) attributes apply (excluding `fill*` — polylines don't fill).
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `color` | `'#3388ff'` | Stroke color |
|
|
|
| `weight` | `3` | Stroke width (px) |
|
|
|
| `opacity` | `1.0` | Stroke opacity |
|
|
|
|
|
|
### `<leaflet-polygon>`
|
|
|
|
|
|
Coordinates are taken from child `<leaflet-line>` elements, not from attributes. All [path style](#path-style) attributes apply.
|
|
|
|
|
|
### `<leaflet-rectangle>`
|
|
|
|
|
|
All [path style](#path-style) attributes apply.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
|
|
|
|
|
|
### `<leaflet-line>`
|
|
|
|
|
|
Vertex helper — not rendered directly. Used as a child of `<leaflet-polyline>` or `<leaflet-polygon>`.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Latitude |
|
|
|
| `lng` | `0` | Longitude |
|
|
|
|
|
|
### `<leaflet-image-overlay>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `url` | `''` | Image URL |
|
|
|
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
|
|
|
| `opacity` | `1.0` | Overlay opacity |
|
|
|
| `alt` | `''` | Alt text |
|
|
|
| `interactive` | — | Receive mouse/touch events |
|
|
|
| `cross-origin` | `''` | CORS setting for the image |
|
|
|
| `error-overlay-url` | `''` | Fallback image on load error |
|
|
|
| `z-index` | `0` | Z-index |
|
|
|
| `class-name` | `''` | CSS class for the image element |
|
|
|
|
|
|
### `<leaflet-video-overlay>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `url` | `''` | Video URL |
|
|
|
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
|
|
|
| `opacity` | `1.0` | Overlay opacity |
|
|
|
| `alt` | `''` | Alt text |
|
|
|
| `interactive` | — | Receive mouse/touch events |
|
|
|
| `cross-origin` | `''` | CORS setting for the video |
|
|
|
| `loop` | — | Loop playback |
|
|
|
| `autoplay` | — | Start playing automatically |
|
|
|
| `muted` | — | Mute audio |
|
|
|
| `playsinline` | — | Play inline (mobile) |
|
|
|
|
|
|
### `<leaflet-svg-overlay>`
|
|
|
|
|
|
Content comes from an inline `<svg>` child element. No `url` attribute.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
|
|
|
| `opacity` | `1.0` | Overlay opacity |
|
|
|
| `interactive` | — | Receive mouse/touch events |
|
|
|
| `cross-origin` | `''` | CORS setting |
|
|
|
| `z-index` | `0` | Z-index |
|
|
|
| `class-name` | `''` | CSS class for the SVG element |
|
|
|
|
|
|
### `<leaflet-layer-group>`
|
|
|
|
|
|
Passthrough container. No attributes. Accepts layers, popups, and tooltips as children.
|
|
|
|
|
|
### `<leaflet-feature-group>`
|
|
|
|
|
|
Passthrough container with Leaflet's `FeatureGroup` (supports `getBounds()`, style propagation, etc.). No attributes. Accepts layers, popups, and tooltips as children.
|
|
|
|
|
|
### `<leaflet-geojson>`
|
|
|
|
|
|
All [path style](#path-style) attributes apply.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `data` | `''` | GeoJSON string |
|
|
|
| `stroke` | `''` | Stroke color (overrides `color`) |
|
|
|
| `line-cap` | `'round'` | Line cap style |
|
|
|
| `line-join` | `'round'` | Line join style |
|
|
|
| `dash-array` | `''` | Dash pattern |
|
|
|
| `dash-offset` | `''` | Dash offset |
|
|
|
| `fill-rule` | `'evenodd'` | Fill rule |
|
|
|
|
|
|
### `<leaflet-popup>`
|
|
|
|
|
|
Content comes from `innerHTML`, not an attribute. Mutations to innerHTML sync automatically via a `MutationObserver`.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Latitude (omit to auto-attach to the parent layer's position) |
|
|
|
| `lng` | `0` | Longitude |
|
|
|
| `max-width` | `300` | Maximum width (px) |
|
|
|
| `min-width` | `50` | Minimum width (px) |
|
|
|
| `max-height` | `0` | Maximum height (px; `0` = unlimited) |
|
|
|
| `auto-pan` | — | Automatically pan the map to keep the popup visible |
|
|
|
| `close-button` | — | Show a close button |
|
|
|
| `auto-close` | — | Close the popup when another popup is opened |
|
|
|
|
|
|
### `<leaflet-tooltip>`
|
|
|
|
|
|
Content comes from `innerHTML`, not an attribute.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `lat` | `0` | Latitude (omit to auto-attach to the parent layer's position) |
|
|
|
| `lng` | `0` | Longitude |
|
|
|
| `pane` | — | Map pane name |
|
|
|
| `offset` | — | Pixel offset as JSON: `[x, y]` |
|
|
|
| `direction` | `'auto'` | Direction: `'right'`, `'left'`, `'top'`, `'bottom'`, `'center'`, `'auto'` |
|
|
|
| `permanent` | — | Always visible (no hover required) |
|
|
|
| `sticky` | — | Follow the mouse |
|
|
|
| `opacity` | `1.0` | Tooltip opacity |
|
|
|
|
|
|
### `<leaflet-control-zoom>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `position` | `'topleft'` | Corner position |
|
|
|
| `zoom-in-text` | `'+'` | Zoom-in button label |
|
|
|
| `zoom-in-title` | `'Zoom in'` | Zoom-in button tooltip |
|
|
|
| `zoom-out-text` | `'-'` | Zoom-out button label |
|
|
|
| `zoom-out-title` | `'Zoom out'` | Zoom-out button tooltip |
|
|
|
|
|
|
### `<leaflet-control-attribution>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `position` | `'bottomright'` | Corner position |
|
|
|
| `prefix` | `''` | Text before the attribution |
|
|
|
|
|
|
### `<leaflet-control-scale>`
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `position` | `'bottomleft'` | Corner position |
|
|
|
| `max-width` | `100` | Maximum width of the scale (px) |
|
|
|
| `metric` | — | Show metric scale (m/km) |
|
|
|
| `imperial` | — | Show imperial scale (mi/ft) |
|
|
|
| `update-when-idle` | — | Update only when the map stops moving |
|
|
|
|
|
|
## Path style
|
|
|
|
|
|
The following attributes apply to `<leaflet-circle>`, `<leaflet-circle-marker>`, `<leaflet-polyline>`, `<leaflet-polygon>`, `<leaflet-rectangle>`, and `<leaflet-geojson>`.
|
|
|
|
|
|
| Attribute | Default | Description |
|
|
|
|---|---|---|
|
|
|
| `color` | `'#3388ff'` | Stroke color |
|
|
|
| `weight` | `3` | Stroke width (px) |
|
|
|
| `opacity` | `1.0` | Stroke opacity |
|
|
|
| `line-cap` | `'round'` | Line cap: `'butt'`, `'round'`, `'square'` |
|
|
|
| `line-join` | `'round'` | Line join: `'miter'`, `'round'`, `'bevel'` |
|
|
|
| `dash-array` | `''` | Dash pattern, e.g. `'5, 10'` |
|
|
|
| `dash-offset` | `''` | Dash offset |
|
|
|
| `fill` | — | Enable fill |
|
|
|
| `fill-color` | `'#3388ff'` | Fill color |
|
|
|
| `fill-opacity` | `0.2` | Fill opacity |
|
|
|
| `fill-rule` | `'evenodd'` | Fill rule: `'nonzero'`, `'evenodd'` |
|
|
|
|
|
|
## Nesting rules
|
|
|
|
|
|
- **Popup / tooltip as child of a layer** → bound via `bindPopup` / `bindTooltip`.
|
|
|
- **Layer as child of a group** → added via `addLayer`.
|
|
|
- **Any layer as child of `leaflet-map`** → added to the map directly.
|
|
|
- **`leaflet-polygon` / `leaflet-polyline`** take their coordinates from `<leaflet-line>` children, not from attributes.
|
|
|
|
|
|
## Development
|
|
|
|
|
|
```bash
|
|
|
npm run build # tsc emits individual ESM modules; Rollup bundles ESM, CJS + UMD (minified/unminified)
|
|
|
npm run typecheck # tsc --noEmit
|
|
|
npm run lint # ESLint
|
|
|
npm run format # Prettier
|
|
|
# serve the project root with any static-file server and open index.html
|
|
|
```
|