src/Tooltip.js
/**
* @file melon/Tooltip
* @author leon([email protected])
*/
import React, {Component, PropTypes} from 'react';
import ReactDOM from 'react-dom';
import dom from './common/util/dom';
import {create} from 'melon-core/classname/cxBuilder';
const cx = create('Tooltip');
export default class Tooltip extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.onMouseEnter = this.onMouseEnter.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.state = {
isShown: false
};
}
componentDidMount() {
const popup = this.popup = Tooltip.createPopup();
this.renderPopup(popup, this.props.content);
}
componentWillUnmount() {
Tooltip.destroyPopup(this.popup);
this.popup = null;
}
componentDidUpdate() {
this.renderPopup(this.popup, this.props.content);
}
onClick(e) {
this.toggle();
}
onMouseEnter(e) {
this.show();
}
onMouseLeave(e) {
this.hide();
}
isShown() {
return this.state.isShown;
}
toggle() {
this.isShown() ? this.hide() : this.show();
}
show() {
this.setState({isShown: true});
}
hide() {
this.setState({isShown: false});
}
getPosition() {
const main = this.main;
if (!this.isShown() || !main) {
return {
left: -10000,
top: 0,
opacity: 0,
width: 'auto',
height: 'auto'
};
}
const props = this.props;
const {
direction,
offsetX,
offsetY
} = props;
const popup = this.popup.childNodes[0];
const position = dom.getPosition(main);
const {offsetWidth, offsetHeight} = popup;
const styles = {opacity: 1};
switch (direction) {
case 'top':
styles.left = position.left + (position.width - offsetWidth) / 2;
styles.top = position.top - offsetHeight - offsetY;
break;
case 'bottom':
styles.left = (position.width - offsetWidth) / 2 + position.left;
styles.top = position.top + position.height + offsetY;
break;
case 'left':
styles.top = (position.height - offsetHeight) / 2 + position.top;
styles.left = position.left - offsetWidth - offsetY;
break;
case 'right':
styles.top = (position.height - offsetHeight) / 2 + position.top;
styles.left = position.left + position.width + offsetX;
break;
}
return styles;
}
renderPopup(target, content) {
ReactDOM.render(
<div
className={cx().part('popup').build()}
style={this.getPosition()}>
{content}
</div>,
target
);
}
render() {
const props = this.props;
const {
mode,
direction,
children,
style
} = props;
const onClick = mode === 'click' ? this.onClick : null;
const onMouseEnter = mode === 'over' ? this.onMouseEnter : null;
const onMouseLeave = mode === 'over' ? this.onMouseLeave : null;
return (
<div
ref={main => {
if (main) {
this.main = main;
}
}}
style={style}
className={cx(props).addStates({direction}).build()}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}>
{children}
</div>
);
}
}
Tooltip.displayName = 'Tooltip';
Tooltip.propTypes = {
arrow: PropTypes.bool.isRequired,
direction: PropTypes.oneOf(['top', 'bottom', 'left', 'right']).isRequired,
mode: PropTypes.oneOf(['over', 'click']),
content: PropTypes.node.isRequired
};
Tooltip.defaultProps = {
arrow: true,
direction: 'bottom',
mode: 'over',
offsetX: 14,
offsetY: 14
};
(function () {
let container;
Tooltip.createPopup = function () {
if (!container) {
container = document.createElement('div');
container.className = cx().part('container').build();
document.body.appendChild(container);
}
const popup = document.createElement('div');
container.appendChild(popup);
return popup;
};
Tooltip.destroyPopup = function (popup) {
ReactDOM.unmountComponentAtNode(popup);
container.removeChild(popup);
};
})();