function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

import React from "react";
import PropTypes from "prop-types";
import Tether from "tether";
/**
 * Intelligently position elements on a page.
 * @param {Object} props - Properties passed to component
 * @returns {ReactElement}
 */

var OverlayWrapper =
/*#__PURE__*/
function (_React$Component) {
  _inherits(OverlayWrapper, _React$Component);

  function OverlayWrapper(props) {
    var _this;

    _classCallCheck(this, OverlayWrapper);

    _this = _possibleConstructorReturn(this, _getPrototypeOf(OverlayWrapper).call(this, props));
    _this.state = {
      isOverlayOpen: false
    };
    _this.onChildClick = _this.onChildClick.bind(_assertThisInitialized(_this));
    _this.onChildMouseOver = _this.onChildMouseOver.bind(_assertThisInitialized(_this));
    _this.onChildMouseOut = _this.onChildMouseOut.bind(_assertThisInitialized(_this));
    _this.disableTether = _this.disableTether.bind(_assertThisInitialized(_this));
    return _this;
  }

  _createClass(OverlayWrapper, [{
    key: "getChildContext",
    value: function getChildContext() {
      return {
        hideOverlay: this.disableTether
      };
    }
  }, {
    key: "componentDidMount",
    value: function componentDidMount() {
      var options = {
        element: this._overlayEl,
        target: this._activatorEl.firstChild,
        attachment: "".concat(this.props.verticalAttachment, " ").concat(this.props.horizontalAttachment),
        constraints: [{
          to: "window",
          attachment: this.props.attachmentConstraint,
          pin: this.props.isConstrainedToScreen
        }]
      };

      if (this.props.verticalTargetAttachment && this.props.horizontalTargetAttachment) {
        options.targetAttachment = "".concat(this.props.verticalTargetAttachment, " ").concat(this.props.horizontalTargetAttachment);
      }

      this.initTether = function () {
        return new Tether(options);
      };
    }
  }, {
    key: "enableTether",
    value: function enableTether() {
      var _this2 = this;

      // If tether hasn't yet been created, do it now.
      if (!this._tether) {
        this._tether = this.initTether();
      }

      this.setState({
        isOverlayOpen: true
      });

      this._tether.enable();

      if (typeof this.props.onShow === "function") {
        this.props.onShow();
      } // Reposition once the overlay is visible because Tether can't
      // properly calculate the positioning when the overlay is not
      // displayed.


      setTimeout(function () {
        _this2._tether.position();
      }); // These functions must be stored so that it can be still be removed even
      // though `bind` was used: http://stackoverflow.com/a/22870717/316602

      this._bodyClickListener = this.props.shouldHideOnClick && this.isClickWithinOverlayOrChildren.bind(this);
      this._documentEscapeListener = this.onEscapeKey.bind(this);

      if (this._bodyClickListener) {
        document.addEventListener("click", this._bodyClickListener);
      }

      document.addEventListener("keyup", this._documentEscapeListener);
    }
  }, {
    key: "disableTether",
    value: function disableTether() {
      // If tether hasn't been created, return early
      if (!this._tether) {
        return;
      }

      var shouldHide = true;

      if (typeof this.props.onHide === "function") {
        shouldHide = this.props.onHide();
      }

      if (shouldHide === false) {
        // Exit out of this function before hiding the `overlay` if the supplied
        // `onHide` function returns false. This is useful for fine-tuned control
        // of when the `overlay` should close.
        return;
      }

      this.setState({
        isOverlayOpen: false
      });

      this._tether.disable();

      this.removeBodyEventListener();
    }
  }, {
    key: "removeBodyEventListener",
    value: function removeBodyEventListener() {
      if (this._bodyClickListener) {
        document.removeEventListener("click", this._bodyClickListener);
      }

      if (this._documentEscapeListener) {
        document.removeEventListener("keyup", this._documentEscapeListener);
      }
    }
    /**
     * Disable Tether if a user clicks outside of the `overlay` and `children`.
     * @param {Object} event - Click event
     */

  }, {
    key: "isClickWithinOverlayOrChildren",
    value: function isClickWithinOverlayOrChildren(event) {
      var target = event.target;
      var shouldCloseOverlay = true;

      while (target.parentNode !== null && shouldCloseOverlay) {
        if (target === this._overlayEl || target === this._activatorEl) {
          shouldCloseOverlay = false;
        }

        target = target.parentNode;
      }

      if (shouldCloseOverlay) {
        this.disableTether();
      }
    }
  }, {
    key: "componentWillUnmount",
    value: function componentWillUnmount() {
      this.removeBodyEventListener(); // If tether hasn't been created, return early

      if (!this._tether) {
        return;
      }

      this._tether.destroy();

      if (this._tether.element && this._tether.element.remove) {
        this._tether.element.remove();
      }
    }
  }, {
    key: "onChildClick",
    value: function onChildClick(event, child) {
      // Toggle the `overlay` by enabling or disabling Tether.
      if (this.state.isOverlayOpen) {
        // Disable Tether when not visible for performance reasons.
        this.disableTether();
      } else {
        // Enable Tether when visible.
        this.enableTether();
      } // Run the `children`'s `onClick` if it exists.


      if (child.props.onClick) {
        child.props.onClick(event);
      }
    }
  }, {
    key: "onChildMouseOver",
    value: function onChildMouseOver(event, child) {
      // Enable Tether when visible.
      this.enableTether(); // Run the `children`'s `onMouseOver` if it exists.

      if (child.props.onMouseOver) {
        child.props.onMouseOver(event);
      }
    }
  }, {
    key: "onChildMouseOut",
    value: function onChildMouseOut(event, child) {
      // Disable Tether when not visible for performance reasons.
      this.disableTether(); // Run the `children`'s `onMouseOut` if it exists.

      if (child.props.onMouseOut) {
        child.props.onMouseOut(event);
      }
    }
  }, {
    key: "onEscapeKey",
    value: function onEscapeKey(event) {
      // Escape key
      if (event.keyCode === 27) {
        this.disableTether();
      }
    }
  }, {
    key: "render",
    value: function render() {
      var _this3 = this;

      var _this$props = this.props,
          attachmentConstraint = _this$props.attachmentConstraint,
          behavior = _this$props.behavior,
          children = _this$props.children,
          className = _this$props.className,
          horizontalAttachment = _this$props.horizontalAttachment,
          horizontalTargetAttachment = _this$props.horizontalTargetAttachment,
          isConstrainedToScreen = _this$props.isConstrainedToScreen,
          onHide = _this$props.onHide,
          onShow = _this$props.onShow,
          overlay = _this$props.overlay,
          shouldHideOnClick = _this$props.shouldHideOnClick,
          testSection = _this$props.testSection,
          verticalAttachment = _this$props.verticalAttachment,
          verticalTargetAttachment = _this$props.verticalTargetAttachment,
          props = _objectWithoutProperties(_this$props, ["attachmentConstraint", "behavior", "children", "className", "horizontalAttachment", "horizontalTargetAttachment", "isConstrainedToScreen", "onHide", "onShow", "overlay", "shouldHideOnClick", "testSection", "verticalAttachment", "verticalTargetAttachment"]);

      var eventHandlerProps;
      var child = React.Children.only(children);

      switch (behavior) {
        case "click":
          eventHandlerProps = {
            onClick: function onClick(event) {
              return _this3.onChildClick.call(null, event, child);
            }
          };
          break;

        case "hover":
          eventHandlerProps = {
            onMouseOver: function onMouseOver(event) {
              return _this3.onChildMouseOver.call(null, event, child);
            },
            onMouseOut: function onMouseOut(event) {
              return _this3.onChildMouseOut.call(null, event, child);
            }
          };
          break;

        default:
      }

      var block = {
        display: "inline-block"
      };
      var overlayState = this.state.isOverlayOpen ? {
        display: "block"
      } : {
        display: "none"
      };
      return (
        /* eslint-disable react/jsx-no-bind */
        React.createElement("div", _extends({
          className: className,
          "data-oui-component": true,
          "data-test-section": testSection,
          style: block
        }, props), React.createElement("div", _extends({
          className: "click-area",
          style: block,
          ref: function ref(_ref) {
            _this3._activatorEl = _ref;
          }
        }, eventHandlerProps), child), React.createElement("div", {
          ref: function ref(_ref2) {
            _this3._overlayEl = _ref2;
          },
          style: overlayState
        }, overlay))
        /* eslint-enable */

      );
    }
  }]);

  return OverlayWrapper;
}(React.Component);

