@vaadin/grid 25.0.0-alpha9 → 25.0.0-beta2

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 (84) hide show
  1. package/all-imports.js +1 -1
  2. package/package.json +12 -15
  3. package/src/array-data-provider.js +3 -2
  4. package/src/styles/vaadin-grid-base-styles.js +256 -91
  5. package/src/styles/vaadin-grid-sorter-base-styles.js +15 -2
  6. package/src/styles/vaadin-grid-tree-toggle-base-styles.js +4 -4
  7. package/src/vaadin-grid-a11y-mixin.js +26 -10
  8. package/src/vaadin-grid-column-auto-width-mixin.js +6 -11
  9. package/src/vaadin-grid-column-mixin.d.ts +1 -0
  10. package/src/vaadin-grid-column-mixin.js +1 -16
  11. package/src/vaadin-grid-column-reordering-mixin.js +2 -2
  12. package/src/vaadin-grid-data-provider-mixin.js +15 -10
  13. package/src/vaadin-grid-drag-and-drop-mixin.js +1 -1
  14. package/src/vaadin-grid-event-context-mixin.d.ts +2 -0
  15. package/src/vaadin-grid-event-context-mixin.js +1 -0
  16. package/src/vaadin-grid-filter.js +1 -1
  17. package/src/vaadin-grid-helpers.js +9 -0
  18. package/src/vaadin-grid-keyboard-navigation-mixin.js +1 -5
  19. package/src/vaadin-grid-mixin.d.ts +1 -0
  20. package/src/vaadin-grid-mixin.js +36 -38
  21. package/src/vaadin-grid-resize-mixin.d.ts +10 -0
  22. package/src/vaadin-grid-resize-mixin.js +73 -0
  23. package/src/vaadin-grid-row-details-mixin.js +1 -5
  24. package/src/vaadin-grid-scroll-mixin.js +5 -87
  25. package/src/vaadin-grid-selection-column-base-mixin.d.ts +1 -1
  26. package/src/vaadin-grid-selection-column-base-mixin.js +1 -1
  27. package/src/vaadin-grid-selection-column.d.ts +2 -0
  28. package/src/vaadin-grid-selection-column.js +2 -1
  29. package/src/vaadin-grid-sort-column.d.ts +2 -0
  30. package/src/vaadin-grid-sorter.d.ts +3 -3
  31. package/src/vaadin-grid-sorter.js +4 -4
  32. package/src/vaadin-grid-tree-column-mixin.js +7 -8
  33. package/src/vaadin-grid-tree-toggle.d.ts +4 -4
  34. package/src/vaadin-grid-tree-toggle.js +5 -5
  35. package/src/vaadin-grid.d.ts +1 -0
  36. package/src/vaadin-grid.js +3 -1
  37. package/vaadin-grid-column-group.js +1 -1
  38. package/vaadin-grid-column.js +1 -1
  39. package/vaadin-grid-filter-column.js +1 -1
  40. package/vaadin-grid-filter.js +1 -1
  41. package/vaadin-grid-selection-column.js +1 -1
  42. package/vaadin-grid-sort-column.js +1 -1
  43. package/vaadin-grid-sorter.js +1 -1
  44. package/vaadin-grid-tree-column.js +1 -1
  45. package/vaadin-grid-tree-toggle.js +1 -1
  46. package/vaadin-grid.js +1 -1
  47. package/web-types.json +13 -13
  48. package/web-types.lit.json +12 -12
  49. package/src/styles/vaadin-grid-core-styles.d.ts +0 -8
  50. package/src/styles/vaadin-grid-core-styles.js +0 -388
  51. package/src/styles/vaadin-grid-filter-core-styles.d.ts +0 -8
  52. package/src/styles/vaadin-grid-filter-core-styles.js +0 -18
  53. package/src/styles/vaadin-grid-sorter-core-styles.d.ts +0 -8
  54. package/src/styles/vaadin-grid-sorter-core-styles.js +0 -61
  55. package/src/styles/vaadin-grid-tree-toggle-core-styles.d.ts +0 -8
  56. package/src/styles/vaadin-grid-tree-toggle-core-styles.js +0 -78
  57. package/theme/lumo/all-imports.d.ts +0 -11
  58. package/theme/lumo/all-imports.js +0 -11
  59. package/theme/lumo/vaadin-grid-column-group.d.ts +0 -1
  60. package/theme/lumo/vaadin-grid-column-group.js +0 -1
  61. package/theme/lumo/vaadin-grid-column.d.ts +0 -1
  62. package/theme/lumo/vaadin-grid-column.js +0 -1
  63. package/theme/lumo/vaadin-grid-filter-column.d.ts +0 -2
  64. package/theme/lumo/vaadin-grid-filter-column.js +0 -2
  65. package/theme/lumo/vaadin-grid-filter.d.ts +0 -2
  66. package/theme/lumo/vaadin-grid-filter.js +0 -2
  67. package/theme/lumo/vaadin-grid-selection-column.d.ts +0 -2
  68. package/theme/lumo/vaadin-grid-selection-column.js +0 -2
  69. package/theme/lumo/vaadin-grid-sort-column.d.ts +0 -2
  70. package/theme/lumo/vaadin-grid-sort-column.js +0 -2
  71. package/theme/lumo/vaadin-grid-sorter-styles.d.ts +0 -3
  72. package/theme/lumo/vaadin-grid-sorter-styles.js +0 -52
  73. package/theme/lumo/vaadin-grid-sorter.d.ts +0 -2
  74. package/theme/lumo/vaadin-grid-sorter.js +0 -2
  75. package/theme/lumo/vaadin-grid-styles.d.ts +0 -6
  76. package/theme/lumo/vaadin-grid-styles.js +0 -405
  77. package/theme/lumo/vaadin-grid-tree-column.d.ts +0 -2
  78. package/theme/lumo/vaadin-grid-tree-column.js +0 -2
  79. package/theme/lumo/vaadin-grid-tree-toggle-styles.d.ts +0 -3
  80. package/theme/lumo/vaadin-grid-tree-toggle-styles.js +0 -81
  81. package/theme/lumo/vaadin-grid-tree-toggle.d.ts +0 -2
  82. package/theme/lumo/vaadin-grid-tree-toggle.js +0 -2
  83. package/theme/lumo/vaadin-grid.d.ts +0 -2
  84. package/theme/lumo/vaadin-grid.js +0 -2
