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