UNPKG

@helpscout/hsds-react

Version:

React component library for Help Scout's Design System

400 lines (320 loc) • 13.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _reactDom = _interopRequireDefault(require("react-dom")); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = _interopRequireDefault(require("lodash.isfunction")); var _lodash2 = _interopRequireDefault(require("lodash.isnil")); var _getComponentDefaultProp = _interopRequireDefault(require("@helpscout/react-utils/dist/getComponentDefaultProp")); var _hoistNonReactStatics = _interopRequireDefault(require("@helpscout/react-utils/dist/hoistNonReactStatics")); var _getComponentName = _interopRequireDefault(require("@helpscout/react-utils/dist/getComponentName")); var _Animate = _interopRequireDefault(require("../Animate")); var _KeypressListener = _interopRequireDefault(require("../KeypressListener")); var _Portal = _interopRequireDefault(require("../Portal")); var _Keys = _interopRequireDefault(require("../../constants/Keys")); var _id = require("../../utilities/id"); var _PortalWrapper = require("./PortalWrapper.utils"); var _PortalWrapper2 = _interopRequireDefault(require("./PortalWrapper.Content")); var _WithRouterCheck = _interopRequireDefault(require("../WithRouterCheck")); var _jsxRuntime = require("react/jsx-runtime"); function noop() {} var defaultOptions = { id: 'PortalWrapper', timeout: 100, alwaysCloseIfLast: true }; var managerNamespace = 'HSDSPortalWrapperGlobalManager'; var uniqueIndex = (0, _id.createUniqueIndexFactory)(1000); function shouldPreventClosing(_ref) { var preventEscActionElements = _ref.preventEscActionElements, target = _ref.target; if (target) { for (var index = 0; index < preventEscActionElements.length; index++) { if (target.classList.contains(preventEscActionElements[index])) { return true; } } } return false; } var PortalWrapper = function PortalWrapper(options) { if (options === void 0) { options = defaultOptions; } return function (ComposedComponent) { var extendedOptions = (0, _extends2.default)({}, defaultOptions, options); var uniqueID = (0, _id.createUniqueIDFactory)(extendedOptions.id); var PortalWrapper = /*#__PURE__*/function (_React$PureComponent) { (0, _inheritsLoose2.default)(PortalWrapper, _React$PureComponent); // Welcome aboard, Mr. Manager! // Wow, I'm Mr. Manager! // Well, manager… we we just say manager. function PortalWrapper(props, context) { var _this; _this = _React$PureComponent.call(this, props, context) || this; _this.triggerComponent = null; _this.triggerNode = null; _this._isMounted = false; _this._portalWrapperId = uniqueIndex(); _this._MrManager = (0, _PortalWrapper.setupManager)(managerNamespace); _this.openPortal = function () { _this.safeSetState({ isOpen: true }); }; _this.closePortal = function () { if (_this._MrManager.max() === _this._portalWrapperId) { _this.forceClosePortal(); } }; _this.forceClosePortal = function () { _this.safeSetState({ isOpen: false }); }; _this.handleOnEsc = function (event) { var preventEscActionElements = _this.props.preventEscActionElements; var target = event.target; if (_this.state.isOpen && !shouldPreventClosing({ target: target, preventEscActionElements: preventEscActionElements })) { event && event.stopPropagation(); _this.handleOnClose(); } }; _this.handleOnClose = function (onClose) { var onBeforeClose = _this.props.onBeforeClose; if ((0, _lodash.default)(onClose)) { if (onBeforeClose) { onBeforeClose(function () { return _this.sequenceClosePortal(onClose); }); } else { _this.sequenceClosePortal(onClose); } } else { _this.closePortal(); } }; var composedWrapperClassName = (0, _getComponentDefaultProp.default)(ComposedComponent, 'wrapperClassName'); var composedWrapperTimeout = (0, _getComponentDefaultProp.default)(ComposedComponent, 'timeout'); var timeout = props.timeout !== undefined ? props.timeout : composedWrapperTimeout !== undefined ? composedWrapperTimeout : extendedOptions.timeout; _this.state = { isOpen: props.isOpen, id: uniqueID(), timeout: timeout, renderContent: false, wrapperClassName: (0, _classnames.default)(props.wrapperClassName, composedWrapperClassName) }; return _this; } var _proto = PortalWrapper.prototype; _proto.getChildContext = function getChildContext() { return { closePortal: this.closePortal }; }; _proto.componentDidMount = function componentDidMount() { var _this$props = this.props, path = _this$props.path, delayedContentRender = _this$props.delayedContentRender; this._isMounted = true; this.setTriggerNode(); if (this.routeMatches(path)) { this.openPortal(); } if (this.state.isOpen && delayedContentRender) { this.safeSetState({ renderContent: true }); } }; _proto.UNSAFE_componentWillReceiveProps = function UNSAFE_componentWillReceiveProps(nextProps) { var isOpen = nextProps.isOpen, path = nextProps.path; if (this.routeMatches(path)) { return this.openPortal(); } if (isOpen === this.props.isOpen) return false; if (isOpen !== this.state.isOpen) { return isOpen ? this.openPortal() : this.forceClosePortal(); } }; _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState, snapshot) { var isOpen = this.state.isOpen; // Only refocus if closed and was previously opened if (!isOpen && prevState.isOpen) { this.refocusTriggerNode(); } if (isOpen !== prevState.isOpen && isOpen && this.props.delayedContentRender) { this.safeSetState({ renderContent: true }); } }; _proto.componentWillUnmount = function componentWillUnmount() { this._isMounted = false; this.triggerComponent = null; this.triggerNode = null; }; _proto.safeSetState = function safeSetState(state, callback) { if (this._isMounted) { this.setState(state, callback); } }; _proto.setTriggerNode = function setTriggerNode() { if (this.triggerComponent) { this.triggerNode = _reactDom.default.findDOMNode(this.triggerComponent); } }; _proto.refocusTriggerNode = function refocusTriggerNode() { if (this.triggerNode) { this.triggerNode.focus(); } }; _proto.routeMatches = function routeMatches(path) { var _this$props2 = this.props, exact = _this$props2.exact, history = _this$props2.history; if (!history) return false; if (path && history && history.location) { return !(0, _lodash2.default)((0, _PortalWrapper.matchPath)(history.location.pathname, { path: path, exact: exact })); } else { return false; } }; _proto.sequenceClosePortal = function sequenceClosePortal(onClose) {// requestAnimationFrame(() => onClose()) }; _proto.renderTrigger = function renderTrigger() { var _this2 = this; var trigger = this.props.trigger; var isValidTrigger = trigger && /*#__PURE__*/_react.default.isValidElement(trigger); if (!isValidTrigger) return null; var triggerOnClick = function triggerOnClick() { var onClick = trigger.props.onClick; if ((0, _lodash.default)(onClick)) { onClick.apply(void 0, arguments); } _this2.openPortal(); }; var triggerRef = function triggerRef(node) { var ref = trigger.ref; if ((0, _lodash.default)(ref)) { ref(node); } _this2.triggerComponent = node; }; return /*#__PURE__*/_react.default.cloneElement(trigger, { onClick: triggerOnClick, ref: triggerRef }); }; _proto.renderPortal = function renderPortal() { var _this$props3 = this.props, className = _this$props3.className, exact = _this$props3.exact, isOpenProps = _this$props3.isOpenProps, onBeforeClose = _this$props3.onBeforeClose, onBeforeOpen = _this$props3.onBeforeOpen, onClose = _this$props3.onClose, onOpen = _this$props3.onOpen, path = _this$props3.path, renderTo = _this$props3.renderTo, trigger = _this$props3.trigger, timeoutProp = _this$props3.timeout, propsWrapperClassName = _this$props3.wrapperClassName, closeOnEscape = _this$props3.closeOnEscape, rest = (0, _objectWithoutPropertiesLoose2.default)(_this$props3, ["className", "exact", "isOpenProps", "onBeforeClose", "onBeforeOpen", "onClose", "onOpen", "path", "renderTo", "trigger", "timeout", "wrapperClassName", "closeOnEscape"]); // Remapping open/mount state for ComposedComponent var _this$state = this.state, id = _this$state.id, portalIsOpen = _this$state.isOpen, timeout = _this$state.timeout, wrapperClassName = _this$state.wrapperClassName; var openPortal = this.openPortal; var handleOnClose = this.handleOnClose; var uniqueIndex = getUniqueIndex(id, options.id); var zIndex = !(0, _lodash2.default)(options.zIndex) ? options.zIndex + uniqueIndex : null; return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Animate.default, { animateOnMount: false, timeout: portalIsOpen ? timeout : undefined, in: portalIsOpen, unmountOnExit: true, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Portal.default, (0, _extends2.default)({ className: wrapperClassName, onClose: onClose, onOpen: onOpen, id: id, renderTo: renderTo, timeout: timeout }, rest, { children: (!this.props.delayedContentRender || this.state.renderContent) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_PortalWrapper2.default, { manager: this._MrManager, id: this._portalWrapperId, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ComposedComponent, (0, _extends2.default)({ className: className, openPortal: openPortal, closePortal: handleOnClose, onClose: onClose, portalIsOpen: portalIsOpen, portalIsMounted: portalIsOpen, forceClosePortal: this.forceClosePortal, trigger: trigger, zIndex: zIndex }, rest)) }) })) }); }; _proto.renderEventListener = function renderEventListener() { var closeOnEscape = this.props.closeOnEscape; return closeOnEscape ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_KeypressListener.default, { keyCode: _Keys.default.ESCAPE, handler: this.handleOnEsc, type: "keydown" }) : null; }; _proto.render = function render() { return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { className: "c-PortalWrapper", children: [this.renderEventListener(), this.renderTrigger(), this.renderPortal()] }); }; return PortalWrapper; }(_react.default.PureComponent); PortalWrapper.defaultProps = { closeOnEscape: true, isOpen: false, delayedContentRender: false, preventEscActionElements: extendedOptions.preventEscActionElements || [] }; PortalWrapper.childContextTypes = { closePortal: noop }; PortalWrapper.displayName = "withPortal(" + (0, _getComponentName.default)(ComposedComponent) + ")"; return (0, _hoistNonReactStatics.default)((0, _WithRouterCheck.default)(PortalWrapper), ComposedComponent); }; }; function getUniqueIndex(id, namespace) { return parseInt(id.replace(namespace, ''), 10); } PortalWrapper.Content = _PortalWrapper2.default; PortalWrapper.propTypes = Object.assign(_Portal.default.propTypes, { closeOnEscape: _propTypes.default.bool, isOpen: _propTypes.default.bool, trigger: _propTypes.default.any, isOpenProps: _propTypes.default.bool, preventEscActionElements: _propTypes.default.arrayOf(_propTypes.default.string), wrapperClassName: _propTypes.default.string, delayedContentRender: _propTypes.default.bool }); var _default = PortalWrapper; exports.default = _default;