Loading src/components/GraphEditor/GraphEditor.jsx +68 −73 Original line number Diff line number Diff line Loading @@ -32,11 +32,11 @@ const coseBilkentOptions = { name: 'cose-bilkent', // Called on `layoutready` ready: function ready() { console.log('coseBilkent ready'); // console.log('coseBilkent ready'); }, // Called on `layoutstop` stop: function stop() { console.log('coseBilkent stop'); // console.log('coseBilkent stop'); }, // Whether to include labels in node dimensions. Useful for avoiding label overlap nodeDimensionsIncludeLabels: true, Loading Loading @@ -112,8 +112,8 @@ export default class GraphEditor extends Component { constructor(props) { super(props); this.styles = DEFAULT_STYLES.concat(this.props.extraStyles); this.reposition = {}; const {extraStyles} = props; this.styles = DEFAULT_STYLES.concat(extraStyles); this.cyId = `cy-${Date.now()}`; } Loading Loading @@ -190,26 +190,26 @@ export default class GraphEditor extends Component { const node = nodes[id]; return JSON.parse(JSON.stringify(node.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || node.registries.position || {x: 100, y: 100})); || node.registries.position || { x: 100, y: 100, })); }; const isRenderedAsGmeConnection = (id) => { return nodes[id] && typeof nodes[id].pointers.src === 'string' && typeof nodes[id].pointers.dst === 'string' && targetsExist(nodes[id].pointers.src, nodes[id].pointers.dst); } const targetsExist = (src, dst) => nodes[src] && nodes[dst] && src !== activeNode && dst !== activeNode; const targetsExist = (src, dst) => { return nodes[src] && nodes[dst] && src !== activeNode && dst !== activeNode; }; const isRenderedAsGmeConnection = id => nodes[id] && typeof nodes[id].pointers.src === 'string' && typeof nodes[id].pointers.dst === 'string' && targetsExist(nodes[id].pointers.src, nodes[id].pointers.dst); const hasEdgeTargets = (src, dst) => { function isEdge(target) { return nodes[target].pointers && typeof nodes[target].pointers.src === 'string' && typeof nodes[target].pointers.dst === 'string' && targetsExist(nodes[target].pointers.src, nodes[target].pointers.dst); return nodes[target].pointers && typeof nodes[target].pointers.src === 'string' && typeof nodes[target].pointers.dst === 'string' && targetsExist(nodes[target].pointers.src, nodes[target].pointers.dst); } return isEdge(src) || isEdge(dst); Loading Loading @@ -327,8 +327,8 @@ ${activeSelection.includes(id) ? 'in-active-selection' : ''}`, Object.keys(childData.pointers) .forEach((pName) => { const targetId = childData.pointers[pName]; if (!targetId || activeFilters[`pointers$${pName}`] || !targetsExist(id, targetId) || (isGmeConnection && (pName === 'src' || pName === 'dst'))) { if (!targetId || activeFilters[`pointers$${pName}`] || !targetsExist(id, targetId) || (isGmeConnection && (pName === 'src' || pName === 'dst'))) { return; } Loading Loading @@ -371,26 +371,31 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, return result; } storePosition = (nodeId) => { const {gmeClient} = this.props; storePositions = (repositions) => { const {gmeClient, nodes} = this.props; if (Object.keys(this.reposition).length > 0) { if (repositions.length > 0) { gmeClient.startTransaction(); Object.keys(this.reposition) .forEach((id) => { if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) { gmeClient.setRegistry( id, CONSTANTS.CYTOSCAPE_POS_REG_KEY, this.reposition[id].position, ); repositions.forEach((posData) => { const childNodeDesc = nodes[posData.id]; if (!childNodeDesc) { return; } }); const floorPos = { x: Math.floor(posData.position.x), y: Math.floor(posData.position.y), }; if (!childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || floorPos.x !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x || floorPos.y !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y) { gmeClient.setRegistry(posData.id, CONSTANTS.CYTOSCAPE_POS_REG_KEY, floorPos); } }); gmeClient.completeTransaction(); } this.reposition = {}; }; showFilterSelector = () => { Loading @@ -417,51 +422,42 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, }); }; attachCytoscapeHandlers() { this.cy.on('position', ({target}) => { const {nodes} = this.props; const nodeId = target.id(); const childNodeDesc = nodes[nodeId]; if (!childNodeDesc) { return; } const floorPos = { x: Math.floor(target.position().x), y: Math.floor(target.position().y), atLayoutStop = () => { this.storePositions(this.cy.nodes('.gme-node') .map(ele => ({ id: ele.id(), position: ele.position(), }))); }; if (!childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY]) { this.reposition[nodeId] = { id: nodeId, position: floorPos, }; } else if (floorPos.x !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x || floorPos.y !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y) { this.reposition[nodeId] = { id: nodeId, position: floorPos, }; } }); this.cy.on('free', ({target}) => { attachCytoscapeHandlers() { this.cy.on('dragfree', ({target}) => { const {setActiveSelection} = this.props; if (typeof target.id === 'function' && target.hasClass('aux-node') === false) { this.storePosition(target.id()); const repositions = [ { id: target.id(), position: target.position(), }, ]; target.descendants('node.gme-node') .forEach(child => repositions.push({ id: child.id(), position: child.position(), })); this.storePositions(repositions); setTimeout(() => { setActiveSelection([target.id()]); }); } else { // Free outside of canvas this.reposition = {}; setActiveSelection([]); } }); this.cy.on('vclick', 'node', (e) => { this.reposition = {}; const {setActiveSelection, gmeClient} = this.props; const {createPointer} = this.state; if (typeof e.target.id === 'function') { Loading @@ -469,7 +465,6 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, this.setState({createPointer: null}); gmeClient.setPointer(createPointer.nodeId, createPointer.ptrName, createPointer.target); } else if (e.target.hasClass('aux-node') === false) { // console.log('vclick', e.target.id(), JSON.stringify(this.reposition)); this.setState({ showNodeMenu: { id: e.target.id(), Loading @@ -494,9 +489,9 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, this.cy.on('mouseover', 'node', (e) => { const {createPointer} = this.state; const {gmeClient} = this.props; if (createPointer && typeof e.target.id === 'function' && e.target.hasClass('aux-node') === false) { if (createPointer && typeof e.target.id === 'function' && e.target.hasClass('aux-node') === false) { const gmeNode = gmeClient.getNode(e.target.id()); if (gmeNode && gmeNode.isValidTargetOf(createPointer.nodeId, createPointer.ptrName) && createPointer.target !== e.target.id()) { Loading Loading @@ -561,7 +556,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, onClick={() => { const layout = this.cy.layout(coseBilkentOptions); layout.on('layoutstop', () => { this.storePosition(activeNode); this.atLayoutStop(); }); layout.run(); }} Loading @@ -574,7 +569,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, onClick={() => { const layout = this.cy.layout({name: 'dagre'}); layout.on('layoutstop', () => { this.storePosition(activeNode); this.atLayoutStop(); }); layout.run(); }} Loading Loading
src/components/GraphEditor/GraphEditor.jsx +68 −73 Original line number Diff line number Diff line Loading @@ -32,11 +32,11 @@ const coseBilkentOptions = { name: 'cose-bilkent', // Called on `layoutready` ready: function ready() { console.log('coseBilkent ready'); // console.log('coseBilkent ready'); }, // Called on `layoutstop` stop: function stop() { console.log('coseBilkent stop'); // console.log('coseBilkent stop'); }, // Whether to include labels in node dimensions. Useful for avoiding label overlap nodeDimensionsIncludeLabels: true, Loading Loading @@ -112,8 +112,8 @@ export default class GraphEditor extends Component { constructor(props) { super(props); this.styles = DEFAULT_STYLES.concat(this.props.extraStyles); this.reposition = {}; const {extraStyles} = props; this.styles = DEFAULT_STYLES.concat(extraStyles); this.cyId = `cy-${Date.now()}`; } Loading Loading @@ -190,26 +190,26 @@ export default class GraphEditor extends Component { const node = nodes[id]; return JSON.parse(JSON.stringify(node.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || node.registries.position || {x: 100, y: 100})); || node.registries.position || { x: 100, y: 100, })); }; const isRenderedAsGmeConnection = (id) => { return nodes[id] && typeof nodes[id].pointers.src === 'string' && typeof nodes[id].pointers.dst === 'string' && targetsExist(nodes[id].pointers.src, nodes[id].pointers.dst); } const targetsExist = (src, dst) => nodes[src] && nodes[dst] && src !== activeNode && dst !== activeNode; const targetsExist = (src, dst) => { return nodes[src] && nodes[dst] && src !== activeNode && dst !== activeNode; }; const isRenderedAsGmeConnection = id => nodes[id] && typeof nodes[id].pointers.src === 'string' && typeof nodes[id].pointers.dst === 'string' && targetsExist(nodes[id].pointers.src, nodes[id].pointers.dst); const hasEdgeTargets = (src, dst) => { function isEdge(target) { return nodes[target].pointers && typeof nodes[target].pointers.src === 'string' && typeof nodes[target].pointers.dst === 'string' && targetsExist(nodes[target].pointers.src, nodes[target].pointers.dst); return nodes[target].pointers && typeof nodes[target].pointers.src === 'string' && typeof nodes[target].pointers.dst === 'string' && targetsExist(nodes[target].pointers.src, nodes[target].pointers.dst); } return isEdge(src) || isEdge(dst); Loading Loading @@ -327,8 +327,8 @@ ${activeSelection.includes(id) ? 'in-active-selection' : ''}`, Object.keys(childData.pointers) .forEach((pName) => { const targetId = childData.pointers[pName]; if (!targetId || activeFilters[`pointers$${pName}`] || !targetsExist(id, targetId) || (isGmeConnection && (pName === 'src' || pName === 'dst'))) { if (!targetId || activeFilters[`pointers$${pName}`] || !targetsExist(id, targetId) || (isGmeConnection && (pName === 'src' || pName === 'dst'))) { return; } Loading Loading @@ -371,26 +371,31 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, return result; } storePosition = (nodeId) => { const {gmeClient} = this.props; storePositions = (repositions) => { const {gmeClient, nodes} = this.props; if (Object.keys(this.reposition).length > 0) { if (repositions.length > 0) { gmeClient.startTransaction(); Object.keys(this.reposition) .forEach((id) => { if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) { gmeClient.setRegistry( id, CONSTANTS.CYTOSCAPE_POS_REG_KEY, this.reposition[id].position, ); repositions.forEach((posData) => { const childNodeDesc = nodes[posData.id]; if (!childNodeDesc) { return; } }); const floorPos = { x: Math.floor(posData.position.x), y: Math.floor(posData.position.y), }; if (!childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || floorPos.x !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x || floorPos.y !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y) { gmeClient.setRegistry(posData.id, CONSTANTS.CYTOSCAPE_POS_REG_KEY, floorPos); } }); gmeClient.completeTransaction(); } this.reposition = {}; }; showFilterSelector = () => { Loading @@ -417,51 +422,42 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, }); }; attachCytoscapeHandlers() { this.cy.on('position', ({target}) => { const {nodes} = this.props; const nodeId = target.id(); const childNodeDesc = nodes[nodeId]; if (!childNodeDesc) { return; } const floorPos = { x: Math.floor(target.position().x), y: Math.floor(target.position().y), atLayoutStop = () => { this.storePositions(this.cy.nodes('.gme-node') .map(ele => ({ id: ele.id(), position: ele.position(), }))); }; if (!childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY]) { this.reposition[nodeId] = { id: nodeId, position: floorPos, }; } else if (floorPos.x !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x || floorPos.y !== childNodeDesc.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y) { this.reposition[nodeId] = { id: nodeId, position: floorPos, }; } }); this.cy.on('free', ({target}) => { attachCytoscapeHandlers() { this.cy.on('dragfree', ({target}) => { const {setActiveSelection} = this.props; if (typeof target.id === 'function' && target.hasClass('aux-node') === false) { this.storePosition(target.id()); const repositions = [ { id: target.id(), position: target.position(), }, ]; target.descendants('node.gme-node') .forEach(child => repositions.push({ id: child.id(), position: child.position(), })); this.storePositions(repositions); setTimeout(() => { setActiveSelection([target.id()]); }); } else { // Free outside of canvas this.reposition = {}; setActiveSelection([]); } }); this.cy.on('vclick', 'node', (e) => { this.reposition = {}; const {setActiveSelection, gmeClient} = this.props; const {createPointer} = this.state; if (typeof e.target.id === 'function') { Loading @@ -469,7 +465,6 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, this.setState({createPointer: null}); gmeClient.setPointer(createPointer.nodeId, createPointer.ptrName, createPointer.target); } else if (e.target.hasClass('aux-node') === false) { // console.log('vclick', e.target.id(), JSON.stringify(this.reposition)); this.setState({ showNodeMenu: { id: e.target.id(), Loading @@ -494,9 +489,9 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, this.cy.on('mouseover', 'node', (e) => { const {createPointer} = this.state; const {gmeClient} = this.props; if (createPointer && typeof e.target.id === 'function' && e.target.hasClass('aux-node') === false) { if (createPointer && typeof e.target.id === 'function' && e.target.hasClass('aux-node') === false) { const gmeNode = gmeClient.getNode(e.target.id()); if (gmeNode && gmeNode.isValidTargetOf(createPointer.nodeId, createPointer.ptrName) && createPointer.target !== e.target.id()) { Loading Loading @@ -561,7 +556,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, onClick={() => { const layout = this.cy.layout(coseBilkentOptions); layout.on('layoutstop', () => { this.storePosition(activeNode); this.atLayoutStop(); }); layout.run(); }} Loading @@ -574,7 +569,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, onClick={() => { const layout = this.cy.layout({name: 'dagre'}); layout.on('layoutstop', () => { this.storePosition(activeNode); this.atLayoutStop(); }); layout.run(); }} Loading