plotly / react-cytoscapejs

React component for Cytoscape.js network visualisations
MIT License
483 stars 68 forks source link

Can't Change Layout to Preset #3

Closed xhluca closed 5 years ago

xhluca commented 6 years ago

Description

Whenever the layout is changed to something else (random, grid, etc.), the component is rendered correctly. however, whenever it is changed back to preset, nothing happens (it stays stuck in the previous layout).

44219898-229af580-a14b-11e8-92c4-44df8d42e12b

Files

App.js is written as such:

import React, { Component } from 'react';
import CytoscapeComponent from './react-cytoscapejs/src/component.js';
import Select from 'react-select';

const options = [
  { value: 'preset', label: 'preset' },
  { value: 'random', label: 'random' },
  { value: 'grid', label: 'grid' },
  { value: 'circle', label: 'circle' }
];

const elements = [
    {
        data: {id: 'one', label: 'Node 1'},
        position: {x: 50, y: 50}
    },
    {
        data: {id: 'two', label: 'Node 2'},
        position: {x: 200, y: 200}
    },
    {
        data: {id: 'three', label: 'Node 3'},
        position: {x: 100, y: 150}
    },
    {
        data: {id: 'four', label: 'Node 4'},
        position: {x: 400, y: 50}
    },
    {
        data: {id: 'five', label: 'Node 5'},
        position: {x: 250, y: 100}
    },
    {
        data: {id: 'six', label: 'Node 6', parent: 'three'},
        position: {x: 150, y: 150}
    },

    {data: {
        source: 'one',
        target: 'two',
        label: 'Edge from Node1 to Node2'
    }},
    {data: {
        source: 'one',
        target: 'five',
        label: 'Edge from Node 1 to Node 5'
    }},
    {data: {
        source: 'two',
        target: 'four',
        label: 'Edge from Node 2 to Node 4'
    }},
    {data: {
        source: 'three',
        target: 'five',
        label: 'Edge from Node 3 to Node 5'
    }},
    {data: {
        source: 'three',
        target: 'two',
        label: 'Edge from Node 3 to Node 2'
    }},
    {data: {
        source: 'four',
        target: 'four',
        label: 'Edge from Node 4 to Node 4'
    }},
    {data: {
        source: 'four',
        target: 'six',
        label: 'Edge from Node 4 to Node 6'
    }},
    {data: {
        source: 'five',
        target: 'one',
        label: 'Edge from Node 5 to Node 1'
    }},
];

class App extends Component {
    state = {
      selectedOption: 'preset',
      layout: 'preset',
      style: {width:'800px', height: '500px'}
    }

    handleChange = (selectedOption) => {
      this.setState({
        selectedOption: selectedOption,
        layout: selectedOption.value
      });
      console.log(`Option selected:`, selectedOption);
    }

    render() {
      const { selectedOption, style, layout } = this.state;

      return (
        <div>
          <CytoscapeComponent
              elements={elements}
              style={style}
              layout={{name: layout}}
          />
          <Select
            value={selectedOption}
            onChange={this.handleChange}
            options={options}
          />
        </div>
      );
    }
}

export default App;

Reproducing Error

Steps toward reproducing the error in command line:

# Create the app
npx create-react-app preset
cd preset

# Add dependencies
yarn add cytoscape
yarn add react-select
cd src
git clone https://github.com/plotly/react-cytoscapejs.git

# Here, replace App.js with the file described in the issue.

cd ..
yarn build
yarn start
maxkfranz commented 6 years ago

The preset layout is a null-op, unless you specify the positions field in the layout options.

See http://js.cytoscape.org/#layouts/preset

At init, a preset layout means that you would just use the positions specified in the elements JSON directly -- i.e. a null-op doesn't change the initial positions. Normally, a layout would overwrite the positions at init.

Example of post-init preset layout:

let elesJson = [
  { data: { id: 'a' }, position: { x: 0, y: 100 } },
  { data: { id: 'b' }, position: { x: 0, y: 200 } },
  { data: { id: 'c' }, position: { x: 0, y: 300 } }
];

// map of id => position obj (this example uses the initial positions)
// NB you must copy the position b/c cytoscape owns the objects you pass to it
let presetPositions = elesJson.reduce((positions, eleJson) => {
  positions[eleJson.data.id] = _.clone(eleJson.position);

  return positions;
}, {});

let cy = cytoscape({
  elements: elesJson
  // more options...
});

// some time after init restore the initial positions...
cy.layout({ name: 'preset', positions: presetPositions }).run();
xhluca commented 6 years ago

Very clearly explain, thank you! I'll explore this further.

maxkfranz commented 5 years ago

@xhlulu Can this be closed?

xhluca commented 5 years ago

Hello yes, this can be closed. Sorry.