From 28f91685ce26d63326fb4fe30d5a2d6ad3695b8d Mon Sep 17 00:00:00 2001 From: ralphwetzel Date: Thu, 13 Jan 2022 23:33:06 +0100 Subject: [PATCH] Subflow: Add labels to OUTPUT nodes --- .../src/js/ui/editors/panes/appearance.js | 12 ++ .../@node-red/editor-client/src/js/ui/view.js | 187 +++++++++++++++--- 2 files changed, 175 insertions(+), 24 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js index 046141b52..f265c09db 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js @@ -501,6 +501,12 @@ } var v = $(this).val(); hasNonBlankLabel = hasNonBlankLabel || v!== ""; + + // mark changed output port labels as dirty + if (node.type === "subflow" && node.outputLabels[index] !== v) { + node.out[index].dirty = true; + } + newValue[index] = v; }); @@ -509,6 +515,12 @@ changes.outputLabels = node.outputLabels; node.outputLabels = newValue; changed = true; + + // trigger redraw of dirty port labels + if (node.type === "subflow") { + RED.view.redraw(); + } + } return changed; } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 166cb2901..bda3bba2a 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -3728,28 +3728,88 @@ RED.view = (function() { if (activeSubflow) { var subflowOutputs = nodeLayer.selectAll(".red-ui-flow-subflow-port-output").data(activeSubflow.out,function(d,i){ return d.id;}); subflowOutputs.exit().remove(); - var outGroup = subflowOutputs.enter().insert("svg:g").attr("class","red-ui-flow-node red-ui-flow-subflow-port-output").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"}); + var outGroup = subflowOutputs.enter().insert("svg:g").attr("class","red-ui-flow-node red-ui-flow-subflow-port-output") outGroup.each(function(d,i) { - d.w=40; - d.h=40; + + var node = d3.select(this); + var nodeContents = document.createDocumentFragment(); + + d.h = 40; + d.resize = true; + d.dirty = true; + + var mainRect = document.createElementNS("http://www.w3.org/2000/svg","rect"); + mainRect.__data__ = d; + mainRect.setAttribute("class", "red-ui-flow-subflow-port"); + mainRect.setAttribute("rx", 8); + mainRect.setAttribute("ry", 8); + mainRect.setAttribute("width", 40); + mainRect.setAttribute("height", 40); + node[0][0].__mainRect__ = mainRect; + d3.select(mainRect) + .on("mouseup",nodeMouseUp) + .on("mousedown",nodeMouseDown) + .on("touchstart",nodeTouchStart) + .on("touchend",nodeTouchEnd) + nodeContents.appendChild(mainRect); + + var output_groupEl = document.createElementNS("http://www.w3.org/2000/svg","g"); + output_groupEl.setAttribute("x",0); + output_groupEl.setAttribute("y",0); + + var output_output = document.createElementNS("http://www.w3.org/2000/svg","text"); + output_output.setAttribute("class","red-ui-flow-port-label"); + output_output.style["font-size"] = "10px"; + output_output.textContent = "output"; + output_groupEl.appendChild(output_output); + node[0][0].__outputOutput__ = output_output; + + var output_number = document.createElementNS("http://www.w3.org/2000/svg","text"); + output_number.setAttribute("class","red-ui-flow-port-label red-ui-flow-port-index"); + output_number.setAttribute("x",0); + output_number.setAttribute("y",0); + output_number.textContent = d.i+1; + output_groupEl.appendChild(output_number); + node[0][0].__outputNumber__ = output_number; + + var output_border = document.createElementNS("http://www.w3.org/2000/svg","path"); + output_border.setAttribute("d","M 40 1 l 0 38") + output_border.setAttribute("class", "red-ui-flow-node-icon-shade-border") + output_groupEl.appendChild(output_border); + node[0][0].__outputBorder__ = output_border; + + nodeContents.appendChild(output_groupEl); + + var text = document.createElementNS("http://www.w3.org/2000/svg","g"); + text.setAttribute("class","red-ui-flow-port-label"); + text.setAttribute("transform","translate(38,0)"); + text.setAttribute('style', 'fill : #888'); // hard coded here! + node[0][0].__textGroup__ = text; + nodeContents.append(text); + + var portEl = document.createElementNS("http://www.w3.org/2000/svg","g"); + portEl.setAttribute('transform','translate(-5,15)') + + var port = document.createElementNS("http://www.w3.org/2000/svg","rect"); + port.setAttribute("class","red-ui-flow-port"); + port.setAttribute("rx",3); + port.setAttribute("ry",3); + port.setAttribute("width",10); + port.setAttribute("height",10); + portEl.appendChild(port); + + d3.select(port) + .on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} ) + .on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) + .on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);}) + .on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) + .on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);}) + .on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);}); + + node[0][0].__port__ = portEl + nodeContents.appendChild(portEl); + node[0][0].appendChild(nodeContents); }); - outGroup.append("rect").attr("class","red-ui-flow-subflow-port").attr("rx",8).attr("ry",8).attr("width",40).attr("height",40) - // TODO: This is exactly the same set of handlers used for regular nodes - DRY - .on("mouseup",nodeMouseUp) - .on("mousedown",nodeMouseDown) - .on("touchstart",nodeTouchStart) - .on("touchend",nodeTouchEnd) - - outGroup.append("g").attr('transform','translate(-5,15)').append("rect").attr("class","red-ui-flow-port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10) - .on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} ) - .on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) - .on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);}) - .on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) - .on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);}) - .on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);}); - - outGroup.append("svg:text").attr("class","red-ui-flow-port-label").attr("x",20).attr("y",12).style("font-size","10px").text("output"); - outGroup.append("svg:text").attr("class","red-ui-flow-port-label red-ui-flow-port-index").attr("x",20).attr("y",28).text(function(d,i){ return i+1}); var subflowInputs = nodeLayer.selectAll(".red-ui-flow-subflow-port-input").data(activeSubflow.in,function(d,i){ return d.id;}); subflowInputs.exit().remove(); @@ -3802,11 +3862,90 @@ RED.view = (function() { subflowOutputs.each(function(d,i) { if (d.dirty) { - var output = d3.select(this); - output.classed("red-ui-flow-node-selected",function(d) { return d.selected; }) - output.selectAll(".red-ui-flow-port-index").text(function(d){ return d.i+1}); - output.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; }); + + var port_height = 40; + + var self = this; + var thisNode = d3.select(this); + dirtyNodes[d.id] = d; + + var label = getPortLabel(activeSubflow, PORT_TYPE_OUTPUT, d.i) || ""; + var hideLabel = (label.length < 1) + + var labelParts; + if (d.resize || this.__hideLabel__ !== hideLabel || this.__label__ !== label) { + labelParts = getLabelParts(label, "red-ui-flow-node-label"); + if (labelParts.lines.length !== this.__labelLineCount__ || this.__label__ !== label) { + d.resize = true; + } + this.__label__ = label; + this.__labelLineCount__ = labelParts.lines.length; + + if (hideLabel) { + d.h = Math.max(port_height,(d.outputs || 0) * 15); + } else { + d.h = Math.max(6+24*labelParts.lines.length,(d.outputs || 0) * 15, port_height); + } + this.__hideLabel__ = hideLabel; + } + + if (d.resize) { + var ow = d.w; + if (hideLabel) { + d.w = port_height; + } else { + d.w = Math.max(port_height,20*(Math.ceil((labelParts.width+50+7)/20)) ); + } + if (ow !== undefined) { + d.x += (d.w-ow)/2; + } + d.resize = false; + } + + this.setAttribute("transform", "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"); + // This might be the first redraw after a node has been click-dragged to start a move. + // So its selected state might have changed since the last redraw. + this.classList.toggle("red-ui-flow-node-selected", !!d.selected ) + if (mouse_mode != RED.state.MOVING_ACTIVE) { + this.classList.toggle("red-ui-flow-node-disabled", d.d === true); + this.__mainRect__.setAttribute("width", d.w) + this.__mainRect__.setAttribute("height", d.h) + this.__mainRect__.classList.toggle("red-ui-flow-node-highlighted",!!d.highlighted ); + + if (labelParts) { + // The label has changed + var sa = labelParts.lines; + var sn = labelParts.lines.length; + var textLines = this.__textGroup__.childNodes; + while(textLines.length > sn) { + textLines[textLines.length-1].remove(); + } + for (var i=0; i