Files
Automata/packages/editor-ui/src/composables/useKeybindings.ts

86 lines
2.2 KiB
TypeScript

import { useActiveElement, useEventListener } from '@vueuse/core';
import { useDeviceSupport } from 'n8n-design-system';
import type { MaybeRef } from 'vue';
import { computed, toValue, type MaybeRefOrGetter, unref } from 'vue';
type KeyMap = Record<string, (event: KeyboardEvent) => void>;
export const useKeybindings = (
keymap: MaybeRefOrGetter<KeyMap>,
options?: {
disabled: MaybeRef<boolean>;
},
) => {
const activeElement = useActiveElement();
const { isCtrlKeyPressed } = useDeviceSupport();
const isDisabled = computed(() => unref(options?.disabled));
const ignoreKeyPresses = computed(() => {
if (!activeElement.value) return false;
const active = activeElement.value;
const isInput = ['INPUT', 'TEXTAREA'].includes(active.tagName);
const isContentEditable = active.closest('[contenteditable]') !== null;
const isIgnoreClass = active.closest('.ignore-key-press') !== null;
return isInput || isContentEditable || isIgnoreClass;
});
const normalizedKeymap = computed(() =>
Object.fromEntries(
Object.entries(toValue(keymap))
.map(([shortcut, handler]) => {
const shortcuts = shortcut.split('|');
return shortcuts.map((s) => [normalizeShortcutString(s), handler]);
})
.flat(),
),
);
function normalizeShortcutString(shortcut: string) {
return shortcut
.split(/[+_-]/)
.map((key) => key.toLowerCase())
.sort((a, b) => a.localeCompare(b))
.join('+');
}
function toShortcutString(event: KeyboardEvent) {
const { shiftKey, altKey } = event;
const ctrlKey = isCtrlKeyPressed(event);
const keys = [event.key];
const modifiers: string[] = [];
if (shiftKey) {
modifiers.push('shift');
}
if (ctrlKey) {
modifiers.push('ctrl');
}
if (altKey) {
modifiers.push('alt');
}
return normalizeShortcutString([...modifiers, ...keys].join('+'));
}
function onKeyDown(event: KeyboardEvent) {
if (ignoreKeyPresses.value || isDisabled.value) return;
const shortcutString = toShortcutString(event);
const handler = normalizedKeymap.value[shortcutString];
if (handler) {
event.preventDefault();
event.stopPropagation();
handler(event);
}
}
useEventListener(document, 'keydown', onKeyDown);
};