UNPKG

@helpscout/hsds-react

Version:

React component library for Help Scout's Design System

230 lines (175 loc) 7.59 kB
"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;