import _extends from "@babel/runtime/helpers/extends";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
// Copyright 1999-2020. Plesk International GmbH. All rights reserved.

/* eslint-disable react/no-deprecated */
import React, { cloneElement, Component, createRef } from 'react';
import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Measure from 'react-measure';
import { CLS_PREFIX, Z_INDEX_POPOVER } from '../../constants';
import Popper from '../Popper';
import Button from '../Button';
import Heading from '../Heading';
import AutoClosable from '../AutoClosable';
import { isLikeText } from '../Translate';
import { wrapFunction } from '../utils';
import OnDarkContext from '../OnDarkContext';
import FocusTrap from '../FocusTrap';
const PLACEMENT_MAP = {
  'top-left': 'top-end',
  'top-right': 'top-start',
  'right-top': 'right-end',
  'right-bottom': 'right-start',
  'bottom-left': 'bottom-end',
  'bottom-right': 'bottom-start',
  'left-top': 'left-end',
  'left-bottom': 'left-start'
};
const ARROW_HEIGHT = 26;

const isControlled = props => props.visible !== null;

const normalize = value => String(Number(value)) === String(value) ? `${value}px` : value;
/**
 * `Popover` component is non-modal pop-up window used for providing additional information
 * for an interface element. It also may contain Form with few fields or any other small content.
 * @since 0.0.42
 */


class Popover extends Component {
  constructor(...args) {
    super(...args);

    _defineProperty(this, "state", {
      show: this.props.defaultVisible,
      arrowInHeader: false
    });

    _defineProperty(this, "contentRef", /*#__PURE__*/createRef());

    _defineProperty(this, "targetRef", /*#__PURE__*/createRef());

    _defineProperty(this, "checkArrowPosition", () => {
      const {
        title
      } = this.props;
      const {
        show,
        arrowInHeader
      } = this.state;

      if (!show || !this.popperData) {
        return;
      }

      const {
        actualPlacement,
        arrowStyle
      } = this.popperData;
      const {
        top
      } = arrowStyle;
      const [direction] = actualPlacement.split('-');
      let newArrowInHeader = false;
      const directionBottomOrPopoverLowEnough = direction === 'bottom' || top !== null && this.headerBounds && top < this.headerBounds.height - ARROW_HEIGHT / 2;
      const rightDirection = direction !== 'top' && directionBottomOrPopoverLowEnough;

      if (title && this.headerBounds && rightDirection) {
        newArrowInHeader = true;
      }

      if (arrowInHeader !== newArrowInHeader) {
        this.setState({
          arrowInHeader: newArrowInHeader
        });
      }
    });

    _defineProperty(this, "hotkeyHandler", e => {
      if (!e.isDefaultPrevented() && e.key === 'Escape') {
        this.handleClose();
        e.preventDefault();
      }
    });

    _defineProperty(this, "handleTargetClick", e => {
      const {
        show
      } = this.state;
      const {
        canCloseOnOutsideClick
      } = this.props;

      if (show && !canCloseOnOutsideClick) {
        return;
      }

      this.targetRef.current = e.currentTarget;
      this.setState(prevState => ({
        show: !prevState.show
      }));
    });

    _defineProperty(this, "handleClose", () => {
      const {
        show
      } = this.state;

      if (!show) {
        return;
      }

      const {
        onClose,
        canCloseOnEscapePress,
        autoFocus
      } = this.props;
      const isNeedFocus = autoFocus && canCloseOnEscapePress && this.prevFocusElement && (!document.activeElement || this.contentRef.current.contains(document.activeElement));

      if (onClose) {
        onClose();
      } else {
        this.setState({
          show: false
        });
      }

      if (isNeedFocus) {
        this.prevFocusElement.focus();
      }
    });

    _defineProperty(this, "handleHeaderResize", contentRect => {
      this.headerBounds = contentRect.bounds;
      this.checkArrowPosition();
    });

    _defineProperty(this, "handlePopperUpdate", popperData => {
      this.popperData = popperData;
      this.checkArrowPosition();
    });

    _defineProperty(this, "getIgnoreNodes", () => {
      const {
        targetRef
      } = this.props;

      if (targetRef || this.targetRef.current) {
        return [targetRef, this.targetRef];
      }

      return [findDOMNode(this)];
    });
  }

