@vaadin/grid 25.3.0-alpha6 → 25.3.0-alpha8
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.
- package/custom-elements.json +191 -132
- package/package.json +12 -12
- package/src/directives/cell-content-directive.js +33 -0
- package/src/vaadin-grid-a11y-mixin.js +0 -9
- package/src/vaadin-grid-column-auto-width-mixin.js +1 -5
- package/src/vaadin-grid-column-group-mixin.js +3 -14
- package/src/vaadin-grid-column-mixin.d.ts +3 -3
- package/src/vaadin-grid-column-mixin.js +43 -55
- package/src/vaadin-grid-column-reordering-mixin.js +9 -4
- package/src/vaadin-grid-column-resizing-mixin.js +4 -0
- package/src/vaadin-grid-dynamic-columns-mixin.js +0 -5
- package/src/vaadin-grid-filter.js +1 -0
- package/src/vaadin-grid-header-footer-rendering-mixin.js +296 -0
- package/src/vaadin-grid-mixin.js +20 -158
- package/src/vaadin-grid-sort-column-mixin.js +11 -2
- package/src/vaadin-grid-sorter.js +1 -0
- package/src/vaadin-grid-tree-toggle.js +1 -0
- package/src/vaadin-grid.js +1 -0
- package/web-types.json +20 -94
- package/web-types.lit.json +10 -10
|
@@ -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 {
|
|
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
|
-
*
|
|
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,
|
|
264
|
+
'_widthChanged(width, _cells)',
|
|
264
265
|
'_frozenChanged(frozen, _headerCell, _footerCell, _cells)',
|
|
265
266
|
'_frozenToEndChanged(frozenToEnd, _headerCell, _footerCell, _cells)',
|
|
266
|
-
'_flexGrowChanged(flexGrow,
|
|
267
|
-
'_textAlignChanged(textAlign, _cells
|
|
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
|
|
274
|
+
'_resizableChanged(resizable)',
|
|
274
275
|
'_reorderStatusChanged(_reorderStatus, _headerCell, _footerCell, _cells)',
|
|
275
276
|
'_hiddenChanged(hidden, _headerCell, _footerCell, _cells)',
|
|
276
277
|
'_rowHeaderChanged(rowHeader, _cells)',
|
|
277
|
-
'__headerFooterPartNameChanged(
|
|
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.
|
|
330
|
+
this._cells?.forEach((cell) => {
|
|
317
331
|
if (!cell._content.parentNode) {
|
|
318
332
|
this._grid.appendChild(cell._content);
|
|
319
333
|
}
|
|
@@ -332,7 +346,7 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
332
346
|
return;
|
|
333
347
|
}
|
|
334
348
|
|
|
335
|
-
this.
|
|
349
|
+
this._cells?.forEach((cell) => {
|
|
336
350
|
if (cell._content.parentNode) {
|
|
337
351
|
cell._content.parentNode.removeChild(cell._content);
|
|
338
352
|
}
|
|
@@ -368,7 +382,9 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
368
382
|
this.parentElement._columnPropChanged('flexGrow');
|
|
369
383
|
}
|
|
370
384
|
|
|
371
|
-
this.
|
|
385
|
+
this._grid?.__scheduleRenderHeaderFooter?.();
|
|
386
|
+
|
|
387
|
+
this._cells?.forEach((cell) => {
|
|
372
388
|
cell.style.flexGrow = flexGrow;
|
|
373
389
|
});
|
|
374
390
|
}
|
|
@@ -379,7 +395,9 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
379
395
|
this.parentElement._columnPropChanged('width');
|
|
380
396
|
}
|
|
381
397
|
|
|
382
|
-
this.
|
|
398
|
+
this._grid?.__scheduleRenderHeaderFooter?.();
|
|
399
|
+
|
|
400
|
+
this._cells?.forEach((cell) => {
|
|
383
401
|
cell.style.width = width;
|
|
384
402
|
});
|
|
385
403
|
}
|
|
@@ -485,27 +503,12 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
485
503
|
}
|
|
486
504
|
|
|
487
505
|
/** @private */
|
|
488
|
-
_resizableChanged(resizable
|
|
489
|
-
if (resizable === undefined ||
|
|
506
|
+
_resizableChanged(resizable) {
|
|
507
|
+
if (resizable === undefined || this._grid === undefined) {
|
|
490
508
|
return;
|
|
491
509
|
}
|
|
492
510
|
|
|
493
|
-
|
|
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
|
-
}
|
|
511
|
+
this._grid.__scheduleRenderHeaderFooter?.();
|
|
509
512
|
}
|
|
510
513
|
|
|
511
514
|
/** @private */
|
|
@@ -513,12 +516,10 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
513
516
|
if (textAlign === undefined || this._grid === undefined) {
|
|
514
517
|
return;
|
|
515
518
|
}
|
|
516
|
-
if (['start', 'end', 'center'].indexOf(textAlign) === -1) {
|
|
517
|
-
console.warn('textAlign can only be set as "start", "end" or "center"');
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
519
|
|
|
521
|
-
this.
|
|
520
|
+
this._grid.__scheduleRenderHeaderFooter?.();
|
|
521
|
+
|
|
522
|
+
this._cells?.forEach((cell) => {
|
|
522
523
|
cell._content.style.textAlign = textAlign;
|
|
523
524
|
});
|
|
524
525
|
}
|
|
@@ -531,7 +532,7 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
531
532
|
|
|
532
533
|
if (!!hidden !== !!this._previousHidden && this._grid) {
|
|
533
534
|
if (hidden === true) {
|
|
534
|
-
this.
|
|
535
|
+
this._cells?.forEach((cell) => {
|
|
535
536
|
if (cell._content.parentNode) {
|
|
536
537
|
cell._content.parentNode.removeChild(cell._content);
|
|
537
538
|
}
|
|
@@ -632,9 +633,8 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
632
633
|
}
|
|
633
634
|
|
|
634
635
|
this.__renderCellsContent(headerRenderer, [headerCell]);
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
636
|
+
|
|
637
|
+
this._grid?.__scheduleRenderHeaderFooter();
|
|
638
638
|
}
|
|
639
639
|
|
|
640
640
|
/** @protected */
|
|
@@ -643,19 +643,8 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
/** @private */
|
|
646
|
-
__headerFooterPartNameChanged(
|
|
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
|
-
});
|
|
646
|
+
__headerFooterPartNameChanged() {
|
|
647
|
+
this._grid?.__scheduleRenderHeaderFooter?.();
|
|
659
648
|
}
|
|
660
649
|
|
|
661
650
|
/**
|
|
@@ -689,9 +678,8 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
689
678
|
}
|
|
690
679
|
|
|
691
680
|
this.__renderCellsContent(footerRenderer, [footerCell]);
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}
|
|
681
|
+
|
|
682
|
+
this._grid?.__scheduleRenderHeaderFooter();
|
|
695
683
|
}
|
|
696
684
|
|
|
697
685
|
/** @protected */
|
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
@@ -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,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
|
*/
|
|
@@ -0,0 +1,296 @@
|
|
|
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, nothing, render } from 'lit';
|
|
7
|
+
import { cache } from 'lit/directives/cache.js';
|
|
8
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
9
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
10
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
11
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
12
|
+
import { microTask } from '@vaadin/component-base/src/async.js';
|
|
13
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
14
|
+
import { partMap } from '@vaadin/component-base/src/directives/part-map.js';
|
|
15
|
+
import { cellContent } from './directives/cell-content-directive.js';
|
|
16
|
+
|
|
17
|
+
function isContentCell(column, level, columnTree) {
|
|
18
|
+
const isLastRow = level === columnTree.length - 1;
|
|
19
|
+
return isLastRow || column.localName === 'vaadin-grid-column-group';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isHeaderRowVisible(columns, level, columnTree) {
|
|
23
|
+
return columns.some((column) => {
|
|
24
|
+
if (column.hidden || !isContentCell(column, level, columnTree)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (column.headerRenderer) {
|
|
29
|
+
// The column has a header renderer -> row should be visible
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (column.header === null) {
|
|
34
|
+
// The column header is explicitly set to null -> doesn't block hiding the row
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return column.path || column.header !== undefined;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isFooterRowVisible(columns, level, columnTree) {
|
|
43
|
+
return columns.some((column) => {
|
|
44
|
+
if (column.hidden || !isContentCell(column, level, columnTree)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return column.footerRenderer;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Converts a whitespace separated list of custom part names
|
|
54
|
+
* into an object accepted by the `partMap` directive.
|
|
55
|
+
*/
|
|
56
|
+
function getCustomParts(partName) {
|
|
57
|
+
return Object.fromEntries(
|
|
58
|
+
(partName ?? '')
|
|
59
|
+
.split(' ')
|
|
60
|
+
.filter((name) => name !== '')
|
|
61
|
+
.map((name) => [name, true]),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A mixin providing rendering of header and footer rows based on the column tree.
|
|
67
|
+
*/
|
|
68
|
+
export const HeaderFooterRenderingMixin = (superClass) =>
|
|
69
|
+
class HeaderFooterRenderingMixin extends superClass {
|
|
70
|
+
/** @private */
|
|
71
|
+
__scheduleRenderHeaderFooter() {
|
|
72
|
+
this.__renderHeaderFooterDebouncer = Debouncer.debounce(this.__renderHeaderFooterDebouncer, microTask, () => {
|
|
73
|
+
this.__renderHeaderFooter();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @private */
|
|
78
|
+
__renderHeaderFooter() {
|
|
79
|
+
this.__renderHeaderFooterDebouncer?.cancel();
|
|
80
|
+
|
|
81
|
+
const sortedColumnTree = (this._columnTree ?? []).map((columns) => {
|
|
82
|
+
return columns.toSorted((a, b) => a._order - b._order);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
sortedColumnTree.flat().forEach((column) => {
|
|
86
|
+
column._emptyCells = [];
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
this.#renderHeader(sortedColumnTree);
|
|
90
|
+
this.#renderFooter(sortedColumnTree);
|
|
91
|
+
|
|
92
|
+
this._resetKeyboardNavigation();
|
|
93
|
+
this.__a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#renderHeader(columnTree) {
|
|
97
|
+
const rows = this.#getRows(columnTree, 'header');
|
|
98
|
+
render(rows.map(this.#renderHeaderRow), this.$.header, { host: this });
|
|
99
|
+
|
|
100
|
+
this.$.table.toggleAttribute('has-header', !!this.$.header.querySelector('tr:not([hidden])'));
|
|
101
|
+
|
|
102
|
+
this.$.header.querySelectorAll('.header-cell').forEach((cell) => {
|
|
103
|
+
const column = cell._column;
|
|
104
|
+
const isColumnRow = cell.parentElement === this.$.header.lastElementChild;
|
|
105
|
+
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
106
|
+
column._headerCell = cell;
|
|
107
|
+
} else {
|
|
108
|
+
column._emptyCells.push(cell);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#renderHeaderRow = ({ level, cells, isLastRow, isFirstRow, isRowVisible }) => {
|
|
114
|
+
const rowParts = {
|
|
115
|
+
'first-header-row': isFirstRow,
|
|
116
|
+
'last-header-row': isLastRow,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return html`
|
|
120
|
+
<tr
|
|
121
|
+
role="row"
|
|
122
|
+
part="row header-row${partMap(rowParts)}"
|
|
123
|
+
class="row header-row${classMap(rowParts)}"
|
|
124
|
+
tabindex="-1"
|
|
125
|
+
?hidden=${!isRowVisible}
|
|
126
|
+
>
|
|
127
|
+
${repeat(
|
|
128
|
+
cells,
|
|
129
|
+
({ column }) => column._id,
|
|
130
|
+
({ column, isFirstCell, isLastCell, isContentCell }) => {
|
|
131
|
+
// `cache` keeps the cell and its rendered content when the
|
|
132
|
+
// column gets hidden, so it can be restored as-is when the
|
|
133
|
+
// column is shown again.
|
|
134
|
+
if (column.hidden) {
|
|
135
|
+
return cache(nothing);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const cellParts = {
|
|
139
|
+
'first-header-row-cell': isFirstRow,
|
|
140
|
+
'last-header-row-cell': isLastRow,
|
|
141
|
+
'first-column-cell': isFirstCell,
|
|
142
|
+
'last-column-cell': isLastCell,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const customCellParts = isContentCell ? getCustomParts(column.headerPartName) : {};
|
|
146
|
+
|
|
147
|
+
return cache(html`
|
|
148
|
+
<th
|
|
149
|
+
role="columnheader"
|
|
150
|
+
part="cell header-cell${partMap({ ...cellParts, ...customCellParts })}"
|
|
151
|
+
class="cell header-cell${classMap(cellParts)}"
|
|
152
|
+
style="${styleMap({
|
|
153
|
+
width: column.width,
|
|
154
|
+
'flex-grow': column.flexGrow,
|
|
155
|
+
})}"
|
|
156
|
+
?first-column="${isFirstCell}"
|
|
157
|
+
?last-column="${isLastCell}"
|
|
158
|
+
@keydown="${this.__onCellKeyDown}"
|
|
159
|
+
@mousedown=${this.__onCellMouseDown}
|
|
160
|
+
@mouseenter=${this.__onCellMouseEnter}
|
|
161
|
+
@mouseleave=${this.__onCellMouseLeave}
|
|
162
|
+
colspan="${ifDefined(column._colSpan)}"
|
|
163
|
+
aria-colspan="${ifDefined(column._colSpan)}"
|
|
164
|
+
tabindex="-1"
|
|
165
|
+
._column=${column}
|
|
166
|
+
>
|
|
167
|
+
${cellContent(this, `vaadin-grid-header-cell-content-${level}-${column._id}`, {
|
|
168
|
+
textAlign: column.textAlign,
|
|
169
|
+
})}
|
|
170
|
+
${column.resizable ? html`<div part="resize-handle" class="resize-handle"></div>` : nothing}
|
|
171
|
+
</th>
|
|
172
|
+
`);
|
|
173
|
+
},
|
|
174
|
+
)}
|
|
175
|
+
</tr>
|
|
176
|
+
`;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
#renderFooter(columnTree) {
|
|
180
|
+
const rows = this.#getRows(columnTree, 'footer');
|
|
181
|
+
render(rows.map(this.#renderFooterRow), this.$.footer, { host: this });
|
|
182
|
+
|
|
183
|
+
this.$.table.toggleAttribute('has-footer', !!this.$.footer.querySelector('tr:not([hidden])'));
|
|
184
|
+
|
|
185
|
+
this.$.footer.querySelectorAll('.footer-cell').forEach((cell) => {
|
|
186
|
+
const column = cell._column;
|
|
187
|
+
const isColumnRow = cell.parentElement === this.$.footer.firstElementChild;
|
|
188
|
+
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
189
|
+
column._footerCell = cell;
|
|
190
|
+
} else {
|
|
191
|
+
column._emptyCells.push(cell);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
#renderFooterRow = ({ level, cells, isLastRow, isFirstRow, isRowVisible }) => {
|
|
197
|
+
const rowParts = {
|
|
198
|
+
'first-footer-row': isFirstRow,
|
|
199
|
+
'last-footer-row': isLastRow,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return html`
|
|
203
|
+
<tr
|
|
204
|
+
role="row"
|
|
205
|
+
part="row footer-row${partMap(rowParts)}"
|
|
206
|
+
class="row footer-row${classMap(rowParts)}"
|
|
207
|
+
tabindex="-1"
|
|
208
|
+
?hidden=${!isRowVisible}
|
|
209
|
+
>
|
|
210
|
+
${repeat(
|
|
211
|
+
cells,
|
|
212
|
+
({ column }) => column._id,
|
|
213
|
+
({ column, isFirstCell, isLastCell, isContentCell }) => {
|
|
214
|
+
// `cache` keeps the cell and its rendered content when the
|
|
215
|
+
// column gets hidden, so it can be restored as-is when the
|
|
216
|
+
// column is shown again.
|
|
217
|
+
if (column.hidden) {
|
|
218
|
+
return cache(nothing);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const cellParts = {
|
|
222
|
+
'first-footer-row-cell': isFirstRow,
|
|
223
|
+
'last-footer-row-cell': isLastRow,
|
|
224
|
+
'first-column-cell': isFirstCell,
|
|
225
|
+
'last-column-cell': isLastCell,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const customCellParts = isContentCell ? getCustomParts(column.footerPartName) : {};
|
|
229
|
+
|
|
230
|
+
return cache(html`
|
|
231
|
+
<td
|
|
232
|
+
role="gridcell"
|
|
233
|
+
part="cell footer-cell${partMap({ ...cellParts, ...customCellParts })}"
|
|
234
|
+
class="cell footer-cell${classMap(cellParts)}"
|
|
235
|
+
style="${styleMap({
|
|
236
|
+
width: column.width,
|
|
237
|
+
'flex-grow': column.flexGrow,
|
|
238
|
+
})}"
|
|
239
|
+
?first-column="${isFirstCell}"
|
|
240
|
+
?last-column="${isLastCell}"
|
|
241
|
+
@keydown="${this.__onCellKeyDown}"
|
|
242
|
+
@mousedown=${this.__onCellMouseDown}
|
|
243
|
+
@mouseenter=${this.__onCellMouseEnter}
|
|
244
|
+
@mouseleave=${this.__onCellMouseLeave}
|
|
245
|
+
colspan="${ifDefined(column._colSpan)}"
|
|
246
|
+
aria-colspan="${ifDefined(column._colSpan)}"
|
|
247
|
+
tabindex="-1"
|
|
248
|
+
._column=${column}
|
|
249
|
+
>
|
|
250
|
+
${cellContent(this, `vaadin-grid-footer-cell-content-${level}-${column._id}`, {
|
|
251
|
+
textAlign: column.textAlign,
|
|
252
|
+
})}
|
|
253
|
+
</td>
|
|
254
|
+
`);
|
|
255
|
+
},
|
|
256
|
+
)}
|
|
257
|
+
</tr>
|
|
258
|
+
`;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
#getRows(columnTree, section) {
|
|
262
|
+
let rows = columnTree.map((columns, level) => {
|
|
263
|
+
const visibleColumns = columns.filter((column) => !column.hidden);
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
level,
|
|
267
|
+
cells: columns.map((column) => {
|
|
268
|
+
return {
|
|
269
|
+
column,
|
|
270
|
+
isFirstCell: column === visibleColumns.at(0),
|
|
271
|
+
isLastCell: column === visibleColumns.at(-1),
|
|
272
|
+
isContentCell: isContentCell(column, level, columnTree),
|
|
273
|
+
};
|
|
274
|
+
}),
|
|
275
|
+
isRowVisible:
|
|
276
|
+
section === 'header'
|
|
277
|
+
? isHeaderRowVisible(columns, level, columnTree)
|
|
278
|
+
: isFooterRowVisible(columns, level, columnTree),
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (section === 'footer') {
|
|
283
|
+
rows = rows.toReversed();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const visibleRows = rows.filter((row) => row.isRowVisible);
|
|
287
|
+
|
|
288
|
+
return rows.map((row) => {
|
|
289
|
+
return {
|
|
290
|
+
...row,
|
|
291
|
+
isFirstRow: row === visibleRows.at(0),
|
|
292
|
+
isLastRow: row === visibleRows.at(-1),
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
};
|