@vaadin/grid 24.2.0-alpha5 → 24.2.0-dev.538d07bdf

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/grid",
3
- "version": "24.2.0-alpha5",
3
+ "version": "24.2.0-dev.538d07bdf",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -46,14 +46,14 @@
46
46
  "dependencies": {
47
47
  "@open-wc/dedupe-mixin": "^1.3.0",
48
48
  "@polymer/polymer": "^3.0.0",
49
- "@vaadin/a11y-base": "24.2.0-alpha5",
50
- "@vaadin/checkbox": "24.2.0-alpha5",
51
- "@vaadin/component-base": "24.2.0-alpha5",
52
- "@vaadin/lit-renderer": "24.2.0-alpha5",
53
- "@vaadin/text-field": "24.2.0-alpha5",
54
- "@vaadin/vaadin-lumo-styles": "24.2.0-alpha5",
55
- "@vaadin/vaadin-material-styles": "24.2.0-alpha5",
56
- "@vaadin/vaadin-themable-mixin": "24.2.0-alpha5"
49
+ "@vaadin/a11y-base": "24.2.0-dev.538d07bdf",
50
+ "@vaadin/checkbox": "24.2.0-dev.538d07bdf",
51
+ "@vaadin/component-base": "24.2.0-dev.538d07bdf",
52
+ "@vaadin/lit-renderer": "24.2.0-dev.538d07bdf",
53
+ "@vaadin/text-field": "24.2.0-dev.538d07bdf",
54
+ "@vaadin/vaadin-lumo-styles": "24.2.0-dev.538d07bdf",
55
+ "@vaadin/vaadin-material-styles": "24.2.0-dev.538d07bdf",
56
+ "@vaadin/vaadin-themable-mixin": "24.2.0-dev.538d07bdf"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@esm-bundle/chai": "^4.3.4",
@@ -65,5 +65,5 @@
65
65
  "web-types.json",
66
66
  "web-types.lit.json"
67
67
  ],
68
- "gitHead": "73db22a5e8993e3ce48d1e6540d30eff9cb5c257"
68
+ "gitHead": "86c2fd5f37cf1240af98f7c7d752518c8d701db2"
69
69
  }
@@ -33,31 +33,6 @@ export type GridDataProvider<TItem> = (
33
33
  callback: GridDataProviderCallback<TItem>,
34
34
  ) => void;
35
35
 
36
- export declare class ItemCache<TItem> {
37
- grid: HTMLElement;
38
- parentCache: ItemCache<TItem> | undefined;
39
- parentItem: TItem | undefined;
40
- itemCaches: object | null;
41
- items: object | null;
42
- effectiveSize: number;
43
- size: number;
44
- pendingRequests: object | null;
45
-
46
- constructor(grid: HTMLElement, parentCache: ItemCache<TItem> | undefined, parentItem: TItem | undefined);
47
-
48
- isLoading(): boolean;
49
-
50
- getItemForIndex(index: number): TItem | undefined;
51
-
52
- updateSize(): void;
53
-
54
- ensureSubCacheForScaledIndex(scaledIndex: number): void;
55
-
56
- getCacheAndIndex(index: number): { cache: ItemCache<TItem>; scaledIndex: number };
57
-
58
- getFlatIndex(scaledIndex: number): number;
59
- }
60
-
61
36
  export declare function DataProviderMixin<TItem, T extends Constructor<HTMLElement>>(
62
37
  base: T,
63
38
  ): Constructor<DataProviderMixinClass<TItem>> & T;
@@ -4,115 +4,10 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { timeOut } from '@vaadin/component-base/src/async.js';
7
+ import { DataProviderController } from '@vaadin/component-base/src/data-provider-controller.js';
7
8
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
8
9
  import { getBodyRowCells, updateCellsPart, updateState } from './vaadin-grid-helpers.js';
9
10
 
