Loading src/components/GraphEditor/ContextMenu.jsx +26 −50 Original line number Diff line number Diff line Loading @@ -12,12 +12,12 @@ import MenuItem from '@material-ui/core/MenuItem'; export default class ContextMenu extends Component { static propTypes = { gmeClient: PropTypes.object.isRequired, data: PropTypes.object.isRequired, nodeId: PropTypes.string.isRequired, // FIXME: should be nodeIds eventX: PropTypes.number.isRequired, eventY: PropTypes.number.isRequired, onClose: PropTypes.func.isRequired, setActiveNode: PropTypes.func.isRequired, createPointer: PropTypes.func.isRequired, readOnly: PropTypes.bool, }; Loading @@ -41,6 +41,13 @@ export default class ContextMenu extends Component { onClose(); } getCreatePointerFunc = (ptrName) => { const {createPointer, nodeId} = this.props; return () => { createPointer(nodeId, ptrName); }; } render() { const { gmeClient, Loading @@ -48,63 +55,32 @@ export default class ContextMenu extends Component { eventX, eventY, readOnly, data, onClose, } = this.props; // console.log(data); const menuItems = []; const nodeObj = gmeClient.getNode(nodeId); if (false) { const metaNodeObj = gmeClient.getNode(nodeObj.getMetaTypeId()); const pointerNames = metaNodeObj.getValidPointerNames(); menuItems.push(( <MenuItem key="meta-type" onClick={this.props.onClose} disabled={readOnly}> <MenuItem key="meta-type" onClick={onClose} disabled={readOnly}> {`<<${metaNodeObj.getAttribute('name')}>>`} </MenuItem>)); if (metaNodeObj.getAttribute('name') === 'System') { menuItems.push(( <MenuItem key="open-sub-system" onClick={this.setActiveNode} > Open Subsystem ... </MenuItem>)); } } else if (data.memberAttrs && data.memberAttrs.length > 0) { let args; data.memberAttrs.forEach((memAttr) => { if (memAttr.name === 'args') { args = memAttr.value ? `(${memAttr.value})` : '()'; } }); if (args) { pointerNames.forEach((ptr) => { menuItems.push(( <MenuItem key="invocation-args" onClick={this.props.onClose}> Invocation arguments: {args} <MenuItem key={`create-pointer-${ptr}`} onClick={this.getCreatePointerFunc(ptr)} > {`Create pointer ${ptr} from here ...`} </MenuItem>)); } } else if (data.pointerName === 'post' || data.pointerName === 'pre') { const methodNode = gmeClient.getNode(data.source); const modeNode = gmeClient.getNode(data.target); if (methodNode && modeNode) { // const parentName = gmeClient.getNode(modeNode.getParentId()).getAttribute('name'); // const modeName = modeNode.getAttribute('name'); const methodName = methodNode.getAttribute('name'); menuItems.push(( <MenuItem key="pre-post-condition" onClick={this.props.onClose}> {`${data.pointerName}-condition for ${methodName}`} </MenuItem>)); } } else if (data.setName === 'network-nodes') { menuItems.push(( <MenuItem key="cell-network-nodes" onClick={this.props.onClose}> {`${data.setName}`} </MenuItem>)); } if (menuItems.length === 0) { this.props.onClose(); return <div/>; } }); return ( <Menu Loading @@ -116,7 +92,7 @@ export default class ContextMenu extends Component { left: eventX, }} open onClose={this.props.onClose} onClose={onClose} > {menuItems} </Menu> Loading src/components/GraphEditor/FilterSelector.jsx +63 −57 Original line number Diff line number Diff line Loading @@ -34,13 +34,20 @@ export default class FilterSelector extends Component { static defaultProps = { open: true, } }; render() { const {validItems, activeItems} = this.props; const { validItems, activeItems, handleToggle, open, onClose, } = this.props; const lists = []; Object.keys(validItems).forEach((groupName) => { Object.keys(validItems) .forEach((groupName) => { const items = []; validItems[groupName].forEach((item) => { if (item.hidden) { Loading @@ -55,9 +62,8 @@ export default class FilterSelector extends Component { icon = <TrendingFlatIcon style={{color: 'rgb(85, 123, 139)'}}/>; break; case 'sets': icon = (item.name === 'network-nodes') ? <MoreHorizIcon/> : <CallSplitIcon className="rotate-90"/>; icon = (item.name === 'network-nodes') ? <MoreHorizIcon/> : <CallSplitIcon className="rotate-90"/>; break; case 'nodes': icon = item.isConnection ? <RemoveIcon/> : <FiberManualRecordIcon/>; Loading @@ -76,7 +82,7 @@ export default class FilterSelector extends Component { <ListItemSecondaryAction> <Switch onChange={() => { this.props.handleToggle(itemId); handleToggle(itemId); }} checked={activeItems[itemId]} /> Loading @@ -100,7 +106,7 @@ export default class FilterSelector extends Component { }); return ( <Dialog open={this.props.open} onClose={this.props.onClose}> <Dialog open={open} onClose={onClose}> <DialogTitle id="filter-dialog-title">Apply Filters</DialogTitle> <DialogContent> {lists} Loading src/components/GraphEditor/GraphEditor.jsx +231 −163 Original line number Diff line number Diff line Loading @@ -85,6 +85,12 @@ const DEFAULT_STYLES = [ 'background-color': 'rgba(82, 168, 236, 0.6)', }, }, { selector: 'node.valid-pointer-target', style: { 'background-color': 'rgb(0, 0, 255)', }, }, // { // selector: 'edge.in-active-selection', // style: { Loading Loading @@ -186,7 +192,8 @@ export default class GraphEditor extends Component { const {validFilters} = this.props; const res = {}; Object.keys(validFilters).forEach((filterType) => { Object.keys(validFilters) .forEach((filterType) => { validFilters[filterType].forEach((f) => { res[`${filterType}$${f.name}`] = f.active; }); Loading @@ -200,25 +207,27 @@ export default class GraphEditor extends Component { return res; })(), showFilterSelector: false, createPointer: null, }; componentWillReceiveProps(newProps) { const {activeNode} = newProps; if (activeNode !== this.props.activeNode) { componentWillReceiveProps({activeNode}) { const {activeNode: preActiveNode} = this.props; if (activeNode !== preActiveNode) { this.reposition = {}; } } onListItemClick = id => ( (/* e */) => { this.props.setActiveSelection([id]); }) const {setActiveSelection} = this.props; setActiveSelection([id]); }); onListItemDoubleClick = id => ( (/* e */) => { this.props.setActiveNode(id); }) const {setActiveNode} = this.props; setActiveNode(id); }); getCytoscapeElements() { const { Loading @@ -227,7 +236,7 @@ export default class GraphEditor extends Component { activeSelection, nodes, } = this.props; const {activeFilters} = this.state; const {activeFilters, createPointer} = this.state; // https://github.com/ybarukh/react-cytoscape/blob/master/sample/app/Graph.js // http://js.cytoscape.org/#notation/elements-json const result = { Loading @@ -250,11 +259,12 @@ export default class GraphEditor extends Component { // parentNode = children[parentNode.parent]; // } return JSON.parse(JSON.stringify(parentNode.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || parentNode.registries.position)); return JSON.parse(JSON.stringify(parentNode.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || parentNode.registries.position)); }; Object.keys(nodes).forEach((id) => { Object.keys(nodes) .forEach((id) => { if (id === activeNode) { return; } Loading @@ -280,8 +290,9 @@ export default class GraphEditor extends Component { data: { id, name: childData.attributes.name, label: childData.attributes.label ? `${childData.attributes.name}::${childData.attributes.label}` : childData.attributes.name, label: childData.attributes.label ? `${childData.attributes.name}::${childData.attributes.label}` : childData.attributes.name, parent: childData.parent, metaType: childData.metaType, }, Loading @@ -290,6 +301,10 @@ export default class GraphEditor extends Component { classes: `${activeSelection.includes(id) ? 'in-active-selection' : ''}`, }; if (createPointer && createPointer.target === id) { cytoData.classes += ' valid-pointer-target'; } result.elements.nodes.push(cytoData); // Keep track of the containers s.t. they cannot be grabbed. Loading @@ -298,7 +313,8 @@ export default class GraphEditor extends Component { nodeIdsWithChildren[childData.parent] = true; } Object.keys(childData.sets).forEach((setName) => { Object.keys(childData.sets) .forEach((setName) => { if (!childData.sets[setName] || activeFilters[`sets$${setName}`]) { return; } Loading @@ -313,7 +329,8 @@ export default class GraphEditor extends Component { label: setName, memberAttrs: setMemberData.memberAttrs, }, classes: `set-member ${activeSelection.includes(edgeId) ? 'in-active-selection' : ''}`, classes: `set-member ${activeSelection.includes(edgeId) ? 'in-active-selection' : ''}`, }; if (setMemberData.label !== null) { Loading @@ -324,7 +341,8 @@ export default class GraphEditor extends Component { }); }); Object.keys(childData.pointers).forEach((pName) => { Object.keys(childData.pointers) .forEach((pName) => { if (!childData.pointers[pName] || activeFilters[`pointers$${pName}`]) { return; } Loading Loading @@ -356,7 +374,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, } }); Object.keys(nodeIdsWithChildren).forEach((id) => { Object.keys(nodeIdsWithChildren) .forEach((id) => { nodeMap[id].data.hasChildren = true; }); Loading @@ -368,7 +387,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, if (Object.keys(this.reposition).length > 0) { gmeClient.startTransaction(); Object.keys(this.reposition).forEach((id) => { Object.keys(this.reposition) .forEach((id) => { if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) { gmeClient.setRegistry( id, Loading @@ -389,52 +409,69 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, }; toggleFilter = (filterId) => { this.setState({ this.setState(({activeFilters}) => ({ activeFilters: update( this.state.activeFilters, {[filterId]: {$set: !this.state.activeFilters[filterId]}}, activeFilters, {[filterId]: {$set: activeFilters[filterId]}}, ), })); }; startNewPointer = (nodeId, ptrName) => { this.setState({ showNodeMenu: false, createPointer: { nodeId, ptrName, target: null, }, }); }; attachCytoscapeHandlers() { this.cy.on('position', (e) => { const cyNode = e.target; const childNodeDesc = this.props.nodes[cyNode.id()]; this.cy.on('position', ({target}) => { const {nodes} = this.props; const childNodeDesc = nodes[target.id()]; const floorPos = { x: Math.floor(cyNode.position().x), y: Math.floor(cyNode.position().y), x: Math.floor(target.position().x), y: Math.floor(target.position().y), }; if (childNodeDesc && (!childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x !== floorPos.x || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y !== floorPos.y)) { this.reposition[cyNode.id()] = { id: cyNode.id(), if (childNodeDesc && (!childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x !== floorPos.x || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y !== floorPos.y)) { this.reposition[target.id()] = { id: target.id(), position: floorPos, }; } }); this.cy.on('free', (e) => { const cyNode = e.target; if (typeof cyNode.id === 'function') { this.cy.on('free', ({target}) => { const {setActiveSelection} = this.props; if (typeof target.id === 'function') { // console.log('free', cyNode.id(), JSON.stringify(this.reposition)); this.storePosition(cyNode.id()); this.storePosition(target.id()); setTimeout(() => { this.props.setActiveSelection([e.target.id()]); setActiveSelection([target.id()]); }); } else { // Free outside of canvas this.reposition = {}; this.props.setActiveSelection([]); setActiveSelection([]); } }); this.cy.on('vclick', (e) => { this.reposition = {}; const {setActiveSelection, gmeClient} = this.props; const {createPointer} = this.state; if (typeof e.target.id === 'function') { if (createPointer && createPointer.target === e.target.id()) { this.setState({createPointer: null}); gmeClient.setPointer(createPointer.nodeId, createPointer.ptrName, createPointer.target); } else { // console.log('vclick', e.target.id(), JSON.stringify(this.reposition)); this.setState({ showNodeMenu: { Loading @@ -445,13 +482,33 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, y: e.originalEvent.clientY, }, }, createPointer: null, }); } setTimeout(() => { this.props.setActiveSelection([e.target.id()]); setActiveSelection([e.target.id()]); }); } else { this.props.setActiveSelection([]); setActiveSelection([]); } }); this.cy.on('mouseover', 'node', (e) => { const {createPointer} = this.state; const {gmeClient} = this.props; if (createPointer && typeof e.target.id === 'function') { const gmeNode = gmeClient.getNode(e.target.id()); if (gmeNode && gmeNode.isValidTargetOf(createPointer.nodeId, createPointer.ptrName) && createPointer.target !== e.target.id()) { this.setState({ createPointer: { nodeId: createPointer.nodeId, prtName: createPointer.prtName, target: e.target.id(), }, }); } } }); } Loading @@ -460,6 +517,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, const { showNodeMenu, showFilterSelector, activeFilters, } = this.state; const { readOnly, Loading @@ -470,6 +528,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, activeNode, activeSelection, nodes, setActiveNode, validFilters, } = this.props; const cytoData = this.getCytoscapeElements(); Loading @@ -477,7 +537,11 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, const activeNodeName = nodes[activeNode] ? nodes[activeNode].attributes.name : ''; return ( <div style={{width: '100%', height: '100%'}}> <div style={{ width: '100%', height: '100%', }} > <div style={{ position: 'absolute', top: 5, Loading @@ -487,7 +551,10 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, > {readOnly ? <LockIcon/> : null} <span style={{fontSize: 24, color: isActivePanel ? null : 'grey'}} style={{ fontSize: 24, color: isActivePanel ? null : 'grey', }} > {activeNodeName} <Tooltip id="tooltip-auto-layout" title="Run auto-layout. Warning this will move everything!"> Loading Loading @@ -551,7 +618,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, layout={{name: 'preset'/* preset dagre */}} style={DEFAULT_STYLES.concat(cytoData.style)} /> {showNodeMenu && showNodeMenu.id === activeSelection[0] ? {showNodeMenu && showNodeMenu.id === activeSelection[0] ? ( <ContextMenu gmeClient={gmeClient} nodeId={showNodeMenu.id} Loading @@ -561,13 +629,13 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, onClose={() => { this.setState({showNodeMenu: null}); }} setActiveNode={this.props.setActiveNode} /> : null} setActiveNode={setActiveNode} />) : null} <FilterSelector open={showFilterSelector} validItems={this.props.validFilters} activeItems={this.state.activeFilters} validItems={validFilters} activeItems={activeFilters} handleToggle={this.toggleFilter} onClose={(() => this.setState({showFilterSelector: false}))} /> Loading Loading
src/components/GraphEditor/ContextMenu.jsx +26 −50 Original line number Diff line number Diff line Loading @@ -12,12 +12,12 @@ import MenuItem from '@material-ui/core/MenuItem'; export default class ContextMenu extends Component { static propTypes = { gmeClient: PropTypes.object.isRequired, data: PropTypes.object.isRequired, nodeId: PropTypes.string.isRequired, // FIXME: should be nodeIds eventX: PropTypes.number.isRequired, eventY: PropTypes.number.isRequired, onClose: PropTypes.func.isRequired, setActiveNode: PropTypes.func.isRequired, createPointer: PropTypes.func.isRequired, readOnly: PropTypes.bool, }; Loading @@ -41,6 +41,13 @@ export default class ContextMenu extends Component { onClose(); } getCreatePointerFunc = (ptrName) => { const {createPointer, nodeId} = this.props; return () => { createPointer(nodeId, ptrName); }; } render() { const { gmeClient, Loading @@ -48,63 +55,32 @@ export default class ContextMenu extends Component { eventX, eventY, readOnly, data, onClose, } = this.props; // console.log(data); const menuItems = []; const nodeObj = gmeClient.getNode(nodeId); if (false) { const metaNodeObj = gmeClient.getNode(nodeObj.getMetaTypeId()); const pointerNames = metaNodeObj.getValidPointerNames(); menuItems.push(( <MenuItem key="meta-type" onClick={this.props.onClose} disabled={readOnly}> <MenuItem key="meta-type" onClick={onClose} disabled={readOnly}> {`<<${metaNodeObj.getAttribute('name')}>>`} </MenuItem>)); if (metaNodeObj.getAttribute('name') === 'System') { menuItems.push(( <MenuItem key="open-sub-system" onClick={this.setActiveNode} > Open Subsystem ... </MenuItem>)); } } else if (data.memberAttrs && data.memberAttrs.length > 0) { let args; data.memberAttrs.forEach((memAttr) => { if (memAttr.name === 'args') { args = memAttr.value ? `(${memAttr.value})` : '()'; } }); if (args) { pointerNames.forEach((ptr) => { menuItems.push(( <MenuItem key="invocation-args" onClick={this.props.onClose}> Invocation arguments: {args} <MenuItem key={`create-pointer-${ptr}`} onClick={this.getCreatePointerFunc(ptr)} > {`Create pointer ${ptr} from here ...`} </MenuItem>)); } } else if (data.pointerName === 'post' || data.pointerName === 'pre') { const methodNode = gmeClient.getNode(data.source); const modeNode = gmeClient.getNode(data.target); if (methodNode && modeNode) { // const parentName = gmeClient.getNode(modeNode.getParentId()).getAttribute('name'); // const modeName = modeNode.getAttribute('name'); const methodName = methodNode.getAttribute('name'); menuItems.push(( <MenuItem key="pre-post-condition" onClick={this.props.onClose}> {`${data.pointerName}-condition for ${methodName}`} </MenuItem>)); } } else if (data.setName === 'network-nodes') { menuItems.push(( <MenuItem key="cell-network-nodes" onClick={this.props.onClose}> {`${data.setName}`} </MenuItem>)); } if (menuItems.length === 0) { this.props.onClose(); return <div/>; } }); return ( <Menu Loading @@ -116,7 +92,7 @@ export default class ContextMenu extends Component { left: eventX, }} open onClose={this.props.onClose} onClose={onClose} > {menuItems} </Menu> Loading
src/components/GraphEditor/FilterSelector.jsx +63 −57 Original line number Diff line number Diff line Loading @@ -34,13 +34,20 @@ export default class FilterSelector extends Component { static defaultProps = { open: true, } }; render() { const {validItems, activeItems} = this.props; const { validItems, activeItems, handleToggle, open, onClose, } = this.props; const lists = []; Object.keys(validItems).forEach((groupName) => { Object.keys(validItems) .forEach((groupName) => { const items = []; validItems[groupName].forEach((item) => { if (item.hidden) { Loading @@ -55,9 +62,8 @@ export default class FilterSelector extends Component { icon = <TrendingFlatIcon style={{color: 'rgb(85, 123, 139)'}}/>; break; case 'sets': icon = (item.name === 'network-nodes') ? <MoreHorizIcon/> : <CallSplitIcon className="rotate-90"/>; icon = (item.name === 'network-nodes') ? <MoreHorizIcon/> : <CallSplitIcon className="rotate-90"/>; break; case 'nodes': icon = item.isConnection ? <RemoveIcon/> : <FiberManualRecordIcon/>; Loading @@ -76,7 +82,7 @@ export default class FilterSelector extends Component { <ListItemSecondaryAction> <Switch onChange={() => { this.props.handleToggle(itemId); handleToggle(itemId); }} checked={activeItems[itemId]} /> Loading @@ -100,7 +106,7 @@ export default class FilterSelector extends Component { }); return ( <Dialog open={this.props.open} onClose={this.props.onClose}> <Dialog open={open} onClose={onClose}> <DialogTitle id="filter-dialog-title">Apply Filters</DialogTitle> <DialogContent> {lists} Loading
src/components/GraphEditor/GraphEditor.jsx +231 −163 Original line number Diff line number Diff line Loading @@ -85,6 +85,12 @@ const DEFAULT_STYLES = [ 'background-color': 'rgba(82, 168, 236, 0.6)', }, }, { selector: 'node.valid-pointer-target', style: { 'background-color': 'rgb(0, 0, 255)', }, }, // { // selector: 'edge.in-active-selection', // style: { Loading Loading @@ -186,7 +192,8 @@ export default class GraphEditor extends Component { const {validFilters} = this.props; const res = {}; Object.keys(validFilters).forEach((filterType) => { Object.keys(validFilters) .forEach((filterType) => { validFilters[filterType].forEach((f) => { res[`${filterType}$${f.name}`] = f.active; }); Loading @@ -200,25 +207,27 @@ export default class GraphEditor extends Component { return res; })(), showFilterSelector: false, createPointer: null, }; componentWillReceiveProps(newProps) { const {activeNode} = newProps; if (activeNode !== this.props.activeNode) { componentWillReceiveProps({activeNode}) { const {activeNode: preActiveNode} = this.props; if (activeNode !== preActiveNode) { this.reposition = {}; } } onListItemClick = id => ( (/* e */) => { this.props.setActiveSelection([id]); }) const {setActiveSelection} = this.props; setActiveSelection([id]); }); onListItemDoubleClick = id => ( (/* e */) => { this.props.setActiveNode(id); }) const {setActiveNode} = this.props; setActiveNode(id); }); getCytoscapeElements() { const { Loading @@ -227,7 +236,7 @@ export default class GraphEditor extends Component { activeSelection, nodes, } = this.props; const {activeFilters} = this.state; const {activeFilters, createPointer} = this.state; // https://github.com/ybarukh/react-cytoscape/blob/master/sample/app/Graph.js // http://js.cytoscape.org/#notation/elements-json const result = { Loading @@ -250,11 +259,12 @@ export default class GraphEditor extends Component { // parentNode = children[parentNode.parent]; // } return JSON.parse(JSON.stringify(parentNode.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || parentNode.registries.position)); return JSON.parse(JSON.stringify(parentNode.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || parentNode.registries.position)); }; Object.keys(nodes).forEach((id) => { Object.keys(nodes) .forEach((id) => { if (id === activeNode) { return; } Loading @@ -280,8 +290,9 @@ export default class GraphEditor extends Component { data: { id, name: childData.attributes.name, label: childData.attributes.label ? `${childData.attributes.name}::${childData.attributes.label}` : childData.attributes.name, label: childData.attributes.label ? `${childData.attributes.name}::${childData.attributes.label}` : childData.attributes.name, parent: childData.parent, metaType: childData.metaType, }, Loading @@ -290,6 +301,10 @@ export default class GraphEditor extends Component { classes: `${activeSelection.includes(id) ? 'in-active-selection' : ''}`, }; if (createPointer && createPointer.target === id) { cytoData.classes += ' valid-pointer-target'; } result.elements.nodes.push(cytoData); // Keep track of the containers s.t. they cannot be grabbed. Loading @@ -298,7 +313,8 @@ export default class GraphEditor extends Component { nodeIdsWithChildren[childData.parent] = true; } Object.keys(childData.sets).forEach((setName) => { Object.keys(childData.sets) .forEach((setName) => { if (!childData.sets[setName] || activeFilters[`sets$${setName}`]) { return; } Loading @@ -313,7 +329,8 @@ export default class GraphEditor extends Component { label: setName, memberAttrs: setMemberData.memberAttrs, }, classes: `set-member ${activeSelection.includes(edgeId) ? 'in-active-selection' : ''}`, classes: `set-member ${activeSelection.includes(edgeId) ? 'in-active-selection' : ''}`, }; if (setMemberData.label !== null) { Loading @@ -324,7 +341,8 @@ export default class GraphEditor extends Component { }); }); Object.keys(childData.pointers).forEach((pName) => { Object.keys(childData.pointers) .forEach((pName) => { if (!childData.pointers[pName] || activeFilters[`pointers$${pName}`]) { return; } Loading Loading @@ -356,7 +374,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, } }); Object.keys(nodeIdsWithChildren).forEach((id) => { Object.keys(nodeIdsWithChildren) .forEach((id) => { nodeMap[id].data.hasChildren = true; }); Loading @@ -368,7 +387,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, if (Object.keys(this.reposition).length > 0) { gmeClient.startTransaction(); Object.keys(this.reposition).forEach((id) => { Object.keys(this.reposition) .forEach((id) => { if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) { gmeClient.setRegistry( id, Loading @@ -389,52 +409,69 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, }; toggleFilter = (filterId) => { this.setState({ this.setState(({activeFilters}) => ({ activeFilters: update( this.state.activeFilters, {[filterId]: {$set: !this.state.activeFilters[filterId]}}, activeFilters, {[filterId]: {$set: activeFilters[filterId]}}, ), })); }; startNewPointer = (nodeId, ptrName) => { this.setState({ showNodeMenu: false, createPointer: { nodeId, ptrName, target: null, }, }); }; attachCytoscapeHandlers() { this.cy.on('position', (e) => { const cyNode = e.target; const childNodeDesc = this.props.nodes[cyNode.id()]; this.cy.on('position', ({target}) => { const {nodes} = this.props; const childNodeDesc = nodes[target.id()]; const floorPos = { x: Math.floor(cyNode.position().x), y: Math.floor(cyNode.position().y), x: Math.floor(target.position().x), y: Math.floor(target.position().y), }; if (childNodeDesc && (!childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x !== floorPos.x || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y !== floorPos.y)) { this.reposition[cyNode.id()] = { id: cyNode.id(), if (childNodeDesc && (!childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY] || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x !== floorPos.x || childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y !== floorPos.y)) { this.reposition[target.id()] = { id: target.id(), position: floorPos, }; } }); this.cy.on('free', (e) => { const cyNode = e.target; if (typeof cyNode.id === 'function') { this.cy.on('free', ({target}) => { const {setActiveSelection} = this.props; if (typeof target.id === 'function') { // console.log('free', cyNode.id(), JSON.stringify(this.reposition)); this.storePosition(cyNode.id()); this.storePosition(target.id()); setTimeout(() => { this.props.setActiveSelection([e.target.id()]); setActiveSelection([target.id()]); }); } else { // Free outside of canvas this.reposition = {}; this.props.setActiveSelection([]); setActiveSelection([]); } }); this.cy.on('vclick', (e) => { this.reposition = {}; const {setActiveSelection, gmeClient} = this.props; const {createPointer} = this.state; if (typeof e.target.id === 'function') { if (createPointer && createPointer.target === e.target.id()) { this.setState({createPointer: null}); gmeClient.setPointer(createPointer.nodeId, createPointer.ptrName, createPointer.target); } else { // console.log('vclick', e.target.id(), JSON.stringify(this.reposition)); this.setState({ showNodeMenu: { Loading @@ -445,13 +482,33 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, y: e.originalEvent.clientY, }, }, createPointer: null, }); } setTimeout(() => { this.props.setActiveSelection([e.target.id()]); setActiveSelection([e.target.id()]); }); } else { this.props.setActiveSelection([]); setActiveSelection([]); } }); this.cy.on('mouseover', 'node', (e) => { const {createPointer} = this.state; const {gmeClient} = this.props; if (createPointer && typeof e.target.id === 'function') { const gmeNode = gmeClient.getNode(e.target.id()); if (gmeNode && gmeNode.isValidTargetOf(createPointer.nodeId, createPointer.ptrName) && createPointer.target !== e.target.id()) { this.setState({ createPointer: { nodeId: createPointer.nodeId, prtName: createPointer.prtName, target: e.target.id(), }, }); } } }); } Loading @@ -460,6 +517,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, const { showNodeMenu, showFilterSelector, activeFilters, } = this.state; const { readOnly, Loading @@ -470,6 +528,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, activeNode, activeSelection, nodes, setActiveNode, validFilters, } = this.props; const cytoData = this.getCytoscapeElements(); Loading @@ -477,7 +537,11 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, const activeNodeName = nodes[activeNode] ? nodes[activeNode].attributes.name : ''; return ( <div style={{width: '100%', height: '100%'}}> <div style={{ width: '100%', height: '100%', }} > <div style={{ position: 'absolute', top: 5, Loading @@ -487,7 +551,10 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, > {readOnly ? <LockIcon/> : null} <span style={{fontSize: 24, color: isActivePanel ? null : 'grey'}} style={{ fontSize: 24, color: isActivePanel ? null : 'grey', }} > {activeNodeName} <Tooltip id="tooltip-auto-layout" title="Run auto-layout. Warning this will move everything!"> Loading Loading @@ -551,7 +618,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, layout={{name: 'preset'/* preset dagre */}} style={DEFAULT_STYLES.concat(cytoData.style)} /> {showNodeMenu && showNodeMenu.id === activeSelection[0] ? {showNodeMenu && showNodeMenu.id === activeSelection[0] ? ( <ContextMenu gmeClient={gmeClient} nodeId={showNodeMenu.id} Loading @@ -561,13 +629,13 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`, onClose={() => { this.setState({showNodeMenu: null}); }} setActiveNode={this.props.setActiveNode} /> : null} setActiveNode={setActiveNode} />) : null} <FilterSelector open={showFilterSelector} validItems={this.props.validFilters} activeItems={this.state.activeFilters} validItems={validFilters} activeItems={activeFilters} handleToggle={this.toggleFilter} onClose={(() => this.setState({showFilterSelector: false}))} /> Loading