From f78be9050a9d5a7e851aa495fcd5a17150731659 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 10:49:14 +0100 Subject: [PATCH 01/24] Reorder inital load so projects:load event emits before any nodes:add --- .../@node-red/editor-client/src/js/red.js | 14 ++++++++------ .../editor-client/src/js/ui/projects/projects.js | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index fd2adf05f..290b692c3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -124,9 +124,9 @@ var RED = (function() { $("#red-ui-palette > .red-ui-palette-spinner").hide(); $(".red-ui-palette-scroll").removeClass("hide"); $("#red-ui-palette-search").removeClass("hide"); - loadFlows(function() { - if (RED.settings.theme("projects.enabled",false)) { - RED.projects.refresh(function(activeProject) { + if (RED.settings.theme("projects.enabled",false)) { + RED.projects.refresh(function(activeProject) { + loadFlows(function() { RED.sidebar.info.refresh() if (!activeProject) { // Projects enabled but no active project @@ -140,12 +140,14 @@ var RED = (function() { } completeLoad(); }); - } else { + }); + } else { + loadFlows(function() { // Projects disabled by the user RED.sidebar.info.refresh() completeLoad(); - } - }); + }); + } } else { var config = configs.shift(); appendNodeConfig(config,stepConfig); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js index da54a81c8..001429067 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js @@ -2312,6 +2312,7 @@ RED.projects = (function() { if (data.active) { $.getJSON("projects/"+data.active, function(project) { activeProject = project; + RED.events.emit("projects:load",activeProject); RED.sidebar.versionControl.refresh(true); if (done) { done(activeProject); From 2069cc4392804fbdb81d87c952ceb568c0b0f2aa Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 10:49:54 +0100 Subject: [PATCH 02/24] Add flows:reorder event --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 587a23d5b..19e598251 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -348,6 +348,9 @@ RED.nodes = (function() { workspacesOrder.splice(targetIndex,0,ws.id); } RED.events.emit('flows:add',ws); + if (targetIndex !== undefined) { + RED.events.emit('flows:reorder',workspacesOrder) + } } function getWorkspace(id) { return workspaces[id]; From d9f710aa52a524fc520e93eb480032aa91889abb Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:03:02 +0100 Subject: [PATCH 03/24] Only update disabled workspace css if it is the active ws --- .../@node-red/editor-client/src/js/ui/workspaces.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 7a5f7a86c..8bb0fde68 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -372,7 +372,9 @@ RED.workspaces = (function() { var changes = { disabled: workspace.disabled }; workspace.disabled = disabled; $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled); - $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); + if (id === activeWorkspace) { + $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); + } var historyEvent = { t: "edit", changes:changes, From 1a9c4b771409e60fd470f1c304aa51a33ec3186c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:03:43 +0100 Subject: [PATCH 04/24] All node button to be clicked via api call --- .../@node-red/editor-client/src/js/ui/view.js | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 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 35bcbb4e6..d15ef09d4 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 @@ -3060,7 +3060,9 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } function nodeButtonClicked(d) { if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); + if (d3.event) { + d3.event.stopPropagation(); + } return; } var activeWorkspace = RED.workspaces.active(); @@ -3087,7 +3089,9 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.nodeActionDisabled")}),"warning"); } } - d3.event.preventDefault(); + if (d3.event) { + d3.event.preventDefault(); + } } function showTouchMenu(obj,pos) { @@ -4660,42 +4664,44 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } RED.workspaces.show(id); } else { var node = RED.nodes.node(id); - if (node._def.category !== 'config' && node.z) { - node.highlighted = true; - node.dirty = true; - RED.workspaces.show(node.z); + if (node) { + if (node._def.category !== 'config' && node.z) { + node.highlighted = true; + node.dirty = true; + RED.workspaces.show(node.z); - var screenSize = [chart.width()/scaleFactor,chart.height()/scaleFactor]; - var scrollPos = [chart.scrollLeft()/scaleFactor,chart.scrollTop()/scaleFactor]; + var screenSize = [chart.width()/scaleFactor,chart.height()/scaleFactor]; + var scrollPos = [chart.scrollLeft()/scaleFactor,chart.scrollTop()/scaleFactor]; - if (node.x < scrollPos[0] || node.y < scrollPos[1] || node.x > screenSize[0]+scrollPos[0] || node.y > screenSize[1]+scrollPos[1]) { - var deltaX = '-='+(((scrollPos[0] - node.x) + screenSize[0]/2)*scaleFactor); - var deltaY = '-='+(((scrollPos[1] - node.y) + screenSize[1]/2)*scaleFactor); - chart.animate({ - scrollLeft: deltaX, - scrollTop: deltaY - },200); - } - - if (!node._flashing) { - node._flashing = true; - var flash = 22; - var flashFunc = function() { - flash--; - node.dirty = true; - if (flash >= 0) { - node.highlighted = !node.highlighted; - setTimeout(flashFunc,100); - } else { - node.highlighted = false; - delete node._flashing; - } - RED.view.redraw(); + if (node.x < scrollPos[0] || node.y < scrollPos[1] || node.x > screenSize[0]+scrollPos[0] || node.y > screenSize[1]+scrollPos[1]) { + var deltaX = '-='+(((scrollPos[0] - node.x) + screenSize[0]/2)*scaleFactor); + var deltaY = '-='+(((scrollPos[1] - node.y) + screenSize[1]/2)*scaleFactor); + chart.animate({ + scrollLeft: deltaX, + scrollTop: deltaY + },200); } - flashFunc(); + + if (!node._flashing) { + node._flashing = true; + var flash = 22; + var flashFunc = function() { + flash--; + node.dirty = true; + if (flash >= 0) { + node.highlighted = !node.highlighted; + setTimeout(flashFunc,100); + } else { + node.highlighted = false; + delete node._flashing; + } + RED.view.redraw(); + } + flashFunc(); + } + } else if (node._def.category === 'config') { + RED.sidebar.config.show(id); } - } else if (node._def.category === 'config') { - RED.sidebar.config.show(id); } } }, @@ -4776,6 +4782,11 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } scroll: function(x,y) { chart.scrollLeft(chart.scrollLeft()+x); chart.scrollTop(chart.scrollTop()+y) + }, + clickNodeButton: function(n) { + if (n._def.button) { + nodeButtonClicked(n); + } } }; })(); From 2eaea02489cf0275719ee2bfdb16c6775acef7bc Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:04:04 +0100 Subject: [PATCH 05/24] Make selected list item more distinct --- .../node_modules/@node-red/editor-client/src/sass/colors.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/colors.scss b/packages/node_modules/@node-red/editor-client/src/sass/colors.scss index d95f8516f..96be7b5ab 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/colors.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/colors.scss @@ -23,7 +23,7 @@ $primary-background: #f3f3f3;//#0ff; $secondary-background: #fff;//#ff0; $secondary-background-selected: #efefef;//#e9e900; $secondary-background-inactive: #f0f0f0;//#f0f000; -$secondary-background-hover: #ddd;//#dd0; +$secondary-background-hover: #e6e6e6;//#dd0; $secondary-background-disabled: #f9f9f9;//#fafa0; $tertiary-background: #f7f7f7;//#f0f; @@ -94,7 +94,7 @@ $list-item-secondary-color: $secondary-text-color; $list-item-background: $secondary-background; $list-item-background-disabled: $secondary-background-inactive; $list-item-background-hover: $secondary-background-hover; -$list-item-background-selected: $secondary-background-selected; +$list-item-background-selected: #ffebc7; // #fff1e5; $list-item-border-selected: $secondary-text-color-selected; $tab-text-color-active: $header-text-color; From 0150769c176cb3e841e6ddb526f6e9b5473bf752 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:04:41 +0100 Subject: [PATCH 06/24] EditableList api calls must not return nested list items --- .../src/js/ui/common/editableList.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js index 06ccc97d9..3df2f9a51 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js @@ -183,7 +183,7 @@ if (this.options.resizeItem) { var that = this; this.element.children().each(function(i) { - that.options.resizeItem($(this).find(".red-ui-editableList-item-content"),i); + that.options.resizeItem($(this).children(".red-ui-editableList-item-content"),i); }); } }, @@ -223,7 +223,7 @@ var items = this.element.children(); var that = this; items.sort(function(A,B) { - return that.activeSort($(A).find(".red-ui-editableList-item-content").data('data'),$(B).find(".red-ui-editableList-item-content").data('data')); + return that.activeSort($(A).children(".red-ui-editableList-item-content").data('data'),$(B).children(".red-ui-editableList-item-content").data('data')); }); $.each(items,function(idx,li) { that.element.append(li); @@ -305,7 +305,7 @@ } if (this.options.addItem) { var index = that.element.children().length-1; - setTimeout(function() { + // setTimeout(function() { that.options.addItem(row,index,data); if (that.activeFilter) { try { @@ -321,7 +321,7 @@ that.uiContainer.scrollTop(that.element.height()); },0); } - },0); + // },0); } }, addItem: function(data) { @@ -334,7 +334,7 @@ }, removeItem: function(data) { var items = this.element.children().filter(function(f) { - return data === $(this).find(".red-ui-editableList-item-content").data('data'); + return data === $(this).children(".red-ui-editableList-item-content").data('data'); }); items.remove(); if (this.options.removeItem) { @@ -342,7 +342,7 @@ } }, items: function() { - return this.element.children().map(function(i) { return $(this).find(".red-ui-editableList-item-content"); }); + return this.element.children().map(function(i) { return $(this).children(".red-ui-editableList-item-content"); }); }, empty: function() { this.element.empty(); @@ -365,14 +365,14 @@ }, show: function(item) { var items = this.element.children().filter(function(f) { - return item === $(this).find(".red-ui-editableList-item-content").data('data'); + return item === $(this).children(".red-ui-editableList-item-content").data('data'); }); if (items.length > 0) { this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top) } }, getItem: function(li) { - var el = li.find(".red-ui-editableList-item-content"); + var el = li.children(".red-ui-editableList-item-content"); if (el.length) { return el.data('data'); } else { From 28418288e33147b0b923ec96d2c833190e74dca7 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:05:32 +0100 Subject: [PATCH 07/24] Allow RED.notify.popover to have a position offset --- .../@node-red/editor-client/src/js/ui/common/popover.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 fca2f26f1..8b796f4fd 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 @@ -278,6 +278,7 @@ RED.popover = (function() { var closeCallback = options.onclose; var target = options.target; var align = options.align || "left"; + var offset = options.offset || [0,0]; var pos = target.offset(); var targetWidth = target.width(); @@ -285,7 +286,7 @@ RED.popover = (function() { var panelHeight = panel.height(); var panelWidth = panel.width(); - var top = (targetHeight+pos.top); + var top = (targetHeight+pos.top) + offset[1]; if (top+panelHeight > $(window).height()) { top -= (top+panelHeight)-$(window).height() + 5; } @@ -296,12 +297,12 @@ RED.popover = (function() { if (align === "left") { panel.css({ top: top+"px", - left: (pos.left)+"px", + left: (pos.left+offset[0])+"px", }); } else if(align === "right") { panel.css({ top: top+"px", - left: (pos.left-panelWidth)+"px", + left: (pos.left-panelWidth+offset[0])+"px", }); } panel.slideDown(100); From 5c0b500f48403df547e8994f300dffcea4a4856b Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:06:28 +0100 Subject: [PATCH 08/24] Reorder group creation so groups:add is fired before nodes:change --- .../@node-red/editor-client/src/js/ui/group.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js index a513e01ea..aa4e07117 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js @@ -427,15 +427,16 @@ RED.group = (function() { h: 0, _def: RED.group.def } + + group.z = nodes[0].z; + RED.nodes.addGroup(group); + try { addToGroup(group,nodes); } catch(err) { RED.notify(err,"error"); return; } - group.z = nodes[0].z; - - RED.nodes.addGroup(group); return group; } function addToGroup(group,nodes) { From d2d872f51c65352baa5f2c5a910a089d9b659118 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:14:47 +0100 Subject: [PATCH 09/24] TreeList updates for the outliner sidebar All data items now get their `item.treeList` api added even if deferBuild is set. This means the apis can be used regardless of whether the tree has built their ui pieces. Also adds a number of new api calls Top-level methods: - clearSelection - clears selection from the list - filter(filterFunc) - filters the tree using the provided function Data item functions: - item.treeList.sortChildren(sortFunction) - item.treeList.replaceElement(element) --- .../src/js/ui/common/treeList.js | 340 +++++++++++++----- 1 file changed, 242 insertions(+), 98 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js index ed04733c9..8e3281b3b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js @@ -27,7 +27,8 @@ * * methods: * - data(items) - clears existing items and replaces with new data - * + * - clearSelection - clears the selected items + * - filter(filterFunc) - filters the tree using the provided function * events: * - treelistselect : function(event, item) {} * - treelistconfirm : function(event,item) {} @@ -59,9 +60,9 @@ * properties and functions: * * item.parent - set to the parent item + * item.depth - the depth in the tree (0 == root) * item.treeList.container * item.treeList.label - the label element for the item - * item.treeList.depth - the depth in the tree (0 == root) * item.treeList.parentList - the editableList instance this item is in * item.treeList.remove() - removes the item from the tree * item.treeList.makeLeaf(detachChildElements) - turns an element with children into a leaf node, @@ -78,8 +79,8 @@ * Optionally selects the item after adding. * item.treeList.expand(done) - expands the parent item to show children. Optional 'done' callback. * item.treeList.collapse() - collapse the parent item to hide children. - * - * + * item.treeList.sortChildren(sortFunction) - does a one-time sort of the children using sortFunction + * item.treeList.replaceElement(element) - replace the custom element for the item * * */ @@ -151,7 +152,7 @@ } }); this._data = []; - + this._items = {}; this._topList = $('
    ').css({ position:'absolute', top: 0, @@ -244,7 +245,8 @@ that._trigger("changeparent",null,evt); }); that._trigger("sort",null,parent); - } + }, + filter: parent.treeList.childFilter }); if (!!that.options.sortable) { subtree.addClass('red-ui-treeList-sortable'); @@ -289,21 +291,175 @@ } return reparentedEvent; }, - _addSubtree: function(parentList, container, item, depth) { + _initItem: function(item,depth) { + if (item.treeList) { + return; + } var that = this; + this._items[item.id] = item; item.treeList = {}; - item.treeList.depth = depth; - item.treeList.container = container; - - item.treeList.parentList = parentList; + item.depth = depth; item.treeList.remove = function() { - parentList.editableList('removeItem',item); + if (item.treeList.parentList) { + item.treeList.parentList.editableList('removeItem',item); + } if (item.parent) { var index = item.parent.children.indexOf(item); item.parent.children.splice(index,1) that._trigger("sort",null,item.parent); } + delete(that._items[item.id]); } + item.treeList.insertChildAt = function(newItem,position,select) { + newItem.parent = item; + item.children.splice(position,0,newItem); + var processChildren = function(parent,i) { + that._initItem(i,parent.depth+1) + i.parent = parent; + if (i.children && typeof i.children !== 'function') { + i.children.forEach(function(item) { + processChildren(i, item, parent.depth+2) + }); + } + } + processChildren(item,newItem); + + if (!item.deferBuild) { + item.treeList.childList.editableList('insertItemAt',newItem,position) + if (select) { + setTimeout(function() { + that.select(newItem) + },100); + } + that._trigger("sort",null,item); + } + } + item.treeList.addChild = function(newItem,select) { + item.treeList.insertChildAt(newItem,item.children.length,select); + } + item.treeList.expand = function(done) { + if (!item.children) { + if (done) { done() } + return; + } + if (!item.treeList.container) { + item.expanded = true; + if (done) { done() } + return; + } + var container = item.treeList.container; + if (container.hasClass("expanded")) { + if (done) { done() } + return; + } + + if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) { + container.addClass('built'); + var childrenAdded = false; + var spinner; + var startTime = 0; + var completeBuild = function(children) { + childrenAdded = true; + item.treeList.childList = that._addChildren(container,item,children,depth).hide(); + var delta = Date.now() - startTime; + if (delta < 400) { + setTimeout(function() { + item.treeList.childList.slideDown('fast'); + if (spinner) { + spinner.remove(); + } + },400-delta); + } else { + item.treeList.childList.slideDown('fast'); + if (spinner) { + spinner.remove(); + } + } + item.expanded = true; + if (done) { done() } + that._trigger("childrenloaded",null,item) + } + if (typeof item.children === 'function') { + item.children(completeBuild,item); + } else { + delete item.deferBuild; + completeBuild(item.children); + } + if (!childrenAdded) { + startTime = Date.now(); + spinner = $('
    ').css({ + "background-position": (35+depth*20)+'px 50%' + }).appendTo(container); + } + + } else { + if (that._loadingData) { + item.treeList.childList.show(); + } else { + item.treeList.childList.slideDown('fast'); + } + item.expanded = true; + if (done) { done() } + } + container.addClass("expanded"); + } + item.treeList.collapse = function() { + if (!item.children) { + return; + } + item.expanded = false; + if (item.treeList.container) { + item.treeList.childList.slideUp('fast'); + item.treeList.container.removeClass("expanded"); + } + } + item.treeList.sortChildren = function(sortFunc) { + if (!item.children) { + return; + } + item.children.sort(sortFunc); + if (item.treeList.childList) { + // Do a one-off sort of the list, which means calling sort twice: + // 1. first with the desired sort function + item.treeList.childList.editableList('sort',sortFunc); + // 2. and then with null to remove it + item.treeList.childList.editableList('sort',null); + } + } + item.treeList.replaceElement = function (element) { + if (item.element && item.treeList.container) { + $(item.element).remove(); + item.element = element; + $(item.element).appendTo(label); + $(item.element).css({ + width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)" + }) + } + } + + if (item.children && typeof item.children !== "function") { + item.children.forEach(function(i) { + that._initItem(i,depth+1); + }) + } + }, + _addSubtree: function(parentList, container, item, depth) { + var that = this; + this._initItem(item,depth); + // item.treeList = {}; + // item.treeList.depth = depth; + item.treeList.container = container; + + item.treeList.parentList = parentList; + // item.treeList.remove = function() { + // parentList.editableList('removeItem',item); + // if (item.parent) { + // var index = item.parent.children.indexOf(item); + // item.parent.children.splice(index,1) + // that._trigger("sort",null,item.parent); + // } + // delete(that._items[item.id]); + // } var label = $("
    ",{class:"red-ui-treeList-label"}).appendTo(container); item.treeList.label = label; @@ -357,6 +513,7 @@ treeListIcon.off("click.red-ui-treeList-expand"); delete item.children; container.removeClass("expanded"); + delete item.expanded; } item.treeList.makeParent = function(children) { if (treeListIcon.children().length) { @@ -388,86 +545,6 @@ item.treeList.childList = that._addChildren(container,item,item.children,depth).hide(); } } - item.treeList.insertChildAt = function(newItem,position,select) { - newItem.parent = item; - item.children.splice(position,0,newItem); - - if (!item.deferBuild) { - item.treeList.childList.editableList('insertItemAt',newItem,position) - if (select) { - setTimeout(function() { - that.select(newItem) - },100); - } - that._trigger("sort",null,item); - } - } - item.treeList.addChild = function(newItem,select) { - item.treeList.insertChildAt(newItem,item.children.length,select); - } - item.treeList.expand = function(done) { - if (!item.children) { - return; - } - if (container.hasClass("expanded")) { - if (done) { done() } - return; - } - if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) { - container.addClass('built'); - var childrenAdded = false; - var spinner; - var startTime = 0; - var completeBuild = function(children) { - childrenAdded = true; - item.treeList.childList = that._addChildren(container,item,children,depth).hide(); - var delta = Date.now() - startTime; - if (delta < 400) { - setTimeout(function() { - item.treeList.childList.slideDown('fast'); - if (spinner) { - spinner.remove(); - } - },400-delta); - } else { - item.treeList.childList.slideDown('fast'); - if (spinner) { - spinner.remove(); - } - } - if (done) { done() } - that._trigger("childrenloaded",null,item) - } - if (typeof item.children === 'function') { - item.children(completeBuild,item); - } else { - delete item.deferBuild; - completeBuild(item.children); - } - if (!childrenAdded) { - startTime = Date.now(); - spinner = $('
    ').css({ - "background-position": (35+depth*20)+'px 50%' - }).appendTo(container); - } - - } else { - if (that._loadingData) { - item.treeList.childList.show(); - } else { - item.treeList.childList.slideDown('fast'); - } - if (done) { done() } - } - container.addClass("expanded"); - } - item.treeList.collapse = function() { - if (!item.children) { - return; - } - item.treeList.childList.slideUp('fast'); - container.removeClass("expanded"); - } var treeListIcon = $('').appendTo(label); if (item.children) { @@ -499,7 +576,9 @@ } } else { label.on("click", function(e) { - that._topList.find(".selected").removeClass("selected"); + if (!that.options.multi) { + that._topList.find(".selected").removeClass("selected"); + } label.addClass("selected"); that._trigger("select",e,item) }) @@ -508,9 +587,22 @@ that._trigger("confirm",e,item); } }) + item.treeList.select = function(v) { + if (!that.options.multi) { + that._topList.find(".selected").removeClass("selected"); + } + label.toggleClass("selected",v); + if (v) { + that._trigger("select",null,item) + } + } } if (item.icon) { - $('').appendTo(label); + if (typeof item.icon === "string") { + $('').appendTo(label); + } else { + $('').appendTo(label).append(item.icon); + } } if (item.hasOwnProperty('label') || item.hasOwnProperty('sublabel')) { if (item.hasOwnProperty('label')) { @@ -542,6 +634,7 @@ var that = this; if (items !== undefined) { this._data = items; + this._items = {}; this._topList.editableList('empty'); this._loadingData = true; for (var i=0; i 0 + } + return this._topList.editableList('filter', filter); } }); From a5b33d11fc378471ad9c7bbc2d1133b935550920 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 11:17:19 +0100 Subject: [PATCH 10/24] [outline] Add outline section to info sidebar --- Gruntfile.js | 1 + .../editor-client/locales/en-US/editor.json | 3 +- .../src/js/ui/tab-info-outliner.js | 426 ++++++++++++++++++ .../editor-client/src/js/ui/tab-info.js | 8 + .../editor-client/src/js/ui/utils.js | 4 +- .../editor-client/src/sass/search.scss | 4 +- .../editor-client/src/sass/tab-info.scss | 170 +++++++ 7 files changed, 613 insertions(+), 3 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js diff --git a/Gruntfile.js b/Gruntfile.js index b6474182a..b39256d88 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -165,6 +165,7 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", "packages/node_modules/@node-red/editor-client/src/js/ui/palette.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js", "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index f67a3de9a..7567ada3a 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -583,7 +583,8 @@ "nodeHelp": "Node Help", "none":"None", "arrayItems": "__count__ items", - "showTips":"You can open the tips from the settings panel" + "showTips":"You can open the tips from the settings panel", + "outline": "Outline" }, "config": { "name": "Configuration nodes", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js new file mode 100644 index 000000000..82d3ce1c0 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -0,0 +1,426 @@ +RED.sidebar.info.outliner = (function() { + + var treeList; + + var flowList; + var subflowList; + var globalConfigNodes; + + var objects = {}; + + var objectBacklog = {}; + + function getFlowData(project) { + var flowData = [ + { + label: "Flows", + expanded: true, + children: [ + { + id: "__global__", + label: "Global", + children: [] + } + ] + }, + { + label: "Subflows", + children: [] + } + ] + flowList = flowData[0]; + subflowList = flowData[1]; + globalConfigNodes = flowList.children[0]; + + if (project) { + flowData = [ + { + element: getProjectLabel(project), + icon: "fa fa-archive", + children: flowData, + expanded: true + } + ] + } + return flowData; + } + + function getProjectLabel(p) { + var div = $('
    ',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); + var contentDiv = $('
    ',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); + contentDiv.text(p.name); + var controls = $('
    ',{class:"red-ui-info-outline-item-controls"}).appendTo(div); + var editProjectButton = $('') + .appendTo(controls) + .on("click", function(evt) { + evt.preventDefault(); + RED.projects.editProject(); + }); + RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings')); + return div; + } + + var empties = {}; + function getEmptyItem(id) { + var item = { + empty: true, + element: $('
    ').text("empty") + } + empties[id] = item; + return item; + } + + function getNodeLabelText(n) { + var label = n.name || n.id; + if (n._def.label) { + try { + label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||""; + } catch(err) { + console.log("Definition error: "+type+".label",err); + } + } + return label; + } + + function getNodeLabel(n) { + var div = $('
    ',{class:"red-ui-info-outline-item"}); + var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}).appendTo(div); + if (n.type === "group") { + div.addClass('red-ui-info-outline-item-group') + } else { + var colour = RED.utils.getNodeColor(n.type,n._def); + nodeDiv.css('backgroundColor',colour); + } + var icon_url = RED.utils.getNodeIcon(n._def,n); + var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); + RED.utils.createIconElement(icon_url, iconContainer, false); + var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); + var labelText = getNodeLabelText(n); + var label = $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); + if (labelText) { + label.text(labelText) + } else { + label.html(" ") + } + + addControls(n, div); + + return div; + } + + function getFlowLabel(n) { + var div = $('
    ',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); + var contentDiv = $('
    ',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); + contentDiv.text(typeof n === "string"? n : n.label); + addControls(n, div); + return div; + } + + function getSubflowLabel(n) { + + var div = $('
    ',{class:"red-ui-info-outline-item"}); + var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}).appendTo(div); + if (n.type === "group") { + div.addClass('red-ui-info-outline-item-group') + } else { + var colour = RED.utils.getNodeColor(n.type,n._def); + nodeDiv.css('backgroundColor',colour); + } + var icon_url = RED.utils.getNodeIcon(n._def,n); + var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); + RED.utils.createIconElement(icon_url, iconContainer, false); + var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); + var labelText = getNodeLabelText(n); + var label = $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); + if (labelText) { + label.text(labelText) + } else { + label.html(" ") + } + + addControls(n, div); + + return div; + + + // var div = $('
    ',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); + // var contentDiv = $('
    ',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); + // contentDiv.text(n.name || n.id); + // addControls(n, div); + // return div; + } + + function addControls(n,div) { + if (n.type === 'group') { + return; + } + var controls = $('
    ',{class:"red-ui-info-outline-item-controls red-ui-info-outline-item-hover-controls"}).appendTo(div); + if (n._def.button) { + $('').appendTo(controls).on("click",function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + RED.view.clickNodeButton(n); + }) + } + + + if (n.type !== 'group') { + $('').appendTo(controls).on("click",function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + RED.view.reveal(n.id); + }) + } + if (n.type !== 'group' && n.type !== 'subflow') { + $('').appendTo(controls).on("click",function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + if (n.type === 'tab') { + if (n.disabled) { + RED.workspaces.enable(n.id) + } else { + RED.workspaces.disable(n.id) + } + } else { + // TODO: this ought to be a utility function in RED.nodes + var historyEvent = { + t: "edit", + node: n, + changed: n.changed, + changes: { + d: n.d + }, + dirty:RED.nodes.dirty() + } + if (n.d) { + delete n.d; + } else { + n.d = true; + } + n.dirty = true; + n.changed = true; + RED.events.emit("nodes:change",n); + RED.nodes.dirty(true) + RED.view.redraw(); + } + }); + } else { + $('
    ').appendTo(controls) + } + controls.find("button").on("dblclick", function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + }) + } + + function onProjectLoad(activeProject) { + var newFlowData = getFlowData(activeProject); + treeList.treeList('data',newFlowData); + } + + function build() { + var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '400px'}); + var toolbar = $("
    ", {class:"red-ui-info-outline-toolbar"}).appendTo(container); + + var searchInput = $('').appendTo(toolbar).searchBox({ + delay: 100, + change: function() { + var val = $(this).val().trim().toLowerCase(); + if (val) { + var c = treeList.treeList('filter',function(item) { + if (item.depth === 0) { + return true; + } + if (item.id && objects[item.id]) { + var l = ((objects[item.id].type||"")+" "+(objects[item.id].name||"")+" "+(objects[item.id].id||"")+" "+(objects[item.id].label||"")).toLowerCase(); + var isMatch = l.indexOf(val) > -1; + if (isMatch) { + return true; + } + } + return false; + }) + } else { + treeList.treeList('filter',null); + + } + } + }); + + treeList = $("
    ").css({width: "100%"}).appendTo(container).treeList({ + data:getFlowData() + }) + // treeList.on('treelistselect', function(e,item) { + // console.log(item) + // RED.view.reveal(item.id); + // }) + // treeList.treeList('data',[ ... ] ) + treeList.on('treelistconfirm', function(e,item) { + var node = RED.nodes.node(item.id); + if (node) { + if (node._def.category === "config") { + RED.editor.editConfig("", node.type, node.id); + } else { + RED.editor.edit(node); + } + } + }) + + RED.events.on("projects:load", onProjectLoad) + + RED.events.on("flows:add", onFlowAdd) + RED.events.on("flows:remove", onObjectRemove) + RED.events.on("flows:change", onFlowChange) + RED.events.on("flows:reorder", onFlowsReorder) + + RED.events.on("subflows:add", onSubflowAdd) + RED.events.on("subflows:remove", onObjectRemove) + RED.events.on("subflows:change", onSubflowChange) + + RED.events.on("nodes:add",onNodeAdd); + RED.events.on("nodes:remove",onObjectRemove); + RED.events.on("nodes:change",onNodeChange); + + RED.events.on("groups:add",onNodeAdd); + RED.events.on("groups:remove",onObjectRemove); + RED.events.on("groups:change",onNodeChange); + + RED.events.on("view:selection-changed", onSelectionChanged); + + + // ["links","nodes","flows","subflows","groups"].forEach(function(t) { + // ["add","remove","change"].forEach(function(v) { + // RED.events.on(t+":"+v, function(n) { console.log(t+":"+v,n)}) + // }) + // }) + // RED.events.on("workspace:clear", function() { console.log("workspace:clear")}) + + return container; + } + function onFlowAdd(ws) { + objects[ws.id] = { + id: ws.id, + element: getFlowLabel(ws), + children:[getEmptyItem(ws.id)], + deferBuild: true, + icon: "red-ui-icons red-ui-icons-flow" + } + flowList.treeList.addChild(objects[ws.id]) + objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) + objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) + + + } + function onFlowChange(n) { + var existingObject = objects[n.id]; + existingObject.element.find(".red-ui-info-outline-item-label").text(n.label || n.id); + existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled) + existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled) + } + function onFlowsReorder(order) { + var indexMap = {}; + order.forEach(function(id,index) { + indexMap[id] = index; + }) + + flowList.treeList.sortChildren(function(A,B) { + if (A.id === "__global__") { return -1 } + if (B.id === "__global__") { return 1 } + return indexMap[A.id] - indexMap[B.id] + }) + } + function onSubflowAdd(sf) { + objects[sf.id] = { + id: sf.id, + element: getSubflowLabel(sf), + children:[getEmptyItem(sf.id)], + deferBuild: true + } + subflowList.treeList.addChild(objects[sf.id]) + } + function onSubflowChange(n) { + var existingObject = objects[n.id]; + + existingObject.treeList.replaceElement(getSubflowLabel(n)); + // existingObject.element.find(".red-ui-info-outline-item-label").text(n.name || n.id); + + } + + function onNodeChange(n) { + var existingObject = objects[n.id]; + var parent = n.g||n.z; + + var nodeLabelText = getNodeLabelText(n); + if (nodeLabelText) { + existingObject.element.find(".red-ui-info-outline-item-label").text(nodeLabelText); + } else { + existingObject.element.find(".red-ui-info-outline-item-label").html(" "); + } + + if (parent !== existingObject.parent.id) { + existingObject.treeList.remove(); + if (!parent) { + globalConfigNodes.treeList.addChild(existingObject); + } else { + objects[parent].treeList.addChild(existingObject) + } + } + existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) + } + function onObjectRemove(n) { + var existingObject = objects[n.id]; + existingObject.treeList.remove(); + delete objects[n.d] + var parent = existingObject.parent; + if (parent.children.length === 0) { + parent.treeList.addChild(getEmptyItem(parent.id)); + } + } + + function onNodeAdd(n) { + objects[n.id] = { + id: n.id, + element: getNodeLabel(n) + } + if (n.type === "group") { + objects[n.id].children = []; + objects[n.id].deferBuild = true; + } + var parent = n.g||n.z; + if (parent) { + if (objects[parent]) { + if (empties[parent]) { + empties[parent].treeList.remove(); + delete empties[parent]; + } + objects[parent].treeList.addChild(objects[n.id]) + } else { + // The parent hasn't been added yet + console.log("missing",parent) + } + } else { + // No parent - add to Global flow list + globalConfigNodes.treeList.addChild(objects[n.id]) + } + objects[n.id].element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) + } + + function onSelectionChanged(selection) { + // treeList.treeList('clearSelection'); + // console.log(selection); + if (selection.nodes) { + selection.nodes.forEach(function(n) { + // console.log("..",n.id); + treeList.treeList('show',n.id); + if (objects[n.id].treeList) { + objects[n.id].treeList.select(true); + } + + }); + } + } + + return { + build: build + } +})(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js index 39683e41e..c6e9aec52 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js @@ -18,6 +18,7 @@ RED.sidebar.info = (function() { var content; var sections; var propertiesSection; + var outlinerSection; var infoSection; var helpSection; var tipBox; @@ -39,6 +40,13 @@ RED.sidebar.info = (function() { container: stackContainer }).hide(); + outlinerSection = sections.add({ + title: RED._("sidebar.info.outline"), + collapsible: true + }) + outlinerSection.expand(); + RED.sidebar.info.outliner.build().appendTo(outlinerSection.content); + propertiesSection = sections.add({ title: RED._("sidebar.info.info"), collapsible: true diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index a9c1c9500..7e3d72b89 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -859,7 +859,9 @@ RED.utils = (function() { } function getNodeIcon(def,node) { - if (def.category === 'config') { + if (node && node.type === 'group') { + return "font-awesome/fa-object-group" + } else if (def.category === 'config') { return RED.settings.apiRootUrl+"icons/node-red/cog.svg" } else if (node && node.type === 'tab') { return RED.settings.apiRootUrl+"icons/node-red/subflow.svg" diff --git a/packages/node_modules/@node-red/editor-client/src/sass/search.scss b/packages/node_modules/@node-red/editor-client/src/sass/search.scss index 0ec8b6525..273793896 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/search.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/search.scss @@ -87,6 +87,8 @@ } .red-ui-palette-icon { width: 15px; + position:relative; + left: -1px; } .red-ui-search-result-description { margin-left:28px; @@ -153,7 +155,7 @@ width: 30px; float:left; height: 25px; - border-radius: 5px; + border-radius: 3px; border: 1px solid $node-border; background-position: 5% 50%; background-repeat: no-repeat; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss index bc72f7532..1605c9c76 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss @@ -279,3 +279,173 @@ div.red-ui-info-table { border-radius: 4px; padding: 2px 4px 2px; } + +.red-ui-help-search { + border-bottom: 1px solid $secondary-border-color; +} + + +.red-ui-info-outline { + display: flex; + flex-direction: column; + + .red-ui-treeList { + flex-grow: 1; + } + + .red-ui-treeList-container,.red-ui-editableList-border { + border: none; + border-radius: 0; + } + .red-ui-treeList-label { + font-size: 13px; + padding: 2px 0; + overflow: hidden; + } + .red-ui-info-outline-item { + display: inline-block; + padding: 0; + font-size: 13px; + border: none; + .red-ui-palette-icon-fa { + position: relative; + top: 1px; + left: 0px; + } + &:hover { + background: inherit + } + + &.red-ui-info-outline-item-flow { + .red-ui-search-result-description { + margin-left: 4px; + } + } + &.red-ui-info-outline-item-group .red-ui-search-result-node { + background: none; + border-color: transparent; + .red-ui-palette-icon-container { + background: none; + } + .red-ui-palette-icon-fa { + color: $secondary-text-color; + font-size: 18px; + } + } + &.red-ui-info-outline-item-empty { + font-style: italic; + color: $form-placeholder-color; + } + } + + .red-ui-search-result-node { + width: 24px; + height: 20px; + margin-top: 1px; + } + + .red-ui-palette-icon-container { + width: 24px; + } + .red-ui-palette-icon { + width: 20px; + } + .red-ui-search-result-description { + margin-left: 32px; + line-height: 22px; + white-space: nowrap; + } + .red-ui-search-result-node-label { + color: $secondary-text-color; + } +} +.red-ui-info-outline-item-control-spacer { + display: inline-block; + width: 23px; +} + +.red-ui-info-outline-item-controls { + position: absolute; + top:0; + bottom: 0; + right: 0px; + padding: 2px 0 0 1px; + text-align: right; + background: $list-item-background; + + .red-ui-treeList-label:hover & { + background: $list-item-background-hover; + } + .red-ui-treeList-label.selected & { + background: $list-item-background-selected; + } + + + &.red-ui-info-outline-item-hover-controls button { + min-width: 23px; + } + + .red-ui-treeList-label:not(:hover) &.red-ui-info-outline-item-hover-controls { + button { + border: none; + background: none; + } + } + .red-ui-info-outline-item-control-reveal, + .red-ui-info-outline-item-control-action { + display: none; + } + .red-ui-treeList-label:hover & { + .red-ui-info-outline-item-control-reveal, + .red-ui-info-outline-item-control-action { + display: inline-block; + } + } + + .fa-ban { + display: none; + } + .red-ui-info-outline-item.red-ui-info-outline-item-disabled & { + .fa-ban { + display: inline-block; + } + .fa-circle-thin { + display: none; + } + } + button { + margin-right: 3px + } +} +.red-ui-info-outline-item-disabled { + .red-ui-search-result-node { + opacity: 0.4; + } + .red-ui-info-outline-item-label { + font-style: italic; + color: $secondary-text-color-disabled; + } + .red-ui-icons-flow { + opacity: 0.4; + } +} +.red-ui-icons { + display: inline-block; + width: 18px; + &:before { + white-space: pre; + content: ' ' + } + +} + +.red-ui-icons-flow { + background-image: url('images/subflow_tab.svg'); + background-repeat: no-repeat; + background-size: contain; + filter: brightness(2.5); +} + +.red-ui-info-outline-toolbar { + border-bottom: 1px solid $secondary-border-color; +} From 55a5917282c637eaa9e02b40064abe1b6239c3ab Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 27 Apr 2020 14:43:22 +0100 Subject: [PATCH 11/24] [search] Refactor search to use editor events to generate index --- .../editor-client/src/js/ui/search.js | 140 +++++++++++------- 1 file changed, 88 insertions(+), 52 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js index 9bb4cefb0..3fae8ec62 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js @@ -23,8 +23,7 @@ RED.search = (function() { var visible = false; var index = {}; - var keys = []; - var results = []; + var currentResults = []; var previousActiveElement; @@ -66,23 +65,9 @@ RED.search = (function() { } } - function indexWorkspace() { - index = {}; - RED.nodes.eachWorkspace(indexNode); - RED.nodes.eachSubflow(indexNode); - RED.nodes.eachConfig(indexNode); - RED.nodes.eachNode(indexNode); - keys = Object.keys(index); - keys.sort(); - keys.forEach(function(key) { - index[key] = Object.keys(index[key]).map(function(id) { - return index[key][id]; - }) - }) - } - function search(val) { - searchResults.editableList('empty'); + var results = []; + var keys = Object.keys(index); var typeFilter; var m = /(?:^| )type:([^ ]+)/.exec(val); if (m) { @@ -92,8 +77,7 @@ RED.search = (function() { val = val.trim(); - selected = -1; - results = []; + if (val.length > 0 || typeFilter) { val = val.toLowerCase(); var i; @@ -104,10 +88,14 @@ RED.search = (function() { var key = keys[i]; var kpos = keys[i].indexOf(val); if (kpos > -1) { - for (j=0;j 0) { - for (i=0;i 25) { - searchResults.editableList('addItem', { - more: { - results: results, - start: 25 - } - }) - } - } else { - searchResults.editableList('addItem',{}); - } } + return results; } function ensureSelectedIsVisible() { @@ -161,13 +135,32 @@ RED.search = (function() { searchInput = $('').appendTo(searchDiv).searchBox({ delay: 200, change: function() { - search($(this).val()); + searchResults.editableList('empty'); + selected = -1; + currentResults = search($(this).val()); + if (currentResults.length > 0) { + for (i=0;i 25) { + searchResults.editableList('addItem', { + more: { + results: currentResults, + start: 25 + } + }) + } + } else { + searchResults.editableList('addItem',{}); + } + + } }); searchInput.on('keydown',function(evt) { var children; - if (results.length > 0) { + if (currentResults.length > 0) { if (evt.keyCode === 40) { // Down children = searchResults.children(); @@ -199,21 +192,21 @@ RED.search = (function() { var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data'); if (object) { searchResults.editableList('removeItem',object); - for (i=object.more.start;i object.more.start+25) { + if (currentResults.length > object.more.start+25) { searchResults.editableList('addItem', { more: { - results: results, + results: currentResults, start: object.more.start+25 } }) } } } else { - if (results.length > 0) { - reveal(results[Math.max(0,selected)].node); + if (currentResults.length > 0) { + reveal(currentResults[Math.max(0,selected)].node); } } } @@ -234,13 +227,13 @@ RED.search = (function() { div.on("click", function(evt) { evt.preventDefault(); searchResults.editableList('removeItem',object); - for (i=object.more.start;i object.more.start+25) { + if (currentResults.length > object.more.start+25) { searchResults.editableList('addItem', { more: { - results: results, + results: currentResults, start: object.more.start+25 } }) @@ -308,7 +301,7 @@ RED.search = (function() { $("#red-ui-palette-shade").show(); $("#red-ui-sidebar-shade").show(); $("#red-ui-sidebar-separator").hide(); - indexWorkspace(); + if (dialog === null) { createDialog(); } @@ -342,6 +335,28 @@ RED.search = (function() { } } + function clearIndex() { + index = {}; + } + + function addItemToIndex(item) { + indexNode(item); + } + function removeItemFromIndex(item) { + var keys = Object.keys(index); + for (var i=0,l=keys.length;i Date: Mon, 27 Apr 2020 15:23:39 +0100 Subject: [PATCH 12/24] [outline] Connect search dialog to outline filter box --- .../editor-client/src/js/ui/search.js | 5 ++++ .../src/js/ui/tab-info-outliner.js | 27 ++++++++++--------- .../editor-client/src/sass/search.scss | 7 +++++ .../editor-client/src/sass/tab-info.scss | 2 +- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js index 3fae8ec62..f84fae02f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js @@ -157,6 +157,11 @@ RED.search = (function() { } }); + var copySearchContainer = $('').appendTo(searchDiv).on('click', function(evt) { + evt.preventDefault(); + RED.sidebar.info.outliner.search(searchInput.val()) + hide(); + }); searchInput.on('keydown',function(evt) { var children; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index 82d3ce1c0..2975098ec 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -1,7 +1,7 @@ RED.sidebar.info.outliner = (function() { var treeList; - + var searchInput; var flowList; var subflowList; var globalConfigNodes; @@ -222,23 +222,21 @@ RED.sidebar.info.outliner = (function() { var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '400px'}); var toolbar = $("
    ", {class:"red-ui-info-outline-toolbar"}).appendTo(container); - var searchInput = $('').appendTo(toolbar).searchBox({ - delay: 100, + searchInput = $('').appendTo(toolbar).searchBox({ + delay: 300, change: function() { - var val = $(this).val().trim().toLowerCase(); + var val = $(this).val(); + var searchResults = RED.search.search(val); if (val) { + var resultMap = {}; + for (var i=0,l=searchResults.length;i -1; - if (isMatch) { - return true; - } - } - return false; + return item.id && objects[item.id] && resultMap[item.id] }) } else { treeList.treeList('filter',null); @@ -421,6 +419,9 @@ RED.sidebar.info.outliner = (function() { } return { - build: build + build: build, + search: function(val) { + searchInput.searchBox('value',val) + } } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/search.scss b/packages/node_modules/@node-red/editor-client/src/sass/search.scss index 273793896..27ebb1a04 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/search.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/search.scss @@ -24,6 +24,13 @@ top: 0px; border: 1px solid $primary-border-color; box-shadow: 0 0 10px $shadow; + background: $secondary-background; + + .red-ui-searchBox-container { + display: inline-block; + margin-right: 6px; + width: calc(100% - 30px); + } } .red-ui-type-search { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss index 1605c9c76..699203703 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss @@ -369,7 +369,7 @@ div.red-ui-info-table { top:0; bottom: 0; right: 0px; - padding: 2px 0 0 1px; + padding: 2px 8px 0 1px; text-align: right; background: $list-item-background; From 597c4a2e4f4d34b953892099d93ec5af260e9f94 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 1 May 2020 17:37:15 +0100 Subject: [PATCH 13/24] Add createNodeIcon and getDarkerColor to RED.utils --- .../src/js/ui/common/colorPicker.js | 25 +------ .../editor-client/src/js/ui/editor.js | 17 ++++- .../editor-client/src/js/ui/search.js | 12 +--- .../editor-client/src/js/ui/utils.js | 71 +++++++++++++++++-- 4 files changed, 84 insertions(+), 41 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js index 0c7944c84..3739cd970 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js @@ -1,26 +1,5 @@ RED.colorPicker = (function() { - function getDarkerColor(c) { - var r,g,b; - if (/^#[a-f0-9]{6}$/i.test(c)) { - r = parseInt(c.substring(1, 3), 16); - g = parseInt(c.substring(3, 5), 16); - b = parseInt(c.substring(5, 7), 16); - } else if (/^#[a-f0-9]{3}$/i.test(c)) { - r = parseInt(c.substring(1, 2)+c.substring(1, 2), 16); - g = parseInt(c.substring(2, 3)+c.substring(2, 3), 16); - b = parseInt(c.substring(3, 4)+c.substring(3, 4), 16); - } else { - return c; - } - var l = 0.3 * r/255 + 0.59 * g/255 + 0.11 * b/255 ; - r = Math.max(0,r-50); - g = Math.max(0,g-50); - b = Math.max(0,b-50); - var s = ((r<<16) + (g<<8) + b).toString(16); - return '#'+'000000'.slice(0, 6-s.length)+s; - } - function create(options) { var color = options.value; var id = options.id; @@ -57,7 +36,7 @@ RED.colorPicker = (function() { "background-color": color, "opacity": opacity }); - var border = getDarkerColor(color); + var border = RED.utils.getDarkerColor(color); if (border[0] === '#') { border += Math.round(255*Math.floor(opacity*100)/100).toString(16); } else { @@ -132,7 +111,7 @@ RED.colorPicker = (function() { height: height+"px", margin: margin+"px", backgroundColor: col, - "border-color": getDarkerColor(col) + "border-color": RED.utils.getDarkerColor(col) }).appendTo(row); button.on("click", function (e) { e.preventDefault(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index e36e5c969..d5e6d8c1a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -790,6 +790,11 @@ RED.editor = (function() { nodeDiv.css({ 'backgroundColor': backgroundColor }); + var borderColor = RED.utils.getDarkerColor(backgroundColor); + if (borderColor !== backgroundColor) { + nodeDiv.css('border-color',borderColor) + } + } var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); RED.utils.createIconElement(icon_url, iconContainer, true); @@ -932,7 +937,12 @@ RED.editor = (function() { $("#red-ui-editor-node-color").on('change', function(ev) { // Horribly out of scope... - nodeDiv.css('backgroundColor',$(this).val()); + var colour = $(this).val(); + nodeDiv.css('backgroundColor',colour); + var borderColor = RED.utils.getDarkerColor(colour); + if (borderColor !== colour) { + nodeDiv.css('border-color',borderColor) + } }) } @@ -948,6 +958,11 @@ RED.editor = (function() { var colour = RED.utils.getNodeColor(node.type, node._def); var icon_url = RED.utils.getNodeIcon(node._def,node); nodeDiv.css('backgroundColor',colour); + var borderColor = RED.utils.getDarkerColor(colour); + if (borderColor !== colour) { + nodeDiv.css('border-color',borderColor) + } + var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); RED.utils.createIconElement(icon_url, iconContainer, true); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js index f84fae02f..7be689c4a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js @@ -251,17 +251,7 @@ RED.search = (function() { var def = node._def; div = $('',{href:'#',class:"red-ui-search-result"}).appendTo(container); - var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}).appendTo(div); - var colour = RED.utils.getNodeColor(node.type,def); - var icon_url = RED.utils.getNodeIcon(def,node); - if (node.type === 'tab') { - colour = "#C0DEED"; - } - nodeDiv.css('backgroundColor',colour); - - var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); - RED.utils.createIconElement(icon_url, iconContainer, true); - + RED.utils.createNodeIcon(node).appendTo(div); var contentDiv = $('
    ',{class:"red-ui-search-result-node-description"}).appendTo(div); if (node.z) { var workspace = RED.nodes.workspace(node.z); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index 7e3d72b89..e8306bd0f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -806,9 +806,9 @@ RED.utils = (function() { function separateIconPath(icon) { var result = {module: "", file: ""}; if (icon) { - var index = icon.indexOf('icons/'); - if (index !== -1) { - icon = icon.substring(index+6); + var index = icon.indexOf(RED.settings.apiRootUrl+'icons/'); + if (index === 0) { + icon = icon.substring((RED.settings.apiRootUrl+'icons/').length); } index = icon.indexOf('/'); if (index !== -1) { @@ -859,12 +859,15 @@ RED.utils = (function() { } function getNodeIcon(def,node) { - if (node && node.type === 'group') { + if (node && node.type === '_selection_') { + return "font-awesome/fa-object-ungroup"; + } else if (node && node.type === 'group') { return "font-awesome/fa-object-group" } else if (def.category === 'config') { return RED.settings.apiRootUrl+"icons/node-red/cog.svg" } else if (node && node.type === 'tab') { - return RED.settings.apiRootUrl+"icons/node-red/subflow.svg" + return "red-ui-icons/red-ui-icons-flow" + // return RED.settings.apiRootUrl+"images/subflow_tab.svg" } else if (node && node.type === 'unknown') { return RED.settings.apiRootUrl+"icons/node-red/alert.svg" } else if (node && node.icon) { @@ -923,6 +926,8 @@ RED.utils = (function() { var l; if (node.type === 'tab') { l = node.label || defaultLabel + } else if (node.type === 'group') { + l = node.name || defaultLabel } else { l = node._def.label; try { @@ -1056,11 +1061,63 @@ RED.utils = (function() { } // If the specified name is not defined in font-awesome, show arrow-in icon. iconUrl = RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg" + } else if (iconPath.module === "red-ui-icons") { + var redIconElement = $('').appendTo(iconContainer); + redIconElement.addClass("red-ui-palette-icon red-ui-icons " + iconPath.file); + return; } var imageIconElement = $('
    ',{class:"red-ui-palette-icon"}).appendTo(iconContainer); imageIconElement.css("backgroundImage", "url("+iconUrl+")"); } + function createNodeIcon(node) { + var def = node._def; + var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}) + if (node.type === "_selection_") { + nodeDiv.addClass("red-ui-palette-icon-selection"); + } else if (node.type === "group") { + nodeDiv.addClass("red-ui-palette-icon-group"); + } else if (node.type === 'tab') { + nodeDiv.addClass("red-ui-palette-icon-flow"); + } else { + var colour = RED.utils.getNodeColor(node.type,def); + // if (node.type === 'tab') { + // colour = "#C0DEED"; + // } + nodeDiv.css('backgroundColor',colour); + var borderColor = getDarkerColor(colour); + if (borderColor !== colour) { + nodeDiv.css('border-color',borderColor) + } + } + + var icon_url = RED.utils.getNodeIcon(def,node); + var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); + RED.utils.createIconElement(icon_url, iconContainer, true); + return nodeDiv; + } + + function getDarkerColor(c) { + var r,g,b; + if (/^#[a-f0-9]{6}$/i.test(c)) { + r = parseInt(c.substring(1, 3), 16); + g = parseInt(c.substring(3, 5), 16); + b = parseInt(c.substring(5, 7), 16); + } else if (/^#[a-f0-9]{3}$/i.test(c)) { + r = parseInt(c.substring(1, 2)+c.substring(1, 2), 16); + g = parseInt(c.substring(2, 3)+c.substring(2, 3), 16); + b = parseInt(c.substring(3, 4)+c.substring(3, 4), 16); + } else { + return c; + } + var l = 0.3 * r/255 + 0.59 * g/255 + 0.11 * b/255 ; + r = Math.max(0,r-50); + g = Math.max(0,g-50); + b = Math.max(0,b-50); + var s = ((r<<16) + (g<<8) + b).toString(16); + return '#'+'000000'.slice(0, 6-s.length)+s; + } + return { createObjectElement: buildMessageElement, getMessageProperty: getMessageProperty, @@ -1078,6 +1135,8 @@ RED.utils = (function() { parseContextKey: parseContextKey, createIconElement: createIconElement, sanitize: sanitize, - renderMarkdown: renderMarkdown + renderMarkdown: renderMarkdown, + createNodeIcon: createNodeIcon, + getDarkerColor: getDarkerColor } })(); From 73dde4de51357b9ee4c7fdafca6ee960f75cbf1b Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 1 May 2020 17:37:42 +0100 Subject: [PATCH 14/24] Allow node edit dialog to be opened on a non-default tab --- .../node_modules/@node-red/editor-client/src/js/ui/editor.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index d5e6d8c1a..7baf72575 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -1086,7 +1086,7 @@ RED.editor = (function() { return nodeInfoEditor; } - function showEditDialog(node) { + function showEditDialog(node, defaultTab) { var editing_node = node; var isDefaultIcon; var defaultIcon; @@ -1604,6 +1604,9 @@ RED.editor = (function() { prepareEditDialog(node,node._def,"node-input", function() { trayBody.i18n(); finishedBuilding = true; + if (defaultTab) { + editorTabs.activateTab(defaultTab); + } done(); }); }, From aca61c0354c51599059822442f96430374df17fd Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 1 May 2020 17:38:23 +0100 Subject: [PATCH 15/24] Modify RED.panels to use flexbox position --- .../editor-client/src/js/ui/common/panels.js | 8 ++++---- .../@node-red/editor-client/src/sass/panels.scss | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js index 5a82f9d35..31d9936d7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js @@ -52,11 +52,11 @@ RED.panels = (function() { var newSizes = [panelSizes[0]+delta,panelSizes[1]-delta]; if (vertical) { $(children[0]).height(newSizes[0]); - $(children[1]).height(newSizes[1]); + // $(children[1]).height(newSizes[1]); ui.position.top -= delta; } else { $(children[0]).width(newSizes[0]); - $(children[1]).width(newSizes[1]); + // $(children[1]).width(newSizes[1]); ui.position.left -= delta; } if (options.resize) { @@ -99,10 +99,10 @@ RED.panels = (function() { panelSizes = [topPanelSize,bottomPanelSize]; if (vertical) { $(children[0]).outerHeight(panelSizes[0]); - $(children[1]).outerHeight(panelSizes[1]); + // $(children[1]).outerHeight(panelSizes[1]); } else { $(children[0]).outerWidth(panelSizes[0]); - $(children[1]).outerWidth(panelSizes[1]); + // $(children[1]).outerWidth(panelSizes[1]); } } if (options.resize) { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/panels.scss b/packages/node_modules/@node-red/editor-client/src/sass/panels.scss index 9f99db5d4..3da9fa969 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/panels.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/panels.scss @@ -22,9 +22,19 @@ // border: 1px solid red; box-sizing: border-box; } + display: flex; + flex-direction: column; + + >.red-ui-panel:first-child { + flex: 0 0 auto; + } + >.red-ui-panel:last-child { + flex: 1 1 auto; + } } .red-ui-panels-separator { + flex: 0 0 auto; border-top: 1px solid $secondary-border-color; border-bottom: 1px solid $secondary-border-color; height: 7px; @@ -41,6 +51,8 @@ .red-ui-panels.red-ui-panels-horizontal { height: 100%; + flex-direction: row; + &>.red-ui-panel { vertical-align: top; display: inline-block; From 78c86880e4dc2daf3b937c680788eeade9908948 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 1 May 2020 17:39:54 +0100 Subject: [PATCH 16/24] [outline] Update information section of info sidebar --- .../src/js/ui/common/treeList.js | 78 +++-- .../editor-client/src/js/ui/editors/buffer.js | 2 +- .../src/js/ui/editors/expression.js | 4 +- .../src/js/ui/tab-info-outliner.js | 227 +++++++------- .../editor-client/src/js/ui/tab-info.js | 283 ++++++++++-------- .../editor-client/src/sass/editor.scss | 3 - .../editor-client/src/sass/palette.scss | 15 + .../editor-client/src/sass/tab-info.scss | 77 ++++- 8 files changed, 415 insertions(+), 274 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js index 8e3281b3b..5c6d151c9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js @@ -101,6 +101,8 @@ var target; switch(evt.keyCode) { case 13: // ENTER + evt.preventDefault(); + evt.stopPropagation(); if (selected.children) { if (selected.treeList.container.hasClass("expanded")) { selected.treeList.collapse() @@ -113,6 +115,8 @@ break; case 37: // LEFT + evt.preventDefault(); + evt.stopPropagation(); if (selected.children&& selected.treeList.container.hasClass("expanded")) { selected.treeList.collapse() } else if (selected.parent) { @@ -120,6 +124,8 @@ } break; case 38: // UP + evt.preventDefault(); + evt.stopPropagation(); target = that._getPreviousSibling(selected); if (target) { target = that._getLastDescendant(target); @@ -129,6 +135,8 @@ } break; case 39: // RIGHT + evt.preventDefault(); + evt.stopPropagation(); if (selected.children) { if (!selected.treeList.container.hasClass("expanded")) { selected.treeList.expand() @@ -136,6 +144,8 @@ } break case 40: //DOWN + evt.preventDefault(); + evt.stopPropagation(); if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) { target = selected.children[0]; } else { @@ -339,17 +349,17 @@ } item.treeList.expand = function(done) { if (!item.children) { - if (done) { done() } + if (done) { done(false) } return; } if (!item.treeList.container) { item.expanded = true; - if (done) { done() } + if (done) { done(false) } return; } var container = item.treeList.container; if (container.hasClass("expanded")) { - if (done) { done() } + if (done) { done(false) } return; } @@ -376,7 +386,7 @@ } } item.expanded = true; - if (done) { done() } + if (done) { done(true) } that._trigger("childrenloaded",null,item) } if (typeof item.children === 'function') { @@ -399,7 +409,7 @@ item.treeList.childList.slideDown('fast'); } item.expanded = true; - if (done) { done() } + if (done) { done(!that._loadingData) } } container.addClass("expanded"); } @@ -427,13 +437,16 @@ } } item.treeList.replaceElement = function (element) { - if (item.element && item.treeList.container) { - $(item.element).remove(); + if (item.element) { + if (item.treeList.container) { + $(item.element).remove(); + $(element).appendTo(item.treeList.label); + var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(item.depth*20); + $(element).css({ + width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)" + }) + } item.element = element; - $(item.element).appendTo(label); - $(item.element).css({ - width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)" - }) } } @@ -595,6 +608,7 @@ if (v) { that._trigger("select",null,item) } + that.reveal(item); } } if (item.icon) { @@ -658,10 +672,15 @@ stack.unshift(i); i = i.parent; } - var handleStack = function() { + var isOpening = false; + var handleStack = function(opening) { + isOpening = isOpening ||opening var item = stack.shift(); if (stack.length === 0) { - item.treeList.parentList.editableList('show',item); + setTimeout(function() { + that.reveal(item); + },isOpening?200:0); + // item.treeList.parentList.editableList('show',item); } else { item.treeList.expand(handleStack) } @@ -674,13 +693,27 @@ // } // } }, + reveal: function(item) { + var listOffset = this._topList.offset().top; + var itemOffset = item.treeList.label.offset().top; + var scrollTop = this._topList.parent().scrollTop(); + itemOffset -= listOffset+scrollTop; + var treeHeight = this._topList.parent().height(); + var itemHeight = item.treeList.label.outerHeight(); + if (itemOffset < itemHeight/2) { + this._topList.parent().scrollTop(scrollTop+itemOffset-itemHeight/2-itemHeight) + } else if (itemOffset+itemHeight > treeHeight) { + this._topList.parent().scrollTop(scrollTop+((itemOffset+2.5*itemHeight)-treeHeight)); + } + console.log("scroll now",this._topList.parent().scrollTop()) + }, select: function(item) { if (!this.options.multi) { this._topList.find(".selected").removeClass("selected"); } + this.show(item.id); item.treeList.label.addClass("selected"); this._trigger("select",null,item) - }, clearSelection: function() { this._topList.find(".selected").removeClass("selected"); @@ -700,16 +733,16 @@ return undefined; } }, - filter: function(filterFunc) { + filter: function(filterFunc,expandResults) { var filter = function(item) { - // console.log(item); - var childCount = 0; + var matchCount = 0; if (filterFunc && filterFunc(item)) { - childCount++; + matchCount++; } + var childCount = 0; if (item.children && typeof item.children !== "function") { if (item.treeList.childList) { - childCount += item.treeList.childList.editableList('filter', filter); + childCount = item.treeList.childList.editableList('filter', filter); } else { item.treeList.childFilter = filter; if (filterFunc) { @@ -718,13 +751,18 @@ childCount++; } }) + } } + matchCount += childCount; + if (childCount > 0) { + item.treeList.expand(); + } } if (!filterFunc) { return true } - return childCount > 0 + return matchCount > 0 } return this._topList.editableList('filter', filter); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js index ec5b78c99..f837d0cab 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js @@ -15,7 +15,7 @@ **/ (function() { - var template = ''; + var template = ''; function stringToUTF8Array(str) { var data = []; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js index 9a9765c35..3127a2813 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js @@ -318,9 +318,9 @@ var p2 = $("#red-ui-editor-type-expression-panel-info > .form-row > div:first-child"); p2Height -= p2.outerHeight(true) + 20; $(".red-ui-editor-type-expression-tab-content").height(p2Height); - $("#red-ui-editor-type-expression-test-data").css("height",(p2Height-5)+"px"); + $("#red-ui-editor-type-expression-test-data").css("height",(p2Height-25)+"px"); testDataEditor.resize(); - $("#red-ui-editor-type-expression-test-result").css("height",(p2Height-5)+"px"); + $("#red-ui-editor-type-expression-test-result").css("height",(p2Height-25)+"px"); testResultEditor.resize(); } }); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index 2975098ec..e40ae4261 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -2,6 +2,7 @@ RED.sidebar.info.outliner = (function() { var treeList; var searchInput; + var projectInfo; var flowList; var subflowList; var globalConfigNodes; @@ -10,47 +11,37 @@ RED.sidebar.info.outliner = (function() { var objectBacklog = {}; - function getFlowData(project) { + function getFlowData() { var flowData = [ { label: "Flows", expanded: true, - children: [ - { - id: "__global__", - label: "Global", - children: [] - } - ] + children: [] }, { label: "Subflows", children: [] + }, + { + id: "__global__", + label: "Global Configuration Nodes", + children: [] } ] flowList = flowData[0]; subflowList = flowData[1]; - globalConfigNodes = flowList.children[0]; + globalConfigNodes = flowData[2]; - if (project) { - flowData = [ - { - element: getProjectLabel(project), - icon: "fa fa-archive", - children: flowData, - expanded: true - } - ] - } return flowData; } function getProjectLabel(p) { var div = $('
    ',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); + div.css("width", "calc(100% - 40px)"); var contentDiv = $('
    ',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); contentDiv.text(p.name); var controls = $('
    ',{class:"red-ui-info-outline-item-controls"}).appendTo(div); - var editProjectButton = $('') + var editProjectButton = $('') .appendTo(controls) .on("click", function(evt) { evt.preventDefault(); @@ -84,16 +75,7 @@ RED.sidebar.info.outliner = (function() { function getNodeLabel(n) { var div = $('
    ',{class:"red-ui-info-outline-item"}); - var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}).appendTo(div); - if (n.type === "group") { - div.addClass('red-ui-info-outline-item-group') - } else { - var colour = RED.utils.getNodeColor(n.type,n._def); - nodeDiv.css('backgroundColor',colour); - } - var icon_url = RED.utils.getNodeIcon(n._def,n); - var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); - RED.utils.createIconElement(icon_url, iconContainer, false); + RED.utils.createNodeIcon(n).appendTo(div); var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); var labelText = getNodeLabelText(n); var label = $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); @@ -119,16 +101,7 @@ RED.sidebar.info.outliner = (function() { function getSubflowLabel(n) { var div = $('
    ',{class:"red-ui-info-outline-item"}); - var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}).appendTo(div); - if (n.type === "group") { - div.addClass('red-ui-info-outline-item-group') - } else { - var colour = RED.utils.getNodeColor(n.type,n._def); - nodeDiv.css('backgroundColor',colour); - } - var icon_url = RED.utils.getNodeIcon(n._def,n); - var iconContainer = $('
    ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); - RED.utils.createIconElement(icon_url, iconContainer, false); + RED.utils.createNodeIcon(n).appendTo(div); var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); var labelText = getNodeLabelText(n); var label = $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); @@ -214,86 +187,93 @@ RED.sidebar.info.outliner = (function() { } function onProjectLoad(activeProject) { - var newFlowData = getFlowData(activeProject); + var newFlowData = getFlowData(); + getProjectLabel(activeProject).appendTo(projectInfo); treeList.treeList('data',newFlowData); } function build() { - var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '400px'}); - var toolbar = $("
    ", {class:"red-ui-info-outline-toolbar"}).appendTo(container); + var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '100%'}); + var toolbar = $("
    ", {class:"red-ui-sidebar-header red-ui-info-outline-toolbar"}).appendTo(container); + toolbar.on("click", function(evt) { + evt.stopPropagation(); + }) - searchInput = $('').appendTo(toolbar).searchBox({ - delay: 300, - change: function() { - var val = $(this).val(); - var searchResults = RED.search.search(val); - if (val) { - var resultMap = {}; - for (var i=0,l=searchResults.length;i').appendTo(toolbar).searchBox({ + delay: 300, + change: function() { + var val = $(this).val(); + var searchResults = RED.search.search(val); + if (val) { + var resultMap = {}; + for (var i=0,l=searchResults.length;i").css({width: "100%"}).appendTo(container).treeList({ - data:getFlowData() - }) - // treeList.on('treelistselect', function(e,item) { - // console.log(item) - // RED.view.reveal(item.id); - // }) - // treeList.treeList('data',[ ... ] ) - treeList.on('treelistconfirm', function(e,item) { - var node = RED.nodes.node(item.id); - if (node) { - if (node._def.category === "config") { - RED.editor.editConfig("", node.type, node.id); - } else { - RED.editor.edit(node); - } + projectInfo = $('
    ').appendTo(container) + + //
    Space Monkey
    ').appendTo(container) + + treeList = $("
    ").css({width: "100%"}).appendTo(container).treeList({ + data:getFlowData() + }) + // treeList.on('treelistselect', function(e,item) { + // console.log(item) + // RED.view.reveal(item.id); + // }) + // treeList.treeList('data',[ ... ] ) + treeList.on('treelistconfirm', function(e,item) { + var node = RED.nodes.node(item.id); + if (node) { + if (node._def.category === "config") { + RED.editor.editConfig("", node.type, node.id); + } else { + RED.editor.edit(node); } - }) + } + }) - RED.events.on("projects:load", onProjectLoad) + RED.events.on("projects:load", onProjectLoad) - RED.events.on("flows:add", onFlowAdd) - RED.events.on("flows:remove", onObjectRemove) - RED.events.on("flows:change", onFlowChange) - RED.events.on("flows:reorder", onFlowsReorder) + RED.events.on("flows:add", onFlowAdd) + RED.events.on("flows:remove", onObjectRemove) + RED.events.on("flows:change", onFlowChange) + RED.events.on("flows:reorder", onFlowsReorder) - RED.events.on("subflows:add", onSubflowAdd) - RED.events.on("subflows:remove", onObjectRemove) - RED.events.on("subflows:change", onSubflowChange) + RED.events.on("subflows:add", onSubflowAdd) + RED.events.on("subflows:remove", onObjectRemove) + RED.events.on("subflows:change", onSubflowChange) - RED.events.on("nodes:add",onNodeAdd); - RED.events.on("nodes:remove",onObjectRemove); - RED.events.on("nodes:change",onNodeChange); + RED.events.on("nodes:add",onNodeAdd); + RED.events.on("nodes:remove",onObjectRemove); + RED.events.on("nodes:change",onNodeChange); - RED.events.on("groups:add",onNodeAdd); - RED.events.on("groups:remove",onObjectRemove); - RED.events.on("groups:change",onNodeChange); + RED.events.on("groups:add",onNodeAdd); + RED.events.on("groups:remove",onObjectRemove); + RED.events.on("groups:change",onNodeChange); - RED.events.on("view:selection-changed", onSelectionChanged); + RED.events.on("view:selection-changed", onSelectionChanged); - // ["links","nodes","flows","subflows","groups"].forEach(function(t) { - // ["add","remove","change"].forEach(function(v) { - // RED.events.on(t+":"+v, function(n) { console.log(t+":"+v,n)}) - // }) - // }) - // RED.events.on("workspace:clear", function() { console.log("workspace:clear")}) - - return container; + // ["links","nodes","flows","subflows","groups"].forEach(function(t) { + // ["add","remove","change"].forEach(function(v) { + // RED.events.on(t+":"+v, function(n) { console.log(t+":"+v,n)}) + // }) + // }) + // RED.events.on("workspace:clear", function() { console.log("workspace:clear")}) + return container; } function onFlowAdd(ws) { objects[ws.id] = { @@ -330,18 +310,22 @@ RED.sidebar.info.outliner = (function() { function onSubflowAdd(sf) { objects[sf.id] = { id: sf.id, - element: getSubflowLabel(sf), + element: getNodeLabel(sf), children:[getEmptyItem(sf.id)], deferBuild: true } subflowList.treeList.addChild(objects[sf.id]) } - function onSubflowChange(n) { - var existingObject = objects[n.id]; - - existingObject.treeList.replaceElement(getSubflowLabel(n)); + function onSubflowChange(sf) { + var existingObject = objects[sf.id]; + existingObject.treeList.replaceElement(getNodeLabel(sf)); // existingObject.element.find(".red-ui-info-outline-item-label").text(n.name || n.id); - + RED.nodes.eachNode(function(n) { + if (n.type == "subflow:"+sf.id) { + var sfInstance = objects[n.id]; + sfInstance.treeList.replaceElement(getNodeLabel(n)); + } + }); } function onNodeChange(n) { @@ -404,24 +388,27 @@ RED.sidebar.info.outliner = (function() { } function onSelectionChanged(selection) { - // treeList.treeList('clearSelection'); - // console.log(selection); - if (selection.nodes) { - selection.nodes.forEach(function(n) { - // console.log("..",n.id); - treeList.treeList('show',n.id); - if (objects[n.id].treeList) { - objects[n.id].treeList.select(true); - } - - }); - } + treeList.treeList('clearSelection'); + // // console.log(selection); + // if (selection.nodes) { + // selection.nodes.forEach(function(n) { + // // console.log("..",n.id); + // treeList.treeList('show',n.id); + // if (objects[n.id].treeList) { + // objects[n.id].treeList.select(true); + // } + // + // }); + // } } return { build: build, search: function(val) { searchInput.searchBox('value',val) + }, + reveal: function(node) { + treeList.treeList('select', objects[node.id]) } } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js index c6e9aec52..725785e33 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js @@ -16,17 +16,29 @@ RED.sidebar.info = (function() { var content; - var sections; - var propertiesSection; - var outlinerSection; + var panels; var infoSection; - var helpSection; + + var propertiesPanelContent; + var propertiesPanelHeader; + var propertiesPanelHeaderIcon; + var propertiesPanelHeaderLabel; + var propertiesPanelHeaderReveal; + var propertiesPanelHeaderHelp; + + var selectedObject; + var tipBox; + // TODO: remove this var expandedSections = { "property": false }; + function resizeStack() { + var h = $(content).parent().height(); + panels.resize(h) + } function init() { content = document.createElement("div"); @@ -36,37 +48,74 @@ RED.sidebar.info = (function() { var stackContainer = $("
    ",{class:"red-ui-sidebar-info-stack"}).appendTo(content); - sections = RED.stack.create({ - container: stackContainer - }).hide(); + var outlinerPanel = $("
    ").height("calc(70%)").appendTo(stackContainer); + var propertiesPanel = $("
    ").css({ + "overflow":"hidden", + "height":"100%", + "display": "flex", + "flex-direction": "column" + }).appendTo(stackContainer); + propertiesPanelHeader = $("
    ", {class:"red-ui-palette-header red-ui-info-header"}).css({ + "flex":"0 0 auto" + }).appendTo(propertiesPanel); - outlinerSection = sections.add({ - title: RED._("sidebar.info.outline"), - collapsible: true - }) - outlinerSection.expand(); - RED.sidebar.info.outliner.build().appendTo(outlinerSection.content); + propertiesPanelHeaderIcon = $("").appendTo(propertiesPanelHeader); + propertiesPanelHeaderLabel = $("").appendTo(propertiesPanelHeader); + propertiesPanelHeaderHelp = $('').css({ + position: 'absolute', + top: '12px', + right: '38px' + }).on("click", function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + if (selectedObject) { + RED.sidebar.info.outliner.reveal(selectedObject); + } + }).appendTo(propertiesPanelHeader); + RED.popover.tooltip(propertiesPanelHeaderHelp,"Show help"); - propertiesSection = sections.add({ - title: RED._("sidebar.info.info"), - collapsible: true + + propertiesPanelHeaderReveal = $('').css({ + position: 'absolute', + top: '12px', + right: '8px' + }).on("click", function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + if (selectedObject) { + RED.sidebar.info.outliner.reveal(selectedObject); + } + }).appendTo(propertiesPanelHeader); + RED.popover.tooltip(propertiesPanelHeaderReveal,"Show in outline"); + + + propertiesPanelContent = $("
    ").css({ + "flex":"1 1 auto", + "overflow-y":"scroll", + }).appendTo(propertiesPanel); + + + panels = RED.panels.create({container: stackContainer}) + panels.ratio(0.6); + RED.sidebar.info.outliner.build().appendTo(outlinerPanel); + + + RED.sidebar.addTab({ + id: "info", + label: RED._("sidebar.info.label"), + name: RED._("sidebar.info.name"), + iconClass: "fa fa-info", + action:"core:show-info-tab", + content: content, + pinned: true, + enableOnEdit: true }); - propertiesSection.expand(); - infoSection = sections.add({ - title: RED._("sidebar.info.desc"), - collapsible: true - }); - infoSection.expand(); - infoSection.content.css("padding","6px"); + $(window).on("resize", resizeStack); + $(window).on("focus", resizeStack); - helpSection = sections.add({ - title: RED._("sidebar.info.nodeHelp"), - collapsible: true - }); - helpSection.expand(); - helpSection.content.css("padding","6px"); + // Tip Box var tipContainer = $('
    ').appendTo(content); tipBox = $('
    ').appendTo(tipContainer); var tipButtons = $('
    ').appendTo(tipContainer); @@ -83,17 +132,6 @@ RED.sidebar.info = (function() { RED.actions.invoke("core:toggle-show-tips"); RED.notify(RED._("sidebar.info.showTips")); }); - - RED.sidebar.addTab({ - id: "info", - label: RED._("sidebar.info.label"), - name: RED._("sidebar.info.name"), - iconClass: "fa fa-info", - action:"core:show-info-tab", - content: content, - pinned: true, - enableOnEdit: true - }); if (tips.enabled()) { tips.start(); } else { @@ -121,42 +159,29 @@ RED.sidebar.info = (function() { refreshSelection(); return; } - sections.show(); - $(propertiesSection.content).empty(); - $(infoSection.content).empty(); - $(helpSection.content).empty(); - infoSection.title.text(RED._("sidebar.info.desc")); + $(propertiesPanelContent).empty(); var propRow; - var table = $('
    ').appendTo(propertiesSection.content); + var table = $('
    ').appendTo(propertiesPanelContent); var tableBody = $('').appendTo(table); var subflowNode; var subflowUserCount; - var activeProject = RED.projects.getActiveProject(); - if (activeProject) { - propRow = $(''+ RED._("sidebar.project.name") + '').appendTo(tableBody); - $(propRow.children()[1]).text(activeProject.name||""); - $('').appendTo(tableBody); - var editProjectButton = $('') - .appendTo(propRow.children()[1]) - .on("click", function(evt) { - evt.preventDefault(); - RED.projects.editProject(); - }); - RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings')); - } - propertiesSection.container.show(); - infoSection.container.show(); - helpSection.container.show(); if (node === null) { return; } else if (Array.isArray(node)) { // Multiple things selected // - hide help and info sections + propertiesPanelHeaderIcon.empty(); + RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon); + propertiesPanelHeaderLabel.text("Selection"); + propertiesPanelHeaderReveal.hide(); + propertiesPanelHeaderHelp.hide(); + selectedObject = null; + var types = { nodes:0, flows:0, @@ -175,8 +200,7 @@ RED.sidebar.info = (function() { types.nodes++; } }); - helpSection.container.hide(); - infoSection.container.hide(); + // infoSection.container.hide(); // - show the count of selected nodes propRow = $(''+RED._("sidebar.info.selection")+"").appendTo(tableBody); @@ -197,10 +221,10 @@ RED.sidebar.info = (function() { // A single 'thing' selected. // Check to see if this is a subflow or subflow instance - var m = /^subflow(:(.+))?$/.exec(node.type); - if (m) { - if (m[2]) { - subflowNode = RED.nodes.subflow(m[2]); + var subflowRegex = /^subflow(:(.+))?$/.exec(node.type); + if (subflowRegex) { + if (subflowRegex[2]) { + subflowNode = RED.nodes.subflow(subflowRegex[2]); } else { subflowNode = node; } @@ -213,25 +237,21 @@ RED.sidebar.info = (function() { } }); } + + propertiesPanelHeaderIcon.empty(); + RED.utils.createNodeIcon(node).appendTo(propertiesPanelHeaderIcon); + propertiesPanelHeaderLabel.text(RED.utils.getNodeLabel(node, node.type+" "+node.id)); + propertiesPanelHeaderReveal.show(); + selectedObject = node; + if (node.type === "tab" || node.type === "subflow") { // If nothing is selected, but we're on a flow or subflow tab. - propRow = $(''+RED._("sidebar.info."+(node.type==='tab'?'flow':'subflow'))+'').appendTo(tableBody); - RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); - propRow = $(''+RED._("sidebar.info.tabName")+"").appendTo(tableBody); - $(propRow.children()[1]).text(node.label||node.name||""); - if (node.type === "tab") { - propRow = $(''+RED._("sidebar.info.status")+'').appendTo(tableBody); - $(propRow.children()[1]).text((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled")) - } + propertiesPanelHeaderHelp.hide(); } else if (node.type === "group") { - // An actual node is selected in the editor - build up its properties table - propRow = $(''+RED._("sidebar.info.group")+"").appendTo(tableBody); - RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); - if (node.name) { - propRow = $(''+RED._("common.label.name")+'').appendTo(tableBody); - $('').text(node.name).appendTo(propRow.children()[1]); - } - propRow = $(' ').appendTo(tableBody); + propertiesPanelHeaderHelp.hide(); + + propRow = $(''+RED._("sidebar.info.group")+'').appendTo(tableBody); + var typeCounts = { nodes:0, groups: 0 @@ -254,22 +274,30 @@ RED.sidebar.info = (function() { } else { - // An actual node is selected in the editor - build up its properties table - propRow = $(''+RED._("sidebar.info.node")+"").appendTo(tableBody); - RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); - if (node.type !== "subflow" && node.type !== "unknown" && node.name) { - propRow = $(''+RED._("common.label.name")+'').appendTo(tableBody); - $('').text(node.name).appendTo(propRow.children()[1]); + propertiesPanelHeaderHelp.show (); + + propRow = $('').appendTo(tableBody); + + if (!subflowRegex) { + $(propRow.children()[0]).text(RED._("sidebar.info.node")) + } else { + $(propRow.children()[0]).text(RED._("sidebar.info.subflow")) } - if (!m) { - propRow = $(''+RED._("sidebar.info.type")+"").appendTo(tableBody); + RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); + + if (!subflowRegex) { + propRow = $(''+RED._("sidebar.info.type")+'').appendTo(tableBody); $(propRow.children()[1]).text((node.type === "unknown")?node._orig.type:node.type); if (node.type === "unknown") { $('').prependTo($(propRow.children()[1])) } } + var count = 0; - if (!m && node.type != "subflow" && node.type != "group") { + if (!subflowRegex && node.type != "subflow" && node.type != "group") { + + var blankRow = $('').appendTo(tableBody); + var defaults; if (node.type === 'unknown') { defaults = {}; @@ -284,7 +312,6 @@ RED.sidebar.info = (function() { $(propRow.children()[1]).text(RED.nodes.getType(node.type).set.module); count++; } - $('').appendTo(tableBody); if (defaults) { for (var n in defaults) { @@ -292,7 +319,8 @@ RED.sidebar.info = (function() { var val = node[n]; var type = typeof val; count++; - propRow = $(''+n+"").appendTo(tableBody); + propRow = $('').appendTo(tableBody); + $(propRow.children()[0]).text(n); if (defaults[n].type) { var configNode = RED.nodes.node(val); if (!configNode) { @@ -322,37 +350,35 @@ RED.sidebar.info = (function() { } } if (count > 0) { - $(''+RED._("sidebar.info.showMore")+''+RED._("sidebar.info.showLess")+' ').appendTo(tableBody); + $(''+RED._("sidebar.info.showMore")+''+RED._("sidebar.info.showLess")+' ').appendTo(blankRow.children()[0]); } } if (node.type !== 'tab') { - if (m) { + if (subflowRegex) { $(''+RED._("sidebar.info.subflow")+'').appendTo(tableBody); $(''+RED._("common.label.name")+''+RED.utils.sanitize(subflowNode.name)+'').appendTo(tableBody); } } } - if (m) { + if (subflowRegex) { propRow = $(''+RED._("subflow.category")+'').appendTo(tableBody); var category = subflowNode.category||"subflows"; $(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category})) $(''+RED._("sidebar.info.instances")+""+subflowUserCount+'').appendTo(tableBody); } - var helpText = ""; - if (node.type === "tab" || node.type === "subflow") { - $(helpSection.container).hide(); - } else { - $(helpSection.container).show(); - if (subflowNode && node.type !== "subflow") { - // Selected a subflow instance node. - // - The subflow template info goes into help - helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||(''+RED._("sidebar.info.none")+'')); - } else { - helpText = $("script[data-help-name='"+node.type+"']").html()||(''+RED._("sidebar.info.none")+''); - } - setInfoText(helpText, helpSection.content); - } + // var helpText = ""; + // if (node.type === "tab" || node.type === "subflow") { + // } else { + // if (subflowNode && node.type !== "subflow") { + // // Selected a subflow instance node. + // // - The subflow template info goes into help + // helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||(''+RED._("sidebar.info.none")+'')); + // } else { + // helpText = $("script[data-help-name='"+node.type+"']").html()||(''+RED._("sidebar.info.none")+''); + // } + // setInfoText(helpText, helpSection.content); + // } var infoText = ""; @@ -364,7 +390,26 @@ RED.sidebar.info = (function() { if (node.info) { infoText = infoText + RED.utils.renderMarkdown(node.info || "") } - setInfoText(infoText, infoSection.content); + var infoSectionContainer = $("
    ").css("padding","0 6px 6px").appendTo(propertiesPanelContent) + + // var editInfo = $('').appendTo(infoSectionContainer).on("click", function(evt) { + // //.text(RED._("sidebar.info.editDescription")) + // evt.preventDefault(); + // evt.stopPropagation(); + // if (node.type === 'tab') { + // + // } else if (node.type === 'subflow') { + // + // } else if (node.type === 'group') { + // + // } else if (node._def.category !== 'config') { + // RED.editor.edit(node,"editor-tab-description"); + // } else { + // + // } + // }) + + setInfoText(infoText, infoSectionContainer); $(".red-ui-sidebar-info-stack").scrollTop(0); $(".node-info-property-header").on("click", function(e) { @@ -380,7 +425,7 @@ RED.sidebar.info = (function() { // propRow = $('Actions').appendTo(tableBody); // var actionBar = $(propRow.children()[1]); // - // // var actionBar = $('
    ',{style:"background: #fefefe; padding: 3px;"}).appendTo(propertiesSection.content); + // // var actionBar = $('
    ',{style:"background: #fefefe; padding: 3px;"}).appendTo(propertiesPanel); // $('').appendTo(actionBar); // $('').appendTo(actionBar); // $('').appendTo(actionBar); @@ -495,11 +540,11 @@ RED.sidebar.info = (function() { // tips.stop(); // sections.show(); refresh(null); - propertiesSection.container.hide(); - helpSection.container.hide(); - infoSection.container.show(); - infoSection.title.text(title||RED._("sidebar.info.desc")); - setInfoText(html,infoSection.content); + // propertiesSection.container.hide(); + console.warn("Missing RED.sidebar.tab.set") + // infoSection.container.show(); + // infoSection.title.text(title||RED._("sidebar.info.desc")); + // setInfoText(html,infoSection.content); $(".red-ui-sidebar-info-stack").scrollTop(0); } diff --git a/packages/node_modules/@node-red/editor-client/src/sass/editor.scss b/packages/node_modules/@node-red/editor-client/src/sass/editor.scss index e5d80cd72..b18583372 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/editor.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/editor.scss @@ -304,9 +304,6 @@ button.red-ui-button-small &:first-child { padding: 20px 20px 0; } - &:last-child { - padding-bottom: 20px; - } } } .red-ui-editor-type-expression-tab-content { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/palette.scss b/packages/node_modules/@node-red/editor-client/src/sass/palette.scss index ea4b06c52..922a31e33 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/palette.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/palette.scss @@ -186,6 +186,21 @@ background-size: contain; background-repeat: no-repeat; } +.red-ui-search-result-node { + &.red-ui-palette-icon-flow, + &.red-ui-palette-icon-group, + &.red-ui-palette-icon-selection { + background: none; + border-color: transparent; + .red-ui-palette-icon-container { + background: none; + } + .red-ui-palette-icon-fa { + color: $secondary-text-color; + font-size: 18px; + } + } +} .red-ui-palette-icon-fa { color: white; position: absolute; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss index 699203703..4c8ddcc38 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss @@ -14,16 +14,34 @@ * limitations under the License. **/ +.red-ui-sidebar-info { + height: 100%; +} .red-ui-sidebar-info hr { margin: 10px 0; } +.red-ui-info-header { + padding-left: 9px; + line-height: 21px; + cursor: default; + > * { + vertical-align: middle + } + > span { + display: inline-block; + margin-left: 5px; + } + border-bottom: 1px solid $secondary-border-color; +} table.red-ui-info-table { font-size: 14px; margin: 0 0 10px; width: 100%; } table.red-ui-info-table tr:not(.blank) { - border-top: 1px solid $secondary-border-color; + &:not(:first-child) { + border-top: 1px solid $secondary-border-color; + } border-bottom: 1px solid $secondary-border-color; } .red-ui-help-property-expand { @@ -214,12 +232,13 @@ div.red-ui-info-table { } .red-ui-sidebar-info-stack { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - overflow-y: scroll; + height: 100%; + // position: absolute; + // top: 0; + // bottom: 0; + // left: 0; + // right: 0; + // overflow-y: scroll; } .red-ui-help-tips { display: none; @@ -291,6 +310,12 @@ div.red-ui-info-table { .red-ui-treeList { flex-grow: 1; + position: relative; + } + .red-ui-treeList-container { + position: absolute; + top: 0; + bottom: 0; } .red-ui-treeList-container,.red-ui-editableList-border { @@ -302,6 +327,10 @@ div.red-ui-info-table { padding: 2px 0; overflow: hidden; } + .red-ui-info-outline-project { + border-bottom: 1px solid $secondary-border-color; + } + .red-ui-info-outline-item { display: inline-block; padding: 0; @@ -369,7 +398,7 @@ div.red-ui-info-table { top:0; bottom: 0; right: 0px; - padding: 2px 8px 0 1px; + padding: 2px 3px 0 1px; text-align: right; background: $list-item-background; @@ -429,6 +458,9 @@ div.red-ui-info-table { opacity: 0.4; } } + + + .red-ui-icons { display: inline-block; width: 18px; @@ -447,5 +479,32 @@ div.red-ui-info-table { } .red-ui-info-outline-toolbar { - border-bottom: 1px solid $secondary-border-color; + min-height: 39px; + box-sizing: border-box; + // padding-left: 9px; + // box-sizing: border-box; + // background: $palette-header-background; + // border-bottom: 1px solid $secondary-border-color; + + .red-ui-searchBox-container { + position: absolute; + top: 6px; + right: 8px; + width: calc(100% - 150px); + max-width: 250px; + background: $palette-header-background; + input.red-ui-searchBox-input { + border: 1px solid $secondary-border-color; + border-radius: 3px; + font-size: 12px; + height: 26px; + } + input:focus.red-ui-searchBox-input { + border: 1px solid $secondary-border-color; + } + i.fa-search, i.fa-times { + top: 7px; + } + } + } From fc2a9a85ff99be07a2fbf3a12f0c80b1f8eb664b Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 1 May 2020 17:51:44 +0100 Subject: [PATCH 17/24] [outline] Reveal selected item after clearing outline filter --- .../@node-red/editor-client/src/js/ui/common/treeList.js | 2 ++ .../@node-red/editor-client/src/js/ui/tab-info-outliner.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js index 5c6d151c9..c243fe7a7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js @@ -730,6 +730,8 @@ if (s.length) { return s.parent().data('data'); } else { + // TODO: This may be a bug.. it causes the call to return itself + // not undefined. return undefined; } }, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index e40ae4261..1b381354d 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -217,6 +217,10 @@ RED.sidebar.info.outliner = (function() { },true) } else { treeList.treeList('filter',null); + var selected = treeList.treeList('selected'); + if (selected.id) { + treeList.treeList('show',selected.id); + } } } From 010e20989a477c133335fce8701f983db2225f8d Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 6 May 2020 16:12:07 +0100 Subject: [PATCH 18/24] [popover] Allow hover-type popovers to contain buttons --- .../editor-client/src/js/ui/common/popover.js | 23 +++++++++++++++++-- .../editor-client/src/sass/popover.scss | 10 ++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) 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 8b796f4fd..6f8724a8a 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 @@ -136,6 +136,23 @@ RED.popover = (function() { closePopup(true); }); } + if (trigger === 'hover' && options.interactive) { + div.on('mouseenter', function(e) { + clearTimeout(timer); + active = true; + }) + div.on('mouseleave', function(e) { + if (timer) { + clearTimeout(timer); + } + if (active) { + timer = setTimeout(function() { + active = false; + closePopup(); + },delay.hide); + } + }) + } if (instant) { div.show(); } else { @@ -163,8 +180,10 @@ RED.popover = (function() { if (trigger === 'hover') { target.on('mouseenter',function(e) { clearTimeout(timer); - active = true; - timer = setTimeout(openPopup,delay.show); + if (!active) { + active = true; + timer = setTimeout(openPopup,delay.show); + } }); target.on('mouseleave disabled', function(e) { if (timer) { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/popover.scss b/packages/node_modules/@node-red/editor-client/src/sass/popover.scss index 95097a30e..872f32024 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/popover.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/popover.scss @@ -150,6 +150,16 @@ .red-ui-popover a.red-ui-button, .red-ui-popover button.red-ui-button { + &:not(.primary) { + border-color: $popover-button-border-color; + background: $popover-background; + color: $popover-color !important; + } + &:not(.primary):not(.disabled):not(.ui-button-disabled):hover { + border-color: $popover-button-border-color-hover; + } + + &.primary { border-color: $popover-button-border-color; } From 9f29149d870b623e05bec8b670afa518fcf055e9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 6 May 2020 16:15:12 +0100 Subject: [PATCH 19/24] [help-sidebar] Add help sidebar --- Gruntfile.js | 1 + .../editor-client/locales/en-US/editor.json | 11 +- .../@node-red/editor-client/src/js/red.js | 3 +- .../editor-client/src/js/ui/common/panels.js | 7 + .../editor-client/src/js/ui/editors/buffer.js | 3 +- .../src/js/ui/editors/expression.js | 3 +- .../editor-client/src/js/ui/palette.js | 28 +- .../editor-client/src/js/ui/sidebar.js | 1 + .../editor-client/src/js/ui/tab-help.js | 355 ++++++++++++++++++ .../src/js/ui/tab-info-outliner.js | 13 +- .../editor-client/src/js/ui/tab-info.js | 22 +- .../editor-client/src/sass/panels.scss | 1 + .../editor-client/src/sass/style.scss | 1 + .../editor-client/src/sass/tab-help.scss | 27 ++ .../editor-client/src/sass/tab-info.scss | 18 +- 15 files changed, 443 insertions(+), 51 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js create mode 100644 packages/node_modules/@node-red/editor-client/src/sass/tab-help.scss diff --git a/Gruntfile.js b/Gruntfile.js index b39256d88..c7867bd35 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -166,6 +166,7 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/ui/palette.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js", "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 7567ada3a..f0443ad8e 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -584,7 +584,16 @@ "none":"None", "arrayItems": "__count__ items", "showTips":"You can open the tips from the settings panel", - "outline": "Outline" + "outline": "Outline", + "globalConfig": "Global Configuration Nodes" + }, + "help": { + "name": "Help", + "label": "help", + "search": "Search help", + "nodeHelp": "Node Help", + "showHelp": "Show help", + "showTopics": "Show topics" }, "config": { "name": "Configuration nodes", diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index 290b692c3..330b2f9a5 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -433,8 +433,7 @@ var RED = (function() { ''+ '
    '; - RED.sidebar.info.set(aboutHeader+RED.utils.renderMarkdown(data)); - RED.sidebar.info.show(); + RED.sidebar.help.set(aboutHeader+RED.utils.renderMarkdown(data)); }); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js index 31d9936d7..285c7c9bb 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js @@ -29,6 +29,10 @@ RED.panels = (function() { if (!vertical) { container.addClass("red-ui-panels-horizontal"); } + + $(children[0]).addClass("red-ui-panel"); + $(children[1]).addClass("red-ui-panel"); + var separator = $('
    ').insertAfter(children[0]); var startPosition; var panelSizes = []; @@ -71,6 +75,9 @@ RED.panels = (function() { var panel = { ratio: function(ratio) { + if (ratio === undefined) { + return panelRatio; + } panelRatio = ratio; modifiedSizes = true; if (ratio === 0 || ratio === 1) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js index f837d0cab..a1e244290 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/buffer.js @@ -187,8 +187,7 @@ $(".red-ui-editor-type-buffer-type").on("click", function(e) { e.preventDefault(); - RED.sidebar.info.set(RED._("bufferEditor.modeDesc")); - RED.sidebar.info.show(); + RED.sidebar.help.set(RED._("bufferEditor.modeDesc")); }) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js index 3127a2813..64dec3a62 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js @@ -237,8 +237,7 @@ var changeTimer; $(".red-ui-editor-type-expression-legacy").on("click", function(e) { e.preventDefault(); - RED.sidebar.info.set(RED._("expressionEditor.compatModeDesc")); - RED.sidebar.info.show(); + RED.sidebar.help.set(RED._("expressionEditor.compatModeDesc")); }) var testExpression = function() { var value = testDataEditor.getValue(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js index 345f7e7f3..170ff149a 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js @@ -165,6 +165,7 @@ RED.palette = (function() { metaData = typeInfo.set.module+" : "; } metaData += type; + $('').appendTo(popOverContent) $('

    ',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent); } } catch(err) { @@ -255,6 +256,7 @@ RED.palette = (function() { var popover = RED.popover.create({ target:d, trigger: "hover", + interactive: true, width: "300px", content: "hi", delay: { show: 750, hide: 50 } @@ -270,19 +272,19 @@ RED.palette = (function() { // html: true, // container:'body' // }); - d.on("click", function() { - RED.view.focus(); - var helpText; - if (nt.indexOf("subflow:") === 0) { - helpText = RED.utils.renderMarkdown(RED.nodes.subflow(nt.substring(8)).info||"")||(''+RED._("sidebar.info.none")+''); - } else { - helpText = $("script[data-help-name='"+d.attr("data-palette-type")+"']").html()||(''+RED._("sidebar.info.none")+''); - } - // Don't look too closely. RED.sidebar.info.set will set the 'Description' - // section of the sidebar. Pass in the title of the Help section so it looks - // right. - RED.sidebar.info.set(helpText,RED._("sidebar.info.nodeHelp")); - }); + // d.on("click", function() { + // RED.view.focus(); + // var helpText; + // if (nt.indexOf("subflow:") === 0) { + // helpText = RED.utils.renderMarkdown(RED.nodes.subflow(nt.substring(8)).info||"")||(''+RED._("sidebar.info.none")+''); + // } else { + // helpText = $("script[data-help-name='"+d.attr("data-palette-type")+"']").html()||(''+RED._("sidebar.info.none")+''); + // } + // // Don't look too closely. RED.sidebar.info.set will set the 'Description' + // // section of the sidebar. Pass in the title of the Help section so it looks + // // right. + // RED.sidebar.type.show(helpText,RED._("sidebar.info.nodeHelp")); + // }); var chart = $("#red-ui-workspace-chart"); var chartSVG = $("#red-ui-workspace-chart>svg").get(0); var activeSpliceLink; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js b/packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js index 9e89add0c..64465ae17 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js @@ -250,6 +250,7 @@ RED.sidebar = (function() { RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar"); showSidebar(); RED.sidebar.info.init(); + RED.sidebar.help.init(); RED.sidebar.config.init(); RED.sidebar.context.init(); // hide info bar at start if screen rather narrow... diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js new file mode 100644 index 000000000..65a7ec38a --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js @@ -0,0 +1,355 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ +RED.sidebar.help = (function() { + + var content; + var toolbar; + var helpSection; + var panels; + var panelRatio; + var helpTopics = []; + var treeList; + var tocPanel; + var helpIndex = {}; + + + function resizeStack() { + var h = $(content).parent().height() - toolbar.outerHeight(); + panels.resize(h) + } + + function init() { + + content = document.createElement("div"); + content.className = "red-ui-sidebar-info" + + toolbar = $("

    ", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(content); + + $(' ').appendTo(toolbar) + + + var showTOCButton = toolbar.find('#red-ui-sidebar-help-show-toc') + + RED.popover.tooltip(showTOCButton,RED._("sidebar.help.showTopics")); + + + showTOCButton.on("click",function(e) { + e.preventDefault(); + if ($(this).hasClass('selected')) { + hideTOC(); + } else { + showTOC(); + } + }); + + // var searchInput = $('').appendTo(toolbar).searchBox({ + // delay: 300, + // change: function() {} + // }); + + + var stackContainer = $("
    ",{class:"red-ui-sidebar-help-stack"}).appendTo(content); + // var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '100%'}); + + + tocPanel = $("
    ", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer); + var helpPanel = $("
    ").css({ + "overflow-y": "scroll" + }).appendTo(stackContainer); + + panels = RED.panels.create({ + container: stackContainer + }) + panels.ratio(0.5); + + // var searchDiv = $("
    ",{class: "red-ui-help-search"}).appendTo(tocPanel); + + helpSearch = $('').appendTo(toolbar).searchBox({ + delay: 100, + change: function() { + var val = $(this).val(); + if (val) { + showTOC(); + var c = treeList.treeList('filter',function(item) { + if (item.depth === 0) { + return true; + } + return item.id && item.id.indexOf(val) > -1 + },true) + } else { + treeList.treeList('filter',null); + var selected = treeList.treeList('selected'); + if (selected.id) { + treeList.treeList('show',selected.id); + } + + } + } + }) + + helpSection = $("
    ",{class:"red-ui-help"}).css({ + "padding":"6px", + }).appendTo(helpPanel) + + treeList = $("
    ").css({width: "100%"}).appendTo(tocPanel).treeList({data: []}) + treeList.on('treelistselect', function(e,item) { + if (item.nodeType) { + showHelp(item.nodeType); + } + }) + + RED.sidebar.addTab({ + id: "help", + label: RED._("sidebar.help.label"), + name: RED._("sidebar.help.name"), + iconClass: "fa fa-book", + action:"core:show-help-tab", + content: content, + pinned: true, + enableOnEdit: true, + onchange: function() { + resizeStack() + } + }); + + $(window).on("resize", resizeStack); + $(window).on("focus", resizeStack); + + var refreshTimer; + + RED.events.on('registry:node-type-added', function(nodeType) { + if (!refreshTimer) { + refreshTimer = setTimeout(function() { + refreshTimer = null; + refreshHelpIndex(); + },500); + } + }); + + RED.actions.add("core:show-help-tab",show); + + } + + function hideTOC() { + var tocButton = $('#red-ui-sidebar-help-show-toc') + if (tocButton.hasClass('selected')) { + tocButton.removeClass('selected'); + panelRatio = panels.ratio(); + tocPanel.css({"transition":"height 0.2s"}) + panels.ratio(0) + setTimeout(function() { + tocPanel.css({"transition":""}) + },250); + } + } + function showTOC() { + var tocButton = $('#red-ui-sidebar-help-show-toc') + if (!tocButton.hasClass('selected')) { + tocButton.addClass('selected'); + tocPanel.css({"transition":"height 0.2s"}) + panels.ratio(Math.max(0.3,Math.min(panelRatio,0.7))); + setTimeout(function() { + tocPanel.css({"transition":""}) + },250); + } + } + + function refreshHelpIndex() { + helpTopics = []; + var modules = RED.nodes.registry.getModuleList(); + var moduleNames = Object.keys(modules); + moduleNames.sort(); + + var helpData = [{ + label: RED._("sidebar.help.nodeHelp"), + children: [], + expanded: true + }] + + moduleNames.forEach(function(moduleName) { + var module = modules[moduleName]; + var nodeTypes = []; + + var setNames = Object.keys(module.sets); + setNames.forEach(function(setName) { + module.sets[setName].types.forEach(function(nodeType) { + if ($("script[data-help-name='"+nodeType+"']").length) { + nodeTypes.push({ + id: moduleName+"/"+nodeType, + nodeType: nodeType, + element:getNodeLabel({_def:RED.nodes.getType(nodeType),type:nodeType}) + }) + } + }) + }) + if (nodeTypes.length > 0) { + helpData[0].children.push({ + id: moduleName, + icon: "fa fa-cube", + label: moduleName, + children: nodeTypes + }) + } + }); + treeList.treeList("data",helpData); + } + + function getNodeLabel(n) { + var div = $('
    ',{class:"red-ui-info-outline-item"}); + RED.utils.createNodeIcon(n).appendTo(div); + var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); + $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).text(n.type).appendTo(contentDiv); + return div; + } + + function showHelp(nodeType) { + helpSection.empty(); + var helpText = $("script[data-help-name='"+nodeType+"']").html()||(''+RED._("sidebar.info.none")+''); + var title = nodeType; + setInfoText(title, helpText, helpSection); + + var ratio = panels.ratio(); + if (ratio > 0.7) { + panels.ratio(0.7) + } + + } + + function show(type) { + RED.sidebar.show("help"); + if (type) { + hideTOC(); + showHelp(type); + } + } + + // TODO: DRY - projects.js + function addTargetToExternalLinks(el) { + $(el).find("a").each(function(el) { + var href = $(this).attr('href'); + if (/^https?:/.test(href)) { + $(this).attr('target','_blank'); + } + }); + return el; + } + function refresh(node) { + if (node === undefined) { + refreshSelection(); + return; + } + var subflowNode; + helpSection.empty(); + if (node === null || Array.isArray(node)) { + return; + } else { + // A single 'thing' selected. + + // Check to see if this is a subflow or subflow instance + var m = /^subflow(:(.+))?$/.exec(node.type); + if (m) { + if (m[2]) { + subflowNode = RED.nodes.subflow(m[2]); + } else { + subflowNode = node; + } + } + var helpText = ""; + if (node.type === "tab" || node.type === "subflow") { + } else { + if (subflowNode && node.type !== "subflow") { + // Selected a subflow instance node. + // - The subflow template info goes into help + helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||(''+RED._("sidebar.info.none")+'')); + setInfoText(node.type, helpText, helpSection); + } else { + helpSearch.searchBox("value",node.type); + // helpText = $("script[data-help-name='"+node.type+"']").html()||(''+RED._("sidebar.info.none")+''); + } + } + + // $(".red-ui-sidebar-info-stack").scrollTop(0); + } + } + function setInfoText(title, infoText,target) { + if (title) { + $("

    ",{class:"red-ui-help-title"}).text(title).appendTo(target); + } + var info = addTargetToExternalLinks($('
    '+infoText+'
    ')).appendTo(target); + info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "" ); + var foldingHeader = "H3"; + info.find(foldingHeader).wrapInner('') + .find("a").prepend('').on("click", function(e) { + e.preventDefault(); + var isExpanded = $(this).hasClass('expanded'); + var el = $(this).parent().next(); + while(el.length === 1 && el[0].nodeName !== foldingHeader) { + el.toggle(!isExpanded); + el = el.next(); + } + $(this).toggleClass('expanded',!isExpanded); + }) + target.parent().scrollTop(0); + } + + function set(html,title) { + $(helpSection).empty(); + setInfoText(title,html,helpSection); + show(); + } + + // function refreshSelection(selection) { + // if (selection === undefined) { + // selection = RED.view.selection(); + // } + // if (selection.nodes) { + // if (selection.nodes.length == 1) { + // var node = selection.nodes[0]; + // if (node.type === "subflow" && node.direction) { + // refresh(RED.nodes.subflow(node.z)); + // } else { + // refresh(node); + // } + // } else { + // refresh(selection.nodes); + // } + // } else if (selection.flows || selection.subflows) { + // refresh(selection.flows); + // } else { + // var activeWS = RED.workspaces.active(); + // + // var flow = RED.nodes.workspace(activeWS) || RED.nodes.subflow(activeWS); + // if (flow) { + // refresh(flow); + // } else { + // var workspace = RED.nodes.workspace(RED.workspaces.active()); + // if (workspace && workspace.info) { + // refresh(workspace); + // } else { + // refresh(null) + // // clear(); + // } + // } + // } + // } + // RED.events.on("view:selection-changed",refreshSelection); + + return { + init: init, + show: show, + set: set + } +})(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index 1b381354d..28ef2f74d 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -14,17 +14,17 @@ RED.sidebar.info.outliner = (function() { function getFlowData() { var flowData = [ { - label: "Flows", + label: RED._("menu.label.flows"), expanded: true, children: [] }, { - label: "Subflows", + label: RED._("menu.label.subflows"), children: [] }, { id: "__global__", - label: "Global Configuration Nodes", + label: RED._("sidebar.info.globalConfig"), children: [] } ] @@ -194,12 +194,9 @@ RED.sidebar.info.outliner = (function() { function build() { var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '100%'}); - var toolbar = $("
    ", {class:"red-ui-sidebar-header red-ui-info-outline-toolbar"}).appendTo(container); - toolbar.on("click", function(evt) { - evt.stopPropagation(); - }) + var toolbar = $("
    ", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(container); - searchInput = $('').appendTo(toolbar).searchBox({ + searchInput = $('').appendTo(toolbar).searchBox({ delay: 300, change: function() { var val = $(this).val(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js index 725785e33..91c92ee1e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js @@ -48,7 +48,10 @@ RED.sidebar.info = (function() { var stackContainer = $("
    ",{class:"red-ui-sidebar-info-stack"}).appendTo(content); - var outlinerPanel = $("
    ").height("calc(70%)").appendTo(stackContainer); + var outlinerPanel = $("
    ").css({ + "overflow": "hidden", + "height": "calc(70%)" + }).appendTo(stackContainer); var propertiesPanel = $("
    ").css({ "overflow":"hidden", "height":"100%", @@ -61,7 +64,7 @@ RED.sidebar.info = (function() { propertiesPanelHeaderIcon = $("").appendTo(propertiesPanelHeader); propertiesPanelHeaderLabel = $("").appendTo(propertiesPanelHeader); - propertiesPanelHeaderHelp = $('').css({ + propertiesPanelHeaderHelp = $('').css({ position: 'absolute', top: '12px', right: '38px' @@ -69,10 +72,10 @@ RED.sidebar.info = (function() { evt.preventDefault(); evt.stopPropagation(); if (selectedObject) { - RED.sidebar.info.outliner.reveal(selectedObject); + RED.sidebar.help.show(selectedObject.type) } }).appendTo(propertiesPanelHeader); - RED.popover.tooltip(propertiesPanelHeaderHelp,"Show help"); + RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp")); propertiesPanelHeaderReveal = $('').css({ @@ -537,15 +540,8 @@ RED.sidebar.info = (function() { } function set(html,title) { - // tips.stop(); - // sections.show(); - refresh(null); - // propertiesSection.container.hide(); - console.warn("Missing RED.sidebar.tab.set") - // infoSection.container.show(); - // infoSection.title.text(title||RED._("sidebar.info.desc")); - // setInfoText(html,infoSection.content); - $(".red-ui-sidebar-info-stack").scrollTop(0); + console.warn("Deprecated use of RED.sidebar.info.set - use RED.sidebar.help.set instead") + RED.sidebar.help.set(html,title); } function refreshSelection(selection) { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/panels.scss b/packages/node_modules/@node-red/editor-client/src/sass/panels.scss index 3da9fa969..455aab891 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/panels.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/panels.scss @@ -47,6 +47,7 @@ .red-ui-panel { overflow: auto; height: calc(50% - 4px); + position: relative; } .red-ui-panels.red-ui-panels-horizontal { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/style.scss b/packages/node_modules/@node-red/editor-client/src/sass/style.scss index 088e5c1b8..ca572ea46 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/style.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/style.scss @@ -42,6 +42,7 @@ @import "tab-config"; @import "tab-context"; @import "tab-info"; +@import "tab-help"; @import "popover"; @import "flow"; @import "palette-editor"; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-help.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-help.scss new file mode 100644 index 000000000..fe4f9fb84 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-help.scss @@ -0,0 +1,27 @@ +.red-ui-sidebar-help-stack { + // height: calc(100% - 39px); +} +.red-ui-help-search { + border-bottom: 1px solid $secondary-border-color; +} + +.red-ui-sidebar-help-toc { + .red-ui-treeList-label { + font-size: 13px; + padding: 2px 0; + overflow: hidden; + white-space: nowrap; + } + +} +#red-ui-sidebar-help-show-toc { + i.fa-angle-right { + transition: all 0.2s ease-in-out; + } + &.selected { + i.fa-angle-right { + transform: rotate(90deg); + } + } + +} diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss index 4c8ddcc38..2d1c9de80 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss @@ -39,9 +39,7 @@ table.red-ui-info-table { width: 100%; } table.red-ui-info-table tr:not(.blank) { - &:not(:first-child) { - border-top: 1px solid $secondary-border-color; - } + border-top: 1px solid $secondary-border-color; border-bottom: 1px solid $secondary-border-color; } .red-ui-help-property-expand { @@ -143,6 +141,9 @@ div.red-ui-info-table { font-size: 1.296em; line-height: 1.3em; margin: 8px auto; + &.red-ui-help-title { + border-bottom: 1px solid $tertiary-border-color; + } } h2 { font-weight: 500; @@ -299,12 +300,7 @@ div.red-ui-info-table { padding: 2px 4px 2px; } -.red-ui-help-search { - border-bottom: 1px solid $secondary-border-color; -} - - -.red-ui-info-outline { +.red-ui-info-outline,.red-ui-sidebar-help-toc { display: flex; flex-direction: column; @@ -478,9 +474,11 @@ div.red-ui-info-table { filter: brightness(2.5); } -.red-ui-info-outline-toolbar { +.red-ui-info-toolbar { min-height: 39px; + height: 39px; box-sizing: border-box; + text-align: left; // padding-left: 9px; // box-sizing: border-box; // background: $palette-header-background; From 17891d373b69d7b748b1ace3ae19d0e15e98009b Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 7 May 2020 10:32:25 +0100 Subject: [PATCH 20/24] [outliner] Fix positioning of tips box --- .../@node-red/editor-client/src/js/ui/tab-info.js | 11 ++++++++--- .../@node-red/editor-client/src/sass/tab-info.scss | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js index 91c92ee1e..5d548103e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js @@ -28,6 +28,7 @@ RED.sidebar.info = (function() { var selectedObject; + var tipContainer; var tipBox; // TODO: remove this @@ -36,8 +37,10 @@ RED.sidebar.info = (function() { }; function resizeStack() { - var h = $(content).parent().height(); - panels.resize(h) + if (panels) { + var h = $(content).parent().height() - tipContainer.outerHeight(); + panels.resize(h) + } } function init() { @@ -119,7 +122,7 @@ RED.sidebar.info = (function() { // Tip Box - var tipContainer = $('
    ').appendTo(content); + tipContainer = $('
    ').appendTo(content); tipBox = $('
    ').appendTo(tipContainer); var tipButtons = $('
    ').appendTo(tipContainer); @@ -503,6 +506,7 @@ RED.sidebar.info = (function() { } function startTips() { $(".red-ui-sidebar-info").addClass('show-tips'); + resizeStack(); if (enabled) { if (!startTimeout && !refreshTimeout) { if (tipCount === -1) { @@ -516,6 +520,7 @@ RED.sidebar.info = (function() { } function stopTips() { $(".red-ui-sidebar-info").removeClass('show-tips'); + resizeStack(); clearInterval(refreshTimeout); clearTimeout(startTimeout); refreshTimeout = null; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss index 2d1c9de80..2bd436b1c 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss @@ -247,20 +247,23 @@ div.red-ui-info-table { left:0; right:0; bottom: 0; - height: 150px; + height: 0; + transition: height 0.2s, padding 0.2s; box-sizing: border-box; border-top: 1px solid $secondary-border-color; background-color: $secondary-background; - padding: 20px; + padding: 0; box-shadow: 0 5px 20px 0px $shadow; overflow-y: auto; } .red-ui-sidebar-info.show-tips { .red-ui-sidebar-info-stack { - bottom: 150px; + height: calc(100% - 150px); } .red-ui-help-tips { display: block; + height: 150px; + padding: 20px; } } From f25e4ea5205df73640165686391777fb8360f936 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 7 May 2020 12:34:15 +0100 Subject: [PATCH 21/24] [help-sidebar] Include subflow help in sidebar --- .../src/js/ui/common/treeList.js | 38 +++++++--- .../editor-client/src/js/ui/tab-help.js | 75 +++++++++++++++---- 2 files changed, 85 insertions(+), 28 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js index c243fe7a7..dbb14c6df 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js @@ -663,9 +663,14 @@ return this._data; } }, - show: function(id) { + show: function(item) { + if (typeof item === "string") { + item = this._items[item] + } + if (!item) { + return; + } var that = this; - var item = this._items[id]; var stack = []; var i = item; while(i) { @@ -680,20 +685,19 @@ setTimeout(function() { that.reveal(item); },isOpening?200:0); - // item.treeList.parentList.editableList('show',item); } else { item.treeList.expand(handleStack) } } handleStack(); - - // for (var i=0;i treeHeight) { this._topList.parent().scrollTop(scrollTop+((itemOffset+2.5*itemHeight)-treeHeight)); } - console.log("scroll now",this._topList.parent().scrollTop()) }, - select: function(item) { + select: function(item, triggerEvent) { + if (typeof item === "string") { + item = this._items[item] + } if (!this.options.multi) { this._topList.find(".selected").removeClass("selected"); } + if (!item) { + return; + } this.show(item.id); item.treeList.label.addClass("selected"); - this._trigger("select",null,item) + if (triggerEvent !== false) { + this._trigger("select",null,item) + } }, clearSelection: function() { this._topList.find(".selected").removeClass("selected"); @@ -767,6 +778,9 @@ return matchCount > 0 } return this._topList.editableList('filter', filter); + }, + get: function(id) { + return this._items[id] || null; } }); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js index 65a7ec38a..bb2085533 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js @@ -80,14 +80,15 @@ RED.sidebar.help = (function() { helpSearch = $('').appendTo(toolbar).searchBox({ delay: 100, change: function() { - var val = $(this).val(); + var val = $(this).val().toLowerCase(); if (val) { showTOC(); var c = treeList.treeList('filter',function(item) { if (item.depth === 0) { return true; } - return item.id && item.id.indexOf(val) > -1 + return (item.nodeType && item.nodeType.indexOf(val) > -1) || + (item.subflowLabel && item.subflowLabel.indexOf(val) > -1) },true) } else { treeList.treeList('filter',null); @@ -128,21 +129,30 @@ RED.sidebar.help = (function() { $(window).on("resize", resizeStack); $(window).on("focus", resizeStack); - var refreshTimer; - - RED.events.on('registry:node-type-added', function(nodeType) { - if (!refreshTimer) { - refreshTimer = setTimeout(function() { - refreshTimer = null; - refreshHelpIndex(); - },500); - } - }); + RED.events.on('registry:node-type-added', queueRefresh); + RED.events.on('registry:node-type-removed', queueRefresh); + RED.events.on('subflows:change', refreshSubflow); RED.actions.add("core:show-help-tab",show); } + var refreshTimer; + function queueRefresh() { + if (!refreshTimer) { + refreshTimer = setTimeout(function() { + refreshTimer = null; + refreshHelpIndex(); + },500); + } + } + + function refreshSubflow(sf) { + var item = treeList.treeList('get',"node-type:subflow:"+sf.id); + item.subflowLabel = sf._def.label().toLowerCase(); + item.treeList.replaceElement(getNodeLabel({_def:sf._def,type:sf._def.label()})); + } + function hideTOC() { var tocButton = $('#red-ui-sidebar-help-show-toc') if (tocButton.hasClass('selected')) { @@ -163,6 +173,10 @@ RED.sidebar.help = (function() { panels.ratio(Math.max(0.3,Math.min(panelRatio,0.7))); setTimeout(function() { tocPanel.css({"transition":""}) + var selected = treeList.treeList('selected'); + if (selected.id) { + treeList.treeList('show',selected); + } },250); } } @@ -179,6 +193,24 @@ RED.sidebar.help = (function() { expanded: true }] + var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)}); + if (subflows.length > 0) { + helpData[0].children.push({ + label: RED._("menu.label.subflows"), + children: [] + }) + subflows.forEach(function(nodeType) { + var sf = RED.nodes.getType(nodeType); + helpData[0].children[0].children.push({ + id:"node-type:"+nodeType, + nodeType: nodeType, + subflowLabel: sf.label().toLowerCase(), + element: getNodeLabel({_def:sf,type:sf.label()}) + }) + }) + } + + moduleNames.forEach(function(moduleName) { var module = modules[moduleName]; var nodeTypes = []; @@ -188,7 +220,7 @@ RED.sidebar.help = (function() { module.sets[setName].types.forEach(function(nodeType) { if ($("script[data-help-name='"+nodeType+"']").length) { nodeTypes.push({ - id: moduleName+"/"+nodeType, + id: "node-type:"+nodeType, nodeType: nodeType, element:getNodeLabel({_def:RED.nodes.getType(nodeType),type:nodeType}) }) @@ -211,20 +243,30 @@ RED.sidebar.help = (function() { var div = $('
    ',{class:"red-ui-info-outline-item"}); RED.utils.createNodeIcon(n).appendTo(div); var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); - $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).text(n.type).appendTo(contentDiv); + $('
    ',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).text(n.name||n.type).appendTo(contentDiv); return div; } function showHelp(nodeType) { helpSection.empty(); - var helpText = $("script[data-help-name='"+nodeType+"']").html()||(''+RED._("sidebar.info.none")+''); - var title = nodeType; + var helpText; + var title; + var m = /^subflow(:(.+))?$/.exec(nodeType); + if (m && m[2]) { + var subflowNode = RED.nodes.subflow(m[2]); + helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||(''+RED._("sidebar.info.none")+'')); + title = subflowNode.name || nodeType; + } else { + helpText = $("script[data-help-name='"+nodeType+"']").html()||(''+RED._("sidebar.info.none")+''); + title = nodeType; + } setInfoText(title, helpText, helpSection); var ratio = panels.ratio(); if (ratio > 0.7) { panels.ratio(0.7) } + treeList.treeList("select","node-type:"+nodeType, false); } @@ -284,6 +326,7 @@ RED.sidebar.help = (function() { // $(".red-ui-sidebar-info-stack").scrollTop(0); } } + function setInfoText(title, infoText,target) { if (title) { $("

    ",{class:"red-ui-help-title"}).text(title).appendTo(target); From 4af1cf1d1f132ae5bf7e83730b6a5fc4426b5707 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 7 May 2020 15:19:56 +0100 Subject: [PATCH 22/24] [help-sidebar] Refresh help on node selection --- .../editor-client/locales/en-US/editor.json | 3 +- .../editor-client/src/js/ui/tab-help.js | 114 ++++-------------- 2 files changed, 26 insertions(+), 91 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index f0443ad8e..36690a06b 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -593,7 +593,8 @@ "search": "Search help", "nodeHelp": "Node Help", "showHelp": "Show help", - "showTopics": "Show topics" + "showTopics": "Show topics", + "noHelp": "No help topic selected" }, "config": { "name": "Configuration nodes", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js index bb2085533..11359010f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js @@ -37,15 +37,9 @@ RED.sidebar.help = (function() { content.className = "red-ui-sidebar-info" toolbar = $("
    ", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(content); - - $(' ').appendTo(toolbar) - - + $('').appendTo(toolbar) var showTOCButton = toolbar.find('#red-ui-sidebar-help-show-toc') - RED.popover.tooltip(showTOCButton,RED._("sidebar.help.showTopics")); - - showTOCButton.on("click",function(e) { e.preventDefault(); if ($(this).hasClass('selected')) { @@ -55,15 +49,7 @@ RED.sidebar.help = (function() { } }); - // var searchInput = $('').appendTo(toolbar).searchBox({ - // delay: 300, - // change: function() {} - // }); - - var stackContainer = $("
    ",{class:"red-ui-sidebar-help-stack"}).appendTo(content); - // var container = $("
    ", {class:"red-ui-info-outline"}).css({'height': '100%'}); - tocPanel = $("
    ", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer); var helpPanel = $("
    ").css({ @@ -75,8 +61,6 @@ RED.sidebar.help = (function() { }) panels.ratio(0.5); - // var searchDiv = $("
    ",{class: "red-ui-help-search"}).appendTo(tocPanel); - helpSearch = $('').appendTo(toolbar).searchBox({ delay: 100, change: function() { @@ -105,6 +89,8 @@ RED.sidebar.help = (function() { "padding":"6px", }).appendTo(helpPanel) + $(''+RED._("sidebar.help.noHelp")+'').appendTo(helpSection); + treeList = $("
    ").css({width: "100%"}).appendTo(tocPanel).treeList({data: []}) treeList.on('treelistselect', function(e,item) { if (item.nodeType) { @@ -228,6 +214,9 @@ RED.sidebar.help = (function() { }) }) if (nodeTypes.length > 0) { + nodeTypes.sort(function(A,B) { + return A.nodeType.localeCompare(B.nodeType) + }) helpData[0].children.push({ id: moduleName, icon: "fa fa-cube", @@ -288,44 +277,6 @@ RED.sidebar.help = (function() { }); return el; } - function refresh(node) { - if (node === undefined) { - refreshSelection(); - return; - } - var subflowNode; - helpSection.empty(); - if (node === null || Array.isArray(node)) { - return; - } else { - // A single 'thing' selected. - - // Check to see if this is a subflow or subflow instance - var m = /^subflow(:(.+))?$/.exec(node.type); - if (m) { - if (m[2]) { - subflowNode = RED.nodes.subflow(m[2]); - } else { - subflowNode = node; - } - } - var helpText = ""; - if (node.type === "tab" || node.type === "subflow") { - } else { - if (subflowNode && node.type !== "subflow") { - // Selected a subflow instance node. - // - The subflow template info goes into help - helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||(''+RED._("sidebar.info.none")+'')); - setInfoText(node.type, helpText, helpSection); - } else { - helpSearch.searchBox("value",node.type); - // helpText = $("script[data-help-name='"+node.type+"']").html()||(''+RED._("sidebar.info.none")+''); - } - } - - // $(".red-ui-sidebar-info-stack").scrollTop(0); - } - } function setInfoText(title, infoText,target) { if (title) { @@ -354,41 +305,24 @@ RED.sidebar.help = (function() { show(); } - // function refreshSelection(selection) { - // if (selection === undefined) { - // selection = RED.view.selection(); - // } - // if (selection.nodes) { - // if (selection.nodes.length == 1) { - // var node = selection.nodes[0]; - // if (node.type === "subflow" && node.direction) { - // refresh(RED.nodes.subflow(node.z)); - // } else { - // refresh(node); - // } - // } else { - // refresh(selection.nodes); - // } - // } else if (selection.flows || selection.subflows) { - // refresh(selection.flows); - // } else { - // var activeWS = RED.workspaces.active(); - // - // var flow = RED.nodes.workspace(activeWS) || RED.nodes.subflow(activeWS); - // if (flow) { - // refresh(flow); - // } else { - // var workspace = RED.nodes.workspace(RED.workspaces.active()); - // if (workspace && workspace.info) { - // refresh(workspace); - // } else { - // refresh(null) - // // clear(); - // } - // } - // } - // } - // RED.events.on("view:selection-changed",refreshSelection); + function refreshSelection(selection) { + if (selection === undefined) { + selection = RED.view.selection(); + } + if (selection.nodes) { + if (selection.nodes.length == 1) { + var node = selection.nodes[0]; + if (node.type === "subflow" && node.direction) { + // ignore subflow virtual ports + } else if (node.type !== 'group'){ + showHelp(node.type); + } + } else { + refresh(selection.nodes); + } + } + } + RED.events.on("view:selection-changed",refreshSelection); return { init: init, From f87698438db8fae6ab82756bd757ac72eaea7dd5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 7 May 2020 21:48:47 +0100 Subject: [PATCH 23/24] [outliner] Handle switching projects properly --- .../@node-red/editor-client/src/js/nodes.js | 1 - .../editor-client/src/js/ui/subflow.js | 6 ++-- .../src/js/ui/tab-info-outliner.js | 32 +++++++------------ 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 974a69b24..1eac45494 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1524,7 +1524,6 @@ RED.nodes = (function() { groupsByZ = {}; var subflowIds = Object.keys(subflows); - subflows = {}; subflowIds.forEach(function(id) { RED.subflow.removeSubflow(id) }); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index b60cbe991..8eda5f5b7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -463,15 +463,15 @@ RED.subflow = (function() { var activeSubflow = RED.nodes.subflow(id); RED.nodes.eachNode(function(n) { - if (n.type == "subflow:"+activeSubflow.id) { + if (n.type == "subflow:"+id) { removedNodes.push(n); } - if (n.z == activeSubflow.id) { + if (n.z == id) { removedNodes.push(n); } }); RED.nodes.eachConfig(function(n) { - if (n.z == activeSubflow.id) { + if (n.z == id) { removedNodes.push(n); } }); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index 28ef2f74d..ec06808e7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -3,14 +3,13 @@ RED.sidebar.info.outliner = (function() { var treeList; var searchInput; var projectInfo; + var projectInfoLabel; var flowList; var subflowList; var globalConfigNodes; var objects = {}; - var objectBacklog = {}; - function getFlowData() { var flowData = [ { @@ -187,8 +186,11 @@ RED.sidebar.info.outliner = (function() { } function onProjectLoad(activeProject) { + objects = {}; var newFlowData = getFlowData(); - getProjectLabel(activeProject).appendTo(projectInfo); + projectInfoLabel.empty(); + getProjectLabel(activeProject).appendTo(projectInfoLabel); + projectInfo.show(); treeList.treeList('data',newFlowData); } @@ -223,7 +225,8 @@ RED.sidebar.info.outliner = (function() { } }); - projectInfo = $('
    ').appendTo(container) + projectInfo = $('
    ').hide().appendTo(container) + projectInfoLabel = $('').appendTo(projectInfo); //
    Space Monkey
    ').appendTo(container) @@ -267,15 +270,13 @@ RED.sidebar.info.outliner = (function() { RED.events.on("view:selection-changed", onSelectionChanged); + RED.events.on("workspace:clear", onWorkspaceClear) - // ["links","nodes","flows","subflows","groups"].forEach(function(t) { - // ["add","remove","change"].forEach(function(v) { - // RED.events.on(t+":"+v, function(n) { console.log(t+":"+v,n)}) - // }) - // }) - // RED.events.on("workspace:clear", function() { console.log("workspace:clear")}) return container; } + function onWorkspaceClear() { + treeList.treeList('data',getFlowData()); + } function onFlowAdd(ws) { objects[ws.id] = { id: ws.id, @@ -390,17 +391,6 @@ RED.sidebar.info.outliner = (function() { function onSelectionChanged(selection) { treeList.treeList('clearSelection'); - // // console.log(selection); - // if (selection.nodes) { - // selection.nodes.forEach(function(n) { - // // console.log("..",n.id); - // treeList.treeList('show',n.id); - // if (objects[n.id].treeList) { - // objects[n.id].treeList.select(true); - // } - // - // }); - // } } return { From 88bc022e2a1962166f19337174f20add529f4567 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 11 May 2020 09:00:12 +0100 Subject: [PATCH 24/24] [help-sidebar] hide toc when directly setting content --- .../node_modules/@node-red/editor-client/src/js/ui/tab-help.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js index 11359010f..b480dde67 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js @@ -302,6 +302,7 @@ RED.sidebar.help = (function() { function set(html,title) { $(helpSection).empty(); setInfoText(title,html,helpSection); + hideTOC(); show(); }