From 7cd92faf0d822349b2cb7699cd6ab3bbe82c2768 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 12 Oct 2021 16:49:38 +0100 Subject: [PATCH] Separate 'focus' from 'selected' state in treeList --- .../src/js/ui/common/treeList.js | 98 +++++++++++++------ .../src/js/ui/tab-info-outliner.js | 4 +- .../editor-client/src/sass/tab-info.scss | 11 ++- .../src/sass/ui/common/treeList.scss | 6 ++ 4 files changed, 85 insertions(+), 34 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 9ee508de6..9fca87e75 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 @@ -24,6 +24,9 @@ * - rootSortable: boolean - if 'sortable' is set, then setting this to * false, prevents items being sorted to the * top level of the tree + * - autoSelect: boolean - default true - triggers item selection when navigating + * list by keyboard. If the list has checkboxed items + * you probably want to set this to false * * methods: * - data(items) - clears existing items and replaces with new data @@ -50,6 +53,7 @@ * deferBuild: true/false, // don't build any ui elements for the item's children * until it is expanded by the user. * element: // custom dom element to use for the item - ignored if `label` is set + * collapsible: true/false, // prevent a parent item from being collapsed. default true. * } * ] * @@ -90,77 +94,96 @@ $.widget( "nodered.treeList", { _create: function() { var that = this; - + var autoSelect = true; + if (that.options.autoSelect === false) { + autoSelect = false; + } this.element.addClass('red-ui-treeList'); this.element.attr("tabIndex",0); var wrapper = $('
',{class:'red-ui-treeList-container'}).appendTo(this.element); this.element.on('keydown', function(evt) { - var selected = that._topList.find(".selected").parent().data('data'); - if (!selected && (evt.keyCode === 40 || evt.keyCode === 38)) { - that.select(that._data[0]); + var focussed = that._topList.find(".focus").parent().data('data'); + if (!focussed && (evt.keyCode === 40 || evt.keyCode === 38)) { + if (that._data[0]) { + if (autoSelect) { + that.select(that._data[0]); + } else { + that._topList.find(".focus").removeClass("focus") + } + that._data[0].treeList.label.addClass('focus') + } return; } var target; switch(evt.keyCode) { + case 32: // SPACE case 13: // ENTER evt.preventDefault(); evt.stopPropagation(); - if (selected.children) { - if (selected.treeList.container.hasClass("expanded")) { - selected.treeList.collapse() + if (focussed.checkbox) { + focussed.treeList.checkbox.trigger("click"); + } else if (focussed.radio) { + focussed.treeList.radio.trigger("click"); + } else if (focussed.children) { + if (focussed.treeList.container.hasClass("expanded")) { + focussed.treeList.collapse() } else { - selected.treeList.expand() + focussed.treeList.expand() } } else { - that._trigger("confirm",null,selected) + that._trigger("confirm",null,focussed) } - break; case 37: // LEFT evt.preventDefault(); evt.stopPropagation(); - if (selected.children&& selected.treeList.container.hasClass("expanded")) { - selected.treeList.collapse() - } else if (selected.parent) { - target = selected.parent; + if (focussed.children&& focussed.treeList.container.hasClass("expanded")) { + focussed.treeList.collapse() + } else if (focussed.parent) { + target = focussed.parent; } break; case 38: // UP evt.preventDefault(); evt.stopPropagation(); - target = that._getPreviousSibling(selected); + target = that._getPreviousSibling(focussed); if (target) { target = that._getLastDescendant(target); } - if (!target && selected.parent) { - target = selected.parent; + if (!target && focussed.parent) { + target = focussed.parent; } break; case 39: // RIGHT evt.preventDefault(); evt.stopPropagation(); - if (selected.children) { - if (!selected.treeList.container.hasClass("expanded")) { - selected.treeList.expand() + if (focussed.children) { + if (!focussed.treeList.container.hasClass("expanded")) { + focussed.treeList.expand() } } 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]; + if (focussed.children && Array.isArray(focussed.children) && focussed.children.length > 0 && focussed.treeList.container.hasClass("expanded")) { + target = focussed.children[0]; } else { - target = that._getNextSibling(selected); - while (!target && selected.parent) { - selected = selected.parent; - target = that._getNextSibling(selected); + target = that._getNextSibling(focussed); + while (!target && focussed.parent) { + focussed = focussed.parent; + target = that._getNextSibling(focussed); } } break } if (target) { - that.select(target); + if (autoSelect) { + that.select(target); + } else { + that._topList.find(".focus").removeClass("focus") + } + target.treeList.label.addClass('focus') } }); this._data = []; @@ -463,6 +486,9 @@ container.addClass("expanded"); } item.treeList.collapse = function() { + if (item.collapsible === false) { + return + } if (!item.children) { return; } @@ -583,7 +609,7 @@ // Already a parent because we've got the angle-right icon return; } - $('').appendTo(treeListIcon); + $('').toggleClass("hide",item.collapsible === false).appendTo(treeListIcon); treeListIcon.on("click.red-ui-treeList-expand", function(e) { e.stopPropagation(); e.preventDefault(); @@ -634,6 +660,8 @@ label.on("click", function(e) { e.stopPropagation(); cb.trigger("click"); + that._topList.find(".focus").removeClass("focus") + label.addClass('focus') }) } item.treeList.select = function(v) { @@ -641,6 +669,7 @@ cb.trigger("click"); } } + item.treeList.checkbox = cb; selectWrapper.appendTo(label) } else if (item.radio) { var selectWrapper = $(''); @@ -669,6 +698,8 @@ label.on("click", function(e) { e.stopPropagation(); cb.trigger("click"); + that._topList.find(".focus").removeClass("focus") + label.addClass('focus') }) } item.treeList.select = function(v) { @@ -677,6 +708,7 @@ } } selectWrapper.appendTo(label) + item.treeList.radio = cb; } else { label.on("click", function(e) { if (!that.options.multi) { @@ -684,10 +716,14 @@ } label.addClass("selected"); that._selected.add(item); + that._topList.find(".focus").removeClass("focus") + label.addClass('focus') that._trigger("select",e,item) }) label.on("dblclick", function(e) { + that._topList.find(".focus").removeClass("focus") + label.addClass('focus') if (!item.children) { that._trigger("confirm",e,item); } @@ -835,6 +871,9 @@ if (item.treeList.label) { item.treeList.label.addClass("selected"); } + + that._topList.find(".focus").removeClass("focus"); + if (triggerEvent !== false) { this._trigger("select",null,item) } @@ -842,6 +881,9 @@ clearSelection: function() { this._selected.forEach(function(item) { item.selected = false; + if (item.treeList.checkbox) { + item.treeList.checkbox.prop('checked',false) + } if (item.treeList.label) { item.treeList.label.removeClass("selected") } 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 52606cd88..3159622b6 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 @@ -287,11 +287,11 @@ RED.sidebar.info.outliner = (function() { 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]}) + // RED.view.select({nodes:[node]}) } else if (node._def.category === "config") { RED.sidebar.info.refresh(node); } else { - RED.view.select({nodes:[]}) + // RED.view.select({nodes:[]}) } } }) 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 89ad06ca2..72422f350 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 @@ -434,16 +434,19 @@ div.red-ui-info-table { } .red-ui-info-outline-item-controls { position: absolute; - top:0; - bottom: 0; - right: 0px; - padding: 2px 3px 0 1px; + top:1px; + bottom: 1px; + right: 1px; + padding: 1px 2px 0 1px; text-align: right; background: $list-item-background; .red-ui-treeList-label:hover & { background: $list-item-background-hover; } + .red-ui-treeList-label.focus & { + background: $list-item-background-hover; + } .red-ui-treeList-label.selected & { background: $list-item-background-selected; } diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss index 3621328d8..beba6b047 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss @@ -89,6 +89,12 @@ color: $list-item-color; text-decoration: none; } + &.focus, &.focus .red-ui-treeList-sublabel-text { + background: $list-item-background-hover; + outline: 1px solid $form-input-focus-color !important; + outline-offset: -1px; + color: $list-item-color; + } &.selected, &.selected .red-ui-treeList-sublabel-text { background: $list-item-background-selected; outline: none;