lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
113 lines • 3.8 kB
JavaScript
const INPUT_WINDOW_MS = 200;
/**
* A helper class for checking whether the tab key is being pressed, and whether
* the direction is reversed (by having shift pressed).
*
* @category Helper
*/
export class TabKeyHelper {
constructor() {
this.lastTabEvent = 0;
this.tabState = null;
this.references = new Set();
this.windowFocused = false;
this.waitQueue = new Set();
this.downListener = (event) => {
if (event.key === 'Tab') {
this.handleTabEvent();
this.tabState = event.shiftKey;
}
};
this.upListener = (event) => {
if (event.key === 'Tab') {
this.handleTabEvent();
this.tabState = null;
}
};
this.focusListener = (_event) => {
// HACK relatedTarget is unreliable for checking if window got
// focused on chromium. use windowFocused flag instead
if (this.windowFocused) {
return;
}
this.windowFocused = true;
};
this.blurListener = (event) => {
if (event.relatedTarget === null) {
this.windowFocused = false;
this.tabState = null;
}
};
}
handleTabEvent() {
this.lastTabEvent = Date.now();
for (const callback of Array.from(this.waitQueue)) {
callback();
}
}
get pressed() {
return this.tabState !== null;
}
get directionReversed() {
return this.tabState === true;
}
isTabInitiatedFocus() {
if (this.pressed) {
return Promise.resolve(true);
}
else {
// HACK most browsers either never dispatch a tab key event, or only
// dispatch the up event, which comes AFTER the focus event.
// wait in a predefined time window for the up event if needed
const checkTime = Date.now();
return new Promise((resolve, _reject) => {
let timeout = null;
const callback = () => {
if (timeout !== null) {
clearTimeout(timeout);
}
this.waitQueue.delete(callback);
if (checkTime - this.lastTabEvent <= INPUT_WINDOW_MS) {
resolve(true);
}
else {
resolve(false);
}
};
this.waitQueue.add(callback);
timeout = setTimeout(callback, INPUT_WINDOW_MS);
});
}
}
ref(key) {
if (this.references.size === 0) {
window.addEventListener('keydown', this.downListener, { capture: true });
window.addEventListener('keyup', this.upListener);
window.addEventListener('focus', this.focusListener);
window.addEventListener('blur', this.blurListener, { capture: true, passive: true });
}
this.references.add(key);
}
unref(key) {
this.references.delete(key);
if (this.references.size === 0) {
window.removeEventListener('keydown', this.downListener, { capture: true });
window.removeEventListener('keyup', this.upListener);
window.removeEventListener('focus', this.focusListener);
window.removeEventListener('blur', this.blurListener, { capture: true });
}
}
}
let tabKeyHelper = null;
/**
* Get the global TabKeyHelper
*
* @category Helper
*/
export function getTabKeyHelper() {
if (!tabKeyHelper) {
tabKeyHelper = new TabKeyHelper();
}
return tabKeyHelper;
}
//# sourceMappingURL=TabKeyHelper.js.map