/**
* Copyright IBM Corp. 2020, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { on } from '../on';
/**
* Fires the given event if focus goes out of the given element.
*
* @param {Element} element The element to monitor the focus on.
* @param {Element[]} sentinelNodes
* The focus sentinel nodes.
* If these nodes gets focus, we see it as focus went out of the `element`.
* @param {string} [eventRequestFocusWrap=cds-request-focus-wrap] The event name.
* @returns {Function} The handle to remove the event handler.
*/
function focuswrap(
element,
sentinelNodes,
eventRequestFocusWrap = 'cds-request-focus-wrap'
) {
return on(element, 'focusout', function handleFocusout(event) {
const { target, relatedTarget } = event;
const [startSentinelNode, endSentinelNode] = sentinelNodes;
const oldContains =
target &&
target !== this &&
(event.currentTarget.contains(target) ||
event.currentTarget.host?.contains(target)) &&
sentinelNodes.indexOf(target) < 0;
const currentContains =
relatedTarget &&
relatedTarget !== this &&
(event.currentTarget.contains(relatedTarget) ||
event.currentTarget.host?.contains(relatedTarget)) &&
sentinelNodes.indexOf(relatedTarget) < 0;
// FF fires `focusout` event even if the page itself is losing focus (e.g. upon following outside link).
// In such case, we do nothing.
if (oldContains && !currentContains && relatedTarget) {
let comparisonResult = target.compareDocumentPosition(relatedTarget);
if (relatedTarget === startSentinelNode) {
comparisonResult = Node.DOCUMENT_POSITION_PRECEDING;
} else if (relatedTarget === endSentinelNode) {
comparisonResult = Node.DOCUMENT_POSITION_FOLLOWING;
} else {
comparisonResult = -1;
}
element.dispatchEvent(
new CustomEvent(eventRequestFocusWrap, {
bubbles: true,
cancelable: false,
composed: true,
detail: {
comparisonResult,
},
})
);
}
});
}
export default focuswrap;