From 844bf29de1018c65e2f8708d72afb98308f6156a Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 24 Jun 2021 17:40:26 +0100 Subject: [PATCH] Add RED.view.annotations api --- Gruntfile.js | 1 + .../editor-client/src/js/ui/common/popover.js | 4 - .../src/js/ui/view-annotations.js | 148 ++++++++++++++++++ .../@node-red/editor-client/src/js/ui/view.js | 101 ++++++------ .../editor-client/src/sass/variables.scss | 5 + 5 files changed, 200 insertions(+), 59 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js diff --git a/Gruntfile.js b/Gruntfile.js index 5d9b8ba5c..df9939a8c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -170,6 +170,7 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", "packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js", "packages/node_modules/@node-red/editor-client/src/js/ui/view.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js", "packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js", "packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js", "packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js index 7049c3164..8d7553069 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js @@ -308,11 +308,9 @@ RED.popover = (function() { // DOWN if (currentItem.length > 0) { if (currentItem.index() === menuOptions.length-1) { - console.log("WARP TO TOP") // Wrap to top of list list.children().first().children().first().focus(); } else { - console.log("GO DOWN ONE") currentItem.next().children().first().focus(); } } else { @@ -323,11 +321,9 @@ RED.popover = (function() { // UP if (currentItem.length > 0) { if (currentItem.index() === 0) { - console.log("WARP TO BOTTOM") // Wrap to bottom of list list.children().last().children().first().focus(); } else { - console.log("GO UP ONE") currentItem.prev().children().first().focus(); } } else { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js new file mode 100644 index 000000000..e3a6a1114 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js @@ -0,0 +1,148 @@ +RED.view.annotations = (function() { + + var annotations = {}; + + function init() { + RED.hooks.add("viewRedrawNode.annotations", function(evt) { + try { + if (evt.node.__pendingAnnotation__) { + addAnnotation(evt.node.__pendingAnnotation__,evt); + delete evt.node.__pendingAnnotation__; + } + var badgeDX = 0; + var controlDX = 0; + for (var i=0,l=evt.el.__annotations__.length;i boolean + * } + */ + function register(id, opts) { + annotations[id] = opts + RED.hooks.add("viewAddNode.annotation-"+id, function(evt) { + if (opts.filter && !opts.filter(evt.node)) { + return; + } + addAnnotation(id,evt); + }); + + var nodes = RED.view.getActiveNodes(); + nodes.forEach(function(n) { + n.__pendingAnnotation__ = id; + }) + RED.view.redraw(); + + } + + function addAnnotation(id,evt) { + var opts = annotations[id]; + evt.el.__annotations__ = evt.el.__annotations__ || []; + var annotationGroup = document.createElementNS("http://www.w3.org/2000/svg","g"); + annotationGroup.setAttribute("class",opts.class || ""); + evt.el.__annotations__.push({ + id:id, + element: annotationGroup + }); + var annotation = opts.element(evt.node); + if (opts.tooltip) { + annotation.addEventListener("mouseenter", getAnnotationMouseEnter(annotation,evt.node,opts.tooltip)); + annotation.addEventListener("mouseleave", annotationMouseLeave); + } + annotationGroup.appendChild(annotation); + evt.el.appendChild(annotationGroup); + } + + + function unregister(id) { + delete annotations[id] + RED.hooks.remove("*.annotation-"+id); + RED.view.redraw(); + } + + var badgeHoverTimeout; + var badgeHover; + function getAnnotationMouseEnter(annotation,node,tooltip) { + return function() { + var text = typeof tooltip === "function"?tooltip(node):tooltip; + if (text) { + clearTimeout(badgeHoverTimeout); + badgeHoverTimeout = setTimeout(function() { + var pos = RED.view.getElementPosition(annotation); + var rect = annotation.getBoundingClientRect(); + badgeHoverTimeout = null; + badgeHover = RED.view.showTooltip( + (pos[0]+rect.width/2), + (pos[1]), + text, + "top" + ); + },500); + } + } + } + function annotationMouseLeave() { + clearTimeout(badgeHoverTimeout); + if (badgeHover) { + badgeHover.remove(); + badgeHover = null; + } + } + + return { + init: init, + register:register, + unregister:unregister + } + +})(); 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 a30af74b4..dc8041d8d 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 @@ -546,10 +546,44 @@ RED.view = (function() { } }); + RED.view.annotations.init(); RED.view.navigator.init(); RED.view.tools.init(); + + + RED.view.annotations.register("red-ui-flow-node-changed",{ + type: "badge", + class: "red-ui-flow-node-changed", + element: function() { + var changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle"); + changeBadge.setAttribute("cx",5); + changeBadge.setAttribute("cy",5); + changeBadge.setAttribute("r",5); + return changeBadge; + }, + show: function(n) { return n.changed||n.moved } + }) + + RED.view.annotations.register("red-ui-flow-node-error",{ + type: "badge", + class: "red-ui-flow-node-error", + element: function(d) { + var errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path"); + errorBadge.setAttribute("d","M 0,9 l 10,0 -5,-8 z"); + return errorBadge + }, + tooltip: function(d) { + if (d.validationErrors && d.validationErrors.length > 0) { + return RED._("editor.errors.invalidProperties")+"\n - "+d.validationErrors.join("\n - ") + } + }, + show: function(n) { return !n.valid } + }) + } + + function updateGrid() { var gridTicks = []; for (var i=0;i 0) { - clearTimeout(portLabelHoverTimeout); - var node = this; - portLabelHoverTimeout = setTimeout(function() { - var pos = getElementPosition(node); - portLabelHoverTimeout = null; - portLabelHover = showTooltip( - (pos[0]), - (pos[1]), - RED._("editor.errors.invalidProperties")+"\n - "+d.validationErrors.join("\n - "), - "top" - ); - },500); - } - } - function errorBadgeMouseLeave() { - clearTimeout(portLabelHoverTimeout); - if (portLabelHover) { - portLabelHover.remove(); - portLabelHover = null; - } - } - function redrawStatus(d,nodeEl) { if (d.z !== RED.workspaces.active()) { return; @@ -3938,31 +3947,11 @@ RED.view = (function() { nodeContents.appendChild(statusEl); - - var changeBadgeG = document.createElementNS("http://www.w3.org/2000/svg","g"); - changeBadgeG.setAttribute("class","red-ui-flow-node-changed hide"); - changeBadgeG.setAttribute("transform","translate(20, -2)"); - node[0][0].__changeBadge__ = changeBadgeG; - var changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle"); - changeBadge.setAttribute("r",5); - changeBadgeG.appendChild(changeBadge); - nodeContents.appendChild(changeBadgeG); - - - var errorBadgeG = document.createElementNS("http://www.w3.org/2000/svg","g"); - errorBadgeG.setAttribute("class","red-ui-flow-node-error hide"); - errorBadgeG.setAttribute("transform","translate(0, -2)"); - node[0][0].__errorBadge__ = errorBadgeG; - var errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path"); - errorBadge.setAttribute("d","M -5,4 l 10,0 -5,-8 z"); - errorBadgeG.appendChild(errorBadge); - errorBadge.__data__ = d; - errorBadge.addEventListener("mouseenter", errorBadgeMouseEnter); - errorBadge.addEventListener("mouseleave", errorBadgeMouseLeave); - nodeContents.appendChild(errorBadgeG); - node[0][0].appendChild(nodeContents); + + RED.hooks.trigger("viewAddNode",{node:d,el:this}) }); + node.each(function(d,i) { if (d.dirty) { var self = this; @@ -4202,10 +4191,10 @@ RED.view = (function() { ); faIcon.attr("y",(d.h+13)/2); } - this.__changeBadge__.setAttribute("transform", "translate("+(d.w-10)+", -2)"); - this.__changeBadge__.classList.toggle("hide", !(d.changed||d.moved)); - this.__errorBadge__.setAttribute("transform", "translate("+(d.w-10-((d.changed||d.moved)?14:0))+", -2)"); - this.__errorBadge__.classList.toggle("hide", d.valid); + // this.__changeBadge__.setAttribute("transform", "translate("+(d.w-10)+", -2)"); + // this.__changeBadge__.classList.toggle("hide", !(d.changed||d.moved)); + // 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); @@ -4254,8 +4243,6 @@ RED.view = (function() { // }); } - RED.hooks.trigger("viewAddNode",{node:d,el:this}) - if (d.dirtyStatus) { redrawStatus(d,this); } @@ -4270,6 +4257,8 @@ RED.view = (function() { } } } + + RED.hooks.trigger("viewRedrawNode",{node:d,el:this}) }); var link = linkLayer.selectAll(".red-ui-flow-link").data( activeLinks, @@ -5300,6 +5289,8 @@ RED.view = (function() { }, redrawStatus: redrawStatus, showQuickAddDialog:showQuickAddDialog, - calculateNodeDimensions: calculateNodeDimensions + calculateNodeDimensions: calculateNodeDimensions, + getElementPosition:getElementPosition, + showTooltip:showTooltip }; })(); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/variables.scss b/packages/node_modules/@node-red/editor-client/src/sass/variables.scss index 63a4b1d14..72b573a2e 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/variables.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/variables.scss @@ -81,6 +81,11 @@ --red-ui-node-status-changed-border: #{$node-status-changed-border}; --red-ui-node-status-changed-background: #{$node-status-changed-background}; + + + --red-ui-node-border: #{$node-border}; + --red-ui-node-port-background:#{$node-port-background}; + --red-ui-node-label-color: #{$node-label-color}; --red-ui-node-selected-color: #{$node-selected-color}; --red-ui-port-selected-color: #{$port-selected-color};