You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
6.0 KiB
231 lines
6.0 KiB
10 years ago
|
"use strict";
|
||
|
|
||
|
var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };
|
||
|
|
||
|
var _react = require("react");
|
||
|
|
||
|
var React = _interopRequire(_react);
|
||
|
|
||
|
var cloneElement = _react.cloneElement;
|
||
|
|
||
|
var OverlayMixin = _interopRequire(require("./OverlayMixin"));
|
||
|
|
||
|
var domUtils = _interopRequire(require("./utils/domUtils"));
|
||
|
|
||
|
var createChainedFunction = _interopRequire(require("./utils/createChainedFunction"));
|
||
|
|
||
|
var assign = _interopRequire(require("./utils/Object.assign"));
|
||
|
|
||
|
/**
|
||
|
* Check if value one is inside or equal to the of value
|
||
|
*
|
||
|
* @param {string} one
|
||
|
* @param {string|array} of
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
function isOneOf(one, of) {
|
||
|
if (Array.isArray(of)) {
|
||
|
return of.indexOf(one) >= 0;
|
||
|
}
|
||
|
return one === of;
|
||
|
}
|
||
|
|
||
|
var OverlayTrigger = React.createClass({
|
||
|
displayName: "OverlayTrigger",
|
||
|
|
||
|
mixins: [OverlayMixin],
|
||
|
|
||
|
propTypes: {
|
||
|
trigger: React.PropTypes.oneOfType([React.PropTypes.oneOf(["manual", "click", "hover", "focus"]), React.PropTypes.arrayOf(React.PropTypes.oneOf(["click", "hover", "focus"]))]),
|
||
|
placement: React.PropTypes.oneOf(["top", "right", "bottom", "left"]),
|
||
|
delay: React.PropTypes.number,
|
||
|
delayShow: React.PropTypes.number,
|
||
|
delayHide: React.PropTypes.number,
|
||
|
defaultOverlayShown: React.PropTypes.bool,
|
||
|
overlay: React.PropTypes.node.isRequired
|
||
|
},
|
||
|
|
||
|
getDefaultProps: function getDefaultProps() {
|
||
|
return {
|
||
|
placement: "right",
|
||
|
trigger: ["hover", "focus"]
|
||
|
};
|
||
|
},
|
||
|
|
||
|
getInitialState: function getInitialState() {
|
||
|
return {
|
||
|
isOverlayShown: this.props.defaultOverlayShown == null ? false : this.props.defaultOverlayShown,
|
||
|
overlayLeft: null,
|
||
|
overlayTop: null
|
||
|
};
|
||
|
},
|
||
|
|
||
|
show: function show() {
|
||
|
this.setState({
|
||
|
isOverlayShown: true
|
||
|
}, function () {
|
||
|
this.updateOverlayPosition();
|
||
|
});
|
||
|
},
|
||
|
|
||
|
hide: function hide() {
|
||
|
this.setState({
|
||
|
isOverlayShown: false
|
||
|
});
|
||
|
},
|
||
|
|
||
|
toggle: function toggle() {
|
||
|
if (this.state.isOverlayShown) {
|
||
|
this.hide();
|
||
|
} else {
|
||
|
this.show();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
renderOverlay: function renderOverlay() {
|
||
|
if (!this.state.isOverlayShown) {
|
||
|
return React.createElement("span", null);
|
||
|
}
|
||
|
|
||
|
return cloneElement(this.props.overlay, {
|
||
|
onRequestHide: this.hide,
|
||
|
placement: this.props.placement,
|
||
|
positionLeft: this.state.overlayLeft,
|
||
|
positionTop: this.state.overlayTop
|
||
|
});
|
||
|
},
|
||
|
|
||
|
render: function render() {
|
||
|
if (this.props.trigger === "manual") {
|
||
|
return React.Children.only(this.props.children);
|
||
|
}
|
||
|
|
||
|
var props = {};
|
||
|
|
||
|
if (isOneOf("click", this.props.trigger)) {
|
||
|
props.onClick = createChainedFunction(this.toggle, this.props.onClick);
|
||
|
}
|
||
|
|
||
|
if (isOneOf("hover", this.props.trigger)) {
|
||
|
props.onMouseOver = createChainedFunction(this.handleDelayedShow, this.props.onMouseOver);
|
||
|
props.onMouseOut = createChainedFunction(this.handleDelayedHide, this.props.onMouseOut);
|
||
|
}
|
||
|
|
||
|
if (isOneOf("focus", this.props.trigger)) {
|
||
|
props.onFocus = createChainedFunction(this.handleDelayedShow, this.props.onFocus);
|
||
|
props.onBlur = createChainedFunction(this.handleDelayedHide, this.props.onBlur);
|
||
|
}
|
||
|
|
||
|
return cloneElement(React.Children.only(this.props.children), props);
|
||
|
},
|
||
|
|
||
|
componentWillUnmount: function componentWillUnmount() {
|
||
|
clearTimeout(this._hoverDelay);
|
||
|
},
|
||
|
|
||
|
componentDidMount: function componentDidMount() {
|
||
|
if (this.props.defaultOverlayShown) {
|
||
|
this.updateOverlayPosition();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
handleDelayedShow: function handleDelayedShow() {
|
||
|
if (this._hoverDelay != null) {
|
||
|
clearTimeout(this._hoverDelay);
|
||
|
this._hoverDelay = null;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var delay = this.props.delayShow != null ? this.props.delayShow : this.props.delay;
|
||
|
|
||
|
if (!delay) {
|
||
|
this.show();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this._hoverDelay = setTimeout((function () {
|
||
|
this._hoverDelay = null;
|
||
|
this.show();
|
||
|
}).bind(this), delay);
|
||
|
},
|
||
|
|
||
|
handleDelayedHide: function handleDelayedHide() {
|
||
|
if (this._hoverDelay != null) {
|
||
|
clearTimeout(this._hoverDelay);
|
||
|
this._hoverDelay = null;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var delay = this.props.delayHide != null ? this.props.delayHide : this.props.delay;
|
||
|
|
||
|
if (!delay) {
|
||
|
this.hide();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this._hoverDelay = setTimeout((function () {
|
||
|
this._hoverDelay = null;
|
||
|
this.hide();
|
||
|
}).bind(this), delay);
|
||
|
},
|
||
|
|
||
|
updateOverlayPosition: function updateOverlayPosition() {
|
||
|
if (!this.isMounted()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var pos = this.calcOverlayPosition();
|
||
|
|
||
|
this.setState({
|
||
|
overlayLeft: pos.left,
|
||
|
overlayTop: pos.top
|
||
|
});
|
||
|
},
|
||
|
|
||
|
calcOverlayPosition: function calcOverlayPosition() {
|
||
|
var childOffset = this.getPosition();
|
||
|
|
||
|
var overlayNode = this.getOverlayDOMNode();
|
||
|
var overlayHeight = overlayNode.offsetHeight;
|
||
|
var overlayWidth = overlayNode.offsetWidth;
|
||
|
|
||
|
switch (this.props.placement) {
|
||
|
case "right":
|
||
|
return {
|
||
|
top: childOffset.top + childOffset.height / 2 - overlayHeight / 2,
|
||
|
left: childOffset.left + childOffset.width
|
||
|
};
|
||
|
case "left":
|
||
|
return {
|
||
|
top: childOffset.top + childOffset.height / 2 - overlayHeight / 2,
|
||
|
left: childOffset.left - overlayWidth
|
||
|
};
|
||
|
case "top":
|
||
|
return {
|
||
|
top: childOffset.top - overlayHeight,
|
||
|
left: childOffset.left + childOffset.width / 2 - overlayWidth / 2
|
||
|
};
|
||
|
case "bottom":
|
||
|
return {
|
||
|
top: childOffset.top + childOffset.height,
|
||
|
left: childOffset.left + childOffset.width / 2 - overlayWidth / 2
|
||
|
};
|
||
|
default:
|
||
|
throw new Error("calcOverlayPosition(): No such placement of \"" + this.props.placement + "\" found.");
|
||
|
}
|
||
|
},
|
||
|
|
||
|
getPosition: function getPosition() {
|
||
|
var node = React.findDOMNode(this);
|
||
|
var container = this.getContainerDOMNode();
|
||
|
|
||
|
var offset = container.tagName === "BODY" ? domUtils.getOffset(node) : domUtils.getPosition(node, container);
|
||
|
|
||
|
return assign({}, offset, {
|
||
|
height: node.offsetHeight,
|
||
|
width: node.offsetWidth
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
module.exports = OverlayTrigger;
|