fix MultiLineTextInput swallowing ALL key events

This commit is contained in:
Aran-Fey
2025-01-09 13:46:13 +01:00
parent 382fa9e66e
commit 9af7a6f698
4 changed files with 84 additions and 61 deletions

View File

@@ -35,16 +35,20 @@ export class MultiLineTextInputComponent extends ComponentBase {
// In addition to notifying the backend, also include the input's
// current value. This ensures any event handlers actually use the up-to
// date value.
this.inputBox.inputElement.addEventListener("keydown", (event) => {
if (event.key === "Enter" && event.shiftKey) {
this.state.text = this.inputBox.value;
this.sendMessageToBackend({
text: this.state.text,
});
this.inputBox.inputElement.addEventListener(
"keydown",
(event) => {
if (event.key === "Enter" && event.shiftKey) {
this.state.text = this.inputBox.value;
this.sendMessageToBackend({
text: this.state.text,
});
markEventAsHandled(event);
}
});
markEventAsHandled(event);
}
},
{ capture: true }
);
// Eat click events so the element can't be clicked-through
element.addEventListener("click", (event) => {

View File

@@ -71,29 +71,29 @@ export class TextInputComponent extends ComponentBase {
// In addition to notifying the backend, also include the input's
// current value. This ensures any event handlers actually use the up-to
// date value.
this.inputBox.inputElement.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
// Update the state
this.state.text = this.inputBox.value;
this.inputBox.inputElement.addEventListener(
"keydown",
(event) => {
if (event.key === "Enter") {
// Update the state
this.state.text = this.inputBox.value;
// There is no need for the debouncer to report this call, since
// Python will already trigger both change & confirm events when
// it receives the message that is about to be sent.
this.onChangeLimiter.clear();
// There is no need for the debouncer to report this call, since
// Python will already trigger both change & confirm events when
// it receives the message that is about to be sent.
this.onChangeLimiter.clear();
// Inform the backend
this.sendMessageToBackend({
type: "confirm",
text: this.state.text,
});
// Inform the backend
this.sendMessageToBackend({
type: "confirm",
text: this.state.text,
});
markEventAsHandled(event);
} else if (hasDefaultHandler(event)) {
// Don't `.preventDefault()` because then the user can't type
event.stopPropagation();
event.stopImmediatePropagation();
}
});
markEventAsHandled(event);
}
},
{ capture: true }
);
// Eat click events so the element can't be clicked-through
element.addEventListener("click", (event) => {
@@ -163,35 +163,3 @@ export class TextInputComponent extends ComponentBase {
this.inputBox.focus();
}
}
function hasDefaultHandler(event: KeyboardEvent): boolean {
if (event.key.length === 1) {
return true;
}
if (
[
"Backspace",
"Delete",
"Enter",
"Home",
"End",
"Tab",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
"ArrowDown",
].includes(event.key)
) {
return true;
}
if (
(event.ctrlKey || event.metaKey) &&
["a", "c", "x", "v", "z", "y"].includes(event.key)
) {
return true;
}
return false;
}

View File

@@ -93,6 +93,18 @@ export class InputBox {
this.connectClickHandlers();
}
// Eat keyboard events that have an effect on the input field
this._inputElement.addEventListener(
"keypress",
(event: KeyboardEvent) => {
if (this._hasDefaultHandler(event)) {
// Don't `.preventDefault()` because then the user can't type
event.stopPropagation();
event.stopImmediatePropagation();
}
}
);
// When keyboard focus is lost, check if the input is empty so that the
// floating label can position itself accordingly
this._inputElement.addEventListener("blur", () => {
@@ -153,6 +165,44 @@ export class InputBox {
this._inputElement.addEventListener("pointerdown", stopPropagation);
}
private _hasDefaultHandler(event: KeyboardEvent): boolean {
// Letters are simply inputted into the text field
if (event.key.length === 1) {
return true;
}
if (
[
"Backspace",
"Delete",
"Home",
"End",
"Tab",
"ArrowLeft",
"ArrowRight",
].includes(event.key)
) {
return true;
}
if (
(event.ctrlKey || event.metaKey) &&
["a", "c", "x", "v", "z", "y"].includes(event.key)
) {
return true;
}
// Multi-line inputs have some extra hotkeys
if (
this._inputElement.tagName === "TEXTAREA" &&
["ArrowUp", "ArrowDown", "Enter"].includes(event.key)
) {
return true;
}
return false;
}
get inputElement(): HTMLInputElement {
return this._inputElement;
}

View File

@@ -63,6 +63,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
"Topic :: Software Development :: Libraries :: Python Modules",