@dynatrace/strato-geo 3.7.1 → 3.8.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.
@@ -66,7 +66,7 @@ const MapToolbar = () => {
66
66
  initialA11yState: initialA11yToolbarTabIndexContext,
67
67
  onMouseEnter: handleMouseEnter,
68
68
  onMouseLeave: handleMouseLeave,
69
- menuDisplayMode: "never",
69
+ menuDisplayMode: "collapsed",
70
70
  children: [
71
71
  isZoomEnabled && /* @__PURE__ */ jsx(MapZoomInOutButtons, {}),
72
72
  isZoomEnabled && /* @__PURE__ */ jsx(MapResetButton, {}),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/map/components/toolbar/MapToolbar.tsx"],
4
- "sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\n\nimport {\n _ChartToolbarRenderer as ChartToolbarRenderer,\n _useOverlayTooltipReducer as useTooltipReducer,\n} from '@dynatrace/strato-components/charts';\n\nimport { MapDownloadDataButton } from './buttons/MapDownloadDataButton.js';\nimport { MapResetButton } from './buttons/MapResetButton.js';\nimport { MapZoomInOutButtons } from './buttons/MapZoomInOutButtons.js';\nimport { MapZoomToFitButton } from './buttons/MapZoomToFitButton.js';\nimport { initialA11yToolbarTabIndexContext } from './constants.js';\nimport { useMapConfig } from '../../hooks/use-map-config.js';\nimport { useMapRawData } from '../../hooks/use-map-raw-data.js';\nimport { MapInitialViewProvider } from '../../providers/map-initial-view.provider.js';\nimport { useMapOverlayState } from '../../store/selectors.js';\nimport { useSetState } from '../../store/store.js';\nimport { buildAndDownloadCsv } from '../../utils/build-and-download-csv.js';\n\nconst defaultToolbarPlacement = 'top-right';\n\nexport const MapToolbar = () => {\n const { toolbar: toolbarConfig, interactions: interactionsConfig } =\n useMapConfig();\n const data = useMapRawData();\n\n const { current: map } = useMap();\n const { mouseInBounds } = useMapOverlayState();\n const setState = useSetState();\n const dispatch = useTooltipReducer();\n const isZoomEnabled =\n toolbarConfig?.zoom?.enabled ?? interactionsConfig?.zoom?.enabled ?? false;\n const isZoomToFitEnabled =\n toolbarConfig?.zoom?.enabled ??\n interactionsConfig?.zoomToFit?.enabled ??\n false;\n const isDownloadDataEnabled = toolbarConfig?.downloadCSV?.enabled ?? false;\n\n const hasZoomControls = isZoomEnabled || isZoomToFitEnabled;\n const hasControls = hasZoomControls || isDownloadDataEnabled;\n const hasOnlyDownloadCSV = isDownloadDataEnabled && !hasZoomControls;\n\n const handleMouseEnter = () => {\n dispatch({ type: 'RESET_TOOLTIP' })();\n if (map) {\n map.queryRenderedFeatures().forEach((feature) => {\n if (feature.id !== undefined && feature.layer.source) {\n map.setFeatureState(\n { source: feature.layer.source, id: feature.id },\n { active: false, isAnyActive: false },\n );\n }\n });\n }\n setState((prev) => ({\n ...prev,\n }));\n };\n\n const handleMouseLeave = () => {\n setState((prev) => ({\n ...prev,\n tooltip: { ...prev.tooltip, enabled: true },\n }));\n };\n\n const handleDownloadDataToolbar = () => {\n buildAndDownloadCsv(data);\n };\n\n return hasControls ? (\n <MapInitialViewProvider>\n <ChartToolbarRenderer\n visible={mouseInBounds}\n collapsed={toolbarConfig?.collapsed}\n draggable={toolbarConfig?.draggable}\n placement={toolbarConfig?.placement ?? defaultToolbarPlacement}\n handleDownloadDataToolbar={handleDownloadDataToolbar}\n isDownloadEnabled={!hasOnlyDownloadCSV && isDownloadDataEnabled}\n initialA11yState={initialA11yToolbarTabIndexContext}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n menuDisplayMode=\"never\"\n >\n {isZoomEnabled && <MapZoomInOutButtons />}\n {isZoomEnabled && <MapResetButton />}\n {isZoomToFitEnabled && <MapZoomToFitButton />}\n {isDownloadDataEnabled && <MapDownloadDataButton />}\n </ChartToolbarRenderer>\n </MapInitialViewProvider>\n ) : null;\n};\n\nMapToolbar['displayName'] = 'MapToolbar';\n"],
4
+ "sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\n\nimport {\n _ChartToolbarRenderer as ChartToolbarRenderer,\n _useOverlayTooltipReducer as useTooltipReducer,\n} from '@dynatrace/strato-components/charts';\n\nimport { MapDownloadDataButton } from './buttons/MapDownloadDataButton.js';\nimport { MapResetButton } from './buttons/MapResetButton.js';\nimport { MapZoomInOutButtons } from './buttons/MapZoomInOutButtons.js';\nimport { MapZoomToFitButton } from './buttons/MapZoomToFitButton.js';\nimport { initialA11yToolbarTabIndexContext } from './constants.js';\nimport { useMapConfig } from '../../hooks/use-map-config.js';\nimport { useMapRawData } from '../../hooks/use-map-raw-data.js';\nimport { MapInitialViewProvider } from '../../providers/map-initial-view.provider.js';\nimport { useMapOverlayState } from '../../store/selectors.js';\nimport { useSetState } from '../../store/store.js';\nimport { buildAndDownloadCsv } from '../../utils/build-and-download-csv.js';\n\nconst defaultToolbarPlacement = 'top-right';\n\nexport const MapToolbar = () => {\n const { toolbar: toolbarConfig, interactions: interactionsConfig } =\n useMapConfig();\n const data = useMapRawData();\n\n const { current: map } = useMap();\n const { mouseInBounds } = useMapOverlayState();\n const setState = useSetState();\n const dispatch = useTooltipReducer();\n const isZoomEnabled =\n toolbarConfig?.zoom?.enabled ?? interactionsConfig?.zoom?.enabled ?? false;\n const isZoomToFitEnabled =\n toolbarConfig?.zoom?.enabled ??\n interactionsConfig?.zoomToFit?.enabled ??\n false;\n const isDownloadDataEnabled = toolbarConfig?.downloadCSV?.enabled ?? false;\n\n const hasZoomControls = isZoomEnabled || isZoomToFitEnabled;\n const hasControls = hasZoomControls || isDownloadDataEnabled;\n const hasOnlyDownloadCSV = isDownloadDataEnabled && !hasZoomControls;\n\n const handleMouseEnter = () => {\n dispatch({ type: 'RESET_TOOLTIP' })();\n if (map) {\n map.queryRenderedFeatures().forEach((feature) => {\n if (feature.id !== undefined && feature.layer.source) {\n map.setFeatureState(\n { source: feature.layer.source, id: feature.id },\n { active: false, isAnyActive: false },\n );\n }\n });\n }\n setState((prev) => ({\n ...prev,\n }));\n };\n\n const handleMouseLeave = () => {\n setState((prev) => ({\n ...prev,\n tooltip: { ...prev.tooltip, enabled: true },\n }));\n };\n\n const handleDownloadDataToolbar = () => {\n buildAndDownloadCsv(data);\n };\n\n return hasControls ? (\n <MapInitialViewProvider>\n <ChartToolbarRenderer\n visible={mouseInBounds}\n collapsed={toolbarConfig?.collapsed}\n draggable={toolbarConfig?.draggable}\n placement={toolbarConfig?.placement ?? defaultToolbarPlacement}\n handleDownloadDataToolbar={handleDownloadDataToolbar}\n isDownloadEnabled={!hasOnlyDownloadCSV && isDownloadDataEnabled}\n initialA11yState={initialA11yToolbarTabIndexContext}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n menuDisplayMode=\"collapsed\"\n >\n {isZoomEnabled && <MapZoomInOutButtons />}\n {isZoomEnabled && <MapResetButton />}\n {isZoomToFitEnabled && <MapZoomToFitButton />}\n {isDownloadDataEnabled && <MapDownloadDataButton />}\n </ChartToolbarRenderer>\n </MapInitialViewProvider>\n ) : null;\n};\n\nMapToolbar['displayName'] = 'MapToolbar';\n"],
5
5
  "mappings": "AAwEM,SAYoB,KAZpB;AAxEN,SAAS,cAAc;AAEvB;AAAA,EACE,yBAAyB;AAAA,EACzB,6BAA6B;AAAA,OACxB;AAEP,SAAS,6BAA6B;AACtC,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,0BAA0B;AACnC,SAAS,yCAAyC;AAClD,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B,SAAS,2BAA2B;AAEpC,MAAM,0BAA0B;AAEzB,MAAM,aAAa,MAAM;AAC9B,QAAM,EAAE,SAAS,eAAe,cAAc,mBAAmB,IAC/D,aAAa;AACf,QAAM,OAAO,cAAc;AAE3B,QAAM,EAAE,SAAS,IAAI,IAAI,OAAO;AAChC,QAAM,EAAE,cAAc,IAAI,mBAAmB;AAC7C,QAAM,WAAW,YAAY;AAC7B,QAAM,WAAW,kBAAkB;AACnC,QAAM,gBACJ,eAAe,MAAM,WAAW,oBAAoB,MAAM,WAAW;AACvE,QAAM,qBACJ,eAAe,MAAM,WACrB,oBAAoB,WAAW,WAC/B;AACF,QAAM,wBAAwB,eAAe,aAAa,WAAW;AAErE,QAAM,kBAAkB,iBAAiB;AACzC,QAAM,cAAc,mBAAmB;AACvC,QAAM,qBAAqB,yBAAyB,CAAC;AAErD,QAAM,mBAAmB,MAAM;AAC7B,aAAS,EAAE,MAAM,gBAAgB,CAAC,EAAE;AACpC,QAAI,KAAK;AACP,UAAI,sBAAsB,EAAE,QAAQ,CAAC,YAAY;AAC/C,YAAI,QAAQ,OAAO,UAAa,QAAQ,MAAM,QAAQ;AACpD,cAAI;AAAA,YACF,EAAE,QAAQ,QAAQ,MAAM,QAAQ,IAAI,QAAQ,GAAG;AAAA,YAC/C,EAAE,QAAQ,OAAO,aAAa,MAAM;AAAA,UACtC;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AACA,aAAS,CAAC,UAAU;AAAA,MAClB,GAAG;AAAA,IACL,EAAE;AAAA,EACJ;AAEA,QAAM,mBAAmB,MAAM;AAC7B,aAAS,CAAC,UAAU;AAAA,MAClB,GAAG;AAAA,MACH,SAAS,EAAE,GAAG,KAAK,SAAS,SAAS,KAAK;AAAA,IAC5C,EAAE;AAAA,EACJ;AAEA,QAAM,4BAA4B,MAAM;AACtC,wBAAoB,IAAI;AAAA,EAC1B;AAEA,SAAO,cACL,oBAAC,0BACC;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,WAAW,eAAe;AAAA,MAC1B,WAAW,eAAe;AAAA,MAC1B,WAAW,eAAe,aAAa;AAAA,MACvC;AAAA,MACA,mBAAmB,CAAC,sBAAsB;AAAA,MAC1C,kBAAkB;AAAA,MAClB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,iBAAgB;AAAA,MAEf;AAAA,yBAAiB,oBAAC,uBAAoB;AAAA,QACtC,iBAAiB,oBAAC,kBAAe;AAAA,QACjC,sBAAsB,oBAAC,sBAAmB;AAAA,QAC1C,yBAAyB,oBAAC,yBAAsB;AAAA;AAAA;AAAA,EACnD,GACF,IACE;AACN;AAEA,WAAW,aAAa,IAAI;",
