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 dbb14c6df..656f2764f 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 @@ -40,7 +40,8 @@ * label: 'Local', // label for the item * sublabel: 'Local', // a sub-label for the item * icon: 'fa fa-rocket', // (optional) icon for the item - * selected: true/false, // (optional) if present, display checkbox accordingly + * checkbox: true/false, // (optional) if present, display checkbox accordingly + * selected: true/false, // (optional) whether the item is selected or not * children: [] | function(done,item) // (optional) an array of child items, or a function * // that will call the `done` callback with an array * // of child items @@ -163,6 +164,7 @@ }); this._data = []; this._items = {}; + this._selected = new Set(); this._topList = $('
    ').css({ position:'absolute', top: 0, @@ -318,6 +320,7 @@ item.parent.children.splice(index,1) that._trigger("sort",null,item.parent); } + delete item.treeList; delete(that._items[item.id]); } item.treeList.insertChildAt = function(newItem,position,select) { @@ -564,7 +567,7 @@ item.treeList.makeParent(); } - if (item.hasOwnProperty('selected')) { + if (item.checkbox) { var selectWrapper = $('').appendTo(label); var cb = $('').prop('checked',item.selected).appendTo(selectWrapper); label.toggleClass("selected",item.selected); @@ -573,6 +576,11 @@ }); cb.on('change', function(e) { item.selected = this.checked; + if (item.selected) { + that._selected.add(item); + } else { + that._selected.delete(item); + } label.toggleClass("selected",this.checked); that._trigger("select",e,item); }) @@ -590,9 +598,11 @@ } else { label.on("click", function(e) { if (!that.options.multi) { - that._topList.find(".selected").removeClass("selected"); + that.clearSelection(); } label.addClass("selected"); + that._selected.add(item); + that._trigger("select",e,item) }) label.on("dblclick", function(e) { @@ -602,14 +612,21 @@ }) item.treeList.select = function(v) { if (!that.options.multi) { - that._topList.find(".selected").removeClass("selected"); + that.clearSelection(); } label.toggleClass("selected",v); if (v) { + that._selected.add(item); that._trigger("select",null,item) + } else { + that._selected.delete(item); } that.reveal(item); } + if (item.selected) { + that._selected.add(item); + } + label.toggleClass("selected",!!item.selected); } if (item.icon) { if (typeof item.icon === "string") { @@ -663,7 +680,7 @@ return this._data; } }, - show: function(item) { + show: function(item, done) { if (typeof item === "string") { item = this._items[item] } @@ -684,6 +701,7 @@ if (stack.length === 0) { setTimeout(function() { that.reveal(item); + if (done) { done(); } },isOpening?200:0); } else { item.treeList.expand(handleStack) @@ -710,36 +728,53 @@ this._topList.parent().scrollTop(scrollTop+((itemOffset+2.5*itemHeight)-treeHeight)); } }, - select: function(item, triggerEvent) { + select: function(item, triggerEvent, deselectExisting) { + var that = this; + if (!this.options.multi && deselectExisting !== false) { + this.clearSelection(); + } + if (Array.isArray(item)) { + item.forEach(function(i) { + that.select(i,triggerEvent,false); + }) + return; + } 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.show(item.id); + item.selected = true; + this._selected.add(item); + + if (item.treeList.label) { + item.treeList.label.addClass("selected"); + } if (triggerEvent !== false) { this._trigger("select",null,item) } }, clearSelection: function() { - this._topList.find(".selected").removeClass("selected"); + this._selected.forEach(function(item) { + item.selected = false; + if (item.treeList.label) { + item.treeList.label.removeClass("selected") + } + }); + this._selected.clear(); }, selected: function() { - var s = this._topList.find(".selected"); + var selected = []; + this._selected.forEach(function(item) { + selected.push(item); + }) if (this.options.multi) { - var res = []; - s.each(function() { - res.push($(this).parent().data('data')); - }) - return res; + return selected; } - if (s.length) { - return s.parent().data('data'); + if (selected.length) { + return selected[0] } else { // TODO: This may be a bug.. it causes the call to return itself // not undefined. 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 09c9720ae..a0bf74cb4 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 @@ -1092,6 +1092,7 @@ RED.editor = (function() { var defaultIcon; var nodeInfoEditor; var finishedBuilding = false; + var skipInfoRefreshOnClose = false; editStack.push(node); RED.view.state(RED.state.EDITING); @@ -1532,9 +1533,6 @@ RED.editor = (function() { collapsible: true, menu: false }); - if (editing_node) { - RED.sidebar.info.refresh(editing_node); - } var ns; if (node._def.set.module === "node-red") { ns = "node-red"; @@ -1614,7 +1612,7 @@ RED.editor = (function() { if (RED.view.state() != RED.state.IMPORT_DRAGGING) { RED.view.state(RED.state.DEFAULT); } - if (editing_node) { + if (editing_node && !skipInfoRefreshOnClose) { RED.sidebar.info.refresh(editing_node); } RED.workspaces.refresh(); @@ -1642,6 +1640,7 @@ RED.editor = (function() { text: RED._("subflow.edit"), click: function() { RED.workspaces.show(id); + skipInfoRefreshOnClose = true; $("#node-dialog-ok").trigger("click"); } }); 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 c0c6e889a..49f2a8702 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 @@ -504,7 +504,9 @@ RED.group = (function() { } } } - + if (g) { + RED.events.emit("groups:change",group) + } markDirty(group); } function removeFromGroup(group, nodes, reparent) { 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 c75dba284..4c469425b 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 @@ -59,7 +59,7 @@ RED.sidebar.help = (function() { panels = RED.panels.create({ container: stackContainer }) - panels.ratio(0.5); + panels.ratio(0.3); helpSearch = $('').appendTo(toolbar).searchBox({ delay: 100, @@ -255,6 +255,7 @@ RED.sidebar.help = (function() { if (ratio > 0.7) { panels.ratio(0.7) } + treeList.treeList("show","node-type:"+nodeType) treeList.treeList("select","node-type:"+nodeType, false); } 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 5a6e11ce3..2e8a6939e 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 @@ -61,7 +61,7 @@ RED.sidebar.info.outliner = (function() { } function getNodeLabelText(n) { - var label = n.name || n.id; + var label = n.name || n.type+": "+n.id; if (n._def.label) { try { label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||""; @@ -131,11 +131,11 @@ RED.sidebar.info.outliner = (function() { RED.view.clickNodeButton(n); }) } - $('').appendTo(controls).on("click",function(evt) { - evt.preventDefault(); - evt.stopPropagation(); - RED.view.reveal(n.id); - }) + // $('').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(); @@ -226,11 +226,16 @@ RED.sidebar.info.outliner = (function() { 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('treelistselect', function(e,item) { + var node = RED.nodes.node(item.id) || RED.nodes.group(item.id); + if (node) { + if (node.type === 'group' || node._def.category !== "config") { + RED.view.select({nodes:[node]}) + } else { + RED.view.select({nodes:[]}) + } + } + }) treeList.on('treelistconfirm', function(e,item) { var node = RED.nodes.node(item.id); if (node) { @@ -276,7 +281,8 @@ RED.sidebar.info.outliner = (function() { element: getFlowLabel(ws), children:[getEmptyItem(ws.id)], deferBuild: true, - icon: "red-ui-icons red-ui-icons-flow" + icon: "red-ui-icons red-ui-icons-flow", + gutter: getGutter(ws) } flowList.treeList.addChild(objects[ws.id]) objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) @@ -307,7 +313,8 @@ RED.sidebar.info.outliner = (function() { id: sf.id, element: getNodeLabel(sf), children:[getEmptyItem(sf.id)], - deferBuild: true + deferBuild: true, + gutter: getGutter(sf) } subflowList.treeList.addChild(objects[sf.id]) } @@ -353,11 +360,20 @@ RED.sidebar.info.outliner = (function() { parent.treeList.addChild(getEmptyItem(parent.id)); } } - + function getGutter(n) { + var span = $("",{class:"red-ui-info-outline-gutter"}); + $('').appendTo(span).on("click",function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + RED.view.reveal(n.id); + }) + return span; + } function onNodeAdd(n) { objects[n.id] = { id: n.id, - element: getNodeLabel(n) + element: getNodeLabel(n), + gutter: getGutter(n) } if (n.type === "group") { objects[n.id].children = []; @@ -383,7 +399,7 @@ RED.sidebar.info.outliner = (function() { } function onSelectionChanged(selection) { - treeList.treeList('clearSelection'); + // treeList.treeList('clearSelection'); } return { @@ -391,8 +407,20 @@ RED.sidebar.info.outliner = (function() { search: function(val) { searchInput.searchBox('value',val) }, + select: function(node) { + if (node) { + if (Array.isArray(node)) { + treeList.treeList('select', node.map(function(n) { return objects[n.id] }), false) + } else { + treeList.treeList('select', objects[node.id], false) + + } + } else { + treeList.treeList('clearSelection') + } + }, reveal: function(node) { - treeList.treeList('select', objects[node.id]) + treeList.treeList('show', 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 4d1af6e4b..16cf49efb 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 @@ -70,12 +70,12 @@ RED.sidebar.info = (function() { propertiesPanelHeaderHelp = $('').css({ position: 'absolute', top: '12px', - right: '38px' + right: '32px' }).on("click", function(evt) { evt.preventDefault(); evt.stopPropagation(); if (selectedObject) { - RED.sidebar.help.show(selectedObject.type) + RED.sidebar.help.show(selectedObject.type); } }).appendTo(propertiesPanelHeader); RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp")); @@ -90,6 +90,7 @@ RED.sidebar.info = (function() { evt.stopPropagation(); if (selectedObject) { RED.sidebar.info.outliner.reveal(selectedObject); + RED.view.reveal(selectedObject.id); } }).appendTo(propertiesPanelHeader); RED.popover.tooltip(propertiesPanelHeaderReveal,RED._("sidebar.help.showInOutline")); @@ -176,10 +177,12 @@ RED.sidebar.info = (function() { var subflowUserCount; if (node === null) { + RED.sidebar.info.outliner.select(null); return; } else if (Array.isArray(node)) { // Multiple things selected // - hide help and info sections + RED.sidebar.info.outliner.select(node); propertiesPanelHeaderIcon.empty(); RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon); @@ -226,6 +229,8 @@ RED.sidebar.info = (function() { } else { // A single 'thing' selected. + RED.sidebar.info.outliner.select(node); + // Check to see if this is a subflow or subflow instance var subflowRegex = /^subflow(:(.+))?$/.exec(node.type); if (subflowRegex) { @@ -246,17 +251,30 @@ RED.sidebar.info = (function() { propertiesPanelHeaderIcon.empty(); RED.utils.createNodeIcon(node).appendTo(propertiesPanelHeaderIcon); - propertiesPanelHeaderLabel.text(RED.utils.getNodeLabel(node, node.type+" "+node.id)); + propertiesPanelHeaderLabel.text(RED.utils.getNodeLabel(node, node.type+": "+node.id)); propertiesPanelHeaderReveal.show(); selectedObject = node; + propRow = $('').appendTo(tableBody); + var objectType = "node"; + if (node.type === "subflow" || subflowRegex) { + objectType = "subflow"; + } else if (node.type === "tab") { + objectType = "flow"; + }else if (node.type === "group") { + objectType = "group"; + } + $(propRow.children()[0]).text(RED._("sidebar.info."+objectType)) + RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); + if (node.type === "tab" || node.type === "subflow") { // If nothing is selected, but we're on a flow or subflow tab. propertiesPanelHeaderHelp.hide(); + } else if (node.type === "group") { propertiesPanelHeaderHelp.hide(); - propRow = $(''+RED._("sidebar.info.group")+'').appendTo(tableBody); + propRow = $(' ').appendTo(tableBody); var typeCounts = { nodes:0, @@ -280,16 +298,7 @@ RED.sidebar.info = (function() { } else { - propertiesPanelHeaderHelp.show (); - - propRow = $('').appendTo(tableBody); - - if (!subflowRegex) { - $(propRow.children()[0]).text(RED._("sidebar.info.node")) - } else { - $(propRow.children()[0]).text(RED._("sidebar.info.subflow")) - } - RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); + propertiesPanelHeaderHelp.show(); if (!subflowRegex) { propRow = $(''+RED._("sidebar.info.type")+'').appendTo(tableBody); 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 84bb1dac5..acfc8eca7 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 @@ -4665,45 +4665,51 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } }, getGroupAtPoint: getGroupAt, getActiveGroup: function() { return activeGroup }, - reveal: function(id) { + reveal: function(id,triggerHighlight) { if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) { RED.workspaces.show(id); } else { var node = RED.nodes.node(id) || RED.nodes.group(id); if (node) { if (node.z && (node.type === "group" || node._def.category !== 'config')) { - 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]; - - 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); + var cx = node.x; + var cy = node.y; + if (node.type === "group") { + cx += node.w/2; + cy += node.h/2; + } + if (cx < scrollPos[0] || cy < scrollPos[1] || cx > screenSize[0]+scrollPos[0] || cy > screenSize[1]+scrollPos[1]) { + var deltaX = '-='+(((scrollPos[0] - cx) + screenSize[0]/2)*scaleFactor); + var deltaY = '-='+(((scrollPos[1] - cy) + 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; + if (triggerHighlight !== false) { + node.highlighted = true; + 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(); } - RED.view.redraw(); + flashFunc(); } - flashFunc(); } } else if (node._def.category === 'config') { RED.sidebar.config.show(id); 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 8bb0fde68..f50a40050 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 @@ -387,7 +387,7 @@ RED.workspaces = (function() { RED.nodes.dirty(true); RED.sidebar.config.refresh(); var selection = RED.view.selection(); - if (!selection.nodes && !selection.links) { + if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) { RED.sidebar.info.refresh(workspace); } if (changes.hasOwnProperty('disabled')) { 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 2bd436b1c..38754d475 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 @@ -391,7 +391,15 @@ div.red-ui-info-table { display: inline-block; width: 23px; } - +.red-ui-info-outline-gutter { + display:none; + position: absolute; + top: 2px; + left: 2px; + .red-ui-treeList-label:hover & { + display: inline; + } +} .red-ui-info-outline-item-controls { position: absolute; top:0;