@trackunit/react-map 0.2.210 → 0.2.211

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/index.cjs.js CHANGED
@@ -5888,10 +5888,10 @@ const useFitToContent = (api, handles, options) => {
5888
5888
  };
5889
5889
 
5890
5890
  /**
5891
- * Single dev-warning channel for `useHeatmap` (invalid data, invalid style).
5891
+ * Single dev-warning channel for `useHeatmap` (invalid data, invalid style, unhandled render errors).
5892
5892
  */
5893
5893
  const warnHeatmap = (message, ...details) => {
5894
- // eslint-disable-next-line no-console -- Intentional: the one heatmap warning channel; invalid input is dropped or defaulted and must stay visible
5894
+ // eslint-disable-next-line no-console -- Intentional: the one heatmap warning channel; invalid input is dropped or defaulted and render errors without onError must stay visible
5895
5895
  console.warn(`[useHeatmap] ${message}`, ...details);
5896
5896
  };
5897
5897
 
@@ -6040,8 +6040,41 @@ const EMPTY_CONTROLS$2 = {};
6040
6040
  * - Renderers: ADR-0029 (`libs/react/map/docs/adr/0029-heatmap-provider-specific-renderers.md`).
6041
6041
  */
6042
6042
  const useHeatmap = (options) => {
6043
- const { id, name, data, getPosition, getWeight, style, controls, loading, fitParticipation = "all" } = options;
6043
+ const { id, name, data, getPosition, getWeight, style, onError, controls, loading, fitParticipation = "all", } = options;
6044
6044
  const layerReady = useLayerReady(loading);
6045
+ const onErrorRef = react.useRef(onError);
6046
+ react.useEffect(() => {
6047
+ onErrorRef.current = onError;
6048
+ }, [onError]);
6049
+ const errorHandler = reactCoreHooks.useErrorHandlerOrNull();
6050
+ // Sentry at most once per layer/code per hook mount, like the shape hooks' fill-tiling reports.
6051
+ const sentryReportedKeysRef = react.useRef(new Set());
6052
+ const captureOnce = react.useCallback((key, message, cause, fingerprintCode) => {
6053
+ if (errorHandler === null || sentryReportedKeysRef.current.has(key))
6054
+ return;
6055
+ sentryReportedKeysRef.current.add(key);
6056
+ errorHandler.captureException(new Error(message, { cause }), {
6057
+ level: "warning",
6058
+ fingerprint: ["react-map", "heatmap", fingerprintCode],
6059
+ });
6060
+ }, [errorHandler]);
6061
+ const reportError = react.useCallback((error) => {
6062
+ const payload = JSON.stringify({ layerId: error.layerId, code: error.code });
6063
+ captureOnce(`${error.layerId}:${error.code}`, `Heatmap failed to render: ${payload}`, error.cause, error.code);
6064
+ const handler = onErrorRef.current;
6065
+ if (handler !== undefined) {
6066
+ // Adapters call this mid-sync; a throwing handler must not skip the rest of the layer snapshot.
6067
+ try {
6068
+ handler(error);
6069
+ }
6070
+ catch (handlerError) {
6071
+ warnHeatmap(`onError for heatmap "${error.layerId}" threw.`, handlerError, error.cause);
6072
+ captureOnce(`${error.layerId}:on-error-threw`, `Heatmap onError threw: ${payload}`, handlerError, "on-error-threw");
6073
+ }
6074
+ return;
6075
+ }
6076
+ warnHeatmap(`Heatmap "${error.layerId}" failed to render (${error.code}).`, error.cause);
6077
+ }, [captureOnce]);
6045
6078
  // Keyed on `data` (adjust-state-during-render + isEqual) so inline accessors
6046
6079
  // don't re-normalize the whole dataset on every render. Accessor changes
6047
6080
  // apply on the next `data` change.
@@ -6078,6 +6111,7 @@ const useHeatmap = (options) => {
6078
6111
  layerType: "heatmap",
6079
6112
  features: normalized.features,
6080
6113
  style: resolvedStyle,
6114
+ onError: reportError,
6081
6115
  pointCount: normalized.features.features.length,
6082
6116
  totalCount: data.length,
6083
6117
  }), [
@@ -6090,6 +6124,7 @@ const useHeatmap = (options) => {
6090
6124
  fitParticipation,
6091
6125
  normalized.features,
6092
6126
  resolvedStyle,
6127
+ reportError,
6093
6128
  data.length,
6094
6129
  ]);
6095
6130
  };
@@ -8482,6 +8517,7 @@ function toMapLayer(handle) {
8482
8517
  id: handle.id,
8483
8518
  features: handle.features,
8484
8519
  style: handle.style,
8520
+ onError: handle.onError,
8485
8521
  };
