feat: use local implementation
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
let KEYBINDING_MODIFIER_KEYS = ['Shift', 'Meta', 'Alt', 'Control'];
|
||||
let PLATFORM = typeof navigator === 'object' ? navigator.platform : '';
|
||||
let APPLE_DEVICE = /Mac|iPod|iPhone|iPad/.test(PLATFORM);
|
||||
let MOD = APPLE_DEVICE ? 'Meta' : 'Control';
|
||||
let ALT_GRAPH_ALIASES =
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
PLATFORM === 'Win32' ? ['Control', 'Alt'] : APPLE_DEVICE ? ['Alt'] : [];
|
||||
|
||||
function getModifierState(event, mod) {
|
||||
return typeof event.getModifierState === 'function'
|
||||
? event.getModifierState(mod) ||
|
||||
(ALT_GRAPH_ALIASES.includes(mod) && event.getModifierState('AltGraph'))
|
||||
: false;
|
||||
}
|
||||
|
||||
export function parseKeybinding(str) {
|
||||
return str
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(press => {
|
||||
let mods = press.split(/\b\+/);
|
||||
let key = mods.pop();
|
||||
mods = mods.map(mod => (mod === '$mod' ? MOD : mod));
|
||||
return [mods, key];
|
||||
});
|
||||
}
|
||||
|
||||
function match(event, press) {
|
||||
const [mods, key] = press;
|
||||
|
||||
// Check if the pressed key matches the expected key
|
||||
if (key.toUpperCase() !== event.key.toUpperCase() && key !== event.code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if all required modifiers are pressed
|
||||
if (mods.some(mod => !getModifierState(event, mod))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if any unexpected modifiers are pressed
|
||||
if (
|
||||
KEYBINDING_MODIFIER_KEYS.some(
|
||||
mod => !mods.includes(mod) && key !== mod && getModifierState(event, mod)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function createKeybindingsHandler(keyBindingMap) {
|
||||
let keyBindings = Object.keys(keyBindingMap).map(key => {
|
||||
return [parseKeybinding(key), keyBindingMap[key]];
|
||||
});
|
||||
|
||||
let possibleMatches = new Map();
|
||||
let timer = null;
|
||||
|
||||
return event => {
|
||||
if (!(event instanceof KeyboardEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(event.key, event.code, event.getModifierState('Control'));
|
||||
|
||||
keyBindings.forEach(keyBinding => {
|
||||
let sequence = keyBinding[0];
|
||||
let callback = keyBinding[1];
|
||||
|
||||
let prev = possibleMatches.get(sequence);
|
||||
let remainingExpectedPresses = prev ?? sequence;
|
||||
let currentExpectedPress = remainingExpectedPresses[0];
|
||||
|
||||
let matches = match(event, currentExpectedPress);
|
||||
|
||||
if (!matches) {
|
||||
if (!getModifierState(event, event.key)) {
|
||||
possibleMatches.delete(sequence);
|
||||
}
|
||||
} else if (remainingExpectedPresses.length > 1) {
|
||||
possibleMatches.set(sequence, remainingExpectedPresses.slice(1));
|
||||
} else {
|
||||
possibleMatches.delete(sequence);
|
||||
callback(event);
|
||||
}
|
||||
});
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
timer = setTimeout(possibleMatches.clear.bind(possibleMatches), 1000);
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isActiveElementTypeable, isEscape } from '../helpers/KeyboardHelpers';
|
||||
|
||||
import { createKeybindingsHandler } from 'tinykeys';
|
||||
import { createKeybindingsHandler } from 'shared/helpers/keybindings';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
|
||||
// this is a store that stores the handler globally, and only gets reset on reload
|
||||
|
||||
Reference in New Issue
Block a user