UNPKG

react-native-big-list

Version:

High-performance, virtualized list for React Native. Efficiently renders large datasets with recycler API for smooth scrolling and low memory usage. Ideal for fast, scalable, customizable lists on Android, iOS, and web.

405 lines (386 loc) 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _BigListItem = require("./BigListItem"); var _BigListItemRecycler = _interopRequireDefault(require("./BigListItemRecycler")); var _utils = require("./utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class BigListProcessor { /** * Constructor. * @param {ScrollView} scrollView * @param {array[]|object|null|undefined} sections * @param {number|function|null|undefined} headerHeight * @param {number|function|null|undefined} footerHeight * @param {number|function|null|undefined} sectionHeaderHeight * @param {number|function|null|undefined} itemHeight * @param {number|function|null|undefined} sectionFooterHeight * @param {number|function|null|undefined} insetTop * @param {number|function|null|undefined} insetBottom * @param {number|null|undefined} numColumns */ constructor(_ref) { let { scrollView, sections, headerHeight, footerHeight, sectionHeaderHeight, itemHeight, sectionFooterHeight, insetTop, insetBottom, numColumns } = _ref; this.headerHeight = headerHeight; this.footerHeight = footerHeight; this.sectionHeaderHeight = sectionHeaderHeight; this.itemHeight = itemHeight; this.sectionFooterHeight = sectionFooterHeight; this.sections = sections; this.insetTop = insetTop; this.insetBottom = insetBottom; this.uniform = (0, _utils.isNumeric)(itemHeight); this.scrollView = scrollView; this.numColumns = numColumns; } /** * Get item height. * @returns {number|*} */ getItemHeight(section, index) { const { itemHeight } = this; return (0, _utils.isNumeric)(itemHeight) ? Number(itemHeight) : itemHeight(section, index); } /** * Get header height. * @returns {number|*} */ getHeaderHeight() { const { headerHeight } = this; return (0, _utils.isNumeric)(headerHeight) ? Number(headerHeight) : headerHeight(); } /** * Get footer height. * @returns {number|*} */ getFooterHeight() { const { footerHeight } = this; return (0, _utils.isNumeric)(footerHeight) ? Number(footerHeight) : footerHeight(); } /** * Get section height. * @returns {number|*} */ getSectionHeaderHeight(section) { const { sectionHeaderHeight } = this; return (0, _utils.isNumeric)(sectionHeaderHeight) ? Number(sectionHeaderHeight) : sectionHeaderHeight(section); } /** * Get section footer height. * @returns {number|*} */ getSectionFooterHeight(section) { const { sectionFooterHeight } = this; return (0, _utils.isNumeric)(sectionFooterHeight) ? Number(sectionFooterHeight) : sectionFooterHeight(section); } /** * Process list items. * @param {number} top * @param {number} bottom * @param {array} prevItems * @returns {{items: [], height: *}} */ process(top, bottom, prevItems) { var _this = this; const { sections } = this; const items = []; const recycler = new _BigListItemRecycler.default(prevItems); let position; let counter = -1; // Counter of items per row pushed let height = this.insetTop; let spacerHeight = height; /** * The width of the row is the entire line. * @param {object} item * @returns {boolean} */ const isFullRow = item => { // Only items can be rendered with column format, so all others are full row return item.type !== _BigListItem.BigListItemType.ITEM; }; /** * Is visible below. * @param {object} item * @returns {boolean} */ const isVisibleBelow = item => { const { height: itemHeight } = item; counter = -1; if (height > bottom) { spacerHeight += itemHeight; return false; } else { return true; } }; /** * Is the item visible. * @param {object} item * @param {bool} force * @returns {boolean} */ const isVisible = function (item) { let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; // Check section headers visibility below if (item.type === _BigListItem.BigListItemType.SECTION_HEADER) { return isVisibleBelow(item); } // Dimensions const { height: itemHeight } = item; const fullRow = isFullRow(item); const prevHeight = height; // Increase or reset counter counter = fullRow ? -1 : counter + 1; if (fullRow || counter % _this.numColumns === 0) { height += itemHeight; } // Check if is visible if (force || height > top && prevHeight < bottom) { return true; } else { if (fullRow || counter % _this.numColumns === 0) { spacerHeight += itemHeight; } return false; } }; /** * Get recycled views and push items. * @param {object} itemsArray */ const push = function () { for (var _len = arguments.length, itemsArray = new Array(_len), _key = 0; _key < _len; _key++) { itemsArray[_key] = arguments[_key]; } itemsArray.forEach(item => { items.push(recycler.get(item)); }); }; /** * Push spacer. * @param {object} item */ const pushSpacer = item => { if (spacerHeight > 0) { push({ type: _BigListItem.BigListItemType.SPACER, position: item.position - spacerHeight, height: spacerHeight, section: item.section, index: item.index }); spacerHeight = 0; } }; /** * Push the item when is visible. * @param {object} item * @param {bool} force */ const pushItem = function (item) { let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (isVisible(item, force)) { pushSpacer(item); push(item); } }; /** * Calculate spacer height. */ const getSpacerHeight = () => { let itemsCounter = -1; return items.reduce((totalHeight, item, i) => { if (i !== items.length - 1) { const fullRow = isFullRow(item); itemsCounter = fullRow ? 0 : itemsCounter + 1; if (fullRow || itemsCounter % this.numColumns === 0) { return totalHeight + item.height; } } return totalHeight; }, 0); }; // Header const headerHeight = this.getHeaderHeight(); if (headerHeight > 0) { position = height; pushItem({ type: _BigListItem.BigListItemType.HEADER, position: position, height: headerHeight }, true); } // Sections for (let section = 0; section < sections.length; section++) { const rows = sections[section]; if (rows === 0) { continue; } // Section Header const sectionHeaderHeight = this.getSectionHeaderHeight(section); position = height; height += sectionHeaderHeight; if (section > 1 && items.length > 0 && items[items.length - 1].type === _BigListItem.BigListItemType.SECTION_HEADER) { // Top Spacer const initialSpacerHeight = getSpacerHeight(); const prevSection = items[items.length - 1]; items.splice(0, items.length); push({ type: _BigListItem.BigListItemType.HEADER, position: position, height: headerHeight }, { type: _BigListItem.BigListItemType.SPACER, position: 0, height: initialSpacerHeight - headerHeight, section: prevSection.section, index: 0 }, prevSection); } pushItem({ type: _BigListItem.BigListItemType.SECTION_HEADER, position: position, height: sectionHeaderHeight, section: section }); // Items let itemHeight = this.getItemHeight(section); for (let index = 0; index < rows; index++) { if (!this.uniform) { itemHeight = this.getItemHeight(section, index); } position = height; pushItem({ type: _BigListItem.BigListItemType.ITEM, position: position, height: itemHeight, section: section, index: index }); } // Section Footer const sectionFooterHeight = this.getSectionFooterHeight(section); if (sectionFooterHeight > 0) { position = height; pushItem({ type: _BigListItem.BigListItemType.SECTION_FOOTER, position: position, height: sectionFooterHeight, section: section }); } } // Footer const footerHeight = this.getFooterHeight(); if (footerHeight > 0) { position = height; pushItem({ type: _BigListItem.BigListItemType.FOOTER, position: position, height: footerHeight }, true); } // Bottom Spacer height += this.insetBottom; spacerHeight += this.insetBottom; if (spacerHeight > 0) { push({ type: _BigListItem.BigListItemType.SPACER, position: height - spacerHeight, height: spacerHeight, section: sections.length }); } recycler.fill(); return { height, items }; } /** * Scroll to position. * @param {int} targetSection * @param {int} targetIndex * @param {boolean} animated */ scrollToPosition(targetSection, targetIndex, animated) { const { sections, insetTop } = this; // Header + inset let scrollTop = insetTop + this.getHeaderHeight(); let section = 0; let foundIndex = false; while (section <= targetSection) { const rows = Math.ceil(sections[section] / this.numColumns); if (rows === 0) { section += 1; continue; } // Section header scrollTop += this.getSectionHeaderHeight(section); // Items if (this.uniform) { const uniformHeight = this.getItemHeight(section); if (section === targetSection) { scrollTop += uniformHeight * Math.ceil(targetIndex / this.numColumns); foundIndex = true; } else { scrollTop += uniformHeight * rows; } } else { for (let index = 0; index < rows; index++) { if (section < targetSection || section === targetSection && index < targetIndex) { scrollTop += this.getItemHeight(section, Math.ceil(index / this.numColumns)); } else if (section === targetSection && index === targetIndex) { foundIndex = true; break; } } } // Section footer if (!foundIndex) { scrollTop += this.getSectionFooterHeight(section); } section += 1; } this.scrollView.scrollTo({ x: 0, y: Math.max(0, scrollTop - this.getSectionHeaderHeight(targetSection)), animated }); return true; } } exports.default = BigListProcessor; //# sourceMappingURL=BigListProcessor.js.map