@orbat-mapper/tactical-draw 0.2.0-alpha.21 → 0.2.0-alpha.23

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 CHANGED
@@ -87,6 +87,22 @@ if (session && "canCommit" in session) {
87
87
  }
88
88
  ```
89
89
 
90
+ Live edit and transform sessions expose a session-local `history`. Completed
91
+ gestures and authored programmatic mutations are undoable without settling the
92
+ session; pointer-move preview frames are not history steps:
93
+
94
+ ```ts
95
+ const active = td.activeSession;
96
+ const history =
97
+ (active && "history" in active ? active.history : null) ?? td.activeTransformSession?.history;
98
+ if (history) history.undo();
99
+ ```
100
+
101
+ `history.subscribe(...)` reports `canUndo`, `canRedo`, and optional operation
102
+ labels. Committing folds only the selected working state into the host document
103
+ as one operation. Changing a transform session's members with `setGraphics()`
104
+ establishes a new local baseline and clears both stacks.
105
+
90
106
  ## Bring your own engine
91
107
 
92
108
  Implement `MapAdapter` (or extend `BaseMapAdapter`) against your map engine
package/dist/index.d.mts CHANGED
@@ -12,6 +12,11 @@ interface PointerEventInfo {
12
12
  * mouse paths populate it; touch leaves it `undefined`.
13
13
  */
14
14
  altKey?: boolean;
15
+ /**
16
+ * Whether Shift was held for this pointer event. Rotation gestures use the
17
+ * live value to constrain transform-box angles to 15-degree increments.
18
+ */
19
+ shiftKey?: boolean;
15
20
  /** Engine-native event, for drivers that need to call preventDefault etc. */
16
21
  native?: unknown;
17
22
  }
@@ -672,6 +677,22 @@ type EditMode = "reshape" | "transform" | "labeldrag" | "delete";
672
677
  */
673
678
  type SettleReason = "close" | "error" | TacticalDrawAbortReason;
674
679
  //#endregion
680
+ //#region src/tactical-draw/session-history.d.ts
681
+ type SessionChangeSource = "gesture" | "undo" | "redo" | "programmatic";
682
+ interface SessionHistoryState {
683
+ readonly canUndo: boolean;
684
+ readonly canRedo: boolean;
685
+ readonly undoLabel?: string;
686
+ readonly redoLabel?: string;
687
+ }
688
+ interface SessionHistory {
689
+ /** Current availability and optional labels. Each session retains at most 100 completed steps. */
690
+ readonly state: SessionHistoryState;
691
+ undo(): boolean;
692
+ redo(): boolean;
693
+ subscribe(listener: (state: SessionHistoryState) => void): () => void;
694
+ }
695
+ //#endregion
675
696
  //#region src/tactical-draw/edit-session.d.ts
676
697
  /**
677
698
  * Surface delivered via `td.edit(measure, opts).onSession` and synchronously
@@ -683,6 +704,8 @@ type SettleReason = "close" | "error" | TacticalDrawAbortReason;
683
704
  * asynchronously).
684
705
  */
685
706
  interface EditSession {
707
+ /** Undo/redo for completed mutations within this live session. */
708
+ readonly history: SessionHistory;
686
709
  /** Immutable edit-start input. */
687
710
  readonly graphic: ControlMeasure;
688
711
  /** Live complete working graphic. Copy on read. */
@@ -820,6 +843,8 @@ interface EditSession {
820
843
  * observe partial state. See PRD #40 user-stories 23–24.
821
844
  */
822
845
  interface EditChangeEvent {
846
+ /** How this working-copy transition was initiated. */
847
+ readonly source: SessionChangeSource;
823
848
  /** Working state after the completed gesture. */
824
849
  readonly graphic: GraphicSnapshot<ControlMeasure>;
825
850
  /** State immediately before this gesture. */
@@ -840,6 +865,7 @@ interface EditChangeEvent {
840
865
  //#endregion
841
866
  //#region src/tactical-draw/graphic-edit-session.d.ts
842
867
  interface GraphicEditChangeEvent<G extends Graphic = Graphic> {
868
+ readonly source: SessionChangeSource;
843
869
  readonly graphic: GraphicSnapshot<G>;
844
870
  readonly previous: GraphicSnapshot<G>;
845
871
  readonly session: GraphicEditSession<G>;
@@ -849,6 +875,7 @@ interface GraphicEditChangeEvent<G extends Graphic = Graphic> {
849
875
  }
850
876
  /** Common selection/editing contract shared by control measures and point symbols. */
851
877
  interface GraphicEditSession<G extends Graphic = Graphic> {
878
+ readonly history: SessionHistory;
852
879
  /** Immutable edit-start input. */
853
880
  readonly graphic: G;
854
881
  /** Live working graphic. Copy on read. */
@@ -885,6 +912,8 @@ interface GraphicEditSession<G extends Graphic = Graphic> {
885
912
  * delivers, asynchronously).
886
913
  */
887
914
  interface TransformSession {
915
+ /** Undo/redo for completed gestures within the current membership baseline. */
916
+ readonly history: SessionHistory;
888
917
  /**
889
918
  * Live view of the current members' join-time input graphics — a fresh array
890
919
  * on each read, in current membership order. Grows/shrinks as [[setGraphics]]
@@ -926,6 +955,11 @@ interface TransformSession {
926
955
  * current box angle. Session-scoped state (`sizeAnchor`, box angle,
927
956
  * non-geometry dirty) is unchanged.
928
957
  *
958
+ * Calling this establishes a new session-history baseline: both undo and
959
+ * redo are cleared after retained members carry their working state
960
+ * forward. Membership changes and already-emitted exit snapshots are not
961
+ * undoable within the live session.
962
+ *
929
963
  * **Return value** — the committed exit snapshots of the members removed *by
930
964
  * this call*, built exactly like [[close]]'s snapshots (pixel size baked to
931
965
  * ground unless `sizeAnchor` is `"screen"`, then cloned and rendered); an
@@ -1036,6 +1070,8 @@ interface TransformSession {
1036
1070
  * `EditChangeEvent` for the identical emission contract this mirrors.
1037
1071
  */
1038
1072
  interface TransformChangeEvent {
1073
+ /** How this working-copy transition was initiated. */
1074
+ readonly source: SessionChangeSource;
1039
1075
  /** Working state after the completed gesture, index-aligned with `session.graphics` as of this emission. */
1040
1076
  readonly graphics: GraphicSnapshot<ControlMeasure>[];
1041
1077
  /** State immediately before this gesture, index-aligned with `session.graphics` as of this emission. */
@@ -1058,6 +1094,7 @@ interface TransformChangeEvent {
1058
1094
  //#endregion
1059
1095
  //#region src/tactical-draw/graphic-transform-session.d.ts
1060
1096
  interface GraphicTransformChangeEvent {
1097
+ readonly source: SessionChangeSource;
1061
1098
  readonly graphics: GraphicSnapshot[];
1062
1099
  readonly previous: GraphicSnapshot[];
1063
1100
  readonly session: GraphicTransformSession;
@@ -1066,6 +1103,7 @@ interface GraphicTransformChangeEvent {
1066
1103
  abort(): void;
1067
1104
  }
1068
1105
  interface GraphicTransformSession {
1106
+ readonly history: SessionHistory;
1069
1107
  readonly graphics: readonly Graphic[];
1070
1108
  readonly workingGraphics: readonly Graphic[];
1071
1109
  readonly dirty: boolean;
@@ -1146,6 +1184,55 @@ interface DrawSession {
1146
1184
  onSettled(handler: (reason: SettleReason) => void): () => void;
1147
1185
  }
1148
1186
  //#endregion
1187
+ //#region src/tactical-draw/snapping.d.ts
1188
+ type SnapInteraction = "draw" | "reshape" | "translate" | "label-drag";
1189
+ interface SnapCandidateRequest {
1190
+ coordinate: Position;
1191
+ pixel: PixelCoordinate;
1192
+ interaction: SnapInteraction;
1193
+ graphicId?: string;
1194
+ controlPointIndex?: number;
1195
+ originalEvent?: unknown;
1196
+ }
1197
+ interface SnapCandidate {
1198
+ id: string;
1199
+ coordinate: Position;
1200
+ kind: string;
1201
+ graphicId?: string;
1202
+ controlPointIndex?: number;
1203
+ priority?: number;
1204
+ }
1205
+ type SnapCandidateProvider = (request: SnapCandidateRequest) => readonly SnapCandidate[];
1206
+ interface SnapResult {
1207
+ coordinate: Position;
1208
+ pixel: PixelCoordinate;
1209
+ candidate: SnapCandidate;
1210
+ distancePixels: number;
1211
+ }
1212
+ interface GraphicGeometrySnappingOptions {
1213
+ /** Snap to the nearest point along generated linework and polygon boundaries. Default `true`. */
1214
+ edges?: boolean;
1215
+ /** Snap to generated geometry vertices. Default `false`. */
1216
+ vertices?: boolean;
1217
+ }
1218
+ interface SnappingOptions {
1219
+ enabled?: boolean;
1220
+ tolerancePixels?: number;
1221
+ sources?: {
1222
+ graphics?: boolean;
1223
+ /**
1224
+ * Snap to generated control-measure geometry. `true` enables edges with
1225
+ * vertices disabled; pass an object to select either candidate type.
1226
+ * Text/label point features and the active graphic are excluded.
1227
+ */
1228
+ graphicGeometry?: boolean | GraphicGeometrySnappingOptions;
1229
+ external?: SnapCandidateProvider;
1230
+ };
1231
+ bypass?: (originalEvent: unknown) => boolean;
1232
+ /** Called for each resolution attempt; `null` clears a host-rendered indicator. */
1233
+ onSnap?: (result: SnapResult | null) => void;
1234
+ }
1235
+ //#endregion
1149
1236
  //#region src/tactical-draw/point-symbol-draw-controller.d.ts
1150
1237
  type DrawPointSymbolDraft = Pick<PointSymbol, "kind" | "sidc"> & Partial<Omit<PointSymbol, "kind" | "sidc">>;
1151
1238
  //#endregion
@@ -1196,6 +1283,8 @@ interface TacticalDrawOptions {
1196
1283
  * the draw API itself are deferred (PRD #40 user-story-5 note).
1197
1284
  */
1198
1285
  generateId?: () => string;
1286
+ /** Optional engine-level snapping for draw and edit interactions. */
1287
+ snapping?: SnappingOptions;
1199
1288
  }
1200
1289
  interface EditOptions {
1201
1290
  signal?: AbortSignal;
@@ -1344,6 +1433,8 @@ declare class TacticalDraw {
1344
1433
  private readonly defaultInteractionStyle;
1345
1434
  private readonly generateId;
1346
1435
  private readonly pointSymbols;
1436
+ private snappingOptions;
1437
+ private readonly resolveSnap;
1347
1438
  private destroyed;
1348
1439
  private hasRendered;
1349
1440
  private readonly activeInteraction;
@@ -1394,7 +1485,12 @@ declare class TacticalDraw {
1394
1485
  * surrounding collection state.
1395
1486
  */
1396
1487
  private lastRenderedMeasures;
1488
+ private snapGeometryFeatures;
1397
1489
  constructor(adapter: MapAdapter, options?: TacticalDrawOptions);
1490
+ /** Replace snapping configuration without recreating the map adapter or façade. */
1491
+ setSnappingOptions(options?: SnappingOptions): void;
1492
+ /** Clear transient host affordances when snapping or its interaction ends. */
1493
+ private clearSnapResult;
1398
1494
  /**
1399
1495
  * Render `measures` into features and write them to the graphics layer.
1400
1496
  * Routes every measure through [[renderMeasureForAdapter]] so pixel sizing is
@@ -1782,4 +1878,4 @@ declare function nearestWorldCopyLng(lng: number, refLng: number): number;
1782
1878
  /** `nearestWorldCopyLng` applied to a lon/lat position. */
1783
1879
  declare function nearestWorldCopyPosition(position: Position, refLng: number): Position;
1784
1880
  //#endregion
1785
- export { BaseMapAdapter, type CombinedAbort, DEFAULT_LABEL_SIZE_PIXELS, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, type DrawMeasureDraft, type DrawOptions, type DrawPointSymbolDraft, type DrawSession, type EditChangeEvent, type EditManyOptions, type EditMode, type EditOptions, type EditPointerDriver, type EditSession, type FeatureOps, type Graphic, type GraphicEditChangeEvent, type GraphicEditSession, type GraphicRender, type GraphicSnapshot, type GraphicTransformChangeEvent, type GraphicTransformSession, type GuideStyle, type HandleStyle, type IconResource, type InteractionHit, type InteractionStyle, type LayerId, type MapAdapter, type MapCursor, type MapEvent, type MapEventHandler, type NativeEventEntry, type PickEvent, type PickHandler, type PickOptions, type PixelCoordinate, type PointSymbol, type PointSymbolAnchor, type PointSymbolCapability, PointSymbolCapabilityRequiredError, type PointSymbolColors, type PointSymbolFootprint, type PointSymbolGraphicModifiers, type PointSymbolRender, type PointSymbolSize, type PointSymbolStyle, type PointerCallback, PointerCallbackHub, type PointerEventInfo, type SettleReason, type SizeAnchor, type StyleOptions, TOUCH_HIT_TOLERANCE_PX, TacticalDraw, TacticalDrawAbortError, type TacticalDrawAbortReason, TacticalDrawDestroyedError, type TacticalDrawLayerOptions, type TacticalDrawOptions, type TextStyleOptions, type ToPixel, type TransformBox, type TransformChangeEvent, type TransformSession, bindAbortable, clonePointSymbol, coerceHandleStyle, combineWithHostSignal, getSizeAnchor, hitTestFeature, hitTestIconFeature, ignoreAbort, isPointSymbol, isTacticalDrawAbortError, nearestWorldCopyLng, nearestWorldCopyPosition, pickHitTolerance, pointSymbolFootprint, resolveGroundTextSizePixels, tryControlMeasureIdFromFeature };
1881
+ export { BaseMapAdapter, type CombinedAbort, DEFAULT_LABEL_SIZE_PIXELS, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, type DrawMeasureDraft, type DrawOptions, type DrawPointSymbolDraft, type DrawSession, type EditChangeEvent, type EditManyOptions, type EditMode, type EditOptions, type EditPointerDriver, type EditSession, type FeatureOps, type Graphic, type GraphicEditChangeEvent, type GraphicEditSession, type GraphicGeometrySnappingOptions, type GraphicRender, type GraphicSnapshot, type GraphicTransformChangeEvent, type GraphicTransformSession, type GuideStyle, type HandleStyle, type IconResource, type InteractionHit, type InteractionStyle, type LayerId, type MapAdapter, type MapCursor, type MapEvent, type MapEventHandler, type NativeEventEntry, type PickEvent, type PickHandler, type PickOptions, type PixelCoordinate, type PointSymbol, type PointSymbolAnchor, type PointSymbolCapability, PointSymbolCapabilityRequiredError, type PointSymbolColors, type PointSymbolFootprint, type PointSymbolGraphicModifiers, type PointSymbolRender, type PointSymbolSize, type PointSymbolStyle, type PointerCallback, PointerCallbackHub, type PointerEventInfo, type SessionChangeSource, type SessionHistory, type SessionHistoryState, type SettleReason, type SizeAnchor, type SnapCandidate, type SnapCandidateProvider, type SnapCandidateRequest, type SnapInteraction, type SnapResult, type SnappingOptions, type StyleOptions, TOUCH_HIT_TOLERANCE_PX, TacticalDraw, TacticalDrawAbortError, type TacticalDrawAbortReason, TacticalDrawDestroyedError, type TacticalDrawLayerOptions, type TacticalDrawOptions, type TextStyleOptions, type ToPixel, type TransformBox, type TransformChangeEvent, type TransformSession, bindAbortable, clonePointSymbol, coerceHandleStyle, combineWithHostSignal, getSizeAnchor, hitTestFeature, hitTestIconFeature, ignoreAbort, isPointSymbol, isTacticalDrawAbortError, nearestWorldCopyLng, nearestWorldCopyPosition, pickHitTolerance, pointSymbolFootprint, resolveGroundTextSizePixels, tryControlMeasureIdFromFeature };