How to reset to initial coordinates after some drag operation with the library react-cytoscape.js

Viewed 456

I'm learning the library react-cytoscape.js and trying to run the offical demo.

enter image description here The demo shows that the left graph will rerender after you change the data in right area and click the 'Render' button.

I didn't make any changes to the demo code, but I have a question how to get all nodes reset to initial coordinates after some drag operation.

I thought that the data of right textarea recorded all initial coordinates, after dragged nodes, then clicked 'Render' button, the graph should be rerender base on the right data.

Please see my online demo.

window.addEventListener('DOMContentLoaded', function () {
  const exampleProps = {
    id: 'cy',
    className: 'foo bar',
    style: {
      'border': '1px solid #ccc',
      'width': '400px',
      'height': '400px'
    },
    global: 'cy',
    elements: [
      { data: { id: 'a', label: 'apple' }, position: { x: 0, y: 0 } },
      { data: { id: 'b', label: 'banana' }, position: { x: 100, y: 0 } },
      { data: { id: 'c', label: 'cherry' }, position: { x: 200, y: 0 } }
    ],
    layout: {
      name: 'preset'
    }
  };

  class TestComponent extends React.Component {
    constructor(props) {
      super(props);

      props.setStateRef(this.setState.bind(this));

      this.state = exampleProps;
    }
    
    render() {
      return React.createElement(ReactCytoscape, this.state);
    }
  }

  const textBox = document.getElementById('props');
  const btn = document.getElementById('update');

  btn.addEventListener('click', () => {
    update(JSON.parse(textBox.value));
  });

  let update;

  ReactDOM.render(
    React.createElement(TestComponent, { setStateRef: ref => update = ref }),
    document.getElementById('root')
  );

  textBox.value = JSON.stringify(exampleProps, null, 2);
});

When you drag node at first time, and click render button, all nodes are back to their original positions.

When you drag node again and click render button, the dragged nodes can't be back to their original coordinates defined in data.

My expectation is that all nodes should be positioned to the coordinates defined in right data textarea , no matter how many times you click 'render' button.

=============== Update ================

I find if I always change positions at right data textarea and then click 'Render' button, nodes could be layout at specified position. If change node's position through dragging then click 'Render' button, nodes couldn't be layout at position defined at right textarea.

1 Answers

You'll need to save the current nodes json via cy.json() and load them back into you cytoscape instance via cy.json({...}) again. In my example below, I use the browsers local storage to achieve a state, with React you can probably save it in a state or just write it to the field besides the cytoscape component and read the json aferwards.

The important part here is to render the graph with the updated json,I don't think that your example code updates the text box?

The following code does not work with stackoverflow (localStorage is not permitted for security reasons), but I have this code as a working codepen here. Also, the nodes styles are currently not exportet via json, but there are some questions on SO explaining how to do that (I think):

