@vaadin/grid 25.3.0-alpha6 → 25.3.0-dev.1fa5a51482
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 +173 -114
- package/package.json +12 -12
- package/src/directives/cell-content-directive.js +32 -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.js +26 -29
- package/src/vaadin-grid-column-reordering-mixin.js +9 -4
- 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 +265 -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 +8 -82
- package/web-types.lit.json +4 -4
|
@@ -0,0 +1,265 @@
|
|
|
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 { microTask } from '@vaadin/component-base/src/async.js';
|
|
12
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
13
|
+
import { partMap } from '@vaadin/component-base/src/directives/part-map.js';
|
|
14
|
+
import { cellContent } from './directives/cell-content-directive.js';
|
|
15
|
+
|
|
16
|
+
function isEmptyCell(column, level, columnTree) {
|
|
17
|
+
const isColumnRow = level === columnTree.length - 1;
|
|
18
|
+
return !isColumnRow && column.localName !== 'vaadin-grid-column-group';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isHeaderRowVisible(columns, level, columnTree) {
|
|
22
|
+
return columns.some((column) => {
|
|
23
|
+
if (column.hidden || isEmptyCell(column, level, columnTree)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (column.headerRenderer) {
|
|
28
|
+
// The column has a header renderer -> row should be visible
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (column.header === null) {
|
|
33
|
+
// The column header is explicitly set to null -> doesn't block hiding the row
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return column.path || column.header !== undefined;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isFooterRowVisible(columns, level, columnTree) {
|
|
42
|
+
return columns.some((column) => {
|
|
43
|
+
if (column.hidden || isEmptyCell(column, level, columnTree)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return column.footerRenderer;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A mixin providing rendering of header and footer rows based on the column tree.
|
|
53
|
+
*/
|
|
54
|
+
export const HeaderFooterRenderingMixin = (superClass) =>
|
|
55
|
+
class HeaderFooterRenderingMixin extends superClass {
|
|
56
|
+
/** @private */
|
|
57
|
+
__scheduleRenderHeaderFooter() {
|
|
58
|
+
this.__renderHeaderFooterDebouncer = Debouncer.debounce(this.__renderHeaderFooterDebouncer, microTask, () => {
|
|
59
|
+
this.__renderHeaderFooter();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @private */
|
|
64
|
+
__renderHeaderFooter() {
|
|
65
|
+
this.__renderHeaderFooterDebouncer?.cancel();
|
|
66
|
+
|
|
67
|
+
const sortedColumnTree = (this._columnTree ?? []).map((columns) => {
|
|
68
|
+
return columns.toSorted((a, b) => a._order - b._order);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
sortedColumnTree.flat().forEach((column) => {
|
|
72
|
+
column._emptyCells = [];
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.#renderHeader(sortedColumnTree);
|
|
76
|
+
this.#renderFooter(sortedColumnTree);
|
|
77
|
+
|
|
78
|
+
this._resetKeyboardNavigation();
|
|
79
|
+
this.__a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#renderHeader(columnTree) {
|
|
83
|
+
const rows = this.#getRows(columnTree, 'header');
|
|
84
|
+
render(rows.map(this.#renderHeaderRow), this.$.header, { host: this });
|
|
85
|
+
|
|
86
|
+
this.$.table.toggleAttribute('has-header', !!this.$.header.querySelector('tr:not([hidden])'));
|
|
87
|
+
|
|
88
|
+
this.$.header.querySelectorAll('.header-cell').forEach((cell) => {
|
|
89
|
+
const column = cell._column;
|
|
90
|
+
const isColumnRow = cell.parentElement === this.$.header.lastElementChild;
|
|
91
|
+
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
92
|
+
column._headerCell = cell;
|
|
93
|
+
} else {
|
|
94
|
+
column._emptyCells.push(cell);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#renderHeaderRow = ({ level, cells, isLastRow, isFirstRow, isRowVisible }) => {
|
|
100
|
+
const rowParts = {
|
|
101
|
+
'first-header-row': isFirstRow,
|
|
102
|
+
'last-header-row': isLastRow,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return html`
|
|
106
|
+
<tr
|
|
107
|
+
role="row"
|
|
108
|
+
part="row header-row${partMap(rowParts)}"
|
|
109
|
+
class="row header-row${classMap(rowParts)}"
|
|
110
|
+
tabindex="-1"
|
|
111
|
+
?hidden=${!isRowVisible}
|
|
112
|
+
>
|
|
113
|
+
${repeat(
|
|
114
|
+
cells,
|
|
115
|
+
({ column }) => column._id,
|
|
116
|
+
({ column, isFirstCell, isLastCell }) => {
|
|
117
|
+
// `cache` keeps the cell and its rendered content when the
|
|
118
|
+
// column gets hidden, so it can be restored as-is when the
|
|
119
|
+
// column is shown again.
|
|
120
|
+
if (column.hidden) {
|
|
121
|
+
return cache(nothing);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const cellParts = {
|
|
125
|
+
'first-header-row-cell': isFirstRow,
|
|
126
|
+
'last-header-row-cell': isLastRow,
|
|
127
|
+
'first-column-cell': isFirstCell,
|
|
128
|
+
'last-column-cell': isLastCell,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return cache(html`
|
|
132
|
+
<th
|
|
133
|
+
role="columnheader"
|
|
134
|
+
part="cell header-cell${partMap(cellParts)}"
|
|
135
|
+
class="cell header-cell${classMap(cellParts)}"
|
|
136
|
+
?first-column="${isFirstCell}"
|
|
137
|
+
?last-column="${isLastCell}"
|
|
138
|
+
@keydown="${this.__onCellKeyDown}"
|
|
139
|
+
@mousedown=${this.__onCellMouseDown}
|
|
140
|
+
@mouseenter=${this.__onCellMouseEnter}
|
|
141
|
+
@mouseleave=${this.__onCellMouseLeave}
|
|
142
|
+
colspan="${ifDefined(column._colSpan)}"
|
|
143
|
+
aria-colspan="${ifDefined(column._colSpan)}"
|
|
144
|
+
tabindex="-1"
|
|
145
|
+
._column=${column}
|
|
146
|
+
>
|
|
147
|
+
${cellContent(this, `vaadin-grid-header-cell-content-${level}-${column._id}`)}
|
|
148
|
+
${column.resizable ? html`<div part="resize-handle" class="resize-handle"></div>` : nothing}
|
|
149
|
+
</th>
|
|
150
|
+
`);
|
|
151
|
+
},
|
|
152
|
+
)}
|
|
153
|
+
</tr>
|
|
154
|
+
`;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
#renderFooter(columnTree) {
|
|
158
|
+
const rows = this.#getRows(columnTree, 'footer');
|
|
159
|
+
render(rows.map(this.#renderFooterRow), this.$.footer, { host: this });
|
|
160
|
+
|
|
161
|
+
this.$.table.toggleAttribute('has-footer', !!this.$.footer.querySelector('tr:not([hidden])'));
|
|
162
|
+
|
|
163
|
+
this.$.footer.querySelectorAll('.footer-cell').forEach((cell) => {
|
|
164
|
+
const column = cell._column;
|
|
165
|
+
const isColumnRow = cell.parentElement === this.$.footer.firstElementChild;
|
|
166
|
+
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
167
|
+
column._footerCell = cell;
|
|
168
|
+
} else {
|
|
169
|
+
column._emptyCells.push(cell);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#renderFooterRow = ({ level, cells, isLastRow, isFirstRow, isRowVisible }) => {
|
|
175
|
+
const rowParts = {
|
|
176
|
+
'first-footer-row': isFirstRow,
|
|
177
|
+
'last-footer-row': isLastRow,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return html`
|
|
181
|
+
<tr
|
|
182
|
+
role="row"
|
|
183
|
+
part="row footer-row${partMap(rowParts)}"
|
|
184
|
+
class="row footer-row${classMap(rowParts)}"
|
|
185
|
+
tabindex="-1"
|
|
186
|
+
?hidden=${!isRowVisible}
|
|
187
|
+
>
|
|
188
|
+
${repeat(
|
|
189
|
+
cells,
|
|
190
|
+
({ column }) => column._id,
|
|
191
|
+
({ column, isFirstCell, isLastCell }) => {
|
|
192
|
+
// `cache` keeps the cell and its rendered content when the
|
|
193
|
+
// column gets hidden, so it can be restored as-is when the
|
|
194
|
+
// column is shown again.
|
|
195
|
+
if (column.hidden) {
|
|
196
|
+
return cache(nothing);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const cellParts = {
|
|
200
|
+
'first-footer-row-cell': isFirstRow,
|
|
201
|
+
'last-footer-row-cell': isLastRow,
|
|
202
|
+
'first-column-cell': isFirstCell,
|
|
203
|
+
'last-column-cell': isLastCell,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
return cache(html`
|
|
207
|
+
<td
|
|
208
|
+
role="gridcell"
|
|
209
|
+
part="cell footer-cell${partMap(cellParts)}"
|
|
210
|
+
class="cell footer-cell${classMap(cellParts)}"
|
|
211
|
+
?first-column="${isFirstCell}"
|
|
212
|
+
?last-column="${isLastCell}"
|
|
213
|
+
@keydown="${this.__onCellKeyDown}"
|
|
214
|
+
@mousedown=${this.__onCellMouseDown}
|
|
215
|
+
@mouseenter=${this.__onCellMouseEnter}
|
|
216
|
+
@mouseleave=${this.__onCellMouseLeave}
|
|
217
|
+
colspan="${ifDefined(column._colSpan)}"
|
|
218
|
+
aria-colspan="${ifDefined(column._colSpan)}"
|
|
219
|
+
tabindex="-1"
|
|
220
|
+
._column=${column}
|
|
221
|
+
>
|
|
222
|
+
${cellContent(this, `vaadin-grid-footer-cell-content-${level}-${column._id}`)}
|
|
223
|
+
</td>
|
|
224
|
+
`);
|
|
225
|
+
},
|
|
226
|
+
)}
|
|
227
|
+
</tr>
|
|
228
|
+
`;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
#getRows(columnTree, section) {
|
|
232
|
+
let rows = columnTree.map((columns, level) => {
|
|
233
|
+
const visibleColumns = columns.filter((column) => !column.hidden);
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
level,
|
|
237
|
+
cells: columns.map((column) => {
|
|
238
|
+
return {
|
|
239
|
+
column,
|
|
240
|
+
isFirstCell: column === visibleColumns.at(0),
|
|
241
|
+
isLastCell: column === visibleColumns.at(-1),
|
|
242
|
+
};
|
|
243
|
+
}),
|
|
244
|
+
isRowVisible:
|
|
245
|
+
section === 'header'
|
|
246
|
+
? isHeaderRowVisible(columns, level, columnTree)
|
|
247
|
+
: isFooterRowVisible(columns, level, columnTree),
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (section === 'footer') {
|
|
252
|
+
rows = rows.toReversed();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const visibleRows = rows.filter((row) => row.isRowVisible);
|
|
256
|
+
|
|
257
|
+
return rows.map((row) => {
|
|
258
|
+
return {
|
|
259
|
+
...row,
|
|
260
|
+
isFirstRow: row === visibleRows.at(0),
|
|
261
|
+
isLastRow: row === visibleRows.at(-1),
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
};
|
package/src/vaadin-grid-mixin.js
CHANGED
|
@@ -4,9 +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 { TabindexMixin } from '@vaadin/a11y-base/src/tabindex-mixin.js';
|
|
7
|
-
import { microTask } from '@vaadin/component-base/src/async.js';
|
|
8
7
|
import { isAndroid, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
9
|
-
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
10
8
|
import { setTouchAction } from '@vaadin/component-base/src/gestures.js';
|
|
11
9
|
import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
|
|
12
10
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
@@ -22,6 +20,7 @@ import { DragAndDropMixin } from './vaadin-grid-drag-and-drop-mixin.js';
|
|
|
22
20
|
import { DynamicColumnsMixin } from './vaadin-grid-dynamic-columns-mixin.js';
|
|
23
21
|
import { EventContextMixin } from './vaadin-grid-event-context-mixin.js';
|
|
24
22
|
import { FilterMixin } from './vaadin-grid-filter-mixin.js';
|
|
23
|
+
import { HeaderFooterRenderingMixin } from './vaadin-grid-header-footer-rendering-mixin.js';
|
|
25
24
|
import {
|
|
26
25
|
getBodyRowCells,
|
|
27
26
|
getClosestCell,
|
|
@@ -48,17 +47,21 @@ export const GridMixin = (superClass) =>
|
|
|
48
47
|
ArrayDataProviderMixin(
|
|
49
48
|
DataProviderMixin(
|
|
50
49
|
DynamicColumnsMixin(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
HeaderFooterRenderingMixin(
|
|
51
|
+
ActiveItemMixin(
|
|
52
|
+
ScrollMixin(
|
|
53
|
+
SelectionMixin(
|
|
54
|
+
SortMixin(
|
|
55
|
+
RowDetailsMixin(
|
|
56
|
+
KeyboardNavigationMixin(
|
|
57
|
+
A11yMixin(
|
|
58
|
+
FilterMixin(
|
|
59
|
+
ColumnReorderingMixin(
|
|
60
|
+
ColumnResizingMixin(
|
|
61
|
+
EventContextMixin(
|
|
62
|
+
DragAndDropMixin(StylingMixin(TabindexMixin(ResizeMixin(superClass)))),
|
|
63
|
+
),
|
|
64
|
+
),
|
|
62
65
|
),
|
|
63
66
|
),
|
|
64
67
|
),
|
|
@@ -351,7 +354,7 @@ export const GridMixin = (superClass) =>
|
|
|
351
354
|
updatePart(row, 'row', true);
|
|
352
355
|
updatePart(row, 'body-row', true);
|
|
353
356
|
if (this._columnTree) {
|
|
354
|
-
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body',
|
|
357
|
+
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body', true);
|
|
355
358
|
}
|
|
356
359
|
rows.push(row);
|
|
357
360
|
}
|
|
@@ -444,11 +447,10 @@ export const GridMixin = (superClass) =>
|
|
|
444
447
|
* @param {!HTMLTableRowElement} row
|
|
445
448
|
* @param {!Array<!GridColumn>} columns
|
|
446
449
|
* @param {?string} section
|
|
447
|
-
* @param {boolean} isColumnRow
|
|
448
450
|
* @param {boolean} noNotify
|
|
449
451
|
* @private
|
|
450
452
|
*/
|
|
451
|
-
__initRow(row, columns, section = 'body',
|
|
453
|
+
__initRow(row, columns, section = 'body', noNotify = false) {
|
|
452
454
|
const contentsFragment = document.createDocumentFragment();
|
|
453
455
|
|
|
454
456
|
iterateRowCells(row, (cell) => {
|
|
@@ -516,30 +518,6 @@ export const GridMixin = (superClass) =>
|
|
|
516
518
|
if (!noNotify) {
|
|
517
519
|
column._cells = [...column._cells];
|
|
518
520
|
}
|
|
519
|
-
} else {
|
|
520
|
-
// Header & footer
|
|
521
|
-
const tagName = section === 'header' ? 'th' : 'td';
|
|
522
|
-
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
523
|
-
cell = column[`_${section}Cell`];
|
|
524
|
-
if (!cell) {
|
|
525
|
-
cell = this._createCell(tagName);
|
|
526
|
-
}
|
|
527
|
-
cell._column = column;
|
|
528
|
-
row.appendChild(cell);
|
|
529
|
-
column[`_${section}Cell`] = cell;
|
|
530
|
-
} else {
|
|
531
|
-
if (!column._emptyCells) {
|
|
532
|
-
column._emptyCells = [];
|
|
533
|
-
}
|
|
534
|
-
cell = column._emptyCells.find((cell) => cell._vacant) || this._createCell(tagName);
|
|
535
|
-
cell._column = column;
|
|
536
|
-
row.appendChild(cell);
|
|
537
|
-
if (column._emptyCells.indexOf(cell) === -1) {
|
|
538
|
-
column._emptyCells.push(cell);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
updatePart(cell, 'cell', true);
|
|
542
|
-
updatePart(cell, `${section}-cell`, true);
|
|
543
521
|
}
|
|
544
522
|
|
|
545
523
|
if (!cell._content.parentElement) {
|
|
@@ -549,10 +527,6 @@ export const GridMixin = (superClass) =>
|
|
|
549
527
|
cell._column = column;
|
|
550
528
|
});
|
|
551
529
|
|
|
552
|
-
if (section !== 'body') {
|
|
553
|
-
this.__debounceUpdateHeaderFooterRowVisibility(row);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
530
|
// Might be empty if only cache was used
|
|
557
531
|
this.appendChild(contentsFragment);
|
|
558
532
|
|
|
@@ -560,75 +534,6 @@ export const GridMixin = (superClass) =>
|
|
|
560
534
|
this._updateFirstAndLastColumnForRow(row);
|
|
561
535
|
}
|
|
562
536
|
|
|
563
|
-
/**
|
|
564
|
-
* @param {HTMLTableRowElement} row
|
|
565
|
-
* @protected
|
|
566
|
-
*/
|
|
567
|
-
__debounceUpdateHeaderFooterRowVisibility(row) {
|
|
568
|
-
row.__debounceUpdateHeaderFooterRowVisibility = Debouncer.debounce(
|
|
569
|
-
row.__debounceUpdateHeaderFooterRowVisibility,
|
|
570
|
-
microTask,
|
|
571
|
-
() => this.__updateHeaderFooterRowVisibility(row),
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* @param {HTMLTableRowElement} row
|
|
577
|
-
* @protected
|
|
578
|
-
*/
|
|
579
|
-
__updateHeaderFooterRowVisibility(row) {
|
|
580
|
-
if (!row) {
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const visibleRowCells = Array.from(row.children).filter((cell) => {
|
|
585
|
-
const column = cell._column;
|
|
586
|
-
if (column._emptyCells && column._emptyCells.indexOf(cell) > -1) {
|
|
587
|
-
// The cell is an "empty cell" -> doesn't block hiding the row
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
if (row.parentElement === this.$.header) {
|
|
591
|
-
if (column.headerRenderer) {
|
|
592
|
-
// The cell is the header cell of a column that has a header renderer
|
|
593
|
-
// -> row should be visible
|
|
594
|
-
return true;
|
|
595
|
-
}
|
|
596
|
-
if (column.header === null) {
|
|
597
|
-
// The column header is explicilty set to null -> doesn't block hiding the row
|
|
598
|
-
return false;
|
|
599
|
-
}
|
|
600
|
-
if (column.path || column.header !== undefined) {
|
|
601
|
-
// The column has an explicit non-null header or a path that generates a header
|
|
602
|
-
// -> row should be visible
|
|
603
|
-
return true;
|
|
604
|
-
}
|
|
605
|
-
} else if (column.footerRenderer) {
|
|
606
|
-
// The cell is the footer cell of a column that has a footer renderer
|
|
607
|
-
// -> row should be visible
|
|
608
|
-
return true;
|
|
609
|
-
}
|
|
610
|
-
return false;
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
if (row.hidden !== !visibleRowCells.length) {
|
|
614
|
-
row.hidden = !visibleRowCells.length;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if (row.parentElement === this.$.header) {
|
|
618
|
-
this.$.table.toggleAttribute('has-header', this.$.header.querySelector('tr:not([hidden])'));
|
|
619
|
-
this.__updateHeaderFooterRowParts('header');
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
if (row.parentElement === this.$.footer) {
|
|
623
|
-
this.$.table.toggleAttribute('has-footer', this.$.footer.querySelector('tr:not([hidden])'));
|
|
624
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Make sure the section has a tabbable element
|
|
628
|
-
this._resetKeyboardNavigation();
|
|
629
|
-
this.__a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
537
|
/** @private */
|
|
633
538
|
__updateVirtualizerElement(row, index) {
|
|
634
539
|
this._preventScrollerRotatingCellFocus(row, index);
|
|
@@ -681,46 +586,17 @@ export const GridMixin = (superClass) =>
|
|
|
681
586
|
*/
|
|
682
587
|
_renderColumnTree(columnTree) {
|
|
683
588
|
iterateChildren(this.$.items, (row) => {
|
|
684
|
-
this.__initRow(row, columnTree[columnTree.length - 1], 'body',
|
|
589
|
+
this.__initRow(row, columnTree[columnTree.length - 1], 'body', true);
|
|
685
590
|
this.__updateRow(row);
|
|
686
591
|
});
|
|
687
592
|
|
|
688
|
-
|
|
689
|
-
const headerRow = document.createElement('tr');
|
|
690
|
-
headerRow.setAttribute('role', 'row');
|
|
691
|
-
headerRow.setAttribute('tabindex', '-1');
|
|
692
|
-
updatePart(headerRow, 'row', true);
|
|
693
|
-
updatePart(headerRow, 'header-row', true);
|
|
694
|
-
this.$.header.appendChild(headerRow);
|
|
695
|
-
|
|
696
|
-
const footerRow = document.createElement('tr');
|
|
697
|
-
footerRow.setAttribute('role', 'row');
|
|
698
|
-
footerRow.setAttribute('tabindex', '-1');
|
|
699
|
-
updatePart(footerRow, 'row', true);
|
|
700
|
-
updatePart(footerRow, 'footer-row', true);
|
|
701
|
-
this.$.footer.appendChild(footerRow);
|
|
702
|
-
}
|
|
703
|
-
while (this.$.header.children.length > columnTree.length) {
|
|
704
|
-
this.$.header.removeChild(this.$.header.firstElementChild);
|
|
705
|
-
this.$.footer.removeChild(this.$.footer.firstElementChild);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
iterateChildren(this.$.header, (headerRow, index) => {
|
|
709
|
-
this.__initRow(headerRow, columnTree[index], 'header', index === columnTree.length - 1);
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
iterateChildren(this.$.footer, (footerRow, index) => {
|
|
713
|
-
this.__initRow(footerRow, columnTree[columnTree.length - 1 - index], 'footer', index === 0);
|
|
714
|
-
});
|
|
593
|
+
this.__renderHeaderFooter();
|
|
715
594
|
|
|
716
595
|
// Sizer rows
|
|
717
596
|
this.__initRow(this.$.sizer, columnTree[columnTree.length - 1]);
|
|
718
597
|
|
|
719
|
-
this.__updateHeaderFooterRowParts('header');
|
|
720
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
721
598
|
this._resizeHandler();
|
|
722
599
|
this._frozenCellsChanged();
|
|
723
|
-
this._updateFirstAndLastColumn();
|
|
724
600
|
this._resetKeyboardNavigation();
|
|
725
601
|
this.__a11yUpdateHeaderRows();
|
|
726
602
|
this.__a11yUpdateFooterRows();
|
|
@@ -728,20 +604,6 @@ export const GridMixin = (superClass) =>
|
|
|
728
604
|
this.__updateHeaderAndFooter();
|
|
729
605
|
}
|
|
730
606
|
|
|
731
|
-
/** @private */
|
|
732
|
-
__updateHeaderFooterRowParts(section) {
|
|
733
|
-
const visibleRows = [...this.$[section].querySelectorAll('tr:not([hidden])')];
|
|
734
|
-
[...this.$[section].children].forEach((row) => {
|
|
735
|
-
updatePart(row, `first-${section}-row`, row === visibleRows.at(0));
|
|
736
|
-
updatePart(row, `last-${section}-row`, row === visibleRows.at(-1));
|
|
737
|
-
|
|
738
|
-
getBodyRowCells(row).forEach((cell) => {
|
|
739
|
-
updatePart(cell, `first-${section}-row-cell`, row === visibleRows.at(0));
|
|
740
|
-
updatePart(cell, `last-${section}-row-cell`, row === visibleRows.at(-1));
|
|
741
|
-
});
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
|
|
745
607
|
/**
|
|
746
608
|
* @param {!HTMLElement} row
|
|
747
609
|
* @param {boolean} loading
|
|
@@ -46,16 +46,25 @@ export const GridSortColumnMixin = (superClass) =>
|
|
|
46
46
|
*/
|
|
47
47
|
_defaultHeaderRenderer(root, _column) {
|
|
48
48
|
let sorter = root.firstElementChild;
|
|
49
|
-
|
|
49
|
+
const isNewSorter = !sorter;
|
|
50
|
+
if (isNewSorter) {
|
|
50
51
|
sorter = document.createElement('vaadin-grid-sorter');
|
|
51
52
|
sorter.addEventListener('direction-changed', this.__boundOnDirectionChanged);
|
|
52
|
-
root.appendChild(sorter);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
sorter.path = this.path;
|
|
56
56
|
sorter.__rendererDirection = this.direction;
|
|
57
57
|
sorter.direction = this.direction;
|
|
58
58
|
sorter.textContent = this.__getHeader(this.header, this.path);
|
|
59
|
+
|
|
60
|
+
// Append the sorter only after its direction has been set. If the cell
|
|
61
|
+
// content is already connected (as with the declarative header rendering),
|
|
62
|
+
// appending an unconfigured sorter makes it notify its default `null`
|
|
63
|
+
// direction before `__rendererDirection` is set, which would reset the
|
|
64
|
+
// column's direction. See __onDirectionChanged.
|
|
65
|
+
if (isNewSorter) {
|
|
66
|
+
root.appendChild(sorter);
|
|
67
|
+
}
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
/**
|
|
@@ -51,6 +51,7 @@ import { GridSorterMixin } from './vaadin-grid-sorter-mixin.js';
|
|
|
51
51
|
* @fires {CustomEvent} direction-changed - Fired when the `direction` property changes.
|
|
52
52
|
* @fires {CustomEvent} sorter-changed - Fired when the `path` or `direction` property changes.
|
|
53
53
|
*
|
|
54
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
54
55
|
* @customElement vaadin-grid-sorter
|
|
55
56
|
* @extends HTMLElement
|
|
56
57
|
*/
|
|
@@ -59,6 +59,7 @@ import { GridTreeToggleMixin } from './vaadin-grid-tree-toggle-mixin.js';
|
|
|
59
59
|
*
|
|
60
60
|
* @fires {CustomEvent} expanded-changed - Fired when the `expanded` property changes.
|
|
61
61
|
*
|
|
62
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
62
63
|
* @customElement vaadin-grid-tree-toggle
|
|
63
64
|
* @extends HTMLElement
|
|
64
65
|
*/
|
package/src/vaadin-grid.js
CHANGED
|
@@ -274,6 +274,7 @@ const DEFAULT_I18N = {
|
|
|
274
274
|
* @fires {CustomEvent} size-changed - Fired when the `size` property changes.
|
|
275
275
|
* @fires {CustomEvent} item-toggle - Fired when the user selects or deselects an item through the selection column.
|
|
276
276
|
*
|
|
277
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
277
278
|
* @customElement vaadin-grid
|
|
278
279
|
* @extends HTMLElement
|
|
279
280
|
*/
|