@trackunit/react-map 0.0.4-alpha-9d327375fc1.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.
Files changed (224) hide show
  1. package/README.md +296 -0
  2. package/index.cjs.js +9851 -0
  3. package/index.d.ts +1 -0
  4. package/index.esm.js +9752 -0
  5. package/package.json +34 -0
  6. package/src/annotations/getDescriptorContentKey.d.ts +14 -0
  7. package/src/annotations/mapAnnotations.d.ts +76 -0
  8. package/src/annotations/useMapAnnotation.d.ts +16 -0
  9. package/src/annotations/useMapAnnotations.d.ts +17 -0
  10. package/src/appearance/AppearancePreview.d.ts +27 -0
  11. package/src/appearance/AppearancePreviewGrid.d.ts +29 -0
  12. package/src/appearance/appearanceTypes.d.ts +84 -0
  13. package/src/appearance/useMapAppearanceControls.d.ts +29 -0
  14. package/src/clusters/ClusterMarker.d.ts +41 -0
  15. package/src/clusters/ClusterStick.d.ts +97 -0
  16. package/src/clusters/formatClusterCount.d.ts +11 -0
  17. package/src/clusters/stories/ClusterMarkerCompactFormatsDemo.d.ts +7 -0
  18. package/src/clusters/stories/ClusterMarkerCountOnlyDemo.d.ts +7 -0
  19. package/src/clusters/stories/ClusterMarkerDefaultDemo.d.ts +12 -0
  20. package/src/clusters/stories/ClusterMarkerPlaygroundDemo.d.ts +10 -0
  21. package/src/clusters/stories/ClusterMarkerStateDemo.d.ts +9 -0
  22. package/src/clusters/stories/ClusterMarkerWithSegmentsDemo.d.ts +7 -0
  23. package/src/clusters/stories/ClusterMarkerWithSticksDemo.d.ts +10 -0
  24. package/src/clusters/useClusterCountFormat.d.ts +19 -0
  25. package/src/controls/AnnotationRenderer.d.ts +17 -0
  26. package/src/controls/AnnotationStack.d.ts +21 -0
  27. package/src/controls/ControlRenderer.d.ts +20 -0
  28. package/src/controls/Controls.d.ts +15 -0
  29. package/src/controls/annotationButtonClasses.d.ts +13 -0
  30. package/src/controls/collapseControls.d.ts +37 -0
  31. package/src/controls/defineControlStack.d.ts +10 -0
  32. package/src/controls/deriveResponsiveMode.d.ts +19 -0
  33. package/src/controls/longitudeToGlobeIcon.d.ts +3 -0
  34. package/src/controls/renderers/boolean/BooleanButton.d.ts +16 -0
  35. package/src/controls/renderers/boolean/MenuBooleanItem.d.ts +20 -0
  36. package/src/controls/renderers/boolean/MenuCheckbox.d.ts +14 -0
  37. package/src/controls/renderers/boolean/MenuToggle.d.ts +14 -0
  38. package/src/controls/renderers/button/Button.d.ts +14 -0
  39. package/src/controls/renderers/button/MenuButton.d.ts +16 -0
  40. package/src/controls/renderers/custom/Custom.d.ts +22 -0
  41. package/src/controls/renderers/menu/Menu.d.ts +23 -0
  42. package/src/controls/renderers/menu/Modal.d.ts +21 -0
  43. package/src/controls/renderers/menu/SectionHeader.d.ts +14 -0
  44. package/src/controls/renderers/menu/Separator.d.ts +11 -0
  45. package/src/controls/renderers/radio-group/MenuRadioGroup.d.ts +15 -0
  46. package/src/controls/renderers/radio-group/RadioGroup.d.ts +19 -0
  47. package/src/controls/renderers/radio-group/RadioGroupField.d.ts +17 -0
  48. package/src/controls/renderers/search/MenuSearch.d.ts +15 -0
  49. package/src/controls/renderers/search/Search.d.ts +14 -0
  50. package/src/controls/renderers/select/MenuSelect.d.ts +15 -0
  51. package/src/controls/renderers/select/Select.d.ts +14 -0
  52. package/src/controls/renderers/stepper/MenuStepper.d.ts +17 -0
  53. package/src/controls/renderers/stepper/Stepper.d.ts +18 -0
  54. package/src/controls/renderers/toggle-group/MenuToggleGroup.d.ts +17 -0
  55. package/src/controls/renderers/toggle-group/ToggleGroup.d.ts +15 -0
  56. package/src/controls/renderers/toggle-group/ToggleGroupField.d.ts +19 -0
  57. package/src/controls/renderingRules.d.ts +26 -0
  58. package/src/controls/types.d.ts +395 -0
  59. package/src/controls/useAnimatedPresence.d.ts +15 -0
  60. package/src/controls/useAnnotationPresence.d.ts +16 -0
  61. package/src/controls/useControlStack.d.ts +21 -0
  62. package/src/controls/useControlsConfig.d.ts +27 -0
  63. package/src/controls/usePopoverPlacement.d.ts +33 -0
  64. package/src/core/LoadingIndicatorPill.d.ts +9 -0
  65. package/src/core/MapLoadingState.d.ts +17 -0
  66. package/src/core/SafeAreaLayoutContext.d.ts +5 -0
  67. package/src/core/__testHelpers.d.ts +23 -0
  68. package/src/core/createMapComponent.d.ts +32 -0
  69. package/src/core/loadingIndicator.d.ts +21 -0
  70. package/src/core/padWithInsets.d.ts +5 -0
  71. package/src/core/types.d.ts +208 -0
  72. package/src/core/useCameraIdle.d.ts +2 -0
  73. package/src/core/useCameraState.d.ts +3 -0
  74. package/src/core/useMap.d.ts +57 -0
  75. package/src/core/useMapAdapterState.d.ts +14 -0
  76. package/src/core/useMapKeyboardNavigation.d.ts +38 -0
  77. package/src/core/usePreviewMap.d.ts +43 -0
  78. package/src/index.d.ts +69 -0
  79. package/src/interactions/interactionReducer.d.ts +31 -0
  80. package/src/layers/Layers.d.ts +32 -0
  81. package/src/layers/__testHelpers.d.ts +24 -0
  82. package/src/layers/buildExpandedIds.d.ts +17 -0
  83. package/src/layers/image-overlay/useImageOverlay.d.ts +39 -0
  84. package/src/layers/internal/MapLayerContext.d.ts +24 -0
  85. package/src/layers/internal/adaptiveMarkerResolution.d.ts +33 -0
  86. package/src/layers/internal/computeBounds.d.ts +34 -0
  87. package/src/layers/internal/countFeaturesInViewport.d.ts +14 -0
  88. package/src/layers/internal/createLazyGetter.d.ts +16 -0
  89. package/src/layers/internal/layerHelpers.d.ts +18 -0
  90. package/src/layers/internal/markerPortalZIndex.d.ts +17 -0
  91. package/src/layers/internal/useDblClickFit.d.ts +13 -0
  92. package/src/layers/internal/useEntityInteraction.d.ts +21 -0
  93. package/src/layers/internal/useFitFeatureBounds.d.ts +19 -0
  94. package/src/layers/internal/useLayerHandleSync.d.ts +18 -0
  95. package/src/layers/internal/useLayerReady.d.ts +25 -0
  96. package/src/layers/internal/useMarkerMountBridge.d.ts +41 -0
  97. package/src/layers/internal/useViewportContext.d.ts +34 -0
  98. package/src/layers/mapFocus.d.ts +29 -0
  99. package/src/layers/routes/useRoute.d.ts +39 -0
  100. package/src/layers/shared/measureLabelWidth.d.ts +11 -0
  101. package/src/layers/types.d.ts +131 -0
  102. package/src/layers/useExpandedIds.d.ts +20 -0
  103. package/src/layers/useFitToContent.d.ts +57 -0
  104. package/src/layers/useLayers.d.ts +58 -0
  105. package/src/layers/useMarkers/adaptiveHelpers.d.ts +156 -0
  106. package/src/layers/useMarkers/resolveServerClusters.d.ts +26 -0
  107. package/src/layers/useMarkers/useAdaptiveMarkerHelpers.d.ts +38 -0
  108. package/src/layers/useMarkers/useMarkers.d.ts +127 -0
  109. package/src/layers/useShapes/InteractiveDecoration.d.ts +23 -0
  110. package/src/layers/useShapes/MultiGeometryPlusBadge.d.ts +17 -0
  111. package/src/layers/useShapes/MultiPartBadge.d.ts +11 -0
  112. package/src/layers/useShapes/ShapeAnnotationLabel.d.ts +30 -0
  113. package/src/layers/useShapes/ShapeIcon.d.ts +21 -0
  114. package/src/layers/useShapes/ShapeLabelPill.d.ts +25 -0
  115. package/src/layers/useShapes/buildShapeLabelResolutionContext.d.ts +23 -0
  116. package/src/layers/useShapes/decorationFactories.d.ts +47 -0
  117. package/src/layers/useShapes/edge/ShapeEdgeLabel.d.ts +13 -0
  118. package/src/layers/useShapes/edge/computeEdgeAutoPlacement.d.ts +69 -0
  119. package/src/layers/useShapes/edge/computeEdgeProperties.d.ts +21 -0
  120. package/src/layers/useShapes/edge/edgeInsetsForFeature.d.ts +27 -0
  121. package/src/layers/useShapes/edge/edgeReservations.d.ts +36 -0
  122. package/src/layers/useShapes/edge/findBestEdgePosition.d.ts +247 -0
  123. package/src/layers/useShapes/extractMultiParts.d.ts +25 -0
  124. package/src/layers/useShapes/findBestVisibleVertex.d.ts +18 -0
  125. package/src/layers/useShapes/multiPartDecorations.d.ts +19 -0
  126. package/src/layers/useShapes/shapeDecorations.d.ts +154 -0
  127. package/src/layers/useShapes/shapeFeatureBuilders.d.ts +25 -0
  128. package/src/layers/useShapes/shapeLabelPolicy.d.ts +42 -0
  129. package/src/layers/useShapes/shapeLabelResolution.d.ts +45 -0
  130. package/src/layers/useShapes/shapeSyntheticMarkerRender.d.ts +16 -0
  131. package/src/layers/useShapes/useShapeDecorations.d.ts +56 -0
  132. package/src/layers/useShapes/useShapeLabelHelpers.d.ts +29 -0
  133. package/src/layers/useShapes/useShapes.d.ts +50 -0
  134. package/src/markers/MapMarker.d.ts +115 -0
  135. package/src/markers/MapMarkerIcon.d.ts +21 -0
  136. package/src/markers/animation/applyStickFrame.d.ts +15 -0
  137. package/src/markers/animation/spring.d.ts +45 -0
  138. package/src/markers/animation/stickFrame.d.ts +82 -0
  139. package/src/markers/animation/usePillBackgroundPaint.d.ts +15 -0
  140. package/src/markers/animation/useStickAnimation.d.ts +21 -0
  141. package/src/markers/animation/useStickTween.d.ts +28 -0
  142. package/src/markers/geometry/stickPositioning.d.ts +69 -0
  143. package/src/markers/geometry/useDirectionIndicator.d.ts +8 -0
  144. package/src/markers/internal/MapMarkerStickSurface.d.ts +47 -0
  145. package/src/markers/model/mapMarkerDomPortalStacking.d.ts +22 -0
  146. package/src/markers/model/markerDomTypes.d.ts +68 -0
  147. package/src/markers/model/markerResolvers.d.ts +34 -0
  148. package/src/markers/model/markerSizeMap.d.ts +82 -0
  149. package/src/markers/model/markerTuningParams.d.ts +60 -0
  150. package/src/markers/model/useMarkerStateResolvers.d.ts +25 -0
  151. package/src/markers/shared/MarkerAnimatedSurface.d.ts +20 -0
  152. package/src/markers/shared/MarkerDiscContent.d.ts +11 -0
  153. package/src/markers/shared/mapMarkerVariants.d.ts +35 -0
  154. package/src/markers/shared/markerColors.d.ts +55 -0
  155. package/src/markers/shared/useMarkerColors.d.ts +23 -0
  156. package/src/markers/stories/demoes/AnimationCircleVsPillDemo.d.ts +7 -0
  157. package/src/markers/stories/demoes/CircleIconGuidanceDemo.d.ts +2 -0
  158. package/src/markers/stories/demoes/DefaultDemo.d.ts +14 -0
  159. package/src/markers/stories/demoes/DirectionBehaviorDemo.d.ts +2 -0
  160. package/src/markers/stories/demoes/PillLabelLengthsDemo.d.ts +2 -0
  161. package/src/markers/stories/demoes/StatesDemo.d.ts +7 -0
  162. package/src/markers/stories/demoes/StickModeDemo.d.ts +2 -0
  163. package/src/markers/stories/demoes/StickModePlaygroundDemo.d.ts +10 -0
  164. package/src/markers/stories/helpers/AbsoluteGridPicker.d.ts +17 -0
  165. package/src/markers/stories/helpers/InlineLabel.d.ts +4 -0
  166. package/src/markers/stories/helpers/InlineSlider.d.ts +11 -0
  167. package/src/markers/stories/helpers/LabeledMarker.d.ts +7 -0
  168. package/src/markers/stories/helpers/MarkerRow.d.ts +4 -0
  169. package/src/markers/stories/helpers/SectionHeader.d.ts +4 -0
  170. package/src/markers/stories/helpers/StatesMarkerCell.d.ts +15 -0
  171. package/src/markers/stories/helpers/ThemedPreview.d.ts +10 -0
  172. package/src/markers/stories/helpers/mapMarkerStoryConstants.d.ts +4 -0
  173. package/src/panel/Panel.d.ts +60 -0
  174. package/src/panel/PanelOrchestrator.d.ts +24 -0
  175. package/src/panel/preload/preloadInitiators.d.ts +44 -0
  176. package/src/panel/preload/useHoverPreloadInitiator.d.ts +8 -0
  177. package/src/panel/preload/usePanelPreload.d.ts +46 -0
  178. package/src/panel/preload/useProximityPreloadInitiator.d.ts +13 -0
  179. package/src/panel/store/panels.d.ts +76 -0
  180. package/src/panel/storyHelpers.d.ts +9 -0
  181. package/src/panel/usePanel.d.ts +81 -0
  182. package/src/panel/utils/autoPan.d.ts +78 -0
  183. package/src/panel/utils/panelShellConstants.d.ts +8 -0
  184. package/src/panel/utils/useAutoPanResolver.d.ts +23 -0
  185. package/src/panel/utils/useWheelForwarding.d.ts +18 -0
  186. package/src/stories/InteractionDisplay.d.ts +9 -0
  187. package/src/stories/PositionDisplay.d.ts +9 -0
  188. package/src/stories/_storyCoordUtils.d.ts +8 -0
  189. package/src/testing/mockMapApi.d.ts +29 -0
  190. package/src/translation.d.ts +30 -0
  191. package/translation.cjs.js +30 -0
  192. package/translation.cjs10.js +30 -0
  193. package/translation.cjs11.js +30 -0
  194. package/translation.cjs12.js +30 -0
  195. package/translation.cjs13.js +30 -0
  196. package/translation.cjs14.js +30 -0
  197. package/translation.cjs15.js +30 -0
  198. package/translation.cjs16.js +30 -0
  199. package/translation.cjs17.js +30 -0
  200. package/translation.cjs2.js +30 -0
  201. package/translation.cjs3.js +30 -0
  202. package/translation.cjs4.js +30 -0
  203. package/translation.cjs5.js +30 -0
  204. package/translation.cjs6.js +30 -0
  205. package/translation.cjs7.js +30 -0
  206. package/translation.cjs8.js +30 -0
  207. package/translation.cjs9.js +30 -0
  208. package/translation.esm.js +28 -0
  209. package/translation.esm10.js +28 -0
  210. package/translation.esm11.js +28 -0
  211. package/translation.esm12.js +28 -0
  212. package/translation.esm13.js +28 -0
  213. package/translation.esm14.js +28 -0
  214. package/translation.esm15.js +28 -0
  215. package/translation.esm16.js +28 -0
  216. package/translation.esm17.js +28 -0
  217. package/translation.esm2.js +28 -0
  218. package/translation.esm3.js +28 -0
  219. package/translation.esm4.js +28 -0
  220. package/translation.esm5.js +28 -0
  221. package/translation.esm6.js +28 -0
  222. package/translation.esm7.js +28 -0
  223. package/translation.esm8.js +28 -0
  224. package/translation.esm9.js +28 -0
