@element-editor/adapter-leaflet 0.0.0-stage → 0.2.1
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/AGENTS.md +80 -0
- package/LICENSE +21 -0
- package/README.md +45 -2
- package/dist/ElementEditorControls.d.cts +111 -0
- package/dist/ElementEditorControls.d.ts +111 -0
- package/dist/adapter-leaflet.css +1 -0
- package/dist/components/BoundaryLayer.d.cts +5 -0
- package/dist/components/BoundaryLayer.d.ts +5 -0
- package/dist/components/DrawingLayer.d.cts +3 -0
- package/dist/components/DrawingLayer.d.ts +3 -0
- package/dist/components/DrawingPreviewLayer.d.cts +1 -0
- package/dist/components/DrawingPreviewLayer.d.ts +1 -0
- package/dist/components/FeatureInteractionLayer.d.cts +6 -0
- package/dist/components/FeatureInteractionLayer.d.ts +6 -0
- package/dist/components/GeometryProcessingPreview.d.cts +1 -0
- package/dist/components/GeometryProcessingPreview.d.ts +1 -0
- package/dist/components/MeasurePreviewLayer.d.cts +1 -0
- package/dist/components/MeasurePreviewLayer.d.ts +1 -0
- package/dist/components/MeasurementTooltip.d.cts +1 -0
- package/dist/components/MeasurementTooltip.d.ts +1 -0
- package/dist/components/PendingPointMarker.d.cts +2 -0
- package/dist/components/PendingPointMarker.d.ts +2 -0
- package/dist/components/RoutePreviewLayer.d.cts +1 -0
- package/dist/components/RoutePreviewLayer.d.ts +1 -0
- package/dist/components/SnapController.d.cts +1 -0
- package/dist/components/SnapController.d.ts +1 -0
- package/dist/components/TextLabelLayer.d.cts +10 -0
- package/dist/components/TextLabelLayer.d.ts +10 -0
- package/dist/components/TransformControls.d.cts +1 -0
- package/dist/components/TransformControls.d.ts +1 -0
- package/dist/components/featureLayers/FeatureElements.d.cts +28 -0
- package/dist/components/featureLayers/FeatureElements.d.ts +28 -0
- package/dist/components/featureLayers/GeometryCollectionLayer.d.cts +13 -0
- package/dist/components/featureLayers/GeometryCollectionLayer.d.ts +13 -0
- package/dist/components/featureLayers/LineLayer.d.cts +14 -0
- package/dist/components/featureLayers/LineLayer.d.ts +14 -0
- package/dist/components/featureLayers/MultiLineLayer.d.cts +13 -0
- package/dist/components/featureLayers/MultiLineLayer.d.ts +13 -0
- package/dist/components/featureLayers/MultiPointLayer.d.cts +12 -0
- package/dist/components/featureLayers/MultiPointLayer.d.ts +12 -0
- package/dist/components/featureLayers/MultiPolygonLayer.d.cts +13 -0
- package/dist/components/featureLayers/MultiPolygonLayer.d.ts +13 -0
- package/dist/components/featureLayers/POIMarker.d.cts +13 -0
- package/dist/components/featureLayers/POIMarker.d.ts +13 -0
- package/dist/components/featureLayers/PolygonLayer.d.cts +14 -0
- package/dist/components/featureLayers/PolygonLayer.d.ts +14 -0
- package/dist/components/featureLayers/mountQueue.d.cts +41 -0
- package/dist/components/featureLayers/mountQueue.d.ts +41 -0
- package/dist/components/featureLayers/renderer.d.cts +25 -0
- package/dist/components/featureLayers/renderer.d.ts +25 -0
- package/dist/components/featureLayers/shared.d.cts +44 -0
- package/dist/components/featureLayers/shared.d.ts +44 -0
- package/dist/components/featureLayers/useProgressiveMount.d.cts +30 -0
- package/dist/components/featureLayers/useProgressiveMount.d.ts +30 -0
- package/dist/components/previews/CirclePreview.d.cts +1 -0
- package/dist/components/previews/CirclePreview.d.ts +1 -0
- package/dist/components/previews/LinePreview.d.cts +1 -0
- package/dist/components/previews/LinePreview.d.ts +1 -0
- package/dist/components/previews/PolygonPreview.d.cts +1 -0
- package/dist/components/previews/PolygonPreview.d.ts +1 -0
- package/dist/components/previews/RectPreview.d.cts +1 -0
- package/dist/components/previews/RectPreview.d.ts +1 -0
- package/dist/components/tools/BoxSelectHandler.d.cts +16 -0
- package/dist/components/tools/BoxSelectHandler.d.ts +16 -0
- package/dist/components/tools/MapClickHandler.d.cts +6 -0
- package/dist/components/tools/MapClickHandler.d.ts +6 -0
- package/dist/components/tools/MultiDragHandler.d.cts +9 -0
- package/dist/components/tools/MultiDragHandler.d.ts +9 -0
- package/dist/components/tools/OsmTraceHandler.d.cts +1 -0
- package/dist/components/tools/OsmTraceHandler.d.ts +1 -0
- package/dist/components/tools/VertexEditor.d.cts +13 -0
- package/dist/components/tools/VertexEditor.d.ts +13 -0
- package/dist/contexts/FeatureStyleContext.d.cts +15 -0
- package/dist/contexts/FeatureStyleContext.d.ts +15 -0
- package/dist/contexts/MapControllerContext.d.cts +2 -0
- package/dist/contexts/MapControllerContext.d.ts +2 -0
- package/dist/hooks/lastPointer.d.cts +13 -0
- package/dist/hooks/lastPointer.d.ts +13 -0
- package/dist/hooks/useCursorWhileMode.d.cts +23 -0
- package/dist/hooks/useCursorWhileMode.d.ts +23 -0
- package/dist/hooks/useDuplicateDrag.d.cts +9 -0
- package/dist/hooks/useDuplicateDrag.d.ts +9 -0
- package/dist/hooks/useFeatureDrag.d.cts +20 -0
- package/dist/hooks/useFeatureDrag.d.ts +20 -0
- package/dist/hooks/useMapInteractionMachine.d.cts +13 -0
- package/dist/hooks/useMapInteractionMachine.d.ts +13 -0
- package/dist/hooks/useModifierDragCursor.d.cts +9 -0
- package/dist/hooks/useModifierDragCursor.d.ts +9 -0
- package/dist/hooks/usePartDrag.d.cts +23 -0
- package/dist/hooks/usePartDrag.d.ts +23 -0
- package/dist/index.cjs +52 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.es.js +4246 -0
- package/dist/store/StoreContext.d.cts +1 -0
- package/dist/store/StoreContext.d.ts +1 -0
- package/dist/utils/boxSelect.d.cts +4 -0
- package/dist/utils/boxSelect.d.ts +4 -0
- package/dist/utils/dragUtils.d.cts +12 -0
- package/dist/utils/dragUtils.d.ts +12 -0
- package/dist/utils/featureTooltip.d.cts +19 -0
- package/dist/utils/featureTooltip.d.ts +19 -0
- package/dist/utils/freehandCommitFlag.d.cts +2 -0
- package/dist/utils/freehandCommitFlag.d.ts +2 -0
- package/dist/utils/freshFeature.d.cts +17 -0
- package/dist/utils/freshFeature.d.ts +17 -0
- package/dist/utils/geojson.d.cts +3 -0
- package/dist/utils/geojson.d.ts +3 -0
- package/dist/utils/measureClickFlag.d.cts +2 -0
- package/dist/utils/measureClickFlag.d.ts +2 -0
- package/dist/utils/modifierDragFlag.d.cts +2 -0
- package/dist/utils/modifierDragFlag.d.ts +2 -0
- package/dist/utils/pinPath.d.cts +2 -0
- package/dist/utils/pinPath.d.ts +2 -0
- package/dist/utils/rafThrottle.d.cts +11 -0
- package/dist/utils/rafThrottle.d.ts +11 -0
- package/dist/utils/snapping.d.cts +25 -0
- package/dist/utils/snapping.d.ts +25 -0
- package/package.json +98 -3
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { MapInteractionMachine } from '../../hooks/useMapInteractionMachine.js';
|
|
3
|
+
import { OpenFeatureCtxMenu } from './shared.js';
|
|
4
|
+
export declare const MultiPolygonLayer: import('react').NamedExoticComponent<{
|
|
5
|
+
feature: GeoJSONFeature;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
isMultiSelected: boolean;
|
|
8
|
+
multiDragDelta: [number, number] | null;
|
|
9
|
+
onVertexEdit: () => void;
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
interaction: MapInteractionMachine;
|
|
12
|
+
onContextMenu?: OpenFeatureCtxMenu;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { MapInteractionMachine } from '../../hooks/useMapInteractionMachine.cjs';
|
|
3
|
+
import { OpenFeatureCtxMenu } from './shared.cjs';
|
|
4
|
+
export declare const POIMarker: import('react').NamedExoticComponent<{
|
|
5
|
+
feature: GeoJSONFeature;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
isMultiSelected: boolean;
|
|
8
|
+
multiDragDelta: [number, number] | null;
|
|
9
|
+
multiDuplicateDrag: boolean;
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
interaction: MapInteractionMachine;
|
|
12
|
+
onContextMenu?: OpenFeatureCtxMenu;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { MapInteractionMachine } from '../../hooks/useMapInteractionMachine.js';
|
|
3
|
+
import { OpenFeatureCtxMenu } from './shared.js';
|
|
4
|
+
export declare const POIMarker: import('react').NamedExoticComponent<{
|
|
5
|
+
feature: GeoJSONFeature;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
isMultiSelected: boolean;
|
|
8
|
+
multiDragDelta: [number, number] | null;
|
|
9
|
+
multiDuplicateDrag: boolean;
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
interaction: MapInteractionMachine;
|
|
12
|
+
onContextMenu?: OpenFeatureCtxMenu;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { MapInteractionMachine } from '../../hooks/useMapInteractionMachine.cjs';
|
|
3
|
+
import { OpenFeatureCtxMenu } from './shared.cjs';
|
|
4
|
+
export declare const PolygonLayer: import('react').NamedExoticComponent<{
|
|
5
|
+
feature: GeoJSONFeature;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
isMultiSelected: boolean;
|
|
8
|
+
multiDragDelta: [number, number] | null;
|
|
9
|
+
multiDuplicateDrag: boolean;
|
|
10
|
+
onVertexEdit: () => void;
|
|
11
|
+
readonly: boolean;
|
|
12
|
+
interaction: MapInteractionMachine;
|
|
13
|
+
onContextMenu?: OpenFeatureCtxMenu;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { MapInteractionMachine } from '../../hooks/useMapInteractionMachine.js';
|
|
3
|
+
import { OpenFeatureCtxMenu } from './shared.js';
|
|
4
|
+
export declare const PolygonLayer: import('react').NamedExoticComponent<{
|
|
5
|
+
feature: GeoJSONFeature;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
isMultiSelected: boolean;
|
|
8
|
+
multiDragDelta: [number, number] | null;
|
|
9
|
+
multiDuplicateDrag: boolean;
|
|
10
|
+
onVertexEdit: () => void;
|
|
11
|
+
readonly: boolean;
|
|
12
|
+
interaction: MapInteractionMachine;
|
|
13
|
+
onContextMenu?: OpenFeatureCtxMenu;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
export type ViewBounds = {
|
|
3
|
+
w: number;
|
|
4
|
+
s: number;
|
|
5
|
+
e: number;
|
|
6
|
+
n: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* What's left to mount progressively, in mount order. Built once per data /
|
|
10
|
+
* selection change (O(new features)); each mount tick then takes the next
|
|
11
|
+
* chunk from `pos` (O(chunk)).
|
|
12
|
+
*/
|
|
13
|
+
export type MountQueue = {
|
|
14
|
+
items: GeoJSONFeature[];
|
|
15
|
+
pos: number;
|
|
16
|
+
bounds: ViewBounds | null;
|
|
17
|
+
};
|
|
18
|
+
export declare const EMPTY_QUEUE: MountQueue;
|
|
19
|
+
/** Mount order: features in the view first, each part keeping its given order. */
|
|
20
|
+
export declare function viewportFirst(features: GeoJSONFeature[], bounds: ViewBounds | null): GeoJSONFeature[];
|
|
21
|
+
/**
|
|
22
|
+
* Full pass: the features that need mounting (`fresh`, in render order) against the
|
|
23
|
+
* previous queue. Returns the first chunk to mount now and the queue of the rest.
|
|
24
|
+
* A big new batch (more features the previous queue didn't hold than one chunk,
|
|
25
|
+
* e.g. an import landing, even while an earlier batch is still mounting) mounts
|
|
26
|
+
* nothing in the render that brought it: that render is often the import's own
|
|
27
|
+
* long task. The first chunk then waits for the next tick.
|
|
28
|
+
*/
|
|
29
|
+
export declare function planMount(fresh: GeoJSONFeature[], prev: MountQueue, chunk: number, bounds: ViewBounds | null): {
|
|
30
|
+
now: GeoJSONFeature[];
|
|
31
|
+
queue: MountQueue;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Mount tick: the next `size` features of the queue, skipping any already mounted
|
|
35
|
+
* (e.g. selected meanwhile). A pan since the queue was built re-sorts what's left
|
|
36
|
+
* so the new view mounts first.
|
|
37
|
+
*/
|
|
38
|
+
export declare function nextChunk(q: MountQueue, size: number, bounds: ViewBounds | null, mounted: (id: string) => boolean): {
|
|
39
|
+
chunk: GeoJSONFeature[];
|
|
40
|
+
queue: MountQueue;
|
|
41
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
export type ViewBounds = {
|
|
3
|
+
w: number;
|
|
4
|
+
s: number;
|
|
5
|
+
e: number;
|
|
6
|
+
n: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* What's left to mount progressively, in mount order. Built once per data /
|
|
10
|
+
* selection change (O(new features)); each mount tick then takes the next
|
|
11
|
+
* chunk from `pos` (O(chunk)).
|
|
12
|
+
*/
|
|
13
|
+
export type MountQueue = {
|
|
14
|
+
items: GeoJSONFeature[];
|
|
15
|
+
pos: number;
|
|
16
|
+
bounds: ViewBounds | null;
|
|
17
|
+
};
|
|
18
|
+
export declare const EMPTY_QUEUE: MountQueue;
|
|
19
|
+
/** Mount order: features in the view first, each part keeping its given order. */
|
|
20
|
+
export declare function viewportFirst(features: GeoJSONFeature[], bounds: ViewBounds | null): GeoJSONFeature[];
|
|
21
|
+
/**
|
|
22
|
+
* Full pass: the features that need mounting (`fresh`, in render order) against the
|
|
23
|
+
* previous queue. Returns the first chunk to mount now and the queue of the rest.
|
|
24
|
+
* A big new batch (more features the previous queue didn't hold than one chunk,
|
|
25
|
+
* e.g. an import landing, even while an earlier batch is still mounting) mounts
|
|
26
|
+
* nothing in the render that brought it: that render is often the import's own
|
|
27
|
+
* long task. The first chunk then waits for the next tick.
|
|
28
|
+
*/
|
|
29
|
+
export declare function planMount(fresh: GeoJSONFeature[], prev: MountQueue, chunk: number, bounds: ViewBounds | null): {
|
|
30
|
+
now: GeoJSONFeature[];
|
|
31
|
+
queue: MountQueue;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Mount tick: the next `size` features of the queue, skipping any already mounted
|
|
35
|
+
* (e.g. selected meanwhile). A pan since the queue was built re-sorts what's left
|
|
36
|
+
* so the new view mounts first.
|
|
37
|
+
*/
|
|
38
|
+
export declare function nextChunk(q: MountQueue, size: number, bounds: ViewBounds | null, mounted: (id: string) => boolean): {
|
|
39
|
+
chunk: GeoJSONFeature[];
|
|
40
|
+
queue: MountQueue;
|
|
41
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type FeatureRenderer = 'canvas' | 'svg';
|
|
2
|
+
/** Width of the invisible SVG hit path under a line: 8 px of slack each side of the cursor. */
|
|
3
|
+
export declare const LINE_HIT_WEIGHT = 16;
|
|
4
|
+
/** Casing under unselected lines: `width` px each side of the stroke, white at `opacity` (× the line's); none while the canvas holds more than `maxPaths` paths. */
|
|
5
|
+
export declare const LINE_CASING: {
|
|
6
|
+
color: string;
|
|
7
|
+
width: number;
|
|
8
|
+
opacity: number;
|
|
9
|
+
maxPaths: number;
|
|
10
|
+
};
|
|
11
|
+
/** The renderer for unselected paths; undefined = the map's default (SVG). */
|
|
12
|
+
export declare const IdleRendererContext: import('react').Context<import('leaflet').Renderer | undefined>;
|
|
13
|
+
export declare function useIdleRenderer(map: L.Map, mode: FeatureRenderer): L.Renderer | undefined;
|
|
14
|
+
/** DOM element to style for a path's cursor: its SVG element, or the shared canvas. */
|
|
15
|
+
export declare function pathElement(layer: L.Path): Element | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Puts a feature's layers on the idle canvas while unselected and on SVG (on top)
|
|
18
|
+
* while selected. Returns the renderer to mount with. On a flip it swaps the
|
|
19
|
+
* existing layers in place; `onSwap` lets the caller fix per-renderer options
|
|
20
|
+
* (set before the re-add) and re-target hover state afterwards.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useFeatureRenderer(selected: boolean, getLayers: () => Iterable<L.Path | null | undefined>, onSwap?: {
|
|
23
|
+
before?: (l: L.Path, onCanvas: boolean) => void;
|
|
24
|
+
after?: () => void;
|
|
25
|
+
}): L.Renderer | undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type FeatureRenderer = 'canvas' | 'svg';
|
|
2
|
+
/** Width of the invisible SVG hit path under a line: 8 px of slack each side of the cursor. */
|
|
3
|
+
export declare const LINE_HIT_WEIGHT = 16;
|
|
4
|
+
/** Casing under unselected lines: `width` px each side of the stroke, white at `opacity` (× the line's); none while the canvas holds more than `maxPaths` paths. */
|
|
5
|
+
export declare const LINE_CASING: {
|
|
6
|
+
color: string;
|
|
7
|
+
width: number;
|
|
8
|
+
opacity: number;
|
|
9
|
+
maxPaths: number;
|
|
10
|
+
};
|
|
11
|
+
/** The renderer for unselected paths; undefined = the map's default (SVG). */
|
|
12
|
+
export declare const IdleRendererContext: import('react').Context<import('leaflet').Renderer | undefined>;
|
|
13
|
+
export declare function useIdleRenderer(map: L.Map, mode: FeatureRenderer): L.Renderer | undefined;
|
|
14
|
+
/** DOM element to style for a path's cursor: its SVG element, or the shared canvas. */
|
|
15
|
+
export declare function pathElement(layer: L.Path): Element | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Puts a feature's layers on the idle canvas while unselected and on SVG (on top)
|
|
18
|
+
* while selected. Returns the renderer to mount with. On a flip it swaps the
|
|
19
|
+
* existing layers in place; `onSwap` lets the caller fix per-renderer options
|
|
20
|
+
* (set before the re-add) and re-target hover state afterwards.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useFeatureRenderer(selected: boolean, getLayers: () => Iterable<L.Path | null | undefined>, onSwap?: {
|
|
23
|
+
before?: (l: L.Path, onCanvas: boolean) => void;
|
|
24
|
+
after?: () => void;
|
|
25
|
+
}): L.Renderer | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { LeafletMouseEvent, PathOptions } from 'leaflet';
|
|
3
|
+
import { FeatureStyleFn } from '../../contexts/FeatureStyleContext.cjs';
|
|
4
|
+
export declare const IDLE_LINE: PathOptions;
|
|
5
|
+
export declare const IDLE_POLY: PathOptions;
|
|
6
|
+
export declare const HOVER_POLY: PathOptions;
|
|
7
|
+
/**
|
|
8
|
+
* The integrator's `getFeatureStyle` over `featureColor` (the unselected look), composed
|
|
9
|
+
* once per editor by FeatureInteractionLayer and provided through FeatureStyleContext.
|
|
10
|
+
* `fillColor` falls back to `color`, as on MapLibre. Keep the result referentially stable:
|
|
11
|
+
* every feature layer restyles when it changes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function composeFeatureStyle(getFeatureStyle: FeatureStyleFn | undefined, featureColor: string | undefined): FeatureStyleFn | undefined;
|
|
14
|
+
/** This feature's custom (unselected) style, from the composed FeatureStyleContext. */
|
|
15
|
+
export declare function useCustomStyle(feature: GeoJSONFeature): PathOptions | undefined;
|
|
16
|
+
/** Pin colour from the custom style: fill first, then outline (like MapLibre's circle). */
|
|
17
|
+
export declare const pinColor: (custom: PathOptions | undefined) => string | undefined;
|
|
18
|
+
/** Unselected look: defaults + custom style + optional hover boost. */
|
|
19
|
+
export declare function idleStyle(base: PathOptions, custom: PathOptions | undefined, hover: PathOptions | null): PathOptions;
|
|
20
|
+
/** Selected / dragging look. Leaflet's setStyle merges, so keys a custom style set
|
|
21
|
+
* (dashArray, lineCap, …) are put back to Leaflet's defaults instead of leaking in. */
|
|
22
|
+
export declare function litStyle(custom: PathOptions | undefined, polygon: boolean, lit: PathOptions): PathOptions;
|
|
23
|
+
/** Accent palette for selected-feature Path styles (SVG attributes need concrete colours); provided once by FeatureInteractionLayer. */
|
|
24
|
+
export declare const AccentContext: import('react').Context<import('@element-editor/core').AccentPalette>;
|
|
25
|
+
export type OpenFeatureCtxMenu = (args: {
|
|
26
|
+
feature: GeoJSONFeature;
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
requestPreciseCoords?: () => void;
|
|
30
|
+
}) => void;
|
|
31
|
+
/** Build a Leaflet `contextmenu` handler that opens the shared feature context menu. */
|
|
32
|
+
export declare function makeContextMenuHandler(feature: GeoJSONFeature, readonly: boolean, open: OpenFeatureCtxMenu | undefined): (e: LeafletMouseEvent) => void;
|
|
33
|
+
/** `idle`: the pin's custom colour (featureColor / getFeatureStyle), undefined = default. */
|
|
34
|
+
export declare function makePinIcon(selected: boolean, multiSelected?: boolean, idle?: string): import('leaflet').DivIcon;
|
|
35
|
+
/**
|
|
36
|
+
* Hover tint: an uncoloured, unselected pin goes one step darker slate (a custom colour is kept,
|
|
37
|
+
* like MapLibre). Set on the marker's existing element, never by swapping the icon: a swapped-in
|
|
38
|
+
* element sits under the cursor without a mouseover, so it never gets the mouseout and the tint stuck.
|
|
39
|
+
*/
|
|
40
|
+
export declare function tintPin(layer: {
|
|
41
|
+
getElement?: () => Element | undefined;
|
|
42
|
+
} | null | undefined, on: boolean): void;
|
|
43
|
+
export declare function idlePin(color?: string): import('leaflet').DivIcon;
|
|
44
|
+
export declare function makeDimmedPinIcon(color?: string): import('leaflet').DivIcon;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
import { LeafletMouseEvent, PathOptions } from 'leaflet';
|
|
3
|
+
import { FeatureStyleFn } from '../../contexts/FeatureStyleContext.js';
|
|
4
|
+
export declare const IDLE_LINE: PathOptions;
|
|
5
|
+
export declare const IDLE_POLY: PathOptions;
|
|
6
|
+
export declare const HOVER_POLY: PathOptions;
|
|
7
|
+
/**
|
|
8
|
+
* The integrator's `getFeatureStyle` over `featureColor` (the unselected look), composed
|
|
9
|
+
* once per editor by FeatureInteractionLayer and provided through FeatureStyleContext.
|
|
10
|
+
* `fillColor` falls back to `color`, as on MapLibre. Keep the result referentially stable:
|
|
11
|
+
* every feature layer restyles when it changes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function composeFeatureStyle(getFeatureStyle: FeatureStyleFn | undefined, featureColor: string | undefined): FeatureStyleFn | undefined;
|
|
14
|
+
/** This feature's custom (unselected) style, from the composed FeatureStyleContext. */
|
|
15
|
+
export declare function useCustomStyle(feature: GeoJSONFeature): PathOptions | undefined;
|
|
16
|
+
/** Pin colour from the custom style: fill first, then outline (like MapLibre's circle). */
|
|
17
|
+
export declare const pinColor: (custom: PathOptions | undefined) => string | undefined;
|
|
18
|
+
/** Unselected look: defaults + custom style + optional hover boost. */
|
|
19
|
+
export declare function idleStyle(base: PathOptions, custom: PathOptions | undefined, hover: PathOptions | null): PathOptions;
|
|
20
|
+
/** Selected / dragging look. Leaflet's setStyle merges, so keys a custom style set
|
|
21
|
+
* (dashArray, lineCap, …) are put back to Leaflet's defaults instead of leaking in. */
|
|
22
|
+
export declare function litStyle(custom: PathOptions | undefined, polygon: boolean, lit: PathOptions): PathOptions;
|
|
23
|
+
/** Accent palette for selected-feature Path styles (SVG attributes need concrete colours); provided once by FeatureInteractionLayer. */
|
|
24
|
+
export declare const AccentContext: import('react').Context<import('@element-editor/core').AccentPalette>;
|
|
25
|
+
export type OpenFeatureCtxMenu = (args: {
|
|
26
|
+
feature: GeoJSONFeature;
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
requestPreciseCoords?: () => void;
|
|
30
|
+
}) => void;
|
|
31
|
+
/** Build a Leaflet `contextmenu` handler that opens the shared feature context menu. */
|
|
32
|
+
export declare function makeContextMenuHandler(feature: GeoJSONFeature, readonly: boolean, open: OpenFeatureCtxMenu | undefined): (e: LeafletMouseEvent) => void;
|
|
33
|
+
/** `idle`: the pin's custom colour (featureColor / getFeatureStyle), undefined = default. */
|
|
34
|
+
export declare function makePinIcon(selected: boolean, multiSelected?: boolean, idle?: string): import('leaflet').DivIcon;
|
|
35
|
+
/**
|
|
36
|
+
* Hover tint: an uncoloured, unselected pin goes one step darker slate (a custom colour is kept,
|
|
37
|
+
* like MapLibre). Set on the marker's existing element, never by swapping the icon: a swapped-in
|
|
38
|
+
* element sits under the cursor without a mouseover, so it never gets the mouseout and the tint stuck.
|
|
39
|
+
*/
|
|
40
|
+
export declare function tintPin(layer: {
|
|
41
|
+
getElement?: () => Element | undefined;
|
|
42
|
+
} | null | undefined, on: boolean): void;
|
|
43
|
+
export declare function idlePin(color?: string): import('leaflet').DivIcon;
|
|
44
|
+
export declare function makeDimmedPinIcon(color?: string): import('leaflet').DivIcon;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
3
|
+
import { ViewBounds } from './mountQueue.cjs';
|
|
4
|
+
export type { ViewBounds } from './mountQueue.cjs';
|
|
5
|
+
/**
|
|
6
|
+
* Progressive mounting: a batch of new features bigger than one tick's
|
|
7
|
+
* budget (initial load, import, a zoom-out) mounts a chunk per tick, the
|
|
8
|
+
* ones in the viewport first, instead of creating every Leaflet layer in one
|
|
9
|
+
* long task. `mountedIdsRef` is what the last commit rendered; features
|
|
10
|
+
* already in it, and the `alwaysMount` ones (selected / vertex-edit), always render.
|
|
11
|
+
*
|
|
12
|
+
* A data / selection change runs a full pass (O(culled)): it rebuilds the
|
|
13
|
+
* element list and the queue of features still to mount (see `planMount`:
|
|
14
|
+
* viewport first, reverse list order so the first in the list draws on top).
|
|
15
|
+
* A mount tick with nothing else changed is O(chunk): it takes the next chunk
|
|
16
|
+
* off the queue and appends it as one new group to the last commit's list.
|
|
17
|
+
* The chunk size adapts: an EMA of ms per mounted feature, measured on ticks
|
|
18
|
+
* only (render → layout effect, plus the layers' passive effects), sizes the
|
|
19
|
+
* next chunk to MOUNT_BUDGET_MS.
|
|
20
|
+
* Committed features also carry the group they mounted in: each group is a
|
|
21
|
+
* memoised <FeatureGroup>, so a commit re-reconciles only the groups whose
|
|
22
|
+
* elements changed, not every mounted feature. Render order doesn't affect
|
|
23
|
+
* z-order: Leaflet stacks layers in addLayer order.
|
|
24
|
+
*
|
|
25
|
+
* `build(list, mounts, partial)` returns one element per feature of `list`, or
|
|
26
|
+
* null to skip it (it must skip those `mounts` rejects). `partial` = a tick:
|
|
27
|
+
* `list` is just the new chunk. It runs inside a useMemo over `features`, `deps`
|
|
28
|
+
* and the mount tick only, so `viewBounds` (a pan) alone never rebuilds the list.
|
|
29
|
+
*/
|
|
30
|
+
export declare function useProgressiveMount(features: GeoJSONFeature[], alwaysMount: (f: GeoJSONFeature) => boolean, viewBounds: ViewBounds | null, build: (list: GeoJSONFeature[], mounts: (f: GeoJSONFeature) => boolean, partial: boolean) => (React.ReactElement | null)[], deps: unknown[]): React.ReactElement;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
3
|
+
import { ViewBounds } from './mountQueue.js';
|
|
4
|
+
export type { ViewBounds } from './mountQueue.js';
|
|
5
|
+
/**
|
|
6
|
+
* Progressive mounting: a batch of new features bigger than one tick's
|
|
7
|
+
* budget (initial load, import, a zoom-out) mounts a chunk per tick, the
|
|
8
|
+
* ones in the viewport first, instead of creating every Leaflet layer in one
|
|
9
|
+
* long task. `mountedIdsRef` is what the last commit rendered; features
|
|
10
|
+
* already in it, and the `alwaysMount` ones (selected / vertex-edit), always render.
|
|
11
|
+
*
|
|
12
|
+
* A data / selection change runs a full pass (O(culled)): it rebuilds the
|
|
13
|
+
* element list and the queue of features still to mount (see `planMount`:
|
|
14
|
+
* viewport first, reverse list order so the first in the list draws on top).
|
|
15
|
+
* A mount tick with nothing else changed is O(chunk): it takes the next chunk
|
|
16
|
+
* off the queue and appends it as one new group to the last commit's list.
|
|
17
|
+
* The chunk size adapts: an EMA of ms per mounted feature, measured on ticks
|
|
18
|
+
* only (render → layout effect, plus the layers' passive effects), sizes the
|
|
19
|
+
* next chunk to MOUNT_BUDGET_MS.
|
|
20
|
+
* Committed features also carry the group they mounted in: each group is a
|
|
21
|
+
* memoised <FeatureGroup>, so a commit re-reconciles only the groups whose
|
|
22
|
+
* elements changed, not every mounted feature. Render order doesn't affect
|
|
23
|
+
* z-order: Leaflet stacks layers in addLayer order.
|
|
24
|
+
*
|
|
25
|
+
* `build(list, mounts, partial)` returns one element per feature of `list`, or
|
|
26
|
+
* null to skip it (it must skip those `mounts` rejects). `partial` = a tick:
|
|
27
|
+
* `list` is just the new chunk. It runs inside a useMemo over `features`, `deps`
|
|
28
|
+
* and the mount tick only, so `viewBounds` (a pan) alone never rebuilds the list.
|
|
29
|
+
*/
|
|
30
|
+
export declare function useProgressiveMount(features: GeoJSONFeature[], alwaysMount: (f: GeoJSONFeature) => boolean, viewBounds: ViewBounds | null, build: (list: GeoJSONFeature[], mounts: (f: GeoJSONFeature) => boolean, partial: boolean) => (React.ReactElement | null)[], deps: unknown[]): React.ReactElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function CirclePreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function CirclePreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function LinePreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function LinePreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function PolygonPreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function PolygonPreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function RectPreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function RectPreview(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type BoxSelectHandlerProps = {
|
|
2
|
+
onSelect: (featureId: string, indices: number[]) => void;
|
|
3
|
+
onDragStart: () => void;
|
|
4
|
+
readonly: boolean;
|
|
5
|
+
/** Vertex-mode only: read the currently-selected vertex indices when the
|
|
6
|
+
* shift+drag begins so live preview can show (initial ∪ in-box) without
|
|
7
|
+
* losing previously additive picks. */
|
|
8
|
+
getInitialVertexIndices?: () => number[];
|
|
9
|
+
/** Vertex-mode only: per-tick preview of the rubber-band selection so the
|
|
10
|
+
* parent can highlight vertices in real time as the user drags. Sends the
|
|
11
|
+
* full target set (`initial ∪ in-box`); replace, don't merge. */
|
|
12
|
+
onVertexBoxPreview?: (featureId: string, indices: number[]) => void;
|
|
13
|
+
};
|
|
14
|
+
/** Handles Shift+drag rubber-band selection of points and line vertices. */
|
|
15
|
+
export declare function BoxSelectHandler({ onSelect, onDragStart, readonly, getInitialVertexIndices, onVertexBoxPreview }: BoxSelectHandlerProps): null;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type BoxSelectHandlerProps = {
|
|
2
|
+
onSelect: (featureId: string, indices: number[]) => void;
|
|
3
|
+
onDragStart: () => void;
|
|
4
|
+
readonly: boolean;
|
|
5
|
+
/** Vertex-mode only: read the currently-selected vertex indices when the
|
|
6
|
+
* shift+drag begins so live preview can show (initial ∪ in-box) without
|
|
7
|
+
* losing previously additive picks. */
|
|
8
|
+
getInitialVertexIndices?: () => number[];
|
|
9
|
+
/** Vertex-mode only: per-tick preview of the rubber-band selection so the
|
|
10
|
+
* parent can highlight vertices in real time as the user drags. Sends the
|
|
11
|
+
* full target set (`initial ∪ in-box`); replace, don't merge. */
|
|
12
|
+
onVertexBoxPreview?: (featureId: string, indices: number[]) => void;
|
|
13
|
+
};
|
|
14
|
+
/** Handles Shift+drag rubber-band selection of points and line vertices. */
|
|
15
|
+
export declare function BoxSelectHandler({ onSelect, onDragStart, readonly, getInitialVertexIndices, onVertexBoxPreview }: BoxSelectHandlerProps): null;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type MultiDragHandlerProps = {
|
|
2
|
+
onDragDelta: (delta: [number, number]) => void;
|
|
3
|
+
onDragEnd: (delta: [number, number], isDuplicate: boolean) => void;
|
|
4
|
+
onDuplicateDragChange: (isDuplicate: boolean) => void;
|
|
5
|
+
readonly: boolean;
|
|
6
|
+
};
|
|
7
|
+
/** Handles Cmd/Ctrl+drag (move) or Alt+drag (duplicate) on multi-selected features. */
|
|
8
|
+
export declare function MultiDragHandler({ onDragDelta, onDragEnd, onDuplicateDragChange, readonly }: MultiDragHandlerProps): null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type MultiDragHandlerProps = {
|
|
2
|
+
onDragDelta: (delta: [number, number]) => void;
|
|
3
|
+
onDragEnd: (delta: [number, number], isDuplicate: boolean) => void;
|
|
4
|
+
onDuplicateDragChange: (isDuplicate: boolean) => void;
|
|
5
|
+
readonly: boolean;
|
|
6
|
+
};
|
|
7
|
+
/** Handles Cmd/Ctrl+drag (move) or Alt+drag (duplicate) on multi-selected features. */
|
|
8
|
+
export declare function MultiDragHandler({ onDragDelta, onDragEnd, onDuplicateDragChange, readonly }: MultiDragHandlerProps): null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function OsmTraceHandler(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function OsmTraceHandler(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
type VertexEditorProps = {
|
|
3
|
+
feature: GeoJSONFeature;
|
|
4
|
+
selectedIndices: number[];
|
|
5
|
+
onExit: () => void;
|
|
6
|
+
onDeleteSelected: () => void;
|
|
7
|
+
/** Replace the point selection (FeatureInteractionLayer owns it). */
|
|
8
|
+
onSelectIndices: (indices: number[]) => void;
|
|
9
|
+
onFeatureClick: () => void;
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function VertexEditor({ feature, selectedIndices, onExit, onDeleteSelected, onSelectIndices, onFeatureClick, readonly, }: VertexEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GeoJSONFeature } from '@element-editor/core';
|
|
2
|
+
type VertexEditorProps = {
|
|
3
|
+
feature: GeoJSONFeature;
|
|
4
|
+
selectedIndices: number[];
|
|
5
|
+
onExit: () => void;
|
|
6
|
+
onDeleteSelected: () => void;
|
|
7
|
+
/** Replace the point selection (FeatureInteractionLayer owns it). */
|
|
8
|
+
onSelectIndices: (indices: number[]) => void;
|
|
9
|
+
onFeatureClick: () => void;
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function VertexEditor({ feature, selectedIndices, onExit, onDeleteSelected, onSelectIndices, onFeatureClick, readonly, }: VertexEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { PathOptions } from 'leaflet';
|
|
3
|
+
import { FeatureStyle, GeoJSONFeature } from '@element-editor/core';
|
|
4
|
+
export type { FeatureStyle };
|
|
5
|
+
/**
|
|
6
|
+
* Per-feature style: the engine-neutral `FeatureStyle` plus any other Leaflet
|
|
7
|
+
* `PathOptions` (dashArray, lineCap, …). `fillColor` falls back to `color`;
|
|
8
|
+
* `undefined`/`null` = default look.
|
|
9
|
+
*/
|
|
10
|
+
export type FeatureStyleFn = (feature: GeoJSONFeature) => (FeatureStyle & PathOptions) | undefined | null;
|
|
11
|
+
export declare function FeatureStyleProvider({ getFeatureStyle, children, }: {
|
|
12
|
+
getFeatureStyle?: FeatureStyleFn;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function useFeatureStyle(): FeatureStyleFn | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { PathOptions } from 'leaflet';
|
|
3
|
+
import { FeatureStyle, GeoJSONFeature } from '@element-editor/core';
|
|
4
|
+
export type { FeatureStyle };
|
|
5
|
+
/**
|
|
6
|
+
* Per-feature style: the engine-neutral `FeatureStyle` plus any other Leaflet
|
|
7
|
+
* `PathOptions` (dashArray, lineCap, …). `fillColor` falls back to `color`;
|
|
8
|
+
* `undefined`/`null` = default look.
|
|
9
|
+
*/
|
|
10
|
+
export type FeatureStyleFn = (feature: GeoJSONFeature) => (FeatureStyle & PathOptions) | undefined | null;
|
|
11
|
+
export declare function FeatureStyleProvider({ getFeatureStyle, children, }: {
|
|
12
|
+
getFeatureStyle?: FeatureStyleFn;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function useFeatureStyle(): FeatureStyleFn | undefined;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { default as L } from 'leaflet';
|
|
2
|
+
export type PointerState = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
ctrlKey: boolean;
|
|
6
|
+
metaKey: boolean;
|
|
7
|
+
altKey: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function getLastPointer(): PointerState;
|
|
10
|
+
export declare function isPointerOver(el: Element | null | undefined): boolean;
|
|
11
|
+
/** Layout-free pre-check for isPointerOver: is the pointer within `px` of this map point?
|
|
12
|
+
* Lets a bulk mount skip getBoundingClientRect on all but the marker under the cursor. */
|
|
13
|
+
export declare function isPointerNear(map: L.Map, latlng: L.LatLngExpression, px?: number): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { default as L } from 'leaflet';
|
|
2
|
+
export type PointerState = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
ctrlKey: boolean;
|
|
6
|
+
metaKey: boolean;
|
|
7
|
+
altKey: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function getLastPointer(): PointerState;
|
|
10
|
+
export declare function isPointerOver(el: Element | null | undefined): boolean;
|
|
11
|
+
/** Layout-free pre-check for isPointerOver: is the pointer within `px` of this map point?
|
|
12
|
+
* Lets a bulk mount skip getBoundingClientRect on all but the marker under the cursor. */
|
|
13
|
+
export declare function isPointerNear(map: L.Map, latlng: L.LatLngExpression, px?: number): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the latest map cursor lat/lng while a caller-specified mode is active
|
|
3
|
+
* and resets on every `editingMode` transition so exiting-and-re-entering the
|
|
4
|
+
* same tool (e.g. press R → off → R from a new cursor position) doesn't leak
|
|
5
|
+
* the previous session's coordinates on the first frame.
|
|
6
|
+
*
|
|
7
|
+
* React's "set state during render" reset pattern is used intentionally — it
|
|
8
|
+
* produces a synchronous re-render with cleared state before the browser
|
|
9
|
+
* paints, so there's no stale-coord flash.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
*
|
|
13
|
+
* const mouse = useCursorWhileMode(editingMode, editingMode === 'draw-rect')
|
|
14
|
+
*
|
|
15
|
+
* Pass a predicate that's true whenever cursor updates should flow into state.
|
|
16
|
+
* On mouseout (cursor leaves the map container) the value resets to null, so
|
|
17
|
+
* tooltips / previews that depend on it disappear.
|
|
18
|
+
*
|
|
19
|
+
* Any component that captures cursor position during a drawing mode should use
|
|
20
|
+
* this hook rather than re-implementing the pattern — see
|
|
21
|
+
* `feedback_mode_gated_cursor_state.md` for the history of that bug class.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useCursorWhileMode(editingMode: string, isActive: boolean): [number, number] | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the latest map cursor lat/lng while a caller-specified mode is active
|
|
3
|
+
* and resets on every `editingMode` transition so exiting-and-re-entering the
|
|
4
|
+
* same tool (e.g. press R → off → R from a new cursor position) doesn't leak
|
|
5
|
+
* the previous session's coordinates on the first frame.
|
|
6
|
+
*
|
|
7
|
+
* React's "set state during render" reset pattern is used intentionally — it
|
|
8
|
+
* produces a synchronous re-render with cleared state before the browser
|
|
9
|
+
* paints, so there's no stale-coord flash.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
*
|
|
13
|
+
* const mouse = useCursorWhileMode(editingMode, editingMode === 'draw-rect')
|
|
14
|
+
*
|
|
15
|
+
* Pass a predicate that's true whenever cursor updates should flow into state.
|
|
16
|
+
* On mouseout (cursor leaves the map container) the value resets to null, so
|
|
17
|
+
* tooltips / previews that depend on it disappear.
|
|
18
|
+
*
|
|
19
|
+
* Any component that captures cursor position during a drawing mode should use
|
|
20
|
+
* this hook rather than re-implementing the pattern — see
|
|
21
|
+
* `feedback_mode_gated_cursor_state.md` for the history of that bug class.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useCursorWhileMode(editingMode: string, isActive: boolean): [number, number] | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages the isDuplicateDrag ref for Alt+drag duplicate across geometry layer components.
|
|
3
|
+
* When local drag is active (dragDelta set), uses the local ref; otherwise uses multi-drag state.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useDuplicateDrag(dragDelta: [number, number] | null, multiDuplicateDrag: boolean): {
|
|
6
|
+
isDupDrag: boolean;
|
|
7
|
+
setDuplicateDrag: (v: boolean) => void;
|
|
8
|
+
resetDuplicateDrag: () => void;
|
|
9
|
+
};
|