10
- /**
11
- * @private
12
- */
13
- export const ItemCache = class ItemCache {
14
- /**
15
- * @param {!HTMLElement} grid
16
- * @param {!ItemCache | undefined} parentCache
17
- * @param {!GridItem | undefined} parentItem
18
- */
19
- constructor(grid, parentCache, parentItem) {
20
- /** @type {!HTMLElement} */
21
- this.grid = grid;
22
- /** @type {!ItemCache | undefined} */
23
- this.parentCache = parentCache;
24
- /** @type {!GridItem | undefined} */
25
- this.parentItem = parentItem;
26
- /** @type {object} */
27
- this.itemCaches = {};
28
- /** @type {object[]} */
29
- this.items = [];
30
- /** @type {number} */
31
- this.effectiveSize = 0;
32
- /** @type {number} */
33
- this.size = 0;
34
- /** @type {object} */
35
- this.pendingRequests = {};
36
- }
37
-
38
- /**
39
- * @return {boolean}
40
- */
41
- isLoading() {
42
- return Boolean(
43
- Object.keys(this.pendingRequests).length ||
44
- Object.keys(this.itemCaches).filter((index) => {
45
- return this.itemCaches[index].isLoading();
46
- })[0],
47
- );
48
- }
49
-
50
- /**
51
- * @param {number} index
52
- * @return {!GridItem | undefined}
53
- */
54
- getItemForIndex(index) {
55
- const { cache, scaledIndex } = this.getCacheAndIndex(index);
56
- return cache.items[scaledIndex];
57
- }
58
-
59
- updateSize() {
60
- this.effectiveSize =
61
- !this.parentItem || this.grid._isExpanded(this.parentItem)
62
- ? this.size +
63
- Object.keys(this.itemCaches).reduce((prev, curr) => {
64
- const subCache = this.itemCaches[curr];
65
- subCache.updateSize();
66
- return prev + subCache.effectiveSize;
67
- }, 0)
68
- : 0;
69
- }
70
-
71
- /**
72
- * @param {number} scaledIndex
73
- */
74
- ensureSubCacheForScaledIndex(scaledIndex) {
75
- if (!this.itemCaches[scaledIndex]) {
76
- const subCache = new ItemCache(this.grid, this, this.items[scaledIndex]);
77
- this.itemCaches[scaledIndex] = subCache;
78
- this.grid._loadPage(0, subCache);
79
- }
80
- }
81
-
82
- /**
83
- * @param {number} index
84
- * @return {{cache: !ItemCache, scaledIndex: number}}
85
- */
86
- getCacheAndIndex(index) {
87
- let thisLevelIndex = index;
88
- for (const [index, subCache] of Object.entries(this.itemCaches)) {
89
- const numberIndex = Number(index);
90
- if (thisLevelIndex <= numberIndex) {
91
- return { cache: this, scaledIndex: thisLevelIndex };
92
- } else if (thisLevelIndex <= numberIndex + subCache.effectiveSize) {
93
- return subCache.getCacheAndIndex(thisLevelIndex - numberIndex - 1);
94
- }
95
- thisLevelIndex -= subCache.effectiveSize;
96
- }
97
- return { cache: this, scaledIndex: thisLevelIndex };
98
- }
99
-
100
- /**
101
- * Gets the scaled index as flattened index on this cache level.
102
- * In practice, this means that the effective size of any expanded
103
- * subcaches preceding the index are added to the value.
104
- * @param {number} scaledIndex
105
- * @return {number} The flat index on this cache level.
106
- */
107
- getFlatIndex(scaledIndex) {
108
- const clampedIndex = Math.max(0, Math.min(this.size - 1, scaledIndex));
109
-
110
- return Object.entries(this.itemCaches).reduce((prev, [index, subCache]) => {
111
- return clampedIndex > Number(index) ? prev + subCache.effectiveSize : prev;
112
- }, clampedIndex);
113
- }
114
- };
115
-
116
11
  /**
117
12
  * @polymerMixin
118
13
  */
@@ -180,18 +75,6 @@ export const DataProviderMixin = (superClass) =>
180
75
  reflectToAttribute: true,
181
76
  },
182
77
 
183
- /**
184
- * @type {!ItemCache}
185
- * @protected
186
- */
187
- _cache: {
188
- type: Object,
189
- value() {
190
- const cache = new ItemCache(this);
191
- return cache;
192
- },
193
- },
194
-
195
78
  /**
196
79
  * @protected
197
80
  */
@@ -243,12 +126,32 @@ export const DataProviderMixin = (superClass) =>
243
126
  return ['_sizeChanged(size)', '_expandedItemsChanged(expandedItems.*)'];
244
127
  }
245
128
 
