livebook/assets/js/keyboard_control/index.js

105 lines
2.7 KiB
JavaScript

import { getAttributeOrThrow, parseBoolean } from "../lib/attribute";
import { cancelEvent, isEditableElement } from "../lib/utils";
/**
* A hook for ControlComponent to handle user keyboard interactions.
*
* Configuration:
*
* * `data-keydown-enabled` - whether keydown events should be intercepted
*
* * `data-keyup-enabled` - whether keyup events should be intercepted
*
* * `data-target` - the target to send live events to
*/
const KeyboardControl = {
mounted() {
this.props = getProps(this);
this.handleDocumentKeyDown = (event) => {
handleDocumentKeyDown(this, event);
};
// We intentionally register on window rather than document,
// to intercept clicks as early on as possible, even before
// the session shortcuts
window.addEventListener("keydown", this.handleDocumentKeyDown, true);
this.handleDocumentKeyUp = (event) => {
handleDocumentKeyUp(this, event);
};
window.addEventListener("keyup", this.handleDocumentKeyUp, true);
this.handleDocumentFocus = (event) => {
handleDocumentFocus(this, event);
};
// Note: the focus event doesn't bubble, so we register for the capture phase
window.addEventListener("focus", this.handleDocumentFocus, true);
},
updated() {
this.props = getProps(this);
},
destroyed() {
window.removeEventListener("keydown", this.handleDocumentKeyDown, true);
window.removeEventListener("keyup", this.handleDocumentKeyUp, true);
window.removeEventListener("focus", this.handleDocumentFocus, true);
},
};
function getProps(hook) {
return {
isKeydownEnabled: getAttributeOrThrow(
hook.el,
"data-keydown-enabled",
parseBoolean
),
isKeyupEnabled: getAttributeOrThrow(
hook.el,
"data-keyup-enabled",
parseBoolean
),
target: getAttributeOrThrow(hook.el, "data-target"),
};
}
function handleDocumentKeyDown(hook, event) {
if (keyboardEnabled(hook)) {
cancelEvent(event);
}
if (hook.props.isKeydownEnabled) {
if (event.repeat) {
return;
}
const key = event.key;
hook.pushEventTo(hook.props.target, "keydown", { key });
}
}
function handleDocumentKeyUp(hook, event) {
if (keyboardEnabled(hook)) {
cancelEvent(event);
}
if (hook.props.isKeyupEnabled) {
const key = event.key;
hook.pushEventTo(hook.props.target, "keyup", { key });
}
}
function handleDocumentFocus(hook, event) {
if (hook.props.isKeydownEnabled && isEditableElement(event.target)) {
hook.pushEventTo(hook.props.target, "disable_keyboard", {});
}
}
function keyboardEnabled(hook) {
return hook.props.isKeydownEnabled || hook.props.isKeyupEnabled;
}
export default KeyboardControl;