mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 19:17:48 +00:00 
			
		
		
		
	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:
		| @@ -175,6 +175,9 @@ export default { | |||||||
|       const allConversations = document.querySelectorAll( |       const allConversations = document.querySelectorAll( | ||||||
|         '.conversations-list .conversation' |         '.conversations-list .conversation' | ||||||
|       ); |       ); | ||||||
|  |       if (hasPressedAltAndMKey(e)) { | ||||||
|  |         this.$refs.arrowDownButton.$el.click(); | ||||||
|  |       } | ||||||
|       if (hasPressedAltAndEKey(e)) { |       if (hasPressedAltAndEKey(e)) { | ||||||
|         const activeConversation = document.querySelector( |         const activeConversation = document.querySelector( | ||||||
|           'div.conversations-list div.conversation.active' |           'div.conversations-list div.conversation.active' | ||||||
|   | |||||||
| @@ -50,10 +50,17 @@ | |||||||
|         :show="showOptionsMenu" |         :show="showOptionsMenu" | ||||||
|         @toggle-accounts="toggleAccountModal" |         @toggle-accounts="toggleAccountModal" | ||||||
|         @show-support-chat-window="toggleSupportChatWindow" |         @show-support-chat-window="toggleSupportChatWindow" | ||||||
|  |         @key-shortcut-modal="toggleKeyShortcutModal" | ||||||
|         @close="toggleOptions" |         @close="toggleOptions" | ||||||
|       /> |       /> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  |     <woot-key-shortcut-modal | ||||||
|  |       v-if="showShortcutModal" | ||||||
|  |       @close="closeKeyShortcutModal" | ||||||
|  |       @clickaway="closeKeyShortcutModal" | ||||||
|  |     /> | ||||||
|  |  | ||||||
|     <account-selector |     <account-selector | ||||||
|       :show-account-modal="showAccountModal" |       :show-account-modal="showAccountModal" | ||||||
|       @close-account-modal="toggleAccountModal" |       @close-account-modal="toggleAccountModal" | ||||||
| @@ -86,6 +93,9 @@ import OptionsMenu from './sidebarComponents/OptionsMenu.vue'; | |||||||
| import AccountSelector from './sidebarComponents/AccountSelector.vue'; | import AccountSelector from './sidebarComponents/AccountSelector.vue'; | ||||||
| import AddAccountModal from './sidebarComponents/AddAccountModal.vue'; | import AddAccountModal from './sidebarComponents/AddAccountModal.vue'; | ||||||
| import AddLabelModal from '../../routes/dashboard/settings/labels/AddLabel'; | 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 { | export default { | ||||||
|   components: { |   components: { | ||||||
| @@ -97,14 +107,16 @@ export default { | |||||||
|     AccountSelector, |     AccountSelector, | ||||||
|     AddAccountModal, |     AddAccountModal, | ||||||
|     AddLabelModal, |     AddLabelModal, | ||||||
|  |     WootKeyShortcutModal, | ||||||
|   }, |   }, | ||||||
|   mixins: [adminMixin, alertMixin], |   mixins: [adminMixin, alertMixin, eventListenerMixins], | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       showOptionsMenu: false, |       showOptionsMenu: false, | ||||||
|       showAccountModal: false, |       showAccountModal: false, | ||||||
|       showCreateAccountModal: false, |       showCreateAccountModal: false, | ||||||
|       showAddLabelModal: false, |       showAddLabelModal: false, | ||||||
|  |       showShortcutModal: false, | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -254,7 +266,19 @@ export default { | |||||||
|     this.$store.dispatch('teams/get'); |     this.$store.dispatch('teams/get'); | ||||||
|     this.setChatwootUser(); |     this.setChatwootUser(); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   methods: { |   methods: { | ||||||
|  |     toggleKeyShortcutModal() { | ||||||
|  |       this.showShortcutModal = true; | ||||||
|  |     }, | ||||||
|  |     closeKeyShortcutModal() { | ||||||
|  |       this.showShortcutModal = false; | ||||||
|  |     }, | ||||||
|  |     handleKeyEvents(e) { | ||||||
|  |       if (hasPressedCommandAndForwardSlash(e)) { | ||||||
|  |         this.toggleKeyShortcutModal(); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     toggleSupportChatWindow() { |     toggleSupportChatWindow() { | ||||||
|       window.$chatwoot.toggle(); |       window.$chatwoot.toggle(); | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -26,6 +26,16 @@ | |||||||
|             Contact Support |             Contact Support | ||||||
|           </woot-button> |           </woot-button> | ||||||
|         </woot-dropdown-item> |         </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> |         <woot-dropdown-item> | ||||||
|           <router-link |           <router-link | ||||||
|             :to="`/app/accounts/${accountId}/profile/settings`" |             :to="`/app/accounts/${accountId}/profile/settings`" | ||||||
|   | |||||||
| @@ -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 = {}; | ||||||
| @@ -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> | ||||||
| @@ -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', | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
| @@ -98,6 +98,7 @@ | |||||||
|     "CHANGE_ACCOUNTS": "Switch Account", |     "CHANGE_ACCOUNTS": "Switch Account", | ||||||
|     "SELECTOR_SUBTITLE": "Select an account from the following list", |     "SELECTOR_SUBTITLE": "Select an account from the following list", | ||||||
|     "PROFILE_SETTINGS": "Profile Settings", |     "PROFILE_SETTINGS": "Profile Settings", | ||||||
|  |     "KEYBOARD_SHORTCUTS": "Keyboard Shortcuts", | ||||||
|     "LOGOUT": "Logout" |     "LOGOUT": "Logout" | ||||||
|   }, |   }, | ||||||
|   "APP_GLOBAL": { |   "APP_GLOBAL": { | ||||||
| @@ -159,5 +160,30 @@ | |||||||
|       }, |       }, | ||||||
|       "SUBMIT": "Submit" |       "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": "/" | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,10 @@ export const hasPressedShift = e => { | |||||||
|   return e.shiftKey; |   return e.shiftKey; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | export const hasPressedCommandAndForwardSlash = e => { | ||||||
|  |   return e.metaKey && e.keyCode === 191; | ||||||
|  | }; | ||||||
|  |  | ||||||
| export const hasPressedAltAndCKey = e => { | export const hasPressedAltAndCKey = e => { | ||||||
|   return e.altKey && e.keyCode === 67; |   return e.altKey && e.keyCode === 67; | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Sivin Varghese
					Sivin Varghese