  static getDerivedStateFromProps(props, state) {
    return {
      show: isControlled(props) ? props.visible : state.show
    };
  }

  componentDidUpdate(prevProps, prevState) {
    const {
      canCloseOnEscapePress,
      autoFocus
    } = this.props;
    const {
      show
    } = this.state;

    if (autoFocus && canCloseOnEscapePress && !prevState.show && show) {
      setTimeout(() => this.focus(), 0);
    }
  }

  focus() {
    if (this.contentRef.current === null || document.activeElement === null || !this.state.show) {
      return;
    }

    this.prevFocusElement = document.activeElement;
    const isFocusOutside = !this.contentRef.current.contains(document.activeElement);

    if (isFocusOutside) {
      const autofocusElement = this.contentRef.current.querySelector('[autofocus]');

      if (autofocusElement !== null) {
        autofocusElement.focus();
      } else if (this.contentRef.current) {
        this.contentRef.current.focus();
      }
    }
  }

  render() {
    const {
      baseClassName,
      className,
      visible,
      defaultVisible,
      title,
      placement,
      canCloseOnOutsideClick,
      canCloseOnEscapePress,
      target,
      targetRef,
      intent,
      wide,
      width,
      height,
      extra,
      onClose,
      children,
      ...props
    } = this.props;
    const {
      show,
      arrowInHeader
    } = this.state;
    let behavior;

    if (placement !== 'auto') {
      const [direction] = placement.split('-');

      if (direction === 'top') {
        behavior = ['top', 'bottom', 'right', 'left'];
      } else if (direction === 'right') {
        behavior = ['right', 'left', 'bottom', 'top'];
      } else if (direction === 'bottom') {
        behavior = ['bottom', 'top', 'left', 'right'];
      } else if (direction === 'left') {
        behavior = ['left', 'right', 'top', 'bottom'];
      }
    }

    const close = /*#__PURE__*/React.createElement(Button, {
      className: classNames(`${baseClassName}__close`, title && `${baseClassName}__close--heading`),
      onClick: this.handleClose,
      ghost: true,
      icon: {
        name: 'cross-mark',
        size: title ? 16 : 12
      }
    });
    const heading = title && /*#__PURE__*/React.createElement(Measure, {
      onResize: this.handleHeaderResize,
      bounds: true
    }, ({
      measureRef
    }) => /*#__PURE__*/React.createElement("div", {
      ref: measureRef,
      className: `${baseClassName}__heading`
    }, isLikeText(title) ? /*#__PURE__*/React.createElement(Heading, {
      level: "5",
      className: `${baseClassName}__title`
    }, title) : title, /*#__PURE__*/React.createElement("div", {
      className: `${baseClassName}__extra`
    }, extra, close)));
    const trigger = isControlled(this.props) ? target : /*#__PURE__*/cloneElement(target, {
      className: classNames(target.props.className, `${baseClassName}__target`),
      onClick: wrapFunction(target.props.onClick, this.handleTargetClick)
    });
    const popperPlacement = PLACEMENT_MAP[placement] || placement;
    let wrapperStyle;

    if (height) {
      wrapperStyle = {
        height: normalize(height)
      };
    }

    const content = /*#__PURE__*/React.createElement(FocusTrap, {
      ref: this.contentRef,
      onKeyUp: canCloseOnEscapePress && show ? this.hotkeyHandler : null,
      className: `${baseClassName}__inner`,
      style: wrapperStyle
    }, height ? heading : heading || close, /*#__PURE__*/React.createElement("div", {
      className: `${baseClassName}__content`
    }, /*#__PURE__*/React.createElement("div", {
      className: `${baseClassName}__content-inner`
    }, children)));
    return /*#__PURE__*/React.createElement(Popper, _extends({
      target: trigger,
      targetRef: targetRef ? targetRef : this.targetRef,
      arrow: /*#__PURE__*/React.createElement("span", {
        className: classNames(`${baseClassName}__arrow`, arrowInHeader && `${baseClassName}__arrow--heading`)
      }),
      placement: popperPlacement,
      onUpdate: this.handlePopperUpdate,
      className: classNames({
        [`${baseClassName}--heading`]: title,
        [`${baseClassName}--${intent}`]: intent,
        [`${baseClassName}--wide`]: wide || width,
        [`${baseClassName}--width-${width}`]: width,
        [`${baseClassName}--on-dark`]: this.context === true
      }, className),
      baseClassName: baseClassName,
      behavior: behavior,
      zIndex: Z_INDEX_POPOVER,
      show: show
    }, props), canCloseOnOutsideClick ? /*#__PURE__*/React.createElement(AutoClosable, {
      onClose: this.handleClose,
      ignoreNodes: this.getIgnoreNodes,
      nodeRef: this.contentRef
    }, content) : content);
  }

}

