Completely updated React, fixed #11, (hopefully)

This commit is contained in:
2018-03-04 19:11:49 -05:00
parent 6e0afd6e2a
commit 34e5f5139a
13674 changed files with 333464 additions and 473223 deletions

View File

@@ -1,27 +1,19 @@
/* eslint-disable react/no-multi-comp, no-underscore-dangle */
// @flow
import React, { Children } from 'react';
import type { Element, Node } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
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 { capitalize } from '../utils/helpers';
import RefHolder from '../internal/RefHolder';
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<{ element: Element<any> }> {
render() {
return this.props.element;
}
}
export const styles = (theme: Object) => ({
export const styles = theme => ({
root: {
display: 'inline',
flexDirection: 'inherit', // Makes the wrapper more transparent.
@@ -33,24 +25,23 @@ export const styles = (theme: Object) => ({
pointerEvents: 'none',
},
tooltip: {
background: grey[700],
backgroundColor: theme.palette.grey[700],
borderRadius: 2,
color: common.fullWhite,
color: common.white,
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,
}),
minHeight: 0,
padding: theme.spacing.unit,
fontSize: theme.typography.pxToRem(14),
lineHeight: `${theme.typography.round(16 / 14)}em`,
[theme.breakpoints.up('sm')]: {
minHeight: 22,
lineHeight: '22px',
padding: `0 ${theme.spacing.unit}px`,
padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px`,
fontSize: theme.typography.pxToRem(10),
lineHeight: `${theme.typography.round(14 / 10)}em`,
},
},
tooltipLeft: {
@@ -87,21 +78,7 @@ export const styles = (theme: Object) => ({
},
});
export type Placement =
| 'bottom-end'
| 'bottom-start'
| 'bottom'
| 'left-end'
| 'left-start'
| 'left'
| 'right-end'
| 'right-start'
| 'right'
| 'top-end'
| 'top-start'
| 'top';
function flipPlacement(placement: Placement): Placement {
function flipPlacement(placement) {
switch (placement) {
case 'bottom-end':
return 'bottom-start';
@@ -116,109 +93,13 @@ function flipPlacement(placement: Placement): Placement {
}
}
type ProvidedProps = {
classes: Object,
disableTriggerFocus: boolean,
disableTriggerHover: boolean,
disableTriggerTouch: boolean,
enterDelay: number,
leaveDelay: number,
placement: Placement,
theme: Object,
};
export type Props = {
/**
* Tooltip reference component.
*/
children: Element<any>,
/**
* Useful to extend the style applied to components.
*/
classes?: Object,
/**
* @ignore
*/
className?: string,
/**
* Do not respond to focus events.
*/
disableTriggerFocus?: boolean,
/**
* Do not respond to hover events.
*/
disableTriggerHover?: boolean,
/**
* Do not respond to long press touch events.
*/
disableTriggerTouch?: boolean,
/**
* The relationship between the tooltip and the wrapper componnet is not clear from the DOM.
* By providind this property, we can use aria-describedby to solve the accessibility issue.
*/
id?: string,
/**
* Callback fired when the tooltip requests to be closed.
*
* @param {object} event The event source of the callback
*/
onRequestClose?: Function,
/**
* Callback fired when the tooltip requests to be open.
*
* @param {object} event The event source of the callback
*/
onRequestOpen?: Function,
/**
* If `true`, the tooltip is shown.
*/
open?: boolean,
/**
* Tooltip title.
*/
title: Node,
/**
* The number of milliseconds to wait before showing the tooltip.
*/
enterDelay?: number,
/**
* The number of milliseconds to wait before hidding the tooltip.
*/
leaveDelay?: number,
/**
* Tooltip placement
*/
placement?: Placement,
/**
* Properties applied to the `Popper` element.
*/
PopperProps?: Object,
/**
* @ignore
*/
theme?: Object,
};
type State = {
open?: boolean,
};
class Tooltip extends React.Component<ProvidedProps & Props, State> {
static defaultProps = {
disableTriggerFocus: false,
disableTriggerHover: false,
disableTriggerTouch: false,
enterDelay: 0,
leaveDelay: 0,
placement: 'bottom',
};
class Tooltip extends React.Component {
state = {};
componentWillMount() {
const { props } = this;
this.isControlled = props.open !== undefined;
this.isControlled = props.open != null;
if (!this.isControlled) {
// not controlled, use internal state
@@ -232,7 +113,6 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
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.',
@@ -266,16 +146,14 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
handleRequestOpen = event => {
const { children } = this.props;
if (typeof children !== 'string') {
const childrenProps = Children.only(children).props;
const childrenProps = children.props;
if (event.type === 'focus' && childrenProps.onFocus) {
childrenProps.onFocus(event);
}
if (event.type === 'focus' && childrenProps.onFocus) {
childrenProps.onFocus(event);
}
if (event.type === 'mouseover' && childrenProps.onMouseOver) {
childrenProps.onMouseOver(event);
}
if (event.type === 'mouseover' && childrenProps.onMouseOver) {
childrenProps.onMouseOver(event);
}
if (this.ignoreNonTouchEvents && event.type !== 'touchstart') {
@@ -297,23 +175,21 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
this.setState({ open: true });
}
if (this.props.onRequestOpen) {
this.props.onRequestOpen(event, true);
if (this.props.onOpen) {
this.props.onOpen(event, true);
}
};
handleRequestClose = event => {
handleClose = event => {
const { children } = this.props;
if (typeof children !== 'string') {
const childrenProps = Children.only(children).props;
const childrenProps = children.props;
if (event.type === 'blur' && childrenProps.onBlur) {
childrenProps.onBlur(event);
}
if (event.type === 'blur' && childrenProps.onBlur) {
childrenProps.onBlur(event);
}
if (event.type === 'mouseleave' && childrenProps.onMouseLeave) {
childrenProps.onMouseLeave(event);
}
if (event.type === 'mouseleave' && childrenProps.onMouseLeave) {
childrenProps.onMouseLeave(event);
}
clearTimeout(this.leaveTimer);
@@ -333,20 +209,18 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
this.setState({ open: false });
}
if (this.props.onRequestClose) {
this.props.onRequestClose(event, false);
if (this.props.onClose) {
this.props.onClose(event, false);
}
};
handleTouchStart = event => {
this.ignoreNonTouchEvents = true;
const { children } = this.props;
if (typeof children !== 'string') {
const childrenProps = Children.only(children).props;
const childrenProps = children.props;
if (childrenProps.onTouchStart) {
childrenProps.onTouchStart(event);
}
if (childrenProps.onTouchStart) {
childrenProps.onTouchStart(event);
}
clearTimeout(this.touchTimer);
@@ -358,12 +232,10 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
handleTouchEnd = event => {
const { children } = this.props;
if (typeof children !== 'string') {
const childrenProps = Children.only(children).props;
const childrenProps = children.props;
if (childrenProps.onTouchEnd) {
childrenProps.onTouchEnd(event);
}
if (childrenProps.onTouchEnd) {
childrenProps.onTouchEnd(event);
}
clearTimeout(this.touchTimer);
@@ -385,20 +257,24 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
enterDelay,
id,
leaveDelay,
onClose,
onOpen,
open: openProp,
onRequestClose,
onRequestOpen,
theme,
title,
placement: rawPlacement,
PopperProps: { PopperClassName, ...PopperOther } = {},
theme,
title,
...other
} = this.props;
const placement = theme.direction === 'rtl' ? flipPlacement(rawPlacement) : rawPlacement;
const open = this.isControlled ? openProp : this.state.open;
let open = this.isControlled ? openProp : this.state.open;
const childrenProps = {};
if (title === '') {
open = false;
}
childrenProps['aria-describedby'] = id;
if (!disableTriggerTouch) {
@@ -408,40 +284,35 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
if (!disableTriggerHover) {
childrenProps.onMouseOver = this.handleRequestOpen;
childrenProps.onMouseLeave = this.handleRequestClose;
childrenProps.onMouseLeave = this.handleClose;
}
if (!disableTriggerFocus) {
childrenProps.onFocus = this.handleRequestOpen;
childrenProps.onBlur = this.handleRequestClose;
childrenProps.onBlur = this.handleClose;
}
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'),
);
}
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 (
<EventListener target="window" onResize={this.handleResize}>
<Manager className={classNames(classes.root, className)} {...other}>
<Target>
{({ targetProps }) => (
<TargetChildren
element={
typeof childrenProp !== 'string'
? React.cloneElement(childrenProp, childrenProps)
: childrenProp
}
<RefHolder
ref={node => {
this.children = findDOMNode(node);
targetProps.ref(this.children);
}}
/>
>
{React.cloneElement(childrenProp, childrenProps)}
</RefHolder>
)}
</Target>
<Popper
@@ -457,18 +328,34 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
this.popper = node;
}}
>
<div
id={id}
role="tooltip"
aria-hidden={!open}
className={classNames(
classes.tooltip,
{ [classes.tooltipOpen]: open },
classes[`tooltip${capitalizeFirstLetter(placement.split('-')[0])}`],
)}
>
{title}
</div>
{({ popperProps, restProps }) => {
const actualPlacement = popperProps['data-placement'] || placement;
return (
<div
{...popperProps}
{...restProps}
style={{
...popperProps.style,
top: popperProps.style.top || 0,
left: popperProps.style.left || 0,
...restProps.style,
}}
>
<div
id={id}
role="tooltip"
aria-hidden={!open}
className={classNames(
classes.tooltip,
{ [classes.tooltipOpen]: open },
classes[`tooltip${capitalize(actualPlacement.split('-')[0])}`],
)}
>
{title}
</div>
</div>
);
}}
</Popper>
</Manager>
</EventListener>
@@ -476,4 +363,98 @@ class Tooltip extends React.Component<ProvidedProps & Props, State> {
}
}
Tooltip.propTypes = {
/**
* 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.
*/
disableTriggerFocus: PropTypes.bool,
/**
* Do not respond to hover events.
*/
disableTriggerHover: PropTypes.bool,
/**
* Do not respond to long press touch events.
*/
disableTriggerTouch: PropTypes.bool,
/**
* The number of milliseconds to wait before showing the tooltip.
*/
enterDelay: 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 hidding the tooltip.
*/
leaveDelay: 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 = {
disableTriggerFocus: false,
disableTriggerHover: false,
disableTriggerTouch: false,
enterDelay: 0,
leaveDelay: 0,
placement: 'bottom',
};
export default withStyles(styles, { name: 'MuiTooltip', withTheme: true })(Tooltip);