@acorex/cdk 22.2.0-next.0 → 22.2.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1168,6 +1168,7 @@ class AXListDataSource extends DataSource {
1168
1168
  this.config = config;
1169
1169
  this.debounceTime = 0;
1170
1170
  this.subscription = new Subscription();
1171
+ this.connected = false;
1171
1172
  this._range = { start: 0, end: 0 };
1172
1173
  this.source = config.source;
1173
1174
  if (config.debounceTime)
@@ -1176,9 +1177,20 @@ class AXListDataSource extends DataSource {
1176
1177
  this.dataStream = new BehaviorSubject(this.config.source.cachedItems);
1177
1178
  this.source.onChanged.subscribe((data) => {
1178
1179
  this.dataStream.next(data.cachedItems);
1180
+ // A refresh (e.g. a new search) only reloads page 0. When the rendered range stays the same,
1181
+ // CDK emits no `viewChange`, so the other visible pages would otherwise stay as loading rows.
1182
+ if (this.connected) {
1183
+ this.loadMissingPagesInRange(data.totalCount);
1184
+ }
1179
1185
  });
1180
1186
  }
1181
1187
  connect(collectionViewer) {
1188
+ // The same instance is reconnected when its viewport is destroyed and recreated
1189
+ // (e.g. a lookup search shows "no results" and then matches again). A closed
1190
+ // Subscription tears down anything added to it immediately, so start a fresh one.
1191
+ this.subscription.unsubscribe();
1192
+ this.subscription = new Subscription();
1193
+ this.connected = true;
1182
1194
  this.subscription.add(collectionViewer.viewChange
1183
1195
  .pipe(debounceTime(this.debounceTime))
1184
1196
  .pipe(distinctUntilChanged())
@@ -1193,7 +1205,22 @@ class AXListDataSource extends DataSource {
1193
1205
  return this.dataStream;
1194
1206
  }
1195
1207
  disconnect() {
1208
+ this.connected = false;
1196
1209
  this.subscription.unsubscribe();
1210
+ this._range = { start: 0, end: 0 };
1211
+ }
1212
+ loadMissingPagesInRange(totalCount) {
1213
+ const end = Math.min(this._range.end, totalCount);
1214
+ if (end <= this._range.start) {
1215
+ return;
1216
+ }
1217
+ const startPage = this.getPageForIndex(this._range.start);
1218
+ const endPage = this.getPageForIndex(end - 1);
1219
+ for (let i = startPage; i <= endPage; i++) {
1220
+ if (!this.source.isPageFetched(i)) {
1221
+ this.source.setPage(i);
1222
+ }
1223
+ }
1197
1224
  }
1198
1225
  getPageForIndex(index) {
1199
1226
  return Math.floor(index / this.source.config.pageSize);
@@ -1231,6 +1258,9 @@ class AXDataSource {
1231
1258
  this._totalCount = 0;
1232
1259
  this._items = [];
1233
1260
  this.fetchedPages = new Set();
1261
+ /** Bumped on every reset so responses requested before it are dropped instead of corrupting the cache. */
1262
+ this.loadGeneration = 0;
1263
+ this.pendingLoads = 0;
1234
1264
  this._page = 0;
1235
1265
  this._query = { skip: 0, take: 0, sort: [] };
1236
1266
  this.onChanged = new Subject();
@@ -1248,6 +1278,8 @@ class AXDataSource {
1248
1278
  if (this.fetchedPages.has(page) && this.useCache) {
1249
1279
  return;
1250
1280
  }
1281
+ const generation = this.loadGeneration;
1282
+ this.pendingLoads++;
1251
1283
  this.setLoadingState(true);
1252
1284
  this.fetchedPages.add(page);
1253
1285
  this._query.take = this.config.pageSize;
@@ -1255,25 +1287,50 @@ class AXDataSource {
1255
1287
  this.config
1256
1288
  .load({ ...this._query })
1257
1289
  .then((result) => {
1258
- if (this._totalCount != result.total) {
1259
- this._cachedItems = new Array(result.total);
1260
- this._totalCount = result.total;
1290
+ if (generation !== this.loadGeneration) {
1291
+ return;
1261
1292
  }
1262
- this._items = result.items;
1263
- this._cachedItems.splice(page * this.config.pageSize, this.config.pageSize, ...result.items);
1264
- const event = {
1265
- cachedItems: this.cachedItems,
1266
- items: this.items,
1267
- page: page,
1268
- totalCount: this.totalCount,
1269
- totalPages: this.totalPages,
1270
- };
1271
- this.onChanged.next(event);
1293
+ this.applyLoadResult(page, result);
1294
+ }, (error) => {
1295
+ if (generation === this.loadGeneration) {
1296
+ this.fetchedPages.delete(page);
1297
+ }
1298
+ throw error;
1272
1299
  })
1273
1300
  .finally(() => {
1274
- this.setLoadingState(false);
1301
+ if (generation !== this.loadGeneration) {
1302
+ return;
1303
+ }
1304
+ this.pendingLoads = Math.max(0, this.pendingLoads - 1);
1305
+ if (this.pendingLoads === 0) {
1306
+ this.setLoadingState(false);
1307
+ }
1275
1308
  });
1276
1309
  }
1310
+ applyLoadResult(page, result) {
1311
+ if (this._totalCount != result.total) {
1312
+ if (this._totalCount > 0) {
1313
+ // Rows of other pages are dropped with the old array, so they must be fetched again.
1314
+ this.fetchedPages = new Set([page]);
1315
+ }
1316
+ this._cachedItems = new Array(result.total);
1317
+ this._totalCount = result.total;
1318
+ }
1319
+ this._items = result.items;
1320
+ this._cachedItems.splice(page * this.config.pageSize, this.config.pageSize, ...result.items);
1321
+ const event = {
1322
+ cachedItems: this.cachedItems,
1323
+ items: this.items,
1324
+ page: page,
1325
+ totalCount: this.totalCount,
1326
+ totalPages: this.totalPages,
1327
+ };
1328
+ this.onChanged.next(event);
1329
+ }
1330
+ /** Whether `page` was loaded (or is loading) for the current filter/sort. */
1331
+ isPageFetched(page) {
1332
+ return this.fetchedPages.has(page);
1333
+ }
1277
1334
  async getItemsByFilter(filter) {
1278
1335
  return await this.config.load({ ...this.query, filter });
1279
1336
  }
@@ -1282,7 +1339,7 @@ class AXDataSource {
1282
1339
  this.onLoadingChanged.next(this._isLoading);
1283
1340
  }
1284
1341
  setPage(page) {
1285
- if (this._page != page || this._items.length == 0) {
1342
+ if (this._page != page || this._items.length == 0 || !this.fetchedPages.has(page)) {
1286
1343
  this._page = page;
1287
1344
  this.load();
1288
1345
  }
@@ -1301,12 +1358,22 @@ class AXDataSource {
1301
1358
  this._query.filter = null;
1302
1359
  }
1303
1360
  reset() {
1361
+ const wasLoading = this._isLoading;
1362
+ this.resetState();
1363
+ if (wasLoading) {
1364
+ this.setLoadingState(false);
1365
+ }
1366
+ }
1367
+ /** Resets paging state without emitting `onLoadingChanged`, so a following `load()` keeps the loading state continuous. */
1368
+ resetState() {
1304
1369
  this._query.filter = null;
1305
1370
  this._query.sort = [];
1306
1371
  this._query.skip = 0;
1307
1372
  this._items = new Array(0);
1308
1373
  this._cachedItems = new Array(this.config.pageSize);
1309
1374
  this.fetchedPages.clear();
1375
+ this.loadGeneration++;
1376
+ this.pendingLoads = 0;
1310
1377
  this._page = 0;
1311
1378
  this._totalCount = 0;
1312
1379
  }
@@ -1314,7 +1381,7 @@ class AXDataSource {
1314
1381
  const currentFilter = this._query.filter;
1315
1382
  const currentSort = this._query.sort;
1316
1383
  if (options.reset) {
1317
- this.reset();
1384
+ this.resetState();
1318
1385
  }
1319
1386
  this._query.filter = currentFilter;
1320
1387
  this._query.sort = currentSort;