import _extends from "@babel/runtime/helpers/builtin/extends";
import _objectSpread from "@babel/runtime/helpers/builtin/objectSpread";
import _objectWithoutProperties from "@babel/runtime/helpers/builtin/objectWithoutProperties";

/* eslint-disable react/no-multi-comp, no-underscore-dangle */
import React from 'react';
import PropTypes from 'prop-types';
import EventListener from 'react-event-listener';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.

import warning from 'warning';
import classNames from 'classnames';
import { Manager, Popper, Target } from 'react-popper';
import { capitalize } from '../utils/helpers';
import RootRef from '../RootRef';
import Portal from '../Portal';
import common from '../colors/common';
import withStyles from '../styles/withStyles';
export const styles = theme => ({
  popper: {
    zIndex: theme.zIndex.tooltip,
    pointerEvents: 'none',
    '&$open': {
      pointerEvents: 'auto'
    }
  },
  open: {},
  tooltip: {
    backgroundColor: theme.palette.grey[700],
    borderRadius: theme.shape.borderRadius,
    color: common.white,
    fontFamily: theme.typography.fontFamily,
    opacity: 0,
    transform: 'scale(0)',
    transition: theme.transitions.create(['opacity', 'transform'], {
      duration: theme.transitions.duration.shortest,
      easing: theme.transitions.easing.easeIn
    }),
    minHeight: 0,
    padding: '4px 8px',
    fontSize: theme.typography.pxToRem(10),
    lineHeight: `${theme.typography.round(14 / 10)}em`,
    '&$open': {
      opacity: 0.9,
      transform: 'scale(1)',
      transition: theme.transitions.create(['opacity', 'transform'], {
        duration: theme.transitions.duration.shortest,
        easing: theme.transitions.easing.easeOut
      })
    }
  },
  touch: {
    padding: '8px 16px',
    fontSize: theme.typography.pxToRem(14),
    lineHeight: `${theme.typography.round(16 / 14)}em`
  },
  tooltipPlacementLeft: {
    transformOrigin: 'right center',
    margin: '0 24px',
    [theme.breakpoints.up('sm')]: {
      margin: '0 14px'
    }
  },
  tooltipPlacementRight: {
    transformOrigin: 'left center',
    margin: '0 24px',
    [theme.breakpoints.up('sm')]: {
      margin: '0 14px'
    }
  },
  tooltipPlacementTop: {
    transformOrigin: 'center bottom',
    margin: '24px 0',
    [theme.breakpoints.up('sm')]: {
      margin: '14px 0'
    }
  },
  tooltipPlacementBottom: {
    transformOrigin: 'center top',
    margin: '24px 0',
    [theme.breakpoints.up('sm')]: {
      margin: '14px 0'
    }
  }
});

function flipPlacement(placement) {
  switch (placement) {
    case 'bottom-end':
      return 'bottom-start';

    case 'bottom-start':
      return 'bottom-end';

    case 'top-end':
      return 'top-start';

    case 'top-start':
      return 'top-end';

    default:
      return placement;
  }
}