@@ -3,7 +3,7 @@
3
3
  * Copyright (c) 2016 - 2025 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import '@vaadin/component-base/src/style-props.js';
6
+ import '@vaadin/component-base/src/styles/style-props.js';
7
7
  import { css } from 'lit';
8
8
 
9
9
  export const gridTreeToggleStyles = css`
@@ -36,7 +36,7 @@ export const gridTreeToggleStyles = css`
36
36
  }
37
37
 
38
38
  [part='toggle'] {
39
- margin-inline-end: var(--vaadin-gap-container-inline);
39
+ margin-inline-end: var(--vaadin-gap-s);
40
40
  }
41
41
 
42
42
  [part='toggle']::before {
@@ -45,7 +45,7 @@ export const gridTreeToggleStyles = css`
45
45
  width: var(--vaadin-icon-size, 1lh);
46
46
  height: var(--vaadin-icon-size, 1lh);
47
47
  background: currentColor;
48
- mask-image: var(--_vaadin-icon-chevron-down);
48
+ mask: var(--_vaadin-icon-chevron-down) 50% / var(--vaadin-icon-visual-size, 100%) no-repeat;
49
49
  }
50
50
 
51
51
  :host(:not([expanded])) [part='toggle']::before {
@@ -54,7 +54,7 @@ export const gridTreeToggleStyles = css`
54
54
 
55
55
  @media (prefers-reduced-motion: no-preference) {
56
56
  [part='toggle']::before {
57
- transition: rotate 120ms;
57
+ transition: var(--_non-focused-row-none, rotate 120ms);
58
58
  }
59
59
  }
60
60
 
@@ -3,7 +3,7 @@
3
3
  * Copyright (c) 2016 - 2025 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
6
+ import { findTreeToggleCell, iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
7
7
 
8
8
  /**
9
9
  * @polymerMixin
@@ -22,7 +22,7 @@ export const A11yMixin = (superClass) =>
22
22
  };
23
23
  }
24
24
  static get observers() {
25
- return ['_a11yUpdateGridSize(size, _columnTree)'];
25
+ return ['_a11yUpdateGridSize(size, _columnTree, __emptyState)'];
26
26
  }
27
27
 
28
28
  /** @private */
@@ -34,21 +34,27 @@ export const A11yMixin = (superClass) =>
34
34
 
35
35
  /** @private */
36
36
  _a11yGetFooterRowCount(_columnTree) {
37
- return _columnTree.filter((level) => level.some((col) => col.headerRenderer)).length;
37
+ return _columnTree.filter((level) => level.some((col) => col.footerRenderer)).length;
38
38
  }
39
39
 
40
40
  /** @private */
41
- _a11yUpdateGridSize(size, _columnTree) {
41
+ _a11yUpdateGridSize(size, _columnTree, emptyState) {
42
42
  if (size === undefined || _columnTree === undefined) {
43
43
  return;
44
44
  }
45
45
 
46
+ const headerRowsCount = this._a11yGetHeaderRowCount(_columnTree);
47
+ const footerRowsCount = this._a11yGetFooterRowCount(_columnTree);
48
+ const bodyRowsCount = emptyState ? 1 : size;
49
+ const rowsCount = bodyRowsCount + headerRowsCount + footerRowsCount;
50
+
51
+ this.$.table.setAttribute('aria-rowcount', rowsCount);
52
+
46
53
  const bodyColumns = _columnTree[_columnTree.length - 1];
47
- this.$.table.setAttribute(
48
- 'aria-rowcount',
49
- size + this._a11yGetHeaderRowCount(_columnTree) + this._a11yGetFooterRowCount(_columnTree),
50
- );
51
- this.$.table.setAttribute('aria-colcount', (bodyColumns && bodyColumns.length) || 0);
54
+ // If no header and footer rows while the empty state is active, count as one column
55
+ // Otherwise, use the number of body columns, if present
56
+ const columnsCount = emptyState ? 1 : (rowsCount && bodyColumns && bodyColumns.length) || 0;
57
+ this.$.table.setAttribute('aria-colcount', columnsCount);
52
58
 
53
59
  this._a11yUpdateHeaderRows();
54
60
  this._a11yUpdateFooterRows();
@@ -95,12 +101,22 @@ export const A11yMixin = (superClass) =>
95
101
  * @protected
96
102
  */
97
103
  _a11yUpdateRowExpanded(row) {
104
+ const toggleCell = findTreeToggleCell(row);
98
105
  if (this.__isRowExpandable(row)) {
99
106
  row.setAttribute('aria-expanded', 'false');
107
+ if (toggleCell) {
108
+ toggleCell.setAttribute('aria-expanded', 'false');
109
+ }
100
110
  } else if (this.__isRowCollapsible(row)) {
101
111
  row.setAttribute('aria-expanded', 'true');
112
+ if (toggleCell) {
113
+ toggleCell.setAttribute('aria-expanded', 'true');
114
+ }
102
115
  } else {
103
116
  row.removeAttribute('aria-expanded');
117
+ if (toggleCell) {
118
+ toggleCell.removeAttribute('aria-expanded');
119
+ }
104
120
  }
105
121
  }
106
122
 
@@ -132,7 +148,7 @@ export const A11yMixin = (superClass) =>
132
148
  }
133
149
 
134
150
  /**
135
- * @param {!HTMLElement} row
151
+ * @param {!HTMLElement} cell
136
152
  * @param {number} colspan
137
153
  * @protected
138
154
  */
@@ -30,14 +30,12 @@ export const ColumnAutoWidthMixin = (superClass) =>
30
30
  ];
31
31
  }
32
32
 
33
- constructor() {
34
- super();
35
- this.addEventListener('animationend', this.__onAnimationEndAutoWidth);
36
- }
33
+ /** @protected */
34
+ updated(props) {
35
+ super.updated(props);
37
36
 
38
- /** @private */
39
- __onAnimationEndAutoWidth(e) {
40
- if (e.animationName.indexOf('vaadin-grid-appear') === 0) {
37
+ // If the grid was hidden and is now visible
38
+ if (props.has('__hostVisible') && !props.get('__hostVisible')) {
41
39
  this.__tryToRecalculateColumnWidthsIfPending();
42
40
  }
43
41
  }
@@ -137,10 +135,7 @@ export const ColumnAutoWidthMixin = (superClass) =>
137
135
  return this.__getIntrinsicWidth(innerColumn) + shareOfInnerColumnFromNecessaryExtraSpace;
138
136
  }
139
137
 
140
- /**
141
- * @param {!Array<!GridColumn>} cols the columns to auto size based on their content width
142
- * @private
143
- */
138
+ /** @private */
144
139
  _recalculateColumnWidths() {
145
140
  // Flush to make sure DOM is up-to-date when measuring the column widths
146
141
  this.__virtualizer.flush();
@@ -147,6 +147,7 @@ export declare class GridColumnMixinClass<
147
147
  * - `model.level` Level of the tree represented with a horizontal offset of the toggle button.
148
148
  * - `model.selected` Selected state.
149
149
  * - `model.detailsOpened` Details opened state.
150
+ * - `model.hasChildren` Whether the item has children.
150
151
  */
151
152
  renderer: GridBodyRenderer<TItem, Column> | null | undefined;
152
153
 
@@ -536,24 +536,8 @@ export const ColumnBaseMixin = (superClass) =>
536
536
  return;
537
537
  }
538
538
 
539
- let textAlignFallback;
540
- if (getComputedStyle(this._grid).direction === 'ltr') {
541
- if (textAlign === 'start') {
542
- textAlignFallback = 'left';
543
- } else if (textAlign === 'end') {
544
- textAlignFallback = 'right';
545
- }
546
- } else if (textAlign === 'start') {
547
- textAlignFallback = 'right';
548
- } else if (textAlign === 'end') {
549
- textAlignFallback = 'left';
550
- }
551
-
552
539
  this._allCells.forEach((cell) => {
553
540
  cell._content.style.textAlign = textAlign;
554
- if (getComputedStyle(cell._content).textAlign !== textAlign) {
555
- cell._content.style.textAlign = textAlignFallback;
556
- }
557
541
  });
558
542
  }
559
543
 
@@ -885,6 +869,7 @@ export const GridColumnMixin = (superClass) =>
885
869
  * - `model.level` Level of the tree represented with a horizontal offset of the toggle button.
886
870
  * - `model.selected` Selected state.
887
871
  * - `model.detailsOpened` Details opened state.
872
+ * - `model.hasChildren` Whether the item has children.
888
873
  *
889
874
  * @type {GridBodyRenderer | null | undefined}
890
875
  */
@@ -127,9 +127,9 @@ export const ColumnReorderingMixin = (superClass) =>
127
127
  return;
128
128
  }
