UNPKG

@wix/design-system

Version:

@wix/design-system

598 lines (594 loc) 18.4 kB
"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; this._handleClickOutside = event => { var { onClickOutside: onClickOutsideCallback, shown, disableClickOutsideWhenClosed = true } = 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 = event => { var { onTabOut } = this.props; 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; 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, skin } = this.props; var shouldAnimate = (0, _utils.shouldAnimatePopover)({ timeout }); var modifiers = (0, _modifiers.createModifiers)({ minWidth, width, dynamicWidth, moveBy, appendTo, shouldAnimate, flip, placement, fixed, isTestEnv }); var popperWithArrow = /*#__PURE__*/_react.default.createElement(_reactPopper.Popper, { modifiers: modifiers, placement: placement, __self: this, __source: { fileName: _jsxFileName, lineNumber: 167, 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: (0, _mergeRefs.mergeRefs)(ref, this.transitionRef), style: _objectSpread(_objectSpread({}, popperStyles), {}, { zIndex }), __self: this, __source: { fileName: _jsxFileName, lineNumber: 178, 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: 195, columnNumber: 15 } }, childrenObject.Content)); }); var popper = /*#__PURE__*/_react.default.createElement(_reactPopper.Popper, { modifiers: modifiers, placement: placement, __self: this, __source: { fileName: _jsxFileName, lineNumber: 220, columnNumber: 7 } }, _ref2 => { var { ref, style: popperStyles, placement: popperPlacement, scheduleUpdate } = _ref2; 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: 229, 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, { 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: 279, 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: 304, columnNumber: 7 } }, popper) : popper; } renderArrow(arrowProps, moveArrowTo, placement, customArrow) { var { skin } = 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, placement }), __self: this, __source: { fileName: _jsxFileName, lineNumber: 332, columnNumber: 7 } })); } componentDidMount() { var { shown, onTabOut } = this.props; this.appendPortalToNode(); 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); } } appendPortalToNode() { var { appendTo } = this.props; 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'); /** * 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) { 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; } } updatePosition() { if (this.popperScheduleUpdate) { this.popperScheduleUpdate(); } } componentDidUpdate(prevProps) { var { skin, className, shown } = this.props; // 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(); } } render() { var { onMouseEnter, onMouseLeave, onKeyDown, onClick, children, className, style, fluid, skin, dataHook, zIndex, excludeClass, interactive } = 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__*/_react.default.createElement(_reactPopper.Manager, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 540, 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: 541, 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: 550, columnNumber: 11 } }), /*#__PURE__*/_react.default.createElement(_reactPopper.Reference, { innerRef: r => this.targetRef = r, __self: this, __source: { fileName: _jsxFileName, lineNumber: 561, 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, onMouseEnter: interactive ? undefined : onMouseEnter, onMouseLeave: interactive ? undefined : onMouseLeave, __self: this, __source: { fileName: _jsxFileName, lineNumber: 563, 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