class Tooltip extends React.Component {
  // Corresponds to 10 frames at 60 Hz.
  constructor(props) {
    super(props);
    this.enterTimer = null;
    this.leaveTimer = null;
    this.touchTimer = null;
    this.closeTimer = null;
    this.isControlled = null;
    this.popper = null;
    this.children = null;
    this.ignoreNonTouchEvents = false;
    this.handleResize = debounce(() => {
      if (this.popper) {
        this.popper._popper.scheduleUpdate();
      }
    }, 166);
    this.state = {};

    this.handleEnter = event => {
      const {
        children,
        enterDelay
      } = this.props;
      const childrenProps = children.props;

      if (event.type === 'focus' && childrenProps.onFocus) {
        childrenProps.onFocus(event);
      }

      if (event.type === 'mouseover' && childrenProps.onMouseOver) {
        childrenProps.onMouseOver(event);
      }

      if (this.ignoreNonTouchEvents && event.type !== 'touchstart') {
        return;
      }

      clearTimeout(this.enterTimer);
      clearTimeout(this.leaveTimer);

      if (enterDelay) {
        event.persist();
        this.enterTimer = setTimeout(() => {
          this.handleOpen(event);
        }, enterDelay);
      } else {
        this.handleOpen(event);
      }
    };

    this.handleOpen = event => {
      if (!this.isControlled) {
        this.setState({
          open: true
        });
      }

      if (this.props.onOpen) {
        this.props.onOpen(event, true);
      }
    };

    this.handleLeave = event => {
      const {
        children,
        leaveDelay
      } = this.props;
      const childrenProps = children.props;

      if (event.type === 'blur' && childrenProps.onBlur) {
        childrenProps.onBlur(event);
      }

      if (event.type === 'mouseleave' && childrenProps.onMouseLeave) {
        childrenProps.onMouseLeave(event);
      }

      clearTimeout(this.enterTimer);
      clearTimeout(this.leaveTimer);

      if (leaveDelay) {
        event.persist();
        this.leaveTimer = setTimeout(() => {
          this.handleClose(event);
        }, leaveDelay);
      } else {
        this.handleClose(event);
      }
    };

    this.handleClose = event => {
      if (!this.isControlled) {
        this.setState({
          open: false
        });
      }

      if (this.props.onClose) {
        this.props.onClose(event, false);
      }

      clearTimeout(this.closeTimer);
      this.closeTimer = setTimeout(() => {
        this.ignoreNonTouchEvents = false;
      }, this.props.theme.transitions.duration.shortest);
    };

    this.handleTouchStart = event => {
      this.ignoreNonTouchEvents = true;
      const {
        children,
        enterTouchDelay
      } = this.props;
      const childrenProps = children.props;

      if (childrenProps.onTouchStart) {
        childrenProps.onTouchStart(event);
      }

      clearTimeout(this.leaveTimer);
      clearTimeout(this.closeTimer);
      clearTimeout(this.touchTimer);
      event.persist();
      this.touchTimer = setTimeout(() => {
        this.handleEnter(event);
      }, enterTouchDelay);
    };

    this.handleTouchEnd = event => {
      const {
        children,
        leaveTouchDelay
      } = this.props;
      const childrenProps = children.props;

      if (childrenProps.onTouchEnd) {
        childrenProps.onTouchEnd(event);
      }

      clearTimeout(this.touchTimer);
      clearTimeout(this.leaveTimer);
      event.persist();
      this.leaveTimer = setTimeout(() => {
        this.handleClose(event);
      }, leaveTouchDelay);
    };

    this.isControlled = props.open != null;

    if (!this.isControlled) {
      // not controlled, use internal state
      this.state.open = false;
    }
  }

  componentDidMount() {
    process.env.NODE_ENV !== "production" ? warning(!this.children || !this.children.disabled || !this.children.tagName.toLowerCase() === 'button', ['Material-UI: you are providing a disabled `button` child to the Tooltip component.', 'A disabled element does not fire events.', "Tooltip needs to listen to the child element's events to display the title.", '', 'Place a `div` container on top of the element.'].join('\n')) : void 0;
  }

  componentWillUnmount() {
    clearTimeout(this.enterTimer);
    clearTimeout(this.leaveTimer);
    clearTimeout(this.touchTimer);
    clearTimeout(this.closeTimer);
    this.handleResize.clear();
  }

