bill 1 рік тому
батько
коміт
af7fc1c8ed
1 змінених файлів з 111 додано та 88 видалено
  1. 111 88
      src/board/_plugins/history/history.ts

+ 111 - 88
src/board/_plugins/history/history.ts

@@ -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();
   }
 }