solid-map-gl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 GIShub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ ![Banner](./docs/banner.png)
2
+
3
+ # GIShub - Solid Mapbox GL JS
4
+
5
+ <!-- [![Node CI](https://github.com/urbica/react-map-gl/workflows/Node%20CI/badge.svg)](https://github.com/urbica/react-map-gl/actions)
6
+ [![codecov](https://codecov.io/gh/urbica/react-map-gl/branch/main/graph/badge.svg)](https://codecov.io/gh/urbica/react-map-gl)
7
+ [![npm](https://img.shields.io/npm/dt/@urbica/react-map-gl.svg?style=popout)](https://www.npmjs.com/package/@urbica/react-map-gl)
8
+ [![npm](https://img.shields.io/npm/v/@urbica/react-map-gl.svg?style=popout)](https://www.npmjs.com/package/@urbica/react-map-gl)
9
+ ![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@urbica/react-map-gl.svg) -->
10
+
11
+ [Solid](https://www.solidjs.com/) Component Library for [Mapbox GL JS.](https://github.com/mapbox/mapbox-gl-js) Mapbox GL JS is a JavaScript library that renders interactive maps from vector tiles and Mapbox styles using WebGL. This project is intended to be as close as possible to the [Mapbox GL JS API.](https://docs.mapbox.com/mapbox-gl-js/api/)
12
+
13
+ - [Installation](#installation)
14
+ - [Components](#components)
15
+ - [Usage](#usage)
16
+ - [Static Map](#static-map)
17
+ - [Interactive Map](#interactive-map)
18
+ - [Map with Source and Layer](#map-with-source-and-layer)
19
+ - [Map with GeoJSON Source](#map-with-geojson-source)
20
+
21
+ <br>
22
+
23
+ ![Gallery](./docs/gallery.png)
24
+
25
+ ## Installation
26
+
27
+ ```shell
28
+ yarn add gishub/solid-map-gl
29
+ ```
30
+
31
+ ```shell
32
+ npm install --save gishub/solid-map-gl
33
+ ```
34
+
35
+ ### Optional Dependencies
36
+
37
+ If you want to use `LanguageControl` and `TrafficControl`:
38
+
39
+ ```shell
40
+ yarn add @mapbox/mapbox-gl-language @mapbox/mapbox-gl-traffic
41
+ ```
42
+
43
+ ```shell
44
+ npm install --save @mapbox/mapbox-gl-language @mapbox/mapbox-gl-traffic
45
+ ```
46
+
47
+ ## Components
48
+
49
+ | Component | Description |
50
+ | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
51
+ | [MapGL](src/components/MapGL) | Represents map on the page |
52
+ | [Source](src/components/Source) | [Sources](https://docs.mapbox.com/mapbox-gl-js/api/#sources) specify the geographic features to be rendered on the map |
53
+ | [Layer](src/components/Layer) | [Layers](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers) specify the `Sources` style |
54
+ | [Sky](src/components/Sky) | Specify the Sky Layer |
55
+ | [Fog](src/components/Fog) | Specify the Fog Layer |
56
+ | [Terrain](src/components/Terrain) | Specify the Terrain Layer |
57
+ | [Image](src/components/Image) | Adds an image to the map style |
58
+ | [Popup](src/components/Popup) | Component for [Mapbox GL JS Popup](https://docs.mapbox.com/mapbox-gl-js/api/#popup) |
59
+ | [Marker](src/components/Marker) | Component for [Mapbox GL JS Marker](https://docs.mapbox.com/mapbox-gl-js/api/#marker) |
60
+ | [Control](src/components/Control) | Represents the map's control |
61
+
62
+ ## Usage
63
+
64
+ To use any of Mapbox’s tools, APIs, or SDKs, you’ll need a Mapbox [access token](https://www.mapbox.com/help/define-access-token/). Mapbox uses access tokens to associate requests to API resources with your account. You can find all your access tokens, create new ones, or delete existing ones on your [API access tokens page](https://www.mapbox.com/studio/account/tokens/).
65
+
66
+ ### Static Map
67
+
68
+ By default, `MapGL` component renders in a static mode. That means that the user cannot interact with the map.
69
+
70
+ ```jsx
71
+ import MapGL from '../solid-map-gl'
72
+
73
+ <MapGL
74
+ options={{
75
+ accessToken: { MAPBOX_ACCESS_TOKEN },
76
+ style: 'mapbox://styles/mapbox/light-v10',
77
+ }}
78
+ viewport={{
79
+ center: [-122.41, 37.78]
80
+ zoom: 11,
81
+ }}>
82
+ </MapGL>
83
+ ```
84
+
85
+ ### Interactive Map
86
+
87
+ In most cases, you will want the user to interact with the map. To do this, you need to provide `onViewportChange` handler, that will update the map's viewport state.
88
+
89
+ ```jsx
90
+ import { createSignal } from 'solid-js'
91
+ import MapGL, { Viewport } from '../solid-map-gl'
92
+
93
+ const [viewport, setViewport] = createSignal({
94
+ center: [-122.41, 37.78]
95
+ zoom: 11,
96
+ } as Viewport)
97
+
98
+ <MapGL
99
+ options={{
100
+ accessToken: { MAPBOX_ACCESS_TOKEN },
101
+ style: 'mapbox://styles/mapbox/light-v10',
102
+ }}
103
+ viewport={viewport()}
104
+ onViewportChange={evt => setViewport(evt)}>
105
+ </MapGL>
106
+ ```
107
+
108
+ ### Map with Source and Layer
109
+
110
+ [Sources](https://docs.mapbox.com/mapbox-gl-js/api/sources/) specify the geographic features to be rendered on the map.
111
+
112
+ [Layers](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/) specify the Sources styles. The type of layer is specified by the `"type"` property in the layers style object, and must be one of `background`, `fill`, `line`, `symbol`, `raster`, `circle`, `fill-extrusion`, `heatmap`, `hillshade`.
113
+
114
+ Except for layers of the `background` type, each layer needs to refer to a source. Either buy referencing the `id` in `sourceId` or by nesting the layer within the source component. Layers take the data that they get from a source, optionally filter features, and then define how those features are styled.
115
+
116
+ ```jsx
117
+ import MapGL, { Source, Layer } from '../solid-map-gl'
118
+
119
+ <MapGL
120
+ options={{
121
+ accessToken: { MAPBOX_ACCESS_TOKEN },
122
+ style: 'mapbox://styles/mapbox/light-v10',
123
+ }}>
124
+ <Source
125
+ source={{
126
+ type: 'vector',
127
+ url: 'mapbox://mapbox.mapbox-terrain-v2'
128
+ }}>
129
+ <Layer style={{
130
+ type: 'line',
131
+ 'source-layer': 'contour'
132
+ paint: {
133
+ 'line-color': '#877b59',
134
+ 'line-width': 1,
135
+ }
136
+ }} />
137
+ </Source>
138
+ </MapGL>
139
+ ```
140
+
141
+ ### Map with GeoJSON Source
142
+
143
+ To draw a GeoJSON on a map, add `Source` with the `type` property set to `geojson` and `data` property set to a URL or inline [GeoJSON](http://geojson.org/).
144
+
145
+ ```jsx
146
+ import { createSignal } from 'solid-js'
147
+ import MapGL, { Source, Layer } from '../solid-map-gl'
148
+
149
+ const [viewport, setViewport] = createSignal({
150
+ center: [-122.486052, 37.830348],
151
+ zoom: 15,
152
+ })
153
+
154
+ const data = {
155
+ type: 'Feature',
156
+ geometry: {
157
+ type: 'LineString',
158
+ coordinates: [
159
+ [-122.48369693756104, 37.83381888486939],
160
+ [-122.48348236083984, 37.83317489144141],
161
+ [-122.48339653015138, 37.83270036637107],
162
+ [-122.48356819152832, 37.832056363179625],
163
+ [-122.48404026031496, 37.83114119107971],
164
+ [-122.48404026031496, 37.83049717427869],
165
+ [-122.48348236083984, 37.829920943955045],
166
+ [-122.48356819152832, 37.82954808664175],
167
+ [-122.48507022857666, 37.82944639795659],
168
+ [-122.48610019683838, 37.82880236636284],
169
+ [-122.48695850372314, 37.82931081282506],
170
+ [-122.48700141906738, 37.83080223556934],
171
+ [-122.48751640319824, 37.83168351665737],
172
+ [-122.48803138732912, 37.832158048267786],
173
+ [-122.48888969421387, 37.83297152392784],
174
+ [-122.48987674713133, 37.83263257682617],
175
+ [-122.49043464660643, 37.832937629287755],
176
+ [-122.49125003814696, 37.832429207817725],
177
+ [-122.49163627624512, 37.832564787218985],
178
+ [-122.49223709106445, 37.83337825839438],
179
+ [-122.49378204345702, 37.83368330777276],
180
+ ],
181
+ },
182
+ }
183
+
184
+ <MapGL
185
+ options={{
186
+ accessToken: { MAPBOX_ACCESS_TOKEN },
187
+ style: 'mapbox://styles/mapbox/light-v10',
188
+ }}
189
+ viewport={viewport()}
190
+ onViewportChange={evt => setViewport(evt)}>
191
+ <Source
192
+ source={{
193
+ type: 'geojson',
194
+ data: {data}
195
+ }}>
196
+ <Layer style={{
197
+ type: 'line',
198
+ layout: {
199
+ 'line-join': 'round',
200
+ 'line-cap': 'round',
201
+ }
202
+ paint: {
203
+ 'line-color': '#888',
204
+ 'line-width': 8,
205
+ }
206
+ }} />
207
+ </Source>
208
+ </MapGL>
209
+ ```
210
+
211
+ ### Custom Layers support
212
+
213
+ [Custom layers](https://docs.mapbox.com/mapbox-gl-js/api/properties/#customlayerinterface) allow a user to render directly into the Map's GL context using the map's camera.
214
+
215
+ Here is an example using [deck.gl.](https://github.com/uber/deck.gl)
216
+
217
+ ```jsx
218
+ import { createSignal } from 'solid-js'
219
+ import MapGL, { Source, Layer } from '../solid-map-gl'
220
+ import { MapboxLayer } from '@deck.gl/mapbox'
221
+ import { ScatterplotLayer } from '@deck.gl/layers'
222
+
223
+ const [viewport, setViewport] = createSignal({
224
+ center: [-74.5, 40],
225
+ zoom: 9,
226
+ })
227
+
228
+ const myDeckLayer = new MapboxLayer({
229
+ id: 'my-scatterplot',
230
+ type: ScatterplotLayer,
231
+ data: [{ position: [-74.5, 40], size: 1000 }],
232
+ getPosition: d => d.position,
233
+ getRadius: d => d.size,
234
+ getColor: [255, 0, 0],
235
+ })
236
+
237
+ <MapGL
238
+ options={{
239
+ accessToken: { MAPBOX_ACCESS_TOKEN },
240
+ style: 'mapbox://styles/mapbox/light-v10',
241
+ }}
242
+ viewport={viewport()}
243
+ onViewportChange={evt => setViewport(evt)}>
244
+ <Layer customLayer={myDeckLayer} />
245
+ </MapGL>
246
+ ```
247
+
248
+ ## Changelog
249
+
250
+ - 1.0.0 - Initial release
@@ -0,0 +1,386 @@
1
+ import { template, createComponent, insert, effect, classList } from 'solid-js/web';
2
+ import { useTransition, createContext, onCleanup, onMount, createEffect, useContext, createUniqueId, createSignal } from 'solid-js';
3
+ import mapboxgl from 'mapbox-gl';
4
+
5
+ const _tmpl$$2 = template(`<main></main>`, 2);
6
+ const [pending, start] = useTransition();
7
+ const MapContext = createContext();
8
+
9
+ const useMap = () => useContext(MapContext);
10
+
11
+ const MapGL = props => {
12
+ let map;
13
+ let mapRef;
14
+ onCleanup(() => map.remove());
15
+ onMount(() => {
16
+ map = new mapboxgl.Map({ ...props.options,
17
+ container: mapRef,
18
+ bounds: props.viewport.bounds,
19
+ center: props.viewport.center,
20
+ zoom: props.viewport.zoom || null,
21
+ pitch: props.viewport.pitch || null,
22
+ bearing: props.viewport.bearing || null,
23
+ fitBoundsOptions: {
24
+ padding: props.viewport.padding
25
+ }
26
+ });
27
+ });
28
+ createEffect(() => props.onMouseMove && map.on('mousemove', event => props.onMouseMove(event.lngLat)));
29
+
30
+ const _onViewportChange = () => {
31
+ const viewport = {
32
+ center: map.getCenter(),
33
+ zoom: map.getZoom(),
34
+ pitch: map.getPitch(),
35
+ bearing: map.getBearing(),
36
+ padding: props.viewport.padding
37
+ };
38
+ props.onViewportChange(viewport);
39
+ };
40
+
41
+ createEffect(() => props.onViewportChange && map.on('dragend', _onViewportChange).on('moveend', _onViewportChange).on('zoomend', _onViewportChange).on('rotateend', _onViewportChange).on('pitchend', _onViewportChange).on('boxzoomend', _onViewportChange));
42
+ createEffect(async () => {
43
+ if (!props.viewport.bounds) return; // await map.once('idle')
44
+
45
+ map.fitBounds(props.viewport.bounds, {
46
+ padding: props.viewport.padding
47
+ });
48
+ });
49
+ let index = 0;
50
+ createEffect(() => {
51
+ if (props.triggerResize) {
52
+ index = 0;
53
+ window.requestAnimationFrame(function loop() {
54
+ if (index < 15) {
55
+ index++;
56
+ window.requestAnimationFrame(loop);
57
+ start(() => map.resize());
58
+ }
59
+ });
60
+ }
61
+ });
62
+ return createComponent(MapContext.Provider, {
63
+ value: () => map,
64
+
65
+ get children() {
66
+ const _el$ = _tmpl$$2.cloneNode(true);
67
+
68
+ const _ref$ = mapRef;
69
+ typeof _ref$ === "function" ? _ref$(_el$) : mapRef = _el$;
70
+
71
+ _el$.style.setProperty("height", "100%");
72
+
73
+ _el$.style.setProperty("width", "100%");
74
+
75
+ insert(_el$, () => props.children);
76
+
77
+ effect(_p$ => {
78
+ const _v$ = props.class || '',
79
+ _v$2 = props.classList;
80
+
81
+ _v$ !== _p$._v$ && (_el$.className = _p$._v$ = _v$);
82
+ _p$._v$2 = classList(_el$, _v$2, _p$._v$2);
83
+ return _p$;
84
+ }, {
85
+ _v$: undefined,
86
+ _v$2: undefined
87
+ });
88
+
89
+ return _el$;
90
+ }
91
+
92
+ });
93
+ };
94
+
95
+ const SourceContext = createContext();
96
+
97
+ const useSource = () => useContext(SourceContext);
98
+
99
+ const SourceComponent = props => {
100
+ const map = useMap();
101
+ props.id = props.id || createUniqueId(); // Add Source
102
+
103
+ onMount(async () => {
104
+ !map().isStyleLoaded() && (await map().once('styledata'));
105
+ !map().getSource(props.id) && map().addSource(props.id, props.source);
106
+ }); // Remove Source
107
+
108
+ onCleanup(() => {
109
+ map().getStyle().layers.forEach(layer => layer.source === props.id && map().removeLayer(layer.id));
110
+ map().getSource(props.id) && map().removeSource(props.id);
111
+ }); // Update GeoJSON Data
112
+
113
+ createEffect(async () => {
114
+ if (props.source.type !== 'geojson' && !props.source.data) return;
115
+ map().getSource(props.id) && map().getSource(props.id).setData(props.source.data);
116
+ });
117
+ return createComponent(SourceContext.Provider, {
118
+ get value() {
119
+ return {
120
+ source: props.id
121
+ };
122
+ },
123
+
124
+ get children() {
125
+ return props.children;
126
+ }
127
+
128
+ });
129
+ };
130
+
131
+ const diff = (newProps = {}, prevProps = {}) => {
132
+ const keys = new Set([...Object.keys(newProps), ...Object.keys(prevProps)]);
133
+ return [...keys].reduce((acc, key) => {
134
+ const value = newProps[key];
135
+
136
+ if (value !== prevProps[key]) {
137
+ acc.push([key, value]);
138
+ }
139
+
140
+ return acc;
141
+ }, []);
142
+ };
143
+
144
+ const Layer = props => {
145
+ const map = useMap();
146
+ const source = useSource();
147
+ props.id = props.id || createUniqueId(); // Add Layer
148
+
149
+ onMount(async () => {
150
+ !map().isStyleLoaded() && (await map().once('styledata'));
151
+ if (map().getLayer(props.id)) return;
152
+ map().addLayer(props.layer || { ...props.style,
153
+ id: props.id,
154
+ source: source.source
155
+ }, props.beforeType ? map().getStyle().layers.find(l => l.type === props.beforeType).id : props.beforeId);
156
+ }); // Update Style
157
+
158
+ createEffect(prev => {
159
+ if (!props.style && map().getLayer(props.id)) return;
160
+ diff(props.style.paint, prev.paint).forEach(([key, value]) => map().setPaintProperty(props.id, key, value));
161
+ diff(props.style.layout, prev.layout).forEach(([key, value]) => map().setLayoutProperty(props.id, key, value));
162
+ if (props.style.minzoom !== prev.minzoom || props.style.maxzoom !== prev.max) map().setLayerZoomRange(props.id, props.style.minzoom, props.style.maxzoom);
163
+ return props.style;
164
+ }, props.style); // Update Visibility
165
+
166
+ createEffect(async () => {
167
+ if (!props.visible) return;
168
+ !map().loaded() && (await map().once('styledata'));
169
+ map().setLayoutProperty(props.id, 'visibility', props.visible ? 'visible' : 'none', {
170
+ validate: false
171
+ });
172
+ }); // Update Filter
173
+
174
+ createEffect(async () => {
175
+ if (!props.filter) return;
176
+ !map().loaded() && (await map().once('styledata'));
177
+ map().setFilter(props.id, props.filter);
178
+ }); // Update Feature State
179
+
180
+ createEffect(async () => {
181
+ if (!props.featureState?.id) return;
182
+ map().loaded() ? null : await map().once('render');
183
+ map().removeFeatureState({
184
+ source: source.source,
185
+ sourceLayer: props.style['source-layer']
186
+ });
187
+ map().setFeatureState({
188
+ source: source.source,
189
+ sourceLayer: props.style['source-layer'],
190
+ id: props.featureState.id
191
+ }, props.featureState.state);
192
+ });
193
+ return props.children;
194
+ };
195
+
196
+ const Fog = props => {
197
+ const map = useMap(); // Add Fog Layer
198
+
199
+ onMount(async () => {
200
+ !map().isStyleLoaded() && (await map().once('styledata'));
201
+ map().setFog(props.style);
202
+ }); // Remove Fog Layer
203
+
204
+ onCleanup(() => map().setFog(null)); // Update Visibility
205
+
206
+ createEffect(async () => {
207
+ if (props.visible === undefined) return;
208
+ !map().isStyleLoaded() && (await map().once('styledata'));
209
+ map().setFog(props.visible ? props.style : null);
210
+ });
211
+ return props.children;
212
+ };
213
+
214
+ const Sky = props => {
215
+ const map = useMap();
216
+ const layerId = '_sky'; // Add Sky Layer
217
+
218
+ onMount(async () => {
219
+ !map().isStyleLoaded() && (await map().once('styledata'));
220
+ !map().getLayer(layerId) && map().addLayer({ ...props.style,
221
+ id: layerId
222
+ });
223
+ }); // Remove Sky Layer
224
+
225
+ onCleanup(() => map().getLayer(layerId) && map().removeLayer(layerId)); // Update Visibility
226
+
227
+ createEffect(async () => {
228
+ if (props.visible === undefined) return;
229
+ !map().isStyleLoaded() && (await map().once('styledata'));
230
+ map().setLayoutProperty(layerId, 'visibility', props.visible ? 'visible' : 'none');
231
+ });
232
+ return props.children;
233
+ };
234
+
235
+ const Terrain = props => {
236
+ const map = useMap();
237
+ const source = useSource(); // Add Terrain Layer
238
+
239
+ onMount(async () => {
240
+ !map().isStyleLoaded() && (await map().once('styledata'));
241
+ map().setTerrain({ ...props.style,
242
+ source: source.source
243
+ });
244
+ }); // Remove Terrain Layer
245
+
246
+ onCleanup(() => map().setTerrain(null)); // Update Visibility
247
+
248
+ createEffect(async () => {
249
+ if (props.visible === undefined) return;
250
+ !map().isStyleLoaded() && (await map().once('styledata'));
251
+ map().setTerrain(props.visible ? props.style : null);
252
+ });
253
+ return props.children;
254
+ };
255
+
256
+ const Control = props => {
257
+ const map = useMap();
258
+ const [control, setControl] = createSignal(null); // Add Control
259
+
260
+ onMount(async () => {
261
+ let control;
262
+
263
+ switch (props.type) {
264
+ case 'scale':
265
+ control = new mapboxgl.ScaleControl(props.options);
266
+ break;
267
+
268
+ case 'attribution':
269
+ control = new mapboxgl.AttributionControl(props.options);
270
+ break;
271
+
272
+ case 'fullscreen':
273
+ control = new mapboxgl.FullscreenControl(props.options);
274
+ break;
275
+
276
+ case 'geolocate':
277
+ control = new mapboxgl.GeolocateControl(props.options);
278
+ break;
279
+
280
+ default:
281
+ control = new mapboxgl.NavigationControl(props.options);
282
+ // case 'language':
283
+ // const MapboxLanguage = (await import('@mapbox/mapbox-gl-language'))
284
+ // .default
285
+ // control = new MapboxLanguage(props.options)
286
+ // break
287
+ // case 'traffic':
288
+ // const MapboxTraffic = (await import('@mapbox/mapbox-gl-traffic'))
289
+ // .default
290
+ // await import('@mapbox/mapbox-gl-traffic/mapbox-gl-traffic.css')
291
+ // control = new MapboxTraffic(props.options)
292
+ // break
293
+ }
294
+
295
+ map().addControl(control, props.position);
296
+ setControl(control);
297
+ });
298
+ onCleanup(() => map().removeControl(control()));
299
+ return props.children;
300
+ };
301
+
302
+ const _tmpl$$1 = template(`<div></div>`, 2);
303
+
304
+ const Marker = props => {
305
+ const map = useMap();
306
+ let marker = null; // Add Marker
307
+
308
+ onMount(async () => {
309
+ marker = new mapboxgl.Marker(props.options).setLngLat(props.lnglat).setPopup(props.children ? new mapboxgl.Popup().setDOMContent((() => {
310
+ const _el$ = _tmpl$$1.cloneNode(true);
311
+
312
+ insert(_el$, () => props.children);
313
+
314
+ return _el$;
315
+ })()) : null).addTo(map());
316
+ }); // Remove Marker
317
+
318
+ onCleanup(() => marker.remove()); // Update Position
319
+
320
+ createEffect(() => marker.setLngLat(props.lnglat));
321
+ return [];
322
+ };
323
+
324
+ const _tmpl$ = template(`<div></div>`, 2);
325
+
326
+ const Popup = props => {
327
+ const map = useMap();
328
+ let popup = null; // Add Popup
329
+
330
+ onMount(async () => {
331
+ popup = new mapboxgl.Popup(props.options).setLngLat(props.lnglat).setDOMContent((() => {
332
+ const _el$ = _tmpl$.cloneNode(true);
333
+
334
+ insert(_el$, () => props.children);
335
+
336
+ return _el$;
337
+ })()).addTo(map());
338
+ }); // Remove Popup
339
+
340
+ onCleanup(() => popup.remove()); // Update Position
341
+
342
+ createEffect(() => popup.setLngLat(props.lnglat)); // Update Content
343
+
344
+ createEffect(() => popup.setDOMContent((() => {
345
+ const _el$2 = _tmpl$.cloneNode(true);
346
+
347
+ insert(_el$2, () => props.children);
348
+
349
+ return _el$2;
350
+ })()));
351
+ return [];
352
+ };
353
+
354
+ const Image = props => {
355
+ const map = useMap(); // Add Image
356
+
357
+ onMount(() => {
358
+ if (props.url) {
359
+ map().loadImage(props.url, (error, image) => {
360
+ if (error) throw error;
361
+ !map().hasImage(props.id) && map().addImage(props.id, image);
362
+ });
363
+ } else {
364
+ !map().hasImage(props.id) && map().addImage(props.id, props.image, props.options);
365
+ }
366
+ }); // Remove Image
367
+
368
+ onCleanup(() => map().hasImage(props.id) && map().removeImage(props.id)); // Update Image
369
+
370
+ createEffect(async () => {
371
+ if (props.image && props.url) return;
372
+
373
+ if (props.url) {
374
+ map().loadImage(props.url, (error, image) => {
375
+ if (error) throw error;
376
+ !map().hasImage(props.id) && map().updateImage(props.id, image);
377
+ });
378
+ } else {
379
+ map().hasImage(props.id) && map().updateImage(props.id, props.image);
380
+ }
381
+ });
382
+ return [];
383
+ };
384
+
385
+ export { Control, Fog, Image, Layer, Marker, Popup, Sky, SourceComponent as Source, Terrain, MapGL as default };
386
+ //# sourceMappingURL=index.js.map