1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge pull request #3405 from Steve-Mcl/continuous-search

Add feature: Continuous search tools (search previous, search next)
This commit is contained in:
Nick O'Leary 2022-03-14 18:38:04 +00:00 committed by GitHub
commit a0acc89fcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 252 additions and 41 deletions

View File

@ -149,7 +149,11 @@
"toggle-navigator": "Toggle navigator", "toggle-navigator": "Toggle navigator",
"zoom-out": "Zoom out", "zoom-out": "Zoom out",
"zoom-reset": "Reset zoom", "zoom-reset": "Reset zoom",
"zoom-in": "Zoom in" "zoom-in": "Zoom in",
"search-flows": "Search flows",
"search-prev": "Previous",
"search-next": "Next",
"search-counter": "\"__term__\" __result__ of __count__"
}, },
"user": { "user": {
"loggedInAs": "Logged in as __name__", "loggedInAs": "Logged in as __name__",

View File

@ -91,6 +91,9 @@
"alt-a c": "core:align-selection-to-center", "alt-a c": "core:align-selection-to-center",
"alt-a h": "core:distribute-selection-horizontally", "alt-a h": "core:distribute-selection-horizontally",
"alt-a v": "core:distribute-selection-vertically", "alt-a v": "core:distribute-selection-vertically",
"shift-f": "core:search-previous",
"f": "core:search-next",
"alt-l l": "core:split-wire-with-link-nodes" "alt-l l": "core:split-wire-with-link-nodes"
} }
} }

View File

