mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 03:27:52 +00:00
chore: Improvement is keyboard shortcuts (#10925)
This commit is contained in:
@@ -127,7 +127,7 @@ export default {
|
|||||||
const uploadRef = ref(false);
|
const uploadRef = ref(false);
|
||||||
|
|
||||||
const keyboardEvents = {
|
const keyboardEvents = {
|
||||||
'Alt+KeyA': {
|
'$mod+Alt+KeyA': {
|
||||||
action: () => {
|
action: () => {
|
||||||
// TODO: This is really hacky, we need to replace the file picker component with
|
// TODO: This is really hacky, we need to replace the file picker component with
|
||||||
// a custom one, where the logic and the component markup is isolated.
|
// a custom one, where the logic and the component markup is isolated.
|
||||||
|
|||||||
@@ -1,41 +1,34 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useDetectKeyboardLayout } from 'dashboard/composables/useDetectKeyboardLayout';
|
import { useDetectKeyboardLayout } from 'dashboard/composables/useDetectKeyboardLayout';
|
||||||
import { SHORTCUT_KEYS } from './constants';
|
import { SHORTCUT_KEYS, KEYS } from './constants';
|
||||||
import {
|
import {
|
||||||
LAYOUT_QWERTZ,
|
LAYOUT_QWERTZ,
|
||||||
keysToModifyInQWERTZ,
|
keysToModifyInQWERTZ,
|
||||||
} from 'shared/helpers/KeyboardHelpers';
|
} from 'shared/helpers/KeyboardHelpers';
|
||||||
import Hotkey from 'dashboard/components/base/Hotkey.vue';
|
import Hotkey from 'dashboard/components/base/Hotkey.vue';
|
||||||
|
|
||||||
defineProps({
|
defineProps({ show: Boolean });
|
||||||
show: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
defineEmits(['close']);
|
defineEmits(['close']);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const shortcutKeys = SHORTCUT_KEYS;
|
|
||||||
const currentLayout = ref(null);
|
const currentLayout = ref(null);
|
||||||
|
|
||||||
const title = item => t(`KEYBOARD_SHORTCUTS.TITLE.${item.label}`);
|
const title = computed(
|
||||||
|
() => item => t(`KEYBOARD_SHORTCUTS.TITLE.${item.label}`)
|
||||||
|
);
|
||||||
|
|
||||||
// Added this function to check if the keySet needs a shift key
|
// Added this function to check if the keySet needs a shift key
|
||||||
// This is used to display the shift key in the modal
|
// This is used to display the shift key in the modal
|
||||||
// If the current layout is QWERTZ and the keySet contains a key that needs a shift key
|
// If the current layout is QWERTZ and the keySet contains a key that needs a shift key
|
||||||
// If layout is QWERTZ then we add the Shift+keysToModify to fix an known issue
|
// If layout is QWERTZ then we add the Shift+keysToModify to fix an known issue
|
||||||
// https://github.com/chatwoot/chatwoot/issues/9492
|
// https://github.com/chatwoot/chatwoot/issues/9492
|
||||||
const needsShiftKey = keySet => {
|
const needsShiftKey = computed(
|
||||||
return (
|
() => keySet =>
|
||||||
currentLayout.value === LAYOUT_QWERTZ &&
|
currentLayout.value === LAYOUT_QWERTZ &&
|
||||||
keySet.some(key => keysToModifyInQWERTZ.has(key))
|
keySet.some(key => keysToModifyInQWERTZ.has(key))
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
currentLayout.value = await useDetectKeyboardLayout();
|
currentLayout.value = await useDetectKeyboardLayout();
|
||||||
@@ -55,80 +48,46 @@ onMounted(async () => {
|
|||||||
</h5>
|
</h5>
|
||||||
<div class="flex items-center gap-2 mb-1 ml-2">
|
<div class="flex items-center gap-2 mb-1 ml-2">
|
||||||
<Hotkey custom-class="min-h-[28px] min-w-[60px] normal-case key">
|
<Hotkey custom-class="min-h-[28px] min-w-[60px] normal-case key">
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.WINDOWS_KEY_AND_COMMAND_KEY') }}
|
{{ KEYS.WIN }}
|
||||||
</Hotkey>
|
</Hotkey>
|
||||||
<Hotkey custom-class="min-h-[28px] min-w-[36px] key">
|
<Hotkey custom-class="min-h-[28px] min-w-[36px] key">
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.FORWARD_SLASH_KEY') }}
|
{{ KEYS.SLASH }}
|
||||||
</Hotkey>
|
</Hotkey>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 px-8 pt-0 pb-8 gap-x-5 gap-y-3">
|
<div class="grid grid-cols-2 px-8 pt-0 pb-8 gap-x-5 gap-y-3">
|
||||||
<div class="flex justify-between items-center min-w-[25rem]">
|
|
||||||
<h5 class="text-sm text-slate-800 dark:text-slate-100">
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.TITLE.OPEN_CONVERSATION') }}
|
|
||||||
</h5>
|
|
||||||
<div class="flex items-center gap-2 mb-1 ml-2">
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<Hotkey custom-class="min-h-[28px] min-w-[60px] normal-case key">
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.ALT_OR_OPTION_KEY') }}
|
|
||||||
</Hotkey>
|
|
||||||
<Hotkey custom-class="min-h-[28px] w-9 key"> {{ 'J' }} </Hotkey>
|
|
||||||
<span
|
|
||||||
class="flex items-center text-sm font-semibold text-slate-800 dark:text-slate-100"
|
|
||||||
>
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.FORWARD_SLASH_KEY') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Hotkey custom-class="min-h-[28px] min-w-[60px] normal-case key">
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.ALT_OR_OPTION_KEY') }}
|
|
||||||
</Hotkey>
|
|
||||||
<Hotkey custom-class="w-9 key"> {{ 'K' }} </Hotkey>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-between items-center min-w-[25rem]">
|
|
||||||
<h5 class="text-sm text-slate-800 dark:text-slate-100">
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.TITLE.RESOLVE_AND_NEXT') }}
|
|
||||||
</h5>
|
|
||||||
<div class="flex items-center gap-2 mb-1 ml-2">
|
|
||||||
<Hotkey custom-class="min-h-[28px] min-w-[60px] normal-case key">
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.WINDOWS_KEY_AND_COMMAND_KEY') }}
|
|
||||||
</Hotkey>
|
|
||||||
<Hotkey custom-class="min-h-[28px] min-w-[60px] normal-case key">
|
|
||||||
{{ $t('KEYBOARD_SHORTCUTS.KEYS.ALT_OR_OPTION_KEY') }}
|
|
||||||
</Hotkey>
|
|
||||||
<Hotkey custom-class="w-9 key"> {{ 'E' }} </Hotkey>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-for="shortcutKey in shortcutKeys"
|
v-for="shortcut in SHORTCUT_KEYS"
|
||||||
:key="shortcutKey.id"
|
:key="shortcut.id"
|
||||||
class="flex justify-between items-center min-w-[25rem]"
|
class="flex justify-between items-center min-w-[25rem]"
|
||||||
>
|
>
|
||||||
<h5 class="text-sm text-slate-800 min-w-[36px] dark:text-slate-100">
|
<h5 class="text-sm text-slate-800 min-w-[36px] dark:text-slate-100">
|
||||||
{{ title(shortcutKey) }}
|
{{ title(shortcut) }}
|
||||||
</h5>
|
</h5>
|
||||||
<div class="flex items-center gap-2 mb-1 ml-2">
|
<div class="flex items-center gap-2 mb-1 ml-2">
|
||||||
<Hotkey
|
<template v-if="needsShiftKey(shortcut.keySet)">
|
||||||
v-if="needsShiftKey(shortcutKey.keySet)"
|
<Hotkey custom-class="min-h-[28px] min-w-[36px] key">
|
||||||
custom-class="min-h-[28px] min-w-[36px] key"
|
{{ KEYS.SHIFT }}
|
||||||
>
|
</Hotkey>
|
||||||
{{ 'Shift' }}
|
</template>
|
||||||
</Hotkey>
|
|
||||||
<Hotkey
|
<template v-for="(key, index) in shortcut.displayKeys" :key="index">
|
||||||
:class="{ 'min-w-[60px]': shortcutKey.firstKey !== 'Up' }"
|
<template v-if="key !== KEYS.SLASH">
|
||||||
custom-class="min-h-[28px] normal-case key"
|
<Hotkey
|
||||||
>
|
custom-class="min-h-[28px] min-w-[36px] key normal-case"
|
||||||
{{ shortcutKey.firstKey }}
|
>
|
||||||
</Hotkey>
|
{{ key }}
|
||||||
<Hotkey
|
</Hotkey>
|
||||||
:class="{ 'normal-case': shortcutKey.secondKey === 'Down' }"
|
</template>
|
||||||
custom-class="min-h-[28px] min-w-[36px] key"
|
<span
|
||||||
>
|
v-else
|
||||||
{{ shortcutKey.secondKey }}
|
class="flex items-center text-sm font-semibold text-slate-800 dark:text-slate-100"
|
||||||
</Hotkey>
|
>
|
||||||
|
{{ key }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,86 +1,95 @@
|
|||||||
|
export const KEYS = {
|
||||||
|
ALT: 'Alt / ⌥',
|
||||||
|
WIN: 'Win / ⌘',
|
||||||
|
SHIFT: 'Shift',
|
||||||
|
SLASH: '/',
|
||||||
|
UP: 'Up',
|
||||||
|
DOWN: 'Down',
|
||||||
|
};
|
||||||
|
|
||||||
export const SHORTCUT_KEYS = [
|
export const SHORTCUT_KEYS = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
label: 'NAVIGATE_DROPDOWN',
|
label: 'OPEN_CONVERSATION',
|
||||||
firstKey: 'Up',
|
displayKeys: [KEYS.ALT, 'J', KEYS.SLASH, KEYS.ALT, 'K'],
|
||||||
secondKey: 'Down',
|
keySet: ['Alt+KeyJ', 'Alt+KeyK'],
|
||||||
keySet: ['ArrowUp', 'ArrowDown'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
label: 'RESOLVE_CONVERSATION',
|
label: 'RESOLVE_AND_NEXT',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.WIN, KEYS.ALT, 'E'],
|
||||||
secondKey: 'E',
|
keySet: ['$mod+Alt+KeyE'],
|
||||||
keySet: ['Alt+KeyE'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
label: 'GO_TO_CONVERSATION_DASHBOARD',
|
label: 'NAVIGATE_DROPDOWN',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.UP, KEYS.DOWN],
|
||||||
secondKey: 'C',
|
keySet: ['ArrowUp', 'ArrowDown'],
|
||||||
keySet: ['Alt+KeyC'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
label: 'ADD_ATTACHMENT',
|
label: 'RESOLVE_CONVERSATION',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'E'],
|
||||||
secondKey: 'A',
|
keySet: ['Alt+KeyE'],
|
||||||
keySet: ['Alt+KeyA'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
label: 'GO_TO_CONTACTS_DASHBOARD',
|
label: 'GO_TO_CONVERSATION_DASHBOARD',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'C'],
|
||||||
secondKey: 'V',
|
keySet: ['Alt+KeyC'],
|
||||||
keySet: ['Alt+KeyV'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
label: 'TOGGLE_SIDEBAR',
|
label: 'ADD_ATTACHMENT',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.WIN, KEYS.ALT, 'A'],
|
||||||
secondKey: 'O',
|
keySet: ['$mod+Alt+KeyA'],
|
||||||
keySet: ['Alt+KeyO'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: 7,
|
||||||
label: 'GO_TO_REPORTS_SIDEBAR',
|
label: 'GO_TO_CONTACTS_DASHBOARD',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'V'],
|
||||||
secondKey: 'R',
|
keySet: ['Alt+KeyV'],
|
||||||
keySet: ['Alt+KeyR'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: 8,
|
||||||
label: 'MOVE_TO_NEXT_TAB',
|
label: 'TOGGLE_SIDEBAR',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'O'],
|
||||||
secondKey: 'N',
|
keySet: ['Alt+KeyO'],
|
||||||
keySet: ['Alt+KeyN'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 9,
|
id: 9,
|
||||||
label: 'GO_TO_SETTINGS',
|
label: 'GO_TO_REPORTS_SIDEBAR',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'R'],
|
||||||
secondKey: 'S',
|
keySet: ['Alt+KeyR'],
|
||||||
keySet: ['Alt+KeyS'],
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
label: 'MOVE_TO_NEXT_TAB',
|
||||||
|
displayKeys: [KEYS.ALT, 'N'],
|
||||||
|
keySet: ['Alt+KeyN'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 11,
|
||||||
label: 'SWITCH_TO_PRIVATE_NOTE',
|
label: 'GO_TO_SETTINGS',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'S'],
|
||||||
secondKey: 'P',
|
keySet: ['Alt+KeyS'],
|
||||||
keySet: ['Alt+KeyP'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12,
|
id: 12,
|
||||||
label: 'SWITCH_TO_REPLY',
|
label: 'SWITCH_TO_PRIVATE_NOTE',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'P'],
|
||||||
secondKey: 'L',
|
keySet: ['Alt+KeyP'],
|
||||||
keySet: ['Alt+KeyL'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 13,
|
id: 13,
|
||||||
|
label: 'SWITCH_TO_REPLY',
|
||||||
|
displayKeys: [KEYS.ALT, 'L'],
|
||||||
|
keySet: ['Alt+KeyL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
label: 'TOGGLE_SNOOZE_DROPDOWN',
|
label: 'TOGGLE_SNOOZE_DROPDOWN',
|
||||||
firstKey: 'Alt / ⌥',
|
displayKeys: [KEYS.ALT, 'M'],
|
||||||
secondKey: 'M',
|
|
||||||
keySet: ['Alt+KeyM'],
|
keySet: ['Alt+KeyM'],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -389,11 +389,6 @@
|
|||||||
"SWITCH_TO_PRIVATE_NOTE": "Switch to Private Note",
|
"SWITCH_TO_PRIVATE_NOTE": "Switch to Private Note",
|
||||||
"SWITCH_TO_REPLY": "Switch to Reply",
|
"SWITCH_TO_REPLY": "Switch to Reply",
|
||||||
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
|
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
|
||||||
},
|
|
||||||
"KEYS": {
|
|
||||||
"WINDOWS_KEY_AND_COMMAND_KEY": "Win / ⌘",
|
|
||||||
"ALT_OR_OPTION_KEY": "Alt / ⌥",
|
|
||||||
"FORWARD_SLASH_KEY": "/"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user