@dynatrace/strato-geo 3.6.0 → 3.7.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/esm/map/MapView.js +42 -34
- package/esm/map/MapView.js.map +2 -2
- package/esm/map/components/BubbleLayer/BubbleCircleLayer.js +2 -0
- package/esm/map/components/BubbleLayer/BubbleCircleLayer.js.map +2 -2
- package/esm/map/components/BubbleLayer/BubbleLayer.js +4 -1
- package/esm/map/components/BubbleLayer/BubbleLayer.js.map +2 -2
- package/esm/map/components/BubbleLayer/utils/parse-bubble-data-to-geo-json.js +5 -5
- package/esm/map/components/BubbleLayer/utils/parse-bubble-data-to-geo-json.js.map +2 -2
- package/esm/map/components/ChoroplethLayer/ChoroplethLayer.js +5 -2
- package/esm/map/components/ChoroplethLayer/ChoroplethLayer.js.map +2 -2
- package/esm/map/components/ChoroplethLayer/ChoroplethLayerTooltip.js.map +2 -2
- package/esm/map/components/ChoroplethLayer/ChoroplethOutlineLayer.js +3 -1
- package/esm/map/components/ChoroplethLayer/ChoroplethOutlineLayer.js.map +2 -2
- package/esm/map/components/ChoroplethLayer/utils/parse-region-data-to-geo-json.js +6 -5
- package/esm/map/components/ChoroplethLayer/utils/parse-region-data-to-geo-json.js.map +2 -2
- package/esm/map/components/ConnectionLayer/ConnectionLayer.js +1 -1
- package/esm/map/components/ConnectionLayer/ConnectionLayer.js.map +2 -2
- package/esm/map/components/ConnectionLayer/ConnectionLayerLine.js +3 -0
- package/esm/map/components/ConnectionLayer/ConnectionLayerLine.js.map +2 -2
- package/esm/map/components/ConnectionLayer/ConnectionLayerTooltip.js +7 -9
- package/esm/map/components/ConnectionLayer/ConnectionLayerTooltip.js.map +2 -2
- package/esm/map/components/ConnectionLayer/utils/parse-connection-data-to-geo-json.js +20 -18
- package/esm/map/components/ConnectionLayer/utils/parse-connection-data-to-geo-json.js.map +2 -2
- package/esm/map/components/DotLayer/DotLayer.js +4 -1
- package/esm/map/components/DotLayer/DotLayer.js.map +2 -2
- package/esm/map/components/DotLayer/DotLayerTooltip.js.map +2 -2
- package/esm/map/components/DotLayer/utils/parse-dot-data-to-geo-json.js +5 -5
- package/esm/map/components/DotLayer/utils/parse-dot-data-to-geo-json.js.map +2 -2
- package/esm/map/components/MapContent.js +22 -12
- package/esm/map/components/MapContent.js.map +2 -2
- package/esm/map/contexts/geo-data-lookup.context.js +8 -0
- package/esm/map/contexts/geo-data-lookup.context.js.map +7 -0
- package/esm/map/hooks/use-active-interaction.js +59 -43
- package/esm/map/hooks/use-active-interaction.js.map +2 -2
- package/esm/map/hooks/use-attach-image-from-icon.js +4 -2
- package/esm/map/hooks/use-attach-image-from-icon.js.map +2 -2
- package/esm/map/hooks/use-hover-interaction.js +59 -41
- package/esm/map/hooks/use-hover-interaction.js.map +2 -2
- package/esm/map/hooks/use-layer-before-id.js +24 -0
- package/esm/map/hooks/use-layer-before-id.js.map +7 -0
- package/esm/map/hooks/use-map-runtime-error.js +93 -0
- package/esm/map/hooks/use-map-runtime-error.js.map +7 -0
- package/esm/map/hooks/use-overlay-events.js +11 -2
- package/esm/map/hooks/use-overlay-events.js.map +2 -2
- package/esm/map/hooks/use-webgl-context-error.js +2 -1
- package/esm/map/hooks/use-webgl-context-error.js.map +2 -2
- package/esm/map/index.js.map +1 -1
- package/esm/map/slots/Tooltip.js.map +2 -2
- package/esm/map/utils/attach-image-from-shape.js +4 -2
- package/esm/map/utils/attach-image-from-shape.js.map +2 -2
- package/esm/map/utils/extract-layers-data.js +24 -15
- package/esm/map/utils/extract-layers-data.js.map +2 -2
- package/esm/map/utils/is-browser-firefox.js +7 -0
- package/esm/map/utils/is-browser-firefox.js.map +7 -0
- package/esm/map/utils/parse-tooltip-data.js +22 -7
- package/esm/map/utils/parse-tooltip-data.js.map +2 -2
- package/map/MapView.js +42 -34
- package/map/components/BubbleLayer/BubbleCircleLayer.d.ts +2 -1
- package/map/components/BubbleLayer/BubbleCircleLayer.js +2 -0
- package/map/components/BubbleLayer/BubbleLayer.js +4 -1
- package/map/components/BubbleLayer/utils/parse-bubble-data-to-geo-json.d.ts +3 -1
- package/map/components/BubbleLayer/utils/parse-bubble-data-to-geo-json.js +5 -5
- package/map/components/ChoroplethLayer/ChoroplethLayer.js +5 -2
- package/map/components/ChoroplethLayer/ChoroplethOutlineLayer.d.ts +1 -0
- package/map/components/ChoroplethLayer/ChoroplethOutlineLayer.js +3 -1
- package/map/components/ChoroplethLayer/utils/parse-region-data-to-geo-json.d.ts +3 -1
- package/map/components/ChoroplethLayer/utils/parse-region-data-to-geo-json.js +6 -5
- package/map/components/ConnectionLayer/ConnectionLayer.js +1 -1
- package/map/components/ConnectionLayer/ConnectionLayerLine.js +3 -0
- package/map/components/ConnectionLayer/ConnectionLayerTooltip.js +7 -9
- package/map/components/ConnectionLayer/utils/parse-connection-data-to-geo-json.d.ts +3 -1
- package/map/components/ConnectionLayer/utils/parse-connection-data-to-geo-json.js +20 -18
- package/map/components/DotLayer/DotLayer.js +4 -1
- package/map/components/DotLayer/utils/parse-dot-data-to-geo-json.d.ts +3 -1
- package/map/components/DotLayer/utils/parse-dot-data-to-geo-json.js +5 -5
- package/map/components/MapContent.js +21 -12
- package/map/contexts/geo-data-lookup.context.d.ts +9 -0
- package/map/{components/ConnectionLayer/utils/restore-null-props.js → contexts/geo-data-lookup.context.js} +8 -9
- package/map/hooks/use-active-interaction.d.ts +8 -1
- package/map/hooks/use-active-interaction.js +58 -42
- package/map/hooks/use-attach-image-from-icon.js +4 -2
- package/map/hooks/use-hover-interaction.d.ts +6 -2
- package/map/hooks/use-hover-interaction.js +52 -39
- package/map/hooks/use-layer-before-id.d.ts +13 -0
- package/map/hooks/{use-map-mouse-move.js → use-layer-before-id.js} +20 -15
- package/map/hooks/use-map-runtime-error.d.ts +34 -0
- package/map/hooks/use-map-runtime-error.js +112 -0
- package/map/hooks/use-overlay-events.js +11 -2
- package/map/hooks/use-webgl-context-error.js +2 -1
- package/map/slots/Tooltip.d.ts +2 -0
- package/map/types/connection-layer.d.ts +1 -8
- package/map/types/tooltip.d.ts +1 -0
- package/map/utils/attach-image-from-shape.js +4 -2
- package/map/utils/extract-layers-data.d.ts +2 -0
- package/map/utils/extract-layers-data.js +24 -15
- package/map/utils/is-browser-firefox.d.ts +5 -0
- package/map/utils/is-browser-firefox.js +26 -0
- package/map/utils/parse-tooltip-data.d.ts +11 -3
- package/map/utils/parse-tooltip-data.js +22 -7
- package/package.json +2 -2
- package/esm/map/components/ConnectionLayer/utils/restore-null-props.js +0 -9
- package/esm/map/components/ConnectionLayer/utils/restore-null-props.js.map +0 -7
- package/esm/map/hooks/use-map-mouse-move.js +0 -19
- package/esm/map/hooks/use-map-mouse-move.js.map +0 -7
- package/map/components/ConnectionLayer/utils/restore-null-props.d.ts +0 -2
- package/map/hooks/use-map-mouse-move.d.ts +0 -2
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
import { useMap } from "@vis.gl/react-maplibre";
|
|
2
2
|
import { isNil, isUndefined } from "lodash-es";
|
|
3
|
-
import { useCallback, useEffect } from "react";
|
|
4
|
-
import { BASE_LAYER_IDS } from "../constants.js";
|
|
3
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
5
4
|
import { getMinValueFeature } from "../utils/get-min-value-feature.js";
|
|
6
|
-
const useActiveInteraction = () => {
|
|
5
|
+
const useActiveInteraction = (interactiveLayerIds) => {
|
|
7
6
|
const map = useMap().current;
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const featureIdRef = useRef(void 0);
|
|
8
|
+
const sourceIdRef = useRef(void 0);
|
|
9
|
+
const interactiveLayerIdsRef = useRef(interactiveLayerIds);
|
|
10
|
+
interactiveLayerIdsRef.current = interactiveLayerIds;
|
|
10
11
|
const handleClick = useCallback(
|
|
11
12
|
({ point }) => {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
let features = [];
|
|
14
|
+
try {
|
|
15
|
+
features = map.queryRenderedFeatures(point, {
|
|
16
|
+
layers: interactiveLayerIdsRef.current
|
|
17
|
+
});
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
let allFeatures = [];
|
|
22
|
+
try {
|
|
23
|
+
allFeatures = map.queryRenderedFeatures(void 0, {
|
|
24
|
+
layers: interactiveLayerIdsRef.current
|
|
25
|
+
});
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
14
28
|
const layerId = features?.[0]?.layer?.id;
|
|
15
29
|
const hasHoveredFeatures = !isNil(features) && features.length > 0 && !isUndefined(layerId);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!isUndefined(featureId) && !isUndefined(sourceId)) {
|
|
30
|
+
if (hasHoveredFeatures) {
|
|
31
|
+
if (!isUndefined(featureIdRef.current) && !isUndefined(sourceIdRef.current)) {
|
|
19
32
|
map.setFeatureState(
|
|
20
|
-
{ source:
|
|
33
|
+
{ source: sourceIdRef.current, id: featureIdRef.current },
|
|
21
34
|
{ active: false }
|
|
22
35
|
);
|
|
23
36
|
}
|
|
24
37
|
const minFeature = getMinValueFeature(features);
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
featureIdRef.current = minFeature.id;
|
|
39
|
+
sourceIdRef.current = minFeature.layer.source;
|
|
27
40
|
const activeState = features[0].state.active;
|
|
28
41
|
map.setFeatureState(
|
|
29
|
-
{ source:
|
|
42
|
+
{ source: sourceIdRef.current, id: featureIdRef.current },
|
|
30
43
|
{ active: !activeState }
|
|
31
44
|
);
|
|
32
45
|
allFeatures.forEach((feature) => {
|
|
@@ -37,46 +50,49 @@ const useActiveInteraction = () => {
|
|
|
37
50
|
);
|
|
38
51
|
}
|
|
39
52
|
});
|
|
40
|
-
} else {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
}
|
|
53
|
+
} else if (!isUndefined(featureIdRef.current) && !isUndefined(sourceIdRef.current)) {
|
|
54
|
+
map.setFeatureState(
|
|
55
|
+
{ source: sourceIdRef.current, id: featureIdRef.current },
|
|
56
|
+
{ active: false }
|
|
57
|
+
);
|
|
58
|
+
allFeatures.forEach((feature) => {
|
|
59
|
+
if (feature.id !== void 0 && feature.layer.source !== void 0) {
|
|
60
|
+
map.setFeatureState(
|
|
61
|
+
{ source: feature.layer.source, id: feature.id },
|
|
62
|
+
{ isAnyActive: false }
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
55
66
|
}
|
|
56
67
|
},
|
|
57
|
-
[
|
|
68
|
+
[map]
|
|
58
69
|
);
|
|
59
70
|
const onEscapeKeyPressedHandler = useCallback(
|
|
60
71
|
(event) => {
|
|
61
72
|
if (event.key === "Escape") {
|
|
62
|
-
if (!isUndefined(
|
|
73
|
+
if (!isUndefined(featureIdRef.current) && !isUndefined(sourceIdRef.current)) {
|
|
63
74
|
map.setFeatureState(
|
|
64
|
-
{ source:
|
|
75
|
+
{ source: sourceIdRef.current, id: featureIdRef.current },
|
|
65
76
|
{ active: false }
|
|
66
77
|
);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
try {
|
|
79
|
+
const allFeatures = map.queryRenderedFeatures(void 0, {
|
|
80
|
+
layers: interactiveLayerIdsRef.current
|
|
81
|
+
});
|
|
82
|
+
allFeatures.forEach((feature) => {
|
|
83
|
+
if (feature.id !== void 0 && feature.layer.source !== void 0) {
|
|
84
|
+
map.setFeatureState(
|
|
85
|
+
{ source: feature.layer.source, id: feature.id },
|
|
86
|
+
{ isAnyActive: false }
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
76
92
|
}
|
|
77
93
|
}
|
|
78
94
|
},
|
|
79
|
-
[
|
|
95
|
+
[map]
|
|
80
96
|
);
|
|
81
97
|
useEffect(() => {
|
|
82
98
|
map.on("click", handleClick);
|
|
@@ -85,7 +101,7 @@ const useActiveInteraction = () => {
|
|
|
85
101
|
map.off("click", handleClick);
|
|
86
102
|
window.removeEventListener("keyup", onEscapeKeyPressedHandler);
|
|
87
103
|
};
|
|
88
|
-
}, []);
|
|
104
|
+
}, [map, handleClick, onEscapeKeyPressedHandler]);
|
|
89
105
|
};
|
|
90
106
|
export {
|
|
91
107
|
useActiveInteraction
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/map/hooks/use-active-interaction.ts"],
|
|
4
|
-
"sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { isNil, isUndefined } from 'lodash-es';\nimport type { MapLayerMouseEvent } from 'maplibre-gl';\nimport { useCallback, useEffect } from 'react';\n\nimport {
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,OAAO,mBAAmB;AAEnC,SAAS,aAAa,
|
|
4
|
+
"sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { isNil, isUndefined } from 'lodash-es';\nimport type { MapLayerMouseEvent } from 'maplibre-gl';\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { getMinValueFeature } from '../utils/get-min-value-feature.js';\n\n/**\n * Handles click-to-activate interactions on map features.\n *\n * Scopes `queryRenderedFeatures` to `interactiveLayerIds` to prevent accidental\n * activation of base/background layers. Both query calls are try-caught to\n * contain maplibre-gl runtime errors without crashing the component tree.\n */\nexport const useActiveInteraction = (interactiveLayerIds: string[]) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const map = useMap().current!;\n\n /** Tracks the ID of the currently active feature across renders. */\n const featureIdRef = useRef<string | number | undefined>(undefined);\n /** Tracks the source of the currently active feature across renders. */\n const sourceIdRef = useRef<string | undefined>(undefined);\n /**\n * Kept in a ref so that stable callbacks always read the latest layer list\n * without needing to be recreated when the array reference changes.\n */\n const interactiveLayerIdsRef = useRef(interactiveLayerIds);\n interactiveLayerIdsRef.current = interactiveLayerIds;\n\n const handleClick = useCallback(\n ({ point }: MapLayerMouseEvent) => {\n let features: ReturnType<typeof map.queryRenderedFeatures> = [];\n\n try {\n features = map.queryRenderedFeatures(point, {\n layers: interactiveLayerIdsRef.current,\n });\n } catch {\n return;\n }\n\n // Keep the broad query for isAnyActive so all interactive features receive the flag.\n let allFeatures: ReturnType<typeof map.queryRenderedFeatures> = [];\n try {\n allFeatures = map.queryRenderedFeatures(undefined, {\n layers: interactiveLayerIdsRef.current,\n });\n } catch {\n // falls through with empty allFeatures\n }\n\n const layerId = features?.[0]?.layer?.id;\n\n const hasHoveredFeatures =\n !isNil(features) && features.length > 0 && !isUndefined(layerId);\n\n if (hasHoveredFeatures) {\n if (\n !isUndefined(featureIdRef.current) &&\n !isUndefined(sourceIdRef.current)\n ) {\n // If there's already an active feature, remove the active state.\n map.setFeatureState(\n { source: sourceIdRef.current, id: featureIdRef.current },\n { active: false },\n );\n }\n\n const minFeature = getMinValueFeature(features);\n\n featureIdRef.current = minFeature.id;\n sourceIdRef.current = minFeature.layer.source;\n const activeState = features[0].state.active;\n\n // Add the active state to the closest feature.\n map.setFeatureState(\n { source: sourceIdRef.current, id: featureIdRef.current },\n { active: !activeState },\n );\n allFeatures.forEach((feature) => {\n if (feature.id !== undefined && feature.layer.source !== undefined) {\n map.setFeatureState(\n { source: feature.layer.source, id: feature.id },\n { isAnyActive: !activeState },\n );\n }\n }); //TODO: change to inactive\n } else if (\n !isUndefined(featureIdRef.current) &&\n !isUndefined(sourceIdRef.current)\n ) {\n // Remove the active state from the last active feature.\n map.setFeatureState(\n { source: sourceIdRef.current, id: featureIdRef.current },\n { active: false },\n );\n allFeatures.forEach((feature) => {\n if (feature.id !== undefined && feature.layer.source !== undefined) {\n map.setFeatureState(\n { source: feature.layer.source, id: feature.id },\n { isAnyActive: false },\n );\n }\n });\n }\n },\n [map],\n );\n\n const onEscapeKeyPressedHandler = useCallback(\n (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n if (\n !isUndefined(featureIdRef.current) &&\n !isUndefined(sourceIdRef.current)\n ) {\n map.setFeatureState(\n { source: sourceIdRef.current, id: featureIdRef.current },\n { active: false },\n );\n\n try {\n const allFeatures = map.queryRenderedFeatures(undefined, {\n layers: interactiveLayerIdsRef.current,\n });\n allFeatures.forEach((feature) => {\n if (\n feature.id !== undefined &&\n feature.layer.source !== undefined\n ) {\n map.setFeatureState(\n { source: feature.layer.source, id: feature.id },\n { isAnyActive: false },\n );\n }\n });\n } catch {\n // falls through \u2014 active state is already cleared above\n }\n }\n }\n },\n [map],\n );\n\n useEffect(() => {\n map.on('click', handleClick);\n window.addEventListener('keyup', onEscapeKeyPressedHandler);\n return () => {\n map.off('click', handleClick);\n window.removeEventListener('keyup', onEscapeKeyPressedHandler);\n };\n }, [map, handleClick, onEscapeKeyPressedHandler]);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,6 +8,7 @@ import { attachImageToMap } from "../utils/attach-image-to-map.js";
|
|
|
8
8
|
import { createBitmapConfigOptions } from "../utils/create-bitmap-config-options.js";
|
|
9
9
|
import { getDataUri } from "../utils/get-data-uri.js";
|
|
10
10
|
import { getScaledSymbolSize } from "../utils/get-scaled-symbol-size.js";
|
|
11
|
+
import { isFirefox } from "../utils/is-browser-firefox.js";
|
|
11
12
|
const useAttachImageFromIcon = (icon, suffix, outputSize) => {
|
|
12
13
|
const { elementAsString, ref } = useSafeSvgParser();
|
|
13
14
|
const defaultScaledIconSize = getScaledSymbolSize();
|
|
@@ -25,14 +26,15 @@ const useAttachImageFromIcon = (icon, suffix, outputSize) => {
|
|
|
25
26
|
fakeDomContainer.remove();
|
|
26
27
|
const stringUrl = getDataUri(elementAsString);
|
|
27
28
|
img.addEventListener("load", () => {
|
|
28
|
-
createImageBitmap(
|
|
29
|
+
const bitmapPromise = isFirefox() ? createImageBitmap(img, createBitmapConfigOptions(outputSize)) : createImageBitmap(
|
|
29
30
|
img,
|
|
30
31
|
0,
|
|
31
32
|
0,
|
|
32
33
|
DEFAULT_INPUT_ICON_SIZE,
|
|
33
34
|
DEFAULT_INPUT_ICON_SIZE,
|
|
34
35
|
createBitmapConfigOptions(outputSize)
|
|
35
|
-
)
|
|
36
|
+
);
|
|
37
|
+
bitmapPromise.then((bitmap) => {
|
|
36
38
|
if (!map) {
|
|
37
39
|
return;
|
|
38
40
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/map/hooks/use-attach-image-from-icon.ts"],
|
|
4
|
-
"sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { isString } from 'lodash-es';\nimport { createElement, type ReactNode, useLayoutEffect } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { useSafeSvgParser } from './use-safe-svg-parser.js';\nimport { DEFAULT_INPUT_ICON_SIZE } from '../constants.js';\nimport type { MapShape } from '../types/shapes.js';\nimport { attachImageToMap } from '../utils/attach-image-to-map.js';\nimport { createBitmapConfigOptions } from '../utils/create-bitmap-config-options.js';\nimport { getDataUri } from '../utils/get-data-uri.js';\nimport { getScaledSymbolSize } from '../utils/get-scaled-symbol-size.js';\n\nexport const useAttachImageFromIcon = (\n icon: string | MapShape | ReactNode,\n suffix: string,\n outputSize?: number,\n) => {\n const { elementAsString, ref } = useSafeSvgParser();\n const defaultScaledIconSize = getScaledSymbolSize();\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { current: map } = useMap()!;\n const img = new Image(defaultScaledIconSize, defaultScaledIconSize);\n\n /*\n * As we can only create a reference in a React Node, but we need a virtual\n * 'root' element in the DOM to attach it, we create a 'div'\n * container that lately will hold the React Node. This is done to ensure\n * everything is cached prior any manipulation.\n */\n // Create a div element that acts as a container node in the DOM\n const fakeDomContainer = document.createElement('div');\n useLayoutEffect(() => {\n if (isString(icon)) {\n return;\n }\n // Create a React Node that will have a ref callback to be run when loaded and\n // the input icon as a children\n const reactNodeRefContainer = createElement('div', { ref }, icon);\n // Attach React Node with the ref callback into DOM Node\n const root = createRoot(fakeDomContainer);\n root.render(reactNodeRefContainer);\n }, [icon]);\n\n // Remove cached element to free resources\n fakeDomContainer.remove();\n\n const stringUrl = getDataUri(elementAsString);\n\n img.addEventListener('load', () => {\n createImageBitmap(\n img,\n
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,eAA+B,uBAAuB;AAC/D,SAAS,kBAAkB;AAE3B,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AAExC,SAAS,wBAAwB;AACjC,SAAS,iCAAiC;AAC1C,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;
|
|
4
|
+
"sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { isString } from 'lodash-es';\nimport { createElement, type ReactNode, useLayoutEffect } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { useSafeSvgParser } from './use-safe-svg-parser.js';\nimport { DEFAULT_INPUT_ICON_SIZE } from '../constants.js';\nimport type { MapShape } from '../types/shapes.js';\nimport { attachImageToMap } from '../utils/attach-image-to-map.js';\nimport { createBitmapConfigOptions } from '../utils/create-bitmap-config-options.js';\nimport { getDataUri } from '../utils/get-data-uri.js';\nimport { getScaledSymbolSize } from '../utils/get-scaled-symbol-size.js';\nimport { isFirefox } from '../utils/is-browser-firefox.js';\n\nexport const useAttachImageFromIcon = (\n icon: string | MapShape | ReactNode,\n suffix: string,\n outputSize?: number,\n) => {\n const { elementAsString, ref } = useSafeSvgParser();\n const defaultScaledIconSize = getScaledSymbolSize();\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { current: map } = useMap()!;\n const img = new Image(defaultScaledIconSize, defaultScaledIconSize);\n\n /*\n * As we can only create a reference in a React Node, but we need a virtual\n * 'root' element in the DOM to attach it, we create a 'div'\n * container that lately will hold the React Node. This is done to ensure\n * everything is cached prior any manipulation.\n */\n // Create a div element that acts as a container node in the DOM\n const fakeDomContainer = document.createElement('div');\n useLayoutEffect(() => {\n if (isString(icon)) {\n return;\n }\n // Create a React Node that will have a ref callback to be run when loaded and\n // the input icon as a children\n const reactNodeRefContainer = createElement('div', { ref }, icon);\n // Attach React Node with the ref callback into DOM Node\n const root = createRoot(fakeDomContainer);\n root.render(reactNodeRefContainer);\n }, [icon]);\n\n // Remove cached element to free resources\n fakeDomContainer.remove();\n\n const stringUrl = getDataUri(elementAsString);\n\n img.addEventListener('load', () => {\n // Firefox rasterizes SVG sources at the Image element's display size, not\n // at the SVG's intrinsic dimensions, so an explicit source rect clips the\n // icon. Drop the rect on Firefox only \u2014 other browsers keep the original\n // path.\n const bitmapPromise = isFirefox()\n ? createImageBitmap(img, createBitmapConfigOptions(outputSize))\n : createImageBitmap(\n img,\n 0,\n 0,\n DEFAULT_INPUT_ICON_SIZE,\n DEFAULT_INPUT_ICON_SIZE,\n createBitmapConfigOptions(outputSize),\n );\n bitmapPromise.then((bitmap) => {\n if (!map) {\n return;\n }\n const iconName = `custom-icon-${suffix}`;\n attachImageToMap(map, bitmap, iconName);\n });\n });\n img.src = stringUrl;\n\n return !isString(icon);\n};\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,eAA+B,uBAAuB;AAC/D,SAAS,kBAAkB;AAE3B,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AAExC,SAAS,wBAAwB;AACjC,SAAS,iCAAiC;AAC1C,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AACpC,SAAS,iBAAiB;AAEnB,MAAM,yBAAyB,CACpC,MACA,QACA,eACG;AACH,QAAM,EAAE,iBAAiB,IAAI,IAAI,iBAAiB;AAClD,QAAM,wBAAwB,oBAAoB;AAGlD,QAAM,EAAE,SAAS,IAAI,IAAI,OAAO;AAChC,QAAM,MAAM,IAAI,MAAM,uBAAuB,qBAAqB;AASlE,QAAM,mBAAmB,SAAS,cAAc,KAAK;AACrD,kBAAgB,MAAM;AACpB,QAAI,SAAS,IAAI,GAAG;AAClB;AAAA,IACF;AAGA,UAAM,wBAAwB,cAAc,OAAO,EAAE,IAAI,GAAG,IAAI;AAEhE,UAAM,OAAO,WAAW,gBAAgB;AACxC,SAAK,OAAO,qBAAqB;AAAA,EACnC,GAAG,CAAC,IAAI,CAAC;AAGT,mBAAiB,OAAO;AAExB,QAAM,YAAY,WAAW,eAAe;AAE5C,MAAI,iBAAiB,QAAQ,MAAM;AAKjC,UAAM,gBAAgB,UAAU,IAC5B,kBAAkB,KAAK,0BAA0B,UAAU,CAAC,IAC5D;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,0BAA0B,UAAU;AAAA,IACtC;AACJ,kBAAc,KAAK,CAAC,WAAW;AAC7B,UAAI,CAAC,KAAK;AACR;AAAA,MACF;AACA,YAAM,WAAW,eAAe,MAAM;AACtC,uBAAiB,KAAK,QAAQ,QAAQ;AAAA,IACxC,CAAC;AAAA,EACH,CAAC;AACD,MAAI,MAAM;AAEV,SAAO,CAAC,SAAS,IAAI;AACvB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { useMap } from "@vis.gl/react-maplibre";
|
|
2
|
-
import { isNil, isString, isUndefined } from "lodash-es";
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { isNil, isString, isUndefined, throttle } from "lodash-es";
|
|
3
|
+
import {
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef
|
|
8
|
+
} from "react";
|
|
5
9
|
import {
|
|
6
10
|
getAssociatedFeatures,
|
|
7
11
|
hasAssociatedFeatures,
|
|
8
12
|
isAssociatedFeature
|
|
9
13
|
} from "../utils/associated-features.js";
|
|
10
14
|
import { getMinValueFeature } from "../utils/get-min-value-feature.js";
|
|
15
|
+
const MOUSEMOVE_THROTTLE_MS = 16;
|
|
16
|
+
const blurTrackedFeature = (map, featureIdRef, sourceIdRef) => {
|
|
17
|
+
if (!isNil(featureIdRef.current) && !isNil(sourceIdRef.current)) {
|
|
18
|
+
blurFeature(map, sourceIdRef.current, featureIdRef.current);
|
|
19
|
+
featureIdRef.current = void 0;
|
|
20
|
+
sourceIdRef.current = void 0;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
11
23
|
const featureExists = (map, source, id) => {
|
|
12
24
|
const isSourcePresent = map.getSource(source) !== void 0;
|
|
13
25
|
return isSourcePresent && map.getFeatureState({
|
|
@@ -49,55 +61,61 @@ const hoverFeature = (map, source, id) => {
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
};
|
|
52
|
-
const useHoverInteraction = () => {
|
|
64
|
+
const useHoverInteraction = (interactiveLayerIds) => {
|
|
53
65
|
const map = useMap().current;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const featureIdRef = useRef(void 0);
|
|
67
|
+
const sourceIdRef = useRef(void 0);
|
|
68
|
+
const interactiveLayerIdsRef = useRef(interactiveLayerIds);
|
|
69
|
+
interactiveLayerIdsRef.current = interactiveLayerIds;
|
|
70
|
+
const handleMouseOut = useCallback(() => {
|
|
71
|
+
if (!isNil(map)) {
|
|
72
|
+
blurTrackedFeature(map, featureIdRef, sourceIdRef);
|
|
73
|
+
}
|
|
74
|
+
}, [map]);
|
|
75
|
+
const throttledMouseMove = useMemo(
|
|
76
|
+
() => throttle(
|
|
77
|
+
({ point }) => {
|
|
78
|
+
if (isNil(map)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let features;
|
|
82
|
+
try {
|
|
83
|
+
features = map.queryRenderedFeatures(point, {
|
|
84
|
+
layers: interactiveLayerIdsRef.current
|
|
85
|
+
}).filter((feature) => !isAssociatedFeature(feature.properties.id));
|
|
86
|
+
} catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
69
89
|
const layerId = features?.[0]?.layer?.id;
|
|
70
90
|
const hasHoveredFeatures = !isNil(features) && features.length > 0 && !isUndefined(layerId);
|
|
71
|
-
|
|
72
|
-
if (hasHoveredFeatures && !isBaseLayer) {
|
|
73
|
-
map.getCanvas().style.cursor = "pointer";
|
|
74
|
-
if (!isUndefined(featureId) && !isUndefined(sourceId)) {
|
|
75
|
-
blurFeature(map, sourceId, featureId);
|
|
76
|
-
}
|
|
77
|
-
const minFeature = getMinValueFeature(features);
|
|
78
|
-
featureId = minFeature.id;
|
|
79
|
-
sourceId = minFeature.layer.source;
|
|
80
|
-
if (!isUndefined(featureId)) {
|
|
81
|
-
hoverFeature(map, sourceId, featureId);
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
91
|
+
if (!hasHoveredFeatures) {
|
|
84
92
|
map.getCanvas().style.cursor = "grab";
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
93
|
+
blurTrackedFeature(map, featureIdRef, sourceIdRef);
|
|
94
|
+
return;
|
|
88
95
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
map.getCanvas().style.cursor = "pointer";
|
|
97
|
+
blurTrackedFeature(map, featureIdRef, sourceIdRef);
|
|
98
|
+
const minFeature = getMinValueFeature(features);
|
|
99
|
+
featureIdRef.current = minFeature.id;
|
|
100
|
+
sourceIdRef.current = minFeature.layer.source;
|
|
101
|
+
if (!isUndefined(featureIdRef.current)) {
|
|
102
|
+
hoverFeature(map, sourceIdRef.current, featureIdRef.current);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
MOUSEMOVE_THROTTLE_MS,
|
|
106
|
+
{ trailing: true }
|
|
107
|
+
),
|
|
108
|
+
[map]
|
|
92
109
|
);
|
|
93
110
|
useEffect(() => {
|
|
94
|
-
map?.on("mousemove",
|
|
111
|
+
map?.on("mousemove", throttledMouseMove);
|
|
95
112
|
map?.on("mouseout", handleMouseOut);
|
|
96
113
|
return () => {
|
|
97
|
-
|
|
114
|
+
throttledMouseMove.cancel();
|
|
115
|
+
map?.off("mousemove", throttledMouseMove);
|
|
98
116
|
map?.off("mouseout", handleMouseOut);
|
|
99
117
|
};
|
|
100
|
-
}, []);
|
|
118
|
+
}, [map, throttledMouseMove, handleMouseOut]);
|
|
101
119
|
};
|
|
102
120
|
export {
|
|
103
121
|
useHoverInteraction
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/map/hooks/use-hover-interaction.ts"],
|
|
4
|
-
"sourcesContent": ["import type { MapRef } from '@vis.gl/react-maplibre';\nimport { useMap } from '@vis.gl/react-maplibre';\nimport { isNil, isString, isUndefined } from 'lodash-es';\nimport type { MapLayerMouseEvent } from 'maplibre-gl';\nimport { useCallback
|
|
5
|
-
"mappings": "AACA,SAAS,cAAc;AACvB,SAAS,OAAO,UAAU,
|
|
4
|
+
"sourcesContent": ["import type { MapRef } from '@vis.gl/react-maplibre';\nimport { useMap } from '@vis.gl/react-maplibre';\nimport { isNil, isString, isUndefined, throttle } from 'lodash-es';\nimport type { MapLayerMouseEvent } from 'maplibre-gl';\nimport {\n type MutableRefObject,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\n\nimport {\n getAssociatedFeatures,\n hasAssociatedFeatures,\n isAssociatedFeature,\n} from '../utils/associated-features.js';\nimport { getMinValueFeature } from '../utils/get-min-value-feature.js';\n\n/** Minimum milliseconds between processed mousemove events (~1 frame at 60 fps). */\nconst MOUSEMOVE_THROTTLE_MS = 16;\n\nconst blurTrackedFeature = (\n map: MapRef,\n featureIdRef: MutableRefObject<string | number | undefined>,\n sourceIdRef: MutableRefObject<string | undefined>,\n) => {\n if (!isNil(featureIdRef.current) && !isNil(sourceIdRef.current)) {\n blurFeature(map, sourceIdRef.current, featureIdRef.current);\n featureIdRef.current = undefined;\n sourceIdRef.current = undefined;\n }\n};\n\n/**\n * Checks whether a feature exists from a given source\n *\n * @param map -\n * @param source -\n * @param id -\n */\nconst featureExists = (\n map: MapRef,\n source: string,\n id: string | number,\n): boolean => {\n const isSourcePresent = map.getSource(source) !== undefined;\n\n return (\n isSourcePresent &&\n map.getFeatureState({\n source,\n id,\n }) !== undefined\n );\n};\n\n/**\n * Removes hovered state from a feature and its associated features from a given source\n * @param map -\n * @param source -\n * @param id -\n */\nconst blurFeature = (map: MapRef, source: string, id: string | number) => {\n map.setFeatureState({ source, id }, { hover: false });\n\n if (isString(id) && hasAssociatedFeatures(id)) {\n for (const associatedFeature of getAssociatedFeatures('connection')) {\n const associatedSource = `${source}-direction`;\n const associatedId = `${id}-${associatedFeature}`;\n\n if (featureExists(map, associatedSource, associatedId)) {\n map.setFeatureState(\n { source: associatedSource, id: associatedId },\n {\n hover: false,\n },\n );\n }\n }\n }\n};\n\n/**\n * Sets hovered state to a feature and its associated features from a given source\n * @param map -\n * @param source -\n * @param id -\n */\nconst hoverFeature = (map: MapRef, source: string, id: string | number) => {\n map.setFeatureState({ source, id }, { hover: true });\n\n if (isString(id) && hasAssociatedFeatures(id)) {\n for (const associatedFeature of getAssociatedFeatures('connection')) {\n const associatedSource = `${source}-direction`;\n const associatedId = `${id}-${associatedFeature}`;\n\n if (featureExists(map, associatedSource, associatedId)) {\n map.setFeatureState(\n { source: `${source}-direction`, id: `${id}-${associatedFeature}` },\n {\n hover: true,\n },\n );\n }\n }\n }\n};\n\n/**\n * Sets and removes hovered state to the features depending on mouse position.\n *\n * @param interactiveLayerIds - maplibre-gl layer IDs to scope the feature query.\n * Scoping to data layers prevents touching base-map vector tiles whose internal\n * feature index may be in a transitional state (root cause of APPDEV-17854).\n */\nexport const useHoverInteraction = (interactiveLayerIds: string[]) => {\n const map = useMap().current;\n\n const featureIdRef = useRef<string | number | undefined>(undefined);\n const sourceIdRef = useRef<string | undefined>(undefined);\n\n // Keep a ref so the stable callback always sees the latest layer IDs even\n // if the consumer updates them after the first render.\n const interactiveLayerIdsRef = useRef(interactiveLayerIds);\n interactiveLayerIdsRef.current = interactiveLayerIds;\n\n const handleMouseOut = useCallback(() => {\n if (!isNil(map)) {\n blurTrackedFeature(map, featureIdRef, sourceIdRef);\n }\n }, [map]);\n\n const throttledMouseMove = useMemo(\n () =>\n throttle(\n ({ point }: MapLayerMouseEvent) => {\n if (isNil(map)) {\n return;\n }\n\n let features;\n try {\n features = map\n .queryRenderedFeatures(point, {\n layers: interactiveLayerIdsRef.current,\n })\n .filter((feature) => !isAssociatedFeature(feature.properties.id));\n } catch {\n return;\n }\n\n const layerId = features?.[0]?.layer?.id;\n const hasHoveredFeatures =\n !isNil(features) && features.length > 0 && !isUndefined(layerId);\n\n // No BASE_LAYER_IDS check needed: querying only interactiveLayerIds\n // already excludes base-map layers entirely.\n if (!hasHoveredFeatures) {\n map.getCanvas().style.cursor = 'grab';\n blurTrackedFeature(map, featureIdRef, sourceIdRef);\n return;\n }\n\n map.getCanvas().style.cursor = 'pointer';\n blurTrackedFeature(map, featureIdRef, sourceIdRef);\n\n const minFeature = getMinValueFeature(features);\n featureIdRef.current = minFeature.id;\n sourceIdRef.current = minFeature.layer.source;\n\n if (!isUndefined(featureIdRef.current)) {\n hoverFeature(map, sourceIdRef.current, featureIdRef.current);\n }\n },\n MOUSEMOVE_THROTTLE_MS,\n { trailing: true },\n ),\n [map],\n );\n\n useEffect(() => {\n map?.on('mousemove', throttledMouseMove);\n map?.on('mouseout', handleMouseOut);\n return () => {\n throttledMouseMove.cancel();\n map?.off('mousemove', throttledMouseMove);\n map?.off('mouseout', handleMouseOut);\n };\n }, [map, throttledMouseMove, handleMouseOut]);\n};\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,cAAc;AACvB,SAAS,OAAO,UAAU,aAAa,gBAAgB;AAEvD;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AAGnC,MAAM,wBAAwB;AAE9B,MAAM,qBAAqB,CACzB,KACA,cACA,gBACG;AACH,MAAI,CAAC,MAAM,aAAa,OAAO,KAAK,CAAC,MAAM,YAAY,OAAO,GAAG;AAC/D,gBAAY,KAAK,YAAY,SAAS,aAAa,OAAO;AAC1D,iBAAa,UAAU;AACvB,gBAAY,UAAU;AAAA,EACxB;AACF;AASA,MAAM,gBAAgB,CACpB,KACA,QACA,OACY;AACZ,QAAM,kBAAkB,IAAI,UAAU,MAAM,MAAM;AAElD,SACE,mBACA,IAAI,gBAAgB;AAAA,IAClB;AAAA,IACA;AAAA,EACF,CAAC,MAAM;AAEX;AAQA,MAAM,cAAc,CAAC,KAAa,QAAgB,OAAwB;AACxE,MAAI,gBAAgB,EAAE,QAAQ,GAAG,GAAG,EAAE,OAAO,MAAM,CAAC;AAEpD,MAAI,SAAS,EAAE,KAAK,sBAAsB,EAAE,GAAG;AAC7C,eAAW,qBAAqB,sBAAsB,YAAY,GAAG;AACnE,YAAM,mBAAmB,GAAG,MAAM;AAClC,YAAM,eAAe,GAAG,EAAE,IAAI,iBAAiB;AAE/C,UAAI,cAAc,KAAK,kBAAkB,YAAY,GAAG;AACtD,YAAI;AAAA,UACF,EAAE,QAAQ,kBAAkB,IAAI,aAAa;AAAA,UAC7C;AAAA,YACE,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAQA,MAAM,eAAe,CAAC,KAAa,QAAgB,OAAwB;AACzE,MAAI,gBAAgB,EAAE,QAAQ,GAAG,GAAG,EAAE,OAAO,KAAK,CAAC;AAEnD,MAAI,SAAS,EAAE,KAAK,sBAAsB,EAAE,GAAG;AAC7C,eAAW,qBAAqB,sBAAsB,YAAY,GAAG;AACnE,YAAM,mBAAmB,GAAG,MAAM;AAClC,YAAM,eAAe,GAAG,EAAE,IAAI,iBAAiB;AAE/C,UAAI,cAAc,KAAK,kBAAkB,YAAY,GAAG;AACtD,YAAI;AAAA,UACF,EAAE,QAAQ,GAAG,MAAM,cAAc,IAAI,GAAG,EAAE,IAAI,iBAAiB,GAAG;AAAA,UAClE;AAAA,YACE,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AASO,MAAM,sBAAsB,CAAC,wBAAkC;AACpE,QAAM,MAAM,OAAO,EAAE;AAErB,QAAM,eAAe,OAAoC,MAAS;AAClE,QAAM,cAAc,OAA2B,MAAS;AAIxD,QAAM,yBAAyB,OAAO,mBAAmB;AACzD,yBAAuB,UAAU;AAEjC,QAAM,iBAAiB,YAAY,MAAM;AACvC,QAAI,CAAC,MAAM,GAAG,GAAG;AACf,yBAAmB,KAAK,cAAc,WAAW;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,qBAAqB;AAAA,IACzB,MACE;AAAA,MACE,CAAC,EAAE,MAAM,MAA0B;AACjC,YAAI,MAAM,GAAG,GAAG;AACd;AAAA,QACF;AAEA,YAAI;AACJ,YAAI;AACF,qBAAW,IACR,sBAAsB,OAAO;AAAA,YAC5B,QAAQ,uBAAuB;AAAA,UACjC,CAAC,EACA,OAAO,CAAC,YAAY,CAAC,oBAAoB,QAAQ,WAAW,EAAE,CAAC;AAAA,QACpE,QAAQ;AACN;AAAA,QACF;AAEA,cAAM,UAAU,WAAW,CAAC,GAAG,OAAO;AACtC,cAAM,qBACJ,CAAC,MAAM,QAAQ,KAAK,SAAS,SAAS,KAAK,CAAC,YAAY,OAAO;AAIjE,YAAI,CAAC,oBAAoB;AACvB,cAAI,UAAU,EAAE,MAAM,SAAS;AAC/B,6BAAmB,KAAK,cAAc,WAAW;AACjD;AAAA,QACF;AAEA,YAAI,UAAU,EAAE,MAAM,SAAS;AAC/B,2BAAmB,KAAK,cAAc,WAAW;AAEjD,cAAM,aAAa,mBAAmB,QAAQ;AAC9C,qBAAa,UAAU,WAAW;AAClC,oBAAY,UAAU,WAAW,MAAM;AAEvC,YAAI,CAAC,YAAY,aAAa,OAAO,GAAG;AACtC,uBAAa,KAAK,YAAY,SAAS,aAAa,OAAO;AAAA,QAC7D;AAAA,MACF;AAAA,MACA;AAAA,MACA,EAAE,UAAU,KAAK;AAAA,IACnB;AAAA,IACF,CAAC,GAAG;AAAA,EACN;AAEA,YAAU,MAAM;AACd,SAAK,GAAG,aAAa,kBAAkB;AACvC,SAAK,GAAG,YAAY,cAAc;AAClC,WAAO,MAAM;AACX,yBAAmB,OAAO;AAC1B,WAAK,IAAI,aAAa,kBAAkB;AACxC,WAAK,IAAI,YAAY,cAAc;AAAA,IACrC;AAAA,EACF,GAAG,CAAC,KAAK,oBAAoB,cAAc,CAAC;AAC9C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useMap } from "@vis.gl/react-maplibre";
|
|
2
|
+
import { useContext } from "react";
|
|
3
|
+
import { LayerIdsContext } from "../contexts/layer-ids.context.js";
|
|
4
|
+
const useLayerBeforeId = (layerId) => {
|
|
5
|
+
const layerIds = useContext(LayerIdsContext);
|
|
6
|
+
const { current: map } = useMap();
|
|
7
|
+
if (!map) {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
const currentIndex = layerIds.indexOf(layerId);
|
|
11
|
+
if (currentIndex === -1) {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
for (let i = currentIndex + 1; i < layerIds.length; i++) {
|
|
15
|
+
if (map.getLayer(layerIds[i])) {
|
|
16
|
+
return layerIds[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
useLayerBeforeId
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=use-layer-before-id.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/map/hooks/use-layer-before-id.ts"],
|
|
4
|
+
"sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { useContext } from 'react';\n\nimport { LayerIdsContext } from '../contexts/layer-ids.context.js';\n\n/**\n * Returns the maplibre layer id that the current layer should be inserted before,\n * based on the JSX order of `MapView` children.\n *\n * Without this, layers that mount asynchronously (e.g. `{apiData && <ChoroplethLayer>}`)\n * are appended to the top of the maplibre stack and end up visually above siblings\n * that mounted earlier \u2014 even when JSX puts them first. Passing the returned value as\n * `beforeId` to a `<Layer>` keeps the rendered stack aligned with JSX order.\n *\n * Only returns an id that actually exists in the map right now, so the result is\n * always safe to pass to `map.addLayer(opts, beforeId)`.\n */\nexport const useLayerBeforeId = (layerId: string): string | undefined => {\n const layerIds = useContext(LayerIdsContext);\n const { current: map } = useMap();\n\n if (!map) {\n return undefined;\n }\n\n const currentIndex = layerIds.indexOf(layerId);\n if (currentIndex === -1) {\n return undefined;\n }\n\n for (let i = currentIndex + 1; i < layerIds.length; i++) {\n if (map.getLayer(layerIds[i])) {\n return layerIds[i];\n }\n }\n\n return undefined;\n};\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAE3B,SAAS,uBAAuB;AAczB,MAAM,mBAAmB,CAAC,YAAwC;AACvE,QAAM,WAAW,WAAW,eAAe;AAC3C,QAAM,EAAE,SAAS,IAAI,IAAI,OAAO;AAEhC,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,SAAS,QAAQ,OAAO;AAC7C,MAAI,iBAAiB,IAAI;AACvB,WAAO;AAAA,EACT;AAEA,WAAS,IAAI,eAAe,GAAG,IAAI,SAAS,QAAQ,KAAK;AACvD,QAAI,IAAI,SAAS,SAAS,CAAC,CAAC,GAAG;AAC7B,aAAO,SAAS,CAAC;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useMap } from "@vis.gl/react-maplibre";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { useWebGLContextError } from "./use-webgl-context-error.js";
|
|
4
|
+
const MAX_RETRIES = 3;
|
|
5
|
+
const ERROR_DEBOUNCE_MS = 500;
|
|
6
|
+
const MAPLIBRE_RUNTIME_ERROR_PATTERNS = [
|
|
7
|
+
"Could not compile fragment shader",
|
|
8
|
+
"Could not compile vertex shader",
|
|
9
|
+
"Program failed to link",
|
|
10
|
+
"feature index out of bounds",
|
|
11
|
+
"Out of bounds. Index requested"
|
|
12
|
+
];
|
|
13
|
+
const isMaplibreRuntimeError = (message) => MAPLIBRE_RUNTIME_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
|
|
14
|
+
const useMapRuntimeError = ({
|
|
15
|
+
onError,
|
|
16
|
+
onRetry
|
|
17
|
+
}) => {
|
|
18
|
+
const { current: mapRef } = useMap();
|
|
19
|
+
const retryCountRef = useRef(0);
|
|
20
|
+
const lastErrorTimeRef = useRef(0);
|
|
21
|
+
const isMapActiveRef = useRef(true);
|
|
22
|
+
const onErrorRef = useRef(onError);
|
|
23
|
+
onErrorRef.current = onError;
|
|
24
|
+
const onRetryRef = useRef(onRetry);
|
|
25
|
+
onRetryRef.current = onRetry;
|
|
26
|
+
const handleError = useCallback(() => {
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
if (now - lastErrorTimeRef.current < ERROR_DEBOUNCE_MS) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
lastErrorTimeRef.current = now;
|
|
32
|
+
retryCountRef.current += 1;
|
|
33
|
+
if (retryCountRef.current >= MAX_RETRIES) {
|
|
34
|
+
onErrorRef.current();
|
|
35
|
+
} else {
|
|
36
|
+
onRetryRef.current?.(retryCountRef.current);
|
|
37
|
+
}
|
|
38
|
+
}, []);
|
|
39
|
+
useWebGLContextError(handleError);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const map = mapRef?.getMap();
|
|
42
|
+
if (!map) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const handleMapError = ({ error }) => {
|
|
46
|
+
if (isMaplibreRuntimeError(error.message)) {
|
|
47
|
+
handleError();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
map.on("error", handleMapError);
|
|
51
|
+
return () => {
|
|
52
|
+
map.off("error", handleMapError);
|
|
53
|
+
};
|
|
54
|
+
}, [mapRef, handleError]);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const handleWindowError = (event) => {
|
|
57
|
+
if (!isMapActiveRef.current) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (isMaplibreRuntimeError(event.message)) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
handleError();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
window.addEventListener("error", handleWindowError);
|
|
66
|
+
return () => {
|
|
67
|
+
window.removeEventListener("error", handleWindowError);
|
|
68
|
+
};
|
|
69
|
+
}, [handleError]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const map = mapRef?.getMap();
|
|
72
|
+
if (!map) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const handleRender = () => {
|
|
76
|
+
isMapActiveRef.current = true;
|
|
77
|
+
};
|
|
78
|
+
const handleIdle = () => {
|
|
79
|
+
isMapActiveRef.current = false;
|
|
80
|
+
retryCountRef.current = 0;
|
|
81
|
+
};
|
|
82
|
+
map.on("render", handleRender);
|
|
83
|
+
map.on("idle", handleIdle);
|
|
84
|
+
return () => {
|
|
85
|
+
map.off("render", handleRender);
|
|
86
|
+
map.off("idle", handleIdle);
|
|
87
|
+
};
|
|
88
|
+
}, [mapRef]);
|
|
89
|
+
};
|
|
90
|
+
export {
|
|
91
|
+
useMapRuntimeError
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=use-map-runtime-error.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/map/hooks/use-map-runtime-error.ts"],
|
|
4
|
+
"sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { useWebGLContextError } from './use-webgl-context-error.js';\n\nconst MAX_RETRIES = 3;\n\n/** Prevents a burst of identical errors from exhausting the retry budget in one frame. */\nconst ERROR_DEBOUNCE_MS = 500;\n\n/**\n * Patterns that identify errors thrown from the maplibre-gl internal render\n * pipeline (shader compilation, program linking, vector-tile feature decoding).\n * These originate from rAF callbacks or from maplibre's own error event and\n * are not caused by consumer code.\n *\n * APPDEV-17938\n */\nconst MAPLIBRE_RUNTIME_ERROR_PATTERNS: readonly string[] = [\n 'Could not compile fragment shader',\n 'Could not compile vertex shader',\n 'Program failed to link',\n 'feature index out of bounds',\n 'Out of bounds. Index requested',\n];\n\nconst isMaplibreRuntimeError = (message: string): boolean =>\n MAPLIBRE_RUNTIME_ERROR_PATTERNS.some((pattern) => message.includes(pattern));\n\nexport interface UseMapRuntimeErrorOptions {\n /**\n * Called when the retry budget (3 attempts) is exhausted and the map cannot\n * recover. Throwing inside this callback will propagate to the nearest React\n * ErrorBoundary, showing the fallback UI.\n */\n onError: () => void;\n /**\n * Optional callback invoked on each recoverable error attempt before the\n * budget is exhausted. The `attempt` argument is 1-indexed.\n */\n onRetry?: (attempt: number) => void;\n}\n\n/**\n * Unified hook that guards MapView against maplibre-gl runtime errors from\n * three distinct sources:\n *\n * 1. **WebGL context loss** (`webglcontextlost` on the canvas) \u2014 composed via\n * `useWebGLContextError`.\n * 2. **maplibre's own error events** (`map.on('error', ...)`), covering tile\n * load failures and other internally dispatched errors.\n * 3. **Synchronous throws from the rAF render loop** that escape to\n * `window.onerror` \u2014 shader compilation, program link, and feature-index\n * out-of-bounds errors that maplibre does not catch internally.\n *\n * A shared retry counter (max 3) is maintained across all sources. Error\n * counting is debounced at 500 ms so that a burst of errors within a single\n * render frame counts as one occurrence. The counter resets whenever the map\n * fires an `idle` event (indicating a successful render). Once the budget is\n * exhausted, `onError` is invoked.\n *\n * APPDEV-17938\n */\nexport const useMapRuntimeError = ({\n onError,\n onRetry,\n}: UseMapRuntimeErrorOptions) => {\n const { current: mapRef } = useMap();\n const retryCountRef = useRef(0);\n const lastErrorTimeRef = useRef(0);\n // Tracks whether this map instance is actively rendering. Without this guard,\n // all mounted <MapView> instances would increment on a single window error\n // event. Initialised to true so early errors (before first idle) are caught.\n const isMapActiveRef = useRef(true);\n\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const onRetryRef = useRef(onRetry);\n onRetryRef.current = onRetry;\n\n const handleError = useCallback(() => {\n const now = Date.now();\n\n if (now - lastErrorTimeRef.current < ERROR_DEBOUNCE_MS) {\n return;\n }\n\n lastErrorTimeRef.current = now;\n retryCountRef.current += 1;\n\n if (retryCountRef.current >= MAX_RETRIES) {\n onErrorRef.current();\n } else {\n onRetryRef.current?.(retryCountRef.current);\n }\n }, []);\n\n useWebGLContextError(handleError);\n\n useEffect(() => {\n const map = mapRef?.getMap();\n if (!map) {\n return;\n }\n\n const handleMapError = ({ error }: { error: Error }) => {\n if (isMaplibreRuntimeError(error.message)) {\n handleError();\n }\n };\n\n map.on('error', handleMapError);\n return () => {\n map.off('error', handleMapError);\n };\n }, [mapRef, handleError]);\n\n // rAF render-loop throws escape to window because they run outside React's\n // render cycle and are not caught by component-level error boundaries.\n useEffect(() => {\n const handleWindowError = (event: ErrorEvent) => {\n if (!isMapActiveRef.current) {\n return;\n }\n if (isMaplibreRuntimeError(event.message)) {\n // Stops the red console overlay in dev and top-level error boundaries.\n event.preventDefault();\n handleError();\n }\n };\n\n window.addEventListener('error', handleWindowError);\n return () => {\n window.removeEventListener('error', handleWindowError);\n };\n }, [handleError]);\n\n useEffect(() => {\n const map = mapRef?.getMap();\n if (!map) {\n return;\n }\n\n const handleRender = () => {\n isMapActiveRef.current = true;\n };\n\n const handleIdle = () => {\n isMapActiveRef.current = false;\n retryCountRef.current = 0;\n };\n\n map.on('render', handleRender);\n map.on('idle', handleIdle);\n return () => {\n map.off('render', handleRender);\n map.off('idle', handleIdle);\n };\n }, [mapRef]);\n};\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,aAAa,WAAW,cAAc;AAE/C,SAAS,4BAA4B;AAErC,MAAM,cAAc;AAGpB,MAAM,oBAAoB;AAU1B,MAAM,kCAAqD;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,yBAAyB,CAAC,YAC9B,gCAAgC,KAAK,CAAC,YAAY,QAAQ,SAAS,OAAO,CAAC;AAoCtE,MAAM,qBAAqB,CAAC;AAAA,EACjC;AAAA,EACA;AACF,MAAiC;AAC/B,QAAM,EAAE,SAAS,OAAO,IAAI,OAAO;AACnC,QAAM,gBAAgB,OAAO,CAAC;AAC9B,QAAM,mBAAmB,OAAO,CAAC;AAIjC,QAAM,iBAAiB,OAAO,IAAI;AAElC,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,cAAc,YAAY,MAAM;AACpC,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,MAAM,iBAAiB,UAAU,mBAAmB;AACtD;AAAA,IACF;AAEA,qBAAiB,UAAU;AAC3B,kBAAc,WAAW;AAEzB,QAAI,cAAc,WAAW,aAAa;AACxC,iBAAW,QAAQ;AAAA,IACrB,OAAO;AACL,iBAAW,UAAU,cAAc,OAAO;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,uBAAqB,WAAW;AAEhC,YAAU,MAAM;AACd,UAAM,MAAM,QAAQ,OAAO;AAC3B,QAAI,CAAC,KAAK;AACR;AAAA,IACF;AAEA,UAAM,iBAAiB,CAAC,EAAE,MAAM,MAAwB;AACtD,UAAI,uBAAuB,MAAM,OAAO,GAAG;AACzC,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,QAAI,GAAG,SAAS,cAAc;AAC9B,WAAO,MAAM;AACX,UAAI,IAAI,SAAS,cAAc;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,CAAC;AAIxB,YAAU,MAAM;AACd,UAAM,oBAAoB,CAAC,UAAsB;AAC/C,UAAI,CAAC,eAAe,SAAS;AAC3B;AAAA,MACF;AACA,UAAI,uBAAuB,MAAM,OAAO,GAAG;AAEzC,cAAM,eAAe;AACrB,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,WAAO,iBAAiB,SAAS,iBAAiB;AAClD,WAAO,MAAM;AACX,aAAO,oBAAoB,SAAS,iBAAiB;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,YAAU,MAAM;AACd,UAAM,MAAM,QAAQ,OAAO;AAC3B,QAAI,CAAC,KAAK;AACR;AAAA,IACF;AAEA,UAAM,eAAe,MAAM;AACzB,qBAAe,UAAU;AAAA,IAC3B;AAEA,UAAM,aAAa,MAAM;AACvB,qBAAe,UAAU;AACzB,oBAAc,UAAU;AAAA,IAC1B;AAEA,QAAI,GAAG,UAAU,YAAY;AAC7B,QAAI,GAAG,QAAQ,UAAU;AACzB,WAAO,MAAM;AACX,UAAI,IAAI,UAAU,YAAY;AAC9B,UAAI,IAAI,QAAQ,UAAU;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AACb;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
1
2
|
import {
|
|
2
3
|
_useOverlayTooltipReducer as useTooltipReducer,
|
|
3
4
|
_useOverlayTooltipStore as useOverlayTooltipStore,
|
|
4
5
|
_useOverlayChart as useOverlayChart
|
|
5
6
|
} from "@dynatrace/strato-components/charts";
|
|
7
|
+
import { GeoDataLookupContext } from "../contexts/geo-data-lookup.context.js";
|
|
6
8
|
import { useSetStateOverlay, useSetState } from "../store/store.js";
|
|
7
9
|
import { buildGeoTooltipState } from "../utils/build-geo-tooltip-state.js";
|
|
8
10
|
import { extractDataFromEvent } from "../utils/parse-tooltip-data.js";
|
|
@@ -19,6 +21,7 @@ const layerIdToGeometry = (layerId) => {
|
|
|
19
21
|
return "geoDot";
|
|
20
22
|
};
|
|
21
23
|
const useOverlayEvents = () => {
|
|
24
|
+
const dataLookupRegistry = useContext(GeoDataLookupContext);
|
|
22
25
|
const setOverlayState = useSetStateOverlay();
|
|
23
26
|
const setState = useSetState();
|
|
24
27
|
const dispatch = useTooltipReducer();
|
|
@@ -53,7 +56,10 @@ const useOverlayEvents = () => {
|
|
|
53
56
|
if (currentState.pinned) {
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
|
-
const { data, hoveredLayerId } = extractDataFromEvent(
|
|
59
|
+
const { data, hoveredLayerId } = extractDataFromEvent(
|
|
60
|
+
event,
|
|
61
|
+
dataLookupRegistry
|
|
62
|
+
);
|
|
57
63
|
overlay.clear();
|
|
58
64
|
setTooltipMarker(hoveredLayerId, event.lngLat);
|
|
59
65
|
const pos = getAbsolutePosition(event);
|
|
@@ -83,7 +89,10 @@ const useOverlayEvents = () => {
|
|
|
83
89
|
}
|
|
84
90
|
};
|
|
85
91
|
const handleMouseClick = (event) => {
|
|
86
|
-
const { data, featureId, hoveredLayerId } = extractDataFromEvent(
|
|
92
|
+
const { data, featureId, hoveredLayerId } = extractDataFromEvent(
|
|
93
|
+
event,
|
|
94
|
+
dataLookupRegistry
|
|
95
|
+
);
|
|
87
96
|
if (!featureId) {
|
|
88
97
|
hideTooltip();
|
|
89
98
|
return;
|