@trackunit/react-map 0.1.25 → 0.1.27
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 +79 -21
- package/index.esm.js +79 -21
- package/package.json +11 -11
package/index.cjs.js
CHANGED
|
@@ -7577,14 +7577,17 @@ const degreesPerPixel = (zoom, tileSize) => 360 / (tileSize * Math.pow(2, zoom))
|
|
|
7577
7577
|
const shapesUnderCursor = (position, features, options) => {
|
|
7578
7578
|
const { zoom, tileSize, strokeWidthFor, visibleFillFor, layerGeodesic, featureStyles } = options;
|
|
7579
7579
|
// Densify before boundary-distance and point-in-polygon math so stroke/fill
|
|
7580
|
-
// hit-tests follow great-circle arcs on large polygons.
|
|
7580
|
+
// hit-tests follow great-circle arcs on large polygons. Always defer to
|
|
7581
|
+
// densifyGeodesicFeatures' per-feature resolution (geodesic override > layer
|
|
7582
|
+
// default) rather than short-circuiting on layerGeodesic === false — that
|
|
7583
|
+
// matches the Mapbox render path, so the hit-test geometry never diverges
|
|
7584
|
+
// from the painted edge when a feature opts into geodesic under a non-geodesic
|
|
7585
|
+
// layer. The util returns the original collection when nothing is densified.
|
|
7581
7586
|
const featureCollection = {
|
|
7582
7587
|
type: "FeatureCollection",
|
|
7583
7588
|
features: Array.from(features),
|
|
7584
7589
|
};
|
|
7585
|
-
const densifiedFeatures = layerGeodesic
|
|
7586
|
-
? reactMapAdapterShared.densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features
|
|
7587
|
-
: features;
|
|
7590
|
+
const densifiedFeatures = reactMapAdapterShared.densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features;
|
|
7588
7591
|
const degPerPx = degreesPerPixel(zoom, tileSize);
|
|
7589
7592
|
const hits = [];
|
|
7590
7593
|
for (const feature of densifiedFeatures) {
|
|
@@ -7696,14 +7699,16 @@ const promoteToFront = (order, id) => order.includes(id) ? [id, ...order.filter(
|
|
|
7696
7699
|
* preserved and translucent fills do not compound under the winner.
|
|
7697
7700
|
*/
|
|
7698
7701
|
const computePromotionGroupFills = (members, winnerId, restingPeerFills = new Map(), layerGeodesic = undefined, featureStyles = undefined) => {
|
|
7699
|
-
// Densify before peer-vs-winner clip so promotion clips follow great-circle
|
|
7702
|
+
// Densify before peer-vs-winner clip so promotion clips follow great-circle
|
|
7703
|
+
// arcs. Always defer to densifyGeodesicFeatures' per-feature resolution
|
|
7704
|
+
// (geodesic override > layer default) rather than short-circuiting on
|
|
7705
|
+
// layerGeodesic === false — keeps the clip geometry in step with the render
|
|
7706
|
+
// path. The util returns the original collection when nothing is densified.
|
|
7700
7707
|
const featureCollection = {
|
|
7701
7708
|
type: "FeatureCollection",
|
|
7702
7709
|
features: Array.from(members),
|
|
7703
7710
|
};
|
|
7704
|
-
const densifiedMembers = layerGeodesic
|
|
7705
|
-
? reactMapAdapterShared.densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features
|
|
7706
|
-
: members;
|
|
7711
|
+
const densifiedMembers = reactMapAdapterShared.densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features;
|
|
7707
7712
|
const geometries = new Map();
|
|
7708
7713
|
for (const feature of densifiedMembers) {
|
|
7709
7714
|
if (feature.id === undefined)
|
|
@@ -7743,14 +7748,16 @@ const computePromotionGroupFills = (members, winnerId, restingPeerFills = new Ma
|
|
|
7743
7748
|
const computeFillTiling = (input) => {
|
|
7744
7749
|
const { features, viewportBounds, resolveStackOrder, selectedFeatureId, suppressedFeatureIds, layerGeodesic, featureStyles, } = input;
|
|
7745
7750
|
// Densify before any polygon/boundary math so clip geometry follows great-circle
|
|
7746
|
-
// arcs on large polygons (mirrors the ADR-0024 precedent for edge labels).
|
|
7751
|
+
// arcs on large polygons (mirrors the ADR-0024 precedent for edge labels). Always
|
|
7752
|
+
// defer to densifyGeodesicFeatures' per-feature resolution (geodesic override >
|
|
7753
|
+
// layer default) rather than short-circuiting on layerGeodesic === false — keeps
|
|
7754
|
+
// the clip geometry in step with the Mapbox render path. The util returns the
|
|
7755
|
+
// original collection when nothing is densified.
|
|
7747
7756
|
const featureCollection = {
|
|
7748
7757
|
type: "FeatureCollection",
|
|
7749
7758
|
features: Array.from(features),
|
|
7750
7759
|
};
|
|
7751
|
-
const densifiedFeatures = layerGeodesic
|
|
7752
|
-
? reactMapAdapterShared.densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features
|
|
7753
|
-
: features;
|
|
7760
|
+
const densifiedFeatures = reactMapAdapterShared.densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features;
|
|
7754
7761
|
const records = [];
|
|
7755
7762
|
for (const feature of densifiedFeatures) {
|
|
7756
7763
|
if (feature.id === undefined)
|
|
@@ -10128,6 +10135,8 @@ const useShapeFillTiling = ({ api, handles, onFillGeometriesChange, onZIndexOver
|
|
|
10128
10135
|
}, [suppressedFillIdsByHandle]);
|
|
10129
10136
|
const debounceTimerRef = react.useRef(null);
|
|
10130
10137
|
const idleRecomputeTimerRef = react.useRef(null);
|
|
10138
|
+
/** Pending deferred selection re-tile (double-rAF handle); see selection effect. */
|
|
10139
|
+
const selectionRecomputeRafRef = react.useRef(null);
|
|
10131
10140
|
// Currently emitted overrides — used for hit-testing visible fills in handleSettle.
|
|
10132
10141
|
const prevFillOverridesRef = react.useRef(EMPTY_OVERRIDES);
|
|
10133
10142
|
const prevZIndexOverridesRef = react.useRef(EMPTY_ZINDEX_OVERRIDES);
|
|
@@ -10382,8 +10391,30 @@ const useShapeFillTiling = ({ api, handles, onFillGeometriesChange, onZIndexOver
|
|
|
10382
10391
|
return;
|
|
10383
10392
|
recompute();
|
|
10384
10393
|
}, [recompute, tilingContentKey]);
|
|
10394
|
+
// Selection forces the selected feature to the front of its overlap group,
|
|
10395
|
+
// which requires a resting re-tile (computeFillTiling depends on
|
|
10396
|
+
// selectedFeatureId via resolveStackOrder). For large layers that re-tile is
|
|
10397
|
+
// expensive (~600ms for 200 polygons over global bounds) and must NOT block
|
|
10398
|
+
// the interaction's first paint — e.g. a panel opening on the clicked feature.
|
|
10399
|
+
// Defer it past the next paint with a double rAF so the click commits and
|
|
10400
|
+
// paints immediately, then the selected polygon restacks one frame later.
|
|
10401
|
+
// Rapid selections coalesce: each schedule cancels the previous pending one.
|
|
10385
10402
|
react.useEffect(() => {
|
|
10386
|
-
|
|
10403
|
+
if (selectionRecomputeRafRef.current !== null) {
|
|
10404
|
+
cancelAnimationFrame(selectionRecomputeRafRef.current);
|
|
10405
|
+
}
|
|
10406
|
+
selectionRecomputeRafRef.current = requestAnimationFrame(() => {
|
|
10407
|
+
selectionRecomputeRafRef.current = requestAnimationFrame(() => {
|
|
10408
|
+
selectionRecomputeRafRef.current = null;
|
|
10409
|
+
recompute();
|
|
10410
|
+
});
|
|
10411
|
+
});
|
|
10412
|
+
return () => {
|
|
10413
|
+
if (selectionRecomputeRafRef.current !== null) {
|
|
10414
|
+
cancelAnimationFrame(selectionRecomputeRafRef.current);
|
|
10415
|
+
selectionRecomputeRafRef.current = null;
|
|
10416
|
+
}
|
|
10417
|
+
};
|
|
10387
10418
|
}, [recompute, selectedFeatureId]);
|
|
10388
10419
|
// Suppression changes must trigger a recompute so EMPTY_FILL entries are
|
|
10389
10420
|
// added / removed even when the map is idle (covers the decorations-vs-tiling
|
|
@@ -10635,7 +10666,24 @@ const Layers = ({ layers, api, onEntityClick, onEntityDblClick }) => {
|
|
|
10635
10666
|
const [viewportStyleOverrides, setViewportStyleOverrides] = react.useState(new Map());
|
|
10636
10667
|
const [fillGeometryOverrides, setFillGeometryOverrides] = react.useState(new Map());
|
|
10637
10668
|
const [zIndexOverrides, setZIndexOverrides] = react.useState(new Map());
|
|
10669
|
+
// Mirror the authoritative hovered entity into a ref. The settle channel fires
|
|
10670
|
+
// on a ~120ms debounce (outside React's render cycle) and needs to read the
|
|
10671
|
+
// current hover synchronously.
|
|
10672
|
+
const hoveredEntityRef = react.useRef(layers.interaction.hoveredEntity);
|
|
10673
|
+
react.useEffect(() => {
|
|
10674
|
+
hoveredEntityRef.current = layers.interaction.hoveredEntity;
|
|
10675
|
+
}, [layers.interaction.hoveredEntity]);
|
|
10638
10676
|
const onSettledHover = react.useCallback((handleId, featureId) => {
|
|
10677
|
+
// Z-order truth: markers and clusters render above shapes (DOM/symbol portals
|
|
10678
|
+
// over the polygon canvas). When a point entity is the current hovered entity
|
|
10679
|
+
// it is the authoritative top hit under the cursor, so the shape settle — which
|
|
10680
|
+
// is not itself position-gated against the marker layer — must not overwrite it.
|
|
10681
|
+
// Without this guard, moving onto an asset pill that overlaps a site would leave
|
|
10682
|
+
// the covered site as `hoveredEntity` (a stale cross-channel hover write). We read
|
|
10683
|
+
// the single source of truth (`hoveredEntity`) rather than a parallel guard flag.
|
|
10684
|
+
const hovered = hoveredEntityRef.current;
|
|
10685
|
+
if (hovered !== null && (hovered.type === "marker" || hovered.type === "cluster"))
|
|
10686
|
+
return;
|
|
10639
10687
|
// Fill tiling only fires on polygonal features — shapeType is always "polygon".
|
|
10640
10688
|
layers.hover({ type: "shape", id: featureId, handleId, shapeType: "polygon" });
|
|
10641
10689
|
}, [layers]);
|
|
@@ -10697,17 +10745,27 @@ const Layers = ({ layers, api, onEntityClick, onEntityDblClick }) => {
|
|
|
10697
10745
|
});
|
|
10698
10746
|
}, [layers.handles, adaptiveByLayer, viewportStyleOverrides, fillGeometryOverrides, zIndexOverrides, queryAt]);
|
|
10699
10747
|
const { renderedSourceIds, hasSourceReadiness, readyRevision } = useLayerHandleSync(layerPort, enrichedHandles, layers.interaction, decorationLayers);
|
|
10700
|
-
const hoveredEntityRef = react.useRef(layers.interaction.hoveredEntity);
|
|
10701
|
-
react.useEffect(() => {
|
|
10702
|
-
hoveredEntityRef.current = layers.interaction.hoveredEntity;
|
|
10703
|
-
}, [layers.interaction.hoveredEntity]);
|
|
10704
10748
|
const coordinatedSelect = react.useCallback((entity) => {
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
}
|
|
10708
|
-
else {
|
|
10749
|
+
const hovered = hoveredEntityRef.current;
|
|
10750
|
+
if (entity === null || hovered === null) {
|
|
10709
10751
|
layers.select(entity);
|
|
10752
|
+
return;
|
|
10753
|
+
}
|
|
10754
|
+
// Default to the entity the click actually carried — it is the freshest,
|
|
10755
|
+
// most direct signal of what the user hit, and it hit-tests per element
|
|
10756
|
+
// for markers/clusters/routes. The single exception is a shape-on-shape
|
|
10757
|
+
// conflict: the adapter resolves polygon clicks with its own z-order and
|
|
10758
|
+
// full (unclipped) hit areas, so it cannot honor the fill-tiling ownership
|
|
10759
|
+
// model that decides which overlapping site is visually on top (ADR-0021).
|
|
10760
|
+
// There the settle-resolved hovered shape is the precise winner, so we
|
|
10761
|
+
// trust it instead. This is also why an asset pill overlapping a site
|
|
10762
|
+
// selects the asset: a marker click is never a shape, so it bypasses the
|
|
10763
|
+
// exception even when a stale site shape is still the hovered entity.
|
|
10764
|
+
if (entity.type === "shape" && hovered.type === "shape") {
|
|
10765
|
+
layers.select(hovered);
|
|
10766
|
+
return;
|
|
10710
10767
|
}
|
|
10768
|
+
layers.select(entity);
|
|
10711
10769
|
}, [layers]);
|
|
10712
10770
|
const fitOnDblClick = useDblClickFit(api, layers.handles);
|
|
10713
10771
|
const handleDblClick = react.useCallback((entity) => {
|
package/index.esm.js
CHANGED
|
@@ -7576,14 +7576,17 @@ const degreesPerPixel = (zoom, tileSize) => 360 / (tileSize * Math.pow(2, zoom))
|
|
|
7576
7576
|
const shapesUnderCursor = (position, features, options) => {
|
|
7577
7577
|
const { zoom, tileSize, strokeWidthFor, visibleFillFor, layerGeodesic, featureStyles } = options;
|
|
7578
7578
|
// Densify before boundary-distance and point-in-polygon math so stroke/fill
|
|
7579
|
-
// hit-tests follow great-circle arcs on large polygons.
|
|
7579
|
+
// hit-tests follow great-circle arcs on large polygons. Always defer to
|
|
7580
|
+
// densifyGeodesicFeatures' per-feature resolution (geodesic override > layer
|
|
7581
|
+
// default) rather than short-circuiting on layerGeodesic === false — that
|
|
7582
|
+
// matches the Mapbox render path, so the hit-test geometry never diverges
|
|
7583
|
+
// from the painted edge when a feature opts into geodesic under a non-geodesic
|
|
7584
|
+
// layer. The util returns the original collection when nothing is densified.
|
|
7580
7585
|
const featureCollection = {
|
|
7581
7586
|
type: "FeatureCollection",
|
|
7582
7587
|
features: Array.from(features),
|
|
7583
7588
|
};
|
|
7584
|
-
const densifiedFeatures = layerGeodesic
|
|
7585
|
-
? densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features
|
|
7586
|
-
: features;
|
|
7589
|
+
const densifiedFeatures = densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features;
|
|
7587
7590
|
const degPerPx = degreesPerPixel(zoom, tileSize);
|
|
7588
7591
|
const hits = [];
|
|
7589
7592
|
for (const feature of densifiedFeatures) {
|
|
@@ -7695,14 +7698,16 @@ const promoteToFront = (order, id) => order.includes(id) ? [id, ...order.filter(
|
|
|
7695
7698
|
* preserved and translucent fills do not compound under the winner.
|
|
7696
7699
|
*/
|
|
7697
7700
|
const computePromotionGroupFills = (members, winnerId, restingPeerFills = new Map(), layerGeodesic = undefined, featureStyles = undefined) => {
|
|
7698
|
-
// Densify before peer-vs-winner clip so promotion clips follow great-circle
|
|
7701
|
+
// Densify before peer-vs-winner clip so promotion clips follow great-circle
|
|
7702
|
+
// arcs. Always defer to densifyGeodesicFeatures' per-feature resolution
|
|
7703
|
+
// (geodesic override > layer default) rather than short-circuiting on
|
|
7704
|
+
// layerGeodesic === false — keeps the clip geometry in step with the render
|
|
7705
|
+
// path. The util returns the original collection when nothing is densified.
|
|
7699
7706
|
const featureCollection = {
|
|
7700
7707
|
type: "FeatureCollection",
|
|
7701
7708
|
features: Array.from(members),
|
|
7702
7709
|
};
|
|
7703
|
-
const densifiedMembers = layerGeodesic
|
|
7704
|
-
? densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features
|
|
7705
|
-
: members;
|
|
7710
|
+
const densifiedMembers = densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features;
|
|
7706
7711
|
const geometries = new Map();
|
|
7707
7712
|
for (const feature of densifiedMembers) {
|
|
7708
7713
|
if (feature.id === undefined)
|
|
@@ -7742,14 +7747,16 @@ const computePromotionGroupFills = (members, winnerId, restingPeerFills = new Ma
|
|
|
7742
7747
|
const computeFillTiling = (input) => {
|
|
7743
7748
|
const { features, viewportBounds, resolveStackOrder, selectedFeatureId, suppressedFeatureIds, layerGeodesic, featureStyles, } = input;
|
|
7744
7749
|
// Densify before any polygon/boundary math so clip geometry follows great-circle
|
|
7745
|
-
// arcs on large polygons (mirrors the ADR-0024 precedent for edge labels).
|
|
7750
|
+
// arcs on large polygons (mirrors the ADR-0024 precedent for edge labels). Always
|
|
7751
|
+
// defer to densifyGeodesicFeatures' per-feature resolution (geodesic override >
|
|
7752
|
+
// layer default) rather than short-circuiting on layerGeodesic === false — keeps
|
|
7753
|
+
// the clip geometry in step with the Mapbox render path. The util returns the
|
|
7754
|
+
// original collection when nothing is densified.
|
|
7746
7755
|
const featureCollection = {
|
|
7747
7756
|
type: "FeatureCollection",
|
|
7748
7757
|
features: Array.from(features),
|
|
7749
7758
|
};
|
|
7750
|
-
const densifiedFeatures = layerGeodesic
|
|
7751
|
-
? densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features
|
|
7752
|
-
: features;
|
|
7759
|
+
const densifiedFeatures = densifyGeodesicFeatures(featureCollection, layerGeodesic ?? true, featureStyles).features;
|
|
7753
7760
|
const records = [];
|
|
7754
7761
|
for (const feature of densifiedFeatures) {
|
|
7755
7762
|
if (feature.id === undefined)
|
|
@@ -10127,6 +10134,8 @@ const useShapeFillTiling = ({ api, handles, onFillGeometriesChange, onZIndexOver
|
|
|
10127
10134
|
}, [suppressedFillIdsByHandle]);
|
|
10128
10135
|
const debounceTimerRef = useRef(null);
|
|
10129
10136
|
const idleRecomputeTimerRef = useRef(null);
|
|
10137
|
+
/** Pending deferred selection re-tile (double-rAF handle); see selection effect. */
|
|
10138
|
+
const selectionRecomputeRafRef = useRef(null);
|
|
10130
10139
|
// Currently emitted overrides — used for hit-testing visible fills in handleSettle.
|
|
10131
10140
|
const prevFillOverridesRef = useRef(EMPTY_OVERRIDES);
|
|
10132
10141
|
const prevZIndexOverridesRef = useRef(EMPTY_ZINDEX_OVERRIDES);
|
|
@@ -10381,8 +10390,30 @@ const useShapeFillTiling = ({ api, handles, onFillGeometriesChange, onZIndexOver
|
|
|
10381
10390
|
return;
|
|
10382
10391
|
recompute();
|
|
10383
10392
|
}, [recompute, tilingContentKey]);
|
|
10393
|
+
// Selection forces the selected feature to the front of its overlap group,
|
|
10394
|
+
// which requires a resting re-tile (computeFillTiling depends on
|
|
10395
|
+
// selectedFeatureId via resolveStackOrder). For large layers that re-tile is
|
|
10396
|
+
// expensive (~600ms for 200 polygons over global bounds) and must NOT block
|
|
10397
|
+
// the interaction's first paint — e.g. a panel opening on the clicked feature.
|
|
10398
|
+
// Defer it past the next paint with a double rAF so the click commits and
|
|
10399
|
+
// paints immediately, then the selected polygon restacks one frame later.
|
|
10400
|
+
// Rapid selections coalesce: each schedule cancels the previous pending one.
|
|
10384
10401
|
useEffect(() => {
|
|
10385
|
-
|
|
10402
|
+
if (selectionRecomputeRafRef.current !== null) {
|
|
10403
|
+
cancelAnimationFrame(selectionRecomputeRafRef.current);
|
|
10404
|
+
}
|
|
10405
|
+
selectionRecomputeRafRef.current = requestAnimationFrame(() => {
|
|
10406
|
+
selectionRecomputeRafRef.current = requestAnimationFrame(() => {
|
|
10407
|
+
selectionRecomputeRafRef.current = null;
|
|
10408
|
+
recompute();
|
|
10409
|
+
});
|
|
10410
|
+
});
|
|
10411
|
+
return () => {
|
|
10412
|
+
if (selectionRecomputeRafRef.current !== null) {
|
|
10413
|
+
cancelAnimationFrame(selectionRecomputeRafRef.current);
|
|
10414
|
+
selectionRecomputeRafRef.current = null;
|
|
10415
|
+
}
|
|
10416
|
+
};
|
|
10386
10417
|
}, [recompute, selectedFeatureId]);
|
|
10387
10418
|
// Suppression changes must trigger a recompute so EMPTY_FILL entries are
|
|
10388
10419
|
// added / removed even when the map is idle (covers the decorations-vs-tiling
|
|
@@ -10634,7 +10665,24 @@ const Layers = ({ layers, api, onEntityClick, onEntityDblClick }) => {
|
|
|
10634
10665
|
const [viewportStyleOverrides, setViewportStyleOverrides] = useState(new Map());
|
|
10635
10666
|
const [fillGeometryOverrides, setFillGeometryOverrides] = useState(new Map());
|
|
10636
10667
|
const [zIndexOverrides, setZIndexOverrides] = useState(new Map());
|
|
10668
|
+
// Mirror the authoritative hovered entity into a ref. The settle channel fires
|
|
10669
|
+
// on a ~120ms debounce (outside React's render cycle) and needs to read the
|
|
10670
|
+
// current hover synchronously.
|
|
10671
|
+
const hoveredEntityRef = useRef(layers.interaction.hoveredEntity);
|
|
10672
|
+
useEffect(() => {
|
|
10673
|
+
hoveredEntityRef.current = layers.interaction.hoveredEntity;
|
|
10674
|
+
}, [layers.interaction.hoveredEntity]);
|
|
10637
10675
|
const onSettledHover = useCallback((handleId, featureId) => {
|
|
10676
|
+
// Z-order truth: markers and clusters render above shapes (DOM/symbol portals
|
|
10677
|
+
// over the polygon canvas). When a point entity is the current hovered entity
|
|
10678
|
+
// it is the authoritative top hit under the cursor, so the shape settle — which
|
|
10679
|
+
// is not itself position-gated against the marker layer — must not overwrite it.
|
|
10680
|
+
// Without this guard, moving onto an asset pill that overlaps a site would leave
|
|
10681
|
+
// the covered site as `hoveredEntity` (a stale cross-channel hover write). We read
|
|
10682
|
+
// the single source of truth (`hoveredEntity`) rather than a parallel guard flag.
|
|
10683
|
+
const hovered = hoveredEntityRef.current;
|
|
10684
|
+
if (hovered !== null && (hovered.type === "marker" || hovered.type === "cluster"))
|
|
10685
|
+
return;
|
|
10638
10686
|
// Fill tiling only fires on polygonal features — shapeType is always "polygon".
|
|
10639
10687
|
layers.hover({ type: "shape", id: featureId, handleId, shapeType: "polygon" });
|
|
10640
10688
|
}, [layers]);
|
|
@@ -10696,17 +10744,27 @@ const Layers = ({ layers, api, onEntityClick, onEntityDblClick }) => {
|
|
|
10696
10744
|
});
|
|
10697
10745
|
}, [layers.handles, adaptiveByLayer, viewportStyleOverrides, fillGeometryOverrides, zIndexOverrides, queryAt]);
|
|
10698
10746
|
const { renderedSourceIds, hasSourceReadiness, readyRevision } = useLayerHandleSync(layerPort, enrichedHandles, layers.interaction, decorationLayers);
|
|
10699
|
-
const hoveredEntityRef = useRef(layers.interaction.hoveredEntity);
|
|
10700
|
-
useEffect(() => {
|
|
10701
|
-
hoveredEntityRef.current = layers.interaction.hoveredEntity;
|
|
10702
|
-
}, [layers.interaction.hoveredEntity]);
|
|
10703
10747
|
const coordinatedSelect = useCallback((entity) => {
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
}
|
|
10707
|
-
else {
|
|
10748
|
+
const hovered = hoveredEntityRef.current;
|
|
10749
|
+
if (entity === null || hovered === null) {
|
|
10708
10750
|
layers.select(entity);
|
|
10751
|
+
return;
|
|
10752
|
+
}
|
|
10753
|
+
// Default to the entity the click actually carried — it is the freshest,
|
|
10754
|
+
// most direct signal of what the user hit, and it hit-tests per element
|
|
10755
|
+
// for markers/clusters/routes. The single exception is a shape-on-shape
|
|
10756
|
+
// conflict: the adapter resolves polygon clicks with its own z-order and
|
|
10757
|
+
// full (unclipped) hit areas, so it cannot honor the fill-tiling ownership
|
|
10758
|
+
// model that decides which overlapping site is visually on top (ADR-0021).
|
|
10759
|
+
// There the settle-resolved hovered shape is the precise winner, so we
|
|
10760
|
+
// trust it instead. This is also why an asset pill overlapping a site
|
|
10761
|
+
// selects the asset: a marker click is never a shape, so it bypasses the
|
|
10762
|
+
// exception even when a stale site shape is still the hovered entity.
|
|
10763
|
+
if (entity.type === "shape" && hovered.type === "shape") {
|
|
10764
|
+
layers.select(hovered);
|
|
10765
|
+
return;
|
|
10709
10766
|
}
|
|
10767
|
+
layers.select(entity);
|
|
10710
10768
|
}, [layers]);
|
|
10711
10769
|
const fitOnDblClick = useDblClickFit(api, layers.handles);
|
|
10712
10770
|
const handleDblClick = useCallback((entity) => {
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-map",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@trackunit/react-components": "2.1.
|
|
11
|
-
"@trackunit/css-class-variance-utilities": "1.13.
|
|
12
|
-
"@trackunit/react-form-components": "2.1.
|
|
13
|
-
"@trackunit/react-core-hooks": "1.17.
|
|
14
|
-
"@trackunit/geo-json-utils": "1.14.
|
|
15
|
-
"@trackunit/i18n-library-translation": "2.0.
|
|
16
|
-
"@trackunit/react-modal": "2.1.
|
|
10
|
+
"@trackunit/react-components": "2.1.38",
|
|
11
|
+
"@trackunit/css-class-variance-utilities": "1.13.42",
|
|
12
|
+
"@trackunit/react-form-components": "2.1.40",
|
|
13
|
+
"@trackunit/react-core-hooks": "1.17.51",
|
|
14
|
+
"@trackunit/geo-json-utils": "1.14.45",
|
|
15
|
+
"@trackunit/i18n-library-translation": "2.0.39",
|
|
16
|
+
"@trackunit/react-modal": "2.1.41",
|
|
17
17
|
"react-minimal-pie-chart": "^8.4.0",
|
|
18
|
-
"@trackunit/react-map-adapter-shared": "0.0.
|
|
19
|
-
"@trackunit/react-map-color-utils": "0.0.
|
|
20
|
-
"@trackunit/ui-design-tokens": "1.13.
|
|
18
|
+
"@trackunit/react-map-adapter-shared": "0.0.26",
|
|
19
|
+
"@trackunit/react-map-color-utils": "0.0.11",
|
|
20
|
+
"@trackunit/ui-design-tokens": "1.13.42",
|
|
21
21
|
"@floating-ui/react": "^0.26.25",
|
|
22
22
|
"es-toolkit": "^1.39.10",
|
|
23
23
|
"tailwind-merge": "^2.0.0",
|