grommet
Version:
focus on the essential experience
256 lines (202 loc) • 10.1 kB
JavaScript
;
exports.__esModule = true;
exports.InfiniteScroll = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactDom = require("react-dom");
var _utils = require("../../utils");
var _Box = require("../Box");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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 _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
var Ref = /*#__PURE__*/function (_Component) {
_inheritsLoose(Ref, _Component);
function Ref() {
return _Component.apply(this, arguments) || this;
}
var _proto = Ref.prototype;
_proto.render = function render() {
var children = this.props.children;
return children;
};
return Ref;
}(_react.Component);
var InfiniteScroll = function InfiniteScroll(_ref) {
var children = _ref.children,
_ref$items = _ref.items,
items = _ref$items === void 0 ? [] : _ref$items,
onMore = _ref.onMore,
renderMarker = _ref.renderMarker,
replace = _ref.replace,
show = _ref.show,
_ref$step = _ref.step,
step = _ref$step === void 0 ? 50 : _ref$step;
// the last page we have items for
var lastPage = (0, _react.useMemo)(function () {
return Math.floor(items.length / step);
}, [items.length, step]); // the first page we are displaying
var _useState = (0, _react.useState)(0),
beginPage = _useState[0],
setBeginPage = _useState[1]; // the last page we are displaying
var _useState2 = (0, _react.useState)(show ? Math.floor((show + step) / step) - 1 : 0),
endPage = _useState2[0],
setEndPage = _useState2[1]; // how tall we've measured a page to be
var _useState3 = (0, _react.useState)(),
pageHeight = _useState3[0],
setPageHeight = _useState3[1]; // how much area a page requires
var _useState4 = (0, _react.useState)(),
pageArea = _useState4[0],
setPageArea = _useState4[1]; // whether the items are laid out in a grid instead of linearly
var _useState5 = (0, _react.useState)(),
multiColumn = _useState5[0],
setMultiColumn = _useState5[1]; // what we're waiting for onMore to give us
var _useState6 = (0, _react.useState)(0),
pendingLength = _useState6[0],
setPendingLength = _useState6[1];
var belowMarkerRef = (0, _react.useRef)();
var firstPageItemRef = (0, _react.useRef)();
var lastPageItemRef = (0, _react.useRef)();
var showRef = (0, _react.useRef)(); // calculating space based on where the first and last items being displayed
// are located
(0, _react.useEffect)(function () {
if (firstPageItemRef.current && lastPageItemRef.current && !pageHeight) {
/* eslint-disable react/no-find-dom-node */
var beginRect = firstPageItemRef.current.getBoundingClientRect ? firstPageItemRef.current.getBoundingClientRect() : (0, _reactDom.findDOMNode)(firstPageItemRef.current).getBoundingClientRect();
var endRect = lastPageItemRef.current.getBoundingClientRect ? lastPageItemRef.current.getBoundingClientRect() : (0, _reactDom.findDOMNode)(lastPageItemRef.current).getBoundingClientRect();
var nextPageHeight = endRect.top + endRect.height - beginRect.top; // Check if the items are arranged in a single column or not.
var nextMultiColumn = nextPageHeight / step < endRect.height;
var nextPageArea = endRect.height * endRect.width * step;
setPageHeight(nextPageHeight);
setPageArea(nextPageArea);
setMultiColumn(nextMultiColumn);
}
}, [pageHeight, step]); // scroll handling
(0, _react.useEffect)(function () {
var scrollParents;
var onScroll = function onScroll() {
var scrollParent = scrollParents[0]; // Determine the window into the first scroll parent
var top;
var height;
var width;
if (scrollParent === document) {
top = document.documentElement.scrollTop || document.body.scrollTop;
height = window.innerHeight;
width = window.innerWidth;
} else {
top = scrollParent.scrollTop;
var rect = scrollParent.getBoundingClientRect();
height = rect.height;
width = rect.width;
} // Figure out which pages we should make visible based on the scroll
// window.
var offset = height / 4;
var nextBeginPage = replace ? Math.min(lastPage, Math.max(0, multiColumn ? Math.floor(Math.max(0, top - offset) * width / pageArea) : Math.floor(Math.max(0, top - offset) / pageHeight))) : 0;
var nextEndPage = Math.min(lastPage, Math.max(!replace && endPage || 0, multiColumn ? Math.ceil((top + height + offset) * width / pageArea) : Math.floor((top + height + offset) / pageHeight)));
if (nextBeginPage !== beginPage) setBeginPage(nextBeginPage);
if (nextEndPage !== endPage) setEndPage(nextEndPage);
};
if (pageHeight && belowMarkerRef.current) {
scrollParents = (0, _utils.findScrollParents)(belowMarkerRef.current);
scrollParents.forEach(function (scrollParent) {
return scrollParent.addEventListener('scroll', onScroll);
});
onScroll();
}
return function () {
if (scrollParents) {
scrollParents.forEach(function (scrollParent) {
return scrollParent.removeEventListener('scroll', onScroll);
});
}
};
}, [beginPage, endPage, lastPage, multiColumn, pageArea, pageHeight, replace]); // check if we need to ask for more
(0, _react.useEffect)(function () {
if (onMore && endPage === lastPage && items.length >= pendingLength) {
// remember we've asked for more, so we don't keep asking if it takes
// a while
setPendingLength(items.length + 1);
onMore();
}
}, [endPage, items.length, lastPage, onMore, pendingLength, step]); // scroll to any 'show'
(0, _react.useEffect)(function () {
// ride out any animation delays, 100ms empirically measured
var timer = setTimeout(function () {
if (show && showRef.current) {
var showNode = showRef.current.scrollIntoView ? showRef.current : (0, _reactDom.findDOMNode)(showRef.current);
var scrollParent = (0, _utils.findScrollParent)(showNode);
if ((0, _utils.isNodeBeforeScroll)(showNode, scrollParent)) {
showNode.scrollIntoView(true);
} else if ((0, _utils.isNodeAfterScroll)(showNode, scrollParent)) {
showNode.scrollIntoView(false);
}
}
}, 100);
return function () {
return clearTimeout(timer);
};
}, [show]);
var firstIndex = beginPage * step;
var lastIndex = Math.min((endPage + 1) * step, items.length) - 1;
var result = [];
if (replace && pageHeight && firstIndex) {
var marker = /*#__PURE__*/_react["default"].createElement(_Box.Box, {
key: "above",
flex: false,
height: beginPage * pageHeight + "px"
});
if (renderMarker) {
// need to give it a key
marker = /*#__PURE__*/_react["default"].cloneElement(renderMarker(marker), {
key: 'above'
});
}
result.push(marker);
}
items.slice(firstIndex, lastIndex + 1).forEach(function (item, index) {
var itemsIndex = firstIndex + index; // We only need page refs if we don't know the pageHeight
// The new way, we pass the ref we want to the children render function.
var ref;
if (!pageHeight && itemsIndex === 0) ref = firstPageItemRef;else if (!pageHeight && (itemsIndex === step - 1 || itemsIndex === lastIndex)) ref = lastPageItemRef;else if (show && show === itemsIndex) ref = showRef;
var child = children(item, itemsIndex, ref); // The old way, if we don't see that our ref was set, wrap it
if (!pageHeight && itemsIndex === 0 && child.ref !== firstPageItemRef) {
child = /*#__PURE__*/_react["default"].createElement(Ref, {
key: "first",
ref: firstPageItemRef
}, child);
} else if (!pageHeight && (itemsIndex === step - 1 || itemsIndex === lastIndex) && child.ref !== lastPageItemRef) {
child = /*#__PURE__*/_react["default"].createElement(Ref, {
key: "last",
ref: lastPageItemRef
}, child);
}
if (show && show === itemsIndex && child.ref !== showRef) {
child = /*#__PURE__*/_react["default"].createElement(Ref, {
key: "show",
ref: showRef
}, child);
}
result.push(child);
});
if (endPage < lastPage || replace || onMore) {
var _marker = /*#__PURE__*/_react["default"].createElement(_Box.Box, {
key: "below",
ref: belowMarkerRef,
flex: false,
height: (replace ? (lastPage - endPage) * pageHeight : 0) + "px"
});
if (renderMarker) {
// need to give it a key
_marker = /*#__PURE__*/_react["default"].cloneElement(renderMarker(_marker), {
key: 'below'
});
}
result.push(_marker);
}
return result;
};
var InfiniteScrollDoc;
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require
InfiniteScrollDoc = require('./doc').doc(InfiniteScroll);
}
var InfiniteScrollWrapper = InfiniteScrollDoc || InfiniteScroll;
exports.InfiniteScroll = InfiniteScrollWrapper;