import React,{PropTypes, cloneElement} from 'react';
import ReactDOM from 'react/lib/ReactDOM';
import Component from './utils/Component';
import classnames from 'classnames';
/**
* <h5>提示模块,主要包括一下两个组件:</h5>
* <strong><a href='../classes/Whisper.html'>Whisper</a></strong><br/>
* <strong><a href='../classes/Popover.html'>Popover</a></strong><br>
* <h6>点击以上链接或者左侧导航栏的组件名称链接进行查看</h6>
* @module 提示组件
* @main 提示组件
* @static
*
*/
/**
* 倾听者组件<br/>
* - 配合提示组件使用, 通过target设置显示的目标。
* - 可通过placement设置目标物的显示位置, 可选top、bottom、left、right、top left、top right、bottom left、bottom right。
* - 可通过distance设置目标物到点击对象(倾听者)的位置。
* - 可通过onTargetChange定义目标物显隐时额外的回调函数。
*
* 主要属性和接口:
* - target:目标物。
* - placement:目标物的显示位置, 默认bottom。
* - distance:目标物到点击对象(倾听者)的位置, 默认15。
* - onTargetChange:目标物显隐时额外的回调函数。
*
* 示例:
* ```code
* const popover = ( // 可以通过style自定义位置
* <Popover>
* <ul className="ph-popover-list">
* <li className="ph-popover-item">未上线单店</li>
* <li className="ph-popover-item">未上线连锁店</li>
* </ul>
* </Popover>
* );
* ```
* ```code
* <Whisper placement="top" onTargetChange={()=>{console.log('气泡出现消失时额外的执行函数');}} target={popover} distance={10} >Top</Whisper>
* ```
*
* @class Whisper
* @module 提示组件
* @extends Component
* @constructor
* @since 1.0.0
* @demo popover|popover.js {展示}
* @show true
* */
export default class Whisper extends Component{
static propTypes = {
/**
* 样式前缀
* @property classPrefix
* @type String
* @default 'whisper'
* */
classPrefix: PropTypes.string,
/**
* 标签tagName
* @property componentTag
* @type String
* */
componentTag: PropTypes.string,
/**
* 显示的目标气泡
* @property target
* @type Object
* */
target: PropTypes.object,
/**
* 气泡的位置,默认bottom
* @property placement
* @type String
* */
placement: PropTypes.string,
/**
* 气泡距离点击物的位置,默认15
* @property distance
* @type Number
* */
distance: PropTypes.number,
/**
* 气泡显隐时可执行的额外函数,自定义
* @method onTargetChange
* @type Function
* */
onTargetChange: PropTypes.func
};
static defaultProps = {
distance: 15,
placement: 'bottom',
classPrefix:'whisper',
componentTag: 'div',
classMapping : {}
};
constructor(props, context) {
super(props, context);
this.visible = false;
this._layer = document.createElement('div');
}
componentDidMount(){
setTimeout(()=>{
this.getWhisperPosition();
},0);
}
getWhisperPosition(){
this.position = {};
this.size = {};
this.position.x = parseInt(this.whisper.offsetLeft);
this.position.y = parseInt(this.whisper.offsetTop);
this.size.width = parseInt(this.whisper.offsetWidth);
this.size.height = parseInt(this.whisper.offsetHeight);
this.calcTooltipPosition();
}
calcTooltipPosition(){
const ARROW_SIZE = this.props.distance;
let winWidth = parseInt(document.body.clientWidth),
winHeight = parseInt(document.body.clientHeight);
document.body.style.position = 'relative';
this.style = {};
switch(this.props.placement){
case 'top':
this.style.bottom = winHeight - this.position.y + ARROW_SIZE;
this.style.left = this.position.x + this.size.width/2;
break;
case 'bottom':
this.style.top = this.position.y + this.size.height + ARROW_SIZE;
this.style.left = this.position.x + this.size.width/2;
break;
case 'left':
this.style.right = winWidth - this.position.x + ARROW_SIZE;
this.style.top = this.position.y + this.size.height/2;
break;
case 'right':
this.style.left = this.position.x + this.size.width + ARROW_SIZE;
this.style.top = this.position.y + this.size.height/2;
break;
case 'top left':
this.style.bottom = winHeight - this.position.y + ARROW_SIZE;
this.style.left = this.position.x;
break;
case 'top right':
this.style.bottom = winHeight - this.position.y + ARROW_SIZE;
this.style.right = winWidth - this.position.x - this.size.width;
break;
case 'bottom left':
this.style.top = this.position.y + this.size.height + ARROW_SIZE;
this.style.left = this.position.x;
break;
case 'bottom right':
this.style.top = this.position.y + this.size.height + ARROW_SIZE;
this.style.right = winWidth - this.position.x - this.size.width;
break;
default:
this.style.top = 0;
this.style.left = 0;
}
}
onToggle(){
this.visible = !this.visible;
if(this.visible){
this.renderTarget();
}else{
this.removeTarget();
}
if(this.props.onTargetChange) this.props.onTargetChange();
}
onClose(){
this.visible = false;
this.removeTarget();
}
getTarget(){
return cloneElement(this.props.target, {
styles: this.style,
placement: this.props.placement,
onClose: this.onClose.bind(this),
whisper: this.whisper,
setVisible: this.setVisible
});
}
renderTarget(){
let newTarget = this.getTarget();
ReactDOM.unstable_renderSubtreeIntoContainer(this, newTarget, this._layer);
document.body.appendChild(this._layer);
}
removeTarget(){
ReactDOM.unmountComponentAtNode(this._layer);
document.body.removeChild(this._layer);
}
componentWillUnmount(){
if(this.visible) this.onClose();
}
render(){
let {className, children} = this.props;
return (
<div {...this.props}
className={classnames(this.getProperty(true), className)}
onClick={::this.onToggle}
ref={(whisper)=>{this.whisper = whisper}}
>
{children}
</div>
);
}
}