UNPKG

react-bootstrap

Version:

Bootstrap 3 components build with React

347 lines (278 loc) 10.6 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _OverlayMixin = require('./OverlayMixin'); var _OverlayMixin2 = _interopRequireDefault(_OverlayMixin); var _RootCloseWrapper = require('./RootCloseWrapper'); var _RootCloseWrapper2 = _interopRequireDefault(_RootCloseWrapper); var _utilsCreateChainedFunction = require('./utils/createChainedFunction'); var _utilsCreateChainedFunction2 = _interopRequireDefault(_utilsCreateChainedFunction); var _utilsCreateContextWrapper = require('./utils/createContextWrapper'); var _utilsCreateContextWrapper2 = _interopRequireDefault(_utilsCreateContextWrapper); var _utilsDomUtils = require('./utils/domUtils'); var _utilsDomUtils2 = _interopRequireDefault(_utilsDomUtils); var _utilsObjectAssign = require('./utils/Object.assign'); var _utilsObjectAssign2 = _interopRequireDefault(_utilsObjectAssign); /** * 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 = _react2['default'].createClass({ displayName: 'OverlayTrigger', mixins: [_OverlayMixin2['default']], propTypes: { trigger: _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.oneOf(['manual', 'click', 'hover', 'focus']), _react2['default'].PropTypes.arrayOf(_react2['default'].PropTypes.oneOf(['click', 'hover', 'focus']))]), placement: _react2['default'].PropTypes.oneOf(['top', 'right', 'bottom', 'left']), delay: _react2['default'].PropTypes.number, delayShow: _react2['default'].PropTypes.number, delayHide: _react2['default'].PropTypes.number, defaultOverlayShown: _react2['default'].PropTypes.bool, overlay: _react2['default'].PropTypes.node.isRequired, containerPadding: _react2['default'].PropTypes.number, rootClose: _react2['default'].PropTypes.bool }, getDefaultProps: function getDefaultProps() { return { placement: 'right', trigger: ['hover', 'focus'], containerPadding: 0 }; }, getInitialState: function getInitialState() { return { isOverlayShown: this.props.defaultOverlayShown == null ? false : this.props.defaultOverlayShown, overlayLeft: null, overlayTop: null, arrowOffsetLeft: null, arrowOffsetTop: 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 _react2['default'].createElement('span', null); } var overlay = (0, _react.cloneElement)(this.props.overlay, { onRequestHide: this.hide, placement: this.props.placement, positionLeft: this.state.overlayLeft, positionTop: this.state.overlayTop, arrowOffsetLeft: this.state.arrowOffsetLeft, arrowOffsetTop: this.state.arrowOffsetTop }); if (this.props.rootClose) { return _react2['default'].createElement( _RootCloseWrapper2['default'], { onRootClose: this.hide }, overlay ); } else { return overlay; } }, render: function render() { var child = _react2['default'].Children.only(this.props.children); if (this.props.trigger === 'manual') { return child; } var props = {}; props.onClick = (0, _utilsCreateChainedFunction2['default'])(child.props.onClick, this.props.onClick); if (isOneOf('click', this.props.trigger)) { props.onClick = (0, _utilsCreateChainedFunction2['default'])(this.toggle, props.onClick); } if (isOneOf('hover', this.props.trigger)) { props.onMouseOver = (0, _utilsCreateChainedFunction2['default'])(this.handleDelayedShow, this.props.onMouseOver); props.onMouseOut = (0, _utilsCreateChainedFunction2['default'])(this.handleDelayedHide, this.props.onMouseOut); } if (isOneOf('focus', this.props.trigger)) { props.onFocus = (0, _utilsCreateChainedFunction2['default'])(this.handleDelayedShow, this.props.onFocus); props.onBlur = (0, _utilsCreateChainedFunction2['default'])(this.handleDelayedHide, this.props.onBlur); } return (0, _react.cloneElement)(child, 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; } this.setState(this.calcOverlayPosition()); }, calcOverlayPosition: function calcOverlayPosition() { var childOffset = this.getPosition(); var overlayNode = this.getOverlayDOMNode(); var overlayHeight = overlayNode.offsetHeight; var overlayWidth = overlayNode.offsetWidth; var placement = this.props.placement; var overlayLeft = undefined, overlayTop = undefined, arrowOffsetLeft = undefined, arrowOffsetTop = undefined; if (placement === 'left' || placement === 'right') { overlayTop = childOffset.top + (childOffset.height - overlayHeight) / 2; if (placement === 'left') { overlayLeft = childOffset.left - overlayWidth; } else { overlayLeft = childOffset.left + childOffset.width; } var topDelta = this._getTopDelta(overlayTop, overlayHeight); overlayTop += topDelta; arrowOffsetTop = 50 * (1 - 2 * topDelta / overlayHeight) + '%'; arrowOffsetLeft = null; } else if (placement === 'top' || placement === 'bottom') { overlayLeft = childOffset.left + (childOffset.width - overlayWidth) / 2; if (placement === 'top') { overlayTop = childOffset.top - overlayHeight; } else { overlayTop = childOffset.top + childOffset.height; } var leftDelta = this._getLeftDelta(overlayLeft, overlayWidth); overlayLeft += leftDelta; arrowOffsetLeft = 50 * (1 - 2 * leftDelta / overlayWidth) + '%'; arrowOffsetTop = null; } else { throw new Error('calcOverlayPosition(): No such placement of "' + this.props.placement + '" found.'); } return { overlayLeft: overlayLeft, overlayTop: overlayTop, arrowOffsetLeft: arrowOffsetLeft, arrowOffsetTop: arrowOffsetTop }; }, _getTopDelta: function _getTopDelta(top, overlayHeight) { var containerDimensions = this._getContainerDimensions(); var containerScroll = containerDimensions.scroll; var containerHeight = containerDimensions.height; var padding = this.props.containerPadding; var topEdgeOffset = top - padding - containerScroll; var bottomEdgeOffset = top + padding - containerScroll + overlayHeight; if (topEdgeOffset < 0) { return -topEdgeOffset; } else if (bottomEdgeOffset > containerHeight) { return containerHeight - bottomEdgeOffset; } else { return 0; } }, _getLeftDelta: function _getLeftDelta(left, overlayWidth) { var containerDimensions = this._getContainerDimensions(); var containerWidth = containerDimensions.width; var padding = this.props.containerPadding; var leftEdgeOffset = left - padding; var rightEdgeOffset = left + padding + overlayWidth; if (leftEdgeOffset < 0) { return -leftEdgeOffset; } else if (rightEdgeOffset > containerWidth) { return containerWidth - rightEdgeOffset; } else { return 0; } }, _getContainerDimensions: function _getContainerDimensions() { var containerNode = this.getContainerDOMNode(); var width = undefined, height = undefined, scroll = undefined; if (containerNode.tagName === 'BODY') { width = window.innerWidth; height = window.innerHeight; scroll = _utilsDomUtils2['default'].ownerDocument(containerNode).documentElement.scrollTop || containerNode.scrollTop; } else { width = containerNode.offsetWidth; height = containerNode.offsetHeight; scroll = containerNode.scrollTop; } return { width: width, height: height, scroll: scroll }; }, getPosition: function getPosition() { var node = _react2['default'].findDOMNode(this); var container = this.getContainerDOMNode(); var offset = container.tagName === 'BODY' ? _utilsDomUtils2['default'].getOffset(node) : _utilsDomUtils2['default'].getPosition(node, container); return (0, _utilsObjectAssign2['default'])({}, offset, { height: node.offsetHeight, width: node.offsetWidth }); } }); /** * Creates a new OverlayTrigger class that forwards the relevant context * * This static method should only be called at the module level, instead of in * e.g. a render() method, because it's expensive to create new classes. * * For example, you would want to have: * * > export default OverlayTrigger.withContext({ * > myContextKey: React.PropTypes.object * > }); * * and import this when needed. */ OverlayTrigger.withContext = (0, _utilsCreateContextWrapper2['default'])(OverlayTrigger, 'overlay'); exports['default'] = OverlayTrigger; module.exports = exports['default'];