mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
refactor: remove global shortcuts, move Shortcuts
- update injections in a few components - use Shift+Backspace for back (prevent clashes with the \b) - use string context for single instance components
This commit is contained in:
parent
3fd4cd16be
commit
e1b502b138
@ -57,9 +57,9 @@ import { checkForUpdates } from './utils/ipcCalls';
|
||||
import { updateConfigFiles } from './utils/misc';
|
||||
import { updatePrintTemplates } from './utils/printTemplates';
|
||||
import { Search } from './utils/search';
|
||||
import { setGlobalShortcuts } from './utils/shortcuts';
|
||||
import { Shortcuts } from './utils/shortcuts';
|
||||
import { routeTo } from './utils/ui';
|
||||
import { Shortcuts, useKeys } from './utils/vueUtils';
|
||||
import { useKeys } from './utils/vueUtils';
|
||||
|
||||
enum Screen {
|
||||
Desk = 'Desk',
|
||||
@ -102,7 +102,7 @@ export default defineComponent({
|
||||
WindowsTitleBar,
|
||||
},
|
||||
async mounted() {
|
||||
setGlobalShortcuts(this.shortcuts as Shortcuts);
|
||||
// setGlobalShortcuts(this.shortcuts as Shortcuts);
|
||||
this.setInitialScreen();
|
||||
},
|
||||
watch: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<a
|
||||
ref="backlink"
|
||||
class="
|
||||
cursor-pointer
|
||||
font-semibold
|
||||
@ -16,6 +17,26 @@
|
||||
</a>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { shortcutsKey } from 'src/utils/injectionKeys';
|
||||
import { ref, inject } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({});
|
||||
|
||||
const COMPONENT_NAME = 'BackLink';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
backlink: ref<HTMLAnchorElement | null>(null),
|
||||
shortcuts: inject(shortcutsKey),
|
||||
};
|
||||
},
|
||||
activated() {
|
||||
this.shortcuts?.shift.set(COMPONENT_NAME, ['Backspace'], () => {
|
||||
this.backlink?.click();
|
||||
});
|
||||
},
|
||||
deactivated() {
|
||||
this.shortcuts?.delete(COMPONENT_NAME);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -88,15 +88,21 @@
|
||||
<script>
|
||||
import { Report } from 'reports/Report';
|
||||
import { isNumeric } from 'src/utils';
|
||||
import { languageDirectionKey } from 'src/utils/injectionKeys';
|
||||
import { defineComponent } from 'vue';
|
||||
import Paginator from '../Paginator.vue';
|
||||
import WithScroll from '../WithScroll.vue';
|
||||
import { inject } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
report: Report,
|
||||
},
|
||||
inject: ['languageDirection'],
|
||||
setup() {
|
||||
return {
|
||||
languageDirection: inject(languageDirectionKey),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
wconst: 8,
|
||||
|
@ -213,6 +213,8 @@ import { defineComponent, inject, nextTick } from 'vue';
|
||||
import Button from './Button.vue';
|
||||
import Modal from './Modal.vue';
|
||||
|
||||
const COMPONENT_NAME = 'SearchBar';
|
||||
|
||||
type SchemaFilters = { value: string; label: string; index: number }[];
|
||||
|
||||
export default defineComponent({
|
||||
@ -247,7 +249,7 @@ export default defineComponent({
|
||||
this.openModal = false;
|
||||
},
|
||||
deactivated() {
|
||||
this.shortcuts!.delete(this);
|
||||
this.shortcuts?.delete(COMPONENT_NAME);
|
||||
},
|
||||
methods: {
|
||||
openDocs() {
|
||||
@ -287,7 +289,7 @@ export default defineComponent({
|
||||
},
|
||||
setShortcuts() {
|
||||
for (const { shortcut, callback } of this.getShortcuts()) {
|
||||
this.shortcuts!.pmod.set(this, [shortcut], callback);
|
||||
this.shortcuts!.pmod.set(COMPONENT_NAME, [shortcut], callback);
|
||||
}
|
||||
},
|
||||
modKeyText(key: string): string {
|
||||
|
@ -20,7 +20,7 @@
|
||||
v-for="(s, i) in g.shortcuts"
|
||||
:key="g.label + ' ' + i"
|
||||
class="grid gap-4 items-start"
|
||||
style="grid-template-columns: 6rem auto"
|
||||
style="grid-template-columns: 8rem auto"
|
||||
>
|
||||
<ShortcutKeys class="text-base" :keys="s.shortcut" />
|
||||
<div class="whitespace-normal text-base">{{ s.description }}</div>
|
||||
@ -69,7 +69,7 @@ export default defineComponent({
|
||||
description: t`Open Quick Search`,
|
||||
},
|
||||
{
|
||||
shortcut: [ShortcutKey.delete],
|
||||
shortcut: [ShortcutKey.shift, ShortcutKey.delete],
|
||||
description: t`Go back to the previous page`,
|
||||
},
|
||||
{
|
||||
|
@ -190,6 +190,8 @@ import Icon from './Icon.vue';
|
||||
import Modal from './Modal.vue';
|
||||
import ShortcutsHelper from './ShortcutsHelper.vue';
|
||||
|
||||
const COMPONENT_NAME = 'Sidebar';
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['change-db-file'],
|
||||
setup() {
|
||||
@ -231,15 +233,15 @@ export default defineComponent({
|
||||
this.setActiveGroup();
|
||||
});
|
||||
|
||||
this.shortcuts?.shift.set(this, ['KeyH'], () => {
|
||||
this.shortcuts?.shift.set(COMPONENT_NAME, ['KeyH'], () => {
|
||||
if (document.body === document.activeElement) {
|
||||
this.toggleSidebar();
|
||||
}
|
||||
});
|
||||
this.shortcuts?.set(this, ['F1'], () => this.openDocumentation());
|
||||
this.shortcuts?.set(COMPONENT_NAME, ['F1'], () => this.openDocumentation());
|
||||
},
|
||||
unmounted() {
|
||||
this.shortcuts?.delete(this);
|
||||
this.shortcuts?.delete(COMPONENT_NAME);
|
||||
},
|
||||
methods: {
|
||||
routeTo,
|
||||
|
@ -146,12 +146,14 @@ import { isCredit } from 'models/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { languageDirectionKey } from 'src/utils/injectionKeys';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { openQuickEdit } from 'src/utils/ui';
|
||||
import { getMapFromList, removeAtIndex } from 'utils/index';
|
||||
import { nextTick } from 'vue';
|
||||
import Button from '../components/Button.vue';
|
||||
import { inject } from 'vue';
|
||||
import { handleErrorWithDialog } from '../errorHandling';
|
||||
|
||||
export default {
|
||||
@ -159,6 +161,11 @@ export default {
|
||||
Button,
|
||||
PageHeader,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
languageDirection: inject(languageDirectionKey),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isAllCollapsed: true,
|
||||
@ -172,7 +179,6 @@ export default {
|
||||
refetchTotals: false,
|
||||
};
|
||||
},
|
||||
inject: ['languageDirection'],
|
||||
async mounted() {
|
||||
await this.setTotalDebitAndCredit();
|
||||
fyo.doc.observer.on('sync:AccountingLedgerEntry', () => {
|
||||
|
@ -246,6 +246,8 @@ import TemplateBuilderHint from './TemplateBuilderHint.vue';
|
||||
import TemplateEditor from './TemplateEditor.vue';
|
||||
import { inject } from 'vue';
|
||||
|
||||
const COMPONENT_NAME = 'TemplateBuilder';
|
||||
|
||||
export default defineComponent({
|
||||
props: { name: String },
|
||||
components: {
|
||||
@ -313,7 +315,7 @@ export default defineComponent({
|
||||
},
|
||||
deactivated(): void {
|
||||
docsPathRef.value = '';
|
||||
this.shortcuts?.delete(this);
|
||||
this.shortcuts?.delete(COMPONENT_NAME);
|
||||
},
|
||||
methods: {
|
||||
setShortcuts() {
|
||||
@ -321,13 +323,13 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
|
||||
this.shortcuts.ctrl.set(this, ['Enter'], this.setTemplate);
|
||||
this.shortcuts.ctrl.set(this, ['KeyE'], this.toggleEditMode);
|
||||
this.shortcuts.ctrl.set(this, ['KeyH'], this.toggleShowHints);
|
||||
this.shortcuts.ctrl.set(this, ['Equal'], () =>
|
||||
this.shortcuts.ctrl.set(COMPONENT_NAME, ['Enter'], this.setTemplate);
|
||||
this.shortcuts.ctrl.set(COMPONENT_NAME, ['KeyE'], this.toggleEditMode);
|
||||
this.shortcuts.ctrl.set(COMPONENT_NAME, ['KeyH'], this.toggleShowHints);
|
||||
this.shortcuts.ctrl.set(COMPONENT_NAME, ['Equal'], () =>
|
||||
this.setScale(this.scale + 0.1)
|
||||
);
|
||||
this.shortcuts.ctrl.set(this, ['Minus'], () =>
|
||||
this.shortcuts.ctrl.set(COMPONENT_NAME, ['Minus'], () =>
|
||||
this.setScale(this.scale - 0.1)
|
||||
);
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { InjectionKey, Ref } from 'vue';
|
||||
import { Search } from './search';
|
||||
import type { Shortcuts, useKeys } from './vueUtils';
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import type { Search } from './search';
|
||||
import type { Shortcuts } from './shortcuts';
|
||||
import type { useKeys } from './vueUtils';
|
||||
|
||||
export const languageDirectionKey = Symbol('languageDirection') as InjectionKey<
|
||||
Ref<'ltr' | 'rtl'>
|
||||
|
@ -163,3 +163,7 @@ export function getCreateFiltersFromListViewFilters(filters: QueryFilter) {
|
||||
|
||||
return createFilters;
|
||||
}
|
||||
|
||||
export function getIsMac() {
|
||||
return navigator.userAgent.indexOf('Mac') !== -1;
|
||||
}
|
||||
|
@ -1,17 +1,221 @@
|
||||
import router from 'src/router';
|
||||
import { Shortcuts } from './vueUtils';
|
||||
import { Keys } from 'utils/types';
|
||||
import { watch } from 'vue';
|
||||
import { getIsMac } from './misc';
|
||||
import { useKeys } from './vueUtils';
|
||||
|
||||
interface ModMap {
|
||||
alt: boolean;
|
||||
ctrl: boolean;
|
||||
meta: boolean;
|
||||
shift: boolean;
|
||||
repeat: boolean;
|
||||
}
|
||||
|
||||
type Mod = keyof ModMap;
|
||||
type Context = unknown;
|
||||
type ShortcutFunction = () => void;
|
||||
type ShortcutConfig = {
|
||||
callback: ShortcutFunction;
|
||||
propagate: boolean;
|
||||
};
|
||||
|
||||
type ShortcutMap = Map<Context, Map<string, ShortcutConfig>>;
|
||||
|
||||
const mods: Readonly<Mod[]> = ['alt', 'ctrl', 'meta', 'repeat', 'shift'];
|
||||
|
||||
export class Shortcuts {
|
||||
keys: Keys;
|
||||
isMac: boolean;
|
||||
contextStack: Context[];
|
||||
shortcuts: ShortcutMap;
|
||||
modMap: Partial<Record<Mod, boolean>>;
|
||||
|
||||
constructor(keys?: Keys) {
|
||||
this.contextStack = [];
|
||||
this.modMap = {};
|
||||
this.keys = keys ?? useKeys();
|
||||
this.shortcuts = new Map();
|
||||
this.isMac = getIsMac();
|
||||
|
||||
watch(this.keys, (keys) => {
|
||||
this.#trigger(keys);
|
||||
});
|
||||
}
|
||||
|
||||
#trigger(keys: Keys) {
|
||||
const key = this.getKey(Array.from(keys.pressed), keys);
|
||||
for (const context of this.contextStack.reverse()) {
|
||||
const obj = this.shortcuts.get(context)?.get(key);
|
||||
if (!obj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
obj.callback();
|
||||
if (!obj.propagate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setGlobalShortcuts(shortcuts: Shortcuts) {
|
||||
/**
|
||||
* PMod : if macOS then Meta (⌘) else Ctrl, both Left and Right
|
||||
* Check if a context is present or if a shortcut
|
||||
* is present in a context.
|
||||
*
|
||||
* Backspace : Go to the previous page
|
||||
*
|
||||
* @param context context in which the shortcut is to be checked
|
||||
* @param shortcut shortcut that is to be checked
|
||||
* @returns
|
||||
*/
|
||||
shortcuts.set(window, ['Backspace'], async () => {
|
||||
if (document.body !== document.activeElement) {
|
||||
has(context: Context, shortcut?: string[]) {
|
||||
if (!shortcut) {
|
||||
return this.shortcuts.has(context);
|
||||
}
|
||||
|
||||
const contextualShortcuts = this.shortcuts.get(context);
|
||||
if (!contextualShortcuts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const key = this.getKey(shortcut);
|
||||
return contextualShortcuts.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a function to a shortcut in a given context.
|
||||
*
|
||||
* @param context context object to which the shortcut belongs
|
||||
* @param shortcut keyboard event codes used as shortcut chord
|
||||
* @param callback function to be called when the shortcut is pressed
|
||||
* @param propagate whether to check and executs shortcuts in parent contexts
|
||||
* @param removeIfSet whether to delete the set shortcut
|
||||
*/
|
||||
set(
|
||||
context: Context,
|
||||
shortcut: string[],
|
||||
callback: ShortcutFunction,
|
||||
propagate: boolean = false,
|
||||
removeIfSet: boolean = true
|
||||
) {
|
||||
if (!this.shortcuts.has(context)) {
|
||||
this.shortcuts.set(context, new Map());
|
||||
}
|
||||
const contextualShortcuts = this.shortcuts.get(context)!;
|
||||
|
||||
const key = this.getKey(shortcut);
|
||||
if (removeIfSet) {
|
||||
contextualShortcuts.delete(key);
|
||||
}
|
||||
|
||||
if (contextualShortcuts.has(key)) {
|
||||
throw new Error(`Shortcut ${key} already exists.`);
|
||||
}
|
||||
|
||||
this.#pushContext(context);
|
||||
contextualShortcuts.set(key, { callback, propagate });
|
||||
}
|
||||
|
||||
/**
|
||||
* Either delete a single shortcut or all the shortcuts in
|
||||
* a given context.
|
||||
*
|
||||
* @param context context from which the shortcut is to be removed
|
||||
* @param shortcut shortcut that is to be deleted
|
||||
* @returns boolean indicating success
|
||||
*/
|
||||
delete(context: Context, shortcut?: string[]): boolean {
|
||||
if (!shortcut) {
|
||||
this.#removeContext(context);
|
||||
return this.shortcuts.delete(context);
|
||||
}
|
||||
|
||||
const contextualShortcuts = this.shortcuts.get(context);
|
||||
if (!contextualShortcuts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const key = this.getKey(shortcut);
|
||||
return contextualShortcuts.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts shortcuts list and modMap to a string to be
|
||||
* used as a shortcut key.
|
||||
*
|
||||
* @param shortcut array of shortcut keys
|
||||
* @param modMap boolean map of mod keys to be used
|
||||
* @returns string to be used as the shortcut Map key
|
||||
*/
|
||||
getKey(shortcut: string[], modMap?: Partial<ModMap>): string {
|
||||
const _modMap = modMap || this.modMap;
|
||||
this.modMap = {};
|
||||
|
||||
const shortcutString = shortcut.sort().join('+');
|
||||
const modString = mods.filter((k) => _modMap[k]).join('+');
|
||||
if (shortcutString && modString) {
|
||||
return modString + '+' + shortcutString;
|
||||
}
|
||||
|
||||
if (!modString) {
|
||||
return shortcutString;
|
||||
}
|
||||
|
||||
if (!shortcutString) {
|
||||
return modString;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#pushContext(context: Context) {
|
||||
this.#removeContext(context);
|
||||
this.contextStack.push(context);
|
||||
}
|
||||
|
||||
#removeContext(context: Context) {
|
||||
const index = this.contextStack.indexOf(context);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.back();
|
||||
});
|
||||
this.contextStack = [
|
||||
this.contextStack.slice(0, index),
|
||||
this.contextStack.slice(index + 1),
|
||||
].flat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut Modifiers
|
||||
*/
|
||||
get alt() {
|
||||
this.modMap['alt'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get ctrl() {
|
||||
this.modMap['ctrl'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get meta() {
|
||||
this.modMap['meta'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get shift() {
|
||||
this.modMap['shift'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get repeat() {
|
||||
this.modMap['repeat'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get pmod() {
|
||||
if (this.isMac) {
|
||||
return this.meta;
|
||||
}
|
||||
|
||||
return this.ctrl;
|
||||
}
|
||||
}
|
||||
|
@ -1,226 +1,6 @@
|
||||
import { Keys } from 'utils/types';
|
||||
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||
|
||||
interface ModMap {
|
||||
alt: boolean;
|
||||
ctrl: boolean;
|
||||
meta: boolean;
|
||||
shift: boolean;
|
||||
repeat: boolean;
|
||||
}
|
||||
|
||||
type Mod = keyof ModMap;
|
||||
|
||||
interface Keys extends ModMap {
|
||||
pressed: Set<string>;
|
||||
}
|
||||
|
||||
type Context = unknown;
|
||||
type ShortcutFunction = () => void;
|
||||
type ShortcutConfig = {
|
||||
callback: ShortcutFunction;
|
||||
propagate: boolean;
|
||||
};
|
||||
|
||||
type ShortcutMap = Map<Context, Map<string, ShortcutConfig>>;
|
||||
|
||||
const mods: Readonly<Mod[]> = ['alt', 'ctrl', 'meta', 'repeat', 'shift'];
|
||||
|
||||
export class Shortcuts {
|
||||
keys: Keys;
|
||||
isMac: boolean;
|
||||
contextStack: Context[];
|
||||
shortcuts: ShortcutMap;
|
||||
modMap: Partial<Record<Mod, boolean>>;
|
||||
|
||||
constructor(keys?: Keys) {
|
||||
this.contextStack = [];
|
||||
this.modMap = {};
|
||||
this.keys = keys ?? useKeys();
|
||||
this.shortcuts = new Map();
|
||||
this.isMac = getIsMac();
|
||||
|
||||
watch(this.keys, (keys) => {
|
||||
this.#trigger(keys);
|
||||
});
|
||||
}
|
||||
|
||||
#trigger(keys: Keys) {
|
||||
const key = this.getKey(Array.from(keys.pressed), keys);
|
||||
for (const context of this.contextStack.reverse()) {
|
||||
const obj = this.shortcuts.get(context)?.get(key);
|
||||
if (!obj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
obj.callback();
|
||||
if (!obj.propagate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a context is present or if a shortcut
|
||||
* is present in a context.
|
||||
*
|
||||
*
|
||||
* @param context context in which the shortcut is to be checked
|
||||
* @param shortcut shortcut that is to be checked
|
||||
* @returns
|
||||
*/
|
||||
has(context: Context, shortcut?: string[]) {
|
||||
if (!shortcut) {
|
||||
return this.shortcuts.has(context);
|
||||
}
|
||||
|
||||
const contextualShortcuts = this.shortcuts.get(context);
|
||||
if (!contextualShortcuts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const key = this.getKey(shortcut);
|
||||
return contextualShortcuts.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a function to a shortcut in a given context.
|
||||
*
|
||||
* @param context context object to which the shortcut belongs
|
||||
* @param shortcut keyboard event codes used as shortcut chord
|
||||
* @param callback function to be called when the shortcut is pressed
|
||||
* @param propagate whether to check and executs shortcuts in parent contexts
|
||||
* @param removeIfSet whether to delete the set shortcut
|
||||
*/
|
||||
set(
|
||||
context: Context,
|
||||
shortcut: string[],
|
||||
callback: ShortcutFunction,
|
||||
propagate: boolean = false,
|
||||
removeIfSet: boolean = true
|
||||
) {
|
||||
if (!this.shortcuts.has(context)) {
|
||||
this.shortcuts.set(context, new Map());
|
||||
}
|
||||
const contextualShortcuts = this.shortcuts.get(context)!;
|
||||
|
||||
const key = this.getKey(shortcut);
|
||||
if (removeIfSet) {
|
||||
contextualShortcuts.delete(key);
|
||||
}
|
||||
|
||||
if (contextualShortcuts.has(key)) {
|
||||
throw new Error(`Shortcut ${key} already exists.`);
|
||||
}
|
||||
|
||||
this.#pushContext(context);
|
||||
contextualShortcuts.set(key, { callback, propagate });
|
||||
}
|
||||
|
||||
/**
|
||||
* Either delete a single shortcut or all the shortcuts in
|
||||
* a given context.
|
||||
*
|
||||
* @param context context from which the shortcut is to be removed
|
||||
* @param shortcut shortcut that is to be deleted
|
||||
* @returns boolean indicating success
|
||||
*/
|
||||
delete(context: Context, shortcut?: string[]): boolean {
|
||||
if (!shortcut) {
|
||||
this.#removeContext(context);
|
||||
return this.shortcuts.delete(context);
|
||||
}
|
||||
|
||||
const contextualShortcuts = this.shortcuts.get(context);
|
||||
if (!contextualShortcuts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const key = this.getKey(shortcut);
|
||||
return contextualShortcuts.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts shortcuts list and modMap to a string to be
|
||||
* used as a shortcut key.
|
||||
*
|
||||
* @param shortcut array of shortcut keys
|
||||
* @param modMap boolean map of mod keys to be used
|
||||
* @returns string to be used as the shortcut Map key
|
||||
*/
|
||||
getKey(shortcut: string[], modMap?: Partial<ModMap>): string {
|
||||
const _modMap = modMap || this.modMap;
|
||||
this.modMap = {};
|
||||
|
||||
const shortcutString = shortcut.sort().join('+');
|
||||
const modString = mods.filter((k) => _modMap[k]).join('+');
|
||||
if (shortcutString && modString) {
|
||||
return modString + '+' + shortcutString;
|
||||
}
|
||||
|
||||
if (!modString) {
|
||||
return shortcutString;
|
||||
}
|
||||
|
||||
if (!shortcutString) {
|
||||
return modString;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#pushContext(context: Context) {
|
||||
this.#removeContext(context);
|
||||
this.contextStack.push(context);
|
||||
}
|
||||
|
||||
#removeContext(context: Context) {
|
||||
const index = this.contextStack.indexOf(context);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.contextStack = [
|
||||
this.contextStack.slice(0, index),
|
||||
this.contextStack.slice(index + 1),
|
||||
].flat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut Modifiers
|
||||
*/
|
||||
get alt() {
|
||||
this.modMap['alt'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get ctrl() {
|
||||
this.modMap['ctrl'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get meta() {
|
||||
this.modMap['meta'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get shift() {
|
||||
this.modMap['shift'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get repeat() {
|
||||
this.modMap['repeat'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get pmod() {
|
||||
if (this.isMac) {
|
||||
return this.meta;
|
||||
}
|
||||
|
||||
return this.ctrl;
|
||||
}
|
||||
}
|
||||
import { getIsMac } from './misc';
|
||||
|
||||
export function useKeys() {
|
||||
const isMac = getIsMac();
|
||||
@ -292,7 +72,3 @@ export function useMouseLocation() {
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
function getIsMac() {
|
||||
return navigator.userAgent.indexOf('Mac') !== -1;
|
||||
}
|
||||
|
@ -54,3 +54,15 @@ export type PropertyEnum<T extends Record<string, any>> = {
|
||||
};
|
||||
|
||||
export type TemplateFile = { file: string; template: string; modified: string };
|
||||
|
||||
export interface Keys extends ModMap {
|
||||
pressed: Set<string>;
|
||||
}
|
||||
|
||||
interface ModMap {
|
||||
alt: boolean;
|
||||
ctrl: boolean;
|
||||
meta: boolean;
|
||||
shift: boolean;
|
||||
repeat: boolean;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user