var elements1 = [{
    group: "nodes",
    data: {
      id: "n0"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n1"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n2"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n3"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n4",
      parent1: "n37"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n5"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n6"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n7",
      parent1: "n37"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n8",
      parent1: "n37"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n9",
      parent1: "n37"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n10",
      parent1: "n38"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n12"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n13"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n14"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n15"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n16"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n17"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n18"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n19"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n20"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n21"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n22"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n23"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n24",
      parent1: "n39"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n25",
      parent1: "n39"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n26",
      parent1: "n42"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n27",
      parent1: "n42"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n28",
      parent1: "n42"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n29",
      parent1: "n40"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n31",
      parent1: "n41"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n32",
      parent1: "n41"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n33",
      parent1: "n41"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n34",
      parent1: "n41"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n35",
      parent1: "n41"
    }
  },
  {
    group: "nodes",
    data: {
      id: "n36",
      parent1: "n41"
    }
  },
  {
    group: "edges",
    data: {
      id: "e0",
      source: "n0",
      target: "n1"
    }
  },
  {
    group: "edges",
    data: {
      id: "e1",
      source: "n1",
      target: "n2"
    }
  },
  {
    group: "edges",
    data: {
      id: "e2",
      source: "n2",
      target: "n3"
    }
  },
  {
    group: "edges",
    data: {
      id: "e3",
      source: "n0",
      target: "n3"
    }
  },
  {
    group: "edges",
    data: {
      id: "e4",
      source: "n1",
      target: "n4"
    }
  },
  {
    group: "edges",
    data: {
      id: "e5",
      source: "n2",
      target: "n4"
    }
  },
  {
    group: "edges",
    data: {
      id: "e6",
      source: "n4",
      target: "n5"
    }
  },
  {
    group: "edges",
    data: {
      id: "e7",
      source: "n5",
      target: "n6"
    }
  },
  {
    group: "edges",
    data: {
      id: "e8",
      source: "n4",
      target: "n6"
    }
  },
  {
    group: "edges",
    data: {
      id: "e9",
      source: "n4",
      target: "n7"
    }
  },
  {
    group: "edges",
    data: {
      id: "e10",
      source: "n7",
      target: "n8"
    }
  },
  {
    group: "edges",
    data: {
      id: "e11",
      source: "n8",
      target: "n9"
    }
  },
  {
    group: "edges",
    data: {
      id: "e12",
      source: "n7",
      target: "n9"
    }
  },
  {
    group: "edges",
    data: {
      id: "e13",
      source: "n13",
      target: "n14"
    }
  },
  {
    group: "edges",
    data: {
      id: "e14",
      source: "n12",
      target: "n14"
    }
  },
  {
    group: "edges",
    data: {
      id: "e15",
      source: "n14",
      target: "n15"
    }
  },
  {
    group: "edges",
    data: {
      id: "e16",
      source: "n14",
      target: "n16"
    }
  },
  {
    group: "edges",
    data: {
      id: "e17",
      source: "n15",
      target: "n17"
    }
  },
  {
    group: "edges",
    data: {
      id: "e18",
      source: "n17",
      target: "n18"
    }
  },
  {
    group: "edges",
    data: {
      id: "e19",
      source: "n18",
      target: "n19"
    }
  },
  {
    group: "edges",
    data: {
      id: "e20",
      source: "n17",
      target: "n20"
    }
  },
  {
    group: "edges",
    data: {
      id: "e21",
      source: "n19",
      target: "n20"
    }
  },
  {
    group: "edges",
    data: {
      id: "e22",
      source: "n16",
      target: "n20"
    }
  },
  {
    group: "edges",
    data: {
      id: "e23",
      source: "n20",
      target: "n21"
    }
  },
  {
    group: "edges",
    data: {
      id: "e25",
      source: "n23",
      target: "n24"
    }
  },
  {
    group: "edges",
    data: {
      id: "e26",
      source: "n24",
      target: "n25"
    }
  },
  {
    group: "edges",
    data: {
      id: "e30",
      source: "n26",
      target: "n27"
    }
  },
  {
    group: "edges",
    data: {
      id: "e31",
      source: "n26",
      target: "n28"
    }
  },
  {
    group: "edges",
    data: {
      id: "e33",
      source: "n21",
      target: "n31"
    }
  },
  {
    group: "edges",
    data: {
      id: "e35",
      source: "n31",
      target: "n33"
    }
  },
  {
    group: "edges",
    data: {
      id: "e36",
      source: "n31",
      target: "n34"
    }
  },
  {
    group: "edges",
    data: {
      id: "e37",
      source: "n33",
      target: "n34"
    }
  },
  {
    group: "edges",
    data: {
      id: "e38",
      source: "n32",
      target: "n35"
    }
  },
  {
    group: "edges",
    data: {
      id: "e39",
      source: "n32",
      target: "n36"
    }
  }
];

