Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions packages/docs/components/MapboxMap/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: MapboxMap examples

# Examples

Every example below registers a single `MapboxMap` component — the child components (markers, popups, controls, sources, layers, images, clusters) are resolved automatically once the map is loaded. Each example loads the [Mapbox GL stylesheet](./#installation) from a CDN and picks a Mapbox style through the `map-options` option, which forwards any [`Map` option](https://docs.mapbox.com/mapbox-gl-js/api/map/#map-parameters) to mapbox-gl. Replace the access token with your own [access token](https://docs.mapbox.com/help/getting-started/access-tokens/); the token used here is a public, restricted demo token.
Every component in the family is self-registering, so each example registers exactly the components it uses — `MapboxMap` on its own for a bare map, plus the child components it declares (markers, popups, controls, sources, layers, images, clusters). Each child resolves its parent `MapboxMap` on its own once mounted. Each example loads the [Mapbox GL stylesheet](./#installation) from a CDN and picks a Mapbox style through the `map-options` option, which forwards any [`Map` option](https://docs.mapbox.com/mapbox-gl-js/api/map/#map-parameters) to mapbox-gl. Replace the access token with your own [access token](https://docs.mapbox.com/help/getting-started/access-tokens/); the token used here is a public, restricted demo token.

Child components that should not take part in the normal document flow (markers, popups, controls, sources, layers, images, clusters) are wrapped in a `hidden` element: their markup is only used as a declarative definition, the actual rendering happens on the map canvas.

Expand Down Expand Up @@ -102,38 +102,39 @@ Register images against the map sprite so they can be referenced from a symbol l

## Cluster

The `MapboxCluster` component displays a clustered GeoJSON source, wiring the cluster circles, the cluster count labels and the unclustered points, together with the click-to-zoom interaction. Its `data` option takes the URL of a `.geojson` file, or you can pass inline GeoJSON through a [`geojson` script ref](#inline-geojson-cluster). The example below passes the points inline; click a cluster to zoom in and split it.
The `MapboxCluster` component is a clustered GeoJSON **source driver**: it derives its source from the `MapboxClusterItem`s in its subtree, wiring the cluster circles, the cluster count labels and the unclustered points, together with the click-to-zoom interaction. Each item is at once a list entry and a map feature — the same markup drives both. The example below declares a hidden list of items; click a cluster to zoom in and split it.

<PreviewPlayground
:html="() => import('./stories/cluster/app.twig')"
:script="() => import('./stories/cluster/app.js?raw')"
:css="() => import('./stories/cluster/app.css?raw')"
/>

### Inline GeoJSON cluster {#inline-geojson-cluster}
### Items drive the source {#cluster-items}

Instead of a `data` URL, pass the GeoJSON inline through a `<script data-ref="geojson" type="application/json">` child. When the ref is present, its parsed content is used as the clustered source data and the `data` option is ignored:
The cluster owns no authored data. Each point is a `MapboxClusterItem` that self-registers with the closest `MapboxCluster` ancestor; the cluster derives a clustered GeoJSON `FeatureCollection` from the registry and rebuilds it (debounced) whenever items mount or unmount:

```html
<div
hidden
data-component="MapboxCluster"
data-option-cluster-radius="60"
data-option-clusters-paint='{ "circle-color": "#51bbd6", "circle-radius": 20 }'
data-option-unclustered-point-paint='{ "circle-color": "#11b4da", "circle-radius": 5 }'>
<script data-ref="geojson" type="application/json">
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [2.35, 48.86] } },
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [2.29, 48.86] } },
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-0.13, 51.51] } }
]
}
</script>
<div hidden data-component="MapboxCluster" data-option-cluster-radius="60">
<ul>
<li
data-component="MapboxClusterItem"
data-option-id="a"
data-option-lng-lat="[2.35, 48.86]"></li>
<li
data-component="MapboxClusterItem"
data-option-id="b"
data-option-lng-lat="[2.29, 48.86]"></li>
<li
data-component="MapboxClusterItem"
data-option-id="c"
data-option-lng-lat="[-0.13, 51.51]"></li>
</ul>
</div>
```

The cluster reports a click on an unclustered point through its `item-click` event but never selects or flies on its own. To turn this into a full "find a store near you" experience — selection, popups, viewport filtering and address search — wrap the cluster in a [`StoreLocator`](/components/StoreLocator/) orchestrator.

## Listening to map events

The `MapboxMap` component re-emits the Mapbox map events. Listen to them from a parent component by defining `on<ComponentName><EventName>` methods — for example `onMapboxMapClick` or `onMapboxMapMapLoad` for the custom `map-load` event.
Expand Down
24 changes: 12 additions & 12 deletions packages/docs/components/MapboxMap/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ badges: [JS]

# MapboxMap <Badges :texts="$frontmatter.badges" />

The `MapboxMap` component and its family let you build [Mapbox GL](https://docs.mapbox.com/mapbox-gl-js/) maps declaratively, straight from your HTML, with [js-toolkit](https://js-toolkit.studiometa.dev/) — no Vue, no framework runtime. A `MapboxMap` element owns the underlying Mapbox `Map` instance, and every other component (markers, popups, controls, sources, layers, images, clusters) is authored as a child element that registers itself against that map once it is loaded.
The `MapboxMap` component and its family let you build [Mapbox GL](https://docs.mapbox.com/mapbox-gl-js/) maps declaratively, straight from your HTML, with [js-toolkit](https://js-toolkit.studiometa.dev/). A `MapboxMap` element owns the underlying Mapbox `Map` instance, and every other component (markers, popups, controls, sources, layers, images, clusters) is authored as a child element that registers itself against that map once it is loaded.

These components are published in the standalone [`@studiometa/ui-mapbox`](https://www.npmjs.com/package/@studiometa/ui-mapbox) package and replace the [`@studiometa/vue-mapbox-gl`](https://www.npmjs.com/package/@studiometa/vue-mapbox-gl) library. If you are coming from the Vue library, read the [migration guide](/migration-guides/vue-mapbox-gl/).

Expand Down Expand Up @@ -39,7 +39,7 @@ You will also need a [Mapbox access token](https://docs.mapbox.com/help/getting-

## Usage

Register the `MapboxMap` component with [`registerComponent`](https://js-toolkit.studiometa.dev/api/helpers/registerComponent.html). All child components are declared internally, so you only ever register `MapboxMap` itself — they are resolved automatically once the map is loaded.
[Register](/guide/usage/#registering-components) each component your page uses: a bare map with just a `container` needs only `MapboxMap`, and each marker, popup, control, source, layer, image or cluster you declare must be registered too. Registration order does not matter, because a child registered before its `MapboxMap` still wires up once the map connects.

Author the map with a root `MapboxMap` element holding a `container` ref, and give it a size through CSS.

Expand Down Expand Up @@ -69,22 +69,22 @@ registerComponent(MapboxMap);

:::

::: tip Options are read once at mount
Component options are read a single time when the map mounts and are **not** reactive, unlike the Vue library. To move the map or update its data afterwards, call the underlying Mapbox objects directly (e.g. `instance.map.setCenter(...)`). See the [reactivity note](./js-api#reactivity-and-updates) for details.
:::
`mapbox-gl` is a heavy dependency (~230&nbsp;kB gzipped, more with the geocoder), so the recommended default is to register each component lazily — see [Lazy loading](#lazy-loading) below.

## Lazy loading

`mapbox-gl` is a heavy dependency (~230&nbsp;kB gzipped, more with the geocoder). Registering `MapboxMap` eagerly pulls it into your main bundle even on pages that never render a map. Prefer **lazy registration** so the map is code-split into its own chunk and only loaded when it is actually needed.
Keep `mapbox-gl` out of your main bundle by lazy-registering each component with js-toolkit's [`importWhen*` helpers](https://js-toolkit.studiometa.dev/api/helpers/importWhenVisible.html) and the per-component subpaths. `importWhenVisible` defers the dynamic import until a `MapboxMap` element scrolls into view, then hands the resolved component to `registerComponent`, code-splitting `mapbox-gl` into its own chunk loaded only when a map is actually needed.

js-toolkit ships [`importWhen*` helpers](https://js-toolkit.studiometa.dev/api/helpers/importWhenVisible.html) for this. `importWhenVisible` defers the dynamic import until a `MapboxMap` element scrolls into view, then hands the resolved component to `registerComponent`:
Every component is available at its own subpath (`@studiometa/ui-mapbox/<Component>`), whose default export is the component class — so the dynamic import needs no destructuring. Because each component is registered independently, lazy-register each one you use: deferring `MapboxMap` pulls in `mapbox-gl`, but a marker or a cluster is its own module and must get its own lazy registration.

```js
import { registerComponent, importWhenVisible } from '@studiometa/js-toolkit';
import { registerComponents, importWhenVisible } from '@studiometa/js-toolkit';

registerComponent(importWhenVisible(() => import('@studiometa/ui-mapbox/MapboxMap'), 'MapboxMap'));
registerComponents(
importWhenVisible(() => import('@studiometa/ui-mapbox/MapboxMap'), 'MapboxMap'),
importWhenVisible(() => import('@studiometa/ui-mapbox/MapboxMarker'), 'MapboxMarker'),
importWhenVisible(() => import('@studiometa/ui-mapbox/MapboxPopup'), 'MapboxPopup'),
);
```

Every component is also available at its own subpath (`@studiometa/ui-mapbox/<Component>`), whose default export is the component class — so the dynamic import needs no destructuring.

Because every marker, popup, control, source, layer, image and cluster is a child of `MapboxMap`, deferring the map defers the whole family — `mapbox-gl` included. Reach for a different trigger when it fits better: `importWhenIdle` (load during browser idle time), `importOnInteraction` (wait for a first click/focus/touch on the element) or `importOnMediaQuery` (load only above a breakpoint, e.g. to skip the map on small screens).
Reach for a different trigger when it fits better: `importWhenIdle` (load during browser idle time), `importOnInteraction` (wait for a first click/focus/touch on the element) or `importOnMediaQuery` (load only above a breakpoint, e.g. to skip the map on small screens).
Loading
Loading