From fa6d7b69c7fb5a11c90da3bbefbf29e078518134 Mon Sep 17 00:00:00 2001 From: viliusle Date: Mon, 21 Jun 2021 23:38:55 +0300 Subject: [PATCH] #204 - global search --- package-lock.json | 7 +- package.json | 1 + src/css/component.css | 21 ++++ src/js/config-menu.js | 6 ++ src/js/core/base-search.js | 166 +++++++++++++++++++++++++++++++ src/js/libs/popup.js | 4 +- src/js/main.js | 2 + src/js/modules/help/shortcuts.js | 2 + src/js/modules/tools/search.js | 15 +++ 9 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 src/js/core/base-search.js create mode 100644 src/js/modules/tools/search.js diff --git a/package-lock.json b/package-lock.json index 6015a0b..4dca4ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "miniPaint", - "version": "4.7.1", + "version": "4.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2937,6 +2937,11 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "fuzzysort": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.1.4.tgz", + "integrity": "sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", diff --git a/package.json b/package.json index 66248d3..8fb2425 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "blueimp-canvas-to-blob": "^3.28.0", "exif-js": "^2.3.0", "file-saver": "^2.0.5", + "fuzzysort": "^1.1.4", "gif.js.optimized": "^1.0.1", "hermite-resize": "git+https://github.com/viliusle/Hermite-resize.git", "jquery": "^3.5.1", diff --git a/src/css/component.css b/src/css/component.css index 6efeb02..6791eb1 100644 --- a/src/css/component.css +++ b/src/css/component.css @@ -709,3 +709,24 @@ button img{ background-color: var(--background-color-active); color: var(--text-color-active); } + +/* global search */ +#global_search_results{ + padding-top: 10px; + font-size: 14px; +} +#global_search_results .search-result { + padding: 3px 5px; +} +#global_search_results .search-result.active{ + background-color: var(--background-color-active); + color: var(--text-color-active); + border-radius: 2px; +} +#global_search_results b{ + color: var(--text-color-red); +} + +.popup.shortcuts table{ + line-height: 1; +} \ No newline at end of file diff --git a/src/js/config-menu.js b/src/js/config-menu.js index 97f95af..a5f8bff 100644 --- a/src/js/config-menu.js +++ b/src/js/config-menu.js @@ -722,6 +722,12 @@ const menuDefinition = [ } ] }, + { + name: 'Search', + shortcut: 'F3', + ellipsis: true, + target: 'tools/search.search' + }, { name: 'Settings', ellipsis: true, diff --git a/src/js/core/base-search.js b/src/js/core/base-search.js new file mode 100644 index 0000000..256d088 --- /dev/null +++ b/src/js/core/base-search.js @@ -0,0 +1,166 @@ +/* + * miniPaint - https://github.com/viliusle/miniPaint + * author: Vilius L. + */ + +import config from './../config.js'; +import Dialog_class from './../libs/popup.js'; +import Base_gui_class from './base-gui.js'; +const fuzzysort = require('fuzzysort'); + +var instance = null; + +class Base_search_class { + + constructor() { + //singleton + if (instance) { + return instance; + } + instance = this; + + this.POP = new Dialog_class(); + this.Base_gui = new Base_gui_class(); + this.db = null; + + this.events(); + } + + events() { + document.addEventListener('keydown', (event) => { + if (this.POP.get_active_instances() > 0) { + return; + } + + var code = event.key.toLowerCase(); + if (code == "f3") { + //open + this.search(); + event.preventDefault(); + } + }, false); + + document.addEventListener('input', (event) => { + if(document.querySelector('#pop_data_search') == null){ + return; + } + + var node = document.querySelector('#global_search_results'); + node.innerHTML = ''; + + var query = event.target.value; + if(query == ''){ + return; + } + + let results = fuzzysort.go(query, this.db, { + keys: ['title'], + limit: 10, + threshold: -50000, + }); + + //show + for(var i = 0; i < results.length; i++) { + var item = results[i]; + + var className = "search-result n" + (i+1); + if(i == 0){ + className += " active"; + } + + node.innerHTML += "
" + + fuzzysort.highlight(item[0]) + "
"; + } + }, false); + + //allow to select with arrow keys + document.addEventListener('keydown', function (e) { + if(document.querySelector('#global_search_results') == null + || document.querySelector('.search-result') == null){ + return; + } + var k = e.key; + + if (k == "ArrowUp") { + var target = document.querySelector('.search-result.active'); + var index = Array.from(target.parentNode.children).indexOf(target); + if(index > 0){ + index--; + } + target.classList.remove('active'); + var target2 =document.querySelector('#global_search_results').childNodes[index]; + target2.classList.add('active'); + e.preventDefault(); + } + else if (k == "ArrowDown") { + var target = document.querySelector('.search-result.active'); + var index = Array.from(target.parentNode.children).indexOf(target); + var total = target.parentNode.childElementCount; + if(index < total - 1){ + index++; + } + target.classList.remove('active'); + var target2 = document.querySelector('#global_search_results').childNodes[index]; + target2.classList.add('active'); + e.preventDefault(); + } + + }, false); + } + + search() { + var _this = this; + + //init DB + if(this.db === null) { + this.db = Object.keys(this.Base_gui.modules); + for(var i in this.db){ + this.db[i] = { + key: this.db[i], + title: this.db[i].replace(/_/i, ' '), + }; + } + } + + var settings = { + title: 'Search', + params: [ + {name: "search", title: "Search:", value: ""}, + ], + on_load: function (params, popup) { + var node = document.createElement("div"); + node.id = 'global_search_results'; + node.innerHTML = ''; + popup.el.querySelector('.dialog_content').appendChild(node); + }, + on_finish: function (params) { + //execute + var target = document.querySelector('.search-result.active'); + if(target){ + //execute + var key = target.dataset.key; + var class_object = this.Base_gui.modules[key]; + var function_name = _this.get_function_from_path(key); + + _this.POP.hide(); + class_object[function_name](); + } + }, + }; + this.POP.show(settings); + + //on input change + document.getElementById("pop_data_search").select(); + } + + get_function_from_path(path){ + var parts = path.split("/"); + var result = parts[parts.length - 1]; + result = result.replace(/-/, '_'); + + return result; + } + +} + +export default Base_search_class; diff --git a/src/js/libs/popup.js b/src/js/libs/popup.js index c4f318e..e7dd592 100644 --- a/src/js/libs/popup.js +++ b/src/js/libs/popup.js @@ -181,9 +181,7 @@ class Dialog_class { if (code == "Escape") { //escape - if (window.POP === this) { - this.hide(false); - } + this.hide(false); } }, false); diff --git a/src/js/main.js b/src/js/main.js index 2ad746f..9d3eac0 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -19,6 +19,7 @@ import Base_gui_class from './core/base-gui.js'; import Base_layers_class from './core/base-layers.js'; import Base_tools_class from './core/base-tools.js'; import Base_state_class from './core/base-state.js'; +import Base_search_class from './core/base-search.js'; import File_open_class from './modules/file/open.js'; import File_save_class from './modules/file/save.js'; import * as Actions from './actions/index.js'; @@ -31,6 +32,7 @@ window.addEventListener('load', function (e) { var Base_state = new Base_state_class(); var File_open = new File_open_class(); var File_save = new File_save_class(); + var Base_search = new Base_search_class(); // Register singletons in app module app.Actions = Actions; diff --git a/src/js/modules/help/shortcuts.js b/src/js/modules/help/shortcuts.js index 794129a..a4cf412 100644 --- a/src/js/modules/help/shortcuts.js +++ b/src/js/modules/help/shortcuts.js @@ -10,8 +10,10 @@ class Help_shortcuts_class { shortcuts() { var settings = { title: 'Keyboard Shortcuts', + className: 'shortcuts', params: [ {title: "F", value: 'Auto Adjust Colors'}, + {title: "F3", value: 'Search'}, {title: "Ctrl + C", value: 'Copy to Clipboard'}, {title: "D", value: 'Duplicate'}, {title: "S", value: 'Export'}, diff --git a/src/js/modules/tools/search.js b/src/js/modules/tools/search.js new file mode 100644 index 0000000..fb9f622 --- /dev/null +++ b/src/js/modules/tools/search.js @@ -0,0 +1,15 @@ +import Base_search_class from './../../core/base-search.js'; + +class Tools_search_class { + + constructor() { + this.Base_search = new Base_search_class(); + } + + search() { + this.Base_search.search(); + } + +} + +export default Tools_search_class; \ No newline at end of file