|
207 | 207 | grid.activeItem = item; |
208 | 208 | } |
209 | 209 | }); |
210 | | - |
| 210 | + |
211 | 211 | grid.selectedItems = Object.values(selectedKeys); |
212 | 212 | }); |
213 | 213 |
|
|
277 | 277 | detailsVisibleOnClick = visibleOnClick; |
278 | 278 | }); |
279 | 279 |
|
280 | | - grid.$connector._getPageIfSameLevel = tryCatchWrapper(function(parentKey, index, defaultPage) { |
281 | | - let cacheAndIndex = grid._cache.getCacheAndIndex(index); |
282 | | - let parentItem = cacheAndIndex.cache.parentItem; |
283 | | - let parentKeyOfIndex = (parentItem) ? grid.getItemId(parentItem) : root; |
284 | | - if(parentKey !== parentKeyOfIndex) { |
285 | | - return defaultPage; |
286 | | - } else { |
287 | | - return grid._getPageForIndex(cacheAndIndex.scaledIndex); |
| 280 | + grid.$connector._getSameLevelPage = tryCatchWrapper(function (parentKey, currentCache, currentCacheItemIndex) { |
| 281 | + const currentParentKey = currentCache.parentItem ? grid.getItemId(currentCache.parentItem) : root; |
| 282 | + if (currentParentKey === parentKey) { |
| 283 | + // Level match found |
| 284 | + return grid._getPageForIndex(currentCacheItemIndex); |
288 | 285 | } |
289 | | - }) |
| 286 | + const { parentCache } = currentCache; |
| 287 | + if (!parentCache) { |
| 288 | + // There is no parent cache to match level |
| 289 | + return null; |
| 290 | + } |
| 291 | + const parentCacheItemIndex = Object.entries(parentCache.itemCaches).find( |
| 292 | + ([index, cache]) => cache === currentCache |
| 293 | + )[0]; |
| 294 | + // Traverse the tree upwards until a match is found or the end is reached |
| 295 | + return this._getSameLevelPage(parentKey, parentCache, parentCacheItemIndex); |
| 296 | + }); |
290 | 297 |
|
291 | 298 | grid.$connector.getCacheByKey = tryCatchWrapper(function(key) { |
292 | 299 | let cacheAndIndex = grid._cache.getCacheAndIndexByKey(key); |
|
350 | 357 | }) |
351 | 358 |
|
352 | 359 | grid.$connector.fetchPage = tryCatchWrapper(function(fetch, page, parentKey) { |
| 360 | + // Adjust the requested page to be within the valid range in case |
| 361 | + // the grid size has changed while fetchPage was debounced. |
| 362 | + if (parentKey === root) { |
| 363 | + page = Math.min(page, Math.floor((grid.size - 1) / grid.pageSize)); |
| 364 | + } |
| 365 | + |
353 | 366 | // Determine what to fetch based on scroll position and not only |
354 | 367 | // what grid asked for |
| 368 | + let start = grid._virtualStart; |
| 369 | + let end = grid._virtualEnd; |
355 | 370 |
|
356 | 371 | // The buffer size could be multiplied by some constant defined by the user, |
357 | 372 | // if he needs to reduce the number of items sent to the Grid to improve performance |
358 | 373 | // or to increase it to make Grid smoother when scrolling |
359 | | - let start = grid._virtualStart; |
360 | | - let end = grid._virtualEnd; |
361 | 374 | let buffer = end - start; |
362 | 375 |
|
363 | 376 | let firstNeededIndex = Math.max(0, start + grid._vidxOffset - buffer); |
364 | 377 | let lastNeededIndex = Math.min(end + grid._vidxOffset + buffer, grid._effectiveSize); |
365 | 378 |
|
366 | | - let firstNeededPage = page; |
367 | | - let lastNeededPage = page; |
| 379 | + let pageRange = [null, null]; |
368 | 380 | for(let idx = firstNeededIndex; idx <= lastNeededIndex; idx++) { |
369 | | - firstNeededPage = Math.min(firstNeededPage, grid.$connector._getPageIfSameLevel(parentKey, idx, firstNeededPage)); |
370 | | - lastNeededPage = Math.max(lastNeededPage, grid.$connector._getPageIfSameLevel(parentKey, idx, lastNeededPage)); |
| 381 | + const { cache, scaledIndex } = grid._cache.getCacheAndIndex(idx); |
| 382 | + |
| 383 | + const sameLevelPage = grid.$connector._getSameLevelPage(parentKey, cache, scaledIndex); |
| 384 | + if (sameLevelPage === null) { |
| 385 | + continue; |
| 386 | + } |
| 387 | + |
| 388 | + pageRange[0] = Math.min(pageRange[0] !== null ? pageRange[0] : sameLevelPage, sameLevelPage); |
| 389 | + pageRange[1] = Math.max(pageRange[1] !== null ? pageRange[1] : sameLevelPage, sameLevelPage); |
371 | 390 | } |
372 | 391 |
|
373 | | - let firstPage = Math.max(0, firstNeededPage); |
374 | | - let lastPage = (parentKey !== root) ? lastNeededPage: Math.min(lastNeededPage, Math.floor(grid.size / grid.pageSize)); |
375 | | - let lastRequestedRange = lastRequestedRanges[parentKey]; |
376 | | - if(!lastRequestedRange) { |
377 | | - lastRequestedRange = [-1, -1]; |
| 392 | + // When the viewport doesn't contain the requested page or it doesn't contain any items from |
| 393 | + // the requested level at all, it means that the scroll position has changed while fetchPage |
| 394 | + // was debounced. For example, it can happen if the user scrolls the grid to the bottom and |
| 395 | + // then immediately back to the top. In this case, the request for the last page will be left |
| 396 | + // hanging. To avoid this, as a workaround, we reset the range to only include the requested page |
| 397 | + // to make sure all hanging requests are resolved. After that, the grid requests the first page |
| 398 | + // or whatever in the viewport again. |
| 399 | + if (pageRange.some((p) => p === null) || page < pageRange[0] || page > pageRange[1]) { |
| 400 | + pageRange = [page, page]; |
378 | 401 | } |
379 | | - if (lastRequestedRange[0] != firstPage || lastRequestedRange[1] != lastPage) { |
380 | | - lastRequestedRange = [firstPage, lastPage]; |
381 | | - lastRequestedRanges[parentKey] = lastRequestedRange; |
382 | | - let count = lastPage - firstPage + 1; |
383 | | - fetch(firstPage * grid.pageSize, count * grid.pageSize); |
| 402 | + |
| 403 | + let lastRequestedRange = lastRequestedRanges[parentKey] || [-1, -1]; |
| 404 | + if (lastRequestedRange[0] != pageRange[0] || lastRequestedRange[1] != pageRange[1]) { |
| 405 | + lastRequestedRanges[parentKey] = pageRange; |
| 406 | + let pageCount = pageRange[1] - pageRange[0] + 1; |
| 407 | + fetch(pageRange[0] * grid.pageSize, pageCount * grid.pageSize); |
384 | 408 | } |
385 | | - }) |
| 409 | + }); |
386 | 410 |
|
387 | 411 | grid.dataProvider = tryCatchWrapper(function(params, callback) { |
388 | 412 | if (params.pageSize != grid.pageSize) { |
|
906 | 930 | const callback = rootPageCallbacks[page]; |
907 | 931 | if ((cache[root] && cache[root][page]) || page < lastRequestedRange[0] || +page > lastRequestedRangeEnd) { |
908 | 932 | delete rootPageCallbacks[page]; |
909 | | - |
| 933 | + |
910 | 934 | if (cache[root][page]) { |
911 | 935 | // Cached data is available, resolve the callback |
912 | 936 | callback(cache[root][page]); |
|
0 commit comments