This topic is tangentially related to prosemirror.
I really like the way the prosemirror-keymap
library handles capturing keypress events, and I wanted to be able to do something similar for registering event listeners outside of prosemirror. As a result, I created a helper function called keyHandler
that wraps a callback and makes sure it’s only called for the specificed key-combination.
My goal was to be able to write handlers like this:
document.addEventListener("keypress", keyHandler("Ctrl-/", () => {
console.log("Pressed Ctrl + /")
})
What I wound up making seems to work, but I don’t fully understand the keymap package. The code looks like:
import * as keyName from "w3c-keyname"
export function keyHandler(binding: string, callback: Function) {
let normalizedBinding = normalizeKeyName(binding)
return function(event: KeyboardEvent): void {
let name = keyName(event)
let isChar = name.length == 1 && name != " "
let notCharName = modifiers(name, event, !isChar)
let shiftName = modifiers(name, event, true)
let modPressed = event.shiftKey || event.altKey || event.metaKey
let baseName = keyName.base[event.keyCode]
if (notCharName === normalizedBinding) {
callback(event)
} else if (isChar && modPressed && baseName != name) {
if (shiftName === normalizedBinding) {
callback(event)
}
}
}
}
/**
* Everything below here is taken from the `prosemirror-keymap` package.
*/
const mac = typeof navigator != "undefined" ? /Mac/.test(navigator.platform) : false
function normalizeKeyName(name: string): string {
let parts = name.split(/-(?!$)/),
result = parts[parts.length - 1]
if (result == "Space") result = " "
let alt, ctrl, shift, meta
for (let i = 0; i < parts.length - 1; i++) {
let mod = parts[i]
if (/^(cmd|meta|m)$/i.test(mod)) meta = true
else if (/^a(lt)?$/i.test(mod)) alt = true
else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true
else if (/^s(hift)?$/i.test(mod)) shift = true
else if (/^mod$/i.test(mod)) {
if (mac) meta = true
else ctrl = true
} else throw new Error("Unrecognized modifier name: " + mod)
}
if (alt) result = "Alt-" + result
if (ctrl) result = "Ctrl-" + result
if (meta) result = "Meta-" + result
if (shift) result = "Shift-" + result
return result
}
function modifiers(name: string, event: KeyboardEvent, shift: boolean) {
if (event.altKey) name = "Alt-" + name
if (event.ctrlKey) name = "Ctrl-" + name
if (event.metaKey) name = "Meta-" + name
if (shift !== false && event.shiftKey) name = "Shift-" + name
return name
}
If @marijn or any others have some feedback on this it would be greatly appreciated. I would also be up for open-sourcing the code.