🔀 Merge pull request #94 from Lissy93/FEATURE/service-keyboard-shortcuts
[FEATURE] Support custom keybindings
This commit is contained in:
@@ -20,8 +20,9 @@
|
||||
<Icon :icon="icon" :url="url" :size="itemSize" :color="color"
|
||||
v-bind:style="customStyles" class="bounce" />
|
||||
<!-- Small icon, showing opening method on hover -->
|
||||
<ItemOpenMethodIcon class="opening-method-icon" :isSmall="!icon" :openingMethod="target"
|
||||
:position="itemSize === 'medium'? 'bottom right' : 'top right'"/>
|
||||
<ItemOpenMethodIcon class="opening-method-icon" :isSmall="!icon || itemSize === 'small'"
|
||||
:openingMethod="target" :position="itemSize === 'medium'? 'bottom right' : 'top right'"
|
||||
:hotkey="hotkey" />
|
||||
<!-- Status indicator dot (if enabled) showing weather srevice is availible -->
|
||||
<StatusIndicator
|
||||
class="status-indicator"
|
||||
@@ -60,6 +61,7 @@ export default {
|
||||
color: String, // Optional text and icon color, specified in hex code
|
||||
backgroundColor: String, // Optional item background color
|
||||
url: String, // URL to the resource, optional but recommended
|
||||
hotkey: Number, // Shortcut for quickly launching app
|
||||
target: { // Where resource will open, either 'newtab', 'sametab' or 'modal'
|
||||
type: String,
|
||||
default: 'newtab',
|
||||
@@ -119,9 +121,10 @@ export default {
|
||||
},
|
||||
/* Returns configuration object for the tooltip */
|
||||
getTooltipOptions() {
|
||||
const hotkeyText = this.hotkey ? `\nPress '${this.hotkey}' to launch` : '';
|
||||
return {
|
||||
disabled: !this.description,
|
||||
content: this.description,
|
||||
content: this.description + hotkeyText,
|
||||
trigger: 'hover focus',
|
||||
hideOnTargetClick: true,
|
||||
html: false,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
:statusCheckUrl="item.statusCheckUrl"
|
||||
:statusCheckHeaders="item.statusCheckHeaders"
|
||||
:itemSize="newItemSize"
|
||||
:hotkey="item.hotkey"
|
||||
:enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)"
|
||||
:statusCheckInterval="getStatusCheckInterval()"
|
||||
@itemClicked="$emit('itemClicked')"
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<div :class="makeClass(position, isSmall, isTransparent)">
|
||||
<NewTabOpenIcon v-if="openingMethod === 'newtab'" />
|
||||
<SameTabOpenIcon v-else-if="openingMethod === 'sametab'" />
|
||||
<IframeOpenIcon v-else-if="openingMethod === 'modal'" />
|
||||
<WorkspaceOpenIcon v-else-if="openingMethod === 'workspace'" />
|
||||
<div>
|
||||
<div :class="makeClass(position, isSmall, isTransparent)">
|
||||
<NewTabOpenIcon v-if="openingMethod === 'newtab'" />
|
||||
<SameTabOpenIcon v-else-if="openingMethod === 'sametab'" />
|
||||
<IframeOpenIcon v-else-if="openingMethod === 'modal'" />
|
||||
<WorkspaceOpenIcon v-else-if="openingMethod === 'workspace'" />
|
||||
</div>
|
||||
<div v-if="hotkey" :class="`hotkey-denominator ${makeClass(position, isSmall, isTransparent)}`">
|
||||
{{ hotkey }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -23,6 +28,7 @@ export default {
|
||||
isSmall: Boolean, // If true, will apply small class
|
||||
position: String, // Position classes: top, bottom, left, right
|
||||
isTransparent: Boolean, // If true, will apply opacity
|
||||
hotkey: Number, // Optional hotkey to also display
|
||||
},
|
||||
methods: {
|
||||
/* Returns custom class string, from optional props */
|
||||
@@ -49,7 +55,6 @@ export default {
|
||||
width: 1rem;
|
||||
margin: 2px;
|
||||
path {
|
||||
// fill: var(--primary);
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
@@ -68,4 +73,17 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
div.hotkey-denominator {
|
||||
position: absolute;
|
||||
font-size: 0.8rem;
|
||||
margin: 2px;
|
||||
bottom: 2px;
|
||||
color: currentColor;
|
||||
border-radius: 18px;
|
||||
border: 1px solid currentColor;
|
||||
padding: 0.1rem 0.4rem 0.2rem 0.4rem;
|
||||
&.top { right: 0; } // Position opposite of opening method icon
|
||||
&.bottom { left: 0; }
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<script>
|
||||
|
||||
import ArrowKeyNavigation from '@/utils/ArrowKeyNavigation';
|
||||
import { getCustomKeyShortcuts } from '@/utils/ConfigHelpers';
|
||||
|
||||
export default {
|
||||
name: 'FilterTile',
|
||||
@@ -28,6 +29,7 @@ export default {
|
||||
return {
|
||||
input: '', // Users current search term
|
||||
akn: new ArrowKeyNavigation(), // Class that manages arrow key naviagtion
|
||||
getCustomKeyShortcuts,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@@ -38,8 +40,11 @@ export default {
|
||||
if (!this.active) return;
|
||||
if (/^[a-zA-Z]$/.test(key) && currentElem !== 'filter-tiles') {
|
||||
/* Letter key pressed - start searching */
|
||||
this.$refs.filter.focus();
|
||||
if (this.$refs.filter) this.$refs.filter.focus();
|
||||
this.userIsTypingSomething();
|
||||
} else if (/^[0-9]$/.test(key)) {
|
||||
/* Number key pressed, check if user has a custom binding */
|
||||
this.handleHotKey(key);
|
||||
} else if (keyCode >= 37 && keyCode <= 40) {
|
||||
/* Arrow key pressed - start navigation */
|
||||
this.akn.arrowNavigation(keyCode);
|
||||
@@ -61,6 +66,14 @@ export default {
|
||||
document.activeElement.blur(); // Remove focus
|
||||
this.akn.resetIndex(); // Reset current element index
|
||||
},
|
||||
handleHotKey(key) {
|
||||
const usersHotKeys = this.getCustomKeyShortcuts();
|
||||
usersHotKeys.forEach((hotkey) => {
|
||||
if (hotkey.hotkey === parseInt(key, 10)) {
|
||||
if (hotkey.url) window.open(hotkey.url, '_blank');
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -60,3 +60,17 @@ export const getCustomColors = () => {
|
||||
const configColors = config.appConfig.customColors || {};
|
||||
return Object.assign(configColors, localColors);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of items which the user has assigned a hotkey to
|
||||
* So that when the hotkey is pressed, the app/ service can be launched
|
||||
*/
|
||||
export const getCustomKeyShortcuts = () => {
|
||||
const results = [];
|
||||
const sections = config.sections || [];
|
||||
sections.forEach((section) => {
|
||||
const itemsWithHotKeys = section.items.filter(item => item.hotkey);
|
||||
results.push(itemsWithHotKeys.map(item => ({ hotkey: item.hotkey, url: item.url })));
|
||||
});
|
||||
return results.flat();
|
||||
};
|
||||
|
||||
@@ -364,6 +364,10 @@
|
||||
"default": "newtab",
|
||||
"description": "Opening method, when item is clicked"
|
||||
},
|
||||
"hotkey": {
|
||||
"type": "number",
|
||||
"description": "A numeric shortcut key, between 0 and 9. Useful for quickly launching frequently used applications"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "A custom fill color of the item"
|
||||
|
||||
Reference in New Issue
Block a user