aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJozef Selesi <jozef.selesi@sky.uk>2020-03-26 19:12:43 +0000
committerJozef Selesi <jozef.selesi@sky.uk>2020-04-04 10:59:10 +0100
commita4de4a3a71e460141b740564ef22d1c79760db4e (patch)
treef7934df7d7daae99b03441cad8f192aa7dbed033
parent0503e77861fcdfeaf20e1cc06dbc2f49d06bc45b (diff)
downloadhomer-a4de4a3a71e460141b740564ef22d1c79760db4e.tar.gz
homer-a4de4a3a71e460141b740564ef22d1c79760db4e.tar.zst
homer-a4de4a3a71e460141b740564ef22d1c79760db4e.zip
Add keyboard shortcuts to navigate to the first search result.
-rw-r--r--README.md7
-rw-r--r--app.js20
-rw-r--r--index.html8
3 files changed, 32 insertions, 3 deletions
diff --git a/README.md b/README.md
index 5ab7c26..f052fc9 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,13 @@ A dead simple static **HOM**epage for your serv**ER** to keep your services on h
3 3
4**Check out the live demo [here](https://homer-demo.netlify.com/).** 4**Check out the live demo [here](https://homer-demo.netlify.com/).**
5 5
6It supports keyboard shortcuts:
7
8* `/` Start searching.
9* `Escape` Stop searching.
10* `Enter` Open the first matching result (respects the bookmark's `_target` property).
11* `Alt`/`Option` + `Enter` Open the first matching result in a new tab.
12
6If you need authentication support, you're on your own (it can be secured using a web server auth module or exposing it only through a VPN network / SSH tunnel, ...) 13If you need authentication support, you're on your own (it can be secured using a web server auth module or exposing it only through a VPN network / SSH tunnel, ...)
7 14
8![screenshot](https://raw.github.com/bastienwirtz/homer/master/screenshot.png) 15![screenshot](https://raw.github.com/bastienwirtz/homer/master/screenshot.png)
diff --git a/app.js b/app.js
index 36508ab..e796110 100644
--- a/app.js
+++ b/app.js
@@ -84,6 +84,26 @@ const app = new Vue({
84 }, 84 },
85 toggleMenu: function() { 85 toggleMenu: function() {
86 this.showMenu = !this.showMenu; 86 this.showMenu = !this.showMenu;
87 },
88 matchesFilter: function(item) {
89 return (item.name.toLowerCase().includes(this.filter.toLowerCase())
90 || (item.tag && item.tag.toLowerCase().includes(this.filter.toLowerCase())))
91 },
92 firstMatchingService: function() {
93 for (group of this.config.services) {
94 for (item of group.items) {
95 if (this.matchesFilter(item)) {
96 return item;
97 }
98 }
99 }
100 return null;
101 },
102 navigateToFirstService: function(target) {
103 service = this.firstMatchingService();
104 if (service) {
105 window.open(service.url, target || service.target || '_self');
106 }
87 } 107 }
88 }, 108 },
89 mounted() { 109 mounted() {
diff --git a/index.html b/index.html
index 85c9b78..d427002 100644
--- a/index.html
+++ b/index.html
@@ -59,7 +59,9 @@
59 :class="['fas', vlayout ? 'fa-list' : 'fa-columns']"></i></a> 59 :class="['fas', vlayout ? 'fa-list' : 'fa-columns']"></i></a>
60 <div class="search-bar"> 60 <div class="search-bar">
61 <label for="search" class="search-label"></label> 61 <label for="search" class="search-label"></label>
62 <input type="text" id="search" ref="search" v-model="filter" /> 62 <input type="text" id="search" ref="search" v-model="filter"
63 v-on:keyup.enter.exact="navigateToFirstService()"
64 v-on:keyup.alt.enter="navigateToFirstService('_blank')" />
63 </div> 65 </div>
64 </div> 66 </div>
65 </div> 67 </div>
@@ -95,7 +97,7 @@
95 v-else>#</span> 97 v-else>#</span>
96 {{ group.name }}</h2> 98 {{ group.name }}</h2>
97 <service v-for="item in group.items" v-bind:item="item" class="column is-one-third-widescreen" 99 <service v-for="item in group.items" v-bind:item="item" class="column is-one-third-widescreen"
98 v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"> 100 v-if="!filter || (item && matchesFilter(item))">
99 </service> 101 </service>
100 </template> 102 </template>
101 </div> 103 </div>
@@ -106,7 +108,7 @@
106 <h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span> 108 <h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span>
107 {{ group.name }}</h2> 109 {{ group.name }}</h2>
108 <service v-for="item in group.items" v-bind:item="item" 110 <service v-for="item in group.items" v-bind:item="item"
109 v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"> 111 v-if="!filter || (item && matchesFilter(item))">
110 </service> 112 </service>
111 </div> 113 </div>
112 </div> 114 </div>