feat: Creates a modal showing all the available keyboard shortcuts (#2728)

* feat: Adds modal showing all the available keyboard shortcuts

* Minor fixes

* Minor fixes

* Spacing fixes

* fix translations

* Adds i18n

* Review fixes

* Review fixes

* spacing fixes

* Review fixes

* Minor fixes

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Sivin Varghese 2021-08-10 09:53:40 +05:30 committed by GitHub
parent bdc4ecffc1
commit c3314dd186
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 353 additions and 1 deletions

View file

@ -175,6 +175,9 @@ export default {
const allConversations = document.querySelectorAll(
'.conversations-list .conversation'
);
if (hasPressedAltAndMKey(e)) {
this.$refs.arrowDownButton.$el.click();
}
if (hasPressedAltAndEKey(e)) {
const activeConversation = document.querySelector(
'div.conversations-list div.conversation.active'

View file

@ -50,10 +50,17 @@
:show="showOptionsMenu"
@toggle-accounts="toggleAccountModal"
@show-support-chat-window="toggleSupportChatWindow"
@key-shortcut-modal="toggleKeyShortcutModal"
@close="toggleOptions"
/>
</div>
<woot-key-shortcut-modal
v-if="showShortcutModal"
@close="closeKeyShortcutModal"
@clickaway="closeKeyShortcutModal"
/>
<account-selector
:show-account-modal="showAccountModal"
@close-account-modal="toggleAccountModal"
@ -86,6 +93,9 @@ import OptionsMenu from './sidebarComponents/OptionsMenu.vue';
import AccountSelector from './sidebarComponents/AccountSelector.vue';
import AddAccountModal from './sidebarComponents/AddAccountModal.vue';
import AddLabelModal from '../../routes/dashboard/settings/labels/AddLabel';
import WootKeyShortcutModal from 'components/widgets/modal/WootKeyShortcutModal';
import { hasPressedCommandAndForwardSlash } from 'shared/helpers/KeyboardHelpers';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
export default {
components: {
@ -97,14 +107,16 @@ export default {
AccountSelector,
AddAccountModal,
AddLabelModal,
WootKeyShortcutModal,
},
mixins: [adminMixin, alertMixin],
mixins: [adminMixin, alertMixin, eventListenerMixins],
data() {
return {
showOptionsMenu: false,
showAccountModal: false,
showCreateAccountModal: false,
showAddLabelModal: false,
showShortcutModal: false,
};
},
@ -254,7 +266,19 @@ export default {
this.$store.dispatch('teams/get');
this.setChatwootUser();
},
methods: {
toggleKeyShortcutModal() {
this.showShortcutModal = true;
},
closeKeyShortcutModal() {
this.showShortcutModal = false;
},
handleKeyEvents(e) {
if (hasPressedCommandAndForwardSlash(e)) {
this.toggleKeyShortcutModal();
}
},
toggleSupportChatWindow() {
window.$chatwoot.toggle();
},

View file

@ -26,6 +26,16 @@
Contact Support
</woot-button>
</woot-dropdown-item>
<woot-dropdown-item>
<woot-button
variant="clear"
size="small"
class=" change-accounts--button"
@click="$emit('key-shortcut-modal')"
>
{{ $t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS') }}
</woot-button>
</woot-dropdown-item>
<woot-dropdown-item>
<router-link
:to="`/app/accounts/${accountId}/profile/settings`"

View file

@ -0,0 +1,17 @@
import WootKeyboardShortcutModal from './WootKeyShortcutModal.vue';
export default {
title: 'Components/Shortcuts/Keyboard Shortcut',
component: WootKeyboardShortcutModal,
argTypes: {},
};
const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { WootKeyboardShortcutModal },
template:
'<woot-keyboard-shortcut-modal v-bind="$props"></woot-keyboard-shortcut-modal>',
});
export const KeyboardShortcut = Template.bind({});
KeyboardShortcut.args = {};

View file

@ -0,0 +1,182 @@
<template>
<transition name="slide-up">
<div class="modal-mask">
<div v-on-clickaway="() => $emit('clickaway')" class="modal-container">
<div class="header-wrap">
<div class="title-shortcut-key__wrap">
<h2 class="page-title">
{{ $t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS') }}
</h2>
<div class="shortcut-key__wrap">
<p class="shortcut-key">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.COMMAND_KEY') }}
</p>
<p class="shortcut-key key">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.FORWARD_SLASH_KEY') }}
</p>
</div>
</div>
<i class="ion-android-close modal--close" @click="$emit('close')"></i>
</div>
<div class="shortcut__wrap">
<div class="title-key__wrap">
<span class="sub-block-title">
{{ $t('KEYBOARD_SHORTCUTS.TITLE.OPEN_CONVERSATION') }}
</span>
<div class="shortcut-key__wrap">
<div class="open-conversation__key">
<span class="shortcut-key">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.ALT_OR_OPTION_KEY') }}
</span>
<span class="shortcut-key">
J
</span>
<span class="forward-slash sub-block-title">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.FORWARD_SLASH_KEY') }}
</span>
</div>
<span class="shortcut-key">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.ALT_OR_OPTION_KEY') }}
</span>
<span class="shortcut-key key">
K
</span>
</div>
</div>
<div class="title-key__wrap">
<span class="sub-block-title">
{{ $t('KEYBOARD_SHORTCUTS.TITLE.RESOLVE_AND_NEXT') }}
</span>
<div class="shortcut-key__wrap">
<span class="shortcut-key">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.COMMAND_KEY') }}
</span>
<span class="shortcut-key">
{{ $t('KEYBOARD_SHORTCUTS.KEYS.ALT_OR_OPTION_KEY') }}
</span>
<span class="shortcut-key key">
E
</span>
</div>
</div>
<div
v-for="shortcutKey in shortcutKeys"
:key="shortcutKey.id"
class="title-key__wrap"
>
<span class="sub-block-title">
{{ title(shortcutKey) }}
</span>
<div class="shortcut-key__wrap">
<span class="shortcut-key">
{{ shortcutKey.firstkey }}
</span>
<span class="shortcut-key key">
{{ shortcutKey.secondKey }}
</span>
</div>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
import { mixin as clickaway } from 'vue-clickaway';
import { SHORTCUT_KEYS } from './constants';
export default {
mixins: [clickaway],
data() {
return {
shortcutKeys: SHORTCUT_KEYS,
};
},
methods: {
title(item) {
return this.$t(`KEYBOARD_SHORTCUTS.TITLE.${item.label}`);
},
},
};
</script>
<style lang="scss" scoped>
.modal-container {
padding: var(--space-medium) var(--space-large) var(--space-large)
var(--space-large);
width: fit-content;
}
.header-wrap {
display: flex;
justify-content: space-between;
align-items: center;
}
.title-shortcut-key__wrap {
display: flex;
margin-bottom: var(--space-small);
}
.page-title {
font-size: var(--font-size-big);
font-weight: var(--font-weight-bold);
}
.shortcut-key__wrap {
display: flex;
align-items: center;
margin-bottom: var(--space-smaller);
margin-left: var(--space-small);
}
.shortcut__wrap {
display: grid;
grid-template-columns: repeat(2, 0.5fr);
gap: var(--space-smaller) var(--space-large);
margin-top: var(--space-small);
}
.title-key__wrap {
display: flex;
justify-content: space-between;
align-items: center;
min-width: 40rem;
}
.sub-block-title {
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
}
.forward-slash {
display: flex;
align-items: center;
font-weight: var(--font-weight-bold);
}
.shortcut-key {
background: var(--color-background);
padding: var(--space-small) var(--space-one);
font-weight: var(--font-weight-bold);
font-size: var(--font-size-mini);
align-items: center;
border-radius: var(--border-radius-normal);
margin-right: var(--space-small);
}
.key {
display: flex;
justify-content: center;
min-width: var(--space-large);
margin-right: 0;
}
.open-conversation__key {
display: flex;
margin-right: var(--space-small);
}
</style>

View file

@ -0,0 +1,86 @@
export const SHORTCUT_KEYS = [
{
id: 1,
label: 'NAVIGATE_DROPDOWN',
firstkey: 'Up',
secondKey: 'Down',
},
{
id: 2,
label: 'RESOLVE_CONVERSATION',
firstkey: 'Alt / ⌥',
secondKey: 'E',
},
{
id: 3,
label: 'GO_TO_CONVERSATION_DASHBOARD',
firstkey: 'Alt / ⌥',
secondKey: 'C',
},
{
id: 4,
label: 'ADD_ATTACHMENT',
firstkey: 'Alt / ⌥',
secondKey: 'A',
},
{
id: 5,
label: 'GO_TO_CONTACTS_DASHBOARD',
firstkey: 'Alt / ⌥',
secondKey: 'V',
},
{
id: 6,
label: 'TOGGLE_SIDEBAR',
firstkey: 'Alt / ⌥',
secondKey: 'O',
},
{
id: 7,
label: 'GO_TO_REPORTS_SIDEBAR',
firstkey: 'Alt / ⌥',
secondKey: 'R',
},
{
id: 8,
label: 'MOVE_TO_NEXT_TAB',
firstkey: 'Alt / ⌥',
secondKey: 'N',
},
{
id: 9,
label: 'GO_TO_SETTINGS',
firstkey: 'Alt / ⌥',
secondKey: 'S',
},
{
id: 10,
label: 'SWITCH_CONVERSATION_STATUS',
firstkey: 'Alt / ⌥',
secondKey: 'B',
},
{
id: 11,
label: 'SWITCH_TO_PRIVATE_NOTE',
firstkey: 'Alt / ⌥',
secondKey: 'P',
},
{
id: 12,
label: 'TOGGLE_RICH_CONTENT_EDITOR',
firstkey: 'Alt / ⌥',
secondKey: 'W',
},
{
id: 13,
label: 'SWITCH_TO_REPLY',
firstkey: 'Alt / ⌥',
secondKey: 'L',
},
{
id: 14,
label: 'TOGGLE_SNOOZE_DROPDOWN',
firstkey: 'Alt / ⌥',
secondKey: 'M',
},
];

View file

@ -98,6 +98,7 @@
"CHANGE_ACCOUNTS": "Switch Account",
"SELECTOR_SUBTITLE": "Select an account from the following list",
"PROFILE_SETTINGS": "Profile Settings",
"KEYBOARD_SHORTCUTS": "Keyboard Shortcuts",
"LOGOUT": "Logout"
},
"APP_GLOBAL": {
@ -159,5 +160,30 @@
},
"SUBMIT": "Submit"
}
},
"KEYBOARD_SHORTCUTS": {
"TITLE": {
"OPEN_CONVERSATION": "Open conversation",
"RESOLVE_AND_NEXT": "Resolve and move to next",
"NAVIGATE_DROPDOWN": "Navigate dropdown items",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
"ADD_ATTACHMENT": "Add Attachment",
"GO_TO_CONTACTS_DASHBOARD": "Go to Contacts Dashboard",
"TOGGLE_SIDEBAR": "Toggle Sidebar",
"GO_TO_REPORTS_SIDEBAR": "Go to Reports sidebar",
"MOVE_TO_NEXT_TAB": "Move to next tab in conversation list",
"GO_TO_SETTINGS": "Go to Settings",
"SWITCH_CONVERSATION_STATUS": "Switch Conversation status",
"SWITCH_TO_PRIVATE_NOTE": "Switch to Private Note",
"TOGGLE_RICH_CONTENT_EDITOR": "Toggle Rich Content editor",
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
},
"KEYS": {
"COMMAND_KEY": "⌘",
"ALT_OR_OPTION_KEY": "Alt / ⌥",
"FORWARD_SLASH_KEY": "/"
}
}
}

View file

@ -10,6 +10,10 @@ export const hasPressedShift = e => {
return e.shiftKey;
};
export const hasPressedCommandAndForwardSlash = e => {
return e.metaKey && e.keyCode === 191;
};
export const hasPressedAltAndCKey = e => {
return e.altKey && e.keyCode === 67;
};