mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add delete-selection-and-reconnect action
This commit is contained in:
parent
30f2b96c68
commit
154a4e23dd
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user