bill vor 1 Jahr
Ursprung
Commit
63e3df2ad6

+ 33 - 148
src/board/_plugins/history/history.ts

@@ -1,131 +1,17 @@
 import { Root } from "../../core";
-import { History } from "stateshot";
-import { inRevise } from "../../shared";
-import { EditModeChange } from "../../core/base/entity-root-server";
 import mitt from "mitt";
-
-export type HistoryState = {
-  hasUndo: boolean;
-  hasRedo: boolean;
-};
-
-export type StateCallback = (state: HistoryState) => void;
-
-export class SingleHistory<T = any> {
-  stateChange: StateCallback;
-  history: History<{ data: T }>;
-  state = {
-    hasUndo: false,
-    hasRedo: false,
-  };
-
-  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;
-
-    if (inRevise(this.state, this.__prevState)) {
-      this.stateChange({ ...this.state });
-      this.__prevState = { ...this.state };
-    }
-  }
-
-  get data() {
-    return this.history.get()?.data;
-  }
-
-  undo(): T {
-    if (this.history.hasUndo) {
-      this.history.undo();
-      this.syncState();
-    }
-    return this.data;
-  }
-
-  redo(): T {
-    if (this.history.hasRedo) {
-      this.history.redo();
-      this.syncState();
-    }
-    return this.data;
-  }
-
-  push(data: T) {
-    this.history.pushSync({ data });
-    this.syncState();
-  }
-
-  clear() {
-    this.history.reset();
-  }
-}
-
-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.historyStack.length = 0;
-  }
-}
+import { HistoryState, MergeHistory } from "./merge-history";
+import {
+  cancelTrack,
+  continueTrack,
+  pauseTrack,
+  track,
+  TrackChange,
+} from "./track";
 
 export type HistoryPluginProps<T> = {
   init: () => T;
-  get: (args: EditModeChange) => T;
+  get: (args: TrackChange) => T;
   set: (data: T) => void;
 };
 
@@ -138,6 +24,7 @@ export class HistoryPlugin<T = any> {
 
   constructor(props: HistoryPluginProps<T>) {
     this.props = props;
+    this.entityChange = this.entityChange.bind(this);
   }
 
   private __changeTreeRelease: () => void;
@@ -155,37 +42,35 @@ export class HistoryPlugin<T = any> {
         this.history.push(this.props.init());
       }
 
-      tree.setHistory(this);
+      track(tree, this.entityChange);
       this.tree = tree;
-      this.timelyPush();
       this.__changeTreeRelease = () => {
-        this.unTimelyPush();
-        tree.setHistory(null);
+        cancelTrack(tree, this.entityChange);
       };
     }
   }
 
-  private unTimelyPush: () => void;
-  private timelyPush() {
-    this.unTimelyPush && this.unTimelyPush();
-
-    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;
-    };
+  pauseTrack() {
+    pauseTrack(this.tree);
+  }
+
+  continueTrack() {
+    continueTrack(this.tree);
+  }
+
+  entityChange(change: TrackChange) {
+    this.hasRecovery || this.history.push(this.props.get(change));
+  }
+
+  branch() {
+    this.history.branch();
+  }
+
+  merge() {
+    if (this.history.historyStack.length <= 1) {
+      throw "historyStack 没有可合并的内容";
+    }
+    this.history.merge();
   }
 
   undo() {

+ 120 - 0
src/board/_plugins/history/merge-history.ts

@@ -0,0 +1,120 @@
+import { History } from "stateshot";
+import { inRevise } from "../../shared";
+
+export type HistoryState = {
+  hasUndo: boolean;
+  hasRedo: boolean;
+};
+
+export type StateCallback = (state: HistoryState) => void;
+
+export class SingleHistory<T = any> {
+  stateChange: StateCallback;
+  history: History<{ data: T }>;
+  state = {
+    hasUndo: false,
+    hasRedo: false,
+  };
+
+  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;
+
+    if (inRevise(this.state, this.__prevState)) {
+      this.stateChange({ ...this.state });
+      this.__prevState = { ...this.state };
+    }
+  }
+
+  get data() {
+    return this.history.get()?.data;
+  }
+
+  undo(): T {
+    if (this.history.hasUndo) {
+      this.history.undo();
+      this.syncState();
+    }
+    return this.data;
+  }
+
+  redo(): T {
+    if (this.history.hasRedo) {
+      this.history.redo();
+      this.syncState();
+    }
+    return this.data;
+  }
+
+  push(data: T) {
+    this.history.pushSync({ data });
+    this.syncState();
+  }
+
+  clear() {
+    this.history.reset();
+  }
+}
+
+export class MergeHistory<T> {
+  private stateChange: StateCallback;
+  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 };
+      }
+    });
+    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.historyStack.length = 0;
+  }
+}

