UNPKG

@helpscout/hsds-react

Version:

React component library for Help Scout's Design System

283 lines (222 loc) 8.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = 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 _getValidProps = _interopRequireDefault(require("@helpscout/react-utils/dist/getValidProps")); var _classnames = _interopRequireDefault(require("classnames")); var _Collapsible = require("./Collapsible.css"); var _jsxRuntime = require("react/jsx-runtime"); var Collapsible = /*#__PURE__*/function (_React$Component) { (0, _inheritsLoose2.default)(Collapsible, _React$Component); function Collapsible() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this; _this.state = { height: 0, animationState: 'idle' }; _this._isMounted = false; _this.node = void 0; _this.heightNode = void 0; _this.shouldFireStateCallback = function (prevProps, prevState) { return prevProps.isOpen !== _this.props.isOpen && prevState.animationState !== _this.state.animationState; }; _this.setNodeRef = function (node) { return _this.node = node; }; _this.setHeightNodeRef = function (node) { return _this.heightNode = node; }; return _this; } var _proto = Collapsible.prototype; _proto.componentDidMount = function componentDidMount() { this._isMounted = true; }; _proto.componentWillUnmount = function componentWillUnmount() { this._isMounted = false; }; _proto.UNSAFE_componentWillReceiveProps = function UNSAFE_componentWillReceiveProps(nextProps) { var willOpen = nextProps.isOpen; if (willOpen !== this.props.isOpen) { this.safeSetState({ animationState: 'measuring' }); } }; _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) { var wasOpen = prevProps.isOpen; this.handleAnimation(wasOpen); if (this.shouldFireStateCallback(prevProps, prevState)) { this.handleAnimationStateCallback(); } }; _proto.safeSetState = function safeSetState(state) { if (this._isMounted) { this.setState(state); } }; _proto.handleAnimation = function handleAnimation(wasOpen) { var _this2 = this; var animationState = this.state.animationState; var duration = this.props.duration; requestAnimationFrame(function () { switch (animationState) { case 'measuring': _this2.safeSetState({ animationState: wasOpen ? 'closingStart' : 'openingStart', height: wasOpen && _this2.heightNode ? _this2.heightNode.scrollHeight : 0 }); break; case 'closingStart': _this2.safeSetState({ animationState: 'closing', height: 0 }); break; /* Reliable to test in JSDOM due to timeouts */ case 'closing': setTimeout(function () { _this2.safeSetState({ animationState: 'closed' }); }, duration); break; case 'openingStart': _this2.safeSetState({ animationState: 'opening', height: _this2.heightNode ? _this2.heightNode.scrollHeight : 0 }); break; /* Reliable to test in JSDOM due to timeouts */ case 'opening': setTimeout(function () { _this2.safeSetState({ animationState: 'opened' }); }, duration); break; default: break; } }); }; _proto.handleAnimationStateCallback = function handleAnimationStateCallback() { var animationState = this.state.animationState; var _this$props = this.props, onOpen = _this$props.onOpen, onClose = _this$props.onClose; switch (animationState) { case 'opened': onOpen(); break; case 'closed': onClose(); break; default: break; } }; _proto.collapsibleHeight = function collapsibleHeight(isOpen, animationState, height) { if (animationState === 'idle' && isOpen) { return isOpen ? 'auto' : null; } if (animationState === 'measuring') { return isOpen ? null : 'auto'; } if (animationState === 'opened') { return 'auto'; } return (height || 0) + "px"; }; _proto.getTransitionDuration = function getTransitionDuration() { var _this$props2 = this.props, duration = _this$props2.duration, durationOpen = _this$props2.durationOpen, durationClose = _this$props2.durationClose; var animationState = this.state.animationState; var openDuration = durationOpen !== undefined ? durationOpen : duration; var closeDuration = durationClose !== undefined ? durationClose : duration; var isOpening = animationState.indexOf('closing') < 0; return isOpening ? openDuration : closeDuration; }; _proto.render = function render() { var _this$props3 = this.props, className = _this$props3.className, children = _this$props3.children, duration = _this$props3.duration, durationOpen = _this$props3.durationOpen, durationClose = _this$props3.durationClose, isOpen = _this$props3.isOpen, onOpen = _this$props3.onOpen, onClose = _this$props3.onClose, style = _this$props3.style, preRenderContent = _this$props3.preRenderContent, rest = (0, _objectWithoutPropertiesLoose2.default)(_this$props3, ["className", "children", "duration", "durationOpen", "durationClose", "isOpen", "onOpen", "onClose", "style", "preRenderContent"]); var _this$state = this.state, animationState = _this$state.animationState, height = _this$state.height; var animating = animationState !== 'idle'; var closed = animationState === 'closed'; var componentClassName = (0, _classnames.default)('c-Collapsible', isOpen && 'is-open', animating && 'is-animating', closed && 'is-closed', className); var displayHeight = this.collapsibleHeight(isOpen, animationState, height); var content = animating || isOpen || preRenderContent ? children : null; var collapseStyle = { height: displayHeight, transitionDuration: this.getTransitionDuration() + "ms" }; var componentStyle = (0, _extends2.default)({}, style, collapseStyle); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Collapsible.CollapsibleUI, (0, _extends2.default)({}, (0, _getValidProps.default)(rest), { "aria-hidden": !isOpen, style: componentStyle, className: componentClassName, ref: this.setNodeRef, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { ref: this.setHeightNodeRef, children: content }) })); }; return Collapsible; }(_react.default.Component); function noop() {} Collapsible.defaultProps = { 'data-cy': 'Collapsible', duration: 300, isOpen: false, onOpen: noop, onClose: noop, preRenderContent: false }; Collapsible.propTypes = { /** Custom class names to be added to the component. */ className: _propTypes.default.string, /** Time (ms) for the expand/collapse animation. */ duration: _propTypes.default.number, /** Time (ms) for the expand animation. */ durationOpen: _propTypes.default.number, /** Time (ms) for the collapse animation. */ durationClose: _propTypes.default.number, /** Opens/collapses the component. */ isOpen: _propTypes.default.bool, /** Callback function when the component closes. */ onClose: _propTypes.default.func, /** Callback function when the component opens. */ onOpen: _propTypes.default.func, /** Custom styles to be added to the component. */ style: _propTypes.default.any, /** Data attr for Cypress tests. */ 'data-cy': _propTypes.default.string, /** Flag indicating if content should be pre-rendered to the DOM (otherwise the content would not be in the DOM until opened) */ preRenderContent: _propTypes.default.bool }; var _default = Collapsible; exports.default = _default;