@zwishing/emap 0.1.3 → 0.3.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/CHANGELOG.md +250 -1
- package/FEATURES.md +455 -0
- package/README.md +415 -205
- package/dist/core/event-map.d.ts +67 -0
- package/dist/core/feature-event-dispatcher.d.ts +70 -0
- package/dist/core/handler-manager.d.ts +49 -0
- package/dist/core/handler.d.ts +48 -0
- package/dist/core/handlers/box-select.d.ts +54 -0
- package/dist/core/handlers/click-select.d.ts +31 -0
- package/dist/core/handlers/drag-pan.d.ts +28 -0
- package/dist/core/handlers/draw-feature.d.ts +76 -0
- package/dist/core/handlers/lasso-select.d.ts +57 -0
- package/dist/core/handlers/scroll-zoom.d.ts +24 -0
- package/dist/core/handlers/select-geometry.d.ts +24 -0
- package/dist/core/handlers/select-mode.d.ts +14 -0
- package/dist/core/handlers/transform-feature.d.ts +41 -0
- package/dist/core/handlers/vertex-edit.d.ts +98 -0
- package/dist/core/pointer-event-dispatcher.d.ts +40 -0
- package/dist/core/tween.d.ts +1 -0
- package/dist/emap.css +42 -8
- package/dist/emap.js +2 -2
- package/dist/emap.mjs +1 -1
- package/dist/geo/camera.d.ts +100 -0
- package/dist/geo/projection.d.ts +8 -1
- package/dist/geo/viewport.d.ts +18 -0
- package/dist/index.d.ts +26 -2
- package/dist/map/edit-state-store.d.ts +1 -1
- package/dist/map/map.d.ts +89 -2
- package/dist/map/selection.d.ts +5 -2
- package/dist/renderer/edit-overlay-renderer.d.ts +2 -1
- package/dist/source/source.d.ts +2 -2
- package/dist/source/topology-source.d.ts +33 -4
- package/dist/ui/box-select-control.d.ts +13 -37
- package/dist/ui/draw-feature-control.d.ts +6 -71
- package/dist/ui/lasso-select-control.d.ts +14 -61
- package/dist/ui/status-control.d.ts +2 -2
- package/dist/ui/vertex-edit-control.d.ts +5 -100
- package/package.json +5 -1
- package/dist/core/drag-pan-handler.d.ts +0 -28
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { EditMode } from '../map/edit-state-store';
|
|
2
|
+
import type { HistoryStateSnapshot } from '../edit/edit-history';
|
|
3
|
+
import type { FeatureRef } from '../map/types';
|
|
4
|
+
import type { ValidatorResult } from '../validation/validator';
|
|
5
|
+
import type { FeatureEvent } from './feature-event-dispatcher';
|
|
6
|
+
import type { PointerMoveEvent } from './pointer-event-dispatcher';
|
|
7
|
+
/**
|
|
8
|
+
* Camera lifecycle payload (P9). `zoom` is `log2(scale)` — meaningful for
|
|
9
|
+
* WebMercator, otherwise the documented log2 convention. `originalEvent`
|
|
10
|
+
* is `null` for programmatic camera moves.
|
|
11
|
+
*/
|
|
12
|
+
export interface CameraEvent {
|
|
13
|
+
center: [number, number];
|
|
14
|
+
zoom: number;
|
|
15
|
+
originalEvent: MouseEvent | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Typed payloads for every event emitted ON the `Emap` instance (P10).
|
|
19
|
+
* Scoped to `this.fire(…)` in `src/map/map.ts` + the camera lifecycle
|
|
20
|
+
* wired through the camera host. Handler-internal events (`pan`,
|
|
21
|
+
* `panstart`, `mousewheel*`) and sub-dispatcher events (`tick`, `change`,
|
|
22
|
+
* `workerjob*`, source `dataloading`) are intentionally excluded — they
|
|
23
|
+
* are not delivered via `map.on`.
|
|
24
|
+
*
|
|
25
|
+
* This is an `interface` (not a `type`) so consumers can augment it:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* declare module '@zwishing/emap' {
|
|
29
|
+
* interface EmapEventMap { myCustomEvent: { detail: string } }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export interface EmapEventMap {
|
|
34
|
+
movestart: CameraEvent;
|
|
35
|
+
moveend: CameraEvent;
|
|
36
|
+
zoomstart: CameraEvent;
|
|
37
|
+
zoom: CameraEvent;
|
|
38
|
+
zoomend: CameraEvent;
|
|
39
|
+
move: Partial<CameraEvent>;
|
|
40
|
+
crschange: Record<never, never>;
|
|
41
|
+
resize: Record<never, never>;
|
|
42
|
+
data: {
|
|
43
|
+
sourceId?: string;
|
|
44
|
+
reason?: 'edit' | 'load' | 'replace' | 'reproject';
|
|
45
|
+
};
|
|
46
|
+
editmodechange: {
|
|
47
|
+
mode: EditMode;
|
|
48
|
+
};
|
|
49
|
+
error: {
|
|
50
|
+
error: Error;
|
|
51
|
+
};
|
|
52
|
+
historychange: Partial<HistoryStateSnapshot>;
|
|
53
|
+
selectionchange: {
|
|
54
|
+
selected: FeatureRef[];
|
|
55
|
+
added: FeatureRef[];
|
|
56
|
+
removed: FeatureRef[];
|
|
57
|
+
};
|
|
58
|
+
validationfailed: {
|
|
59
|
+
results: ValidatorResult[];
|
|
60
|
+
label: string;
|
|
61
|
+
};
|
|
62
|
+
mousemove: PointerMoveEvent;
|
|
63
|
+
'feature:click': FeatureEvent;
|
|
64
|
+
'feature:hover': FeatureEvent;
|
|
65
|
+
'feature:enter': FeatureEvent;
|
|
66
|
+
'feature:leave': FeatureEvent;
|
|
67
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { NormalizedPointerEvent } from './handler';
|
|
2
|
+
import type { FeatureRef } from '../map/types';
|
|
3
|
+
import type { Feature } from '../map/feature-accessor';
|
|
4
|
+
/** Restrict which features a `feature:*` listener hears. Omitted = all visible. */
|
|
5
|
+
export interface FeatureEventFilter {
|
|
6
|
+
source?: string;
|
|
7
|
+
layers?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface FeatureEvent {
|
|
10
|
+
type: string;
|
|
11
|
+
feature: Feature;
|
|
12
|
+
ref: FeatureRef;
|
|
13
|
+
point: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
lngLat: [number, number];
|
|
18
|
+
modifiers: {
|
|
19
|
+
shift: boolean;
|
|
20
|
+
ctrl: boolean;
|
|
21
|
+
alt: boolean;
|
|
22
|
+
meta: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** The DOM event that produced this (typed `Event`; it is a PointerEvent/MouseEvent at runtime). */
|
|
25
|
+
originalEvent: Event;
|
|
26
|
+
}
|
|
27
|
+
type Cb = (e: FeatureEvent) => void;
|
|
28
|
+
/** Minimal surface the dispatcher needs from Emap (keeps it unit-testable). */
|
|
29
|
+
export interface FeatureEventMapHost {
|
|
30
|
+
queryFeatures(geom: [number, number], opts?: {
|
|
31
|
+
layers?: string[];
|
|
32
|
+
}): Array<{
|
|
33
|
+
source: string;
|
|
34
|
+
layer: string;
|
|
35
|
+
id: number;
|
|
36
|
+
}>;
|
|
37
|
+
unproject(x: number, y: number): [number, number];
|
|
38
|
+
features: {
|
|
39
|
+
get(ref: FeatureRef): Feature | null;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Delegated feature-event registry + enter/leave/hover state machine.
|
|
44
|
+
* Fed normalized pointer events by `HandlerManager`'s sink (click + non-
|
|
45
|
+
* gesture move). Reuses `host.queryFeatures` (existing FeatureQuery/LOD
|
|
46
|
+
* path) — no new hit-test. Decoupled from selection.
|
|
47
|
+
*/
|
|
48
|
+
export declare class FeatureEventDispatcher {
|
|
49
|
+
private _host;
|
|
50
|
+
private _reg;
|
|
51
|
+
private _lastKey;
|
|
52
|
+
private _lastRef;
|
|
53
|
+
/** Maps user-supplied cb → its once-wrapper so `off(type, userCb)` can resolve the wrap. */
|
|
54
|
+
private _onceWrap;
|
|
55
|
+
private _onChange?;
|
|
56
|
+
constructor(_host: FeatureEventMapHost);
|
|
57
|
+
/** Install a callback invoked whenever the registry changes (on/off/once auto-fire). */
|
|
58
|
+
setOnChange(fn: () => void): void;
|
|
59
|
+
hasListeners(): boolean;
|
|
60
|
+
/** `on(type, filter, cb)` or `on(type, cb)`. Non-feature types are rejected (Emap routes them). */
|
|
61
|
+
on(type: string, filter: FeatureEventFilter | undefined, cb: Cb): void;
|
|
62
|
+
/** Register a one-time listener. Cancellable via `off(type, cb)` using the original `cb`. */
|
|
63
|
+
once(type: string, filter: FeatureEventFilter | undefined, cb: Cb): void;
|
|
64
|
+
off(type: string, cb: Cb): void;
|
|
65
|
+
/** Topmost hit at a point, restricted to the union of all registered filters' layers. */
|
|
66
|
+
private _hit;
|
|
67
|
+
private _emit;
|
|
68
|
+
dispatch(ev: NormalizedPointerEvent): void;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Handler, type NormalizedPointerEvent } from './handler';
|
|
2
|
+
/**
|
|
3
|
+
* Registry of named interaction handlers owned by Emap. Owns the
|
|
4
|
+
* single-DOM-listener pointer arbitration layer: installs one set of pointer
|
|
5
|
+
* listeners, normalizes events, and dispatches to enabled handlers in fixed
|
|
6
|
+
* priority order with an exclusive-gesture lock.
|
|
7
|
+
*/
|
|
8
|
+
export declare class HandlerManager {
|
|
9
|
+
private _handlers;
|
|
10
|
+
private _element;
|
|
11
|
+
private _exclusive;
|
|
12
|
+
private _pointerSink;
|
|
13
|
+
private _onDown;
|
|
14
|
+
private _onMove;
|
|
15
|
+
private _onUp;
|
|
16
|
+
private _onClick;
|
|
17
|
+
private _onContextMenu;
|
|
18
|
+
private _onDblClick;
|
|
19
|
+
/** Register a handler; re-registering the same `name` replaces the prior one. */
|
|
20
|
+
register(handler: Handler): void;
|
|
21
|
+
/** Is a sibling handler currently registered AND enabled? */
|
|
22
|
+
isEnabledByName(name: string): boolean;
|
|
23
|
+
get(name: string): Handler | undefined;
|
|
24
|
+
/** Legacy `interactive` shim: toggles the pan + zoom handlers together. */
|
|
25
|
+
applyInteractive(interactive: boolean): void;
|
|
26
|
+
/** Install the single pointer-listener set + start arbitrating. */
|
|
27
|
+
attach(element: HTMLElement): void;
|
|
28
|
+
/** Remove all listeners and clear any active exclusive gesture. */
|
|
29
|
+
detach(): void;
|
|
30
|
+
/** Install (or clear with `null`) a passive observer fed click + non-gesture move. */
|
|
31
|
+
setPointerSink(sink: ((ev: NormalizedPointerEvent) => void) | null): void;
|
|
32
|
+
private _norm;
|
|
33
|
+
private _orderedEnabled;
|
|
34
|
+
private _dispatch;
|
|
35
|
+
private _dispatchInner;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the effective cursor and write it to the attached element.
|
|
38
|
+
* Order: active exclusive handler's cursor → first enabled handler in
|
|
39
|
+
* PRIORITY with a non-empty cursor → '' (clear → CSS/page default).
|
|
40
|
+
* Idempotent: only writes when the value changes.
|
|
41
|
+
*/
|
|
42
|
+
private _applyCursor;
|
|
43
|
+
/**
|
|
44
|
+
* Recompute the effective cursor. Call after a handler changes
|
|
45
|
+
* cursor-relevant state OUTSIDE a pointer event (e.g. enable()/disable()
|
|
46
|
+
* flipping a mode). No-op when not attached.
|
|
47
|
+
*/
|
|
48
|
+
refreshCursor(): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Outcome of a handler processing one normalized event. */
|
|
2
|
+
export type HandlerResult = 'passthrough' | 'consumed' | 'exclusive';
|
|
3
|
+
export interface PointerModifiers {
|
|
4
|
+
shift: boolean;
|
|
5
|
+
ctrl: boolean;
|
|
6
|
+
alt: boolean;
|
|
7
|
+
meta: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** A DOM pointer/wheel event normalized to element-local coords + modifiers. */
|
|
10
|
+
export interface NormalizedPointerEvent {
|
|
11
|
+
kind: 'down' | 'move' | 'up' | 'wheel' | 'click' | 'contextmenu' | 'dblclick';
|
|
12
|
+
/** Element-local pixel position. */
|
|
13
|
+
point: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
modifiers: PointerModifiers;
|
|
18
|
+
/** Mouse button for down/up/click; 0 otherwise. */
|
|
19
|
+
button: number;
|
|
20
|
+
/** Wheel delta (sign already normalized: +1 zoom-in direction). Absent if N/A. */
|
|
21
|
+
wheelDirection?: number;
|
|
22
|
+
originalEvent: Event;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A unit of interaction (pan, zoom, selection…). Every interaction on the map
|
|
26
|
+
* is one of these. Mirrors MapLibre's handler model.
|
|
27
|
+
*/
|
|
28
|
+
export interface Handler<Opts = unknown> {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
enable(): void;
|
|
31
|
+
disable(): void;
|
|
32
|
+
isEnabled(): boolean;
|
|
33
|
+
setOptions(opts: Partial<Opts>): void;
|
|
34
|
+
getOptions(): Readonly<Opts>;
|
|
35
|
+
/** Process one normalized event; report whether it was consumed. */
|
|
36
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
37
|
+
/**
|
|
38
|
+
* CSS cursor this handler wants for its CURRENT state, or `null` =
|
|
39
|
+
* no opinion (HandlerManager falls through to lower-priority handlers,
|
|
40
|
+
* then the page/CSS default). Optional — absent ⇒ no opinion.
|
|
41
|
+
* An empty string `''` is treated the same as `null` (no opinion);
|
|
42
|
+
* to clear to the page/CSS default, simply have no enabled handler
|
|
43
|
+
* with an opinion (HandlerManager writes `''` itself in that case).
|
|
44
|
+
*/
|
|
45
|
+
getCursor?(): string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Runtime guard — used by HandlerManager.register and by consumers. */
|
|
48
|
+
export declare function isHandler(v: unknown): v is Handler;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { EventDispatcher } from '../events';
|
|
2
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
3
|
+
import { type ModifierCombo } from './select-mode';
|
|
4
|
+
import type { SelectHandlerOptions } from './click-select';
|
|
5
|
+
import type { Emap } from '../../map/map';
|
|
6
|
+
/** Visual style for the rubber-band rectangle. */
|
|
7
|
+
export interface BoxSelectStyle {
|
|
8
|
+
/** Stroke color. Default `'#0078ff'`. */
|
|
9
|
+
stroke?: string;
|
|
10
|
+
/** Stroke width in CSS pixels. Default `1.5`. */
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Fill color. Default `'rgba(0, 120, 255, 0.1)'`. */
|
|
13
|
+
fill?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface BoxSelectHandlerOptions extends SelectHandlerOptions {
|
|
16
|
+
/** Min drag diagonal (px) before a release counts as a box. Default 4. */
|
|
17
|
+
dragThreshold?: number;
|
|
18
|
+
/** Combo that must be held at pointerdown to start. Default 'shift'. */
|
|
19
|
+
dragActivator?: ModifierCombo;
|
|
20
|
+
style?: BoxSelectStyle;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Named `boxSelect` handler. Disabled by default. On `dragActivator`+down
|
|
24
|
+
* it captures the gesture (`'exclusive'`), draws a rubber-band rectangle in
|
|
25
|
+
* `_canvasContainer`, and on release past `dragThreshold` runs
|
|
26
|
+
* `queryFeatures(bbox)` → `select(mode)`. Mode is resolved by stripping the
|
|
27
|
+
* activator then matching `modifiers` — reproducing the legacy
|
|
28
|
+
* `BoxSelectControl` behavior exactly. Mutually exclusive with `lassoSelect`
|
|
29
|
+
* (enforced in HandlerManager — a later task).
|
|
30
|
+
*/
|
|
31
|
+
export declare class BoxSelectHandler extends EventDispatcher implements Handler<BoxSelectHandlerOptions> {
|
|
32
|
+
private _map;
|
|
33
|
+
readonly name = "boxSelect";
|
|
34
|
+
private _enabled;
|
|
35
|
+
private _opts;
|
|
36
|
+
private _start;
|
|
37
|
+
private _overlay;
|
|
38
|
+
/** Set by HandlerManager.register. */
|
|
39
|
+
_manager?: {
|
|
40
|
+
isEnabledByName(name: string): boolean;
|
|
41
|
+
};
|
|
42
|
+
constructor(_map: Emap);
|
|
43
|
+
enable(): void;
|
|
44
|
+
disable(): void;
|
|
45
|
+
isEnabled(): boolean;
|
|
46
|
+
setOptions(opts: Partial<BoxSelectHandlerOptions>): void;
|
|
47
|
+
getOptions(): Readonly<BoxSelectHandlerOptions>;
|
|
48
|
+
getCursor(): string | null;
|
|
49
|
+
private get _threshold();
|
|
50
|
+
private get _activator();
|
|
51
|
+
private _ensureOverlay;
|
|
52
|
+
private _cancel;
|
|
53
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
54
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EventDispatcher } from '../events';
|
|
2
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
3
|
+
import { type ModifierCombo, type SelectMode } from './select-mode';
|
|
4
|
+
import type { Emap } from '../../map/map';
|
|
5
|
+
export interface SelectHandlerOptions {
|
|
6
|
+
/** Restrict selectable layers; default = all visible. */
|
|
7
|
+
layers?: string[];
|
|
8
|
+
source?: string;
|
|
9
|
+
/** Modifier→mode table. Defaults reproduce a sensible click model. */
|
|
10
|
+
modifiers?: Partial<Record<SelectMode, ModifierCombo>>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Named `clickSelect` handler. Acts only on `kind:'click'` (the arbiter
|
|
14
|
+
* synthesizes it from a non-dragging down/up, so it never fights a
|
|
15
|
+
* box/lasso drag). Hit → resolve mode → `Emap.select`; empty click whose
|
|
16
|
+
* resolved mode is `'replace'` clears the selection. Opt-in (disabled by
|
|
17
|
+
* default) so `new Emap()` behavior is unchanged.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ClickSelectHandler extends EventDispatcher implements Handler<SelectHandlerOptions> {
|
|
20
|
+
private _map;
|
|
21
|
+
readonly name = "clickSelect";
|
|
22
|
+
private _enabled;
|
|
23
|
+
private _opts;
|
|
24
|
+
constructor(_map: Emap);
|
|
25
|
+
enable(): void;
|
|
26
|
+
disable(): void;
|
|
27
|
+
isEnabled(): boolean;
|
|
28
|
+
setOptions(opts: Partial<SelectHandlerOptions>): void;
|
|
29
|
+
getOptions(): Readonly<SelectHandlerOptions>;
|
|
30
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { EventDispatcher } from '../events';
|
|
2
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
3
|
+
export interface DragPanOptions {
|
|
4
|
+
/** Reserve shift+drag for higher-level handlers (P2b selection). Default true. */
|
|
5
|
+
skipShiftDrag: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Named `dragPan` handler. Phase 2a: pan logic lives in `handlePointer`,
|
|
9
|
+
* driven by HandlerManager's single pointer-listener arbiter — the handler
|
|
10
|
+
* owns no DOM listeners. Emits `panstart` / `pan {dx,dy}` / `panend` exactly
|
|
11
|
+
* as before so Emap's viewport wiring is unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export declare class DragPanHandler extends EventDispatcher implements Handler<DragPanOptions> {
|
|
14
|
+
readonly name = "dragPan";
|
|
15
|
+
private _opts;
|
|
16
|
+
private _enabled;
|
|
17
|
+
private _dragging;
|
|
18
|
+
private _last;
|
|
19
|
+
private _onGestureStart?;
|
|
20
|
+
constructor(opts?: Partial<DragPanOptions>, onGestureStart?: () => void);
|
|
21
|
+
enable(): void;
|
|
22
|
+
disable(): void;
|
|
23
|
+
isEnabled(): boolean;
|
|
24
|
+
setOptions(opts: Partial<DragPanOptions>): void;
|
|
25
|
+
getOptions(): Readonly<DragPanOptions>;
|
|
26
|
+
getCursor(): string | null;
|
|
27
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
28
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
2
|
+
import { Emap } from '../../map/map';
|
|
3
|
+
export type DrawGeometryType = 'polygon' | 'polyline' | 'point';
|
|
4
|
+
export interface DrawFeatureOptions {
|
|
5
|
+
/** Target source ID where new features will be added */
|
|
6
|
+
source: string;
|
|
7
|
+
/** Target layer name within the source (optional, defaults to first matching geometry layer) */
|
|
8
|
+
sourceLayer?: string;
|
|
9
|
+
/** Geometry type to draw */
|
|
10
|
+
type: DrawGeometryType;
|
|
11
|
+
/** Pixel threshold for vertex snapping (default: 10) */
|
|
12
|
+
snapThreshold?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Sources to snap against. Defaults to `[options.source]` so the existing
|
|
15
|
+
* single-source behaviour is preserved when omitted. Pass `['a', 'b']` to
|
|
16
|
+
* also snap onto another source (e.g. drawing in source A while tracing
|
|
17
|
+
* along reference data in source B).
|
|
18
|
+
*/
|
|
19
|
+
snapSources?: string[];
|
|
20
|
+
/** Enable vertex snap (default `true`). */
|
|
21
|
+
snapToVertex?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Enable edge snap — perpendicular projection onto an arc segment
|
|
24
|
+
* (default `true`). Disable to restrict snap to existing vertices only.
|
|
25
|
+
*/
|
|
26
|
+
snapToEdge?: boolean;
|
|
27
|
+
/** CSS color for the drawing line (default: '#0078ff') */
|
|
28
|
+
drawColor?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare class DrawFeatureHandler implements Handler<DrawFeatureOptions> {
|
|
31
|
+
readonly name = "drawFeature";
|
|
32
|
+
private readonly _map;
|
|
33
|
+
private _enabled;
|
|
34
|
+
private _opts;
|
|
35
|
+
/** Injected by HandlerManager.register (cursor recompute on mode flips). */
|
|
36
|
+
_manager?: {
|
|
37
|
+
refreshCursor(): void;
|
|
38
|
+
};
|
|
39
|
+
private _vertices;
|
|
40
|
+
private _cursorCoord;
|
|
41
|
+
private _snapCoord;
|
|
42
|
+
private _snapKind;
|
|
43
|
+
private _snapThreshold;
|
|
44
|
+
private _onMapMoveBound;
|
|
45
|
+
private _onKeyDownBound;
|
|
46
|
+
constructor(map: Emap, opts?: Partial<DrawFeatureOptions>);
|
|
47
|
+
isEnabled(): boolean;
|
|
48
|
+
setOptions(o: Partial<DrawFeatureOptions>): void;
|
|
49
|
+
getCursor(): string | null;
|
|
50
|
+
getOptions(): Readonly<DrawFeatureOptions>;
|
|
51
|
+
enable(): void;
|
|
52
|
+
disable(): void;
|
|
53
|
+
private _quietDisable;
|
|
54
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
55
|
+
private _onMapMove;
|
|
56
|
+
private _onMouseMove;
|
|
57
|
+
private _onClick;
|
|
58
|
+
private _onDblClick;
|
|
59
|
+
private _onKeyDown;
|
|
60
|
+
private _findSnapTarget;
|
|
61
|
+
private _commitFeature;
|
|
62
|
+
private _resolveTargetLayer;
|
|
63
|
+
private _appendArc;
|
|
64
|
+
private _updateDrawState;
|
|
65
|
+
private _clearDrawState;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Closest point on segment `[a, b]` to point `p`, all in 2-D pixel space.
|
|
69
|
+
* Parameter `t = ((p-a)·(b-a)) / |b-a|²` is clamped to `[0, 1]` so the
|
|
70
|
+
* returned point is always on the segment (not its infinite line). For a
|
|
71
|
+
* zero-length segment, returns endpoint `a` and `dist(p, a)`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function projectPointOnSegment(p: [number, number], a: [number, number], b: [number, number]): {
|
|
74
|
+
point: [number, number];
|
|
75
|
+
dist: number;
|
|
76
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { EventDispatcher } from '../events';
|
|
2
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
3
|
+
import { type ModifierCombo } from './select-mode';
|
|
4
|
+
import type { SelectHandlerOptions } from './click-select';
|
|
5
|
+
import type { Emap } from '../../map/map';
|
|
6
|
+
/** Visual style for the rubber-band lasso path. */
|
|
7
|
+
export interface LassoSelectStyle {
|
|
8
|
+
/** Stroke color. Default `'#0078ff'`. */
|
|
9
|
+
stroke?: string;
|
|
10
|
+
/** Stroke width in CSS pixels. Default `1.5`. */
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Fill color of the closed shape preview. Default `'rgba(0, 120, 255, 0.1)'`. */
|
|
13
|
+
fill?: string;
|
|
14
|
+
/** SVG `stroke-dasharray`. Default `'4 3'` (subtly dashed). */
|
|
15
|
+
strokeDasharray?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Back-compat alias — historically the handler-side name. */
|
|
18
|
+
export type LassoStyle = LassoSelectStyle;
|
|
19
|
+
export interface LassoSelectHandlerOptions extends SelectHandlerOptions {
|
|
20
|
+
dragThreshold?: number;
|
|
21
|
+
dragActivator?: ModifierCombo;
|
|
22
|
+
/** Loose hit (any part inside) vs strict containment. Default true. */
|
|
23
|
+
includeIntersecting?: boolean;
|
|
24
|
+
style?: LassoSelectStyle;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Named `lassoSelect` handler. Disabled by default. `dragActivator`+down →
|
|
28
|
+
* `'exclusive'`; each move appends to a pixel-space path drawn as an SVG
|
|
29
|
+
* polyline in `_canvasContainer`; release closes the polygon, bbox-prefilters
|
|
30
|
+
* via `queryFeatures`, then refines per-feature with the shared
|
|
31
|
+
* `featureMatchesLasso`. Mutually exclusive with `boxSelect` (a later task).
|
|
32
|
+
*/
|
|
33
|
+
export declare class LassoSelectHandler extends EventDispatcher implements Handler<LassoSelectHandlerOptions> {
|
|
34
|
+
private _map;
|
|
35
|
+
readonly name = "lassoSelect";
|
|
36
|
+
private _enabled;
|
|
37
|
+
private _opts;
|
|
38
|
+
private _path;
|
|
39
|
+
private _svg;
|
|
40
|
+
private _poly;
|
|
41
|
+
/** Set by HandlerManager.register. */
|
|
42
|
+
_manager?: {
|
|
43
|
+
isEnabledByName(name: string): boolean;
|
|
44
|
+
};
|
|
45
|
+
constructor(_map: Emap);
|
|
46
|
+
enable(): void;
|
|
47
|
+
disable(): void;
|
|
48
|
+
isEnabled(): boolean;
|
|
49
|
+
setOptions(opts: Partial<LassoSelectHandlerOptions>): void;
|
|
50
|
+
getOptions(): Readonly<LassoSelectHandlerOptions>;
|
|
51
|
+
getCursor(): string | null;
|
|
52
|
+
private get _threshold();
|
|
53
|
+
private get _activator();
|
|
54
|
+
private _ensureSvg;
|
|
55
|
+
private _cancel;
|
|
56
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { EventDispatcher } from '../events';
|
|
2
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
3
|
+
export interface ScrollZoomOptions {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Named `scrollZoom` handler. Phase 1: adapts the existing MouseWheel DOM
|
|
7
|
+
* primitive, re-emitting its 'mousewheel' event. When disabled, re-emission
|
|
8
|
+
* is suppressed (MouseWheel itself has no enable flag, so the handler gates
|
|
9
|
+
* at the boundary).
|
|
10
|
+
*/
|
|
11
|
+
export declare class ScrollZoomHandler extends EventDispatcher implements Handler<ScrollZoomOptions> {
|
|
12
|
+
readonly name = "scrollZoom";
|
|
13
|
+
private _wheel;
|
|
14
|
+
private _enabled;
|
|
15
|
+
private _onGestureStart?;
|
|
16
|
+
constructor(element: HTMLElement, onGestureStart?: () => void);
|
|
17
|
+
enable(): void;
|
|
18
|
+
disable(): void;
|
|
19
|
+
isEnabled(): boolean;
|
|
20
|
+
setOptions(_opts: Partial<ScrollZoomOptions>): void;
|
|
21
|
+
getOptions(): Readonly<ScrollZoomOptions>;
|
|
22
|
+
handlePointer(_ev: NormalizedPointerEvent): HandlerResult;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Emap, FeatureRef } from '../../map/map';
|
|
2
|
+
/**
|
|
3
|
+
* Even-odd ray-cast point-in-polygon test. The `polygon` may be open or
|
|
4
|
+
* closed (last point == first point) — we don't require either form,
|
|
5
|
+
* because we wrap via `j = (i - 1 + n) % n` modulo arithmetic. Self-
|
|
6
|
+
* intersecting polygons follow standard even-odd semantics (which usually
|
|
7
|
+
* matches the user's drawn intent).
|
|
8
|
+
*/
|
|
9
|
+
export declare function pointInPolygon(point: [number, number], polygon: Array<[number, number]>): boolean;
|
|
10
|
+
/** [xmin, ymin, xmax, ymax] of a 2-D point sequence. */
|
|
11
|
+
export declare function pathBBox(path: Array<[number, number]>): [number, number, number, number];
|
|
12
|
+
export declare function featureMatchesLasso(map: Emap, ref: {
|
|
13
|
+
source: string;
|
|
14
|
+
layer: string;
|
|
15
|
+
id: number;
|
|
16
|
+
geometryType?: string;
|
|
17
|
+
}, lasso: Array<[number, number]>, includeIntersecting: boolean): boolean;
|
|
18
|
+
/** Dedupe `(source, layer, id)` triples — same feature can show up under
|
|
19
|
+
* multiple visible layers when a source has e.g. fill + line styling. */
|
|
20
|
+
export declare function dedupeRefs(features: ReadonlyArray<{
|
|
21
|
+
source: string;
|
|
22
|
+
layer: string;
|
|
23
|
+
id: number;
|
|
24
|
+
}>): FeatureRef[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PointerModifiers } from '../handler';
|
|
2
|
+
/** How a selection gesture combines with the existing selection. */
|
|
3
|
+
export type SelectMode = 'replace' | 'add' | 'toggle';
|
|
4
|
+
/** A canonical chord of modifier keys, '+'-joined in shift+ctrl+alt+meta order. */
|
|
5
|
+
export type ModifierCombo = 'none' | 'shift' | 'ctrl' | 'alt' | 'meta' | 'shift+ctrl' | 'shift+alt' | 'shift+meta' | 'ctrl+alt' | 'ctrl+meta' | 'alt+meta' | 'shift+ctrl+alt' | 'shift+ctrl+meta' | 'shift+alt+meta' | 'ctrl+alt+meta' | 'shift+ctrl+alt+meta';
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a {@link SelectMode} from held modifiers.
|
|
8
|
+
*
|
|
9
|
+
* When `activator` is given (box/lasso drag gate), its keys are stripped
|
|
10
|
+
* from the held set first — the gesture-end chord is matched on whatever
|
|
11
|
+
* was held *beyond* the activator. With no `activator` (clickSelect) the
|
|
12
|
+
* full held chord is matched. Unmatched → `'replace'`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveMode(modifiers: PointerModifiers, table: Partial<Record<SelectMode, ModifierCombo>>, activator?: ModifierCombo): SelectMode;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Handler, HandlerResult, NormalizedPointerEvent } from '../handler';
|
|
2
|
+
import type { Emap } from '../../map/map';
|
|
3
|
+
import type { DragSessionOptions } from '../../map/types';
|
|
4
|
+
export interface TransformFeatureOptions {
|
|
5
|
+
/** Active transform. Default 'translate'. */
|
|
6
|
+
mode?: 'translate' | 'rotate' | 'scale';
|
|
7
|
+
/** Forwarded to begin*Session (e.g. { splitSharedArcs }). */
|
|
8
|
+
session?: DragSessionOptions;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Named `transformFeature` handler. Modal (`enable()`→`setEditMode
|
|
12
|
+
* ('transform')`). On pointerdown over an **already-selected** feature it
|
|
13
|
+
* opens the matching editSession (selection itself is delegated to the
|
|
14
|
+
* modifier-aware `clickSelect`/`boxSelect`); pointermove streams the
|
|
15
|
+
* per-frame delta (translate dx/dy, or rotate/scale matrix vs the session
|
|
16
|
+
* origin); pointerup commits (one undo entry); Escape cancels. Absorbs the
|
|
17
|
+
* boilerplate previously hand-wired in the transform examples.
|
|
18
|
+
*/
|
|
19
|
+
export declare class TransformFeatureHandler implements Handler<TransformFeatureOptions> {
|
|
20
|
+
private _map;
|
|
21
|
+
readonly name = "transformFeature";
|
|
22
|
+
private _enabled;
|
|
23
|
+
private _opts;
|
|
24
|
+
private _translate;
|
|
25
|
+
private _affine;
|
|
26
|
+
private _lastMap;
|
|
27
|
+
private _prevAngle;
|
|
28
|
+
private _prevDist;
|
|
29
|
+
private _onKeyDownBound;
|
|
30
|
+
constructor(_map: Emap);
|
|
31
|
+
private get _mode();
|
|
32
|
+
enable(): void;
|
|
33
|
+
disable(): void;
|
|
34
|
+
private _quietDisable;
|
|
35
|
+
isEnabled(): boolean;
|
|
36
|
+
setOptions(o: Partial<TransformFeatureOptions>): void;
|
|
37
|
+
getOptions(): Readonly<TransformFeatureOptions>;
|
|
38
|
+
private _cancelSession;
|
|
39
|
+
private _onKeyDown;
|
|
40
|
+
handlePointer(ev: NormalizedPointerEvent): HandlerResult;
|
|
41
|
+
}
|