6
6
  "names": []
7
7
  }
@@ -1,13 +1,38 @@
1
1
  import { useMap } from "@vis.gl/react-maplibre";
2
2
  import { isNil, isUndefined } from "lodash-es";
3
3
  import { useCallback, useEffect, useRef } from "react";
4
+ import { useOverlayTooltipSelector } from "./use-geo-tooltip-sync.js";
4
5
  import { getMinValueFeature } from "../utils/get-min-value-feature.js";
5
6
  const useActiveInteraction = (interactiveLayerIds) => {
6
7
  const map = useMap().current;
7
8
  const featureIdRef = useRef(void 0);
8
9
  const sourceIdRef = useRef(void 0);
10
+ const pinnedFromStore = useOverlayTooltipSelector((state) => state.pinned);
11
+ const prevPinnedRef = useRef(false);
9
12
  const interactiveLayerIdsRef = useRef(interactiveLayerIds);
10
13
  interactiveLayerIdsRef.current = interactiveLayerIds;
14
+ const clearActiveFeatureStates = () => {
15
+ if (!isUndefined(featureIdRef.current) && !isUndefined(sourceIdRef.current)) {
16
+ map.setFeatureState(
17
+ { source: sourceIdRef.current, id: featureIdRef.current },
18
+ { active: false }
19
+ );
20
+ try {
21
+ const allFeatures = map.queryRenderedFeatures(void 0, {
22
+ layers: interactiveLayerIdsRef.current
23
+ });
24
+ allFeatures.forEach((feature) => {
25
+ if (feature.id !== void 0 && feature.layer.source !== void 0) {
26
+ map.setFeatureState(
27
+ { source: feature.layer.source, id: feature.id },
28
+ { isAnyActive: false }
29
+ );
30
+ }
31
+ });
32
+ } catch {
33
+ }
34
+ }
35
+ };
11
36
  const handleClick = useCallback(
12
37
  ({ point }) => {
13
38
  let features = [];
@@ -70,30 +95,20 @@ const useActiveInteraction = (interactiveLayerIds) => {
70
95
  const onEscapeKeyPressedHandler = useCallback(
71
96
  (event) => {
72
97
  if (event.key === "Escape") {
73
- if (!isUndefined(featureIdRef.current) && !isUndefined(sourceIdRef.current)) {
74
- map.setFeatureState(
75
- { source: sourceIdRef.current, id: featureIdRef.current },
76
- { active: false }
77
- );
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
- }
92
- }
98
+ clearActiveFeatureStates();
93
99
  }
94
100
  },
95
101
  [map]
96
102
  );
103
+ useEffect(() => {
104
+ const wasJustUnpinned = prevPinnedRef.current && !pinnedFromStore;
105
+ prevPinnedRef.current = pinnedFromStore;
106
+ if (wasJustUnpinned) {
107
+ clearActiveFeatureStates();
108
+ featureIdRef.current = void 0;
109
+ sourceIdRef.current = void 0;
110
+ }
111
+ }, [pinnedFromStore, map]);
97
112
  useEffect(() => {
98
113
  map.on("click", handleClick);
99
114
  window.addEventListener("keyup", onEscapeKeyPressedHandler);
@@ -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, 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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",
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 { useOverlayTooltipSelector } from './use-geo-tooltip-sync.js';\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 const pinnedFromStore = useOverlayTooltipSelector((state) => state.pinned);\n const prevPinnedRef = useRef(false);\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 clearActiveFeatureStates = () => {\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 (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 } catch {\n // falls through \u2014 active state is already cleared above\n }\n }\n };\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 clearActiveFeatureStates();\n }\n },\n [map],\n );\n\n useEffect(() => {\n const wasJustUnpinned = prevPinnedRef.current && !pinnedFromStore;\n prevPinnedRef.current = pinnedFromStore;\n\n if (wasJustUnpinned) {\n clearActiveFeatureStates();\n featureIdRef.current = undefined;\n sourceIdRef.current = undefined;\n }\n }, [pinnedFromStore, map]);\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
  }
@@ -1,16 +1,16 @@
1
1
  import { useMap } from "@vis.gl/react-maplibre";
2
- import { isString, isNil } from "lodash-es";
2
+ import { isString } from "lodash-es";
3
+ import { useEffect } from "react";
3
4
  import { useAttachImageFromIcon } from "./use-attach-image-from-icon.js";
4
5
  import { attachImageFromString } from "../utils/attach-image-from-string.js";
5
6
  const useAttachSymbolToMap = (icon, suffix = "", outputSize) => {
6
7
  const { current: map } = useMap();
7
8
  const customIconAttached = useAttachImageFromIcon(icon, suffix, outputSize);
8
- if (isNil(map)) {
9
- return null;
10
- }
11
- if (!customIconAttached && isString(icon)) {
12
- attachImageFromString(map, icon, suffix, outputSize);
13
- }
9
+ useEffect(() => {
10
+ if (!customIconAttached && isString(icon) && map) {
11
+ attachImageFromString(map, icon, suffix, outputSize);
12
+ }
13
+ }, [customIconAttached, icon, map, outputSize, suffix]);
14
14
  };
15
15
  export {
16
16
  useAttachSymbolToMap
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/map/hooks/use-attach-symbol-to-map.ts"],
4
- "sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { isString, isNil } from 'lodash-es';\nimport { type ReactNode } from 'react';\n\nimport { useAttachImageFromIcon } from './use-attach-image-from-icon.js';\nimport type { MapShape } from '../types/shapes.js';\nimport { attachImageFromString } from '../utils/attach-image-from-string.js';\n\nexport const useAttachSymbolToMap = (\n icon: string | MapShape | ReactNode,\n suffix: string = '',\n outputSize?: number,\n) => {\n const { current: map } = useMap();\n const customIconAttached = useAttachImageFromIcon(icon, suffix, outputSize);\n\n if (isNil(map)) {\n return null;\n }\n\n if (!customIconAttached && isString(icon)) {\n attachImageFromString(map, icon, suffix, outputSize);\n }\n};\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,UAAU,aAAa;AAGhC,SAAS,8BAA8B;AAEvC,SAAS,6BAA6B;AAE/B,MAAM,uBAAuB,CAClC,MACA,SAAiB,IACjB,eACG;AACH,QAAM,EAAE,SAAS,IAAI,IAAI,OAAO;AAChC,QAAM,qBAAqB,uBAAuB,MAAM,QAAQ,UAAU;AAE1E,MAAI,MAAM,GAAG,GAAG;AACd,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,sBAAsB,SAAS,IAAI,GAAG;AACzC,0BAAsB,KAAK,MAAM,QAAQ,UAAU;AAAA,EACrD;AACF;",
4
+ "sourcesContent": ["import { useMap } from '@vis.gl/react-maplibre';\nimport { isString } from 'lodash-es';\nimport { useEffect, type ReactNode } from 'react';\n\nimport { useAttachImageFromIcon } from './use-attach-image-from-icon.js';\nimport type { MapShape } from '../types/shapes.js';\nimport { attachImageFromString } from '../utils/attach-image-from-string.js';\n\nexport const useAttachSymbolToMap = (\n icon: string | MapShape | ReactNode,\n suffix: string = '',\n outputSize?: number,\n) => {\n const { current: map } = useMap();\n const customIconAttached = useAttachImageFromIcon(icon, suffix, outputSize);\n\n useEffect(() => {\n if (!customIconAttached && isString(icon) && map) {\n attachImageFromString(map, icon, suffix, outputSize);\n }\n }, [customIconAttached, icon, map, outputSize, suffix]);\n};\n"],
5
+ "mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,iBAAiC;AAE1C,SAAS,8BAA8B;AAEvC,SAAS,6BAA6B;AAE/B,MAAM,uBAAuB,CAClC,MACA,SAAiB,IACjB,eACG;AACH,QAAM,EAAE,SAAS,IAAI,IAAI,OAAO;AAChC,QAAM,qBAAqB,uBAAuB,MAAM,QAAQ,UAAU;AAE1E,YAAU,MAAM;AACd,QAAI,CAAC,sBAAsB,SAAS,IAAI,KAAK,KAAK;AAChD,4BAAsB,KAAK,MAAM,QAAQ,UAAU;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,oBAAoB,MAAM,KAAK,YAAY,MAAM,CAAC;AACxD;",
6
6
  "names": []
7
7
  }
@@ -6,6 +6,7 @@ import {
6
6
  useMemo,
7
7
  useRef
8
8
  } from "react";
9
+ import { useOverlayTooltipSelector } from "./use-geo-tooltip-sync.js";
9
10
  import {
10
11
  getAssociatedFeatures,
11
12
  hasAssociatedFeatures,
@@ -67,6 +68,10 @@ const useHoverInteraction = (interactiveLayerIds) => {
67
68
  const sourceIdRef = useRef(void 0);
68
69
  const interactiveLayerIdsRef = useRef(interactiveLayerIds);
69
70
  interactiveLayerIdsRef.current = interactiveLayerIds;
71
+ const inBoundsFromStore = useOverlayTooltipSelector(
72
+ (state) => state.inBounds
73
+ );
74
+ const prevInBoundsRef = useRef(false);
70
75
  const handleMouseOut = useCallback(() => {
71
76
  if (!isNil(map)) {
72
77
  blurTrackedFeature(map, featureIdRef, sourceIdRef);
@@ -107,6 +112,13 @@ const useHoverInteraction = (interactiveLayerIds) => {
107
112
  ),
108
113
  [map]
109
114
  );
115
+ useEffect(() => {
116
+ const wasJustHidden = prevInBoundsRef.current && !inBoundsFromStore;
117
+ prevInBoundsRef.current = inBoundsFromStore;
118
+ if (wasJustHidden && !isNil(map)) {
119
+ blurTrackedFeature(map, featureIdRef, sourceIdRef);
120
+ }
121
+ }, [inBoundsFromStore, map]);
110
122
  useEffect(() => {
111
123
  map?.on("mousemove", throttledMouseMove);
112
124
  map?.on("mouseout", handleMouseOut);
@@ -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, 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": "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",
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 { useOverlayTooltipSelector } from './use-geo-tooltip-sync.js';\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 inBoundsFromStore = useOverlayTooltipSelector(\n (state) => state.inBounds,\n );\n const prevInBoundsRef = useRef(false);\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 const wasJustHidden = prevInBoundsRef.current && !inBoundsFromStore;\n prevInBoundsRef.current = inBoundsFromStore;\n\n if (wasJustHidden && !isNil(map)) {\n blurTrackedFeature(map, featureIdRef, sourceIdRef);\n }\n }, [inBoundsFromStore, map]);\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": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import { useContext } from "react";
1
+ import { useContext, useRef } from "react";
2
2
  import {
3
3
  _useOverlayTooltipReducer as useTooltipReducer,
4
4
  _useOverlayTooltipStore as useOverlayTooltipStore,
@@ -27,6 +27,7 @@ const useOverlayEvents = () => {
27
27
  const dispatch = useTooltipReducer();
28
28
  const store = useOverlayTooltipStore();
29
29
  const overlay = useOverlayChart();
30
+ const pinnedFeatureIdRef = useRef(void 0);
30
31
  const getAbsolutePosition = (event) => {
31
32
  return {
32
33
  x: Math.round(event.originalEvent.clientX),
@@ -46,6 +47,23 @@ const useOverlayEvents = () => {
46
47
  const hideTooltip = () => {
47
48
  dispatch({ type: "RESET_TOOLTIP" })();
48
49
  setTooltipMarker(void 0, void 0);
50
+ pinnedFeatureIdRef.current = void 0;
51
+ };
52
+ const showHoverTooltip = (event, data, hoveredLayerId) => {
53
+ overlay.clear();
54
+ setTooltipMarker(hoveredLayerId, event.lngLat);
55
+ const pos = getAbsolutePosition(event);
56
+ store.setState(
57
+ buildGeoTooltipState({
58
+ absoluteX: pos.x,
59
+ absoluteY: pos.y,
60
+ relativeX: Math.round(event.point.x),
61
+ relativeY: Math.round(event.point.y),
62
+ pinned: false,
63
+ geometry: layerIdToGeometry(hoveredLayerId),
64
+ metadata: data
65
+ })
66
+ );
49
67
  };
50
68
  const handleMouseEnter = () => {
51
69
  setOverlayState({ mouseInBounds: true });
@@ -60,20 +78,7 @@ const useOverlayEvents = () => {
60
78
  event,
61
79
  dataLookupRegistry
62
80
  );
63
- overlay.clear();
64
- setTooltipMarker(hoveredLayerId, event.lngLat);
65
- const pos = getAbsolutePosition(event);
66
- store.setState(
67
- buildGeoTooltipState({
68
- absoluteX: pos.x,
69
- absoluteY: pos.y,
70
- relativeX: Math.round(event.point.x),
71
- relativeY: Math.round(event.point.y),
72
- pinned: false,
73
- geometry: layerIdToGeometry(hoveredLayerId),
74
- metadata: data
75
- })
76
- );
81
+ showHoverTooltip(event, data ?? [], hoveredLayerId);
77
82
  } else {
78
83
  const currentState = store.getState();
79
84
  if (!currentState.pinned) {
@@ -101,8 +106,15 @@ const useOverlayEvents = () => {
101
106
  hideTooltip();
102
107
  return;
103
108
  }
109
+ const currentState = store.getState();
110
+ if (currentState.pinned && pinnedFeatureIdRef.current === featureId) {
111
+ pinnedFeatureIdRef.current = void 0;
112
+ showHoverTooltip(event, data ?? [], hoveredLayerId);
113
+ return;
114
+ }
104
115
  overlay.clear();
105
116
  setTooltipMarker(hoveredLayerId, event.lngLat);
117
+ pinnedFeatureIdRef.current = featureId;
106
118
  const pos = getAbsolutePosition(event);
107
119
  store.setState(
108
120
  buildGeoTooltipState({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/map/hooks/use-overlay-events.ts"],
4
- "sourcesContent": ["import type { MapLayerMouseEvent } from 'maplibre-gl';\nimport { useContext } from 'react';\n\nimport {\n _useOverlayTooltipReducer as useTooltipReducer,\n _useOverlayTooltipStore as useOverlayTooltipStore,\n _useOverlayChart as useOverlayChart,\n type _OverlayTooltipGeometryTypes as GeometryTypes,\n} from '@dynatrace/strato-components/charts';\n\nimport { GeoDataLookupContext } from '../contexts/geo-data-lookup.context.js';\nimport { useSetStateOverlay, useSetState } from '../store/store.js';\nimport { buildGeoTooltipState } from '../utils/build-geo-tooltip-state.js';\nimport { extractDataFromEvent } from '../utils/parse-tooltip-data.js';\n\nconst layerIdToGeometry = (layerId: string | undefined): GeometryTypes => {\n if (layerId?.includes('bubble')) {\n return 'geoBubble';\n }\n if (layerId?.includes('choropleth')) {\n return 'geoChoropleth';\n }\n if (layerId?.includes('connection')) {\n return 'geoConnection';\n }\n return 'geoDot';\n};\n\nexport const useOverlayEvents = () => {\n const dataLookupRegistry = useContext(GeoDataLookupContext);\n const setOverlayState = useSetStateOverlay();\n const setState = useSetState();\n const dispatch = useTooltipReducer();\n const store = useOverlayTooltipStore();\n const overlay = useOverlayChart();\n\n const getAbsolutePosition = (event: MapLayerMouseEvent) => {\n return {\n x: Math.round(event.originalEvent.clientX),\n y: Math.round(event.originalEvent.clientY),\n };\n };\n\n const setTooltipMarker = (\n hoveredLayerId: string | undefined,\n location: { lng: number; lat: number } | undefined,\n ) => {\n setState((prev) => ({\n ...prev,\n tooltip: {\n ...prev.tooltip,\n hoveredLayerId,\n location: location as typeof prev.tooltip.location,\n },\n }));\n };\n\n const hideTooltip = () => {\n dispatch({ type: 'RESET_TOOLTIP' })();\n setTooltipMarker(undefined, undefined);\n };\n\n const handleMouseEnter = () => {\n setOverlayState({ mouseInBounds: true });\n };\n\n const handleMouseMove = (event: MapLayerMouseEvent) => {\n if (event.features?.length) {\n const currentState = store.getState();\n\n if (currentState.pinned) {\n return;\n }\n\n const { data, hoveredLayerId } = extractDataFromEvent(\n event,\n dataLookupRegistry,\n );\n\n overlay.clear();\n\n setTooltipMarker(hoveredLayerId, event.lngLat);\n\n const pos = getAbsolutePosition(event);\n store.setState(\n buildGeoTooltipState({\n absoluteX: pos.x,\n absoluteY: pos.y,\n relativeX: Math.round(event.point.x),\n relativeY: Math.round(event.point.y),\n pinned: false,\n geometry: layerIdToGeometry(hoveredLayerId),\n metadata: data,\n }),\n );\n } else {\n const currentState = store.getState();\n if (!currentState.pinned) {\n hideTooltip();\n }\n }\n };\n\n const handleMouseLeave = () => {\n setOverlayState({ mouseInBounds: false });\n const currentState = store.getState();\n if (!currentState.pinned) {\n hideTooltip();\n }\n };\n\n const handleMouseClick = (event: MapLayerMouseEvent) => {\n const { data, featureId, hoveredLayerId } = extractDataFromEvent(\n event,\n dataLookupRegistry,\n );\n\n if (!featureId) {\n hideTooltip();\n return;\n }\n\n if (!hoveredLayerId) {\n hideTooltip();\n return;\n }\n\n overlay.clear();\n\n setTooltipMarker(hoveredLayerId, event.lngLat);\n\n const pos = getAbsolutePosition(event);\n store.setState(\n buildGeoTooltipState({\n absoluteX: pos.x,\n absoluteY: pos.y,\n relativeX: Math.round(event.point.x),\n relativeY: Math.round(event.point.y),\n pinned: true,\n geometry: layerIdToGeometry(hoveredLayerId),\n metadata: data,\n }),\n );\n };\n\n const hideTooltipIfNotPinned = () => {\n const currentState = store.getState();\n if (!currentState.pinned) {\n hideTooltip();\n }\n };\n\n return {\n handleMouseEnter,\n handleMouseMove,\n handleMouseLeave,\n handleMouseClick,\n handleZoom: hideTooltipIfNotPinned,\n handleDrag: hideTooltipIfNotPinned,\n };\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { MapLayerMouseEvent } from 'maplibre-gl';\nimport { useContext, useRef } from 'react';\n\nimport {\n _useOverlayTooltipReducer as useTooltipReducer,\n _useOverlayTooltipStore as useOverlayTooltipStore,\n _useOverlayChart as useOverlayChart,\n type _OverlayTooltipGeometryTypes as GeometryTypes,\n} from '@dynatrace/strato-components/charts';\n\nimport { GeoDataLookupContext } from '../contexts/geo-data-lookup.context.js';\nimport { useSetStateOverlay, useSetState } from '../store/store.js';\nimport { buildGeoTooltipState } from '../utils/build-geo-tooltip-state.js';\nimport { extractDataFromEvent } from '../utils/parse-tooltip-data.js';\n\nconst layerIdToGeometry = (layerId: string | undefined): GeometryTypes => {\n if (layerId?.includes('bubble')) {\n return 'geoBubble';\n }\n if (layerId?.includes('choropleth')) {\n return 'geoChoropleth';\n }\n if (layerId?.includes('connection')) {\n return 'geoConnection';\n }\n return 'geoDot';\n};\n\nexport const useOverlayEvents = () => {\n const dataLookupRegistry = useContext(GeoDataLookupContext);\n const setOverlayState = useSetStateOverlay();\n const setState = useSetState();\n const dispatch = useTooltipReducer();\n const store = useOverlayTooltipStore();\n const overlay = useOverlayChart();\n const pinnedFeatureIdRef = useRef<string | undefined>(undefined);\n\n const getAbsolutePosition = (event: MapLayerMouseEvent) => {\n return {\n x: Math.round(event.originalEvent.clientX),\n y: Math.round(event.originalEvent.clientY),\n };\n };\n\n const setTooltipMarker = (\n hoveredLayerId: string | undefined,\n location: { lng: number; lat: number } | undefined,\n ) => {\n setState((prev) => ({\n ...prev,\n tooltip: {\n ...prev.tooltip,\n hoveredLayerId,\n location: location as typeof prev.tooltip.location,\n },\n }));\n };\n\n const hideTooltip = () => {\n dispatch({ type: 'RESET_TOOLTIP' })();\n setTooltipMarker(undefined, undefined);\n pinnedFeatureIdRef.current = undefined;\n };\n\n const showHoverTooltip = (\n event: MapLayerMouseEvent,\n data: unknown[],\n hoveredLayerId: string | undefined,\n ) => {\n overlay.clear();\n\n setTooltipMarker(hoveredLayerId, event.lngLat);\n\n const pos = getAbsolutePosition(event);\n store.setState(\n buildGeoTooltipState({\n absoluteX: pos.x,\n absoluteY: pos.y,\n relativeX: Math.round(event.point.x),\n relativeY: Math.round(event.point.y),\n pinned: false,\n geometry: layerIdToGeometry(hoveredLayerId),\n metadata: data,\n }),\n );\n };\n\n const handleMouseEnter = () => {\n setOverlayState({ mouseInBounds: true });\n };\n\n const handleMouseMove = (event: MapLayerMouseEvent) => {\n if (event.features?.length) {\n const currentState = store.getState();\n\n if (currentState.pinned) {\n return;\n }\n\n const { data, hoveredLayerId } = extractDataFromEvent(\n event,\n dataLookupRegistry,\n );\n\n showHoverTooltip(event, data ?? [], hoveredLayerId);\n } else {\n const currentState = store.getState();\n if (!currentState.pinned) {\n hideTooltip();\n }\n }\n };\n\n const handleMouseLeave = () => {\n setOverlayState({ mouseInBounds: false });\n const currentState = store.getState();\n if (!currentState.pinned) {\n hideTooltip();\n }\n };\n\n const handleMouseClick = (event: MapLayerMouseEvent) => {\n const { data, featureId, hoveredLayerId } = extractDataFromEvent(\n event,\n dataLookupRegistry,\n );\n\n if (!featureId) {\n hideTooltip();\n return;\n }\n\n if (!hoveredLayerId) {\n hideTooltip();\n return;\n }\n\n const currentState = store.getState();\n if (currentState.pinned && pinnedFeatureIdRef.current === featureId) {\n // Unpin and immediately fall back to the hover tooltip so the feature\n // stays described while the pointer is still over it (mirrors the chart\n // OverlayTooltip behaviour on non-touch devices).\n pinnedFeatureIdRef.current = undefined;\n showHoverTooltip(event, data ?? [], hoveredLayerId);\n return;\n }\n\n overlay.clear();\n\n setTooltipMarker(hoveredLayerId, event.lngLat);\n pinnedFeatureIdRef.current = featureId;\n\n const pos = getAbsolutePosition(event);\n store.setState(\n buildGeoTooltipState({\n absoluteX: pos.x,\n absoluteY: pos.y,\n relativeX: Math.round(event.point.x),\n relativeY: Math.round(event.point.y),\n pinned: true,\n geometry: layerIdToGeometry(hoveredLayerId),\n metadata: data,\n }),\n );\n };\n\n const hideTooltipIfNotPinned = () => {\n const currentState = store.getState();\n if (!currentState.pinned) {\n hideTooltip();\n }\n };\n\n return {\n handleMouseEnter,\n handleMouseMove,\n handleMouseLeave,\n handleMouseClick,\n handleZoom: hideTooltipIfNotPinned,\n handleDrag: hideTooltipIfNotPinned,\n };\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_SYMBOL_SIZE } from "../constants.js";
2
- const getScaledSymbolSize = (symbolSize = DEFAULT_SYMBOL_SIZE) => symbolSize * devicePixelRatio;
2
+ const getScaledSymbolSize = (symbolSize = DEFAULT_SYMBOL_SIZE) => Math.max(1, symbolSize * devicePixelRatio);
3
3
  export {
4
4
  getScaledSymbolSize
5
5
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/map/utils/get-scaled-symbol-size.ts"],
4
- "sourcesContent": ["import { DEFAULT_SYMBOL_SIZE } from '../constants.js';\n\n/** Scales the icon size by dpr to preserve the size on HiDPI screens */\nexport const getScaledSymbolSize = (symbolSize = DEFAULT_SYMBOL_SIZE) =>\n symbolSize * devicePixelRatio;\n"],
5
- "mappings": "AAAA,SAAS,2BAA2B;AAG7B,MAAM,sBAAsB,CAAC,aAAa,wBAC/C,aAAa;",
4
+ "sourcesContent": ["import { DEFAULT_SYMBOL_SIZE } from '../constants.js';\n\n/** Scales the icon size by dpr to preserve the size on HiDPI screens */\nexport const getScaledSymbolSize = (symbolSize = DEFAULT_SYMBOL_SIZE) =>\n Math.max(1, symbolSize * devicePixelRatio);\n"],
5
+ "mappings": "AAAA,SAAS,2BAA2B;AAG7B,MAAM,sBAAsB,CAAC,aAAa,wBAC/C,KAAK,IAAI,GAAG,aAAa,gBAAgB;",
6
6
  "names": []
7
7
  }
@@ -1,34 +1,34 @@
1
1
  {
2
2
  "11eDVd4MkXWk8Bok": {
3
- "notes": "Text in a tooltip shown when hovering the \"Zoom in\" action in the geo map chart toolbar",
4
- "translation": "Zoom in"
3
+ "translation": "Zoom in",
4
+ "notes": "Text in a tooltip shown when hovering the \"Zoom in\" action in the geo map chart toolbar"
5
5
  },
6
6
  "5DTYqeFoXLMaXUHR": {
7
- "notes": "Text in a tooltip shown when hovering the \"Zoom out\" action in the geo map chart toolbar",
8
- "translation": "Zoom out"
7
+ "translation": "Zoom out",
8
+ "notes": "Text in a tooltip shown when hovering the \"Zoom out\" action in the geo map chart toolbar"
9
9
  },
10
10
  "EhcEZc1+Xc9wYsXg": {
11
- "notes": "Text in a tooltip shown when hovering the \"Zoom to fit\" action in the geo map chart toolbar",
12
- "translation": "Zoom to fit"
11
+ "translation": "Zoom to fit",
12
+ "notes": "Text in a tooltip shown when hovering the \"Zoom to fit\" action in the geo map chart toolbar"
13
13
  },
14
14
  "TXZNTtB5qReH8XE3": {
15
- "notes": "Message to display when all maps are hidden.",
16
- "translation": "Maps have been disabled in your environment - to enable these, please reach out to your environment administrator."
15
+ "translation": "Maps have been disabled in your environment - to enable these, please reach out to your environment administrator.",
16
+ "notes": "Message to display when all maps are hidden."
17
17
  },
18
18
  "g2qESM+ibB5sUyjf": {
19
- "notes": "Text in a tooltip shown when hovering a tooltip item and hovering the copy button next to its coordinates",
20
- "translation": "Copy coordinates"
19
+ "translation": "Copy coordinates",
20
+ "notes": "Text in a tooltip shown when hovering a tooltip item and hovering the copy button next to its coordinates"
21
21
  },
22
22
  "hVE7x0G0Q7/ih93A": {
23
- "notes": "Text in a tooltip shown when hovering the \"Reset\" action in the geo map chart toolbar",
24
- "translation": "Reset"
23
+ "translation": "Reset",
24
+ "notes": "Text in a tooltip shown when hovering the \"Reset\" action in the geo map chart toolbar"
25
25
  },
26
26
  "hlwWx47trlDMOy+V": {
27
- "notes": "Text in a tooltip shown when hovering the \"Download Data\" action in the geo map chart toolbar",
28
- "translation": "Download Data as CSV"
27
+ "translation": "Download Data as CSV",
28
+ "notes": "Text in a tooltip shown when hovering the \"Download Data\" action in the geo map chart toolbar"
29
29
  },
30
30
  "tS7epcIvtrbIhuBy": {
31
- "notes": "Message to display when an error occurred while getting map data.",
32
- "translation": "An error occurred: Failed to load map data."
31
+ "translation": "An error occurred: Failed to load map data.",
32
+ "notes": "Message to display when an error occurred while getting map data."
33
33
  }
34
34
  }
@@ -86,7 +86,7 @@ const MapToolbar = () => {
86
86
  initialA11yState: import_constants.initialA11yToolbarTabIndexContext,
87
87
  onMouseEnter: handleMouseEnter,
88
88
  onMouseLeave: handleMouseLeave,
89
- menuDisplayMode: "never",
89
+ menuDisplayMode: "collapsed",
90
90
  children: [
91
91
  isZoomEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MapZoomInOutButtons.MapZoomInOutButtons, {}),
92
92
  isZoomEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MapResetButton.MapResetButton, {}),
@@ -24,13 +24,38 @@ module.exports = __toCommonJS(use_active_interaction_exports);
24
24
  var import_react_maplibre = require("@vis.gl/react-maplibre");
25
25
  var import_lodash_es = require("lodash");
26
26
  var import_react = require("react");
27
+ var import_use_geo_tooltip_sync = require("./use-geo-tooltip-sync.js");
27
28
  var import_get_min_value_feature = require("../utils/get-min-value-feature.js");
28
29
  const useActiveInteraction = (interactiveLayerIds) => {
29
30
  const map = (0, import_react_maplibre.useMap)().current;
30
31
  const featureIdRef = (0, import_react.useRef)(void 0);
31
32
  const sourceIdRef = (0, import_react.useRef)(void 0);
33
+ const pinnedFromStore = (0, import_use_geo_tooltip_sync.useOverlayTooltipSelector)((state) => state.pinned);
34
+ const prevPinnedRef = (0, import_react.useRef)(false);
32
35
  const interactiveLayerIdsRef = (0, import_react.useRef)(interactiveLayerIds);
33
36
  interactiveLayerIdsRef.current = interactiveLayerIds;
37
+ const clearActiveFeatureStates = () => {
38
+ if (!(0, import_lodash_es.isUndefined)(featureIdRef.current) && !(0, import_lodash_es.isUndefined)(sourceIdRef.current)) {
39
+ map.setFeatureState(
40
+ { source: sourceIdRef.current, id: featureIdRef.current },
41
+ { active: false }
42
+ );
43
+ try {
44
+ const allFeatures = map.queryRenderedFeatures(void 0, {
45
+ layers: interactiveLayerIdsRef.current
46
+ });
47
+ allFeatures.forEach((feature) => {
48
+ if (feature.id !== void 0 && feature.layer.source !== void 0) {
49
+ map.setFeatureState(
50
+ { source: feature.layer.source, id: feature.id },
51
+ { isAnyActive: false }
52
+ );
53
+ }
54
+ });
55
+ } catch {
56
+ }
57
+ }
58
+ };
34
59
  const handleClick = (0, import_react.useCallback)(
35
60
  ({ point }) => {
36
61
  let features = [];
@@ -93,30 +118,20 @@ const useActiveInteraction = (interactiveLayerIds) => {
93
118
  const onEscapeKeyPressedHandler = (0, import_react.useCallback)(
94
119
  (event) => {
95
120
  if (event.key === "Escape") {
96
- if (!(0, import_lodash_es.isUndefined)(featureIdRef.current) && !(0, import_lodash_es.isUndefined)(sourceIdRef.current)) {
97
- map.setFeatureState(
98
- { source: sourceIdRef.current, id: featureIdRef.current },
99
- { active: false }
100
- );
101
- try {
102
- const allFeatures = map.queryRenderedFeatures(void 0, {
103
- layers: interactiveLayerIdsRef.current
104
- });
105
- allFeatures.forEach((feature) => {
106
- if (feature.id !== void 0 && feature.layer.source !== void 0) {
107
- map.setFeatureState(
108
- { source: feature.layer.source, id: feature.id },
109
- { isAnyActive: false }
110
- );
111
- }
112
- });
113
- } catch {
114
- }
115
- }
121
+ clearActiveFeatureStates();
116
122
  }
117
123
  },
118
124
  [map]
119
125
  );
126
+ (0, import_react.useEffect)(() => {
127
+ const wasJustUnpinned = prevPinnedRef.current && !pinnedFromStore;
128
+ prevPinnedRef.current = pinnedFromStore;
129
+ if (wasJustUnpinned) {
130
+ clearActiveFeatureStates();
131
+ featureIdRef.current = void 0;
132
+ sourceIdRef.current = void 0;
133
+ }
134
+ }, [pinnedFromStore, map]);
120
135
  (0, import_react.useEffect)(() => {
121
136
  map.on("click", handleClick);
122
137
  window.addEventListener("keyup", onEscapeKeyPressedHandler);
@@ -1,3 +1,3 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { MapShape } from '../types/shapes.js';
3
- export declare const useAttachSymbolToMap: (icon: string | MapShape | ReactNode, suffix?: string, outputSize?: number) => null | undefined;
3
+ export declare const useAttachSymbolToMap: (icon: string | MapShape | ReactNode, suffix?: string, outputSize?: number) => void;
@@ -23,15 +23,15 @@ __export(use_attach_symbol_to_map_exports, {
23
23
  module.exports = __toCommonJS(use_attach_symbol_to_map_exports);
24
24
  var import_react_maplibre = require("@vis.gl/react-maplibre");
25
25
  var import_lodash_es = require("lodash");
26
+ var import_react = require("react");
26
27
  var import_use_attach_image_from_icon = require("./use-attach-image-from-icon.js");
27
28
  var import_attach_image_from_string = require("../utils/attach-image-from-string.js");
28
29
  const useAttachSymbolToMap = (icon, suffix = "", outputSize) => {
29
30
  const { current: map } = (0, import_react_maplibre.useMap)();
30
31
  const customIconAttached = (0, import_use_attach_image_from_icon.useAttachImageFromIcon)(icon, suffix, outputSize);
31
- if ((0, import_lodash_es.isNil)(map)) {
32
- return null;
33
- }
34
- if (!customIconAttached && (0, import_lodash_es.isString)(icon)) {
35
- (0, import_attach_image_from_string.attachImageFromString)(map, icon, suffix, outputSize);
36
- }
32
+ (0, import_react.useEffect)(() => {
33
+ if (!customIconAttached && (0, import_lodash_es.isString)(icon) && map) {
34
+ (0, import_attach_image_from_string.attachImageFromString)(map, icon, suffix, outputSize);
35
+ }
36
+ }, [customIconAttached, icon, map, outputSize, suffix]);
37
37
  };