Allow port appearance to vary if connected

5092-port-state
Nick O'Leary 2025-06-09 10:46:32 +01:00
parent 56df614b38
commit ef49374334
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
3 changed files with 133 additions and 43 deletions

View File

@ -725,7 +725,9 @@ RED.nodes = (function() {
}
allNodes.addNode(newNode);
if (!nodeLinks[n.id]) {
nodeLinks[n.id] = {in:[],out:[]};
nodeLinks[n.id] = {
inCount:[],outCount:[],in:[],out:[]
}
}
}
RED.events.emit('nodes:add',newNode, opt);
@ -744,15 +746,19 @@ RED.nodes = (function() {
if (l.source) {
// Possible the node hasn't been added yet
if (!nodeLinks[l.source.id]) {
nodeLinks[l.source.id] = {in:[],out:[]};
nodeLinks[l.source.id] = {inCount:[],outCount:[],in:[],out:[]};
}
nodeLinks[l.source.id].out.push(l);
nodeLinks[l.source.id].outCount[l.sourcePort] = (nodeLinks[l.source.id].outCount[l.sourcePort] || 0) + 1
l.source.dirty = true;
}
if (l.target) {
if (!nodeLinks[l.target.id]) {
nodeLinks[l.target.id] = {in:[],out:[]};
nodeLinks[l.target.id] = {inCount:[],outCount:[],in:[],out:[]};
}
nodeLinks[l.target.id].in.push(l);
nodeLinks[l.target.id].inCount[0] = (nodeLinks[l.target.id].inCount[0] || 0) + 1
l.target.dirty = true;
}
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
linkTabMap[l.source.z].push(l);
@ -945,15 +951,19 @@ RED.nodes = (function() {
if (index != -1) {
links.splice(index,1);
if (l.source && nodeLinks[l.source.id]) {
l.source.dirty = true;
var sIndex = nodeLinks[l.source.id].out.indexOf(l)
if (sIndex !== -1) {
nodeLinks[l.source.id].out.splice(sIndex,1)
nodeLinks[l.source.id].outCount[l.sourcePort]--
}
}
if (l.target && nodeLinks[l.target.id]) {
l.target.dirty = true;
var tIndex = nodeLinks[l.target.id].in.indexOf(l)
if (tIndex !== -1) {
nodeLinks[l.target.id].in.splice(tIndex,1)
nodeLinks[l.target.id].inCount[0]--
}
}
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
@ -3386,6 +3396,20 @@ RED.nodes = (function() {
}
return [];
},
getNodeLinkCount: function(id, portType, index) {
// We *could* just let callers use `getNodeLinks` and get the
// the length for themselves. However, that function creates
// a clone of the array - which is needless work if all you
// want is the length
if (nodeLinks[id]) {
if (portType === 1) {
return nodeLinks[id].inCount[index] || 0
} else {
return nodeLinks[id].outCount[index] || 0
}
}
return 0;
},
addWorkspace: addWorkspace,
removeWorkspace: removeWorkspace,
getWorkspaceOrder: function() { return [...workspacesOrder] },

View File

@ -4967,45 +4967,106 @@ RED.view = (function() {
this.__textGroup__.setAttribute("transform", "translate(38,"+yp+")");
}
var inputPorts = thisNode.selectAll(".red-ui-flow-port-input");
if ((!isLink || (showAllLinkPorts === -1 && !activeLinkNodes[d.id])) && d.inputs === 0 && !inputPorts.empty()) {
inputPorts.each(function(d,i) {
if (!d.__ghost) {
RED.hooks.trigger("viewRemovePort",{
node:d,
el:self,
port:d3.select(this)[0][0],
portType: "input",
portIndex: 0
})
}
}).remove();
} else if (((isLink && (showAllLinkPorts===PORT_TYPE_INPUT||activeLinkNodes[d.id]))|| d.inputs === 1) && inputPorts.empty()) {
var inputGroup = thisNode.append("g").attr("class","red-ui-flow-port-input");
var inputGroupPorts;
if (d.type === "link in") {
inputGroupPorts = inputGroup.append("circle")
.attr("cx",-1).attr("cy",5)
.attr("r",5)
.attr("class","red-ui-flow-port red-ui-flow-link-port")
let numInputs = d.inputs;
if (isLink && d.type === "link in") {
if (showAllLinkPorts===PORT_TYPE_INPUT || activeLinkNodes[d.id]) {
numInputs = 1;
} else {
inputGroupPorts = inputGroup.append("rect").attr("class","red-ui-flow-port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
}
inputGroup[0][0].__port__ = inputGroupPorts[0][0];
inputGroupPorts[0][0].__data__ = this.__data__;
inputGroupPorts[0][0].__portType__ = PORT_TYPE_INPUT;
inputGroupPorts[0][0].__portIndex__ = 0;
if (!d.__ghost) {
inputGroupPorts.on("mousedown",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
.on("touchstart",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();})
.on("mouseup",function(d){portMouseUp(d,PORT_TYPE_INPUT,0);} )
.on("touchend",function(d){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);});
RED.hooks.trigger("viewAddPort",{node:d,el: this, port: inputGroup[0][0], portType: "input", portIndex: 0})
numInputs = 0;
}
}
// Remove extra ports
while (this.__inputs__.length > numInputs) {
const port = this.__inputs__.pop();
if (!d.__ghost) {
RED.hooks.trigger("viewRemovePort",{
node: d,
el: this,
port: port,
portType: "input",
portIndex: this.__inputs__.length
})
}
port.remove();
}
for (let portIndex = 0; portIndex < numInputs; portIndex++ ) {
let portGroup;
if (portIndex === this.__inputs__.length) {
portGroup = document.createElementNS("http://www.w3.org/2000/svg","g");
portGroup.setAttribute("class","red-ui-flow-port-input");
var portPort;
if (d.type === "link in") {
portPort = document.createElementNS("http://www.w3.org/2000/svg","circle");
portPort.setAttribute("cx",-1);
portPort.setAttribute("cy",5);
portPort.setAttribute("r",5);
portPort.setAttribute("class","red-ui-flow-port red-ui-flow-link-port");
} else {
portPort = document.createElementNS("http://www.w3.org/2000/svg","rect");
portPort.setAttribute("rx",3);
portPort.setAttribute("ry",3);
portPort.setAttribute("width",10);
portPort.setAttribute("height",10);
portPort.setAttribute("class","red-ui-flow-port");
}
portGroup.appendChild(portPort);
portGroup.__port__ = portPort;
portPort.__data__ = this.__data__;
portPort.__portType__ = PORT_TYPE_INPUT;
portPort.__portIndex__ = portIndex;
if (!d.__ghost) {
portPort.addEventListener("mousedown", portMouseDownProxy);
portPort.addEventListener("touchstart", portTouchStartProxy);
portPort.addEventListener("mouseup", portMouseUpProxy);
portPort.addEventListener("touchend", portTouchEndProxy);
portPort.addEventListener("mouseover", portMouseOverProxy);
portPort.addEventListener("mouseout", portMouseOutProxy);
}
this.appendChild(portGroup);
this.__inputs__.push(portGroup);
if (!d.__ghost) {
RED.hooks.trigger("viewAddPort",{node:d,el: this, port: portGroup, portType: "input", portIndex: portIndex})
}
} else {
portGroup = this.__inputs__[portIndex];
}
const y = (d.h/2)-((numInputs-1)/2)*13;
portGroup.setAttribute("transform","translate(-5,"+((y+13*portIndex)-5)+")")
portGroup.classList.toggle("red-ui-flow-port-connected",RED.nodes.getNodeLinkCount(d.id,PORT_TYPE_INPUT,portIndex) > 0 )
}
// } else if (((isLink && (showAllLinkPorts===PORT_TYPE_INPUT||activeLinkNodes[d.id]))|| d.inputs === 1) && inputPorts.empty()) {
// var inputGroup = thisNode.append("g").attr("class","red-ui-flow-port-input");
// var inputGroupPorts;
// if (d.type === "link in") {
// inputGroupPorts = inputGroup.append("circle")
// .attr("cx",-1).attr("cy",5)
// .attr("r",5)
// .attr("class","red-ui-flow-port red-ui-flow-link-port")
// } else {
// inputGroupPorts = inputGroup.append("rect").attr("class","red-ui-flow-port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
// }
// inputGroup[0][0].__port__ = inputGroupPorts[0][0];
// inputGroupPorts[0][0].__data__ = this.__data__;
// inputGroupPorts[0][0].__portType__ = PORT_TYPE_INPUT;
// inputGroupPorts[0][0].__portIndex__ = 0;
// if (!d.__ghost) {
// inputGroupPorts.on("mousedown",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
// .on("touchstart",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();})
// .on("mouseup",function(d){portMouseUp(d,PORT_TYPE_INPUT,0);} )
// .on("touchend",function(d){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);});
// RED.hooks.trigger("viewAddPort",{node:d,el: this, port: inputGroup[0][0], portType: "input", portIndex: 0})
// }
// }
// inputPorts.classList.toggle("red-ui-flow-port-connected",RED.nodes.getNodeLinkCount(d.id,PORT_TYPE_INPUT,0) > 0 )
var numOutputs = d.outputs;
if (isLink && d.type === "link out") {
if (d.mode !== "return" && (showAllLinkPorts===PORT_TYPE_OUTPUT || activeLinkNodes[d.id])) {
@ -5075,6 +5136,7 @@ RED.view = (function() {
var x = d.w - 5;
var y = (d.h/2)-((numOutputs-1)/2)*13;
portGroup.setAttribute("transform","translate("+x+","+((y+13*portIndex)-5)+")")
portGroup.classList.toggle("red-ui-flow-port-connected",RED.nodes.getNodeLinkCount(d.id,PORT_TYPE_OUTPUT,portIndex) > 0 )
}
if (d._def.icon) {
var icon = thisNode.select(".red-ui-flow-node-icon");
@ -5121,10 +5183,10 @@ RED.view = (function() {
// this.__errorBadge__.setAttribute("transform", "translate("+(d.w-10-((d.changed||d.moved)?14:0))+", -2)");
// this.__errorBadge__.classList.toggle("hide", d.valid);
thisNode.selectAll(".red-ui-flow-port-input").each(function(d,i) {
var port = d3.select(this);
port.attr("transform",function(d){return "translate(-5,"+((d.h/2)-5)+")";})
});
// thisNode.selectAll(".red-ui-flow-port-input").each(function(d,i) {
// var port = d3.select(this);
// port.attr("transform",function(d){return "translate(-5,"+((d.h/2)-5)+")";})
// });
if (d._def.button) {
var buttonEnabled = isButtonEnabled(d);

View File

@ -234,6 +234,10 @@ svg:not(.red-ui-workspace-lasso-active) {
fill: var(--red-ui-node-port-background);
cursor: crosshair;
}
.red-ui-flow-port-connected .red-ui-flow-port {
// TODO: what should a connected port look like?
fill: var(--red-ui-node-port-background);
}
.red-ui-flow-node-error {
fill: var(--red-ui-node-status-error-background);