import type { Placement } from "@floating-ui/react"; import type { GeoJsonGeometry, GeoJsonPosition } from "@trackunit/geo-json-utils"; import type { DomPortalDescriptor, PixelOffset, ShapeEntity } from "@trackunit/react-map-adapter-shared"; import type { RefCallback } from "react"; import type { EdgeLabelAnchor, EdgeLabelPlacementResolver } from "./edge/findBestEdgePosition"; import type { ResolveShapeLabel } from "./shapeLabelResolution"; /** * Which side of an edge a decoration extends toward. * * - `"outward"` / `"inward"` — resolved from polygon winding order. For LineStrings, falls back to `"above"`. * - `"above"` / `"below"` — explicit screen-space direction. * * Default is `"outward"`. */ export type EdgeSide = "outward" | "inward" | "above" | "below"; /** * Where a decoration attaches to a shape's geometry. All variants use category * prefixes (`vertex-*` / `edge-*`) except `position` which is universal. */ export type DecorationAnchor = /** Explicit geographic coordinate. Works with any geometry type. */ Readonly<{ type: "position"; position: GeoJsonPosition; }> /** Specific vertex by index in the outer coordinate ring. * Returns null for empty geometries or out-of-bounds indices. */ | Readonly<{ type: "vertex-index"; vertexIndex: number; }> /** Extremal vertex in a cardinal direction (geographic bounding corner). * Returns null for Point geometry. */ | Readonly<{ type: "vertex-corner"; corner: "sw" | "nw" | "ne" | "se"; }> /** Viewport-aware automatic vertex selection. Picks the visible vertex * closest to the viewport center across ALL parts of multi-geometry. * Returns null if no vertices are visible. For Point: the coordinate. * Resolved by the unified hook, not by `resolveDecorationPosition`. */ | Readonly<{ type: "vertex-auto"; }> /** Midpoint of a specific edge. Optional `side` controls placement relative * to the edge direction. Returns null for Point geometry or out-of-bounds edge. */ | Readonly<{ type: "edge-midpoint"; edgeIndex: number; side?: EdgeSide; }> /** Parametric position (`t` in 0..1) along a specific edge. Optional `side` * controls placement. Returns null for Point geometry or out-of-bounds edge. */ | Readonly<{ type: "edge-parametric"; edgeIndex: number; t: number; side?: EdgeSide; }> /** Viewport-aware automatic edge selection for label placement. * Best visible edge for Polygon/LineString; above the coordinate for Point. * Optional `resolveLabel` callback decides per-frame whether to render * normally (`"auto"`), overflow a too-narrow edge (`"force"`), or suppress * (`"hide"`) — see {@link ResolveShapeLabel}. When omitted, behaves as `"auto"`. * `labelPlacementResolver` receives `context.isForced = true` on the retry * pass so it can adapt anchor or side selection without a separate option. * Resolved by the unified hook, not by `resolveDecorationPosition`. */ | Readonly<{ type: "edge-auto"; label: string; resolveLabel?: ResolveShapeLabel; labelAnchor?: EdgeLabelAnchor; labelPlacementResolver?: EdgeLabelPlacementResolver; }>; /** * Declarative descriptor for a visual adornment attached to a shape feature. * * The decoration system resolves the anchor to a geographic position, manages * viewport gating and marker source lifecycle, and tracks the pixel space * each decoration reserves (so edge labels can compute safe insets). */ export type ShapeDecoration = Readonly<{ /** Stable identifier (unique within the feature's decoration list). */ id: string; /** Where this decoration attaches to the geometry. */ anchor: DecorationAnchor; /** Pixel radius this decoration occupies — used for edge-reservation calculations. */ radiusPx: number; /** Additional pixel displacement from the resolved anchor position (screen coordinates). */ pixelOffset?: PixelOffset; /** DOM render function passed to the synthetic marker source. */ render: DomPortalDescriptor["renderFn"]; /** * Properties passed through as GeoJSON feature properties to the render function. * The render function receives these as the `item` argument via portal descriptors. */ sourceProperties?: Readonly>; /** Whether to hide the decoration when its position falls outside the viewport. Default: true. */ viewportClip?: boolean; /** * Enable entity hover/select interaction. When true, the system wraps the * rendered output with an interactive button providing cursor, keyboard a11y, * and focus ring. Default: false. */ interactive?: boolean; /** * Supplementary click handler called after entity selection. * Only invoked when `interactive` is true (or when the unified hook * determines that interaction is enabled for this decoration). */ onClick?: (entity: ShapeEntity) => void; /** * Supplementary hover handler called after entity hover state changes. * Receives the entity on hover-enter, null on hover-leave. */ onHover?: (entity: ShapeEntity | null) => void; /** * When present, the edge-label portal container element is forwarded to * this ref callback so a panel (e.g. `usePanel`) can anchor to it. * Only active for `edge-auto` decorations. */ panelAnchorRef?: RefCallback; /** * Called with the outward Floating UI `Placement` when an `edge-auto` * decoration is placed. Lets the panel's `usePanel` start from the correct * side so Floating UI's flip middleware only changes sides when necessary. * * Mapping: `outwardSide "above"` → `"top"`, `"below"` → `"bottom"`. * Point-geometry labels emit no call (no shape-edge direction to infer). */ onEdgePlacement?: (placement: Placement) => void; }>; /** * A decoration whose anchor has been resolved to a concrete geographic position. */ export type ResolvedDecoration = Readonly<{ decoration: ShapeDecoration; position: GeoJsonPosition; /** Index of the vertex the decoration sits on (if vertex/corner anchor), or null for edge anchors. */ vertexIndex: number | null; /** Index of the edge the decoration sits on (if edge anchor), or null for vertex anchors. */ edgeIndex: number | null; /** Start and end coordinates of the resolved edge (edge-* anchors only). Null for vertex/position anchors. */ edgeEndpoints: Readonly<{ start: GeoJsonPosition; end: GeoJsonPosition; }> | null; }>; /** * Resolve a `DecorationAnchor` to a geographic position given the geometry's * coordinate ring. Returns null if the anchor references an out-of-bounds * index or the geometry has no coordinates. */ export declare const resolveDecorationPosition: (anchor: DecorationAnchor, geometry: GeoJsonGeometry) => Omit | null;