@helpscout/hsds-react
Version:
React component library for Help Scout's Design System
230 lines (175 loc) • 7.59 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = exports.ChatScroller = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose"));
var _react = _interopRequireDefault(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _reactDom = _interopRequireDefault(require("react-dom"));
var _getValidProps = _interopRequireDefault(require("@helpscout/react-utils/dist/getValidProps"));
var _getDocumentFromComponent = _interopRequireDefault(require("@helpscout/react-utils/dist/getDocumentFromComponent"));
var _getShallowDiffs2 = _interopRequireDefault(require("@helpscout/react-utils/dist/getShallowDiffs"));
var _smoothScroll = require("../../utilities/smoothScroll");
var _ChatScroller = require("./ChatScroller.utils");
var ChatScroller = /*#__PURE__*/function (_React$PureComponent) {
(0, _inheritsLoose2.default)(ChatScroller, _React$PureComponent);
function ChatScroller() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _React$PureComponent.call.apply(_React$PureComponent, [this].concat(args)) || this;
_this.childRef = void 0;
_this.document = null;
_this.node = void 0;
_this.scrollableNode = void 0;
_this.setChildNodeRef = function (ref) {
return _this.childRef = ref;
};
return _this;
}
var _proto = ChatScroller.prototype;
_proto.componentDidMount = function componentDidMount() {
this.setNodes();
this.forceScrollToBottom();
};
_proto.componentDidUpdate = function componentDidUpdate(prevProps) {
this.resetNodes(prevProps);
if (this.shouldScrollOnUpdate(prevProps)) {
this.autoScrollToLatestMessage();
}
if (prevProps.forceScrollToBottomProp !== this.props.forceScrollToBottomProp) {
this.forceScrollToBottom();
}
};
_proto.resetNodes = function resetNodes(nextProps) {
var _getShallowDiffs = (0, _getShallowDiffs2.default)(nextProps, this.props),
diffs = _getShallowDiffs.diffs;
if (!diffs.length) return;
var matches = ['scrollableSelector', 'scrollableNode'];
if (diffs.some(function (item) {
return matches.includes(item);
})) {
this.setNodes();
}
};
_proto.shouldScrollOnUpdate = function shouldScrollOnUpdate(prevProps) {
var _this2 = this;
var hasChanged = this.props.propsToCheck.reduce(function (result, prop) {
if (result) return result;
return prevProps[prop] !== _this2.props[prop];
}, false);
return hasChanged;
};
_proto.getLatestMessageNode = function getLatestMessageNode() {
if (!this.scrollableNode) return;
var messageChatNodes = this.scrollableNode.querySelectorAll(this.props.messageSelectors);
return messageChatNodes[messageChatNodes.length - 1];
};
_proto.autoScrollToLatestMessage = function autoScrollToLatestMessage() {
var _this$props = this.props,
distanceForAutoScroll = _this$props.distanceForAutoScroll,
offsetThreshold = _this$props.offsetThreshold,
smoothScrollDuration = _this$props.smoothScrollDuration;
if (!this.scrollableNode) return;
var messageNode = this.getLatestMessageNode();
if (!messageNode) return;
/**
* Ignoring the following things as enzyme/JSDOM does not provide a
* (sane) way to test out scrolling calculation/behaviour.
*/
var scrollProps = (0, _ChatScroller.getScrollProps)({
distanceForAutoScroll: distanceForAutoScroll,
messageNode: messageNode,
offsetThreshold: offsetThreshold,
scrollableNode: this.scrollableNode
});
if ((0, _ChatScroller.shouldAutoScroll)(scrollProps)) {
this.handleScroll({
duration: smoothScrollDuration,
node: this.scrollableNode,
position: scrollProps.position
});
}
};
_proto.forceScrollToBottom = function forceScrollToBottom() {
if (!this.scrollableNode) return;
this.handleScroll({
duration: this.props.smoothScrollDuration,
node: this.scrollableNode,
position: this.scrollableNode.scrollHeight
});
};
_proto.handleScroll = function handleScroll(scrollProps) {
if (!this.scrollableNode) return;
(0, _smoothScroll.smoothScrollTo)(scrollProps);
this.props.onScroll({
target: this.scrollableNode
});
};
_proto.setNodes = function setNodes() {
this.node = _reactDom.default.findDOMNode(this.childRef);
this.document = (0, _getDocumentFromComponent.default)(this.childRef) || document;
var innerNode = this.node && this.node.querySelector(this.props.scrollableSelector);
var outerNode = this.document.querySelector(this.props.scrollableSelector);
this.scrollableNode = this.props.scrollableNode || innerNode || outerNode;
};
_proto.render = function render() {
var _this$props2 = this.props,
children = _this$props2.children,
rest = (0, _objectWithoutPropertiesLoose2.default)(_this$props2, ["children"]);
if (!children) return null;
var child = _react.default.Children.only(children);
return /*#__PURE__*/_react.default.cloneElement(child, (0, _extends2.default)({
ref: this.setChildNodeRef
}, (0, _extends2.default)({}, (0, _getValidProps.default)(rest))));
};
return ChatScroller;
}(_react.default.PureComponent);
exports.ChatScroller = ChatScroller;
function noop() {}
ChatScroller.defaultProps = {
'data-cy': 'ChatScroller',
distanceForAutoScroll: 150,
isTyping: false,
messageSelectors: '.c-MessageChat, .c-MessageAction',
offsetThreshold: 0.3,
onScroll: noop,
propsToCheck: ['messages', 'lastMessageId', 'isTyping'],
scrollableSelector: '.c-ScrollableNode',
smoothScrollDuration: 100
};
ChatScroller.propTypes = {
/** Custom class names to be added to the component. */
className: _propTypes.default.string,
/** A range to enable auto-scrolling. */
distanceForAutoScroll: _propTypes.default.number,
/** Update this prop to force scroll to bottom. */
forceScrollToBottomProp: _propTypes.default.any,
/** A chat-based event used to trigger auto-scrolling. */
isTyping: _propTypes.default.bool,
/** Chat data used to trigger auto-scrolling. */
lastMessageId: _propTypes.default.string,
/** Chat data used to trigger auto-scrolling. */
messages: _propTypes.default.arrayOf(_propTypes.default.any),
/** DOM selector(s) for chat message elements. */
messageSelectors: _propTypes.default.string,
offsetThreshold: _propTypes.default.number,
/** Callback function when component is scrolled. */
onScroll: _propTypes.default.func,
/** A collection of props to check to initiate the scroll. */
propsToCheck: _propTypes.default.arrayOf(_propTypes.default.string),
/** Duration (ms) for smooth scrolling. */
smoothScrollDuration: _propTypes.default.number,
/** DOM Element to check for scrolling. */
scrollableNode: _propTypes.default.any,
/** DOM selector for the scrollable message container. */
scrollableSelector: _propTypes.default.string,
scrollCondition: _propTypes.default.func,
/** Data attr for Cypress tests. */
'data-cy': _propTypes.default.string
};
var _default = ChatScroller;
exports.default = _default;