From 18c8bbb0fcae2a2aa1b4c93f680ae590e6a530a7 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 29 Sep 2016 23:46:29 +0100 Subject: [PATCH] Add workspace search option --- Gruntfile.js | 1 + editor/icons/cog.png | Bin 0 -> 493 bytes editor/js/main.js | 3 + editor/js/nodes.js | 9 +- editor/js/ui/keyboard.js | 1 + editor/js/ui/search.js | 287 ++++++++++++++++++++++++++++++ editor/js/ui/tab-config.js | 36 +++- editor/js/ui/view.js | 40 +++++ editor/sass/flow.scss | 2 + editor/sass/search.scss | 106 +++++++++++ editor/sass/style.scss | 1 + red/api/locales/en-US/editor.json | 9 +- 12 files changed, 489 insertions(+), 6 deletions(-) create mode 100644 editor/icons/cog.png create mode 100644 editor/js/ui/search.js create mode 100644 editor/sass/search.scss diff --git a/Gruntfile.js b/Gruntfile.js index 93047c202..2c5da4ab1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -130,6 +130,7 @@ module.exports = function(grunt) { "editor/js/ui/clipboard.js", "editor/js/ui/library.js", "editor/js/ui/notifications.js", + "editor/js/ui/search.js", "editor/js/ui/subflow.js", "editor/js/ui/touch/radialMenu.js" ], diff --git a/editor/icons/cog.png b/editor/icons/cog.png new file mode 100644 index 0000000000000000000000000000000000000000..e84cb16b22f345cd1caca2fb382346fdce928c11 GIT binary patch literal 493 zcmVK4+9P<=hY4@e zqf|s)3!~`N3v?}#tO!v9`#QM7xc&c`9>al!WoPDFW_~*>&24T1PJuS?3Y^UX$N)LN zPXT@4)2}{|^1d99nFQMdo`5cJ1XK-dw5s0M^|3|Ea0Zr@)RdH*vZxnHMd#duBrO@o zvZU3Bgj-2BeqWHZ6ZqF8twdf0)Jha=#nq=7l#{R7a(3?z-W2}l4HBT6Zb z1RNMqDnKGGuwk0q0?IKk`)nH4Zzp9-!w0+=Fzz%I$@wxv0SA%_&bfPOd{RCny=#2* zzBE)XADme+-+XRl$wfc#9pKRSC;S{L-q!&hf!_rLvcQJluLC^;>j6pc+no4)#awXW jKi4*JJT+i$|Ioexr'+ ''+ ''+ + ''+ ''+ ''+ ''+ diff --git a/editor/js/ui/search.js b/editor/js/ui/search.js new file mode 100644 index 000000000..177ecdd39 --- /dev/null +++ b/editor/js/ui/search.js @@ -0,0 +1,287 @@ +/** + * Copyright 2013, 2016 IBM Corp. + * + * 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.search = (function() { + + + var dialog = null; + var searchInput; + var searchResults; + var selected = -1; + + var index = {}; + var keys = []; + var results = []; + + function indexNode(n) { + var l = ""; + if (n._def && n._def.label) { + l = n._def.label; + try { + l = (typeof l === "function" ? l.call(n) : l); + if (l) { + l = (""+l).toLowerCase(); + index[l] = index[l] || {}; + index[l][n.id] = {node:n,label:l} + } + } catch(err) { + console.log("Definition error: "+n.type+".label",err); + } + } + l = l||n.label||n.name||n.id||""; + + + var properties = ['id','type','name','label','info']; + for (var i=0;i 0) { + val = val.toLowerCase(); + var i; + var j; + var list = []; + var nodes = {}; + for (i=0;i -1) { + for (j=0;j 0) { + for (i=0;i scrollHeight) { + scrollWindow.animate({scrollTop: '-='+(scrollHeight-(y+h)-10)},50); + } else if (y<0) { + scrollWindow.animate({scrollTop: '+='+(y-10)},50); + } + } + + function createDialog() { + dialog = $("
",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#main-container"); + var searchDiv = $("
",{class:"red-ui-search-container"}).appendTo(dialog); + searchInput = $('').appendTo(searchDiv).searchBox({ + delay: 200, + change: function() { + search($(this).val()); + } + }); + searchInput.on('keydown',function(evt) { + var children; + if (results.length > 0) { + if (evt.keyCode === 40) { + // Down + children = searchResults.children(); + if (selected < children.length-1) { + if (selected > -1) { + $(children[selected]).removeClass('selected'); + } + selected++; + } + $(children[selected]).addClass('selected'); + ensureSelectedIsVisible(); + evt.preventDefault(); + } else if (evt.keyCode === 38) { + // Up + children = searchResults.children(); + if (selected > 0) { + if (selected < children.length) { + $(children[selected]).removeClass('selected'); + } + selected--; + } + $(children[selected]).addClass('selected'); + ensureSelectedIsVisible(); + evt.preventDefault(); + } else if (evt.keyCode === 13) { + // Enter + if (results.length > 0) { + reveal(results[Math.max(0,selected)].node); + } + } + } + }); + + var searchResultsDiv = $("
",{class:"red-ui-search-results-container"}).appendTo(dialog); + searchResults = $('
    ',{id:"search-result-list", style:"position: absolute;top: 5px;bottom: 5px;left: 5px;right: 5px;"}).appendTo(searchResultsDiv).editableList({ + addButton: false, + addItem: function(container,i,object) { + var node = object.node; + if (node === undefined) { + $('
    ',{class:"red-ui-search-empty"}).html(RED._('search.empty')).appendTo(container); + + } else { + var def = node._def; + var div = $('',{href:'#',class:"red-ui-search-result"}).appendTo(container); + + var nodeDiv = $('
    ',{class:"red-ui-search-result-node"}).appendTo(div); + var colour = def.color; + var icon_url = "arrow-in.png"; + if (node.type === 'tab') { + colour = "#C0DEED"; + icon_url = "subflow.png"; + } else if (def.category === 'config') { + icon_url = "cog.png"; + } else if (node.type === 'unknown') { + icon_url = "alert.png"; + } else { + try { + icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon); + } catch(err) { + console.log("Definition error: "+nt+".icon",err); + } + } + nodeDiv.css('backgroundColor',colour); + + var iconContainer = $('
    ',{class:"palette_icon_container"}).appendTo(nodeDiv); + $('
    ',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer); + + var contentDiv = $('
    ',{class:"red-ui-search-result-description"}).appendTo(div); + if (node.z) { + var workspace = RED.nodes.workspace(node.z); + if (!workspace) { + workspace = RED.nodes.subflow(node.z); + workspace = "subflow:"+workspace.name; + } else { + workspace = "flow:"+workspace.label; + } + $('
    ',{class:"red-ui-search-result-node-flow"}).html(workspace).appendTo(contentDiv); + } + + $('
    ',{class:"red-ui-search-result-node-label"}).html(object.label || node.id).appendTo(contentDiv); + $('
    ',{class:"red-ui-search-result-node-type"}).html(node.type).appendTo(contentDiv); + $('
    ',{class:"red-ui-search-result-node-id"}).html(node.id).appendTo(contentDiv); + + div.click(function(evt) { + evt.preventDefault(); + reveal(node); + }); + } + }, + scrollOnAdd: false + }); + + } + function reveal(node) { + hide(); + RED.view.reveal(node.id); + // if (node.type === 'tab' || node.type === 'subflow') { + // RED.workspaces.show(node.id); + // } else { + // if (node._def.category !== 'config' && node.z) { + // RED.workspaces.show(node.z); + // } + // } + } + + var visible = false; + function show() { + if (!visible) { + RED.keyboard.add("*",/* ESCAPE */ 27,function(){hide();d3.event.preventDefault();}); + $("#header-shade").show(); + $("#editor-shade").show(); + $("#palette-shade").show(); + $("#sidebar-shade").show(); + indexWorkspace(); + if (dialog === null) { + createDialog(); + } + dialog.slideDown(); + visible = true; + } + searchInput.focus(); + } + function hide() { + if (visible) { + RED.keyboard.remove(/* ESCAPE */ 27); + visible = false; + $("#header-shade").hide(); + $("#editor-shade").hide(); + $("#palette-shade").hide(); + $("#sidebar-shade").hide(); + if (dialog !== null) { + dialog.slideUp(200,function() { + searchInput.searchBox('value',''); + }); + } + } + } + + function init() { + RED.keyboard.add("*",/* . */ 190,{ctrl:true},function(){show();d3.event.preventDefault();}); + } + + return { + init: init, + show: show, + hide: hide + }; + +})(); diff --git a/editor/js/ui/tab-config.js b/editor/js/ui/tab-config.js index c1bfaf943..76fea9810 100644 --- a/editor/js/ui/tab-config.js +++ b/editor/js/ui/tab-config.js @@ -149,7 +149,7 @@ RED.sidebar.config = (function() { currentType = node.type; } - var entry = $('
  1. ').appendTo(list); + var entry = $('
  2. ').appendTo(list); $('
    ').text(label).appendTo(entry); var iconContainer = $('
    ',{class:"palette_icon_container palette_icon_container_right"}).text(node.users.length).appendTo(entry); @@ -280,8 +280,8 @@ RED.sidebar.config = (function() { } - function show(unused) { - if (unused !== undefined) { + function show(id) { + if (typeof id === 'boolean') { if (unused) { $('#workspace-config-node-filter-unused').click(); } else { @@ -289,6 +289,36 @@ RED.sidebar.config = (function() { } } refreshConfigNodeList(); + if (typeof id === "string") { + $('#workspace-config-node-filter-all').click(); + id = id.replace(/\./g,"-"); + setTimeout(function() { + var node = $(".palette_node_id_"+id); + var y = node.position().top; + var h = node.height(); + var scrollWindow = $(".sidebar-node-config"); + var scrollHeight = scrollWindow.height(); + + if (y+h > scrollHeight) { + scrollWindow.animate({scrollTop: '-='+(scrollHeight-(y+h)-30)},150); + } else if (y<0) { + scrollWindow.animate({scrollTop: '+='+(y-10)},150); + } + var flash = 21; + 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); + } RED.sidebar.show("config"); } return { diff --git a/editor/js/ui/view.js b/editor/js/ui/view.js index f0cbc6d79..8a34731f3 100644 --- a/editor/js/ui/view.js +++ b/editor/js/ui/view.js @@ -2385,6 +2385,46 @@ RED.view = (function() { } } return result; + }, + reveal: function(id) { + if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) { + 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); + RED.view.redraw(); + + var screenSize = [$("#chart").width(),$("#chart").height()]; + var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()]; + + 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); + var deltaY = '-='+((scrollPos[1] - node.y) + screenSize[1]/2); + $("#chart").animate({ + scrollLeft: deltaX, + scrollTop: deltaY + },200); + } + + var flash = 22; + var flashFunc = function() { + flash--; + node.highlighted = !node.highlighted; + node.dirty = true; + RED.view.redraw(); + if (flash >= 0) { + setTimeout(flashFunc,100); + } + } + flashFunc(); + } else if (node._def.category === 'config') { + RED.sidebar.config.show(id); + } + } } + }; })(); diff --git a/editor/sass/flow.scss b/editor/sass/flow.scss index 42f1a473b..88dad4622 100644 --- a/editor/sass/flow.scss +++ b/editor/sass/flow.scss @@ -157,6 +157,8 @@ stroke: $node-selected-color !important; } .node_highlighted { + border-color: #dd1616 !important; + border-style: dashed !important; stroke: #dd1616; stroke-width: 2; stroke-dasharray: 10, 4; diff --git a/editor/sass/search.scss b/editor/sass/search.scss new file mode 100644 index 000000000..52386529c --- /dev/null +++ b/editor/sass/search.scss @@ -0,0 +1,106 @@ +/** + * Copyright 2016 IBM Corp. + * + * 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-ui-search { + z-index:1000; + display: none; + position: absolute; + width: 500px; + background: white; + left: 50%; + margin-left: -250px; + top: 0px; + border: 1px solid $primary-border-color; + box-shadow: 0 0 10px rgba(0,0,0,0.4); + + ol { + } +} +.red-ui-search-container { + padding: 3px; + border-bottom: 1px solid $secondary-border-color; +} +.red-ui-search-results-container { + position:relative; + height: 300px; + padding: 5px; + background: $background-color; + .red-ui-editableList-container { + background: white; + border-radius: 2px; + padding: 0; + background: $background-color; + li { + padding: 0; + + &.selected .red-ui-search-result { + border-color: $primary-border-color; + } + + } + } +} +.red-ui-search-result { + padding: 8px 2px 8px 5px; + display: block; + cursor: pointer; + color: $form-text-color; + border: 2px solid white; + &:hover { + text-decoration: none; + border-color: $primary-border-color; + color: $form-text-color; + } +} + +.red-ui-search-result-node { + display: inline-block; + width: 30px; + float:left; + height: 25px; + background: #ddd; + border-radius: 5px; + border: 1px solid #999; + background-position: 5% 50%; + background-repeat: no-repeat; + background-size: contain; + position: relative; +} +.red-ui-search-result-description { + margin-left: 40px; + margin-right: 5px; +} +.red-ui-search-result-node-label { + font-weight: bold; +} +.red-ui-search-result-node-type { + font-style: italic; +} +.red-ui-search-result-node-flow { + float:right; + font-size: 0.9em; +} +.red-ui-search-result-node-id { + display:none; + font-size: 0.9em; +} +.red-ui-search-empty { + padding: 10px; + text-align: center; + font-style: italic; + background: $background-color; + color: $form-placeholder-color; +} diff --git a/editor/sass/style.scss b/editor/sass/style.scss index a38c7eae8..6b2e738be 100644 --- a/editor/sass/style.scss +++ b/editor/sass/style.scss @@ -34,6 +34,7 @@ @import "editor"; @import "library"; +@import "search"; @import "tabs"; @import "tab-config"; diff --git a/red/api/locales/en-US/editor.json b/red/api/locales/en-US/editor.json index aa0d202df..d157b7675 100644 --- a/red/api/locales/en-US/editor.json +++ b/red/api/locales/en-US/editor.json @@ -39,6 +39,7 @@ "displayConfig": "Configuration nodes", "import": "Import", "export": "Export", + "search": "Find", "clipboard": "Clipboard", "library": "Library", "examples": "Examples", @@ -164,7 +165,7 @@ "addRemoveNode": "Add/remove node from selection", "deleteSelected": "Delete selected nodes or link", "importNode": "Import nodes", - "exportNode": "Export selected nodes", + "exportNode": "Export nodes", "nudgeNode": "Move selected node(s) by a small amount", "moveNode": "Move selected node(s) by a large amount", "toggleSidebar": "Toggle sidebar", @@ -172,7 +173,8 @@ "copyNode": "Copy selected nodes", "cutNode": "Cut selected nodes", "pasteNode": "Paste nodes", - "undoChange": "Undo the last change performed" + "undoChange": "Undo the last change performed", + "searchBox": "Open search box" }, "library": { "openLibrary": "Open Library...", @@ -305,5 +307,8 @@ }, "editableList": { "add": "add" + }, + "search": { + "empty": "No matches found" } }
Ctrl/⌘ + Space'+RED._("keyboard.toggleSidebar")+'
Ctrl/⌘ + .'+RED._("keyboard.searchBox")+'
Delete'+RED._("keyboard.deleteSelected")+'
Backspace