@vaadin/grid 24.2.0-beta3 → 24.2.0-beta4

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.
Files changed (34) hide show
  1. package/package.json +10 -10
  2. package/src/vaadin-grid-array-data-provider-mixin.js +1 -1
  3. package/src/vaadin-grid-column-group.d.ts +14 -4
  4. package/src/vaadin-grid-column-group.js +355 -5
  5. package/src/vaadin-grid-column.d.ts +138 -11
  6. package/src/vaadin-grid-column.js +876 -3
  7. package/src/vaadin-grid-data-provider-mixin.d.ts +30 -6
  8. package/src/vaadin-grid-data-provider-mixin.js +241 -100
  9. package/src/vaadin-grid-drag-and-drop-mixin.js +1 -1
  10. package/src/vaadin-grid-filter.d.ts +21 -4
  11. package/src/vaadin-grid-filter.js +84 -5
  12. package/src/vaadin-grid-keyboard-navigation-mixin.js +2 -2
  13. package/src/vaadin-grid-mixin.js +10 -10
  14. package/src/vaadin-grid-scroll-mixin.js +1 -1
  15. package/src/vaadin-grid-sorter.d.ts +32 -3
  16. package/src/vaadin-grid-sorter.js +181 -5
  17. package/src/vaadin-grid-tree-column.d.ts +7 -9
  18. package/src/vaadin-grid-tree-column.js +82 -3
  19. package/src/vaadin-grid-tree-toggle.d.ts +27 -4
  20. package/src/vaadin-grid-tree-toggle.js +141 -9
  21. package/web-types.json +98 -98
  22. package/web-types.lit.json +42 -42
  23. package/src/vaadin-grid-column-group-mixin.d.ts +0 -20
  24. package/src/vaadin-grid-column-group-mixin.js +0 -369
  25. package/src/vaadin-grid-column-mixin.d.ts +0 -156
  26. package/src/vaadin-grid-column-mixin.js +0 -887
  27. package/src/vaadin-grid-filter-element-mixin.d.ts +0 -34
  28. package/src/vaadin-grid-filter-element-mixin.js +0 -99
  29. package/src/vaadin-grid-sorter-mixin.d.ts +0 -44
  30. package/src/vaadin-grid-sorter-mixin.js +0 -198
  31. package/src/vaadin-grid-tree-column-mixin.d.ts +0 -19
  32. package/src/vaadin-grid-tree-column-mixin.js +0 -92
  33. package/src/vaadin-grid-tree-toggle-mixin.d.ts +0 -39
  34. package/src/vaadin-grid-tree-toggle-mixin.js +0 -151
@@ -4,10 +4,6 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
7
- import type {
8
- DataProvider,
9
- DataProviderCallback,
10
- } from '@vaadin/component-base/src/data-provider-controller/data-provider-controller.js';
11
7
  import { GridSorterDirection } from './vaadin-grid-sorter.js';
12
8
 
13
9
  export { GridSorterDirection };
@@ -22,7 +18,7 @@ export interface GridSorterDefinition {
22
18
  direction: GridSorterDirection;
23
19
  }
24
20
 
25
- export type GridDataProviderCallback<TItem> = DataProviderCallback<TItem>;
21
+ export type GridDataProviderCallback<TItem> = (items: TItem[], size?: number) => void;
26
22
 
27
23
  export type GridDataProviderParams<TItem> = {
28
24
  page: number;
@@ -32,7 +28,35 @@ export type GridDataProviderParams<TItem> = {
32
28
  parentItem?: TItem;
33
29
  };
34
30
 
35
- export type GridDataProvider<TItem> = DataProvider<TItem, GridDataProviderParams<TItem>>;
31
+ export type GridDataProvider<TItem> = (
32
+ params: GridDataProviderParams<TItem>,
33
+ callback: GridDataProviderCallback<TItem>,
34
+ ) => void;
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
+ }
36
60
 
