diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js
new file mode 100644
index 000000000..7c9bd67f9
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js
@@ -0,0 +1,217 @@
+/**
+ * Copyright JS Foundation and other contributors, http://js.foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+RED.group = (function() {
+
+ var _groupEditTemplate = '';
+
+ var groupDef = {
+ defaults:{
+ name:{value:""},
+ style:{value:{}}
+ },
+ category: "config",
+ oneditprepare: function() {
+ var style = this.style || {};
+ $("#node-input-style-stroke").val(style.stroke || "#eeeeee")
+ $("#node-input-style-fill").val(style.fill || "none")
+ },
+ oneditresize: function(size) {
+ },
+ oneditsave: function() {
+ this.style.stroke = $("#node-input-style-stroke").val();
+ this.style.fill = $("#node-input-style-fill").val();
+ },
+ set:{
+ module: "node-red"
+ }
+ }
+
+ function init() {
+
+
+ RED.actions.add("core:group-selection", function() { groupSelection() })
+
+ $(_groupEditTemplate).appendTo("#red-ui-editor-node-configs");
+
+ }
+
+ function groupSelection() {
+ var selection = RED.view.selection();
+ // var groupNodes = new Set();
+ //
+ // if (selection.groups) {
+ // selection.groups.forEach(function(g) {
+ // g.nodes.forEach()
+ // })
+ // }
+ //
+ // if (selection.nodes) {
+ //
+ // }
+
+ if (selection.nodes) {
+ var group = createGroup(selection.nodes);
+ if (group) {
+ RED.view.select({groups:[group]})
+ }
+ }
+ }
+ function createGroup(nodes) {
+ if (nodes.length === 0) {
+ return;
+ }
+ // nodes is an array
+ // each node must be on the same tab (z)
+ var group = {
+ id: RED.nodes.id(),
+ type: 'group',
+ nodes: [],
+ style: {
+ stroke: "#999",
+ fill: "none"
+ },
+ x: Number.POSITIVE_INFINITY,
+ y: Number.POSITIVE_INFINITY,
+ w: 0,
+ h: 0,
+ _def: RED.group.def
+ }
+ try {
+ addToGroup(group,nodes);
+ } catch(err) {
+ RED.notify(err,"error");
+ return;
+ }
+ group.z = nodes[0].z;
+
+ RED.nodes.addGroup(group);
+ return group;
+ }
+
+ function addToGroup(group,nodes) {
+ if (!Array.isArray(nodes)) {
+ nodes = [nodes];
+ }
+ var i,n,z;
+ var g;
+ // First pass - validate we can safely add these nodes to the group
+ for (i=0;i -1) {
+ g.nodes.splice(ni,1)
+ }
+ }
+ n.g = group.id;
+ group.nodes.push(n);
+ group.x = Math.min(group.x,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0));
+ group.y = Math.min(group.y,n.y-n.h/2-25);
+ group.w = Math.max(group.w,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0) - group.x),
+ group.h = Math.max(group.h,n.y+n.h/2+25-group.y);
+ }
+ }
+
+ function getNodes(group,recursive) {
+ var nodes = [];
+ group.nodes.forEach(function(n) {
+ if (!recursive || n.type !== 'group') {
+ nodes.push(n);
+ } else {
+ nodes = nodes.concat(getNodes(n,recursive))
+ }
+ })
+ return nodes;
+ }
+
+ function groupContains(group,item) {
+ if (item.g === group.id) {
+ return true;
+ }
+ for (var i=0;i 0) {
var gridOffset = [0,0];
node = moving_set[0];
@@ -1327,20 +1326,15 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); }
if (!node.n.g && activeGroups) {
if (!groupHoverTimer) {
groupHoverTimer = setTimeout(function() {
- activeHoverGroup = null;
+ activeHoverGroup = getGroupAt(node.n.x,node.n.y);
for (var i=0;i= g.pos.x0 && node.n.x <= g.pos.x1 &&
- node.n.y >= g.pos.y0 && node.n.y <= g.pos.y1
- ) {
- g.dirty = !g.hovered;
+ if (g === activeHoverGroup) {
g.hovered = true;
- activeHoverGroup = g;
- } else {
- // Mark dirty if it is selected
- g.dirty = g.hovered;
+ g.dirty = true;
+ } else if (g.hovered) {
g.hovered = false;
+ g.dirty = true;
}
}
groupHoverTimer = null;
@@ -1972,7 +1966,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
RED.notify(RED._("clipboard.nodeCopied",{count:nns.length}),{id:"clipboard"});
}
}
-
+
function calculateTextWidth(str, className, offset) {
var result=convertLineBreakCharacter(str);
var width = 0;
@@ -2714,11 +2708,12 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
}
}
+
function groupMouseDown(g) {
var mouse = d3.touches(this.parentNode)[0]||d3.mouse(this.parentNode);
- if (! (mouse[0] < g.pos.x0+10 || mouse[0] > g.pos.x1-10 || mouse[1] < g.pos.y0+10 || mouse[1] > g.pos.y1-10) ) {
- return
- }
+ // if (! (mouse[0] < g.x+10 || mouse[0] > g.x+g.w-10 || mouse[1] < g.y+10 || mouse[1] > g.y+g.h-10) ) {
+ // return
+ // }
focusView();
if (d3.event.button === 1) {
@@ -2768,10 +2763,8 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
if (d3.event.button != 2) {
var d = g.nodes[0];
prepareDrag(mouse);
- mousedown_group.dx0 = mousedown_group.pos.x0 - mouse[0];
- mousedown_group.dy0 = mousedown_group.pos.y0 - mouse[1];
- mousedown_group.dx1 = mousedown_group.pos.x1 - mouse[0];
- mousedown_group.dy1 = mousedown_group.pos.y1 - mouse[1];
+ mousedown_group.dx = mousedown_group.x - mouse[0];
+ mousedown_group.dy = mousedown_group.y - mouse[1];
}
}
@@ -2787,7 +2780,8 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
}
if (includeNodes) {
var currentSet = new Set(moving_set.map(function(n) { return n.n }));
- g.nodes.forEach(function(n) {
+ var allNodes = RED.group.getNodes(g,true);
+ allNodes.forEach(function(n) {
if (!currentSet.has(n)) {
moving_set.push({n:n})
// n.selected = true;
@@ -2820,48 +2814,62 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
}
}
function getGroupAt(x,y) {
+ var candidateGroups = {};
for (var i=0;i= g.pos.x0 && x <= g.pos.x1 && y >= g.pos.y0 && y <= g.pos.y1) {
- return g;
+ if (x >= g.x && x <= g.x + g.w && y >= g.y && y <= g.y + g.h) {
+ candidateGroups[g.id] = g;
}
}
- return null;
+ var ids = Object.keys(candidateGroups);
+ if (ids.length > 1) {
+ ids.forEach(function(id) {
+ if (candidateGroups[id] && candidateGroups[id].g) {
+ delete candidateGroups[candidateGroups[id].g]
+ }
+ })
+ ids = Object.keys(candidateGroups);
+ }
+ if (ids.length === 0) {
+ return null;
+ } else {
+ return candidateGroups[ids[0]]
+ }
}
- function groupHandleMouseDown(group, groupEl, handle,handleIndex) {
- d3.event.stopPropagation();
- console.log("GHANDLE MD");
- if (d3.event.button != 2) {
- mousedown_group = group;
- group.activeHandle = handleIndex;
- var mouse = d3.touches(handle.parentNode.parentNode)[0]||d3.mouse(handle.parentNode.parentNode);
- switch(handleIndex) {
- case 0: group.ox = group.pos.x0; group.oy = group.pos.y0; break;
- case 1: group.ox = group.pos.x1; group.oy = group.pos.y0; break;
- case 2: group.ox = group.pos.x1; group.oy = group.pos.y1; break;
- case 3: group.ox = group.pos.x0; group.oy = group.pos.y1; break;
- }
- group.dx = group.ox - mouse[0];
- group.dy = group.oy - mouse[1];
- console.log("START",group.ox, group.oy);
- mouse_offset = d3.mouse(document.body);
- if (isNaN(mouse_offset[0])) {
- mouse_offset = d3.touches(document.body)[0];
- }
- mouse_mode = RED.state.GROUP_RESIZE;
- }
- }
- function groupHandleMouseUp(group,groupEl,handle,handleIndex) {
- console.log("GHANDLE MU");
- d3.event.stopPropagation();
- delete group.ox;
- delete group.oy;
- delete group.dx;
- delete group.dy;
- resetMouseVars();
- mouse_mode = RED.state.DEFAULT;
- }
+ // function groupHandleMouseDown(group, groupEl, handle,handleIndex) {
+ // d3.event.stopPropagation();
+ // console.log("GHANDLE MD");
+ // if (d3.event.button != 2) {
+ // mousedown_group = group;
+ // group.activeHandle = handleIndex;
+ // var mouse = d3.touches(handle.parentNode.parentNode)[0]||d3.mouse(handle.parentNode.parentNode);
+ // switch(handleIndex) {
+ // case 0: group.ox = group.pos.x0; group.oy = group.pos.y0; break;
+ // case 1: group.ox = group.pos.x1; group.oy = group.pos.y0; break;
+ // case 2: group.ox = group.pos.x1; group.oy = group.pos.y1; break;
+ // case 3: group.ox = group.pos.x0; group.oy = group.pos.y1; break;
+ // }
+ // group.dx = group.ox - mouse[0];
+ // group.dy = group.oy - mouse[1];
+ // console.log("START",group.ox, group.oy);
+ // mouse_offset = d3.mouse(document.body);
+ // if (isNaN(mouse_offset[0])) {
+ // mouse_offset = d3.touches(document.body)[0];
+ // }
+ // mouse_mode = RED.state.GROUP_RESIZE;
+ // }
+ // }
+ // function groupHandleMouseUp(group,groupEl,handle,handleIndex) {
+ // console.log("GHANDLE MU");
+ // d3.event.stopPropagation();
+ // delete group.ox;
+ // delete group.oy;
+ // delete group.dx;
+ // delete group.dy;
+ // resetMouseVars();
+ // mouse_mode = RED.state.DEFAULT;
+ // }
function isButtonEnabled(d) {
var buttonEnabled = true;
@@ -3712,18 +3720,19 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
}
d.dirty = false;
-
if (d.g) {
if (!dirtyGroups[d.g]) {
- dirtyGroups[d.g] = RED.nodes.group(d.g);
- }
- var group = dirtyGroups[d.g];
- group.pos = {
- x0: Math.min(group.pos.x0,d.x-d.w/2-25-((d._def.button && d._def.align!=="right")?20:0)),
- y0: Math.min(group.pos.y0,d.y-d.h/2-25),
- x1: Math.max(group.pos.x1,d.x+d.w/2+25+((d._def.button && d._def.align=="right")?20:0)),
- y1: Math.max(group.pos.y1,d.y+d.h/2+25)
+ var gg = d.g;
+ while (gg && !dirtyGroups[gg]) {
+ dirtyGroups[gg] = RED.nodes.group(gg);
+ gg = dirtyGroups[gg].g;
+ }
}
+ // var group = dirtyGroups[d.g];
+ // group.x = Math.min(group.x,d.x-d.w/2-25-((d._def.button && d._def.align!=="right")?20:0));
+ // group.y = Math.min(group.y,d.y-d.h/2-25),
+ // group.w = Math.max(group.w, d.x+d.w/2+25+((d._def.button && d._def.align=="right")?20:0) - group.x),
+ // group.h = Math.max(group.h, d.y+d.h/2+25 - group.y)
}
}
});
@@ -3981,20 +3990,21 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
.attr('rx',1).attr('ry',1).style({
"fill":"none",
"stroke": "#ff7f0e",
+ "pointer-events": "stroke",
"stroke-opacity": 0,
"stroke-width": 15
})
+
g.append('rect').classed("red-ui-flow-group-body",true)
.attr('rx',1).attr('ry',1).style({
+ "pointer-events": "none",
"fill":d.fill||"none",
"stroke": d.stroke||"none",
"stroke-width": 2
})
-
-
- d.dirty = true;
g.on("mousedown",groupMouseDown).on("mouseup",groupMouseUp)
+ d.dirty = true;
});
group.each(function(d,i) {
if (d.dirty || dirtyGroups[d.id]) {
@@ -4004,26 +4014,33 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
var maxX = 0;
var maxY = 0;
d.nodes.forEach(function(n) {
- minX = Math.min(minX,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0));
- minY = Math.min(minY,n.y-n.h/2-25);
- maxX = Math.max(maxX,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0));
- maxY = Math.max(maxY,n.y+n.h/2+25);
+ if (n.type !== "group") {
+ minX = Math.min(minX,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0));
+ minY = Math.min(minY,n.y-n.h/2-25);
+ maxX = Math.max(maxX,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0));
+ maxY = Math.max(maxY,n.y+n.h/2+25);
+ } else {
+ minX = Math.min(minX,n.x-25)
+ minY = Math.min(minY,n.y-25)
+ maxX = Math.max(maxX,n.x+n.w+25)
+ maxY = Math.max(maxY,n.y+n.h+25)
+ }
});
- d.pos = {
- x0: minX, y0: minY,
- x1: maxX, y1: maxY
- }
+ d.x = minX;
+ d.y = minY;
+ d.w = maxX - minX;
+ d.h = maxY - minY;
- g.attr("transform","translate("+d.pos.x0+","+d.pos.y0+")");
+ g.attr("transform","translate("+d.x+","+d.y+")");
g.selectAll(".red-ui-flow-group-outline")
- .attr("width",d.pos.x1-d.pos.x0)
- .attr("height",d.pos.y1-d.pos.y0)
+ .attr("width",d.w)
+ .attr("height",d.h)
.style("stroke-opacity",function(d) { if (d.selected) { return 0.3 } return 0});
g.selectAll(".red-ui-flow-group-body")
- .attr("width",d.pos.x1-d.pos.x0)
- .attr("height",d.pos.y1-d.pos.y0)
+ .attr("width",d.w)
+ .attr("height",d.h)
.style("stroke",function(d) { /*if (d.selected) { return "#ff7f0e" } */return d.style.stroke || "none"})
.style("stroke-dasharray", function(d) { return (d.active||d.hovered)?"10 4":"none"})
.style("fill", function(d) { return d.style.fill || "none"})
@@ -4338,16 +4355,34 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
},
selection: function() {
var selection = {};
+
+ var allNodes = new Set();
+
if (moving_set.length > 0) {
- selection.nodes = moving_set.map(function(n) { return n.n;});
+ moving_set.forEach(function(n) {
+ allNodes.add(n.n);
+ });
+ }
+ var selectedGroups = activeGroups.filter(function(g) { return g.selected });
+ if (selectedGroups.length > 0) {
+ if (selectedGroups.length === 1 && selectedGroups[0].active) {
+ // Let nodes be nodes
+ } else {
+ selectedGroups.forEach(function(g) {
+ var groupNodes = RED.group.getNodes(g,true);
+ groupNodes.forEach(function(n) {
+ allNodes.delete(n);
+ });
+ allNodes.add(g);
+ });
+ }
+ }
+ if (allNodes.size > 0) {
+ selection.nodes = Array.from(allNodes);
}
if (selected_link != null) {
selection.link = selected_link;
}
- selection.groups = activeGroups.filter(function(g) { return g.selected })
- if (selection.groups.length === 0) {
- delete selection.groups;
- }
return selection;
},
scale: function() {