forgeplus-react/node_modules/material-ui/es/Tooltip/Tooltip.js

563 lines
15 KiB
JavaScript

import _extends from "@babel/runtime/helpers/extends";
import _objectSpread from "@babel/runtime/helpers/objectSpread";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
/* eslint-disable react/no-multi-comp, no-underscore-dangle */
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import EventListener from 'react-event-listener';
import debounce from 'lodash/debounce';
import warning from 'warning';
import classNames from 'classnames';
import { Manager, Popper, Target } from 'react-popper';
import { capitalize } from '../utils/helpers';
import RootRef from '../internal/RootRef';
import Portal from '../Portal';
import common from '../colors/common';
import withStyles from '../styles/withStyles';
export const styles = theme => ({
// Will be gone once we drop React 15.x support.
root: {
display: 'inline-block',
flexDirection: 'inherit' // Makes the wrapper more transparent.
},
popper: {
zIndex: theme.zIndex.tooltip,
pointerEvents: 'none',
'&$open': {
pointerEvents: 'auto'
}
},
open: {},
tooltip: {
backgroundColor: theme.palette.grey[700],
borderRadius: 2,
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: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px`,
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: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`,
fontSize: theme.typography.pxToRem(14),
lineHeight: `${theme.typography.round(16 / 14)}em`
},
tooltipPlacementLeft: {
transformOrigin: 'right center',
margin: `0 ${theme.spacing.unit * 3}px`,
[theme.breakpoints.up('sm')]: {
margin: '0 14px'
}
},
tooltipPlacementRight: {
transformOrigin: 'left center',
margin: `0 ${theme.spacing.unit * 3}px`,
[theme.breakpoints.up('sm')]: {
margin: '0 14px'
}
},
tooltipPlacementTop: {
transformOrigin: 'center bottom',
margin: `${theme.spacing.unit * 3}px 0`,
[theme.breakpoints.up('sm')]: {
margin: '14px 0'
}
},
tooltipPlacementBottom: {
transformOrigin: 'center top',
margin: `${theme.spacing.unit * 3}px 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 {
constructor(props, context) {
super(props, context);
Object.defineProperty(this, "state", {
configurable: true,
enumerable: true,
writable: true,
value: {}
});
Object.defineProperty(this, "enterTimer", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "leaveTimer", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "touchTimer", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "closeTimer", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "isControlled", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "popper", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "children", {
configurable: true,
enumerable: true,
writable: true,
value: null
});
Object.defineProperty(this, "ignoreNonTouchEvents", {
configurable: true,
enumerable: true,
writable: true,
value: false
});
Object.defineProperty(this, "handleResize", {
configurable: true,
enumerable: true,
writable: true,
value: debounce(() => {
if (this.popper) {
this.popper._popper.scheduleUpdate();
}
}, 166)
});
Object.defineProperty(this, "handleEnter", {
configurable: true,
enumerable: true,
writable: true,
value: 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);
}
}
});
Object.defineProperty(this, "handleOpen", {
configurable: true,
enumerable: true,
writable: true,
value: event => {
if (!this.isControlled) {
this.setState({
open: true
});
}
if (this.props.onOpen) {
this.props.onOpen(event, true);
}
}
});
Object.defineProperty(this, "handleLeave", {
configurable: true,
enumerable: true,
writable: true,
value: 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);
}
}
});
Object.defineProperty(this, "handleClose", {
configurable: true,
enumerable: true,
writable: true,
value: 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);
}
});
Object.defineProperty(this, "handleTouchStart", {
configurable: true,
enumerable: true,
writable: true,
value: 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);
}
});
Object.defineProperty(this, "handleTouchEnd", {
configurable: true,
enumerable: true,
writable: true,
value: 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.cancel();
}
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: ReactDOM.createPortal ? false : 'div',
className: classNames(classes.root, className)
}, 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,
/**
* Useful to extend the style applied to components.
*/
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);