@vaadin/grid 25.0.0-beta3 → 25.0.0-beta5

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.
@@ -8,7 +8,7 @@ import { css } from 'lit';
8
8
 
9
9
  export const gridTreeToggleStyles = css`
10
10
  :host {
11
- display: flex;
11
+ display: inline-flex;
12
12
  max-width: 100%;
13
13
  pointer-events: none;
14
14
  }
@@ -35,6 +35,13 @@ export const gridTreeToggleStyles = css`
35
35
  width: calc(var(--_level, 0) * var(--vaadin-grid-tree-toggle-level-offset, 16px));
36
36
  }
37
37
 
38
+ /* Baseline alignment */
39
+ #level-spacer::before {
40
+ content: '\\2003' / '';
41
+ display: inline-block;
42
+ width: 0;
43
+ }
44
+
38
45
  [part='toggle'] {
39
46
  margin-inline-end: var(--vaadin-gap-s);
40
47
  }
@@ -66,6 +73,7 @@ export const gridTreeToggleStyles = css`
66
73
  display: block;
67
74
  overflow: hidden;
68
75
  text-overflow: ellipsis;
76
+ flex: 1;
69
77
  }
70
78
 
71
79
  @media (forced-colors: active) {
@@ -22,73 +22,74 @@ export const A11yMixin = (superClass) =>
22
22
  };
23
23
  }
24
24
  static get observers() {
25
- return ['_a11yUpdateGridSize(size, _columnTree, __emptyState)'];
25
+ return ['__a11yUpdateGridSize(size, _columnTree, __emptyState)'];
26
26
  }
27
27
 
28
28
  /** @private */
29
- _a11yGetHeaderRowCount(_columnTree) {
29
+ __a11yGetHeaderRowCount(_columnTree) {
30
30
  return _columnTree.filter((level) =>
31
31
  level.some((col) => col.headerRenderer || (col.path && col.header !== null) || col.header),
32
32
  ).length;
33
33
  }
34
34
 
35
35
  /** @private */
36
- _a11yGetFooterRowCount(_columnTree) {
36
+ __a11yGetFooterRowCount(_columnTree) {
37
37
  return _columnTree.filter((level) => level.some((col) => col.footerRenderer)).length;
38
38
  }
39
39
 
40
40
  /** @private */
41
- _a11yUpdateGridSize(size, _columnTree, emptyState) {
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);
46
+ const headerRowsCount = this.__a11yGetHeaderRowCount(_columnTree);
47
+ const footerRowsCount = this.__a11yGetFooterRowCount(_columnTree);
48
48
  const bodyRowsCount = emptyState ? 1 : size;
49
49
  const rowsCount = bodyRowsCount + headerRowsCount + footerRowsCount;
50
50
 
51
51
  this.$.table.setAttribute('aria-rowcount', rowsCount);
52
52
 
53
53
  const bodyColumns = _columnTree[_columnTree.length - 1];
54
+
54
55
  // If no header and footer rows while the empty state is active, count as one column
55
56
  // Otherwise, use the number of body columns, if present
56
57
  const columnsCount = emptyState ? 1 : (rowsCount && bodyColumns && bodyColumns.length) || 0;
57
58
  this.$.table.setAttribute('aria-colcount', columnsCount);
58
59
 
59
- this._a11yUpdateHeaderRows();
60
- this._a11yUpdateFooterRows();
60
+ this.__a11yUpdateHeaderRows();
61
+ this.__a11yUpdateFooterRows();
61
62
  }
62
63
 
63
- /** @protected */
64
- _a11yUpdateHeaderRows() {
64
+ /** @private */
65
+ __a11yUpdateHeaderRows() {
65
66
  iterateChildren(this.$.header, (headerRow, index) => {
66
67
  headerRow.setAttribute('aria-rowindex', index + 1);
67
68
  });
68
69
  }
69
70
 
70
- /** @protected */
71
- _a11yUpdateFooterRows() {
71
+ /** @private */
72
+ __a11yUpdateFooterRows() {
72
73
  iterateChildren(this.$.footer, (footerRow, index) => {
73
- footerRow.setAttribute('aria-rowindex', this._a11yGetHeaderRowCount(this._columnTree) + this.size + index + 1);
74
+ footerRow.setAttribute('aria-rowindex', this.__a11yGetHeaderRowCount(this._columnTree) + this.size + index + 1);
74
75
  });
75
76
  }
76
77
 
77
78
  /**
78
79
  * @param {!HTMLElement} row
79
80
  * @param {number} index
80
- * @protected
81
+ * @private
81
82
  */
82
- _a11yUpdateRowRowindex(row, index) {
83
- row.setAttribute('aria-rowindex', index + this._a11yGetHeaderRowCount(this._columnTree) + 1);
83
+ __a11yUpdateRowRowindex(row) {
84
+ row.setAttribute('aria-rowindex', row.index + this.__a11yGetHeaderRowCount(this._columnTree) + 1);
84
85
  }
85
86
 
86
87
  /**
87
88
  * @param {!HTMLElement} row
88
89
  * @param {boolean} selected
89
- * @protected
90
+ * @private
90
91
  */
91
- _a11yUpdateRowSelected(row, selected) {
92
+ __a11yUpdateRowSelected(row, selected) {
92
93
  // Jaws reads selection only for rows, NVDA only for cells
93
94
  row.setAttribute('aria-selected', Boolean(selected));
94
95
  iterateRowCells(row, (cell) => {
@@ -98,9 +99,9 @@ export const A11yMixin = (superClass) =>
98
99
 
99
100
  /**
100
101
  * @param {!HTMLElement} row
101
- * @protected
102
+ * @private
102
103
  */
103
- _a11yUpdateRowExpanded(row) {
104
+ __a11yUpdateRowExpanded(row) {
104
105
  const toggleCell = findTreeToggleCell(row);
105
106
  if (this.__isRowExpandable(row)) {
106
107
  row.setAttribute('aria-expanded', 'false');
@@ -123,9 +124,9 @@ export const A11yMixin = (superClass) =>
123
124
  /**
124
125
  * @param {!HTMLElement} row
125
126
  * @param {number} level
126
- * @protected
127
+ * @private
127
128
  */
128
- _a11yUpdateRowLevel(row, level) {
129
+ __a11yUpdateRowLevel(row, level) {
129
130
  // Set level for the expandable rows itself, and all the nested rows.
130
131
  if (level > 0 || this.__isRowCollapsible(row) || this.__isRowExpandable(row)) {
131
132
  row.setAttribute('aria-level', level + 1);
@@ -137,9 +138,9 @@ export const A11yMixin = (superClass) =>
137
138
  /**
138
139
  * @param {!HTMLElement} row
139
140
  * @param {!HTMLElement} detailsCell
140
- * @protected
141
+ * @private
141
142
  */
142
- _a11ySetRowDetailsCell(row, detailsCell) {
143
+ __a11ySetRowDetailsCell(row, detailsCell) {
143
144
  iterateRowCells(row, (cell) => {
144
145
  if (cell !== detailsCell) {
145
146
  cell.setAttribute('aria-controls', detailsCell.id);
@@ -150,14 +151,14 @@ export const A11yMixin = (superClass) =>
150
151
  /**
151
152
  * @param {!HTMLElement} cell
152
153
  * @param {number} colspan
153
- * @protected
154
+ * @private
154
155
  */
155
- _a11yUpdateCellColspan(cell, colspan) {
156
+ __a11yUpdateCellColspan(cell, colspan) {
156
157
  cell.setAttribute('aria-colspan', Number(colspan));
157
158
  }
158
159
 
159
- /** @protected */
160
- _a11yUpdateSorters() {
160
+ /** @private */
161
+ __a11yUpdateSorters() {
161
162
  Array.from(this.querySelectorAll('vaadin-grid-sorter')).forEach((sorter) => {
162
163
  let cellContent = sorter.parentNode;
163
164
  while (cellContent && cellContent.localName !== 'vaadin-grid-cell-content') {
@@ -73,7 +73,7 @@ export const ActiveItemMixin = (superClass) =>
73
73
  // No clicked cell available
74
74
  !cell ||
75
75
  // Cell is a details cell
76
- cell.getAttribute('part').includes('details-cell') ||
76
+ cell.part.contains('details-cell') ||
77
77
  // Cell is the empty state cell
78
78
  cell === this.$.emptystatecell ||
79
79
  // Cell content is focused
@@ -314,13 +314,13 @@ export const GridColumnGroupMixin = (superClass) =>
314
314
  if (headerCell) {
315
315
  headerCell.setAttribute('colspan', colSpan);
316
316
  if (this._grid) {
317
- this._grid._a11yUpdateCellColspan(headerCell, colSpan);
317
+ this._grid.__a11yUpdateCellColspan(headerCell, colSpan);
318
318
  }
319
319
  }
320
320
  if (footerCell) {
321
321
  footerCell.setAttribute('colspan', colSpan);
322
322
  if (this._grid) {
323
- this._grid._a11yUpdateCellColspan(footerCell, colSpan);
323
+ this._grid.__a11yUpdateCellColspan(footerCell, colSpan);
324
324
  }
325
325
  }
326
326
  }
@@ -7,7 +7,7 @@ import { animationFrame } from '@vaadin/component-base/src/async.js';
7
7
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
8
8
  import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
9
9
  import { get } from '@vaadin/component-base/src/path-utils.js';
10
- import { updateCellState } from './vaadin-grid-helpers.js';
10
+ import { updateCellState, updatePart } from './vaadin-grid-helpers.js';
11
11
 
12
12
  /**
13
13
  * @polymerMixin
@@ -518,7 +518,7 @@ export const ColumnBaseMixin = (superClass) =>
518
518
 
519
519
  if (resizable) {
520
520
  const handle = document.createElement('div');
521
- handle.setAttribute('part', 'resize-handle');
521
+ updatePart(handle, 'resize-handle', true);
522
522
  cell.appendChild(handle);
523
523
  }
524
524
  }
@@ -134,7 +134,7 @@ export const ColumnReorderingMixin = (superClass) =>
134
134
  }
135
135
 
136
136
  const headerCell = this._cellFromPoint(e.detail.x, e.detail.y);
137
- if (!headerCell || !headerCell.getAttribute('part').includes('header-cell')) {
137
+ if (!headerCell || !headerCell.part.contains('header-cell')) {
138
138
  return;
139
139
  }
140
140
 
@@ -20,22 +20,16 @@ export const ColumnResizingMixin = (superClass) =>
20
20
  scroller.addEventListener('touchmove', (e) => scroller.hasAttribute('column-resizing') && e.preventDefault());
21
21
 
22
22
  // Disable contextmenu on any resize separator.
23
- scroller.addEventListener(
24
- 'contextmenu',
25
- (e) => e.target.getAttribute('part') === 'resize-handle' && e.preventDefault(),
26
- );
23
+ scroller.addEventListener('contextmenu', (e) => e.target.part.contains('resize-handle') && e.preventDefault());
27
24
 
28
25
  // Disable native cell focus when resizing
29
- scroller.addEventListener(
30
- 'mousedown',
31
- (e) => e.target.getAttribute('part') === 'resize-handle' && e.preventDefault(),
32
- );
26
+ scroller.addEventListener('mousedown', (e) => e.target.part.contains('resize-handle') && e.preventDefault());
33
27
  }
34
28
 
35
29
  /** @private */
36
30
  _onHeaderTrack(e) {
37
31
  const handle = e.target;
38
- if (handle.getAttribute('part') === 'resize-handle') {
32
+ if (handle.part.contains('resize-handle')) {
39
33
  const cell = handle.parentElement;
40
34
  let column = cell._column;
41
35
 
@@ -447,7 +447,7 @@ export const DragAndDropMixin = (superClass) =>
447
447
  return rows
448
448
  .map((row) => {
449
449
  return Array.from(row.children)
450
- .filter((cell) => !cell.hidden && cell.getAttribute('part').indexOf('details-cell') === -1)
450
+ .filter((cell) => !cell.hidden && !cell.part.contains('details-cell'))
451
451
  .sort((a, b) => {
452
452
  return a._column._order > b._column._order ? 1 : -1;
453
453
  })
@@ -40,8 +40,8 @@ export const EventContextMixin = (superClass) =>
40
40
  return context;
41
41
  }
42
42
 
43
- context.section = ['body', 'header', 'footer', 'details'].find(
44
- (section) => cell.getAttribute('part').indexOf(section) > -1,
43
+ context.section = ['body', 'header', 'footer', 'details'].find((section) =>
44
+ cell.part.contains(`${section}-cell`),
45
45
  );
46
46
 
47
47
  if (cell._column) {
@@ -5,7 +5,6 @@
5
5
  */
6
6
  import { microTask } from '@vaadin/component-base/src/async.js';
7
7
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
8
- import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
9
8
 
10
9
  /**
11
10
  * Returns the cells of the given row, excluding the details cell.
@@ -81,15 +80,13 @@ export function updateState(element, attribute, value) {
81
80
 
82
81
  /**
83
82
  * @param {!HTMLElement} element
84
- * @param {boolean | string | null | undefined} value
85
83
  * @param {string} part
84
+ * @param {boolean | string | null | undefined} value
86
85
  */
87
- export function updatePart(element, value, part) {
88
- if (value || value === '') {
89
- addValueToAttribute(element, 'part', part);
90
- } else {
91
- removeValueFromAttribute(element, 'part', part);
92
- }
86
+ export function updatePart(element, part, value) {
87
+ element.classList.toggle(part, value || value === '');
88
+ element.part.toggle(part, value || value === '');
89
+ element.part.length === 0 && element.removeAttribute('part');
93
90
  }
94
91
 
95
92
  /**
@@ -99,7 +96,7 @@ export function updatePart(element, value, part) {
99
96
  */
100
97
  export function updateCellsPart(cells, part, value) {
101
98
  cells.forEach((cell) => {
102
- updatePart(cell, value, part);
99
+ updatePart(cell, part, value);
103
100
  });
104
101
  }
105
102
 
@@ -117,7 +114,7 @@ export function updateBooleanRowStates(row, states) {
117
114
  const rowPart = `${state}-row`;
118
115
 
119
116
  // Row part attribute
120
- updatePart(row, value, rowPart);
117
+ updatePart(row, rowPart, value);
121
118
 
122
119
  // Cells part attribute
123
120
  updateCellsPart(cells, `${rowPart}-cell`, value);
@@ -140,14 +137,14 @@ export function updateStringRowStates(row, states) {
140
137
  // remove previous part from row and cells if there was any
141
138
  if (prevValue) {
142
139
  const prevRowPart = `${state}-${prevValue}-row`;
143
- updatePart(row, false, prevRowPart);
140
+ updatePart(row, prevRowPart, false);
144
141
  updateCellsPart(cells, `${prevRowPart}-cell`, false);
145
142
  }
146
143
 
147
144
  // set new part to rows and cells if there is a value
148
145
  if (value) {
149
146
  const rowPart = `${state}-${value}-row`;
150
- updatePart(row, value, rowPart);
147
+ updatePart(row, rowPart, value);
151
148
  updateCellsPart(cells, `${rowPart}-cell`, value);
152
149
  }
153
150
  });
@@ -166,11 +163,11 @@ export function updateCellState(cell, attribute, value, part, oldPart) {
166
163
 
167
164
  // Remove old part from the attribute
168
165
  if (oldPart) {
169
- updatePart(cell, false, oldPart);
166
+ updatePart(cell, oldPart, false);
170
167
  }
171
168
 
172
169
  // Add new part to the cell attribute
173
- updatePart(cell, value, part || `${attribute}-cell`);
170
+ updatePart(cell, part || `${attribute}-cell`, value);
174
171
  }
175
172
 
176
173
  /**
@@ -6,7 +6,7 @@
6
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
- import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
9
+ import { updatePart } from './vaadin-grid-helpers.js';
10
10
 
11
11
  function isRow(element) {
12
12
  return element instanceof HTMLTableRowElement;
@@ -169,11 +169,11 @@ export const KeyboardNavigationMixin = (superClass) =>
169
169
  /** @private */
170
170
  _focusedCellChanged(focusedCell, oldFocusedCell) {
171
171
  if (oldFocusedCell) {
172
- removeValueFromAttribute(oldFocusedCell, 'part', 'focused-cell');
172
+ updatePart(oldFocusedCell, 'focused-cell', false);
173
173
  }
174
174
 
175
175
  if (focusedCell) {
176
- addValueToAttribute(focusedCell, 'part', 'focused-cell');
176
+ updatePart(focusedCell, 'focused-cell', true);
177
177
  }
178
178
  }
179
179
 
@@ -603,7 +603,7 @@ export const KeyboardNavigationMixin = (superClass) =>
603
603
  this._scrollHorizontallyToCell(dstCell);
604
604
  }
605
605
 
606
- dstCell.focus();
606
+ dstCell.focus({ preventScroll: true });
607
607
  }
608
608
  }
609
609
 
@@ -1016,8 +1016,9 @@ export const KeyboardNavigationMixin = (superClass) =>
1016
1016
  const dstRow = dstCell.parentNode;
1017
1017
  const dstCellIndex = Array.from(dstRow.children).indexOf(dstCell);
1018
1018
  const tableRect = this.$.table.getBoundingClientRect();
1019
- let leftBoundary = tableRect.left,
1020
- rightBoundary = tableRect.right;
1019
+ const scrollbarWidth = this.$.table.clientWidth - this.$.table.offsetWidth;
1020
+ let leftBoundary = tableRect.left - (this.__isRTL ? scrollbarWidth : 0);
1021
+ let rightBoundary = tableRect.right + (this.__isRTL ? 0 : scrollbarWidth);
1021
1022
  for (let i = dstCellIndex - 1; i >= 0; i--) {
1022
1023
  const cell = dstRow.children[i];
1023
1024
  if (cell.hasAttribute('hidden') || isDetailsCell(cell)) {
@@ -1040,10 +1041,10 @@ export const KeyboardNavigationMixin = (superClass) =>
1040
1041
  }
1041
1042
 
1042
1043
  if (dstCellRect.left < leftBoundary) {
1043
- this.$.table.scrollLeft += Math.round(dstCellRect.left - leftBoundary);
1044
+ this.$.table.scrollLeft += dstCellRect.left - leftBoundary;
1044
1045
  }
1045
1046
  if (dstCellRect.right > rightBoundary) {
1046
- this.$.table.scrollLeft += Math.round(dstCellRect.right - rightBoundary);
1047
+ this.$.table.scrollLeft += dstCellRect.right - rightBoundary;
1047
1048
  }
1048
1049
  }
1049
1050
 
@@ -31,15 +31,10 @@ import type { GridRowDetailsRenderer, RowDetailsMixinClass } from './vaadin-grid
31
31
  import type { ScrollMixinClass } from './vaadin-grid-scroll-mixin.js';
32
32
  import type { SelectionMixinClass } from './vaadin-grid-selection-mixin.js';
33
33
  import type { SortMixinClass } from './vaadin-grid-sort-mixin.js';
34
- import type {
35
- GridCellClassNameGenerator,
36
- GridCellPartNameGenerator,
37
- StylingMixinClass,
38
- } from './vaadin-grid-styling-mixin.js';
34
+ import type { GridCellPartNameGenerator, StylingMixinClass } from './vaadin-grid-styling-mixin.js';
39
35
 
40
36
  export {
41
37
  GridBodyRenderer,
42
- GridCellClassNameGenerator,
43
38
  GridCellPartNameGenerator,
44
39
  GridDataProvider,
45
40
  GridDataProviderCallback,
@@ -28,6 +28,7 @@ import {
28
28
  iterateRowCells,
29
29
  updateBooleanRowStates,
30
30
  updateCellsPart,
31
+ updatePart,
31
32
  updateState,
32
33
  } from './vaadin-grid-helpers.js';
33
34
  import { KeyboardNavigationMixin } from './vaadin-grid-keyboard-navigation-mixin.js';
@@ -353,9 +354,10 @@ export const GridMixin = (superClass) =>
353
354
  const rows = [];
354
355
  for (let i = 0; i < count; i++) {
355
356
  const row = document.createElement('tr');
356
- row.setAttribute('part', 'row body-row');
357
357
  row.setAttribute('role', 'row');
358
358
  row.setAttribute('tabindex', '-1');
359
+ updatePart(row, 'row', true);
360
+ updatePart(row, 'body-row', true);
359
361
  if (this._columnTree) {
360
362
  this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body', false, true);
361
363
  }
@@ -502,7 +504,8 @@ export const GridMixin = (superClass) =>
502
504
  }
503
505
  column._cells.push(cell);
504
506
  }
505
- cell.setAttribute('part', 'cell body-cell');
507
+ updatePart(cell, 'cell', true);
508
+ updatePart(cell, 'body-cell', true);
506
509
  cell.__parentRow = row;
507
510
  // Cache the cell reference
508
511
  row.__cells.push(cell);
@@ -532,7 +535,7 @@ export const GridMixin = (superClass) =>
532
535
  row.appendChild(detailsCell);
533
536
  // Cache the details cell reference
534
537
  row.__detailsCell = detailsCell;
535
- this._a11ySetRowDetailsCell(row, detailsCell);
538
+ this.__a11ySetRowDetailsCell(row, detailsCell);
536
539
  detailsCell._vacant = false;
537
540
  }
538
541
 
@@ -564,7 +567,8 @@ export const GridMixin = (superClass) =>
564
567
  column._emptyCells.push(cell);
565
568
  }
566
569
  }
567
- cell.part.add('cell', `${section}-cell`);
570
+ updatePart(cell, 'cell', true);
571
+ updatePart(cell, `${section}-cell`, true);
568
572
  }
569
573
 
570
574
  if (!cell._content.parentElement) {
@@ -639,9 +643,19 @@ export const GridMixin = (superClass) =>
639
643
  row.hidden = !visibleRowCells.length;
640
644
  }
641
645
 
646
+ if (row.parentElement === this.$.header) {
647
+ this.$.table.toggleAttribute('has-header', this.$.header.querySelector('tr:not([hidden])'));
648
+ this.__updateHeaderFooterRowParts('header');
649
+ }
650
+
651
+ if (row.parentElement === this.$.footer) {
652
+ this.$.table.toggleAttribute('has-footer', this.$.footer.querySelector('tr:not([hidden])'));
653
+ this.__updateHeaderFooterRowParts('footer');
654
+ }
655
+
642
656
  // Make sure the section has a tabbable element
643
657
  this._resetKeyboardNavigation();
644
- this._a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
658
+ this.__a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
645
659
  }
646
660
 
647
661
  /** @private */
@@ -653,11 +667,6 @@ export const GridMixin = (superClass) =>
653
667
  }
654
668
 
655
669
  row.index = index;
656
-
657
- this._updateRowOrderParts(row, index);
658
-
659
- this._a11yUpdateRowRowindex(row, index);
660
-
661
670
  this.__ensureRowItem(row);
662
671
  this.__ensureRowHierarchy(row);
663
672
  this.__updateRow(row);
@@ -670,17 +679,17 @@ export const GridMixin = (superClass) =>
670
679
  }
671
680
 
672
681
  /** @private */
673
- _updateRowOrderParts(row, index = row.index) {
682
+ __updateRowOrderParts(row) {
674
683
  updateBooleanRowStates(row, {
675
- first: index === 0,
676
- last: index === this._flatSize - 1,
677
- odd: index % 2 !== 0,
678
- even: index % 2 === 0,
684
+ first: row.index === 0,
685
+ last: row.index === this._flatSize - 1,
686
+ odd: row.index % 2 !== 0,
687
+ even: row.index % 2 === 0,
679
688
  });
680
689
  }
681
690
 
682
691
  /** @private */
683
- _updateRowStateParts(row, { item, expanded, selected, detailsOpened }) {
692
+ __updateRowStateParts(row, { item, expanded, selected, detailsOpened }) {
684
693
  updateBooleanRowStates(row, {
685
694
  expanded,
686
695
  collapsed: this.__isRowExpandable(row),
@@ -702,24 +711,22 @@ export const GridMixin = (superClass) =>
702
711
  _renderColumnTree(columnTree) {
703
712
  iterateChildren(this.$.items, (row) => {
704
713
  this.__initRow(row, columnTree[columnTree.length - 1], 'body', false, true);
705
-
706
- const model = this.__getRowModel(row);
707
- this._updateRowOrderParts(row);
708
- this._updateRowStateParts(row, model);
709
- this._filterDragAndDrop(row, model);
714
+ this.__updateRow(row);
710
715
  });
711
716
 
712
717
  while (this.$.header.children.length < columnTree.length) {
713
718
  const headerRow = document.createElement('tr');
714
- headerRow.setAttribute('part', 'row');
715
719
  headerRow.setAttribute('role', 'row');
716
720
  headerRow.setAttribute('tabindex', '-1');
721
+ updatePart(headerRow, 'row', true);
722
+ updatePart(headerRow, 'header-row', true);
717
723
  this.$.header.appendChild(headerRow);
718
724
 
719
725
  const footerRow = document.createElement('tr');
720
- footerRow.setAttribute('part', 'row');
721
726
  footerRow.setAttribute('role', 'row');
722
727
  footerRow.setAttribute('tabindex', '-1');
728
+ updatePart(footerRow, 'row', true);
729
+ updatePart(footerRow, 'footer-row', true);
723
730
  this.$.footer.appendChild(footerRow);
724
731
  }
725
732
  while (this.$.header.children.length > columnTree.length) {
@@ -727,36 +734,43 @@ export const GridMixin = (superClass) =>
727
734
  this.$.footer.removeChild(this.$.footer.firstElementChild);
728
735
  }
729
736
 
730
- iterateChildren(this.$.header, (headerRow, index, rows) => {
737
+ iterateChildren(this.$.header, (headerRow, index) => {
731
738
  this.__initRow(headerRow, columnTree[index], 'header', index === columnTree.length - 1);
732
-
733
- const cells = getBodyRowCells(headerRow);
734
- updateCellsPart(cells, 'first-header-row-cell', index === 0);
735
- updateCellsPart(cells, 'last-header-row-cell', index === rows.length - 1);
736
739
  });
737
740
 
738
- iterateChildren(this.$.footer, (footerRow, index, rows) => {
741
+ iterateChildren(this.$.footer, (footerRow, index) => {
739
742
  this.__initRow(footerRow, columnTree[columnTree.length - 1 - index], 'footer', index === 0);
740
-
741
- const cells = getBodyRowCells(footerRow);
742
- updateCellsPart(cells, 'first-footer-row-cell', index === 0);
743
- updateCellsPart(cells, 'last-footer-row-cell', index === rows.length - 1);
744
743
  });
745
744
 
746
745
  // Sizer rows
747
746
  this.__initRow(this.$.sizer, columnTree[columnTree.length - 1]);
748
747
 
748
+ this.__updateHeaderFooterRowParts('header');
749
+ this.__updateHeaderFooterRowParts('footer');
749
750
  this._resizeHandler();
750
751
  this._frozenCellsChanged();
751
752
  this._updateFirstAndLastColumn();
752
753
  this._resetKeyboardNavigation();
753
- this._a11yUpdateHeaderRows();
754
- this._a11yUpdateFooterRows();
755
- this.generateCellClassNames();
754
+ this.__a11yUpdateHeaderRows();
755
+ this.__a11yUpdateFooterRows();
756
756
  this.generateCellPartNames();
757
757
  this.__updateHeaderAndFooter();
758
758
  }
759
759
 
760
+ /** @private */
761
+ __updateHeaderFooterRowParts(section) {
762
+ const visibleRows = [...this.$[section].querySelectorAll('tr:not([hidden])')];
763
+ [...this.$[section].children].forEach((row) => {
764
+ updatePart(row, `first-${section}-row`, row === visibleRows.at(0));
765
+ updatePart(row, `last-${section}-row`, row === visibleRows.at(-1));
766
+
767
+ getBodyRowCells(row).forEach((cell) => {
768
+ updatePart(cell, `first-${section}-row-cell`, row === visibleRows.at(0));
769
+ updatePart(cell, `last-${section}-row-cell`, row === visibleRows.at(-1));
770
+ });
771
+ });
772
+ }
773
+
760
774
  /**
761
775
  * @param {!HTMLElement} row
762
776
  * @param {boolean} loading
@@ -773,7 +787,6 @@ export const GridMixin = (superClass) =>
773
787
 
774
788
  if (loading) {
775
789
  // Run style generators for the loading row to have custom names cleared
776
- this._generateCellClassNames(row);
777
790
  this._generateCellPartNames(row);
778
791
  }
779
792
  }
@@ -783,6 +796,9 @@ export const GridMixin = (superClass) =>
783
796
  * @private
784
797
  */
785
798
  __updateRow(row) {
799
+ this.__a11yUpdateRowRowindex(row);
800
+ this.__updateRowOrderParts(row);
801
+
786
802
  const item = this.__getRowItem(row);
787
803
  if (item) {
788
804
  this.__updateRowLoading(row, false);
@@ -796,12 +812,11 @@ export const GridMixin = (superClass) =>
796
812
 
797
813
  this._toggleDetailsCell(row, model.detailsOpened);
798
814
 
799
- this._a11yUpdateRowLevel(row, model.level);
800
- this._a11yUpdateRowSelected(row, model.selected);
815
+ this.__a11yUpdateRowLevel(row, model.level);
816
+ this.__a11yUpdateRowSelected(row, model.selected);
801
817
 
802
- this._updateRowStateParts(row, model);
818
+ this.__updateRowStateParts(row, model);
803
819
 
804
- this._generateCellClassNames(row, model);
805
820
  this._generateCellPartNames(row, model);
806
821
  this._filterDragAndDrop(row, model);
807
822
  this.__updateDragSourceParts(row, model);
@@ -818,7 +833,7 @@ export const GridMixin = (superClass) =>
818
833
 
819
834
  this._updateDetailsCellHeight(row);
820
835
 
821
- this._a11yUpdateRowExpanded(row, model.expanded);
836
+ this.__a11yUpdateRowExpanded(row, model.expanded);
822
837
  }
823
838
 
824
839
  /** @private */