+ 112 - 0
src/board/_plugins/history/track.ts

@@ -0,0 +1,112 @@
+import { Entity, Root } from "../../core";
+
+const trackRoots = new WeakMap<
+  Root,
+  {
+    destory: (callback: (change: TrackChange) => void) => void;
+    pause: () => void;
+    continue: () => void;
+    onChanges: Array<(change: TrackChange) => void>;
+  }
+>();
+
+export type TrackChange = {
+  addEntity?: Entity;
+  setEntity?: Entity;
+  delEntity?: Entity;
+};
+export const track = (root: Root, onChange: (change: TrackChange) => void) => {
+  let status = trackRoots.get(root);
+  if (status) {
+    status.onChanges.push(onChange);
+    return;
+  } else {
+    status = {
+      destory: (onChange) => {
+        const ndx = status.onChanges.indexOf(onChange);
+        if (~ndx) {
+          status.onChanges.splice(ndx, 1);
+        }
+        if (status.onChanges.length === 0) {
+          destory();
+        }
+      },
+      pause: () => (pause = true),
+      continue: () => (pause = false),
+      onChanges: [onChange],
+    };
+  }
+
+  const callbacks = status.onChanges;
+  let pause = false;
+  const addHandler = (entity: Entity) => {
+    if (!pause) {
+      callbacks.forEach((onChange) => onChange({ addEntity: entity }));
+    }
+  };
+
+  const delHandler = (entity: Entity) => {
+    if (!pause) {
+      callbacks.forEach((onChange) => onChange({ delEntity: entity }));
+    }
+  };
+
+  // 存储正在拖拽的entity
+  const changeEntitys = new Set<Entity>();
+  const setHandler = (entity: Entity) => {
+    if (!pause) {
+      if (!entity.root.dragEntity) {
+        callbacks.forEach((onChange) => onChange({ setEntity: entity }));
+        changeEntitys.delete(entity);
+      } else {
+        changeEntitys.add(entity);
+      }
+    }
+  };
+  const triggerDragHandler = () => {
+    console.log(changeEntitys.size);
+    changeEntitys.forEach(setHandler);
+  };
+
+  root.bus.on("addEntity", addHandler);
+  root.bus.on("delEntity", delHandler);
+  root.bus.on("setEntity", setHandler);
+  root.bus.on("triggerDrag", triggerDragHandler);
+
+  const destory = () => {
+    root.bus.off("setEntity", setHandler);
+    root.bus.off("delEntity", delHandler);
+    root.bus.off("addEntity", addHandler);
+    root.bus.off("triggerDrag", triggerDragHandler);
+    trackRoots.delete(root);
+  };
+};
+
+export const cancelTrack = (
+  root: Root,
+  onChange?: (change: TrackChange) => void
+) => {
+  if (!hasTrack(root)) return;
+  const status = trackRoots.get(root);
+  if (onChange) {
+    status.destory(onChange);
+  } else {
+    while (status.onChanges.length) {
+      status.destory(status.onChanges[0]);
+    }
+  }
+};
+
+export const hasTrack = (root: Root) => trackRoots.has(root);
+
+export const pauseTrack = (root: Root) => {
+  if (hasTrack(root)) {
+    trackRoots.get(root).pause();
+  }
+};
+
+export const continueTrack = (root: Root) => {
+  if (hasTrack(root)) {
+    trackRoots.get(root).continue();
+  }
+};