129
129
 
130
- // Cancel reordering if there are draggable nodes on the event path
130
+ // Cancel reordering if there are draggable nodes on the event path following this element
131
131
  const path = e.composedPath && e.composedPath();
132
- if (path && path.some((node) => node.hasAttribute && node.hasAttribute('draggable'))) {
132
+ if (path && path.slice(0, Math.max(0, path.indexOf(this))).some((node) => node.draggable)) {
133
133
  return;
134
134
  }
135
135
 
@@ -181,6 +181,12 @@ export const DataProviderMixin = (superClass) =>
181
181
  this.requestContentUpdate();
182
182
  }
183
183
 
184
+ /** @private */
185
+ __getRowLevel(row) {
186
+ const { level } = this._dataProviderController.getFlatIndexContext(row.index);
187
+ return level;
188
+ }
189
+
184
190
  /** @private */
185
191
  __getRowItem(row) {
186
192
  const { item } = this._dataProviderController.getFlatIndexContext(row.index);
@@ -216,6 +222,15 @@ export const DataProviderMixin = (superClass) =>
216
222
  return this.__expandedKeys && this.__expandedKeys.has(this.getItemId(item));
217
223
  }
218
224
 
225
+ /**
226
+ * @param {!GridItem} item
227
+ * @return {boolean}
228
+ * @protected
229
+ */
230
+ _hasChildren(item) {
231
+ return this.itemHasChildrenPath && item && !!get(this.itemHasChildrenPath, item);
232
+ }
233
+
219
234
  /** @private */
