@trackunit/react-map 0.1.22 → 0.1.25

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/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@trackunit/react-map",
3
- "version": "0.1.22",
3
+ "version": "0.1.25",
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.33",
11
- "@trackunit/css-class-variance-utilities": "1.13.37",
12
- "@trackunit/react-form-components": "2.1.35",
13
- "@trackunit/react-core-hooks": "1.17.46",
14
- "@trackunit/geo-json-utils": "1.14.40",
15
- "@trackunit/i18n-library-translation": "2.0.34",
16
- "@trackunit/react-modal": "2.1.36",
10
+ "@trackunit/react-components": "2.1.36",
11
+ "@trackunit/css-class-variance-utilities": "1.13.40",
12
+ "@trackunit/react-form-components": "2.1.38",
13
+ "@trackunit/react-core-hooks": "1.17.49",
14
+ "@trackunit/geo-json-utils": "1.14.43",
15
+ "@trackunit/i18n-library-translation": "2.0.37",
16
+ "@trackunit/react-modal": "2.1.39",
17
17
  "react-minimal-pie-chart": "^8.4.0",
18
- "@trackunit/react-map-adapter-shared": "0.0.21",
19
- "@trackunit/react-map-color-utils": "0.0.6",
20
- "@trackunit/ui-design-tokens": "1.13.37",
18
+ "@trackunit/react-map-adapter-shared": "0.0.24",
19
+ "@trackunit/react-map-color-utils": "0.0.9",
20
+ "@trackunit/ui-design-tokens": "1.13.40",
21
21
  "@floating-ui/react": "^0.26.25",
22
22
  "es-toolkit": "^1.39.10",
23
23
  "tailwind-merge": "^2.0.0",
@@ -0,0 +1,36 @@
1
+ import type { GeoJsonPosition, MapApi } from "../core/types";
2
+ /**
3
+ * A point entity that can be tested against the cursor. `position` is the
4
+ * geographic anchor as `[lng, lat]`.
5
+ */
6
+ export type EntityNearCursor = Readonly<{
7
+ id: string;
8
+ position: GeoJsonPosition;
9
+ }>;
10
+ export type UseEntitiesNearCursorOptions = Readonly<{
11
+ /** The point entities to hit-test against the cursor on every move. */
12
+ entities: ReadonlyArray<EntityNearCursor>;
13
+ /** Hit radius around the cursor, in CSS pixels. */
14
+ radiusPx: number;
15
+ }>;
16
+ /**
17
+ * The **Entities Near Cursor** primitive: returns a referentially-stable
18
+ * `ReadonlySet<string>` of entity ids whose projected position is within
19
+ * `radiusPx` CSS pixels of the projected cursor, nearest-first.
20
+ *
21
+ * It is the point-marker counterpart to **Shapes Under Cursor** and runs on the
22
+ * same pure Web Mercator substrate (`@trackunit/geo-json-utils`) fed by
23
+ * `useCameraState` + the RAF-throttled `pointermove` event — adapter-agnostic by
24
+ * construction (see ADR-0025). The hook owns radius geometry only; consumers
25
+ * decide why the ids are wanted (preload, highlight, …) and may cap the set to
26
+ * the nearest N via iteration order.
27
+ *
28
+ * The returned Set keeps a stable identity until either its membership or its
29
+ * nearest-first ordering changes, so wiring it straight into a memo or query
30
+ * does not churn while the cursor drifts within the same cluster of dots at the
31
+ * same relative distances. The host only re-renders when membership or ordering
32
+ * actually changes: the cursor lives in a ref and recomputes commit through an
33
+ * order-sensitive functional update that bails out (returns the previous Set)
34
+ * when nothing changed.
35
+ */
36
+ export declare const useEntitiesNearCursor: (api: MapApi, options: UseEntitiesNearCursorOptions) => ReadonlySet<string>;
package/src/index.d.ts CHANGED
@@ -38,6 +38,7 @@ export { MARKER_TUNING } from "./markers/model/markerTuningParams";
38
38
  export { cvaMapMarker, cvaMarkerIndicator } from "./markers/shared/mapMarkerVariants";
39
39
  export { MARKER_DARK_PILL, MARKER_LIGHT_PILL } from "./markers/shared/markerColors";
40
40
  export type { MarkerColorConfig, MarkerColorCssVars, ResolvedMarkerColors } from "./markers/shared/markerColors";
41
+ export { useEntitiesNearCursor, type EntityNearCursor, type UseEntitiesNearCursorOptions, } from "./cursor/useEntitiesNearCursor";
41
42
  export { type HoverPanelPreloadInitiator, type PanelPreloadInitiator, type PanelPreloadInitiatorType, type ProximityPanelPreloadInitiator, } from "./panel/preload/preloadInitiators";