37
61
  export declare function DataProviderMixin<TItem, T extends Constructor<HTMLElement>>(
38
62
  base: T,
@@ -4,11 +4,116 @@
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/data-provider-controller.js';
8
7
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
9
8
  import { get } from '@vaadin/component-base/src/path-utils.js';
10
9
  import { getBodyRowCells, updateCellsPart, updateState } from './vaadin-grid-helpers.js';
11
10
 
11
+ /**
12
+ * @private
13
+ */
14
+ export const ItemCache = class ItemCache {
15
+ /**
16
+ * @param {!HTMLElement} grid
17
+ * @param {!ItemCache | undefined} parentCache
18
+ * @param {!GridItem | undefined} parentItem
19
+ */
20
+ constructor(grid, parentCache, parentItem) {
21
+ /** @type {!HTMLElement} */
22
+ this.grid = grid;
23
+ /** @type {!ItemCache | undefined} */
24
+ this.parentCache = parentCache;
25
+ /** @type {!GridItem | undefined} */
26
+ this.parentItem = parentItem;
27
+ /** @type {object} */
28
+ this.itemCaches = {};
29
+ /** @type {object[]} */
30
+ this.items = [];
31
+ /** @type {number} */
32
+ this.effectiveSize = 0;
33
+ /** @type {number} */
34
+ this.size = 0;
35
+ /** @type {object} */
36
+ this.pendingRequests = {};
37
+ }
38
+
39
+ /**
40
+ * @return {boolean}
41
+ */
42
+ isLoading() {
43
+ return Boolean(
44
+ Object.keys(this.pendingRequests).length ||
45
+ Object.keys(this.itemCaches).filter((index) => {
46
+ return this.itemCaches[index].isLoading();
47
+ })[0],
48
+ );
49
+ }
50
+
51
+ /**
52
+ * @param {number} index
53
+ * @return {!GridItem | undefined}
54
+ */
55
+ getItemForIndex(index) {
56
+ const { cache, scaledIndex } = this.getCacheAndIndex(index);
57
+ return cache.items[scaledIndex];
58
+ }
59
+
60
+ updateSize() {
61
+ this.effectiveSize =
62
+ !this.parentItem || this.grid._isExpanded(this.parentItem)
63
+ ? this.size +
64
+ Object.keys(this.itemCaches).reduce((prev, curr) => {
65
+ const subCache = this.itemCaches[curr];
66
+ subCache.updateSize();
67
+ return prev + subCache.effectiveSize;
68
+ }, 0)
69
+ : 0;
70
+ }
71
+
72
+ /**
73
+ * @param {number} scaledIndex
74
+ */
75
+ ensureSubCacheForScaledIndex(scaledIndex) {
76
+ if (!this.itemCaches[scaledIndex]) {
77
+ const subCache = new ItemCache(this.grid, this, this.items[scaledIndex]);
78
+ this.itemCaches[scaledIndex] = subCache;
79
+ this.grid._loadPage(0, subCache);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @param {number} index
85
+ * @return {{cache: !ItemCache, scaledIndex: number}}
86
+ */
87
+ getCacheAndIndex(index) {
88
+ let thisLevelIndex = index;
89
+ for (const [index, subCache] of Object.entries(this.itemCaches)) {
90
+ const numberIndex = Number(index);
91
+ if (thisLevelIndex <= numberIndex) {
92
+ return { cache: this, scaledIndex: thisLevelIndex };
93
+ } else if (thisLevelIndex <= numberIndex + subCache.effectiveSize) {
94
+ return subCache.getCacheAndIndex(thisLevelIndex - numberIndex - 1);
95
+ }
96
+ thisLevelIndex -= subCache.effectiveSize;
97
+ }
98
+ return { cache: this, scaledIndex: thisLevelIndex };
99
+ }
100
+
101
+ /**
102
+ * Gets the scaled index as flattened index on this cache level.
103
+ * In practice, this means that the effective size of any expanded
104
+ * subcaches preceding the index are added to the value.
105
+ * @param {number} scaledIndex
106
+ * @return {number} The flat index on this cache level.
107
+ */
108
+ getFlatIndex(scaledIndex) {
109
+ const clampedIndex = Math.max(0, Math.min(this.size - 1, scaledIndex));
110
+
111
+ return Object.entries(this.itemCaches).reduce((prev, [index, subCache]) => {
112
+ return clampedIndex > Number(index) ? prev + subCache.effectiveSize : prev;
113
+ }, clampedIndex);
114
+ }
115
+ };
116
+
12
117
  /**
13
118
  * @polymerMixin
14
119
  */
@@ -76,6 +181,18 @@ export const DataProviderMixin = (superClass) =>
76
181
  reflectToAttribute: true,
77
182
  },
78
183
 
184
+ /**
185
+ * @type {!ItemCache}
186
+ * @protected
187
+ */
188
+ _cache: {
189
+ type: Object,
190
+ value() {
191
+ const cache = new ItemCache(this);
192
+ return cache;
193
+ },
194
+ },
195
+
79
196
  /**
80
197
  * @protected
81
198
  */
@@ -127,50 +244,12 @@ export const DataProviderMixin = (superClass) =>
127
244
  return ['_sizeChanged(size)', '_expandedItemsChanged(expandedItems.*)'];
128
245
  }
129
246
 
130
- constructor() {
131
- super();
132
-
133
- /** @type {DataProviderController} */
134
- this._dataProviderController = new DataProviderController(this, {
135
- size: this.size,
136
- pageSize: this.pageSize,
137
- isExpanded: this._isExpanded.bind(this),
138
- dataProvider: this.dataProvider ? this.dataProvider.bind(this) : null,
139
- dataProviderParams: () => {
140
- return {
141
- sortOrders: this._mapSorters(),
142
- filters: this._mapFilters(),
143
- };
144
- },
145
- });
146
-
147
- this._dataProviderController.addEventListener('page-requested', this._onDataProviderPageRequested.bind(this));
148
- this._dataProviderController.addEventListener('page-received', this._onDataProviderPageReceived.bind(this));
149
- this._dataProviderController.addEventListener('page-loaded', this._onDataProviderPageLoaded.bind(this));
150
- }
151
-
152
- /**
153
- * @protected
154
- * @deprecated since 24.3 and will be removed in Vaadin 25.
155
- */
156
- get _cache() {
157
- console.warn('<vaadin-grid> The `_cache` property is deprecated and will be removed in Vaadin 25.');
158
- return this._dataProviderController.rootCache;
159
- }
160
-
161
- /**
162
- * @protected
163
- * @deprecated since 24.3 and will be removed in Vaadin 25.
164
- */
165
- get _effectiveSize() {
166
- console.warn('<vaadin-grid> The `_effectiveSize` property is deprecated and will be removed in Vaadin 25.');
167
- return this._flatSize;
168
- }
169
-
170
247
  /** @private */
171
248
  _sizeChanged(size) {
172
- this._dataProviderController.setSize(size);
173
- this._flatSize = this._dataProviderController.flatSize;
249
+ const delta = size - this._cache.size;
250
+ this._cache.size += delta;
251
+ this._cache.effectiveSize += delta;
252
+ this._effectiveSize = this._cache.effectiveSize;
174
253
  }
175
254
 
176
255
  /** @private */
@@ -188,22 +267,22 @@ export const DataProviderMixin = (superClass) =>
188
267
  * @protected
189
268
  */
190
269
  _getItem(index, el) {
191
- if (index >= this._flatSize) {
270
+ if (index >= this._effectiveSize) {
192
271
  return;
193
272
  }
194
273
 
195
274
  el.index = index;
196
-
197
- const { item } = this._dataProviderController.getFlatIndexContext(index);
275
+ const { cache, scaledIndex } = this._cache.getCacheAndIndex(index);
276
+ const item = cache.items[scaledIndex];
198
277
  if (item) {
199
278
  this.__updateLoading(el, false);
200
279
  this._updateItem(el, item);
201
280
  if (this._isExpanded(item)) {
202
- this._dataProviderController.ensureFlatIndexHierarchy(index);
281
+ cache.ensureSubCacheForScaledIndex(scaledIndex);
203
282
  }
204
283
  } else {
205
284
  this.__updateLoading(el, true);
206
- this._dataProviderController.ensureFlatIndexLoaded(index);
285
+ this._loadPage(this._getPageForIndex(scaledIndex), cache);
207
286
  }
208
287
  }
209
288
 
@@ -243,8 +322,8 @@ export const DataProviderMixin = (superClass) =>
243
322
 
244
323
  /** @private */
245
324
  _expandedItemsChanged() {
246
- this._dataProviderController.recalculateFlatSize();
247
- this._flatSize = this._dataProviderController.flatSize;
325
+ this._cache.updateSize();
326
+ this._effectiveSize = this._cache.effectiveSize;
248
327
  this.__updateVisibleRows();
249
328
  }
250
329
 
@@ -284,8 +363,13 @@ export const DataProviderMixin = (superClass) =>
284
363
  * @return {number}
285
364
  * @protected
286
365
  */
287
- _getIndexLevel(index = 0) {
288
- const { level } = this._dataProviderController.getFlatIndexContext(index);
366
+ _getIndexLevel(index) {
367
+ let { cache } = this._cache.getCacheAndIndex(index);
368
+ let level = 0;
369
+ while (cache.parentCache) {
370
+ cache = cache.parentCache;
371
+ level += 1;
372
+ }
289
373
  return level;
290
374
  }
291
375
 
@@ -293,70 +377,105 @@ export const DataProviderMixin = (superClass) =>
293
377
  * @param {number} page
294
378
  * @param {ItemCache} cache
295
379
  * @protected
296
- * @deprecated since 24.3 and will be removed in Vaadin 25.
297
380
  */
298
381
  _loadPage(page, cache) {
299
- console.warn('<vaadin-grid> The `_loadPage` method is deprecated and will be removed in Vaadin 25.');
300
- this._dataProviderController.__loadCachePage(cache, page);
301
- }
302
-
303
- /** @protected */
304
- _onDataProviderPageRequested() {
305
- this._setLoading(true);
306
- }
307
-
308
- /** @protected */
309
- _onDataProviderPageReceived() {
310
- // With the new items added, update the cache size and the grid's effective size
311
- this._flatSize = this._dataProviderController.flatSize;
382
+ // Make sure same page isn't requested multiple times.
383
+ if (!cache.pendingRequests[page] && this.dataProvider) {
384
+ this._setLoading(true);
385
+ cache.pendingRequests[page] = true;
386
+ const params = {
387
+ page,
388
+ pageSize: this.pageSize,
389
+ sortOrders: this._mapSorters(),
390
+ filters: this._mapFilters(),
391
+ parentItem: cache.parentItem,
392
+ };
393
+
394
+ this.dataProvider(params, (items, size) => {
395
+ if (size !== undefined) {
396
+ cache.size = size;
397
+ } else if (params.parentItem) {
398
+ cache.size = items.length;
399
+ }
312
400
 
313
- // After updating the cache, check if some of the expanded items should have sub-caches loaded
314
- this._getRenderedRows().forEach((row) => {
315
- this._dataProviderController.ensureFlatIndexHierarchy(row.index);
316
- });
401
+ // Populate the cache with new items
402
+ items.forEach((item, itemsIndex) => {
403
+ const itemIndex = page * this.pageSize + itemsIndex;
404
+ cache.items[itemIndex] = item;
405
+ });
406
+
407
+ // With the new items added, update the cache size and the grid's effective size
408
+ this._cache.updateSize();
409
+ this._effectiveSize = this._cache.effectiveSize;
410
+
411
+ // After updating the cache, check if some of the expanded items should have sub-caches loaded
412
+ this._getRenderedRows().forEach((row) => {
413
+ const { cache, scaledIndex } = this._cache.getCacheAndIndex(row.index);
414
+ const item = cache.items[scaledIndex];
415
+ if (item && this._isExpanded(item)) {
416
+ cache.ensureSubCacheForScaledIndex(scaledIndex);
417
+ }
418
+ });
419
+
420
+ this._hasData = true;
421
+
422
+ // Remove the pending request
423
+ delete cache.pendingRequests[page];
424
+
425
+ // Schedule a debouncer to update the visible rows
426
+ this._debouncerApplyCachedData = Debouncer.debounce(this._debouncerApplyCachedData, timeOut.after(0), () => {
427
+ this._setLoading(false);
428
+
429
+ this._getRenderedRows().forEach((row) => {
430
+ const cachedItem = this._cache.getItemForIndex(row.index);
431
+ if (cachedItem) {
432
+ this._getItem(row.index, row);
433
+ }
434
+ });
435
+
436
+ this.__scrollToPendingIndexes();
437
+ });
438
+
439
+ // If the grid is not loading anything, flush the debouncer immediately
440
+ if (!this._cache.isLoading()) {
441
+ this._debouncerApplyCachedData.flush();
442
+ }
317
443
 
318
- this._hasData = true;
444
+ // Notify that new data has been received
445
+ this._onDataProviderPageLoaded();
446
+ });
447
+ }
319
448
  }
320
449
 
321
450
  /** @protected */
322
- _onDataProviderPageLoaded() {
323
- // Schedule a debouncer to update the visible rows
324
- this._debouncerApplyCachedData = Debouncer.debounce(this._debouncerApplyCachedData, timeOut.after(0), () => {
325
- this._setLoading(false);
326
-
327
- this._getRenderedRows().forEach((row) => {
328
- const { item } = this._dataProviderController.getFlatIndexContext(row.index);
329
- if (item) {
330
- this._getItem(row.index, row);
331
- }
332
- });
333
-
334
- this.__scrollToPendingIndexes();
335
- });
451
+ _onDataProviderPageLoaded() {}
336
452
 
337
- // If the grid is not loading anything, flush the debouncer immediately
338
- if (!this._dataProviderController.isLoading()) {
339
- this._debouncerApplyCachedData.flush();
340
- }
453
+ /**
454
+ * @param {number} index
455
+ * @return {number}
456
+ * @private
457
+ */
458
+ _getPageForIndex(index) {
459
+ return Math.floor(index / this.pageSize);
341
460
  }
342
461
 
343
462
  /**
344
463
  * Clears the cached pages and reloads data from dataprovider when needed.
345
464
  */
346
465
  clearCache() {
347
- this._dataProviderController.clearCache();
466
+ this._cache = new ItemCache(this);
467
+ this._cache.size = this.size || 0;
468
+ this._cache.updateSize();
348
469
  this._hasData = false;
349
470
  this.__updateVisibleRows();
350
471
 
351
- if (!this._flatSize) {
352
- this._dataProviderController.loadFirstPage();
472
+ if (!this._effectiveSize) {
473
+ this._loadPage(0, this._cache);
353
474
  }
354
475
  }
355
476
 
356
477
  /** @private */
357
478
  _pageSizeChanged(pageSize, oldPageSize) {
358
- this._dataProviderController.setPageSize(pageSize);
359
-
360
479
  if (oldPageSize !== undefined && pageSize !== oldPageSize) {
361
480
  this.clearCache();
362
481
  }
@@ -364,7 +483,7 @@ export const DataProviderMixin = (superClass) =>
364
483
 
365
484
  /** @protected */
366
485
  _checkSize() {
367
- if (this.size === undefined && this._flatSize === 0) {
486
+ if (this.size === undefined && this._effectiveSize === 0) {
368
487
  console.warn(
369
488
  'The <vaadin-grid> needs the total number of items in' +
370
489
  ' order to display rows, which you can specify either by setting' +
@@ -376,8 +495,6 @@ export const DataProviderMixin = (superClass) =>
376
495
 
377
496
  /** @private */
378
497
  _dataProviderChanged(dataProvider, oldDataProvider) {
379
- this._dataProviderController.setDataProvider(dataProvider ? dataProvider.bind(this) : null);
380
-
381
498
  if (oldDataProvider !== undefined) {
382
499
  this.clearCache();
383
500
  }
@@ -396,7 +513,7 @@ export const DataProviderMixin = (superClass) =>
396
513
  if (!this._hasData) {
397
514
  // Load data before adding rows to make sure they have content when
398
515
  // rendered for the first time.
399
- this._dataProviderController.loadFirstPage();
516
+ this._loadPage(0, this._cache);
400
517
  }
401
518
  }
402
519
 
@@ -446,15 +563,39 @@ export const DataProviderMixin = (superClass) =>
446
563
  // ending up in a loading state. Try scrolling to the index until the target
447
564
  // index stabilizes.
448
565
  let targetIndex;
449
- while (targetIndex !== (targetIndex = this._dataProviderController.getFlatIndexByPath(indexes))) {
566
+ while (targetIndex !== (targetIndex = this.__getGlobalFlatIndex(indexes))) {
450
567
  this._scrollToFlatIndex(targetIndex);
451
568
  }
452
569
 
453
- if (this._dataProviderController.isLoading() || !this.clientHeight) {
570
+ if (this._cache.isLoading() || !this.clientHeight) {
454
571
  this.__pendingScrollToIndexes = indexes;
455
572
  }
456
573
  }
457
574
 
575
+ /**
576
+ * Recursively returns the globally flat index of the item the given indexes point to.
577
+ * Each index in the array points to a sub-item of the previous index.
578
+ * Using `Infinity` as an index will point to the last item on the level.
579
+ *
580
+ * @param {!Array<number>} indexes
581
+ * @param {!ItemCache} cache
582
+ * @param {number} flatIndex
583
+ * @return {number}
584
+ * @private
585
+ */
586
+ __getGlobalFlatIndex([levelIndex, ...subIndexes], cache = this._cache, flatIndex = 0) {
587
+ if (levelIndex === Infinity) {
588
+ // Treat Infinity as the last index on the level
589
+ levelIndex = cache.size - 1;
590
+ }
591
+ const flatIndexOnLevel = cache.getFlatIndex(levelIndex);
592
+ const subCache = cache.itemCaches[levelIndex];
593
+ if (subCache && subCache.effectiveSize && subIndexes.length) {
594
+ return this.__getGlobalFlatIndex(subIndexes, subCache, flatIndex + flatIndexOnLevel + 1);
595
+ }
596
+ return flatIndex + flatIndexOnLevel;
597
+ }
598
+
458
599
  /** @private */
459
600
  __scrollToPendingIndexes() {
460
601
  if (this.__pendingScrollToIndexes && this.$.items.children.length) {
@@ -211,7 +211,7 @@ export const DragAndDropMixin = (superClass) =>
211
211
 
212
212
  let row = e.composedPath().find((node) => node.localName === 'tr');
213
213
 
214
- if (!this._flatSize || this.dropMode === DropMode.ON_GRID) {
214
+ if (!this._effectiveSize || this.dropMode === DropMode.ON_GRID) {
215
215
  // The grid is empty or "on-grid" drop mode was used, always default to "empty"
216
216
  this._dropLocation = DropLocation.EMPTY;
217
217
  } else if (!row || row.parentNode !== this.$.items) {
@@ -3,11 +3,18 @@
3
3
  * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
6
7
 
7
- import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
8
- import { GridFilterElementMixin, type GridFilterEventMap } from './vaadin-grid-filter-element-mixin.js';
8
+ /**
9
+ * Fired when the `value` property changes.
10
+ */
11
+ export type GridFilterValueChangedEvent = CustomEvent<{ value: string }>;
12
+
13
+ export interface GridFilterCustomEventMap {
14
+ 'value-changed': GridFilterValueChangedEvent;
15
+ }
9
16
 
10
- export * from './vaadin-grid-filter-element-mixin.js';
17
+ export interface GridFilterEventMap extends HTMLElementEventMap, GridFilterCustomEventMap {}
11
18
 
12
19
  /**
13
20
  * `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
@@ -34,7 +41,17 @@ export * from './vaadin-grid-filter-element-mixin.js';
34
41
  *
35
42
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
36
43
  */
37
- declare class GridFilter extends GridFilterElementMixin(ThemableMixin(HTMLElement)) {
44
+ declare class GridFilter extends ControllerMixin(HTMLElement) {
45
+ /**
46
+ * JS Path of the property in the item used for filtering the data.
47
+ */
48
+ path: string | null | undefined;
49
+
50
+ /**
51
+ * Current filter value.
52
+ */
53
+ value: string | null | undefined;
54
+
38
55
  addEventListener<K extends keyof GridFilterEventMap>(
39
56
  type: K,
40
57
  listener: (this: GridFilter, ev: GridFilterEventMap[K]) => void,
@@ -5,9 +5,11 @@
5
5
  */
6
6
  import '@vaadin/text-field/src/vaadin-text-field.js';
7
7
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
8
+ import { timeOut } from '@vaadin/component-base/src/async.js';
9
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
10
+ import { Debouncer } from '@vaadin/component-base/src/debounce.js';
8
11
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
9
- import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
10
- import { GridFilterElementMixin } from './vaadin-grid-filter-element-mixin.js';
12
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
11
13
 
12
14
  /**
13
15
  * `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
@@ -36,16 +38,93 @@ import { GridFilterElementMixin } from './vaadin-grid-filter-element-mixin.js';
36
38
  *
37
39
  * @customElement
38
40
  * @extends HTMLElement
39
- * @mixes GridFilterElementMixin
40
41
  */
41
- class GridFilter extends GridFilterElementMixin(ThemableMixin(PolymerElement)) {
42
+ class GridFilter extends ControllerMixin(PolymerElement) {
42
43
  static get template() {
43
- return html`<slot></slot>`;
44
+ return html`
45
+ <style>
46
+ :host {
47
+ display: inline-flex;
48
+ max-width: 100%;
49
+ }
50
+
51
+ ::slotted(*) {
52
+ width: 100%;
53
+ box-sizing: border-box;
54
+ }
55
+ </style>
56
+ <slot></slot>
57
+ `;
44
58
  }
45
59
 
46
60
  static get is() {
47
61
  return 'vaadin-grid-filter';
48
62
  }
63
+
64
+ static get properties() {
65
+ return {
66
+ /**
67
+ * JS Path of the property in the item used for filtering the data.
68
+ */
69
+ path: String,
70
+
71
+ /**
72
+ * Current filter value.
73
+ */
74
+ value: {
75
+ type: String,
76
+ notify: true,
77
+ },
78
+
79
+ /** @private */
80
+ _textField: {
81
+ type: Object,
82
+ },
83
+ };
84
+ }
85
+
86
+ static get observers() {
87
+ return ['_filterChanged(path, value, _textField)'];
88
+ }
89
+
90
+ /** @protected */
91
+ ready() {
92
+ super.ready();
93
+
94
+ this._filterController = new SlotController(this, '', 'vaadin-text-field', {
95
+ initializer: (field) => {
96
+ field.addEventListener('value-changed', (e) => {
97
+ this.value = e.detail.value;
98
+ });
99
+
100
+ this._textField = field;
101
+ },
102
+ });
103
+ this.addController(this._filterController);
104
+ }
105
+
106
+ /** @private */
107
+ _filterChanged(path, value, textField) {
108
+ if (path === undefined || value === undefined || !textField) {
109
+ return;
110
+ }
111
+ if (this._previousValue === undefined && value === '') {
112
+ return;
113
+ }
114
+
115
+ textField.value = value;
116
+ this._previousValue = value;
117
+
118
+ this._debouncerFilterChanged = Debouncer.debounce(this._debouncerFilterChanged, timeOut.after(200), () => {
119
+ this.dispatchEvent(new CustomEvent('filter-changed', { bubbles: true }));
120
+ });
121
+ }
122
+
123
+ focus() {
124
+ if (this._textField) {
125
+ this._textField.focus();
126
+ }
127
+ }
49
128
  }
50
129
 
51
130
  defineCustomElement(GridFilter);