mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add delete-selection-and-reconnect action
This commit is contained in:
		| @@ -38,7 +38,9 @@ | ||||
|     }, | ||||
|     "red-ui-workspace": { | ||||
|         "backspace": "core:delete-selection", | ||||
|         "ctrl-backspace": "core:delete-selection-and-reconnect", | ||||
|         "delete": "core:delete-selection", | ||||
|         "ctrl-delete": "core:delete-selection-and-reconnect", | ||||
|         "enter": "core:edit-selected-node", | ||||
|         "ctrl-enter": "core:go-to-selection", | ||||
|         "ctrl-c": "core:copy-selection-to-internal-clipboard", | ||||
|   | ||||
| @@ -15,6 +15,9 @@ | ||||
|  **/ | ||||
| RED.nodes = (function() { | ||||
|  | ||||
|     var PORT_TYPE_INPUT = 1; | ||||
|     var PORT_TYPE_OUTPUT = 0; | ||||
|  | ||||
|     var node_defs = {}; | ||||
|     var linkTabMap = {}; | ||||
|  | ||||
| @@ -2458,6 +2461,144 @@ RED.nodes = (function() { | ||||
|         return helpContent; | ||||
|     } | ||||
|  | ||||
|     function getNodeIslands(nodes) { | ||||
|         var selectedNodes = new Set(nodes); | ||||
|         // Maps node => island index | ||||
|         var nodeToIslandIndex = new Map(); | ||||
|         // Maps island index => [nodes in island] | ||||
|         var islandIndexToNodes = new Map(); | ||||
|         var internalLinks = new Set(); | ||||
|         nodes.forEach((node, index) => { | ||||
|             nodeToIslandIndex.set(node,index); | ||||
|             islandIndexToNodes.set(index, [node]); | ||||
|             var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT); | ||||
|             var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT); | ||||
|             inboundLinks.forEach(l => { | ||||
|                 if (selectedNodes.has(l.source)) { | ||||
|                     internalLinks.add(l) | ||||
|                 } | ||||
|             }) | ||||
|             outboundLinks.forEach(l => { | ||||
|                 if (selectedNodes.has(l.target)) { | ||||
|                     internalLinks.add(l) | ||||
|                 } | ||||
|             }) | ||||
|         }) | ||||
|  | ||||
|         internalLinks.forEach(l => { | ||||
|             let source = l.source; | ||||
|             let target = l.target; | ||||
|             if (nodeToIslandIndex.get(source) !== nodeToIslandIndex.get(target)) { | ||||
|                 let sourceIsland = nodeToIslandIndex.get(source); | ||||
|                 let islandToMove = nodeToIslandIndex.get(target); | ||||
|                 let nodesToMove = islandIndexToNodes.get(islandToMove); | ||||
|                 nodesToMove.forEach(n => { | ||||
|                     nodeToIslandIndex.set(n,sourceIsland); | ||||
|                     islandIndexToNodes.get(sourceIsland).push(n); | ||||
|                 }) | ||||
|                 islandIndexToNodes.delete(islandToMove); | ||||
|             } | ||||
|         }) | ||||
|         const result = []; | ||||
|         islandIndexToNodes.forEach((nodes,index) => { | ||||
|             result.push(nodes); | ||||
|         }) | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     function detachNodes(nodes) { | ||||
|         let allSelectedNodes = []; | ||||
|         nodes.forEach(node => { | ||||
|             if (node.type === 'group') { | ||||
|                 let groupNodes = RED.group.getNodes(node,true,true); | ||||
|                 allSelectedNodes = allSelectedNodes.concat(groupNodes); | ||||
|             } else { | ||||
|                 allSelectedNodes.push(node); | ||||
|             } | ||||
|         }) | ||||
|         if (allSelectedNodes.length > 0 ) { | ||||
|             const nodeIslands = RED.nodes.getNodeIslands(allSelectedNodes); | ||||
|             let removedLinks = []; | ||||
|             let newLinks = []; | ||||
|             let createdLinkIds = new Set(); | ||||
|  | ||||
|             nodeIslands.forEach(nodes => { | ||||
|                 let selectedNodes = new Set(nodes); | ||||
|                 let allInboundLinks = []; | ||||
|                 let allOutboundLinks = []; | ||||
|                 // Identify links that enter or exit this island of nodes | ||||
|                 nodes.forEach(node => { | ||||
|                     var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT); | ||||
|                     var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT); | ||||
|                     inboundLinks.forEach(l => { | ||||
|                         if (!selectedNodes.has(l.source)) { | ||||
|                             allInboundLinks.push(l) | ||||
|                         } | ||||
|                     }) | ||||
|                     outboundLinks.forEach(l => { | ||||
|                         if (!selectedNodes.has(l.target)) { | ||||
|                             allOutboundLinks.push(l) | ||||
|                         } | ||||
|                     }) | ||||
|                 }); | ||||
|  | ||||
|  | ||||
|                 // Identify the links to restore | ||||
|                 allInboundLinks.forEach(inLink => { | ||||
|                     // For Each inbound link, | ||||
|                     //  - get source node. | ||||
|                     //  - trace through to all outbound links | ||||
|                     let sourceNode = inLink.source; | ||||
|                     let targetNodes = new Set(); | ||||
|                     let visited = new Set(); | ||||
|                     let stack = [inLink.target]; | ||||
|                     while (stack.length > 0) { | ||||
|                         let node = stack.pop(stack); | ||||
|                         visited.add(node) | ||||
|                         let links = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT); | ||||
|                         links.forEach(l => { | ||||
|                             if (visited.has(l.target)) { | ||||
|                                 return | ||||
|                             } | ||||
|                             visited.add(l.target); | ||||
|                             if (selectedNodes.has(l.target)) { | ||||
|                                 // internal link | ||||
|                                 stack.push(l.target) | ||||
|                             } else { | ||||
|                                 targetNodes.add(l.target) | ||||
|                             } | ||||
|                         }) | ||||
|                     } | ||||
|                     targetNodes.forEach(target => { | ||||
|                         let linkId = `${sourceNode.id}[${inLink.sourcePort}] -> ${target.id}` | ||||
|                         if (!createdLinkIds.has(linkId)) { | ||||
|                             createdLinkIds.add(linkId); | ||||
|                             let link = { | ||||
|                                 source: sourceNode, | ||||
|                                 sourcePort: inLink.sourcePort, | ||||
|                                 target: target | ||||
|                             } | ||||
|                             let existingLinks = RED.nodes.filterLinks(link) | ||||
|                             if (existingLinks.length === 0) { | ||||
|                                 newLinks.push(link); | ||||
|                             } | ||||
|                         } | ||||
|                     }) | ||||
|                 }) | ||||
|  | ||||
|                 // 2. delete all those links | ||||
|                 allInboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)}) | ||||
|                 allOutboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)}) | ||||
|             }) | ||||
|  | ||||
|             newLinks.forEach(l => RED.nodes.addLink(l)); | ||||
|             return { | ||||
|                 newLinks, | ||||
|                 removedLinks | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|             RED.events.on("registry:node-type-added",function(type) { | ||||
| @@ -2539,7 +2680,7 @@ RED.nodes = (function() { | ||||
|         add: addNode, | ||||
|         remove: removeNode, | ||||
|         clear: clear, | ||||
|  | ||||
|         detachNodes: detachNodes, | ||||
|         moveNodesForwards: moveNodesForwards, | ||||
|         moveNodesBackwards: moveNodesBackwards, | ||||
|         moveNodesToFront: moveNodesToFront, | ||||
| @@ -2638,6 +2779,7 @@ RED.nodes = (function() { | ||||
|         getAllFlowNodes: getAllFlowNodes, | ||||
|         getAllUpstreamNodes: getAllUpstreamNodes, | ||||
|         getAllDownstreamNodes: getAllDownstreamNodes, | ||||
|         getNodeIslands: getNodeIslands, | ||||
|         createExportableNodeSet: createExportableNodeSet, | ||||
|         createCompleteNodeSet: createCompleteNodeSet, | ||||
|         updateConfigNodeUsers: updateConfigNodeUsers, | ||||
|   | ||||
| @@ -590,12 +590,14 @@ RED.group = (function() { | ||||
|         markDirty(group); | ||||
|     } | ||||
|  | ||||
|     function getNodes(group,recursive) { | ||||
|     function getNodes(group,recursive,excludeGroup) { | ||||
|         var nodes = []; | ||||
|         group.nodes.forEach(function(n) { | ||||
|             nodes.push(n); | ||||
|             if (n.type !== 'group' || !excludeGroup) { | ||||
|                 nodes.push(n); | ||||
|             } | ||||
|             if (recursive && n.type === 'group') { | ||||
|                 nodes = nodes.concat(getNodes(n,recursive)) | ||||
|                 nodes = nodes.concat(getNodes(n,recursive,excludeGroup)) | ||||
|             } | ||||
|         }) | ||||
|         return nodes; | ||||
|   | ||||
| @@ -562,6 +562,7 @@ RED.view = (function() { | ||||
|         }) | ||||
|  | ||||
|         RED.actions.add("core:delete-selection",deleteSelection); | ||||
|         RED.actions.add("core:delete-selection-and-reconnect",function() { deleteSelection(true) }); | ||||
|         RED.actions.add("core:edit-selected-node",editSelection); | ||||
|         RED.actions.add("core:go-to-selection",function() { | ||||
|             if (movingSet.length() > 0) { | ||||
| @@ -1916,9 +1917,11 @@ RED.view = (function() { | ||||
|             return; | ||||
|         } | ||||
|         if (mouse_mode === RED.state.DETACHED_DRAGGING) { | ||||
|             var node = movingSet.get(0); | ||||
|             node.n.x = node.ox; | ||||
|             node.n.y = node.oy; | ||||
|             for (var j=0;j<movingSet.length();j++) { | ||||
|                 var n = movingSet.get(j); | ||||
|                 n.n.x = n.ox; | ||||
|                 n.n.y = n.oy; | ||||
|             } | ||||
|             clearSelection(); | ||||
|             RED.history.pop(); | ||||
|             mouse_mode = 0; | ||||
| @@ -2162,7 +2165,7 @@ RED.view = (function() { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function deleteSelection() { | ||||
|     function deleteSelection(reconnectWires) { | ||||
|         if (mouse_mode === RED.state.SELECTING_NODE) { | ||||
|             return; | ||||
|         } | ||||
| @@ -2220,6 +2223,16 @@ RED.view = (function() { | ||||
|             var removedSubflowInputs = []; | ||||
|             var removedSubflowStatus; | ||||
|             var subflowInstances = []; | ||||
|             var historyEvents = []; | ||||
|  | ||||
|             if (reconnectWires) { | ||||
|                 var reconnectResult = RED.nodes.detachNodes(movingSet.nodes()) | ||||
|                 var addedLinks = reconnectResult.newLinks; | ||||
|                 if (addedLinks.length > 0) { | ||||
|                     historyEvents.push({ t:'add', links: addedLinks }) | ||||
|                 } | ||||
|                 removedLinks = removedLinks.concat(reconnectResult.removedLinks) | ||||
|             } | ||||
|  | ||||
|             var startDirty = RED.nodes.dirty(); | ||||
|             var startChanged = false; | ||||
| @@ -2310,7 +2323,6 @@ RED.view = (function() { | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var linkRemoveHistoryEvents = []; | ||||
|             if (selectedLinks.length() > 0) { | ||||
|                 selectedLinks.forEach(function(link) { | ||||
|                     if (link.link) { | ||||
| @@ -2318,31 +2330,22 @@ RED.view = (function() { | ||||
|                         var targetId = link.target.id; | ||||
|                         var sourceIdIndex = link.target.links.indexOf(sourceId); | ||||
|                         var targetIdIndex = link.source.links.indexOf(targetId); | ||||
|  | ||||
|                         linkRemoveHistoryEvents.push({ | ||||
|                             t:"multi", | ||||
|                             events: [ | ||||
|                                 { | ||||
|                                     t: "edit", | ||||
|                                     node: link.source, | ||||
|                                     changed: link.source.changed, | ||||
|                                     changes: { | ||||
|                                         links: $.extend(true,{},{v:link.source.links}).v | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     t: "edit", | ||||
|                                     node: link.target, | ||||
|                                     changed: link.target.changed, | ||||
|                                     changes: { | ||||
|                                         links: $.extend(true,{},{v:link.target.links}).v | ||||
|                                     } | ||||
|                                 } | ||||
|  | ||||
|                             ], | ||||
|                             dirty:startDirty | ||||
|                         historyEvents.push({ | ||||
|                             t: "edit", | ||||
|                             node: link.source, | ||||
|                             changed: link.source.changed, | ||||
|                             changes: { | ||||
|                                 links: $.extend(true,{},{v:link.source.links}).v | ||||
|                             } | ||||
|                         }) | ||||
|                         historyEvents.push({ | ||||
|                             t: "edit", | ||||
|                             node: link.target, | ||||
|                             changed: link.target.changed, | ||||
|                             changes: { | ||||
|                                 links: $.extend(true,{},{v:link.target.links}).v | ||||
|                             } | ||||
|                         }) | ||||
|  | ||||
|                         link.source.changed = true; | ||||
|                         link.target.changed = true; | ||||
|                         link.target.links.splice(sourceIdIndex,1); | ||||
| @@ -2373,11 +2376,11 @@ RED.view = (function() { | ||||
|             if (removedSubflowStatus) { | ||||
|                 historyEvent.subflow.status = removedSubflowStatus; | ||||
|             } | ||||
|             if (linkRemoveHistoryEvents.length > 0) { | ||||
|                 linkRemoveHistoryEvents.unshift(historyEvent); | ||||
|             if (historyEvents.length > 0) { | ||||
|                 historyEvents.unshift(historyEvent); | ||||
|                 RED.history.push({ | ||||
|                     t:"multi", | ||||
|                     events: linkRemoveHistoryEvents | ||||
|                     events: historyEvents | ||||
|                 }) | ||||
|             } else { | ||||
|                 RED.history.push(historyEvent); | ||||
| @@ -2460,80 +2463,23 @@ RED.view = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function detachSelectedNodes() { | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes && selection.nodes.length === 1) { | ||||
|             // var selectedNodes = new Set(selection.nodes); | ||||
|             // | ||||
|             // var allInboundLinks = []; | ||||
|             // var allOutboundLinks = []; | ||||
|             // | ||||
|             // selection.nodes.forEach(node => { | ||||
|             //     var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT); | ||||
|             //     var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT); | ||||
|             //     inboundLinks.forEach(l => { | ||||
|             //         if (!selectedNodes.has(l.source)) { | ||||
|             //             allInboundLinks.push(l); | ||||
|             //         } | ||||
|             //     }) | ||||
|             //     outboundLinks.forEach(l => { | ||||
|             //         if (!selectedNodes.has(l.target)) { | ||||
|             //             allOutboundLinks.push(l); | ||||
|             //         } | ||||
|             //     }) | ||||
|             // }) | ||||
|  | ||||
|  | ||||
|             var node = selection.nodes[0]; | ||||
|             // 1. find all the links attached to this node | ||||
|             var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT); | ||||
|             var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT); | ||||
|  | ||||
|  | ||||
|             // 2. delete all those links | ||||
|             inboundLinks.forEach(l => RED.nodes.removeLink(l)) | ||||
|             outboundLinks.forEach(l => RED.nodes.removeLink(l)) | ||||
|  | ||||
|             // 3. restore links from all source nodes to all target nodes | ||||
|  | ||||
|             var newLinks = []; | ||||
|             var createdLinkIds = new Set(); | ||||
|  | ||||
|             inboundLinks.forEach(inLink => { | ||||
|                 outboundLinks.forEach(outLink => { | ||||
|                     var linkId = inLink.source.id+":"+inLink.sourcePort+":"+outLink.target.id | ||||
|                     if (!createdLinkIds.has(linkId)) { | ||||
|                         createdLinkIds.add(linkId); | ||||
|                         var link = { | ||||
|                             source: inLink.source, | ||||
|                             sourcePort: inLink.sourcePort, | ||||
|                             target: outLink.target | ||||
|                         }; | ||||
|                         var existingLinks = RED.nodes.filterLinks(link) | ||||
|                         if (existingLinks.length === 0) { | ||||
|                             newLinks.push(link); | ||||
|                             RED.nodes.addLink(link); | ||||
|                         } | ||||
|                     } | ||||
|                 }) | ||||
|             }) | ||||
|  | ||||
|             var oldLinks = inboundLinks.concat(outboundLinks); | ||||
|  | ||||
|             if (oldLinks.length) { | ||||
|         if (selection.nodes) { | ||||
|             const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes); | ||||
|             if (removedLinks.length || newLinks.length) { | ||||
|                 RED.history.push({ | ||||
|                     t: "multi", | ||||
|                     events: [ | ||||
|                         { t:'delete', links: oldLinks }, | ||||
|                         { t:'delete', links: removedLinks }, | ||||
|                         { t:'add', links: newLinks } | ||||
|                     ], | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 }) | ||||
|                 RED.nodes.dirty(true) | ||||
|             } | ||||
|  | ||||
|  | ||||
|             prepareDrag([node.x,node.y]); | ||||
|             prepareDrag([selection.nodes[0].x,selection.nodes[0].y]); | ||||
|             mouse_mode = RED.state.DETACHED_DRAGGING; | ||||
|             RED.view.redraw(true); | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user