@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.
- package/README.md +296 -0
- package/index.cjs.js +9851 -0
- package/index.d.ts +1 -0
- package/index.esm.js +9752 -0
- package/package.json +34 -0
- package/src/annotations/getDescriptorContentKey.d.ts +14 -0
- package/src/annotations/mapAnnotations.d.ts +76 -0
- package/src/annotations/useMapAnnotation.d.ts +16 -0
- package/src/annotations/useMapAnnotations.d.ts +17 -0
- package/src/appearance/AppearancePreview.d.ts +27 -0
- package/src/appearance/AppearancePreviewGrid.d.ts +29 -0
- package/src/appearance/appearanceTypes.d.ts +84 -0
- package/src/appearance/useMapAppearanceControls.d.ts +29 -0
- package/src/clusters/ClusterMarker.d.ts +41 -0
- package/src/clusters/ClusterStick.d.ts +97 -0
- package/src/clusters/formatClusterCount.d.ts +11 -0
- package/src/clusters/stories/ClusterMarkerCompactFormatsDemo.d.ts +7 -0
- package/src/clusters/stories/ClusterMarkerCountOnlyDemo.d.ts +7 -0
- package/src/clusters/stories/ClusterMarkerDefaultDemo.d.ts +12 -0
- package/src/clusters/stories/ClusterMarkerPlaygroundDemo.d.ts +10 -0
- package/src/clusters/stories/ClusterMarkerStateDemo.d.ts +9 -0
- package/src/clusters/stories/ClusterMarkerWithSegmentsDemo.d.ts +7 -0
- package/src/clusters/stories/ClusterMarkerWithSticksDemo.d.ts +10 -0
- package/src/clusters/useClusterCountFormat.d.ts +19 -0
- package/src/controls/AnnotationRenderer.d.ts +17 -0
- package/src/controls/AnnotationStack.d.ts +21 -0
- package/src/controls/ControlRenderer.d.ts +20 -0
- package/src/controls/Controls.d.ts +15 -0
- package/src/controls/annotationButtonClasses.d.ts +13 -0
- package/src/controls/collapseControls.d.ts +37 -0
- package/src/controls/defineControlStack.d.ts +10 -0
- package/src/controls/deriveResponsiveMode.d.ts +19 -0
- package/src/controls/longitudeToGlobeIcon.d.ts +3 -0
- package/src/controls/renderers/boolean/BooleanButton.d.ts +16 -0
- package/src/controls/renderers/boolean/MenuBooleanItem.d.ts +20 -0
- package/src/controls/renderers/boolean/MenuCheckbox.d.ts +14 -0
- package/src/controls/renderers/boolean/MenuToggle.d.ts +14 -0
- package/src/controls/renderers/button/Button.d.ts +14 -0
- package/src/controls/renderers/button/MenuButton.d.ts +16 -0
- package/src/controls/renderers/custom/Custom.d.ts +22 -0
- package/src/controls/renderers/menu/Menu.d.ts +23 -0
- package/src/controls/renderers/menu/Modal.d.ts +21 -0
- package/src/controls/renderers/menu/SectionHeader.d.ts +14 -0
- package/src/controls/renderers/menu/Separator.d.ts +11 -0
- package/src/controls/renderers/radio-group/MenuRadioGroup.d.ts +15 -0
- package/src/controls/renderers/radio-group/RadioGroup.d.ts +19 -0
- package/src/controls/renderers/radio-group/RadioGroupField.d.ts +17 -0
- package/src/controls/renderers/search/MenuSearch.d.ts +15 -0
- package/src/controls/renderers/search/Search.d.ts +14 -0
- package/src/controls/renderers/select/MenuSelect.d.ts +15 -0
- package/src/controls/renderers/select/Select.d.ts +14 -0
- package/src/controls/renderers/stepper/MenuStepper.d.ts +17 -0
- package/src/controls/renderers/stepper/Stepper.d.ts +18 -0
- package/src/controls/renderers/toggle-group/MenuToggleGroup.d.ts +17 -0
- package/src/controls/renderers/toggle-group/ToggleGroup.d.ts +15 -0
- package/src/controls/renderers/toggle-group/ToggleGroupField.d.ts +19 -0
- package/src/controls/renderingRules.d.ts +26 -0
- package/src/controls/types.d.ts +395 -0
- package/src/controls/useAnimatedPresence.d.ts +15 -0
- package/src/controls/useAnnotationPresence.d.ts +16 -0
- package/src/controls/useControlStack.d.ts +21 -0
- package/src/controls/useControlsConfig.d.ts +27 -0
- package/src/controls/usePopoverPlacement.d.ts +33 -0
- package/src/core/LoadingIndicatorPill.d.ts +9 -0
- package/src/core/MapLoadingState.d.ts +17 -0
- package/src/core/SafeAreaLayoutContext.d.ts +5 -0
- package/src/core/__testHelpers.d.ts +23 -0
- package/src/core/createMapComponent.d.ts +32 -0
- package/src/core/loadingIndicator.d.ts +21 -0
- package/src/core/padWithInsets.d.ts +5 -0
- package/src/core/types.d.ts +208 -0
- package/src/core/useCameraIdle.d.ts +2 -0
- package/src/core/useCameraState.d.ts +3 -0
- package/src/core/useMap.d.ts +57 -0
- package/src/core/useMapAdapterState.d.ts +14 -0
- package/src/core/useMapKeyboardNavigation.d.ts +38 -0
- package/src/core/usePreviewMap.d.ts +43 -0
- package/src/index.d.ts +69 -0
- package/src/interactions/interactionReducer.d.ts +31 -0
- package/src/layers/Layers.d.ts +32 -0
- package/src/layers/__testHelpers.d.ts +24 -0
- package/src/layers/buildExpandedIds.d.ts +17 -0
- package/src/layers/image-overlay/useImageOverlay.d.ts +39 -0
- package/src/layers/internal/MapLayerContext.d.ts +24 -0
- package/src/layers/internal/adaptiveMarkerResolution.d.ts +33 -0
- package/src/layers/internal/computeBounds.d.ts +34 -0
- package/src/layers/internal/countFeaturesInViewport.d.ts +14 -0
- package/src/layers/internal/createLazyGetter.d.ts +16 -0
- package/src/layers/internal/layerHelpers.d.ts +18 -0
- package/src/layers/internal/markerPortalZIndex.d.ts +17 -0
- package/src/layers/internal/useDblClickFit.d.ts +13 -0
- package/src/layers/internal/useEntityInteraction.d.ts +21 -0
- package/src/layers/internal/useFitFeatureBounds.d.ts +19 -0
- package/src/layers/internal/useLayerHandleSync.d.ts +18 -0
- package/src/layers/internal/useLayerReady.d.ts +25 -0
- package/src/layers/internal/useMarkerMountBridge.d.ts +41 -0
- package/src/layers/internal/useViewportContext.d.ts +34 -0
- package/src/layers/mapFocus.d.ts +29 -0
- package/src/layers/routes/useRoute.d.ts +39 -0
- package/src/layers/shared/measureLabelWidth.d.ts +11 -0
- package/src/layers/types.d.ts +131 -0
- package/src/layers/useExpandedIds.d.ts +20 -0
- package/src/layers/useFitToContent.d.ts +57 -0
- package/src/layers/useLayers.d.ts +58 -0
- package/src/layers/useMarkers/adaptiveHelpers.d.ts +156 -0
- package/src/layers/useMarkers/resolveServerClusters.d.ts +26 -0
- package/src/layers/useMarkers/useAdaptiveMarkerHelpers.d.ts +38 -0
- package/src/layers/useMarkers/useMarkers.d.ts +127 -0
- package/src/layers/useShapes/InteractiveDecoration.d.ts +23 -0
- package/src/layers/useShapes/MultiGeometryPlusBadge.d.ts +17 -0
- package/src/layers/useShapes/MultiPartBadge.d.ts +11 -0
- package/src/layers/useShapes/ShapeAnnotationLabel.d.ts +30 -0
- package/src/layers/useShapes/ShapeIcon.d.ts +21 -0
- package/src/layers/useShapes/ShapeLabelPill.d.ts +25 -0
- package/src/layers/useShapes/buildShapeLabelResolutionContext.d.ts +23 -0
- package/src/layers/useShapes/decorationFactories.d.ts +47 -0
- package/src/layers/useShapes/edge/ShapeEdgeLabel.d.ts +13 -0
- package/src/layers/useShapes/edge/computeEdgeAutoPlacement.d.ts +69 -0
- package/src/layers/useShapes/edge/computeEdgeProperties.d.ts +21 -0
- package/src/layers/useShapes/edge/edgeInsetsForFeature.d.ts +27 -0
- package/src/layers/useShapes/edge/edgeReservations.d.ts +36 -0
- package/src/layers/useShapes/edge/findBestEdgePosition.d.ts +247 -0
- package/src/layers/useShapes/extractMultiParts.d.ts +25 -0
- package/src/layers/useShapes/findBestVisibleVertex.d.ts +18 -0
- package/src/layers/useShapes/multiPartDecorations.d.ts +19 -0
- package/src/layers/useShapes/shapeDecorations.d.ts +154 -0
- package/src/layers/useShapes/shapeFeatureBuilders.d.ts +25 -0
- package/src/layers/useShapes/shapeLabelPolicy.d.ts +42 -0
- package/src/layers/useShapes/shapeLabelResolution.d.ts +45 -0
- package/src/layers/useShapes/shapeSyntheticMarkerRender.d.ts +16 -0
- package/src/layers/useShapes/useShapeDecorations.d.ts +56 -0
- package/src/layers/useShapes/useShapeLabelHelpers.d.ts +29 -0
- package/src/layers/useShapes/useShapes.d.ts +50 -0
- package/src/markers/MapMarker.d.ts +115 -0
- package/src/markers/MapMarkerIcon.d.ts +21 -0
- package/src/markers/animation/applyStickFrame.d.ts +15 -0
- package/src/markers/animation/spring.d.ts +45 -0
- package/src/markers/animation/stickFrame.d.ts +82 -0
- package/src/markers/animation/usePillBackgroundPaint.d.ts +15 -0
- package/src/markers/animation/useStickAnimation.d.ts +21 -0
- package/src/markers/animation/useStickTween.d.ts +28 -0
- package/src/markers/geometry/stickPositioning.d.ts +69 -0
- package/src/markers/geometry/useDirectionIndicator.d.ts +8 -0
- package/src/markers/internal/MapMarkerStickSurface.d.ts +47 -0
- package/src/markers/model/mapMarkerDomPortalStacking.d.ts +22 -0
- package/src/markers/model/markerDomTypes.d.ts +68 -0
- package/src/markers/model/markerResolvers.d.ts +34 -0
- package/src/markers/model/markerSizeMap.d.ts +82 -0
- package/src/markers/model/markerTuningParams.d.ts +60 -0
- package/src/markers/model/useMarkerStateResolvers.d.ts +25 -0
- package/src/markers/shared/MarkerAnimatedSurface.d.ts +20 -0
- package/src/markers/shared/MarkerDiscContent.d.ts +11 -0
- package/src/markers/shared/mapMarkerVariants.d.ts +35 -0
- package/src/markers/shared/markerColors.d.ts +55 -0
- package/src/markers/shared/useMarkerColors.d.ts +23 -0
- package/src/markers/stories/demoes/AnimationCircleVsPillDemo.d.ts +7 -0
- package/src/markers/stories/demoes/CircleIconGuidanceDemo.d.ts +2 -0
- package/src/markers/stories/demoes/DefaultDemo.d.ts +14 -0
- package/src/markers/stories/demoes/DirectionBehaviorDemo.d.ts +2 -0
- package/src/markers/stories/demoes/PillLabelLengthsDemo.d.ts +2 -0
- package/src/markers/stories/demoes/StatesDemo.d.ts +7 -0
- package/src/markers/stories/demoes/StickModeDemo.d.ts +2 -0
- package/src/markers/stories/demoes/StickModePlaygroundDemo.d.ts +10 -0
- package/src/markers/stories/helpers/AbsoluteGridPicker.d.ts +17 -0
- package/src/markers/stories/helpers/InlineLabel.d.ts +4 -0
- package/src/markers/stories/helpers/InlineSlider.d.ts +11 -0
- package/src/markers/stories/helpers/LabeledMarker.d.ts +7 -0
- package/src/markers/stories/helpers/MarkerRow.d.ts +4 -0
- package/src/markers/stories/helpers/SectionHeader.d.ts +4 -0
- package/src/markers/stories/helpers/StatesMarkerCell.d.ts +15 -0
- package/src/markers/stories/helpers/ThemedPreview.d.ts +10 -0
- package/src/markers/stories/helpers/mapMarkerStoryConstants.d.ts +4 -0
- package/src/panel/Panel.d.ts +60 -0
- package/src/panel/PanelOrchestrator.d.ts +24 -0
- package/src/panel/preload/preloadInitiators.d.ts +44 -0
- package/src/panel/preload/useHoverPreloadInitiator.d.ts +8 -0
- package/src/panel/preload/usePanelPreload.d.ts +46 -0
- package/src/panel/preload/useProximityPreloadInitiator.d.ts +13 -0
- package/src/panel/store/panels.d.ts +76 -0
- package/src/panel/storyHelpers.d.ts +9 -0
- package/src/panel/usePanel.d.ts +81 -0
- package/src/panel/utils/autoPan.d.ts +78 -0
- package/src/panel/utils/panelShellConstants.d.ts +8 -0
- package/src/panel/utils/useAutoPanResolver.d.ts +23 -0
- package/src/panel/utils/useWheelForwarding.d.ts +18 -0
- package/src/stories/InteractionDisplay.d.ts +9 -0
- package/src/stories/PositionDisplay.d.ts +9 -0
- package/src/stories/_storyCoordUtils.d.ts +8 -0
- package/src/testing/mockMapApi.d.ts +29 -0
- package/src/translation.d.ts +30 -0
- package/translation.cjs.js +30 -0
- package/translation.cjs10.js +30 -0
- package/translation.cjs11.js +30 -0
- package/translation.cjs12.js +30 -0
- package/translation.cjs13.js +30 -0
- package/translation.cjs14.js +30 -0
- package/translation.cjs15.js +30 -0
- package/translation.cjs16.js +30 -0
- package/translation.cjs17.js +30 -0
- package/translation.cjs2.js +30 -0
- package/translation.cjs3.js +30 -0
- package/translation.cjs4.js +30 -0
- package/translation.cjs5.js +30 -0
- package/translation.cjs6.js +30 -0
- package/translation.cjs7.js +30 -0
- package/translation.cjs8.js +30 -0
- package/translation.cjs9.js +30 -0
- package/translation.esm.js +28 -0
- package/translation.esm10.js +28 -0
- package/translation.esm11.js +28 -0
- package/translation.esm12.js +28 -0
- package/translation.esm13.js +28 -0
- package/translation.esm14.js +28 -0
- package/translation.esm15.js +28 -0
- package/translation.esm16.js +28 -0
- package/translation.esm17.js +28 -0
- package/translation.esm2.js +28 -0
- package/translation.esm3.js +28 -0
- package/translation.esm4.js +28 -0
- package/translation.esm5.js +28 -0
- package/translation.esm6.js +28 -0
- package/translation.esm7.js +28 -0
- package/translation.esm8.js +28 -0
- package/translation.esm9.js +28 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type GeoJsonBbox, type GeoJsonGeometry, type GeoJsonPosition } from "@trackunit/geo-json-utils";
|
|
2
|
+
import type { FitBoundsOptions, MapApi } from "../../core/types";
|
|
3
|
+
/**
|
|
4
|
+
* Compute a bounding box from an array of positions.
|
|
5
|
+
* Returns null if the array is empty.
|
|
6
|
+
*
|
|
7
|
+
* @param positions - Array of [longitude, latitude] positions
|
|
8
|
+
* @returns GeoJsonBbox as [minLon, minLat, maxLon, maxLat] or null
|
|
9
|
+
*/
|
|
10
|
+
export declare const computeBoundsFromPositions: (positions: ReadonlyArray<GeoJsonPosition>) => GeoJsonBbox | null;
|
|
11
|
+
/**
|
|
12
|
+
* For a GeoJSON bbox `[minLon, minLat, maxLon, maxLat]`, returns the **larger**
|
|
13
|
+
* of the east–west extent and the north–south extent, in **degrees**.
|
|
14
|
+
*
|
|
15
|
+
* Used when choosing `fitBounds` padding so padding follows the longer geographic
|
|
16
|
+
* axis (a very thin box still gets a sensible scale).
|
|
17
|
+
*/
|
|
18
|
+
export declare const maxLongitudeLatitudeSpanDegrees: (bbox: GeoJsonBbox) => number;
|
|
19
|
+
/**
|
|
20
|
+
* Compute dynamic fitBounds options based on the geographic span of a bounding box.
|
|
21
|
+
* Smaller shapes get more padding and a maxZoom cap to avoid over-zooming;
|
|
22
|
+
* larger shapes get less padding.
|
|
23
|
+
*/
|
|
24
|
+
export declare const computeFitPadding: (maxSpanDegrees: number) => FitBoundsOptions;
|
|
25
|
+
/**
|
|
26
|
+
* Compute bounds from a geometry and fit the map viewport with span-based dynamic padding.
|
|
27
|
+
* No-op if the geometry yields no positions (e.g. empty GeometryCollection).
|
|
28
|
+
*/
|
|
29
|
+
export declare const fitFeatureBounds: (api: MapApi, geometry: GeoJsonGeometry) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Merge multiple bounding boxes into one that encompasses all of them.
|
|
32
|
+
* Ignores null entries. Returns null if all inputs are null.
|
|
33
|
+
*/
|
|
34
|
+
export declare const mergeBounds: (boxes: ReadonlyArray<GeoJsonBbox | null>) => GeoJsonBbox | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type GeoJsonBbox, type GeoJsonFeatureCollection } from "@trackunit/geo-json-utils";
|
|
2
|
+
/**
|
|
3
|
+
* Count features in `collection` whose geometry overlaps (or touches) `bounds`.
|
|
4
|
+
*
|
|
5
|
+
* Returns the total feature count when `bounds` is null (e.g. before the map
|
|
6
|
+
* has reported its first viewport). For Point features this is equivalent to
|
|
7
|
+
* the previous marker-only point-in-bounds check; for non-Point features it is
|
|
8
|
+
* a cheap bbox-vs-bbox intersection — full geometry clipping is intentionally
|
|
9
|
+
* avoided since this runs every adaptive resolution pass.
|
|
10
|
+
*
|
|
11
|
+
* Shared by both the marker adaptive resolution system and the shape label
|
|
12
|
+
* resolution system so the two never disagree on what "in viewport" means.
|
|
13
|
+
*/
|
|
14
|
+
export declare const countFeaturesInViewport: (collection: GeoJsonFeatureCollection, bounds: Readonly<GeoJsonBbox> | null) => number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a getter that lazily computes and caches a value.
|
|
3
|
+
* The getter calls `compute` on first invocation and returns
|
|
4
|
+
* the cached result on subsequent calls.
|
|
5
|
+
*
|
|
6
|
+
* Designed for use with `useMemo`:
|
|
7
|
+
* ```ts
|
|
8
|
+
* const getBounds = useMemo(
|
|
9
|
+
* () => createLazyGetter(() => computeBounds(data)),
|
|
10
|
+
* [data]
|
|
11
|
+
* );
|
|
12
|
+
* ```
|
|
13
|
+
* When `useMemo` re-runs (deps changed), a fresh getter with an
|
|
14
|
+
* empty cache is created, discarding the old one.
|
|
15
|
+
*/
|
|
16
|
+
export declare const createLazyGetter: <TValue>(compute: () => TValue) => (() => TValue);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DomPortalDescriptor, DomRenderState, MapTheme } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import type { UseLayersReturn } from "../useLayers";
|
|
3
|
+
export declare const EMPTY_PORTALS: ReadonlyArray<DomPortalDescriptor>;
|
|
4
|
+
/** No-op subscribe for when a marker/cluster portal store is not available */
|
|
5
|
+
export declare const noopSubscribe: (_callback: () => void) => (() => void);
|
|
6
|
+
/** Empty snapshot for when a marker/cluster portal store is not available */
|
|
7
|
+
export declare const emptySnapshot: () => ReadonlyArray<DomPortalDescriptor>;
|
|
8
|
+
export declare const DEFAULT_RENDER_STATE: DomRenderState;
|
|
9
|
+
/** Delay before clearing hover state to prevent flickering from rapid mouseleave/mouseenter pairs */
|
|
10
|
+
export declare const HOVER_END_DEBOUNCE_MS = 100;
|
|
11
|
+
/**
|
|
12
|
+
* Compute `DomRenderState` for a given feature ID by checking
|
|
13
|
+
* whether it matches the current selected/hovered entity.
|
|
14
|
+
*
|
|
15
|
+
* `isMounting` is always `false` until slice 6 (SAGA-424) wires
|
|
16
|
+
* `useMarkerMountBridge`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const computeItemRenderState: (featureId: string, interaction: UseLayersReturn["interaction"], theme?: MapTheme, labelVisible?: boolean) => DomRenderState;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ClusterRenderConfig, type DomRenderState, type RenderConfig } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import type { LayerHandle, MarkerLayerHandle } from "../types";
|
|
3
|
+
export declare const findMarkersLayerHandle: (handles: ReadonlyArray<LayerHandle>, sourceId: string) => MarkerLayerHandle | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the CSS `z-index` for a marker/cluster DOM portal container.
|
|
6
|
+
*
|
|
7
|
+
* When `resolveDomPortalStacking` is omitted on the active render config,
|
|
8
|
+
* falls back to the pre–stacking-hierarchy behavior: `circle` geometry with
|
|
9
|
+
* interaction-only phases.
|
|
10
|
+
*
|
|
11
|
+
* Accepts both marker `RenderConfig` and cluster `ClusterRenderConfig`. The
|
|
12
|
+
* `DomRenderState` passed in is the marker-shaped state computed by `<Layers>`
|
|
13
|
+
* for z-index purposes (clusters use the same hovered/selected phase
|
|
14
|
+
* vocabulary even though their render callback ultimately receives a simpler
|
|
15
|
+
* `ClusterRenderState`).
|
|
16
|
+
*/
|
|
17
|
+
export declare const resolveMarkerPortalContainerZIndex: (renderConfig: RenderConfig<unknown> | ClusterRenderConfig<unknown, unknown>, sourceData: unknown, itemState: DomRenderState) => number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Entity } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import type { MapApi } from "../../core/types";
|
|
3
|
+
import type { LayerHandle } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a dblclick handler that fits the viewport to the double-clicked
|
|
6
|
+
* entity's bounds. Behaviour per entity type:
|
|
7
|
+
*
|
|
8
|
+
* - **shape**: fit to the individual feature's geometry
|
|
9
|
+
* - **marker**: panTo + zoom to a close level
|
|
10
|
+
* - **cluster**: fit to bounds of contained markers when any resolve from loaded handles; if none (e.g. server cluster), fit to `bbox` when present on the entity
|
|
11
|
+
* - **route**: fit to the route layer's bounds
|
|
12
|
+
*/
|
|
13
|
+
export declare const useDblClickFit: (api: MapApi | undefined, handles: ReadonlyArray<LayerHandle>) => ((entity: Entity) => void) | undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Entity, LayerPort } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
type UseEntityInteractionConfig = Readonly<{
|
|
3
|
+
layerPort: LayerPort | null;
|
|
4
|
+
hover: (entity: Entity | null) => void;
|
|
5
|
+
select: (entity: Entity | null) => void;
|
|
6
|
+
dblClick?: (entity: Entity) => void;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* Subscribes to entity interaction events (click, dblclick, hover-start,
|
|
10
|
+
* hover-end) from the map adapter and dispatches to the provided callbacks.
|
|
11
|
+
*
|
|
12
|
+
* Hover-end is debounced to prevent flickering from rapid mouseleave/mouseenter
|
|
13
|
+
* pairs when the cursor moves within a marker's DOM tree.
|
|
14
|
+
*
|
|
15
|
+
* @returns A stable callback to guard/unguard against adapter hover events.
|
|
16
|
+
* Call with `true` when a DOM-path hover begins (cancels pending timers and
|
|
17
|
+
* suppresses adapter hover-start/hover-end). Call with `false` when the DOM
|
|
18
|
+
* hover ends, allowing adapter events to flow again.
|
|
19
|
+
*/
|
|
20
|
+
export declare const useEntityInteraction: ({ layerPort, hover, select, dblClick, }: UseEntityInteractionConfig) => ((active: boolean) => void);
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { fitFeatureBounds } from "./computeBounds";
|
|
2
|
+
export type FitFeatureBoundsHelpers = Readonly<{
|
|
3
|
+
fitFeatureBounds: typeof fitFeatureBounds;
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* Build-your-own hook: stable reference to the `fitFeatureBounds` map action.
|
|
7
|
+
*
|
|
8
|
+
* Returns the `fitFeatureBounds` helper for consumers who need to programmatically
|
|
9
|
+
* zoom/fit the viewport to a GeoJSON geometry — for example in click handlers that
|
|
10
|
+
* should navigate to a selected shape or annotation.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const { fitFeatureBounds } = useFitFeatureBounds();
|
|
15
|
+
* // On click:
|
|
16
|
+
* fitFeatureBounds(api, feature.geometry);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare const useFitFeatureBounds: () => FitFeatureBoundsHelpers;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LayerPort, MapInteractionState, MapLayer } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import { type RefObject } from "react";
|
|
3
|
+
import type { LayerHandle } from "../types";
|
|
4
|
+
export type UseLayerHandleSyncReturn = Readonly<{
|
|
5
|
+
renderedSourceIds: RefObject<Set<string>>;
|
|
6
|
+
hasSourceReadiness: boolean;
|
|
7
|
+
readyRevision: number;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Syncs layer handles and interaction state to the map adapter via `LayerPort.setSnapshot`.
|
|
11
|
+
*
|
|
12
|
+
* Computes a full `LayerSnapshot` from the current handles and interaction state
|
|
13
|
+
* and pushes it to the adapter on every change. The adapter owns the diff.
|
|
14
|
+
*
|
|
15
|
+
* Also manages `onSourceReady` subscriptions for shape layers so edge labels
|
|
16
|
+
* can defer rendering until the parent shape source has finished drawing.
|
|
17
|
+
*/
|
|
18
|
+
export declare const useLayerHandleSync: (layerPort: LayerPort | null, handles: ReadonlyArray<LayerHandle>, interaction: MapInteractionState, decorationLayers?: ReadonlyArray<MapLayer>) => UseLayerHandleSyncReturn;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of the `useLayerReady` hook.
|
|
3
|
+
* Provides the `ready` promise, `loadsData` flag, and current `isLoading` state
|
|
4
|
+
* for use in layer handles.
|
|
5
|
+
*/
|
|
6
|
+
export type LayerReadyResult = Readonly<{
|
|
7
|
+
/** Resolves when initial data is available. Stable reference across renders. */
|
|
8
|
+
ready: Promise<void>;
|
|
9
|
+
/** Whether this layer fetches its own data (true when `loading` arg is provided). */
|
|
10
|
+
loadsData: boolean;
|
|
11
|
+
/** Current loading state. */
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Shared hook that tracks initial data readiness for a layer.
|
|
16
|
+
*
|
|
17
|
+
* - `loading: undefined` -> not a data-loading layer; ready immediately.
|
|
18
|
+
* - `loading: false` -> data-loading layer with data already available; ready immediately.
|
|
19
|
+
* - `loading: true` -> data-loading layer still waiting; ready is a deferred promise
|
|
20
|
+
* that resolves on the first `true -> false` transition.
|
|
21
|
+
*
|
|
22
|
+
* Once resolved, the promise stays resolved. Subsequent `true -> false` cycles
|
|
23
|
+
* do NOT re-pend -- this hook tracks *initial* readiness only.
|
|
24
|
+
*/
|
|
25
|
+
export declare const useLayerReady: (loading?: boolean) => LayerReadyResult;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { RenderMedium } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
export type UseMarkerMountBridgeReturn = Readonly<{
|
|
3
|
+
mountingIds: ReadonlySet<string>;
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* Pure transition detector — returns the subset of `currentMediums` whose
|
|
7
|
+
* medium is `"dom"` and whose previous medium was `"symbol"`. Exported for
|
|
8
|
+
* unit tests; the React hook below is the consumer.
|
|
9
|
+
*/
|
|
10
|
+
export declare const computeMountingFeatureIds: (currentMediums: ReadonlyMap<string, RenderMedium>, previousMediums: ReadonlyMap<string, RenderMedium>, existingIds: ReadonlySet<string>) => Set<string>;
|
|
11
|
+
/**
|
|
12
|
+
* State-machine hook tracking `symbol → dom` transitions for adaptive markers
|
|
13
|
+
* (ADR-0009 §"Mounting bridge").
|
|
14
|
+
*
|
|
15
|
+
* On the commit where a feature's medium first appears as `"dom"` (after
|
|
16
|
+
* having been `"symbol"`), its id is included in `mountingIds`. On the next
|
|
17
|
+
* commit, `mountingIds` is empty again. `<Layers>` uses this signal to
|
|
18
|
+
* synthesize a first-commit render state with `isMounting: true` and
|
|
19
|
+
* `selected`/`hovered: false` so consumer-side CSS animations can morph from
|
|
20
|
+
* a small "default" footprint into the resting hover/select form.
|
|
21
|
+
*
|
|
22
|
+
* The hook is purely a state machine — it does not touch the DOM or the map
|
|
23
|
+
* adapter. The caller decides what `currentMediums` reflects; in `<Layers>`'s
|
|
24
|
+
* case it is derived from the published DOM portal descriptors so that the
|
|
25
|
+
* transition is detected at the moment the portal first renders, with
|
|
26
|
+
* non-portal adaptive features defaulting to `"symbol"`.
|
|
27
|
+
*
|
|
28
|
+
* Per-feature `previousMedium` is intentionally not exposed in the public
|
|
29
|
+
* render state — only `isMounting` (Q5 of the SAGA-414 design grilling).
|
|
30
|
+
*
|
|
31
|
+
* The two-pass mechanism is owned here: an internal mount store holds the
|
|
32
|
+
* `previousMediums` map and the active `mountingIds` set, and notifies
|
|
33
|
+
* subscribers via `useSyncExternalStore` rather than a React `setState` (so
|
|
34
|
+
* the cascading-render guard rail does not block the legitimate two-pass
|
|
35
|
+
* pattern). The detection layout effect calls `advance`; the passive clear
|
|
36
|
+
* effect calls `clear` after the browser has had a chance to paint the
|
|
37
|
+
* populated set once.
|
|
38
|
+
*/
|
|
39
|
+
export declare const useMarkerMountBridge: (features: ReadonlyArray<{
|
|
40
|
+
id: string;
|
|
41
|
+
}>, currentMediums: ReadonlyMap<string, RenderMedium>) => UseMarkerMountBridgeReturn;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type GeoJsonBbox, type GeoJsonFeature } from "@trackunit/geo-json-utils";
|
|
2
|
+
import type { MapApi } from "../../core/types";
|
|
3
|
+
/**
|
|
4
|
+
* Shared map view state consumed by adaptive rendering systems (marker render
|
|
5
|
+
* medium selection and shape label resolution). Combines high-frequency camera
|
|
6
|
+
* values with low-frequency tile size, so viewport reading is defined once.
|
|
7
|
+
*/
|
|
8
|
+
export type ViewportContext = Readonly<{
|
|
9
|
+
zoom: number;
|
|
10
|
+
bounds: Readonly<GeoJsonBbox> | null;
|
|
11
|
+
tileSize: number;
|
|
12
|
+
/**
|
|
13
|
+
* Returns `true` when the current viewport bounds are fully contained inside
|
|
14
|
+
* the given feature's geometry. Useful for suppressing edge labels and
|
|
15
|
+
* triggering annotation fallbacks when the user has zoomed into a shape.
|
|
16
|
+
*
|
|
17
|
+
* Returns `false` when bounds are not yet available or the feature has no
|
|
18
|
+
* polygon geometry.
|
|
19
|
+
*/
|
|
20
|
+
isViewportInsideFeature: (feature: GeoJsonFeature) => boolean;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Reads `zoom` and `bounds` from the camera channel and `tileSize` from
|
|
24
|
+
* `api.state`, returning a stable `ViewportContext` reference that only changes
|
|
25
|
+
* when one of the underlying values changes. Falls back to sensible defaults
|
|
26
|
+
* while `api` is initializing.
|
|
27
|
+
*
|
|
28
|
+
* Stability matters because the result flows into `useMemo` deps in both
|
|
29
|
+
* `useAdaptiveMarkerResolution` and `useShapeDecorations` — if a new object
|
|
30
|
+
* came back every render the downstream resolution work would re-run for no
|
|
31
|
+
* reason. Bounds are projected to primitive deps so a fresh bbox array with
|
|
32
|
+
* equal values does not invalidate the memo.
|
|
33
|
+
*/
|
|
34
|
+
export declare const useViewportContext: (api: MapApi | undefined) => ViewportContext;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Icon } from "@trackunit/react-components";
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
export type MapFocusTierDisplay = Readonly<{
|
|
4
|
+
/** Icon name for the marker icon slot and future legend. */
|
|
5
|
+
iconName?: ComponentProps<typeof Icon>["name"];
|
|
6
|
+
/** Hex color driving the marker (symbol + DOM) and future chart slice. */
|
|
7
|
+
color?: string;
|
|
8
|
+
/** Optional static label override used by future focus controls. */
|
|
9
|
+
label?: string;
|
|
10
|
+
}>;
|
|
11
|
+
export type MapFocusTier<TAsset> = Readonly<{
|
|
12
|
+
match: (asset: TAsset) => boolean;
|
|
13
|
+
/** Absent means this tier has no visual representation. */
|
|
14
|
+
display?: MapFocusTierDisplay;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Configurable policy driving marker expansion, icon, and color from a
|
|
18
|
+
* single asset property. Tiers are evaluated in order; first match wins.
|
|
19
|
+
*/
|
|
20
|
+
export type MapFocus<TAsset> = Readonly<{
|
|
21
|
+
/**
|
|
22
|
+
* Stable id used by feature state (for example persisted user preference)
|
|
23
|
+
* instead of serializing predicate functions.
|
|
24
|
+
*/
|
|
25
|
+
id: string;
|
|
26
|
+
tiers: ReadonlyArray<MapFocusTier<TAsset>>;
|
|
27
|
+
}>;
|
|
28
|
+
export { buildExpandedIds } from "./buildExpandedIds";
|
|
29
|
+
export { useExpandedIds } from "./useExpandedIds";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type GeoJsonPosition } from "@trackunit/geo-json-utils";
|
|
2
|
+
import type { ControlConfig } from "../../controls/types";
|
|
3
|
+
import type { FitParticipation, RouteLayerHandle, RouteStyle } from "../types";
|
|
4
|
+
export type UseRouteOptions = Readonly<{
|
|
5
|
+
/** Unique ID for this layer */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Human-readable name */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Ordered waypoints that define the route */
|
|
10
|
+
waypoints: ReadonlyArray<GeoJsonPosition>;
|
|
11
|
+
/** Route styling */
|
|
12
|
+
style: RouteStyle;
|
|
13
|
+
/** Whether the route is interactive (clickable/hoverable). Default: true */
|
|
14
|
+
interactive?: boolean;
|
|
15
|
+
/** Optional controls to contribute to the map UI */
|
|
16
|
+
controls?: ReadonlyArray<ControlConfig>;
|
|
17
|
+
/** Whether the layer is currently loading its initial data. Omit for static (non-loading) layers. */
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
/** Controls whether this layer's bounds are included in fit-to-content operations. Default: `"all"` */
|
|
20
|
+
fitParticipation?: FitParticipation;
|
|
21
|
+
}>;
|
|
22
|
+
export type UseRouteReturn = RouteLayerHandle;
|
|
23
|
+
/**
|
|
24
|
+
* `useRoute` -- creates a route layer handle from an array of waypoints.
|
|
25
|
+
*
|
|
26
|
+
* Converts the waypoints to a GeoJSON LineString internally.
|
|
27
|
+
* Routes need at least 2 waypoints to be rendered.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* const route = useRoute({
|
|
32
|
+
* id: "delivery-route",
|
|
33
|
+
* name: "Delivery Route",
|
|
34
|
+
* waypoints: [[-73.98, 40.75], [-73.97, 40.76], [-73.96, 40.77]],
|
|
35
|
+
* style: { color: "blue", width: 3, showDirectionArrows: true },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const useRoute: (options: UseRouteOptions) => UseRouteReturn;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measure the rendered pixel width of label text using an off-screen probe
|
|
3
|
+
* styled with the same CSS classes as shape edge labels (`cvaShapeLabel`).
|
|
4
|
+
*
|
|
5
|
+
* Shape edge labels and other layout utilities that need a DOM-measured width use this.
|
|
6
|
+
*
|
|
7
|
+
* Results are cached per label text. In environments where DOM measurement is
|
|
8
|
+
* unavailable or returns zero (SSR, jsdom), falls back to a character-count
|
|
9
|
+
* estimate (label.length * 6 + 16).
|
|
10
|
+
*/
|
|
11
|
+
export declare const measureLabelWidth: (label: string) => number;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { GeoJsonBbox, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonPosition } from "@trackunit/geo-json-utils";
|
|
2
|
+
import type { AdaptiveMarkerResolution, ClusterConfig, ClusterRenderConfig, RenderConfig, RouteStyle, ShapeInteractiveMode, ShapeStyle } from "@trackunit/react-map-adapter-shared";
|
|
3
|
+
import type { ControlConfig } from "../controls/types";
|
|
4
|
+
import type { ShapeDecoration } from "./useShapes/shapeDecorations";
|
|
5
|
+
import type { ShapeLabelResolutionContext } from "./useShapes/shapeLabelResolution";
|
|
6
|
+
export type { AdaptiveMarkerResolution, AdaptiveRenderConfig, AdaptiveRenderState, AdaptiveResolutionContext, CircleSymbolDescriptor, ClientClusterConfig, ClusterConfig, ClusterDomRenderConfig, ClusterInfo, ClusterRenderConfig, ClusterRenderState, ClusterSymbolRenderConfig, ClusterSymbolStyle, CommonRenderState, DomPortalStackingInput, DomPortalStackingResolver, DomPortalStackingResult, DomRenderConfig, DomRenderState, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, MarkerAnchor, MarkerDomPortalStackGeometry, MarkerDomPortalStackPhase, PixelOffset, RenderConfig, RenderMedium, ResolutionContext, RouteStyle, ServerClusterConfig, ShapeInteractiveMode, ShapeStyle, ShapeStyleOverrides, SymbolDescriptor, SymbolRenderConfig, SymbolRenderState, } from "@trackunit/react-map-adapter-shared";
|
|
7
|
+
export { computeMarkerDomPortalZIndex } from "@trackunit/react-map-adapter-shared";
|
|
8
|
+
/**
|
|
9
|
+
* Controls whether a layer's bounds are included in fit-to-content operations.
|
|
10
|
+
*
|
|
11
|
+
* - `"all"` — participates in initial auto-fit, manual `fitNow()`, and future always-fit mode.
|
|
12
|
+
* - `"initial"` — only participates in the initial auto-fit on first load.
|
|
13
|
+
* - `"none"` — never participates in any fit operation.
|
|
14
|
+
*/
|
|
15
|
+
export type FitParticipation = "all" | "initial" | "none";
|
|
16
|
+
/**
|
|
17
|
+
* Metadata common to all layer types.
|
|
18
|
+
* Every layer hook returns an object extending this shape.
|
|
19
|
+
*/
|
|
20
|
+
export type LayerMeta = Readonly<{
|
|
21
|
+
/** Unique identifier for this layer */
|
|
22
|
+
id: string;
|
|
23
|
+
/** Human-readable name */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Whether this layer fetches its own data */
|
|
26
|
+
loadsData: boolean;
|
|
27
|
+
/** Whether data is currently loading */
|
|
28
|
+
loading: boolean;
|
|
29
|
+
/** Resolves when initial data is available (or immediately if loadsData is false) */
|
|
30
|
+
ready: Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Lazily compute bounding box of all features.
|
|
33
|
+
* Cached internally until source data reference changes.
|
|
34
|
+
*/
|
|
35
|
+
getBounds: () => GeoJsonBbox | null;
|
|
36
|
+
/** Summary counts -- always cheap (derived from .length). Semantics depend on layer type. */
|
|
37
|
+
counts: Readonly<Record<string, number>>;
|
|
38
|
+
/** Controls this layer wants to contribute to the map UI */
|
|
39
|
+
controls: ReadonlyArray<ControlConfig>;
|
|
40
|
+
/**
|
|
41
|
+
* Controls whether this layer's bounds are included in fit-to-content operations.
|
|
42
|
+
* Default: `"all"`.
|
|
43
|
+
*/
|
|
44
|
+
fitParticipation: FitParticipation;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Marker layer handle -- returned by `useMarkers`.
|
|
48
|
+
* Contains all data and configuration needed to render markers and clusters.
|
|
49
|
+
*/
|
|
50
|
+
export interface MarkerLayerHandle extends LayerMeta {
|
|
51
|
+
readonly layerType: "markers";
|
|
52
|
+
/** GeoJSON points for all positioned markers */
|
|
53
|
+
readonly features: GeoJsonFeatureCollection;
|
|
54
|
+
/** Marker render configuration */
|
|
55
|
+
readonly markerRender: RenderConfig<unknown>;
|
|
56
|
+
/** Cluster configuration, if clustering is enabled */
|
|
57
|
+
readonly clusterConfig: ClusterConfig<unknown> | null;
|
|
58
|
+
/** Cluster render configuration, if clustering is enabled */
|
|
59
|
+
readonly clusterRender: ClusterRenderConfig<unknown, unknown> | null;
|
|
60
|
+
/** Server-side cluster features (GeoJSON points), if using server-side clustering */
|
|
61
|
+
readonly clusterFeatures: GeoJsonFeatureCollection | null;
|
|
62
|
+
/**
|
|
63
|
+
* Per-cluster `memberItems` cross-referenced from the hook's `data` array via
|
|
64
|
+
* `getMarkerIds`. Keyed by cluster ID. `null` value when any member ID is
|
|
65
|
+
* missing from the data index. Null map when server clustering is not in
|
|
66
|
+
* use. Consumed by `<Layers>` to build `ClusterRenderState<TItem>` for DOM
|
|
67
|
+
* cluster portals.
|
|
68
|
+
*/
|
|
69
|
+
readonly clusterMemberItems: ReadonlyMap<string, ReadonlyArray<unknown> | null> | null;
|
|
70
|
+
/**
|
|
71
|
+
* Filled by `<Layers>` when `markerRender.mode === "adaptive"` — per-feature
|
|
72
|
+
* resolved display modes for the map adapter (plan §8).
|
|
73
|
+
*/
|
|
74
|
+
readonly adaptiveResolution?: AdaptiveMarkerResolution;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Shape layer handle -- returned by `useShapes`.
|
|
78
|
+
* Contains GeoJSON features (polygons, multipolygons, lines) with styling.
|
|
79
|
+
*/
|
|
80
|
+
export interface ShapeLayerHandle extends LayerMeta {
|
|
81
|
+
readonly layerType: "shapes";
|
|
82
|
+
/** GeoJSON features for all shapes */
|
|
83
|
+
readonly features: GeoJsonFeatureCollection;
|
|
84
|
+
/** Base shape style (static value). When per-feature styling is used, this is `{}`. */
|
|
85
|
+
readonly style: ShapeStyle;
|
|
86
|
+
/** Resolves shape style for static and viewport-aware rendering passes. */
|
|
87
|
+
readonly resolveStyle: (feature: GeoJsonFeature, ctx?: ShapeLabelResolutionContext) => ShapeStyle;
|
|
88
|
+
/** Per-feature resolved styles, keyed by `String(feature.id)`. */
|
|
89
|
+
readonly featureStyles?: ReadonlyMap<string, ShapeStyle>;
|
|
90
|
+
/** Which parts of the shape respond to interaction */
|
|
91
|
+
readonly interactive: ShapeInteractiveMode;
|
|
92
|
+
/**
|
|
93
|
+
* Returns all decorations for a feature, including auto-generated ones
|
|
94
|
+
* (e.g. multi-geometry "+" badges). Merging of custom and auto decorations
|
|
95
|
+
* is handled by `useShapes` at construction time.
|
|
96
|
+
*/
|
|
97
|
+
readonly getDecorations: (feature: GeoJsonFeature) => ReadonlyArray<ShapeDecoration>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Route layer handle -- returned by `useRoute`.
|
|
101
|
+
* Contains a polyline with waypoints and styling.
|
|
102
|
+
*/
|
|
103
|
+
export interface RouteLayerHandle extends LayerMeta {
|
|
104
|
+
readonly layerType: "route";
|
|
105
|
+
/** GeoJSON LineString feature for the route */
|
|
106
|
+
readonly features: GeoJsonFeatureCollection;
|
|
107
|
+
/** Waypoint positions (original input) */
|
|
108
|
+
readonly waypoints: ReadonlyArray<GeoJsonPosition>;
|
|
109
|
+
/** Route style */
|
|
110
|
+
readonly style: RouteStyle;
|
|
111
|
+
/** Whether route is interactive (clickable/hoverable). Default: false */
|
|
112
|
+
readonly interactive: boolean;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Image overlay handle -- returned by `useImageOverlay`.
|
|
116
|
+
* Contains a georeferenced image to display on the map.
|
|
117
|
+
*/
|
|
118
|
+
export interface ImageOverlayHandle extends LayerMeta {
|
|
119
|
+
readonly layerType: "image-overlay";
|
|
120
|
+
/** URL of the image to display */
|
|
121
|
+
readonly url: string;
|
|
122
|
+
/** Geographic bounds the image covers */
|
|
123
|
+
readonly imageBounds: GeoJsonBbox;
|
|
124
|
+
/** Opacity from 0 to 1 */
|
|
125
|
+
readonly opacity: number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Discriminated union of all layer handle types.
|
|
129
|
+
* Discriminated on `layerType`.
|
|
130
|
+
*/
|
|
131
|
+
export type LayerHandle = MarkerLayerHandle | ShapeLayerHandle | RouteLayerHandle | ImageOverlayHandle;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MapFocus } from "./mapFocus";
|
|
2
|
+
/**
|
|
3
|
+
* Stateful wrapper around `buildExpandedIds` that feeds the previous result
|
|
4
|
+
* back as `previousIds`, producing hysteresis: markers that were expanded
|
|
5
|
+
* stay expanded across incremental data updates (e.g. pan/zoom refetches).
|
|
6
|
+
*
|
|
7
|
+
* Memory rules:
|
|
8
|
+
* - A marker no longer in `items` is naturally forgotten (not a candidate).
|
|
9
|
+
* - When `focus.id` changes the memory is reset entirely.
|
|
10
|
+
*
|
|
11
|
+
* Callers replace a bare `useMemo(() => buildExpandedIds(...))` with this
|
|
12
|
+
* hook — the signature is identical except `previousIds` is managed
|
|
13
|
+
* internally.
|
|
14
|
+
*
|
|
15
|
+
* State is kept via the render-phase setState pattern (React docs §derived
|
|
16
|
+
* state): calling setState during render causes React to discard the current
|
|
17
|
+
* render and immediately re-render with the updated state, so callers never
|
|
18
|
+
* observe a stale value. This avoids reading/writing a ref during render.
|
|
19
|
+
*/
|
|
20
|
+
export declare const useExpandedIds: <TAsset>(items: ReadonlyArray<TAsset>, getId: (item: TAsset) => string, focus: MapFocus<TAsset>, limit: number, getPosition?: (item: TAsset) => readonly [number, number, ...Array<number>]) => ReadonlySet<string>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { MapApi } from "../core/types";
|
|
2
|
+
import type { LayerHandle } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Options for the `useFitToContent` hook.
|
|
5
|
+
*/
|
|
6
|
+
export type FitToContentOptions = Readonly<{
|
|
7
|
+
/** Skip the automatic initial fit entirely. Default: false */
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Padding in pixels around the fitted bounds. Default: 50 */
|
|
10
|
+
padding?: number;
|
|
11
|
+
/** Maximum zoom level after fitting. Default: 18 */
|
|
12
|
+
maxZoom?: number;
|
|
13
|
+
/** Timeout in ms before fitting with whatever data is available. Default: 10000 */
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Return type of the `useFitToContent` hook.
|
|
18
|
+
*/
|
|
19
|
+
export type FitToContentResult = Readonly<{
|
|
20
|
+
/** True once the initial fit has been performed (or skipped due to empty bounds). */
|
|
21
|
+
hasFitted: boolean;
|
|
22
|
+
/** True while waiting for participating layers to load before the initial fit. */
|
|
23
|
+
isWaiting: boolean;
|
|
24
|
+
/** Manually trigger a fit-to-content. Animates. Only includes `fitParticipation: "all"` layers. */
|
|
25
|
+
fitNow: () => void;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* `useFitToContent` -- orchestrates a one-time viewport fit once all
|
|
29
|
+
* participating layers have loaded their initial data.
|
|
30
|
+
*
|
|
31
|
+
* Only layers with `fitParticipation !== "none"` are waited on and included
|
|
32
|
+
* in the initial fit. `fitNow()` only includes `fitParticipation: "all"` layers.
|
|
33
|
+
*
|
|
34
|
+
* A configurable timeout (default 10s) ensures the map fits to whatever
|
|
35
|
+
* data is available if some layers are slow.
|
|
36
|
+
*
|
|
37
|
+
* When using this hook alongside an adapter's `initialViewport`, note that the
|
|
38
|
+
* initial viewport serves as a loading-state viewport and will be overridden
|
|
39
|
+
* once layer data arrives and the fit is performed.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const markers = useMarkers({ id: "fleet", ..., loading: isLoading });
|
|
44
|
+
* const shapes = useShapes({ id: "zones", ..., fitParticipation: "none" });
|
|
45
|
+
* const layers = useLayers(api, [markers, shapes]);
|
|
46
|
+
*
|
|
47
|
+
* const { hasFitted, isWaiting, fitNow } = useFitToContent(api, layers.handles);
|
|
48
|
+
*
|
|
49
|
+
* return (
|
|
50
|
+
* <Map>
|
|
51
|
+
* <Layers api={api} layers={layers} />
|
|
52
|
+
* {hasFitted ? <button onClick={fitNow}>Re-fit</button> : null}
|
|
53
|
+
* </Map>
|
|
54
|
+
* );
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare const useFitToContent: (api: MapApi, handles: ReadonlyArray<LayerHandle>, options?: FitToContentOptions) => FitToContentResult;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { GeoJsonBbox } from "@trackunit/geo-json-utils";
|
|
2
|
+
import { type Entity, type MapInteractionState } from "@trackunit/react-map-adapter-shared";
|
|
3
|
+
import type { ControlConfig } from "../controls/types";
|
|
4
|
+
import type { MapApi } from "../core/types";
|
|
5
|
+
import type { LayerHandle } from "./types";
|
|
6
|
+
/**
|
|
7
|
+
* Return type of `useLayers`.
|
|
8
|
+
*/
|
|
9
|
+
export type UseLayersReturn = Readonly<{
|
|
10
|
+
/** Shared interaction state across all layers */
|
|
11
|
+
interaction: MapInteractionState;
|
|
12
|
+
/** Select an entity (or null to clear selection). Works across all layer types. */
|
|
13
|
+
select: (entity: Entity | null) => void;
|
|
14
|
+
/** Hover an entity (or null to clear hover). Works across all layer types. */
|
|
15
|
+
hover: (entity: Entity | null) => void;
|
|
16
|
+
/** Clear all interaction state (selection and hover) */
|
|
17
|
+
clearAll: () => void;
|
|
18
|
+
/**
|
|
19
|
+
* Lazily compute combined bounds of all layers.
|
|
20
|
+
* Calls each layer's getBounds() and merges the results.
|
|
21
|
+
*/
|
|
22
|
+
getBounds: () => GeoJsonBbox | null;
|
|
23
|
+
/** True while any layer is loading */
|
|
24
|
+
loading: boolean;
|
|
25
|
+
/** Resolves when ALL layers have their initial data */
|
|
26
|
+
ready: Promise<void>;
|
|
27
|
+
/** Aggregated controls from all layers */
|
|
28
|
+
controls: ReadonlyArray<ControlConfig>;
|
|
29
|
+
/** Layer handles (passed to Layers component for rendering) */
|
|
30
|
+
handles: ReadonlyArray<LayerHandle>;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* `useLayers` -- aggregates layer handles and manages shared interaction state.
|
|
34
|
+
*
|
|
35
|
+
* This is the central hook that ties all layers together:
|
|
36
|
+
* - Shared hover/select interaction via a reducer (cross-layer, single entity at a time)
|
|
37
|
+
* - Combined bounds across all layers (lazy, cached)
|
|
38
|
+
* - Loading/ready coordination
|
|
39
|
+
* - Aggregated controls
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const fleet = useMarkers({ ... });
|
|
44
|
+
* const shapes = useShapes({ ... });
|
|
45
|
+
* const layers = useLayers(api, [fleet, shapes]);
|
|
46
|
+
*
|
|
47
|
+
* // layers.interaction.selectedEntity
|
|
48
|
+
* // layers.getBounds()
|
|
49
|
+
* // layers.ready
|
|
50
|
+
*
|
|
51
|
+
* return (
|
|
52
|
+
* <Map>
|
|
53
|
+
* <Layers api={api} layers={layers} />
|
|
54
|
+
* </Map>
|
|
55
|
+
* );
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare const useLayers: (api: MapApi, layers: ReadonlyArray<LayerHandle>) => UseLayersReturn;
|