mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 19:17:48 +00:00 
			
		
		
		
	chore: Improvement is keyboard shortcuts (#10925)
This commit is contained in:
		| @@ -127,7 +127,7 @@ export default { | ||||
|     const uploadRef = ref(false); | ||||
|  | ||||
|     const keyboardEvents = { | ||||
|       'Alt+KeyA': { | ||||
|       '$mod+Alt+KeyA': { | ||||
|         action: () => { | ||||
|           // 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. | ||||
|   | ||||
| @@ -1,41 +1,34 @@ | ||||
| <script setup> | ||||
| import { ref, onMounted } from 'vue'; | ||||
| import { ref, computed, onMounted } from 'vue'; | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import { useDetectKeyboardLayout } from 'dashboard/composables/useDetectKeyboardLayout'; | ||||
| import { SHORTCUT_KEYS } from './constants'; | ||||
| import { SHORTCUT_KEYS, KEYS } from './constants'; | ||||
| import { | ||||
|   LAYOUT_QWERTZ, | ||||
|   keysToModifyInQWERTZ, | ||||
| } from 'shared/helpers/KeyboardHelpers'; | ||||
| import Hotkey from 'dashboard/components/base/Hotkey.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   show: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| defineProps({ show: Boolean }); | ||||
| defineEmits(['close']); | ||||
|  | ||||
| const { t } = useI18n(); | ||||
|  | ||||
| const shortcutKeys = SHORTCUT_KEYS; | ||||
| 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 | ||||
| // 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 layout is QWERTZ then we add the Shift+keysToModify to fix an known issue | ||||
| // https://github.com/chatwoot/chatwoot/issues/9492 | ||||
| const needsShiftKey = keySet => { | ||||
|   return ( | ||||
| const needsShiftKey = computed( | ||||
|   () => keySet => | ||||
|     currentLayout.value === LAYOUT_QWERTZ && | ||||
|     keySet.some(key => keysToModifyInQWERTZ.has(key)) | ||||
|   ); | ||||
| }; | ||||
| ); | ||||
|  | ||||
| onMounted(async () => { | ||||
|   currentLayout.value = await useDetectKeyboardLayout(); | ||||
| @@ -55,80 +48,46 @@ onMounted(async () => { | ||||
|           </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') }} | ||||
|               {{ KEYS.WIN }} | ||||
|             </Hotkey> | ||||
|             <Hotkey custom-class="min-h-[28px] min-w-[36px] key"> | ||||
|               {{ $t('KEYBOARD_SHORTCUTS.KEYS.FORWARD_SLASH_KEY') }} | ||||
|               {{ KEYS.SLASH }} | ||||
|             </Hotkey> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <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 | ||||
|           v-for="shortcutKey in shortcutKeys" | ||||
|           :key="shortcutKey.id" | ||||
|           v-for="shortcut in SHORTCUT_KEYS" | ||||
|           :key="shortcut.id" | ||||
|           class="flex justify-between items-center min-w-[25rem]" | ||||
|         > | ||||
|           <h5 class="text-sm text-slate-800 min-w-[36px] dark:text-slate-100"> | ||||
|             {{ title(shortcutKey) }} | ||||
|             {{ title(shortcut) }} | ||||
|           </h5> | ||||
|           <div class="flex items-center gap-2 mb-1 ml-2"> | ||||
|             <Hotkey | ||||
|               v-if="needsShiftKey(shortcutKey.keySet)" | ||||
|               custom-class="min-h-[28px] min-w-[36px] key" | ||||
|             > | ||||
|               {{ 'Shift' }} | ||||
|             </Hotkey> | ||||
|             <Hotkey | ||||
|               :class="{ 'min-w-[60px]': shortcutKey.firstKey !== 'Up' }" | ||||
|               custom-class="min-h-[28px] normal-case key" | ||||
|             > | ||||
|               {{ shortcutKey.firstKey }} | ||||
|             </Hotkey> | ||||
|             <Hotkey | ||||
|               :class="{ 'normal-case': shortcutKey.secondKey === 'Down' }" | ||||
|               custom-class="min-h-[28px] min-w-[36px] key" | ||||
|             > | ||||
|               {{ shortcutKey.secondKey }} | ||||
|             </Hotkey> | ||||
|             <template v-if="needsShiftKey(shortcut.keySet)"> | ||||
|               <Hotkey custom-class="min-h-[28px] min-w-[36px] key"> | ||||
|                 {{ KEYS.SHIFT }} | ||||
|               </Hotkey> | ||||
|             </template> | ||||
|  | ||||
|             <template v-for="(key, index) in shortcut.displayKeys" :key="index"> | ||||
|               <template v-if="key !== KEYS.SLASH"> | ||||
|                 <Hotkey | ||||
|                   custom-class="min-h-[28px] min-w-[36px] key normal-case" | ||||
|                 > | ||||
|                   {{ key }} | ||||
|                 </Hotkey> | ||||
|               </template> | ||||
|               <span | ||||
|                 v-else | ||||
|                 class="flex items-center text-sm font-semibold text-slate-800 dark:text-slate-100" | ||||
|               > | ||||
|                 {{ key }} | ||||
|               </span> | ||||
|             </template> | ||||
|           </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 = [ | ||||
|   { | ||||
|     id: 1, | ||||
|     label: 'NAVIGATE_DROPDOWN', | ||||
|     firstKey: 'Up', | ||||
|     secondKey: 'Down', | ||||
|     keySet: ['ArrowUp', 'ArrowDown'], | ||||
|     label: 'OPEN_CONVERSATION', | ||||
|     displayKeys: [KEYS.ALT, 'J', KEYS.SLASH, KEYS.ALT, 'K'], | ||||
|     keySet: ['Alt+KeyJ', 'Alt+KeyK'], | ||||
|   }, | ||||
|   { | ||||
|     id: 2, | ||||
|     label: 'RESOLVE_CONVERSATION', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'E', | ||||
|     keySet: ['Alt+KeyE'], | ||||
|     label: 'RESOLVE_AND_NEXT', | ||||
|     displayKeys: [KEYS.WIN, KEYS.ALT, 'E'], | ||||
|     keySet: ['$mod+Alt+KeyE'], | ||||
|   }, | ||||
|   { | ||||
|     id: 3, | ||||
|     label: 'GO_TO_CONVERSATION_DASHBOARD', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'C', | ||||
|     keySet: ['Alt+KeyC'], | ||||
|     label: 'NAVIGATE_DROPDOWN', | ||||
|     displayKeys: [KEYS.UP, KEYS.DOWN], | ||||
|     keySet: ['ArrowUp', 'ArrowDown'], | ||||
|   }, | ||||
|   { | ||||
|     id: 4, | ||||
|     label: 'ADD_ATTACHMENT', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'A', | ||||
|     keySet: ['Alt+KeyA'], | ||||
|     label: 'RESOLVE_CONVERSATION', | ||||
|     displayKeys: [KEYS.ALT, 'E'], | ||||
|     keySet: ['Alt+KeyE'], | ||||
|   }, | ||||
|   { | ||||
|     id: 5, | ||||
|     label: 'GO_TO_CONTACTS_DASHBOARD', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'V', | ||||
|     keySet: ['Alt+KeyV'], | ||||
|     label: 'GO_TO_CONVERSATION_DASHBOARD', | ||||
|     displayKeys: [KEYS.ALT, 'C'], | ||||
|     keySet: ['Alt+KeyC'], | ||||
|   }, | ||||
|   { | ||||
|     id: 6, | ||||
|     label: 'TOGGLE_SIDEBAR', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'O', | ||||
|     keySet: ['Alt+KeyO'], | ||||
|     label: 'ADD_ATTACHMENT', | ||||
|     displayKeys: [KEYS.WIN, KEYS.ALT, 'A'], | ||||
|     keySet: ['$mod+Alt+KeyA'], | ||||
|   }, | ||||
|   { | ||||
|     id: 7, | ||||
|     label: 'GO_TO_REPORTS_SIDEBAR', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'R', | ||||
|     keySet: ['Alt+KeyR'], | ||||
|     label: 'GO_TO_CONTACTS_DASHBOARD', | ||||
|     displayKeys: [KEYS.ALT, 'V'], | ||||
|     keySet: ['Alt+KeyV'], | ||||
|   }, | ||||
|   { | ||||
|     id: 8, | ||||
|     label: 'MOVE_TO_NEXT_TAB', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'N', | ||||
|     keySet: ['Alt+KeyN'], | ||||
|     label: 'TOGGLE_SIDEBAR', | ||||
|     displayKeys: [KEYS.ALT, 'O'], | ||||
|     keySet: ['Alt+KeyO'], | ||||
|   }, | ||||
|   { | ||||
|     id: 9, | ||||
|     label: 'GO_TO_SETTINGS', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'S', | ||||
|     keySet: ['Alt+KeyS'], | ||||
|     label: 'GO_TO_REPORTS_SIDEBAR', | ||||
|     displayKeys: [KEYS.ALT, 'R'], | ||||
|     keySet: ['Alt+KeyR'], | ||||
|   }, | ||||
|   { | ||||
|     id: 10, | ||||
|     label: 'MOVE_TO_NEXT_TAB', | ||||
|     displayKeys: [KEYS.ALT, 'N'], | ||||
|     keySet: ['Alt+KeyN'], | ||||
|   }, | ||||
|   { | ||||
|     id: 11, | ||||
|     label: 'SWITCH_TO_PRIVATE_NOTE', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'P', | ||||
|     keySet: ['Alt+KeyP'], | ||||
|     label: 'GO_TO_SETTINGS', | ||||
|     displayKeys: [KEYS.ALT, 'S'], | ||||
|     keySet: ['Alt+KeyS'], | ||||
|   }, | ||||
|   { | ||||
|     id: 12, | ||||
|     label: 'SWITCH_TO_REPLY', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'L', | ||||
|     keySet: ['Alt+KeyL'], | ||||
|     label: 'SWITCH_TO_PRIVATE_NOTE', | ||||
|     displayKeys: [KEYS.ALT, 'P'], | ||||
|     keySet: ['Alt+KeyP'], | ||||
|   }, | ||||
|   { | ||||
|     id: 13, | ||||
|     label: 'SWITCH_TO_REPLY', | ||||
|     displayKeys: [KEYS.ALT, 'L'], | ||||
|     keySet: ['Alt+KeyL'], | ||||
|   }, | ||||
|   { | ||||
|     id: 14, | ||||
|     label: 'TOGGLE_SNOOZE_DROPDOWN', | ||||
|     firstKey: 'Alt / ⌥', | ||||
|     secondKey: 'M', | ||||
|     displayKeys: [KEYS.ALT, 'M'], | ||||
|     keySet: ['Alt+KeyM'], | ||||
|   }, | ||||
| ]; | ||||
|   | ||||
| @@ -389,11 +389,6 @@ | ||||
|       "SWITCH_TO_PRIVATE_NOTE": "Switch to Private Note", | ||||
|       "SWITCH_TO_REPLY": "Switch to Reply", | ||||
|       "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
	 Sivin Varghese
					Sivin Varghese