diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 142606822..08e83ee87 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -514,7 +514,15 @@ RED.history = (function() { } } } - + if (ev.node.type === 'subflow') { + // Ensure ports get a refresh in case of a label change + if (ev.changes.inputLabels) { + ev.node.in.forEach(function(input) { input.dirty = true; }); + } + if (ev.changes.outputLabels) { + ev.node.out.forEach(function(output) { output.dirty = true; }); + } + } ev.node.dirty = true; ev.node.changed = ev.changed; 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 d6dd5112d..2d225a277 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 @@ -489,6 +489,9 @@ changes.inputLabels = node.inputLabels; node.inputLabels = newValue; changed = true; + if (node.type === "subflow") { + node.in[0].dirty = true + } } hasNonBlankLabel = false; newValue = new Array(node.outputs); 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 89019005f..56cbd4399 100644 --- 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 @@ -4424,6 +4424,186 @@ RED.view = (function() { } } + function buildSubflowPort (d) { + const NODE_TYPE = d.direction === "in" ? PORT_TYPE_INPUT : PORT_TYPE_OUTPUT; + // PORT_TYPE is the 'opposite' of NODE_TYPE + const PORT_TYPE = NODE_TYPE === PORT_TYPE_INPUT ? PORT_TYPE_OUTPUT : PORT_TYPE_INPUT; + 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); + + const port_label_group = document.createElementNS("http://www.w3.org/2000/svg","g"); + port_label_group.setAttribute("x",0); + port_label_group.setAttribute("y",0); + node[0][0].__portLabelGroup__ = port_label_group; + + const port_label = document.createElementNS("http://www.w3.org/2000/svg","text"); + port_label.setAttribute("class","red-ui-flow-port-label"); + port_label.style["font-size"] = "10px"; + port_label.textContent = NODE_TYPE === PORT_TYPE_INPUT? "input" : "output"; + port_label_group.appendChild(port_label); + node[0][0].__portLabel__ = port_label; + + if (NODE_TYPE === PORT_TYPE_OUTPUT) { + const port_number = document.createElementNS("http://www.w3.org/2000/svg","text"); + port_number.setAttribute("class","red-ui-flow-port-label red-ui-flow-port-index"); + port_number.setAttribute("x",0); + port_number.setAttribute("y",0); + port_number.textContent = d.i+1; + port_label_group.appendChild(port_number); + node[0][0].__portNumber__ = port_number; + } + + const port_border = document.createElementNS("http://www.w3.org/2000/svg","path"); + port_border.setAttribute("d","M 40 1 l 0 38") + port_border.setAttribute("class", "red-ui-flow-node-icon-shade-border") + port_label_group.appendChild(port_border); + node[0][0].__portBorder__ = port_border; + + nodeContents.appendChild(port_label_group); + + 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); + port.__data__ = d; + + d3.select(port) + .on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE,0);} ) + .on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE,0);d3.event.preventDefault();} ) + .on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE,0);}) + .on("touchend",function(d,i){portMouseUp(d,PORT_TYPE,0);d3.event.preventDefault();} ) + .on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE,0);}) + .on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE,0);}); + + node[0][0].__port__ = portEl + nodeContents.appendChild(portEl); + node[0][0].appendChild(nodeContents); + } + function updateSubflowPort (d) { + if (d.dirty) { + const port_height = 40; + const NODE_TYPE = d.direction === "in" ? PORT_TYPE_INPUT : PORT_TYPE_OUTPUT; + // PORT_TYPE is the 'opposite' of NODE_TYPE + const PORT_TYPE = NODE_TYPE === PORT_TYPE_INPUT ? PORT_TYPE_OUTPUT : PORT_TYPE_INPUT; + + var label = getPortLabel(activeSubflow, NODE_TYPE, 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 sn) { - textLines[textLines.length-1].remove(); - } - for (var i=0; i