@wix/design-system
Version:
@wix/design-system
614 lines (609 loc) • 21.6 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("../../WixDesignSystemProvider/context");
var _PopoverSt = require("../Popover.st.css.js");
var _mergeRefs = require("../../utils/mergeRefs");
var _ZIndex = require("../../common/ZIndex");
var _jsxFileName = "/home/builduser/work/57e038ea7326c1ec/packages/wix-design-system/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.transitionRef = void 0;
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;
// requestAnimationFrame id for the animationFrame position-tracking loop
this._animationFrameId = null;
this._handleClickOutside = event => {
var _this$props = this.props,
onClickOutsideCallback = _this$props.onClickOutside,
shown = _this$props.shown,
_this$props$disableCl = _this$props.disableClickOutsideWhenClosed,
disableClickOutsideWhenClosed = _this$props$disableCl === void 0 ? true : _this$props$disableCl;
if (onClickOutsideCallback && !(disableClickOutsideWhenClosed && !shown)) {
onClickOutsideCallback(event);
}
};
this._onKeyDown = e => {
var onEscPress = this.props.onEscPress;
if (onEscPress && e.key === 'Escape') {
onEscPress(e);
}
};
/**
* Checks to see if the focused element is outside the Popover content
*/
this._onDocumentKeyUp = event => {
var onTabOut = this.props.onTabOut;
if (typeof document !== 'undefined' && this.popoverContentRef.current && !this.popoverContentRef.current.contains(document.activeElement)) {
onTabOut && onTabOut(event);
}
};
this.state = {
isMounted: false,
shown: props.shown || false
};
if (isTestEnv) {
testId = _helpers.popoverTestUtils.generateId();
}
this.clickOutsideRef = /*#__PURE__*/_react.default.createRef();
this.transitionRef = /*#__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.shown;
var _this$props2 = this.props,
moveBy = _this$props2.moveBy,
appendTo = _this$props2.appendTo,
placement = _this$props2.placement,
showArrow = _this$props2.showArrow,
moveArrowTo = _this$props2.moveArrowTo,
flip = _this$props2.flip,
flipModifier = _this$props2.flipModifier,
fixed = _this$props2.fixed,
customArrow = _this$props2.customArrow,
role = _this$props2.role,
id = _this$props2.id,
zIndex = _this$props2.zIndex,
minWidth = _this$props2.minWidth,
maxWidth = _this$props2.maxWidth,
width = _this$props2.width,
dynamicWidth = _this$props2.dynamicWidth,
onEscPress = _this$props2.onEscPress,
tabIndex = _this$props2.tabIndex,
ariaLabel = _this$props2['aria-label'],
ariaLabelledby = _this$props2['aria-labelledby'],
ariaDescribedBy = _this$props2['aria-describedby'],
timeout = _this$props2.timeout,
skin = _this$props2.skin;
var shouldAnimate = (0, _utils.shouldAnimatePopover)({
timeout
});
var modifiers = (0, _modifiers.createModifiers)({
minWidth,
width,
dynamicWidth,
moveBy,
appendTo,
shouldAnimate,
flip,
flipModifier,
placement,
fixed,
isTestEnv
});
var popperWithArrow = /*#__PURE__*/_react.default.createElement(_reactPopper.Popper, {
modifiers: modifiers,
placement: placement,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 172,
columnNumber: 7
}
}, _ref => {
var ref = _ref.ref,
popperStyles = _ref.style,
popperPlacement = _ref.placement,
arrowProps = _ref.arrowProps,
scheduleUpdate = _ref.scheduleUpdate;
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: (0, _mergeRefs.mergeRefs)(ref, this.transitionRef),
style: _objectSpread(_objectSpread({}, popperStyles), {}, {
zIndex
}),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 183,
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,
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: 200,
columnNumber: 15
}
}, childrenObject.Content));
});
var popper = /*#__PURE__*/_react.default.createElement(_reactPopper.Popper, {
modifiers: modifiers,
placement: placement,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 225,
columnNumber: 7
}
}, _ref2 => {
var ref = _ref2.ref,
popperStyles = _ref2.style,
popperPlacement = _ref2.placement,
scheduleUpdate = _ref2.scheduleUpdate;
this.popperScheduleUpdate = scheduleUpdate;
return /*#__PURE__*/_react.default.createElement("div", {
id: id,
ref: (0, _mergeRefs.mergeRefs)(ref, this.popoverContentRef, this.transitionRef),
role: role,
tabIndex: tabIndex,
className: (0, _PopoverSt.st)(_PopoverSt.classes.content, {
skin,
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: 234,
columnNumber: 13
}
}, childrenObject.Content);
});
return this.wrapWithAnimations(showArrow ? popperWithArrow : popper);
}
applyStylesToPortaledNode() {
var shown = this.state.shown;
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.timeout;
var shown = this.state.shown;
var shouldAnimate = (0, _utils.shouldAnimatePopover)(this.props);
return shouldAnimate ? /*#__PURE__*/_react.default.createElement(_reactTransitionGroup.CSSTransition, {
nodeRef: this.transitionRef,
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: 284,
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: 309,
columnNumber: 7
}
}, popper) : popper;
}
renderArrow(arrowProps, moveArrowTo, placement, customArrow) {
var skin = this.props.skin;
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,
placement
}),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 337,
columnNumber: 7
}
}));
}
componentDidMount() {
var _this$props3 = this.props,
shown = _this$props3.shown,
onTabOut = _this$props3.onTabOut,
autoUpdateOptions = _this$props3.autoUpdateOptions;
this.appendPortalToNode();
if (onTabOut && shown) {
this._setBlurByKeyboardListener();
}
if (autoUpdateOptions != null && autoUpdateOptions.animationFrame && shown) {
this._startAnimationFrameLoop();
}
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);
}
}
appendPortalToNode() {
var appendTo = this.props.appendTo;
var _ref3 = this.context,
contextClassName = _ref3.contextClassName;
this.appendToNode = (0, _getAppendToElement.getAppendToElement)(appendTo, this.targetRef);
if (this.appendToNode) {
var _this$portalNode;
(_this$portalNode = this.portalNode) == null || _this$portalNode.remove();
this.portalNode = document.createElement('div');
this.portalNode.setAttribute('data-hook', 'popover-portal');
if (contextClassName) {
this.portalNode.classList.add(contextClassName);
}
/**
* 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.isMounted;
var _this$props4 = this.props,
hideDelay = _this$props4.hideDelay,
onTabOut = _this$props4.onTabOut,
onHide = _this$props4.onHide;
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.isMounted;
var _this$props5 = this.props,
showDelay = _this$props5.showDelay,
onTabOut = _this$props5.onTabOut,
onShow = _this$props5.onShow;
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) {
this.appendToNode.removeChild(this.portalNode);
this.portalNode.remove();
}
this.portalNode = null;
if (this._hideTimeout) {
clearTimeout(this._hideTimeout);
this._hideTimeout = null;
}
if (this._showTimeout) {
clearTimeout(this._showTimeout);
this._showTimeout = null;
}
this._stopAnimationFrameLoop();
}
updatePosition() {
if (this.popperScheduleUpdate) {
this.popperScheduleUpdate();
}
}
_startAnimationFrameLoop() {
if (this._animationFrameId !== null) {
return;
}
var tick = () => {
this.updatePosition();
this._animationFrameId = requestAnimationFrame(tick);
};
this._animationFrameId = requestAnimationFrame(tick);
}
_stopAnimationFrameLoop() {
if (this._animationFrameId !== null) {
cancelAnimationFrame(this._animationFrameId);
}
this._animationFrameId = null;
}
/**
* Starts or stops the rAF position-tracking loop based on
* `autoUpdateOptions.animationFrame` and `state.shown` (the internal
* visibility flag) — this keeps the loop running through `hideDelay`
* while the popover is still on screen, and stops it once the content
* is actually hidden.
*/
_syncAnimationFrameLoop(prevProps, prevState) {
var _this$props$autoUpdat, _prevProps$autoUpdate;
var shouldRun = !!((_this$props$autoUpdat = this.props.autoUpdateOptions) != null && _this$props$autoUpdat.animationFrame && this.state.shown);
var wasRunning = !!((_prevProps$autoUpdate = prevProps.autoUpdateOptions) != null && _prevProps$autoUpdate.animationFrame && prevState.shown);
if (shouldRun && !wasRunning) {
this._startAnimationFrameLoop();
} else if (!shouldRun && wasRunning) {
this._stopAnimationFrameLoop();
}
}
componentDidUpdate(prevProps, prevState) {
var _this$props6 = this.props,
skin = _this$props6.skin,
className = _this$props6.className,
shown = _this$props6.shown;
// allows reactive appendTo
if (prevProps.appendTo !== this.props.appendTo) {
this.appendPortalToNode();
}
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();
}
this._syncAnimationFrameLoop(prevProps, prevState);
}
render() {
var _this$props7 = this.props,
onMouseEnter = _this$props7.onMouseEnter,
onMouseLeave = _this$props7.onMouseLeave,
onKeyDown = _this$props7.onKeyDown,
onClick = _this$props7.onClick,
children = _this$props7.children,
className = _this$props7.className,
style = _this$props7.style,
fluid = _this$props7.fluid,
skin = _this$props7.skin,
dataHook = _this$props7.dataHook,
zIndex = _this$props7.zIndex,
excludeClass = _this$props7.excludeClass,
interactive = _this$props7.interactive;
var _this$state = this.state,
isMounted = _this$state.isMounted,
shown = _this$state.shown;
var childrenObject = (0, _utils.buildChildrenObject)(children, {
Element: null,
Content: null
});
var shouldAnimate = (0, _utils.shouldAnimatePopover)(this.props);
var shouldRenderPopper = isMounted && (shouldAnimate || shown);
return /*#__PURE__*/_react.default.createElement(_reactPopper.Manager, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 598,
columnNumber: 7
}
}, /*#__PURE__*/_react.default.createElement(_ClickOutside.ClickOutside, {
rootRef: this.clickOutsideRef,
onClickOutside: this._handleClickOutside,
excludeClass: excludeClass ? [this.clickOutsideClass, excludeClass] : this.clickOutsideClass,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 599,
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),
"data-zindex": zIndex,
onMouseEnter: interactive ? onMouseEnter : undefined,
onMouseLeave: interactive ? onMouseLeave : undefined
}, (0, _filterDataProps.filterDataProps)(this.props), {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 608,
columnNumber: 11
}
}), /*#__PURE__*/_react.default.createElement(_reactPopper.Reference, {
innerRef: r => this.targetRef = r,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 619,
columnNumber: 13
}
}, _ref4 => {
var ref = _ref4.ref;
return /*#__PURE__*/_react.default.createElement("div", {
ref: ref,
className: _PopoverSt.classes.element,
"data-hook": "popover-element",
onClick: onClick,
onKeyDown: onKeyDown,
onMouseEnter: interactive ? undefined : onMouseEnter,
onMouseLeave: interactive ? undefined : onMouseLeave,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 621,
columnNumber: 17
}
}, childrenObject.Element);
}), shouldRenderPopper && this.renderPopperContent(childrenObject))));
}
}
exports.PopoverCore = PopoverCore;
PopoverCore.displayName = 'Popover';
PopoverCore.defaultProps = {
flip: true,
fixed: false,
zIndex: _ZIndex.ZIndex.popover,
shown: false,
placement: 'bottom',
excludeClass: '',
interactive: true
};
PopoverCore.contextType = _context.WixDesignSystemContext;
PopoverCore.Element = (0, _utils.createComponentThatRendersItsChildren)('Popover.Element');
PopoverCore.Content = (0, _utils.createComponentThatRendersItsChildren)('Popover.Content');
//# sourceMappingURL=PopoverCore.js.map