var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
import withSideEffect from 'react-clientside-effect';
import { moveFocusInside, focusInside, focusIsHidden, expandFocusableNodes, focusNextElement, focusPrevElement, focusFirstElement, focusLastElement, captureFocusRestore, } from 'focus-lock';
import { getOwnerGlobalObject } from '../../lib/globalObject.js';
import { deferAction, extractRef } from './util.js';
import { mediumFocus, mediumBlur, mediumEffect } from './medium.js';
var focusOnBody = function (globalObject) {
    return globalObject.document && globalObject.document.activeElement === globalObject.document.body;
};
var isFreeFocus = function (globalObject) { return focusOnBody(globalObject) || focusIsHidden(); };
var lastActiveTrap = null;
var lastActiveFocus = null;
var lastPortaledElement = null;
var focusWasOutsideWindow = false;
var defaultWhitelist = function () { return true; };
var focusWhitelisted = function (activeElement) { return (lastActiveTrap.whiteList || defaultWhitelist)(activeElement); };
var recordPortal = function (observerNode, portaledElement) {
    lastPortaledElement = { observerNode: observerNode, portaledElement: portaledElement };
};
var focusIsPortaledPair = function (element) {
    return lastPortaledElement && lastPortaledElement.portaledElement === element;
};
function autoGuard(startIndex, end, step, allNodes) {
    var lastGuard = null;
    var i = startIndex;
    do {
        var item = allNodes[i];
        if (item.guard) {
            if (item.node.dataset.focusAutoGuard) {
                lastGuard = item;
            }
        }
        else if (item.lockItem) {
            if (i !== startIndex) {
                // we will tab to the next element
                return;
            }
            lastGuard = null;
        }
        else {
            break;
        }
    } while ((i += step) !== end);
    if (lastGuard) {
        lastGuard.node.tabIndex = 0;
    }
}
var focusWasOutside = function (crossFrameOption) {
    if (crossFrameOption) {
        // with cross frame return true for any value
        return Boolean(focusWasOutsideWindow);
    }
    // in other case return only of focus went a while aho
    return focusWasOutsideWindow === 'meanwhile';
};
var checkInHost = function (check, el, boundary) {
    return el &&
        // find host equal to active element and check nested active element
        ((el.host === check && (!el.activeElement || boundary.contains(el.activeElement))) ||
            // dive up
            (el.parentNode && checkInHost(check, el.parentNode, boundary)));
};
var withinHost = function (activeElement, workingArea) {
    return workingArea.some(function (area) { return checkInHost(activeElement, area, area); });
};
var activateTrap = function () {
    var result = false;
    if (lastActiveTrap) {
        var observed = lastActiveTrap.observed, persistentFocus = lastActiveTrap.persistentFocus, autoFocus = lastActiveTrap.autoFocus, shards = lastActiveTrap.shards, crossFrame = lastActiveTrap.crossFrame, focusOptions = lastActiveTrap.focusOptions;
        var workingNode = observed || (lastPortaledElement && lastPortaledElement.portaledElement);
        var globalObject = void 0;
        if (observed) {
            globalObject = getOwnerGlobalObject(observed);
        }
        else if (typeof window !== 'undefined') {
            globalObject = window;
        }
        else {
            globalObject = undefined;
        }
        var document_1 = globalObject === null || globalObject === void 0 ? void 0 : globalObject.document;
        var activeElement = document_1 && document_1.activeElement;
        if (workingNode) {
            var workingArea = __spreadArray([workingNode], shards.map(extractRef).filter(Boolean), true);
            if (!activeElement || focusWhitelisted(activeElement)) {
                if (persistentFocus ||
                    focusWasOutside(crossFrame) ||
                    !isFreeFocus(globalObject) ||
                    (!lastActiveFocus && autoFocus)) {
                    if (workingNode &&
                        !(
                        // active element is "inside" working area
                        (focusInside(workingArea) ||
                            // check for shadow-dom contained elements
                            (activeElement && withinHost(activeElement, workingArea)) ||
                            focusIsPortaledPair(activeElement)))) {
                        if (document_1 && !lastActiveFocus && activeElement && !autoFocus) {
                            // Check if blur() exists, which is missing on certain elements on IE
                            // @ts-expect-error activeElement is not typed
                            if (activeElement.blur) {
                                // @ts-expect-error activeElement is not typed
                                activeElement.blur();
                            }
                            document_1.body.focus();
                        }
                        else {
                            // @ts-expect-error lastActiveFocus is not typed
                            result = moveFocusInside(workingArea, lastActiveFocus, { focusOptions: focusOptions });
                            lastPortaledElement = {};
                        }
                    }
                    focusWasOutsideWindow = false;
                    lastActiveFocus = document_1 && document_1.activeElement;
                }
            }
            if (document_1 &&
                // element was changed
                activeElement !== document_1.activeElement &&
                // fast check for any auto-guard
                document_1.querySelector('[data-focus-auto-guard]')) {
                var newActiveElement = document_1 && document_1.activeElement;
                var allNodes = expandFocusableNodes(workingArea);
                var focusedIndex = allNodes.map(function (_a) {
                    var node = _a.node;
                    return node;
                }).indexOf(newActiveElement);
                if (focusedIndex > -1) {
                    // remove old focus
                    allNodes
                        .filter(function (_a) {
                        var guard = _a.guard, node = _a.node;
                        return guard && node.dataset.focusAutoGuard;
                    })
                        .forEach(function (_a) {
                        var node = _a.node;
                        return node.removeAttribute('tabIndex');
                    });
                    autoGuard(focusedIndex, allNodes.length, +1, allNodes);
                    autoGuard(focusedIndex, -1, -1, allNodes);
                }
            }
        }
    }
    return result;
};
var onTrap = function (event) {
    if (activateTrap() && event) {
        // prevent scroll jump
        event.stopPropagation();
        event.preventDefault();
    }
};
var onBlur = function () { return deferAction(activateTrap); };
var onFocus = function (event) {
    // detect portal
    var source = event.target;
    var currentNode = event.currentTarget;
    if (currentNode && !currentNode.contains(source)) {
        recordPortal(currentNode, source);
    }
};
var FocusWatcher = function () { return null; };
var onWindowBlur = function () {
    // @ts-expect-error focusWasOutsideWindow is not typed
    focusWasOutsideWindow = 'just';
    // using setTimeout to set  this variable after React/sidecar reaction
    deferAction(function () {
        // @ts-expect-error focusWasOutsideWindow is not typed
        focusWasOutsideWindow = 'meanwhile';
    });
};
var attachHandler = function (globalObject) {
    var _a, _b;
    (_a = globalObject.document) === null || _a === void 0 ? void 0 : _a.addEventListener('focusin', onTrap);
    (_b = globalObject.document) === null || _b === void 0 ? void 0 : _b.addEventListener('focusout', onBlur);
    globalObject.addEventListener('blur', onWindowBlur);
};
var detachHandler = function (globalObject) {
    var _a, _b;
    (_a = globalObject.document) === null || _a === void 0 ? void 0 : _a.removeEventListener('focusin', onTrap);
    (_b = globalObject.document) === null || _b === void 0 ? void 0 : _b.removeEventListener('focusout', onBlur);
    globalObject.removeEventListener('blur', onWindowBlur);
};
function reducePropsToState(propsList) {
    return propsList.filter(function (_a) {
        var disabled = _a.disabled;
        return !disabled;
    });
}
var focusLockAPI = {
    moveFocusInside: moveFocusInside,
    focusInside: focusInside,
    focusNextElement: focusNextElement,
    focusPrevElement: focusPrevElement,
    focusFirstElement: focusFirstElement,
    focusLastElement: focusLastElement,
    captureFocusRestore: captureFocusRestore,
};
function handleStateChangeOnClient(traps) {
    var trap = traps.slice(-1)[0];
    var globalObject;
    if (trap === null || trap === void 0 ? void 0 : trap.observed) {
        globalObject = getOwnerGlobalObject(trap.observed);
    }
    else if (typeof window !== 'undefined') {
        globalObject = window;
    }
    else {
        globalObject = undefined;
    }
    if (trap && !lastActiveTrap && globalObject) {
        attachHandler(globalObject);
    }
    var lastTrap = lastActiveTrap;
    var sameTrap = lastTrap && trap && trap.id === lastTrap.id;
    lastActiveTrap = trap;
    if (lastTrap && !sameTrap) {
        lastTrap.onDeactivation();
        // return focus only of last trap was removed
        if (!traps.filter(function (_a) {
            var id = _a.id;
            return id === lastTrap.id;
        }).length) {
            // allow defer is no other trap is awaiting restore
            lastTrap.returnFocus(!trap);
        }
    }
    if (trap) {
        lastActiveFocus = null;
        if (!sameTrap || lastTrap.observed !== trap.observed) {
            trap.onActivation(focusLockAPI);
        }
        activateTrap();
        deferAction(activateTrap);
    }
    else if (globalObject) {
        detachHandler(globalObject);
        lastActiveFocus = null;
    }
}
// bind medium
mediumFocus.assignSyncMedium(onFocus);
mediumBlur.assignMedium(onBlur);
mediumEffect.assignMedium(function (cb) { return cb(focusLockAPI); });
export default withSideEffect(reducePropsToState, handleStateChangeOnClient)(FocusWatcher);
//# sourceMappingURL=Trap.js.map