UNPKG

@helpscout/hsds-react

Version:

React component library for Help Scout's Design System

254 lines (203 loc) 10.9 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 _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = _interopRequireDefault(require("lodash.isnil")); var _useScrollShadow2 = _interopRequireDefault(require("../../hooks/useScrollShadow")); var _useMeasureNode3 = _interopRequireDefault(require("../../hooks/useMeasureNode")); var _ScrollableContainer = require("./ScrollableContainer.css"); var _jsxRuntime = require("react/jsx-runtime"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function noop() {} function ScrollableContainer(_ref) { var body = _ref.body, className = _ref.className, _ref$dataCy = _ref['data-cy'], dataCy = _ref$dataCy === void 0 ? 'ScrollableContainer' : _ref$dataCy, _ref$drawInitialShado = _ref.drawInitialShadowsDelay, drawInitialShadowsDelay = _ref$drawInitialShado === void 0 ? 0 : _ref$drawInitialShado, footer = _ref.footer, forwardedRef = _ref.forwardedRef, header = _ref.header, _ref$height = _ref.height, height = _ref$height === void 0 ? '500px' : _ref$height, _onScroll = _ref.onScroll, _ref$onScrollableSect = _ref.onScrollableSectionsStateChange, onScrollableSectionsStateChange = _ref$onScrollableSect === void 0 ? noop : _ref$onScrollableSect, _ref$shadows = _ref.shadows, shadows = _ref$shadows === void 0 ? {} : _ref$shadows, _ref$width = _ref.width, width = _ref$width === void 0 ? '300px' : _ref$width, _ref$withResizeObserv = _ref.withResizeObservers, withResizeObservers = _ref$withResizeObserv === void 0 ? {} : _ref$withResizeObserv, withSimpleBar = _ref.withSimpleBar, rest = (0, _objectWithoutPropertiesLoose2.default)(_ref, ["body", "className", "data-cy", "drawInitialShadowsDelay", "footer", "forwardedRef", "header", "height", "onScroll", "onScrollableSectionsStateChange", "shadows", "width", "withResizeObservers", "withSimpleBar"]); var bodyRef = (0, _react.useRef)(null); var _useMeasureNode = (0, _useMeasureNode3.default)({ observeSize: Boolean(withResizeObservers.header) }), headerRect = _useMeasureNode[0], headerEl = _useMeasureNode[1], headerRef = _useMeasureNode[2], headerObserverRef = _useMeasureNode[3]; var _useMeasureNode2 = (0, _useMeasureNode3.default)({ observeSize: Boolean(withResizeObservers.footer) }), footerRect = _useMeasureNode2[0], footerEl = _useMeasureNode2[1], footerRef = _useMeasureNode2[2], footerObserverRef = _useMeasureNode2[3]; var _useScrollShadow = (0, _useScrollShadow2.default)({ bottomRef: footerEl, drawInitialShadowsDelay: drawInitialShadowsDelay, scrollableRef: bodyRef, shadows: shadows, topRef: headerEl, withSimpleBar: withSimpleBar }), handleOnScroll = _useScrollShadow[0], isTopScrolled = _useScrollShadow[1], isBottomScrolled = _useScrollShadow[2]; (0, _react.useEffect)(function () { onScrollableSectionsStateChange({ isTopScrolled: isTopScrolled, isBottomScrolled: isBottomScrolled }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isTopScrolled, isBottomScrolled]); // Cleanup observers if any (0, _react.useEffect)(function () { var footerObserver = footerObserverRef.current; var headerObserver = headerObserverRef.current; return function () { if (!(0, _lodash.default)(footerObserver) && footerObserver instanceof ResizeObserver) { footerObserver.disconnect(); } if (!(0, _lodash.default)(headerObserver) && headerObserver instanceof ResizeObserver) { headerObserver.disconnect(); } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); function renderSection(section) { var sectionContent; var SectionUI; var sectionRef; if (section === 'header') { sectionContent = header; SectionUI = _ScrollableContainer.HeaderUI; sectionRef = headerRef; } else if (section === 'footer') { sectionContent = footer; SectionUI = _ScrollableContainer.FooterUI; sectionRef = footerRef; } else if (section === 'body') { sectionContent = body; SectionUI = _ScrollableContainer.BodyUI; sectionRef = bodyRef; } if (sectionContent) { if (section === 'body' && withSimpleBar) { var _ref2 = body.props || {}, _children = _ref2.children, _className2 = _ref2.className, _rest2 = (0, _objectWithoutPropertiesLoose2.default)(_ref2, ["children", "className"]); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ScrollableContainer.SimpleBarUI, (0, _extends2.default)({ $height: calculateSimpleBarHeight(height, headerRect, footerRect), scrollableNodeProps: { ref: bodyRef, onScroll: function onScroll(e) { handleOnScroll(); _onScroll && _onScroll(e); } }, className: (0, _classnames.default)('ScrollableContainer__body', _className2) }, _rest2, { children: body })); } var _ref3 = sectionContent.props || {}, children = _ref3.children, _className = _ref3.className, _rest = (0, _objectWithoutPropertiesLoose2.default)(_ref3, ["children", "className"]); var component = /*#__PURE__*/_react.default.isValidElement(sectionContent) ? /*#__PURE__*/_react.default.cloneElement(sectionContent, (0, _extends2.default)({}, _rest, { ref: sectionRef })) : /*#__PURE__*/_react.default.createElement('div', { ref: sectionRef }, [sectionContent]); return /*#__PURE__*/(0, _jsxRuntime.jsx)(SectionUI, { className: (0, _classnames.default)("ScrollableContainer__" + section, _className), component: component, onScroll: section === 'body' ? function (e) { handleOnScroll(); _onScroll && _onScroll(e); } : null }); } return null; } return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ScrollableContainer.ContainerScrollUI, (0, _extends2.default)({ className: (0, _classnames.default)('c-ScrollableContainer', className), "data-cy": dataCy, $width: width, $height: height, ref: forwardedRef }, rest, { children: [renderSection('header'), renderSection('body'), renderSection('footer')] })); } function calculateSimpleBarHeight(height, headerRect, footerRect) { var otherSectionsHeight = 0; if (!(0, _lodash.default)(headerRect)) { otherSectionsHeight += headerRect.height; } if (!(0, _lodash.default)(footerRect)) { otherSectionsHeight += footerRect.height; } return "calc(" + height + " - " + otherSectionsHeight + "px)"; } var shadowShape = _propTypes.default.shape({ initial: _propTypes.default.string, scrolled: _propTypes.default.string }); var resizeObserversShape = _propTypes.default.shape({ header: _propTypes.default.bool, footer: _propTypes.default.bool }); ScrollableContainer.propTypes = { /** Custom class names to be added to the component. */ className: _propTypes.default.string, /** Data attr for Cypress tests. */ 'data-cy': _propTypes.default.string, /** An element or content to render as the body (content), this is the one that gets scrolled */ body: _propTypes.default.oneOfType([_propTypes.default.element, _propTypes.default.string]), /** If you're animating a component in, the scrollable element (body) might not have its height determined yet until that animation completes, pass a number in ms equal or larger to the length of the animation to account for this and give React time to get the size. */ drawInitialShadowsDelay: _propTypes.default.number, /** An element or content to render fixed at the bottom of the container */ footer: _propTypes.default.oneOfType([_propTypes.default.element, _propTypes.default.string]), /** An element or content to render fixed at the top of the container */ header: _propTypes.default.oneOfType([_propTypes.default.element, _propTypes.default.string]), /** The container height, can also override with a styled component instead */ height: _propTypes.default.string, /** Callback on the scroll event */ onScroll: _propTypes.default.func, /** Returns the scrolled state of the top and bottom sections when they change `{ isTopScrolled, isBottomScrolled }` */ onScrollableSectionsStateChange: _propTypes.default.func, /** Custom box-shadow values for the initial and scrolled states: `{ initial, scrolled }` */ shadows: shadowShape, /** The container width, can also override with a styled component instead */ width: _propTypes.default.string, /** If your header or footer change height dimensions on scroll, use `ResizeObserver` to have smooth transition, allows to set the observer in both sections or just one: `{ header: true, footer: true }` */ withResizeObservers: resizeObserversShape, /** If you want to use 'simplebar-react' in the body, turn this flag on */ withSimpleBar: _propTypes.default.bool }; var _default = /*#__PURE__*/_react.default.forwardRef(function (props, ref) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(ScrollableContainer, (0, _extends2.default)({ forwardedRef: ref }, props)); }); exports.default = _default;