UNPKG

makeup-exit-emitter

Version:

Emits custom 'focusExit' event when keyboard focus has exited an element and all of it's descendants

79 lines (74 loc) 2.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addFocusExit = addFocusExit; exports.removeFocusExit = removeFocusExit; var _makeupNextId = _interopRequireDefault(require("makeup-next-id")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const focusExitEmitters = new Map(); function doFocusExit(el, fromElement, toElement) { el.dispatchEvent(new CustomEvent("focusExit", { detail: { fromElement, toElement }, bubbles: false // mirror the native mouseleave event })); } function onDocumentFocusIn(e) { const newFocusElement = e.target; const targetIsDescendant = this.el.contains(newFocusElement); // if focus has moved to a focusable descendant if (targetIsDescendant === true) { // set the target as the currently focussed element this.currentFocusElement = newFocusElement; } else { // else focus has not gone to a focusable descendant window.removeEventListener("blur", this.onWindowBlurListener); document.removeEventListener("focusin", this.onDocumentFocusInListener); doFocusExit(this.el, this.currentFocusElement, newFocusElement); this.currentFocusElement = null; } } function onWindowBlur() { doFocusExit(this.el, this.currentFocusElement, undefined); } function onWidgetFocusIn() { // listen for focus moving to anywhere in document // note that mouse click on buttons, checkboxes and radios does not trigger focus events in all browsers! document.addEventListener("focusin", this.onDocumentFocusInListener); // listen for focus leaving the window window.addEventListener("blur", this.onWindowBlurListener); } class FocusExitEmitter { constructor(el) { this.el = el; this.currentFocusElement = null; this.onWidgetFocusInListener = onWidgetFocusIn.bind(this); this.onDocumentFocusInListener = onDocumentFocusIn.bind(this); this.onWindowBlurListener = onWindowBlur.bind(this); this.el.addEventListener("focusin", this.onWidgetFocusInListener); } removeEventListeners() { window.removeEventListener("blur", this.onWindowBlurListener); document.removeEventListener("focusin", this.onDocumentFocusInListener); this.el.removeEventListener("focusin", this.onWidgetFocusInListener); } } // TODO: rename to enableFocusExit when module is renamed to makeup-focus-exit function addFocusExit(el) { (0, _makeupNextId.default)(el); if (!focusExitEmitters.has(el.id)) { focusExitEmitters.set(el.id, new FocusExitEmitter(el)); } return focusExitEmitters.get(el.id); } // TODO: rename to disableFocusExit when module is renamed to makeup-focus-exit function removeFocusExit(el) { const exitEmitter = focusExitEmitters.get(el.id); if (exitEmitter) { exitEmitter.removeEventListeners(); focusExitEmitters.delete(el.id); } }