@helpscout/hsds-react
Version:
React component library for Help Scout's Design System
400 lines (320 loc) • 13.7 kB
JavaScript
"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;