"use strict";

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _react = _interopRequireWildcard(require("react"));

var _reactDom = require("react-dom");

var _propTypes = _interopRequireDefault(require("prop-types"));

var _classnames = _interopRequireDefault(require("classnames"));

var _reactMeasure = _interopRequireDefault(require("react-measure"));

var _constants = require("../../constants");

var _Popper = _interopRequireDefault(require("../Popper"));

var _Button = _interopRequireDefault(require("../Button"));

var _Heading = _interopRequireDefault(require("../Heading"));

var _AutoClosable = _interopRequireDefault(require("../AutoClosable"));

var _Translate = require("../Translate");

var _utils = require("../utils");

var _OnDarkContext = _interopRequireDefault(require("../OnDarkContext"));

var _FocusTrap = _interopRequireDefault(require("../FocusTrap"));

// Copyright 1999-2020. Plesk International GmbH. All rights reserved.

/* eslint-disable react/no-deprecated */
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) ? "".concat(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 _react.Component {
  constructor(...args) {
    super(...args);
    (0, _defineProperty2.default)(this, "state", {
      show: this.props.defaultVisible,
      arrowInHeader: false
    });
    (0, _defineProperty2.default)(this, "contentRef", /*#__PURE__*/(0, _react.createRef)());
    (0, _defineProperty2.default)(this, "targetRef", /*#__PURE__*/(0, _react.createRef)());
    (0, _defineProperty2.default)(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
        });
      }
    });
    (0, _defineProperty2.default)(this, "hotkeyHandler", e => {
      if (!e.isDefaultPrevented() && e.key === 'Escape') {
        this.handleClose();
        e.preventDefault();
      }
    });
    (0, _defineProperty2.default)(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
      }));
    });
    (0, _defineProperty2.default)(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();
      }
    });
    (0, _defineProperty2.default)(this, "handleHeaderResize", contentRect => {
      this.headerBounds = contentRect.bounds;
      this.checkArrowPosition();
    });
    (0, _defineProperty2.default)(this, "handlePopperUpdate", popperData => {
      this.popperData = popperData;
      this.checkArrowPosition();
    });
    (0, _defineProperty2.default)(this, "getIgnoreNodes", () => {
      const {
        targetRef
      } = this.props;

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

      return [(0, _reactDom.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.default.createElement(_Button.default, {
      className: (0, _classnames.default)("".concat(baseClassName, "__close"), title && "".concat(baseClassName, "__close--heading")),
      onClick: this.handleClose,
      ghost: true,
      icon: {
        name: 'cross-mark',
        size: title ? 16 : 12
      }
    });

    const heading = title && /*#__PURE__*/_react.default.createElement(_reactMeasure.default, {
      onResize: this.handleHeaderResize,
      bounds: true
    }, ({
      measureRef
    }) => /*#__PURE__*/_react.default.createElement("div", {
      ref: measureRef,
      className: "".concat(baseClassName, "__heading")
    }, (0, _Translate.isLikeText)(title) ? /*#__PURE__*/_react.default.createElement(_Heading.default, {
      level: "5",
      className: "".concat(baseClassName, "__title")
    }, title) : title, /*#__PURE__*/_react.default.createElement("div", {
      className: "".concat(baseClassName, "__extra")
    }, extra, close)));

    const trigger = isControlled(this.props) ? target : /*#__PURE__*/(0, _react.cloneElement)(target, {
      className: (0, _classnames.default)(target.props.className, "".concat(baseClassName, "__target")),
      onClick: (0, _utils.wrapFunction)(target.props.onClick, this.handleTargetClick)
    });
    const popperPlacement = PLACEMENT_MAP[placement] || placement;
    let wrapperStyle;

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

    const content = /*#__PURE__*/_react.default.createElement(_FocusTrap.default, {
      ref: this.contentRef,
      onKeyUp: canCloseOnEscapePress && show ? this.hotkeyHandler : null,
      className: "".concat(baseClassName, "__inner"),
      style: wrapperStyle
    }, height ? heading : heading || close, /*#__PURE__*/_react.default.createElement("div", {
      className: "".concat(baseClassName, "__content")
    }, /*#__PURE__*/_react.default.createElement("div", {
      className: "".concat(baseClassName, "__content-inner")
    }, children)));

    return /*#__PURE__*/_react.default.createElement(_Popper.default, (0, _extends2.default)({
      target: trigger,
      targetRef: targetRef ? targetRef : this.targetRef,
      arrow: /*#__PURE__*/_react.default.createElement("span", {
        className: (0, _classnames.default)("".concat(baseClassName, "__arrow"), arrowInHeader && "".concat(baseClassName, "__arrow--heading"))
      }),
      placement: popperPlacement,
      onUpdate: this.handlePopperUpdate,
      className: (0, _classnames.default)({
        ["".concat(baseClassName, "--heading")]: title,
        ["".concat(baseClassName, "--").concat(intent)]: intent,
        ["".concat(baseClassName, "--wide")]: wide || width,
        ["".concat(baseClassName, "--width-").concat(width)]: width,
        ["".concat(baseClassName, "--on-dark")]: this.context === true
      }, className),
      baseClassName: baseClassName,
      behavior: behavior,
      zIndex: _constants.Z_INDEX_POPOVER,
      show: show
    }, props), canCloseOnOutsideClick ? /*#__PURE__*/_react.default.createElement(_AutoClosable.default, {
      onClose: this.handleClose,
      ignoreNodes: this.getIgnoreNodes,
      nodeRef: this.contentRef
    }, content) : content);
  }

}

(0, _defineProperty2.default)(Popover, "contextType", _OnDarkContext.default);
Popover.propTypes = {
  /**
   * The reference element used to position the popover.
   * @since 0.0.65
   */
  target: _propTypes.default.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.default.oneOfType([_propTypes.default.func, _propTypes.default.shape({
    current: _propTypes.default.instanceOf(HTMLElement)
  })]),

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

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

  /**
   * Popover title. Header will only be rendered if this prop is defined.
   * @since 0.0.42
   */
  title: _propTypes.default.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.default.func,

  /**
   * Popover placement
   * @since 0.0.42
   */
  placement: _propTypes.default.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.default.bool,

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

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

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

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

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

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

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

  /**
   * @ignore
   */
  className: _propTypes.default.string,

  /**
   * @ignore
   */
  baseClassName: _propTypes.default.string,

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

  /**
   * @ignore
   */
  autoFocus: _propTypes.default.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: "".concat(_constants.CLS_PREFIX, "popover"),
  boundariesElement: undefined,
  autoFocus: true,
  targetRef: undefined
};
var _default = Popover;
exports.default = _default;