From a140e82321bd09c3d12becfdc94cb4a2a9a016d4 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 8 Dec 2022 15:19:22 +0530 Subject: [PATCH] refactor: use App level shortcut --- src/App.vue | 8 +++ src/components/SearchBar.vue | 94 +++++++++++++++++------------------- src/shims-vue-custom.d.ts | 2 +- src/utils/vueUtils.ts | 84 +++++++++++++++++++++++++++++--- 4 files changed, 129 insertions(+), 59 deletions(-) diff --git a/src/App.vue b/src/App.vue index eb8a75e7..ab181b43 100644 --- a/src/App.vue +++ b/src/App.vue @@ -52,20 +52,27 @@ import { checkForUpdates } from './utils/ipcCalls'; import { updateConfigFiles } from './utils/misc'; import { Search } from './utils/search'; import { routeTo } from './utils/ui'; +import { Shortcuts, useKeys } from './utils/vueUtils'; export default { name: 'App', + setup() { + return { keys: useKeys() }; + }, data() { return { activeScreen: null, dbPath: '', companyName: '', searcher: null, + shortcuts: null, }; }, provide() { return { searcher: computed(() => this.searcher), + shortcuts: computed(() => this.shortcuts), + keys: computed(() => this.keys), }; }, components: { @@ -75,6 +82,7 @@ export default { WindowsTitleBar, }, async mounted() { + this.shortcuts = new Shortcuts(this.keys); const lastSelectedFilePath = fyo.config.get( ConfigKeys.LastSelectedFilePath, null diff --git a/src/components/SearchBar.vue b/src/components/SearchBar.vue index 5ff9830f..3d3ff267 100644 --- a/src/components/SearchBar.vue +++ b/src/components/SearchBar.vue @@ -11,7 +11,11 @@ - +
{ - if ( - keys.size === 2 && - keys.has('KeyK') && - (keys.has('MetaLeft') || keys.has('ControlLeft')) - ) { - this.open(); - } - - if (!this.openModal) { - return; - } - - if (keys.size === 1 && keys.has('Escape')) { - this.close(); - } - - const input = this.$refs.input; - if (!getIsNullOrUndef(input) && document.activeElement !== input) { - input.focus(); - } - - this.setFilter(keys); - }); this.openModal = false; }, activated() { + this.setShortcuts(); this.openModal = false; }, + deactivated() { + this.deleteShortcuts(); + }, methods: { openDocs() { openLink('https://docs.frappebooks.com/' + docsPathMap.Search); }, - setFilter(keys) { - if (!keys.has('MetaLeft') && !keys.has('ControlLeft')) { - return; + getShortcuts() { + const modKey = getModKeyCode(this.platform); + const ifOpen = (cb) => () => this.openModal && cb(); + const ifClose = (cb) => () => !this.openModal && cb(); + + const shortcuts = [ + { shortcut: ['KeyK', modKey], callback: ifClose(() => this.open()) }, + { shortcut: ['Escape'], callback: ifOpen(() => this.close()) }, + ]; + + for (const i in searchGroups) { + shortcuts.push({ + shortcut: [modKey, `Digit${Number(i) + 1}`], + callback: ifOpen(() => { + const group = searchGroups[i]; + const value = this.searcher.filters.groupFilters[group]; + if (typeof value !== 'boolean') { + return; + } + + this.searcher.set(group, !value); + }), + }); } - if (!keys.size === 2) { - return; + return shortcuts; + }, + setShortcuts() { + for (const { shortcut, callback } of this.getShortcuts()) { + this.shortcuts.set(shortcut, callback); } - - const matches = [...keys].join(',').match(/Digit(\d+)/); - if (!matches) { - return; + }, + deleteShortcuts() { + for (const { shortcut } of this.getShortcuts()) { + this.shortcuts.delete(shortcut); } - - const digit = matches[1]; - const index = safeParseInt(digit) - 1; - const group = searchGroups[index]; - const value = this.searcher.filters.groupFilters[group]; - if (!group || typeof value !== 'boolean') { - return; - } - - this.searcher.set(group, !value); }, modKey(key) { key = key.toUpperCase(); diff --git a/src/shims-vue-custom.d.ts b/src/shims-vue-custom.d.ts index 7b241cd3..54c1d9b8 100644 --- a/src/shims-vue-custom.d.ts +++ b/src/shims-vue-custom.d.ts @@ -5,6 +5,6 @@ declare module 'vue' { interface ComponentCustomProperties { t: (...args: TranslationLiteral[]) => string; fyo: Fyo; - platform: string; + platform: 'Windows' | 'Linux' | 'Mac'; } } diff --git a/src/utils/vueUtils.ts b/src/utils/vueUtils.ts index 8a476256..4a266ddd 100644 --- a/src/utils/vueUtils.ts +++ b/src/utils/vueUtils.ts @@ -1,19 +1,81 @@ -import { onMounted, onUnmounted, Ref, ref } from 'vue'; +import { onMounted, onUnmounted, Ref, ref, watch } from 'vue'; -export function useKeys(callback?: (keys: Set) => void) { - const keys: Ref> = ref(new Set()); +interface Keys { + pressed: Set; + alt: boolean; + ctrl: boolean; + meta: boolean; + shift: boolean; + repeat: boolean; +} + +export class Shortcuts { + keys: Ref; + shortcuts: Map; + + constructor(keys?: Ref) { + this.keys = keys ?? useKeys(); + this.shortcuts = new Map(); + + watch(this.keys, (keys) => { + this.#trigger(keys); + }); + } + + #trigger(keys: Keys) { + const key = Array.from(keys.pressed).sort().join('+'); + this.shortcuts.get(key)?.(); + } + + has(shortcut: string[]) { + const key = shortcut.sort().join('+'); + return this.shortcuts.has(key); + } + + set(shortcut: string[], callback: Function, removeIfSet: boolean = true) { + const key = shortcut.sort().join('+'); + if (removeIfSet) { + this.shortcuts.delete(key); + } + + if (this.shortcuts.has(key)) { + throw new Error(`Shortcut ${key} already exists.`); + } + + this.shortcuts.set(key, callback); + } + + delete(shortcut: string[]) { + const key = shortcut.sort().join('+'); + this.shortcuts.delete(key); + } +} + +export function useKeys() { + const keys: Ref = ref({ + pressed: new Set(), + alt: false, + ctrl: false, + meta: false, + shift: false, + repeat: false, + }); const keydownListener = (e: KeyboardEvent) => { - keys.value.add(e.code); - callback?.(keys.value); + keys.value.pressed.add(e.code); + keys.value.alt = e.altKey; + keys.value.ctrl = e.ctrlKey; + keys.value.meta = e.metaKey; + keys.value.shift = e.shiftKey; + keys.value.repeat = e.repeat; }; const keyupListener = (e: KeyboardEvent) => { - keys.value.delete(e.code); + keys.value.pressed.delete(e.code); // Key up won't trigger on macOS for other keys. if (e.code === 'MetaLeft') { - keys.value.clear(); + keys.value.pressed.clear(); } }; @@ -47,3 +109,11 @@ export function useMouseLocation() { return loc; } + +export function getModKeyCode(platform: 'Windows' | 'Linux' | 'Mac') { + if (platform === 'Mac') { + return 'MetaLeft'; + } + + return 'CtrlLeft'; +}