OverlayWrapper.propTypes = {
  /** Determines how the attachment is constrained to the target */
  attachmentConstraint: PropTypes.oneOf(["both", "element", "none", "target", "together"]),

  /** Event to listen to and open the overlay */
  behavior: PropTypes.oneOf(["click", "hover"]),

  /** Element that the `overlay` should attach to */
  children: PropTypes.node.isRequired,

  /** CSS class names. */
  className: PropTypes.string,

  /** Side of the `overlay` that should attach to the `children` */
  horizontalAttachment: PropTypes.oneOf(["left", "center", "right"]),

  /** Side of `children` that should attach to the `overlay` */
  horizontalTargetAttachment: PropTypes.oneOf(["left", "center", "right"]),

  /** Attach `overlay` to an edge of the screen if it is going to move off */
  isConstrainedToScreen: PropTypes.bool,

  /**
   * Function that runs when the `overlay` is hidden. Return `false` to prevent
   * the `overlay` from closing.
   */
  onHide: PropTypes.func,

  /** Function that runs when the `overlay` is shown */
  onShow: PropTypes.func,

  /** The element that is attached to the children */
  overlay: PropTypes.node.isRequired,

  /** Should the `overlay` close when clicking outside of it */
  shouldHideOnClick: PropTypes.bool,

  /** Hook for automated JavaScript tests */
  testSection: PropTypes.string,

  /** Vertical edge of the `overlay` that should touch the `children` */
  verticalAttachment: PropTypes.oneOf(["top", "middle", "bottom"]),

  /** Vertical edge of the `children` that should touch the `overlay` */
  verticalTargetAttachment: PropTypes.oneOf(["top", "middle", "bottom"])
};
OverlayWrapper.defaultProps = {
  attachmentConstraint: "together",
  behavior: "click",
  horizontalAttachment: "center",
  isConstrainedToScreen: false,
  onHide: function onHide() {
    return null;
  },
  shouldHideOnClick: true,
  verticalAttachment: "top"
};
OverlayWrapper.childContextTypes = {
  hideOverlay: PropTypes.func
};
export default OverlayWrapper;