220
235
  _expandedItemsChanged() {
221
236
  this._dataProviderController.recalculateFlatSize();
@@ -254,16 +269,6 @@ export const DataProviderMixin = (superClass) =>
254
269
  }
255
270
  }
256
271
 
257
- /**
258
- * @param {number} index
259
- * @return {number}
260
- * @protected
261
- */
262
- _getIndexLevel(index = 0) {
263
- const { level } = this._dataProviderController.getFlatIndexContext(index);
264
- return level;
265
- }
266
-
267
272
  /** @protected */
268
273
  _onDataProviderPageRequested() {
269
274
  this._setLoading(true);
@@ -413,7 +413,7 @@ export const DragAndDropMixin = (superClass) =>
413
413
 
414
414
  /** @private */
415
415
  _onDrop(e) {
416
- if (this.dropMode) {
416
+ if (this.dropMode && this._dropLocation) {
417
417
  e.stopPropagation();
418
418
  e.preventDefault();
419
419
 
@@ -13,6 +13,7 @@ export interface GridEventContext<TItem> {
13
13
  index?: number;
14
14
  selected?: boolean;
15
15
  detailsOpened?: boolean;
16
+ hasChildren?: boolean;
16
17
  expanded?: boolean;
17
18
  level?: number;
18
19
  }
@@ -34,6 +35,7 @@ export declare class EventContextMixinClass<TItem> {
34
35
  * - `detailsOpened`: whether the row details are open for the item
35
36
  * - `expanded`: the expanded state of the tree toggle
36
37
  * - `level`: the tree hierarchy level
38
+ * - `hasChildren`: whether the item has children
37
39
  *
38
40
  * The returned object is populated only when a grid cell, header, footer or row details is found in `event.composedPath()`.
39
41
  * This means mostly mouse and keyboard events. If such a grid part is not found in the path, an empty object is returned.
@@ -21,6 +21,7 @@ export const EventContextMixin = (superClass) =>
21
21
  * - `detailsOpened`: whether the row details are open for the item
22
22
  * - `expanded`: the expanded state of the tree toggle
23
23
  * - `level`: the tree hierarchy level
24
+ * - `hasChildren`: whether the item has children
24
25
  *
25
26
  * The returned object is populated only when a grid cell, header, footer or row details is found in `event.composedPath()`.
26
27
  * This means mostly mouse and keyboard events. If such a grid part is not found in the path, an empty object is returned.
@@ -9,7 +9,7 @@ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
9
9
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
10
10
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
11
11
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
12
- import { gridFilterStyles } from './styles/vaadin-grid-filter-core-styles.js';
12
+ import { gridFilterStyles } from './styles/vaadin-grid-filter-base-styles.js';
13
13
  import { GridFilterElementMixin } from './vaadin-grid-filter-element-mixin.js';
14
14
 
15
15
  /**
@@ -173,6 +173,15 @@ export function updateCellState(cell, attribute, value, part, oldPart) {
173
173
  updatePart(cell, value, part || `${attribute}-cell`);
174
174
  }
175
175
 
176
+ /**
177
+ * Finds the cell containing the tree toggle element
178
+ * @param {!HTMLElement} row
179
+ * @return {HTMLElement | null}
180
+ */
181
+ export function findTreeToggleCell(row) {
182
+ return getBodyRowCells(row).find((cell) => cell._content.querySelector('vaadin-grid-tree-toggle'));
183
+ }
184
+
176
185
  /**
177
186
  * A helper for observing flattened child column list of an element.
178
187
  */
@@ -7,7 +7,6 @@ import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
7
7
  import { animationFrame } from '@vaadin/component-base/src/async.js';
8
8
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
9
9
  import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
10
- import { get } from '@vaadin/component-base/src/path-utils.js';
11
10
 
12
11
  function isRow(element) {
13
12
  return element instanceof HTMLTableRowElement;
@@ -286,10 +285,7 @@ export const KeyboardNavigationMixin = (superClass) =>
286
285
 
287
286
  /** @private */
288
287
  __isRowExpandable(row) {
289
- if (this.itemHasChildrenPath) {
290
- const item = row._item;
291
- return !!(item && get(this.itemHasChildrenPath, item) && !this._isExpanded(item));
292
- }
288
+ return this._hasChildren(row._item) && !this._isExpanded(row._item);
293
289
  }
294
290
 
295
291
  /** @private */
@@ -62,6 +62,7 @@ export interface GridItemModel<TItem> {
62
62
  expanded?: boolean;
63
63
  level?: number;
64
64
  detailsOpened?: boolean;
65
+ hasChildren?: boolean;
65
66
  }
66
67
 
67
68
  /**
@@ -31,6 +31,7 @@ import {
31
31
  updateState,
32
32
  } from './vaadin-grid-helpers.js';
33
33
  import { KeyboardNavigationMixin } from './vaadin-grid-keyboard-navigation-mixin.js';
34
+ import { ResizeMixin } from './vaadin-grid-resize-mixin.js';
34
35
  import { RowDetailsMixin } from './vaadin-grid-row-details-mixin.js';
35
36
  import { ScrollMixin } from './vaadin-grid-scroll-mixin.js';
36
37
  import { SelectionMixin } from './vaadin-grid-selection-mixin.js';
@@ -57,6 +58,7 @@ import { StylingMixin } from './vaadin-grid-styling-mixin.js';
57
58
  * @mixes EventContextMixin
58
59
  * @mixes StylingMixin
59
60
  * @mixes DragAndDropMixin
61
+ * @mixes ResizeMixin
60
62
  */
61
63
  export const GridMixin = (superClass) =>
62
64
  class extends ColumnAutoWidthMixin(
@@ -73,7 +75,7 @@ export const GridMixin = (superClass) =>
73
75
  FilterMixin(
74
76
  ColumnReorderingMixin(
75
77
  ColumnResizingMixin(
76
- EventContextMixin(DragAndDropMixin(StylingMixin(TabindexMixin(superClass)))),
78
+ EventContextMixin(DragAndDropMixin(StylingMixin(TabindexMixin(ResizeMixin(superClass))))),
77
79
  ),
78
80
  ),
79
81
  ),
@@ -168,11 +170,6 @@ export const GridMixin = (superClass) =>
168
170
  };
169
171
  }
170
172
 
171
- constructor() {
172
- super();
173
- this.addEventListener('animationend', this._onAnimationEnd);
174
- }
175
-
176
173
  /** @private */
177
174
  get _firstVisibleIndex() {
178
175
  const firstVisibleItem = this.__getFirstVisibleItem();
@@ -256,24 +253,13 @@ export const GridMixin = (superClass) =>
256
253
  scrollContainer: this.$.items,
257
254
  scrollTarget: this.$.table,
258
255
  reorderElements: true,
256
+ // Grid rows have a CSS-defined minimum height, so the virtualizer's height
257
+ // placeholder logic can be disabled. This helps save reflows which might
258
+ // otherwise be triggered by this logic because it reads the row height
259
+ // right after updating the rows' content.
260
+ __disableHeightPlaceholder: true,
259
261
  });
260
262
 
261
- new ResizeObserver(() =>
262
- setTimeout(() => {
263
- this.__updateColumnsBodyContentHidden();
264
- }),
265
- ).observe(this.$.table);
266
-
267
- const minHeightObserver = new ResizeObserver(() =>
268
- setTimeout(() => {
269
- this.__updateMinHeight();
270
- }),
271
- );
272
-
273
- minHeightObserver.observe(this.$.header);
274
- minHeightObserver.observe(this.$.items);
275
- minHeightObserver.observe(this.$.footer);
276
-
277
263
  this._tooltipController = new TooltipController(this);
278
264
  this.addController(this._tooltipController);
279
265
  this._tooltipController.setManual(true);
@@ -284,6 +270,31 @@ export const GridMixin = (superClass) =>
284
270
  });
285
271
  }
286
272
 
273
+ /** @protected */
274
+ updated(props) {
275
+ super.updated(props);
276
+
277
+ // If the grid was hidden and is now visible
278
+ if (props.has('__hostVisible') && !props.get('__hostVisible')) {
279
+ // Ensure header and footer have tabbable elements
280
+ this._resetKeyboardNavigation();
281
+
282
+ requestAnimationFrame(() => this.__scrollToPendingIndexes());
283
+ }
284
+
285
+ if (props.has('__headerRect') || props.has('__footerRect') || props.has('__itemsRect')) {
286
+ setTimeout(() => this.__updateMinHeight());
287
+ }
288
+
289
+ if (props.has('__tableRect')) {
290
+ setTimeout(() => this.__updateColumnsBodyContentHidden());
291
+
292
+ // Updating data can change the visibility of the scroll bar. Therefore,
293
+ // the scroll position has to be recalculated.
294
+ this.__updateHorizontalScrollPosition();
295
+ }
296
+ }
297
+
287
298
  /** @private */
288
299
  __getBodyCellCoordinates(cell) {
289
300
  if (this.$.items.contains(cell) && cell.localName === 'td') {
@@ -630,6 +641,7 @@ export const GridMixin = (superClass) =>
630
641
 
631
642
  // Make sure the section has a tabbable element
632
643
  this._resetKeyboardNavigation();
644
+ this._a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
633
645
  }
634
646
 
635
647
  /** @private */
@@ -815,21 +827,6 @@ export const GridMixin = (superClass) =>
815
827
  this.__updateHorizontalScrollPosition();
816
828
  }
817
829
 
818
- /** @private */
819
- _onAnimationEnd(e) {
820
- // ShadyCSS applies scoping suffixes to animation names
821
- if (e.animationName.indexOf('vaadin-grid-appear') === 0) {
822
- e.stopPropagation();
823
-
824
- // Ensure header and footer have tabbable elements
825
- this._resetKeyboardNavigation();
826
-
827
- requestAnimationFrame(() => {
828
- this.__scrollToPendingIndexes();
829
- });
830
- }
831
- }
832
-
833
830
  /**
834
831
  * @param {!HTMLTableRowElement} row
835
832
  * @return {!GridItemModel}
@@ -839,9 +836,10 @@ export const GridMixin = (superClass) =>
839
836
  return {
840
837
  index: row.index,
841
838
  item: row._item,
842
- level: this._getIndexLevel(row.index),
839
+ level: this.__getRowLevel(row),
843
840
  expanded: this._isExpanded(row._item),
844
841
  selected: this._isSelected(row._item),
842
+ hasChildren: this._hasChildren(row._item),
845
843
  detailsOpened: !!this.rowDetailsRenderer && this._isDetailsOpened(row._item),
846
844
  };
847
845
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2025 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
+
8
+ export declare function ResizeMixin<T extends Constructor<HTMLElement>>(base: T): Constructor<ResizeMixinClass> & T;
9
+
10
+ declare class ResizeMixinClass {}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+
7
+ /**
8
+ * A mixin to observe size changes of the grid and its main parts.
9
+ *
10
+ * @polymerMixin
11
+ */
12
+ export const ResizeMixin = (superClass) =>
13
+ class extends superClass {
14
+ static get properties() {
15
+ return {
16
+ /** @private */
17
+ __hostVisible: {
18
+ type: Boolean,
19
+ value: false,
20
+ },
21
+
22
+ /** @private */
23
+ __tableRect: Object,
24
+
25
+ /** @private */
26
+ __headerRect: Object,
27
+
28
+ /** @private */
29
+ __itemsRect: Object,
30
+
31
+ /** @private */
32
+ __footerRect: Object,
33
+ };
34
+ }
35
+
36
+ /** @protected */
37
+ ready() {
38
+ super.ready();
39
+
40
+ const resizeObserver = new ResizeObserver((entries) => {
41
+ const hostEntry = entries.findLast(({ target }) => target === this);
42
+ if (hostEntry) {
43
+ this.__hostVisible = this.checkVisibility();
44
+ }
45
+
46
+ const tableEntry = entries.findLast(({ target }) => target === this.$.table);
47
+ if (tableEntry) {
48
+ this.__tableRect = tableEntry.contentRect;
49
+ }
50
+
51
+ const headerEntry = entries.findLast(({ target }) => target === this.$.header);
52
+ if (headerEntry) {
53
+ this.__headerRect = headerEntry.contentRect;
54
+ }
55
+
56
+ const itemsEntry = entries.findLast(({ target }) => target === this.$.items);
57
+ if (itemsEntry) {
58
+ this.__itemsRect = itemsEntry.contentRect;
59
+ }
60
+
61
+ const footerEntry = entries.findLast(({ target }) => target === this.$.footer);
62
+ if (footerEntry) {
63
+ this.__footerRect = footerEntry.contentRect;
64
+ }
65
+ });
66
+
67
+ resizeObserver.observe(this);
68
+ resizeObserver.observe(this.$.table);
69
+ resizeObserver.observe(this.$.header);
70
+ resizeObserver.observe(this.$.items);
71
+ resizeObserver.observe(this.$.footer);
72
+ }
73
+ };
@@ -68,10 +68,6 @@ export const RowDetailsMixin = (superClass) =>
68
68
  entries.forEach(({ target: cell }) => {
69
69
  this._updateDetailsCellHeight(cell.parentElement);
70
70
  });
71
-
72
- // This workaround is needed until Safari also supports
73
- // ResizeObserver.observe with {box: 'border-box'}
74
- this.__virtualizer.__adapter._resizeHandler();
75
71
  });
76
72
  }
77
73
 
@@ -124,7 +120,7 @@ export const RowDetailsMixin = (superClass) =>
124
120
 
125
121
  /**
126
122
  * @param {!HTMLElement} row
127
- * @param {!GridItem} item
123
+ * @param {boolean} detailsOpened
128
124
  * @protected
129
125
  */
130
126
  _toggleDetailsCell(row, detailsOpened) {