8486
8522
  default: {
8487
8523
  const exhaustiveCheck = handle;
package/index.esm.js CHANGED
@@ -5887,10 +5887,10 @@ const useFitToContent = (api, handles, options) => {
5887
5887
  };
5888
5888
 
5889
5889
  /**
5890
- * Single dev-warning channel for `useHeatmap` (invalid data, invalid style).
5890
+ * Single dev-warning channel for `useHeatmap` (invalid data, invalid style, unhandled render errors).
5891
5891
  */
5892
5892
  const warnHeatmap = (message, ...details) => {
5893
- // eslint-disable-next-line no-console -- Intentional: the one heatmap warning channel; invalid input is dropped or defaulted and must stay visible
5893
+ // eslint-disable-next-line no-console -- Intentional: the one heatmap warning channel; invalid input is dropped or defaulted and render errors without onError must stay visible
5894
5894
  console.warn(`[useHeatmap] ${message}`, ...details);
5895
5895
  };
5896
5896
 
@@ -6039,8 +6039,41 @@ const EMPTY_CONTROLS$2 = {};
6039
6039
  * - Renderers: ADR-0029 (`libs/react/map/docs/adr/0029-heatmap-provider-specific-renderers.md`).
6040
6040
  */
6041
6041
  const useHeatmap = (options) => {
6042
- const { id, name, data, getPosition, getWeight, style, controls, loading, fitParticipation = "all" } = options;
6042
+ const { id, name, data, getPosition, getWeight, style, onError, controls, loading, fitParticipation = "all", } = options;
6043
6043
  const layerReady = useLayerReady(loading);
6044
+ const onErrorRef = useRef(onError);
6045
+ useEffect(() => {
6046
+ onErrorRef.current = onError;
6047
+ }, [onError]);
6048
+ const errorHandler = useErrorHandlerOrNull();
6049
+ // Sentry at most once per layer/code per hook mount, like the shape hooks' fill-tiling reports.
6050
+ const sentryReportedKeysRef = useRef(new Set());
6051
+ const captureOnce = useCallback((key, message, cause, fingerprintCode) => {
6052
+ if (errorHandler === null || sentryReportedKeysRef.current.has(key))
6053
+ return;
6054
+ sentryReportedKeysRef.current.add(key);
6055
+ errorHandler.captureException(new Error(message, { cause }), {
6056
+ level: "warning",
6057
+ fingerprint: ["react-map", "heatmap", fingerprintCode],
6058
+ });
6059
+ }, [errorHandler]);
6060
+ const reportError = useCallback((error) => {
6061
+ const payload = JSON.stringify({ layerId: error.layerId, code: error.code });
6062
+ captureOnce(`${error.layerId}:${error.code}`, `Heatmap failed to render: ${payload}`, error.cause, error.code);
6063
+ const handler = onErrorRef.current;
6064
+ if (handler !== undefined) {
6065
+ // Adapters call this mid-sync; a throwing handler must not skip the rest of the layer snapshot.
6066
+ try {
6067
+ handler(error);
6068
+ }
6069
+ catch (handlerError) {
6070
+ warnHeatmap(`onError for heatmap "${error.layerId}" threw.`, handlerError, error.cause);
6071
+ captureOnce(`${error.layerId}:on-error-threw`, `Heatmap onError threw: ${payload}`, handlerError, "on-error-threw");
6072
+ }
6073
+ return;
6074
+ }
6075
+ warnHeatmap(`Heatmap "${error.layerId}" failed to render (${error.code}).`, error.cause);
6076
+ }, [captureOnce]);
6044
6077
  // Keyed on `data` (adjust-state-during-render + isEqual) so inline accessors
6045
6078
  // don't re-normalize the whole dataset on every render. Accessor changes
6046
6079
  // apply on the next `data` change.
@@ -6077,6 +6110,7 @@ const useHeatmap = (options) => {
6077
6110
  layerType: "heatmap",
6078
6111
  features: normalized.features,
6079
6112
  style: resolvedStyle,
6113
+ onError: reportError,
6080
6114
  pointCount: normalized.features.features.length,
6081
6115
  totalCount: data.length,
6082
6116
  }), [
@@ -6089,6 +6123,7 @@ const useHeatmap = (options) => {
6089
6123
  fitParticipation,
6090
6124
  normalized.features,
6091
6125
  resolvedStyle,
6126
+ reportError,
6092
6127
  data.length,
6093
6128
  ]);
6094
6129
  };
@@ -8481,6 +8516,7 @@ function toMapLayer(handle) {
8481
8516
  id: handle.id,
8482
8517
  features: handle.features,
8483
8518
  style: handle.style,
8519
+ onError: handle.onError,
8484
8520
  };
