@vaadin/grid 25.3.0-dev.3a3c2d7d2a → 25.3.0-rc1

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 (33) hide show
  1. package/custom-elements.json +194 -135
  2. package/package.json +12 -12
  3. package/src/directives/cell-content-directive.js +33 -0
  4. package/src/styles/vaadin-grid-base-styles.js +7 -0
  5. package/src/vaadin-grid-a11y-mixin.js +15 -28
  6. package/src/vaadin-grid-active-item-mixin.js +1 -1
  7. package/src/vaadin-grid-column-auto-width-mixin.js +4 -13
  8. package/src/vaadin-grid-column-group-mixin.js +5 -20
  9. package/src/vaadin-grid-column-mixin.d.ts +3 -3
  10. package/src/vaadin-grid-column-mixin.js +57 -109
  11. package/src/vaadin-grid-column-reordering-mixin.js +10 -5
  12. package/src/vaadin-grid-column-resizing-mixin.js +4 -0
  13. package/src/vaadin-grid-data-provider-mixin.js +2 -2
  14. package/src/vaadin-grid-drag-and-drop-mixin.js +14 -5
  15. package/src/vaadin-grid-dynamic-columns-mixin.js +0 -5
  16. package/src/vaadin-grid-filter-column-mixin.js +1 -1
  17. package/src/vaadin-grid-filter-element-mixin.js +1 -3
  18. package/src/vaadin-grid-filter.js +1 -0
  19. package/src/vaadin-grid-header-footer-rendering-mixin.js +296 -0
  20. package/src/vaadin-grid-keyboard-navigation-mixin.js +3 -7
  21. package/src/vaadin-grid-mixin.js +67 -229
  22. package/src/vaadin-grid-row-details-mixin.js +1 -1
  23. package/src/vaadin-grid-scroll-mixin.js +7 -32
  24. package/src/vaadin-grid-selection-column-base-mixin.js +36 -20
  25. package/src/vaadin-grid-sort-column-mixin.js +11 -2
  26. package/src/vaadin-grid-sorter-mixin.js +3 -7
  27. package/src/vaadin-grid-sorter.js +1 -0
  28. package/src/vaadin-grid-tree-toggle.d.ts +5 -3
  29. package/src/vaadin-grid-tree-toggle.js +6 -3
  30. package/src/vaadin-grid.d.ts +33 -0
  31. package/src/vaadin-grid.js +30 -1
  32. package/web-types.json +22 -96
  33. package/web-types.lit.json +12 -12
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2026 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { html } from 'lit';
7
+ import { AsyncDirective, directive } from 'lit/async-directive.js';
8
+
9
+ /**
10
+ * A directive that manages the `<vaadin-grid-cell-content>` element for a cell.
11
+ */
12
+ class CellContentDirective extends AsyncDirective {
13
+ #cell;
14
+
15
+ update(part, [grid, slotName, { textAlign } = {}]) {
16
+ this.#cell = part.parentNode;
17
+ this.#cell._content ??= document.createElement('vaadin-grid-cell-content');
18
+ this.#cell._content.slot = slotName;
19
+ this.#cell._content.style.textAlign = textAlign ?? '';
20
+
21
+ if (!grid.contains(this.#cell._content)) {
22
+ grid.appendChild(this.#cell._content);
23
+ }
24
+
25
+ return html`<slot name="${slotName}"></slot>`;
26
+ }
27
+
28
+ disconnected() {
29
+ this.#cell._content?.remove();
30
+ }
31
+ }
32
+
33
+ export const cellContent = directive(CellContentDirective);
@@ -597,6 +597,13 @@ export const gridStyles = css`
597
597
  outline-offset: calc(var(--vaadin-grid-border-width, 1px) * -1);
598
598
  }
599
599
 
600
+ /* Styles applied to draggable cell content; see _filterDragAndDrop in vaadin-grid-drag-and-drop-mixin.js. */
601
+ ::slotted(vaadin-grid-cell-content[draggable-source]) {
602
+ -webkit-user-drag: element;
603
+ -webkit-user-select: none;
604
+ user-select: none;
605
+ }
606
+
600
607
  .row[dragover] {
601
608
  z-index: 100 !important;
602
609
  }
@@ -3,6 +3,7 @@
3
3
  * Copyright (c) 2016 - 2026 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
6
7
  import { findTreeToggleCell, getClosestCell, iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
7
8
 
8
9
  export const A11yMixin = (superClass) =>
@@ -19,7 +20,7 @@ export const A11yMixin = (superClass) =>
19
20
  };
20
21
  }
21
22
  static get observers() {
22
- return ['__a11yUpdateGridSize(size, _columnTree, __emptyState)', '__a11yI18nChanged(__effectiveI18n)'];
23
+ return ['__a11yUpdateGridSize(_flatSize, _columnTree, __emptyState)', '__a11yI18nChanged(__effectiveI18n)'];
23
24
  }
24
25
 
25
26
  /** @private */
@@ -43,14 +44,14 @@ export const A11yMixin = (superClass) =>
43
44
  }
44
45
 
45
46
  /** @private */
46
- __a11yUpdateGridSize(size, _columnTree, emptyState) {
47
- if (size === undefined || _columnTree === undefined) {
47
+ __a11yUpdateGridSize(flatSize, _columnTree, emptyState) {
48
+ if (flatSize === undefined || _columnTree === undefined) {
48
49
  return;
49
50
  }
50
51
 
51
52
  const headerRowsCount = this.__a11yGetHeaderRowCount(_columnTree);
52
53
  const footerRowsCount = this.__a11yGetFooterRowCount(_columnTree);
53
- const bodyRowsCount = emptyState ? 1 : size;
54
+ const bodyRowsCount = emptyState ? 1 : flatSize;
54
55
  const rowsCount = bodyRowsCount + headerRowsCount + footerRowsCount;
55
56
 
56
57
  this.$.table.setAttribute('aria-rowcount', rowsCount);
@@ -75,8 +76,12 @@ export const A11yMixin = (superClass) =>
75
76
 
76
77
  /** @private */
77
78
  __a11yUpdateFooterRows() {
79
+ const bodyRowCount = this._flatSize || 0;
78
80
  iterateChildren(this.$.footer, (footerRow, index) => {
79
- footerRow.setAttribute('aria-rowindex', this.__a11yGetHeaderRowCount(this._columnTree) + this.size + index + 1);
81
+ footerRow.setAttribute(
82
+ 'aria-rowindex',
83
+ this.__a11yGetHeaderRowCount(this._columnTree) + bodyRowCount + index + 1,
84
+ );
80
85
  });
81
86
  }
82
87
 
@@ -110,19 +115,13 @@ export const A11yMixin = (superClass) =>
110
115
  const toggleCell = findTreeToggleCell(row);
111
116
  if (this.__isRowExpandable(row)) {
112
117
  row.setAttribute('aria-expanded', 'false');
113
- if (toggleCell) {
114
- toggleCell.setAttribute('aria-expanded', 'false');
115
- }
118
+ toggleCell?.setAttribute('aria-expanded', 'false');
116
119
  } else if (this.__isRowCollapsible(row)) {
117
120
  row.setAttribute('aria-expanded', 'true');
118
- if (toggleCell) {
119
- toggleCell.setAttribute('aria-expanded', 'true');
120
- }
121
+ toggleCell?.setAttribute('aria-expanded', 'true');
121
122
  } else {
122
123
  row.removeAttribute('aria-expanded');
123
- if (toggleCell) {
124
- toggleCell.removeAttribute('aria-expanded');
125
- }
124
+ toggleCell?.removeAttribute('aria-expanded');
126
125
  }
127
126
  }
128
127
 
@@ -133,11 +132,8 @@ export const A11yMixin = (superClass) =>
133
132
  */
134
133
  __a11yUpdateRowLevel(row, level) {
135
134
  // Set level for the expandable rows itself, and all the nested rows.
136
- if (level > 0 || this.__isRowCollapsible(row) || this.__isRowExpandable(row)) {
137
- row.setAttribute('aria-level', level + 1);
138
- } else {
139
- row.removeAttribute('aria-level');
140
- }
135
+ const hasLevel = level > 0 || this.__isRowCollapsible(row) || this.__isRowExpandable(row);
136
+ setOrRemoveAttribute(row, 'aria-level', hasLevel && level + 1);
141
137
  }
142
138
 
143
139
  /**
@@ -153,15 +149,6 @@ export const A11yMixin = (superClass) =>
153
149
  });
154
150
  }
155
151
 
156
- /**
157
- * @param {!HTMLElement} cell
158
- * @param {number} colspan
159
- * @private
160
- */
161
- __a11yUpdateCellColspan(cell, colspan) {
162
- cell.setAttribute('aria-colspan', Number(colspan));
163
- }
164
-
165
152
  /** @private */
166
153
  __a11yUpdateSorters() {
167
154
  Array.from(this.querySelectorAll('vaadin-grid-sorter')).forEach((sorter) => {
@@ -76,7 +76,7 @@ export const ActiveItemMixin = (superClass) =>
76
76
  // Cell is the empty state cell
77
77
  cell === this.$.emptystatecell ||
78
78
  // Cell content is focused
79
- cell._content.contains(this.getRootNode().activeElement) ||
79
+ cell._content.matches(':focus-within') ||
80
80
  // Clicked on a focusable element
81
81
  this._isFocusable(e.target) ||
82
82
  // Clicked on a label element
@@ -137,11 +137,7 @@ export const ColumnAutoWidthMixin = (superClass) =>
137
137
  _recalculateColumnWidths() {
138
138
  // Flush to make sure DOM is up-to-date when measuring the column widths
139
139
  this.__virtualizer.flush();
140
- [...this.$.header.children, ...this.$.footer.children].forEach((row) => {
141
- if (row.__debounceUpdateHeaderFooterRowVisibility) {
142
- row.__debounceUpdateHeaderFooterRowVisibility.flush();
143
- }
144
- });
140
+ this.__renderHeaderFooterDebouncer?.flush();
145
141
 
146
142
  this.__hasHadRenderedRowsForColumnWidthCalculation ||= this._getRenderedRows().length > 0;
147
143
 
@@ -218,11 +214,7 @@ export const ColumnAutoWidthMixin = (superClass) =>
218
214
  }
219
215
  });
220
216
 
221
- if (autoWidth) {
222
- this.$.scroller.setAttribute('measuring-auto-width', '');
223
- } else {
224
- this.$.scroller.removeAttribute('measuring-auto-width');
225
- }
217
+ this.$.scroller.toggleAttribute('measuring-auto-width', autoWidth);
226
218
  }
227
219
 
228
220
  /**
@@ -315,10 +307,9 @@ export const ColumnAutoWidthMixin = (superClass) =>
315
307
  // of the virtualizer update loop, ensuring the grid eventually reaches a stable state.
316
308
  const hasRowsWithUndefinedIndex = [...this.$.items.children].some((row) => row.index === undefined);
317
309
 
318
- const debouncingHiddenChanged = this._debouncerHiddenChanged && this._debouncerHiddenChanged.isActive();
310
+ const debouncingHiddenChanged = this._debouncerHiddenChanged?.isActive();
319
311
 
320
- const debouncingUpdateFrozenColumn =
321
- this.__debounceUpdateFrozenColumn && this.__debounceUpdateFrozenColumn.isActive();
312
+ const debouncingUpdateFrozenColumn = this.__debounceUpdateFrozenColumn?.isActive();
322
313
 
323
314
  // When using a percentage-based height, the grid's effective height may
324
315
  // be 0 until the resize observer in grid-mixin calculates a min-height.
@@ -57,7 +57,7 @@ export const GridColumnGroupMixin = (superClass) =>
57
57
  '_groupFrozenChanged(frozen, _rootColumns)',
58
58
  '_groupFrozenToEndChanged(frozenToEnd, _rootColumns)',
59
59
  '_groupHiddenChanged(hidden)',
60
- '_colSpanChanged(_colSpan, _headerCell, _footerCell)',
60
+ '_colSpanChanged(_colSpan)',
61
61
  '_groupOrderChanged(_order, _rootColumns)',
62
62
  '_groupReorderStatusChanged(_reorderStatus, _rootColumns)',
63
63
  '_groupResizableChanged(resizable, _rootColumns)',
@@ -74,9 +74,7 @@ export const GridColumnGroupMixin = (superClass) =>
74
74
  /** @protected */
75
75
  disconnectedCallback() {
76
76
  super.disconnectedCallback();
77
- if (this._observer) {
78
- this._observer.disconnect();
79
- }
77
+ this._observer?.disconnect();
80
78
  }
81
79
 
82
80
  /**
@@ -307,19 +305,8 @@ export const GridColumnGroupMixin = (superClass) =>
307
305
  }
308
306
 
309
307
  /** @private */
310
- _colSpanChanged(colSpan, headerCell, footerCell) {
311
- if (headerCell) {
312
- headerCell.setAttribute('colspan', colSpan);
313
- if (this._grid) {
314
- this._grid.__a11yUpdateCellColspan(headerCell, colSpan);
315
- }
316
- }
317
- if (footerCell) {
318
- footerCell.setAttribute('colspan', colSpan);
319
- if (this._grid) {
320
- this._grid.__a11yUpdateCellColspan(footerCell, colSpan);
321
- }
322
- }
308
+ _colSpanChanged() {
309
+ this._grid?.__scheduleRenderHeaderFooter?.();
323
310
  }
324
311
 
325
312
  /**
@@ -344,9 +331,7 @@ export const GridColumnGroupMixin = (superClass) =>
344
331
  this._preventHiddenSynchronization = false;
345
332
 
346
333
  // Update the column tree
347
- if (this._grid && this._grid._debounceUpdateColumnTree) {
348
- this._grid._debounceUpdateColumnTree();
349
- }
334
+ this._grid?._debounceUpdateColumnTree?.();
350
335
  });
351
336
  this._observer.flush();
352
337
  }
@@ -12,7 +12,7 @@ export type GridBodyRenderer<TItem, Column extends GridColumnMixin<TItem, Column
12
12
  model: GridItemModel<TItem>,
13
13
  ) => void;
14
14
 
15
- export type GridColumnTextAlign = 'center' | 'end' | 'start' | null;
15
+ export type GridColumnTextAlign = 'center' | 'end' | 'left' | 'right' | 'start' | null;
16
16
 
17
17
  export type GridHeaderFooterRenderer<TItem, Column extends ColumnBaseMixinClass<TItem, Column>> = (
18
18
  root: HTMLElement,
@@ -71,8 +71,8 @@ export declare class ColumnBaseMixinClass<TItem, Column extends ColumnBaseMixinC
71
71
 
72
72
  /**
73
73
  * Aligns the columns cell content horizontally.
74
- * Supported values: "start", "center" and "end".
75
- * @attr {start|center|end} text-align
74
+ *
75
+ * @attr {start|center|end|left|right} text-align
76
76
  */
77
77
  textAlign: GridColumnTextAlign | null | undefined;
78
78
 
@@ -7,7 +7,8 @@ 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, updatePart } from './vaadin-grid-helpers.js';
10
+ import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
11
+ import { updateCellState } from './vaadin-grid-helpers.js';
11
12
 
12
13
  export const ColumnBaseMixin = (superClass) =>
13
14
  class ColumnBaseMixin extends superClass {
@@ -92,8 +93,8 @@ export const ColumnBaseMixin = (superClass) =>
92
93
 
93
94
  /**
94
95
  * Aligns the columns cell content horizontally.
95
- * Supported values: "start", "center" and "end".
96
- * @attr {start|center|end} text-align
96
+ *
97
+ * @attr {start|center|end|left|right} text-align
97
98
  */
98
99
  textAlign: {
99
100
  type: String,
@@ -244,37 +245,26 @@ export const ColumnBaseMixin = (superClass) =>
244
245
  type: Function,
245
246
  computed: '_computeFooterRenderer(footerRenderer, __initialized)',
246
247
  },
247
-
248
- /**
249
- * An internal property that is mainly used by `vaadin-template-renderer`
250
- * to identify grid column elements.
251
- *
252
- * @private
253
- */
254
- __gridColumnElement: {
255
- type: Boolean,
256
- value: true,
257
- },
258
248
  };
259
249
  }
260
250
 
261
251
  static get observers() {
262
252
  return [
263
- '_widthChanged(width, _headerCell, _footerCell, _cells)',
253
+ '_widthChanged(width, _cells)',
264
254
  '_frozenChanged(frozen, _headerCell, _footerCell, _cells)',
265
255
  '_frozenToEndChanged(frozenToEnd, _headerCell, _footerCell, _cells)',
266
- '_flexGrowChanged(flexGrow, _headerCell, _footerCell, _cells)',
267
- '_textAlignChanged(textAlign, _cells, _headerCell, _footerCell)',
256
+ '_flexGrowChanged(flexGrow, _cells)',
257
+ '_textAlignChanged(textAlign, _cells)',
268
258
  '_lastFrozenChanged(_lastFrozen)',
269
259
  '_firstFrozenToEndChanged(_firstFrozenToEnd)',
270
260
  '_onRendererOrBindingChanged(_renderer, _cells, _bodyContentHidden, path)',
271
261
  '_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header)',
272
262
  '_onFooterRendererOrBindingChanged(_footerRenderer, _footerCell)',
273
- '_resizableChanged(resizable, _headerCell)',
263
+ '_resizableChanged(resizable)',
274
264
  '_reorderStatusChanged(_reorderStatus, _headerCell, _footerCell, _cells)',
275
265
  '_hiddenChanged(hidden, _headerCell, _footerCell, _cells)',
276
266
  '_rowHeaderChanged(rowHeader, _cells)',
277
- '__headerFooterPartNameChanged(_headerCell, _footerCell, headerPartName, footerPartName)',
267
+ '__headerFooterPartNameChanged(headerPartName, footerPartName)',
278
268
  ];
279
269
  }
280
270
 
@@ -302,6 +292,19 @@ export const ColumnBaseMixin = (superClass) =>
302
292
  .filter((cell) => cell);
303
293
  }
304
294
 
295
+ constructor() {
296
+ super();
297
+
298
+ /**
299
+ * A stable unique id assigned once per column instance. Used to build
300
+ * cell content slot names that stay stable across re-renders, so lit
301
+ * can reuse the cell content elements when the column tree changes.
302
+ *
303
+ * @protected
304
+ */
305
+ this._id = generateUniqueId();
306
+ }
307
+
305
308
  /** @protected */
306
309
  connectedCallback() {
307
310
  super.connectedCallback();
@@ -313,7 +316,7 @@ export const ColumnBaseMixin = (superClass) =>
313
316
  return;
314
317
  }
315
318
 
316
- this._allCells.forEach((cell) => {
319
+ this._cells?.forEach((cell) => {
317
320
  if (!cell._content.parentNode) {
318
321
  this._grid.appendChild(cell._content);
319
322
  }
@@ -332,10 +335,8 @@ export const ColumnBaseMixin = (superClass) =>
332
335
  return;
333
336
  }
334
337
 
335
- this._allCells.forEach((cell) => {
336
- if (cell._content.parentNode) {
337
- cell._content.parentNode.removeChild(cell._content);
338
- }
338
+ this._cells?.forEach((cell) => {
339
+ cell._content.parentNode?.removeChild(cell._content);
339
340
  });
340
341
  });
341
342
 
@@ -364,46 +365,40 @@ export const ColumnBaseMixin = (superClass) =>
364
365
 
365
366
  /** @private */
366
367
  _flexGrowChanged(flexGrow) {
367
- if (this.parentElement && this.parentElement._columnPropChanged) {
368
- this.parentElement._columnPropChanged('flexGrow');
369
- }
368
+ this.parentElement?._columnPropChanged?.('flexGrow');
370
369
 
371
- this._allCells.forEach((cell) => {
370
+ this._grid?.__scheduleRenderHeaderFooter?.();
371
+
372
+ this._cells?.forEach((cell) => {
372
373
  cell.style.flexGrow = flexGrow;
373
374
  });
374
375
  }
375
376
 
376
377
  /** @private */
377
378
  _widthChanged(width) {
378
- if (this.parentElement && this.parentElement._columnPropChanged) {
379
- this.parentElement._columnPropChanged('width');
380
- }
379
+ this.parentElement?._columnPropChanged?.('width');
381
380
 
382
- this._allCells.forEach((cell) => {
381
+ this._grid?.__scheduleRenderHeaderFooter?.();
382
+
383
+ this._cells?.forEach((cell) => {
383
384
  cell.style.width = width;
384
385
  });
385
386
  }
386
387
 
387
388
  /** @private */
388
389
  _frozenChanged(frozen) {
389
- if (this.parentElement && this.parentElement._columnPropChanged) {
390
- this.parentElement._columnPropChanged('frozen', frozen);
391
- }
390
+ this.parentElement?._columnPropChanged?.('frozen', frozen);
392
391
 
393
392
  this._allCells.forEach((cell) => {
394
393
  updateCellState(cell, 'frozen', frozen);
395
394
  });
396
395
 
397
- if (this._grid && this._grid._frozenCellsChanged) {
398
- this._grid._frozenCellsChanged();
399
- }
396
+ this._grid?._frozenCellsChanged?.();
400
397
  }
401
398
 
402
399
  /** @private */
403
400
  _frozenToEndChanged(frozenToEnd) {
404
- if (this.parentElement && this.parentElement._columnPropChanged) {
405
- this.parentElement._columnPropChanged('frozenToEnd', frozenToEnd);
406
- }
401
+ this.parentElement?._columnPropChanged?.('frozenToEnd', frozenToEnd);
407
402
 
408
403
  this._allCells.forEach((cell) => {
409
404
  // Skip sizer cells to keep correct scrollWidth.
@@ -414,9 +409,7 @@ export const ColumnBaseMixin = (superClass) =>
414
409
  updateCellState(cell, 'frozen-to-end', frozenToEnd);
415
410
  });
416
411
 
417
- if (this._grid && this._grid._frozenCellsChanged) {
418
- this._grid._frozenCellsChanged();
419
- }
412
+ this._grid?._frozenCellsChanged?.();
420
413
  }
421
414
 
422
415
  /** @private */
@@ -448,11 +441,7 @@ export const ColumnBaseMixin = (superClass) =>
448
441
 
449
442
  /** @private */
450
443
  _rowHeaderChanged(rowHeader, cells) {
451
- if (!cells) {
452
- return;
453
- }
454
-
455
- cells.forEach((cell) => {
444
+ cells?.forEach((cell) => {
456
445
  cell.setAttribute('role', rowHeader ? 'rowheader' : 'gridcell');
457
446
  });
458
447
  }
@@ -485,75 +474,47 @@ export const ColumnBaseMixin = (superClass) =>
485
474
  }
486
475
 
487
476
  /** @private */
488
- _resizableChanged(resizable, headerCell) {
489
- if (resizable === undefined || headerCell === undefined) {
477
+ _resizableChanged(resizable) {
478
+ if (resizable === undefined) {
490
479
  return;
491
480
  }
492
481
 
493
- if (headerCell) {
494
- [headerCell].concat(this._emptyCells).forEach((cell) => {
495
- if (cell) {
496
- const existingHandle = cell.querySelector('[part~="resize-handle"]');
497
- if (existingHandle) {
498
- cell.removeChild(existingHandle);
499
- }
500
-
501
- if (resizable) {
502
- const handle = document.createElement('div');
503
- updatePart(handle, 'resize-handle', true);
504
- cell.appendChild(handle);
505
- }
506
- }
507
- });
508
- }
482
+ this._grid?.__scheduleRenderHeaderFooter?.();
509
483
  }
510
484
 
511
485
  /** @private */
512
486
  _textAlignChanged(textAlign) {
513
- if (textAlign === undefined || this._grid === undefined) {
514
- return;
515
- }
516
- if (['start', 'end', 'center'].indexOf(textAlign) === -1) {
517
- console.warn('textAlign can only be set as "start", "end" or "center"');
487
+ if (textAlign === undefined) {
518
488
  return;
519
489
  }
520
490
 
521
- this._allCells.forEach((cell) => {
491
+ this._grid?.__scheduleRenderHeaderFooter?.();
492
+
493
+ this._cells?.forEach((cell) => {
522
494
  cell._content.style.textAlign = textAlign;
523
495
  });
524
496
  }
525
497
 
526
498
  /** @private */
527
499
  _hiddenChanged(hidden) {
528
- if (this.parentElement && this.parentElement._columnPropChanged) {
529
- this.parentElement._columnPropChanged('hidden', hidden);
530
- }
500
+ this.parentElement?._columnPropChanged?.('hidden', hidden);
531
501
 
532
502
  if (!!hidden !== !!this._previousHidden && this._grid) {
533
503
  if (hidden === true) {
534
- this._allCells.forEach((cell) => {
535
- if (cell._content.parentNode) {
536
- cell._content.parentNode.removeChild(cell._content);
537
- }
504
+ this._cells?.forEach((cell) => {
505
+ cell._content.parentNode?.removeChild(cell._content);
538
506
  });
539
507
  }
540
508
  this._grid._debouncerHiddenChanged = Debouncer.debounce(
541
509
  this._grid._debouncerHiddenChanged,
542
510
  animationFrame,
543
511
  () => {
544
- if (this._grid && this._grid._renderColumnTree) {
545
- this._grid._renderColumnTree(this._grid._columnTree);
546
- }
512
+ this._grid?._renderColumnTree?.(this._grid._columnTree);
547
513
  },
548
514
  );
549
515
 
550
- if (this._grid._debounceUpdateFrozenColumn) {
551
- this._grid._debounceUpdateFrozenColumn();
552
- }
553
-
554
- if (this._grid._resetKeyboardNavigation) {
555
- this._grid._resetKeyboardNavigation();
556
- }
516
+ this._grid._debounceUpdateFrozenColumn?.();
517
+ this._grid._resetKeyboardNavigation?.();
557
518
  }
558
519
  this._previousHidden = hidden;
559
520
  }
@@ -632,9 +593,8 @@ export const ColumnBaseMixin = (superClass) =>
632
593
  }
633
594
 
634
595
  this.__renderCellsContent(headerRenderer, [headerCell]);
635
- if (this._grid && headerCell.parentElement) {
636
- this._grid.__debounceUpdateHeaderFooterRowVisibility(headerCell.parentElement);
637
- }
596
+
597
+ this._grid?.__scheduleRenderHeaderFooter?.();
638
598
  }
639
599
 
640
600
  /** @protected */
@@ -643,19 +603,8 @@ export const ColumnBaseMixin = (superClass) =>
643
603
  }
644
604
 
645
605
  /** @private */
646
- __headerFooterPartNameChanged(headerCell, footerCell, headerPartName, footerPartName) {
647
- [
648
- { cell: headerCell, partName: headerPartName },
649
- { cell: footerCell, partName: footerPartName },
650
- ].forEach(({ cell, partName }) => {
651
- if (cell) {
652
- const customParts = cell.__customParts || [];
653
- cell.part.remove(...customParts);
654
-
655
- cell.__customParts = partName ? partName.trim().split(' ') : [];
656
- cell.part.add(...cell.__customParts);
657
- }
658
- });
606
+ __headerFooterPartNameChanged() {
607
+ this._grid?.__scheduleRenderHeaderFooter?.();
659
608
  }
660
609
 
661
610
  /**
@@ -689,9 +638,8 @@ export const ColumnBaseMixin = (superClass) =>
689
638
  }
690
639
 
691
640
  this.__renderCellsContent(footerRenderer, [footerCell]);
692
- if (this._grid && footerCell.parentElement) {
693
- this._grid.__debounceUpdateHeaderFooterRowVisibility(footerCell.parentElement);
694
- }
641
+
642
+ this._grid?.__scheduleRenderHeaderFooter?.();
695
643
  }
696
644
 
697
645
  /** @protected */
@@ -116,7 +116,7 @@ export const ColumnReorderingMixin = (superClass) =>
116
116
  return;
117
117
  }
118
118
 
119
- if (headerCell._content.contains(this.getRootNode().activeElement)) {
119
+ if (headerCell._content.matches(':focus-within')) {
120
120
  // Something was focused inside the cell
121
121
  return;
122
122
  }
@@ -203,6 +203,8 @@ export const ColumnReorderingMixin = (superClass) =>
203
203
  for (let i = startIndex; i !== endIndex; i += direction) {
204
204
  this._swapColumnOrders(this._draggedColumn, levelColumnsInOrder[i + direction]);
205
205
  }
206
+
207
+ this.__renderHeaderFooter();
206
208
  }
207
209
 
208
210
  this._updateGhostPosition(e.detail.x, this._touchDevice ? e.detail.y - 50 : e.detail.y);
@@ -434,7 +436,7 @@ export const ColumnReorderingMixin = (superClass) =>
434
436
  }
435
437
 
436
438
  /**
437
- * Swaps column orders and physically reorders cells in all rows.
439
+ * Swaps column orders and reorders cells in all rows.
438
440
  * @param {!GridColumn} column1
439
441
  * @param {!GridColumn} column2
440
442
  * @protected
@@ -444,8 +446,7 @@ export const ColumnReorderingMixin = (superClass) =>
444
446
  [column1._order, column2._order] = [column2._order, column1._order];
445
447
  const [firstColumn, secondColumn] = column1._order < column2._order ? [column1, column2] : [column2, column1];
446
448
 
447
- // Reorder cells in all rows (header, footer, body, sizer)
448
- [...this.$.header.children, ...this.$.footer.children, ...this.$.items.children, this.$.sizer].forEach((row) => {
449
+ [...this.$.items.children, this.$.sizer].forEach((row) => {
449
450
  const cells = getBodyRowCells(row);
450
451
  const firstColumnCells = cells.filter((cell) => firstColumn.contains(cell._column));
451
452
  const secondColumnFirstCell = cells.find((cell) => secondColumn.contains(cell._column));
@@ -456,8 +457,12 @@ export const ColumnReorderingMixin = (superClass) =>
456
457
  }
457
458
  });
458
459
 
460
+ [...this.$.items.children, this.$.sizer].forEach((row) => {
461
+ this._updateFirstAndLastColumnForRow(row);
462
+ });
463
+
464
+ this.__scheduleRenderHeaderFooter();
459
465
  this._debounceUpdateFrozenColumn();
460
- this._updateFirstAndLastColumn();
461
466
  }
462
467
 
463
468
  /**
@@ -98,6 +98,10 @@ export const ColumnResizingMixin = (superClass) =>
98
98
  cell._column.flexGrow = 0;
99
99
  });
100
100
 
101
+ // Flush the scheduled header/footer render to apply the new widths
102
+ // to the cells before measuring the layout below
103
+ this.__renderHeaderFooterDebouncer?.flush();
104
+
101
105
  const cellFrozenToEnd = this._frozenToEndCells[0];
102
106
 
103
107
  // When handle moves below the cell frozen to end, scroll into view.
@@ -214,7 +214,7 @@ export const DataProviderMixin = (superClass) =>
214
214
  * @protected
215
215
  */
216
216
  _isExpanded(item) {
217
- return this.__expandedKeys && this.__expandedKeys.has(this.getItemId(item));
217
+ return this.__expandedKeys?.has(this.getItemId(item));
218
218
  }
219
219
 
220
220
  /**
@@ -429,7 +429,7 @@ export const DataProviderMixin = (superClass) =>
429
429
 
430
430
  /** @private */
431
431
  __scrollToPendingIndexes() {
432
- if (this.__pendingScrollToIndexes && this.$.items.children.length) {
432
+ if (this.__pendingScrollToIndexes) {
433
433
  const indexes = this.__pendingScrollToIndexes;
434
434
  delete this.__pendingScrollToIndexes;
435
435
  this.scrollToIndex(...indexes);