129
+ constructor() {
130
+ super();
131
+
132
+ /** @type {DataProviderController} */
133
+ this._dataProviderController = new DataProviderController(this, {
134
+ size: this.size,
135
+ pageSize: this.pageSize,
136
+ isExpanded: this._isExpanded.bind(this),
137
+ dataProvider: this.dataProvider ? this.dataProvider.bind(this) : null,
138
+ dataProviderParams: () => {
139
+ return {
140
+ sortOrders: this._mapSorters(),
141
+ filters: this._mapFilters(),
142
+ };
143
+ },
144
+ });
145
+
146
+ this._dataProviderController.addEventListener('page-requested', this._onDataProviderPageRequested.bind(this));
147
+ this._dataProviderController.addEventListener('page-received', this._onDataProviderPageReceived.bind(this));
148
+ this._dataProviderController.addEventListener('page-loaded', this._onDataProviderPageLoaded.bind(this));
149
+ }
150
+
246
151
  /** @private */
247
152
  _sizeChanged(size) {
248
- const delta = size - this._cache.size;
249
- this._cache.size += delta;
250
- this._cache.effectiveSize += delta;
251
- this._effectiveSize = this._cache.effectiveSize;
153
+ this._dataProviderController.setSize(size);
154
+ this._effectiveSize = this._dataProviderController.effectiveSize;
252
155
  }
253
156
 
254
157
  /** @private */
@@ -271,17 +174,17 @@ export const DataProviderMixin = (superClass) =>
271
174
  }
272
175
 
273
176
  el.index = index;
274
- const { cache, scaledIndex } = this._cache.getCacheAndIndex(index);
275
- const item = cache.items[scaledIndex];
177
+
178
+ const { item } = this._dataProviderController.getFlatIndexInfo(index);
276
179
  if (item) {
277
180
  this.__updateLoading(el, false);
278
181
  this._updateItem(el, item);
279
182
  if (this._isExpanded(item)) {
280
- cache.ensureSubCacheForScaledIndex(scaledIndex);
183
+ this._dataProviderController.ensureFlatIndexChildrenLoaded(index);
281
184
  }
282
185
  } else {
283
186
  this.__updateLoading(el, true);
284
- this._loadPage(this._getPageForIndex(scaledIndex), cache);
187
+ this._dataProviderController.ensureFlatIndexLoaded(index);
285
188
  }
286
189
  }
287
190
 
@@ -321,8 +224,8 @@ export const DataProviderMixin = (superClass) =>
321
224
 
322
225
  /** @private */
323
226
  _expandedItemsChanged() {
324
- this._cache.updateSize();
325
- this._effectiveSize = this._cache.effectiveSize;
227
+ this._dataProviderController.recalculateEffectiveSize();
228
+ this._effectiveSize = this._dataProviderController.effectiveSize;
326
229
  this.__updateVisibleRows();
327
230
  }
328
231
 
@@ -362,121 +265,68 @@ export const DataProviderMixin = (superClass) =>
362
265
  * @return {number}
363
266
  * @protected
364
267
  */
