lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
116 lines • 5.31 kB
JavaScript
import { TextInputHandler } from './TextInputHandler.js';
const BLUR_DISMISS_TIMEOUT = 500;
const EV_LISTEN_OPTS = { passive: true, capture: true };
/**
* The default implementation of {@link TextInputHandler}, for devices that have
* a virtual keyboard (such as mobile devices).
*
* Can also be used in a WebXR session if the browser supports it, however, this
* is not recommended, as some devices such as the Oculus Quest 2 will ignore
* the initial value of the text input and the text selection, which leads to a
* bad user experience.
*
* @category Core
*/
export class SystemTextInputHandler extends TextInputHandler {
constructor(listener) {
const input = document.createElement('input');
input.style.position = 'fixed';
input.style.left = '0';
input.style.top = '0';
// XXX can't set width to 0, otherwise selection becomes glitchy
// input.style.width = '0px';
input.style.height = '0px';
input.style.margin = '0';
input.style.padding = '0';
input.style.borderStyle = 'none';
input.style.outlineStyle = 'none';
input.style.opacity = '0';
input.tabIndex = -1;
document.body.appendChild(input);
super(listener, [input]);
this.dismissTimeout = null;
this.handleInput = () => {
var _a, _b;
const text = this.textInputElem.value;
let start = (_a = this.textInputElem.selectionStart) !== null && _a !== void 0 ? _a : text.length;
let end = (_b = this.textInputElem.selectionEnd) !== null && _b !== void 0 ? _b : text.length;
if (this.textInputElem.selectionDirection === 'backward') {
[start, end] = [end, start];
}
this.listener(1 /* TextInputHandlerEventType.Input */, text, start, end);
};
this.handleSelection = () => {
var _a;
let start = this.textInputElem.selectionStart;
if (start === null) {
return;
}
let end = (_a = this.textInputElem.selectionEnd) !== null && _a !== void 0 ? _a : start;
if (this.textInputElem.selectionDirection === 'backward') {
[start, end] = [end, start];
}
this.listener(2 /* TextInputHandlerEventType.MoveCursor */, start, end);
};
this.handleBlur = () => {
if (this.dismissTimeout !== null) {
return;
}
this.dismissTimeout = setTimeout(() => {
this.dismiss();
}, BLUR_DISMISS_TIMEOUT);
};
this.handleFocus = () => {
if (this.dismissTimeout !== null) {
clearTimeout(this.dismissTimeout);
this.dismissTimeout = null;
}
};
this.textInputElem = input;
input.addEventListener('change', this.handleInput, EV_LISTEN_OPTS);
input.addEventListener('input', this.handleInput, EV_LISTEN_OPTS);
input.addEventListener('select', this.handleSelection, EV_LISTEN_OPTS);
input.addEventListener('selectionchange', this.handleSelection, EV_LISTEN_OPTS);
// HACK needed because selectionchange is broken on all chromium-based
// browsers
document.addEventListener('selectionchange', this.handleSelection, EV_LISTEN_OPTS);
input.addEventListener('blur', this.handleBlur, EV_LISTEN_OPTS);
input.addEventListener('focus', this.handleFocus, EV_LISTEN_OPTS);
}
askInput(currentText, selectStart, selectEnd) {
this.textInputElem.value = currentText;
this.select(selectStart, selectEnd);
// HACK delay focus otherwise it doesn't work
setTimeout(() => {
this.textInputElem.focus({ preventScroll: true });
}, 10);
}
select(selectStart, selectEnd) {
if (selectEnd < selectStart) {
this.textInputElem.selectionDirection = "backward";
this.textInputElem.selectionStart = selectEnd;
this.textInputElem.selectionEnd = selectStart;
}
else {
this.textInputElem.selectionStart = selectStart;
this.textInputElem.selectionEnd = selectEnd;
}
}
dismiss() {
this.textInputElem.removeEventListener('change', this.handleInput, EV_LISTEN_OPTS);
this.textInputElem.removeEventListener('input', this.handleInput, EV_LISTEN_OPTS);
this.textInputElem.removeEventListener('select', this.handleSelection, EV_LISTEN_OPTS);
this.textInputElem.removeEventListener('selectionchange', this.handleSelection, EV_LISTEN_OPTS);
// HACK needed because selectionchange is broken on all chromium-based
// browsers
document.removeEventListener('selectionchange', this.handleSelection, EV_LISTEN_OPTS);
this.textInputElem.removeEventListener('blur', this.handleBlur, EV_LISTEN_OPTS);
this.textInputElem.removeEventListener('focus', this.handleFocus, EV_LISTEN_OPTS);
const parent = this.textInputElem.parentElement;
if (parent) {
parent.removeChild(this.textInputElem);
}
this.listener(0 /* TextInputHandlerEventType.Dismiss */);
}
}
//# sourceMappingURL=SystemTextInputHandler.js.map