clientIO / joint

A proven SVG-based JavaScript diagramming library powering exceptional UIs

Home Page:https://jointjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug]: Command manager is crashing on certain undo operations

alexandernst opened this issue · comments

What happened?

I found a certain case in which the command manager will crash while trying to undo a batch operation.

Consider the following steps:

node1 = new shape()
node1.addTo(graph)
node2 = new shape()
node2.addTo(graph)
group = new shape()
group.addTo(graph)
group.embed([node1, node2])

Now consider we use the Selection and the Clipboard plugins to execute the following steps:

selection.collection.reset([group])
clipboard.copyElements(selection.collection, graph)
clipboard.pasteCells(graph)

This will add the following commands to the undo stack of the command manager:

1. add (node1)
2. add (node2)
3. add (group)

At this point, running commandManager.undo() will trigger a crash. This is so because (I think) the command manager will start undoing the operations as so:

1. remove (group)
2. remove (node1)
3. remove (node2)

The problem is that the moment the group element is removed (step 1), node1 and node2 elements will get removed as well (as they are embedded in the group element). When step 2 is executed (remove node1), node1 won't exist, which will lead to the crash.

Demo: https://codesandbox.io/s/rappid-selection-copy-paste-bug-kin1pg

Note that moving lines [99-101] somewhere before creating the nodes will fix the issue (I believe because the undo stack will change the order of the commands).

Code
import "./style.scss";
import "./rappid.css";
import * as joint from "./rappid";

class MyGroup extends joint.dia.Element {
  defaults() {
    return {
      ...super.defaults,
      type: "custom.MyGroup",
      size: { width: 100, height: 250 },
      attrs: {
        body: {
          refWidth: "100%",
          refHeight: "100%",
          strokeWidth: 2,
          stroke: "#000000",
          fill: "#FFFFFF"
        },
        label: {
          textVerticalAnchor: "middle",
          textAnchor: "middle",
          refX: "50%",
          refY: "50%",
          fontSize: 14,
          fill: "#333333"
        }
      }
    };
  }

  markup = [
    {
      tagName: "rect",
      selector: "body"
    },
    {
      tagName: "text",
      selector: "label"
    }
  ];
}

const NS = Object.assign(joint.shapes, {
  custom: { MyGroup }
});

const $node = document.getElementById("canvas");
const graph = new joint.dia.Graph(
  {},
  {
    cellNamespace: NS
  }
);

const paper = new joint.dia.Paper({
  model: graph,
  width: 500,
  height: 500,
  cellViewNamespace: NS
});

const paperScroller = new joint.ui.PaperScroller({
  paper: paper
});

$node.replaceChildren(paperScroller.render().el);

const clipboard = new joint.ui.Clipboard({
  useLocalStorage: true,
  deep: true,
  translate: {
    dx: 120,
    dy: 20
  }
});

const selection = new joint.ui.Selection({
  paper: paper,
  strictSelection: true
});

const commandManager = new joint.dia.CommandManager({
  graph: graph,
  stackLimit: 50
});

var c1 = new joint.shapes.standard.Rectangle({
  size: { width: 50, height: 50 }
});
c1.position(75, 150);
c1.addTo(graph);

var c2 = new joint.shapes.standard.Rectangle({
  size: { width: 50, height: 50 }
});
c2.position(75, 250);
c2.addTo(graph);

const p = new MyGroup();
p.position(50, 100);
p.addTo(graph);
p.embed([c1, c2]);

// Trigger bug
selection.collection.reset([p]);
clipboard.copyElements(selection.collection, graph);
console.log(structuredClone(commandManager.undoStack));
clipboard.pasteCells(graph);
console.log(structuredClone(commandManager.undoStack));
commandManager.undo();

Version

3.7.4

What browsers are you seeing the problem on?

Chrome

What operating system are you seeing the problem on?

Mac

Thanks @alexandernst for the report and the reproducible example. We'll release a patch soon.

@zbynekstara Is this going to be release in JS+ 3.7.2? If "yes", when can I expect it?