UNPKG

react-conventions

Version:

An open source set of React components that implement Ambassador's Design and UX patterns.

162 lines (139 loc) 4.7 kB
import React from 'react' import classNames from 'classnames/bind' import style from './style.scss' import RenderToLayer from '../internal/RenderToLayer' /** * The Tooltip component. */ class Tooltip extends React.Component { constructor(props) { super(props) } state = { showing: false } static defaultProps = { tooltipPlacement: 'top' } static propTypes = { /** * The content to display inside the `Tooltip`. Could be number, string, element or an array containing these types. */ content: React.PropTypes.node, /** * Optional styles to add to the tooltip. */ optClass: React.PropTypes.string, /** * The placement of the tooltip. */ tooltipPlacement: React.PropTypes.oneOf(['left', 'right', 'top', 'bottom']), /** * Whether to insert the tooltip element after the trigger element or append it to the document body. */ appendToBody: React.PropTypes.bool, /** * Whether to show the tooltip element by default. */ show: React.PropTypes.bool, /** * Callback to call when mouseover is called */ mouseOverCallback: React.PropTypes.func, /** * Callback to call when mouseout is called */ mouseOutCallback: React.PropTypes.func } componentDidMount = () => { if (this.props.show) { setTimeout(() => { this.props.show ? this.showTooltip() : null }, 1000) } } componentWillReceiveProps = (nextProps) => { if(typeof nextProps.show !== 'undefined') { this.setState({ showing: nextProps.show }) } } showTooltip = () => { this.tooltipPlacement() this.setState({ showing: true }) } hideTooltip = () => { if (!this.props.show) { this.setState({ showing: false }) } } handleTooltipEnter = () => { if (this.props.mouseOverCallback) { this.props.mouseOverCallback() } } handleTooltipOut = () => { if (this.props.mouseOutCallback) { this.props.mouseOutCallback() } } tooltipPlacement = () => { var triggerRect = this._triggerElement.getBoundingClientRect() this._tooltipPlacement = {} this._tooltipPlacement.translate = triggerRect.width / 2 switch (this.props.tooltipPlacement) { case 'bottom': this._tooltipPlacement.left = triggerRect.left + (triggerRect.right - triggerRect.left) / 2 this._tooltipPlacement.top = triggerRect.bottom break case 'right': this._tooltipPlacement.left = triggerRect.right this._tooltipPlacement.top = triggerRect.top + (triggerRect.bottom - triggerRect.top) / 2 break case 'left': this._tooltipPlacement.left = triggerRect.left this._tooltipPlacement.top = triggerRect.top + (triggerRect.bottom - triggerRect.top) / 2 break default: this._tooltipPlacement.left = triggerRect.left + (triggerRect.right - triggerRect.left) / 2 this._tooltipPlacement.top = triggerRect.top } } getTranslate = () => { return this._tooltipPlacement.translate + 'px' } getStyles = () => { var style = {} if (this.state.showing && !this.props.show || this.state.showing && this.props.show && this.props.tooltipPlacement !== 'top') { style.top = this._tooltipPlacement.top + window.pageYOffset style.left = this._tooltipPlacement.left + window.pageXOffset style.opacity = 0.9 } else if (this.state.showing && this.props.show && this.props.tooltipPlacement === 'top') { style.top = 'inherit' style.left = 'inherit' style.opacity = 0.9 style.transform = `translateX(-50%) translateX(-${this.getTranslate()}) translateY(-100%) translateY(-6px)` } return style } renderTooltip = () => { const cx = classNames.bind(style) const tooltipShowingClass = this.state.showing ? style['tooltip-showing'] : '' const tooltipClass = cx(style['tooltip-component'], this.props.optClass, tooltipShowingClass, style[this.props.tooltipPlacement]) const styles = this.getStyles() return ( <span className={tooltipClass} style={styles} onMouseEnter={this.handleTooltipEnter} onMouseOut={this.handleTooltipOut}> {this.props.content} </span> ) } render = () => { const {content, optClass, tooltipPlacement, appendToBody, show, ...other} = this.props return ( <span onMouseOver={this.showTooltip} onMouseOut={this.hideTooltip} ref={(c) => this._triggerElement = c} {...other}> {this.props.children} {this.props.appendToBody ? <RenderToLayer render={this.renderTooltip} open={true} /> : this.renderTooltip()} </span> ) } } export default Tooltip