solid-map-gl 1.5.3 → 1.6.1

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.
@@ -1,34 +1,36 @@
1
- import { createEffect, onCleanup, createSignal } from 'solid-js';
1
+ import { createEffect, onCleanup } from 'solid-js';
2
2
  import { useMap } from '../MapGL';
3
+ const getControl = (lib, type, options, custom) => {
4
+ if (custom)
5
+ return new custom(options);
6
+ switch (type) {
7
+ case 'navigation':
8
+ return new lib.NavigationControl(options);
9
+ case 'scale':
10
+ return new lib.ScaleControl(options);
11
+ case 'attribution':
12
+ return new lib.AttributionControl(options);
13
+ case 'geolocate':
14
+ return new lib.GeolocateControl(options);
15
+ case 'fullscreen':
16
+ return new lib.FullscreenControl();
17
+ // options || { container: map().container }
18
+ default:
19
+ throw new Error(`Unknown control type: ${type}`);
20
+ }
21
+ };
3
22
  export const Control = props => {
4
- const map = useMap();
5
- const [control, setControl] = createSignal(null);
6
- // Add Control
7
- createEffect(async () => {
8
- const mapLib = map().mapLib;
9
- const getControl = (type, options) => {
10
- if (props.custom)
11
- return new props.custom(options);
12
- switch (type) {
13
- case 'navigation':
14
- return new mapLib.NavigationControl(options);
15
- case 'scale':
16
- return new mapLib.ScaleControl(options);
17
- case 'attribution':
18
- return new mapLib.AttributionControl(options);
19
- case 'geolocate':
20
- return new mapLib.GeolocateControl(options);
21
- case 'fullscreen':
22
- return new mapLib.FullscreenControl();
23
- // options || { container: map().container }
24
- default:
25
- throw new Error(`Unknown control type: ${type}`);
26
- }
27
- };
28
- const control = getControl(props.type, props.options);
29
- control && map()?.addControl(control, props.position);
30
- setControl(control);
23
+ if (!useMap())
24
+ return;
25
+ const [map] = useMap();
26
+ let control = null;
27
+ // Add or Update Control
28
+ createEffect(() => {
29
+ control && map().hasControl(control) && map().removeControl(control);
30
+ control = getControl(map().mapLib, props.type, props.options, props.custom);
31
+ map().addControl(control, props.position);
31
32
  });
32
- onCleanup(() => map()?.removeControl(control()));
33
- return props.children;
33
+ // Remove Control
34
+ onCleanup(() => map()?.removeControl(control));
35
+ return null;
34
36
  };
@@ -19,10 +19,12 @@ const PATTERN = {
19
19
  };
20
20
  export const patternList = Object.keys(PATTERN);
21
21
  export const Image = props => {
22
- const map = useMap()();
22
+ if (!useMap())
23
+ return;
24
+ const [map] = useMap();
23
25
  const [size, setSize] = createSignal({ width: 0, height: 0 });
24
26
  // Remove Image
25
- onCleanup(() => map && map.hasImage(props.id) && map.removeImage(props.id));
27
+ onCleanup(() => map()?.hasImage(props.id) && map()?.removeImage(props.id));
26
28
  // Add or Update Image
27
29
  createEffect(() => {
28
30
  if (!props.id)
@@ -34,16 +36,16 @@ export const Image = props => {
34
36
  : props.options;
35
37
  _loadImage(props.image || _createPattern(props.pattern), data => {
36
38
  const { width, height } = data;
37
- if (!map.hasImage(props.id))
38
- map.addImage(props.id, data, ops);
39
+ if (!map().hasImage(props.id))
40
+ map().addImage(props.id, data, ops);
39
41
  if (!props.pattern &&
40
42
  untrack(() => width === size().width && height === size().height)) {
41
- map.updateImage(props.id, data);
42
- map.triggerRepaint();
43
+ map().updateImage(props.id, data);
44
+ map().triggerRepaint();
43
45
  }
44
46
  else {
45
- map.removeImage(props.id);
46
- map.addImage(props.id, data, ops);
47
+ map().removeImage(props.id);
48
+ map().addImage(props.id, data, ops);
47
49
  }
48
50
  setSize({ width, height });
49
51
  });
@@ -52,7 +54,7 @@ export const Image = props => {
52
54
  const _loadImage = (image, callback) => {
53
55
  if (typeof image !== 'string')
54
56
  return callback(image);
55
- map.loadImage(image, (error, imageData) => {
57
+ map().loadImage(image, (error, imageData) => {
56
58
  if (error)
57
59
  throw error;
58
60
  return callback(imageData);
@@ -13,46 +13,64 @@ const diff = (newProps = {}, prevProps = {}) => {
13
13
  }, []);
14
14
  };
15
15
  export const Layer = props => {
16
- const map = useMap();
17
- const sourceId = useSourceId();
16
+ if (!useMap())
17
+ return;
18
+ const [map] = useMap();
19
+ const sourceId = props.style.source || useSourceId();
18
20
  props.id = props.id || createUniqueId();
19
- //Remove Layer
20
- onCleanup(() => map && map().getLayer(props.id) && map().removeLayer(props.id));
21
+ const debug = (text, value) => {
22
+ map().debug &&
23
+ console.debug('%c[MapGL]', 'color: #10b981', text, value || '');
24
+ };
25
+ // Add Layer
26
+ // if (!map().getLayer(props.id)) {
27
+ map().addLayer(props.customLayer || {
28
+ ...props.style,
29
+ id: props.id,
30
+ source: sourceId,
31
+ metadata: {
32
+ smg: { beforeType: props.beforeType, beforeId: props.beforeId },
33
+ },
34
+ }, props.beforeType
35
+ ? map()
36
+ .getStyle()
37
+ .layers.find(l => l.type === props.beforeType)?.id
38
+ : props.beforeId);
39
+ map().layerIdList.push(props.id);
40
+ debug('Add Layer:', props.id);
21
41
  // Hook up events
22
- createEffect(() => layerEvents.forEach(item => {
42
+ layerEvents.forEach(item => {
23
43
  if (props[item]) {
24
44
  const event = item.slice(2).toLowerCase();
25
- const callback = e => props[item](e);
26
- map()?.on(event, props.id, callback);
27
- onCleanup(() => map()?.off(event, props.id, callback));
45
+ map().on(event, props.id, evt => {
46
+ props[item](evt);
47
+ debug(`Layer '${event}' event on '${props.id}':`, evt);
48
+ });
28
49
  }
29
- }));
50
+ });
51
+ // }
30
52
  // Update Style
31
53
  createEffect((prev) => {
32
- if (!props.style ||
33
- !map().style ||
34
- !map().getSource(sourceId) ||
35
- !map().getLayer(props.id))
54
+ const style = props.style;
55
+ if (style === prev || !map().isStyleLoaded())
36
56
  return;
37
- if (props.style.layout !== prev?.layout)
38
- diff(props.style.layout, prev?.layout).forEach(([key, value]) => map().setLayoutProperty(props.id, key, value, { validate: false }));
39
- if (props.style.paint !== prev?.paint)
40
- diff(props.style.paint, prev?.paint).forEach(([key, value]) => map().setPaintProperty(props.id, key, value, { validate: false }));
41
- if (props.style.minzoom !== prev?.minzoom ||
42
- props.style.maxzoom !== prev?.maxzoom)
43
- map().setLayerZoomRange(props.id, props.style.minzoom, props.style.maxzoom);
44
- if (props.style.filter !== prev?.filter)
45
- map().setFilter(props.id, props.style.filter, { validate: false });
46
- return props.style;
57
+ if (style.layout !== prev?.layout)
58
+ diff(style.layout, prev?.layout).forEach(([key, value]) => map().setLayoutProperty(props.id, key, value, { validate: false }));
59
+ if (style.paint !== prev?.paint)
60
+ diff(style.paint, prev?.paint).forEach(([key, value]) => map().setPaintProperty(props.id, key, value, { validate: false }));
61
+ if (style.minzoom !== prev?.minzoom || style.maxzoom !== prev?.maxzoom)
62
+ map().setLayerZoomRange(props.id, style.minzoom, style.maxzoom);
63
+ if (style.filter !== prev?.filter)
64
+ map().setFilter(props.id, style.filter, { validate: false });
65
+ debug('Update Layer Style:', props.id);
66
+ return style;
47
67
  }, props.style);
48
68
  // Update Visibility
49
69
  createEffect((prev) => {
50
- if (props.visible === undefined ||
51
- props.visible === prev ||
52
- !map().getSource(sourceId) ||
53
- !map().getLayer(props.id))
70
+ if (props.visible === prev)
54
71
  return;
55
72
  map().setLayoutProperty(props.id, 'visibility', props.visible ? 'visible' : 'none', { validate: false });
73
+ debug(`Update Visibility (${props.id}):`, props.visible);
56
74
  return props.visible;
57
75
  }, props.visible);
58
76
  // Update Filter
@@ -61,6 +79,7 @@ export const Layer = props => {
61
79
  return;
62
80
  !map().isStyleLoaded() && (await map().once('styledata'));
63
81
  map().setFilter(props.id, props.filter);
82
+ debug(`Update Filter (${props.id}):`, props.filter);
64
83
  });
65
84
  // Update Feature State
66
85
  createEffect(async () => {
@@ -77,21 +96,7 @@ export const Layer = props => {
77
96
  id: props.featureState.id,
78
97
  }, props.featureState.state);
79
98
  });
80
- // Add Layer
81
- createEffect(() => {
82
- !map().getLayer(props.id) &&
83
- map().addLayer(props.customLayer || {
84
- ...props.style,
85
- id: props.id,
86
- source: sourceId,
87
- metadata: {
88
- smg: { beforeType: props.beforeType, beforeId: props.beforeId },
89
- },
90
- }, props.beforeType
91
- ? map()
92
- ?.getStyle()
93
- .layers.find(l => l.type === props.beforeType)?.id
94
- : props.beforeId);
95
- });
99
+ //Remove Layer
100
+ onCleanup(() => map()?.getLayer(props.id) && map()?.removeLayer(props.id));
96
101
  return props.children;
97
102
  };
@@ -0,0 +1,12 @@
1
+ import { onCleanup, createEffect } from 'solid-js';
2
+ import { useMap } from '../MapGL';
3
+ export const Light = props => {
4
+ if (!useMap())
5
+ return;
6
+ const [map] = useMap();
7
+ // Add or Update Light Layer
8
+ createEffect(() => map().setLight(props.style || {}));
9
+ // Remove Light Layer
10
+ onCleanup(() => map()?.setLight(null));
11
+ return null;
12
+ };
@@ -1,4 +1,4 @@
1
- import { createSignal, createEffect, onMount, onCleanup, createContext, useContext, createUniqueId, untrack, } from 'solid-js';
1
+ import { createSignal, createEffect, onMount, onCleanup, createContext, useContext, createUniqueId, } from 'solid-js';
2
2
  import { mapEvents } from '../../events';
3
3
  import { vectorStyleList } from '../../mapStyles';
4
4
  const MapContext = createContext();
@@ -7,12 +7,17 @@ export const useMap = () => useContext(MapContext);
7
7
  /** Creates a new Map Container */
8
8
  export const MapGL = props => {
9
9
  props.id = props.id || createUniqueId();
10
+ let map;
11
+ let resizeObserver;
10
12
  const [mapRoot, setMapRoot] = createSignal();
11
- const [transitionType, setTransitionType] = createSignal('flyTo');
12
- const [darkMode, setDarkMode] = createSignal(typeof window !== 'undefined' &&
13
- (window.matchMedia('(prefers-color-scheme: dark)').matches ||
14
- document.body.classList.contains('dark')));
15
- const debug = (text, value) => props.debug && console.debug(`${text}: %c${value}`, 'color: #00F');
13
+ const [darkMode, setDarkMode] = createSignal(false);
14
+ const [userInteraction, setUserInteraction] = createSignal(false);
15
+ // debounce user interactions
16
+ createEffect(() => props.onUserInteraction?.(userInteraction()));
17
+ const debug = (text, value) => {
18
+ props.debug &&
19
+ console.debug('%c[MapGL]', 'color: #0ea5e9', text, value || '');
20
+ };
16
21
  const mapRef = (<div id={props.id} class={props?.class} classList={props?.classList} style={props?.style || props?.class || props?.classList
17
22
  ? props.style
18
23
  : { position: 'absolute', inset: 0, ...props.style }}/>);
@@ -31,168 +36,183 @@ export const MapGL = props => {
31
36
  let mapLib = props.mapLib || (await import('mapbox-gl'));
32
37
  if (!mapLib.Map)
33
38
  mapLib = window['maplibregl'] || window['mapboxgl'];
34
- const map = new mapLib.Map({
39
+ if (!mapLib.supported())
40
+ throw new Error('Mapbox GL not supported');
41
+ debug(`Map (v${mapLib.version}) loading...`);
42
+ map = new mapLib.Map({
35
43
  accessToken:
36
44
  //@ts-ignore
37
- import.meta.env.VITE_MAPBOX_ACCESS_TOKEN,
38
- interactive: !!props.onViewportChange,
45
+ props.options.accessToken || import.meta.env.VITE_MAPBOX_ACCESS_TOKEN,
46
+ interactive: props.options.interactive || !!props.onViewportChange,
39
47
  ...props.options,
40
- style: getStyle(props.options?.style, props.darkStyle),
48
+ ...props.viewport,
41
49
  container: mapRef,
42
- bounds: props.viewport?.bounds,
43
- center: props.viewport?.center,
44
- zoom: props.viewport?.zoom || null,
45
- pitch: props.viewport?.pitch || null,
46
- bearing: props.viewport?.bearing || null,
50
+ style: getStyle(props.options?.style, props.darkStyle),
47
51
  fitBoundsOptions: { padding: props.viewport?.padding },
48
52
  });
49
53
  map.mapLib = mapLib;
50
54
  map.debug = props.debug;
51
- // map.container = containerRef
52
- map.once('load', () => setMapRoot(map));
53
- onCleanup(() => map?.remove());
54
- // Listen to map container size changes
55
- const resizeObserver = new ResizeObserver(() => map.resize());
56
- createEffect(() => props.disableResize
57
- ? resizeObserver.disconnect()
58
- : resizeObserver.observe(mapRef));
55
+ map.sourceIdList = [];
56
+ map.layerIdList = [];
57
+ map.once('load', () => {
58
+ setMapRoot(map);
59
+ debug('Map loaded');
60
+ });
61
+ ['mousedown', 'touchstart', 'wheel'].forEach(event => map.on(event, () => setUserInteraction(true)));
62
+ map.on('moveend', evt => !evt.rotate && setUserInteraction(false));
59
63
  // Listen to dark theme changes
60
- const darkTheme = window.matchMedia('(prefers-color-scheme: dark)');
61
- darkTheme.addEventListener('change', () => setDarkMode(darkTheme.matches));
62
- const bodyClassObserver = new MutationObserver(() => setDarkMode(document.body.classList.contains('dark')));
63
- bodyClassObserver.observe(document.body, { attributes: true });
64
+ const darkTheme = typeof window !== 'undefined' &&
65
+ window.matchMedia('(prefers-color-scheme: dark)');
66
+ darkTheme?.addEventListener('change', () => {
67
+ setDarkMode(darkTheme.matches);
68
+ debug('Set dark theme to:', darkTheme.matches?.toString());
69
+ });
70
+ new MutationObserver(() => {
71
+ const darkTheme = document.body.classList.contains('dark');
72
+ setDarkMode(darkTheme);
73
+ debug('Set dark theme to:', darkTheme);
74
+ }).observe(document.body, { attributes: true });
75
+ // Listen to map container size changes
76
+ // !props.disableResize &&
77
+ // new ResizeObserver(() => {
78
+ // map?.resize()
79
+ // debug('Map resized')
80
+ // }).observe(mapRef as Element)
64
81
  // Hook up events
65
- createEffect(() => mapEvents.forEach(item => {
82
+ mapEvents.forEach(item => {
66
83
  const prop = props[item];
67
84
  if (prop) {
68
85
  const event = item.slice(2).toLowerCase();
69
86
  if (typeof prop === 'function') {
70
- const callback = e => prop(e);
71
- map.on(event, callback);
72
- onCleanup(() => map?.off(event, callback));
87
+ map.on(event, e => {
88
+ prop(e);
89
+ debug(`Map '${event}' event:`, e);
90
+ });
73
91
  }
74
92
  else {
75
93
  Object.keys(prop).forEach(layerId => {
76
- const callback = e => prop[layerId](e);
77
- map.on(event, layerId, callback);
78
- onCleanup(() => map?.off(event, layerId, callback));
94
+ map.on(event, layerId, e => {
95
+ prop[layerId](e);
96
+ debug(`Map '${event}' event on '${layerId}':`, e);
97
+ });
79
98
  });
80
99
  }
81
100
  }
82
- }));
83
- // Update debug features
84
- createEffect(() => {
85
- map.showTileBoundaries = props.showTileBoundaries;
86
- map.showTerrainWireframe = props.showTerrainWireframe;
87
- map.showPadding = props.showPadding;
88
- map.showCollisionBoxes = props.showCollisionBoxes;
89
- map.showOverdrawInspector = props.showOverdrawInspector;
90
101
  });
91
- // Update cursor
92
- createEffect(prev => {
93
- if (props.cursorStyle === prev)
94
- return;
95
- debug('Update Cursor to', props.cursorStyle);
96
- map.getCanvas().style.cursor = props.cursorStyle;
97
- return props.cursorStyle;
98
- });
99
- //Update transition type
100
- createEffect(prev => {
101
- if (props.transitionType === prev)
102
- return;
103
- debug('Update Transition to', props.transitionType);
104
- setTransitionType(props.transitionType);
105
- return props.transitionType;
102
+ // Hook up viewport events
103
+ map.on('move', event => {
104
+ const viewport = {
105
+ ...props.viewport,
106
+ id: props.id,
107
+ point: { x: event.originalEvent?.x, y: event.originalEvent?.y },
108
+ center: props.viewport.center.lat
109
+ ? map.getCenter()
110
+ : [map.getCenter().lng, map.getCenter().lat],
111
+ zoom: map.getZoom(),
112
+ pitch: map.getPitch(),
113
+ bearing: map.getBearing(),
114
+ };
115
+ props.onViewportChange && props.onViewportChange(viewport);
106
116
  });
107
- // Update projection
108
- createEffect(prev => {
109
- if (props.options?.projection === prev)
110
- return;
111
- debug('Update Projection to', props.options?.projection.name);
112
- map.setProjection(props.options?.projection);
113
- return props.options?.projection;
117
+ });
118
+ // Update Viewport
119
+ createEffect(() => {
120
+ const viewport = {
121
+ ...props.viewport,
122
+ padding: props.viewport?.padding || 0,
123
+ };
124
+ if (!map || props.id === props.viewport?.id || userInteraction())
125
+ return;
126
+ map?.stop()[props.transitionType || 'flyTo'](viewport);
127
+ debug(`Update Viewport (${props.transitionType || 'flyTo'}):`, viewport);
128
+ });
129
+ // Update boundaries
130
+ createEffect(prev => {
131
+ const bounds = props.viewport?.bounds;
132
+ if (map && bounds && prev !== bounds && !userInteraction()) {
133
+ props.onViewportChange({
134
+ ...props.viewport,
135
+ ...map?.cameraForBounds(bounds, {
136
+ padding: props.viewport?.padding,
137
+ }),
138
+ });
139
+ debug(`Update Viewport Boundaries:`, bounds);
140
+ }
141
+ return bounds;
142
+ }, props.viewport?.bounds);
143
+ // Update Projection
144
+ createEffect(prev => {
145
+ const proj = props.options?.projection;
146
+ if (prev !== proj) {
147
+ map.setProjection(proj);
148
+ debug('Set Projection to:', proj);
149
+ }
150
+ return proj;
151
+ }, props.options?.projection);
152
+ // Update cursor
153
+ createEffect(() => {
154
+ if (!props.cursorStyle || !map)
155
+ return;
156
+ map.getCanvas().style.cursor = props.cursorStyle;
157
+ debug('Set Cursor to:', props.cursorStyle);
158
+ });
159
+ const insertLayers = (list, layers) => {
160
+ layers.forEach(layer => {
161
+ const index = list.findIndex(i => layer.metadata.smg.beforeType
162
+ ? i.type === layer.metadata.smg.beforeType
163
+ : i.id === layer.metadata.smg.beforeId);
164
+ list =
165
+ index === -1
166
+ ? [...list, layer]
167
+ : [...list.slice(0, index), layer, ...list.slice(index + 1)];
114
168
  });
115
- // Update map style
116
- createEffect(prev => {
117
- const style = getStyle(props.options?.style, props.darkStyle);
118
- if (style === prev)
119
- return;
120
- let oldLayers = [];
121
- let oldSources = {};
122
- debug('Update Mapstyle to', style);
123
- if (map.isStyleLoaded()) {
124
- const oldStyle = map.getStyle();
125
- oldLayers = oldStyle.layers.filter(l => l.id.startsWith('cl-'));
126
- oldSources = Object.keys(oldStyle.sources)
127
- .filter(s => s.startsWith('cl-'))
128
- .reduce((obj, key) => ({ ...obj, [key]: oldStyle.sources[key] }), {});
129
- }
169
+ return list;
170
+ };
171
+ // Update map style
172
+ createEffect(prev => {
173
+ const style = getStyle(props.options?.style, props.darkStyle);
174
+ if (map?.isStyleLoaded() && prev !== style) {
175
+ const oldStyle = map.getStyle();
176
+ const oldLayers = oldStyle.layers.filter(l => map.layerIdList.includes(l.id));
177
+ const oldSources = Object.keys(oldStyle.sources)
178
+ .filter(s => map.sourceIdList.includes(s))
179
+ .reduce((obj, key) => ({ ...obj, [key]: oldStyle.sources[key] }), {});
130
180
  map.setStyle(style);
131
181
  map.once('styledata', () => {
182
+ if (!oldLayers)
183
+ return;
132
184
  const newStyle = map.getStyle();
133
185
  map.setStyle({
134
186
  ...newStyle,
135
187
  sources: { ...newStyle.sources, ...oldSources },
136
- layers: [...newStyle.layers, ...oldLayers],
188
+ layers: insertLayers(newStyle.layers, oldLayers),
137
189
  });
138
190
  });
139
- return style;
140
- }, props.options?.style);
141
- // Hook up viewport events
142
- createEffect(() => {
143
- const viewport = {
144
- id: null,
145
- center: map.getCenter(),
146
- zoom: map.getZoom(),
147
- pitch: map.getPitch(),
148
- bearing: map.getBearing(),
149
- padding: props.viewport?.padding,
150
- bounds: props.viewport?.bounds,
151
- };
152
- const callMove = event => {
153
- if (event.originalEvent)
154
- props.onViewportChange &&
155
- props.onViewportChange({ ...viewport, id: props.id });
156
- setTransitionType('jumpTo');
157
- };
158
- const callEnd = event => {
159
- if (event.originalEvent)
160
- props.onViewportChange && props.onViewportChange(viewport);
161
- setTransitionType(props.transitionType);
162
- };
163
- map.on('move', callMove).on('moveend', callEnd);
164
- onCleanup(() => map?.off('move', callMove).off('moveend', callEnd));
165
- });
166
- // Update boundaries
167
- createEffect(prev => {
168
- if (props.viewport?.bounds != prev)
169
- props.onViewportChange({
170
- ...props.viewport,
171
- ...map.cameraForBounds(props.viewport?.bounds, {
172
- padding: props.viewport?.padding,
173
- }),
174
- });
175
- return props.viewport?.bounds;
176
- }, props.viewport?.bounds);
177
- // Update Viewport
191
+ debug('Set Mapstyle to:', style);
192
+ }
193
+ return style;
194
+ }, props.options?.style);
195
+ [
196
+ 'showTileBoundaries',
197
+ 'showTerrainWireframe',
198
+ 'showCollisionBoxes',
199
+ 'showPadding',
200
+ 'showOverdrawInspector',
201
+ ].forEach(item => {
178
202
  createEffect(() => {
179
- if (props.id === props.viewport?.id)
203
+ const prop = props[item];
204
+ if (!map)
180
205
  return;
181
- const viewport = {
182
- ...props.viewport,
183
- padding: props.viewport?.padding || 0,
184
- };
185
- switch (untrack(transitionType)) {
186
- case 'easeTo':
187
- map.stop().easeTo(viewport);
188
- case 'jumpTo':
189
- map.stop().jumpTo(viewport);
190
- default:
191
- map.stop().flyTo(viewport);
192
- }
206
+ map[item] = prop;
207
+ debug(`Set ${item} to:`, prop);
193
208
  });
194
209
  });
195
- return (<MapContext.Provider value={mapRoot}>
210
+ onCleanup(() => {
211
+ resizeObserver?.disconnect();
212
+ map?.remove();
213
+ debug('Map removed');
214
+ });
215
+ return (<MapContext.Provider value={[mapRoot, userInteraction, debug]}>
196
216
  {mapRoot() && props.children}
197
217
  {mapRef}
198
218
  </MapContext.Provider>);