UNPKG

lazy-widgets

Version:

Typescript retained mode GUI for the HTML canvas API

113 lines 3.8 kB
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