var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
import React from 'react';
import { useMergeRefs } from 'use-callback-ref';
import { useGlobal } from '../../lib/renderEnvironment/index.js';
import { FOCUS_DISABLED, FOCUS_GROUP } from './constants.js';
import { hiddenGuard } from './FocusGuard.js';
import { mediumFocus, mediumBlur, mediumSidecar } from './medium.js';
import { FocusScope } from './scope.js';
var emptyArray = [];
var FocusLock = React.forwardRef(function FocusLockUI(props, parentRef) {
    var _a;
    var globalObject = useGlobal();
    var _b = React.useState(null), realObserved = _b[0], setObserved = _b[1];
    var observed = React.useRef(null);
    var isActive = React.useRef(false);
    var originalFocusedElement = React.useRef(null);
    var _c = React.useState({}), update = _c[1];
    var children = props.children, _d = props.disabled, disabled = _d === void 0 ? false : _d, _e = props.noFocusGuards, noFocusGuards = _e === void 0 ? false : _e, _f = props.persistentFocus, persistentFocus = _f === void 0 ? false : _f, _g = props.crossFrame, crossFrame = _g === void 0 ? true : _g, _h = props.autoFocus, autoFocus = _h === void 0 ? true : _h, allowTextSelection = props.allowTextSelection, group = props.group, className = props.className, whiteList = props.whiteList, hasPositiveIndices = props.hasPositiveIndices, _j = props.shards, shards = _j === void 0 ? emptyArray : _j, _k = props.as, Container = _k === void 0 ? 'div' : _k, _l = props.lockProps, containerProps = _l === void 0 ? {} : _l, SideCar = props.sideCar, _m = props.returnFocus, shouldReturnFocus = _m === void 0 ? false : _m, focusOptions = props.focusOptions, onActivationCallback = props.onActivation, onDeactivationCallback = props.onDeactivation;
    var id = React.useState({})[0];
    // SIDE EFFECT CALLBACKS
    var onActivation = React.useCallback(function (_a) {
        var _b, _c;
        var captureFocusRestore = _a.captureFocusRestore;
        if (!originalFocusedElement.current) {
            var activeElement = (_b = globalObject.document) === null || _b === void 0 ? void 0 : _b.activeElement;
            originalFocusedElement.current = activeElement;
            // store stack reference
            if (activeElement !== ((_c = globalObject.document) === null || _c === void 0 ? void 0 : _c.body)) {
                originalFocusedElement.current = captureFocusRestore(activeElement);
            }
        }
        if (observed.current && onActivationCallback) {
            // @ts-expect-error
            onActivationCallback(observed.current);
        }
        isActive.current = true;
        update({});
    }, [onActivationCallback, globalObject]);
    var onDeactivation = React.useCallback(function () {
        isActive.current = false;
        if (onDeactivationCallback) {
            onDeactivationCallback();
        }
        update({});
    }, [onDeactivationCallback]);
    var returnFocus = React.useCallback(function (allowDefer) {
        var _a;
        var focusRestore = originalFocusedElement.current;
        if (focusRestore) {
            var returnFocusTo_1 = (typeof focusRestore === 'function' ? focusRestore() : focusRestore) ||
                ((_a = globalObject.document) === null || _a === void 0 ? void 0 : _a.body) ||
                document.body;
            var howToReturnFocus = typeof shouldReturnFocus === 'function' ? shouldReturnFocus(returnFocusTo_1) : shouldReturnFocus;
            if (howToReturnFocus) {
                var returnFocusOptions_1 = typeof howToReturnFocus === 'object' ? howToReturnFocus : undefined;
                originalFocusedElement.current = null;
                if (allowDefer) {
                    // React might return focus after update
                    // it's safer to defer the action
                    Promise.resolve().then(function () { return returnFocusTo_1.focus(returnFocusOptions_1); });
                }
                else {
                    returnFocusTo_1.focus(returnFocusOptions_1);
                }
            }
        }
    }, [shouldReturnFocus, globalObject]);
    // MEDIUM CALLBACKS
    var onFocus = React.useCallback(function (event) {
        if (isActive.current) {
            mediumFocus.useMedium(event);
        }
    }, []);
    var onBlur = mediumBlur.useMedium;
    // REF PROPAGATION
    // not using real refs due to race conditions
    var setObserveNode = React.useCallback(function (newObserved) {
        if (observed.current !== newObserved) {
            observed.current = newObserved;
            setObserved(newObserved);
        }
    }, []);
    if (process.env.NODE_ENV !== 'production') {
        if (typeof allowTextSelection !== 'undefined') {
            console.warn('React-Focus-Lock: allowTextSelection is deprecated and enabled by default');
        }
        // eslint-disable-next-line react-hooks/rules-of-hooks
        React.useEffect(function () {
            // report incorrect integration - https://github.com/theKashey/react-focus-lock/issues/123
            if (!observed.current && typeof Container !== 'string') {
                console.error('FocusLock: could not obtain ref to internal node');
            }
        }, []);
    }
    var lockProps = __assign((_a = {}, _a[FOCUS_DISABLED] = disabled && 'disabled', _a[FOCUS_GROUP] = group, _a), containerProps);
    var hasLeadingGuards = noFocusGuards !== true;
    // @ts-expect-error hasTailingGuards is not typed
    var hasTailingGuards = hasLeadingGuards && noFocusGuards !== 'tail';
    var mergedRef = useMergeRefs([parentRef, setObserveNode]);
    var focusScopeValue = React.useMemo(function () { return ({
        observed: observed,
        shards: shards,
        enabled: !disabled,
        active: isActive.current,
    }); }, [disabled, isActive.current, shards, realObserved]);
    return (React.createElement(React.Fragment, null,
        hasLeadingGuards && [
            // nearest focus guard
            React.createElement("div", { key: "guard-first", "data-focus-guard": true, tabIndex: disabled ? -1 : 0, style: hiddenGuard }),
            // first tabbed element guard
            hasPositiveIndices ? (React.createElement("div", { key: "guard-nearest", "data-focus-guard": true, tabIndex: disabled ? -1 : 1, style: hiddenGuard })) : null,
        ],
        !disabled && SideCar && (React.createElement(SideCar, { id: id, sideCar: mediumSidecar, observed: realObserved, disabled: disabled, persistentFocus: persistentFocus, crossFrame: crossFrame, autoFocus: autoFocus, whiteList: whiteList, shards: shards, onActivation: onActivation, onDeactivation: onDeactivation, returnFocus: returnFocus, focusOptions: focusOptions })),
        React.createElement(Container, __assign({ ref: mergedRef }, lockProps, { className: className, onBlur: onBlur, onFocus: onFocus }),
            React.createElement(FocusScope.Provider, { value: focusScopeValue }, children)),
        hasTailingGuards && React.createElement("div", { "data-focus-guard": true, tabIndex: disabled ? -1 : 0, style: hiddenGuard })));
});
export default FocusLock;
//# sourceMappingURL=Lock.js.map