+ 1 - 1
src/board/_plugins/pixel-scale/line-scale-view.ts

@@ -2,7 +2,7 @@ import { Entity } from "../../core/base/entity";
 import { Group } from "konva/lib/Group";
 import { Line } from "konva/lib/shapes/Line";
 import { Text } from "konva/lib/shapes/Text";
-import { getNorPosition, UNPosition } from "../../core/helper/dom";
+import { getNorPosition, UNPosition } from "../../core/shared/dom";
 import { getAppropriateWidth } from "./helper";
 import { ScaleViewTypeProps } from "./pixel-scale";
 

+ 1 - 1
src/board/_plugins/viewer/viewer.ts

@@ -4,7 +4,7 @@ import { Pos } from "../../core/type";
 import { alignPort, asyncTimeout, inRevise } from "../../shared";
 import { Transform } from "konva/lib/Util";
 import { openEntityDrag } from "../../core/base/entity-server";
-import { scaleListener } from "../../core/helper/dom";
+import { scaleListener } from "../../core/shared/dom";
 
 export type ViewerPluginProps = {
   move?: boolean;

+ 1 - 7
src/board/components/poi/poi.ts

@@ -1,14 +1,8 @@
-import {
-  Entity,
-  EntityKey,
-  EntityProps,
-  EntityTree,
-} from "../../core/base/entity";
+import { Entity, EntityProps, EntityTree } from "../../core/base/entity";
 import konva from "konva";
 import { openEntityDrag } from "../../core/base/entity-server";
 
 export type PoiData = {
-  id?: EntityKey;
   x: number;
   y: number;
   rotate?: number;

+ 3 - 3
src/board/components/poi/svg-poi.ts

@@ -3,7 +3,7 @@ import {
   getSVGProps,
   PathsShapeAct,
   pathsToShapeAct,
-} from "../../core/helper/svg";
+} from "../../core/shared/svg";
 import { EntityProps } from "../../core/base/entity";
 import konva from "konva";
 
@@ -11,10 +11,10 @@ export type SVGPoiData = PoiData & {
   type: string;
 };
 
-export class SVGPoi extends Poi<konva.Group, SVGPoiData> {
+export class SVGPoi<T extends SVGPoiData> extends Poi<konva.Group, T> {
   private act: PathsShapeAct;
 
-  constructor(props: EntityProps<SVGPoiData>) {
+  constructor(props: EntityProps<T>) {
     const shape = new konva.Group();
     super({ ...props, shape });
 

+ 7 - 3
src/board/components/poi/svg-pois.ts

@@ -1,9 +1,13 @@
-import { EntityProps } from "../../core/base/entity";
+import { EntityProps, EntityType } from "../../core/base/entity";
 import { EntityGroup } from "../../core/base/entity-group";
 import { SVGPoi, SVGPoiData } from "./svg-poi";
 
-export class SVGPois extends EntityGroup<typeof SVGPoi> {
-  constructor(props: EntityProps<SVGPoiData[]>) {
+export type SVGPoisData = SVGPoiData[];
+
+export class SVGPois<T extends SVGPoiData> extends EntityGroup<
+  EntityType<T, SVGPoi<T>["shape"]>
+> {
+  constructor(props: EntityProps<T[]>) {
     super({ type: SVGPoi, key: "svg-pois", ...props });
   }
 }

+ 8 - 270
src/board/core/base/entity-root-server.ts

@@ -1,8 +1,6 @@
 import { Entity } from "../base/entity";
 
 import { Root } from "./entity-root";
-import { Pos } from "../type";
-import { Transform } from "konva/lib/Util";
 import {
   canEntityReply,
   onEntity,
@@ -63,15 +61,8 @@ export type EditModeProps = {
   only?: boolean;
   includeNews?: boolean;
 };
-export type EditModeChange = {
-  addEntitys?: Entity[];
-  setEntitys?: Entity[];
-  delEntitys?: Entity[];
-};
-
 export const openEditMode = (
   root: Root,
-  main: () => any,
   props: EditModeProps = {
     entitys: [],
     only: false,
@@ -82,282 +73,29 @@ export const openEditMode = (
   if (!props.only) props.only = false;
   if (!props.includeNews) props.includeNews = false;
 
-  root.bus.emit("entityChangeBefore");
   const quitOnlyMode =
     props.only && openOnlyMode(root, props.entitys, props.includeNews);
 
   let state = "normal";
-  let resolve: () => void;
+  let complete: () => void;
   const interrupt = () => {
     state = "interrupt";
-    resolve();
+    complete();
   };
 
-  const destoryAutoEmit = root.history && autoEmitDataChange(root);
-
-  const interruptPromise = new Promise<void>((r) => (resolve = r));
-  const draw = Promise.any([interruptPromise, Promise.resolve(main())]).then(
-    () => {
-      quitOnlyMode && quitOnlyMode();
-      destoryAutoEmit && destoryAutoEmit();
-      setTimeout(() => {
-        root.bus.emit("entityChangeAfter");
-      });
-      return { state };
-    }
-  );
+  const interruptPromise = new Promise<void>((r) => (complete = r));
+  const draw = interruptPromise.then(() => {
+    quitOnlyMode && quitOnlyMode();
+    return { state };
+  });
 
   return {
     draw,
-    interrupt,
-  };
-};
-
-export const openEditModePacking = (root: Root, props?: EditModeProps) => {
-  let complete: () => void;
-  const { interrupt } = openEditMode(
-    root,
-    () => {
-      return new Promise<void>((r) => (complete = r));
-    },
-    props
-  );
-  return {
-    interrupt,
     complete,
+    interrupt,
   };
 };
 
-const autoedRoots = new WeakMap<
-  Root,
-  {
-    quete: number;
-    destory: () => void;
-    pause: () => void;
-    continue: () => void;
-  }
->();
-export const hasAutoEmitDataChange = (root: Root) => {
-  return autoedRoots.has(root);
-};
-export const pauseAutoEmitDataChange = (root: Root) => {
-  if (autoedRoots.has(root)) {
-    autoedRoots.get(root).pause();
-  }
-};
-export const continueAutoEmitDataChange = (root: Root) => {
-  if (autoedRoots.has(root)) {
-    autoedRoots.get(root).continue();
-  }
-};
-export const autoEmitDataChange = (root: Root) => {
-  if (autoedRoots.has(root)) {
-    const old = autoedRoots.get(root);
-    old.quete++;
-    return old.destory;
-  }
-  let pause = false;
-  const addHandler = (entity: Entity) =>
-    pause ||
-    (!root.history?.hasRecovery &&
-      root.bus.emit("entityChange", { addEntitys: [entity] }));
-  const delHandler = (entity: Entity) =>
-    pause ||
-    (!root.history?.hasRecovery &&
-      root.bus.emit("entityChange", { delEntitys: [entity] }));
-
-  const changeEntitys = new Set<Entity>();
-  const setHandler = (entity: Entity) => {
-    if (!pause) {
-      if (!entity.root.dragEntity) {
-        !root.history?.hasRecovery &&
-          root.bus.emit("entityChange", { setEntitys: [entity] });
-      } else {
-        changeEntitys.add(entity);
-      }
-    }
-  };
-  const triggerDragHandler = (entity: Entity) => {
-    if (!entity) {
-      changeEntitys.forEach(setHandler);
-    }
-  };
-
-  root.bus.on("addEntity", addHandler);
-  root.bus.on("delEntity", delHandler);
-  root.bus.on("setEntity", setHandler);
-  root.bus.on("triggerDrag", triggerDragHandler);
-
-  const destory = () => {
-    if (--autoedRoots.get(root).quete === 0) {
-      root.bus.off("setEntity", setHandler);
-      root.bus.off("delEntity", delHandler);
-      root.bus.off("addEntity", addHandler);
-      root.bus.off("triggerDrag", triggerDragHandler);
-      autoedRoots.delete(root);
-    }
-  };
-
-  autoedRoots.set(root, {
-    quete: 1,
-    destory,
-    pause: () => (pause = true),
-    continue: () => (pause = false),
-  });
-  return destory;
-};
-
-const cursorResources = {
-  pic_pen_a: "/cursors/pic_pen_a.ico",
-  pic_pen_r: "/cursors/pic_pen_r.ico",
-  pic_pen: "/cursors/pic_pen.ico",
-};
-export const addCursorResource = (key: string, url: string) => {
-  cursorResources[key] = url;
-};
-export const injectSetCursor = (root: Root) => {
-  const cursorStack = [];
-  const setCursorStyle = (ico: string) => {
-    const url = ico in cursorResources ? cursorResources[ico] : null;
-    root.container.style.cursor = url ? `url("${ico}"), auto` : ico;
-  };
-
-  return (ico: string) => {
-    const ndx = cursorStack.length;
-    cursorStack[ndx] = ico;
-    setCursorStyle(ico);
-
-    return () => {
-      cursorStack[ndx] = null;
-      let last = cursorStack.length - 1;
-      for (; last >= 0; last--) {
-        if (cursorStack[last] !== null) {
-          break;
-        }
-      }
-      if (last === -1) {
-        setCursorStyle("inherit");
-        cursorStack.length = 0;
-      } else if (last < ndx) {
-        setCursorStyle(cursorStack[last]);
-      }
-    };
-  };
-};
-
-export const injectConstant = (root: Root) => {
-  const origin: { [key in string]: number | Pos } = {};
-  const current: { [key in string]: number | Pos } = {};
-
-  let mat: Transform;
-  let scale: Pos;
-  let position: Pos;
-  let rCos: number, rSin: number;
-
-  root.bus.on("mounted", function handler() {
-    mat = root.stage.getTransform().invert();
-    scale = root.stage.scale();
-    position = root.stage.position();
-    let radians = root.stage.rotation() * (Math.PI / 180);
-    rCos = Math.cos(radians);
-    rSin = Math.sin(radians);
-
-    root.bus.off("mounted", handler);
-  });
-
-  const invView = (key: string) => {
-    if (key.startsWith("fix:")) {
-      if (typeof origin[key] === "number") {
-        current[key] = mat.point({ x: origin[key], y: 0 }).x;
-      } else {
-        current[key] = mat.point(origin[key]);
-      }
-    }
-  };
-  root.bus.on("changeView", () => {
-    mat = root.stage.getTransform().invert();
-    Object.keys(origin).forEach(invView);
-  });
-
-  const invScale = (key: string) => {
-    if (key.startsWith("fixScale:")) {
-      if (typeof origin[key] === "number") {
-        current[key] = origin[key] / scale.x;
-      } else {
-        current[key] = {
-          x: origin[key].x / scale.x,
-          y: origin[key].y / scale.y,
-        };
-      }
-    }
-  };
-  root.bus.on("changeViewScale", (nscale) => {
-    scale = nscale;
-    Object.keys(origin).forEach(invScale);
-  });
-
-  const invPosition = (key: string) => {
-    if (key.startsWith("fixPosition:")) {
-      if (typeof origin[key] === "number") {
-        current[key] = origin[key] - position.x;
-      } else {
-        current[key] = {
-          x: origin[key].x - position.x,
-          y: origin[key].y - position.y,
-        };
-      }
-    }
-  };
-  root.bus.on("changeViewPosition", (nposition) => {
-    position = nposition;
-    Object.keys(origin).forEach(invPosition);
-  });
-
-  const invRotation = (key: string) => {
-    if (key.startsWith("fixRotation:")) {
-      const p = origin[key];
-      if (typeof p !== "number") {
-        current[key] = {
-          x: p.x * rCos - p.y * rSin,
-          y: p.x * rSin + p.y * rCos,
-        };
-      }
-    }
-  };
-  root.bus.on("changeViewRotation", (rotation) => {
-    let radians = rotation * (Math.PI / 180);
-    rCos = Math.cos(radians);
-    rSin = Math.sin(radians);
-    Object.keys(origin).forEach(invRotation);
-  });
-
-  return {
-    set(key: string, val: number) {
-      origin[key] = val;
-      invView(key);
-      invPosition(key);
-      invRotation(key);
-      invScale(key);
-    },
-    get<T extends number | Pos>(key: string): T {
-      return (current[key] || origin[key]) as T;
-    },
-  };
-};
-
-const rootStack: Root[] = [];
-export const pushRoot = (root: Root) => rootStack.push(root);
-export const popRoot = () => rootStack.pop();
-export const currentRoot = () => rootStack[rootStack.length - 1];
-export const currentConstant = new Proxy(
-  {},
-  {
-    get(_, p) {
-      return currentRoot().constant.get(p as string);
-    },
-  }
-);
-
 export const injectPointerEvents = (root: Root) => {
   const store = {
     hovers: new Set<Entity>(),

+ 6 - 19
src/board/core/base/entity-root.ts

@@ -4,15 +4,13 @@ import { entityMount } from "./entity-server";
 import { Emitter, Pos, RootMat } from "../type";
 import {
   EditModeProps,
-  injectSetCursor,
-  injectConstant,
-  EditModeChange,
-  openEditModePacking,
+  openEditMode,
   injectPointerEvents,
   PointerEvents,
 } from "./entity-root-server";
 import { inRevise } from "../../shared";
 import { Transform } from "konva/lib/Util";
+import { Cursor } from "../helper/cursor";
 
 export type RootEvent = EntityEvent & {
   triggerFocus: Entity;
@@ -22,10 +20,6 @@ export type RootEvent = EntityEvent & {
   delEntity: Entity;
   setEntity: Entity;
 
-  entityChangeBefore: void;
-  entityChange: EditModeChange;
-  entityChangeAfter: void;
-
   changeView: RootMat;
   changeViewPort: Pos;
   changeViewScale: RootMat["scale"];
@@ -47,6 +41,7 @@ export class Root<T extends Entity = any> extends Entity<
   mat: Transform;
   invMat: Transform;
   tempComtainer = document.createElement("div");
+  cursor: Cursor;
 
   constructor() {
     super({
@@ -64,13 +59,6 @@ export class Root<T extends Entity = any> extends Entity<
     this.setTeleport(this.stage);
   }
 
-  history: null | { hasRecovery: boolean };
-  setHistory(history: null | { hasRecovery: boolean }) {
-    this.history = history;
-  }
-
-  setCursor = injectSetCursor(this);
-
   initShape() {
     return new konva.Layer();
   }
@@ -83,7 +71,7 @@ export class Root<T extends Entity = any> extends Entity<
     this.trigger && this.trigger.destory();
   }
 
-  private __editPacking: ReturnType<typeof openEditModePacking>;
+  private __editPacking: ReturnType<typeof openEditMode>;
   get hasEditMode() {
     return !!this.__editPacking;
   }
@@ -91,7 +79,8 @@ export class Root<T extends Entity = any> extends Entity<
     if (this.__editPacking) {
       throw "当前正在编辑模式";
     }
-    this.__editPacking = openEditModePacking(this, props);
+    this.__editPacking = openEditMode(this, props);
+    return this.__editPacking.draw;
   }
 
   leaveEditMode() {
@@ -115,8 +104,6 @@ export class Root<T extends Entity = any> extends Entity<
     return this.invMat.point(pixel);
   }
 
-  constant = injectConstant(this);
-
   private __changeContainerRelease: () => void;
   mount(container: HTMLDivElement = this.tempComtainer): void {
     if (container === this.container && this.isMounted) return;

+ 3 - 19
src/board/core/base/entity-server.ts

@@ -3,15 +3,11 @@ import { debounce, mergeFuns } from "../../shared";
 import { Entity, EntityShape, EntityTransmit } from "./entity";
 import { Root } from "./entity-root";
 import { Stage } from "konva/lib/Stage";
-import { contain } from "../helper/shape";
-import {
-  autoEmitDataChange,
-  hasAutoEmitDataChange,
-} from "./entity-root-server";
+import { contain } from "../shared/shape";
 import { canEntityReply } from "../event";
 import { Pos } from "../type";
 import { KonvaEventObject } from "konva/lib/Node";
-import { getOffset } from "../helper/dom";
+import { getOffset } from "../shared/dom";
 
 export const traversEntityTree = (
   entity: Entity,
@@ -163,7 +159,7 @@ export const summarizeEntity = (entity: Entity) => {
 export const entityInit = <T extends Entity>(entity: T) => {
   entity.bus.emit("createBefore");
   entity.shape = entity.initShape();
-  entity.shape.id(entity.name);
+  entity.shape.name(entity.name);
   entity.bus.emit("created");
 
   let releases = entity.needReleases();
@@ -237,13 +233,6 @@ export const mountEntityTree = <T extends Entity>(
   entityMount(entity);
   if (entity.root) {
     entity.root.bus.emit("addEntity", entity);
-    if (
-      entity.root.history &&
-      !entity.root.history.hasRecovery &&
-      !hasAutoEmitDataChange(entity.root)
-    ) {
-      entity.root.bus.emit("entityChange", { addEntitys: [this] });
-    }
   }
   return entity;
 };
@@ -264,7 +253,6 @@ export const openEntityDrag = <T extends Entity>(
   let canReply = false;
   let moveHandler: ReturnType<DragHandlers>["move"];
   let endHandler: ReturnType<DragHandlers>["end"];
-  let destoryAutoEmit: () => void;
   let start: Pos;
 
   shape.on("dragstart.drag", (ev) => {
@@ -273,9 +261,6 @@ export const openEntityDrag = <T extends Entity>(
       const handlers = getHandlers(ev);
       moveHandler = handlers.move;
       endHandler = handlers.end;
-      if (entity.root.history) {
-        destoryAutoEmit = autoEmitDataChange(entity.root);
-      }
       start = getOffset(ev.evt);
     }
   });
@@ -297,7 +282,6 @@ export const openEntityDrag = <T extends Entity>(
     canReply = false;
     start = null;
     endHandler && endHandler(ev);
-    destoryAutoEmit && destoryAutoEmit();
   });
 
   return () => {

+ 17 - 26
src/board/core/base/entity.ts

@@ -16,14 +16,8 @@ import {
 import { Stage } from "konva/lib/Stage";
 import { Emitter } from "../type";
 import { DEV } from "../../env";
-import {
-  closeOnEntityTree,
-  openOnEntityTree,
-  TreeEvent,
-  TreeEventName,
-} from "../event";
-import { hasAutoEmitDataChange } from "./entity-root-server";
-import { getChangePart } from "../../shared";
+import { TreeEvent, TreeEventName } from "../event";
+import { getChangePart, inRevise } from "../../shared";
 
 export type EntityTransmit = {
   root: Root;
@@ -126,6 +120,10 @@ export class Entity<
     this.teleport = props.teleport as any;
     this.props = props;
     this.attrib = props.attrib;
+    this.__oldAttrib =
+      props.attrib !== void 0
+        ? JSON.parse(JSON.stringify(props.attrib))
+        : void 0;
     this.zIndex = props.zIndex || 0;
     this.key = props.key;
 
@@ -196,6 +194,7 @@ export class Entity<
     summarizeEntity(this);
   }
 
+  private __oldAttrib: any;
   setAttrib(newAttrib: Partial<T>) {
     if (typeof newAttrib === "object" && !Array.isArray(newAttrib)) {
       const { addPort } = getChangePart(
@@ -211,15 +210,12 @@ export class Entity<
     }
     this.attrib = newAttrib as T;
     this.isMounted && this.diffRedraw();
-    if (this.root) {
+    if (this.root && inRevise(this.__oldAttrib, this.attrib)) {
       this.root.bus.emit("setEntity", this);
-      if (
-        this.root.history &&
-        !this.root.history.hasRecovery &&
-        !hasAutoEmitDataChange(this.root)
-      ) {
-        this.root.bus.emit("entityChange", { setEntitys: [this] });
-      }
+      this.__oldAttrib =
+        this.attrib !== void 0
+          ? JSON.parse(JSON.stringify(this.attrib))
+          : void 0;
     }
   }
 
@@ -254,16 +250,6 @@ export class Entity<
   }
 
   destory() {
-    if (this.root) {
-      this.root.bus.emit("delEntity", this);
-      if (
-        this.root.history &&
-        !this.root.history.hasRecovery &&
-        !hasAutoEmitDataChange(this.root)
-      ) {
-        this.root.bus.emit("entityChange", { delEntitys: [this] });
-      }
-    }
     this.bus.emit("destroyBefore");
 
     while (this.children.length) {
@@ -280,7 +266,12 @@ export class Entity<
     } else {
       this.shape.destroy();
     }
+
     this.bus.emit("destroyed");
+    if (this.root) {
+      this.root.bus.emit("delEntity", this);
+    }
+
     this.bus.off("*" as any);
   }
 

+ 28 - 0
src/board/core/helper/constant-factory.ts

@@ -0,0 +1,28 @@
+import { currentRoot } from "./current";
+
+export const constantFactory = <T>(
+  origin: T,
+  getter: <K extends keyof T>(value: T[K]) => T[K]
+): T => {
+  return new Proxy(
+    {},
+    {
+      get(_, key) {
+        return getter(origin[key as string]);
+      },
+    }
+  ) as T;
+};
+
+export const pixelConstantFactory = <T extends { [key in string]: number }>(
+  origin: T
+): T => {
+  return constantFactory(origin, (val) => {
+    if (!currentRoot()) {
+      return val;
+    } else {
+      const scale = currentRoot().stage.scale();
+      return (val / scale.x) as any;
+    }
+  });
+};

+ 6 - 0
src/board/core/helper/current.ts

@@ -0,0 +1,6 @@
+import { Root } from "../base/entity-root";
+
+const rootStack: Root[] = [];
+export const pushRoot = (root: Root) => rootStack.push(root);
+export const popRoot = () => rootStack.pop();
+export const currentRoot = () => rootStack[rootStack.length - 1];

+ 48 - 0
src/board/core/helper/cursor.ts

@@ -0,0 +1,48 @@
+import { Root } from "../base/entity-root";
+
+export class Cursor {
+  static cursorResources: { [key in string]: string } = {
+    picPenA: "/cursors/pic_pen_a.ico",
+    picPenR: "/cursors/pic_pen_r.ico",
+    picPen: "/cursors/pic_pen.ico",
+  };
+
+  static addCursorResource(name: string, url: string) {
+    Cursor.cursorResources[name] = url;
+  }
+
+  private tree: Root;
+  private stack: string[] = [];
+  constructor(root: Root) {
+    this.tree = root;
+    if (this.tree.isMounted) {
+      this.update();
+    } else {
+      this.tree.bus.on("mounted", this.update.bind(this));
+    }
+  }
+
+  set(name: string) {
+    const url =
+      name in Cursor.cursorResources ? Cursor.cursorResources[name] : null;
+    this.tree.container.style.cursor = url ? `url("${url}"), auto` : name;
+  }
+
+  update() {
+    if (this.stack.length === 0) {
+      this.set("inherit");
+    } else {
+      this.set(this.stack[this.stack.length - 1]);
+    }
+  }
+
+  push(name: string) {
+    this.stack[this.stack.length] = name;
+    this.update();
+  }
+
+  pop() {
+    this.stack.pop();
+    this.update();
+  }
+}

+ 3 - 3
src/board/core/index.ts

@@ -6,6 +6,6 @@ export * from "./base/entity-root";
 
 export { canEntityReply, onEntity, emitEntityTree } from "./event/index";
 
-export * from "./helper/shape";
-export * from "./helper/svg";
-export * from "./helper/continuity-draw";
+export * from "./shared/shape";
+export * from "./shared/svg";
+export * from "./shared/continuity";

src/board/core/helper/continuity-draw.ts → src/board/core/shared/continuity.ts


src/board/core/helper/dom.ts → src/board/core/shared/dom.ts


src/board/core/helper/shape.ts → src/board/core/shared/shape.ts


src/board/core/helper/svg.ts → src/board/core/shared/svg.ts