"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LayerBase = void 0;
var tslib_1 = require("tslib");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore "react-portal-compat-context" uses v9 configs via path aliases
var react_portal_compat_context_1 = require("@fluentui/react-portal-compat-context");
var React = require("react");
var ReactDOM = require("react-dom");
var Fabric_1 = require("../../Fabric");
var Utilities_1 = require("../../Utilities");
var Layer_notification_1 = require("./Layer.notification");
var react_hooks_1 = require("@fluentui/react-hooks");
var WindowProvider_1 = require("../../WindowProvider");
var getClassNames = Utilities_1.classNamesFunction();
exports.LayerBase = React.forwardRef(function (props, ref) {
    var registerPortalEl = react_portal_compat_context_1.usePortalCompat();
    var rootRef = React.useRef(null);
    var mergedRef = react_hooks_1.useMergedRefs(rootRef, ref);
    var layerRef = React.useRef();
    var fabricRef = React.useRef(null);
    var shadowRoot = WindowProvider_1.useShadowRoot();
    // Tracks if the layer mount events need to be raised.
    // Required to allow the DOM to render after the layer element is added.
    var _a = React.useState(false), needRaiseLayerMount = _a[0], setNeedRaiseLayerMount = _a[1];
    var eventBubblingEnabled = props.eventBubblingEnabled, styles = props.styles, theme = props.theme, className = props.className, children = props.children, hostId = props.hostId, _b = props.onLayerDidMount, onLayerDidMount = _b === void 0 ? function () { return undefined; } : _b, 
    // eslint-disable-next-line deprecation/deprecation
    _c = props.onLayerMounted, 
    // eslint-disable-next-line deprecation/deprecation
    onLayerMounted = _c === void 0 ? function () { return undefined; } : _c, onLayerWillUnmount = props.onLayerWillUnmount, insertFirst = props.insertFirst;
    var classNames = getClassNames(styles, {
        theme: theme,
        className: className,
        isNotHost: !hostId,
    });
    // Returns the user provided hostId props element, the default target selector,
    // or undefined if document doesn't exist.
    var getHost = function (doc) {
        var _a, _b;
        var root = shadowRoot !== null && shadowRoot !== void 0 ? shadowRoot : doc;
        if (hostId) {
            var layerHost = Layer_notification_1.getLayerHost(hostId);
            if (layerHost) {
                return (_a = layerHost.rootRef.current) !== null && _a !== void 0 ? _a : null;
            }
            return (_b = root.getElementById(hostId)) !== null && _b !== void 0 ? _b : null;
        }
        else {
            var defaultHostSelector = Layer_notification_1.getDefaultTarget();
            // Find the host.
            var host = defaultHostSelector ? root.querySelector(defaultHostSelector) : null;
            // If no host is available, create a container for injecting layers in.
            // Having a container scopes layout computation.
            if (!host) {
                host = Layer_notification_1.createDefaultLayerHost(doc, shadowRoot);
            }
            return host;
        }
    };
    // Removes the current layer element's parentNode and runs onLayerWillUnmount prop if provided.
    var removeLayerElement = function () {
        onLayerWillUnmount === null || onLayerWillUnmount === void 0 ? void 0 : onLayerWillUnmount();
        var elem = layerRef.current;
        // Clear ref before removing from the DOM
        layerRef.current = undefined;
        if (elem && elem.parentNode) {
            elem.parentNode.removeChild(elem);
        }
    };
    // If a doc or host exists, it will remove and update layer parentNodes.
    var createLayerElement = function () {
        var _a;
        var doc = Utilities_1.getDocument(rootRef.current);
        if (!doc) {
            return;
        }
        var host = getHost(doc);
        if (!host) {
            return;
        }
        // Remove and re-create any previous existing layer elements.
        removeLayerElement();
        var el = ((_a = host.ownerDocument) !== null && _a !== void 0 ? _a : doc).createElement('div');
        el.className = classNames.root;
        Utilities_1.setPortalAttribute(el);
        Utilities_1.setVirtualParent(el, rootRef.current);
        insertFirst ? host.insertBefore(el, host.firstChild) : host.appendChild(el);
        layerRef.current = el;
        setNeedRaiseLayerMount(true);
    };
    react_hooks_1.useIsomorphicLayoutEffect(function () {
        createLayerElement();
        // Check if the user provided a hostId prop and register the layer with the ID.
        if (hostId) {
            Layer_notification_1.registerLayer(hostId, createLayerElement);
        }
        var unregisterPortalEl = layerRef.current ? registerPortalEl(layerRef.current) : undefined;
        return function () {
            if (unregisterPortalEl) {
                unregisterPortalEl();
            }
            removeLayerElement();
            if (hostId) {
                Layer_notification_1.unregisterLayer(hostId, createLayerElement);
            }
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps -- should run if the hostId updates.
    }, [hostId]);
    React.useEffect(function () {
        if (layerRef.current && needRaiseLayerMount) {
            onLayerMounted === null || onLayerMounted === void 0 ? void 0 : onLayerMounted();
            onLayerDidMount === null || onLayerDidMount === void 0 ? void 0 : onLayerDidMount();
            setNeedRaiseLayerMount(false);
        }
    }, [needRaiseLayerMount, onLayerMounted, onLayerDidMount]);
    useDebugWarnings(props);
    var focusRectsContext = React.useMemo(function () { return ({
        providerRef: fabricRef,
    }); }, [fabricRef]);
    return (React.createElement("span", { className: "ms-layer", ref: mergedRef }, layerRef.current &&
        ReactDOM.createPortal(React.createElement(Utilities_1.FocusRectsProvider, { value: focusRectsContext },
            React.createElement(Fabric_1.Fabric, tslib_1.__assign({}, (!eventBubblingEnabled && getFilteredEvents()), { className: classNames.content, ref: fabricRef }), children)), layerRef.current)));
});
exports.LayerBase.displayName = 'LayerBase';
var filteredEventProps;
var onFilterEvent = function (ev) {
    // We should just be able to check ev.bubble here and only stop events that are bubbling up. However, even though
    // mouseenter and mouseleave do NOT bubble up, they are showing up as bubbling. Therefore we stop events based on
    // event name rather than ev.bubble.
    if (ev.eventPhase === Event.BUBBLING_PHASE &&
        ev.type !== 'mouseenter' &&
        ev.type !== 'mouseleave' &&
        ev.type !== 'touchstart' &&
        ev.type !== 'touchend') {
        ev.stopPropagation();
    }
};
function getFilteredEvents() {
    if (!filteredEventProps) {
        filteredEventProps = {};
        [
            'onClick',
            'onContextMenu',
            'onDoubleClick',
            'onDrag',
            'onDragEnd',
            'onDragEnter',
            'onDragExit',
            'onDragLeave',
            'onDragOver',
            'onDragStart',
            'onDrop',
            'onMouseDown',
            'onMouseEnter',
            'onMouseLeave',
            'onMouseMove',
            'onMouseOver',
            'onMouseOut',
            'onMouseUp',
            'onTouchMove',
            'onTouchStart',
            'onTouchCancel',
            'onTouchEnd',
            'onKeyDown',
            'onKeyPress',
            'onKeyUp',
            'onFocus',
            'onBlur',
            'onChange',
            'onInput',
            'onInvalid',
            'onSubmit',
        ].forEach(function (name) { return (filteredEventProps[name] = onFilterEvent); });
    }
    return filteredEventProps;
}
function useDebugWarnings(props) {
    if (process.env.NODE_ENV !== 'production') {
        // eslint-disable-next-line react-hooks/rules-of-hooks -- build-time conditional
        react_hooks_1.useWarnings({
            name: 'Layer',
            props: props,
            deprecations: { onLayerMounted: 'onLayerDidMount' },
        });
    }
}
//# sourceMappingURL=Layer.base.js.map