From c1ea3380eb27603c5784cda68638220183e40a96 Mon Sep 17 00:00:00 2001 From: bvmensvoort Date: Sat, 10 Jun 2023 21:27:06 +0200 Subject: [PATCH 1/7] Show errors and statuses of config nodes in the sidebar when no catch nodes are used --- packages/node_modules/@node-red/runtime/lib/flows/Flow.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index b5bedb2d6..564a14516 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -601,10 +601,9 @@ class Flow { // Delegate status to any nodes using this config node for (let userNode in node.users) { if (node.users.hasOwnProperty(userNode)) { - node.users[userNode]._flow.handleStatus(node,statusMessage,node.users[userNode],true); + handled = node.users[userNode]._flow.handleStatus(node,statusMessage,node.users[userNode],true) || handled; } } - handled = true; } else { this.statusNodes.forEach(function(targetStatusNode) { if (targetStatusNode.scope && targetStatusNode.scope.indexOf(reportingNode.id) === -1) { @@ -662,10 +661,9 @@ class Flow { // Delegate status to any nodes using this config node for (let userNode in node.users) { if (node.users.hasOwnProperty(userNode)) { - node.users[userNode]._flow.handleError(node,logMessage,msg,node.users[userNode]); + handled = node.users[userNode]._flow.handleError(node,logMessage,msg,node.users[userNode]) || handled; } } - handled = true; } else { var handledByUncaught = false; From 74ab03288bc6651352129b765d32563796d2d0c4 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Tue, 20 Jun 2023 12:18:03 +0100 Subject: [PATCH 2/7] fix typos in test flows --- test/nodes/core/common/20-inject_spec.js | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/nodes/core/common/20-inject_spec.js b/test/nodes/core/common/20-inject_spec.js index 3e9a35391..ea2baf36c 100644 --- a/test/nodes/core/common/20-inject_spec.js +++ b/test/nodes/core/common/20-inject_spec.js @@ -107,7 +107,7 @@ describe('inject node', function() { }); it('inject name of node as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_NODE_NAME", payloadType: "env", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_NODE_NAME", payloadType: "env", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}]; helper.load(injectNode, flow, function () { var n1 = helper.getNode("n1"); @@ -125,7 +125,7 @@ describe('inject node', function() { }); it('inject id of node as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_NODE_ID", payloadType: "env", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_NODE_ID", payloadType: "env", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}]; helper.load(injectNode, flow, function () { var n1 = helper.getNode("n1"); @@ -143,7 +143,7 @@ describe('inject node', function() { }); it('inject path of node as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_NODE_PATH", payloadType: "env", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_NODE_PATH", payloadType: "env", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}]; helper.load(injectNode, flow, function () { var n1 = helper.getNode("n1"); @@ -162,7 +162,7 @@ describe('inject node', function() { it('inject name of flow as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_FLOW_NAME", payloadType: "env", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_FLOW_NAME", payloadType: "env", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}, {id: "flow", type: "tab", label: "FLOW" }, ]; @@ -182,7 +182,7 @@ describe('inject node', function() { }); it('inject id of flow as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_FLOW_ID", payloadType: "env", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_FLOW_ID", payloadType: "env", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}, {id: "flow", type: "tab", name: "FLOW" }, ]; @@ -202,7 +202,7 @@ describe('inject node', function() { }); it('inject name of group as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"}, {id: "n2", type: "helper"}, {id: "g0", type: "group", name: "GROUP" }, ]; @@ -222,7 +222,7 @@ describe('inject node', function() { }); it('inject id of group as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"}, {id: "n2", type: "helper"}, {id: "g0", type: "group", name: "GROUP" }, ]; @@ -243,7 +243,7 @@ describe('inject node', function() { it('inject name of node as environment variable by substitution ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_NODE_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}]; helper.load(injectNode, flow, function () { var n1 = helper.getNode("n1"); @@ -261,7 +261,7 @@ describe('inject node', function() { }); it('inject id of node as environment variable by substitution ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_NODE_ID}", payloadType: "str", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_ID}", payloadType: "str", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}]; helper.load(injectNode, flow, function () { var n1 = helper.getNode("n1"); @@ -279,7 +279,7 @@ describe('inject node', function() { }); it('inject path of node as environment variable by substitution ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_NODE_PATH}", payloadType: "str", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_PATH}", payloadType: "str", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}]; helper.load(injectNode, flow, function () { var n1 = helper.getNode("n1"); @@ -298,7 +298,7 @@ describe('inject node', function() { it('inject name of flow as environment variable by substitution ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_FLOW_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_FLOW_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}, {id: "flow", type: "tab", label: "FLOW" }, ]; @@ -318,7 +318,7 @@ describe('inject node', function() { }); it('inject id of flow as environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_FLOW_ID}", payloadType: "str", wires: [["n2"]], z: "flow"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_FLOW_ID}", payloadType: "str", wires: [["n2"]], z: "flow"}, {id: "n2", type: "helper"}, {id: "flow", type: "tab", name: "FLOW" }, ]; @@ -338,7 +338,7 @@ describe('inject node', function() { }); it('inject name of group as environment variable by substitution ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_GROUP_NAME}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_NAME}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"}, {id: "n2", type: "helper"}, {id: "g0", type: "group", name: "GROUP" }, ]; @@ -358,7 +358,7 @@ describe('inject node', function() { }); it('inject id of group as environment variable by substitution ', function (done) { - var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "${NR_GROUP_ID}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"}, + var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_ID}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"}, {id: "n2", type: "helper"}, {id: "g0", type: "group", name: "GROUP" }, ]; From 610cb170e796c5b7a86e7416d421b69530e1a255 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 21 Jun 2023 15:45:23 +0100 Subject: [PATCH 3/7] Fix connection keep-alive in http request node --- .../nodes/core/network/21-httprequest.js | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js index 4543c3655..6912c99ea 100644 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js +++ b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js @@ -23,6 +23,8 @@ module.exports = async function(RED) { const { v4: uuid } = require('uuid'); const crypto = require('crypto'); const URL = require("url").URL + const http = require("http") + const https = require("https") var mustache = require("mustache"); var querystring = require("querystring"); var cookie = require("cookie"); @@ -65,16 +67,27 @@ in your Node-RED user directory (${RED.settings.userDir}). function HTTPRequest(n) { RED.nodes.createNode(this,n); checkNodeAgentPatch(); - var node = this; - var nodeUrl = n.url; - var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; - var nodeMethod = n.method || "GET"; - var paytoqs = false; - var paytobody = false; - var redirectList = []; - var sendErrorsToCatch = n.senderr; + const node = this; + const nodeUrl = n.url; + const isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; + const nodeMethod = n.method || "GET"; + let paytoqs = false; + let paytobody = false; + let redirectList = []; + const sendErrorsToCatch = n.senderr; node.headers = n.headers || []; - var nodeHTTPPersistent = n["persist"]; + const useKeepAlive = n["persist"]; + let agents = null + if (useKeepAlive) { + agents = { + http: new http.Agent({ keepAlive: true }), + https: new https.Agent({ keepAlive: true }) + } + node.on('close', function () { + agents.http.destroy() + agents.https.destroy() + }) + } if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); } @@ -560,12 +573,14 @@ in your Node-RED user directory (${RED.settings.userDir}). opts.agent = { http: new HttpProxyAgent(proxyOptions), https: new HttpsProxyAgent(proxyOptions) - }; - + } } else { node.warn("Bad proxy url: "+ prox); } } + if (useKeepAlive && !opts.agent) { + opts.agent = agents + } if (tlsNode) { opts.https = {}; tlsNode.addTLSOptions(opts.https); From 26fc942c795cab26063847288462f9ef36df1b42 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 23 Jun 2023 12:24:48 +0100 Subject: [PATCH 4/7] Improve wiring for horizontally aligned nodes --- .../@node-red/editor-client/src/js/ui/view.js | 158 ++++++++++++------ 1 file changed, 103 insertions(+), 55 deletions(-) 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 9fdbc3dde..b7dd48b66 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 @@ -1033,7 +1033,7 @@ RED.view = (function() { }) } - function generateLinkPath(origX,origY, destX, destY, sc) { + function generateLinkPath(origX,origY, destX, destY, sc, hasStatus = false) { var dy = destY-origY; var dx = destX-origX; var delta = Math.sqrt(dy*dy+dx*dx); @@ -1050,62 +1050,110 @@ RED.view = (function() { } else { scale = 0.4-0.2*(Math.max(0,(node_width-Math.min(Math.abs(dx),Math.abs(dy)))/node_width)); } + function genCP(cp) { + return ` M ${cp[0]-5} ${cp[1]} h 10 M ${cp[0]} ${cp[1]-5} v 10 ` + } if (dx*sc > 0) { - return "M "+origX+" "+origY+ - " C "+(origX+sc*(node_width*scale))+" "+(origY+scaleY*node_height)+" "+ - (destX-sc*(scale)*node_width)+" "+(destY-scaleY*node_height)+" "+ - destX+" "+destY + let cp = [ + [(origX+sc*(node_width*scale)), (origY+scaleY*node_height)], + [(destX-sc*(scale)*node_width), (destY-scaleY*node_height)] + ] + return `M ${origX} ${origY} C ${cp[0][0]} ${cp[0][1]} ${cp[1][0]} ${cp[1][1]} ${destX} ${destY}` + + ` ${genCP(cp[0])} ${genCP(cp[1])}` } else { + let topX, topY, bottomX, bottomY + let cp + let midX = Math.floor(destX-dx/2); + let midY = Math.floor(destY-dy/2); + if (Math.abs(dy) < 10) { + bottomY = Math.max(origY, destY) + (hasStatus?35:25) + let startCurveHeight = bottomY - origY + let endCurveHeight = bottomY - destY + cp = [ + [ origX + sc*15 , origY ], + [ origX + sc*25 , origY + 5 ], + [ origX + sc*25 , origY + startCurveHeight/2 ], - var midX = Math.floor(destX-dx/2); - var midY = Math.floor(destY-dy/2); - // - if (dy === 0) { - midY = destY + node_height; - } - var cp_height = node_height/2; - var y1 = (destY + midY)/2 - var topX =origX + sc*node_width*scale; - var topY = dy>0?Math.min(y1 - dy/2 , origY+cp_height):Math.max(y1 - dy/2 , origY-cp_height); - var bottomX = destX - sc*node_width*scale; - var bottomY = dy>0?Math.max(y1, destY-cp_height):Math.min(y1, destY+cp_height); - var x1 = (origX+topX)/2; - var scy = dy>0?1:-1; - var cp = [ - // Orig -> Top - [x1,origY], - [topX,dy>0?Math.max(origY, topY-cp_height):Math.min(origY, topY+cp_height)], - // Top -> Mid - // [Mirror previous cp] - [x1,dy>0?Math.min(midY, topY+cp_height):Math.max(midY, topY-cp_height)], - // Mid -> Bottom - // [Mirror previous cp] - [bottomX,dy>0?Math.max(midY, bottomY-cp_height):Math.min(midY, bottomY+cp_height)], - // Bottom -> Dest - // [Mirror previous cp] - [(destX+bottomX)/2,destY] - ]; - if (cp[2][1] === topY+scy*cp_height) { - if (Math.abs(dy) < cp_height*10) { - cp[1][1] = topY-scy*cp_height/2; - cp[3][1] = bottomY-scy*cp_height/2; - } - cp[2][0] = topX; - } - return "M "+origX+" "+origY+ - " C "+ - cp[0][0]+" "+cp[0][1]+" "+ - cp[1][0]+" "+cp[1][1]+" "+ - topX+" "+topY+ - " S "+ - cp[2][0]+" "+cp[2][1]+" "+ - midX+" "+midY+ - " S "+ - cp[3][0]+" "+cp[3][1]+" "+ - bottomX+" "+bottomY+ - " S "+ + [ origX + sc*25 , origY + startCurveHeight - 5 ], + [ origX + sc*15 , origY + startCurveHeight ], + [ origX , origY + startCurveHeight ], + + [ destX - sc*15, origY + startCurveHeight ], + [ destX - sc*25, origY + startCurveHeight - 5 ], + [ destX - sc*25, destY + endCurveHeight/2 ], + + [ destX - sc*25, destY + 5 ], + [ destX - sc*15, destY ], + [ destX, destY ], + ] + + return "M "+origX+" "+origY+ + " C "+ + cp[0][0]+" "+cp[0][1]+" "+ + cp[1][0]+" "+cp[1][1]+" "+ + cp[2][0]+" "+cp[2][1]+" "+ + " C " + + cp[3][0]+" "+cp[3][1]+" "+ cp[4][0]+" "+cp[4][1]+" "+ - destX+" "+destY + cp[5][0]+" "+cp[5][1]+" "+ + " h "+dx+ + " C "+ + cp[6][0]+" "+cp[6][1]+" "+ + cp[7][0]+" "+cp[7][1]+" "+ + cp[8][0]+" "+cp[8][1]+" "+ + " C " + + cp[9][0]+" "+cp[9][1]+" "+ + cp[10][0]+" "+cp[10][1]+" "+ + cp[11][0]+" "+cp[11][1]+" " + // +genCP(cp[0])+genCP(cp[1])+genCP(cp[2])+genCP(cp[3])+genCP(cp[4]) + // +genCP(cp[5])+genCP(cp[6])+genCP(cp[7])+genCP(cp[8])+genCP(cp[9])+genCP(cp[10]) + } else { + var cp_height = node_height/2; + var y1 = (destY + midY)/2 + topX = origX + sc*node_width*scale; + topY = dy>0?Math.min(y1 - dy/2 , origY+cp_height):Math.max(y1 - dy/2 , origY-cp_height); + bottomX = destX - sc*node_width*scale; + bottomY = dy>0?Math.max(y1, destY-cp_height):Math.min(y1, destY+cp_height); + var x1 = (origX+topX)/2; + var scy = dy>0?1:-1; + cp = [ + // Orig -> Top + [x1,origY], + [topX,dy>0?Math.max(origY, topY-cp_height):Math.min(origY, topY+cp_height)], + // Top -> Mid + // [Mirror previous cp] + [x1,dy>0?Math.min(midY, topY+cp_height):Math.max(midY, topY-cp_height)], + // Mid -> Bottom + // [Mirror previous cp] + [bottomX,dy>0?Math.max(midY, bottomY-cp_height):Math.min(midY, bottomY+cp_height)], + // Bottom -> Dest + // [Mirror previous cp] + [(destX+bottomX)/2,destY] + ]; + if (cp[2][1] === topY+scy*cp_height) { + if (Math.abs(dy) < cp_height*10) { + cp[1][1] = topY-scy*cp_height/2; + cp[3][1] = bottomY-scy*cp_height/2; + } + cp[2][0] = topX; + } + return "M "+origX+" "+origY+ + " C "+ + cp[0][0]+" "+cp[0][1]+" "+ + cp[1][0]+" "+cp[1][1]+" "+ + topX+" "+topY+ + " S "+ + cp[2][0]+" "+cp[2][1]+" "+ + midX+" "+midY+ + " S "+ + cp[3][0]+" "+cp[3][1]+" "+ + bottomX+" "+bottomY+ + " S "+ + cp[4][0]+" "+cp[4][1]+" "+ + destX+" "+destY + + // +genCP(cp[0])+genCP(cp[1])+genCP(cp[2])+genCP(cp[3])+genCP(cp[4]) + } } } @@ -1722,7 +1770,7 @@ RED.view = (function() { var portY = -((numOutputs-1)/2)*13 +13*sourcePort; var sc = (drag_line.portType === PORT_TYPE_OUTPUT)?1:-1; - drag_line.el.attr("d",generateLinkPath(drag_line.node.x+sc*drag_line.node.w/2,drag_line.node.y+portY,mousePos[0],mousePos[1],sc)); + drag_line.el.attr("d",generateLinkPath(drag_line.node.x+sc*drag_line.node.w/2,drag_line.node.y+portY,mousePos[0],mousePos[1],sc, !!drag_line.node.status)); } d3.event.preventDefault(); } else if (mouse_mode == RED.state.MOVING) { @@ -5067,7 +5115,7 @@ RED.view = (function() { // " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+ // (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+ // d.x2+" "+d.y2; - var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1); + var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1, !!(d.source.status || d.target.status)); if (/NaN/.test(path)) { path = "" } From 11f9ad8ca3772e59e4e117d8787e120917ec9fbb Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 23 Jun 2023 12:29:33 +0100 Subject: [PATCH 5/7] Remove debug for wiring --- packages/node_modules/@node-red/editor-client/src/js/ui/view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b7dd48b66..50ee25dfa 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 @@ -1059,7 +1059,7 @@ RED.view = (function() { [(destX-sc*(scale)*node_width), (destY-scaleY*node_height)] ] return `M ${origX} ${origY} C ${cp[0][0]} ${cp[0][1]} ${cp[1][0]} ${cp[1][1]} ${destX} ${destY}` - + ` ${genCP(cp[0])} ${genCP(cp[1])}` + // + ` ${genCP(cp[0])} ${genCP(cp[1])}` } else { let topX, topY, bottomX, bottomY let cp From 56ed32e4a19bf68e51fd10743e2e62d991d28d5e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 23 Jun 2023 16:09:34 +0100 Subject: [PATCH 6/7] Add background to node status --- .../@node-red/editor-client/src/js/ui/view.js | 41 ++++++++++++++----- .../editor-client/src/sass/flow.scss | 6 ++- 2 files changed, 35 insertions(+), 12 deletions(-) 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 50ee25dfa..a49133af7 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 @@ -4160,21 +4160,27 @@ RED.view = (function() { nodeEl.__statusGroup__.style.display = "none"; } else { nodeEl.__statusGroup__.style.display = "inline"; + let backgroundWidth = 12 var fill = status_colours[d.status.fill]; // Only allow our colours for now if (d.status.shape == null && fill == null) { + backgroundWidth = 0 nodeEl.__statusShape__.style.display = "none"; + nodeEl.__statusBackground__.setAttribute("x", 17) nodeEl.__statusGroup__.setAttribute("transform","translate(-14,"+(d.h+3)+")"); } else { nodeEl.__statusGroup__.setAttribute("transform","translate(3,"+(d.h+3)+")"); var statusClass = "red-ui-flow-node-status-"+(d.status.shape||"dot")+"-"+d.status.fill; nodeEl.__statusShape__.style.display = "inline"; nodeEl.__statusShape__.setAttribute("class","red-ui-flow-node-status "+statusClass); + nodeEl.__statusBackground__.setAttribute("x", 3) } if (d.status.hasOwnProperty('text')) { nodeEl.__statusLabel__.textContent = d.status.text; } else { nodeEl.__statusLabel__.textContent = ""; } + const textSize = nodeEl.__statusLabel__.getBBox() + nodeEl.__statusBackground__.setAttribute('width', backgroundWidth + textSize.width + 6) } delete d.dirtyStatus; } @@ -4580,17 +4586,30 @@ RED.view = (function() { statusEl.style.display = "none"; node[0][0].__statusGroup__ = statusEl; - var statusRect = document.createElementNS("http://www.w3.org/2000/svg","rect"); - statusRect.setAttribute("class","red-ui-flow-node-status"); - statusRect.setAttribute("x",6); - statusRect.setAttribute("y",1); - statusRect.setAttribute("width",9); - statusRect.setAttribute("height",9); - statusRect.setAttribute("rx",2); - statusRect.setAttribute("ry",2); - statusRect.setAttribute("stroke-width","3"); - statusEl.appendChild(statusRect); - node[0][0].__statusShape__ = statusRect; + var statusBackground = document.createElementNS("http://www.w3.org/2000/svg","rect"); + statusBackground.setAttribute("class","red-ui-flow-node-status-background"); + statusBackground.setAttribute("x",3); + statusBackground.setAttribute("y",-1); + statusBackground.setAttribute("width",200); + statusBackground.setAttribute("height",13); + statusBackground.setAttribute("rx",1); + statusBackground.setAttribute("ry",1); + + statusEl.appendChild(statusBackground); + node[0][0].__statusBackground__ = statusBackground; + + + var statusIcon = document.createElementNS("http://www.w3.org/2000/svg","rect"); + statusIcon.setAttribute("class","red-ui-flow-node-status"); + statusIcon.setAttribute("x",6); + statusIcon.setAttribute("y",1); + statusIcon.setAttribute("width",9); + statusIcon.setAttribute("height",9); + statusIcon.setAttribute("rx",2); + statusIcon.setAttribute("ry",2); + statusIcon.setAttribute("stroke-width","3"); + statusEl.appendChild(statusIcon); + node[0][0].__statusShape__ = statusIcon; var statusLabel = document.createElementNS("http://www.w3.org/2000/svg","text"); statusLabel.setAttribute("class","red-ui-flow-node-status-label"); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/flow.scss b/packages/node_modules/@node-red/editor-client/src/sass/flow.scss index 0eb68656b..bd83e3eff 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/flow.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/flow.scss @@ -304,7 +304,11 @@ g.red-ui-flow-node-selected { stroke: var(--red-ui-node-status-colors-#{"" + $current-color}); } } - +.red-ui-flow-node-status-background { + stroke: none; + fill: var(--red-ui-view-background); + fill-opacity: 0.9; +} .red-ui-flow-node-status-label { @include disable-selection; stroke-width: 0; From bd589aa140663c778f99c0b842e2398671307cd0 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Mon, 10 Jul 2023 10:24:28 +0100 Subject: [PATCH 7/7] fix touch mode wire linking --- .../editor-client/src/js/ui/view-tools.js | 36 +++++- .../@node-red/editor-client/src/js/ui/view.js | 115 +++++++++++------- 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index d16843a22..f503beecb 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -1305,6 +1305,39 @@ RED.view.tools = (function() { } } + /** + * Determine if a point is within a node + * @param {*} node - A Node or Junction node + * @param {[Number,Number]} mouse_position The x,y position of the mouse + * @param {Number} [marginX=0] - A margin to add or deduct from the x position (to increase the hit area) + * @param {Number} [marginY=0] - A margin to add or deduct from the y position (to increase the hit area) + * @returns + */ + function isPointInNode (node, [x, y], marginX, marginY) { + marginX = marginX || 0 + marginY = marginY || 0 + + let w = node.w || 10 // junctions dont have any w or h value + let h = node.h || 10 + let x1, x2, y1, y2 + + if (node.type === "junction" || node.type === "group") { + // x/y is the top left of the node + x1 = node.x + y1 = node.y + x2 = node.x + w + y2 = node.y + h + } else { + // x/y is the center of the node + const [xMid, yMid] = [w/2, h/2] + x1 = node.x - xMid + y1 = node.y - yMid + x2 = node.x + xMid + y2 = node.y + yMid + } + return (x >= (x1 - marginX) && x <= (x2 + marginX) && y >= (y1 - marginY) && y <= (y2 + marginY)) + } + return { init: function() { RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); }) @@ -1387,7 +1420,8 @@ RED.view.tools = (function() { * @param {Number} dy */ moveSelection: moveSelection, - calculateGridSnapOffsets: calculateGridSnapOffsets + calculateGridSnapOffsets: calculateGridSnapOffsets, + isPointInNode: isPointInNode } })(); 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 a49133af7..fd9bd3c96 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 @@ -101,7 +101,7 @@ RED.view = (function() { // Note: these are the permitted status colour aliases. The actual RGB values // are set in the CSS - flow.scss/colors.scss - var status_colours = { + const status_colours = { "red": "#c00", "green": "#5a8", "yellow": "#F9DF31", @@ -110,19 +110,32 @@ RED.view = (function() { "gray": "#d3d3d3" } - var PORT_TYPE_INPUT = 1; - var PORT_TYPE_OUTPUT = 0; + const PORT_TYPE_INPUT = 1; + const PORT_TYPE_OUTPUT = 0; - var chart; - var outer; + /** + * The jQuery object for the workspace chart `#red-ui-workspace-chart` div element + * @type {JQuery} #red-ui-workspace-chart HTML Element + */ + let chart; + /** + * The d3 object `#red-ui-workspace-chart` svg element + * @type {d3.Selection} + */ + let outer; + /** + * The d3 object `#red-ui-workspace-chart` svg element (specifically for events) + * @type {d3.Selection} + */ var eventLayer; - var gridLayer; - var linkLayer; - var junctionLayer; - var dragGroupLayer; - var groupSelectLayer; - var nodeLayer; - var groupLayer; + + /** @type {SVGGElement} */ let gridLayer; + /** @type {SVGGElement} */ let linkLayer; + /** @type {SVGGElement} */ let junctionLayer; + /** @type {SVGGElement} */ let dragGroupLayer; + /** @type {SVGGElement} */ let groupSelectLayer; + /** @type {SVGGElement} */ let nodeLayer; + /** @type {SVGGElement} */ let groupLayer; var drag_lines; const movingSet = (function() { @@ -391,16 +404,6 @@ RED.view = (function() { touchStartTime = setTimeout(function() { touchStartTime = null; showTouchMenu(obj,pos); - //lasso = eventLayer.append("rect") - // .attr("ox",point[0]) - // .attr("oy",point[1]) - // .attr("rx",2) - // .attr("ry",2) - // .attr("x",point[0]) - // .attr("y",point[1]) - // .attr("width",0) - // .attr("height",0) - // .attr("class","nr-ui-view-lasso"); },touchLongPressTimeout); } d3.event.preventDefault(); @@ -3094,22 +3097,38 @@ RED.view = (function() { } } document.body.style.cursor = ""; + if (mouse_mode == RED.state.JOINING || mouse_mode == RED.state.QUICK_JOINING) { if (typeof TouchEvent != "undefined" && evt instanceof TouchEvent) { - var found = false; - RED.nodes.eachNode(function(n) { - if (n.z == RED.workspaces.active()) { - var hw = n.w/2; - var hh = n.h/2; - if (n.x-hw mouse_position[0] && - n.y-hhmouse_position[1]) { - found = true; - mouseup_node = n; - portType = mouseup_node.inputs>0?PORT_TYPE_INPUT:PORT_TYPE_OUTPUT; - portIndex = 0; + if (RED.view.DEBUG) { console.warn("portMouseUp: TouchEvent", mouse_mode,d,portType,portIndex); } + const direction = drag_lines[0].portType === PORT_TYPE_INPUT ? PORT_TYPE_OUTPUT : PORT_TYPE_INPUT + let found = false; + for (let nodeIdx = 0; nodeIdx < activeNodes.length; nodeIdx++) { + const n = activeNodes[nodeIdx]; + if (RED.view.tools.isPointInNode(n, mouse_position)) { + found = true; + mouseup_node = n; + // portType = mouseup_node.inputs > 0 ? PORT_TYPE_INPUT : PORT_TYPE_OUTPUT; + portType = direction; + portIndex = 0; + break + } + } + + if (!found && drag_lines.length > 0 && !drag_lines[0].virtualLink) { + for (let juncIdx = 0; juncIdx < activeJunctions.length; juncIdx++) { + // NOTE: a junction is 10px x 10px but the target area is expanded to 30wx20h by adding padding to the bounding box + const jNode = activeJunctions[juncIdx]; + if (RED.view.tools.isPointInNode(jNode, mouse_position, 20, 10)) { + found = true; + mouseup_node = jNode; + portType = direction; + portIndex = 0; + break } } - }); + } + if (!found && activeSubflow) { var subflowPorts = []; if (activeSubflow.status) { @@ -3121,16 +3140,13 @@ RED.view = (function() { if (activeSubflow.out) { subflowPorts = subflowPorts.concat(activeSubflow.out) } - for (var i=0;i mouse_position[0] && - n.y-hhmouse_position[1]) { - found = true; - mouseup_node = n; - portType = mouseup_node.direction === "in"?PORT_TYPE_OUTPUT:PORT_TYPE_INPUT; - portIndex = 0; + for (var i = 0; i < subflowPorts.length; i++) { + const sf = subflowPorts[i]; + if (RED.view.tools.isPointInNode(sf, mouse_position)) { + found = true; + mouseup_node = sf; + portType = mouseup_node.direction === "in" ? PORT_TYPE_OUTPUT : PORT_TYPE_INPUT; + portIndex = 0; break; } } @@ -5015,16 +5031,25 @@ RED.view = (function() { contents.appendChild(junctionOutput); junctionOutput.addEventListener("mouseup", portMouseUpProxy); junctionOutput.addEventListener("mousedown", portMouseDownProxy); - junctionOutput.addEventListener("mouseover", junctionMouseOverProxy); junctionOutput.addEventListener("mouseout", junctionMouseOutProxy); + junctionOutput.addEventListener("touchmove", junctionMouseOverProxy); + junctionOutput.addEventListener("touchend", portMouseUpProxy); + junctionOutput.addEventListener("touchstart", portMouseDownProxy); + junctionInput.addEventListener("mouseover", junctionMouseOverProxy); junctionInput.addEventListener("mouseout", junctionMouseOutProxy); + junctionInput.addEventListener("touchmove", junctionMouseOverProxy); + junctionInput.addEventListener("touchend", portMouseUpProxy); + junctionInput.addEventListener("touchstart", portMouseDownProxy); + junctionBack.addEventListener("mouseover", junctionMouseOverProxy); junctionBack.addEventListener("mouseout", junctionMouseOutProxy); + junctionBack.addEventListener("touchmove", junctionMouseOverProxy); // These handlers expect to be registered as d3 events d3.select(junctionBack).on("mousedown", nodeMouseDown).on("mouseup", nodeMouseUp); + d3.select(junctionBack).on("touchstart", nodeMouseDown).on("touchend", nodeMouseUp); junction[0][0].appendChild(contents); })