UNPKG

wix-style-react

Version:
608 lines (605 loc) • 18.5 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("../../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