@vaadin/grid 24.2.0-dev.f254716fe → 24.3.0-alpha2

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 (40) hide show
  1. package/package.json +11 -11
  2. package/src/vaadin-grid-array-data-provider-mixin.js +1 -1
  3. package/src/vaadin-grid-column-group-mixin.d.ts +20 -0
  4. package/src/vaadin-grid-column-group-mixin.js +369 -0
  5. package/src/vaadin-grid-column-group.d.ts +4 -14
  6. package/src/vaadin-grid-column-group.js +8 -356
  7. package/src/vaadin-grid-column-mixin.d.ts +156 -0
  8. package/src/vaadin-grid-column-mixin.js +887 -0
  9. package/src/vaadin-grid-column.d.ts +11 -138
  10. package/src/vaadin-grid-column.js +6 -876
  11. package/src/vaadin-grid-data-provider-mixin.d.ts +6 -5
  12. package/src/vaadin-grid-data-provider-mixin.js +51 -20
  13. package/src/vaadin-grid-drag-and-drop-mixin.js +1 -1
  14. package/src/vaadin-grid-dynamic-columns-mixin.js +1 -1
  15. package/src/vaadin-grid-filter-column.js +5 -1
  16. package/src/vaadin-grid-filter-element-mixin.d.ts +34 -0
  17. package/src/vaadin-grid-filter-element-mixin.js +99 -0
  18. package/src/vaadin-grid-filter.d.ts +4 -21
  19. package/src/vaadin-grid-filter.js +8 -85
  20. package/src/vaadin-grid-keyboard-navigation-mixin.js +24 -4
  21. package/src/vaadin-grid-mixin.d.ts +218 -0
  22. package/src/vaadin-grid-mixin.js +1022 -0
  23. package/src/vaadin-grid-scroll-mixin.js +1 -1
  24. package/src/vaadin-grid-selection-column-base-mixin.d.ts +6 -0
  25. package/src/vaadin-grid-selection-column-base-mixin.js +151 -0
  26. package/src/vaadin-grid-selection-column.js +4 -1
  27. package/src/vaadin-grid-sort-column.js +5 -1
  28. package/src/vaadin-grid-sorter-mixin.d.ts +44 -0
  29. package/src/vaadin-grid-sorter-mixin.js +198 -0
  30. package/src/vaadin-grid-sorter.d.ts +3 -32
  31. package/src/vaadin-grid-sorter.js +8 -182
  32. package/src/vaadin-grid-tree-column-mixin.d.ts +19 -0
  33. package/src/vaadin-grid-tree-column-mixin.js +92 -0
  34. package/src/vaadin-grid-tree-column.d.ts +9 -7
  35. package/src/vaadin-grid-tree-column.js +7 -82
  36. package/src/vaadin-grid-tree-toggle.js +3 -1
  37. package/src/vaadin-grid.d.ts +5 -190
  38. package/src/vaadin-grid.js +7 -1018
  39. package/web-types.json +2311 -0
  40. package/web-types.lit.json +1007 -0
@@ -4,6 +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 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';
7
11
  import { GridSorterDirection } from './vaadin-grid-sorter.js';
8
12
 
9
13
  export { GridSorterDirection };
@@ -18,7 +22,7 @@ export interface GridSorterDefinition {
18
22
  direction: GridSorterDirection;
19
23
  }
20
24
 
21
- export type GridDataProviderCallback<TItem> = (items: TItem[], size?: number) => void;
25
+ export type GridDataProviderCallback<TItem> = DataProviderCallback<TItem>;
22
26
 