365
- _getIndexLevel(index) {
366
- let { cache } = this._cache.getCacheAndIndex(index);
367
- let level = 0;
368
- while (cache.parentCache) {
369
- cache = cache.parentCache;
370
- level += 1;
371
- }
268
+ _getIndexLevel(index = 0) {
269
+ const { level } = this._dataProviderController.getFlatIndexInfo(index);
372
270
  return level;
373
271
  }
374
272
 
375
- /**
376
- * @param {number} page
377
- * @param {ItemCache} cache
378
- * @protected
379
- */
380
- _loadPage(page, cache) {
381
- // Make sure same page isn't requested multiple times.
382
- if (!cache.pendingRequests[page] && this.dataProvider) {
383
- this._setLoading(true);
384
- cache.pendingRequests[page] = true;
385
- const params = {
386
- page,
387
- pageSize: this.pageSize,
388
- sortOrders: this._mapSorters(),
389
- filters: this._mapFilters(),
390
- parentItem: cache.parentItem,
391
- };
392
-
393
- this.dataProvider(params, (items, size) => {
394
- if (size !== undefined) {
395
- cache.size = size;
396
- } else if (params.parentItem) {
397
- cache.size = items.length;
398
- }
273
+ /** @protected */
274
+ _onDataProviderPageRequested() {
275
+ this._setLoading(true);
276
+ }
399
277
 
400
- // Populate the cache with new items
401
- items.forEach((item, itemsIndex) => {
402
- const itemIndex = page * this.pageSize + itemsIndex;
403
- cache.items[itemIndex] = item;
404
- });
405
-
406
- // With the new items added, update the cache size and the grid's effective size
407
- this._cache.updateSize();
408
- this._effectiveSize = this._cache.effectiveSize;
409
-
410
- // After updating the cache, check if some of the expanded items should have sub-caches loaded
411
- this._getRenderedRows().forEach((row) => {
412
- const { cache, scaledIndex } = this._cache.getCacheAndIndex(row.index);
413
- const item = cache.items[scaledIndex];
414
- if (item && this._isExpanded(item)) {
415
- cache.ensureSubCacheForScaledIndex(scaledIndex);
416
- }
417
- });
418
-
419
- this._hasData = true;
420
-
421
- // Remove the pending request
422
- delete cache.pendingRequests[page];
423
-
424
- // Schedule a debouncer to update the visible rows
425
- this._debouncerApplyCachedData = Debouncer.debounce(this._debouncerApplyCachedData, timeOut.after(0), () => {
426
- this._setLoading(false);
427
-
428
- this._getRenderedRows().forEach((row) => {
429
- const cachedItem = this._cache.getItemForIndex(row.index);
430
- if (cachedItem) {
431
- this._getItem(row.index, row);
432
- }
433
- });
434
-
435
- this.__scrollToPendingIndexes();
436
- });
437
-
438
- // If the grid is not loading anything, flush the debouncer immediately
439
- if (!this._cache.isLoading()) {
440
- this._debouncerApplyCachedData.flush();
441
- }
278
+ /** @protected */
279
+ _onDataProviderPageReceived() {
280
+ // With the new items added, update the cache size and the grid's effective size
281
+ this._effectiveSize = this._dataProviderController.effectiveSize;
442
282
 
443
- // Notify that new data has been received
444
- this._onDataProviderPageLoaded();
445
- });
446
- }
283
+ // After updating the cache, check if some of the expanded items should have sub-caches loaded
284
+ this._getRenderedRows().forEach((row) => {
285
+ this._dataProviderController.ensureFlatIndexChildrenLoaded(row.index);
286
+ });
287
+
288
+ this._hasData = true;
447
289
  }
448
290
 
449
291
  /** @protected */
450
- _onDataProviderPageLoaded() {}
292
+ _onDataProviderPageLoaded() {
293
+ // Schedule a debouncer to update the visible rows
294
+ this._debouncerApplyCachedData = Debouncer.debounce(this._debouncerApplyCachedData, timeOut.after(0), () => {
295
+ this._setLoading(false);
296
+
297
+ this._getRenderedRows().forEach((row) => {
298
+ const { item } = this._dataProviderController.getFlatIndexInfo(row.index);
299
+ if (item) {
300
+ this._getItem(row.index, row);
301
+ }
302
+ });
451
303
 
452
- /**
453
- * @param {number} index
454
- * @return {number}
455
- * @private
456
- */
457
- _getPageForIndex(index) {
458
- return Math.floor(index / this.pageSize);
304
+ this.__scrollToPendingIndexes();
305
+ });
306
+
307
+ // If the grid is not loading anything, flush the debouncer immediately
308
+ if (!this._dataProviderController.isLoading) {
309
+ this._debouncerApplyCachedData.flush();
310
+ }
459
311
  }
460
312
 
461
313
  /**
462
314
  * Clears the cached pages and reloads data from dataprovider when needed.
463
315
  */
464
316
  clearCache() {
465
- this._cache = new ItemCache(this);
466
- this._cache.size = this.size || 0;
467
- this._cache.updateSize();
317
+ this._dataProviderController.clearCache();
468
318
  this._hasData = false;
469
319
  this.__updateVisibleRows();
470
-
471
- if (!this._effectiveSize) {
472
- this._loadPage(0, this._cache);
473
- }
320
+ this._ensureFirstPageLoaded();
474
321
  }
475
322
 
476
323
  /** @private */
477
324
  _pageSizeChanged(pageSize, oldPageSize) {
478
325
  if (oldPageSize !== undefined && pageSize !== oldPageSize) {
479
- this.clearCache();
326
+ this._dataProviderController.setPageSize(pageSize);
327
+ this._hasData = false;
328
+ this.__updateVisibleRows();
329
+ this._ensureFirstPageLoaded();
480
330
  }
481
331
  }
482
332
 
@@ -494,8 +344,11 @@ export const DataProviderMixin = (superClass) =>
494
344
 
495
345
  /** @private */
496
346
  _dataProviderChanged(dataProvider, oldDataProvider) {
347
+ this._dataProviderController.setDataProvider(dataProvider ? dataProvider.bind(this) : null);
348
+
497
349
  if (oldDataProvider !== undefined) {
498
- this.clearCache();
350
+ this._hasData = false;
351
+ this.__updateVisibleRows();
499
352
  }
500
353
 
501
354
  this._ensureFirstPageLoaded();
@@ -512,7 +365,7 @@ export const DataProviderMixin = (superClass) =>
512
365
  if (!this._hasData) {
513
366
  // Load data before adding rows to make sure they have content when
514
367
  // rendered for the first time.
515
- this._loadPage(0, this._cache);
368
+ this._dataProviderController.ensureFirstPageLoaded();
516
369
  }
517
370
  }
518
371
 
@@ -562,39 +415,15 @@ export const DataProviderMixin = (superClass) =>
562
415
  // ending up in a loading state. Try scrolling to the index until the target
563
416
  // index stabilizes.
564
417
  let targetIndex;
565
- while (targetIndex !== (targetIndex = this.__getGlobalFlatIndex(indexes))) {
418
+ while (targetIndex !== (targetIndex = this._dataProviderController.getFlatIndexByPath(indexes))) {
566
419
  this._scrollToFlatIndex(targetIndex);
567
420
  }
568
421
 
569
- if (this._cache.isLoading() || !this.clientHeight) {
422
+ if (this._dataProviderController.isLoading || !this.clientHeight) {
570
423
  this.__pendingScrollToIndexes = indexes;
571
424
  }
572
425
  }
573
426
 
574
- /**
575
- * Recursively returns the globally flat index of the item the given indexes point to.
576
- * Each index in the array points to a sub-item of the previous index.
577
- * Using `Infinity` as an index will point to the last item on the level.
578
- *
579
- * @param {!Array<number>} indexes
580
- * @param {!ItemCache} cache
581
- * @param {number} flatIndex
582
- * @return {number}
583
- * @private
584
- */
585
- __getGlobalFlatIndex([levelIndex, ...subIndexes], cache = this._cache, flatIndex = 0) {
586
- if (levelIndex === Infinity) {
587
- // Treat Infinity as the last index on the level
588
- levelIndex = cache.size - 1;
589
- }
590
- const flatIndexOnLevel = cache.getFlatIndex(levelIndex);
591
- const subCache = cache.itemCaches[levelIndex];
592
- if (subCache && subCache.effectiveSize && subIndexes.length) {
593
- return this.__getGlobalFlatIndex(subIndexes, subCache, flatIndex + flatIndexOnLevel + 1);
594
- }
595
- return flatIndex + flatIndexOnLevel;
596
- }
597
-
598
427
  /** @private */
599
428
  __scrollToPendingIndexes() {
600
429
  if (this.__pendingScrollToIndexes && this.$.items.children.length) {
@@ -475,7 +475,7 @@ export const KeyboardNavigationMixin = (superClass) =>
475
475
  // Row details navigation logic
476
476
  if (activeRowGroup === this.$.items) {
477
477
  const item = activeRow._item;
478
- const dstItem = this._cache.getItemForIndex(dstRowIndex);
478
+ const { item: dstItem } = this._dataProviderController.getFlatIndexInfo(dstRowIndex);
479
479
  // Should we navigate to row details?
480
480
  if (isRowDetails) {
481
481
  dstIsRowDetails = dy === 0;
@@ -300,8 +300,8 @@ import { StylingMixin } from './vaadin-grid-styling-mixin.js';
300
300
  */
301
301
  class Grid extends ElementMixin(
302
302
  ThemableMixin(
303
- DataProviderMixin(
304
- ArrayDataProviderMixin(
303
+ ArrayDataProviderMixin(
304
+ DataProviderMixin(
305
305
  DynamicColumnsMixin(
306
306
  ActiveItemMixin(
307
307
  ScrollMixin(
@@ -738,7 +738,7 @@ class Grid extends ElementMixin(
738
738
  if (!this._columnTree) {
739
739
  return; // No columns
740
740
  }
741
- if (isElementHidden(this) || this._cache.isLoading()) {
741
+ if (isElementHidden(this) || this._dataProviderController.isLoading) {
742
742
  this.__pendingRecalculateColumnWidths = true;
743
743
  return;
744
744
  }
@@ -751,7 +751,7 @@ class Grid extends ElementMixin(
751
751
  if (
752
752
  this.__pendingRecalculateColumnWidths &&
753
753
  !isElementHidden(this) &&
754
- !this._cache.isLoading() &&
754
+ !this._dataProviderController.isLoading &&
755
755
  this.__hasRowsWithClientHeight()
756
756
  ) {
757
757
  this.__pendingRecalculateColumnWidths = false;