diff options
author | Jozef Selesi <jozef.selesi@sky.uk> | 2020-03-26 19:12:43 +0000 |
---|---|---|
committer | Jozef Selesi <jozef.selesi@sky.uk> | 2020-04-04 10:59:10 +0100 |
commit | a4de4a3a71e460141b740564ef22d1c79760db4e (patch) | |
tree | f7934df7d7daae99b03441cad8f192aa7dbed033 | |
parent | 0503e77861fcdfeaf20e1cc06dbc2f49d06bc45b (diff) | |
download | homer-a4de4a3a71e460141b740564ef22d1c79760db4e.tar.gz homer-a4de4a3a71e460141b740564ef22d1c79760db4e.tar.zst homer-a4de4a3a71e460141b740564ef22d1c79760db4e.zip |
Add keyboard shortcuts to navigate to the first search result.
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | app.js | 20 | ||||
-rw-r--r-- | index.html | 8 |
3 files changed, 32 insertions, 3 deletions
@@ -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 | ||
6 | It 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 | |||
6 | If 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, ...) | 13 | If 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) |
@@ -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() { |
@@ -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> |