UNPKG

@ag-grid-community/infinite-row-model

Version:

Advanced Data Grid / Data Table supporting Javascript / Typescript / React / Angular / Vue

656 lines (645 loc) 21.6 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // community-modules/infinite-row-model/src/main.ts var main_exports = {}; __export(main_exports, { InfiniteRowModelModule: () => InfiniteRowModelModule }); module.exports = __toCommonJS(main_exports); // community-modules/infinite-row-model/src/infiniteRowModelModule.ts var import_core4 = require("@ag-grid-community/core"); // community-modules/infinite-row-model/src/infiniteRowModel/infiniteRowModel.ts var import_core3 = require("@ag-grid-community/core"); // community-modules/infinite-row-model/src/infiniteRowModel/infiniteCache.ts var import_core2 = require("@ag-grid-community/core"); // community-modules/infinite-row-model/src/infiniteRowModel/infiniteBlock.ts var import_core = require("@ag-grid-community/core"); var InfiniteBlock = class extends import_core.RowNodeBlock { wireBeans(beans) { this.beans = beans; } constructor(id, parentCache, params) { super(id); this.parentCache = parentCache; this.params = params; this.startRow = id * params.blockSize; this.endRow = this.startRow + params.blockSize; } postConstruct() { this.createRowNodes(); } getBlockStateJson() { return { id: "" + this.getId(), state: { blockNumber: this.getId(), startRow: this.getStartRow(), endRow: this.getEndRow(), pageStatus: this.getState() } }; } setDataAndId(rowNode, data, index) { if ((0, import_core._exists)(data)) { rowNode.setDataAndId(data, index.toString()); } else { rowNode.setDataAndId(void 0, void 0); } } loadFromDatasource() { const params = this.createLoadParams(); if ((0, import_core._missing)(this.params.datasource.getRows)) { (0, import_core._warnOnce)(`datasource is missing getRows method`); return; } window.setTimeout(() => { this.params.datasource.getRows(params); }, 0); } processServerFail() { } createLoadParams() { const params = { startRow: this.getStartRow(), endRow: this.getEndRow(), successCallback: this.pageLoaded.bind(this, this.getVersion()), failCallback: this.pageLoadFailed.bind(this, this.getVersion()), sortModel: this.params.sortModel, filterModel: this.params.filterModel, context: this.gos.getGridCommonParams().context }; return params; } forEachNode(callback, sequence, rowCount) { this.rowNodes.forEach((rowNode, index) => { const rowIndex = this.startRow + index; if (rowIndex < rowCount) { callback(rowNode, sequence.next()); } }); } getLastAccessed() { return this.lastAccessed; } getRow(rowIndex, dontTouchLastAccessed = false) { if (!dontTouchLastAccessed) { this.lastAccessed = this.params.lastAccessedSequence.next(); } const localIndex = rowIndex - this.startRow; return this.rowNodes[localIndex]; } getStartRow() { return this.startRow; } getEndRow() { return this.endRow; } // creates empty row nodes, data is missing as not loaded yet createRowNodes() { this.rowNodes = []; for (let i = 0; i < this.params.blockSize; i++) { const rowIndex = this.startRow + i; const rowNode = new import_core.RowNode(this.beans); rowNode.setRowHeight(this.params.rowHeight); rowNode.uiLevel = 0; rowNode.setRowIndex(rowIndex); rowNode.setRowTop(this.params.rowHeight * rowIndex); this.rowNodes.push(rowNode); } } processServerResult(params) { this.rowNodes.forEach((rowNode, index) => { const data = params.rowData ? params.rowData[index] : void 0; if (!rowNode.id && rowNode.alreadyRendered && data) { this.rowNodes[index] = new import_core.RowNode(this.beans); this.rowNodes[index].setRowIndex(rowNode.rowIndex); this.rowNodes[index].setRowTop(rowNode.rowTop); this.rowNodes[index].setRowHeight(rowNode.rowHeight); rowNode.clearRowTopAndRowIndex(); } this.setDataAndId(this.rowNodes[index], data, this.startRow + index); }); const finalRowCount = params.rowCount != null && params.rowCount >= 0 ? params.rowCount : void 0; this.parentCache.pageLoaded(this, finalRowCount); } destroy() { this.rowNodes.forEach((rowNode) => { rowNode.clearRowTopAndRowIndex(); }); super.destroy(); } }; // community-modules/infinite-row-model/src/infiniteRowModel/infiniteCache.ts var MAX_EMPTY_BLOCKS_TO_KEEP = 2; var InfiniteCache = class extends import_core2.BeanStub { constructor(params) { super(); this.lastRowIndexKnown = false; this.blocks = {}; this.blockCount = 0; this.rowCount = params.initialRowCount; this.params = params; } wireBeans(beans) { this.rowRenderer = beans.rowRenderer; this.focusService = beans.focusService; } // the rowRenderer will not pass dontCreatePage, meaning when rendering the grid, // it will want new pages in the cache as it asks for rows. only when we are inserting / // removing rows via the api is dontCreatePage set, where we move rows between the pages. getRow(rowIndex, dontCreatePage = false) { const blockId = Math.floor(rowIndex / this.params.blockSize); let block = this.blocks[blockId]; if (!block) { if (dontCreatePage) { return void 0; } block = this.createBlock(blockId); } return block.getRow(rowIndex); } createBlock(blockNumber) { const newBlock = this.createBean(new InfiniteBlock(blockNumber, this, this.params)); this.blocks[newBlock.getId()] = newBlock; this.blockCount++; this.purgeBlocksIfNeeded(newBlock); this.params.rowNodeBlockLoader.addBlock(newBlock); return newBlock; } // we have this on infinite row model only, not server side row model, // because for server side, it would leave the children in inconsistent // state - eg if a node had children, but after the refresh it had data // for a different row, then the children would be with the wrong row node. refreshCache() { const nothingToRefresh = this.blockCount == 0; if (nothingToRefresh) { this.purgeCache(); return; } this.getBlocksInOrder().forEach((block) => block.setStateWaitingToLoad()); this.params.rowNodeBlockLoader.checkBlockToLoad(); } destroy() { this.getBlocksInOrder().forEach((block) => this.destroyBlock(block)); super.destroy(); } getRowCount() { return this.rowCount; } isLastRowIndexKnown() { return this.lastRowIndexKnown; } // block calls this, when page loaded pageLoaded(block, lastRow) { if (!this.isAlive()) { return; } if (this.gos.get("debug")) { (0, import_core2._log)(`InfiniteCache - onPageLoaded: page = ${block.getId()}, lastRow = ${lastRow}`); } this.checkRowCount(block, lastRow); this.onCacheUpdated(); } purgeBlocksIfNeeded(blockToExclude) { const blocksForPurging = this.getBlocksInOrder().filter((b) => b != blockToExclude); const lastAccessedComparator = (a, b) => b.getLastAccessed() - a.getLastAccessed(); blocksForPurging.sort(lastAccessedComparator); const maxBlocksProvided = this.params.maxBlocksInCache > 0; const blocksToKeep = maxBlocksProvided ? this.params.maxBlocksInCache - 1 : null; const emptyBlocksToKeep = MAX_EMPTY_BLOCKS_TO_KEEP - 1; blocksForPurging.forEach((block, index) => { const purgeBecauseBlockEmpty = block.getState() === "needsLoading" && index >= emptyBlocksToKeep; const purgeBecauseCacheFull = maxBlocksProvided ? index >= blocksToKeep : false; if (purgeBecauseBlockEmpty || purgeBecauseCacheFull) { if (this.isBlockCurrentlyDisplayed(block)) { return; } if (this.isBlockFocused(block)) { return; } this.removeBlockFromCache(block); } }); } isBlockFocused(block) { const focusedCell = this.focusService.getFocusCellToUseAfterRefresh(); if (!focusedCell) { return false; } if (focusedCell.rowPinned != null) { return false; } const blockIndexStart = block.getStartRow(); const blockIndexEnd = block.getEndRow(); const hasFocus = focusedCell.rowIndex >= blockIndexStart && focusedCell.rowIndex < blockIndexEnd; return hasFocus; } isBlockCurrentlyDisplayed(block) { const startIndex = block.getStartRow(); const endIndex = block.getEndRow() - 1; return this.rowRenderer.isRangeInRenderedViewport(startIndex, endIndex); } removeBlockFromCache(blockToRemove) { if (!blockToRemove) { return; } this.destroyBlock(blockToRemove); } checkRowCount(block, lastRow) { if (typeof lastRow === "number" && lastRow >= 0) { this.rowCount = lastRow; this.lastRowIndexKnown = true; } else if (!this.lastRowIndexKnown) { const lastRowIndex = (block.getId() + 1) * this.params.blockSize; const lastRowIndexPlusOverflow = lastRowIndex + this.params.overflowSize; if (this.rowCount < lastRowIndexPlusOverflow) { this.rowCount = lastRowIndexPlusOverflow; } } } setRowCount(rowCount, lastRowIndexKnown) { this.rowCount = rowCount; if ((0, import_core2._exists)(lastRowIndexKnown)) { this.lastRowIndexKnown = lastRowIndexKnown; } if (!this.lastRowIndexKnown) { if (this.rowCount % this.params.blockSize === 0) { this.rowCount++; } } this.onCacheUpdated(); } forEachNodeDeep(callback) { const sequence = new import_core2.NumberSequence(); this.getBlocksInOrder().forEach((block) => block.forEachNode(callback, sequence, this.rowCount)); } getBlocksInOrder() { const blockComparator = (a, b) => a.getId() - b.getId(); const blocks = (0, import_core2._getAllValuesInObject)(this.blocks).sort(blockComparator); return blocks; } destroyBlock(block) { delete this.blocks[block.getId()]; this.destroyBean(block); this.blockCount--; this.params.rowNodeBlockLoader.removeBlock(block); } // gets called 1) row count changed 2) cache purged 3) items inserted onCacheUpdated() { if (this.isAlive()) { this.destroyAllBlocksPastVirtualRowCount(); this.eventService.dispatchEvent({ type: "storeUpdated" }); } } destroyAllBlocksPastVirtualRowCount() { const blocksToDestroy = []; this.getBlocksInOrder().forEach((block) => { const startRow = block.getId() * this.params.blockSize; if (startRow >= this.rowCount) { blocksToDestroy.push(block); } }); if (blocksToDestroy.length > 0) { blocksToDestroy.forEach((block) => this.destroyBlock(block)); } } purgeCache() { this.getBlocksInOrder().forEach((block) => this.removeBlockFromCache(block)); this.lastRowIndexKnown = false; if (this.rowCount === 0) { this.rowCount = this.params.initialRowCount; } this.onCacheUpdated(); } getRowNodesInRange(firstInRange, lastInRange) { const result = []; let lastBlockId = -1; let inActiveRange = false; const numberSequence = new import_core2.NumberSequence(); let foundGapInSelection = false; this.getBlocksInOrder().forEach((block) => { if (foundGapInSelection) { return; } if (inActiveRange && lastBlockId + 1 !== block.getId()) { foundGapInSelection = true; return; } lastBlockId = block.getId(); block.forEachNode( (rowNode) => { const hitFirstOrLast = rowNode === firstInRange || rowNode === lastInRange; if (inActiveRange || hitFirstOrLast) { result.push(rowNode); } if (hitFirstOrLast) { inActiveRange = !inActiveRange; } }, numberSequence, this.rowCount ); }); const invalidRange = foundGapInSelection || inActiveRange; return invalidRange ? [] : result; } }; // community-modules/infinite-row-model/src/infiniteRowModel/infiniteRowModel.ts var InfiniteRowModel = class extends import_core3.BeanStub { constructor() { super(...arguments); this.beanName = "rowModel"; } wireBeans(beans) { this.filterManager = beans.filterManager; this.sortController = beans.sortController; this.selectionService = beans.selectionService; this.rowRenderer = beans.rowRenderer; this.rowNodeBlockLoader = beans.rowNodeBlockLoader; } getRowBounds(index) { return { rowHeight: this.rowHeight, rowTop: this.rowHeight * index }; } // we don't implement as lazy row heights is not supported in this row model ensureRowHeightsValid() { return false; } postConstruct() { if (this.gos.get("rowModelType") !== "infinite") { return; } this.rowHeight = (0, import_core3._getRowHeightAsNumber)(this.gos); this.addEventListeners(); this.addDestroyFunc(() => this.destroyCache()); this.verifyProps(); } verifyProps() { if (this.gos.exists("initialGroupOrderComparator")) { (0, import_core3._warnOnce)( "initialGroupOrderComparator cannot be used with Infinite Row Model as sorting is done on the server side" ); } } start() { this.setDatasource(this.gos.get("datasource")); } destroy() { this.destroyDatasource(); super.destroy(); } destroyDatasource() { if (this.datasource) { this.destroyBean(this.datasource); this.rowRenderer.datasourceChanged(); this.datasource = null; } } addEventListeners() { this.addManagedEventListeners({ filterChanged: this.onFilterChanged.bind(this), sortChanged: this.onSortChanged.bind(this), newColumnsLoaded: this.onColumnEverything.bind(this), storeUpdated: this.onCacheUpdated.bind(this) }); this.addManagedPropertyListener("datasource", () => this.setDatasource(this.gos.get("datasource"))); this.addManagedPropertyListener("cacheBlockSize", () => this.resetCache()); this.addManagedPropertyListener("rowHeight", () => { this.rowHeight = (0, import_core3._getRowHeightAsNumber)(this.gos); this.cacheParams.rowHeight = this.rowHeight; this.updateRowHeights(); }); } onFilterChanged() { this.reset(); } onSortChanged() { this.reset(); } onColumnEverything() { let resetRequired; if (this.cacheParams) { resetRequired = this.isSortModelDifferent(); } else { resetRequired = true; } if (resetRequired) { this.reset(); } } isSortModelDifferent() { return !(0, import_core3._jsonEquals)(this.cacheParams.sortModel, this.sortController.getSortModel()); } getType() { return "infinite"; } setDatasource(datasource) { this.destroyDatasource(); this.datasource = datasource; if (datasource) { this.reset(); } } isEmpty() { return !this.infiniteCache; } isRowsToRender() { return !!this.infiniteCache; } getNodesInRangeForSelection(firstInRange, lastInRange) { return this.infiniteCache ? this.infiniteCache.getRowNodesInRange(firstInRange, lastInRange) : []; } reset() { if (!this.datasource) { return; } const getRowIdFunc = (0, import_core3._getRowIdCallback)(this.gos); const userGeneratingIds = getRowIdFunc != null; if (!userGeneratingIds) { this.selectionService.reset("rowDataChanged"); } this.resetCache(); } dispatchModelUpdatedEvent() { this.eventService.dispatchEvent({ type: "modelUpdated", // not sure if these should all be false - noticed if after implementing, // maybe they should be true? newPage: false, newPageSize: false, newData: false, keepRenderedRows: true, animate: false }); } resetCache() { this.destroyCache(); this.cacheParams = { // the user provided datasource datasource: this.datasource, // sort and filter model filterModel: this.filterManager?.getFilterModel() ?? {}, sortModel: this.sortController.getSortModel(), rowNodeBlockLoader: this.rowNodeBlockLoader, // properties - this way we take a snapshot of them, so if user changes any, they will be // used next time we create a new cache, which is generally after a filter or sort change, // or a new datasource is set initialRowCount: this.gos.get("infiniteInitialRowCount"), maxBlocksInCache: this.gos.get("maxBlocksInCache"), rowHeight: (0, import_core3._getRowHeightAsNumber)(this.gos), // if user doesn't provide overflow, we use default overflow of 1, so user can scroll past // the current page and request first row of next page overflowSize: this.gos.get("cacheOverflowSize"), // page size needs to be 1 or greater. having it at 1 would be silly, as you would be hitting the // server for one page at a time. so the default if not specified is 100. blockSize: this.gos.get("cacheBlockSize"), // the cache could create this, however it is also used by the pages, so handy to create it // here as the settings are also passed to the pages lastAccessedSequence: new import_core3.NumberSequence() }; this.infiniteCache = this.createBean(new InfiniteCache(this.cacheParams)); this.eventService.dispatchEventOnce({ type: "rowCountReady" }); this.dispatchModelUpdatedEvent(); } updateRowHeights() { this.forEachNode((node) => { node.setRowHeight(this.rowHeight); node.setRowTop(this.rowHeight * node.rowIndex); }); this.dispatchModelUpdatedEvent(); } destroyCache() { if (this.infiniteCache) { this.infiniteCache = this.destroyBean(this.infiniteCache); } } onCacheUpdated() { this.dispatchModelUpdatedEvent(); } getRow(rowIndex) { if (!this.infiniteCache) { return void 0; } if (rowIndex >= this.infiniteCache.getRowCount()) { return void 0; } return this.infiniteCache.getRow(rowIndex); } getRowNode(id) { let result; this.forEachNode((rowNode) => { if (rowNode.id === id) { result = rowNode; } }); return result; } forEachNode(callback) { if (this.infiniteCache) { this.infiniteCache.forEachNodeDeep(callback); } } getTopLevelRowCount() { return this.getRowCount(); } getTopLevelRowDisplayedIndex(topLevelIndex) { return topLevelIndex; } getRowIndexAtPixel(pixel) { if (this.rowHeight !== 0) { const rowIndexForPixel = Math.floor(pixel / this.rowHeight); const lastRowIndex = this.getRowCount() - 1; if (rowIndexForPixel > lastRowIndex) { return lastRowIndex; } return rowIndexForPixel; } return 0; } getRowCount() { return this.infiniteCache ? this.infiniteCache.getRowCount() : 0; } isRowPresent(rowNode) { const foundRowNode = this.getRowNode(rowNode.id); return !!foundRowNode; } refreshCache() { if (this.infiniteCache) { this.infiniteCache.refreshCache(); } } purgeCache() { if (this.infiniteCache) { this.infiniteCache.purgeCache(); } } // for iRowModel isLastRowIndexKnown() { if (this.infiniteCache) { return this.infiniteCache.isLastRowIndexKnown(); } return false; } setRowCount(rowCount, lastRowIndexKnown) { if (this.infiniteCache) { this.infiniteCache.setRowCount(rowCount, lastRowIndexKnown); } } }; // community-modules/infinite-row-model/src/infiniteRowModel/infiniteRowModelApi.ts function refreshInfiniteCache(beans) { beans.rowModelHelperService?.getInfiniteRowModel()?.refreshCache(); } function purgeInfiniteCache(beans) { beans.rowModelHelperService?.getInfiniteRowModel()?.purgeCache(); } function getInfiniteRowCount(beans) { return beans.rowModelHelperService?.getInfiniteRowModel()?.getRowCount(); } // community-modules/infinite-row-model/src/version.ts var VERSION = "32.3.9"; // community-modules/infinite-row-model/src/infiniteRowModelModule.ts var InfiniteRowModelCoreModule = (0, import_core4._defineModule)({ version: VERSION, moduleName: `${import_core4.ModuleNames.InfiniteRowModelModule}-core`, rowModel: "infinite", beans: [InfiniteRowModel], dependantModules: [import_core4._RowNodeBlockModule] }); var InfiniteRowModelApiModule = (0, import_core4._defineModule)({ version: VERSION, moduleName: `${import_core4.ModuleNames.InfiniteRowModelModule}-api`, beans: [import_core4.RowModelHelperService], apiFunctions: { refreshInfiniteCache, purgeInfiniteCache, getInfiniteRowCount }, dependantModules: [InfiniteRowModelCoreModule, import_core4._SsrmInfiniteSharedApiModule] }); var InfiniteRowModelModule = (0, import_core4._defineModule)({ version: VERSION, moduleName: import_core4.ModuleNames.InfiniteRowModelModule, dependantModules: [InfiniteRowModelCoreModule, InfiniteRowModelApiModule] });