8485
8521
  default: {
8486
8522
  const exhaustiveCheck = handle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-map",
3
- "version": "0.2.210",
3
+ "version": "0.2.211",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -14,7 +14,7 @@
14
14
  "@trackunit/geo-json-utils": "1.18.6",
15
15
  "@trackunit/i18n-library-translation": "2.8.6",
16
16
  "react-minimal-pie-chart": "^8.4.0",
17
- "@trackunit/react-map-adapter-shared": "0.0.157",
17
+ "@trackunit/react-map-adapter-shared": "0.0.158",
18
18
  "@trackunit/react-map-color-utils": "0.0.132",
19
19
  "@trackunit/shared-utils": "1.19.6",
20
20
  "@trackunit/ui-design-tokens": "1.18.6",
package/src/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export type { ControlMiddleware, MiddlewareContext } from "./controls/renderingR
24
24
  export type { CategoryMetadata } from "./controls/collapseControls";
25
25
  export type { ClusterEntity, Entity, MapInteractionState, MarkerEntity, RouteEntity, ShapeEntity, ShapeType, } from "@trackunit/react-map-adapter-shared";
26
26
  export { geometryTypeToShapeType, INITIAL_INTERACTION_STATE } from "@trackunit/react-map-adapter-shared";
27
- export type { AdaptiveMarkerResolution, AdaptiveRenderConfig, AdaptiveRenderState, AdaptiveResolutionContext, CircleSymbolDescriptor, ClientClusterConfig, ClusterConfig, ClusterInfo, ClusterRenderState, ClusterSymbolStyle, CommonRenderState, DomPortalStackingInput, DomPortalStackingResolver, DomPortalStackingResult, DomRenderConfig, DomRenderState, FitParticipation, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, HeatmapLayerHandle, HeatmapRgbaColor, ImageOverlayHandle, LayerHandle, LayerMeta, MarkerAnchor, MarkerDomPortalStackGeometry, MarkerDomPortalStackPhase, MarkerLayerHandle, RenderConfig, RenderMedium, ResolutionContext, RouteLayerHandle, RouteStyle, ServerClusterConfig, ShapeInteractiveMode, ShapeLayerHandle, ShapeOverlapConfig, ShapeStyle, ShapeStyleOverrides, ShapesUnderCursorHit, SymbolDescriptor, SymbolRenderConfig, SymbolRenderState, } from "./layers/types";
27
+ export type { AdaptiveMarkerResolution, AdaptiveRenderConfig, AdaptiveRenderState, AdaptiveResolutionContext, CircleSymbolDescriptor, ClientClusterConfig, ClusterConfig, ClusterInfo, ClusterRenderState, ClusterSymbolStyle, CommonRenderState, DomPortalStackingInput, DomPortalStackingResolver, DomPortalStackingResult, DomRenderConfig, DomRenderState, FitParticipation, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, HeatmapLayerHandle, HeatmapRenderError, HeatmapRgbaColor, ImageOverlayHandle, LayerHandle, LayerMeta, MarkerAnchor, MarkerDomPortalStackGeometry, MarkerDomPortalStackPhase, MarkerLayerHandle, RenderConfig, RenderMedium, ResolutionContext, RouteLayerHandle, RouteStyle, ServerClusterConfig, ShapeInteractiveMode, ShapeLayerHandle, ShapeOverlapConfig, ShapeStyle, ShapeStyleOverrides, ShapesUnderCursorHit, SymbolDescriptor, SymbolRenderConfig, SymbolRenderState, } from "./layers/types";
28
28
  export { computeMarkerDomPortalZIndex } from "./layers/types";
29
29
  export { ANCHOR_SELECTOR, HIT_SURFACE_SELECTOR } from "@trackunit/react-map-adapter-shared";
30
30
  export { ClusterMarker, type ClusterMarkerProps, type ClusterMarkerStickInput, type ClusterSegment, } from "./clusters/ClusterMarker";
@@ -1,12 +1,12 @@
1
1
  import type { GeoJsonBbox, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, GeoJsonPosition } from "@trackunit/geo-json-utils";
2
- import type { AdaptiveMarkerResolution, ClusterConfig, ClusterRenderConfig, HeatmapRenderStyle, RenderConfig, RouteStyle, ShapeInteractiveMode, ShapeStyle } from "@trackunit/react-map-adapter-shared";
2
+ import type { AdaptiveMarkerResolution, ClusterConfig, ClusterRenderConfig, HeatmapRenderError, HeatmapRenderStyle, RenderConfig, RouteStyle, ShapeInteractiveMode, ShapeStyle } from "@trackunit/react-map-adapter-shared";
3
3
  import type { CategoryKey, ControlConfig } from "../controls/types";
4
4
  import type { ShapeDecoration } from "./useShapes/shapeDecorations";
5
5
  import type { ShapesUnderCursorHit } from "./useShapes/shapeFillTiling";
6
6
  import type { ShapeLabelResolutionContext } from "./useShapes/shapeLabelResolution";
7
7
  import type { ResolveShapeStackOrder } from "./useShapes/shapeStackOrder";
8
8
  export type { ShapesUnderCursorHit };
9
- export type { AdaptiveMarkerResolution, AdaptiveRenderConfig, AdaptiveRenderState, AdaptiveResolutionContext, CircleSymbolDescriptor, ClientClusterConfig, ClusterConfig, ClusterDomRenderConfig, ClusterInfo, ClusterRenderConfig, ClusterRenderState, ClusterSymbolRenderConfig, ClusterSymbolStyle, CommonRenderState, DomPortalStackingInput, DomPortalStackingResolver, DomPortalStackingResult, DomRenderConfig, DomRenderState, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, HeatmapRenderStyle, HeatmapRgbaColor, MarkerAnchor, MarkerDomPortalStackGeometry, MarkerDomPortalStackPhase, PixelOffset, RenderConfig, RenderMedium, ResolutionContext, RouteStyle, ServerClusterConfig, ShapeInteractiveMode, ShapeStyle, ShapeStyleOverrides, SymbolDescriptor, SymbolRenderConfig, SymbolRenderState, } from "@trackunit/react-map-adapter-shared";
9
+ export type { AdaptiveMarkerResolution, AdaptiveRenderConfig, AdaptiveRenderState, AdaptiveResolutionContext, CircleSymbolDescriptor, ClientClusterConfig, ClusterConfig, ClusterDomRenderConfig, ClusterInfo, ClusterRenderConfig, ClusterRenderState, ClusterSymbolRenderConfig, ClusterSymbolStyle, CommonRenderState, DomPortalStackingInput, DomPortalStackingResolver, DomPortalStackingResult, DomRenderConfig, DomRenderState, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, HeatmapRenderError, HeatmapRenderStyle, HeatmapRgbaColor, MarkerAnchor, MarkerDomPortalStackGeometry, MarkerDomPortalStackPhase, PixelOffset, RenderConfig, RenderMedium, ResolutionContext, RouteStyle, ServerClusterConfig, ShapeInteractiveMode, ShapeStyle, ShapeStyleOverrides, SymbolDescriptor, SymbolRenderConfig, SymbolRenderState, } from "@trackunit/react-map-adapter-shared";
10
10
  export { computeMarkerDomPortalZIndex } from "@trackunit/react-map-adapter-shared";
11
11
  /**
12
12
  * Controls whether a layer's bounds are included in fit-to-content operations.
@@ -191,6 +191,8 @@ export interface HeatmapLayerHandle extends LayerMeta {
191
191
  readonly layerType: "heatmap";
192
192
  readonly features: GeoJsonFeatureCollection;
193
193
  readonly style: HeatmapRenderStyle;
194
+ /** Stable error reporter forwarded to the adapter; calls the consumer's latest `onError` or warns. */
195
+ readonly onError: (error: HeatmapRenderError) => void;
194
196
  }
