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.
leaflet-components/README.md

223 lines
7.6 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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
```
Available on JSR as `@buddy/leaflet-components`.
## 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.
---
## Other components
### Tile layers
| Element | Key attributes |
|---|---|
| `leaflet-tile-layer` | `url`, `attribution`, `min-zoom`, `max-zoom`, `opacity`, `z-index` |
| `leaflet-tile-layer-wms` | `url`, `layers`, `styles`, `format`, `transparent`, `version` |
### Vector layers
Path style attributes (`color`, `weight`, `opacity`, `fill`, `fill-color`, `fill-opacity`, `dash-array`, `line-cap`, `line-join`) are shared by all vector layers.
| Element | Additional attributes | Notes |
|---|---|---|
| `leaflet-circle` | `lat`, `lng`, `radius` (meters) | |
| `leaflet-circle-marker` | `lat`, `lng`, `radius` (pixels) | |
| `leaflet-polyline` | — | Vertices via `<leaflet-line>` children |
| `leaflet-polygon` | — | Vertices via `<leaflet-line>` children |
| `leaflet-rectangle` | `bounds` (JSON) | |
| `leaflet-line` | `lat`, `lng` | Vertex only — not rendered directly |
### Markers & overlays
| Element | Key attributes |
|---|---|
| `leaflet-marker` | `lat`, `lng`, `title`, `alt`, `draggable`, `opacity`, `z-index-offset` |
| `leaflet-image-overlay` | `url`, `bounds` (JSON), `opacity`, `alt`, `interactive` |
| `leaflet-video-overlay` | `url`, `bounds` (JSON), `opacity`, `loop`, `autoplay`, `muted` |
| `leaflet-svg-overlay` | `bounds` (JSON), `opacity`, `interactive` — wrap an `<svg>` element |
### Groups & GeoJSON
| Element | Key attributes |
|---|---|
| `leaflet-layer-group` | — |
| `leaflet-feature-group` | — |
| `leaflet-geojson` | `data` (GeoJSON string), plus path style attributes |
### UI layers
Content is set via `innerHTML`, not attributes.
| Element | Key attributes |
|---|---|
| `leaflet-popup` | `lat`, `lng`, `max-width`, `min-width`, `auto-pan`, `close-button`, `auto-close` |
| `leaflet-tooltip` | `lat`, `lng`, `direction`, `permanent`, `sticky`, `opacity` |
### Controls
| Element | Key attributes |
|---|---|
| `leaflet-control-zoom` | `position`, `zoom-in-text`, `zoom-out-text` |
| `leaflet-control-attribution` | `position`, `prefix` |
| `leaflet-control-scale` | `position`, `max-width`, `metric`, `imperial` |
## 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 # type-check + emit .d.ts via tsc, then bundle to dist/index.js via esbuild
npm run lint # ESLint
npm run format # Prettier
npx vite # open index.html for a live demo
```