|
@@ -1,6 +1,6 @@
|
|
|
import { Root } from "../../core";
|
|
|
import { History } from "stateshot";
|
|
|
-import { debounce, inRevise } from "../../shared";
|
|
|
+import { inRevise } from "../../shared";
|
|
|
import { EditModeChange } from "../../core/base/entity-root-server";
|
|
|
import mitt from "mitt";
|
|
|
|
|
@@ -9,84 +9,117 @@ export type HistoryState = {
|
|
|
hasRedo: boolean;
|
|
|
};
|
|
|
|
|
|
+export type StateCallback = (state: HistoryState) => void;
|
|
|
+
|
|
|
export class SingleHistory<T = any> {
|
|
|
- props: HistoryPluginProps<T> & {
|
|
|
- stateChange: (state: HistoryState) => void;
|
|
|
- };
|
|
|
- tree: Root;
|
|
|
+ stateChange: StateCallback;
|
|
|
history: History<{ data: T }>;
|
|
|
state = {
|
|
|
hasUndo: false,
|
|
|
hasRedo: false,
|
|
|
};
|
|
|
|
|
|
- constructor(
|
|
|
- props: HistoryPluginProps<T> & {
|
|
|
- stateChange: (state: HistoryState) => void;
|
|
|
- }
|
|
|
- ) {
|
|
|
- this.props = props;
|
|
|
+ constructor(stateChange: StateCallback) {
|
|
|
+ this.stateChange = stateChange;
|
|
|
+ this.history = new History();
|
|
|
}
|
|
|
|
|
|
+ private __prevState: HistoryState;
|
|
|
private syncState() {
|
|
|
if (!this.history) return;
|
|
|
this.state.hasRedo = this.history.hasRedo;
|
|
|
this.state.hasUndo = this.history.hasUndo;
|
|
|
- this.props.stateChange({ ...this.state });
|
|
|
+
|
|
|
+ if (inRevise(this.state, this.__prevState)) {
|
|
|
+ this.stateChange({ ...this.state });
|
|
|
+ this.__prevState = { ...this.state };
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- setTree(tree: Root | null) {
|
|
|
- this.tree = tree;
|
|
|
- this.history = new History();
|
|
|
+ get data() {
|
|
|
+ return this.history.get()?.data;
|
|
|
}
|
|
|
|
|
|
- undo() {
|
|
|
+ undo(): T {
|
|
|
if (this.history.hasUndo) {
|
|
|
this.history.undo();
|
|
|
- this.props.set(this.get());
|
|
|
this.syncState();
|
|
|
}
|
|
|
+ return this.data;
|
|
|
}
|
|
|
|
|
|
- get() {
|
|
|
- return this.history.get()?.data;
|
|
|
- }
|
|
|
-
|
|
|
- redo() {
|
|
|
+ redo(): T {
|
|
|
if (this.history.hasRedo) {
|
|
|
this.history.redo();
|
|
|
- this.props.set(this.history.get().data);
|
|
|
this.syncState();
|
|
|
}
|
|
|
+ return this.data;
|
|
|
}
|
|
|
|
|
|
push(data: T) {
|
|
|
- // if (inRevise(data, this.get())) {
|
|
|
this.history.pushSync({ data });
|
|
|
this.syncState();
|
|
|
- // }
|
|
|
}
|
|
|
|
|
|
clear() {
|
|
|
this.history.reset();
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- unTimelyPush: () => void;
|
|
|
- timelyPush() {
|
|
|
- this.unTimelyPush && this.unTimelyPush();
|
|
|
- const changeHandler = debounce((data: EditModeChange) => {
|
|
|
- this.push(this.props.get(data));
|
|
|
- }, 16);
|
|
|
- this.tree.bus.on("entityChange", changeHandler);
|
|
|
- this.unTimelyPush = () => {
|
|
|
- this.tree.bus.off("entityChange", changeHandler);
|
|
|
- this.unTimelyPush = null;
|
|
|
- };
|
|
|
+export class MergeHistory<T> {
|
|
|
+ private stateChange: StateCallback;
|
|
|
+ private historyStack: SingleHistory<T>[] = [];
|
|
|
+
|
|
|
+ constructor(stateChange: StateCallback) {
|
|
|
+ this.stateChange = stateChange;
|
|
|
+ }
|
|
|
+
|
|
|
+ get current() {
|
|
|
+ return this.historyStack[this.historyStack.length - 1];
|
|
|
+ }
|
|
|
+
|
|
|
+ private __prevState: HistoryState;
|
|
|
+ branch() {
|
|
|
+ const single = new SingleHistory((state) => {
|
|
|
+ if (inRevise(this.__prevState, state)) {
|
|
|
+ this.stateChange({ ...state });
|
|
|
+ this.__prevState = { ...state };
|
|
|
+ }
|
|
|
+ });
|
|
|
+ console.log(this);
|
|
|
+ this.historyStack.push(single);
|
|
|
+ }
|
|
|
+
|
|
|
+ merge() {
|
|
|
+ const lastStack = this.historyStack.pop();
|
|
|
+ if (lastStack.state.hasUndo) {
|
|
|
+ this.current.push(lastStack.data);
|
|
|
+ }
|
|
|
+ lastStack.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ undo() {
|
|
|
+ return this.current.undo();
|
|
|
+ }
|
|
|
+
|
|
|
+ redo() {
|
|
|
+ return this.current.redo();
|
|
|
+ }
|
|
|
+
|
|
|
+ push(data: T) {
|
|
|
+ return this.current.push(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ clear() {
|
|
|
+ return this.current.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ get data() {
|
|
|
+ return this.current.data;
|
|
|
}
|
|
|
|
|
|
destory() {
|
|
|
- this.unTimelyPush && this.unTimelyPush();
|
|
|
- this.clear();
|
|
|
+ this.historyStack.length = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -97,74 +130,59 @@ export type HistoryPluginProps<T> = {
|
|
|
};
|
|
|
|
|
|
export class HistoryPlugin<T = any> {
|
|
|
- private historyStack: SingleHistory<T>[] = [];
|
|
|
- bus = mitt<{ stateChange: HistoryState }>();
|
|
|
+ bus = mitt<{ stateChange: HistoryState; recovery: boolean }>();
|
|
|
props: HistoryPluginProps<T>;
|
|
|
tree: Root;
|
|
|
+ history: MergeHistory<T>;
|
|
|
hasRecovery = false;
|
|
|
|
|
|
constructor(props: HistoryPluginProps<T>) {
|
|
|
this.props = props;
|
|
|
}
|
|
|
|
|
|
- get current() {
|
|
|
- return this.historyStack[this.historyStack.length - 1];
|
|
|
- }
|
|
|
-
|
|
|
+ private __changeTreeRelease: () => void;
|
|
|
setTree(tree: Root) {
|
|
|
if (tree === this.tree) return;
|
|
|
- tree && tree.setHistory(this);
|
|
|
- this.tree && this.tree.setHistory(null);
|
|
|
-
|
|
|
- this.tree = tree;
|
|
|
- this.historyStack.length = 0;
|
|
|
- this.pushSingle();
|
|
|
- this.timelyPush();
|
|
|
- this.current.push(this.props.init());
|
|
|
- }
|
|
|
-
|
|
|
- private __prevState: HistoryState;
|
|
|
- private pushSingle() {
|
|
|
- const single = new SingleHistory({
|
|
|
- ...this.props,
|
|
|
- stateChange: (state) => {
|
|
|
- if (inRevise(this.__prevState, state)) {
|
|
|
- this.bus.emit("stateChange", state);
|
|
|
- this.__prevState = state;
|
|
|
- }
|
|
|
- },
|
|
|
- });
|
|
|
- single.setTree(this.tree);
|
|
|
- if (this.current) {
|
|
|
- this.current.unTimelyPush();
|
|
|
- single.push(this.current.get());
|
|
|
+ if (this.tree) {
|
|
|
+ this.__changeTreeRelease();
|
|
|
}
|
|
|
- single.timelyPush();
|
|
|
- this.historyStack.push(single);
|
|
|
- }
|
|
|
-
|
|
|
- private popSingle() {
|
|
|
- const lastStack = this.historyStack.pop();
|
|
|
- if (lastStack.state.hasUndo) {
|
|
|
- this.current.push(lastStack.get());
|
|
|
- console.log("合并历史");
|
|
|
+ if (tree) {
|
|
|
+ if (!this.history) {
|
|
|
+ this.history = new MergeHistory<T>((state) => {
|
|
|
+ this.bus.emit("stateChange", state);
|
|
|
+ });
|
|
|
+ this.history.branch();
|
|
|
+ this.history.push(this.props.init());
|
|
|
+ }
|
|
|
+
|
|
|
+ tree.setHistory(this);
|
|
|
+ this.tree = tree;
|
|
|
+ this.timelyPush();
|
|
|
+ this.__changeTreeRelease = () => {
|
|
|
+ this.unTimelyPush();
|
|
|
+ tree.setHistory(null);
|
|
|
+ };
|
|
|
}
|
|
|
- lastStack.destory();
|
|
|
- this.current.timelyPush();
|
|
|
}
|
|
|
|
|
|
private unTimelyPush: () => void;
|
|
|
private timelyPush() {
|
|
|
this.unTimelyPush && this.unTimelyPush();
|
|
|
|
|
|
- const beforeHandler = this.pushSingle.bind(this);
|
|
|
- const afterHandler = this.popSingle.bind(this);
|
|
|
+ const beforeHandler = this.history.branch.bind(this);
|
|
|
+ const afterHandler = this.history.merge.bind(this);
|
|
|
+ const changeHandler = (change: EditModeChange) => {
|
|
|
+ console.log("???");
|
|
|
+ this.history.push(this.props.get(change));
|
|
|
+ };
|
|
|
|
|
|
this.tree.bus.on("entityChangeBefore", beforeHandler);
|
|
|
+ this.tree.bus.on("entityChange", changeHandler);
|
|
|
this.tree.bus.on("entityChangeAfter", afterHandler);
|
|
|
|
|
|
this.unTimelyPush = () => {
|
|
|
this.tree.bus.off("entityChangeBefore", beforeHandler);
|
|
|
+ this.tree.bus.off("entityChange", changeHandler);
|
|
|
this.tree.bus.off("entityChangeAfter", afterHandler);
|
|
|
this.unTimelyPush = null;
|
|
|
};
|
|
@@ -172,25 +190,30 @@ export class HistoryPlugin<T = any> {
|
|
|
|
|
|
undo() {
|
|
|
this.hasRecovery = true;
|
|
|
- this.current.undo();
|
|
|
+ this.props.set(this.history.undo());
|
|
|
this.hasRecovery = false;
|
|
|
}
|
|
|
|
|
|
redo() {
|
|
|
this.hasRecovery = true;
|
|
|
- this.current.redo();
|
|
|
+ this.props.set(this.history.redo());
|
|
|
this.hasRecovery = false;
|
|
|
}
|
|
|
|
|
|
push(data: T) {
|
|
|
- this.current.push(data);
|
|
|
+ this.history.push(data);
|
|
|
}
|
|
|
|
|
|
clear() {
|
|
|
- this.current.clear();
|
|
|
+ this.history.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ get data() {
|
|
|
+ return this.history.data;
|
|
|
}
|
|
|
|
|
|
- get() {
|
|
|
- return this.current.get();
|
|
|
+ destory() {
|
|
|
+ this.setTree(null);
|
|
|
+ this.history.destory();
|
|
|
}
|
|
|
}
|