|
import React, {Component, PropTypes} from 'react'; |
|
import classNames from 'classnames'; |
|
import ReactDOM from 'react-dom'; |
|
/** |
|
* PopOverContent Component. |
|
*/ |
|
export default class Content extends Component { |
|
static propTypes = { |
|
show: PropTypes.bool, |
|
className: PropTypes.string, |
|
contentWidth: PropTypes.number, |
|
children: PropTypes.any, |
|
placement: PropTypes.string, |
|
hideOnBlur: PropTypes.bool |
|
}; |
|
|
|
constructor() { |
|
super(); |
|
this.state = { |
|
show: false |
|
}; |
|
} |
|
|
|
setPosition = (triggerNode) => { |
|
const popOverContentNode = this.getNodeObject(); |
|
const placement = this.getPlacement(popOverContentNode); |
|
|
|
const position = this.getPosition(placement, popOverContentNode, triggerNode); |
|
popOverContentNode.style.top = position.top + 'px'; |
|
popOverContentNode.style.left = position.left + 'px'; |
|
popOverContentNode.classList.add(placement); |
|
} |
|
|
|
getPosition = (placement, contentNode, triggerNode) => { |
|
if ( placement === 'top' ) { |
|
return {left: -1 * (contentNode.offsetWidth / 2 - triggerNode.offsetWidth / 2), top: -1 * (contentNode.offsetHeight) }; |
|
} else if (placement === 'bottom') { |
|
return {left: -1 * (contentNode.offsetWidth / 2 - triggerNode.offsetWidth / 2), top: (triggerNode.offsetHeight) }; |
|
} else if (placement === 'left') { |
|
return {left: contentNode.offsetWidth * -1, top: -1 * (contentNode.offsetHeight / 2 - triggerNode.offsetHeight / 2) }; |
|
} |
|
// If placement is right |
|
return {left: triggerNode.scrollWidth, top: -1 * (contentNode.offsetHeight / 2 - triggerNode.offsetHeight / 2) }; |
|
} |
|
|
|
getNodeObject() { |
|
return ReactDOM.findDOMNode(this); |
|
} |
|
|
|
static getViewPortPos() { |
|
const width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); |
|
const height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); |
|
return {width, height}; |
|
} |
|
|
|
/** |
|
* Dynamically set the placement if popover content gets hidden. |
|
* @param popOverContentNode |
|
* @returns {*|string} |
|
*/ |
|
getPlacement = (popOverContentNode) => { |
|
const viewPort = Content.getViewPortPos(); |
|
const nodeRect = popOverContentNode.getBoundingClientRect(); |
|
let placement = this.props.placement || 'top'; |
|
|
|
if (placement === 'top' && (nodeRect.top + nodeRect.height < 0)) { |
|
placement = 'bottom'; |
|
} else if (placement === 'bottom' && (nodeRect.top + nodeRect.height > viewPort.height)) { |
|
placement = 'top'; |
|
} else if (placement === 'right' && (nodeRect.left + nodeRect.width > viewPort.width)) { |
|
placement = 'left'; |
|
} else if (placement === 'left' && (nodeRect.left < 0)) { |
|
placement = 'right'; |
|
} |
|
return placement; |
|
} |
|
|
|
toggleDisplay = (triggerEvent, triggerNode) => { |
|
this.setState({show: !this.state.show}, () => { |
|
if (this.state.show) { |
|
this.setPosition(triggerNode); |
|
} else { |
|
this.resetPosition(); |
|
} |
|
const windowClickHandler = (evt) => { |
|
const target = evt.srcElement || evt.originalTarget; |
|
if (!this.findClosestParent(target, '.popOverTrigger')) { |
|
this.setState({show: false}); |
|
} |
|
document.body.removeEventListener('click', windowClickHandler, false); |
|
}; |
|
|
|
if (this.state.show && this.props.hideOnBlur) { |
|
document.body.addEventListener('click', windowClickHandler, false); |
|
} |
|
}); |
|
} |
|
findClosestParent = (el, sel) => { |
|
let ele = el; |
|
while ((ele) && !((ele.matches || ele.matchesSelector).call(ele, sel))) { |
|
ele = ele.parentElement; |
|
} |
|
return ele; |
|
} |
|
|
|
resetPosition() { |
|
const popOverContentNode = this.getNodeObject(); |
|
popOverContentNode.style.left = 0; |
|
popOverContentNode.style.top = 0; |
|
} |
|
|
|
render = () => { |
|
const {className} = this.props; |
|
const styles = require('./PopOver.scss'); |
|
const displayClass = this.state.show ? 'show' : 'hide'; |
|
const style = {width: this.props.contentWidth || 250}; |
|
return ( |
|
<div className={classNames(styles.popOverContent, className, displayClass, '_popOverContent')} ref="popOverContent" style={style}>{this.props.children}</div> |
|
); |
|
} |
|
} |
|
|