import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { usePopper } from './thirdparty/react-popper/usePopper';
import { css } from '@patternfly/react-styles';
import { FindRefWrapper } from './FindRefWrapper';
import '@patternfly/react-styles/css/components/Popper/Popper.css';
const hash = {
    left: 'right',
    right: 'left',
    bottom: 'top',
    top: 'bottom',
    'top-start': 'bottom-end',
    'top-end': 'bottom-start',
    'bottom-start': 'top-end',
    'bottom-end': 'top-start',
    'left-start': 'right-end',
    'left-end': 'right-start',
    'right-start': 'left-end',
    'right-end': 'left-start'
};
const getOppositePlacement = (placement) => placement.replace(/left|right|bottom|top|top-start|top-end|bottom-start|bottom-end|right-start|right-end|left-start|left-end/g, (matched) => hash[matched]);
export const getOpacityTransition = (animationDuration) => `opacity ${animationDuration}ms cubic-bezier(.54, 1.5, .38, 1.11)`;
export const Popper = ({ trigger, popper, popperMatchesTriggerWidth = true, direction = 'down', position = 'left', placement, appendTo = () => document.body, zIndex = 9999, isVisible = true, positionModifiers, distance = 0, onMouseEnter, onMouseLeave, onFocus, onBlur, onDocumentClick, onTriggerClick, onTriggerEnter, onPopperClick, onPopperMouseEnter, onPopperMouseLeave, onDocumentKeyDown, enableFlip = true, flipBehavior = 'flip', reference, removeFindDomNode = false, popperRef }) => {
    const [triggerElement, setTriggerElement] = React.useState(null);
    const [refElement, setRefElement] = React.useState(null);
    const [popperElement, setPopperElement] = React.useState(null);
    const [ready, setReady] = React.useState(false);
    const refOrTrigger = refElement || triggerElement;
    const onDocumentClickCallback = React.useCallback((event) => onDocumentClick(event, refOrTrigger, popperElement), [isVisible, triggerElement, refElement, popperElement, onDocumentClick]);
    React.useEffect(() => {
        setReady(true);
    }, []);
    React.useEffect(() => {
        if (reference) {
            if (reference.current) {
                setRefElement(reference.current);
            }
            else if (typeof reference === 'function') {
                setRefElement(reference());
            }
        }
    }, [reference]);
    React.useEffect(() => {
        // When the popperRef is defined or the popper visiblity changes, ensure the popper element is up to date
        if (popperRef) {
            if (popperRef.current) {
                setPopperElement(popperRef.current);
            }
            else if (typeof popperRef === 'function') {
                setPopperElement(popperRef());
            }
        }
    }, [isVisible, popperRef]);
    const addEventListener = (listener, element, event, capture = false) => {
        if (listener && element) {
            element.addEventListener(event, listener, { capture });
        }
    };
    const removeEventListener = (listener, element, event, capture = false) => {
        if (listener && element) {
            element.removeEventListener(event, listener, { capture });
        }
    };
    React.useEffect(() => {
        addEventListener(onMouseEnter, refOrTrigger, 'mouseenter');
        addEventListener(onMouseLeave, refOrTrigger, 'mouseleave');
        addEventListener(onFocus, refOrTrigger, 'focus');
        addEventListener(onBlur, refOrTrigger, 'blur');
        addEventListener(onTriggerClick, refOrTrigger, 'click');
        addEventListener(onTriggerEnter, refOrTrigger, 'keydown');
        addEventListener(onPopperClick, popperElement, 'click');
        addEventListener(onPopperMouseEnter, popperElement, 'mouseenter');
        addEventListener(onPopperMouseLeave, popperElement, 'mouseleave');
        onDocumentClick && addEventListener(onDocumentClickCallback, document, 'click', true);
        addEventListener(onDocumentKeyDown, document, 'keydown', true);
        // Trigger a Popper update when content changes.
        const observer = new MutationObserver(() => {
            update && update();
        });
        popperElement && observer.observe(popperElement, { attributes: true, childList: true, subtree: true });
        return () => {
            removeEventListener(onMouseEnter, refOrTrigger, 'mouseenter');
            removeEventListener(onMouseLeave, refOrTrigger, 'mouseleave');
            removeEventListener(onFocus, refOrTrigger, 'focus');
            removeEventListener(onBlur, refOrTrigger, 'blur');
            removeEventListener(onTriggerClick, refOrTrigger, 'click');
            removeEventListener(onTriggerEnter, refOrTrigger, 'keydown');
            removeEventListener(onPopperClick, popperElement, 'click');
            removeEventListener(onPopperMouseEnter, popperElement, 'mouseenter');
            removeEventListener(onPopperMouseLeave, popperElement, 'mouseleave');
            onDocumentClick && removeEventListener(onDocumentClickCallback, document, 'click', true);
            removeEventListener(onDocumentKeyDown, document, 'keydown', true);
            observer.disconnect();
        };
    }, [
        triggerElement,
        popperElement,
        onMouseEnter,
        onMouseLeave,
        onFocus,
        onBlur,
        onTriggerClick,
        onTriggerEnter,
        onPopperClick,
        onPopperMouseEnter,
        onPopperMouseLeave,
        onDocumentClick,
        onDocumentKeyDown,
        refElement
    ]);
    const getPlacement = () => {
        if (placement) {
            return placement;
        }
        let convertedPlacement = direction === 'up' ? 'top' : 'bottom';
        if (position !== 'center') {
            convertedPlacement = `${convertedPlacement}-${position === 'right' ? 'end' : 'start'}`;
        }
        return convertedPlacement;
    };
    const getPlacementMemo = React.useMemo(getPlacement, [direction, position, placement]);
    const getOppositePlacementMemo = React.useMemo(() => getOppositePlacement(getPlacement()), [
        direction,
        position,
        placement
    ]);
    const sameWidthMod = React.useMemo(() => ({
        name: 'sameWidth',
        enabled: popperMatchesTriggerWidth,
        phase: 'beforeWrite',
        requires: ['computeStyles'],
        fn: ({ state }) => {
            state.styles.popper.width = `${state.rects.reference.width}px`;
        },
        effect: ({ state }) => {
            state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`;
            return () => { };
        }
    }), [popperMatchesTriggerWidth]);
    const { styles: popperStyles, attributes, update, forceUpdate } = usePopper(refOrTrigger, popperElement, {
        placement: getPlacementMemo,
        modifiers: [
            {
                name: 'offset',
                options: {
                    offset: [0, distance]
                }
            },
            {
                name: 'preventOverflow',
                enabled: false
            },
            {
                // adds attribute [data-popper-reference-hidden] to the popper element which can be used to hide it using CSS
                name: 'hide',
                enabled: true
            },
            {
                name: 'flip',
                enabled: getPlacementMemo.startsWith('auto') || enableFlip,
                options: {
                    fallbackPlacements: flipBehavior === 'flip' ? [getOppositePlacementMemo] : flipBehavior
                }
            },
            sameWidthMod
        ]
    });
    React.useEffect(() => {
        forceUpdate && forceUpdate();
    }, [popper]);
    // Returns the CSS modifier class in order to place the Popper's arrow properly
    // Depends on the position of the Popper relative to the reference element
    const modifierFromPopperPosition = () => {
        if (attributes && attributes.popper && attributes.popper['data-popper-placement']) {
            const popperPlacement = attributes.popper['data-popper-placement'];
            return positionModifiers[popperPlacement];
        }
        return positionModifiers.top;
    };
    const options = Object.assign({ className: css(popper.props && popper.props.className, positionModifiers && modifierFromPopperPosition()), style: Object.assign(Object.assign(Object.assign({}, ((popper.props && popper.props.style) || {})), popperStyles.popper), { zIndex }) }, attributes.popper);
    const menuWithPopper = React.cloneElement(popper, options);
    const getTarget = () => {
        if (typeof appendTo === 'function') {
            return appendTo();
        }
        return appendTo;
    };
    /**
     * To enable strict mode, the popper must either have its reference defined via the popperRef property,
     * or the removeFindDomNode flag must be present. Even if the reference is passed in, unlike the trigger,
     * the popper property must still be passed.
     * The trigger must similarly have either its reference defined via the reference property, or the
     * removeFindDomNode flag must be present. The trigger property is not required when the reference is passed.
     *
     * Strict mode may be enabled by passing both reference properties to Popper, or by passing the
     * removeFindDomNode flag with either reference property, or by solely passing the removeFindDomNode
     * flag.
     */
    let popperPortal;
    if (removeFindDomNode) {
        // If removeFindDomNode is passed, use the removeFindDomNode method of wrapping divs
        popperPortal = (React.createElement("div", { style: { display: 'contents' }, ref: node => setPopperElement(node === null || node === void 0 ? void 0 : node.firstElementChild) }, menuWithPopper));
    }
    else if (popperRef) {
        // If removeFindDomNode is not passed and popperRef is passed, use the popperRef method
        popperPortal = menuWithPopper;
    }
    else {
        // If neither removeFindDomNode and popperRef exist, use the old method of FindRefWrapper
        popperPortal = (React.createElement(FindRefWrapper, { onFoundRef: (foundRef) => setPopperElement(foundRef) }, menuWithPopper));
    }
    return (React.createElement(React.Fragment, null,
        !reference && trigger && React.isValidElement(trigger) && !removeFindDomNode && (React.createElement(FindRefWrapper, { onFoundRef: (foundRef) => setTriggerElement(foundRef) }, trigger)),
        !reference && trigger && React.isValidElement(trigger) && removeFindDomNode && (React.createElement("div", { style: { display: 'contents' }, ref: node => setTriggerElement(node === null || node === void 0 ? void 0 : node.firstElementChild) }, trigger)),
        ready && isVisible && ReactDOM.createPortal(popperPortal, getTarget())));
};
Popper.displayName = 'Popper';
//# sourceMappingURL=Popper.js.map