diff --git a/actions/generals.js b/actions/generals.js
index d3ea7e1a2..6d1fd7298 100644
--- a/actions/generals.js
+++ b/actions/generals.js
@@ -6,8 +6,7 @@ export const VFB_UI_UPDATED = 'VFB_UI_UPDATED';
export const INSTANCE_ADDED = 'INSTANCE_ADDED';
export const SHOW_GRAPH = 'SHOW_GRAPH';
export const UPDATE_GRAPH = 'UPDATE_GRAPH';
-export const LOAD_CIRCUIT_BROWSER = 'LOAD_CIRCUIT_BROWSER';
-export const UPDATE_CIRCUIT_BROWSER = 'UPDATE_CIRCUIT_BROWSER';
+export const UPDATE_CIRCUIT_QUERY = 'UPDATE_CIRCUIT_QUERY';
export const INSTANCE_SELECTED = 'INSTANCE_SELECTION';
export const INSTANCE_DELETED = 'INSTANCE_DELETED';
export const INSTANCE_VISIBILITY_CHANGED = 'INSTANCE_VISIBILITY_CHANGED';
@@ -40,7 +39,7 @@ export const vfbGraph = (type, instance, queryIndex) => ({
});
export const vfbCircuitBrowser = (type, instance) => ({
- type: LOAD_CIRCUIT_BROWSER,
+ type: type,
data: { instance : instance }
});
diff --git a/components/VFBMain.js b/components/VFBMain.js
index 27fd1ebe4..e2aaf2e5a 100644
--- a/components/VFBMain.js
+++ b/components/VFBMain.js
@@ -17,7 +17,7 @@ import VFBQuickHelp from './interface/VFBOverview/QuickHelp';
import VFBGraph from './interface/VFBGraph/VFBGraph';
import VFBCircuitBrowser from './interface/VFBCircuitBrowser/VFBCircuitBrowser';
import { connect } from "react-redux";
-import { SHOW_GRAPH, LOAD_CIRCUIT_BROWSER, VFB_LOAD_TERM_INFO, SHOW_LIST_VIEWER } from './../actions/generals';
+import { SHOW_GRAPH, UPDATE_CIRCUIT_QUERY, VFB_LOAD_TERM_INFO, SHOW_LIST_VIEWER } from './../actions/generals';
require('../css/base.less');
require('../css/VFBMain.less');
@@ -1110,7 +1110,7 @@ class VFBMain extends React.Component {
}
}
- if ( this.props.generals.type == LOAD_CIRCUIT_BROWSER ) {
+ if ( this.props.generals.type == UPDATE_CIRCUIT_QUERY ) {
if ( !this.state.circuitBrowserVisible ) {
this.setState({
UIUpdated: true,
diff --git a/components/configuration/VFBGraph/graphConfiguration.js b/components/configuration/VFBGraph/graphConfiguration.js
index 77d0b7037..d622b01e8 100644
--- a/components/configuration/VFBGraph/graphConfiguration.js
+++ b/components/configuration/VFBGraph/graphConfiguration.js
@@ -1,4 +1,4 @@
-var locationCypherQuery = instance => ({
+var whatIsCypherQuery = instance => ({
"statements": [
{
"statement": "MATCH p=(n:Entity {short_form:'" + instance + "'})-[r:INSTANCEOF|part_of|has_synaptic_terminal_in|has_presynaptic_terminal_in|"
diff --git a/components/configuration/VFBListViewer/controlsMenuConfiguration.js b/components/configuration/VFBListViewer/controlsMenuConfiguration.js
index 6203e90fd..9b1997469 100644
--- a/components/configuration/VFBListViewer/controlsMenuConfiguration.js
+++ b/components/configuration/VFBListViewer/controlsMenuConfiguration.js
@@ -1,5 +1,20 @@
import React from "react";
+var ACTIONS = {
+ COLOR : 'color',
+ INFO : 'info',
+ DELETE : 'delete',
+ SELECT : 'select',
+ DESELECT : 'deselect',
+ SHOW : 'hide',
+ HIDE : 'show',
+ ZOOM_TO : 'zoom_to',
+ SHOW_VOLUME : 'show_volume',
+ HIDE_VOLUME : 'hide_volume',
+ SHOW_SKELETON : 'show_skeleton',
+ HIDE_SKELETON : 'hide_skeleton',
+};
+
const controlsMenuConf = {
itemOptions: { customArrow: },
// Global configuration for Menu buttons and drop down
@@ -51,6 +66,7 @@ const controlsMenuConf = {
}
}
},
+ actions : ACTIONS,
// Buttons to display inside the Menu
buttons: [
{
@@ -70,21 +86,22 @@ const controlsMenuConf = {
{
label: "Show Info",
icon: "fa fa-info",
- action: "info"
+ action: { handlerAction: ACTIONS.INFO }
},
{
toggle : {
condition : entity => entity.isSelected(),
+ isVisible : entity => entity.isVisible(),
options : {
false : {
label: "Select",
icon: "fa fa-check-circle-o",
- action: entity => entity.select()
+ action: { handlerAction: ACTIONS.SELECT, }
},
true : {
label: "Unselect",
icon: "fa fa-check-circle",
- action: entity => entity.deselect()
+ action: { handlerAction: ACTIONS.DESELECT, }
}
}
}
@@ -96,12 +113,12 @@ const controlsMenuConf = {
false : {
label: "Show",
icon: "fa fa-eye",
- action: entity => entity.show()
+ action: { handlerAction: ACTIONS.SHOW, }
},
true : {
label: "Hide",
icon: "fa fa-eye-slash",
- action: entity => entity.hide()
+ action: { handlerAction: ACTIONS.HIDE, }
}
}
}
@@ -110,36 +127,73 @@ const controlsMenuConf = {
label: "Delete",
icon: "fa fa-trash",
isVisible : entity => entity.getId() != window.templateID,
- action: 'delete'
+ action: { handlerAction: ACTIONS.DELETE },
},
{
label: "Zoom To",
icon: "fa fa-search-plus",
- action: entity => GEPPETTO.SceneController.zoomTo([entity])
+ action: { handlerAction: ACTIONS.ZOOM_TO },
+ isVisible : entity => entity.isVisible()
},
{
- label: "Show As",
+ label: "Show Volume",
icon: "",
- action: "",
+ action: {},
position: "right-start",
list: [
{
toggle : {
- condition : entity => entity.isVisible(),
+ condition : entity => {
+ var visible = false;
+ if (entity.getType()[entity.getId() + "_obj"] != undefined && entity[entity.getId() + "_obj"] != undefined) {
+ visible = GEPPETTO.SceneController.isVisible([entity[entity.getId() + "_obj"]]);
+ }
+ return visible;
+ },
+ isVisible : entity => entity.getType().hasVariable(entity.getId() + '_obj'),
options : {
false : {
- label: "Show 3D Volume",
+ label: "Enable 3D Volume",
icon: "gpt-shapeshow",
- action: entity => {
- entity.show()
- }
+ action: { handlerAction: ACTIONS.SHOW_VOLUME }
},
true : {
- label: "Hide 3D Volume",
+ label: "Disable 3D Volume",
icon: "gpt-shapehide",
- action: entity => {
- entity.hide()
- }
+ action: { handlerAction: ACTIONS.HIDE_VOLUME }
+ }
+ }
+ }
+ },
+ ]
+ },
+ {
+ label: "Show Skeleton",
+ icon: "",
+ action: {},
+ position: "right-start",
+ list: [
+ {
+ toggle : {
+ condition : entity => {
+ var visible = false;
+ if (entity.getType()[entity.getId() + "_swc"] != undefined && entity.getType()[entity.getId() + "_swc"].getType().getMetaType() != GEPPETTO.Resources.IMPORT_TYPE && entity[entity.getId() + "_swc"] != undefined) {
+ visible = GEPPETTO.SceneController.isVisible([entity[entity.getId() + "_swc"]]);
+ }
+ return visible;
+ },
+ isVisible : entity => entity.getType().hasVariable(entity.getId() + '_swc'),
+ options : {
+ false : {
+ label: "Enable 3D Skeleton",
+ icon: "gpt-3dhide",
+ tooltip : "Show 3D Skeleton",
+ action: { handlerAction: ACTIONS.SHOW_SKELETON }
+ },
+ true : {
+ label: "Disable 3D Skeleton",
+ icon: "gpt-3dshow",
+ action: { handlerAction: ACTIONS.HIDE_SKELETON }
}
}
}
@@ -149,7 +203,8 @@ const controlsMenuConf = {
{
label: "Color",
icon: "fa fa-tint",
- action: 'color'
+ action: { handlerAction: ACTIONS.COLOR },
+ isVisible : entity => entity.isVisible()
},
]
}
diff --git a/components/configuration/VFBToolbar/vfbtoolbarHTML.js b/components/configuration/VFBToolbar/vfbtoolbarHTML.js
index 5e7cd675b..a8e3c85b5 100644
--- a/components/configuration/VFBToolbar/vfbtoolbarHTML.js
+++ b/components/configuration/VFBToolbar/vfbtoolbarHTML.js
@@ -12,8 +12,8 @@ var feedback
+ "you can engage directly with our developer community on GitHub "
+ "[VirtualFlyBrain/VFB2].
"
+ "If you have a GitHub account you can easily raise a new issue: "
- + "
"
+ ""
+ " This could simply be a question or a new feature request, but if you have found a bug we missed please copy in "
diff --git a/components/interface/VFBCircuitBrowser/Controls.js b/components/interface/VFBCircuitBrowser/Controls.js
index f3dea2a64..16dbb0323 100644
--- a/components/interface/VFBCircuitBrowser/Controls.js
+++ b/components/interface/VFBCircuitBrowser/Controls.js
@@ -22,6 +22,8 @@ import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
+import { connect } from "react-redux";
+import { UPDATE_CIRCUIT_QUERY } from './../../../actions/generals';
/**
* Create a local theme to override some default values in material-ui components
@@ -122,7 +124,7 @@ class Controls extends Component {
this.state = {
typingTimeout: 0,
expanded : true,
- neuronFields : this.props.neurons
+ neuronFields : ["", ""]
};
this.addNeuron = this.addNeuron.bind(this);
this.neuronTextfieldModified = this.neuronTextfieldModified.bind(this);
@@ -135,8 +137,12 @@ class Controls extends Component {
}
componentDidMount () {
- this.setState( { expanded : !this.props.resultsAvailable() } );
+ let neurons = [...this.props.neurons];
+ this.setState( { expanded : !this.props.resultsAvailable(), neuronFields : neurons } );
+ this.circuitQuerySelected = this.props.circuitQuerySelected;
}
+
+ componentDidUpdate () {}
/**
* Deletes neuron field, updates control component right after
@@ -146,12 +152,17 @@ class Controls extends Component {
if ( event.target.id === "" ) {
id = parseInt(event.target.parentElement.id);
}
+
// remove neuron textfield
let neurons = this.state.neuronFields;
neurons.splice(id,1);
+
+ this.props.vfbCircuitBrowser(UPDATE_CIRCUIT_QUERY, neurons);
+
// Update state with one less neuron textfield
this.setState( { neuronFields : neurons } );
+ // If neuron fields are validated, let the VFBCircuitBrowser component know, it will do a graph update
if ( this.fieldsValidated(neurons) ) {
this.props.queriesUpdated(neurons);
}
@@ -176,7 +187,7 @@ class Controls extends Component {
* Validates neurons ID's are valid, checks there's at least 8 numbers in it
*/
fieldsValidated (neurons) {
- var pattern = /\d{8}/;
+ var pattern = /^[a-zA-Z0-9].*_[a-zA-Z0-9]{8}$/;
for ( var i = 0 ; i < neurons.length ; i++ ){
if ( neurons[i] === "" ) {
return false;
@@ -195,6 +206,8 @@ class Controls extends Component {
typingTimeout (target) {
let neurons = this.state.neuronFields;
neurons[target.id] = target.value;
+ this.circuitQuerySelected = neurons;
+ this.props.vfbCircuitBrowser(UPDATE_CIRCUIT_QUERY, neurons);
if ( this.fieldsValidated(neurons) ) {
this.setState( { neuronFields : neurons } );
this.props.queriesUpdated(neurons);
@@ -230,40 +243,36 @@ class Controls extends Component {
*/
getUpdatedNeuronFields () {
let neuronFields = this.state.neuronFields;
- let neuronMatch = false;
- // Query preselected
- let queriesPassed = Object.keys(this.circuitQuerySelected).length > 0;
-
- if ( queriesPassed) {
- // If query is preselected and is not on the list already
- if ( !this.state.neuronFields.includes(this.circuitQuerySelected) ) {
- for ( var i = 0 ; i < neuronFields.length ; i++ ) {
- if ( this.state.neuronFields[i] === "" ) {
- neuronFields[i] = this.circuitQuerySelected;
- neuronMatch = true;
+ let added = false;
+ for ( var i = 0; i < this.props.circuitQuerySelected.length; i++ ){
+ if ( !this.state.neuronFields.includes(this.props.circuitQuerySelected[i])) {
+ for ( var j = 0 ; j < neuronFields.length ; j++ ) {
+ if ( this.state.neuronFields[j] === "" ) {
+ neuronFields[j] = this.props.circuitQuerySelected[i];
+ added = true;
break;
}
}
- } else if ( queriesPassed && neuronFields.includes(this.circuitQuerySelected) ) {
- neuronMatch = true;
- }
- }
-
- // If preselected query is not on list of existing queries
- if ( !neuronMatch ) {
- if ( queriesPassed ) {
- if ( neuronFields.length < configuration.maxNeurons ) {
- neuronFields.push(this.circuitQuerySelected);
+
+ if ( this.props.circuitQuerySelected.length > neuronFields.length && !this.state.neuronFields.includes(this.circuitQuerySelected[i])) {
+ if ( neuronFields.length < configuration.maxNeurons && this.props.circuitQuerySelected !== "" ) {
+ neuronFields.push(this.props.circuitQuerySelected[i]);
+ }
}
}
}
+ if ( this.fieldsValidated(neuronFields) ) {
+ this.props.queriesUpdated(neuronFields);
+ }
+
return neuronFields;
}
render () {
let self = this;
const { classes } = this.props;
+ this.circuitQuerySelected = this.props.circuitQuerySelected;
let neuronFields = this.getUpdatedNeuronFields()
let expanded = this.state.expanded;
@@ -390,4 +399,12 @@ class Controls extends Component {
Controls.propTypes = { classes: PropTypes.object.isRequired };
-export default withStyles(styles)(Controls);
+function mapStateToProps (state) {
+ return { ...state }
+}
+
+function mapDispatchToProps (dispatch) {
+ return { vfbCircuitBrowser: (type, path) => dispatch ( { type : type, data : { instance : path } }), }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef : true } )(withStyles(styles)(Controls));
diff --git a/components/interface/VFBCircuitBrowser/VFBCircuitBrowser.js b/components/interface/VFBCircuitBrowser/VFBCircuitBrowser.js
index 56de50645..b2810b8bd 100644
--- a/components/interface/VFBCircuitBrowser/VFBCircuitBrowser.js
+++ b/components/interface/VFBCircuitBrowser/VFBCircuitBrowser.js
@@ -162,6 +162,7 @@ class VFBCircuitBrowser extends Component {
graph : { nodes : [], links : [] } ,
legend : {},
loading : true,
+ queryLoaded : false,
dropDownAnchorEl : null,
neurons : ["", ""],
hops : Math.ceil((configuration.maxHops - configuration.minHops) / 2),
@@ -194,6 +195,8 @@ class VFBCircuitBrowser extends Component {
let self = this;
this.__isMounted = true;
this.updateGraph(this.state.neurons , Math.ceil((configuration.maxHops - configuration.minHops) / 2));
+ const { circuitQuerySelected } = this.props;
+ this.circuitQuerySelected = circuitQuerySelected;
}
componentDidUpdate () {
@@ -207,6 +210,10 @@ class VFBCircuitBrowser extends Component {
} else if ( !this.props.visible ) {
this.focused = false;
}
+
+ if ( this.circuitQuerySelected !== this.props.circuitQuerySelected ) {
+ this.circuitQuerySelected = this.props.circuitQuerySelected;
+ }
}
componentWillUnmount () {
@@ -214,10 +221,19 @@ class VFBCircuitBrowser extends Component {
}
/**
- * New neurons have been entered by user, update graph
+ * New neurons have been entered by user, update graph.
+ * @neurons (array): New list of neurons user has entered
*/
queriesUpdated (neurons) {
- this.updateGraph(neurons, this.state.hops);
+ // Check if new list of neurons is the same as the ones already rendered on last update
+ var is_same = (this.state.neurons.length == neurons.length) && this.state.neurons.every(function (element, index) {
+ return element === neurons[index];
+ });
+
+ // Request graph update if the list of new neurons is not the same
+ if ( !this.state.loading && !is_same ) {
+ this.updateGraph(neurons, this.state.hops);
+ }
}
/**
@@ -225,6 +241,7 @@ class VFBCircuitBrowser extends Component {
*/
updateHops (hops) {
this.setState({ hops : hops });
+ this.updateGraph(this.state.neurons, hops);
}
resetCamera () {
@@ -234,7 +251,7 @@ class VFBCircuitBrowser extends Component {
}
}
- resize(){
+ resize (){
this.graphResized = true;
this.setState( { reload : !this.state.reload } );
}
@@ -271,7 +288,7 @@ class VFBCircuitBrowser extends Component {
updateGraph (neurons, hops) {
if (this.__isMounted){
// Show loading spinner while cypher query search occurs
- this.setState({ loading : true , neurons : neurons, hops : hops });
+ this.setState({ loading : true , neurons : neurons, hops : hops, queryLoaded : false });
// Perform cypher query
this.queryResults(cypherQuery(neurons.map(d => `'${d}'`).join(','), hops));
}
@@ -308,7 +325,7 @@ class VFBCircuitBrowser extends Component {
worker.onmessage = function (e) {
switch (e.data.resultMessage) {
case "OK":
- self.setState( { graph : e.data.params.results , legend : e.data.params.colorLabels, loading : false });
+ self.setState( { graph : e.data.params.results , legend : e.data.params.colorLabels, loading : false, queryLoaded : true });
self.objectsLoaded = e.data.params.results.nodes.length;
setTimeout( function () {
self.resetCamera();
@@ -353,17 +370,10 @@ class VFBCircuitBrowser extends Component {
render () {
let self = this;
- const { classes } = this.props;
// Detect when the first load of the Graph component happens
- if ( !this.state.loading && this.firstLoad ) {
- // Reset CircuitQuerySelected value after first load
- this.circuitQuerySelected = "";
- }
- if ( !this.state.loading && !this.firstLoad ) {
- this.firstLoad = true;
- }
-
+ const { classes, circuitQuerySelected } = this.props;
+ this.circuitQuerySelected = circuitQuerySelected;
return (
this.state.loading
? this.state.graph.nodes.length > 0 }
resetCamera={self.resetCamera}
@@ -493,10 +504,7 @@ class VFBCircuitBrowser extends Component {
VFBCircuitBrowser.propTypes = { classes: PropTypes.object.isRequired };
function mapStateToProps (state) {
- return {
- circuitQuerySelected : state.generals.circuitQuerySelected,
- circuitBrowserSelected : state.generals.circuitBrowserSelected
- }
+ return { circuitQuerySelected : state.generals.circuitQuerySelected }
}
export default connect(mapStateToProps, null, null, { forwardRef : true } )(withStyles(styles)(VFBCircuitBrowser));
diff --git a/components/interface/VFBGraph/VFBGraph.js b/components/interface/VFBGraph/VFBGraph.js
index 6df87af9c..e272726b8 100644
--- a/components/interface/VFBGraph/VFBGraph.js
+++ b/components/interface/VFBGraph/VFBGraph.js
@@ -5,6 +5,7 @@ import CircularProgress from '@material-ui/core/CircularProgress';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Tooltip from '@material-ui/core/Tooltip';
+import { UPDATE_GRAPH } from './../../../actions/generals';
import { connect } from "react-redux";
/**
@@ -115,20 +116,18 @@ function refineData (e) {
});
// Worker is done, notify main thread
- this.postMessage({ resultMessage: "OK", params: { results: { nodes, links } } });
+ this.postMessage({ resultMessage: "OK", params: { results: { nodes, links }, id : e.data.params.value } });
}
class VFBGraph extends Component {
constructor (props) {
super(props);
- this.state = {
- graph : { nodes : [], links : [] },
- loading : true,
- currentQuery : this.props.instance.id,
+ this.state = {
+ graph : { nodes : [], links : [] },
+ currentQuery : this.props.instance != null ? this.props.instance.id : "",
dropDownAnchorEl : null,
optionsIconColor : stylingConfiguration.defaultRefreshIconColor,
- nodeSelected : { title : "", id : "" },
reload : false
}
this.updateGraph = this.updateGraph.bind(this);
@@ -143,6 +142,7 @@ class VFBGraph extends Component {
this.zoomOut = this.zoomOut.bind(this);
this.selectedNodeLoaded = this.selectedNodeLoaded.bind(this);
this.resize = this.resize.bind(this);
+ this.sync = this.sync.bind(this);
this.highlightNodes = new Set();
this.highlightLinks = new Set();
@@ -157,19 +157,23 @@ class VFBGraph extends Component {
this.graphResized = false;
this.focusedInstance = { id : "" };
this.selectedDropDownQuery = -1;
+ this.loading = true;
+ this.firstLoad = true;
+ this.nodeSelectedID = "";
}
componentDidMount () {
let self = this;
this.__isMounted = true;
- if (this.state.currentQuery !== undefined && this.state.currentQuery !== null){
- if (this.props.instance.getParent() !== null) {
- this.focusedInstance = this.props.instance.getParent();
- } else {
- this.focusedInstance = this.props.instance;
+ if (this.state.currentQuery !== undefined && this.state.currentQuery !== "" && this.state.currentQuery !== null){
+ if ( this.props.instance !== null ) {
+ if (this.props.instance.getParent() !== null) {
+ this.focusedInstance = this.props.instance.getParent();
+ } else {
+ this.focusedInstance = this.props.instance;
+ }
}
-
this.updateGraph();
}
@@ -184,21 +188,68 @@ class VFBGraph extends Component {
if (event.isComposing || event.keyCode === 16) {
self.shiftOn = false;
}
- });
+ });
}
componentDidUpdate () {
let self = this;
+
+ if (this.loading && this.firstLoad) {
+ if (this.state.currentQuery === undefined || this.state.currentQuery === "" || this.state.currentQuery === null){
+ if (this.props.instance !== null && this.props.instance !== undefined) {
+ if (this.props.instance.getParent() !== null) {
+ this.focusedInstance = this.props.instance.getParent();
+ } else {
+ this.focusedInstance = this.props.instance;
+ }
+ this.firstLoad = false;
+ this.updateGraph();
+ } else if (this.props.instanceOnFocus !== null && this.props.instanceOnFocus !== undefined) {
+ if (this.props.instanceOnFocus.getParent() !== null) {
+ this.focusedInstance = this.props.instanceOnFocus.getParent();
+ } else {
+ this.focusedInstance = this.props.instanceOnFocus;
+ }
+ this.firstLoad = false;
+ this.updateGraph();
+ }
+ }
+ }
+
+
// Reset camera if graph component is visible, not focused or has been resized
if ( this.props.visible && ( !this.focused || this.graphResized ) ) {
- setTimeout( function () {
+ /*
+ * Update graph with selected query index from configuration dropdown selection, this is to allow to lauch the component to be launched
+ * with specific configuration dropdown query.
+ */
+ stylingConfiguration.dropDownQueries.map((item, index) => {
+ if ( parseInt(self.props.graphQueryIndex) >= 0 && self.firstLoad ) {
+ if ( parseInt(self.props.graphQueryIndex) === index ) {
+ self.selectedDropDownQuery = index;
+ self.loading = true;
+ let idToSearch = self.props.instanceOnFocus.id;
+ if (self.props.instanceOnFocus.getParent() !== null) {
+ idToSearch = self.props.instanceOnFocus.getParent().id;
+ }
+ self.queryResults(item.query(idToSearch), idToSearch);
+ }
+ }
+ });
+ // Reset camera view after graph component becomes visible
+ setTimeout( function () {
self.resetCamera();
self.focused = true;
+ self.loading = false;
self.graphResized = false;
- }, (self.objectsLoaded * 20));
+ }, (self.objectsLoaded * 50));
} else if ( !this.props.visible ) {
this.focused = false;
- this.selectedDropDownQuery = -1;
+ if ( parseInt(this.props.graphQueryIndex) >= 0 && !this.firstLoad ) {
+ this.selectedDropDownQuery = -1;
+ this.props.vfbGraph(UPDATE_GRAPH, this.focusedInstance, -1);
+ }
+ this.firstLoad = true;
}
}
@@ -212,8 +263,8 @@ class VFBGraph extends Component {
this.focused = true;
}
}
-
- resize(){
+
+ resize (){
this.graphResized = true;
this.setState( { reload : !this.state.reload } );
}
@@ -240,6 +291,7 @@ class VFBGraph extends Component {
* Handle Left click on Nodes
*/
handleNodeLeftClick (node, event) {
+ this.nodeSelectedID = node.title;
this.queryNewInstance(node);
}
@@ -250,6 +302,11 @@ class VFBGraph extends Component {
this.graphRef.current.ggv.current.centerAt(node.x , node.y, 1000);
this.graphRef.current.ggv.current.zoom(2, 1000);
}
+
+ sync () {
+ this.instanceFocusChange(this.props.instanceOnFocus);
+ this.updateGraph();
+ }
/**
* Handle Menu drop down clicks
@@ -257,9 +314,10 @@ class VFBGraph extends Component {
handleMenuClick (query) {
if (this.__isMounted){
// Show loading spinner while cypher query search occurs
- this.setState({ loading : true , dropDownAnchorEl : null });
+ this.loading = true;
+ this.setState({ dropDownAnchorEl : null });
// Perform cypher query
- this.queryResults(query(this.state.currentQuery))
+ this.queryResults(query(this.state.currentQuery), this.state.currentQuery)
}
}
@@ -268,21 +326,20 @@ class VFBGraph extends Component {
*/
queryNewInstance (node) {
window.addVfbId(node.title);
- this.setState({ loading : true, nodeSelected : node, currentQuery : node.title, optionsIconColor : stylingConfiguration.defaultRefreshIconColor });
+ this.loading = true;
+ this.setState({ optionsIconColor : stylingConfiguration.defaultRefreshIconColor });
// Perform cypher query
this.queryResults(cypherQuery(node.title), node.title)
}
selectedNodeLoaded (instance) {
- var loadedId = null;
+ var loadedId = instance.id;
if (instance.getParent() !== null) {
loadedId = instance.getParent().id;
- } else {
- loadedId = instance.id;
}
- if ( this.state.nodeSselected ) {
- if ( this.state.nodeSelected.title === loadedId ) {
+ if ( this.nodeSselected ) {
+ if ( this.nodeSelectedID === loadedId ) {
return true;
}
}
@@ -307,20 +364,19 @@ class VFBGraph extends Component {
* Re-render graph with a new instance
*/
updateGraph () {
- var idToSearch = null;
+ var idToSearch = this.focusedInstance.id;
/*
* function handler called by the VFBMain whenever there is an update of the instance on focus,
* this will reflect and move to the node (if it exists) that we have on focus.
*/
if (this.focusedInstance.getParent() !== null) {
idToSearch = this.focusedInstance.getParent().id;
- } else {
- idToSearch = this.focusedInstance.id;
}
if (this.__isMounted){
// Show loading spinner while cypher query search occurs
- this.setState({ loading : true, currentQuery : idToSearch, optionsIconColor : stylingConfiguration.defaultRefreshIconColor });
+ this.loading = true;
+ this.setState({ optionsIconColor : stylingConfiguration.defaultRefreshIconColor });
// Perform cypher query
this.queryResults(cypherQuery(idToSearch), idToSearch);
}
@@ -357,7 +413,10 @@ class VFBGraph extends Component {
worker.onmessage = function (e) {
switch (e.data.resultMessage) {
case "OK":
- self.setState( { graph : e.data.params.results , loading : false });
+ self.loading = false;
+ self.firstLoad = false;
+ self.focusedInstance = e.data.params;
+ self.setState( { graph : e.data.params.results, currentQuery : e.data.params.id });
self.objectsLoaded = e.data.params.results.nodes.length;
setTimeout( function () {
self.resetCamera();
@@ -373,7 +432,7 @@ class VFBGraph extends Component {
worker.postMessage({ message: "refine", params: { results: response.data, value: instanceID, configuration : configuration, NODE_WIDTH : NODE_WIDTH, NODE_HEIGHT : NODE_HEIGHT } });
})
.catch( function (error) {
- self.setState( { loading : false } );
+ self.loading = false;
})
}
@@ -401,46 +460,43 @@ class VFBGraph extends Component {
render () {
let self = this;
- const { instanceOnFocus, graphQueryIndex } = this.props;
-
+ const { graphQueryIndex } = this.props;
+
let syncColor = this.state.optionsIconColor;
- let loading = this.state.loading;
- if ( this.focusedInstance.id !== "" && instanceOnFocus !== this.focusedInstance.id ) {
- this.instanceFocusChange(instanceOnFocus);
-
+ if (Object.keys(this.props.instanceOnFocus).length === 0 && this.props.instanceOnFocus.constructor === Object) {
+ return (
+ Model not loaded, graph not available yet
+ );
+ }
+
+ if ( this.focusedInstance.id !== "" && !this.props.instanceOnFocus.id.includes(this.focusedInstance.id) ) {
+ // If the length of the graph is 0, request a new query using the instanceOnFocus
if ( this.state.graph.nodes.length === 0 && this.state.graph.links.length === 0 && this.focusedInstance.id !== this.state.currentQuery ){
- let idToSearch = "";
+ let idToSearch = this.focusedInstance.id;
if (this.focusedInstance.getParent() !== null) {
idToSearch = this.focusedInstance.getParent().id;
- } else {
- idToSearch = this.focusedInstance.id;
}
+
syncColor = stylingConfiguration.defaultRefreshIconColor;
// Perform cypher query
- loading = true;
+ this.loading = true;
this.queryResults(cypherQuery(idToSearch), idToSearch)
}
+
+ // Out of sync if instanceOnFocus is not what's on display
if ( this.focusedInstance.id !== this.state.currentQuery ) {
syncColor = stylingConfiguration.outOfSyncIconColor;
}
- } else if (this.focusedInstance.id !== "" && instanceOnFocus === this.focusedInstance.id ){
- stylingConfiguration.dropDownQueries.map((item, index) => {
- if ( self.selectedDropDownQuery === -1 || self.selectedDropDownQuery !== parseInt(graphQueryIndex) ) {
- if ( parseInt(graphQueryIndex) === index ) {
- self.selectedDropDownQuery = index;
- loading = true;
- self.queryResults(item.query(instanceOnFocus));
- }
- }
- })
}
-
- if ( !instanceOnFocus.id.includes(this.focusedInstance.id) ) {
+
+ // Out of sync if instanceOnFocus is not what's on display
+ if ( !this.props.instanceOnFocus.id.includes(this.focusedInstance.id) ) {
syncColor = stylingConfiguration.outOfSyncIconColor;
}
+
return (
- loading
+ this.loading
?
- Reset View}>
+ Reset View}>
- Zoom In}>
+ Zoom In}>
- Zoom Out}>
+ Zoom Out}>
- Refresh}>
+ Refresh for {this.focusedInstance.name} }>
+ onClick={self.sync}>
Options}>
@@ -691,4 +747,8 @@ function mapStateToProps (state) {
}
}
-export default connect(mapStateToProps, null, null, { forwardRef : true } )(VFBGraph);
+function mapDispatchToProps (dispatch) {
+ return { vfbGraph: (type, path, index) => dispatch ( { type : type, data : { instance : path, queryIndex : index } } ) }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef : true } )(VFBGraph);
diff --git a/components/interface/VFBListViewer/ListViewerControlsMenu.js b/components/interface/VFBListViewer/ListViewerControlsMenu.js
index 429ab9e38..44abe0df8 100644
--- a/components/interface/VFBListViewer/ListViewerControlsMenu.js
+++ b/components/interface/VFBListViewer/ListViewerControlsMenu.js
@@ -1,14 +1,11 @@
import React, { Component } from "react";
import Menu from "@geppettoengine/geppetto-ui/menu/Menu";
import { connect } from 'react-redux';
-import controlsConfiguration from '../../configuration/VFBListViewer/controlsMenuConfiguration';
import { SliderPicker } from 'react-color';
import { setTermInfo, SHOW_LIST_VIEWER, INSTANCE_DELETED } from './../../../actions/generals';
-// Special control actions handled by the menu handler
-const COLOR = 'color';
-const INFO = 'info';
-const DELETE = 'delete';
+const controlsConfiguration = require('../../configuration/VFBListViewer/controlsMenuConfiguration').default;
+const ACTIONS = controlsConfiguration.actions;
/**
* Menu component to display controls for VFB List Viewer
@@ -47,12 +44,28 @@ class ListViewerControlsMenu extends Component {
* Handles menu option selection
*/
menuHandler (action, component) {
- // If action belongs to color control, display the color picker
- if ( action === COLOR ) {
- this.setState({ displayColorPicker: true });
- } else if ( action === INFO ) {
- // If action belongs to 'info' control, display term info
- let self = this;
+ switch (action.handlerAction){
+ case ACTIONS.SHOW:
+ this.props.instance.show();
+ break;
+ case ACTIONS.HIDE:
+ this.props.instance.hide();
+ break;
+ case ACTIONS.SELECT:
+ this.props.instance.select();
+ break;
+ case ACTIONS.DESELECT:
+ this.props.instance.deselect();
+ break;
+ case ACTIONS.ZOOM_TO:
+ GEPPETTO.SceneController.zoomTo([this.props.instance]);
+ break;
+ case ACTIONS.DELETE:
+ this.props.instance.delete();
+ this.props.instanceDeleted(INSTANCE_DELETED, this.props.instance);
+ break;
+ case ACTIONS.INFO:
+ var self = this;
/**
* Needs a 100 ms delay before calling the setTermInfo method, this is due to Menu taking
* a few ms to close.
@@ -60,12 +73,60 @@ class ListViewerControlsMenu extends Component {
setTimeout ( () => {
self.props.setTermInfo(self.props.instance, true);
}, 100);
- } else if ( action === DELETE ) {
- this.props.instance.delete();
- this.props.instanceDeleted(INSTANCE_DELETED, this.props.instance);
- } else {
- // For every other action execute the action as is
- action(this.props.instance);
+ break;
+ case ACTIONS.COLOR:
+ this.setState({ displayColorPicker: true });
+ break;
+ case ACTIONS.SHOW_VOLUME:
+ var color = this.props.instance.getColor();
+ var instance = this.props.instance[this.props.instance.getId() + "_obj"];
+ if ( instance === undefined ) {
+ instance = this.props.instance.getType()[this.props.instance.getId() + "_obj"];
+ }
+ if (instance.getType().getMetaType() == GEPPETTO.Resources.IMPORT_TYPE) {
+ var self = this;
+ instance.getType().resolve(function () {
+ self.props.instance.setColor(color);
+ GEPPETTO.trigger('experiment:visibility_changed', instance);
+ GEPPETTO.ControlPanel.refresh();
+ });
+ } else {
+ if (GEPPETTO.SceneController.isInstancePresent(instance)) {
+ GEPPETTO.SceneController.show([instance]);
+ } else {
+ GEPPETTO.SceneController.display(instance); instance.setColor(color);
+ }
+ }
+ break;
+ case ACTIONS.HIDE_VOLUME:
+ var instance = this.props.instance[this.props.instance.getId() + "_obj"];
+ if ( instance === undefined ) {
+ instance = this.props.instance.getType()[this.props.instance.getId() + "_obj"];
+ }
+ instance.hide();
+ break;
+ case ACTIONS.SHOW_SKELETON:
+ var color = this.props.instance.getColor();
+ var instance = this.props.instance[this.props.instance.getId() + "_swc"];
+ if (instance.getType().getMetaType() == GEPPETTO.Resources.IMPORT_TYPE) {
+ var col = instance.getParent().getColor();
+ instance.getType().resolve( function () {
+ instance.setColor(col);
+ GEPPETTO.trigger('experiment:visibility_changed', instance);
+ GEPPETTO.ControlPanel.refresh();
+ });
+ } else {
+ if (GEPPETTO.SceneController.isInstancePresent(instance)) {
+ GEPPETTO.SceneController.show([instance]);
+ } else {
+ GEPPETTO.SceneController.display(instance);
+ instance.setColor(color);
+ }
+ }
+ break;
+ case ACTIONS.HIDE_SKELETON:
+ GEPPETTO.SceneController.hide([this.props.instance[this.props.instance.getId() + "_swc"]]);
+ break;
}
}
@@ -83,7 +144,14 @@ class ListViewerControlsMenu extends Component {
// Button configuration has two options, perform condition to determine which button to use
if ( item.toggle ){
let condition = item.toggle.condition(this.props.instance);
- list.push(item.toggle.options[condition]);
+ if ( item.toggle.isVisible !== undefined) {
+ let visible = item.toggle.isVisible(this.props.instance);
+ if ( visible ) {
+ list.push(item.toggle.options[condition]);
+ }
+ } else {
+ list.push(item.toggle.options[condition]);
+ }
} else {
if ( item.isVisible !== undefined) {
let visible = item.isVisible(this.props.instance);
@@ -96,6 +164,7 @@ class ListViewerControlsMenu extends Component {
}
}
+
/**
* Iterate through button list in Menu configuration
*/
diff --git a/components/interface/VFBListViewer/VFBListViewer.js b/components/interface/VFBListViewer/VFBListViewer.js
index 35fc8f66d..6f0ff6d4e 100644
--- a/components/interface/VFBListViewer/VFBListViewer.js
+++ b/components/interface/VFBListViewer/VFBListViewer.js
@@ -7,6 +7,7 @@ import { connect } from 'react-redux';
require('../../../css/VFBListViewer.less');
const VISUAL_TYPE = "VisualType";
+const COMPOSITE_VISUAL_TYPE = "CompositeVisualType";
/**
* Wrapper class that connects geppetto-client's ListViewer component with VFB.
@@ -36,19 +37,19 @@ class VFBListViewer extends Component {
getInstances () {
// Retrieve all instances from the ModelFactory
let entities = GEPPETTO.ModelFactory.allPaths;
- var visuals = [];
+ var visuals = {};
const { instanceDeleted, idsMap, idsList } = this.props;
// Match Visual Types from ModelFactory
for (var i = 0; i < entities.length; i++) {
- if (entities[i].metaType === VISUAL_TYPE ) {
- if (idsList.includes(entities[i].path.split(".")[0])){
- visuals.push(entities[i]);
+ if (entities[i].metaType === VISUAL_TYPE || entities[i].metaType === COMPOSITE_VISUAL_TYPE ) {
+ if (idsList.includes(entities[i].path.split(".")[0]) && visuals[entities[i].path] === undefined ){
+ visuals[entities[i].path.split(".")[0]] = entities[i];
}
}
}
- return visuals;
+ return Object.values(visuals);
}
render () {
@@ -60,7 +61,7 @@ class VFBListViewer extends Component {
handler={this}
filter={() => true}
columnConfiguration={this.getColumnConfiguration()}
- showPagination={false}
+ infiniteScroll={true}
/>
}
diff --git a/components/interface/VFBTermInfo/VFBTermInfo.js b/components/interface/VFBTermInfo/VFBTermInfo.js
index 889e210a2..ab37a2c72 100644
--- a/components/interface/VFBTermInfo/VFBTermInfo.js
+++ b/components/interface/VFBTermInfo/VFBTermInfo.js
@@ -4,7 +4,7 @@ import Slider from "react-slick";
import Collapsible from 'react-collapsible';
import HTMLViewer from '@geppettoengine/geppetto-ui/html-viewer/HTMLViewer';
import ButtonBarComponent from '@geppettoengine/geppetto-client/components/widgets/popup/ButtonBarComponent';
-import { SHOW_GRAPH, LOAD_CIRCUIT_BROWSER } from './../../../actions/generals';
+import { SHOW_GRAPH, UPDATE_CIRCUIT_QUERY } from './../../../actions/generals';
import { connect } from "react-redux";
var $ = require('jquery');
@@ -126,14 +126,26 @@ class VFBTermInfo extends React.Component {
// Look for root node, create a Variable object with the graphs configuration, and attach it to root type object
if (type.getMetaType() == GEPPETTO.Resources.COMPOSITE_TYPE_NODE) {
- var graphType = new Type({ wrappedObj : { name : GRAPHS, eClass : GRAPHS } })
+ let variables = type.getVariables();
+ let present = false;
- // Variable object holding the information for the graph links
- var graphsVariable = new Variable({ wrappedObj : { name : "Graph for" }, values : graphs });
- graphsVariable.setTypes([graphType]);
+ // Check if link has been added already, if it has, don't add it again
+ for ( var i = 0; i < variables.length; i++ ){
+ if ( variables[i].types[0].wrappedObj.name === GRAPHS ){
+ present = true;
+ }
+ }
+
+ if ( !present ) {
+ var graphType = new Type({ wrappedObj : { name : GRAPHS, eClass : GRAPHS } })
+
+ // Variable object holding the information for the graph links
+ var graphsVariable = new Variable({ wrappedObj : { name : "Graph for" }, values : graphs });
+ graphsVariable.setTypes([graphType]);
- // Add graphs Variable to root node
- type.getVariables().push(graphsVariable);
+ // Add graphs Variable to root node
+ type.getVariables().push(graphsVariable);
+ }
}
}
@@ -153,14 +165,25 @@ class VFBTermInfo extends React.Component {
// Look for root node, create a Variable object with the graphs configuration, and attach it to root type object
if (type.getMetaType() == GEPPETTO.Resources.COMPOSITE_TYPE_NODE) {
- var circuitBrowserType = new Type({ wrappedObj : { name : CIRCUIT_BROWSER, eClass : CIRCUIT_BROWSER } })
+ let variables = type.getVariables();
+ let present = false;
+ // Check if link has been added already, if it has, don't add it again
+ for ( var i = 0; i < variables.length; i++ ){
+ if ( variables[i].types[0].wrappedObj.name === CIRCUIT_BROWSER ){
+ present = true;
+ }
+ }
- // Variable object holding the information for the graph links
- var circuitBrowserVariable = new Variable({ wrappedObj : { name : "Circuit Browser for" }, values : circuitBrowser });
- circuitBrowserVariable.setTypes([circuitBrowserType]);
+ if ( !present ) {
+ var circuitBrowserType = new Type({ wrappedObj : { name : CIRCUIT_BROWSER, eClass : CIRCUIT_BROWSER } })
- // Add graphs Variable to root node
- type.getVariables().push(circuitBrowserVariable);
+ // Variable object holding the information for the graph links
+ var circuitBrowserVariable = new Variable({ wrappedObj : { name : "Circuit Browser for" }, values : circuitBrowser });
+ circuitBrowserVariable.setTypes([circuitBrowserType]);
+
+ // Add graphs Variable to root node
+ type.getVariables().push(circuitBrowserVariable);
+ }
}
}
@@ -711,10 +734,9 @@ class VFBTermInfoWidget extends React.Component {
// Show Circuit Browser
const { vfbCircuitBrowser } = this.props;
/*
- * Path contains the instance and the index of the drop down query options
- * Path is of type : "instance_path, query_index"
+ * Path contains the instancE ID passed to the circuit browser
*/
- vfbCircuitBrowser(LOAD_CIRCUIT_BROWSER, path.split(',')[1]);
+ vfbCircuitBrowser(UPDATE_CIRCUIT_QUERY, path.split(',')[1]);
// Notify VFBMain UI needs to be updated
this.props.uiUpdated();
diff --git a/components/interface/VFBToolbar/VFBToolBar.js b/components/interface/VFBToolbar/VFBToolBar.js
index fc0fbb0bc..5a3ad5252 100644
--- a/components/interface/VFBToolbar/VFBToolBar.js
+++ b/components/interface/VFBToolbar/VFBToolBar.js
@@ -95,10 +95,6 @@ export default class VFBToolBar extends React.Component {
clickFeedback () {
var htmlContent = this.feedbackHTML;
window.ga('vfb.send', 'pageview', (window.location.pathname + '?page=Feedback'));
- // add clinet data to console
- $.getJSON('http://gd.geobytes.com/GetCityDetails?callback=?', function (data) {
- console.log('USER: ' + data.geobytesipaddress + ' ' + data.geobytesfqcn);
- });
// report console log for agrigated analysis
window.ga('vfb.send', 'feedback', window.location.href, window.console.logs.join('\n').replace(/\#/g,escape('#')), );
@@ -151,26 +147,10 @@ export default class VFBToolBar extends React.Component {
}
// return as much of the log up to the last 10 events < 1000 characters:
var logLength = -50;
- var limitedLog = window.console.logs.slice(logLength).join('%0A').replace(
- /\&/g,escape('&')
- ).replace(
- /\#/g,escape('#')
- ).replace(
- /\-/g,'%2D'
- ).replace(
- /\+/g,'%2B'
- );
- while (limitedLog.length > 1000 && logLength < 0) {
+ var limitedLog = window.console.logs.slice(logLength).join('/n');
+ while (limitedLog.length < 1000 && logLength < 0) {
logLength += 1;
- limitedLog = window.console.logs.slice(logLength).join('%0A').replace(
- /\&/g,escape('&')
- ).replace(
- /\#/g,escape('#')
- ).replace(
- /\-/g,'%2D'
- ).replace(
- /\+/g,'%2B'
- );
+ limitedLog = window.console.logs.slice(logLength).join('/n');
}
this.props.htmlOutputHandler(
htmlContent.replace(
diff --git a/reducers/generals.js b/reducers/generals.js
index 50350b189..ed054b7c4 100644
--- a/reducers/generals.js
+++ b/reducers/generals.js
@@ -8,7 +8,8 @@ import {
SHOW_LIST_VIEWER,
LOAD_CYPHER_QUERIES,
SHOW_GRAPH,
- LOAD_CIRCUIT_BROWSER,
+ UPDATE_GRAPH,
+ UPDATE_CIRCUIT_QUERY,
INSTANCE_SELECTED,
INSTANCE_VISIBILITY_CHANGED,
VFB_LOAD_TERM_INFO
@@ -25,15 +26,14 @@ export const GENERAL_DEFAULT_STATE = {
stepsToLoad: 1,
stepsLoaded: 0,
loading: false,
- graphQueryIndex : {},
+ graphQueryIndex : -1,
instanceOnFocus : {},
instanceSelection : {},
instanceDeleted : {},
instanceVisibilityChanged : false,
termInfoVisible : false,
listViewerInfoVisible : true,
- circuitBrowserSelected : false,
- circuitQuerySelected : {},
+ circuitQuerySelected : [],
layout: {
"ThreeDViewer": true,
"StackViewer": true,
@@ -140,13 +140,6 @@ function generalReducer (state, action) {
var idsLoaded = state.idsLoaded;
var newMap = { ...state.idsMap };
- if (newMap[action.data.id] === undefined ) {
- return {
- ...state,
- error: "instance " + action.data.id + "is not present anymore in the map"
- };
- }
-
if (newMap[action.data.id] !== undefined && newMap[action.data.id].components[action.data.component]) {
var newComponents = { ...newMap[action.data.id].components };
newMap[action.data.id].components = newComponents;
@@ -195,7 +188,9 @@ function generalReducer (state, action) {
stepsToLoad: 0,
stepsLoaded: 0,
idsMap: newMap,
- loading: loading
+ loading: loading,
+ instanceOnFocus : Instances[action.data.id] != null ? Instances[action.data.id] : {},
+ idsList : !state.idsList.includes(action.data.id) ? [ ...state.idsList, action.data.id ] : [ ...state.idsList ]
};
}
case VFB_UI_UPDATED:
@@ -209,11 +204,22 @@ function generalReducer (state, action) {
graphQueryIndex : action.data.queryIndex,
instanceOnFocus : action.data.instance
};
- case LOAD_CIRCUIT_BROWSER:
+ case UPDATE_GRAPH:
+ return {
+ ...state,
+ graphQueryIndex : action.data.queryIndex
+ };
+ case UPDATE_CIRCUIT_QUERY:
+ var newQueryMap = [];
+ if ( Array.isArray(action.data.instance) ) {
+ newQueryMap = action.data.instance;
+ } else {
+ !state.circuitQuerySelected.includes(action.data.instance) ? newQueryMap = [...state.circuitQuerySelected, action.data.instance] : newQueryMap = [...state.circuitQuerySelected];
+ }
+
return {
...state,
- circuitQuerySelected : action.data.instance,
- circuitBrowserSelected : true
+ circuitQuerySelected : newQueryMap,
};
case INSTANCE_ADDED:
var newMap = { ...state.idsMap };
@@ -239,7 +245,9 @@ function generalReducer (state, action) {
}
return {
...state,
- idsMap: newMap
+ idsMap: newMap,
+ instanceOnFocus : Instances[newInstance[0]] != null ? Instances[newInstance[0]] : {},
+ idsList : !state.idsList.includes(action.data) ? [ ...state.idsList, action.data ] : [ ...state.idsList ]
};
case INSTANCE_SELECTED:
return {