mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
[groups] Support copy/paste/import/export of groups
This commit is contained in:
parent
9a0c843f29
commit
d1dd7d1d51
@ -500,6 +500,9 @@ RED.nodes = (function() {
|
||||
if (n.d === true) {
|
||||
node.d = true;
|
||||
}
|
||||
if (n.g) {
|
||||
node.g = n.g;
|
||||
}
|
||||
if (node.type == "unknown") {
|
||||
for (var p in n._orig) {
|
||||
if (n._orig.hasOwnProperty(p)) {
|
||||
@ -547,6 +550,9 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n.type === "group") {
|
||||
node.nodes = node.nodes.map(function(n) { return n.id });
|
||||
}
|
||||
if (n._def.category != "config") {
|
||||
node.x = n.x;
|
||||
node.y = n.y;
|
||||
@ -735,6 +741,11 @@ RED.nodes = (function() {
|
||||
nns.push(convertSubflow(subflows[i], exportCredentials));
|
||||
}
|
||||
}
|
||||
for (i in groups) {
|
||||
if (groups.hasOwnProperty(i)) {
|
||||
nns.push(convertNode(groups[i]));
|
||||
}
|
||||
}
|
||||
for (i in configNodes) {
|
||||
if (configNodes.hasOwnProperty(i)) {
|
||||
nns.push(convertNode(configNodes[i], exportCredentials));
|
||||
@ -861,6 +872,7 @@ RED.nodes = (function() {
|
||||
if (n.type != "workspace" &&
|
||||
n.type != "tab" &&
|
||||
n.type != "subflow" &&
|
||||
n.type != "group" &&
|
||||
!registry.getNodeType(n.type) &&
|
||||
n.type.substring(0,8) != "subflow:" &&
|
||||
unknownTypes.indexOf(n.type)==-1) {
|
||||
@ -910,6 +922,7 @@ RED.nodes = (function() {
|
||||
var node_map = {};
|
||||
var new_nodes = [];
|
||||
var new_links = [];
|
||||
var new_groups = [];
|
||||
var nid;
|
||||
var def;
|
||||
var configNode;
|
||||
@ -1077,20 +1090,25 @@ RED.nodes = (function() {
|
||||
y:parseFloat(n.y || 0),
|
||||
z:n.z,
|
||||
type:0,
|
||||
wires:n.wires||[],
|
||||
inputLabels: n.inputLabels,
|
||||
outputLabels: n.outputLabels,
|
||||
icon: n.icon,
|
||||
info: n.info,
|
||||
changed:false,
|
||||
_config:{}
|
||||
};
|
||||
}
|
||||
if (n.type !== "group") {
|
||||
node.wires = n.wires||[];
|
||||
node.inputLabels = n.inputLabels;
|
||||
node.outputLabels = n.outputLabels;
|
||||
node.icon = n.icon;
|
||||
}
|
||||
if (n.hasOwnProperty('l')) {
|
||||
node.l = n.l;
|
||||
}
|
||||
if (n.hasOwnProperty('d')) {
|
||||
node.d = n.d;
|
||||
}
|
||||
if (n.hasOwnProperty('g')) {
|
||||
node.g = n.g;
|
||||
}
|
||||
if (createNewIds) {
|
||||
if (subflow_blacklist[n.z]) {
|
||||
continue;
|
||||
@ -1127,7 +1145,17 @@ RED.nodes = (function() {
|
||||
}
|
||||
node.type = n.type;
|
||||
node._def = def;
|
||||
if (n.type.substring(0,7) === "subflow") {
|
||||
if (node.type === "group") {
|
||||
node._def = RED.group.def;
|
||||
for (d in node._def.defaults) {
|
||||
if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
|
||||
node[d] = n[d];
|
||||
node._config[d] = JSON.stringify(n[d]);
|
||||
}
|
||||
}
|
||||
node._config.x = node.x;
|
||||
node._config.y = node.y;
|
||||
} else if (n.type.substring(0,7) === "subflow") {
|
||||
var parentId = n.type.split(":")[1];
|
||||
var subflow = subflow_blacklist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
||||
if (createNewIds) {
|
||||
@ -1217,13 +1245,19 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
addNode(node);
|
||||
RED.editor.validateNode(node);
|
||||
if (node.type !== "group") {
|
||||
addNode(node);
|
||||
RED.editor.validateNode(node);
|
||||
} else {
|
||||
addGroup(node);
|
||||
}
|
||||
node_map[n.id] = node;
|
||||
// If an 'unknown' config node, it will not have been caught by the
|
||||
// proper config node handling, so needs adding to new_nodes here
|
||||
if (node.type === "unknown" || node._def.category !== "config") {
|
||||
new_nodes.push(node);
|
||||
} else if (node.type === "group") {
|
||||
new_groups.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1258,6 +1292,11 @@ RED.nodes = (function() {
|
||||
}
|
||||
delete n.wires;
|
||||
}
|
||||
if (n.g && node_map[n.g]) {
|
||||
n.g = node_map[n.g].id;
|
||||
} else {
|
||||
delete n.g
|
||||
}
|
||||
for (var d3 in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d3)) {
|
||||
if (n._def.defaults[d3].type && node_map[n[d3]]) {
|
||||
@ -1326,6 +1365,17 @@ RED.nodes = (function() {
|
||||
delete n.status.wires;
|
||||
}
|
||||
}
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
if (n.g && node_map[n.g]) {
|
||||
n.g = node_map[n.g].id;
|
||||
} else {
|
||||
delete n.g;
|
||||
}
|
||||
n.nodes = n.nodes.map(function(id) {
|
||||
return node_map[id];
|
||||
})
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
return [new_nodes,new_links,new_workspaces,new_subflows,missingWorkspace];
|
||||
|
@ -37,7 +37,8 @@ RED.group = (function() {
|
||||
var groupDef = {
|
||||
defaults:{
|
||||
name:{value:""},
|
||||
style:{value:{}}
|
||||
style:{value:{}},
|
||||
nodes:{value:[]}
|
||||
},
|
||||
category: "config",
|
||||
oneditprepare: function() {
|
||||
|
@ -1390,13 +1390,15 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); }
|
||||
if (!d3.event.shiftKey) {
|
||||
clearSelection();
|
||||
}
|
||||
var selectedGroups = [];
|
||||
activeNodes.forEach(function(n) {
|
||||
if (n.z == RED.workspaces.active() && !n.selected) {
|
||||
if (n.x > x && n.x < x2 && n.y > y && n.y < y2) {
|
||||
if (n.g) {
|
||||
var group = RED.nodes.group(n.g);
|
||||
if (!group.selected) {
|
||||
selectGroup(RED.nodes.group(n.g),true);
|
||||
selectGroup(group,true);
|
||||
selectedGroups.push(group)
|
||||
}
|
||||
} else {
|
||||
n.selected = true;
|
||||
@ -1406,6 +1408,17 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); }
|
||||
}
|
||||
}
|
||||
});
|
||||
var selectionChanged = false;
|
||||
do {
|
||||
selectionChanged = false;
|
||||
selectedGroups.forEach(function(g) {
|
||||
if (g.g && g.selected && RED.nodes.group(g.g).selected) {
|
||||
g.selected = false;
|
||||
selectionChanged = true;
|
||||
}
|
||||
})
|
||||
} while(selectionChanged);
|
||||
|
||||
if (activeSubflow) {
|
||||
activeSubflow.in.forEach(function(n) {
|
||||
n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2);
|
||||
@ -1550,11 +1563,16 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); }
|
||||
if (mouse_mode === RED.state.SELECTING_NODE && selectNodesOptions.single) {
|
||||
return;
|
||||
}
|
||||
|
||||
exitActiveGroup();
|
||||
activeGroups.forEach(function(g) {
|
||||
selectGroup(g, true);
|
||||
if (!g.selected) {
|
||||
g.selected = true;
|
||||
if (!g.g) {
|
||||
selectGroup(g, true);
|
||||
if (!g.selected) {
|
||||
g.selected = true;
|
||||
g.dirty = true;
|
||||
}
|
||||
} else {
|
||||
g.selected = false;
|
||||
g.dirty = true;
|
||||
}
|
||||
})
|
||||
@ -1940,8 +1958,16 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
|
||||
nodes = nodes.concat(RED.nodes.filterNodes({z:n.id}));
|
||||
}
|
||||
});
|
||||
} else if (moving_set.length > 0) {
|
||||
nodes = moving_set.map(function(n) { return n.n });
|
||||
} else {
|
||||
if (moving_set.length > 0) {
|
||||
nodes = moving_set.map(function(n) { return n.n });
|
||||
}
|
||||
var groups = activeGroups.filter(function(g) { return g.selected && !g.active });
|
||||
while (groups.length > 0) {
|
||||
var group = groups.shift();
|
||||
nodes.push(group);
|
||||
groups = groups.concat(group.nodes.filter(function(n) { return n.type === "group" }))
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.length > 0) {
|
||||
@ -1966,6 +1992,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
|
||||
}
|
||||
}
|
||||
clipboard = JSON.stringify(nns);
|
||||
console.log(nns);
|
||||
RED.notify(RED._("clipboard.nodeCopied",{count:nns.length}),{id:"clipboard"});
|
||||
}
|
||||
}
|
||||
@ -4536,5 +4563,6 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
|
||||
chart.scrollLeft(chart.scrollLeft()+x);
|
||||
chart.scrollTop(chart.scrollTop()+y)
|
||||
}
|
||||
,ms: function() { return moving_set }
|
||||
};
|
||||
})();
|
||||
|
@ -85,6 +85,7 @@ module.exports = {
|
||||
flow.subflows = {};
|
||||
flow.configs = {};
|
||||
flow.flows = {};
|
||||
flow.groups = {};
|
||||
flow.missingTypes = [];
|
||||
|
||||
config.forEach(function(n) {
|
||||
@ -95,8 +96,12 @@ module.exports = {
|
||||
flow.flows[n.id].configs = {};
|
||||
flow.flows[n.id].nodes = {};
|
||||
}
|
||||
if (n.type === 'group') {
|
||||
flow.groups[n.id] = n;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: why a separate forEach? this can be merged with above
|
||||
config.forEach(function(n) {
|
||||
if (n.type === 'subflow') {
|
||||
flow.subflows[n.id] = n;
|
||||
@ -108,7 +113,7 @@ module.exports = {
|
||||
var linkWires = {};
|
||||
var linkOutNodes = [];
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab') {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
||||
var subflowDetails = subflowInstanceRE.exec(n.type);
|
||||
|
||||
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
|
||||
@ -163,7 +168,7 @@ module.exports = {
|
||||
|
||||
var addedTabs = {};
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab') {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
||||
for (var prop in n) {
|
||||
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
||||
// This property references a global config node
|
||||
|
Loading…
Reference in New Issue
Block a user