semantic-ui-react
Version:
The official Semantic-UI-React integration.
462 lines (350 loc) • 15.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.POSITIONS = undefined;
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
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 _pick2 = require('lodash/pick');
var _pick3 = _interopRequireDefault(_pick2);
var _reduce2 = require('lodash/reduce');
var _reduce3 = _interopRequireDefault(_reduce2);
var _assign2 = require('lodash/assign');
var _assign3 = _interopRequireDefault(_assign2);
var _invoke2 = require('lodash/invoke');
var _invoke3 = _interopRequireDefault(_invoke2);
var _isArray2 = require('lodash/isArray');
var _isArray3 = _interopRequireDefault(_isArray2);
var _mapValues2 = require('lodash/mapValues');
var _mapValues3 = _interopRequireDefault(_mapValues2);
var _isNumber2 = require('lodash/isNumber');
var _isNumber3 = _interopRequireDefault(_isNumber2);
var _includes2 = require('lodash/includes');
var _includes3 = _interopRequireDefault(_includes2);
var _without2 = require('lodash/without');
var _without3 = _interopRequireDefault(_without2);
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _lib = require('../../lib');
var _Portal = require('../../addons/Portal');
var _Portal2 = _interopRequireDefault(_Portal);
var _PopupContent = require('./PopupContent');
var _PopupContent2 = _interopRequireDefault(_PopupContent);
var _PopupHeader = require('./PopupHeader');
var _PopupHeader2 = _interopRequireDefault(_PopupHeader);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var POSITIONS = exports.POSITIONS = ['top left', 'top right', 'bottom right', 'bottom left', 'right center', 'left center', 'top center', 'bottom center'];
/**
* A Popup displays additional information on top of a page.
*/
var Popup = function (_Component) {
(0, _inherits3.default)(Popup, _Component);
function Popup() {
var _ref;
var _temp, _this, _ret;
(0, _classCallCheck3.default)(this, Popup);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = (0, _possibleConstructorReturn3.default)(this, (_ref = Popup.__proto__ || Object.getPrototypeOf(Popup)).call.apply(_ref, [this].concat(args))), _this), _this.state = {}, _this.hideOnScroll = function (e) {
_this.setState({ closed: true });
_lib.eventStack.unsub('scroll', _this.hideOnScroll, { target: window });
setTimeout(function () {
return _this.setState({ closed: false });
}, 50);
_this.handleClose(e);
}, _this.handleClose = function (e) {
(0, _invoke3.default)(_this.props, 'onClose', e, _this.props);
}, _this.handleOpen = function (e) {
_this.coords = e.currentTarget.getBoundingClientRect();
var onOpen = _this.props.onOpen;
if (onOpen) onOpen(e, _this.props);
}, _this.handlePortalMount = function (e) {
var hideOnScroll = _this.props.hideOnScroll;
if (hideOnScroll) _lib.eventStack.sub('scroll', _this.hideOnScroll, { target: window });
(0, _invoke3.default)(_this.props, 'onMount', e, _this.props);
}, _this.handlePortalUnmount = function (e) {
var hideOnScroll = _this.props.hideOnScroll;
if (hideOnScroll) _lib.eventStack.unsub('scroll', _this.hideOnScroll, { target: window });
(0, _invoke3.default)(_this.props, 'onUnmount', e, _this.props);
}, _this.handlePopupRef = function (popupRef) {
_this.popupCoords = popupRef ? popupRef.getBoundingClientRect() : null;
_this.setPopupStyle();
}, _temp), (0, _possibleConstructorReturn3.default)(_this, _ret);
}
(0, _createClass3.default)(Popup, [{
key: 'computePopupStyle',
value: function computePopupStyle(positions) {
var style = { position: 'absolute'
// Do not access window/document when server side rendering
};if (!(0, _lib.isBrowser)()) return style;
var offset = this.props.offset;
var _window = window,
pageYOffset = _window.pageYOffset,
pageXOffset = _window.pageXOffset;
var _document$documentEle = document.documentElement,
clientWidth = _document$documentEle.clientWidth,
clientHeight = _document$documentEle.clientHeight;
if ((0, _includes3.default)(positions, 'right')) {
style.right = Math.round(clientWidth - (this.coords.right + pageXOffset));
style.left = 'auto';
} else if ((0, _includes3.default)(positions, 'left')) {
style.left = Math.round(this.coords.left + pageXOffset);
style.right = 'auto';
} else {
// if not left nor right, we are horizontally centering the element
var xOffset = (this.coords.width - this.popupCoords.width) / 2;
style.left = Math.round(this.coords.left + xOffset + pageXOffset);
style.right = 'auto';
}
if ((0, _includes3.default)(positions, 'top')) {
style.bottom = Math.round(clientHeight - (this.coords.top + pageYOffset));
style.top = 'auto';
} else if ((0, _includes3.default)(positions, 'bottom')) {
style.top = Math.round(this.coords.bottom + pageYOffset);
style.bottom = 'auto';
} else {
// if not top nor bottom, we are vertically centering the element
var yOffset = (this.coords.height + this.popupCoords.height) / 2;
style.top = Math.round(this.coords.bottom + pageYOffset - yOffset);
style.bottom = 'auto';
var _xOffset = this.popupCoords.width + 8;
if ((0, _includes3.default)(positions, 'right')) {
style.right -= _xOffset;
} else {
style.left -= _xOffset;
}
}
if (offset) {
if ((0, _isNumber3.default)(style.right)) {
style.right -= offset;
} else {
style.left -= offset;
}
}
return style;
}
// check if the style would display
// the popup outside of the view port
}, {
key: 'isStyleInViewport',
value: function isStyleInViewport(style) {
var _window2 = window,
pageYOffset = _window2.pageYOffset,
pageXOffset = _window2.pageXOffset;
var _document$documentEle2 = document.documentElement,
clientWidth = _document$documentEle2.clientWidth,
clientHeight = _document$documentEle2.clientHeight;
var element = {
top: style.top,
left: style.left,
width: this.popupCoords.width,
height: this.popupCoords.height
};
if ((0, _isNumber3.default)(style.right)) {
element.left = clientWidth - style.right - element.width;
}
if ((0, _isNumber3.default)(style.bottom)) {
element.top = clientHeight - style.bottom - element.height;
}
// hidden on top
if (element.top < pageYOffset) return false;
// hidden on the bottom
if (element.top + element.height > pageYOffset + clientHeight) return false;
// hidden the left
if (element.left < pageXOffset) return false;
// hidden on the right
if (element.left + element.width > pageXOffset + clientWidth) return false;
return true;
}
}, {
key: 'setPopupStyle',
value: function setPopupStyle() {
if (!this.coords || !this.popupCoords) return;
var position = this.props.position;
var style = this.computePopupStyle(position);
// Lets detect if the popup is out of the viewport and adjust
// the position accordingly
var positions = (0, _without3.default)(POSITIONS, position).concat([position]);
for (var i = 0; !this.isStyleInViewport(style) && i < positions.length; i += 1) {
style = this.computePopupStyle(positions[i]);
position = positions[i];
}
// Append 'px' to every numerical values in the style
style = (0, _mapValues3.default)(style, function (value) {
return (0, _isNumber3.default)(value) ? value + 'px' : value;
});
this.setState({ style: style, position: position });
}
}, {
key: 'getPortalProps',
value: function getPortalProps() {
var portalProps = {};
var _props = this.props,
on = _props.on,
hoverable = _props.hoverable;
var normalizedOn = (0, _isArray3.default)(on) ? on : [on];
if (hoverable) {
portalProps.closeOnPortalMouseLeave = true;
portalProps.mouseLeaveDelay = 300;
}
if ((0, _includes3.default)(normalizedOn, 'click')) {
portalProps.openOnTriggerClick = true;
portalProps.closeOnTriggerClick = true;
portalProps.closeOnDocumentClick = true;
}
if ((0, _includes3.default)(normalizedOn, 'focus')) {
portalProps.openOnTriggerFocus = true;
portalProps.closeOnTriggerBlur = true;
}
if ((0, _includes3.default)(normalizedOn, 'hover')) {
portalProps.openOnTriggerMouseEnter = true;
portalProps.closeOnTriggerMouseLeave = true;
// Taken from SUI: https://git.io/vPmCm
portalProps.mouseLeaveDelay = 70;
portalProps.mouseEnterDelay = 50;
}
return portalProps;
}
}, {
key: 'render',
value: function render() {
var _props2 = this.props,
basic = _props2.basic,
children = _props2.children,
className = _props2.className,
content = _props2.content,
flowing = _props2.flowing,
header = _props2.header,
inverted = _props2.inverted,
size = _props2.size,
trigger = _props2.trigger,
wide = _props2.wide;
var _state = this.state,
position = _state.position,
closed = _state.closed;
var style = (0, _assign3.default)({}, this.state.style, this.props.style);
var classes = (0, _classnames2.default)('ui', position, size, (0, _lib.useKeyOrValueAndKey)(wide, 'wide'), (0, _lib.useKeyOnly)(basic, 'basic'), (0, _lib.useKeyOnly)(flowing, 'flowing'), (0, _lib.useKeyOnly)(inverted, 'inverted'), 'popup transition visible', className);
if (closed) return trigger;
var unhandled = (0, _lib.getUnhandledProps)(Popup, this.props);
var portalPropNames = _Portal2.default.handledProps;
var rest = (0, _reduce3.default)(unhandled, function (acc, val, key) {
if (!(0, _includes3.default)(portalPropNames, key)) acc[key] = val;
return acc;
}, {});
var portalProps = (0, _pick3.default)(unhandled, portalPropNames);
var ElementType = (0, _lib.getElementType)(Popup, this.props);
var popupJSX = _react2.default.createElement(
ElementType,
(0, _extends3.default)({}, rest, { className: classes, style: style, ref: this.handlePopupRef }),
children,
_lib.childrenUtils.isNil(children) && _PopupHeader2.default.create(header),
_lib.childrenUtils.isNil(children) && _PopupContent2.default.create(content)
);
var mergedPortalProps = (0, _extends3.default)({}, this.getPortalProps(), portalProps);
return _react2.default.createElement(
_Portal2.default,
(0, _extends3.default)({}, mergedPortalProps, {
trigger: trigger,
onClose: this.handleClose,
onMount: this.handlePortalMount,
onOpen: this.handleOpen,
onUnmount: this.handlePortalUnmount
}),
popupJSX
);
}
}]);
return Popup;
}(_react.Component);
Popup.defaultProps = {
position: 'top left',
on: 'hover'
};
Popup._meta = {
name: 'Popup',
type: _lib.META.TYPES.MODULE
};
Popup.Content = _PopupContent2.default;
Popup.Header = _PopupHeader2.default;
Popup.handledProps = ['as', 'basic', 'children', 'className', 'content', 'flowing', 'header', 'hideOnScroll', 'hoverable', 'inverted', 'offset', 'on', 'onClose', 'onMount', 'onOpen', 'onUnmount', 'position', 'size', 'style', 'trigger', 'wide'];
exports.default = Popup;
Popup.propTypes = process.env.NODE_ENV !== "production" ? {
/** An element type to render as (string or function). */
as: _lib.customPropTypes.as,
/** Display the popup without the pointing arrow. */
basic: _propTypes2.default.bool,
/** Primary content. */
children: _propTypes2.default.node,
/** Additional classes. */
className: _propTypes2.default.string,
/** Simple text content for the popover. */
content: _lib.customPropTypes.itemShorthand,
/** A flowing Popup has no maximum width and continues to flow to fit its content. */
flowing: _propTypes2.default.bool,
/** Takes up the entire width of its offset container. */
// TODO: implement the Popup fluid layout
// fluid: PropTypes.bool,
/** Header displayed above the content in bold. */
header: _lib.customPropTypes.itemShorthand,
/** Hide the Popup when scrolling the window. */
hideOnScroll: _propTypes2.default.bool,
/** Whether the popup should not close on hover. */
hoverable: _propTypes2.default.bool,
/** Invert the colors of the Popup. */
inverted: _propTypes2.default.bool,
/** Horizontal offset in pixels to be applied to the Popup. */
offset: _propTypes2.default.number,
/** Events triggering the popup. */
on: _propTypes2.default.oneOfType([_propTypes2.default.oneOf(['hover', 'click', 'focus']), _propTypes2.default.arrayOf(_propTypes2.default.oneOf(['hover', 'click', 'focus']))]),
/**
* Called when a close event happens.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onClose: _propTypes2.default.func,
/**
* Called when the portal is mounted on the DOM.
*
* @param {null}
* @param {object} data - All props.
*/
onMount: _propTypes2.default.func,
/**
* Called when an open event happens.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onOpen: _propTypes2.default.func,
/**
* Called when the portal is unmounted from the DOM.
*
* @param {null}
* @param {object} data - All props.
*/
onUnmount: _propTypes2.default.func,
/** Position for the popover. */
position: _propTypes2.default.oneOf(POSITIONS),
/** Popup size. */
size: _propTypes2.default.oneOf((0, _without3.default)(_lib.SUI.SIZES, 'medium', 'big', 'massive')),
/** Custom Popup style. */
style: _propTypes2.default.object,
/** Element to be rendered in-place where the popup is defined. */
trigger: _propTypes2.default.node,
/** Popup width. */
wide: _propTypes2.default.oneOfType([_propTypes2.default.bool, _propTypes2.default.oneOf(['very'])])
} : {};