@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,56 @@
|
|
|
1
|
+
import { type Entity, type MapInteractionState, type MapLayer, type ShapeEntity } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import { type RefObject } from "react";
|
|
3
|
+
import type { MapApi } from "../../core/types";
|
|
4
|
+
import type { LayerHandle, ShapeStyle } from "../types";
|
|
5
|
+
type UseShapeDecorationsConfig = Readonly<{
|
|
6
|
+
onDecorationsChange: (layers: ReadonlyArray<MapLayer>) => void;
|
|
7
|
+
onStyleOverridesChange: (overrides: ReadonlyMap<string, ReadonlyMap<string, ShapeStyle>>) => void;
|
|
8
|
+
api: MapApi | undefined;
|
|
9
|
+
handles: ReadonlyArray<LayerHandle>;
|
|
10
|
+
renderedSourceIds: RefObject<Set<string>>;
|
|
11
|
+
hasSourceReadiness: boolean;
|
|
12
|
+
readyRevision: number;
|
|
13
|
+
interaction: MapInteractionState;
|
|
14
|
+
select?: (entity: Entity | null) => void;
|
|
15
|
+
hover?: (entity: Entity | null) => void;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Interaction data for a single placed decoration. Layers matches portal
|
|
19
|
+
* descriptors to entries via source ID and wraps interactive ones with
|
|
20
|
+
* `<InteractiveDecoration>`.
|
|
21
|
+
*/
|
|
22
|
+
export type InteractionEntry = Readonly<{
|
|
23
|
+
entity: ShapeEntity;
|
|
24
|
+
onClick?: (entity: ShapeEntity) => void;
|
|
25
|
+
onHover?: (entity: ShapeEntity | null) => void;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* External store for the interaction map, consumed by Layers via
|
|
29
|
+
* `useSyncExternalStore` to avoid reading a ref during render.
|
|
30
|
+
*/
|
|
31
|
+
export type InteractionMapStore = Readonly<{
|
|
32
|
+
subscribe: (listener: () => void) => () => void;
|
|
33
|
+
getSnapshot: () => ReadonlyMap<string, InteractionEntry>;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Unified decoration hook — replaces both the old `useShapeDecorations` and
|
|
37
|
+
* `useEdgeLabels` with a single pass per feature.
|
|
38
|
+
*
|
|
39
|
+
* Processing per feature:
|
|
40
|
+
* 1. Collect decorations from `handle.getDecorations`
|
|
41
|
+
* 2. Partition: static anchors vs viewport-aware (`vertex-auto`, `edge-auto`)
|
|
42
|
+
* 3. Resolve static anchors, compute edge properties for `edge-*` anchors
|
|
43
|
+
* 4. `vertex-auto`: `findBestVisibleVertex`
|
|
44
|
+
* 5. `edge-auto`: build `ShapeLabelResolutionContext`, invoke optional
|
|
45
|
+
* `resolveLabel`, then `computeEdgeAutoPlacement` → placed / hidden
|
|
46
|
+
* 6. Create `kind:"anchor"` marker sources for all placed decorations
|
|
47
|
+
* 7. Build interaction map for Layers to wrap interactive portals
|
|
48
|
+
*
|
|
49
|
+
* Has no dependency on `api.annotations` — consumers that want chrome-bar
|
|
50
|
+
* fallback opt in explicitly via `useMapAnnotation`.
|
|
51
|
+
*
|
|
52
|
+
* @returns Interaction map keyed by source ID.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export declare const useShapeDecorations: ({ onDecorationsChange, onStyleOverridesChange, api, handles, renderedSourceIds, hasSourceReadiness, readyRevision, interaction, select, hover, }: UseShapeDecorationsConfig) => InteractionMapStore;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { edgeLabel } from "./decorationFactories";
|
|
2
|
+
import { createShapeLabelPolicy } from "./shapeLabelPolicy";
|
|
3
|
+
export type ShapeLabelHelpers = Readonly<{
|
|
4
|
+
edgeLabel: typeof edgeLabel;
|
|
5
|
+
createShapeLabelPolicy: typeof createShapeLabelPolicy;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Build-your-own hook: stable references to shape edge-label primitives.
|
|
9
|
+
*
|
|
10
|
+
* Returns `edgeLabel` and `createShapeLabelPolicy` for consumers building
|
|
11
|
+
* custom shape layers that need edge-label decorations and the annotation-opt-in
|
|
12
|
+
* coupling described in ADR-0017, without reimplementing the placement logic.
|
|
13
|
+
*
|
|
14
|
+
* Named `useShapeLabelHelpers` to distinguish it from the existing
|
|
15
|
+
* `useShapeDecorations` internal hook.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const { edgeLabel, createShapeLabelPolicy } = useShapeLabelHelpers();
|
|
20
|
+
* const policy = useMemo(() => createShapeLabelPolicy({
|
|
21
|
+
* annotateWhen: (_f, ctx) => ctx.isViewportInsideShape,
|
|
22
|
+
* resolve: (_f, ctx) => ctx.isSelected ? "force" : "auto",
|
|
23
|
+
* }), [createShapeLabelPolicy]);
|
|
24
|
+
*
|
|
25
|
+
* // Inside getDecorations:
|
|
26
|
+
* getDecorations: (feature) => [edgeLabel(feature.properties?.name)],
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const useShapeLabelHelpers: () => ShapeLabelHelpers;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type GeoJsonFeature, type GeoJsonFeatureCollection } from "@trackunit/geo-json-utils";
|
|
2
|
+
import type { ControlConfig } from "../../controls/types";
|
|
3
|
+
import type { FitParticipation, ShapeInteractiveMode, ShapeLayerHandle, ShapeStyle } from "../types";
|
|
4
|
+
import type { ShapeDecoration } from "./shapeDecorations";
|
|
5
|
+
import type { ShapeLabelResolutionContext } from "./shapeLabelResolution";
|
|
6
|
+
export type UseShapesOptions = Readonly<{
|
|
7
|
+
/** Unique ID for this layer */
|
|
8
|
+
id: string;
|
|
9
|
+
/** Human-readable name */
|
|
10
|
+
name: string;
|
|
11
|
+
/** GeoJSON FeatureCollection containing polygon, multipolygon, and/or line features */
|
|
12
|
+
features: GeoJsonFeatureCollection;
|
|
13
|
+
/** Resolves the style for each shape. Called without context for the static pre-pass. */
|
|
14
|
+
resolveStyle: (feature: GeoJsonFeature, ctx?: ShapeLabelResolutionContext) => ShapeStyle;
|
|
15
|
+
/** Which parts of the shape respond to interaction. Default: `"stroke"` */
|
|
16
|
+
interactive?: ShapeInteractiveMode;
|
|
17
|
+
/** Optional controls to contribute to the map UI */
|
|
18
|
+
controls?: ReadonlyArray<ControlConfig>;
|
|
19
|
+
/**
|
|
20
|
+
* Produce custom decorations for each feature. Called per-feature during the
|
|
21
|
+
* decoration rendering pass. These are merged with auto-generated decorations
|
|
22
|
+
* (e.g. multi-geometry "+" badges) unless `skipAutoDecorations` is true.
|
|
23
|
+
*/
|
|
24
|
+
getDecorations?: (feature: GeoJsonFeature) => ReadonlyArray<ShapeDecoration | undefined | null>;
|
|
25
|
+
/** When true, suppress all auto-generated decorations (e.g. multi-geometry "+" badges). Default: false */
|
|
26
|
+
skipAutoDecorations?: boolean;
|
|
27
|
+
/** Whether the layer is currently loading its initial data. Omit for static (non-loading) layers. */
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
/** Controls whether this layer's bounds are included in fit-to-content operations. Default: `"all"` */
|
|
30
|
+
fitParticipation?: FitParticipation;
|
|
31
|
+
}>;
|
|
32
|
+
export type UseShapesReturn = ShapeLayerHandle;
|
|
33
|
+
/**
|
|
34
|
+
* `useShapes` -- creates a shape layer handle from a GeoJSON FeatureCollection.
|
|
35
|
+
*
|
|
36
|
+
* Input is pure GeoJSON. The hook does not transform geometry -- it computes
|
|
37
|
+
* bounds and wraps the data in a layer handle.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* const zones = useShapes({
|
|
42
|
+
* id: "zones",
|
|
43
|
+
* name: "Zones",
|
|
44
|
+
* features: zoneFeatureCollection,
|
|
45
|
+
* resolveStyle: () => ({ fill: "rgba(0,0,255,0.1)", stroke: "blue", strokeWidth: 2 }),
|
|
46
|
+
* interactive: "stroke",
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare const useShapes: (options: UseShapesOptions) => UseShapesReturn;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type CommonProps, type Refable, type Styleable } from "@trackunit/react-components";
|
|
2
|
+
import { type HTMLAttributes, type ReactElement } from "react";
|
|
3
|
+
import type { MapTheme } from "../core/types";
|
|
4
|
+
import type { MarkerDomSize, MarkerState, StickPositioning } from "./model/markerDomTypes";
|
|
5
|
+
import { type MarkerColorConfig } from "./shared/markerColors";
|
|
6
|
+
type MapMarkerDivHandlers = Pick<HTMLAttributes<HTMLDivElement>, "onClick" | "onKeyDown" | "onMouseEnter" | "onMouseLeave" | "onFocus" | "onBlur">;
|
|
7
|
+
type MapMarkerBaseProps = CommonProps & Styleable & Refable<HTMLDivElement> & MapMarkerDivHandlers & {
|
|
8
|
+
/**
|
|
9
|
+
* Five-tier density ladder for the **default** (`state="default"`) **circle** only: diameter,
|
|
10
|
+
* optional icon slot, center dot, and in-circle direction arrow (`MARKER_SIZE_MAP`).
|
|
11
|
+
*
|
|
12
|
+
* In **pill** states (`hovered`, `expanded`, `selected`), disc, icon slot, label text size,
|
|
13
|
+
* pill-direction arrow, and pill padding are fixed to the **`md`** tier (`MARKER_PILL_CONTENT_LAYOUT_SIZE`)
|
|
14
|
+
* so the pill does not scale with this prop. Indicator and **stick** modes both use that split
|
|
15
|
+
* (collapsed circle vs expanded pill at the tip). **Stick anchor pin** (geo end) ignores `size`:
|
|
16
|
+
* fixed **`xs`** when the pin is visible, **`sm`** when `selected` (`upgradeMarkerDomSize` from that
|
|
17
|
+
* base). Same `cvaMarkerIndicator` + `MarkerDiscContent` stack as the map disc. The tip resting circle
|
|
18
|
+
* and pill still follow `size` + resolvers. The tip pill stays fixed **`md`**.
|
|
19
|
+
*/
|
|
20
|
+
size: MarkerDomSize;
|
|
21
|
+
/** Indicator fill (CSS color). */
|
|
22
|
+
color: string;
|
|
23
|
+
state?: MarkerState;
|
|
24
|
+
labelVisible?: boolean;
|
|
25
|
+
/** Heading in degrees; renders GpsArrow (plan §5). */
|
|
26
|
+
direction?: number;
|
|
27
|
+
theme: MapTheme;
|
|
28
|
+
colorConfig?: MarkerColorConfig;
|
|
29
|
+
icon?: ReactElement | null;
|
|
30
|
+
/** When set, drives `aria-expanded` for paired panels (plan §13). */
|
|
31
|
+
panelOpen?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* When provided, the marker renders as a **stick**: an attachment arm from the
|
|
34
|
+
* geographic anchor to the indicator/pill. When omitted, the marker renders as
|
|
35
|
+
* a regular indicator at the anchor (ADR-0010).
|
|
36
|
+
*/
|
|
37
|
+
stickPositioning?: StickPositioning;
|
|
38
|
+
};
|
|
39
|
+
export type MapMarkerProps = (MapMarkerBaseProps & {
|
|
40
|
+
label: string;
|
|
41
|
+
"aria-label"?: string;
|
|
42
|
+
}) | (MapMarkerBaseProps & {
|
|
43
|
+
label?: never;
|
|
44
|
+
"aria-label": string;
|
|
45
|
+
});
|
|
46
|
+
export declare const MapMarker: import("react").ForwardRefExoticComponent<(Omit<CommonProps & Styleable & Refable<HTMLDivElement> & MapMarkerDivHandlers & {
|
|
47
|
+
/**
|
|
48
|
+
* Five-tier density ladder for the **default** (`state="default"`) **circle** only: diameter,
|
|
49
|
+
* optional icon slot, center dot, and in-circle direction arrow (`MARKER_SIZE_MAP`).
|
|
50
|
+
*
|
|
51
|
+
* In **pill** states (`hovered`, `expanded`, `selected`), disc, icon slot, label text size,
|
|
52
|
+
* pill-direction arrow, and pill padding are fixed to the **`md`** tier (`MARKER_PILL_CONTENT_LAYOUT_SIZE`)
|
|
53
|
+
* so the pill does not scale with this prop. Indicator and **stick** modes both use that split
|
|
54
|
+
* (collapsed circle vs expanded pill at the tip). **Stick anchor pin** (geo end) ignores `size`:
|
|
55
|
+
* fixed **`xs`** when the pin is visible, **`sm`** when `selected` (`upgradeMarkerDomSize` from that
|
|
56
|
+
* base). Same `cvaMarkerIndicator` + `MarkerDiscContent` stack as the map disc. The tip resting circle
|
|
57
|
+
* and pill still follow `size` + resolvers. The tip pill stays fixed **`md`**.
|
|
58
|
+
*/
|
|
59
|
+
size: MarkerDomSize;
|
|
60
|
+
/** Indicator fill (CSS color). */
|
|
61
|
+
color: string;
|
|
62
|
+
state?: MarkerState;
|
|
63
|
+
labelVisible?: boolean;
|
|
64
|
+
/** Heading in degrees; renders GpsArrow (plan §5). */
|
|
65
|
+
direction?: number;
|
|
66
|
+
theme: MapTheme;
|
|
67
|
+
colorConfig?: MarkerColorConfig;
|
|
68
|
+
icon?: ReactElement | null;
|
|
69
|
+
/** When set, drives `aria-expanded` for paired panels (plan §13). */
|
|
70
|
+
panelOpen?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* When provided, the marker renders as a **stick**: an attachment arm from the
|
|
73
|
+
* geographic anchor to the indicator/pill. When omitted, the marker renders as
|
|
74
|
+
* a regular indicator at the anchor (ADR-0010).
|
|
75
|
+
*/
|
|
76
|
+
stickPositioning?: StickPositioning;
|
|
77
|
+
} & {
|
|
78
|
+
label: string;
|
|
79
|
+
"aria-label"?: string;
|
|
80
|
+
}, "ref"> | Omit<CommonProps & Styleable & Refable<HTMLDivElement> & MapMarkerDivHandlers & {
|
|
81
|
+
/**
|
|
82
|
+
* Five-tier density ladder for the **default** (`state="default"`) **circle** only: diameter,
|
|
83
|
+
* optional icon slot, center dot, and in-circle direction arrow (`MARKER_SIZE_MAP`).
|
|
84
|
+
*
|
|
85
|
+
* In **pill** states (`hovered`, `expanded`, `selected`), disc, icon slot, label text size,
|
|
86
|
+
* pill-direction arrow, and pill padding are fixed to the **`md`** tier (`MARKER_PILL_CONTENT_LAYOUT_SIZE`)
|
|
87
|
+
* so the pill does not scale with this prop. Indicator and **stick** modes both use that split
|
|
88
|
+
* (collapsed circle vs expanded pill at the tip). **Stick anchor pin** (geo end) ignores `size`:
|
|
89
|
+
* fixed **`xs`** when the pin is visible, **`sm`** when `selected` (`upgradeMarkerDomSize` from that
|
|
90
|
+
* base). Same `cvaMarkerIndicator` + `MarkerDiscContent` stack as the map disc. The tip resting circle
|
|
91
|
+
* and pill still follow `size` + resolvers. The tip pill stays fixed **`md`**.
|
|
92
|
+
*/
|
|
93
|
+
size: MarkerDomSize;
|
|
94
|
+
/** Indicator fill (CSS color). */
|
|
95
|
+
color: string;
|
|
96
|
+
state?: MarkerState;
|
|
97
|
+
labelVisible?: boolean;
|
|
98
|
+
/** Heading in degrees; renders GpsArrow (plan §5). */
|
|
99
|
+
direction?: number;
|
|
100
|
+
theme: MapTheme;
|
|
101
|
+
colorConfig?: MarkerColorConfig;
|
|
102
|
+
icon?: ReactElement | null;
|
|
103
|
+
/** When set, drives `aria-expanded` for paired panels (plan §13). */
|
|
104
|
+
panelOpen?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* When provided, the marker renders as a **stick**: an attachment arm from the
|
|
107
|
+
* geographic anchor to the indicator/pill. When omitted, the marker renders as
|
|
108
|
+
* a regular indicator at the anchor (ADR-0010).
|
|
109
|
+
*/
|
|
110
|
+
stickPositioning?: StickPositioning;
|
|
111
|
+
} & {
|
|
112
|
+
label?: never;
|
|
113
|
+
"aria-label": string;
|
|
114
|
+
}, "ref">) & import("react").RefAttributes<HTMLDivElement>>;
|
|
115
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Icon } from "@trackunit/react-components";
|
|
2
|
+
import { type ComponentProps, type ReactElement } from "react";
|
|
3
|
+
import type { MarkerDomSize } from "./model/markerDomTypes";
|
|
4
|
+
type MapMarkerIconProps = {
|
|
5
|
+
name: ComponentProps<typeof Icon>["name"];
|
|
6
|
+
size: MarkerDomSize;
|
|
7
|
+
/** Rendered as the native HTML title attribute. */
|
|
8
|
+
title?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Renders a correctly-scaled icon for use in the `MapMarker` `icon` prop.
|
|
12
|
+
*
|
|
13
|
+
* Internally always renders a `size="small"` (16×16 micro) icon, then applies a
|
|
14
|
+
* CSS scale transform so the visual size matches the icon slot from
|
|
15
|
+
* `MARKER_SIZE_MAP[size].icon`. Returns `null` for `xs` (no icon slot).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <MapMarker size="md" icon={<MapMarkerIcon name="Truck" size="md" />} />
|
|
19
|
+
*/
|
|
20
|
+
export declare function MapMarkerIcon({ name, size, title }: MapMarkerIconProps): ReactElement | null;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type StickFrameGeometry } from "./stickFrame";
|
|
2
|
+
export type StickFrameRefs = Readonly<{
|
|
3
|
+
surface: HTMLElement | null;
|
|
4
|
+
line: SVGLineElement | null;
|
|
5
|
+
/** Anchor pin: HTML wrapper (indicator disc stack), not an SVG circle. */
|
|
6
|
+
dot: HTMLDivElement | null;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* Single imperative DOM updater for the stick animation. Called from the rAF
|
|
10
|
+
* loop with the current spring progress and a layout snapshot. Keeps all
|
|
11
|
+
* mutation in one place so `MapMarkerStickSurface` can stay declarative.
|
|
12
|
+
*
|
|
13
|
+
* No-ops if a ref is not attached yet (initial render or after unmount).
|
|
14
|
+
*/
|
|
15
|
+
export declare const applyStickFrame: (refs: StickFrameRefs, geometry: StickFrameGeometry, progress: number) => void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure semi-implicit Euler spring stepper used as the single easing
|
|
3
|
+
* source-of-truth for marker motion (stick endpoint + circle↔pill morph).
|
|
4
|
+
*
|
|
5
|
+
* The spring drives a scalar value (typically a `0..1` progress) toward a
|
|
6
|
+
* target. Each `step` call advances the simulation by `dtMs` milliseconds and
|
|
7
|
+
* returns the next state — no React, no DOM, no time source. Callers own time
|
|
8
|
+
* and apply the result however they like (rAF loop, fake timers in tests).
|
|
9
|
+
*
|
|
10
|
+
* Defaults are tuned for a snappy, decelerating attack: fast initial movement
|
|
11
|
+
* with a slight overshoot, settling in ~180–220 ms. Pair with an ease-out
|
|
12
|
+
* curve on CSS-driven companions (see `MARKER_MORPH_EASING`) so the morph
|
|
13
|
+
* reads as one decelerating motion across stick + pill rather than the
|
|
14
|
+
* mushy symmetric ease-in-out it replaced.
|
|
15
|
+
*/
|
|
16
|
+
export type SpringConfig = Readonly<{
|
|
17
|
+
/** Spring constant (higher = snappier). */
|
|
18
|
+
stiffness: number;
|
|
19
|
+
/** Damping coefficient (higher = less overshoot). */
|
|
20
|
+
damping: number;
|
|
21
|
+
/** Effective mass (higher = more inertia). */
|
|
22
|
+
mass: number;
|
|
23
|
+
/** Velocity below which the spring is considered at rest. */
|
|
24
|
+
restVelocity: number;
|
|
25
|
+
/** Distance to target below which the spring is considered at rest. */
|
|
26
|
+
restDelta: number;
|
|
27
|
+
}>;
|
|
28
|
+
export type SpringState = Readonly<{
|
|
29
|
+
value: number;
|
|
30
|
+
velocity: number;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const MARKER_SPRING_CONFIG: SpringConfig;
|
|
33
|
+
export declare const createSpringState: (initialValue: number) => SpringState;
|
|
34
|
+
export declare const isSpringAtRest: (state: SpringState, target: number, config: SpringConfig) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Advances the spring by `dtMs`. Long frames are clamped and split into
|
|
37
|
+
* sub-steps to keep the integrator stable when the tab was throttled.
|
|
38
|
+
*/
|
|
39
|
+
export declare const stepSpring: (state: SpringState, target: number, dtMs: number, config: SpringConfig) => SpringState;
|
|
40
|
+
/**
|
|
41
|
+
* Estimated settle time, in milliseconds, used to derive an equivalent CSS
|
|
42
|
+
* transition duration for properties that cannot be ref-driven (e.g. label
|
|
43
|
+
* grid track). Computed by simulating the spring from 0 to 1.
|
|
44
|
+
*/
|
|
45
|
+
export declare const estimateSpringSettleMs: (config: SpringConfig) => number;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { PillCSSPlacement, TipPoint } from "../geometry/stickPositioning";
|
|
2
|
+
/**
|
|
3
|
+
* Pure geometry snapshot consumed by the per-frame interpolators. Any time
|
|
4
|
+
* the underlying layout changes (size tier, stick angle, label content), the
|
|
5
|
+
* caller computes a fresh `StickFrameGeometry` and the next animation frame
|
|
6
|
+
* naturally reflects it.
|
|
7
|
+
*/
|
|
8
|
+
export type StickFrameGeometry = Readonly<{
|
|
9
|
+
/** Tip position in anchor-relative coordinates (anchor = origin). */
|
|
10
|
+
tip: TipPoint;
|
|
11
|
+
/** CSS placement for the surface's expanded (pill-at-tip) state. */
|
|
12
|
+
pillCSS: PillCSSPlacement;
|
|
13
|
+
/**
|
|
14
|
+
* Half-extents of the SVG that draws the line/dot. The SVG sits centered on
|
|
15
|
+
* the anchor; its internal origin is `(svgHalfExtentX, svgHalfExtentY)`,
|
|
16
|
+
* which is also the anchor in the SVG's own coordinate space.
|
|
17
|
+
*/
|
|
18
|
+
svgHalfExtentX: number;
|
|
19
|
+
svgHalfExtentY: number;
|
|
20
|
+
/** Visible circle diameter in collapsed/circle form. */
|
|
21
|
+
circleDiameterPx: number;
|
|
22
|
+
/** Outer pill height — also the diameter of the cap circle. */
|
|
23
|
+
pillHeightPx: number;
|
|
24
|
+
/** Stick anchor dot radius in pixels. */
|
|
25
|
+
dotRadiusPx: number;
|
|
26
|
+
}>;
|
|
27
|
+
export type SurfacePlacement = Readonly<{
|
|
28
|
+
/** Constant pixel `left` for the surface (top-left of the pill envelope at t=1). */
|
|
29
|
+
leftPx: number;
|
|
30
|
+
/** Constant pixel `top` for the surface (top-left of the pill envelope at t=1). */
|
|
31
|
+
topPx: number;
|
|
32
|
+
/** Animated transform components, ready to write into `style.transform`. */
|
|
33
|
+
translateXPct: number;
|
|
34
|
+
translateXPx: number;
|
|
35
|
+
translateYPx: number;
|
|
36
|
+
}>;
|
|
37
|
+
export type LineEndpoint = Readonly<{
|
|
38
|
+
x2: number;
|
|
39
|
+
y2: number;
|
|
40
|
+
}>;
|
|
41
|
+
export type DotAppearance = Readonly<{
|
|
42
|
+
opacity: number;
|
|
43
|
+
scale: number;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* The constant pixel `left`/`top` for the surface — these never change during
|
|
47
|
+
* the animation. The outer container is anchor-zero (positioned exactly at
|
|
48
|
+
* the geographic anchor), so the surface's `left`/`top` are simply the
|
|
49
|
+
* pill's anchor-relative placement (`pillCSS`) with no extra offset adapter.
|
|
50
|
+
*/
|
|
51
|
+
export declare const computeSurfaceFixedOrigin: (geometry: StickFrameGeometry) => {
|
|
52
|
+
leftPx: number;
|
|
53
|
+
topPx: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Interpolates surface transform between the resting circle-at-anchor pose
|
|
57
|
+
* and the expanded pill-at-tip pose.
|
|
58
|
+
*
|
|
59
|
+
* - At `t = 0`: the visible circle sits centered on the anchor. Implemented
|
|
60
|
+
* by translating the surface's top-left back from its expanded `left`/`top`
|
|
61
|
+
* so the circle of diameter `circleDiameterPx` aligns with the anchor.
|
|
62
|
+
* - At `t = 1`: the surface is at its expanded pose with `pillCSS.translateXPct`
|
|
63
|
+
* placing the appropriate pill cap at the tip.
|
|
64
|
+
*/
|
|
65
|
+
export declare const interpolateSurfacePlacement: (t: number, geometry: StickFrameGeometry) => SurfacePlacement;
|
|
66
|
+
/**
|
|
67
|
+
* Format a transform string in the same three-function shape used previously.
|
|
68
|
+
* Centralized so the spring loop and React renders agree byte-for-byte.
|
|
69
|
+
*/
|
|
70
|
+
export declare const formatSurfaceTransform: (placement: SurfacePlacement) => string;
|
|
71
|
+
/**
|
|
72
|
+
* Interpolates the SVG line endpoint between the anchor (zero-length line) and
|
|
73
|
+
* the tip. Coordinates are in the SVG's own coordinate space — the SVG's
|
|
74
|
+
* internal origin `(svgHalfExtentX, svgHalfExtentY)` is the anchor, so the tip
|
|
75
|
+
* lands at `(svgHalfExtentX + tip.x, svgHalfExtentY + tip.y)` at `t=1`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const interpolateLineEndpoint: (t: number, geometry: StickFrameGeometry) => LineEndpoint;
|
|
78
|
+
/**
|
|
79
|
+
* Interpolates the anchor dot's opacity and scale. Linear blend keeps the dot
|
|
80
|
+
* visually tied to the same progress value as the surface and line.
|
|
81
|
+
*/
|
|
82
|
+
export declare const interpolateDot: (t: number) => DotAppearance;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decides whether the pill background should be painted on the marker surface.
|
|
3
|
+
*
|
|
4
|
+
* The pill background is opaque while the marker is in pill state and is held
|
|
5
|
+
* for the duration of the collapse morph after `isPill` flips back to `false`.
|
|
6
|
+
* Without this lingering paint, the background would drop to transparent the
|
|
7
|
+
* instant the state changes, leaving the stick line visible through the
|
|
8
|
+
* collapsing surface for the entire morph (asymmetric with expand, where the
|
|
9
|
+
* background is opaque from the first frame).
|
|
10
|
+
*
|
|
11
|
+
* `lingerMs` should match the padding/border CSS transition duration so the
|
|
12
|
+
* paint flips off exactly when the surface has finished collapsing back to a
|
|
13
|
+
* circle.
|
|
14
|
+
*/
|
|
15
|
+
export declare const usePillBackgroundPaint: (isPill: boolean, lingerMs: number) => boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
import type { StickFrameGeometry } from "./stickFrame";
|
|
3
|
+
import type { StickTweenHandle } from "./useStickTween";
|
|
4
|
+
export type StickAnimationRefs = Readonly<{
|
|
5
|
+
surfaceRef: RefObject<HTMLDivElement | null>;
|
|
6
|
+
lineRef: RefObject<SVGLineElement | null>;
|
|
7
|
+
dotRef: RefObject<HTMLDivElement | null>;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Per-frame applier. Subscribes refs to the tween and writes the current
|
|
11
|
+
* geometry-derived placement on every animation frame plus every layout
|
|
12
|
+
* commit. `geometry` is captured by ref so prop-driven layout changes (size
|
|
13
|
+
* tier, angle) take effect immediately.
|
|
14
|
+
*
|
|
15
|
+
* The container is anchor-zero (its width/height are 0 and its position is
|
|
16
|
+
* the geographic anchor itself), so the previous "shrink to circle while
|
|
17
|
+
* resting / expand to envelope while animating" container-size dance is no
|
|
18
|
+
* longer needed and `useStickFrameSync` is now the only animation hook the
|
|
19
|
+
* marker needs.
|
|
20
|
+
*/
|
|
21
|
+
export declare const useStickFrameSync: (tween: StickTweenHandle, geometry: StickFrameGeometry) => StickAnimationRefs;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscriber called every animation frame the spring is active. Receives the
|
|
3
|
+
* current progress value (`0..1`) so subscribers can update DOM imperatively
|
|
4
|
+
* without triggering React re-renders.
|
|
5
|
+
*/
|
|
6
|
+
export type StickTweenSubscriber = (progress: number) => void;
|
|
7
|
+
export type StickTweenHandle = Readonly<{
|
|
8
|
+
/** Latest spring value. Read this on attach to render the initial frame. */
|
|
9
|
+
progressRef: {
|
|
10
|
+
readonly current: number;
|
|
11
|
+
};
|
|
12
|
+
/** Register a subscriber. Returns an unsubscribe function. */
|
|
13
|
+
subscribe: (subscriber: StickTweenSubscriber) => () => void;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Drives a single spring toward `target` (`0` or `1`) and notifies subscribers
|
|
17
|
+
* each frame. Subscribers receive the progress value imperatively so the
|
|
18
|
+
* marker DOM (surface position, line endpoint, dot scale) can be updated
|
|
19
|
+
* without re-rendering the React tree on every frame.
|
|
20
|
+
*
|
|
21
|
+
* The hook owns one `requestAnimationFrame` loop per marker. The loop:
|
|
22
|
+
* - starts when `target` differs from the current value,
|
|
23
|
+
* - clamps long frames (handled inside `stepSpring`),
|
|
24
|
+
* - pauses when the document becomes hidden and resumes when visible,
|
|
25
|
+
* - snaps directly to the target when the user prefers reduced motion,
|
|
26
|
+
* - stops cleanly on unmount.
|
|
27
|
+
*/
|
|
28
|
+
export declare const useStickTween: (target: number) => StickTweenHandle;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { StickDistanceMode, StickPositioning } from "../model/markerDomTypes";
|
|
2
|
+
export type TipPoint = Readonly<{
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}>;
|
|
6
|
+
export type PillCSSPlacement = Readonly<{
|
|
7
|
+
pillLeft: number;
|
|
8
|
+
pillTop: number;
|
|
9
|
+
translateXPct: number;
|
|
10
|
+
}>;
|
|
11
|
+
export type StickSvgExtent = Readonly<{
|
|
12
|
+
halfWidth: number;
|
|
13
|
+
halfHeight: number;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Converts a user-facing distance to the internal stick-tip distance (where the
|
|
17
|
+
* SVG line physically ends). The three modes differ in what the user distance
|
|
18
|
+
* measures relative to the pill silhouette:
|
|
19
|
+
*
|
|
20
|
+
* - `"centerline"` — user distance IS the tip distance (line ends at cap center)
|
|
21
|
+
* - `"rounded-edge"` — user distance is to the tangent surface; add cap radius (`pillHeight/2`)
|
|
22
|
+
* - `"bbox-edge"` — user distance is to the nearest bbox face; add the angle-dependent half-extent.
|
|
23
|
+
* Requires `pillWidth` for correct rectangle geometry; falls back to a square assumption if omitted.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveStickInternalLength(userDist: number, mode: StickDistanceMode, angleRad: number, pillHeight: number, pillWidth?: number): number;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the stick tip position from a `StickPositioning` descriptor.
|
|
28
|
+
*
|
|
29
|
+
* The returned point is in anchor-relative coordinates (anchor = (0, 0)) and
|
|
30
|
+
* represents the physical end of the SVG stick line. A soft floor prevents the
|
|
31
|
+
* tip from being closer to the anchor than `dotRadius + pillHeight/2`, which
|
|
32
|
+
* would obscure the dot or overlap the pill over the anchor.
|
|
33
|
+
*
|
|
34
|
+
* @param pillWidth — pill outer width in px, used by `"bbox-edge"` mode for accurate
|
|
35
|
+
* rectangular bounding box geometry. Omit (or pass undefined) to fall back to a square
|
|
36
|
+
* approximation (safe for circle-mode markers where width ≈ height).
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveTentativeTip(positioning: StickPositioning, pillHeight: number, dotRadius: number, pillWidth?: number): TipPoint;
|
|
39
|
+
/**
|
|
40
|
+
* Computes the CSS `left`, `top`, and `translateX(%)` needed to align the pill
|
|
41
|
+
* so the correct rounded cap is tangent to the stick tip.
|
|
42
|
+
*
|
|
43
|
+
* Uses a smooth blend parameterized by `t ∈ [0, 1]`:
|
|
44
|
+
* - `t = 1` (tipX ≥ +pillHeight/2): left cap at tipX, pill extends right
|
|
45
|
+
* - `t = 0.5` (tipX = 0): pill centered on tipX
|
|
46
|
+
* - `t = 0` (tipX ≤ -pillHeight/2): right cap at tipX, pill extends left
|
|
47
|
+
*
|
|
48
|
+
* The blend zone radius is `r = pillHeight/2` (the cap radius). This ensures
|
|
49
|
+
* the facing cap center sits at (tipX, tipY) for any `|tipX| ≥ r`, which keeps
|
|
50
|
+
* the visible stick length angle-independent for `rounded-edge` distance mode.
|
|
51
|
+
* A wider blend zone (e.g. `pillHeight`) displaces the cap from tipX by an
|
|
52
|
+
* amount proportional to pill width, causing the stick to appear shorter at
|
|
53
|
+
* oblique angles.
|
|
54
|
+
*
|
|
55
|
+
* `translateX(-N%)` is relative to the element's own width, so no JS pill-width
|
|
56
|
+
* measurement is needed.
|
|
57
|
+
*/
|
|
58
|
+
export declare function computePillCSSPlacement(tipX: number, tipY: number, pillHeight: number): PillCSSPlacement;
|
|
59
|
+
/**
|
|
60
|
+
* Computes the half-extents of the SVG element that draws the stick line, dot,
|
|
61
|
+
* and (visually) covers the worst-case pill envelope around the anchor.
|
|
62
|
+
*
|
|
63
|
+
* The container itself is anchor-zero (zero-size, positioned exactly at the
|
|
64
|
+
* geographic anchor); the SVG sits centered on that anchor with its internal
|
|
65
|
+
* origin at `(halfWidth, halfHeight)`. The returned extents only need to be
|
|
66
|
+
* recomputed when layout-affecting inputs change (size tier, tip angle, label
|
|
67
|
+
* budget) — never per animation frame.
|
|
68
|
+
*/
|
|
69
|
+
export declare function computeStickSvgExtent(tip: TipPoint, pillHeight: number, dotRadius: number, maxPillWidth: number): StickSvgExtent;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MarkerDomSize } from "../model/markerDomTypes";
|
|
2
|
+
export type DirectionIndicatorDisplay = Readonly<{
|
|
3
|
+
show: boolean;
|
|
4
|
+
pixelSize: number;
|
|
5
|
+
rotationDegrees: number;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function resolveDirectionIndicatorDisplay(direction: number | undefined, effectiveSize: MarkerDomSize): DirectionIndicatorDisplay;
|
|
8
|
+
export declare function useDirectionIndicator(direction: number | undefined, effectiveSize: MarkerDomSize): DirectionIndicatorDisplay;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type CSSProperties, type HTMLAttributes, type ReactElement } from "react";
|
|
2
|
+
import type { MapTheme } from "../../core/types";
|
|
3
|
+
import { type DirectionIndicatorDisplay } from "../geometry/useDirectionIndicator";
|
|
4
|
+
import type { MarkerDomSize, MarkerState, StickPositioning } from "../model/markerDomTypes";
|
|
5
|
+
import type { ResolvedMarkerColors } from "../shared/markerColors";
|
|
6
|
+
type MapMarkerStickSurfaceHandlers = Pick<HTMLAttributes<HTMLDivElement>, "onBlur" | "onClick" | "onFocus" | "onKeyDown" | "onMouseEnter" | "onMouseLeave">;
|
|
7
|
+
export type MapMarkerStickSurfaceProps = MapMarkerStickSurfaceHandlers & {
|
|
8
|
+
readonly accessibleName: string;
|
|
9
|
+
readonly circleLayoutSize: MarkerDomSize;
|
|
10
|
+
readonly className: string | undefined;
|
|
11
|
+
readonly colors: ResolvedMarkerColors;
|
|
12
|
+
readonly contentLayoutSize: MarkerDomSize;
|
|
13
|
+
readonly dataTestId: string | undefined;
|
|
14
|
+
readonly direction: number | undefined;
|
|
15
|
+
readonly directionDisplay: DirectionIndicatorDisplay;
|
|
16
|
+
readonly icon: ReactElement | null;
|
|
17
|
+
readonly iconPx: number;
|
|
18
|
+
readonly labelText: string | undefined;
|
|
19
|
+
readonly labelVisible: boolean | undefined;
|
|
20
|
+
readonly panelOpen: boolean | undefined;
|
|
21
|
+
readonly state: MarkerState;
|
|
22
|
+
readonly stickPositioning: StickPositioning | undefined;
|
|
23
|
+
readonly style: CSSProperties | undefined;
|
|
24
|
+
readonly textPx: number;
|
|
25
|
+
readonly theme: MapTheme;
|
|
26
|
+
};
|
|
27
|
+
export declare const MapMarkerStickSurface: import("react").ForwardRefExoticComponent<MapMarkerStickSurfaceHandlers & {
|
|
28
|
+
readonly accessibleName: string;
|
|
29
|
+
readonly circleLayoutSize: MarkerDomSize;
|
|
30
|
+
readonly className: string | undefined;
|
|
31
|
+
readonly colors: ResolvedMarkerColors;
|
|
32
|
+
readonly contentLayoutSize: MarkerDomSize;
|
|
33
|
+
readonly dataTestId: string | undefined;
|
|
34
|
+
readonly direction: number | undefined;
|
|
35
|
+
readonly directionDisplay: DirectionIndicatorDisplay;
|
|
36
|
+
readonly icon: ReactElement | null;
|
|
37
|
+
readonly iconPx: number;
|
|
38
|
+
readonly labelText: string | undefined;
|
|
39
|
+
readonly labelVisible: boolean | undefined;
|
|
40
|
+
readonly panelOpen: boolean | undefined;
|
|
41
|
+
readonly state: MarkerState;
|
|
42
|
+
readonly stickPositioning: StickPositioning | undefined;
|
|
43
|
+
readonly style: CSSProperties | undefined;
|
|
44
|
+
readonly textPx: number;
|
|
45
|
+
readonly theme: MapTheme;
|
|
46
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DomPortalStackingInput, DomPortalStackingResult } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import type { MarkerState } from "./markerDomTypes";
|
|
3
|
+
type MapMarkerDomPortalStackingBase = DomPortalStackingInput & {
|
|
4
|
+
/** `true` when `<MapMarker stickPositioning={...} />` is used. */
|
|
5
|
+
hasStick: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type MapMarkerDomPortalStackingByPromotion = MapMarkerDomPortalStackingBase & {
|
|
8
|
+
expandedRank: number | null;
|
|
9
|
+
};
|
|
10
|
+
export type MapMarkerDomPortalStackingByMarkerState = MapMarkerDomPortalStackingBase & {
|
|
11
|
+
markerState: MarkerState;
|
|
12
|
+
};
|
|
13
|
+
export type MapMarkerDomPortalStackingParams = MapMarkerDomPortalStackingByPromotion | MapMarkerDomPortalStackingByMarkerState;
|
|
14
|
+
/**
|
|
15
|
+
* Maps `<MapMarker>` presentation + state to `DomPortalStackingResult` for use with
|
|
16
|
+
* `markerRender.resolveDomPortalStacking` in `<Layers>`.
|
|
17
|
+
*
|
|
18
|
+
* Pass either `expandedRank` (for `resolveMarkerState`) or a pre-resolved `markerState`
|
|
19
|
+
* when the expanded state is data-driven and does not use `expandedRank` (for example, story helpers).
|
|
20
|
+
*/
|
|
21
|
+
export declare const resolveMapMarkerDomPortalStacking: (params: MapMarkerDomPortalStackingParams) => DomPortalStackingResult;
|
|
22
|
+
export {};
|