@ -25,6 +25,8 @@ RED.search = (function() {
var searchHistory = []; var searchHistory = [];
var index = {}; var index = {};
var currentResults = []; var currentResults = [];
var activeResults = [];
var currentIndex = 0;
var previousActiveElement; var previousActiveElement;
function indexProperty(node,label,property) { function indexProperty(node,label,property) {
@ -329,7 +331,8 @@ RED.search = (function() {
} }
} else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) { } else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) {
if (currentResults.length > 0) { if (currentResults.length > 0) {
reveal(currentResults[Math.max(0,selected)].node); currentIndex = Math.max(0,selected);
reveal(currentResults[currentIndex].node);
} }
} }
} }
@ -413,6 +416,7 @@ RED.search = (function() {
div.on("click", function(evt) { div.on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();
currentIndex = i;
reveal(node); reveal(node);
}); });
} }
@ -428,13 +432,59 @@ RED.search = (function() {
if (existingIndex > -1) { if (existingIndex > -1) {
searchHistory.splice(existingIndex,1); searchHistory.splice(existingIndex,1);
} }
searchHistory.unshift(searchInput.val()); searchHistory.unshift(searchVal);
hide(); $("#red-ui-view-searchtools-search").data("term", searchVal);
activeResults = Object.assign([], currentResults);
hide(null, activeResults.length > 0);
RED.view.reveal(node.id); RED.view.reveal(node.id);
} }
function revealPrev() {
if (disabled) {
updateSearchToolbar();
return;
}
if (!searchResults || !activeResults.length) {
show();
return;
}
if (currentIndex > 0) {
currentIndex--;
} else {
currentIndex = activeResults.length - 1;
}
const n = activeResults[currentIndex];
if (n && n.node && n.node.id) {
RED.view.reveal(n.node.id);
$("#red-ui-view-searchtools-prev").trigger("focus");
}
updateSearchToolbar();
}
function revealNext() {
if (disabled) {
updateSearchToolbar();
return;
}
if (!searchResults || !activeResults.length) {
show();
return;
}
if (currentIndex < activeResults.length - 1) {
currentIndex++
} else {
currentIndex = 0;
}
const n = activeResults[currentIndex];
if (n && n.node && n.node.id) {
RED.view.reveal(n.node.id);
$("#red-ui-view-searchtools-next").trigger("focus");
}
updateSearchToolbar();
}
function show(v) { function show(v) {
if (disabled) { if (disabled) {
updateSearchToolbar();
return; return;
} }
if (!visible) { if (!visible) {
@ -461,7 +511,7 @@ RED.search = (function() {
searchInput.trigger("focus"); searchInput.trigger("focus");
} }
function hide() { function hide(el, keepSearchToolbar) {
if (visible) { if (visible) {
visible = false; visible = false;
$("#red-ui-header-shade").hide(); $("#red-ui-header-shade").hide();
@ -475,13 +525,37 @@ RED.search = (function() {
}); });
} }
RED.events.emit("search:close"); RED.events.emit("search:close");
if (previousActiveElement) { if (previousActiveElement && (!keepSearchToolbar || !activeResults.length)) {
$(previousActiveElement).trigger("focus"); $(previousActiveElement).trigger("focus");
previousActiveElement = null;
} }
previousActiveElement = null;
}
if(!keepSearchToolbar) {
clearActiveSearch();
}
updateSearchToolbar();
if(keepSearchToolbar && activeResults.length) {
$("#red-ui-view-searchtools-next").trigger("focus");
}
}
function updateSearchToolbar() {
if (!disabled && currentIndex >= 0 && activeResults && activeResults.length) {
let term = $("#red-ui-view-searchtools-search").data("term") || "";
if (term.length > 16) {
term = term.substring(0, 12) + "..."
}
const i18nSearchCounterData = {
term: term,
result: (currentIndex + 1),
count: activeResults.length
}
$("#red-ui-view-searchtools-counter").text(RED._('actions.search-counter', i18nSearchCounterData));
$("#view-search-tools > :not(:first-child)").show(); //show other tools
} else {
clearActiveSearch();
$("#view-search-tools > :not(:first-child)").hide(); //hide all but search button
} }
} }
function clearIndex() { function clearIndex() {
index = {}; index = {};
} }
@ -503,6 +577,12 @@ RED.search = (function() {
addItemToIndex(item); addItemToIndex(item);
} }
function clearActiveSearch() {
activeResults = [];
currentIndex = 0;
$("#red-ui-view-searchtools-search").data("term", "");
}
function getSearchOptions() { function getSearchOptions() {
return [ return [
{label:RED._("search.options.configNodes"), value:"is:config"}, {label:RED._("search.options.configNodes"), value:"is:config"},
@ -513,10 +593,13 @@ RED.search = (function() {
{label:RED._("search.options.unusedSubflows"), value:"is:subflow is:unused"}, {label:RED._("search.options.unusedSubflows"), value:"is:subflow is:unused"},
{label:RED._("search.options.hiddenFlows"), value:"is:hidden"}, {label:RED._("search.options.hiddenFlows"), value:"is:hidden"},
] ]
} }
function init() { function init() {
RED.actions.add("core:search",show); RED.actions.add("core:search",show);
RED.actions.add("core:search-previous",revealPrev);
RED.actions.add("core:search-next",revealNext);
RED.events.on("editor:open",function() { disabled = true; }); RED.events.on("editor:open",function() { disabled = true; });
RED.events.on("editor:close",function() { disabled = false; }); RED.events.on("editor:close",function() { disabled = false; });
@ -527,11 +610,21 @@ RED.search = (function() {
RED.keyboard.add("red-ui-search","escape",hide); RED.keyboard.add("red-ui-search","escape",hide);
RED.keyboard.add("view-search-tools","escape",function() {
clearActiveSearch();
updateSearchToolbar();
});
$("#red-ui-header-shade").on('mousedown',hide); $("#red-ui-header-shade").on('mousedown',hide);
$("#red-ui-editor-shade").on('mousedown',hide); $("#red-ui-editor-shade").on('mousedown',hide);
$("#red-ui-palette-shade").on('mousedown',hide); $("#red-ui-palette-shade").on('mousedown',hide);
$("#red-ui-sidebar-shade").on('mousedown',hide); $("#red-ui-sidebar-shade").on('mousedown',hide);
$("#red-ui-view-searchtools-close").on("click", function close() {
clearActiveSearch();
updateSearchToolbar();
});
$("#red-ui-view-searchtools-close").trigger("click");
RED.events.on("workspace:clear", clearIndex); RED.events.on("workspace:clear", clearIndex);

View File

@ -31,6 +31,7 @@ RED.statusBar = (function() {
function addWidget(options) { function addWidget(options) {
widgets[options.id] = options; widgets[options.id] = options;
var el = $('<span class="red-ui-statusbar-widget"></span>'); var el = $('<span class="red-ui-statusbar-widget"></span>');
el.prop('id', options.id);
options.element.appendTo(el); options.element.appendTo(el);
if (options.align === 'left') { if (options.align === 'left') {
leftBucket.append(el); leftBucket.append(el);

View File

@ -15,6 +15,8 @@
**/ **/
RED.sidebar.config = (function() { RED.sidebar.config = (function() {
let flashingConfigNode;
let flashingConfigNodeTimer;
var content = document.createElement("div"); var content = document.createElement("div");
content.className = "red-ui-sidebar-node-config"; content.className = "red-ui-sidebar-node-config";
@ -145,6 +147,7 @@ RED.sidebar.config = (function() {
var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list); var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry); var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry);
entry.data('node',node.id); entry.data('node',node.id);
nodeDiv.data('node',node.id);
var label = $('<div class="red-ui-palette-label"></div>').text(label).appendTo(nodeDiv); var label = $('<div class="red-ui-palette-label"></div>').text(label).appendTo(nodeDiv);
if (node.d) { if (node.d) {
nodeDiv.addClass("red-ui-palette-node-config-disabled"); nodeDiv.addClass("red-ui-palette-node-config-disabled");
@ -350,6 +353,32 @@ RED.sidebar.config = (function() {
RED.popover.tooltip($('#red-ui-sidebar-config-filter-unused'), RED._("sidebar.config.showAllUnusedConfigNodes")); RED.popover.tooltip($('#red-ui-sidebar-config-filter-unused'), RED._("sidebar.config.showAllUnusedConfigNodes"));
} }
function flashConfigNode(el) {
if(flashingConfigNode && flashingConfigNode.length) {
//cancel current flashing node before flashing new node
clearInterval(flashingConfigNodeTimer);
flashingConfigNodeTimer = null;
flashingConfigNode.children("div").removeClass('highlighted');
flashingConfigNode = null;
}
if(!el || !el.children("div").length) { return; }
flashingConfigNodeTimer = setInterval(function(flashEndTime) {
if (flashEndTime >= Date.now()) {
const highlighted = el.children("div").hasClass("highlighted");
el.children("div").toggleClass('highlighted', !highlighted)
} else {
clearInterval(flashingConfigNodeTimer);
flashingConfigNodeTimer = null;
flashingConfigNode = null;
el.children("div").removeClass('highlighted');
}
}, 100, Date.now() + 2200);
flashingConfigNode = el;
el.children("div").addClass('highlighted');
}
function show(id) { function show(id) {
if (typeof id === 'boolean') { if (typeof id === 'boolean') {
if (id) { if (id) {
@ -374,19 +403,7 @@ RED.sidebar.config = (function() {
} else if (y<0) { } else if (y<0) {
scrollWindow.animate({scrollTop: '+='+(y-10)},150); scrollWindow.animate({scrollTop: '+='+(y-10)},150);
} }
var flash = 21; flashConfigNode(node, id);
var flashFunc = function() {
if ((flash%2)===0) {
node.removeClass('node_highlighted');
} else {
node.addClass('node_highlighted');
}
flash--;
if (flash >= 0) {
setTimeout(flashFunc,100);
}
}
flashFunc();
},100); },100);
} }
RED.sidebar.show("config"); RED.sidebar.show("config");

View File

@ -92,6 +92,9 @@ RED.view = (function() {
var lastClickPosition = []; var lastClickPosition = [];
var selectNodesOptions; var selectNodesOptions;
let flashingNodeId;
let flashingNodeTimer;
var clipboard = ""; var clipboard = "";
// Note: these are the permitted status colour aliases. The actual RGB values // Note: these are the permitted status colour aliases. The actual RGB values
@ -452,6 +455,33 @@ RED.view = (function() {
} }
}); });
//add search to status-toolbar
RED.statusBar.add({
id: "view-search-tools",
align: "left",
hidden: false,
element: $('<span class="button-group">'+
'<button class="red-ui-footer-button" id="red-ui-view-searchtools-search"><i class="fa fa-search"></i></button>' +
'</span>' +
'<span class="button-group search-counter">' +
'<span class="red-ui-footer-button" id="red-ui-view-searchtools-counter">? of ?</span>' +
'</span>' +
'<span class="button-group">' +
'<button class="red-ui-footer-button" id="red-ui-view-searchtools-prev"><i class="fa fa-chevron-left"></i></button>' +
'<button class="red-ui-footer-button" id="red-ui-view-searchtools-next"><i class="fa fa-chevron-right"></i></button>' +
'</span>' +
'<span class="button-group">' +
'<button class="red-ui-footer-button" id="red-ui-view-searchtools-close"><i class="fa fa-close"></i></button>' +
'</span>')
})
$("#red-ui-view-searchtools-search").on("click", searchFlows);
RED.popover.tooltip($("#red-ui-view-searchtools-search"),RED._('actions.search-flows'),'core:search');
$("#red-ui-view-searchtools-prev").on("click", searchPrev);
RED.popover.tooltip($("#red-ui-view-searchtools-prev"),RED._('actions.search-prev'),'core:search-previous');
$("#red-ui-view-searchtools-next").on("click", searchNext);
RED.popover.tooltip($("#red-ui-view-searchtools-next"),RED._('actions.search-next'),'core:search-next');
RED.popover.tooltip($("#red-ui-view-searchtools-close"),RED._('common.label.close'));
// Handle nodes dragged from the palette // Handle nodes dragged from the palette
chart.droppable({ chart.droppable({
accept:".red-ui-palette-node", accept:".red-ui-palette-node",
@ -2032,6 +2062,9 @@ RED.view = (function() {
} }
} }
function zoomZero() { zoomView(1); } function zoomZero() { zoomView(1); }
function searchFlows() { RED.actions.invoke("core:search", $(this).data("term")); }
function searchPrev() { RED.actions.invoke("core:search-previous"); }
function searchNext() { RED.actions.invoke("core:search-next"); }
function zoomView(factor) { function zoomView(factor) {
@ -5805,6 +5838,37 @@ RED.view = (function() {
return result; return result;
} }
function flashNode(n) {
let node = n;
if(typeof node === "string") { node = RED.nodes.node(n); }
if(!node) { return; }
const flashingNode = flashingNodeTimer && flashingNodeId && RED.nodes.node(flashingNodeId);
if(flashingNode) {
//cancel current flashing node before flashing new node
clearInterval(flashingNodeTimer);
flashingNodeTimer = null;
flashingNode.dirty = true;
flashingNode.highlighted = false;
}
flashingNodeTimer = setInterval(function(flashEndTime) {
node.dirty = true;
if (flashEndTime >= Date.now()) {
node.highlighted = !node.highlighted;
} else {
clearInterval(flashingNodeTimer);
flashingNodeTimer = null;
node.highlighted = false;
flashingNodeId = null;
}
RED.view.redraw();
}, 100, Date.now() + 2200)
flashingNodeId = node.id;
node.highlighted = true;
RED.view.redraw();
}
return { return {
init: init, init: init,
state:function(state) { state:function(state) {
@ -5901,7 +5965,7 @@ RED.view = (function() {
getActiveGroup: function() { return activeGroup }, getActiveGroup: function() { return activeGroup },
reveal: function(id,triggerHighlight) { reveal: function(id,triggerHighlight) {
if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) { if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) {
RED.workspaces.show(id); RED.workspaces.show(id, null, null, true);
} else { } else {
var node = RED.nodes.node(id) || RED.nodes.group(id); var node = RED.nodes.node(id) || RED.nodes.group(id);
if (node) { if (node) {
@ -5926,24 +5990,7 @@ RED.view = (function() {
},200); },200);
} }
if (triggerHighlight !== false) { if (triggerHighlight !== false) {
node.highlighted = true; flashNode(node);
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') { } else if (node._def.category === 'config') {
RED.sidebar.config.show(id); RED.sidebar.config.show(id);

View File

@ -24,6 +24,9 @@ RED.workspaces = (function() {
var hideStack = []; var hideStack = [];
var viewStackPos = 0; var viewStackPos = 0;
let flashingTab;
let flashingTabTimer;
function addToViewStack(id) { function addToViewStack(id) {
if (viewStackPos !== viewStack.length) { if (viewStackPos !== viewStack.length) {
viewStack.splice(viewStackPos); viewStack.splice(viewStackPos);
@ -531,6 +534,31 @@ RED.workspaces = (function() {
workspace_tabs.order(order); workspace_tabs.order(order);
} }
function flashTab(tabId) {
if(flashingTab && flashingTab.length) {
//cancel current flashing node before flashing new node
clearInterval(flashingTabTimer);
flashingTabTimer = null;
flashingTab.removeClass('highlighted');
flashingTab = null;
}
let tab = $("#red-ui-tab-" + tabId);
if(!tab || !tab.length) { return; }
flashingTabTimer = setInterval(function(flashEndTime) {
if (flashEndTime >= Date.now()) {
const highlighted = tab.hasClass("highlighted");
tab.toggleClass('highlighted', !highlighted)
} else {
clearInterval(flashingTabTimer);
flashingTabTimer = null;
flashingTab = null;
tab.removeClass('highlighted');
}
}, 100, Date.now() + 2200);
flashingTab = tab;
tab.addClass('highlighted');
}
return { return {
init: init, init: init,
add: addWorkspace, add: addWorkspace,
@ -563,7 +591,7 @@ RED.workspaces = (function() {
isHidden: function(id) { isHidden: function(id) {
return hideStack.includes(id) return hideStack.includes(id)
}, },
show: function(id,skipStack,unhideOnly) { show: function(id,skipStack,unhideOnly,flash) {
if (!workspace_tabs.contains(id)) { if (!workspace_tabs.contains(id)) {
var sf = RED.nodes.subflow(id); var sf = RED.nodes.subflow(id);
if (sf) { if (sf) {
@ -585,6 +613,9 @@ RED.workspaces = (function() {
} }
workspace_tabs.activateTab(id); workspace_tabs.activateTab(id);
} }
if(flash) {
flashTab(id.replace(".","-"))
}
}, },
refresh: function() { refresh: function() {
RED.nodes.eachWorkspace(function(ws) { RED.nodes.eachWorkspace(function(ws) {

View File

@ -42,6 +42,10 @@ ul.red-ui-sidebar-node-config-list {
border-color: transparent; border-color: transparent;
box-shadow: 0 0 0 2px $node-selected-color; box-shadow: 0 0 0 2px $node-selected-color;
} }
&.highlighted {
border-color: transparent;
outline: dashed $node-selected-color 4px;
}
} }
.red-ui-palette-label { .red-ui-palette-label {
margin-left: 8px; margin-left: 8px;

View File

@ -85,6 +85,10 @@
&:not(.active) a:hover+a.red-ui-tab-close { &:not(.active) a:hover+a.red-ui-tab-close {
background: $tab-background-hover; background: $tab-background-hover;
} }
&.highlighted {
box-shadow: 0px 0px 4px 2px $node-selected-color;
border: dashed 1px $node-selected-color;
}
&.active { &.active {
background: $tab-background-active; background: $tab-background-active;
font-weight: bold; font-weight: bold;

View File

@ -135,6 +135,13 @@
margin-top: -1px; margin-top: -1px;
} }
} }
.search-counter {
display: inline-block;
font-size: smaller;
font-weight: 600;
white-space: nowrap;
}
} }
a.red-ui-footer-button, a.red-ui-footer-button,