195
197
  /**
196
198
  * Discriminated union of all layer handle types.
@@ -1,4 +1,5 @@
1
1
  import type { GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ import type { HeatmapRenderError } from "@trackunit/react-map-adapter-shared";
2
3
  import type { CategoryKey, ControlConfig } from "../../controls/types";
3
4
  import type { FitParticipation, HeatmapLayerHandle } from "../types";
4
5
  export type HeatmapStyle = Readonly<{
@@ -21,6 +22,14 @@ export type UseHeatmapOptions<TItem> = Readonly<{
21
22
  getPosition: (item: TItem) => GeoJsonPosition | null;
22
23
  getWeight?: (item: TItem) => number;
23
24
  style?: HeatmapStyle;
25
+ /**
26
+ * Called once per failed renderer load or update (for example when the Google deck.gl chunk fails to load).
27
+ * The basemap and other layers keep rendering. Without it, failures are logged with `console.warn`.
28
+ * Failures are reported to the app's error handler (Sentry) either way.
29
+ * After a renderer failure (for example WebGL context loss on Google), the heatmap stays hidden until the next map
30
+ * layer or interaction change triggers a retry; nothing retries on an idle map.
31
+ */
32
+ onError?: (error: HeatmapRenderError) => void;
24
33
  controls?: Partial<Record<CategoryKey, ReadonlyArray<ControlConfig>>>;
25
34
  loading?: boolean;
26
35
  fitParticipation?: FitParticipation;
@@ -1,4 +1,4 @@
1
1
  /**
2
- * Single dev-warning channel for `useHeatmap` (invalid data, invalid style).
2
+ * Single dev-warning channel for `useHeatmap` (invalid data, invalid style, unhandled render errors).
3
3
  */
4
4
  export declare const warnHeatmap: (message: string, ...details: ReadonlyArray<unknown>) => void;