_defineProperty(Popover, "contextType", OnDarkContext);

Popover.propTypes = {
  /**
   * The reference element used to position the popover.
   * @since 0.0.65
   */
  target: PropTypes.element.isRequired,

  /**
   * A React reference to the DOM element that needs for calculating the position of the popup.
   * It is required only when visible or defaultVisible props are provided.
   * @since 3.12.0
   */
  targetRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({
    current: PropTypes.instanceOf(HTMLElement)
  })]),

  /**
   * Popover visibility. For controlled component only.
   * @since 0.0.65
   */
  visible: PropTypes.bool,

  /**
   * Popover default (on init) visibility. For uncontrolled component only.
   * @since 0.0.66
   */
  defaultVisible: PropTypes.bool,

  /**
   * Popover title. Header will only be rendered if this prop is defined.
   * @since 0.0.42
   */
  title: PropTypes.node,

  /**
   * Click handler for close button. If it is not defined then default behavior (hide popover) will be used.
   * @since 0.0.42
   */
  onClose: PropTypes.func,

  /**
   * Popover placement
   * @since 0.0.42
   */
  placement: PropTypes.oneOf(['auto', 'top-left', 'top', 'top-right', 'right-top', 'right', 'right-bottom', 'bottom-right', 'bottom', 'bottom-left', 'left-bottom', 'left', 'left-top']),

  /**
   * Indicating if clicking outside the popover should close it.
   * @since 0.0.65
   */
  canCloseOnOutsideClick: PropTypes.bool,

  /**
   * Indicating if pressing the escape key should close the popover.
   * @since 0.0.65
   */
  canCloseOnEscapePress: PropTypes.bool,

  /**
   * Content of the Popover
   * @since 0.0.42
   */
  children: PropTypes.node,

  /**
   * Visual intent color to apply to component.
   * @since 0.0.59
   */
  intent: PropTypes.oneOf(['info', 'success', 'warning', 'danger']),

  /**
   * Undo predefined max-width.
   * @since 2.4.0
   */
  wide: PropTypes.bool,

  /**
   * Define the popover width.
   * @since 3.2.0
   */
  width: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),

  /**
   * Define the popover height.
   * @since 3.3.0
   */
  height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

  /**
   * A slot for buttons or links in the header.
   * @since 3.16.0
   */
  extra: PropTypes.node,

  /**
   * @ignore
   */
  className: PropTypes.string,

  /**
   * @ignore
   */
  baseClassName: PropTypes.string,

  /**
   * Boundaries element for preventOverflow property.
   * @since 1.12.4
   */
  boundariesElement: PropTypes.oneOfType([PropTypes.oneOf(['scrollParent', 'window', 'viewport']), PropTypes.instanceOf(HTMLElement)]),

  /**
   * @ignore
   */
  autoFocus: PropTypes.bool
};
Popover.defaultProps = {
  visible: null,
  defaultVisible: false,
  title: undefined,
  onClose: undefined,
  placement: 'auto',
  canCloseOnOutsideClick: true,
  canCloseOnEscapePress: true,
  children: undefined,
  intent: undefined,
  wide: undefined,
  width: undefined,
  height: undefined,
  extra: undefined,
  className: undefined,
  baseClassName: `${CLS_PREFIX}popover`,
  boundariesElement: undefined,
  autoFocus: true,
  targetRef: undefined
};
export default Popover;