import { __assign } from "tslib";
import * as React from 'react';
import { KeyCodes, divProperties, doesElementContainFocus, getDocument, getNativeProps, getWindow, } from '../../Utilities';
import { useMergedRefs, useAsync, useOnEvent } from '@fluentui/react-hooks';
import { useWindow } from '@fluentui/react-window-provider';
function useScrollbarAsync(props, root) {
    var async = useAsync();
    var _a = React.useState(false), needsVerticalScrollBarState = _a[0], setNeedsVerticalScrollBar = _a[1];
    React.useEffect(function () {
        async.requestAnimationFrame(function () {
            var _a;
            // If overflowY is overridden, don't waste time calculating whether the scrollbar is necessary.
            if (props.style && props.style.overflowY) {
                return;
            }
            var needsVerticalScrollBar = false;
            if (root && root.current && ((_a = root.current) === null || _a === void 0 ? void 0 : _a.firstElementChild)) {
                // ClientHeight returns the client height of an element rounded to an
                // integer. On some browsers at different zoom levels this rounding
                // can generate different results for the root container and child even
                // though they are the same height. This causes us to show a scroll bar
                // when not needed. Ideally we would use BoundingClientRect().height
                // instead however seems that the API is 90% slower than using ClientHeight.
                // Therefore instead we will calculate the difference between heights and
                // allow for a 1px difference to still be considered ok and not show the
                // scroll bar.
                var rootHeight = root.current.clientHeight;
                var firstChildHeight = root.current.firstElementChild.clientHeight;
                if (rootHeight > 0 && firstChildHeight > rootHeight) {
                    needsVerticalScrollBar = firstChildHeight - rootHeight > 1;
                }
            }
            if (needsVerticalScrollBarState !== needsVerticalScrollBar) {
                setNeedsVerticalScrollBar(needsVerticalScrollBar);
            }
        });
        return function () { return async.dispose(); };
    });
    return needsVerticalScrollBarState;
}
function defaultFocusRestorer(options) {
    var _a, _b;
    var originalElement = options.originalElement, containsFocus = options.containsFocus;
    if (originalElement && containsFocus && originalElement !== getWindow()) {
        // Make sure that the focus method actually exists
        // In some cases the object might exist but not be a real element.
        // This is primarily for IE 11 and should be removed once IE 11 is no longer in use.
        (_b = (_a = originalElement).focus) === null || _b === void 0 ? void 0 : _b.call(_a);
    }
}
function useRestoreFocus(props, root) {
    var _a = props.onRestoreFocus, onRestoreFocus = _a === void 0 ? defaultFocusRestorer : _a;
    var originalFocusedElement = React.useRef();
    var containsFocus = React.useRef(false);
    React.useLayoutEffect(function () {
        originalFocusedElement.current = getDocument().activeElement;
        return function () {
            var _a, _b;
            (_a = onRestoreFocus) === null || _a === void 0 ? void 0 : _a({
                originalElement: originalFocusedElement.current,
                containsFocus: containsFocus.current,
                documentContainsFocus: ((_b = getDocument()) === null || _b === void 0 ? void 0 : _b.hasFocus()) || false,
            });
            // De-reference DOM Node to avoid retainment via transpiled closure of _onKeyDown
            originalFocusedElement.current = undefined;
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run on first render
    }, []);
    React.useEffect(function () {
        if (doesElementContainFocus(root.current)) {
            containsFocus.current = true;
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run on first render
    }, []);
    useOnEvent(root, 'focus', React.useCallback(function () {
        containsFocus.current = true;
    }, []), true);
    useOnEvent(root, 'blur', React.useCallback(function (ev) {
        /** The popup should update this._containsFocus when:
         * relatedTarget exists AND
         * the relatedTarget is not contained within the popup.
         * If the relatedTarget is within the popup, that means the popup still has focus
         * and focused moved from one element to another within the popup.
         * If relatedTarget is undefined or null that usually means that a
         * keyboard event occurred and focus didn't change
         */
        if (root.current && ev.relatedTarget && !root.current.contains(ev.relatedTarget)) {
            containsFocus.current = false;
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run on first render
    }, []), true);
}
/**
 * This adds accessibility to Dialog and Panel controls
 */
export var Popup = React.forwardRef(function (props, forwardedRef) {
    // Default props
    // eslint-disable-next-line deprecation/deprecation
    props = __assign({ shouldRestoreFocus: true }, props);
    var root = React.useRef();
    var mergedRootRef = useMergedRefs(root, forwardedRef);
    useRestoreFocus(props, root);
    var role = props.role, className = props.className, ariaLabel = props.ariaLabel, ariaLabelledBy = props.ariaLabelledBy, ariaDescribedBy = props.ariaDescribedBy, style = props.style, children = props.children, onDismiss = props.onDismiss;
    var needsVerticalScrollBar = useScrollbarAsync(props, root);
    var onKeyDown = React.useCallback(function (ev) {
        // eslint-disable-next-line deprecation/deprecation
        switch (ev.which) {
            case KeyCodes.escape:
                if (onDismiss) {
                    onDismiss(ev);
                    ev.preventDefault();
                    ev.stopPropagation();
                }
                break;
        }
    }, [onDismiss]);
    var win = useWindow();
    useOnEvent(win, 'keydown', onKeyDown);
    return (React.createElement("div", __assign({ ref: mergedRootRef }, getNativeProps(props, divProperties), { className: className, role: role, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, onKeyDown: onKeyDown, style: __assign({ overflowY: needsVerticalScrollBar ? 'scroll' : undefined, outline: 'none' }, style) }), children));
});
//# sourceMappingURL=Popup.js.map