42
43
  export { usePanelPreload, type UsePanelPreloadOptions } from "./panel/preload/usePanelPreload";
43
44
  export { type PanelStore } from "./panel/store/panels";
@@ -46,7 +47,7 @@ export { type AutoPanContext, type AutoPanResult } from "./panel/utils/autoPan";
46
47
  export type { DecorationAnchor, EdgeSide, ShapeDecoration } from "./layers/useShapes/shapeDecorations";
47
48
  export { type AnnotationContext, type ShapeLabelPolicy } from "./layers/useShapes/shapeLabelPolicy";
48
49
  export type { ResolveShapeLabel, ShapeLabelResolution, ShapeLabelResolutionContext, } from "./layers/useShapes/shapeLabelResolution";
49
- export { type ContendedShape, type ResolveShapeStackOrder, type ShapeStackContext, } from "./layers/useShapes/shapeStackOrder";
50
+ export { type ContendedShape, type ResolveShapeStackOrder } from "./layers/useShapes/shapeStackOrder";
50
51
  export { useImageOverlay, type UseImageOverlayOptions, type UseImageOverlayReturn, } from "./layers/image-overlay/useImageOverlay";
51
52
  export { buildExpandedIds, useExpandedIds, type MapFocus, type MapFocusTier, type MapFocusTierDisplay, } from "./layers/mapFocus";
52
53
  export { useRoute, type UseRouteOptions, type UseRouteReturn } from "./layers/routes/useRoute";
@@ -1,11 +1,13 @@
1
1
  /**
2
- * Measure the rendered pixel width of label text using an off-screen probe
3
- * styled with the same CSS classes as shape edge labels (`cvaShapeLabel`).
2
+ * Measure the rendered pixel width of label text using a Canvas context configured
3
+ * with the same font as shape edge labels (`cvaShapeLabel` / `ShapeLabelPill`).
4
4
  *
5
- * Shape edge labels and other layout utilities that need a DOM-measured width use this.
5
+ * Results are cached per label text. Using Canvas.measureText avoids the forced
6
+ * synchronous layout (reflow) that getBoundingClientRect triggers when called inside
7
+ * a Google Maps onDraw callback — the previous implementation caused ~67ms of blocked
8
+ * main-thread time per draw cycle during zoom (observed in Chrome DevTools trace).
6
9
  *
7
- * Results are cached per label text. In environments where DOM measurement is
8
- * unavailable or returns zero (SSR, jsdom), falls back to a character-count
9
- * estimate (label.length * 6 + 16).
10
+ * Falls back to DOM measurement if canvas is unavailable, and to a character-count
11
+ * estimate in environments without DOM (SSR, jsdom).
10
12
  */
11
13
  export declare const measureLabelWidth: (label: string) => number;
@@ -2,14 +2,13 @@ import { type GeoJsonBbox, type GeoJsonFeature } from "@trackunit/geo-json-utils
2
2
  import type { MapInteractionState } from "@trackunit/react-map-adapter-shared";
3
3
  import type { ShapeLabelResolutionContext } from "./shapeLabelResolution";
4
4
  /**
5
- * Build the per-feature, per-frame context passed to an `edge-auto` anchor's
6
- * `resolveLabel` callback. Pure: no React, no `api` reads — caller supplies the
7
- * already-projected viewport bits, the precomputed `shapesInViewport` count for
8
- * the layer, and the current interaction state.
5
+ * Build the per-feature, per-frame context passed to viewport-aware shape
6
+ * callbacks. Pure: no React, no `api` reads — caller supplies the already-
7
+ * projected viewport bits, the precomputed `shapesInViewport` count for the
8
+ * layer, a lazy overlap count, and the current interaction state.
9
9
  *
10
10
  * Lives next to `shapeLabelResolution.ts` and is consumed by
11
- * `useShapeDecorations` once per feature that has at least one `edge-auto`
12
- * decoration, before any placement work runs.
11
+ * `useShapeDecorations` once per feature, before any placement work runs.
13
12
  */
