diff --git a/components/graph/base.js b/components/graph/base.js index bb60eab..4a42e6e 100644 --- a/components/graph/base.js +++ b/components/graph/base.js @@ -55,6 +55,38 @@ class GraphView extends GraphViewBase { derivedState.nodes = props.layoutEngine.adjustNodes(derivedState.nodes, derivedState.nodesMap); } + if (props.moved && props.selected) { + const edgeKey = `${props.moved.source}_${props.moved.target}`; + const nodeKey = `key-${props.selected.id}`; + + if (derivedState.edgesMap[edgeKey] && derivedState.nodesMap[nodeKey]) { + derivedState.previousMoved = props.moved; + + derivedState.draggingEdge = true; + derivedState.draggedEdge = props.moved; + + derivedState.edgeEndNode = props.selected; + + derivedState.hoveredNode = true; + derivedState.hoveredNodeData = props.selected; + + derivedState.selectedNodeObj = { + nodeId: null, + node: null, + }; + } + } else if (!props.moved && state.previousMoved) { + derivedState.previousMoved = null; + + derivedState.draggingEdge = false; + derivedState.draggedEdge = null; + + derivedState.edgeEndNode = null; + + derivedState.hoveredNode = false; + derivedState.hoveredNodeData = null; + } + return derivedState; } @@ -77,9 +109,39 @@ class GraphView extends GraphViewBase { } } + if (!prevProps.moved && this.props.moved) { + this.removeEdgeElement(this.props.moved.source, this.props.moved.target); + } else if (prevProps.moved && !this.props.moved) { + const container = document.querySelector('#edge-custom-container'); + if (container) { + container.remove(); + } + } + + if (this.props.selected && + this.props.moved && + ( + prevProps.selected !== this.props.selected || + prevProps.moved !== this.props.moved + ) && + this.state.draggedEdge + ) { + this.dragEdge(); + } + super.componentDidUpdate(prevProps, prevState); } + getMouseCoordinates() { + if (this.props.selected && this.props.moved) { + return [ + this.props.selected.x, + this.props.selected.y, + ]; + } + return super.getMouseCoordinates(); + } + getNodeComponent(id, node) { const { nodeTypes, nodeSubtypes, nodeSize, renderNode, renderNodeText, nodeKey } = this.props; return r(Node, { diff --git a/components/graph/index.js b/components/graph/index.js index 6c83e59..65bdb5e 100644 --- a/components/graph/index.js +++ b/components/graph/index.js @@ -526,6 +526,7 @@ class Graph extends React.Component { this.state = { selected: null, + moved: null, }; this._requestedIcons = new Set(); @@ -627,7 +628,8 @@ class Graph extends React.Component { (nextProps.infos === this.props.infos) && (nextProps.preferences === this.props.preferences) && (nextProps.icons === this.props.icons) && - (nextState.selected === this.state.selected) + (nextState.selected === this.state.selected) && + (nextState.moved === this.state.moved) ); } @@ -708,7 +710,7 @@ class Graph extends React.Component { onSwapEdge(sourceNode, targetNode, edge) { if (edge.type === 'sinkInput') { this.props.moveSinkInput(edge.index, targetNode.index); - } else { + } else if (edge.type === 'sourceOutput') { this.props.moveSourceOutput(edge.index, targetNode.index); } } @@ -779,8 +781,19 @@ class Graph extends React.Component { this.graphViewElement.focus(); } - deselect() { - this.setState({ selected: null }); + hotKeyEscape() { + const { moved } = this.state; + + if (moved) { + this.setState({ + moved: null, + }); + return; + } + + this.setState({ + selected: null, + }); } hotKeyMute() { @@ -870,11 +883,19 @@ class Graph extends React.Component { } hotKeyFocusDown() { + if (this._hotKeyMovePosition('down')) { + return; + } + const selected = this._findNextObjectForSelection(this.state.selected, 'down'); this.setState({ selected }); } hotKeyFocusUp() { + if (this._hotKeyMovePosition('up')) { + return; + } + const selected = this._findNextObjectForSelection(this.state.selected, 'up'); this.setState({ selected }); } @@ -932,13 +953,78 @@ class Graph extends React.Component { } hotKeyFocusLeft() { + if (this._hotKeyMovePosition('left')) { + return; + } + this._focusHorizontal('left'); } hotKeyFocusRight() { + if (this._hotKeyMovePosition('right')) { + return; + } + this._focusHorizontal('right'); } + _hotKeyMovePosition(direction) { + const { selected, moved } = this.state; + + if (!selected || + selected !== moved || + ![ 'sink', 'source', 'client', 'module' ].includes(moved.type) + ) { + return false; + } + + const x = direction === 'right' ? 1 : direction === 'left' ? -1 : 0; + const y = direction === 'down' ? 1 : direction === 'up' ? -1 : 0; + + moved.x += x * (size + (size / 12)); + moved.y += y * (size + (size / 12)); + + this.forceUpdate(); + + return true; + } + + hotKeyMove() { + let { selected, moved } = this.state; + + if (!selected) { + return; + } + + if (moved) { + this.onSwapEdge(null, selected, moved); + this.setState({ + selected: moved, + moved: null, + }); + return; + } + + moved = selected; + + if (moved.type === 'sinkInput') { + selected = find( + node => node.id !== moved.target && node.type === 'sink', + this.state.nodes, + ); + } else if (moved.type === 'sourceOutput') { + selected = find( + node => node.id !== moved.target && node.type === 'source', + this.state.nodes, + ); + } + + this.setState({ + selected, + moved, + }); + } + render() { const { nodes, edges } = this.state; @@ -954,6 +1040,7 @@ class Graph extends React.Component { edges, selected: this.state.selected, + moved: this.state.moved, nodeTypes: {}, nodeSubtypes: {}, diff --git a/components/graph/satellites-graph.js b/components/graph/satellites-graph.js index 30560b8..1ad1cd2 100644 --- a/components/graph/satellites-graph.js +++ b/components/graph/satellites-graph.js @@ -92,7 +92,7 @@ class GraphView extends React.Component { static getDerivedStateFromProps(props) { const originalEdgesByTargetNodeKey = groupBy(prop('target'), props.edges); - let { selected } = props; + let { selected, moved } = props; const satelliteEdges = []; @@ -106,6 +106,10 @@ class GraphView extends React.Component { selected = satelliteEdge; } + if (edge === moved) { + moved = satelliteEdge; + } + satelliteEdges.push(satelliteEdge); return satelliteNode; @@ -115,7 +119,9 @@ class GraphView extends React.Component { originalEdgesByTargetNodeKey, satelliteNodesByTargetNodeKey, satelliteEdges, + selected, + moved, }; } @@ -206,7 +212,9 @@ class GraphView extends React.Component { const { satelliteNodesByTargetNodeKey, satelliteEdges: edges, + selected, + moved, } = this.state; const nodes = flatten(map(node => { @@ -219,6 +227,7 @@ class GraphView extends React.Component { ...this.props, selected, + moved, ref: this.graphViewRef, diff --git a/components/hot-keys/index.js b/components/hot-keys/index.js index 303860f..a76cfd3 100644 --- a/components/hot-keys/index.js +++ b/components/hot-keys/index.js @@ -24,11 +24,13 @@ const keyMap = { hotKeyFocusLeft: [ 'h', 'left' ], hotKeyFocusRight: [ 'l', 'right' ], + hotKeyMove: 'm', + hotKeyVolumeDown: [ '/', '9' ], hotKeyVolumeUp: [ '*', '0' ], - hotKeyMute: 'm', - hotKeyShiftMute: 'M', + hotKeyMute: 'space', + hotKeyShiftMute: 'shift+space', }; class MyHotKeys extends React.Component { @@ -72,7 +74,7 @@ class MyHotKeys extends React.Component { hotKeyEscape() { this.hotKeyFocusGraph(); - this.graphRef.current.getWrappedInstance().deselect(); + this.graphRef.current.getWrappedInstance().hotKeyEscape(); } render() {