2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 19:09:01 +00:00

refactor: use App level shortcut

This commit is contained in:
18alantom 2022-12-08 15:19:22 +05:30
parent 0bc22d847b
commit a140e82321
4 changed files with 129 additions and 59 deletions

View File

@ -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

View File

@ -11,7 +11,11 @@
</div>
<!-- Search Modal -->
<Modal :open-modal="openModal" @closemodal="close" :set-close-listener="false">
<Modal
:open-modal="openModal"
@closemodal="close"
:set-close-listener="false"
>
<!-- Search Input -->
<div class="p-1">
<input
@ -191,18 +195,13 @@ import { getBgTextColorClass } from 'src/utils/colors';
import { openLink } from 'src/utils/ipcCalls';
import { docsPathMap } from 'src/utils/misc';
import { getGroupLabelMap, searchGroups } from 'src/utils/search';
import { useKeys } from 'src/utils/vueUtils';
import { getIsNullOrUndef } from 'utils/';
import { getModKeyCode } from 'src/utils/vueUtils';
import { safeParseInt } from 'utils/index';
import { nextTick, watch } from 'vue';
import Button from './Button.vue';
import Modal from './Modal.vue';
export default {
setup() {
const keys = useKeys();
return { keys };
},
data() {
return {
idx: 0,
@ -214,69 +213,62 @@ export default {
allowedLimits: [50, 100, 500, -1],
};
},
inject: ['searcher'],
inject: ['searcher', 'shortcuts'],
components: { Modal, Button },
async mounted() {
if (fyo.store.isDevelopment) {
window.search = this;
}
watch(this.keys, (keys) => {
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();

View File

@ -5,6 +5,6 @@ declare module 'vue' {
interface ComponentCustomProperties {
t: (...args: TranslationLiteral[]) => string;
fyo: Fyo;
platform: string;
platform: 'Windows' | 'Linux' | 'Mac';
}
}

View File

@ -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<string>) => void) {
const keys: Ref<Set<string>> = ref(new Set());
interface Keys {
pressed: Set<string>;
alt: boolean;
ctrl: boolean;
meta: boolean;
shift: boolean;
repeat: boolean;
}
export class Shortcuts {
keys: Ref<Keys>;
shortcuts: Map<string, Function>;
constructor(keys?: Ref<Keys>) {
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<Keys> = ref({
pressed: new Set<string>(),
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';
}