@@ -0,0 +1,27 @@
1
+ import type { GeoJsonFeature } from "@trackunit/geo-json-utils";
2
+ import type { ShapeLayerHandle } from "../../types";
3
+ import { type ResolvedDecoration } from "../shapeDecorations";
4
+ import { type EdgeInsets } from "./findBestEdgePosition";
5
+ /**
6
+ * Compute per-edge insets from pre-resolved decorations.
7
+ *
8
+ * For each edge, the start inset is the maximum `radiusPx` of any
9
+ * decoration anchored at the start vertex, and the end inset is the
10
+ * maximum `radiusPx` at the end vertex. This tells the label system
11
+ * how much to inset from each corner to avoid overlapping decorations.
12
+ *
13
+ * Accepts already-resolved decorations so the caller (the unified hook)
14
+ * can resolve once and reuse the results for both rendering and insets.
15
+ *
16
+ * @param resolved - Decorations whose anchors have already been resolved to positions.
17
+ * @param edgeCount - Number of edges in the feature's geometry (as returned by `extractEdges`).
18
+ */
19
+ export declare const computeEdgeInsetsFromResolved: (resolved: ReadonlyArray<ResolvedDecoration>, edgeCount: number) => ReadonlyArray<EdgeInsets> | undefined;
20
+ /**
21
+ * Compute per-edge insets for a single feature based on its decorations.
22
+ *
23
+ * Convenience wrapper that resolves decoration anchors internally.
24
+ * Prefer `computeEdgeInsetsFromResolved` when decorations have already
25
+ * been resolved (e.g. in the unified hook) to avoid redundant resolution.
26
+ */
27
+ export declare const computeEdgeInsetsForFeature: (feature: GeoJsonFeature, handle: ShapeLayerHandle) => ReadonlyArray<EdgeInsets> | undefined;
@@ -0,0 +1,36 @@
1
+ import { type GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ import type { ResolvedDecoration } from "../shapeDecorations";
3
+ /**
4
+ * Reserved pixel space at each end of an edge, computed from decorations
5
+ * that sit on the edge's start or end vertex.
6
+ */
7
+ export type EdgeReservation = Readonly<{
8
+ /** Total pixel length of the edge. */
9
+ totalPx: number;
10
+ /** Available length after subtracting vertex decoration reservations from both ends. */
11
+ safePx: number;
12
+ /** Pixel inset from the start vertex (left end when the edge reads left-to-right). */
13
+ startInsetPx: number;
14
+ /** Pixel inset from the end vertex (right end when the edge reads left-to-right). */
15
+ endInsetPx: number;
16
+ }>;
17
+ /**
18
+ * Compute edge reservations for a list of edges given the resolved
19
+ * decorations for the same feature.
20
+ *
21
+ * For each edge `[startVertex, endVertex]`, the start inset is the maximum
22
+ * `radiusPx` of any decoration at the start vertex, and the end inset is the
23
+ * maximum `radiusPx` of any decoration at the end vertex. The safe length
24
+ * is `totalPx - startInset - endInset`, clamped to zero.
25
+ *
26
+ * @param edges - Consecutive vertex pairs forming edges (as returned by `extractEdges`).
27
+ * @param decorations - Resolved decorations for the same feature.
28
+ * @param zoom - Current map zoom level.
29
+ * @param tileSize - Tile size (256 for Google, 512 for Mapbox).
30
+ */
31
+ export declare const computeEdgeReservations: (edges: ReadonlyArray<readonly [GeoJsonPosition, GeoJsonPosition]>, decorations: ReadonlyArray<ResolvedDecoration>, zoom: number, tileSize: number) => ReadonlyArray<EdgeReservation>;
32
+ /**
33
+ * Look up the reservation for a specific edge. Returns a default reservation
34
+ * with zero insets if the edge index is out of range.
35
+ */
36
+ export declare const getEdgeReservation: (reservations: ReadonlyArray<EdgeReservation>, edgeIndex: number) => EdgeReservation;
@@ -0,0 +1,247 @@
1
+ import { type GeoJsonBbox, type GeoJsonFeatureCollection, type GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ type ClippedSegment = readonly [number, number, number, number];
3
+ /**
4
+ * - `"fits"` — label fits on the chosen edge.
5
+ * - `"clipped"` — placed for layout but hidden (edge too narrow for auto placement).
6
+ * - `"overflow"` — label is shown even when it extends past the edge (force placement).
7
+ */
8
+ export type EdgeFitMode = "fits" | "clipped" | "overflow";
9
+ export type EdgeIdentity = readonly [GeoJsonPosition, GeoJsonPosition];
10
+ export type EdgeLabelAnchor = "left" | "center" | "right";
11
+ export type EdgeLabelLayout = Readonly<{
12
+ type: "edge";
13
+ directionPx: readonly [number, number];
14
+ anchor: EdgeLabelAnchor;
15
+ outwardSide: "above" | "below";
16
+ }> | Readonly<{
17
+ type: "point";
18
+ }>;
19
+ export type EdgePlacement = Readonly<{
20
+ position: GeoJsonPosition;
21
+ layout: EdgeLabelLayout;
22
+ fitMode: EdgeFitMode;
23
+ edgeIdentity: EdgeIdentity | null;
24
+ /** Pixel width available for the label between both-side insets. */
25
+ availableWidthPx: number;
26
+ }>;
27
+ /**
28
+ * Per-edge insets from decorations sitting on the edge's start/end vertices.
29
+ * Provided by the edge reservation system to avoid labels overlapping decorations.
30
+ */
31
+ export type EdgeInsets = Readonly<{
32
+ startPx: number;
33
+ endPx: number;
34
+ }>;
35
+ /**
36
+ * Liang-Barsky line-segment clipping against an axis-aligned rectangle.
37
+ * Returns clipped endpoints [x0, y0, x1, y1] or null if the segment is entirely outside.
38
+ */
39
+ export declare const clipSegmentToRect: (x0: number, y0: number, x1: number, y1: number, xMin: number, yMin: number, xMax: number, yMax: number) => ClippedSegment | null;
40
+ export { extractEdges } from "@trackunit/geo-json-utils";
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
+ /**
45
+ * Compute the screen-space angle (in degrees) of a line segment, accounting for
46
+ * Web Mercator distortion. Normalized to [-90, 90] so text reads left-to-right.
47
+ *
48
+ * In Web Mercator, 1 degree of latitude spans more pixels than 1 degree of longitude
49
+ * at latitudes away from the equator (by a factor of sec(lat)). The screen-space
50
+ * deltas are therefore:
51
+ * dx_screen ∝ dlng
52
+ * dy_screen ∝ dlat * sec(midLat)
53
+ */
54
+ export declare const edgeScreenAngleDeg: (x0: number, y0: number, x1: number, y1: number, midLatDeg: number) => number;
55
+ /**
56
+ * Compute the pixel length of a line segment at a given zoom level,
57
+ * accounting for Web Mercator latitude distortion.
58
+ *
59
+ * Web Mercator tile math: worldSize = 256 * 2^zoom pixels for 360 degrees of longitude.
60
+ * Latitude pixels scale by sec(lat).
61
+ */
62
+ 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
+ /**
69
+ * Determine which side of an edge the geometry interior lies on, in screen space.
70
+ * Returns "above" if the label should extend above the edge (interior is below),
71
+ * or "below" if the label should extend below (interior is above).
72
+ *
73
+ * Uses the cross product to find which side the centroid is on, then computes
74
+ * the outward normal's y-component to determine the screen direction.
75
+ * In y-down screen coords: right normal of (dx, dy) = (-dy, dx).
76
+ */
77
+ export declare const computeOutwardSide: (cx0: number, cy0: number, cx1: number, cy1: number, centroid: GeoJsonPosition, midLatDeg: number) => "above" | "below";
78
+ export { extractFirstPointCoordinate } from "@trackunit/geo-json-utils";
79
+ export { isPositionInsideRing as isPointInsideRing } from "@trackunit/geo-json-utils";
80
+ /**
81
+ * Per-candidate information passed to a label placement resolver.
82
+ *
83
+ * All pixel coordinates are viewport-relative (origin = top-left of viewport).
84
+ * `outwardBbox` and `inwardBbox` are also viewport-relative bounding boxes for
85
+ * the rotated label rectangle when placed on the outward and inward side respectively.
86
+ */
87
+ export type EdgeCandidateInfo = Readonly<{
88
+ edgeIdx: number;
89
+ edge: EdgeIdentity;
90
+ pxLen: number;
91
+ angleDeg: number;
92
+ availableWidthPx: number;
93
+ labelFits: boolean;
94
+ outwardSide: "above" | "below";
95
+ startInsetPx: number;
96
+ endInsetPx: number;
97
+ directionPx: readonly [number, number];
98
+ /** Viewport-relative pixel coordinate of the reading-direction start. */
99
+ readingStartPx: readonly [number, number];
100
+ /** Viewport-relative pixel coordinate of the reading-direction end. */
101
+ readingEndPx: readonly [number, number];
102
+ /** Viewport-relative bounding box when label is placed on the outward side. */
103
+ outwardBbox: Readonly<{
104
+ minX: number;
105
+ maxX: number;
106
+ minY: number;
107
+ maxY: number;
108
+ }>;
109
+ /** Viewport-relative bounding box when label is placed on the inward side. */
110
+ inwardBbox: Readonly<{
111
+ minX: number;
112
+ maxX: number;
113
+ minY: number;
114
+ maxY: number;
115
+ }>;
116
+ }>;
117
+ /**
118
+ * All information the resolver needs to choose a placement.
119
+ *
120
+ * `isForced` is true when this is the second-pass retry with `minPixelWidth: 0`
121
+ * — meaning no edge was wide enough for the label in the normal pass.
122
+ * The default resolver uses this to center the label (`anchorT: 0.5`) on forced
123
+ * placements. Custom resolvers can use it to adapt their strategy accordingly.
124
+ */
125
+ export type EdgePlacementContext = Readonly<{
126
+ candidates: ReadonlyArray<EdgeCandidateInfo>;
127
+ viewportWidth: number;
128
+ viewportHeight: number;
129
+ labelPixelWidth: number;
130
+ labelAnchor: EdgeLabelAnchor;
131
+ previousEdgeIdentity?: EdgeIdentity;
132
+ isForced: boolean;
133
+ /**
134
+ * t ∈ [0, 1] along the reading direction of the candidate matching
135
+ * `previousEdgeIdentity`, corresponding to the geographic position of the
136
+ * last known anchor. Only set when `previousAnchorGeo` was provided to
137
+ * `findBestEdgePosition` and the previous edge is still in the candidate pool.
138
+ *
139
+ * The default resolver uses this to pin a left-anchored label no further
140
+ * left than it was on the previous frame ("don't slide back" behaviour).
141
+ */
142
+ previousAnchorClippedT?: number;
143
+ /**
144
+ * The `outwardSide` ("above" | "below") of the label on the previous frame.
145
+ * The default resolver uses this to avoid flipping the label to the other
146
+ * side of the line unless the current side no longer fits in the viewport.
147
+ */
148
+ previousLayoutSide?: "above" | "below";
149
+ }>;
150
+ /**
151
+ * The resolver's answer: which candidate to use and on which side.
152
+ *
153
+ * - `anchorT` (0–1 along the reading direction) sets the geographic position
154
+ * on the edge where the label is pinned. Defaults to the position derived
155
+ * from `labelAnchor` when omitted.
156
+ * - `anchor` overrides which part of the label element is pinned to that geo
157
+ * position (left edge / center / right edge of the pill). Defaults to the
158
+ * config `labelAnchor` when omitted. Pair with `anchorT` for visual centering
159
+ * — e.g. `anchorT: 0.5, anchor: "center"` places the center of the pill at
160
+ * the midpoint of the edge.
161
+ */
162
+ export type EdgePlacementDecision = Readonly<{
163
+ edgeIdx: number;
164
+ side: "outward" | "inward";
165
+ anchorT?: number;
166
+ anchor?: EdgeLabelAnchor;
167
+ }>;
168
+ /**
169
+ * Callback signature for custom edge-label placement strategies.
170
+ * Return `null` to suppress the label entirely.
171
+ */
172
+ export type EdgeLabelPlacementResolver = (context: EdgePlacementContext) => EdgePlacementDecision | null;
173
+ /**
174
+ * Find the best position to place an edge label for shape features.
175
+ *
176
+ * Clips all edges against the viewport, optionally filters by screen angle
177
+ * (when `maxReadableAngleDeg` is provided) and pixel length (must be at least
178
+ * `minPixelWidth` at the current zoom), and returns the anchor position + angle of
179
+ * the best qualifying edge.
180
+ *
181
+ * Selection tiers (applied in order after viewport-inside preference):
182
+ * 0. Hysteresis — if `previousEdgeIdentity` is set and a candidate from that
183
+ * edge still fits the label, stick with it to avoid visual jumps during panning.
184
+ * 1. Panning — when hysteresis fails (previous edge left viewport), score
185
+ * candidates by blending normalized angle (|angle|/90) with normalized
186
+ * proximity to the previous midpoint (dist/maxDist), weighted equally
187
+ * (α = 0.5). Nearby candidates win unless much steeper.
188
+ * 2. Initial — when no previous edge exists, pick the least-steep
189
+ * fitting edge (pure min |angleDeg|).
190
+ * 3. Longest fallback — if no edge fits the label, pick the longest.
191
+ *
192
+ * Both ends of the edge are inset so the label doesn't start or end right at
193
+ * a corner (minimum 6px each, or the edge's decoration reservation if larger).
194
+ * Both endpoint reservations (`startPx`/`endPx` from `edgeInsets`) are
195
+ * subtracted from the usable length: the anchor-side inset reserves space at
196
+ * the text origin, and the far-end inset reserves space at the opposite vertex.
197
+ *
198
+ * `fitMode` is determined by comparing usable length against `labelPixelWidth`:
199
+ * - `"fits"` — label fits within the edge minus both endpoint reservations
200
+ * - `"clipped"` — label doesn't fit on this edge
201
+ *
202
+ * When `labelPlacementResolver` is provided it receives the full
203
+ * `EdgePlacementContext` (all candidates with both-side bboxes in viewport-relative
204
+ * coordinates) and returns an `EdgePlacementDecision` (or `null` to suppress).
205
+ * When omitted, `defaultEdgeLabelPlacementResolver` is used.
206
+ *
207
+ * The `outwardSide` indicates which side of the edge is away from the shape
208
+ * interior, computed via centroid cross product.
209
+ */
210
+ export type FindBestEdgePositionConfig = Readonly<{
211
+ features: GeoJsonFeatureCollection;
212
+ viewportBounds: Readonly<GeoJsonBbox>;
213
+ zoom: number;
214
+ minPixelWidth: number;
215
+ labelPixelWidth: number;
216
+ tileSize?: number;
217
+ maxReadableAngleDeg?: number;
218
+ previousEdgeIdentity?: EdgeIdentity;
219
+ /** Geographic position of the previous anchor — used to implement "don't slide back left" hysteresis. */
220
+ previousAnchorGeo?: GeoJsonPosition;
221
+ /**
222
+ * The layout side ("above" | "below") of the label on the previous frame.
223
+ * Used to prevent the label from flipping sides unless the current side
224
+ * no longer fits within the viewport.
225
+ */
226
+ previousLayoutSide?: "above" | "below";
227
+ edgeInsets?: ReadonlyArray<EdgeInsets>;
228
+ labelAnchor?: EdgeLabelAnchor;
229
+ labelPlacementResolver?: EdgeLabelPlacementResolver;
230
+ /** True when this is the forced retry pass (minPixelWidth was relaxed to 0). Passed to EdgePlacementContext. */
231
+ isForced?: boolean;
232
+ }>;
233
+ /**
234
+ * Default edge-label placement strategy.
235
+ *
236
+ * Builds a valid pool from candidates where at least one side (outward or
237
+ * inward) fits within the viewport. Applies the standard selection tiers
238
+ * (hysteresis → proximity blend → least-steep → longest fallback) to pick
239
+ * the best candidate, then chooses the outward side when it fits — falling
240
+ * back to the inward side otherwise.
241
+ *
242
+ * This replaces the previous anchor-nudging approach: instead of pushing the
243
+ * anchor along the edge to avoid horizontal overflow, it simply flips the label
244
+ * to the inward side of the edge, which naturally avoids the overflow.
245
+ */
246
+ export declare const defaultEdgeLabelPlacementResolver: (context: EdgePlacementContext) => EdgePlacementDecision | null;
247
+ export declare const findBestEdgePosition: ({ features, viewportBounds, zoom, minPixelWidth, labelPixelWidth, tileSize, maxReadableAngleDeg, previousEdgeIdentity, previousAnchorGeo, previousLayoutSide, edgeInsets, labelAnchor, labelPlacementResolver, isForced, }: FindBestEdgePositionConfig) => EdgePlacement | null;
@@ -0,0 +1,25 @@
1
+ import type { GeoJsonGeometry, GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ /**
3
+ * Describes a single part of a multi-geometry: its representative position
4
+ * (polygon corner / line joint / multipoint coordinate) and bounds.
5
+ */
6
+ export type PartInfo = Readonly<{
7
+ position: GeoJsonPosition;
8
+ minLng: number;
9
+ maxLng: number;
10
+ minLat: number;
11
+ maxLat: number;
12
+ }>;
13
+ /**
14
+ * Decompose a multi-geometry into per-part info (position + bounds).
15
+ *
16
+ * Returns `null` for single geometries (Polygon, LineString, Point) or
17
+ * multi-geometries that contain only one part. Only returns a non-null
18
+ * array when the geometry genuinely has multiple parts.
19
+ *
20
+ * - MultiPolygon -> southwesternmost outer-ring vertex and bounds of each polygon
21
+ * - MultiLineString -> a joint vertex on each line (`floor(n/2)`; two-point lines: first endpoint)
22
+ * - MultiPoint -> the point itself with degenerate bounds
23
+ * - GeometryCollection -> flattens recursively
24
+ */
25
+ export declare const extractMultiParts: (geometry: GeoJsonGeometry) => ReadonlyArray<PartInfo> | null;
@@ -0,0 +1,18 @@
1
+ import type { GeoJsonBbox, GeoJsonGeometry, GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ /**
3
+ * Find the best visible vertex for the `vertex-auto` anchor.
4
+ *
5
+ * Searches ALL parts of multi-geometry, filters to vertices inside the
6
+ * viewport, and returns the one closest to the viewport center.
7
+ * Returns null if no vertices are visible.
8
+ *
9
+ * For Point geometry, returns the single coordinate (regardless of viewport).
10
+ *
11
+ * When `previousPosition` is provided (hysteresis mode):
12
+ * - If the previous position is still inside the viewport, it is returned
13
+ * unchanged — the label stays put during panning.
14
+ * - If the previous position left the viewport, the nearest visible vertex
15
+ * to that position is chosen (not nearest to viewport center) so the
16
+ * label migrates to the closest alternative.
17
+ */
18
+ export declare const findBestVisibleVertex: (geometry: GeoJsonGeometry, viewportBounds: Readonly<GeoJsonBbox>, previousPosition?: GeoJsonPosition) => GeoJsonPosition | null;
@@ -0,0 +1,19 @@
1
+ import type { GeoJsonFeature } from "@trackunit/geo-json-utils";
2
+ import type { ShapeDecoration } from "./shapeDecorations";
3
+ /**
4
+ * Generate multi-geometry "+" badge decorations for a feature.
5
+ *
6
+ * Returns one decoration per part of a multi-geometry (MultiPolygon,
7
+ * MultiLineString, MultiPoint). Each badge is placed at the part's
8
+ * representative position (from `extractMultiParts`) using a `"position"`
9
+ * anchor with the pre-resolved coordinate.
10
+ *
11
+ * For MultiPoint features a `pixelOffset` displaces the badge to the
12
+ * top-right edge of the point circle so it does not obscure the fill area.
13
+ *
14
+ * Returns an empty array for single-geometry features or multi-geometries
15
+ * with only one part.
16
+ *
17
+ * @internal
18
+ */
19
+ export declare const buildMultiPartDecorations: (feature: GeoJsonFeature, strokeColor: string, pointRadius?: number, strokeWidth?: number) => ReadonlyArray<ShapeDecoration>;
@@ -0,0 +1,154 @@
1
+ import type { Placement } from "@floating-ui/react";
2
+ import type { GeoJsonGeometry, GeoJsonPosition } from "@trackunit/geo-json-utils";
3
+ import type { DomPortalDescriptor, PixelOffset, ShapeEntity } from "@trackunit/react-map-adapter-shared";
4
+ import type { RefCallback } from "react";
5
+ import type { EdgeLabelAnchor, EdgeLabelPlacementResolver } from "./edge/findBestEdgePosition";
6
+ import type { ResolveShapeLabel } from "./shapeLabelResolution";
7
+ /**
8
+ * Which side of an edge a decoration extends toward.
9
+ *
10
+ * - `"outward"` / `"inward"` — resolved from polygon winding order. For LineStrings, falls back to `"above"`.
11
+ * - `"above"` / `"below"` — explicit screen-space direction.
12
+ *
13
+ * Default is `"outward"`.
14
+ */
15
+ export type EdgeSide = "outward" | "inward" | "above" | "below";
16
+ /**
17
+ * Where a decoration attaches to a shape's geometry. All variants use category
18
+ * prefixes (`vertex-*` / `edge-*`) except `position` which is universal.
19
+ */
20
+ export type DecorationAnchor =
21
+ /** Explicit geographic coordinate. Works with any geometry type. */
22
+ Readonly<{
23
+ type: "position";
24
+ position: GeoJsonPosition;
25
+ }>
26
+ /** Specific vertex by index in the outer coordinate ring.
27
+ * Returns null for empty geometries or out-of-bounds indices. */
28
+ | Readonly<{
29
+ type: "vertex-index";
30
+ vertexIndex: number;
31
+ }>
32
+ /** Extremal vertex in a cardinal direction (geographic bounding corner).
33
+ * Returns null for Point geometry. */
34
+ | Readonly<{
35
+ type: "vertex-corner";
36
+ corner: "sw" | "nw" | "ne" | "se";
37
+ }>
38
+ /** Viewport-aware automatic vertex selection. Picks the visible vertex
39
+ * closest to the viewport center across ALL parts of multi-geometry.
40
+ * Returns null if no vertices are visible. For Point: the coordinate.
41
+ * Resolved by the unified hook, not by `resolveDecorationPosition`. */
42
+ | Readonly<{
43
+ type: "vertex-auto";
44
+ }>
45
+ /** Midpoint of a specific edge. Optional `side` controls placement relative
46
+ * to the edge direction. Returns null for Point geometry or out-of-bounds edge. */
47
+ | Readonly<{
48
+ type: "edge-midpoint";
49
+ edgeIndex: number;
50
+ side?: EdgeSide;
51
+ }>
52
+ /** Parametric position (`t` in 0..1) along a specific edge. Optional `side`
53
+ * controls placement. Returns null for Point geometry or out-of-bounds edge. */
54
+ | Readonly<{
55
+ type: "edge-parametric";
56
+ edgeIndex: number;
57
+ t: number;
58
+ side?: EdgeSide;
59
+ }>
60
+ /** Viewport-aware automatic edge selection for label placement.
61
+ * Best visible edge for Polygon/LineString; above the coordinate for Point.
62
+ * Optional `resolveLabel` callback decides per-frame whether to render
63
+ * normally (`"auto"`), overflow a too-narrow edge (`"force"`), or suppress
64
+ * (`"hide"`) — see {@link ResolveShapeLabel}. When omitted, behaves as `"auto"`.
65
+ * `labelPlacementResolver` receives `context.isForced = true` on the retry
66
+ * pass so it can adapt anchor or side selection without a separate option.
67
+ * Resolved by the unified hook, not by `resolveDecorationPosition`. */
68
+ | Readonly<{
69
+ type: "edge-auto";
70
+ label: string;
71
+ resolveLabel?: ResolveShapeLabel;
72
+ labelAnchor?: EdgeLabelAnchor;
73
+ labelPlacementResolver?: EdgeLabelPlacementResolver;
74
+ }>;
75
+ /**
76
+ * Declarative descriptor for a visual adornment attached to a shape feature.
77
+ *
78
+ * The decoration system resolves the anchor to a geographic position, manages
79
+ * viewport gating and marker source lifecycle, and tracks the pixel space
80
+ * each decoration reserves (so edge labels can compute safe insets).
81
+ */
82
+ export type ShapeDecoration = Readonly<{
83
+ /** Stable identifier (unique within the feature's decoration list). */
84
+ id: string;
85
+ /** Where this decoration attaches to the geometry. */
86
+ anchor: DecorationAnchor;
87
+ /** Pixel radius this decoration occupies — used for edge-reservation calculations. */
88
+ radiusPx: number;
89
+ /** Additional pixel displacement from the resolved anchor position (screen coordinates). */
90
+ pixelOffset?: PixelOffset;
91
+ /** DOM render function passed to the synthetic marker source. */
92
+ render: DomPortalDescriptor["renderFn"];
93
+ /**
94
+ * Properties passed through as GeoJSON feature properties to the render function.
95
+ * The render function receives these as the `item` argument via portal descriptors.
96
+ */
97
+ sourceProperties?: Readonly<Record<string, unknown>>;
98
+ /** Whether to hide the decoration when its position falls outside the viewport. Default: true. */
99
+ viewportClip?: boolean;
100
+ /**
101
+ * Enable entity hover/select interaction. When true, the system wraps the
102
+ * rendered output with an interactive button providing cursor, keyboard a11y,
103
+ * and focus ring. Default: false.
104
+ */
105
+ interactive?: boolean;
106
+ /**
107
+ * Supplementary click handler called after entity selection.
108
+ * Only invoked when `interactive` is true (or when the unified hook
109
+ * determines that interaction is enabled for this decoration).
110
+ */
111
+ onClick?: (entity: ShapeEntity) => void;
112
+ /**
113
+ * Supplementary hover handler called after entity hover state changes.
114
+ * Receives the entity on hover-enter, null on hover-leave.
115
+ */
116
+ onHover?: (entity: ShapeEntity | null) => void;
117
+ /**
118
+ * When present, the edge-label portal container element is forwarded to
119
+ * this ref callback so a panel (e.g. `usePanel`) can anchor to it.
120
+ * Only active for `edge-auto` decorations.
121
+ */
122
+ panelAnchorRef?: RefCallback<HTMLElement>;
123
+ /**
124
+ * Called with the outward Floating UI `Placement` when an `edge-auto`
125
+ * decoration is placed. Lets the panel's `usePanel` start from the correct
126
+ * side so Floating UI's flip middleware only changes sides when necessary.
127
+ *
128
+ * Mapping: `outwardSide "above"` → `"top"`, `"below"` → `"bottom"`.
129
+ * Point-geometry labels emit no call (no shape-edge direction to infer).
130
+ */
131
+ onEdgePlacement?: (placement: Placement) => void;
132
+ }>;
133
+ /**
134
+ * A decoration whose anchor has been resolved to a concrete geographic position.
135
+ */
136
+ export type ResolvedDecoration = Readonly<{
137
+ decoration: ShapeDecoration;
138
+ position: GeoJsonPosition;
139
+ /** Index of the vertex the decoration sits on (if vertex/corner anchor), or null for edge anchors. */
140
+ vertexIndex: number | null;
141
+ /** Index of the edge the decoration sits on (if edge anchor), or null for vertex anchors. */
142
+ edgeIndex: number | null;
143
+ /** Start and end coordinates of the resolved edge (edge-* anchors only). Null for vertex/position anchors. */
144
+ edgeEndpoints: Readonly<{
145
+ start: GeoJsonPosition;
146
+ end: GeoJsonPosition;
147
+ }> | null;
148
+ }>;
149
+ /**
150
+ * Resolve a `DecorationAnchor` to a geographic position given the geometry's
151
+ * coordinate ring. Returns null if the anchor references an out-of-bounds
152
+ * index or the geometry has no coordinates.
153
+ */
154
+ export declare const resolveDecorationPosition: (anchor: DecorationAnchor, geometry: GeoJsonGeometry) => Omit<ResolvedDecoration, "decoration"> | null;
@@ -0,0 +1,25 @@
1
+ import type { GeoJsonFeatureCollection, GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ import type { MapTheme } from "../../core/types";
3
+ import type { EdgeFitMode, EdgeLabelLayout } from "./edge/findBestEdgePosition";
4
+ export declare const EDGE_LABEL_SUFFIX = "__edge-label";
5
+ export declare const MULTI_BADGE_SUFFIX = "__multi-badge";
6
+ /** Serializable props for DOM-rendered shape edge labels (mirrors `ShapeEdgeLabel`). */
7
+ export type ShapeEdgeLabelFeaturePayload = Readonly<{
8
+ text: string;
9
+ color: string;
10
+ hoveredColor?: string;
11
+ selectedColor?: string;
12
+ theme: MapTheme;
13
+ layout: EdgeLabelLayout;
14
+ fitMode: EdgeFitMode;
15
+ availableWidthPx: number;
16
+ hidden?: boolean;
17
+ handleId: string;
18
+ shapeType: string;
19
+ featureId: string;
20
+ }>;
21
+ export declare const buildEdgeLabelFeatureCollection: (stableId: string, position: GeoJsonPosition, payload: ShapeEdgeLabelFeaturePayload) => GeoJsonFeatureCollection;
22
+ export type MultiBadgeFeaturePayload = Readonly<{
23
+ strokeColor: string;
24
+ }>;
25
+ export declare const buildMultiBadgeFeatureCollection: (stableId: string, position: GeoJsonPosition, payload: MultiBadgeFeaturePayload) => GeoJsonFeatureCollection;
@@ -0,0 +1,42 @@
1
+ import type { GeoJsonFeature } from "@trackunit/geo-json-utils";
2
+ import type { ResolveShapeLabel, ShapeLabelResolution, ShapeLabelResolutionContext } from "./shapeLabelResolution";
3
+ /**
4
+ * The subset of `ShapeLabelResolutionContext` that governs the
5
+ * annotation/edge-label decision. Kept minimal so the policy doesn't
6
+ * depend on viewport-density or geometry fields it doesn't use.
7
+ */
8
+ export type AnnotationContext = Readonly<{
9
+ /** True when the viewport is fully contained inside the shape (no edges visible). */
10
+ isViewportInsideShape: boolean;
11
+ /** True when the feature is the currently selected entity. */
12
+ isSelected: boolean;
13
+ /** True when the feature is the currently hovered entity. */
14
+ isHovered: boolean;
15
+ }>;
16
+ /**
17
+ * Couples the edge-label resolution function with the annotation opt-in
18
+ * predicate so the two cannot drift apart.
19
+ *
20
+ * `resolveLabel` must return `"hide"` whenever `shouldAnnotate` returns
21
+ * `true` for the same feature — enforced structurally by `createShapeLabelPolicy`.
22
+ *
23
+ * See ADR-0017 for rationale.
24
+ */
25
+ export type ShapeLabelPolicy = Readonly<{
26
+ resolveLabel: ResolveShapeLabel;
27
+ shouldAnnotate: (feature: GeoJsonFeature, ctx: AnnotationContext) => boolean;
28
+ }>;
29
+ /**
30
+ * Factory that creates a `ShapeLabelPolicy` where `resolveLabel` and
31
+ * `shouldAnnotate` are structurally coupled: when `annotateWhen` returns
32
+ * `true` for a feature, `resolveLabel` automatically returns `"hide"` so
33
+ * the edge label is suppressed and the annotation takes over.
34
+ *
35
+ * @param opts.annotateWhen - Predicate; returns true when the annotation
36
+ * fallback should render instead of the edge label.
37
+ * @param opts.resolve - Edge-label resolution used when `annotateWhen` is false.
38
+ */
39
+ export declare const createShapeLabelPolicy: (opts: {
40
+ readonly annotateWhen: (feature: GeoJsonFeature, ctx: AnnotationContext) => boolean;
41
+ readonly resolve: (feature: GeoJsonFeature, ctx: ShapeLabelResolutionContext) => ShapeLabelResolution;
42
+ }) => ShapeLabelPolicy;
@@ -0,0 +1,45 @@
1
+ import type { GeoJsonFeature, GeoJsonGeometry } from "@trackunit/geo-json-utils";
2
+ /**
3
+ * Intent returned by a `resolveLabel` callback on an `edge-auto` anchor.
4
+ *
5
+ * - `"auto"` — run placement normally; hide if no qualifying edge is found.
6
+ * - `"force"` — place on the best available edge even if the label overflows
7
+ * its measured width. Still hides if the feature is entirely off-screen.
8
+ * - `"hide"` — suppress unconditionally.
9
+ */
10
+ export type ShapeLabelResolution = "auto" | "force" | "hide";
11
+ /**
12
+ * Per-feature, per-frame context passed to `resolveLabel`. Lets the consumer
13
+ * decide whether a shape's edge label should render, taking into account
14
+ * zoom, viewport density, geometry, and interaction state.
15
+ *
16
+ * Deliberate parallel to the `resolveMode(item, ctx)` callback used by the
17
+ * adaptive marker render mode (see ADR-0008 / ADR-0016).
18
+ */
19
+ export type ShapeLabelResolutionContext = Readonly<{
20
+ /** Current map zoom level. */
21
+ zoom: number;
22
+ /** Number of features from the same layer that intersect the viewport. */
23
+ shapesInViewport: number;
24
+ /** Number of other in-viewport features whose bbox intersects this feature's bbox. */
25
+ overlappingShapesCount: number;
26
+ /** GeoJSON geometry type of the feature. */
27
+ geometryType: GeoJsonGeometry["type"];
28
+ /** Width of the feature's bounding box in screen pixels at the current zoom. */
29
+ bboxPixelWidth: number;
30
+ /** Height of the feature's bounding box in screen pixels at the current zoom. */
31
+ bboxPixelHeight: number;
32
+ /** True when the viewport is fully contained inside the feature (no edges visible). */
33
+ isViewportInsideShape: boolean;
34
+ /** Number of parts in multi-geometries (1 for single geometries). */
35
+ partCount: number;
36
+ /** True when the feature is the currently selected entity. */
37
+ isSelected: boolean;
38
+ /** True when the feature is the currently hovered entity. */
39
+ isHovered: boolean;
40
+ }>;
41
+ /**
42
+ * Callback signature for `edge-auto` `resolveLabel`. Receives the feature and
43
+ * a snapshot of the resolution context; returns the desired render intent.
44
+ */
45
+ export type ResolveShapeLabel = (feature: GeoJsonFeature, ctx: ShapeLabelResolutionContext) => ShapeLabelResolution;
@@ -0,0 +1,16 @@
1
+ import type { DomPortalDescriptor } from "@trackunit/react-map-adapter-shared";
2
+ /**
3
+ * Stable DOM `renderFn` for synthetic edge-label markers — pure rendering only.
4
+ * Reads visual props from GeoJSON `properties`. Interaction (select/hover) is
5
+ * handled externally by the `InteractiveDecoration` wrapper in Layers.
6
+ *
7
+ * The effective background color follows `selected > hovered > base` priority,
8
+ * matching the adapter paint expression order for stroke colors.
9
+ */
10
+ export declare const shapeEdgeLabelPortalRender: DomPortalDescriptor["renderFn"];
11
+ /**
12
+ * Stable DOM `renderFn` for multi-geometry "+" badges on the map.
13
+ *
14
+ * Follows the same `selected > hovered > base` color priority as edge labels.
15
+ */
16
+ export declare const multiBadgePortalRender: DomPortalDescriptor["renderFn"];