diff --git a/packages/editor-ui/src/components/mixins/workflowHelpers.ts b/packages/editor-ui/src/components/mixins/workflowHelpers.ts index ba4d2fbcf..7b8e5fb37 100644 --- a/packages/editor-ui/src/components/mixins/workflowHelpers.ts +++ b/packages/editor-ui/src/components/mixins/workflowHelpers.ts @@ -468,7 +468,6 @@ export const workflowHelpers = mixins( for (const node of workflowData.nodes) { node.position[0] += offsetPosition[0]; node.position[1] += offsetPosition[1]; - console.log(node.position); } }, }, diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 9c58981e6..710e787df 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -427,6 +427,12 @@ export default mixins( if (lastSelectedNode !== null) { this.$store.commit('setActiveNode', lastSelectedNode.name); } + } else if (e.key === 'ArrowDown' && e.shiftKey === true) { + // Select all downstream nodes + e.stopPropagation(); + e.preventDefault(); + + this.callDebounced('selectDownstreamNodes', 1000); } else if (e.key === 'ArrowDown') { // Set child node active const lastSelectedNode = this.$store.getters.lastSelectedNode; @@ -441,6 +447,12 @@ export default mixins( } this.callDebounced('nodeSelectedByName', 100, connections.main[0][0].node, false, true); + } else if (e.key === 'ArrowUp' && e.shiftKey === true) { + // Select all downstream nodes + e.stopPropagation(); + e.preventDefault(); + + this.callDebounced('selectUpstreamNodes', 1000); } else if (e.key === 'ArrowUp') { // Set parent node active const lastSelectedNode = this.$store.getters.lastSelectedNode; @@ -560,6 +572,41 @@ export default mixins( }); }, + selectUpstreamNodes () { + const lastSelectedNode = this.$store.getters.lastSelectedNode as INodeUi | null; + if (lastSelectedNode === null) { + return; + } + + this.deselectAllNodes(); + + // Get all upstream nodes and select them + const workflow = this.getWorkflow(); + for (const nodeName of workflow.getParentNodes(lastSelectedNode.name)) { + this.nodeSelectedByName(nodeName); + } + + // At the end select the previously selected node again + this.nodeSelectedByName(lastSelectedNode.name); + }, + selectDownstreamNodes () { + const lastSelectedNode = this.$store.getters.lastSelectedNode as INodeUi | null; + if (lastSelectedNode === null) { + return; + } + + this.deselectAllNodes(); + + // Get all downstream nodes and select them + const workflow = this.getWorkflow(); + for (const nodeName of workflow.getChildNodes(lastSelectedNode.name)) { + this.nodeSelectedByName(nodeName); + } + + // At the end select the previously selected node again + this.nodeSelectedByName(lastSelectedNode.name); + }, + cutSelectedNodes () { this.copySelectedNodes(); this.deleteSelectedNodes(); diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index e56e69525..0be39f62c 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -508,7 +508,7 @@ export class Workflow { /** - * Returns all the active nodes before the given one + * Returns all the after the given one * * @param {string} nodeName * @param {string} [type='main'] @@ -516,7 +516,40 @@ export class Workflow { * @returns {string[]} * @memberof Workflow */ - getParentNodes(nodeName: string, type = 'main', depth = -1, checkedNodes?: string[]): string[] { + getChildNodes(nodeName: string, type = 'main', depth = -1): string[] { + return this.getConnectedNodes(this.connectionsBySourceNode, nodeName, type, depth); + } + + + + /** + * Returns all the nodes before the given one + * + * @param {string} nodeName + * @param {string} [type='main'] + * @param {*} [depth=-1] + * @returns {string[]} + * @memberof Workflow + */ + getParentNodes(nodeName: string, type = 'main', depth = -1): string[] { + return this.getConnectedNodes(this.connectionsByDestinationNode, nodeName, type, depth); + } + + + + /** + * Gets all the nodes which are connected nodes starting from + * the given one + * + * @param {IConnections} connections + * @param {string} nodeName + * @param {string} [type='main'] + * @param {*} [depth=-1] + * @param {string[]} [checkedNodes] + * @returns {string[]} + * @memberof Workflow + */ + getConnectedNodes(connections: IConnections, nodeName: string, type = 'main', depth = -1, checkedNodes?: string[]): string[] { depth = depth === -1 ? -1 : depth; const newDepth = depth === -1 ? depth : depth - 1; if (depth === 0) { @@ -524,12 +557,12 @@ export class Workflow { return []; } - if (!this.connectionsByDestinationNode.hasOwnProperty(nodeName)) { + if (!connections.hasOwnProperty(nodeName)) { // Node does not have incoming connections return []; } - if (!this.connectionsByDestinationNode[nodeName].hasOwnProperty(type)) { + if (!connections[nodeName].hasOwnProperty(type)) { // Node does not have incoming connections of given type return []; } @@ -548,7 +581,7 @@ export class Workflow { let nodeIndex: number; let i: number; let parentNodeName: string; - this.connectionsByDestinationNode[nodeName][type].forEach((connectionsByIndex) => { + connections[nodeName][type].forEach((connectionsByIndex) => { connectionsByIndex.forEach((connection) => { if (checkedNodes!.includes(connection.node)) { // Node got checked already before @@ -557,7 +590,7 @@ export class Workflow { returnNodes.unshift(connection.node); - addNodes = this.getParentNodes(connection.node, type, newDepth, checkedNodes); + addNodes = this.getConnectedNodes(connections, connection.node, type, newDepth, checkedNodes); for (i = addNodes.length; i--; i > 0) { // Because nodes can have multiple parents it is possible that