341 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var _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; };
 | 
						|
 | 
						|
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 | 
						|
 | 
						|
/* eslint-disable react/no-multi-comp, no-underscore-dangle */
 | 
						|
import React, { Children } from 'react';
 | 
						|
 | 
						|
import { findDOMNode } from 'react-dom';
 | 
						|
import EventListener from 'react-event-listener';
 | 
						|
import debounce from 'lodash/debounce';
 | 
						|
import warning from 'warning';
 | 
						|
import classNames from 'classnames';
 | 
						|
import { Manager, Target, Popper } from 'react-popper';
 | 
						|
import { capitalizeFirstLetter } from '../utils/helpers';
 | 
						|
import common from '../colors/common';
 | 
						|
import grey from '../colors/grey';
 | 
						|
import withStyles from '../styles/withStyles';
 | 
						|
 | 
						|
// Use a class component so we can get a reference.
 | 
						|
class TargetChildren extends React.Component {
 | 
						|
  render() {
 | 
						|
    return this.props.element;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export const styles = theme => ({
 | 
						|
  root: {
 | 
						|
    display: 'inline',
 | 
						|
    flexDirection: 'inherit' // Makes the wrapper more transparent.
 | 
						|
  },
 | 
						|
  popper: {
 | 
						|
    zIndex: theme.zIndex.tooltip
 | 
						|
  },
 | 
						|
  popperClose: {
 | 
						|
    pointerEvents: 'none'
 | 
						|
  },
 | 
						|
  tooltip: {
 | 
						|
    background: grey[700],
 | 
						|
    borderRadius: 2,
 | 
						|
    color: common.fullWhite,
 | 
						|
    fontFamily: theme.typography.fontFamily,
 | 
						|
    fontSize: theme.typography.pxToRem(14),
 | 
						|
    minHeight: theme.spacing.unit * 4,
 | 
						|
    lineHeight: '32px',
 | 
						|
    opacity: 0,
 | 
						|
    padding: `0 ${theme.spacing.unit}px`,
 | 
						|
    transform: 'scale(0)',
 | 
						|
    transition: theme.transitions.create(['opacity', 'transform'], {
 | 
						|
      duration: theme.transitions.duration.shortest
 | 
						|
    }),
 | 
						|
    [theme.breakpoints.up('sm')]: {
 | 
						|
      minHeight: 22,
 | 
						|
      lineHeight: '22px',
 | 
						|
      padding: `0 ${theme.spacing.unit}px`,
 | 
						|
      fontSize: theme.typography.pxToRem(10)
 | 
						|
    }
 | 
						|
  },
 | 
						|
  tooltipLeft: {
 | 
						|
    transformOrigin: 'right center',
 | 
						|
    margin: `0 ${theme.spacing.unit * 3}px`,
 | 
						|
    [theme.breakpoints.up('sm')]: {
 | 
						|
      margin: '0 14px'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  tooltipRight: {
 | 
						|
    transformOrigin: 'left center',
 | 
						|
    margin: `0 ${theme.spacing.unit * 3}px`,
 | 
						|
    [theme.breakpoints.up('sm')]: {
 | 
						|
      margin: '0 14px'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  tooltipTop: {
 | 
						|
    transformOrigin: 'center bottom',
 | 
						|
    margin: `${theme.spacing.unit * 3}px 0`,
 | 
						|
    [theme.breakpoints.up('sm')]: {
 | 
						|
      margin: '14px 0'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  tooltipBottom: {
 | 
						|
    transformOrigin: 'center top',
 | 
						|
    margin: `${theme.spacing.unit * 3}px 0`,
 | 
						|
    [theme.breakpoints.up('sm')]: {
 | 
						|
      margin: '14px 0'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  tooltipOpen: {
 | 
						|
    opacity: 0.9,
 | 
						|
    transform: 'scale(1)'
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
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 {
 | 
						|
  constructor(...args) {
 | 
						|
    var _temp;
 | 
						|
 | 
						|
    return _temp = super(...args), this.state = {}, this.enterTimer = null, this.leaveTimer = null, this.touchTimer = 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.handleRequestOpen = event => {
 | 
						|
      const { children } = this.props;
 | 
						|
      if (typeof children !== 'string') {
 | 
						|
        const childrenProps = Children.only(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.leaveTimer);
 | 
						|
      if (this.props.enterDelay > 0) {
 | 
						|
        this.leaveTimer = setTimeout(() => {
 | 
						|
          this.requestOpen(event);
 | 
						|
        }, this.props.enterDelay);
 | 
						|
      } else {
 | 
						|
        this.requestOpen(event);
 | 
						|
      }
 | 
						|
    }, this.requestOpen = event => {
 | 
						|
      if (!this.isControlled) {
 | 
						|
        this.setState({ open: true });
 | 
						|
      }
 | 
						|
 | 
						|
      if (this.props.onRequestOpen) {
 | 
						|
        this.props.onRequestOpen(event, true);
 | 
						|
      }
 | 
						|
    }, this.handleRequestClose = event => {
 | 
						|
      const { children } = this.props;
 | 
						|
      if (typeof children !== 'string') {
 | 
						|
        const childrenProps = Children.only(children).props;
 | 
						|
 | 
						|
        if (event.type === 'blur' && childrenProps.onBlur) {
 | 
						|
          childrenProps.onBlur(event);
 | 
						|
        }
 | 
						|
 | 
						|
        if (event.type === 'mouseleave' && childrenProps.onMouseLeave) {
 | 
						|
          childrenProps.onMouseLeave(event);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      clearTimeout(this.leaveTimer);
 | 
						|
      if (this.props.leaveDelay) {
 | 
						|
        this.leaveTimer = setTimeout(() => {
 | 
						|
          this.requestClose(event);
 | 
						|
        }, this.props.leaveDelay);
 | 
						|
      } else {
 | 
						|
        this.requestClose(event);
 | 
						|
      }
 | 
						|
    }, this.requestClose = event => {
 | 
						|
      this.ignoreNonTouchEvents = false;
 | 
						|
 | 
						|
      if (!this.isControlled) {
 | 
						|
        this.setState({ open: false });
 | 
						|
      }
 | 
						|
 | 
						|
      if (this.props.onRequestClose) {
 | 
						|
        this.props.onRequestClose(event, false);
 | 
						|
      }
 | 
						|
    }, this.handleTouchStart = event => {
 | 
						|
      this.ignoreNonTouchEvents = true;
 | 
						|
      const { children } = this.props;
 | 
						|
      if (typeof children !== 'string') {
 | 
						|
        const childrenProps = Children.only(children).props;
 | 
						|
 | 
						|
        if (childrenProps.onTouchStart) {
 | 
						|
          childrenProps.onTouchStart(event);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      clearTimeout(this.touchTimer);
 | 
						|
      event.persist();
 | 
						|
      this.touchTimer = setTimeout(() => {
 | 
						|
        this.handleRequestOpen(event);
 | 
						|
      }, 1e3);
 | 
						|
    }, this.handleTouchEnd = event => {
 | 
						|
      const { children } = this.props;
 | 
						|
      if (typeof children !== 'string') {
 | 
						|
        const childrenProps = Children.only(children).props;
 | 
						|
 | 
						|
        if (childrenProps.onTouchEnd) {
 | 
						|
          childrenProps.onTouchEnd(event);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      clearTimeout(this.touchTimer);
 | 
						|
      clearTimeout(this.leaveTimer);
 | 
						|
      event.persist();
 | 
						|
      this.leaveTimer = setTimeout(() => {
 | 
						|
        this.requestClose(event);
 | 
						|
      }, 1500 + this.props.leaveDelay);
 | 
						|
    }, _temp;
 | 
						|
  }
 | 
						|
 | 
						|
  componentWillMount() {
 | 
						|
    const { props } = this;
 | 
						|
 | 
						|
    this.isControlled = props.open !== undefined;
 | 
						|
 | 
						|
    if (!this.isControlled) {
 | 
						|
      // not controlled, use internal state
 | 
						|
      this.setState({
 | 
						|
        open: false
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  componentDidMount() {
 | 
						|
    warning(!this.children || !this.children.disabled ||
 | 
						|
    // $FlowFixMe
 | 
						|
    !this.children.tagName.toLowerCase() === 'button', ['Material-UI: you are providing a disabled button children to the Tooltip component.', 'A disabled element do not fire events.', 'But the Tooltip needs to listen to the children element events to display the title.', '', 'Place a `div` over top of the element.'].join('\n'));
 | 
						|
  }
 | 
						|
 | 
						|
  componentWillUnmount() {
 | 
						|
    clearTimeout(this.enterTimer);
 | 
						|
    clearTimeout(this.leaveTimer);
 | 
						|
    this.handleResize.cancel();
 | 
						|
  }
 | 
						|
 | 
						|
  render() {
 | 
						|
    const _props = this.props,
 | 
						|
          {
 | 
						|
      children: childrenProp,
 | 
						|
      classes,
 | 
						|
      className,
 | 
						|
      disableTriggerFocus,
 | 
						|
      disableTriggerHover,
 | 
						|
      disableTriggerTouch,
 | 
						|
      enterDelay,
 | 
						|
      id,
 | 
						|
      leaveDelay,
 | 
						|
      open: openProp,
 | 
						|
      onRequestClose,
 | 
						|
      onRequestOpen,
 | 
						|
      theme,
 | 
						|
      title,
 | 
						|
      placement: rawPlacement,
 | 
						|
      PopperProps: { PopperClassName } = {}
 | 
						|
    } = _props,
 | 
						|
          PopperOther = _objectWithoutProperties(_props.PopperProps, ['PopperClassName']),
 | 
						|
          other = _objectWithoutProperties(_props, ['children', 'classes', 'className', 'disableTriggerFocus', 'disableTriggerHover', 'disableTriggerTouch', 'enterDelay', 'id', 'leaveDelay', 'open', 'onRequestClose', 'onRequestOpen', 'theme', 'title', 'placement', 'PopperProps']);
 | 
						|
 | 
						|
    const placement = theme.direction === 'rtl' ? flipPlacement(rawPlacement) : rawPlacement;
 | 
						|
    const open = this.isControlled ? openProp : this.state.open;
 | 
						|
    const childrenProps = {};
 | 
						|
 | 
						|
    childrenProps['aria-describedby'] = id;
 | 
						|
 | 
						|
    if (!disableTriggerTouch) {
 | 
						|
      childrenProps.onTouchStart = this.handleTouchStart;
 | 
						|
      childrenProps.onTouchEnd = this.handleTouchEnd;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!disableTriggerHover) {
 | 
						|
      childrenProps.onMouseOver = this.handleRequestOpen;
 | 
						|
      childrenProps.onMouseLeave = this.handleRequestClose;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!disableTriggerFocus) {
 | 
						|
      childrenProps.onFocus = this.handleRequestOpen;
 | 
						|
      childrenProps.onBlur = this.handleRequestClose;
 | 
						|
    }
 | 
						|
 | 
						|
    if (typeof childrenProp !== 'string' && childrenProp.props) {
 | 
						|
      warning(!childrenProp.props.title, ['Material-UI: you have been providing a `title` property to the child of <Tooltip />.', `Remove this title property \`${childrenProp.props.title}\` or the Tooltip component.`].join('\n'));
 | 
						|
    }
 | 
						|
 | 
						|
    return React.createElement(
 | 
						|
      EventListener,
 | 
						|
      { target: 'window', onResize: this.handleResize },
 | 
						|
      React.createElement(
 | 
						|
        Manager,
 | 
						|
        _extends({ className: classNames(classes.root, className) }, other),
 | 
						|
        React.createElement(
 | 
						|
          Target,
 | 
						|
          null,
 | 
						|
          ({ targetProps }) => React.createElement(TargetChildren, {
 | 
						|
            element: typeof childrenProp !== 'string' ? React.cloneElement(childrenProp, childrenProps) : childrenProp,
 | 
						|
            ref: node => {
 | 
						|
              this.children = findDOMNode(node);
 | 
						|
              targetProps.ref(this.children);
 | 
						|
            }
 | 
						|
          })
 | 
						|
        ),
 | 
						|
        React.createElement(
 | 
						|
          Popper,
 | 
						|
          _extends({
 | 
						|
            placement: placement,
 | 
						|
            eventsEnabled: open,
 | 
						|
            className: classNames(classes.popper, { [classes.popperClose]: !open }, PopperClassName)
 | 
						|
          }, PopperOther, {
 | 
						|
            ref: node => {
 | 
						|
              this.popper = node;
 | 
						|
            }
 | 
						|
          }),
 | 
						|
          React.createElement(
 | 
						|
            'div',
 | 
						|
            {
 | 
						|
              id: id,
 | 
						|
              role: 'tooltip',
 | 
						|
              'aria-hidden': !open,
 | 
						|
              className: classNames(classes.tooltip, { [classes.tooltipOpen]: open }, classes[`tooltip${capitalizeFirstLetter(placement.split('-')[0])}`])
 | 
						|
            },
 | 
						|
            title
 | 
						|
          )
 | 
						|
        )
 | 
						|
      )
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Tooltip.defaultProps = {
 | 
						|
  disableTriggerFocus: false,
 | 
						|
  disableTriggerHover: false,
 | 
						|
  disableTriggerTouch: false,
 | 
						|
  enterDelay: 0,
 | 
						|
  leaveDelay: 0,
 | 
						|
  placement: 'bottom'
 | 
						|
};
 | 
						|
export default withStyles(styles, { name: 'MuiTooltip', withTheme: true })(Tooltip); |