UNPKG

@versionone/ui

Version:

Open-source and community supported collection of common UI components built with React. As an open-sourced and community supported project, VersionOne UI is not formally supported by VersionOne.

344 lines (285 loc) 13.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _reactEventListener = require('react-event-listener'); var _reactEventListener2 = _interopRequireDefault(_reactEventListener); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _lodash = require('lodash.throttle'); var _lodash2 = _interopRequireDefault(_lodash); var _reactDom = require('react-dom'); var _Radium = require('./../utilities/Radium'); var _Radium2 = _interopRequireDefault(_Radium); var _RenderToLayer = require('./../internal/RenderToLayer'); var _RenderToLayer2 = _interopRequireDefault(_RenderToLayer); var _ThemeProvider = require('./../ThemeProvider'); var _ThemeProvider2 = _interopRequireDefault(_ThemeProvider); var _position = require('../utilities/position'); var _CustomPropTypes = require('./../utilities/CustomPropTypes'); var CustomPropTypes = _interopRequireWildcard(_CustomPropTypes); var _Positions = require('./Positions'); var Positions = _interopRequireWildcard(_Positions); var _dom = require('./../utilities/dom'); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var resizeThrottleValue = 50; var offScreenThresholdValue = 0; var centerAlignmentDivisor = 2; var getTargetPosition = function getTargetPosition(targetElement) { return { center: targetElement.scrollWidth / centerAlignmentDivisor, height: targetElement.offsetHeight, left: 0, middle: targetElement.offsetHeight / centerAlignmentDivisor, top: 0, width: targetElement.scrollWidth }; }; var isOffScreen = function isOffScreen(elementPosition, viewportPosition) { return elementPosition.top < offScreenThresholdValue || elementPosition.top > viewportPosition.height || elementPosition.left < offScreenThresholdValue || elementPosition.left > viewportPosition.width; }; var Popover = function (_Component) { _inherits(Popover, _Component); function Popover() { var _ref; _classCallCheck(this, Popover); for (var _len = arguments.length, rest = Array(_len), _key = 0; _key < _len; _key++) { rest[_key] = arguments[_key]; } var _this = _possibleConstructorReturn(this, (_ref = Popover.__proto__ || Object.getPrototypeOf(Popover)).call.apply(_ref, [this].concat(rest))); _this.lastPlaceOnScrollFrameRequest = null; _this.state = { closing: false, open: _this.props.open }; _this.setPlacement = _this.setPlacement.bind(_this); _this.handleRendered = _this.handleRendered.bind(_this); _this.handleResize = (0, _lodash2.default)(_this.setPlacement.bind(_this, false), resizeThrottleValue); _this.handleScroll = _this.placeOnNextAnimationFrame.bind(_this); _this.renderLayer = _this.renderLayer.bind(_this); _this.handleComponentClickAway = _this.handleComponentClickAway.bind(_this); return _this; } _createClass(Popover, [{ key: 'componentDidMount', value: function componentDidMount() { this.setPlacement(); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (nextProps.open === this.state.open) { return; } if (nextProps.open) { this.anchorElement = nextProps.anchorElement || this.props.anchorElement; this.setState({ closing: false, open: true }); return; } this.setState({ open: false }); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.handleResize = null; this.handleScroll = null; } }, { key: 'placeOnNextAnimationFrame', value: function placeOnNextAnimationFrame(evt) { var _this2 = this; if (this.lastPlaceOnScrollFrameRequest) { cancelAnimationFrame(this.lastPlaceOnScrollFrameRequest); } this.lastPlaceOnScrollFrameRequest = requestAnimationFrame(function () { _this2.setPlacement(true, evt); _this2.lastPlaceOnScrollFrameRequest = null; }); } }, { key: 'setPlacement', value: function setPlacement(scrolling, evt) { var _props = this.props, anchorElement = _props.anchorElement, anchorOrigin = _props.anchorOrigin, autoCloseWhenOffScreen = _props.autoCloseWhenOffScreen, open = _props.open, targetOrigin = _props.targetOrigin; if (!open || !this.layer) { return; } var targetLayer = this.layer.getLayer(); if (!targetLayer) { return; } var targetElement = targetLayer.children[0]; if (!targetElement) { return; } var anchorEl = anchorElement || this.anchorElement || (0, _reactDom.findDOMNode)(this); var anchorPosition = (0, _position.getPosition)(anchorEl); var targetPosition = getTargetPosition(targetElement); var viewportPosition = (0, _position.getViewportPosition)(); var isAnchorOffScreen = isOffScreen(anchorPosition, viewportPosition); var documentPosition = (0, _position.getDocumentPosition)(); var referencePoint = !autoCloseWhenOffScreen && isAnchorOffScreen ? documentPosition : viewportPosition; var popoverPosition = (0, _position.adjustPositionWithinBoundaries)(anchorPosition, anchorOrigin, targetPosition, targetOrigin, referencePoint); popoverPosition.top = autoCloseWhenOffScreen ? Math.max(offScreenThresholdValue, popoverPosition.top) : popoverPosition.top; if (scrolling && autoCloseWhenOffScreen && isAnchorOffScreen) { this.requestClose(evt, 'offScreen'); } targetElement.style.left = popoverPosition.left + 'px'; if (!scrolling) { // If there should be a scrollbar if (popoverPosition.top + popoverPosition.height >= viewportPosition.top + viewportPosition.height) { // Size minimum width based on browser width calculation targetElement.style.overflowY = 'scroll'; var widthWithVScrollbar = parseFloat(window.getComputedStyle(targetElement).width); targetElement.style.overflowY = 'hidden'; var widthWithoutVScrollbar = parseFloat(window.getComputedStyle(targetElement).width); targetElement.style.minWidth = Math.max(widthWithoutVScrollbar, widthWithVScrollbar) + 'px'; targetElement.style.overflowY = 'auto'; targetElement.style.overflowX = 'hidden'; } var maxHeight = viewportPosition.height - popoverPosition.top; targetElement.style.maxHeight = maxHeight + 'px'; } targetElement.style.top = popoverPosition.top + 'px'; } }, { key: 'handleRendered', value: function handleRendered() { this.setPlacement(); } }, { key: 'renderLayer', value: function renderLayer() { var _props2 = this.props, children = _props2.children, className = _props2.className, anchorElement = _props2.anchorElement; var open = this.state.open; if (!open) { return null; } var anchorEl = anchorElement || this.anchorElement || (0, _reactDom.findDOMNode)(this); if (!(0, _dom.isDescendant)(document, anchorEl)) { return null; } var style = { position: 'fixed' }; return _react2.default.createElement( 'div', { className: (0, _classnames2.default)(className), style: style }, children ); } }, { key: 'handleComponentClickAway', value: function handleComponentClickAway(evt) { var anchorEl = this.anchorElement || (0, _reactDom.findDOMNode)(this); if (evt.type === 'click' && (0, _dom.isDescendant)(anchorEl, evt.target) || anchorEl === evt.target) return; this.requestClose(evt, 'clickedAway'); } }, { key: 'requestClose', value: function requestClose(evt, reason) { var onRequestClose = this.props.onRequestClose; onRequestClose(evt, reason); } }, { key: 'render', value: function render() { var _this3 = this; var open = this.state.open; return _react2.default.createElement( 'div', null, _react2.default.createElement(_reactEventListener2.default, { target: 'window', onResize: this.handleResize, onScroll: this.handleScroll }), _react2.default.createElement(_RenderToLayer2.default, { open: open, ref: function ref(el) { if (el) { _this3.layer = el; } }, render: this.renderLayer, onComponentClickAway: this.handleComponentClickAway, onRendered: this.handleRendered }) ); } }]); return Popover; }(_react.Component); Popover.defaultProps = { anchorOrigin: { horizontal: Positions.left, vertical: Positions.bottom }, autoCloseWhenOffScreen: true, onRequestClose: function onRequestClose() {}, open: false, targetOrigin: { horizontal: Positions.left, vertical: Positions.top } }; Popover.contextTypes = { theme: _react.PropTypes.shape(_ThemeProvider2.default.themeDefinition).isRequired }; process.env.NODE_ENV !== "production" ? Popover.propTypes = { /** * The element to which the popover will relatively render */ anchorElement: _react.PropTypes.object, /** * The coordinates of the anchor element in which to align to the target popover's origin */ anchorOrigin: CustomPropTypes.origin, /** * If true, the popover will close when it exits the viewport */ autoCloseWhenOffScreen: _react.PropTypes.bool, /** * The children to render within the popover */ children: _react.PropTypes.node, /** * CSS class name for root element of the popover */ className: _react.PropTypes.string, /** * Function called when the popover is requested to close */ onRequestClose: _react.PropTypes.func, /** * If true, the popover will be visible; otherwise it will not render */ open: _react.PropTypes.bool, /** * The coordinates of the popover target in which to align to the anchor's origin */ targetOrigin: CustomPropTypes.origin } : void 0; exports.default = (0, _Radium2.default)(Popover);