document.addEventListener("DOMContentLoaded", function() {
  var cy = (window.cy = cytoscape({
    container: document.getElementById("cy"),

    ready: function() {
      this.nodes().forEach(function(node) {
        let width = [30, 70, 110];
        let size = width[Math.floor(Math.random() * 3)];
        node.css("width", size);
        node.css("height", size);
      });
      this.layout({
        name: "cose-bilkent",
        animationDuration: 1000
      }).run();
    },

    style: [{
        selector: "node",
        style: {
          'content': 'data(id)',
          'text-valign': 'center',
          'text-halign': 'center',
          'background-color': '#ad1a66'
        }
      },

      {
        selector: ":parent",
        style: {
          "background-opacity": 0.333
        }
      },

      {
        selector: "edge",
        style: {
          width: 3,
          "line-color": "#ad1a66"
        }
      }
    ],

    elements: [{
        group: "nodes",
        data: {
          id: "n0"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n1"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n2"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n3"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n4",
          parent: "n37"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n5"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n6"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n7",
          parent: "n37"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n8",
          parent: "n37"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n9",
          parent: "n37"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n10",
          parent: "n38"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n12"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n13"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n14"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n15"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n16"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n17"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n18"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n19"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n20"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n21"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n22"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n23"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n24",
          parent: "n39"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n25",
          parent: "n39"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n26",
          parent: "n42"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n27",
          parent: "n42"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n28",
          parent: "n42"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n29",
          parent: "n40"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n31",
          parent: "n41"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n32",
          parent: "n41"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n33",
          parent: "n41"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n34",
          parent: "n41"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n35",
          parent: "n41"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n36",
          parent: "n41"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n37"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n38"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n39",
          parent: "n43"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n40",
          parent: "n42"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n41",
          parent: "n42"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n42",
          parent: "n43"
        }
      },
      {
        group: "nodes",
        data: {
          id: "n43"
        }
      },
      {
        group: "edges",
        data: {
          id: "e0",
          source: "n0",
          target: "n1"
        }
      },
      {
        group: "edges",
        data: {
          id: "e1",
          source: "n1",
          target: "n2"
        }
      },
      {
        group: "edges",
        data: {
          id: "e2",
          source: "n2",
          target: "n3"
        }
      },
      {
        group: "edges",
        data: {
          id: "e3",
          source: "n0",
          target: "n3"
        }
      },
      {
        group: "edges",
        data: {
          id: "e4",
          source: "n1",
          target: "n4"
        }
      },
      {
        group: "edges",
        data: {
          id: "e5",
          source: "n2",
          target: "n4"
        }
      },
      {
        group: "edges",
        data: {
          id: "e6",
          source: "n4",
          target: "n5"
        }
      },
      {
        group: "edges",
        data: {
          id: "e7",
          source: "n5",
          target: "n6"
        }
      },
      {
        group: "edges",
        data: {
          id: "e8",
          source: "n4",
          target: "n6"
        }
      },
      {
        group: "edges",
        data: {
          id: "e9",
          source: "n4",
          target: "n7"
        }
      },
      {
        group: "edges",
        data: {
          id: "e10",
          source: "n7",
          target: "n8"
        }
      },
      {
        group: "edges",
        data: {
          id: "e11",
          source: "n8",
          target: "n9"
        }
      },
      {
        group: "edges",
        data: {
          id: "e12",
          source: "n7",
          target: "n9"
        }
      },
      {
        group: "edges",
        data: {
          id: "e13",
          source: "n13",
          target: "n14"
        }
      },
      {
        group: "edges",
        data: {
          id: "e14",
          source: "n12",
          target: "n14"
        }
      },
      {
        group: "edges",
        data: {
          id: "e15",
          source: "n14",
          target: "n15"
        }
      },
      {
        group: "edges",
        data: {
          id: "e16",
          source: "n14",
          target: "n16"
        }
      },
      {
        group: "edges",
        data: {
          id: "e17",
          source: "n15",
          target: "n17"
        }
      },
      {
        group: "edges",
        data: {
          id: "e18",
          source: "n17",
          target: "n18"
        }
      },
      {
        group: "edges",
        data: {
          id: "e19",
          source: "n18",
          target: "n19"
        }
      },
      {
        group: "edges",
        data: {
          id: "e20",
          source: "n17",
          target: "n20"
        }
      },
      {
        group: "edges",
        data: {
          id: "e21",
          source: "n19",
          target: "n20"
        }
      },
      {
        group: "edges",
        data: {
          id: "e22",
          source: "n16",
          target: "n20"
        }
      },
      {
        group: "edges",
        data: {
          id: "e23",
          source: "n20",
          target: "n21"
        }
      },
      {
        group: "edges",
        data: {
          id: "e25",
          source: "n23",
          target: "n24"
        }
      },
      {
        group: "edges",
        data: {
          id: "e26",
          source: "n24",
          target: "n25"
        }
      },
      {
        group: "edges",
        data: {
          id: "e27",
          source: "n26",
          target: "n38"
        }
      },
      {
        group: "edges",
        data: {
          id: "e29",
          source: "n26",
          target: "n39"
        }
      },
      {
        group: "edges",
        data: {
          id: "e30",
          source: "n26",
          target: "n27"
        }
      },
      {
        group: "edges",
        data: {
          id: "e31",
          source: "n26",
          target: "n28"
        }
      },
      {
        group: "edges",
        data: {
          id: "e33",
          source: "n21",
          target: "n31"
        }
      },
      {
        group: "edges",
        data: {
          id: "e35",
          source: "n31",
          target: "n33"
        }
      },
      {
        group: "edges",
        data: {
          id: "e36",
          source: "n31",
          target: "n34"
        }
      },
      {
        group: "edges",
        data: {
          id: "e37",
          source: "n33",
          target: "n34"
        }
      },
      {
        group: "edges",
        data: {
          id: "e38",
          source: "n32",
          target: "n35"
        }
      },
      {
        group: "edges",
        data: {
          id: "e39",
          source: "n32",
          target: "n36"
        }
      },
      {
        group: "edges",
        data: {
          id: "e40",
          source: "n16",
          target: "n40"
        }
      }
    ],
  }));

  document.getElementById("layoutButton").addEventListener("click", function() {
    var layout = cy.layout({
      name: "cose-bilkent",
      animate: "end",
      animationEasing: "ease-out",
      animationDuration: 1000,
      randomize: true
    });

    layout.run();
  });
  document.getElementById("randomize").addEventListener("click", function() {
    var layout = cy.layout({
      name: "random",
      animate: true,
      animationDuration: 1000,
      animationEasing: "ease-out"
    });

    layout.run();
  });

  document.getElementById("save").addEventListener("click", function() {
    window.localStorage.setItem("elements", JSON.stringify(cy.json()));
  });

  document.getElementById("reload").addEventListener("click", function() {
    cy.elements().remove();
    cy.json({
      elements: JSON.parse(window.localStorage.getItem("elements")).elements
    }).layout({
      name: 'preset'
    }).run();
    cy.fit();
    cy.center();
  });
});
body {
  font-family: helvetica;
  font-size: 14px;
}

#cy {
  height: 100%;
  width: 90%;
  position: absolute;
}

h1 {
  opacity: 0.5;
  font-size: 1em;
}

button {
  margin-right: 10px;
}
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>

<!--polyfills are needed for this extension for old browsers like IE -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.5.7/shim.min.js"></script>
<script src="https://unpkg.com/layout-base/layout-base.js"></script>
<script src="https://unpkg.com/cose-base/cose-base.js"></script>

<script src="https://cdn.jsdelivr.net/npm/cytoscape-cose-bilkent@4.1.0/cytoscape-cose-bilkent.min.js"></script>

<body>
  <button id="randomize" type="button">Randomize</button>
  <button id="layoutButton" type="button">CoSE-Bilkent</button>
  <button id="save" class="button">save</button>
  <button id="reload" class="button">reload</button>
  <div id="cy"></div>

</body>

Related