23
27
  export type GridDataProviderParams<TItem> = {
24
28
  page: number;
@@ -28,10 +32,7 @@ export type GridDataProviderParams<TItem> = {
28
32
  parentItem?: TItem;
29
33
  };
30
34
 
31
- export type GridDataProvider<TItem> = (
32
- params: GridDataProviderParams<TItem>,
33
- callback: GridDataProviderCallback<TItem>,
34
- ) => void;
35
+ export type GridDataProvider<TItem> = DataProvider<TItem, GridDataProviderParams<TItem>>;
35
36
 
36
37
  export declare function DataProviderMixin<TItem, T extends Constructor<HTMLElement>>(
37
38
  base: T,
@@ -4,8 +4,9 @@
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
+ import { DataProviderController } from '@vaadin/component-base/src/data-provider-controller/data-provider-controller.js';
8
8
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
9
+ import { get } from '@vaadin/component-base/src/path-utils.js';
9
10
  import { getBodyRowCells, updateCellsPart, updateState } from './vaadin-grid-helpers.js';
10
11
 
11
12
  /**
@@ -148,10 +149,28 @@ export const DataProviderMixin = (superClass) =>
148
149
  this._dataProviderController.addEventListener('page-loaded', this._onDataProviderPageLoaded.bind(this));
149
150
  }
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
+
151
170
  /** @private */
152
171
  _sizeChanged(size) {
153
172
  this._dataProviderController.setSize(size);
154
- this._effectiveSize = this._dataProviderController.effectiveSize;
173
+ this._flatSize = this._dataProviderController.flatSize;
155
174
  }
156
175
 
157
176
  /** @private */
@@ -169,18 +188,18 @@ export const DataProviderMixin = (superClass) =>
169
188
  * @protected
170
189
  */
171
190
  _getItem(index, el) {
172
- if (index >= this._effectiveSize) {
191
+ if (index >= this._flatSize) {
173
192
  return;
174
193
  }
175
194
 
176
195
  el.index = index;
177
196
 
178
- const { item } = this._dataProviderController.getFlatIndexInfo(index);
197
+ const { item } = this._dataProviderController.getFlatIndexContext(index);
179
198
  if (item) {
180
199
  this.__updateLoading(el, false);
181
200
  this._updateItem(el, item);
182
201
  if (this._isExpanded(item)) {
183
- this._dataProviderController.ensureFlatIndexChildrenLoaded(index);
202
+ this._dataProviderController.ensureFlatIndexHierarchy(index);
184
203
  }
185
204
  } else {
186
205
  this.__updateLoading(el, true);
@@ -210,7 +229,7 @@ export const DataProviderMixin = (superClass) =>
210
229
  * @return {!GridItem | !unknown}
211
230
  */
212
231
  getItemId(item) {
213
- return this.itemIdPath ? this.get(this.itemIdPath, item) : item;
232
+ return this.itemIdPath ? get(this.itemIdPath, item) : item;
214
233
  }
215
234
 
216
235
  /**
@@ -224,8 +243,8 @@ export const DataProviderMixin = (superClass) =>
224
243
 
225
244
  /** @private */
226
245
  _expandedItemsChanged() {
227
- this._dataProviderController.recalculateEffectiveSize();
228
- this._effectiveSize = this._dataProviderController.effectiveSize;
246
+ this._dataProviderController.recalculateFlatSize();
247
+ this._flatSize = this._dataProviderController.flatSize;
229
248
  this.__updateVisibleRows();
230
249
  }
231
250
 
@@ -266,10 +285,21 @@ export const DataProviderMixin = (superClass) =>
266
285
  * @protected
267
286
  */
268
287
  _getIndexLevel(index = 0) {
269
- const { level } = this._dataProviderController.getFlatIndexInfo(index);
288
+ const { level } = this._dataProviderController.getFlatIndexContext(index);
270
289
  return level;
271
290
  }
272
291
 
292
+ /**
293
+ * @param {number} page
294
+ * @param {ItemCache} cache
295
+ * @protected
296
+ * @deprecated since 24.3 and will be removed in Vaadin 25.
297
+ */
298
+ _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
+
273
303
  /** @protected */
274
304
  _onDataProviderPageRequested() {
275
305
  this._setLoading(true);
@@ -278,11 +308,11 @@ export const DataProviderMixin = (superClass) =>
278
308
  /** @protected */
279
309
  _onDataProviderPageReceived() {
280
310
  // With the new items added, update the cache size and the grid's effective size
281
- this._effectiveSize = this._dataProviderController.effectiveSize;
311
+ this._flatSize = this._dataProviderController.flatSize;
282
312
 
283
313
  // After updating the cache, check if some of the expanded items should have sub-caches loaded
284
314
  this._getRenderedRows().forEach((row) => {
285
- this._dataProviderController.ensureFlatIndexChildrenLoaded(row.index);
315
+ this._dataProviderController.ensureFlatIndexHierarchy(row.index);
286
316
  });
287
317
 
288
318
  this._hasData = true;
@@ -295,7 +325,7 @@ export const DataProviderMixin = (superClass) =>
295
325
  this._setLoading(false);
296
326
 
297
327
  this._getRenderedRows().forEach((row) => {
298
- const { item } = this._dataProviderController.getFlatIndexInfo(row.index);
328
+ const { item } = this._dataProviderController.getFlatIndexContext(row.index);
299
329
  if (item) {
300
330
  this._getItem(row.index, row);
301
331
  }
@@ -317,22 +347,24 @@ export const DataProviderMixin = (superClass) =>
317
347
  this._dataProviderController.clearCache();
318
348
  this._hasData = false;
319
349
  this.__updateVisibleRows();
320
- this._ensureFirstPageLoaded();
350
+
351
+ if (!this._flatSize) {
352
+ this._dataProviderController.loadFirstPage();
353
+ }
321
354
  }
322
355
 
323
356
  /** @private */
324
357
  _pageSizeChanged(pageSize, oldPageSize) {
325
358
  this._dataProviderController.setPageSize(pageSize);
359
+
326
360
  if (oldPageSize !== undefined && pageSize !== oldPageSize) {
327
- this._hasData = false;
328
- this.__updateVisibleRows();
329
- this._ensureFirstPageLoaded();
361
+ this.clearCache();
330
362
  }
331
363
  }
332
364
 
333
365
  /** @protected */
334
366
  _checkSize() {
335
- if (this.size === undefined && this._effectiveSize === 0) {
367
+ if (this.size === undefined && this._flatSize === 0) {
336
368
  console.warn(
337
369
  'The <vaadin-grid> needs the total number of items in' +
338
370
  ' order to display rows, which you can specify either by setting' +
@@ -347,8 +379,7 @@ export const DataProviderMixin = (superClass) =>
347
379
  this._dataProviderController.setDataProvider(dataProvider ? dataProvider.bind(this) : null);
348
380
 
349
381
  if (oldDataProvider !== undefined) {
350
- this._hasData = false;
351
- this.__updateVisibleRows();
382
+ this.clearCache();
352
383
  }
353
384
 
354
385
  this._ensureFirstPageLoaded();
@@ -365,7 +396,7 @@ export const DataProviderMixin = (superClass) =>
365
396
  if (!this._hasData) {
366
397
  // Load data before adding rows to make sure they have content when
367
398
  // rendered for the first time.
368
- this._dataProviderController.ensureFirstPageLoaded();
399
+ this._dataProviderController.loadFirstPage();
369
400
  }
370
401
  }
371
402
 
@@ -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._effectiveSize || this.dropMode === DropMode.ON_GRID) {
214
+ if (!this._flatSize || 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) {
@@ -112,7 +112,7 @@ export const DynamicColumnsMixin = (superClass) =>
112
112
  if (hasColumnElements(info.addedNodes) || hasColumnElements(info.removedNodes)) {
113
113
  const allRemovedCells = info.removedNodes.flatMap((c) => c._allCells);
114
114
  const filterNotConnected = (element) =>
115
- allRemovedCells.filter((cell) => cell._content.contains(element)).length;
115
+ allRemovedCells.filter((cell) => cell && cell._content.contains(element)).length;
116
116
 
117
117
  this.__removeSorters(this._sorters.filter(filterNotConnected));
118
118
  this.__removeFilters(this._filters.filter(filterNotConnected));
@@ -4,6 +4,7 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import './vaadin-grid-filter.js';
7
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
7
8
  import { GridColumn } from './vaadin-grid-column.js';
8
9
 
9
10
  /**
@@ -18,6 +19,9 @@ import { GridColumn } from './vaadin-grid-column.js';
18
19
  * <vaadin-grid-column>
19
20
  * ...
20
21
  * ```
22
+ *
23
+ * @customElement
24
+ * @extends GridColumn
21
25
  */
22
26
  class GridFilterColumn extends GridColumn {
23
27
  static get is() {
@@ -114,6 +118,6 @@ class GridFilterColumn extends GridColumn {
114
118
  }
115
119
  }
116
120
 
117
- customElements.define(GridFilterColumn.is, GridFilterColumn);
121
+ defineCustomElement(GridFilterColumn);
118
122
 
119
123
  export { GridFilterColumn };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { Constructor } from '@open-wc/dedupe-mixin';
7
+ import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
8
+
9
+ /**
10
+ * Fired when the `value` property changes.
11
+ */
12
+ export type GridFilterValueChangedEvent = CustomEvent<{ value: string }>;
13
+
14
+ export interface GridFilterCustomEventMap {
15
+ 'value-changed': GridFilterValueChangedEvent;
16
+ }
17
+
18
+ export interface GridFilterEventMap extends HTMLElementEventMap, GridFilterCustomEventMap {}
19
+
20
+ export declare function GridFilterElementMixin<T extends Constructor<HTMLElement>>(
21
+ base: T,
22
+ ): Constructor<ControllerMixinClass> & Constructor<GridFilterElementMixinClass> & T;
23
+
24
+ declare class GridFilterElementMixinClass {
25
+ /**
26
+ * JS Path of the property in the item used for filtering the data.
27
+ */
28
+ path: string | null | undefined;
29
+
30
+ /**
31
+ * Current filter value.
32
+ */
33
+ value: string | null | undefined;
34
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { timeOut } from '@vaadin/component-base/src/async.js';
7
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
8
+ import { Debouncer } from '@vaadin/component-base/src/debounce.js';
9
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
10
+ import { css, registerStyles } from '@vaadin/vaadin-themable-mixin';
11
+
12
+ registerStyles(
13
+ 'vaadin-grid-filter',
14
+ css`
15
+ :host {
16
+ display: inline-flex;
17
+ max-width: 100%;
18
+ }
19
+
20
+ ::slotted(*) {
21
+ width: 100%;
22
+ box-sizing: border-box;
23
+ }
24
+ `,
25
+ { moduleId: 'vaadin-grid-filter-styles' },
26
+ );
27
+
28
+ /**
29
+ * @polymerMixin
30
+ *
31
+ * @mixes ControllerMixin
32
+ */
33
+ export const GridFilterElementMixin = (superClass) =>
34
+ class extends ControllerMixin(superClass) {
35
+ static get properties() {
36
+ return {
37
+ /**
38
+ * JS Path of the property in the item used for filtering the data.
39
+ */
40
+ path: String,
41
+
42
+ /**
43
+ * Current filter value.
44
+ */
45
+ value: {
46
+ type: String,
47
+ notify: true,
48
+ },
49
+
50
+ /** @private */
51
+ _textField: {
52
+ type: Object,
53
+ },
54
+ };
55
+ }
56
+
57
+ static get observers() {
58
+ return ['_filterChanged(path, value, _textField)'];
59
+ }
60
+
61
+ /** @protected */
62
+ ready() {
63
+ super.ready();
64
+
65
+ this._filterController = new SlotController(this, '', 'vaadin-text-field', {
66
+ initializer: (field) => {
67
+ field.addEventListener('value-changed', (e) => {
68
+ this.value = e.detail.value;
69
+ });
70
+
71
+ this._textField = field;
72
+ },
73
+ });
74
+ this.addController(this._filterController);
75
+ }
76
+
77
+ /** @private */
78
+ _filterChanged(path, value, textField) {
79
+ if (path === undefined || value === undefined || !textField) {
80
+ return;
81
+ }
82
+ if (this._previousValue === undefined && value === '') {
83
+ return;
84
+ }
85
+
86
+ textField.value = value;
87
+ this._previousValue = value;
88
+
89
+ this._debouncerFilterChanged = Debouncer.debounce(this._debouncerFilterChanged, timeOut.after(200), () => {
90
+ this.dispatchEvent(new CustomEvent('filter-changed', { bubbles: true }));
91
+ });
92
+ }
93
+
94
+ focus() {
95
+ if (this._textField) {
96
+ this._textField.focus();
97
+ }
98
+ }
99
+ };
@@ -3,18 +3,11 @@
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';
7
6
 
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
- }
7
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
8
+ import { GridFilterElementMixin, type GridFilterEventMap } from './vaadin-grid-filter-element-mixin.js';
16
9
 
17
- export interface GridFilterEventMap extends HTMLElementEventMap, GridFilterCustomEventMap {}
10
+ export * from './vaadin-grid-filter-element-mixin.js';
18
11
 
19
12
  /**
20
13
  * `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
@@ -41,17 +34,7 @@ export interface GridFilterEventMap extends HTMLElementEventMap, GridFilterCusto
41
34
  *
42
35
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
43
36
  */
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
-
37
+ declare class GridFilter extends GridFilterElementMixin(ThemableMixin(HTMLElement)) {
55
38
  addEventListener<K extends keyof GridFilterEventMap>(
56
39
  type: K,
57
40
  listener: (this: GridFilter, ev: GridFilterEventMap[K]) => void,
@@ -5,10 +5,9 @@
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';
11
- import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
8
+ 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
11
 
13
12
  /**
14
13
  * `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
@@ -35,96 +34,20 @@ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
35
34
  *
36
35
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
37
36
  *
37
+ * @customElement
38
38
  * @extends HTMLElement
39
+ * @mixes GridFilterElementMixin
39
40
  */
40
- class GridFilter extends ControllerMixin(PolymerElement) {
41
+ class GridFilter extends GridFilterElementMixin(ThemableMixin(PolymerElement)) {
41
42
  static get template() {
42
- return html`
43
- <style>
44
- :host {
45
- display: inline-flex;
46
- max-width: 100%;
47
- }
48
-
49
- ::slotted(*) {
50
- width: 100%;
51
- box-sizing: border-box;
52
- }
53
- </style>
54
- <slot></slot>
55
- `;
43
+ return html`<slot></slot>`;
56
44
  }
57
45
 
58
46
  static get is() {
59
47
  return 'vaadin-grid-filter';
60
48
  }
61
-
62
- static get properties() {
63
- return {
64
- /**
65
- * JS Path of the property in the item used for filtering the data.
66
- */
67
- path: String,
68
-
69
- /**
70
- * Current filter value.
71
- */
72
- value: {
73
- type: String,
74
- notify: true,
75
- },
76
-
77
- /** @private */
78
- _textField: {
79
- type: Object,
80
- },
81
- };
82
- }
83
-
84
- static get observers() {
85
- return ['_filterChanged(path, value, _textField)'];
86
- }
87
-
88
- /** @protected */
89
- ready() {
90
- super.ready();
91
-
92
- this._filterController = new SlotController(this, '', 'vaadin-text-field', {
93
- initializer: (field) => {
94
- field.addEventListener('value-changed', (e) => {
95
- this.value = e.detail.value;
96
- });
97
-
98
- this._textField = field;
99
- },
100
- });
101
- this.addController(this._filterController);
102
- }
103
-
104
- /** @private */
105
- _filterChanged(path, value, textField) {
106
- if (path === undefined || value === undefined || !textField) {
107
- return;
108
- }
109
- if (this._previousValue === undefined && value === '') {
110
- return;
111
- }
112
-
113
- textField.value = value;
114
- this._previousValue = value;
115
-
116
- this._debouncerFilterChanged = Debouncer.debounce(this._debouncerFilterChanged, timeOut.after(200), () => {
117
- this.dispatchEvent(new CustomEvent('filter-changed', { bubbles: true }));
118
- });
119
- }
120
-
121
- focus() {
122
- if (this._textField) {
123
- this._textField.focus();
124
- }
125
- }
126
49
  }
127
50
 
128
- customElements.define(GridFilter.is, GridFilter);
51
+ defineCustomElement(GridFilter);
129
52
 
130
53
  export { GridFilter };
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
7
7
  import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
8
+ import { get } from '@vaadin/component-base/src/path-utils.js';
8
9
 
9
10
  /**
10
11
  * @polymerMixin
@@ -266,7 +267,7 @@ export const KeyboardNavigationMixin = (superClass) =>
266
267
  __isRowExpandable(row) {
267
268
  if (this.itemHasChildrenPath) {
268
269
  const item = row._item;
269
- return item && this.get(this.itemHasChildrenPath, item) && !this._isExpanded(item);
270
+ return item && get(this.itemHasChildrenPath, item) && !this._isExpanded(item);
270
271
  }
271
272
  }
272
273
 
@@ -442,7 +443,7 @@ export const KeyboardNavigationMixin = (superClass) =>
442
443
  __navigateRows(dy, activeRow, activeCell) {
443
444
  const currentRowIndex = this.__getIndexInGroup(activeRow, this._focusedItemIndex);
444
445
  const activeRowGroup = activeRow.parentNode;
445
- const maxRowIndex = (activeRowGroup === this.$.items ? this._effectiveSize : activeRowGroup.children.length) - 1;
446
+ const maxRowIndex = (activeRowGroup === this.$.items ? this._flatSize : activeRowGroup.children.length) - 1;
446
447
 
447
448
  // Index of the destination row
448
449
  let dstRowIndex = Math.max(0, Math.min(currentRowIndex + dy, maxRowIndex));
@@ -475,7 +476,7 @@ export const KeyboardNavigationMixin = (superClass) =>
475
476
  // Row details navigation logic
476
477
  if (activeRowGroup === this.$.items) {
477
478
  const item = activeRow._item;
478
- const { item: dstItem } = this._dataProviderController.getFlatIndexInfo(dstRowIndex);
479
+ const { item: dstItem } = this._dataProviderController.getFlatIndexContext(dstRowIndex);
479
480
  // Should we navigate to row details?
480
481
  if (isRowDetails) {
481
482
  dstIsRowDetails = dy === 0;
@@ -681,7 +682,26 @@ export const KeyboardNavigationMixin = (superClass) =>
681
682
  }
682
683
  }
683
684
 
684
- return tabOrder[index];
685
+ let focusStepTarget = tabOrder[index];
686
+
687
+ // If the target focusable is tied to a column that is not visible,
688
+ // find the first visible column and update the target in order to
689
+ // prevent scrolling to the start of the row.
690
+ if (focusStepTarget && focusStepTarget._column && !this.__isColumnInViewport(focusStepTarget._column)) {
691
+ const firstVisibleColumn = this._getColumnsInOrder().find((column) => this.__isColumnInViewport(column));
692
+ if (firstVisibleColumn) {
693
+ if (focusStepTarget === this._headerFocusable) {
694
+ focusStepTarget = firstVisibleColumn._headerCell;
695
+ } else if (focusStepTarget === this._itemsFocusable) {
696
+ const rowIndex = focusStepTarget._column._cells.indexOf(focusStepTarget);
697
+ focusStepTarget = firstVisibleColumn._cells[rowIndex];
698
+ } else if (focusStepTarget === this._footerFocusable) {
699
+ focusStepTarget = firstVisibleColumn._footerCell;
700
+ }
701
+ }
702
+ }
703
+
704
+ return focusStepTarget;
685
705
  }
686
706
 
687
707
  /** @private */