14
13
  export declare const buildShapeLabelResolutionContext: (feature: GeoJsonFeature, args: Readonly<{
15
14
  handleId: string;
@@ -18,6 +17,6 @@ export declare const buildShapeLabelResolutionContext: (feature: GeoJsonFeature,
18
17
  zoom: number;
19
18
  tileSize: number;
20
19
  shapesInViewport: number;
21
- overlappingShapesCount: number;
20
+ getOverlappingShapesCount: () => number;
22
21
  interaction: MapInteractionState;
23
22
  }>) => ShapeLabelResolutionContext | null;
@@ -39,8 +39,6 @@ export type EdgeInsets = Readonly<{
39
39
  export declare const clipSegmentToRect: (x0: number, y0: number, x1: number, y1: number, xMin: number, yMin: number, xMax: number, yMax: number) => ClippedSegment | null;
40
40
  export { extractEdges } from "@trackunit/geo-json-utils";
41
41
  export { computeGeometryCentroid } from "@trackunit/geo-json-utils";
42
- export declare const lngLatToWebMercatorPx: (lng: number, lat: number, zoom: number, tileSize?: number) => readonly [number, number];
43
- export declare const webMercatorPxToLngLat: (px: number, py: number, zoom: number, tileSize?: number) => readonly [number, number];
44
42
  /**
45
43
  * Compute the screen-space angle (in degrees) of a line segment, accounting for
46
44
  * Web Mercator distortion. Normalized to [-90, 90] so text reads left-to-right.
@@ -60,11 +58,6 @@ export declare const edgeScreenAngleDeg: (x0: number, y0: number, x1: number, y1
60
58
  * Latitude pixels scale by sec(lat).
61
59
  */
62
60
  export declare const edgePixelLength: (x0: number, y0: number, x1: number, y1: number, zoom: number, midLatDeg: number, tileSize?: number) => number;
63
- /**
64
- * Convert a pixel distance to latitude degrees at a given zoom level,
65
- * accounting for Web Mercator latitude distortion.
66
- */
67
- export declare const pixelsToLatDegrees: (pixels: number, zoom: number, latDeg: number, tileSize?: number) => number;
68
61
  /**
69
62
  * Determine which side of an edge the geometry interior lies on, in screen space.
70
63
  * Returns "above" if the label should extend above the edge (interior is below),
@@ -29,7 +29,6 @@ export type FillTilingInput = Readonly<{
29
29
  /** Only features whose bbox intersects this are considered. */
30
30
  viewportBounds: GeoJsonBbox;
31
31
  resolveStackOrder: ResolveShapeStackOrder;
32
- zoom: number;
33
32
  /**
34
33
  * Feature id that is currently selected. Selection forces the feature to the
35
34
  * top of its overlap group, overriding resting order. Takes highest priority.
@@ -12,22 +12,15 @@ export type ContendedShape = Readonly<{
12
12
  /** IDs of other contended shapes that fully contain this one. */
13
13
  containedIn: ReadonlyArray<string>;
14
14
  }>;
15
- /**
16
- * Context for a single resolve pass.
17
- */
18
- export type ShapeStackContext = Readonly<{
19
- /** Current map zoom level. */
20
- zoom: number;
21
- }>;
22
15
  /**
23
16
  * Decides fill ownership for a set of overlapping shapes. Returns the contended
24
17
  * feature IDs ordered **top-first** — the first ID owns shared pixels, later
25
18
  * IDs are clipped against the winners ahead of them.
26
19
  *
27
- * Pure: same inputs must yield the same ordering. Swappable per-layer via the
28
- * `overlap.resolveStackOrder` option on `useShapes`.
20
+ * Pure and zoom-invariant: same contended shapes must yield the same ordering.
21
+ * Swappable per-layer via the `overlap.resolveStackOrder` option on `useShapes`.
29
22
  */
30
- export type ResolveShapeStackOrder = (contended: ReadonlyArray<ContendedShape>, ctx: ShapeStackContext) => ReadonlyArray<string>;
23
+ export type ResolveShapeStackOrder = (contended: ReadonlyArray<ContendedShape>) => ReadonlyArray<string>;
31
24
  /**
32
25
  * Default stack-order policy (ADR-0021 "golden path"). In priority order:
33
26
  *
@@ -8,9 +8,9 @@
8
8
  * both states. The inner indicator disc (`cvaMarkerIndicator` below) carries
9
9
  * its own per-size `h-* w-*`; the outer surface sizes intrinsically around it.
10
10
  *
11
- * `transition-all` enables the morph; the duration and timing-function are
12
- * applied inline from `MARKER_TUNING.animation` so the spring config remains
13
- * the single source of truth (no stale `duration-200 ease-in-out` here).
11
+ * `transitionProperty` is set inline in `MapMarker.tsx` to list only the
12
+ * properties that need to animate, intentionally excluding `width` to prevent
13
+ * non-composited layout thrashing. See `MarkerAnimatedSurface.tsx` for details.
14
14
  */
15
15
  export declare const cvaMapMarker: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
16
16
  /**