@dynatrace/strato-geo 3.7.1 → 3.9.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/CHANGELOG.md +2087 -0
- package/esm/map/components/BubbleLayer/BubbleLayerTooltip.js +1 -7
- package/esm/map/components/BubbleLayer/BubbleLayerTooltip.js.map +2 -2
- package/esm/map/components/ChoroplethLayer/ChoroplethLayerTooltip.js +1 -7
- package/esm/map/components/ChoroplethLayer/ChoroplethLayerTooltip.js.map +2 -2
- package/esm/map/components/ConnectionLayer/ConnectionLayerTooltip.js +1 -7
- package/esm/map/components/ConnectionLayer/ConnectionLayerTooltip.js.map +2 -2
- package/esm/map/components/DotLayer/DotLayerTooltip.js +1 -7
- package/esm/map/components/DotLayer/DotLayerTooltip.js.map +2 -2
- package/esm/map/components/toolbar/MapToolbar.js +1 -1
- package/esm/map/components/toolbar/MapToolbar.js.map +1 -1
- package/esm/map/hooks/use-active-interaction.js +35 -20
- package/esm/map/hooks/use-active-interaction.js.map +2 -2
- package/esm/map/hooks/use-attach-symbol-to-map.js +7 -7
- package/esm/map/hooks/use-attach-symbol-to-map.js.map +2 -2
- package/esm/map/hooks/use-hover-interaction.js +12 -0
- package/esm/map/hooks/use-hover-interaction.js.map +2 -2
- package/esm/map/hooks/use-map-runtime-error.js +2 -1
- package/esm/map/hooks/use-map-runtime-error.js.map +2 -2
- package/esm/map/hooks/use-overlay-events.js +27 -15
- package/esm/map/hooks/use-overlay-events.js.map +2 -2
- package/esm/map/utils/get-scaled-symbol-size.js +1 -1
- package/esm/map/utils/get-scaled-symbol-size.js.map +2 -2
- package/lang/uncompiled/en.json +16 -16
- package/map/components/BubbleLayer/BubbleLayerTooltip.js +1 -7
- package/map/components/ChoroplethLayer/ChoroplethLayerTooltip.js +1 -7
- package/map/components/ConnectionLayer/ConnectionLayerTooltip.js +1 -7
- package/map/components/DotLayer/DotLayerTooltip.js +1 -7
- package/map/components/toolbar/MapToolbar.js +1 -1
- package/map/hooks/use-active-interaction.js +35 -20
- package/map/hooks/use-attach-symbol-to-map.d.ts +1 -1
- package/map/hooks/use-attach-symbol-to-map.js +6 -6
- package/map/hooks/use-hover-interaction.js +12 -0
- package/map/hooks/use-map-runtime-error.js +2 -1
- package/map/hooks/use-overlay-events.js +26 -14
- package/map/types/map-view-provider.d.ts +1 -1
- package/map/utils/get-scaled-symbol-size.js +1 -1
- package/package.json +5 -5
|
@@ -32,13 +32,7 @@ const BubbleLayerTooltip = (props) => {
|
|
|
32
32
|
if (isDefaultTooltipHandler(tooltipTemplate)) {
|
|
33
33
|
return template;
|
|
34
34
|
}
|
|
35
|
-
return /* @__PURE__ */ jsx(
|
|
36
|
-
OverlayTooltip,
|
|
37
|
-
{
|
|
38
|
-
legacyTemplate: template,
|
|
39
|
-
symbolAlignment
|
|
40
|
-
}
|
|
41
|
-
);
|
|
35
|
+
return /* @__PURE__ */ jsx(OverlayTooltip, { template, symbolAlignment });
|
|
42
36
|
};
|
|
43
37
|
BubbleLayerTooltip["displayName"] = "BubbleLayerTooltip";
|
|
44
38
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/map/components/BubbleLayer/BubbleLayerTooltip.tsx"],
|
|
4
|
-
"sourcesContent": ["import { sortBy } from 'lodash-es';\n\nimport { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type { Location } from '../../types/location.js';\nimport type {\n BubbleLayerTooltipData,\n BubbleLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface BubbleTooltipStatePayload {\n __color: string;\n __hoveredColor: string;\n __radius: number;\n __lat: number;\n data: Location;\n}\n\nexport interface BubbleLayerTooltipProps {\n tooltipTemplate?: BubbleLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const BubbleLayerTooltip = (props: BubbleLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<BubbleTooltipStatePayload>('geoBubble');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const sanitizedData: BubbleLayerTooltipData[] = data.map((bubbleProps) => {\n const { __color: color, __radius: radius, data: customData } = bubbleProps;\n\n return {\n color,\n radius,\n data: customData,\n };\n });\n\n const sortedBubbles: BubbleLayerTooltipData[] = [\n ...sortBy(sanitizedData, ({ radius }) => radius),\n ];\n const closestPoint = sortedBubbles[0];\n const remainingPoints = sortedBubbles.slice(1);\n\n const template = closestPoint\n ? tooltipTemplate(closestPoint, remainingPoints)\n : null;\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip
|
|
5
|
-
"mappings": "AAqEI;AArEJ,SAAS,cAAc;AAEvB,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAMnC,SAAS,+BAA+B;AAgBjC,MAAM,qBAAqB,CAAC,UAAmC;AACpE,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAA8C,WAAW;AAE3D,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,gBAA0C,KAAK,IAAI,CAAC,gBAAgB;AACxE,UAAM,EAAE,SAAS,OAAO,UAAU,QAAQ,MAAM,WAAW,IAAI;AAE/D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,QAAM,gBAA0C;AAAA,IAC9C,GAAG,OAAO,eAAe,CAAC,EAAE,OAAO,MAAM,MAAM;AAAA,EACjD;AACA,QAAM,eAAe,cAAc,CAAC;AACpC,QAAM,kBAAkB,cAAc,MAAM,CAAC;AAE7C,QAAM,WAAW,eACb,gBAAgB,cAAc,eAAe,IAC7C;AAEJ,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE
|
|
4
|
+
"sourcesContent": ["import { sortBy } from 'lodash-es';\n\nimport { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type { Location } from '../../types/location.js';\nimport type {\n BubbleLayerTooltipData,\n BubbleLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface BubbleTooltipStatePayload {\n __color: string;\n __hoveredColor: string;\n __radius: number;\n __lat: number;\n data: Location;\n}\n\nexport interface BubbleLayerTooltipProps {\n tooltipTemplate?: BubbleLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const BubbleLayerTooltip = (props: BubbleLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<BubbleTooltipStatePayload>('geoBubble');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const sanitizedData: BubbleLayerTooltipData[] = data.map((bubbleProps) => {\n const { __color: color, __radius: radius, data: customData } = bubbleProps;\n\n return {\n color,\n radius,\n data: customData,\n };\n });\n\n const sortedBubbles: BubbleLayerTooltipData[] = [\n ...sortBy(sanitizedData, ({ radius }) => radius),\n ];\n const closestPoint = sortedBubbles[0];\n const remainingPoints = sortedBubbles.slice(1);\n\n const template = closestPoint\n ? tooltipTemplate(closestPoint, remainingPoints)\n : null;\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip template={template} symbolAlignment={symbolAlignment} />\n );\n};\n\nBubbleLayerTooltip['displayName'] = 'BubbleLayerTooltip';\n"],
|
|
5
|
+
"mappings": "AAqEI;AArEJ,SAAS,cAAc;AAEvB,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAMnC,SAAS,+BAA+B;AAgBjC,MAAM,qBAAqB,CAAC,UAAmC;AACpE,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAA8C,WAAW;AAE3D,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,gBAA0C,KAAK,IAAI,CAAC,gBAAgB;AACxE,UAAM,EAAE,SAAS,OAAO,UAAU,QAAQ,MAAM,WAAW,IAAI;AAE/D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,QAAM,gBAA0C;AAAA,IAC9C,GAAG,OAAO,eAAe,CAAC,EAAE,OAAO,MAAM,MAAM;AAAA,EACjD;AACA,QAAM,eAAe,cAAc,CAAC;AACpC,QAAM,kBAAkB,cAAc,MAAM,CAAC;AAE7C,QAAM,WAAW,eACb,gBAAgB,cAAc,eAAe,IAC7C;AAEJ,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE,oBAAC,kBAAe,UAAoB,iBAAkC;AAE1E;AAEA,mBAAmB,aAAa,IAAI;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -24,13 +24,7 @@ const ChoroplethLayerTooltip = (props) => {
|
|
|
24
24
|
if (isDefaultTooltipHandler(tooltipTemplate)) {
|
|
25
25
|
return template;
|
|
26
26
|
}
|
|
27
|
-
return /* @__PURE__ */ jsx(
|
|
28
|
-
OverlayTooltip,
|
|
29
|
-
{
|
|
30
|
-
legacyTemplate: template,
|
|
31
|
-
symbolAlignment
|
|
32
|
-
}
|
|
33
|
-
);
|
|
27
|
+
return /* @__PURE__ */ jsx(OverlayTooltip, { template, symbolAlignment });
|
|
34
28
|
};
|
|
35
29
|
ChoroplethLayerTooltip["displayName"] = "ChoroplethLayerTooltip";
|
|
36
30
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/map/components/ChoroplethLayer/ChoroplethLayerTooltip.tsx"],
|
|
4
|
-
"sourcesContent": ["import { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type {\n ChoroplethLayerTooltipData,\n ChoroplethLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface ChoroplethTooltipStatePayload {\n name: string;\n region_type: string;\n __color: string;\n __hoveredColor: string;\n data: Record<string, unknown>;\n}\n\nexport interface ChoroplethLayerTooltipProps {\n tooltipTemplate?: ChoroplethLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const ChoroplethLayerTooltip = (props: ChoroplethLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<ChoroplethTooltipStatePayload>('geoChoropleth');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const { name, __color: color, data: customData } = data[0];\n const regionData: ChoroplethLayerTooltipData = {\n data: customData,\n name,\n color,\n };\n\n const template = tooltipTemplate(regionData);\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip
|
|
5
|
-
"mappings": "AAuDI;AAvDJ,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAKnC,SAAS,+BAA+B;AAgBjC,MAAM,yBAAyB,CAAC,UAAuC;AAC5E,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAAkD,eAAe;AAEnE,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,MAAM,SAAS,OAAO,MAAM,WAAW,IAAI,KAAK,CAAC;AACzD,QAAM,aAAyC;AAAA,IAC7C,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW,gBAAgB,UAAU;AAE3C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE
|
|
4
|
+
"sourcesContent": ["import { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type {\n ChoroplethLayerTooltipData,\n ChoroplethLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface ChoroplethTooltipStatePayload {\n name: string;\n region_type: string;\n __color: string;\n __hoveredColor: string;\n data: Record<string, unknown>;\n}\n\nexport interface ChoroplethLayerTooltipProps {\n tooltipTemplate?: ChoroplethLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const ChoroplethLayerTooltip = (props: ChoroplethLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<ChoroplethTooltipStatePayload>('geoChoropleth');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const { name, __color: color, data: customData } = data[0];\n const regionData: ChoroplethLayerTooltipData = {\n data: customData,\n name,\n color,\n };\n\n const template = tooltipTemplate(regionData);\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip template={template} symbolAlignment={symbolAlignment} />\n );\n};\n\nChoroplethLayerTooltip['displayName'] = 'ChoroplethLayerTooltip';\n"],
|
|
5
|
+
"mappings": "AAuDI;AAvDJ,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAKnC,SAAS,+BAA+B;AAgBjC,MAAM,yBAAyB,CAAC,UAAuC;AAC5E,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAAkD,eAAe;AAEnE,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,MAAM,SAAS,OAAO,MAAM,WAAW,IAAI,KAAK,CAAC;AACzD,QAAM,aAAyC;AAAA,IAC7C,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW,gBAAgB,UAAU;AAE3C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE,oBAAC,kBAAe,UAAoB,iBAAkC;AAE1E;AAEA,uBAAuB,aAAa,IAAI;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -28,13 +28,7 @@ const ConnectionLayerTooltip = (props) => {
|
|
|
28
28
|
if (isDefaultTooltipHandler(tooltipTemplate)) {
|
|
29
29
|
return template;
|
|
30
30
|
}
|
|
31
|
-
return /* @__PURE__ */ jsx(
|
|
32
|
-
OverlayTooltip,
|
|
33
|
-
{
|
|
34
|
-
legacyTemplate: template,
|
|
35
|
-
symbolAlignment
|
|
36
|
-
}
|
|
37
|
-
);
|
|
31
|
+
return /* @__PURE__ */ jsx(OverlayTooltip, { template, symbolAlignment });
|
|
38
32
|
};
|
|
39
33
|
ConnectionLayerTooltip["displayName"] = "ConnectionLayerTooltip";
|
|
40
34
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/map/components/ConnectionLayer/ConnectionLayerTooltip.tsx"],
|
|
4
|
-
"sourcesContent": ["import { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type { Connection } from '../../types/connection-layer.js';\nimport type {\n ConnectionLayerTooltipData,\n ConnectionLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface ConnectionTooltipPayload {\n id: string;\n __lineColor: string;\n __lineHoveredColor: string;\n __lineWidth: number;\n curve?: string;\n data: Connection;\n}\n\nexport interface ConnectionLayerTooltipProps {\n tooltipTemplate?: ConnectionLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const ConnectionLayerTooltip = (props: ConnectionLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<ConnectionTooltipPayload>('geoConnection');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const {\n __lineColor: color,\n __lineWidth: thickness,\n data: customData,\n } = data[0];\n const connectionData: ConnectionLayerTooltipData<Connection> = {\n color,\n thickness,\n data: customData,\n };\n\n const template = tooltipTemplate(connectionData);\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip
|
|
5
|
-
"mappings": "AA6DI;AA7DJ,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAMnC,SAAS,+BAA+B;AAiBjC,MAAM,yBAAyB,CAAC,UAAuC;AAC5E,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAA6C,eAAe;AAE9D,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM;AAAA,IACJ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM;AAAA,EACR,IAAI,KAAK,CAAC;AACV,QAAM,iBAAyD;AAAA,IAC7D;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,WAAW,gBAAgB,cAAc;AAE/C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE
|
|
4
|
+
"sourcesContent": ["import { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type { Connection } from '../../types/connection-layer.js';\nimport type {\n ConnectionLayerTooltipData,\n ConnectionLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface ConnectionTooltipPayload {\n id: string;\n __lineColor: string;\n __lineHoveredColor: string;\n __lineWidth: number;\n curve?: string;\n data: Connection;\n}\n\nexport interface ConnectionLayerTooltipProps {\n tooltipTemplate?: ConnectionLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const ConnectionLayerTooltip = (props: ConnectionLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<ConnectionTooltipPayload>('geoConnection');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const {\n __lineColor: color,\n __lineWidth: thickness,\n data: customData,\n } = data[0];\n const connectionData: ConnectionLayerTooltipData<Connection> = {\n color,\n thickness,\n data: customData,\n };\n\n const template = tooltipTemplate(connectionData);\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip template={template} symbolAlignment={symbolAlignment} />\n );\n};\n\nConnectionLayerTooltip['displayName'] = 'ConnectionLayerTooltip';\n"],
|
|
5
|
+
"mappings": "AA6DI;AA7DJ,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAMnC,SAAS,+BAA+B;AAiBjC,MAAM,yBAAyB,CAAC,UAAuC;AAC5E,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAA6C,eAAe;AAE9D,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM;AAAA,IACJ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM;AAAA,EACR,IAAI,KAAK,CAAC;AACV,QAAM,iBAAyD;AAAA,IAC7D;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,WAAW,gBAAgB,cAAc;AAE/C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE,oBAAC,kBAAe,UAAoB,iBAAkC;AAE1E;AAEA,uBAAuB,aAAa,IAAI;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -24,13 +24,7 @@ const DotLayerTooltip = (props) => {
|
|
|
24
24
|
if (isDefaultTooltipHandler(tooltipTemplate)) {
|
|
25
25
|
return template;
|
|
26
26
|
}
|
|
27
|
-
return /* @__PURE__ */ jsx(
|
|
28
|
-
OverlayTooltip,
|
|
29
|
-
{
|
|
30
|
-
legacyTemplate: template,
|
|
31
|
-
symbolAlignment
|
|
32
|
-
}
|
|
33
|
-
);
|
|
27
|
+
return /* @__PURE__ */ jsx(OverlayTooltip, { template, symbolAlignment });
|
|
34
28
|
};
|
|
35
29
|
DotLayerTooltip["displayName"] = "DotLayerTooltip";
|
|
36
30
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/map/components/DotLayer/DotLayerTooltip.tsx"],
|
|
4
|
-
"sourcesContent": ["import { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type { Location } from '../../types/location.js';\nimport type {\n DotLayerTooltipData,\n DotLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface DotTooltipStatePayload {\n __color: string;\n __hoveredColor: string;\n __bearing: number;\n __backgroundColor: string;\n __hoveredBackgroundColor: string;\n data: Location;\n}\n\nexport interface DotLayerTooltipProps {\n tooltipTemplate?: DotLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const DotLayerTooltip = (props: DotLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<DotTooltipStatePayload>('geoDot');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const { __color: color, __bearing: bearing, data: customData } = data[0];\n const dotData: DotLayerTooltipData = {\n color,\n bearing,\n data: customData,\n };\n\n const template = tooltipTemplate(dotData);\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip
|
|
5
|
-
"mappings": "AAyDI;AAzDJ,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAMnC,SAAS,+BAA+B;AAiBjC,MAAM,kBAAkB,CAAC,UAAgC;AAC9D,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAA2C,QAAQ;AAErD,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,SAAS,OAAO,WAAW,SAAS,MAAM,WAAW,IAAI,KAAK,CAAC;AACvE,QAAM,UAA+B;AAAA,IACnC;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,WAAW,gBAAgB,OAAO;AAExC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE
|
|
4
|
+
"sourcesContent": ["import { _OverlayTooltip as OverlayTooltip } from '@dynatrace/strato-components/charts';\n\nimport { useGeoLayerTooltip } from '../../hooks/use-geo-layer-tooltip.js';\nimport type { Location } from '../../types/location.js';\nimport type {\n DotLayerTooltipData,\n DotLayerTooltipHandler,\n} from '../../types/tooltip.js';\nimport { isDefaultTooltipHandler } from '../../utils/tooltip-type-guards.js';\n\ninterface DotTooltipStatePayload {\n __color: string;\n __hoveredColor: string;\n __bearing: number;\n __backgroundColor: string;\n __hoveredBackgroundColor: string;\n data: Location;\n}\n\nexport interface DotLayerTooltipProps {\n tooltipTemplate?: DotLayerTooltipHandler;\n hidden?: boolean;\n symbolAlignment?: 'left' | 'right';\n}\n\nexport const DotLayerTooltip = (props: DotLayerTooltipProps) => {\n const { tooltipTemplate, hidden, symbolAlignment } = props;\n\n const { data, inBounds, position } =\n useGeoLayerTooltip<DotTooltipStatePayload>('geoDot');\n\n if (hidden || !tooltipTemplate || !inBounds || !position) {\n return null;\n }\n\n if (!data || !data[0]) {\n return null;\n }\n\n const { __color: color, __bearing: bearing, data: customData } = data[0];\n const dotData: DotLayerTooltipData = {\n color,\n bearing,\n data: customData,\n };\n\n const template = tooltipTemplate(dotData);\n\n if (!template) {\n return null;\n }\n\n if (isDefaultTooltipHandler(tooltipTemplate)) {\n return template;\n }\n\n return (\n <OverlayTooltip template={template} symbolAlignment={symbolAlignment} />\n );\n};\n\nDotLayerTooltip['displayName'] = 'DotLayerTooltip';\n"],
|
|
5
|
+
"mappings": "AAyDI;AAzDJ,SAAS,mBAAmB,sBAAsB;AAElD,SAAS,0BAA0B;AAMnC,SAAS,+BAA+B;AAiBjC,MAAM,kBAAkB,CAAC,UAAgC;AAC9D,QAAM,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI;AAErD,QAAM,EAAE,MAAM,UAAU,SAAS,IAC/B,mBAA2C,QAAQ;AAErD,MAAI,UAAU,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,SAAS,OAAO,WAAW,SAAS,MAAM,WAAW,IAAI,KAAK,CAAC;AACvE,QAAM,UAA+B;AAAA,IACnC;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,WAAW,gBAAgB,OAAO;AAExC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,wBAAwB,eAAe,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,SACE,oBAAC,kBAAe,UAAoB,iBAAkC;AAE1E;AAEA,gBAAgB,aAAa,IAAI;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -66,7 +66,7 @@ const MapToolbar = () => {
|
|
|
66
66
|
initialA11yState: initialA11yToolbarTabIndexContext,
|
|
67
67
|
onMouseEnter: handleMouseEnter,
|
|
68
68
|
onMouseLeave: handleMouseLeave,
|
|
69
|
-
menuDisplayMode: "
|
|
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=\"
|
|
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
|
-
|
|
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
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,OAAO,mBAAmB;AAEnC,SAAS,aAAa,WAAW,cAAc;AAE/C,SAAS,0BAA0B;AAS5B,MAAM,uBAAuB,CAAC,wBAAkC;AAErE,QAAM,MAAM,OAAO,EAAE;AAGrB,QAAM,eAAe,OAAoC,MAAS;AAElE,QAAM,cAAc,OAA2B,MAAS;
|
|
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": "AAAA,SAAS,cAAc;AACvB,SAAS,OAAO,mBAAmB;AAEnC,SAAS,aAAa,WAAW,cAAc;AAE/C,SAAS,iCAAiC;AAC1C,SAAS,0BAA0B;AAS5B,MAAM,uBAAuB,CAAC,wBAAkC;AAErE,QAAM,MAAM,OAAO,EAAE;AAGrB,QAAM,eAAe,OAAoC,MAAS;AAElE,QAAM,cAAc,OAA2B,MAAS;AAExD,QAAM,kBAAkB,0BAA0B,CAAC,UAAU,MAAM,MAAM;AACzE,QAAM,gBAAgB,OAAO,KAAK;AAKlC,QAAM,yBAAyB,OAAO,mBAAmB;AACzD,yBAAuB,UAAU;AAEjC,QAAM,2BAA2B,MAAM;AACrC,QACE,CAAC,YAAY,aAAa,OAAO,KACjC,CAAC,YAAY,YAAY,OAAO,GAChC;AACA,UAAI;AAAA,QACF,EAAE,QAAQ,YAAY,SAAS,IAAI,aAAa,QAAQ;AAAA,QACxD,EAAE,QAAQ,MAAM;AAAA,MAClB;AAEA,UAAI;AACF,cAAM,cAAc,IAAI,sBAAsB,QAAW;AAAA,UACvD,QAAQ,uBAAuB;AAAA,QACjC,CAAC;AACD,oBAAY,QAAQ,CAAC,YAAY;AAC/B,cAAI,QAAQ,OAAO,UAAa,QAAQ,MAAM,WAAW,QAAW;AAClE,gBAAI;AAAA,cACF,EAAE,QAAQ,QAAQ,MAAM,QAAQ,IAAI,QAAQ,GAAG;AAAA,cAC/C,EAAE,aAAa,MAAM;AAAA,YACvB;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc;AAAA,IAClB,CAAC,EAAE,MAAM,MAA0B;AACjC,UAAI,WAAyD,CAAC;AAE9D,UAAI;AACF,mBAAW,IAAI,sBAAsB,OAAO;AAAA,UAC1C,QAAQ,uBAAuB;AAAA,QACjC,CAAC;AAAA,MACH,QAAQ;AACN;AAAA,MACF;AAGA,UAAI,cAA4D,CAAC;AACjE,UAAI;AACF,sBAAc,IAAI,sBAAsB,QAAW;AAAA,UACjD,QAAQ,uBAAuB;AAAA,QACjC,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAEA,YAAM,UAAU,WAAW,CAAC,GAAG,OAAO;AAEtC,YAAM,qBACJ,CAAC,MAAM,QAAQ,KAAK,SAAS,SAAS,KAAK,CAAC,YAAY,OAAO;AAEjE,UAAI,oBAAoB;AACtB,YACE,CAAC,YAAY,aAAa,OAAO,KACjC,CAAC,YAAY,YAAY,OAAO,GAChC;AAEA,cAAI;AAAA,YACF,EAAE,QAAQ,YAAY,SAAS,IAAI,aAAa,QAAQ;AAAA,YACxD,EAAE,QAAQ,MAAM;AAAA,UAClB;AAAA,QACF;AAEA,cAAM,aAAa,mBAAmB,QAAQ;AAE9C,qBAAa,UAAU,WAAW;AAClC,oBAAY,UAAU,WAAW,MAAM;AACvC,cAAM,cAAc,SAAS,CAAC,EAAE,MAAM;AAGtC,YAAI;AAAA,UACF,EAAE,QAAQ,YAAY,SAAS,IAAI,aAAa,QAAQ;AAAA,UACxD,EAAE,QAAQ,CAAC,YAAY;AAAA,QACzB;AACA,oBAAY,QAAQ,CAAC,YAAY;AAC/B,cAAI,QAAQ,OAAO,UAAa,QAAQ,MAAM,WAAW,QAAW;AAClE,gBAAI;AAAA,cACF,EAAE,QAAQ,QAAQ,MAAM,QAAQ,IAAI,QAAQ,GAAG;AAAA,cAC/C,EAAE,aAAa,CAAC,YAAY;AAAA,YAC9B;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,WACE,CAAC,YAAY,aAAa,OAAO,KACjC,CAAC,YAAY,YAAY,OAAO,GAChC;AAEA,YAAI;AAAA,UACF,EAAE,QAAQ,YAAY,SAAS,IAAI,aAAa,QAAQ;AAAA,UACxD,EAAE,QAAQ,MAAM;AAAA,QAClB;AACA,oBAAY,QAAQ,CAAC,YAAY;AAC/B,cAAI,QAAQ,OAAO,UAAa,QAAQ,MAAM,WAAW,QAAW;AAClE,gBAAI;AAAA,cACF,EAAE,QAAQ,QAAQ,MAAM,QAAQ,IAAI,QAAQ,GAAG;AAAA,cAC/C,EAAE,aAAa,MAAM;AAAA,YACvB;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,4BAA4B;AAAA,IAChC,CAAC,UAAyB;AACxB,UAAI,MAAM,QAAQ,UAAU;AAC1B,iCAAyB;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,YAAU,MAAM;AACd,UAAM,kBAAkB,cAAc,WAAW,CAAC;AAClD,kBAAc,UAAU;AAExB,QAAI,iBAAiB;AACnB,+BAAyB;AACzB,mBAAa,UAAU;AACvB,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,iBAAiB,GAAG,CAAC;AAEzB,YAAU,MAAM;AACd,QAAI,GAAG,SAAS,WAAW;AAC3B,WAAO,iBAAiB,SAAS,yBAAyB;AAC1D,WAAO,MAAM;AACX,UAAI,IAAI,SAAS,WAAW;AAC5B,aAAO,oBAAoB,SAAS,yBAAyB;AAAA,IAC/D;AAAA,EACF,GAAG,CAAC,KAAK,aAAa,yBAAyB,CAAC;AAClD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { useMap } from "@vis.gl/react-maplibre";
|
|
2
|
-
import { isString
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,
|
|
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
|
}
|
|
@@ -8,7 +8,8 @@ const MAPLIBRE_RUNTIME_ERROR_PATTERNS = [
|
|
|
8
8
|
"Could not compile vertex shader",
|
|
9
9
|
"Program failed to link",
|
|
10
10
|
"feature index out of bounds",
|
|
11
|
-
"Out of bounds. Index requested"
|
|
11
|
+
"Out of bounds. Index requested",
|
|
12
|
+
"bucket.icon.opacityVertexArray"
|
|
12
13
|
];
|
|
13
14
|
const isMaplibreRuntimeError = (message) => MAPLIBRE_RUNTIME_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
|
|
14
15
|
const useMapRuntimeError = ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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;",
|
|
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 'bucket.icon.opacityVertexArray',\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;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
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
|
-
|
|
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({
|