@zwishing/emap 0.1.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 +38 -0
- package/LICENSE +373 -0
- package/README.md +294 -0
- package/SECURITY.md +56 -0
- package/dist/adapter/mapshaper-adapter.d.ts +282 -0
- package/dist/core/drag-pan-handler.d.ts +28 -0
- package/dist/core/events.d.ts +16 -0
- package/dist/core/interactions.d.ts +20 -0
- package/dist/core/mapshaper-worker-pool.d.ts +151 -0
- package/dist/core/tween.d.ts +26 -0
- package/dist/edit/commands/composite.d.ts +16 -0
- package/dist/edit/commands/dataset-replace.d.ts +43 -0
- package/dist/edit/commands/feature-affine.d.ts +72 -0
- package/dist/edit/commands/feature-create.d.ts +47 -0
- package/dist/edit/commands/feature-delete.d.ts +72 -0
- package/dist/edit/commands/feature-property-change.d.ts +34 -0
- package/dist/edit/commands/feature-translate.d.ts +55 -0
- package/dist/edit/commands/field-add.d.ts +24 -0
- package/dist/edit/commands/field-remove.d.ts +20 -0
- package/dist/edit/commands/field-rename.d.ts +19 -0
- package/dist/edit/commands/split-shared-arcs.d.ts +71 -0
- package/dist/edit/commands/vertex-delete.d.ts +26 -0
- package/dist/edit/commands/vertex-insert.d.ts +26 -0
- package/dist/edit/commands/vertex-move.d.ts +45 -0
- package/dist/edit/edit-command.d.ts +72 -0
- package/dist/edit/edit-history.d.ts +130 -0
- package/dist/edit/transaction.d.ts +59 -0
- package/dist/emap-worker.js +1 -0
- package/dist/emap.css +157 -0
- package/dist/emap.js +5 -0
- package/dist/emap.mjs +5 -0
- package/dist/geo/bounds.d.ts +18 -0
- package/dist/geo/crs-resolver.d.ts +35 -0
- package/dist/geo/projection.d.ts +28 -0
- package/dist/geo/transform.d.ts +19 -0
- package/dist/geo/viewport.d.ts +52 -0
- package/dist/index.d.ts +86 -0
- package/dist/map/attribute-ops.d.ts +61 -0
- package/dist/map/command-args.d.ts +28 -0
- package/dist/map/edit-sessions.d.ts +97 -0
- package/dist/map/edit-state-store.d.ts +41 -0
- package/dist/map/emap-host.d.ts +79 -0
- package/dist/map/feature-accessor.d.ts +43 -0
- package/dist/map/feature-query.d.ts +58 -0
- package/dist/map/highlight-manager.d.ts +17 -0
- package/dist/map/layer-registry.d.ts +33 -0
- package/dist/map/layer.d.ts +29 -0
- package/dist/map/map.d.ts +386 -0
- package/dist/map/mapshaper-ops.d.ts +56 -0
- package/dist/map/op-result.d.ts +46 -0
- package/dist/map/ops/_context.d.ts +41 -0
- package/dist/map/ops/_runner.d.ts +55 -0
- package/dist/map/ops/affine.d.ts +4 -0
- package/dist/map/ops/buffer.d.ts +4 -0
- package/dist/map/ops/check-geometry.d.ts +4 -0
- package/dist/map/ops/clean.d.ts +4 -0
- package/dist/map/ops/clip-erase.d.ts +5 -0
- package/dist/map/ops/data-fill.d.ts +4 -0
- package/dist/map/ops/dissolve.d.ts +20 -0
- package/dist/map/ops/divide.d.ts +4 -0
- package/dist/map/ops/drop-layer.d.ts +4 -0
- package/dist/map/ops/each-filter.d.ts +5 -0
- package/dist/map/ops/explode.d.ts +4 -0
- package/dist/map/ops/filter-fields.d.ts +4 -0
- package/dist/map/ops/filter-geom.d.ts +4 -0
- package/dist/map/ops/filter-islands.d.ts +4 -0
- package/dist/map/ops/filter-slivers.d.ts +4 -0
- package/dist/map/ops/innerlines.d.ts +4 -0
- package/dist/map/ops/intersection-points.d.ts +4 -0
- package/dist/map/ops/join-table.d.ts +4 -0
- package/dist/map/ops/lines.d.ts +4 -0
- package/dist/map/ops/merge-layers.d.ts +4 -0
- package/dist/map/ops/mosaic.d.ts +4 -0
- package/dist/map/ops/points.d.ts +4 -0
- package/dist/map/ops/polygons.d.ts +4 -0
- package/dist/map/ops/project.d.ts +4 -0
- package/dist/map/ops/rebuild-topology.d.ts +4 -0
- package/dist/map/ops/rename-fields.d.ts +4 -0
- package/dist/map/ops/rename-layer.d.ts +4 -0
- package/dist/map/ops/simplify.d.ts +4 -0
- package/dist/map/ops/snap.d.ts +4 -0
- package/dist/map/ops/sort-features.d.ts +4 -0
- package/dist/map/ops/split-layer.d.ts +4 -0
- package/dist/map/ops/union.d.ts +4 -0
- package/dist/map/ops/unique-features.d.ts +4 -0
- package/dist/map/selection.d.ts +73 -0
- package/dist/map/types.d.ts +1072 -0
- package/dist/map/worker-routing.d.ts +40 -0
- package/dist/mapshaper-vendor.js +1 -0
- package/dist/renderer/canvas-painter.d.ts +50 -0
- package/dist/renderer/edit-overlay-renderer.d.ts +22 -0
- package/dist/renderer/painter.d.ts +52 -0
- package/dist/shim.d.ts +1 -0
- package/dist/source/display-arcs.d.ts +49 -0
- package/dist/source/layer-utils.d.ts +12 -0
- package/dist/source/mapshaper-runner.d.ts +22 -0
- package/dist/source/source.d.ts +80 -0
- package/dist/source/topology-source.d.ts +145 -0
- package/dist/types/mapshaper-types.d.ts +182 -0
- package/dist/ui/basemap-control.d.ts +35 -0
- package/dist/ui/box-select-control.d.ts +67 -0
- package/dist/ui/control.d.ts +6 -0
- package/dist/ui/draw-feature-control.d.ts +82 -0
- package/dist/ui/edit-toolbar.d.ts +27 -0
- package/dist/ui/history-control.d.ts +29 -0
- package/dist/ui/lasso-select-control.d.ts +96 -0
- package/dist/ui/navigation-control.d.ts +16 -0
- package/dist/ui/simplify-control.d.ts +40 -0
- package/dist/ui/status-control.d.ts +23 -0
- package/dist/ui/vertex-edit-control.d.ts +111 -0
- package/dist/validation/builtin/topology.d.ts +19 -0
- package/dist/validation/registry.d.ts +23 -0
- package/dist/validation/validator.d.ts +47 -0
- package/package.json +90 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { EditCommand } from '../edit-command';
|
|
2
|
+
import { type MapshaperLayer, type PathShape } from '../../types/mapshaper-types';
|
|
3
|
+
import type { TopologySource } from '../../source/source';
|
|
4
|
+
/**
|
|
5
|
+
* One feature whose shape was rewritten to point at duplicated arc ids.
|
|
6
|
+
* Captured at command-construction time so undo restores it exactly.
|
|
7
|
+
*/
|
|
8
|
+
export interface SplitSharedArcsRemap {
|
|
9
|
+
layer: MapshaperLayer;
|
|
10
|
+
featureId: number;
|
|
11
|
+
/** Snapshot of `layer.shapes[featureId]` BEFORE the split (deep cloned). */
|
|
12
|
+
originalShape: PathShape;
|
|
13
|
+
/** The post-split shape that uses the new arc ids. */
|
|
14
|
+
newShape: PathShape;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Vertex data for a single arc to be appended onto the source's `ArcCollection`.
|
|
18
|
+
* Slices of the original arc's xx/yy (and zz when simplification is in play).
|
|
19
|
+
*/
|
|
20
|
+
export interface AppendedArc {
|
|
21
|
+
xx: Float64Array;
|
|
22
|
+
yy: Float64Array;
|
|
23
|
+
zz?: Float64Array | null;
|
|
24
|
+
}
|
|
25
|
+
export interface SplitSharedArcsCommandOptions {
|
|
26
|
+
source: TopologySource;
|
|
27
|
+
/** Pre-computed shape rewrites — one entry per affected feature. */
|
|
28
|
+
remaps: SplitSharedArcsRemap[];
|
|
29
|
+
/**
|
|
30
|
+
* Vertex data for every arc to append, in the order the new arc ids were
|
|
31
|
+
* assigned (first appended arc id = `originalArcCount`, second = +1, …).
|
|
32
|
+
*/
|
|
33
|
+
appendedArcs: AppendedArc[];
|
|
34
|
+
/** Arc count BEFORE the split. Used by undo to truncate. */
|
|
35
|
+
originalArcCount: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Duplicate "shared" arcs (arcs referenced by both selected and unselected
|
|
39
|
+
* features) so a subsequent translate / rotate / scale on the selection no
|
|
40
|
+
* longer drags unselected neighbours along by their shared boundary.
|
|
41
|
+
*
|
|
42
|
+
* Topology stays valid: unselected features keep their original arc ids,
|
|
43
|
+
* selected features get rewritten to point at the duplicates.
|
|
44
|
+
*
|
|
45
|
+
* Undo restores every rewritten shape to its captured pre-split state and
|
|
46
|
+
* truncates the appended arcs off the `ArcCollection`. Redo re-appends and
|
|
47
|
+
* re-rewrites. Both directions go through `arcs.updateVertexData` so the
|
|
48
|
+
* collection's bounds and indices stay coherent.
|
|
49
|
+
*/
|
|
50
|
+
export declare class SplitSharedArcsCommand implements EditCommand {
|
|
51
|
+
private readonly opts;
|
|
52
|
+
readonly label: string;
|
|
53
|
+
constructor(opts: SplitSharedArcsCommandOptions);
|
|
54
|
+
do(): void;
|
|
55
|
+
undo(): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Walks the dataset's path-feature shapes once, finds arcs referenced by
|
|
59
|
+
* BOTH selected and unselected features, and returns the data needed to
|
|
60
|
+
* construct a {@link SplitSharedArcsCommand}. Returns `null` (no command
|
|
61
|
+
* needed) when the selection has no boundary shared with anything outside.
|
|
62
|
+
*
|
|
63
|
+
* `selected` lists `(layer, featureId)` pairs in the active selection; the
|
|
64
|
+
* caller is responsible for restricting these to one source. `allLayers`
|
|
65
|
+
* is the full layer list of that source so we can find unselected
|
|
66
|
+
* references.
|
|
67
|
+
*/
|
|
68
|
+
export declare function planSharedArcSplit(source: TopologySource, selected: Array<{
|
|
69
|
+
layer: MapshaperLayer;
|
|
70
|
+
featureId: number;
|
|
71
|
+
}>, allLayers: MapshaperLayer[]): SplitSharedArcsCommandOptions | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EditCommand } from '../edit-command';
|
|
2
|
+
import { type ArcCollection } from '../../types/mapshaper-types';
|
|
3
|
+
import type { TopologySource } from '../../source/source';
|
|
4
|
+
import { type MapshaperAdapter } from '../../adapter/mapshaper-adapter';
|
|
5
|
+
export interface VertexDeleteCommandOptions {
|
|
6
|
+
/** Global vertex index of the deleted vertex (recorded before deletion). */
|
|
7
|
+
vertexIndex: number;
|
|
8
|
+
/** Coordinate of the deleted vertex (recorded before deletion). */
|
|
9
|
+
coords: [number, number];
|
|
10
|
+
arcs: ArcCollection;
|
|
11
|
+
source: TopologySource;
|
|
12
|
+
/** Adapter override for tests; defaults to the process-wide singleton. */
|
|
13
|
+
adapter?: MapshaperAdapter;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reverse a vertex deletion by re-inserting at the same global index. Pair
|
|
17
|
+
* with `VertexInsertCommand`'s symmetry: this command's `do()` performs the
|
|
18
|
+
* deletion (called only on redo), `undo()` restores the vertex.
|
|
19
|
+
*/
|
|
20
|
+
export declare class VertexDeleteCommand implements EditCommand {
|
|
21
|
+
private readonly opts;
|
|
22
|
+
readonly label = "Delete vertex";
|
|
23
|
+
constructor(opts: VertexDeleteCommandOptions);
|
|
24
|
+
do(): void;
|
|
25
|
+
undo(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EditCommand } from '../edit-command';
|
|
2
|
+
import { type ArcCollection } from '../../types/mapshaper-types';
|
|
3
|
+
import type { TopologySource } from '../../source/source';
|
|
4
|
+
import { type MapshaperAdapter } from '../../adapter/mapshaper-adapter';
|
|
5
|
+
export interface VertexInsertCommandOptions {
|
|
6
|
+
/** Global vertex index where the new vertex was inserted. */
|
|
7
|
+
insertionId: number;
|
|
8
|
+
/** Coordinate of the inserted vertex. */
|
|
9
|
+
point: [number, number];
|
|
10
|
+
arcs: ArcCollection;
|
|
11
|
+
source: TopologySource;
|
|
12
|
+
/** Adapter override for tests; defaults to the process-wide singleton. */
|
|
13
|
+
adapter?: MapshaperAdapter;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reverse a vertex insertion by deleting at the same global index.
|
|
17
|
+
* mapshaper's deleteVertex shifts subsequent indices back into place via
|
|
18
|
+
* updateVertexData, which also rebuilds arc bounds.
|
|
19
|
+
*/
|
|
20
|
+
export declare class VertexInsertCommand implements EditCommand {
|
|
21
|
+
private readonly opts;
|
|
22
|
+
readonly label = "Insert vertex";
|
|
23
|
+
constructor(opts: VertexInsertCommandOptions);
|
|
24
|
+
do(): void;
|
|
25
|
+
undo(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { EditCommand } from '../edit-command';
|
|
2
|
+
import { type ArcCollection } from '../../types/mapshaper-types';
|
|
3
|
+
import type { TopologySource } from '../../source/source';
|
|
4
|
+
import { type MapshaperAdapter } from '../../adapter/mapshaper-adapter';
|
|
5
|
+
export interface VertexMoveCommandOptions {
|
|
6
|
+
/**
|
|
7
|
+
* IDs of every vertex at the moved topological node. mapshaper's
|
|
8
|
+
* snapVerticesToPoint moves them all in lock-step, preserving topology.
|
|
9
|
+
*/
|
|
10
|
+
vertexIds: number[];
|
|
11
|
+
/** Original coordinate of the topological node before the drag. */
|
|
12
|
+
from: [number, number];
|
|
13
|
+
/** Final coordinate after the drag. */
|
|
14
|
+
to: [number, number];
|
|
15
|
+
arcs: ArcCollection;
|
|
16
|
+
source: TopologySource;
|
|
17
|
+
/** Adapter override for tests; defaults to the process-wide singleton. */
|
|
18
|
+
adapter?: MapshaperAdapter;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reverse a vertex drag (including topologically shared vertices) by
|
|
22
|
+
* re-running mapshaper's snapVertices with the captured `from` /
|
|
23
|
+
* `to` coordinates. The function updates each affected arc's bounds
|
|
24
|
+
* internally, so no extra bookkeeping is required.
|
|
25
|
+
*
|
|
26
|
+
* Mergeable: consecutive moves of the SAME topological node within
|
|
27
|
+
* {@link MERGE_WINDOW_MS} collapse into one undo entry. The merged
|
|
28
|
+
* command keeps the original `from` (the very first pre-drag snapshot)
|
|
29
|
+
* and adopts the latest `to`, so a single undo reverses an entire
|
|
30
|
+
* arrow-key nudge sequence.
|
|
31
|
+
*/
|
|
32
|
+
export declare class VertexMoveCommand implements EditCommand {
|
|
33
|
+
private readonly opts;
|
|
34
|
+
readonly label = "Move vertex";
|
|
35
|
+
/**
|
|
36
|
+
* Wall-clock at construction time; refreshed by `merge()`. Used by
|
|
37
|
+
* `mergeable()` to bound coalescence to a short interactive window.
|
|
38
|
+
*/
|
|
39
|
+
private _mergeAt;
|
|
40
|
+
constructor(opts: VertexMoveCommandOptions);
|
|
41
|
+
do(): void;
|
|
42
|
+
undo(): void;
|
|
43
|
+
mergeable(prev: EditCommand): boolean;
|
|
44
|
+
merge(next: EditCommand): void;
|
|
45
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An undoable edit memento. NOT to be confused with a mapshaper
|
|
3
|
+
* "operation" (`emap.ops.*`) — those wrap a CLI / parsed-command run
|
|
4
|
+
* against a dataset and internally push a `DatasetReplaceCommand` to
|
|
5
|
+
* history. `EditCommand` is the lower-level abstraction that lives on
|
|
6
|
+
* the undo / redo stack.
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle (memento pattern):
|
|
9
|
+
* - The caller mutates the data, then constructs the command and calls
|
|
10
|
+
* `Emap.pushCommand(cmd)`. The command stores any state needed to
|
|
11
|
+
* reverse the change. `pushCommand` does NOT call `do()`.
|
|
12
|
+
* - On undo, `EditHistory` invokes `cmd.undo()` to restore the prior state.
|
|
13
|
+
* - On redo, `EditHistory` invokes `cmd.do()` to re-apply the change.
|
|
14
|
+
*
|
|
15
|
+
* Implementations must capture enough state in their constructor that
|
|
16
|
+
* both `do()` and `undo()` can run multiple times without diverging.
|
|
17
|
+
*/
|
|
18
|
+
export interface EditCommand {
|
|
19
|
+
/** Re-apply the operation. Called by `EditHistory.redo()`. */
|
|
20
|
+
do(): void;
|
|
21
|
+
/** Reverse the operation. Called by `EditHistory.undo()`. */
|
|
22
|
+
undo(): void;
|
|
23
|
+
/** Optional human-readable label, surfaced in `historychange` events. */
|
|
24
|
+
readonly label?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Optional merge predicate, evaluated by `EditHistory.push` against the
|
|
27
|
+
* current top of the undo stack. When `true`, the new command is NOT
|
|
28
|
+
* pushed; instead `prev.merge(this)` is invoked and `prev` absorbs the
|
|
29
|
+
* follow-up's effect. Used to collapse rapid repeated edits — successive
|
|
30
|
+
* arrow-key vertex nudges, mouse-drag tween steps, etc. — into one
|
|
31
|
+
* undoable entry.
|
|
32
|
+
*
|
|
33
|
+
* Implementations should be cheap (O(N) at worst over command-internal
|
|
34
|
+
* fields) and stable (no I/O, no side effects).
|
|
35
|
+
*/
|
|
36
|
+
mergeable?(prev: EditCommand): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Companion to {@link mergeable}: invoked by `EditHistory.push` only
|
|
39
|
+
* when `next.mergeable(this)` returned `true`. Mutates this command in
|
|
40
|
+
* place to absorb `next`'s effect; `next` is discarded after the call
|
|
41
|
+
* returns. Implementations typically refresh a "newest-coordinate"
|
|
42
|
+
* field while preserving the oldest "starting" capture so undo still
|
|
43
|
+
* reverses the cumulative work.
|
|
44
|
+
*/
|
|
45
|
+
merge?(next: EditCommand): void;
|
|
46
|
+
/**
|
|
47
|
+
* Static marker: when `true`, pushing this command tells `EditHistory`
|
|
48
|
+
* to flag every command currently in the undo stack as `stale`. Used by
|
|
49
|
+
* `DatasetReplaceCommand` (clip / dissolve / buffer / …) — those swap
|
|
50
|
+
* out the live dataset object, so commands captured against the OLD
|
|
51
|
+
* dataset can no longer mutate the right reference.
|
|
52
|
+
*
|
|
53
|
+
* The command itself is NOT stale at push time; only the entries below
|
|
54
|
+
* it become stale.
|
|
55
|
+
*/
|
|
56
|
+
readonly invalidatesPriorCommands?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Set by `EditHistory.push` when an `invalidatesPriorCommands` command
|
|
59
|
+
* is pushed above this entry. Stays `true` for the rest of this entry's
|
|
60
|
+
* lifetime — even after the invalidating command is undone, the captured
|
|
61
|
+
* dataset reference is still orphaned (the in-place undo of a
|
|
62
|
+
* dataset-swap restores a deep copy that has a different identity from
|
|
63
|
+
* the object this command was constructed against).
|
|
64
|
+
*
|
|
65
|
+
* `EditHistory.undo` skips stale commands: pops them onto the redo
|
|
66
|
+
* stack without invoking `undo()` and surfaces the skip via the
|
|
67
|
+
* `HistoryStateSnapshot.skippedStale` flag on the next change event.
|
|
68
|
+
* UIs should treat the boundary as a "history barrier" — visible in
|
|
69
|
+
* the list, but un-replayable.
|
|
70
|
+
*/
|
|
71
|
+
stale?: boolean;
|
|
72
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { EditCommand } from './edit-command';
|
|
2
|
+
export interface EditHistoryOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Maximum number of commands retained in the undo stack. Older commands
|
|
5
|
+
* are evicted when this is exceeded. Defaults to 200.
|
|
6
|
+
*/
|
|
7
|
+
maxSize?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface HistoryStateSnapshot {
|
|
10
|
+
canUndo: boolean;
|
|
11
|
+
canRedo: boolean;
|
|
12
|
+
/** Label of the most recent command at the top of the undo stack, if any. */
|
|
13
|
+
label?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Number of commands currently in the undo stack flagged as
|
|
16
|
+
* `stale`. Non-zero after any `DatasetReplaceCommand` lands; the UI
|
|
17
|
+
* can render a divider in the history list at the boundary.
|
|
18
|
+
*/
|
|
19
|
+
staleCount: number;
|
|
20
|
+
/**
|
|
21
|
+
* `true` iff the command that {@link undo} would next visit is stale
|
|
22
|
+
* (i.e. cannot be reversed in place). Provides a single boolean for UI
|
|
23
|
+
* tooltips like "the next undo crosses a topology rebuild — earlier
|
|
24
|
+
* edits cannot be replayed".
|
|
25
|
+
*/
|
|
26
|
+
topIsStale: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* `true` only on the {@link HistoryStateSnapshot} produced immediately
|
|
29
|
+
* after `undo()` skipped a stale command. Resets on the next event.
|
|
30
|
+
* Used by UI to surface a one-shot toast.
|
|
31
|
+
*/
|
|
32
|
+
skippedStale?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Linear undo / redo history.
|
|
36
|
+
*
|
|
37
|
+
* - `push(cmd)` records `cmd` and clears any pending redo entries. It does NOT
|
|
38
|
+
* invoke `cmd.do()` — the caller is expected to have already performed the
|
|
39
|
+
* work; the command captures what was changed so it can be reversed.
|
|
40
|
+
* - `undo()` invokes `cmd.undo()` and moves `cmd` from the undo stack to the
|
|
41
|
+
* redo stack.
|
|
42
|
+
* - `redo()` invokes `cmd.do()` and moves `cmd` back onto the undo stack.
|
|
43
|
+
*/
|
|
44
|
+
export declare class EditHistory {
|
|
45
|
+
private _undoStack;
|
|
46
|
+
private _redoStack;
|
|
47
|
+
private _maxSize;
|
|
48
|
+
/**
|
|
49
|
+
* `true` for exactly one snapshot following an `undo()` / `redo()` call
|
|
50
|
+
* that traversed a stale command. Surfaced via `snapshot().skippedStale`
|
|
51
|
+
* so the host can fire one-shot UI feedback. Reset on the next
|
|
52
|
+
* non-stale traversal or on `clear()`.
|
|
53
|
+
*/
|
|
54
|
+
private _lastSkippedStale;
|
|
55
|
+
/**
|
|
56
|
+
* Active capture buffer for the {@link beginCapture} protocol used by
|
|
57
|
+
* `Transaction`. While non-null, every `push(cmd)` diverts into this
|
|
58
|
+
* buffer instead of mutating the undo stack. `endCapture()` returns
|
|
59
|
+
* the collected commands and unsets this. Nested captures are not
|
|
60
|
+
* supported — a second `beginCapture()` while one is active throws.
|
|
61
|
+
*/
|
|
62
|
+
private _captureBuffer;
|
|
63
|
+
constructor(options?: EditHistoryOptions);
|
|
64
|
+
/**
|
|
65
|
+
* Push a new command, evicting the oldest entry if at capacity.
|
|
66
|
+
*
|
|
67
|
+
* Merge fast-path: if the top of the undo stack defines a `merge`
|
|
68
|
+
* companion AND the incoming command's `mergeable(top)` returns true,
|
|
69
|
+
* the top absorbs the incoming command and nothing is pushed. The
|
|
70
|
+
* redo stack is still cleared (the new edit invalidates forward
|
|
71
|
+
* replay). Used by `VertexMoveCommand` / `FeatureTranslateCommand`
|
|
72
|
+
* to coalesce rapid drag / nudge sequences into one undo step.
|
|
73
|
+
*
|
|
74
|
+
* If `cmd.invalidatesPriorCommands` is `true` (DatasetReplaceCommand),
|
|
75
|
+
* every entry currently in the undo stack is flagged `stale = true`
|
|
76
|
+
* before this command is pushed. The command itself is NOT marked
|
|
77
|
+
* stale — it remains undoable as the boundary itself.
|
|
78
|
+
*/
|
|
79
|
+
push(cmd: EditCommand): void;
|
|
80
|
+
/**
|
|
81
|
+
* Begin a transaction-style capture session. While active, `push(cmd)`
|
|
82
|
+
* routes every command into an internal buffer instead of mutating the
|
|
83
|
+
* undo stack, so a multi-step transaction can batch its child commands
|
|
84
|
+
* and present them as a single undo entry on commit.
|
|
85
|
+
*
|
|
86
|
+
* Throws if a capture is already in progress — nested transactions are
|
|
87
|
+
* not supported. Pair with {@link endCapture}; for safety, transaction
|
|
88
|
+
* code should call `endCapture()` from a `finally`-style path so an
|
|
89
|
+
* error in the middle doesn't leave the history stuck in capture mode.
|
|
90
|
+
*/
|
|
91
|
+
beginCapture(): void;
|
|
92
|
+
/**
|
|
93
|
+
* End the active capture session and return the collected commands in
|
|
94
|
+
* push order. No-op (returns `[]`) if no capture is active. After this
|
|
95
|
+
* call, subsequent `push()` calls resume normal undo-stack behaviour.
|
|
96
|
+
*/
|
|
97
|
+
endCapture(): EditCommand[];
|
|
98
|
+
/** True iff a {@link beginCapture} session is currently active. */
|
|
99
|
+
isCapturing(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Pop the top command, invoke its `undo()`, and move it to the redo stack.
|
|
102
|
+
* Returns the executed command, or `null` if there was nothing to undo.
|
|
103
|
+
*
|
|
104
|
+
* **Stale handling**: when the top command's `stale` flag is `true`, the
|
|
105
|
+
* command is moved to the redo stack WITHOUT invoking `undo()` (its
|
|
106
|
+
* captured references point at an object the live dataset no longer
|
|
107
|
+
* uses; running it would write to the wrong place). The skip is
|
|
108
|
+
* reflected in the next {@link snapshot} via `skippedStale: true`.
|
|
109
|
+
* `undo()` still returns the moved command so callers can detect that
|
|
110
|
+
* a step was traversed.
|
|
111
|
+
*/
|
|
112
|
+
undo(): EditCommand | null;
|
|
113
|
+
/**
|
|
114
|
+
* Pop the top redo entry, invoke its `do()`, and move it back to the undo
|
|
115
|
+
* stack. Returns the executed command, or `null` if there was nothing.
|
|
116
|
+
*
|
|
117
|
+
* Stale commands on the redo stack get the same skip semantics as
|
|
118
|
+
* `undo()` — the redo is a no-op transition because the captured refs
|
|
119
|
+
* are still orphaned even though the entry is moving forward in time.
|
|
120
|
+
*/
|
|
121
|
+
redo(): EditCommand | null;
|
|
122
|
+
/** Empty both stacks. */
|
|
123
|
+
clear(): void;
|
|
124
|
+
canUndo(): boolean;
|
|
125
|
+
canRedo(): boolean;
|
|
126
|
+
/** Number of commands currently in the undo stack. */
|
|
127
|
+
size(): number;
|
|
128
|
+
/** Snapshot of the public history state, suitable for event payloads. */
|
|
129
|
+
snapshot(): HistoryStateSnapshot;
|
|
130
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type OpResult } from '../map/op-result';
|
|
2
|
+
import type { EmapHostInternal } from '../map/emap-host';
|
|
3
|
+
import type { MapshaperDataset } from '../types/mapshaper-types';
|
|
4
|
+
/**
|
|
5
|
+
* Internal-only view of a Transaction used by the runner. Public callers
|
|
6
|
+
* receive the wider {@link Transaction} class.
|
|
7
|
+
*/
|
|
8
|
+
export interface TransactionInternal {
|
|
9
|
+
/** Record the first-touch dataset snapshot for `sourceId`, if not already captured. */
|
|
10
|
+
_captureFirstTouch(sourceId: string, before: MapshaperDataset): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Atomic multi-step edit unit. Stages every command pushed during its
|
|
14
|
+
* lifetime; `commit()` collapses them into one history entry and
|
|
15
|
+
* `rollback()` restores the dataset + selection to the state at
|
|
16
|
+
* construction.
|
|
17
|
+
*
|
|
18
|
+
* Construct via `Emap.beginTransaction()` — direct construction is
|
|
19
|
+
* supported for tests but bypasses the Emap host wiring.
|
|
20
|
+
*/
|
|
21
|
+
export declare class Transaction implements TransactionInternal {
|
|
22
|
+
private readonly _host;
|
|
23
|
+
private readonly _datasetSnapshots;
|
|
24
|
+
private readonly _selectionSnapshot;
|
|
25
|
+
private _closed;
|
|
26
|
+
constructor(host: EmapHostInternal);
|
|
27
|
+
/**
|
|
28
|
+
* Internal — invoked by the runner the first time each source's
|
|
29
|
+
* dataset is about to be swapped within this transaction. The dataset
|
|
30
|
+
* passed here is the pre-op snapshot the runner already produced for
|
|
31
|
+
* its own undo bookkeeping; we keep a reference so rollback can
|
|
32
|
+
* restore the source to its true pre-transaction state regardless of
|
|
33
|
+
* how many ops touched it later.
|
|
34
|
+
*/
|
|
35
|
+
_captureFirstTouch(sourceId: string, before: MapshaperDataset): void;
|
|
36
|
+
/** True iff `commit()` / `rollback()` has not been called yet. */
|
|
37
|
+
get isOpen(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Drain the capture buffer, push a single `CompositeCommand` wrapping
|
|
40
|
+
* every staged child to the undo stack (no-op if nothing was staged),
|
|
41
|
+
* and close the transaction. Returns `OK` on success or
|
|
42
|
+
* `Err.hostRemoved()` if the host was torn down mid-transaction.
|
|
43
|
+
*
|
|
44
|
+
* The composite's children are NOT re-executed — their effects were
|
|
45
|
+
* applied as each child was originally captured.
|
|
46
|
+
*/
|
|
47
|
+
commit(label: string): Promise<OpResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Discard every staged command, restore each touched source's dataset
|
|
50
|
+
* to its pre-transaction snapshot, restore the selection, and close
|
|
51
|
+
* the transaction. Fires `selectionchange`, `historychange`, and
|
|
52
|
+
* schedules a render.
|
|
53
|
+
*
|
|
54
|
+
* Idempotent only in the sense that calling it twice throws — once
|
|
55
|
+
* the transaction is closed it cannot be re-used.
|
|
56
|
+
*/
|
|
57
|
+
rollback(): void;
|
|
58
|
+
private _end;
|
|
59
|
+
}
|