@vaadin/grid 25.2.7 → 25.3.0-alpha10

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 +248 -128
  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 +0 -4
  5. package/src/vaadin-grid-a11y-mixin.js +20 -31
  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 -98
  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 +1 -1
  14. package/src/vaadin-grid-drag-and-drop-mixin.js +2 -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-helpers.js +13 -0
  21. package/src/vaadin-grid-keyboard-navigation-mixin.js +3 -7
  22. package/src/vaadin-grid-mixin.js +69 -228
  23. package/src/vaadin-grid-row-details-mixin.js +1 -1
  24. package/src/vaadin-grid-scroll-mixin.js +2 -11
  25. package/src/vaadin-grid-selection-column-base-mixin.js +66 -21
  26. package/src/vaadin-grid-sort-column-mixin.js +11 -2
  27. package/src/vaadin-grid-sorter-mixin.js +22 -2
  28. package/src/vaadin-grid-sorter.js +2 -1
  29. package/src/vaadin-grid-tree-toggle.js +1 -0
  30. package/src/vaadin-grid.d.ts +63 -1
  31. package/src/vaadin-grid.js +51 -2
  32. package/web-types.json +29 -94
  33. package/web-types.lit.json +17 -10
@@ -87,10 +87,6 @@ export const gridStyles = css`
87
87
  z-index: 0;
88
88
  }
89
89
 
90
- [no-scrollbars]:is([safari], [firefox]) #table {
91
- overflow: hidden;
92
- }
93
-
94
90
  #header,
95
91
  #footer {
96
92
  display: block;
@@ -3,7 +3,8 @@
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 { findTreeToggleCell, iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
6
+ import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
7
+ import { findTreeToggleCell, getClosestCell, iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
7
8
 
8
9
  export const A11yMixin = (superClass) =>
9
10
  class A11yMixin extends superClass {
@@ -19,7 +20,15 @@ export const A11yMixin = (superClass) =>
19
20
  };
20
21
  }
21
22
  static get observers() {
22
- return ['__a11yUpdateGridSize(size, _columnTree, __emptyState)'];
23
+ return ['__a11yUpdateGridSize(size, _columnTree, __emptyState)', '__a11yI18nChanged(__effectiveI18n)'];
24
+ }
25
+
26
+ /** @private */
27
+ __a11yI18nChanged() {
28
+ // Update selection column checkboxes accessible name.
29
+ this.requestContentUpdate();
30
+ // Update sorters accessible name.
31
+ this.__a11yUpdateSorters();
23
32
  }
24
33
 
25
34
  /** @private */
@@ -102,19 +111,13 @@ export const A11yMixin = (superClass) =>
102
111
  const toggleCell = findTreeToggleCell(row);
103
112
  if (this.__isRowExpandable(row)) {
104
113
  row.setAttribute('aria-expanded', 'false');
105
- if (toggleCell) {
106
- toggleCell.setAttribute('aria-expanded', 'false');
107
- }
114
+ toggleCell?.setAttribute('aria-expanded', 'false');
108
115
  } else if (this.__isRowCollapsible(row)) {
109
116
  row.setAttribute('aria-expanded', 'true');
110
- if (toggleCell) {
111
- toggleCell.setAttribute('aria-expanded', 'true');
112
- }
117
+ toggleCell?.setAttribute('aria-expanded', 'true');
113
118
  } else {
114
119
  row.removeAttribute('aria-expanded');
115
- if (toggleCell) {
116
- toggleCell.removeAttribute('aria-expanded');
117
- }
120
+ toggleCell?.removeAttribute('aria-expanded');
118
121
  }
119
122
  }
120
123
 
@@ -125,11 +128,8 @@ export const A11yMixin = (superClass) =>
125
128
  */
126
129
  __a11yUpdateRowLevel(row, level) {
127
130
  // Set level for the expandable rows itself, and all the nested rows.
128
- if (level > 0 || this.__isRowCollapsible(row) || this.__isRowExpandable(row)) {
129
- row.setAttribute('aria-level', level + 1);
130
- } else {
131
- row.removeAttribute('aria-level');
132
- }
131
+ const hasLevel = level > 0 || this.__isRowCollapsible(row) || this.__isRowExpandable(row);
132
+ setOrRemoveAttribute(row, 'aria-level', hasLevel && level + 1);
133
133
  }
134
134
 
135
135
  /**
@@ -145,24 +145,13 @@ export const A11yMixin = (superClass) =>
145
145
  });
146
146
  }
147
147
 
148
- /**
149
- * @param {!HTMLElement} cell
150
- * @param {number} colspan
151
- * @private
152
- */
153
- __a11yUpdateCellColspan(cell, colspan) {
154
- cell.setAttribute('aria-colspan', Number(colspan));
155
- }
156
-
157
148
  /** @private */
158
149
  __a11yUpdateSorters() {
159
150
  Array.from(this.querySelectorAll('vaadin-grid-sorter')).forEach((sorter) => {
160
- let cellContent = sorter.parentNode;
161
- while (cellContent && cellContent.localName !== 'vaadin-grid-cell-content') {
162
- cellContent = cellContent.parentNode;
163
- }
164
- if (cellContent?.assignedSlot) {
165
- const cell = cellContent.assignedSlot.parentNode;
151
+ sorter.__updateAccessibleName?.();
152
+
153
+ const cell = getClosestCell(sorter);
154
+ if (cell) {
166
155
  cell.setAttribute(
167
156
  'aria-sort',
168
157
  {
@@ -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,
@@ -260,21 +261,21 @@ export const ColumnBaseMixin = (superClass) =>
260
261
 
261
262
  static get observers() {
262
263
  return [
263
- '_widthChanged(width, _headerCell, _footerCell, _cells)',
264
+ '_widthChanged(width, _cells)',
264
265
  '_frozenChanged(frozen, _headerCell, _footerCell, _cells)',
265
266
  '_frozenToEndChanged(frozenToEnd, _headerCell, _footerCell, _cells)',
266
- '_flexGrowChanged(flexGrow, _headerCell, _footerCell, _cells)',
267
- '_textAlignChanged(textAlign, _cells, _headerCell, _footerCell)',
267
+ '_flexGrowChanged(flexGrow, _cells)',
268
+ '_textAlignChanged(textAlign, _cells)',
268
269
  '_lastFrozenChanged(_lastFrozen)',
269
270
  '_firstFrozenToEndChanged(_firstFrozenToEnd)',
270
271
  '_onRendererOrBindingChanged(_renderer, _cells, _bodyContentHidden, path)',
271
272
  '_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header)',
272
273
  '_onFooterRendererOrBindingChanged(_footerRenderer, _footerCell)',
273
- '_resizableChanged(resizable, _headerCell)',
274
+ '_resizableChanged(resizable)',
274
275
  '_reorderStatusChanged(_reorderStatus, _headerCell, _footerCell, _cells)',
275
276
  '_hiddenChanged(hidden, _headerCell, _footerCell, _cells)',
276
277
  '_rowHeaderChanged(rowHeader, _cells)',
277
- '__headerFooterPartNameChanged(_headerCell, _footerCell, headerPartName, footerPartName)',
278
+ '__headerFooterPartNameChanged(headerPartName, footerPartName)',
278
279
  ];
279
280
  }
280
281
 
@@ -302,6 +303,19 @@ export const ColumnBaseMixin = (superClass) =>
302
303
  .filter((cell) => cell);
303
304
  }
304
305
 
306
+ constructor() {
307
+ super();
308
+
309
+ /**
310
+ * A stable unique id assigned once per column instance. Used to build
311
+ * cell content slot names that stay stable across re-renders, so lit
312
+ * can reuse the cell content elements when the column tree changes.
313
+ *
314
+ * @protected
315
+ */
316
+ this._id = generateUniqueId();
317
+ }
318
+
305
319
  /** @protected */
306
320
  connectedCallback() {
307
321
  super.connectedCallback();
@@ -313,7 +327,7 @@ export const ColumnBaseMixin = (superClass) =>
313
327
  return;
314
328
  }
315
329
 
316
- this._allCells.forEach((cell) => {
330
+ this._cells?.forEach((cell) => {
317
331
  if (!cell._content.parentNode) {
318
332
  this._grid.appendChild(cell._content);
319
333
  }
@@ -332,10 +346,8 @@ export const ColumnBaseMixin = (superClass) =>
332
346
  return;
333
347
  }
334
348
 
335
- this._allCells.forEach((cell) => {
336
- if (cell._content.parentNode) {
337
- cell._content.parentNode.removeChild(cell._content);
338
- }
349
+ this._cells?.forEach((cell) => {
350
+ cell._content.parentNode?.removeChild(cell._content);
339
351
  });
340
352
  });
341
353
 
@@ -364,46 +376,40 @@ export const ColumnBaseMixin = (superClass) =>
364
376
 
365
377
  /** @private */
366
378
  _flexGrowChanged(flexGrow) {
367
- if (this.parentElement && this.parentElement._columnPropChanged) {
368
- this.parentElement._columnPropChanged('flexGrow');
369
- }
379
+ this.parentElement?._columnPropChanged?.('flexGrow');
370
380
 
371
- this._allCells.forEach((cell) => {
381
+ this._grid?.__scheduleRenderHeaderFooter?.();
382
+
383
+ this._cells?.forEach((cell) => {
372
384
  cell.style.flexGrow = flexGrow;
373
385
  });
374
386
  }
375
387
 
376
388
  /** @private */
377
389
  _widthChanged(width) {
378
- if (this.parentElement && this.parentElement._columnPropChanged) {
379
- this.parentElement._columnPropChanged('width');
380
- }
390
+ this.parentElement?._columnPropChanged?.('width');
381
391
 
382
- this._allCells.forEach((cell) => {
392
+ this._grid?.__scheduleRenderHeaderFooter?.();
393
+
394
+ this._cells?.forEach((cell) => {
383
395
  cell.style.width = width;
384
396
  });
385
397
  }
386
398
 
387
399
  /** @private */
388
400
  _frozenChanged(frozen) {
389
- if (this.parentElement && this.parentElement._columnPropChanged) {
390
- this.parentElement._columnPropChanged('frozen', frozen);
391
- }
401
+ this.parentElement?._columnPropChanged?.('frozen', frozen);
392
402
 
393
403
  this._allCells.forEach((cell) => {
394
404
  updateCellState(cell, 'frozen', frozen);
395
405
  });
396
406
 
397
- if (this._grid && this._grid._frozenCellsChanged) {
398
- this._grid._frozenCellsChanged();
399
- }
407
+ this._grid?._frozenCellsChanged?.();
400
408
  }
401
409
 
402
410
  /** @private */
403
411
  _frozenToEndChanged(frozenToEnd) {
404
- if (this.parentElement && this.parentElement._columnPropChanged) {
405
- this.parentElement._columnPropChanged('frozenToEnd', frozenToEnd);
406
- }
412
+ this.parentElement?._columnPropChanged?.('frozenToEnd', frozenToEnd);
407
413
 
408
414
  this._allCells.forEach((cell) => {
409
415
  // Skip sizer cells to keep correct scrollWidth.
@@ -414,9 +420,7 @@ export const ColumnBaseMixin = (superClass) =>
414
420
  updateCellState(cell, 'frozen-to-end', frozenToEnd);
415
421
  });
416
422
 
417
- if (this._grid && this._grid._frozenCellsChanged) {
418
- this._grid._frozenCellsChanged();
419
- }
423
+ this._grid?._frozenCellsChanged?.();
420
424
  }
421
425
 
422
426
  /** @private */
@@ -448,11 +452,7 @@ export const ColumnBaseMixin = (superClass) =>
448
452
 
449
453
  /** @private */
450
454
  _rowHeaderChanged(rowHeader, cells) {
451
- if (!cells) {
452
- return;
453
- }
454
-
455
- cells.forEach((cell) => {
455
+ cells?.forEach((cell) => {
456
456
  cell.setAttribute('role', rowHeader ? 'rowheader' : 'gridcell');
457
457
  });
458
458
  }
@@ -485,75 +485,47 @@ export const ColumnBaseMixin = (superClass) =>
485
485
  }
486
486
 
487
487
  /** @private */
488
- _resizableChanged(resizable, headerCell) {
489
- if (resizable === undefined || headerCell === undefined) {
488
+ _resizableChanged(resizable) {
489
+ if (resizable === undefined) {
490
490
  return;
491
491
  }
492
492
 
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
- }
493
+ this._grid?.__scheduleRenderHeaderFooter?.();
509
494
  }
510
495
 
511
496
  /** @private */
512
497
  _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"');
498
+ if (textAlign === undefined) {
518
499
  return;
519
500
  }
520
501
 
521
- this._allCells.forEach((cell) => {
502
+ this._grid?.__scheduleRenderHeaderFooter?.();
503
+
504
+ this._cells?.forEach((cell) => {
522
505
  cell._content.style.textAlign = textAlign;
523
506
  });
524
507
  }
525
508
 
526
509
  /** @private */
527
510
  _hiddenChanged(hidden) {
528
- if (this.parentElement && this.parentElement._columnPropChanged) {
529
- this.parentElement._columnPropChanged('hidden', hidden);
530
- }
511
+ this.parentElement?._columnPropChanged?.('hidden', hidden);
531
512
 
532
513
  if (!!hidden !== !!this._previousHidden && this._grid) {
533
514
  if (hidden === true) {
534
- this._allCells.forEach((cell) => {
535
- if (cell._content.parentNode) {
536
- cell._content.parentNode.removeChild(cell._content);
537
- }
515
+ this._cells?.forEach((cell) => {
516
+ cell._content.parentNode?.removeChild(cell._content);
538
517
  });
539
518
  }
540
519
  this._grid._debouncerHiddenChanged = Debouncer.debounce(
541
520
  this._grid._debouncerHiddenChanged,
542
521
  animationFrame,
543
522
  () => {
544
- if (this._grid && this._grid._renderColumnTree) {
545
- this._grid._renderColumnTree(this._grid._columnTree);
546
- }
523
+ this._grid?._renderColumnTree?.(this._grid._columnTree);
547
524
  },
548
525
  );
549
526
 
550
- if (this._grid._debounceUpdateFrozenColumn) {
551
- this._grid._debounceUpdateFrozenColumn();
552
- }
553
-
554
- if (this._grid._resetKeyboardNavigation) {
555
- this._grid._resetKeyboardNavigation();
556
- }
527
+ this._grid._debounceUpdateFrozenColumn?.();
528
+ this._grid._resetKeyboardNavigation?.();
557
529
  }
558
530
  this._previousHidden = hidden;
559
531
  }
@@ -632,9 +604,8 @@ export const ColumnBaseMixin = (superClass) =>
632
604
  }
633
605
 
634
606
  this.__renderCellsContent(headerRenderer, [headerCell]);
635
- if (this._grid && headerCell.parentElement) {
636
- this._grid.__debounceUpdateHeaderFooterRowVisibility(headerCell.parentElement);
637
- }
607
+
608
+ this._grid?.__scheduleRenderHeaderFooter?.();
638
609
  }
639
610
 
640
611
  /** @protected */
@@ -643,19 +614,8 @@ export const ColumnBaseMixin = (superClass) =>
643
614
  }
644
615
 
645
616
  /** @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
- });
617
+ __headerFooterPartNameChanged() {
618
+ this._grid?.__scheduleRenderHeaderFooter?.();
659
619
  }
660
620
 
661
621
  /**
@@ -689,9 +649,8 @@ export const ColumnBaseMixin = (superClass) =>
689
649
  }
690
650
 
691
651
  this.__renderCellsContent(footerRenderer, [footerCell]);
692
- if (this._grid && footerCell.parentElement) {
693
- this._grid.__debounceUpdateHeaderFooterRowVisibility(footerCell.parentElement);
694
- }
652
+
653
+ this._grid?.__scheduleRenderHeaderFooter?.();
695
654
  }
696
655
 
697
656
  /** @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
  /**
@@ -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 { isChrome, isSafari } from '@vaadin/component-base/src/browser-utils.js';
7
+ import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
7
8
  import {
8
9
  getBodyRowCells,
9
10
  iterateChildren,
@@ -494,11 +495,7 @@ export const DragAndDropMixin = (superClass) =>
494
495
  const draggableAttribute = isChrome ? 'draggable-source' : 'draggable';
495
496
 
496
497
  iterateRowCells(row, (cell) => {
497
- if (dragDisabled) {
498
- cell._content.removeAttribute(draggableAttribute);
499
- } else {
500
- cell._content.setAttribute(draggableAttribute, true);
501
- }
498
+ setOrRemoveAttribute(cell._content, draggableAttribute, !dragDisabled);
502
499
  });
503
500
 
504
501
  updateBooleanRowStates(row, {
@@ -143,11 +143,6 @@ export const DynamicColumnsMixin = (superClass) =>
143
143
  });
144
144
  }
145
145
 
146
- /** @protected */
147
- _updateFirstAndLastColumn() {
148
- Array.from(this.shadowRoot.querySelectorAll('tr')).forEach((row) => this._updateFirstAndLastColumnForRow(row));
149
- }
150
-
151
146
  /**
152
147
  * @param {!HTMLElement} row
153
148
  * @protected
@@ -37,7 +37,7 @@ export const GridFilterColumnMixin = (superClass) =>
37
37
  */
38
38
  _defaultHeaderRenderer(root, _column) {
39
39
  let filter = root.firstElementChild;
40
- let textField = filter ? filter.firstElementChild : undefined;
40
+ let textField = filter?.firstElementChild;
41
41
 
42
42
  if (!filter) {
43
43
  filter = document.createElement('vaadin-grid-filter');
@@ -71,8 +71,6 @@ export const GridFilterElementMixin = (superClass) =>
71
71
  }
72
72
 
73
73
  focus() {
74
- if (this._textField) {
75
- this._textField.focus();
76
- }
74
+ this._textField?.focus();
77
75
  }
78
76
  };
@@ -37,6 +37,7 @@ import { GridFilterElementMixin } from './vaadin-grid-filter-element-mixin.js';
37
37
  *
38
38
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
39
39
  *
40
+ * @attr {string} theme - The theme variants to apply to the component.
40
41
  * @customElement vaadin-grid-filter
41
42
  * @extends HTMLElement
42
43
  */