wix-style-react
Version:
wix-style-react
608 lines (605 loc) • 18.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.PopoverCore = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _ReactPortal = _interopRequireDefault(require("../../utils/ReactPortal/ReactPortal"));
var _reactPopper = require("react-popper");
var _reactTransitionGroup = require("react-transition-group");
var _ClickOutside = require("./utils/ClickOutside");
var _modifiers = require("./utils/modifiers");
var _filterDataProps = require("./utils/filter-data-props");
var _uniqueId = _interopRequireDefault(require("lodash/uniqueId"));
var _utils = require("./utils/utils");
var _helpers = require("./utils/helpers");
var _getAppendToElement = require("./utils/getAppendToElement");
var _context = require("../../WixStyleReactProvider/context");
var _PopoverSt = require("../Popover.st.css");
var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/Popover/PopoverCore/PopoverCore.tsx";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
// This is here and not in the test setup because we don't want consumers to need to run it as well
var testId = '0';
var isTestEnv = process.env.NODE_ENV === 'test';
if (isTestEnv && typeof document !== 'undefined' && !document.createRange) {
_helpers.popoverTestUtils.createRange();
}
/**
* Popover
*/
class PopoverCore extends _react.default.Component {
constructor(props) {
super(props);
this.targetRef = void 0;
this.portalNode = null;
this.portalClasses = '';
this.appendToNode = null;
this.clickOutsideRef = null;
this.popoverContentRef = void 0;
this.clickOutsideClass = void 0;
this.contentHook = void 0;
this.popperScheduleUpdate = void 0;
// Timer instances for the show/hide delays
this._hideTimeout = null;
this._showTimeout = null;
this._handleClickOutside = event => {
var {
onClickOutside: onClickOutsideCallback,
shown,
disableClickOutsideWhenClosed
} = this.props;
if (onClickOutsideCallback && !(disableClickOutsideWhenClosed && !shown)) {
onClickOutsideCallback(event);
}
};
this._onKeyDown = e => {
var {
onEscPress
} = this.props;
if (onEscPress && e.key === 'Escape') {
onEscPress(e);
}
};
/**
* Checks to see if the focused element is outside the Popover content
*/
this._onDocumentKeyUp = e => {
var {
onTabOut
} = this.props;
if (typeof document !== 'undefined' && this.popoverContentRef.current && !this.popoverContentRef.current.contains(document.activeElement)) {
onTabOut && onTabOut(e);
}
};
this.state = {
isMounted: false,
shown: props.shown || false
};
if (isTestEnv) {
testId = _helpers.popoverTestUtils.generateId();
}
this.clickOutsideRef = /*#__PURE__*/_react.default.createRef();
this.popoverContentRef = /*#__PURE__*/_react.default.createRef();
this.clickOutsideClass = (0, _uniqueId.default)('clickOutside');
this.contentHook = "popover-content-".concat(props.dataHook || '', "-").concat(testId);
}
focus() {
if (this.popoverContentRef.current) {
this.popoverContentRef.current.focus();
}
}
getPopperContentStructure(childrenObject) {
var {
shown
} = this.state;
var {
moveBy,
appendTo,
placement,
showArrow,
moveArrowTo,
flip,
fixed,
customArrow,
role,
id,
zIndex,
minWidth,
maxWidth,
width,
dynamicWidth,
onEscPress,
tabIndex,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
'aria-describedby': ariaDescribedBy,
timeout,
theme
} = this.props;
var shouldAnimate = (0, _utils.shouldAnimatePopover)({
timeout
});
var modifiers = (0, _modifiers.createModifiers)({
minWidth,
width,
dynamicWidth,
moveBy,
appendTo,
shouldAnimate,
flip,
placement,
fixed,
isTestEnv
});
var mergeRefs = function mergeRefs() {
for (var _len = arguments.length, refs = new Array(_len), _key = 0; _key < _len; _key++) {
refs[_key] = arguments[_key];
}
var filteredRefs = refs.filter(Boolean);
if (!filteredRefs.length) {
return null;
}
if (filteredRefs.length === 0) {
return filteredRefs[0];
}
return inst => {
for (var ref of filteredRefs) {
if (typeof ref === 'function') {
ref(inst);
} else if (ref) {
ref.current = inst;
}
}
};
};
var popperWithArrow = /*#__PURE__*/_react.default.createElement(_reactPopper.Popper, {
modifiers: modifiers,
placement: placement,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 181,
columnNumber: 7
}
}, _ref => {
var {
ref,
style: popperStyles,
placement: popperPlacement,
arrowProps,
scheduleUpdate
} = _ref;
this.popperScheduleUpdate = scheduleUpdate;
return /*#__PURE__*/_react.default.createElement("div", {
"data-hook": "popover-content",
className: (0, _PopoverSt.st)(this.clickOutsideClass, this.context.newBrandingClass),
"data-content-element": this.contentHook,
ref: ref,
style: _objectSpread(_objectSpread({}, popperStyles), {}, {
zIndex
}),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 192,
columnNumber: 13
}
}, showArrow && this.renderArrow(arrowProps, moveArrowTo, popperPlacement || placement, customArrow), /*#__PURE__*/_react.default.createElement("div", {
id: id,
role: role,
tabIndex: tabIndex,
ref: this.popoverContentRef,
style: {
maxWidth
},
className: (0, _PopoverSt.st)(_PopoverSt.classes.content, {
skin: theme,
placement: popperPlacement || placement,
hasArrow: true
}),
onKeyDown: shown && onEscPress ? this._onKeyDown : undefined,
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledby,
"aria-describedby": ariaDescribedBy,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 209,
columnNumber: 15
}
}, childrenObject.Content));
});
var popper = /*#__PURE__*/_react.default.createElement(_reactPopper.Popper, {
modifiers: modifiers,
placement: placement,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 234,
columnNumber: 7
}
}, _ref2 => {
var {
ref,
style: popperStyles,
placement: popperPlacement,
scheduleUpdate
} = _ref2;
this.popperScheduleUpdate = scheduleUpdate;
return /*#__PURE__*/_react.default.createElement("div", {
id: id,
ref: mergeRefs(ref, this.popoverContentRef),
role: role,
tabIndex: tabIndex,
className: (0, _PopoverSt.st)(_PopoverSt.classes.content, {
skin: theme,
placement: popperPlacement || placement
}, this.clickOutsideClass, this.context.newBrandingClass),
"data-hook": "popover-content",
style: _objectSpread(_objectSpread({}, popperStyles), {}, {
zIndex,
maxWidth
}),
"data-content-element": this.contentHook,
onKeyDown: shown && onEscPress ? this._onKeyDown : undefined,
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledby,
"aria-describedby": ariaDescribedBy,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 243,
columnNumber: 13
}
}, childrenObject.Content);
});
return this.wrapWithAnimations(showArrow ? popperWithArrow : popper);
}
applyStylesToPortaledNode() {
var {
shown
} = this.state;
var shouldAnimate = (0, _utils.shouldAnimatePopover)(this.props);
if (shouldAnimate || shown) {
(0, _utils.attachClasses)(this.portalNode, this.portalClasses);
} else {
(0, _utils.detachClasses)(this.portalNode, this.portalClasses);
}
}
wrapWithAnimations(popper) {
var {
timeout
} = this.props;
var {
shown
} = this.state;
var shouldAnimate = (0, _utils.shouldAnimatePopover)(this.props);
return shouldAnimate ? /*#__PURE__*/_react.default.createElement(_reactTransitionGroup.CSSTransition, {
in: shown,
timeout: timeout,
unmountOnExit: true,
classNames: {
enter: _PopoverSt.classes.animationEnter,
enterActive: _PopoverSt.classes.animationEnterActive,
exit: _PopoverSt.classes.animationExit,
exitActive: _PopoverSt.classes.animationExitActive
},
addEndListener: () => {},
onExited: () => (0, _utils.detachClasses)(this.portalNode, this.portalClasses),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 293,
columnNumber: 7
}
}, popper) : popper;
}
renderPopperContent(childrenObject) {
var popper = this.getPopperContentStructure(childrenObject);
return this.portalNode ? /*#__PURE__*/_react.default.createElement(_ReactPortal.default, {
node: this.portalNode,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 317,
columnNumber: 7
}
}, popper) : popper;
}
renderArrow(arrowProps, moveArrowTo, placement, customArrow) {
var {
theme
} = this.props;
var commonProps = {
ref: arrowProps.ref,
key: 'popover-arrow',
'data-hook': 'popover-arrow',
style: _objectSpread(_objectSpread({}, arrowProps.style), (0, _utils.getArrowShift)(moveArrowTo, placement))
};
if (customArrow) {
return customArrow(placement, commonProps);
}
return /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({}, commonProps, {
className: (0, _PopoverSt.st)(_PopoverSt.classes.arrow, {
skin: theme,
placement
}),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 345,
columnNumber: 7
}
}));
}
componentDidMount() {
var {
shown,
onTabOut
} = this.props;
this.initAppendToNode();
if (onTabOut && shown) {
this._setBlurByKeyboardListener();
}
this.setState({
isMounted: true
});
}
_setBlurByKeyboardListener() {
if (typeof document !== 'undefined') {
document.addEventListener('keyup', this._onDocumentKeyUp, true);
}
}
_removeBlurListener() {
if (typeof document !== 'undefined') {
document.removeEventListener('keyup', this._onDocumentKeyUp, true);
}
}
initAppendToNode() {
var {
appendTo
} = this.props;
this.appendToNode = (0, _getAppendToElement.getAppendToElement)(appendTo, this.targetRef);
if (this.appendToNode) {
this.portalNode = document.createElement('div');
this.portalNode.setAttribute('data-hook', 'popover-portal');
/**
* reset overlay wrapping layer
* so that styles from copied classnames
* won't break the overlay:
* - content is position relative to body
* - overlay layer is hidden
*/
Object.assign(this.portalNode.style, {
position: 'static',
display: 'block',
top: 0,
left: 0,
width: 0,
height: 0
});
this.appendToNode.appendChild(this.portalNode);
}
}
hidePopover() {
var {
isMounted
} = this.state;
var {
hideDelay,
onTabOut,
onHide
} = this.props;
if (!isMounted || this._hideTimeout) {
return;
}
if (this._showTimeout) {
clearTimeout(this._showTimeout);
this._showTimeout = null;
}
if (onTabOut) {
this._removeBlurListener();
}
if (hideDelay) {
this._hideTimeout = setTimeout(() => {
this.setState({
shown: false
});
onHide == null || onHide();
}, hideDelay);
} else {
this.setState({
shown: false
});
onHide == null || onHide();
}
}
showPopover() {
var {
isMounted
} = this.state;
var {
showDelay,
onTabOut,
onShow
} = this.props;
if (!isMounted || this._showTimeout) {
return;
}
if (this._hideTimeout) {
clearTimeout(this._hideTimeout);
this._hideTimeout = null;
}
if (onTabOut) {
this._setBlurByKeyboardListener();
}
if (showDelay) {
this._showTimeout = setTimeout(() => {
this.setState({
shown: true
});
onShow == null || onShow();
}, showDelay);
} else {
this.setState({
shown: true
});
onShow == null || onShow();
}
}
componentWillUnmount() {
if (this.portalNode && this.appendToNode && this.appendToNode.children.length) {
// FIXME: What if component is updated with a different appendTo? It is a far-fetched use-case,
// but we would need to remove the portaled node, and created another one.
this.appendToNode.removeChild(this.portalNode);
}
this.portalNode = null;
if (this._hideTimeout) {
clearTimeout(this._hideTimeout);
this._hideTimeout = null;
}
if (this._showTimeout) {
clearTimeout(this._showTimeout);
this._showTimeout = null;
}
}
updatePosition() {
if (this.popperScheduleUpdate) {
this.popperScheduleUpdate();
}
}
componentDidUpdate(prevProps) {
var {
theme: skin,
className,
shown
} = this.props;
if (this.portalNode) {
// Re-calculate the portal's styles
this.portalClasses = (0, _PopoverSt.st)(_PopoverSt.classes.root, {
skin
}, className);
// Apply the styles to the portal
this.applyStylesToPortaledNode();
}
// Update popover visibility
if (prevProps.shown !== shown) {
if (shown) {
this.showPopover();
} else {
this.hidePopover();
}
} else {
// Update popper's position
this.updatePosition();
}
}
render() {
var {
onMouseEnter,
onMouseLeave,
onKeyDown,
onClick,
children,
className,
style,
fluid,
theme: skin,
dataHook,
zIndex,
excludeClass
} = this.props;
var {
isMounted,
shown
} = this.state;
var childrenObject = (0, _utils.buildChildrenObject)(children, {
Element: null,
Content: null
});
var shouldAnimate = (0, _utils.shouldAnimatePopover)(this.props);
var shouldRenderPopper = isMounted && (shouldAnimate || shown);
return (
/*#__PURE__*/
// @ts-ignore
_react.default.createElement(_reactPopper.Manager, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 548,
columnNumber: 7
}
}, /*#__PURE__*/_react.default.createElement(_ClickOutside.ClickOutside, {
rootRef: this.clickOutsideRef,
onClickOutside: shown ? this._handleClickOutside : undefined,
excludeClass: excludeClass ? [this.clickOutsideClass, excludeClass] : this.clickOutsideClass,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 550,
columnNumber: 9
}
}, /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({
ref: this.clickOutsideRef,
style: style,
"data-hook": dataHook,
"data-content-hook": this.contentHook,
className: (0, _PopoverSt.st)(_PopoverSt.classes.root, {
fluid,
skin
}, className),
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave,
"data-zindex": zIndex
}, (0, _filterDataProps.filterDataProps)(this.props), {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 559,
columnNumber: 11
}
}), /*#__PURE__*/_react.default.createElement(_reactPopper.Reference, {
innerRef: r => this.targetRef = r,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 571,
columnNumber: 13
}
}, _ref3 => {
var {
ref
} = _ref3;
return /*#__PURE__*/_react.default.createElement("div", {
ref: ref,
className: _PopoverSt.classes.element,
"data-hook": "popover-element",
onClick: onClick,
onKeyDown: onKeyDown,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 573,
columnNumber: 17
}
}, childrenObject.Element);
}), shouldRenderPopper && this.renderPopperContent(childrenObject))))
);
}
}
exports.PopoverCore = PopoverCore;
PopoverCore.displayName = 'Popover';
PopoverCore.defaultProps = {
flip: true,
fixed: false,
zIndex: 1000,
shown: false,
placement: 'bottom',
excludeClass: ''
};
PopoverCore.contextType = _context.WixStyleReactContext;
PopoverCore.Element = (0, _utils.createComponentThatRendersItsChildren)('Popover.Element');
PopoverCore.Content = (0, _utils.createComponentThatRendersItsChildren)('Popover.Content');
//# sourceMappingURL=PopoverCore.js.map