Keyboard Shortcuts (#193)

Add the keyboard shortcuts button

Add task keyboard shortcuts

Add info

Move keyboard shortcuts modal toggle to menu

Add modal for shortcuts

Add shortkeys for some task actions

Add shortkey to toggle menu

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/193
This commit is contained in:
konrad 2020-07-25 14:52:04 +00:00
parent 5521ba7c71
commit be093e3779
9 changed files with 194 additions and 6 deletions

View file

@ -22,6 +22,7 @@
"vue": "2.6.11",
"vue-drag-resize": "1.4.1",
"vue-easymde": "1.2.2",
"vue-shortkey": "^3.1.7",
"vue-smooth-dnd": "0.8.1",
"vuex": "3.5.1"
},

View file

@ -62,6 +62,7 @@
</router-link>
<a :href="imprintUrl" v-if="imprintUrl" class="dropdown-item" target="_blank">Imprint</a>
<a :href="privacyPolicyUrl" v-if="privacyPolicyUrl" class="dropdown-item" target="_blank">Privacy policy</a>
<a @click="keyboardShortcutsActive = true" class="dropdown-item">Keyboard Shortcuts</a>
<a @click="logout()" class="dropdown-item">
Logout
</a>
@ -137,7 +138,11 @@
</li>
</ul>
</div>
<a @click="menuActive = false" class="collapse-menu-button">Collapse Menu</a>
<a @click="menuActive = false" class="collapse-menu-button" v-shortkey="['ctrl', 'e']" @shortkey="() => menuActive = !menuActive">
Collapse Menu
</a>
<aside class="menu namespaces-lists">
<div class="spinner" :class="{ 'is-loading': namespaceService.loading}"></div>
<template v-for="n in namespaces">
@ -217,6 +222,9 @@
<transition name="fade">
<router-view/>
</transition>
<a class="keyboard-shortcuts-button" @click="keyboardShortcutsActive = true">
<icon icon="keyboard"/>
</a>
</div>
</div>
</div>
@ -270,6 +278,10 @@
<p>Please check your network connection and try again.</p>
</div>
</div>
<transition name="fade">
<keyboard-shortcuts v-if="keyboardShortcutsActive" @close="keyboardShortcutsActive = false"/>
</transition>
</div>
</template>
@ -283,10 +295,12 @@
import swEvents from './ServiceWorker/events'
import Notification from './components/misc/notification'
import {CURRENT_LIST, IS_FULLPAGE, ONLINE} from './store/mutation-types'
import KeyboardShortcuts from './components/misc/keyboard-shortcuts'
export default {
name: 'app',
components: {
KeyboardShortcuts,
Notification,
},
data() {
@ -296,6 +310,7 @@
currentDate: new Date(),
userMenuActive: false,
authTypes: authTypes,
keyboardShortcutsActive: false,
// Service Worker stuff
updateAvailable: false,

View file

@ -0,0 +1,99 @@
<template>
<div class="modal-mask keyboard-shortcuts-modal">
<div class="modal-container" @click.self="close()">
<div class="modal-content">
<div class="card has-background-white has-no-shadow">
<header class="card-header">
<p class="card-header-title">Available Keyboard Shortcuts</p>
</header>
<div class="card-content content">
<p class="info">
The available keyboard shortcuts depend on the current page. Not all shortcuts are available
everywhere.
</p>
<p>
<strong>Toggle The Menu</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>e</span>
</span>
</p>
<p v-if="$route.name === 'list.kanban'">
<strong>Mark a task as done</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>click</span>
</span>
</p>
<template
v-if="$route.name === 'task.detail' || $route.name === 'task.list.detail' || $route.name === 'task.gantt.detail' || $route.name === 'task.kanban.detail' || $route.name === 'task.detail'">
<p>
<strong>Assign this task to a user</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>shift</span>
<i>+</i>
<span>a</span>
</span>
</p>
<p>
<strong>Add labels to this task</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>shift</span>
<i>+</i>
<span>l</span>
</span>
</p>
<p>
<strong>Change the due date of this task</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>shift</span>
<i>+</i>
<span>d</span>
</span>
</p>
<p>
<strong>Add an attachment to this task</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>shift</span>
<i>+</i>
<span>f</span>
</span>
</p>
<p>
<strong>Modify related tasks of this task</strong>
<span class="shortcuts">
<span>ctrl</span>
<i>+</i>
<span>shift</span>
<i>+</i>
<span>r</span>
</span>
</p>
</template>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'keyboard-shortcuts',
methods: {
close() {
this.$emit('close')
},
},
}
</script>

View file

@ -68,6 +68,7 @@ import { faList } from '@fortawesome/free-solid-svg-icons'
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
import { faFilter } from '@fortawesome/free-solid-svg-icons'
import { faFillDrip } from '@fortawesome/free-solid-svg-icons'
import { faKeyboard } from '@fortawesome/free-solid-svg-icons'
import { faComments } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
@ -115,6 +116,7 @@ library.add(faList)
library.add(faEllipsisV)
library.add(faFilter)
library.add(faFillDrip)
library.add(faKeyboard)
Vue.component('icon', FontAwesomeIcon)
@ -125,6 +127,10 @@ Vue.use(VTooltip)
// PWA
import './registerServiceWorker'
// Shortcuts
import vueShortkey from 'vue-shortkey'
Vue.use(vueShortkey)
// Set focus
Vue.directive('focus', {
// When the bound element is inserted into the DOM...

View file

@ -20,3 +20,4 @@
@import 'color-picker';
@import 'namespaces';
@import 'legal';
@import 'keyboard-shortcuts';

View file

@ -0,0 +1,44 @@
.keyboard-shortcuts-button {
position: fixed;
bottom: calc(1rem - 4px);
right: 1rem;
z-index: 4500; // The modal has a z-index of 4000
color: $grey;
transition: color $transition;
}
.keyboard-shortcuts-modal {
z-index: 4600;
.card-content {
text-align: left;
.info {
font-style: italic;
}
p {
display: flex;
justify-content: space-between;
align-items: center;
.shortcuts {
display: flex;
align-items: center;
i {
padding: 0 .25rem;
}
span {
padding: .1rem .35rem;
border: 1px solid $grey-light;
background: lighten($grey-lightest, 2.5);
border-radius: 3px;
font-size: .75rem;
}
}
}
}
}

View file

@ -71,3 +71,7 @@ button.table {
max-width: $desktop;
margin: 0 auto;
}
.has-no-shadow {
box-shadow: none !important;
}

View file

@ -244,11 +244,11 @@
Done!
</template>
</a>
<a class="button" @click="setFieldActive('assignees')">
<a class="button" @click="setFieldActive('assignees')" v-shortkey="['ctrl', 'shift', 'a']" @shortkey="setFieldActive('assignees')">
<span class="icon is-small"><icon icon="users"/></span>
Assign this task to a user
</a>
<a class="button" @click="setFieldActive('labels')">
<a class="button" @click="setFieldActive('labels')" v-shortkey="['ctrl', 'shift', 'l']" @shortkey="setFieldActive('labels')">
<span class="icon is-small"><icon icon="tags"/></span>
Add labels
</a>
@ -256,7 +256,7 @@
<span class="icon is-small"><icon icon="history"/></span>
Set Reminders
</a>
<a class="button" @click="setFieldActive('dueDate')">
<a class="button" @click="setFieldActive('dueDate')" v-shortkey="['ctrl', 'shift', 'd']" @shortkey="setFieldActive('dueDate')">
<span class="icon is-small"><icon icon="calendar"/></span>
Set Due Date
</a>
@ -280,11 +280,11 @@
<span class="icon is-small"><icon icon="percent"/></span>
Set Percent Done
</a>
<a class="button" @click="setFieldActive('attachments')">
<a class="button" @click="setFieldActive('attachments')" v-shortkey="['ctrl', 'shift', 'f']" @shortkey="setFieldActive('attachments')">
<span class="icon is-small"><icon icon="paperclip"/></span>
Add attachments
</a>
<a class="button" @click="setFieldActive('relatedTasks')">
<a class="button" @click="setFieldActive('relatedTasks')" v-shortkey="['ctrl', 'shift', 'r']" @shortkey="setFieldActive('relatedTasks')">
<span class="icon is-small"><icon icon="tasks"/></span>
Add task relations
</a>

View file

@ -4705,6 +4705,11 @@ currently-unhandled@^0.4.1:
dependencies:
array-find-index "^1.0.1"
custom-event-polyfill@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz#9bc993ddda937c1a30ccd335614c6c58c4f87aee"
integrity sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@ -5226,6 +5231,11 @@ elegant-spinner@^1.0.1:
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
element-matches@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/element-matches/-/element-matches-0.1.2.tgz#7345cb71e965bd2b12f725e524591c102198361a"
integrity sha512-yWh1otcs3OKUWDvu/IxyI36ZI3WNaRZlI0uG/DK6fu0pap0VYZ0J5pEGTk1zakme+hT0OKHwhlHc0N5TJhY6yQ==
elliptic@^6.0.0:
version "6.5.2"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762"
@ -12563,6 +12573,14 @@ vue-sfc-descriptor-to-string@^1.0.0:
dependencies:
indent-string "^3.2.0"
vue-shortkey@^3.1.7:
version "3.1.7"
resolved "https://registry.yarnpkg.com/vue-shortkey/-/vue-shortkey-3.1.7.tgz#31c09a99ed597331a6a49a45eeff894a78a19eef"
integrity sha512-Wm/vPXXS+4Wl/LoYpD+cZc0J0HIoVlY8Ep0JLIqqswmAya3XUBtsqKbhzEf9sXo+3rZ5p1YsUyZfXas8XD7YjQ==
dependencies:
custom-event-polyfill "^1.0.7"
element-matches "^0.1.2"
vue-smooth-dnd@0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/vue-smooth-dnd/-/vue-smooth-dnd-0.8.1.tgz#b1c584cfe49b830a402548b4bf08f00f68f430e5"