@vinctus/react-maplibre 1.2.0 → 1.3.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/dist/Layer.js +89 -14
- package/dist/Marker.js +24 -3
- package/dist/Popup.js +41 -11
- package/package.json +1 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/Layer.js
CHANGED
|
@@ -1,31 +1,106 @@
|
|
|
1
|
-
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
2
13
|
import { MapContext } from './Map';
|
|
14
|
+
const addSourceAndLayer = (map, id, strings) => {
|
|
15
|
+
const paint = JSON.parse(strings.paintString);
|
|
16
|
+
const layout = JSON.parse(strings.layoutString);
|
|
17
|
+
// The layer must be removed before its source, and the source added before its layer.
|
|
18
|
+
if (map.getLayer(id)) {
|
|
19
|
+
map.removeLayer(id);
|
|
20
|
+
}
|
|
21
|
+
if (map.getSource(id)) {
|
|
22
|
+
map.removeSource(id);
|
|
23
|
+
}
|
|
24
|
+
map.addSource(id, Object.assign(Object.assign({}, JSON.parse(strings.sourceOptionsString)), { data: JSON.parse(strings.dataString) }));
|
|
25
|
+
map.addLayer(Object.assign(Object.assign({ source: id }, JSON.parse(strings.layerStructureString)), { paint, layout }));
|
|
26
|
+
return { dataString: strings.dataString, paint, layout };
|
|
27
|
+
};
|
|
28
|
+
const updateProperties = (previous, next, setProperty) => {
|
|
29
|
+
const keys = new Set([...Object.keys(previous), ...Object.keys(next)]);
|
|
30
|
+
// A key missing from `next` is set to undefined, which resets it to the style default.
|
|
31
|
+
keys.forEach((key) => {
|
|
32
|
+
if (JSON.stringify(previous[key]) !== JSON.stringify(next[key])) {
|
|
33
|
+
setProperty(key, next[key]);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
};
|
|
3
37
|
export const Layer = ({ layer, source }) => {
|
|
4
38
|
const { map, mapLoaded } = useContext(MapContext);
|
|
5
|
-
const
|
|
6
|
-
const
|
|
39
|
+
const { id } = layer;
|
|
40
|
+
const { paint, layout } = layer, layerStructure = __rest(layer, ["paint", "layout"]);
|
|
41
|
+
const { data } = source, sourceOptions = __rest(source, ["data"]);
|
|
42
|
+
const strings = {
|
|
43
|
+
layerStructureString: JSON.stringify(layerStructure),
|
|
44
|
+
paintString: JSON.stringify(paint !== null && paint !== void 0 ? paint : {}),
|
|
45
|
+
layoutString: JSON.stringify(layout !== null && layout !== void 0 ? layout : {}),
|
|
46
|
+
sourceOptionsString: JSON.stringify(sourceOptions),
|
|
47
|
+
dataString: JSON.stringify(data),
|
|
48
|
+
};
|
|
49
|
+
const { layerStructureString, paintString, layoutString, sourceOptionsString, dataString } = strings;
|
|
50
|
+
// Keeps the newest props available to the creation effect without making it depend on them, so
|
|
51
|
+
// that data, paint and layout changes update the existing source and layer in place.
|
|
52
|
+
const latest = useRef(strings);
|
|
53
|
+
const applied = useRef(null);
|
|
54
|
+
latest.current = strings;
|
|
7
55
|
useEffect(() => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (map && mapLoaded) {
|
|
11
|
-
map.addSource(parsedLayer.id, parsedSource); // when adding, the source must be added first, and then the layer
|
|
12
|
-
map.addLayer(parsedLayer);
|
|
56
|
+
if (!map || !mapLoaded) {
|
|
57
|
+
return;
|
|
13
58
|
}
|
|
59
|
+
applied.current = addSourceAndLayer(map, id, latest.current);
|
|
14
60
|
return () => {
|
|
15
61
|
try {
|
|
16
|
-
if (
|
|
17
|
-
if (map.getLayer(
|
|
18
|
-
map.removeLayer(
|
|
62
|
+
if (!map._removed) {
|
|
63
|
+
if (map.getLayer(id)) {
|
|
64
|
+
map.removeLayer(id);
|
|
19
65
|
}
|
|
20
|
-
if (map.getSource(
|
|
21
|
-
map.removeSource(
|
|
66
|
+
if (map.getSource(id)) {
|
|
67
|
+
map.removeSource(id);
|
|
22
68
|
}
|
|
23
69
|
}
|
|
24
70
|
}
|
|
25
71
|
catch (_a) {
|
|
26
72
|
console.warn('Error cleaning up Layer');
|
|
27
73
|
}
|
|
74
|
+
applied.current = null;
|
|
28
75
|
};
|
|
29
|
-
}, [
|
|
76
|
+
}, [id, layerStructureString, sourceOptionsString, map, mapLoaded]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const current = applied.current;
|
|
79
|
+
if (!map || !mapLoaded || !current) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// The source disappears when the map's style is replaced, so the source and layer are rebuilt
|
|
83
|
+
// from the newest props.
|
|
84
|
+
if (!map.getSource(id)) {
|
|
85
|
+
try {
|
|
86
|
+
applied.current = addSourceAndLayer(map, id, latest.current);
|
|
87
|
+
}
|
|
88
|
+
catch (_a) {
|
|
89
|
+
console.warn('Error restoring Layer');
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (current.dataString !== dataString) {
|
|
94
|
+
const geoJsonSource = map.getSource(id);
|
|
95
|
+
geoJsonSource.setData(JSON.parse(dataString));
|
|
96
|
+
current.dataString = dataString;
|
|
97
|
+
}
|
|
98
|
+
const nextPaint = JSON.parse(paintString);
|
|
99
|
+
const nextLayout = JSON.parse(layoutString);
|
|
100
|
+
updateProperties(current.paint, nextPaint, (key, value) => map.setPaintProperty(id, key, value));
|
|
101
|
+
updateProperties(current.layout, nextLayout, (key, value) => map.setLayoutProperty(id, key, value));
|
|
102
|
+
current.paint = nextPaint;
|
|
103
|
+
current.layout = nextLayout;
|
|
104
|
+
}, [id, dataString, paintString, layoutString, map, mapLoaded]);
|
|
30
105
|
return null;
|
|
31
106
|
};
|
package/dist/Marker.js
CHANGED
|
@@ -13,6 +13,14 @@ export const Marker = ({ children, longitude, latitude, className, anchor, color
|
|
|
13
13
|
// them, so that a position change moves the existing marker instead of recreating it.
|
|
14
14
|
const coordinates = useRef([longitude, latitude]);
|
|
15
15
|
coordinates.current = [longitude, latitude];
|
|
16
|
+
// The popup is compared by its rendered markup because the `popup` element has a new identity on
|
|
17
|
+
// every parent render.
|
|
18
|
+
const popupHtml = popup ? ReactDOMServer.renderToStaticMarkup(popup) : undefined;
|
|
19
|
+
const hasPopup = popupHtml !== undefined;
|
|
20
|
+
const popupHtmlRef = useRef(popupHtml);
|
|
21
|
+
const popupInstance = useRef(null);
|
|
22
|
+
const appliedPopupHtml = useRef(undefined);
|
|
23
|
+
popupHtmlRef.current = popupHtml;
|
|
16
24
|
useEffect(() => {
|
|
17
25
|
if (!map || !mapLoaded) {
|
|
18
26
|
return;
|
|
@@ -43,7 +51,8 @@ export const Marker = ({ children, longitude, latitude, className, anchor, color
|
|
|
43
51
|
}
|
|
44
52
|
}, [marker, latitude, longitude]);
|
|
45
53
|
useEffect(() => {
|
|
46
|
-
|
|
54
|
+
var _a;
|
|
55
|
+
if (!marker || !hasPopup) {
|
|
47
56
|
return;
|
|
48
57
|
}
|
|
49
58
|
const newPopup = new maplibre.Popup({
|
|
@@ -51,7 +60,9 @@ export const Marker = ({ children, longitude, latitude, className, anchor, color
|
|
|
51
60
|
closeButton,
|
|
52
61
|
closeOnClick,
|
|
53
62
|
className: popupClassName,
|
|
54
|
-
}).setHTML(
|
|
63
|
+
}).setHTML((_a = popupHtmlRef.current) !== null && _a !== void 0 ? _a : '');
|
|
64
|
+
popupInstance.current = newPopup;
|
|
65
|
+
appliedPopupHtml.current = popupHtmlRef.current;
|
|
55
66
|
// Attaching the popup to the marker lets MapLibre toggle it on click and keep it anchored to
|
|
56
67
|
// the marker as the marker moves.
|
|
57
68
|
marker.setPopup(newPopup);
|
|
@@ -63,7 +74,17 @@ export const Marker = ({ children, longitude, latitude, className, anchor, color
|
|
|
63
74
|
catch (_a) {
|
|
64
75
|
console.warn('Error cleaning up Marker popup');
|
|
65
76
|
}
|
|
77
|
+
popupInstance.current = null;
|
|
78
|
+
appliedPopupHtml.current = undefined;
|
|
66
79
|
};
|
|
67
|
-
}, [marker,
|
|
80
|
+
}, [marker, hasPopup, popupClassName, closeButton, closeOnClick]);
|
|
81
|
+
// Updating the content in place keeps an open popup open across parent re-renders.
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const existingPopup = popupInstance.current;
|
|
84
|
+
if (existingPopup && popupHtml !== undefined && popupHtml !== appliedPopupHtml.current) {
|
|
85
|
+
existingPopup.setHTML(popupHtml);
|
|
86
|
+
appliedPopupHtml.current = popupHtml;
|
|
87
|
+
}
|
|
88
|
+
}, [popupHtml]);
|
|
68
89
|
return createPortal(children, element);
|
|
69
90
|
};
|
package/dist/Popup.js
CHANGED
|
@@ -1,27 +1,57 @@
|
|
|
1
|
-
import { useEffect, useContext } from 'react';
|
|
1
|
+
import { useEffect, useContext, useRef } from 'react';
|
|
2
2
|
import maplibre from 'maplibre-gl';
|
|
3
3
|
import ReactDOMServer from 'react-dom/server';
|
|
4
4
|
import { MapContext } from './Map';
|
|
5
5
|
export const Popup = ({ children, longitude, latitude, className, closeOnClick, closeButton, }) => {
|
|
6
6
|
const { map, mapLoaded } = useContext(MapContext);
|
|
7
|
+
// The content is compared by its rendered markup because `children` has a new identity on every
|
|
8
|
+
// parent render.
|
|
9
|
+
const html = ReactDOMServer.renderToStaticMarkup(children);
|
|
10
|
+
// Keep the newest props available to the creation effect without making it depend on them, so
|
|
11
|
+
// that content and position changes update the existing popup instead of recreating it.
|
|
12
|
+
const latest = useRef({ html, coordinates: [longitude, latitude] });
|
|
13
|
+
const popupInstance = useRef(null);
|
|
14
|
+
const applied = useRef(null);
|
|
15
|
+
latest.current = { html, coordinates: [longitude, latitude] };
|
|
7
16
|
useEffect(() => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
popup = new maplibre.Popup({ offset: [0, -15], className, closeOnClick, closeButton })
|
|
11
|
-
.setLngLat([longitude, latitude])
|
|
12
|
-
.setHTML(ReactDOMServer.renderToStaticMarkup(children))
|
|
13
|
-
.addTo(map);
|
|
17
|
+
if (!map || !mapLoaded) {
|
|
18
|
+
return;
|
|
14
19
|
}
|
|
20
|
+
const { html: initialHtml, coordinates } = latest.current;
|
|
21
|
+
const popup = new maplibre.Popup({ offset: [0, -15], className, closeOnClick, closeButton })
|
|
22
|
+
.setLngLat(coordinates)
|
|
23
|
+
.setHTML(initialHtml)
|
|
24
|
+
.addTo(map);
|
|
25
|
+
popupInstance.current = popup;
|
|
26
|
+
applied.current = { html: initialHtml, coordinates };
|
|
15
27
|
return () => {
|
|
16
28
|
try {
|
|
17
|
-
|
|
18
|
-
popup.remove();
|
|
19
|
-
}
|
|
29
|
+
popup.remove();
|
|
20
30
|
}
|
|
21
31
|
catch (_a) {
|
|
22
32
|
console.warn('Error cleaning up Popup');
|
|
23
33
|
}
|
|
34
|
+
popupInstance.current = null;
|
|
35
|
+
applied.current = null;
|
|
24
36
|
};
|
|
25
|
-
}, [
|
|
37
|
+
}, [map, mapLoaded, className, closeOnClick, closeButton]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const popup = popupInstance.current;
|
|
40
|
+
const current = applied.current;
|
|
41
|
+
if (popup &&
|
|
42
|
+
current &&
|
|
43
|
+
(current.coordinates[0] !== longitude || current.coordinates[1] !== latitude)) {
|
|
44
|
+
popup.setLngLat([longitude, latitude]);
|
|
45
|
+
current.coordinates = [longitude, latitude];
|
|
46
|
+
}
|
|
47
|
+
}, [longitude, latitude]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const popup = popupInstance.current;
|
|
50
|
+
const current = applied.current;
|
|
51
|
+
if (popup && current && current.html !== html) {
|
|
52
|
+
popup.setHTML(html);
|
|
53
|
+
current.html = html;
|
|
54
|
+
}
|
|
55
|
+
}, [html]);
|
|
26
56
|
return null;
|
|
27
57
|
};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"root":["../src/errorboundary.tsx","../src/geolocatecontrol.tsx","../src/layer.tsx","../src/map.tsx","../src/marker.tsx","../src/navigationcontrol.tsx","../src/popup.tsx","../src/scalecontrol.tsx","../src/index.ts"],"version":"5.8.2"}
|