  render() {
    const _props = this.props,
          {
      children,
      classes,
      className,
      disableFocusListener,
      disableHoverListener,
      disableTouchListener,
      enterDelay,
      enterTouchDelay,
      id,
      leaveDelay,
      leaveTouchDelay,
      onClose,
      onOpen,
      open: openProp,
      placement: placementProp,
      PopperProps: {
        className: PopperClassName
      } = {},
      theme,
      title
    } = _props,
          PopperProps = _objectWithoutProperties(_props.PopperProps, ["className"]),
          other = _objectWithoutProperties(_props, ["children", "classes", "className", "disableFocusListener", "disableHoverListener", "disableTouchListener", "enterDelay", "enterTouchDelay", "id", "leaveDelay", "leaveTouchDelay", "onClose", "onOpen", "open", "placement", "PopperProps", "theme", "title"]);

    const placement = theme.direction === 'rtl' ? flipPlacement(placementProp) : placementProp;
    let open = this.isControlled ? openProp : this.state.open;
    const childrenProps = {
      'aria-describedby': id
    }; // There is no point at displaying an empty tooltip.

    if (title === '') {
      open = false;
    }

    if (!disableTouchListener) {
      childrenProps.onTouchStart = this.handleTouchStart;
      childrenProps.onTouchEnd = this.handleTouchEnd;
    }

    if (!disableHoverListener) {
      childrenProps.onMouseOver = this.handleEnter;
      childrenProps.onMouseLeave = this.handleLeave;
    }

    if (!disableFocusListener) {
      childrenProps.onFocus = this.handleEnter;
      childrenProps.onBlur = this.handleLeave;
    }

    process.env.NODE_ENV !== "production" ? warning(!children.props.title, ['Material-UI: you have been providing a `title` property to the child of <Tooltip />.', `Remove this title property \`${children.props.title}\` or the Tooltip component.`].join('\n')) : void 0;
    return React.createElement(Manager, _extends({
      tag: false
    }, other), React.createElement(EventListener, {
      target: "window",
      onResize: this.handleResize
    }), React.createElement(Target, null, ({
      targetProps
    }) => React.createElement(RootRef, {
      rootRef: node => {
        this.children = node;
        targetProps.ref(this.children);
      }
    }, React.cloneElement(children, childrenProps))), React.createElement(Portal, null, React.createElement(Popper, _extends({
      placement: placement,
      eventsEnabled: open,
      className: classNames(classes.popper, {
        [classes.open]: open
      }, PopperClassName),
      ref: node => {
        this.popper = node;
      }
    }, PopperProps), ({
      popperProps,
      restProps
    }) => {
      const actualPlacement = (popperProps['data-placement'] || placement).split('-')[0];
      return React.createElement("div", _extends({}, popperProps, restProps, {
        style: _objectSpread({}, popperProps.style, {
          top: popperProps.style.top || 0,
          left: popperProps.style.left || 0
        }, restProps.style)
      }), React.createElement("div", {
        id: id,
        role: "tooltip",
        "aria-hidden": !open,
        className: classNames(classes.tooltip, {
          [classes.open]: open
        }, {
          [classes.touch]: this.ignoreNonTouchEvents
        }, classes[`tooltipPlacement${capitalize(actualPlacement)}`])
      }, title));
    })));
  }

}

Tooltip.propTypes = process.env.NODE_ENV !== "production" ? {
  /**
   * Tooltip reference element.
   */
  children: PropTypes.element.isRequired,

  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css-api) below for more details.
   */
  classes: PropTypes.object.isRequired,

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

  /**
   * Do not respond to focus events.
   */
  disableFocusListener: PropTypes.bool,

  /**
   * Do not respond to hover events.
   */
  disableHoverListener: PropTypes.bool,

  /**
   * Do not respond to long press touch events.
   */
  disableTouchListener: PropTypes.bool,

  /**
   * The number of milliseconds to wait before showing the tooltip.
   * This property won't impact the enter touch delay (`enterTouchDelay`).
   */
  enterDelay: PropTypes.number,

  /**
   * The number of milliseconds a user must touch the element before showing the tooltip.
   */
  enterTouchDelay: PropTypes.number,

  /**
   * The relationship between the tooltip and the wrapper component is not clear from the DOM.
   * By providing this property, we can use aria-describedby to solve the accessibility issue.
   */
  id: PropTypes.string,

  /**
   * The number of milliseconds to wait before hiding the tooltip.
   * This property won't impact the leave touch delay (`leaveTouchDelay`).
   */
  leaveDelay: PropTypes.number,

  /**
   * The number of milliseconds after the user stops touching an element before hiding the tooltip.
   */
  leaveTouchDelay: PropTypes.number,

  /**
   * Callback fired when the tooltip requests to be closed.
   *
   * @param {object} event The event source of the callback
   */
  onClose: PropTypes.func,

  /**
   * Callback fired when the tooltip requests to be open.
   *
   * @param {object} event The event source of the callback
   */
  onOpen: PropTypes.func,

  /**
   * If `true`, the tooltip is shown.
   */
  open: PropTypes.bool,

  /**
   * Tooltip placement
   */
  placement: PropTypes.oneOf(['bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),

  /**
   * Properties applied to the `Popper` element.
   */
  PopperProps: PropTypes.object,

  /**
   * @ignore
   */
  theme: PropTypes.object.isRequired,

  /**
   * Tooltip title. Zero-length titles string are never displayed.
   */
  title: PropTypes.node.isRequired
} : {};
Tooltip.defaultProps = {
  disableFocusListener: false,
  disableHoverListener: false,
  disableTouchListener: false,
  enterDelay: 0,
  enterTouchDelay: 1000,
  leaveDelay: 0,
  leaveTouchDelay: 1500,
  placement: 'bottom'
};
export default withStyles(styles, {
  name: 'MuiTooltip',
  withTheme: true
})(Tooltip);