@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,156 @@
1
+ import { type AdaptiveResolutionContext, type CircleSymbolDescriptor, type RenderMedium } from "@trackunit/react-map-adapter-shared";
2
+ import type { MapTheme } from "../../core/types";
3
+ import type { MarkerDomSize } from "../../markers/model/markerDomTypes";
4
+ /**
5
+ * Tunables for {@link pickRenderMedium}. All optional — sensible defaults are
6
+ * documented inline.
7
+ */
8
+ export type PickRenderMediumOptions = Readonly<{
9
+ /**
10
+ * If `markersInViewport` exceeds this, the helper returns `"symbol"` even
11
+ * when selected/hovered promotion does not apply. Keeps DOM marker counts
12
+ * bounded so layout cost stays low.
13
+ *
14
+ * @default 200
15
+ */
16
+ maxMarkersForDom?: number;
17
+ /**
18
+ * When `true`, the helper short-circuits to `"dom"` whenever a marker is
19
+ * hovered or selected, so the rich DOM form is always available for
20
+ * interaction even outside the density envelope.
21
+ *
22
+ * @default true
23
+ */
24
+ domMediumOnInteraction?: boolean;
25
+ }>;
26
+ /**
27
+ * Default heuristic for `AdaptiveRenderConfig.resolveMode` (ADR-0009).
28
+ *
29
+ * Returns `"dom"` when the marker is interacted with (hover/select) or when
30
+ * the viewport is not over-dense; otherwise `"symbol"`. Pure — no React, no
31
+ * allocation per call.
32
+ *
33
+ * Consumers use this directly:
34
+ *
35
+ * ```ts
36
+ * resolveMode: (_item, ctx) => pickRenderMedium(ctx)
37
+ * ```
38
+ *
39
+ * Or tune thresholds:
40
+ *
41
+ * ```ts
42
+ * resolveMode: (_item, ctx) =>
43
+ * pickRenderMedium(ctx, { maxMarkersForDom: 80 })
44
+ * ```
45
+ *
46
+ * The library does not impose this heuristic — `resolveMode` always wins. The
47
+ * helper is a documented starting point for the common case (Q6 of the
48
+ * design grilling).
49
+ */
50
+ export declare const pickRenderMedium: (ctx: AdaptiveResolutionContext, options?: PickRenderMediumOptions) => RenderMedium;
51
+ /**
52
+ * Single zoom→size step. The helper picks the breakpoint with the highest
53
+ * `minZoom` that is still `<= ctx.zoom`.
54
+ */
55
+ export type MarkerSizeBreakpoint = Readonly<{
56
+ /** Zoom at or above which this size applies. */
57
+ minZoom: number;
58
+ /** Size to use once `ctx.zoom >= minZoom`. */
59
+ size: MarkerDomSize;
60
+ }>;
61
+ /**
62
+ * Tunables for {@link pickMarkerSize}.
63
+ */
64
+ export type PickMarkerSizeOptions = Readonly<{
65
+ /**
66
+ * Zoom→size ladder, sorted ascending by `minZoom`. The last breakpoint with
67
+ * `minZoom <= ctx.zoom` wins. If `ctx.zoom` is below every breakpoint, the
68
+ * first breakpoint's `size` is returned (the smallest tier acts as the
69
+ * floor).
70
+ *
71
+ * @default DEFAULT_MARKER_SIZE_BREAKPOINTS
72
+ */
73
+ breakpoints?: ReadonlyArray<MarkerSizeBreakpoint>;
74
+ }>;
75
+ /**
76
+ * Five-tier zoom ladder matching the `MapMarker` `size` prop. The chosen
77
+ * thresholds align with how the existing demos read at typical fleet zooms
78
+ * (city → asset → street).
79
+ */
80
+ export declare const DEFAULT_MARKER_SIZE_BREAKPOINTS: ReadonlyArray<MarkerSizeBreakpoint>;
81
+ /**
82
+ * Default zoom→`MarkerDomSize` mapping for the DOM branch of an adaptive
83
+ * `render`.
84
+ *
85
+ * Pure. Iterates the breakpoint ladder once and picks the largest `minZoom`
86
+ * still satisfied by `ctx.zoom`. The breakpoint list must be sorted ascending
87
+ * by `minZoom`; the helper does not sort defensively because it runs per
88
+ * marker per frame.
89
+ *
90
+ * ```ts
91
+ * <MapMarker size={pickMarkerSize(ctx)} ... />
92
+ * ```
93
+ *
94
+ * Consumers can tune the ladder for denser or sparser maps:
95
+ *
96
+ * ```ts
97
+ * pickMarkerSize(ctx, {
98
+ * breakpoints: [
99
+ * { minZoom: 0, size: "xs" },
100
+ * { minZoom: 10, size: "md" },
101
+ * { minZoom: 14, size: "xl" },
102
+ * ],
103
+ * })
104
+ * ```
105
+ */
106
+ export declare const pickMarkerSize: (ctx: AdaptiveResolutionContext, options?: PickMarkerSizeOptions) => MarkerDomSize;
107
+ /**
108
+ * Tunables for {@link pickSymbolDiameter}. The same ladder shape as
109
+ * {@link pickMarkerSize} so `mode: "adaptive"` consumers can share one
110
+ * options object across both branches.
111
+ */
112
+ export type PickSymbolDiameterOptions = PickMarkerSizeOptions;
113
+ /**
114
+ * Symbol counterpart of {@link pickMarkerSize}: returns a `diameterPx` that
115
+ * matches the `MapMarker` `circle` dimension at the same zoom-derived tier
116
+ * (`MARKER_SIZE_MAP`). Use it inside the symbol arm of an adaptive `render`
117
+ * so the WebGL/canvas dot is the same physical size as the DOM circle the
118
+ * marker is about to morph into.
119
+ *
120
+ * ```ts
121
+ * if (state.medium === "symbol") {
122
+ * return { color, diameterPx: pickSymbolDiameter(ctx) };
123
+ * }
124
+ * ```
125
+ *
126
+ * Pure; defers entirely to {@link pickMarkerSize} for the tier decision.
127
+ */
128
+ export declare const pickSymbolDiameter: (ctx: AdaptiveResolutionContext, options?: PickSymbolDiameterOptions) => number;
129
+ /**
130
+ * Width of the disc border drawn on DOM markers (Tailwind `border` = 1 px).
131
+ * Shared by {@link pickSymbolDescriptor} so DOM and symbol layers stay in sync.
132
+ */
133
+ export declare const MARKER_DISC_BORDER_WIDTH_PX = 1;
134
+ /**
135
+ * Full symbol descriptor for adaptive symbol markers.
136
+ *
137
+ * Symbol markers intentionally stay at the `xs` footprint to keep dense-map
138
+ * rendering stable while zooming. We still compute `diameterPx` via
139
+ * {@link pickSymbolDiameter} so the actual pixel value is sourced from the same
140
+ * `MARKER_SIZE_MAP` as DOM markers.
141
+ *
142
+ * Also derives `borderColor`
143
+ * from the same `color-mix()` formula used by `cvaMarkerIndicator` in CSS:
144
+ * - **light**: darken the fill by 30% (mix with black)
145
+ * - **dark**: lighten the fill by 50% (mix with white)
146
+ *
147
+ * Use this instead of the manual `{ color, diameterPx: pickSymbolDiameter(ctx) }`
148
+ * when you want the symbol circle and the DOM disc to look identical:
149
+ *
150
+ * ```ts
151
+ * if (state.medium === "symbol") {
152
+ * return pickSymbolDescriptor(color, theme, sizingCtx);
153
+ * }
154
+ * ```
155
+ */
156
+ export declare const pickSymbolDescriptor: (color: string, theme: MapTheme, ctx: AdaptiveResolutionContext) => CircleSymbolDescriptor;
@@ -0,0 +1,26 @@
1
+ import type { ClusterConfig, ResolutionContext } from "../types";
2
+ type ServerClusterConfig<TCluster> = Extract<ClusterConfig<TCluster>, {
3
+ mode: "server";
4
+ }>;
5
+ /** Subset of {@link ResolutionContext} that callers supply from map state. */
6
+ export type ViewportResolutionContext = Readonly<Pick<ResolutionContext, "zoom" | "markersInViewport" | "viewportBounds">>;
7
+ /**
8
+ * Applies `resolveGroups` entity-shape decisions to a server cluster list.
9
+ *
10
+ * Per ADR-0016: expand-only resolution. Each cluster gets a `memberItems`
11
+ * cross-reference from `data` via `getId`/`getMarkerIds`. `"expand"` with
12
+ * non-null `memberItems` drops the cluster from the kept set; `"expand"` with
13
+ * null `memberItems` (any member missing from the data index) silently falls
14
+ * back to `"keep"`.
15
+ *
16
+ * `viewportCtx` carries live map state (zoom, density, bounds). When omitted
17
+ * all three default to zero/null — pass it from the consumer's map state so
18
+ * `resolveGroups` callbacks receive real values.
19
+ *
20
+ * Pure function — no hooks, safe to call inside `useMemo`.
21
+ */
22
+ export declare const resolveServerClusters: <TItem, TCluster>(cluster: ServerClusterConfig<TCluster>, data: ReadonlyArray<TItem>, getId: (item: TItem) => string, resolveGroups: (cluster: TCluster, memberItems: ReadonlyArray<TItem> | null, ctx: ResolutionContext) => "keep" | "expand", viewportCtx?: ViewportResolutionContext) => Readonly<{
23
+ data: ReadonlyArray<TCluster>;
24
+ memberItems: ReadonlyMap<string, ReadonlyArray<TItem> | null>;
25
+ }>;
26
+ export {};
@@ -0,0 +1,38 @@
1
+ import { resolveMapMarkerDomPortalStacking } from "../../markers/model/mapMarkerDomPortalStacking";
2
+ import { resolveAdaptiveMarkerState } from "../../markers/model/markerResolvers";
3
+ import { downgradeMarkerDomSize, upgradeMarkerDomSize } from "../../markers/model/markerSizeMap";
4
+ import { DEFAULT_MARKER_SIZE_BREAKPOINTS, MARKER_DISC_BORDER_WIDTH_PX, pickMarkerSize, pickRenderMedium, pickSymbolDescriptor, pickSymbolDiameter } from "./adaptiveHelpers";
5
+ export type AdaptiveMarkerHelpers = Readonly<{
6
+ pickMarkerSize: typeof pickMarkerSize;
7
+ pickRenderMedium: typeof pickRenderMedium;
8
+ pickSymbolDescriptor: typeof pickSymbolDescriptor;
9
+ pickSymbolDiameter: typeof pickSymbolDiameter;
10
+ resolveAdaptiveMarkerState: typeof resolveAdaptiveMarkerState;
11
+ downgradeMarkerDomSize: typeof downgradeMarkerDomSize;
12
+ upgradeMarkerDomSize: typeof upgradeMarkerDomSize;
13
+ resolveMapMarkerDomPortalStacking: typeof resolveMapMarkerDomPortalStacking;
14
+ DEFAULT_MARKER_SIZE_BREAKPOINTS: typeof DEFAULT_MARKER_SIZE_BREAKPOINTS;
15
+ MARKER_DISC_BORDER_WIDTH_PX: typeof MARKER_DISC_BORDER_WIDTH_PX;
16
+ }>;
17
+ /**
18
+ * Build-your-own hook: stable references to the full adaptive marker kit.
19
+ *
20
+ * Returns the complete set of primitives needed to hand-roll an adaptive marker
21
+ * layer (DOM ↔ symbol mode switching, zoom-based sizing, portal stacking) while
22
+ * staying consistent with `useMarkers`'s built-in rendering pipeline.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * const {
27
+ * pickRenderMedium, pickMarkerSize, pickSymbolDescriptor,
28
+ * resolveAdaptiveMarkerState, resolveMapMarkerDomPortalStacking,
29
+ * } = useAdaptiveMarkerHelpers();
30
+ *
31
+ * // Inside your adaptive render callback:
32
+ * const medium = pickRenderMedium(ctx);
33
+ * if (medium === "symbol") return pickSymbolDescriptor(color, theme, ctx);
34
+ * const size = pickMarkerSize(ctx);
35
+ * const state = resolveAdaptiveMarkerState({ selected, hovered });
36
+ * ```
37
+ */
38
+ export declare const useAdaptiveMarkerHelpers: () => AdaptiveMarkerHelpers;
@@ -0,0 +1,127 @@
1
+ import { type GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ import type { ControlConfig } from "../../controls/types";
3
+ import type { ClusterConfig, ClusterInfo, ClusterRenderConfig, FitParticipation, MarkerLayerHandle, RenderConfig, ResolutionContext } from "../types";
4
+ import { type ViewportResolutionContext } from "./resolveServerClusters";
5
+ /**
6
+ * Options for the `useMarkers` hook.
7
+ *
8
+ * Two generics, both inferred from usage:
9
+ * - `TItem` from `data`
10
+ * - `TCluster` from `cluster.data` (server-side) or defaults to `ClusterInfo` (client-side / no clustering)
11
+ */
12
+ export type UseMarkersOptions<TItem, TCluster = ClusterInfo> = Readonly<{
13
+ /** Unique ID for this layer */
14
+ id: string;
15
+ /** Human-readable name */
16
+ name: string;
17
+ /** Source data array */
18
+ data: ReadonlyArray<TItem>;
19
+ /** Extract position from item. Return null for items without position. */
20
+ getPosition: (item: TItem) => GeoJsonPosition | null;
21
+ /** Extract unique ID from item */
22
+ getId: (item: TItem) => string;
23
+ /**
24
+ * Clustering strategy. Omit to disable clustering.
25
+ * - mode: "client" -- adapter clusters for you (Mapbox native, Supercluster, etc.)
26
+ * - mode: "server" -- clusters are pre-computed, TCluster inferred from cluster.data
27
+ */
28
+ cluster?: ClusterConfig<TCluster>;
29
+ /** How markers are rendered (symbol, dom, or adaptive) */
30
+ markerRender: RenderConfig<TItem>;
31
+ /**
32
+ * How clusters are rendered. Only relevant when `cluster` is provided.
33
+ * For server-side clusters, the render function receives the full TCluster.
34
+ * For client-side clusters, it receives ClusterInfo (adapter-produced).
35
+ *
36
+ * Per ADR-0016, the render callback's second argument is
37
+ * `ClusterRenderState<TItem>` carrying `memberItems` cross-referenced from
38
+ * `data` (DOM mode only — symbol mode receives `memberItems: null`).
39
+ */
40
+ clusterRender?: ClusterRenderConfig<TCluster, TItem>;
41
+ /** Optional controls to contribute to the map UI */
42
+ controls?: ReadonlyArray<ControlConfig>;
43
+ /** Whether the layer is currently loading its initial data. Omit for static (non-loading) layers. */
44
+ loading?: boolean;
45
+ /** Controls whether this layer's bounds are included in fit-to-content operations. Default: `"all"` */
46
+ fitParticipation?: FitParticipation;
47
+ /**
48
+ * Per-cluster entity-shape decision (ADR-0016).
49
+ *
50
+ * Called per server cluster with `memberItems` cross-referenced from `data`
51
+ * via `getMarkerIds`. `memberItems` is `null` when any member ID is missing
52
+ * from the `data` index — in that case `"expand"` silently falls back to
53
+ * `"keep"`. Omit to leave existing behaviour unchanged.
54
+ *
55
+ * Expansion is expand-only (`TCluster` is preserved); the cluster feature
56
+ * is removed when `"expand"` is returned with non-null `memberItems`. Member
57
+ * features are not synthesized by the hook — they appear in the marker
58
+ * feature collection because the consumer keeps the corresponding items in
59
+ * `data` (the optimistic-fetch pattern described in ADR-0016).
60
+ */
61
+ resolveGroups?: (cluster: TCluster, memberItems: ReadonlyArray<TItem> | null, ctx: ResolutionContext) => "keep" | "expand";
62
+ /**
63
+ * Live map state forwarded to every `resolveGroups` call.
64
+ *
65
+ * When omitted, `zoom`, `markersInViewport`, and `viewportBounds` default
66
+ * to `0`, `0`, and `null`. Provide this whenever `resolveGroups` is used so
67
+ * the callback receives real viewport signals instead of zeros.
68
+ */
69
+ resolutionContext?: ViewportResolutionContext;
70
+ }>;
71
+ /**
72
+ * Return type of `useMarkers`.
73
+ * Extends `MarkerLayerHandle` with convenience count properties.
74
+ */
75
+ export interface UseMarkersReturn extends MarkerLayerHandle {
76
+ /** All positioned items (excludes items where getPosition returned null) */
77
+ readonly positionedCount: number;
78
+ /** Total items including unpositioned */
79
+ readonly totalCount: number;
80
+ /** Number of clusters (0 when clustering is disabled or data is empty) */
81
+ readonly clusterCount: number;
82
+ }
83
+ /**
84
+ * `useMarkers` -- creates a marker layer handle from arbitrary data.
85
+ *
86
+ * Generic over:
87
+ * - `TItem`: the data item type (inferred from `data`)
88
+ * - `TCluster`: the cluster data type (inferred from `cluster.data` for server-side,
89
+ * defaults to `ClusterInfo` for client-side or no clustering)
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * // Server-side clusters with adaptive rendering: high-importance assets
94
+ * // are promoted to DOM while the rest stay as fast WebGL symbols.
95
+ * const fleet = useMarkers({
96
+ * id: "fleet",
97
+ * name: "Fleet Assets",
98
+ * data: assets,
99
+ * getPosition: (a) => a.location,
100
+ * getId: (a) => a.id,
101
+ * cluster: {
102
+ * mode: "server",
103
+ * data: serverClusters,
104
+ * getPosition: (c) => c.center,
105
+ * getId: (c) => c.clusterId,
106
+ * getMarkerIds: (c) => c.assetIds,
107
+ * getCount: (c) => c.count,
108
+ * getBbox: (c) => c.bbox,
109
+ * },
110
+ * markerRender: {
111
+ * mode: "adaptive",
112
+ * anchor: "center",
113
+ * resolveMode: (a) => (a.importance >= 8 ? "dom" : "symbol"),
114
+ * render: (a, state) => {
115
+ * if (state.medium === "symbol") return { color: statusColor(a.status) };
116
+ * return <AssetMarker asset={a} selected={state.selected} />;
117
+ * },
118
+ * },
119
+ * clusterRender: {
120
+ * mode: "dom",
121
+ * anchor: "center",
122
+ * render: (cluster, state) => <ClusterBubble cluster={cluster} />,
123
+ * },
124
+ * });
125
+ * ```
126
+ */
127
+ export declare const useMarkers: <TItem, TCluster = ClusterInfo>(options: UseMarkersOptions<TItem, TCluster>) => UseMarkersReturn;
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from "react";
2
+ import type { Entity, ShapeEntity } from "@trackunit/react-map-adapter-shared";
3
+ type InteractiveDecorationProps = Readonly<{
4
+ entity: ShapeEntity;
5
+ select: (entity: Entity | null) => void;
6
+ hover: (entity: Entity | null) => void;
7
+ onClick?: (entity: ShapeEntity) => void;
8
+ onDblClick?: (entity: ShapeEntity) => void;
9
+ onHover?: (entity: ShapeEntity | null) => void;
10
+ children: ReactNode;
11
+ }>;
12
+ /**
13
+ * Uniform interactive wrapper for shape decorations.
14
+ *
15
+ * Renders a visually-transparent `<button>` that provides cursor, keyboard
16
+ * activation, focus-visible ring, and ARIA semantics. Click dispatches entity
17
+ * selection, mouseenter/mouseleave dispatches entity hover — both followed
18
+ * by optional supplementary callbacks.
19
+ *
20
+ * @internal
21
+ */
22
+ export declare const InteractiveDecoration: ({ entity, select, hover, onClick, onDblClick, onHover, children, }: InteractiveDecorationProps) => import("react/jsx-runtime").JSX.Element;
23
+ export {};
@@ -0,0 +1,17 @@
1
+ type MultiGeometryPlusBadgeProps = Readonly<{
2
+ size: number;
3
+ color: string;
4
+ /** Stroke color for the cross. Defaults to `"white"`. */
5
+ plusColor?: string;
6
+ className?: string;
7
+ "data-testid"?: string;
8
+ title?: string;
9
+ "aria-label"?: string;
10
+ }>;
11
+ /**
12
+ * Compact circular "+" badge for multi-part GeoJSON geometries.
13
+ *
14
+ * @internal
15
+ */
16
+ export declare const MultiGeometryPlusBadge: ({ size, color, plusColor, className, "data-testid": dataTestId, title, "aria-label": ariaLabel, }: MultiGeometryPlusBadgeProps) => import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1,11 @@
1
+ type MultiPartBadgeProps = Readonly<{
2
+ color: string;
3
+ }>;
4
+ /**
5
+ * Badge rendered on each part of a multi-geometry feature on the map.
6
+ * Compact circular "+" matching `ShapeIcon`'s preview badge at 14px parent size.
7
+ *
8
+ * @internal
9
+ */
10
+ export declare const MultiPartBadge: ({ color }: MultiPartBadgeProps) => import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { GeoJsonGeometry } from "@trackunit/geo-json-utils";
2
+ import type { MapTheme } from "../../core/types";
3
+ type ShapeAnnotationLabelProps = Readonly<{
4
+ geometry: GeoJsonGeometry | null;
5
+ color: string;
6
+ label: string;
7
+ theme: MapTheme;
8
+ /** True when the underlying shape is hovered on the map — darkens the pill to match edge-label hover style. */
9
+ isHovered?: boolean;
10
+ /** True when the underlying shape is selected on the map — darkens the pill more to match edge-label selected style. */
11
+ isSelected?: boolean;
12
+ onClick?: () => void;
13
+ onMouseEnter?: () => void;
14
+ onMouseLeave?: () => void;
15
+ }>;
16
+ /**
17
+ * Annotation-mode shape label shown in the `AnnotationStack` when
18
+ * the viewport is fully inside a shape and no edge is visible.
19
+ *
20
+ * Composes `ShapeLabelPill` with a `ShapeIcon` preview on the left
21
+ * to preserve the site's visual identity (color + geometry type icon)
22
+ * in the annotation chrome bar. When `onClick` is provided, a wrapping
23
+ * `<button>` handles interaction; pass `fitFeatureBounds` here to
24
+ * zoom-to-fit on click.
25
+ *
26
+ * Intended for use inside a `MapAnnotationDescriptor` with
27
+ * `type: "custom"` — see `useFleetSiteAnnotation` for the canonical usage.
28
+ */
29
+ export declare const ShapeAnnotationLabel: ({ geometry, color, label, theme, isHovered, isSelected, onClick, onMouseEnter, onMouseLeave, }: ShapeAnnotationLabelProps) => import("react/jsx-runtime").JSX.Element;
30
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { GeoJsonGeometry } from "@trackunit/geo-json-utils";
2
+ type ShapeIconProps = Readonly<{
3
+ geometry: GeoJsonGeometry | null;
4
+ color: string;
5
+ size: number;
6
+ /** Stroke width in pixels. When omitted, scales automatically with `size`. */
7
+ strokeWidth?: number;
8
+ /** Foreground color for the multi-badge plus. Defaults to `"white"`. */
9
+ badgeColor?: string;
10
+ }>;
11
+ /**
12
+ * Renders a miniature SVG preview of a GeoJSON geometry.
13
+ *
14
+ * For multi-geometries (MultiPolygon, MultiLineString, etc.), shows
15
+ * a "multi" badge in the top-right corner. The badge is absolutely
16
+ * positioned so it does not inflate the icon's layout dimensions.
17
+ *
18
+ * @internal
19
+ */
20
+ export declare const ShapeIcon: ({ geometry, color, size, strokeWidth, badgeColor }: ShapeIconProps) => import("react/jsx-runtime").JSX.Element | null;
21
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import type { MapTheme } from "../../core/types";
3
+ type ShapeLabelPillProps = Readonly<{
4
+ color: string;
5
+ theme: MapTheme;
6
+ borderRadius: string;
7
+ className?: string;
8
+ style?: CSSProperties;
9
+ "data-testid"?: string;
10
+ children: ReactNode;
11
+ }>;
12
+ export declare const cvaShapeLabel: (props?: ({
13
+ theme?: "light" | "dark" | null | undefined;
14
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
+ /**
16
+ * Shared styled container for shape labels (edge labels and annotation labels).
17
+ *
18
+ * Always renders a `<div>` — interaction is handled externally by
19
+ * `InteractiveDecoration` (portal path) or a wrapping `<button>` in
20
+ * `ShapeAnnotationLabel` (annotation path).
21
+ *
22
+ * @internal
23
+ */
24
+ export declare const ShapeLabelPill: ({ color, theme, borderRadius, className, style, "data-testid": dataTestId, children, }: ShapeLabelPillProps) => import("react/jsx-runtime").JSX.Element;
25
+ export {};
@@ -0,0 +1,23 @@
1
+ import { type GeoJsonBbox, type GeoJsonFeature } from "@trackunit/geo-json-utils";
2
+ import type { MapInteractionState } from "@trackunit/react-map-adapter-shared";
3
+ import type { ShapeLabelResolutionContext } from "./shapeLabelResolution";
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.
9
+ *
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.
13
+ */
14
+ export declare const buildShapeLabelResolutionContext: (feature: GeoJsonFeature, args: Readonly<{
15
+ handleId: string;
16
+ featureKey: string;
17
+ viewportBounds: Readonly<GeoJsonBbox>;
18
+ zoom: number;
19
+ tileSize: number;
20
+ shapesInViewport: number;
21
+ overlappingShapesCount: number;
22
+ interaction: MapInteractionState;
23
+ }>) => ShapeLabelResolutionContext | null;
@@ -0,0 +1,47 @@
1
+ import type { Placement } from "@floating-ui/react";
2
+ import type { ShapeEntity } from "@trackunit/react-map-adapter-shared";
3
+ import type { RefCallback } from "react";
4
+ import type { EdgeLabelAnchor, EdgeLabelPlacementResolver } from "./edge/findBestEdgePosition";
5
+ import type { ShapeDecoration } from "./shapeDecorations";
6
+ import type { ResolveShapeLabel } from "./shapeLabelResolution";
7
+ /**
8
+ * Create an edge-auto decoration that places a flush label on the best visible
9
+ * edge of a polygon or line. Returns `undefined` when `label` is nullish, so
10
+ * it can be used directly in a `getDecorations` array and filtered automatically.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const zones = useShapes({
15
+ * id: "zones",
16
+ * features: zoneFeatureCollection,
17
+ * resolveStyle: () => ({ stroke: "blue" }),
18
+ * getDecorations: (f) => [
19
+ * edgeLabel(f.properties?.name as string | undefined),
20
+ * ],
21
+ * });
22
+ * ```
23
+ */
24
+ export declare const edgeLabel: (label: string | undefined | null, options?: Readonly<{
25
+ interactive?: boolean;
26
+ onClick?: (entity: ShapeEntity) => void;
27
+ onHover?: (entity: ShapeEntity | null) => void;
28
+ resolveLabel?: ResolveShapeLabel;
29
+ labelAnchor?: EdgeLabelAnchor;
30
+ /**
31
+ * Custom placement resolver. When omitted, the default resolver is used.
32
+ * The resolver receives `context.isForced = true` on the forced retry pass
33
+ * (when no edge fit the label), allowing anchor or side changes without a
34
+ * separate option.
35
+ */
36
+ labelPlacementResolver?: EdgeLabelPlacementResolver;
37
+ /**
38
+ * Forwarded to `ShapeDecoration.panelAnchorRef`. The edge-label portal
39
+ * container element is passed to this callback so a panel can anchor to it.
40
+ */
41
+ panelAnchorRef?: RefCallback<HTMLElement>;
42
+ /**
43
+ * Forwarded to `ShapeDecoration.onEdgePlacement`. Called with the outward
44
+ * Floating UI `Placement` when the label is placed on an edge.
45
+ */
46
+ onEdgePlacement?: (placement: Placement) => void;
47
+ }>) => ShapeDecoration | undefined;
@@ -0,0 +1,13 @@
1
+ import type { MapTheme } from "../../../core/types";
2
+ import type { EdgeFitMode, EdgeLabelLayout } from "./findBestEdgePosition";
3
+ type ShapeEdgeLabelProps = Readonly<{
4
+ text: string;
5
+ color: string;
6
+ theme: MapTheme;
7
+ layout: EdgeLabelLayout;
8
+ fitMode: EdgeFitMode;
9
+ /** When true the label is invisible, waiting for its parent shape to finish rendering. */
10
+ hidden?: boolean;
11
+ }>;
12
+ export declare const ShapeEdgeLabel: ({ text, color, theme, layout, fitMode, hidden }: ShapeEdgeLabelProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,69 @@
1
+ import type { GeoJsonBbox, GeoJsonFeature, GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ import type { MarkerAnchor } from "@trackunit/react-map-adapter-shared";
3
+ import { type EdgeIdentity, type EdgeInsets, type EdgeLabelAnchor, type EdgeLabelPlacementResolver, type EdgePlacement } from "./findBestEdgePosition";
4
+ /**
5
+ * Placement mode chosen by the caller (typically derived from a `resolveLabel`
6
+ * callback on the `edge-auto` anchor).
7
+ *
8
+ * - `"auto"` — only place when an edge fits the measured label width.
9
+ * - `"force"` — if no edge fits, retry with no width requirement so the best
10
+ * available edge holds the label (may overflow visually).
11
+ *
12
+ * In both modes the label is hidden when the feature is entirely off-screen.
13
+ */
14
+ export type EdgeAutoPlacementMode = "auto" | "force";
15
+ export type EdgeAutoPlacementOptions = Readonly<{
16
+ /** Pixel width measurement callback for the label text. */
17
+ labelPixelWidth: (label: string) => number;
18
+ /** Max angle from horizontal for readable rotated text (degrees). */
19
+ maxReadableAngleDeg: number | undefined;
20
+ /** Previous edge identity for hysteresis — avoids label jumping during panning. */
21
+ previousEdgeIdentity: EdgeIdentity | undefined;
22
+ /** Previous point position for hysteresis — avoids MultiPoint label jumping during panning. */
23
+ previousPointPosition: GeoJsonPosition | undefined;
24
+ /**
25
+ * Geographic position of the previous anchor, for "don't slide back left" hysteresis.
26
+ * When provided, a left-anchored label will not retreat left once the viewport has
27
+ * pushed it rightward along the edge to stay in view.
28
+ */
29
+ previousAnchorGeo?: GeoJsonPosition;
30
+ /**
31
+ * The layout side ("above" | "below") from the previous frame.
32
+ * When provided, the label will not flip sides unless the current side
33
+ * no longer fits within the viewport.
34
+ */
35
+ previousLayoutSide?: "above" | "below";
36
+ /** Per-edge insets from shape decorations (e.g. corner badges) to avoid overlap. */
37
+ edgeInsets: ReadonlyArray<EdgeInsets> | undefined;
38
+ /** Where the label attaches along the selected edge. Defaults to `"left"`. */
39
+ labelAnchor?: EdgeLabelAnchor;
40
+ /** Custom placement resolver. When omitted, `defaultEdgeLabelPlacementResolver` is used. */
41
+ labelPlacementResolver?: EdgeLabelPlacementResolver;
42
+ /** Placement mode. Defaults to `"auto"`. */
43
+ mode?: EdgeAutoPlacementMode;
44
+ }>;
45
+ export type EdgeAutoPlacementOutcome = Readonly<{
46
+ type: "placed";
47
+ placement: EdgePlacement;
48
+ anchor: MarkerAnchor;
49
+ }> | Readonly<{
50
+ type: "hidden";
51
+ }>;
52
+ /**
53
+ * Compute the best placement for a shape label.
54
+ *
55
+ * Centralizes layout decisions: geometry branching (Point vs polygon/line),
56
+ * pixel width estimation, edge-finding for non-point geometries, and
57
+ * placement-to-anchor mapping.
58
+ *
59
+ * Returns a discriminated outcome:
60
+ * - `"placed"` — label positioned on an edge with placement + anchor
61
+ * - `"hidden"` — shape is off-screen, no qualifying edge, or (mode `"auto"`) no
62
+ * edge fits the label width
63
+ *
64
+ * Whether the viewport is fully inside the shape is no longer decided here —
65
+ * callers compute that as part of the `ShapeLabelResolutionContext` so the
66
+ * `resolveLabel` callback can react to it (e.g. hide, or fall back to a
67
+ * caller-managed annotation).
68
+ */
69
+ export declare const computeEdgeAutoPlacement: (feature: GeoJsonFeature, label: string, viewportBounds: Readonly<GeoJsonBbox>, zoom: number, tileSize: number, options: EdgeAutoPlacementOptions) => EdgeAutoPlacementOutcome;
@@ -0,0 +1,21 @@
1
+ import { type GeoJsonGeometry, type GeoJsonPosition } from "@trackunit/geo-json-utils";
2
+ /**
3
+ * Computed properties injected into `sourceProperties` for all edge-* anchors.
4
+ * Render functions read these from the `item` argument via portal descriptors.
5
+ */
6
+ export type EdgeComputedProperties = Readonly<{
7
+ /** Screen-space angle of the edge in degrees, normalized to [-90, 90]. */
8
+ angleDeg: number;
9
+ /** Which side of the edge faces away from the shape interior. "above" for LineStrings. */
10
+ outwardSide: "above" | "below";
11
+ /** Length of the edge in screen pixels at the current zoom level. */
12
+ edgePixelLength: number;
13
+ }>;
14
+ /**
15
+ * Compute edge-specific properties from resolved edge endpoints.
16
+ *
17
+ * For Polygons, `outwardSide` uses the cross product with the geometry centroid
18
+ * to determine which side faces away from the interior. For LineStrings (no
19
+ * interior), `outwardSide` defaults to `"above"`.
20
+ */
21
+ export declare const computeEdgeProperties: (start: GeoJsonPosition, end: GeoJsonPosition, geometry: GeoJsonGeometry, zoom: number, tileSize: number) => EdgeComputedProperties;