@thejones/react-common-components
Version:
React component - semantic ui
497 lines (392 loc) • 19.1 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.POSITIONS = void 0;
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _pick2 = _interopRequireDefault(require("lodash/pick"));
var _reduce2 = _interopRequireDefault(require("lodash/reduce"));
var _assign2 = _interopRequireDefault(require("lodash/assign"));
var _invoke2 = _interopRequireDefault(require("lodash/invoke"));
var _isArray2 = _interopRequireDefault(require("lodash/isArray"));
var _mapValues2 = _interopRequireDefault(require("lodash/mapValues"));
var _isNumber2 = _interopRequireDefault(require("lodash/isNumber"));
var _includes2 = _interopRequireDefault(require("lodash/includes"));
var _without2 = _interopRequireDefault(require("lodash/without"));
var _classnames = _interopRequireDefault(require("classnames"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireWildcard(require("react"));
var _lib = require("../../lib");
var _Portal = _interopRequireDefault(require("../../addons/Portal"));
var _Ref = _interopRequireDefault(require("../../addons/Ref"));
var _PopupContent = _interopRequireDefault(require("./PopupContent"));
var _PopupHeader = _interopRequireDefault(require("./PopupHeader"));
var 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.
*/
exports.POSITIONS = POSITIONS;
var Popup =
/*#__PURE__*/
function (_Component) {
(0, _inherits2.default)(Popup, _Component);
function Popup() {
var _getPrototypeOf2;
var _this;
(0, _classCallCheck2.default)(this, Popup);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(Popup)).call.apply(_getPrototypeOf2, [this].concat(args)));
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "state", {});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "computePopupStyle", function (positions) {
var style = {
position: 'absolute'
};
var context = _this.getContext(); // Do not access window/document when server side rendering
if (!(0, _lib.isBrowser)()) return style;
var _this$props = _this.props,
horizontalOffset = _this$props.horizontalOffset,
verticalOffset = _this$props.verticalOffset;
var _window = window,
pageYOffset = _window.pageYOffset,
pageXOffset = _window.pageXOffset;
var _document$documentEle = document.documentElement,
clientWidth = _document$documentEle.clientWidth,
clientHeight = _document$documentEle.clientHeight;
var coords = _this.coords || context.getBoundingClientRect();
if ((0, _includes2.default)(positions, 'right')) {
style.right = Math.round(clientWidth - (coords.right + pageXOffset));
style.left = 'auto';
} else if ((0, _includes2.default)(positions, 'left')) {
style.left = Math.round(coords.left + pageXOffset);
style.right = 'auto';
} else {
// if not left nor right, we are horizontally centering the element
var xOffset = (coords.width - _this.popupCoords.width) / 2;
style.left = Math.round(coords.left + xOffset + pageXOffset);
style.right = 'auto';
}
if ((0, _includes2.default)(positions, 'top')) {
style.bottom = Math.round(clientHeight - (coords.top + pageYOffset));
style.top = 'auto';
} else if ((0, _includes2.default)(positions, 'bottom')) {
style.top = Math.round(coords.bottom + pageYOffset);
style.bottom = 'auto';
} else {
// if not top nor bottom, we are vertically centering the element
var yOffset = (coords.height + _this.popupCoords.height) / 2;
style.top = Math.round(coords.bottom + pageYOffset - yOffset);
style.bottom = 'auto';
var _xOffset = _this.popupCoords.width + 8;
if ((0, _includes2.default)(positions, 'right')) {
style.right -= _xOffset;
} else {
style.left -= _xOffset;
}
}
if (horizontalOffset) {
if ((0, _isNumber2.default)(style.right)) {
style.right -= horizontalOffset;
} else {
style.left -= horizontalOffset;
}
}
if (verticalOffset) {
if ((0, _isNumber2.default)(style.top)) {
style.top += verticalOffset;
} else {
style.bottom += verticalOffset;
}
}
return style;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "isStyleInViewport", function (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, _isNumber2.default)(style.right)) {
element.left = clientWidth - style.right - element.width;
}
if ((0, _isNumber2.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;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setPopupStyle", function () {
var context = _this.getContext();
if (!_this.coords && !context || !_this.popupCoords) return;
var position = _this.props.position;
var style = _this.computePopupStyle(position);
var keepInViewPort = _this.props.keepInViewPort;
if (keepInViewPort) {
// Lets detect if the popup is out of the viewport and adjust
// the position accordingly
var positions = (0, _without2.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, _mapValues2.default)(style, function (value) {
return (0, _isNumber2.default)(value) ? "".concat(value, "px") : value;
});
_this.setState({
style: style,
position: position
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getPortalProps", function () {
var portalProps = {};
var _this$props2 = _this.props,
on = _this$props2.on,
hoverable = _this$props2.hoverable;
var normalizedOn = (0, _isArray2.default)(on) ? on : [on];
if (hoverable) {
portalProps.closeOnPortalMouseLeave = true;
portalProps.mouseLeaveDelay = 300;
}
if ((0, _includes2.default)(normalizedOn, 'click')) {
portalProps.openOnTriggerClick = true;
portalProps.closeOnTriggerClick = true;
portalProps.closeOnDocumentClick = true;
}
if ((0, _includes2.default)(normalizedOn, 'focus')) {
portalProps.openOnTriggerFocus = true;
portalProps.closeOnTriggerBlur = true;
}
if ((0, _includes2.default)(normalizedOn, 'hover')) {
portalProps.openOnTriggerMouseEnter = true;
portalProps.closeOnTriggerMouseLeave = true; // Taken from SUI: https://git.io/vPmCm
portalProps.mouseLeaveDelay = 70;
portalProps.mouseEnterDelay = 50;
}
return portalProps;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "hideOnScroll", function (e) {
_this.setState({
closed: true
});
_lib.eventStack.unsub('scroll', _this.hideOnScroll, {
target: window
});
_this.timeoutId = setTimeout(function () {
_this.setState({
closed: false
});
}, 50);
_this.handleClose(e);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleClose", function (e) {
(0, _invoke2.default)(_this.props, 'onClose', e, _this.props);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleOpen", function (e) {
_this.coords = _this.getContext().getBoundingClientRect();
(0, _invoke2.default)(_this.props, 'onOpen', e, _this.props);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handlePortalMount", function (e) {
var hideOnScroll = _this.props.hideOnScroll;
if (hideOnScroll) _lib.eventStack.sub('scroll', _this.hideOnScroll, {
target: window
});
_this.setPopupStyle();
(0, _invoke2.default)(_this.props, 'onMount', e, _this.props);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handlePortalUnmount", function (e) {
var hideOnScroll = _this.props.hideOnScroll;
if (hideOnScroll) _lib.eventStack.unsub('scroll', _this.hideOnScroll, {
target: window
});
(0, _invoke2.default)(_this.props, 'onUnmount', e, _this.props);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handlePopupRef", function (popupRef) {
_this.popupCoords = popupRef ? popupRef.getBoundingClientRect() : null;
_this.setPopupStyle();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleTriggerRef", function (triggerRef) {
_this.triggerRef = triggerRef;
_this.setPopupStyle();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getContext", function () {
var context = _this.props.context;
var contextFromProp = (0, _lib.isRefObject)(context) ? context.current : context;
return contextFromProp || _this.triggerRef;
});
return _this;
}
(0, _createClass2.default)(Popup, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
// if horizontal/vertical offsets change, re-calculate the CSS style
var _this$props3 = this.props,
horizontalOffset = _this$props3.horizontalOffset,
verticalOffset = _this$props3.verticalOffset;
if (horizontalOffset !== prevProps.horizontalOffset || verticalOffset !== prevProps.verticalOffset) {
this.setPopupStyle();
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
clearTimeout(this.timeoutId);
}
}, {
key: "render",
value: function render() {
var _this$props4 = this.props,
basic = _this$props4.basic,
children = _this$props4.children,
className = _this$props4.className,
content = _this$props4.content,
flowing = _this$props4.flowing,
header = _this$props4.header,
inverted = _this$props4.inverted,
size = _this$props4.size,
trigger = _this$props4.trigger,
wide = _this$props4.wide,
disabled = _this$props4.disabled;
var _this$state = this.state,
position = _this$state.position,
closed = _this$state.closed;
var style = (0, _assign2.default)({}, this.state.style, this.props.style);
var classes = (0, _classnames.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 || disabled) return trigger;
var unhandled = (0, _lib.getUnhandledProps)(Popup, this.props);
var portalPropNames = _Portal.default.handledProps;
var rest = (0, _reduce2.default)(unhandled, function (acc, val, key) {
if (!(0, _includes2.default)(portalPropNames, key)) acc[key] = val;
return acc;
}, {});
var portalProps = (0, _pick2.default)(unhandled, portalPropNames);
var ElementType = (0, _lib.getElementType)(Popup, this.props);
var popupJSX = _react.default.createElement(_Ref.default, {
innerRef: this.handlePopupRef
}, _react.default.createElement(ElementType, (0, _extends2.default)({}, rest, {
className: classes,
style: style
}), children, _lib.childrenUtils.isNil(children) && _PopupHeader.default.create(header, {
autoGenerateKey: false
}), _lib.childrenUtils.isNil(children) && _PopupContent.default.create(content, {
autoGenerateKey: false
})));
var mergedPortalProps = (0, _objectSpread2.default)({}, this.getPortalProps(), portalProps);
return _react.default.createElement(_Portal.default, (0, _extends2.default)({}, mergedPortalProps, {
onClose: this.handleClose,
onMount: this.handlePortalMount,
onOpen: this.handleOpen,
onUnmount: this.handlePortalUnmount,
trigger: trigger,
triggerRef: this.handleTriggerRef
}), popupJSX);
}
}]);
return Popup;
}(_react.Component);
exports.default = Popup;
(0, _defineProperty2.default)(Popup, "defaultProps", {
position: 'top left',
on: 'hover',
keepInViewPort: true,
disabled: false
});
(0, _defineProperty2.default)(Popup, "Content", _PopupContent.default);
(0, _defineProperty2.default)(Popup, "Header", _PopupHeader.default);
(0, _defineProperty2.default)(Popup, "handledProps", ["as", "basic", "children", "className", "content", "context", "disabled", "flowing", "header", "hideOnScroll", "horizontalOffset", "hoverable", "inverted", "keepInViewPort", "on", "onClose", "onMount", "onOpen", "onUnmount", "position", "size", "style", "trigger", "verticalOffset", "wide"]);
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: _propTypes.default.bool,
/** Primary content. */
children: _propTypes.default.node,
/** Additional classes. */
className: _propTypes.default.string,
/** Simple text content for the popover. */
content: _lib.customPropTypes.itemShorthand,
/** Existing element the pop-up should be bound to. */
context: _propTypes.default.oneOfType([_propTypes.default.object, _lib.customPropTypes.refObject]),
/** A disabled popup only renders its trigger. */
disabled: _propTypes.default.bool,
/** A flowing Popup has no maximum width and continues to flow to fit its content. */
flowing: _propTypes.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: _propTypes.default.bool,
/** Whether the popup should not close on hover. */
hoverable: _propTypes.default.bool,
/** Invert the colors of the Popup. */
inverted: _propTypes.default.bool,
/** Horizontal offset in pixels to be applied to the Popup. */
horizontalOffset: _propTypes.default.number,
/** Vertical offset in pixels to be applied to the Popup. */
verticalOffset: _propTypes.default.number,
/** Events triggering the popup. */
on: _propTypes.default.oneOfType([_propTypes.default.oneOf(['hover', 'click', 'focus']), _propTypes.default.arrayOf(_propTypes.default.oneOf(['hover', 'click', 'focus']))]),
/**
* Called when a close event happens.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onClose: _propTypes.default.func,
/**
* Called when the portal is mounted on the DOM.
*
* @param {null}
* @param {object} data - All props.
*/
onMount: _propTypes.default.func,
/**
* Called when an open event happens.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onOpen: _propTypes.default.func,
/**
* Called when the portal is unmounted from the DOM.
*
* @param {null}
* @param {object} data - All props.
*/
onUnmount: _propTypes.default.func,
/** Position for the popover. */
position: _propTypes.default.oneOf(POSITIONS),
/** Popup size. */
size: _propTypes.default.oneOf((0, _without2.default)(_lib.SUI.SIZES, 'medium', 'big', 'massive')),
/** Custom Popup style. */
style: _propTypes.default.object,
/** Element to be rendered in-place where the popup is defined. */
trigger: _propTypes.default.node,
/** Popup width. */
wide: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.oneOf(['very'])]),
/** Element to be rendered within the confines of the viewport whenever possible. */
keepInViewPort: _propTypes.default.bool
} : {};