UNPKG

jobiqo-cl

Version:

[![CircleCI](https://circleci.com/gh/jobiqo/jobiqo-cl.svg?style=svg&circle-token=5a24efa5b8bbc4879276123e77d0d3f35ca7144c)](https://circleci.com/gh/jobiqo/jobiqo-cl)

288 lines (240 loc) 7.36 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); require('../tabbable/index.js'); require('../xtend/immutable.js'); var index$1 = require('../../_virtual/index5.js_commonjs-proxy'); var immutable$1 = require('../../_virtual/immutable.js_commonjs-proxy'); var listeningFocusTrap = null; function focusTrap(element, userOptions) { var doc = document; var container = typeof element === 'string' ? doc.querySelector(element) : element; var config = immutable$1.default( { returnFocusOnDeactivate: true, escapeDeactivates: true }, userOptions ); var state = { firstTabbableNode: null, lastTabbableNode: null, nodeFocusedBeforeActivation: null, mostRecentlyFocusedNode: null, active: false, paused: false }; var trap = { activate: activate, deactivate: deactivate, pause: pause, unpause: unpause }; return trap; function activate(activateOptions) { if (state.active) return; updateTabbableNodes(); state.active = true; state.paused = false; state.nodeFocusedBeforeActivation = doc.activeElement; var onActivate = activateOptions && activateOptions.onActivate ? activateOptions.onActivate : config.onActivate; if (onActivate) { onActivate(); } addListeners(); return trap; } function deactivate(deactivateOptions) { if (!state.active) return; removeListeners(); state.active = false; state.paused = false; var onDeactivate = deactivateOptions && deactivateOptions.onDeactivate !== undefined ? deactivateOptions.onDeactivate : config.onDeactivate; if (onDeactivate) { onDeactivate(); } var returnFocus = deactivateOptions && deactivateOptions.returnFocus !== undefined ? deactivateOptions.returnFocus : config.returnFocusOnDeactivate; if (returnFocus) { delay(function() { tryFocus(state.nodeFocusedBeforeActivation); }); } return trap; } function pause() { if (state.paused || !state.active) return; state.paused = true; removeListeners(); } function unpause() { if (!state.paused || !state.active) return; state.paused = false; addListeners(); } function addListeners() { if (!state.active) return; // There can be only one listening focus trap at a time if (listeningFocusTrap) { listeningFocusTrap.pause(); } listeningFocusTrap = trap; updateTabbableNodes(); // Delay ensures that the focused element doesn't capture the event // that caused the focus trap activation. delay(function() { tryFocus(getInitialFocusNode()); }); doc.addEventListener('focusin', checkFocusIn, true); doc.addEventListener('mousedown', checkPointerDown, true); doc.addEventListener('touchstart', checkPointerDown, true); doc.addEventListener('click', checkClick, true); doc.addEventListener('keydown', checkKey, true); return trap; } function removeListeners() { if (!state.active || listeningFocusTrap !== trap) return; doc.removeEventListener('focusin', checkFocusIn, true); doc.removeEventListener('mousedown', checkPointerDown, true); doc.removeEventListener('touchstart', checkPointerDown, true); doc.removeEventListener('click', checkClick, true); doc.removeEventListener('keydown', checkKey, true); listeningFocusTrap = null; return trap; } function getNodeForOption(optionName) { var optionValue = config[optionName]; var node = optionValue; if (!optionValue) { return null; } if (typeof optionValue === 'string') { node = doc.querySelector(optionValue); if (!node) { throw new Error('`' + optionName + '` refers to no known node'); } } if (typeof optionValue === 'function') { node = optionValue(); if (!node) { throw new Error('`' + optionName + '` did not return a node'); } } return node; } function getInitialFocusNode() { var node; if (getNodeForOption('initialFocus') !== null) { node = getNodeForOption('initialFocus'); } else if (container.contains(doc.activeElement)) { node = doc.activeElement; } else { node = state.firstTabbableNode || getNodeForOption('fallbackFocus'); } if (!node) { throw new Error( "You can't have a focus-trap without at least one focusable element" ); } return node; } // This needs to be done on mousedown and touchstart instead of click // so that it precedes the focus event. function checkPointerDown(e) { if (container.contains(e.target)) return; if (config.clickOutsideDeactivates) { deactivate({ returnFocus: !index$1.default.isFocusable(e.target) }); } else { e.preventDefault(); } } // In case focus escapes the trap for some strange reason, pull it back in. function checkFocusIn(e) { // In Firefox when you Tab out of an iframe the Document is briefly focused. if (container.contains(e.target) || e.target instanceof Document) { return; } e.stopImmediatePropagation(); tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode()); } function checkKey(e) { if (config.escapeDeactivates !== false && isEscapeEvent(e)) { e.preventDefault(); deactivate(); return; } if (isTabEvent(e)) { checkTab(e); return; } } // Hijack Tab events on the first and last focusable nodes of the trap, // in order to prevent focus from escaping. If it escapes for even a // moment it can end up scrolling the page and causing confusion so we // kind of need to capture the action at the keydown phase. function checkTab(e) { updateTabbableNodes(); if (e.shiftKey && e.target === state.firstTabbableNode) { e.preventDefault(); tryFocus(state.lastTabbableNode); return; } if (!e.shiftKey && e.target === state.lastTabbableNode) { e.preventDefault(); tryFocus(state.firstTabbableNode); return; } } function checkClick(e) { if (config.clickOutsideDeactivates) return; if (container.contains(e.target)) return; e.preventDefault(); e.stopImmediatePropagation(); } function updateTabbableNodes() { var tabbableNodes = index$1.default(container); state.firstTabbableNode = tabbableNodes[0] || getInitialFocusNode(); state.lastTabbableNode = tabbableNodes[tabbableNodes.length - 1] || getInitialFocusNode(); } function tryFocus(node) { if (node === doc.activeElement) return; if (!node || !node.focus) { tryFocus(getInitialFocusNode()); return; } node.focus(); state.mostRecentlyFocusedNode = node; if (isSelectableInput(node)) { node.select(); } } } function isSelectableInput(node) { return ( node.tagName && node.tagName.toLowerCase() === 'input' && typeof node.select === 'function' ); } function isEscapeEvent(e) { return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27; } function isTabEvent(e) { return e.key === 'Tab' || e.keyCode === 9; } function delay(fn) { return setTimeout(fn, 0); } var focusTrap_1 = focusTrap; exports.__moduleExports = focusTrap_1;