508 lines
15 KiB
JavaScript
508 lines
15 KiB
JavaScript
|
|
'use strict';
|
||
|
|
|
||
|
|
Object.defineProperty(exports, "__esModule", {
|
||
|
|
value: true
|
||
|
|
});
|
||
|
|
|
||
|
|
var _extends2 = require('babel-runtime/helpers/extends');
|
||
|
|
|
||
|
|
var _extends3 = _interopRequireDefault(_extends2);
|
||
|
|
|
||
|
|
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
|
||
|
|
|
||
|
|
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
|
||
|
|
|
||
|
|
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
|
||
|
|
|
||
|
|
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
|
||
|
|
|
||
|
|
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
||
|
|
|
||
|
|
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
||
|
|
|
||
|
|
var _createClass2 = require('babel-runtime/helpers/createClass');
|
||
|
|
|
||
|
|
var _createClass3 = _interopRequireDefault(_createClass2);
|
||
|
|
|
||
|
|
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
|
||
|
|
|
||
|
|
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
|
||
|
|
|
||
|
|
var _inherits2 = require('babel-runtime/helpers/inherits');
|
||
|
|
|
||
|
|
var _inherits3 = _interopRequireDefault(_inherits2);
|
||
|
|
|
||
|
|
var _simpleAssign = require('simple-assign');
|
||
|
|
|
||
|
|
var _simpleAssign2 = _interopRequireDefault(_simpleAssign);
|
||
|
|
|
||
|
|
var _react = require('react');
|
||
|
|
|
||
|
|
var _react2 = _interopRequireDefault(_react);
|
||
|
|
|
||
|
|
var _reactDom = require('react-dom');
|
||
|
|
|
||
|
|
var _reactDom2 = _interopRequireDefault(_reactDom);
|
||
|
|
|
||
|
|
var _reactEventListener = require('react-event-listener');
|
||
|
|
|
||
|
|
var _reactEventListener2 = _interopRequireDefault(_reactEventListener);
|
||
|
|
|
||
|
|
var _RenderToLayer = require('../internal/RenderToLayer');
|
||
|
|
|
||
|
|
var _RenderToLayer2 = _interopRequireDefault(_RenderToLayer);
|
||
|
|
|
||
|
|
var _propTypes = require('../utils/propTypes');
|
||
|
|
|
||
|
|
var _propTypes2 = _interopRequireDefault(_propTypes);
|
||
|
|
|
||
|
|
var _Paper = require('../Paper');
|
||
|
|
|
||
|
|
var _Paper2 = _interopRequireDefault(_Paper);
|
||
|
|
|
||
|
|
var _lodash = require('lodash.throttle');
|
||
|
|
|
||
|
|
var _lodash2 = _interopRequireDefault(_lodash);
|
||
|
|
|
||
|
|
var _PopoverAnimationDefault = require('./PopoverAnimationDefault');
|
||
|
|
|
||
|
|
var _PopoverAnimationDefault2 = _interopRequireDefault(_PopoverAnimationDefault);
|
||
|
|
|
||
|
|
var _iOSHelpers = require('../utils/iOSHelpers');
|
||
|
|
|
||
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
|
||
|
|
var styles = {
|
||
|
|
root: {
|
||
|
|
display: 'none'
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
var Popover = function (_Component) {
|
||
|
|
(0, _inherits3.default)(Popover, _Component);
|
||
|
|
|
||
|
|
function Popover(props, context) {
|
||
|
|
(0, _classCallCheck3.default)(this, Popover);
|
||
|
|
|
||
|
|
var _this = (0, _possibleConstructorReturn3.default)(this, (Popover.__proto__ || (0, _getPrototypeOf2.default)(Popover)).call(this, props, context));
|
||
|
|
|
||
|
|
_this.timeout = null;
|
||
|
|
|
||
|
|
_this.renderLayer = function () {
|
||
|
|
var _this$props = _this.props,
|
||
|
|
animated = _this$props.animated,
|
||
|
|
animation = _this$props.animation,
|
||
|
|
anchorEl = _this$props.anchorEl,
|
||
|
|
anchorOrigin = _this$props.anchorOrigin,
|
||
|
|
autoCloseWhenOffScreen = _this$props.autoCloseWhenOffScreen,
|
||
|
|
canAutoPosition = _this$props.canAutoPosition,
|
||
|
|
children = _this$props.children,
|
||
|
|
onRequestClose = _this$props.onRequestClose,
|
||
|
|
style = _this$props.style,
|
||
|
|
targetOrigin = _this$props.targetOrigin,
|
||
|
|
useLayerForClickAway = _this$props.useLayerForClickAway,
|
||
|
|
other = (0, _objectWithoutProperties3.default)(_this$props, ['animated', 'animation', 'anchorEl', 'anchorOrigin', 'autoCloseWhenOffScreen', 'canAutoPosition', 'children', 'onRequestClose', 'style', 'targetOrigin', 'useLayerForClickAway']);
|
||
|
|
|
||
|
|
|
||
|
|
var styleRoot = style;
|
||
|
|
|
||
|
|
if (!animated) {
|
||
|
|
styleRoot = {
|
||
|
|
position: 'fixed',
|
||
|
|
zIndex: _this.context.muiTheme.zIndex.popover
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!_this.state.open) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return _react2.default.createElement(
|
||
|
|
_Paper2.default,
|
||
|
|
(0, _extends3.default)({ style: (0, _simpleAssign2.default)(styleRoot, style) }, other),
|
||
|
|
children
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
var Animation = animation || _PopoverAnimationDefault2.default;
|
||
|
|
|
||
|
|
return _react2.default.createElement(
|
||
|
|
Animation,
|
||
|
|
(0, _extends3.default)({
|
||
|
|
targetOrigin: targetOrigin,
|
||
|
|
style: styleRoot
|
||
|
|
}, other, {
|
||
|
|
open: _this.state.open && !_this.state.closing
|
||
|
|
}),
|
||
|
|
children
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
_this.componentClickAway = function (event) {
|
||
|
|
event.preventDefault();
|
||
|
|
_this.requestClose('clickAway');
|
||
|
|
};
|
||
|
|
|
||
|
|
_this.setPlacement = function (scrolling) {
|
||
|
|
if (!_this.state.open) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!_this.refs.layer.getLayer()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var targetEl = _this.refs.layer.getLayer().children[0];
|
||
|
|
if (!targetEl) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var _this$props2 = _this.props,
|
||
|
|
targetOrigin = _this$props2.targetOrigin,
|
||
|
|
anchorOrigin = _this$props2.anchorOrigin;
|
||
|
|
|
||
|
|
var anchorEl = _this.props.anchorEl || _this.anchorEl;
|
||
|
|
|
||
|
|
var anchor = _this.getAnchorPosition(anchorEl);
|
||
|
|
var target = _this.getTargetPosition(targetEl);
|
||
|
|
|
||
|
|
var targetPosition = {
|
||
|
|
top: anchor[anchorOrigin.vertical] - target[targetOrigin.vertical],
|
||
|
|
left: anchor[anchorOrigin.horizontal] - target[targetOrigin.horizontal]
|
||
|
|
};
|
||
|
|
|
||
|
|
if (scrolling && _this.props.autoCloseWhenOffScreen) {
|
||
|
|
_this.autoCloseWhenOffScreen(anchor);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (_this.props.canAutoPosition) {
|
||
|
|
target = _this.getTargetPosition(targetEl); // update as height may have changed
|
||
|
|
targetPosition = _this.applyAutoPositionIfNeeded(anchor, target, targetOrigin, anchorOrigin, targetPosition);
|
||
|
|
}
|
||
|
|
|
||
|
|
targetEl.style.top = Math.max(0, targetPosition.top) + 'px';
|
||
|
|
targetEl.style.left = Math.max(0, targetPosition.left) + 'px';
|
||
|
|
targetEl.style.maxHeight = window.innerHeight + 'px';
|
||
|
|
};
|
||
|
|
|
||
|
|
_this.handleResize = (0, _lodash2.default)(_this.setPlacement, 100);
|
||
|
|
_this.handleScroll = (0, _lodash2.default)(_this.setPlacement.bind(_this, true), 50);
|
||
|
|
|
||
|
|
_this.state = {
|
||
|
|
open: props.open,
|
||
|
|
closing: false
|
||
|
|
};
|
||
|
|
return _this;
|
||
|
|
}
|
||
|
|
|
||
|
|
(0, _createClass3.default)(Popover, [{
|
||
|
|
key: 'componentDidMount',
|
||
|
|
value: function componentDidMount() {
|
||
|
|
this.setPlacement();
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'componentWillReceiveProps',
|
||
|
|
value: function componentWillReceiveProps(nextProps) {
|
||
|
|
var _this2 = this;
|
||
|
|
|
||
|
|
if (nextProps.open !== this.state.open) {
|
||
|
|
if (nextProps.open) {
|
||
|
|
this.anchorEl = nextProps.anchorEl || this.props.anchorEl;
|
||
|
|
this.setState({
|
||
|
|
open: true,
|
||
|
|
closing: false
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
if (nextProps.animated) {
|
||
|
|
if (this.timeout !== null) return;
|
||
|
|
this.setState({ closing: true });
|
||
|
|
this.timeout = setTimeout(function () {
|
||
|
|
_this2.setState({
|
||
|
|
open: false
|
||
|
|
}, function () {
|
||
|
|
_this2.timeout = null;
|
||
|
|
});
|
||
|
|
}, 500);
|
||
|
|
} else {
|
||
|
|
this.setState({
|
||
|
|
open: false
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'componentDidUpdate',
|
||
|
|
value: function componentDidUpdate() {
|
||
|
|
this.setPlacement();
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'componentWillUnmount',
|
||
|
|
value: function componentWillUnmount() {
|
||
|
|
this.handleResize.cancel();
|
||
|
|
this.handleScroll.cancel();
|
||
|
|
|
||
|
|
if (this.timeout) {
|
||
|
|
clearTimeout(this.timeout);
|
||
|
|
this.timeout = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'requestClose',
|
||
|
|
value: function requestClose(reason) {
|
||
|
|
if (this.props.onRequestClose) {
|
||
|
|
this.props.onRequestClose(reason);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'getAnchorPosition',
|
||
|
|
value: function getAnchorPosition(el) {
|
||
|
|
if (!el) {
|
||
|
|
el = _reactDom2.default.findDOMNode(this);
|
||
|
|
}
|
||
|
|
|
||
|
|
var rect = el.getBoundingClientRect();
|
||
|
|
var a = {
|
||
|
|
top: rect.top,
|
||
|
|
left: rect.left,
|
||
|
|
width: el.offsetWidth,
|
||
|
|
height: el.offsetHeight
|
||
|
|
};
|
||
|
|
|
||
|
|
a.right = rect.right || a.left + a.width;
|
||
|
|
|
||
|
|
// The fixed positioning isn't respected on iOS when an input is focused.
|
||
|
|
// We need to compute the position from the top of the page and not the viewport.
|
||
|
|
if ((0, _iOSHelpers.isIOS)() && document.activeElement.tagName === 'INPUT') {
|
||
|
|
a.bottom = (0, _iOSHelpers.getOffsetTop)(el) + a.height;
|
||
|
|
} else {
|
||
|
|
a.bottom = rect.bottom || a.top + a.height;
|
||
|
|
}
|
||
|
|
a.middle = a.left + (a.right - a.left) / 2;
|
||
|
|
a.center = a.top + (a.bottom - a.top) / 2;
|
||
|
|
|
||
|
|
return a;
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'getTargetPosition',
|
||
|
|
value: function getTargetPosition(targetEl) {
|
||
|
|
return {
|
||
|
|
top: 0,
|
||
|
|
center: targetEl.offsetHeight / 2,
|
||
|
|
bottom: targetEl.offsetHeight,
|
||
|
|
left: 0,
|
||
|
|
middle: targetEl.offsetWidth / 2,
|
||
|
|
right: targetEl.offsetWidth
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'autoCloseWhenOffScreen',
|
||
|
|
value: function autoCloseWhenOffScreen(anchorPosition) {
|
||
|
|
if (anchorPosition.top < 0 || anchorPosition.top > window.innerHeight || anchorPosition.left < 0 || anchorPosition.left > window.innerWidth) {
|
||
|
|
this.requestClose('offScreen');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'getOverlapMode',
|
||
|
|
value: function getOverlapMode(anchor, target, median) {
|
||
|
|
if ([anchor, target].indexOf(median) >= 0) return 'auto';
|
||
|
|
if (anchor === target) return 'inclusive';
|
||
|
|
return 'exclusive';
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'getPositions',
|
||
|
|
value: function getPositions(anchor, target) {
|
||
|
|
var a = (0, _extends3.default)({}, anchor);
|
||
|
|
var t = (0, _extends3.default)({}, target);
|
||
|
|
|
||
|
|
var positions = {
|
||
|
|
x: ['left', 'right'].filter(function (p) {
|
||
|
|
return p !== t.horizontal;
|
||
|
|
}),
|
||
|
|
y: ['top', 'bottom'].filter(function (p) {
|
||
|
|
return p !== t.vertical;
|
||
|
|
})
|
||
|
|
};
|
||
|
|
|
||
|
|
var overlap = {
|
||
|
|
x: this.getOverlapMode(a.horizontal, t.horizontal, 'middle'),
|
||
|
|
y: this.getOverlapMode(a.vertical, t.vertical, 'center')
|
||
|
|
};
|
||
|
|
|
||
|
|
positions.x.splice(overlap.x === 'auto' ? 0 : 1, 0, 'middle');
|
||
|
|
positions.y.splice(overlap.y === 'auto' ? 0 : 1, 0, 'center');
|
||
|
|
|
||
|
|
if (overlap.y !== 'auto') {
|
||
|
|
a.vertical = a.vertical === 'top' ? 'bottom' : 'top';
|
||
|
|
if (overlap.y === 'inclusive') {
|
||
|
|
t.vertical = t.vertical;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (overlap.x !== 'auto') {
|
||
|
|
a.horizontal = a.horizontal === 'left' ? 'right' : 'left';
|
||
|
|
if (overlap.y === 'inclusive') {
|
||
|
|
t.horizontal = t.horizontal;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
positions: positions,
|
||
|
|
anchorPos: a
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'applyAutoPositionIfNeeded',
|
||
|
|
value: function applyAutoPositionIfNeeded(anchor, target, targetOrigin, anchorOrigin, targetPosition) {
|
||
|
|
var _getPositions = this.getPositions(anchorOrigin, targetOrigin),
|
||
|
|
positions = _getPositions.positions,
|
||
|
|
anchorPos = _getPositions.anchorPos;
|
||
|
|
|
||
|
|
if (targetPosition.top < 0 || targetPosition.top + target.bottom > window.innerHeight) {
|
||
|
|
var newTop = anchor[anchorPos.vertical] - target[positions.y[0]];
|
||
|
|
if (newTop + target.bottom <= window.innerHeight) {
|
||
|
|
targetPosition.top = Math.max(0, newTop);
|
||
|
|
} else {
|
||
|
|
newTop = anchor[anchorPos.vertical] - target[positions.y[1]];
|
||
|
|
if (newTop + target.bottom <= window.innerHeight) {
|
||
|
|
targetPosition.top = Math.max(0, newTop);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (targetPosition.left < 0 || targetPosition.left + target.right > window.innerWidth) {
|
||
|
|
var newLeft = anchor[anchorPos.horizontal] - target[positions.x[0]];
|
||
|
|
if (newLeft + target.right <= window.innerWidth) {
|
||
|
|
targetPosition.left = Math.max(0, newLeft);
|
||
|
|
} else {
|
||
|
|
newLeft = anchor[anchorPos.horizontal] - target[positions.x[1]];
|
||
|
|
if (newLeft + target.right <= window.innerWidth) {
|
||
|
|
targetPosition.left = Math.max(0, newLeft);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return targetPosition;
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
key: 'render',
|
||
|
|
value: function render() {
|
||
|
|
return _react2.default.createElement(
|
||
|
|
'div',
|
||
|
|
{ style: styles.root },
|
||
|
|
_react2.default.createElement(_reactEventListener2.default, {
|
||
|
|
target: 'window',
|
||
|
|
onScroll: this.handleScroll,
|
||
|
|
onResize: this.handleResize
|
||
|
|
}),
|
||
|
|
_react2.default.createElement(_RenderToLayer2.default, {
|
||
|
|
ref: 'layer',
|
||
|
|
open: this.state.open,
|
||
|
|
componentClickAway: this.componentClickAway,
|
||
|
|
useLayerForClickAway: this.props.useLayerForClickAway,
|
||
|
|
render: this.renderLayer
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}]);
|
||
|
|
return Popover;
|
||
|
|
}(_react.Component);
|
||
|
|
|
||
|
|
Popover.defaultProps = {
|
||
|
|
anchorOrigin: {
|
||
|
|
vertical: 'bottom',
|
||
|
|
horizontal: 'left'
|
||
|
|
},
|
||
|
|
animated: true,
|
||
|
|
autoCloseWhenOffScreen: true,
|
||
|
|
canAutoPosition: true,
|
||
|
|
onRequestClose: function onRequestClose() {},
|
||
|
|
open: false,
|
||
|
|
style: {
|
||
|
|
overflowY: 'auto'
|
||
|
|
},
|
||
|
|
targetOrigin: {
|
||
|
|
vertical: 'top',
|
||
|
|
horizontal: 'left'
|
||
|
|
},
|
||
|
|
useLayerForClickAway: true,
|
||
|
|
zDepth: 1
|
||
|
|
};
|
||
|
|
Popover.contextTypes = {
|
||
|
|
muiTheme: _react.PropTypes.object.isRequired
|
||
|
|
};
|
||
|
|
process.env.NODE_ENV !== "production" ? Popover.propTypes = {
|
||
|
|
/**
|
||
|
|
* This is the DOM element that will be used to set the position of the
|
||
|
|
* popover.
|
||
|
|
*/
|
||
|
|
anchorEl: _react.PropTypes.object,
|
||
|
|
/**
|
||
|
|
* This is the point on the anchor where the popover's
|
||
|
|
* `targetOrigin` will attach to.
|
||
|
|
* Options:
|
||
|
|
* vertical: [top, center, bottom]
|
||
|
|
* horizontal: [left, middle, right].
|
||
|
|
*/
|
||
|
|
anchorOrigin: _propTypes2.default.origin,
|
||
|
|
/**
|
||
|
|
* If true, the popover will apply transitions when
|
||
|
|
* it is added to the DOM.
|
||
|
|
*/
|
||
|
|
animated: _react.PropTypes.bool,
|
||
|
|
/**
|
||
|
|
* Override the default animation component used.
|
||
|
|
*/
|
||
|
|
animation: _react.PropTypes.func,
|
||
|
|
/**
|
||
|
|
* If true, the popover will hide when the anchor is scrolled off the screen.
|
||
|
|
*/
|
||
|
|
autoCloseWhenOffScreen: _react.PropTypes.bool,
|
||
|
|
/**
|
||
|
|
* If true, the popover (potentially) ignores `targetOrigin`
|
||
|
|
* and `anchorOrigin` to make itself fit on screen,
|
||
|
|
* which is useful for mobile devices.
|
||
|
|
*/
|
||
|
|
canAutoPosition: _react.PropTypes.bool,
|
||
|
|
/**
|
||
|
|
* The content of the popover.
|
||
|
|
*/
|
||
|
|
children: _react.PropTypes.node,
|
||
|
|
/**
|
||
|
|
* The CSS class name of the root element.
|
||
|
|
*/
|
||
|
|
className: _react.PropTypes.string,
|
||
|
|
/**
|
||
|
|
* Callback function fired when the popover is requested to be closed.
|
||
|
|
*
|
||
|
|
* @param {string} reason The reason for the close request. Possibles values
|
||
|
|
* are 'clickAway' and 'offScreen'.
|
||
|
|
*/
|
||
|
|
onRequestClose: _react.PropTypes.func,
|
||
|
|
/**
|
||
|
|
* If true, the popover is visible.
|
||
|
|
*/
|
||
|
|
open: _react.PropTypes.bool,
|
||
|
|
/**
|
||
|
|
* Override the inline-styles of the root element.
|
||
|
|
*/
|
||
|
|
style: _react.PropTypes.object,
|
||
|
|
/**
|
||
|
|
* This is the point on the popover which will attach to
|
||
|
|
* the anchor's origin.
|
||
|
|
* Options:
|
||
|
|
* vertical: [top, center, bottom]
|
||
|
|
* horizontal: [left, middle, right].
|
||
|
|
*/
|
||
|
|
targetOrigin: _propTypes2.default.origin,
|
||
|
|
/**
|
||
|
|
* If true, the popover will render on top of an invisible
|
||
|
|
* layer, which will prevent clicks to the underlying
|
||
|
|
* elements, and trigger an `onRequestClose('clickAway')` call.
|
||
|
|
*/
|
||
|
|
useLayerForClickAway: _react.PropTypes.bool,
|
||
|
|
/**
|
||
|
|
* The zDepth of the popover.
|
||
|
|
*/
|
||
|
|
zDepth: _propTypes2.default.zDepth
|
||
|
|
} : void 0;
|
||
|
|
exports.default = Popover;
|