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 _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 _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 _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); }

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

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

/*
Copyright (c) 2018-2019 Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/

/* global document */

/* eslint-disable react/no-find-dom-node */
import * as React from 'react';
import { getOverride, getOverrideProps } from '../helpers/overrides.js';
import getBuiId from '../utils/get-bui-id.js';
import { ACCESSIBILITY_TYPE, PLACEMENT, TRIGGER_TYPE, ANIMATE_OUT_TIME, ANIMATE_IN_TIME } from './constants.js';
import { Layer, TetherBehavior } from '../layer/index.js';
import { Arrow as StyledArrow, Body as StyledBody, Inner as StyledInner } from './styled-components.js';
import { fromPopperPlacement } from './utils.js';
import defaultProps from './default-props.js';

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

  function Popover() {
    var _getPrototypeOf2;

    var _this;

    _classCallCheck(this, Popover);

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(Popover)).call.apply(_getPrototypeOf2, [this].concat(args)));

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "animateInTimer", void 0);

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "animateOutTimer", void 0);

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "animateOutCompleteTimer", void 0);

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onMouseEnterTimer", void 0);

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onMouseLeaveTimer", void 0);

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "generatedId", '');

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "anchorRef", React.createRef());

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "popperRef", React.createRef());

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "arrowRef", React.createRef());

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", _this.getDefaultState(_this.props));

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "animateIn", function () {
      if (_this.props.isOpen) {
        _this.setState({
          isAnimating: true
        });
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "animateOut", function () {
      if (!_this.props.isOpen) {
        _this.setState({
          isAnimating: true
        }); // Remove the popover from the DOM after animation finishes


        _this.animateOutCompleteTimer = setTimeout(function () {
          _this.setState({
            isAnimating: false,
            // Reset to ideal placement specified in props
            placement: _this.props.placement
          });
        }, _this.props.animateOutTime || ANIMATE_OUT_TIME);
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onAnchorClick", function (e) {
      if (_this.props.onClick) {
        _this.props.onClick(e);
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onAnchorMouseEnter", function () {
      // First clear any existing close timers, this ensures that the user can
      // move their mouse from the popover back to anchor without it hiding
      if (_this.onMouseLeaveTimer) {
        clearTimeout(_this.onMouseLeaveTimer);
      }

      _this.triggerOnMouseEnterWithDelay();
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onAnchorMouseLeave", function () {
      // Clear any existing open timer, otherwise popover could be stuck open
      if (_this.onMouseEnterTimer) {
        clearTimeout(_this.onMouseEnterTimer);
      }

      _this.triggerOnMouseLeaveWithDelay();
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onPopoverMouseEnter", function () {
      if (_this.onMouseLeaveTimer) {
        clearTimeout(_this.onMouseLeaveTimer);
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onPopoverMouseLeave", function () {
      _this.triggerOnMouseLeaveWithDelay();
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onKeyPress", function (evt) {
      if (evt.key === 'Escape' && _this.props.onEsc) {
        _this.props.onEsc();
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onPopperUpdate", function (normalizedOffsets, data) {
      var placement = fromPopperPlacement(data.placement) || PLACEMENT.top;

      _this.setState({
        arrowOffset: normalizedOffsets.arrow,
        popoverOffset: normalizedOffsets.popper,
        placement: placement
      }); // Now that element has been positioned, we can animate it in


      _this.animateInTimer = setTimeout(_this.animateIn, ANIMATE_IN_TIME);
      return data;
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "triggerOnMouseLeave", function () {
      if (_this.props.onMouseLeave) {
        _this.props.onMouseLeave();
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "triggerOnMouseEnter", function () {
      if (_this.props.onMouseEnter) {
        _this.props.onMouseEnter();
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onDocumentClick", function (evt) {
      var target = evt.target;
      var popper = _this.popperRef.current;
      var anchor = _this.anchorRef.current; // Ignore document click if it came from popover or anchor

      if (!popper || popper === target || popper.contains(target)) {
        return;
      }

      if (!anchor || anchor === target || anchor.contains(target)) {
        return;
      }

      if (_this.props.onClickOutside) {
        _this.props.onClickOutside(evt);
      }
    });

    return _this;
  }

  _createClass(Popover, [{
    key: "componentDidMount",
    value: function componentDidMount() {
      this.generatedId = getBuiId();
      this.setState({
        isMounted: true
      });
    }
  }, {
    key: "componentDidUpdate",
    value: function componentDidUpdate(prevProps, prevState) {
      this.init(prevProps, prevState);
    }
  }, {
    key: "init",
    value: function init(prevProps, prevState) {
      if (this.props.isOpen !== prevProps.isOpen || this.state.isMounted !== prevState.isMounted || this.state.isLayerMounted !== prevState.isLayerMounted) {
        // Transition from closed to open.
        if (this.props.isOpen && this.state.isLayerMounted) {
          // Clear any existing timers (like previous animateOutCompleteTimer)
          this.clearTimers();
          this.addDomEvents();
          return;
        } // Transition from open to closed.


        if (!this.props.isOpen && prevProps.isOpen) {
          this.removeDomEvents();
          this.animateOutTimer = setTimeout(this.animateOut, 20);
          return;
        }
      }
    }
  }, {
    key: "componentWillUnmount",
    value: function componentWillUnmount() {
      this.removeDomEvents();
      this.clearTimers();
    }
  }, {
    key: "getDefaultState",
    value: function getDefaultState(props) {
      return {
        isAnimating: false,
        arrowOffset: {
          left: 0,
          top: 0
        },
        popoverOffset: {
          left: 0,
          top: 0
        },
        placement: props.placement,
        isMounted: false,
        isLayerMounted: false
      };
    }
  }, {
    key: "clearTimers",
    value: function clearTimers() {
      [this.animateInTimer, this.animateOutTimer, this.animateOutCompleteTimer, this.onMouseEnterTimer, this.onMouseLeaveTimer].forEach(function (timerId) {
        if (timerId) {
          clearTimeout(timerId);
        }
      });
    }
  }, {
    key: "triggerOnMouseLeaveWithDelay",
    value: function triggerOnMouseLeaveWithDelay() {
      var onMouseLeaveDelay = this.props.onMouseLeaveDelay;

      if (onMouseLeaveDelay) {
        this.onMouseLeaveTimer = setTimeout(this.triggerOnMouseLeave, onMouseLeaveDelay);
        return;
      }

      this.triggerOnMouseLeave();
    }
  }, {
    key: "triggerOnMouseEnterWithDelay",
    value: function triggerOnMouseEnterWithDelay() {
      var onMouseEnterDelay = this.props.onMouseEnterDelay;

      if (onMouseEnterDelay) {
        this.onMouseEnterTimer = setTimeout(this.triggerOnMouseEnter, onMouseEnterDelay);
        return;
      }

      this.triggerOnMouseEnter();
    }
  }, {
    key: "addDomEvents",
    value: function addDomEvents() {
      if (typeof document !== 'undefined') {
        // using mousedown event so that callback runs before events on children inside of the popover
        document.addEventListener('mousedown', this.onDocumentClick);
        document.addEventListener('keyup', this.onKeyPress);
      }
    }
  }, {
    key: "removeDomEvents",
    value: function removeDomEvents() {
      if (typeof document !== 'undefined') {
        document.removeEventListener('mousedown', this.onDocumentClick);
        document.removeEventListener('keyup', this.onKeyPress);
      }
    }
  }, {
    key: "isClickTrigger",
    value: function isClickTrigger() {
      return this.props.triggerType === TRIGGER_TYPE.click;
    }
  }, {
    key: "isHoverTrigger",
    value: function isHoverTrigger() {
      return this.props.triggerType === TRIGGER_TYPE.hover;
    }
  }, {
    key: "isAccessibilityTypeMenu",
    value: function isAccessibilityTypeMenu() {
      return this.props.accessibilityType === ACCESSIBILITY_TYPE.menu;
    }
  }, {
    key: "isAccessibilityTypeTooltip",
    value: function isAccessibilityTypeTooltip() {
      return this.props.accessibilityType === ACCESSIBILITY_TYPE.tooltip;
    }
  }, {
    key: "getAnchorIdAttr",
    value: function getAnchorIdAttr() {
      var popoverId = this.getPopoverIdAttr();
      return popoverId ? "".concat(popoverId, "__anchor") : null;
    }
  }, {
    key: "getPopoverIdAttr",
    value: function getPopoverIdAttr() {
      return this.props.id || this.generatedId || null;
    }
  }, {
    key: "getAnchorProps",
    value: function getAnchorProps() {
      var isOpen = this.props.isOpen;
      var anchorProps = {
        key: 'popover-anchor',
        ref: this.anchorRef
      };
      var anchorId = this.getAnchorIdAttr();
      var popoverId = this.getPopoverIdAttr();

      if (this.isAccessibilityTypeMenu()) {
        anchorProps['aria-haspopup'] = 'true';
        anchorProps['aria-expanded'] = isOpen ? 'true' : 'false';
        var relationAttr = this.isClickTrigger() ? 'aria-controls' : 'aria-owns';
        anchorProps[relationAttr] = isOpen ? popoverId : null;
      } else if (this.isAccessibilityTypeTooltip()) {
        anchorProps.id = anchorId;
        anchorProps['aria-describedby'] = isOpen ? popoverId : null;
      }

      if (this.isHoverTrigger()) {
        anchorProps.onMouseEnter = this.onAnchorMouseEnter;
        anchorProps.onMouseLeave = this.onAnchorMouseLeave; // Make it focusable too

        anchorProps.onBlur = this.props.onBlur;
        anchorProps.onFocus = this.props.onFocus;
      } else {
        anchorProps.onClick = this.onAnchorClick;
      }

      return anchorProps;
    }
  }, {
    key: "getPopoverBodyProps",
    value: function getPopoverBodyProps() {
      var bodyProps = {};
      var popoverId = this.getPopoverIdAttr();

      if (this.isAccessibilityTypeMenu()) {
        bodyProps.id = popoverId;
      } else if (this.isAccessibilityTypeTooltip()) {
        bodyProps.id = popoverId;
        bodyProps.role = 'tooltip';
      }

      if (this.isHoverTrigger()) {
        bodyProps.onMouseEnter = this.onPopoverMouseEnter;
        bodyProps.onMouseLeave = this.onPopoverMouseLeave;
      }

      return bodyProps;
    }
  }, {
    key: "getSharedProps",
    value: function getSharedProps() {
      var _this$props = this.props,
          isOpen = _this$props.isOpen,
          showArrow = _this$props.showArrow;
      var _this$state = this.state,
          isAnimating = _this$state.isAnimating,
          arrowOffset = _this$state.arrowOffset,
          popoverOffset = _this$state.popoverOffset,
          placement = _this$state.placement;
      return {
        $showArrow: !!showArrow,
        $arrowOffset: arrowOffset,
        $popoverOffset: popoverOffset,
        $placement: placement,
        $isAnimating: isAnimating,
        $isOpen: isOpen
      };
    }
  }, {
    key: "getAnchorFromChildren",
    value: function getAnchorFromChildren() {
      var children = this.props.children;
      var childArray = React.Children.toArray(children);

      if (childArray.length !== 1) {
        // eslint-disable-next-line no-console
        console.error("[baseui] Exactly 1 child must be passed to Popover/Tooltip, found ".concat(childArray.length, " children"));
      }

      return childArray[0];
    }
  }, {
    key: "renderAnchor",
    value: function renderAnchor() {
      var anchor = this.getAnchorFromChildren();

      if (!anchor) {
        return null;
      }

      var isValidElement = React.isValidElement(anchor);
      var anchorProps = this.getAnchorProps();

      if (_typeof(anchor) === 'object' && isValidElement) {
        return React.cloneElement(anchor, anchorProps);
      }

      return React.createElement("span", anchorProps, anchor);
    }
  }, {
    key: "renderPopover",
    value: function renderPopover() {
      var _this$props2 = this.props,
          showArrow = _this$props2.showArrow,
          _this$props2$override = _this$props2.overrides,
          overrides = _this$props2$override === void 0 ? {} : _this$props2$override,
          content = _this$props2.content;
      var ArrowOverride = overrides.Arrow,
          BodyOverride = overrides.Body,
          InnerOverride = overrides.Inner;
      var Arrow = getOverride(ArrowOverride) || StyledArrow;
      var Body = getOverride(BodyOverride) || StyledBody;
      var Inner = getOverride(InnerOverride) || StyledInner;
      var sharedProps = this.getSharedProps();
      var bodyProps = this.getPopoverBodyProps();
      return React.createElement(Body, _extends({
        key: "popover-body",
        ref: this.popperRef,
        "data-baseweb": this.props['data-baseweb'] || 'popover'
      }, bodyProps, sharedProps, getOverrideProps(BodyOverride)), showArrow ? React.createElement(Arrow, _extends({
        key: "popover-arrow",
        ref: this.arrowRef
      }, sharedProps, getOverrideProps(ArrowOverride))) : null, React.createElement(Inner, _extends({
        key: "popover-inner"
      }, sharedProps, getOverrideProps(InnerOverride)), typeof content === 'function' ? content() : content));
    }
  }, {
    key: "render",
    value: function render() {
      var _this2 = this;

      var rendered = [this.renderAnchor()]; // Only render popover on the browser (portals aren't supported server-side)

      if (typeof document !== 'undefined') {
        if (this.state.isMounted && this.props.isOpen) {
          rendered.push(React.createElement(Layer, {
            key: 'new-layer',
            mountNode: this.props.mountNode,
            onMount: function onMount() {
              return _this2.setState({
                isLayerMounted: true
              });
            },
            onUnmount: function onUnmount() {
              return _this2.setState({
                isLayerMounted: false
              });
            }
          }, React.createElement(TetherBehavior, {
            anchorRef: this.anchorRef.current,
            arrowRef: this.arrowRef.current,
            popperRef: this.popperRef.current // Remove the `ignoreBoundary` prop in the next major version
            // and have it replaced with the TetherBehavior props overrides
            ,
            popperOptions: {
              modifiers: {
                preventOverflow: {
                  enabled: !this.props.ignoreBoundary
                }
              }
            },
            onPopperUpdate: this.onPopperUpdate,
            placement: this.state.placement
          }, this.renderPopover())));
        }
      }

      return rendered;
    }
  }]);

  return Popover;
}(React.Component);

_defineProperty(Popover, "defaultProps", defaultProps);

export default Popover;
/* eslint-enable react/no-find-dom-node */