@vaadin/grid 24.2.0-dev.f254716fe → 24.3.0-alpha2
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/package.json +11 -11
- package/src/vaadin-grid-array-data-provider-mixin.js +1 -1
- package/src/vaadin-grid-column-group-mixin.d.ts +20 -0
- package/src/vaadin-grid-column-group-mixin.js +369 -0
- package/src/vaadin-grid-column-group.d.ts +4 -14
- package/src/vaadin-grid-column-group.js +8 -356
- package/src/vaadin-grid-column-mixin.d.ts +156 -0
- package/src/vaadin-grid-column-mixin.js +887 -0
- package/src/vaadin-grid-column.d.ts +11 -138
- package/src/vaadin-grid-column.js +6 -876
- package/src/vaadin-grid-data-provider-mixin.d.ts +6 -5
- package/src/vaadin-grid-data-provider-mixin.js +51 -20
- package/src/vaadin-grid-drag-and-drop-mixin.js +1 -1
- package/src/vaadin-grid-dynamic-columns-mixin.js +1 -1
- package/src/vaadin-grid-filter-column.js +5 -1
- package/src/vaadin-grid-filter-element-mixin.d.ts +34 -0
- package/src/vaadin-grid-filter-element-mixin.js +99 -0
- package/src/vaadin-grid-filter.d.ts +4 -21
- package/src/vaadin-grid-filter.js +8 -85
- package/src/vaadin-grid-keyboard-navigation-mixin.js +24 -4
- package/src/vaadin-grid-mixin.d.ts +218 -0
- package/src/vaadin-grid-mixin.js +1022 -0
- package/src/vaadin-grid-scroll-mixin.js +1 -1
- package/src/vaadin-grid-selection-column-base-mixin.d.ts +6 -0
- package/src/vaadin-grid-selection-column-base-mixin.js +151 -0
- package/src/vaadin-grid-selection-column.js +4 -1
- package/src/vaadin-grid-sort-column.js +5 -1
- package/src/vaadin-grid-sorter-mixin.d.ts +44 -0
- package/src/vaadin-grid-sorter-mixin.js +198 -0
- package/src/vaadin-grid-sorter.d.ts +3 -32
- package/src/vaadin-grid-sorter.js +8 -182
- package/src/vaadin-grid-tree-column-mixin.d.ts +19 -0
- package/src/vaadin-grid-tree-column-mixin.js +92 -0
- package/src/vaadin-grid-tree-column.d.ts +9 -7
- package/src/vaadin-grid-tree-column.js +7 -82
- package/src/vaadin-grid-tree-toggle.js +3 -1
- package/src/vaadin-grid.d.ts +5 -190
- package/src/vaadin-grid.js +7 -1018
- package/web-types.json +2311 -0
- package/web-types.lit.json +1007 -0
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2016 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { isElementHidden } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
7
|
+
import { TabindexMixin } from '@vaadin/a11y-base/src/tabindex-mixin.js';
|
|
8
|
+
import { animationFrame, microTask } from '@vaadin/component-base/src/async.js';
|
|
9
|
+
import { isAndroid, isChrome, isFirefox, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
10
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
11
|
+
import { getClosestElement } from '@vaadin/component-base/src/dom-utils.js';
|
|
12
|
+
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
13
|
+
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
14
|
+
import { Virtualizer } from '@vaadin/component-base/src/virtualizer.js';
|
|
15
|
+
import { A11yMixin } from './vaadin-grid-a11y-mixin.js';
|
|
16
|
+
import { ActiveItemMixin } from './vaadin-grid-active-item-mixin.js';
|
|
17
|
+
import { ArrayDataProviderMixin } from './vaadin-grid-array-data-provider-mixin.js';
|
|
18
|
+
import { ColumnReorderingMixin } from './vaadin-grid-column-reordering-mixin.js';
|
|
19
|
+
import { ColumnResizingMixin } from './vaadin-grid-column-resizing-mixin.js';
|
|
20
|
+
import { DataProviderMixin } from './vaadin-grid-data-provider-mixin.js';
|
|
21
|
+
import { DragAndDropMixin } from './vaadin-grid-drag-and-drop-mixin.js';
|
|
22
|
+
import { DynamicColumnsMixin } from './vaadin-grid-dynamic-columns-mixin.js';
|
|
23
|
+
import { EventContextMixin } from './vaadin-grid-event-context-mixin.js';
|
|
24
|
+
import { FilterMixin } from './vaadin-grid-filter-mixin.js';
|
|
25
|
+
import {
|
|
26
|
+
getBodyRowCells,
|
|
27
|
+
iterateChildren,
|
|
28
|
+
iterateRowCells,
|
|
29
|
+
updateBooleanRowStates,
|
|
30
|
+
updateCellsPart,
|
|
31
|
+
} from './vaadin-grid-helpers.js';
|
|
32
|
+
import { KeyboardNavigationMixin } from './vaadin-grid-keyboard-navigation-mixin.js';
|
|
33
|
+
import { RowDetailsMixin } from './vaadin-grid-row-details-mixin.js';
|
|
34
|
+
import { ScrollMixin } from './vaadin-grid-scroll-mixin.js';
|
|
35
|
+
import { SelectionMixin } from './vaadin-grid-selection-mixin.js';
|
|
36
|
+
import { SortMixin } from './vaadin-grid-sort-mixin.js';
|
|
37
|
+
import { StylingMixin } from './vaadin-grid-styling-mixin.js';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A mixin providing common grid functionality.
|
|
41
|
+
*
|
|
42
|
+
* @polymerMixin
|
|
43
|
+
* @mixes A11yMixin
|
|
44
|
+
* @mixes ActiveItemMixin
|
|
45
|
+
* @mixes ArrayDataProviderMixin
|
|
46
|
+
* @mixes ColumnResizingMixin
|
|
47
|
+
* @mixes DataProviderMixin
|
|
48
|
+
* @mixes DynamicColumnsMixin
|
|
49
|
+
* @mixes FilterMixin
|
|
50
|
+
* @mixes RowDetailsMixin
|
|
51
|
+
* @mixes ScrollMixin
|
|
52
|
+
* @mixes SelectionMixin
|
|
53
|
+
* @mixes SortMixin
|
|
54
|
+
* @mixes KeyboardNavigationMixin
|
|
55
|
+
* @mixes ColumnReorderingMixin
|
|
56
|
+
* @mixes EventContextMixin
|
|
57
|
+
* @mixes StylingMixin
|
|
58
|
+
* @mixes DragAndDropMixin
|
|
59
|
+
*/
|
|
60
|
+
export const GridMixin = (superClass) =>
|
|
61
|
+
class extends ArrayDataProviderMixin(
|
|
62
|
+
DataProviderMixin(
|
|
63
|
+
DynamicColumnsMixin(
|
|
64
|
+
ActiveItemMixin(
|
|
65
|
+
ScrollMixin(
|
|
66
|
+
SelectionMixin(
|
|
67
|
+
SortMixin(
|
|
68
|
+
RowDetailsMixin(
|
|
69
|
+
KeyboardNavigationMixin(
|
|
70
|
+
A11yMixin(
|
|
71
|
+
FilterMixin(
|
|
72
|
+
ColumnReorderingMixin(
|
|
73
|
+
ColumnResizingMixin(
|
|
74
|
+
EventContextMixin(DragAndDropMixin(StylingMixin(TabindexMixin(superClass)))),
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
) {
|
|
88
|
+
static get observers() {
|
|
89
|
+
return [
|
|
90
|
+
'_columnTreeChanged(_columnTree, _columnTree.*)',
|
|
91
|
+
'_flatSizeChanged(_flatSize, __virtualizer, _hasData, _columnTree)',
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static get properties() {
|
|
96
|
+
return {
|
|
97
|
+
/** @private */
|
|
98
|
+
_safari: {
|
|
99
|
+
type: Boolean,
|
|
100
|
+
value: isSafari,
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/** @private */
|
|
104
|
+
_ios: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
value: isIOS,
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/** @private */
|
|
110
|
+
_firefox: {
|
|
111
|
+
type: Boolean,
|
|
112
|
+
value: isFirefox,
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/** @private */
|
|
116
|
+
_android: {
|
|
117
|
+
type: Boolean,
|
|
118
|
+
value: isAndroid,
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/** @private */
|
|
122
|
+
_touchDevice: {
|
|
123
|
+
type: Boolean,
|
|
124
|
+
value: isTouch,
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* If true, the grid's height is defined by its rows.
|
|
129
|
+
*
|
|
130
|
+
* Effectively, this disables the grid's virtual scrolling so that all the rows are rendered in the DOM at once.
|
|
131
|
+
* If the grid has a large number of items, using the feature is discouraged to avoid performance issues.
|
|
132
|
+
* @attr {boolean} all-rows-visible
|
|
133
|
+
* @type {boolean}
|
|
134
|
+
*/
|
|
135
|
+
allRowsVisible: {
|
|
136
|
+
type: Boolean,
|
|
137
|
+
value: false,
|
|
138
|
+
reflectToAttribute: true,
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/** @private */
|
|
142
|
+
__pendingRecalculateColumnWidths: {
|
|
143
|
+
type: Boolean,
|
|
144
|
+
value: true,
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/** @private */
|
|
148
|
+
isAttached: {
|
|
149
|
+
value: false,
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* An internal property that is mainly used by `vaadin-template-renderer`
|
|
154
|
+
* to identify grid elements.
|
|
155
|
+
*
|
|
156
|
+
* @private
|
|
157
|
+
*/
|
|
158
|
+
__gridElement: {
|
|
159
|
+
type: Boolean,
|
|
160
|
+
value: true,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
constructor() {
|
|
166
|
+
super();
|
|
167
|
+
this.addEventListener('animationend', this._onAnimationEnd);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @private */
|
|
171
|
+
get _firstVisibleIndex() {
|
|
172
|
+
const firstVisibleItem = this.__getFirstVisibleItem();
|
|
173
|
+
return firstVisibleItem ? firstVisibleItem.index : undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** @private */
|
|
177
|
+
get _lastVisibleIndex() {
|
|
178
|
+
const lastVisibleItem = this.__getLastVisibleItem();
|
|
179
|
+
return lastVisibleItem ? lastVisibleItem.index : undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** @protected */
|
|
183
|
+
connectedCallback() {
|
|
184
|
+
super.connectedCallback();
|
|
185
|
+
this.isAttached = true;
|
|
186
|
+
this.recalculateColumnWidths();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @protected */
|
|
190
|
+
disconnectedCallback() {
|
|
191
|
+
super.disconnectedCallback();
|
|
192
|
+
this.isAttached = false;
|
|
193
|
+
this._hideTooltip(true);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** @private */
|
|
197
|
+
__getFirstVisibleItem() {
|
|
198
|
+
return this._getRenderedRows().find((row) => this._isInViewport(row));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** @private */
|
|
202
|
+
__getLastVisibleItem() {
|
|
203
|
+
return this._getRenderedRows()
|
|
204
|
+
.reverse()
|
|
205
|
+
.find((row) => this._isInViewport(row));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** @private */
|
|
209
|
+
_isInViewport(item) {
|
|
210
|
+
const scrollTargetRect = this.$.table.getBoundingClientRect();
|
|
211
|
+
const itemRect = item.getBoundingClientRect();
|
|
212
|
+
const headerHeight = this.$.header.getBoundingClientRect().height;
|
|
213
|
+
const footerHeight = this.$.footer.getBoundingClientRect().height;
|
|
214
|
+
return (
|
|
215
|
+
itemRect.bottom > scrollTargetRect.top + headerHeight && itemRect.top < scrollTargetRect.bottom - footerHeight
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** @private */
|
|
220
|
+
_getRenderedRows() {
|
|
221
|
+
return Array.from(this.$.items.children)
|
|
222
|
+
.filter((item) => !item.hidden)
|
|
223
|
+
.sort((a, b) => a.index - b.index);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** @protected */
|
|
227
|
+
_getRowContainingNode(node) {
|
|
228
|
+
const content = getClosestElement('vaadin-grid-cell-content', node);
|
|
229
|
+
if (!content) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const cell = content.assignedSlot.parentElement;
|
|
234
|
+
return cell.parentElement;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** @protected */
|
|
238
|
+
_isItemAssignedToRow(item, row) {
|
|
239
|
+
const model = this.__getRowModel(row);
|
|
240
|
+
return this.getItemId(item) === this.getItemId(model.item);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** @protected */
|
|
244
|
+
ready() {
|
|
245
|
+
super.ready();
|
|
246
|
+
|
|
247
|
+
this.__virtualizer = new Virtualizer({
|
|
248
|
+
createElements: this._createScrollerRows.bind(this),
|
|
249
|
+
updateElement: this._updateScrollerItem.bind(this),
|
|
250
|
+
scrollContainer: this.$.items,
|
|
251
|
+
scrollTarget: this.$.table,
|
|
252
|
+
reorderElements: true,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
new ResizeObserver(() =>
|
|
256
|
+
setTimeout(() => {
|
|
257
|
+
this.__updateFooterPositioning();
|
|
258
|
+
this.__updateColumnsBodyContentHidden();
|
|
259
|
+
this.__tryToRecalculateColumnWidthsIfPending();
|
|
260
|
+
}),
|
|
261
|
+
).observe(this.$.table);
|
|
262
|
+
|
|
263
|
+
processTemplates(this);
|
|
264
|
+
|
|
265
|
+
this._tooltipController = new TooltipController(this);
|
|
266
|
+
this.addController(this._tooltipController);
|
|
267
|
+
this._tooltipController.setManual(true);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** @private */
|
|
271
|
+
__getBodyCellCoordinates(cell) {
|
|
272
|
+
if (this.$.items.contains(cell) && cell.localName === 'td') {
|
|
273
|
+
return {
|
|
274
|
+
item: cell.parentElement._item,
|
|
275
|
+
column: cell._column,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** @private */
|
|
281
|
+
__focusBodyCell({ item, column }) {
|
|
282
|
+
const row = this._getRenderedRows().find((row) => row._item === item);
|
|
283
|
+
const cell = row && [...row.children].find((cell) => cell._column === column);
|
|
284
|
+
if (cell) {
|
|
285
|
+
cell.focus();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** @protected */
|
|
290
|
+
_focusFirstVisibleRow() {
|
|
291
|
+
const row = this.__getFirstVisibleItem();
|
|
292
|
+
this.__rowFocusMode = true;
|
|
293
|
+
row.focus();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** @private */
|
|
297
|
+
_flatSizeChanged(flatSize, virtualizer, hasData, columnTree) {
|
|
298
|
+
if (virtualizer && hasData && columnTree) {
|
|
299
|
+
// Changing the virtualizer size may result in the row with focus getting hidden
|
|
300
|
+
const cell = this.shadowRoot.activeElement;
|
|
301
|
+
const cellCoordinates = this.__getBodyCellCoordinates(cell);
|
|
302
|
+
|
|
303
|
+
const previousSize = virtualizer.size || 0;
|
|
304
|
+
virtualizer.size = flatSize;
|
|
305
|
+
|
|
306
|
+
// Request an update for the previous last row to have the "last" state removed
|
|
307
|
+
virtualizer.update(previousSize - 1, previousSize - 1);
|
|
308
|
+
if (flatSize < previousSize) {
|
|
309
|
+
// Size was decreased, so the new last row requires an explicit update
|
|
310
|
+
virtualizer.update(flatSize - 1, flatSize - 1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// If the focused cell's parent row got hidden by the size change, focus the corresponding new cell
|
|
314
|
+
if (cellCoordinates && cell.parentElement.hidden) {
|
|
315
|
+
this.__focusBodyCell(cellCoordinates);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Make sure the body has a tabbable element
|
|
319
|
+
this._resetKeyboardNavigation();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** @private */
|
|
324
|
+
__hasRowsWithClientHeight() {
|
|
325
|
+
return !!Array.from(this.$.items.children).filter((row) => row.clientHeight).length;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** @private */
|
|
329
|
+
__getIntrinsicWidth(col) {
|
|
330
|
+
if (!this.__intrinsicWidthCache.has(col)) {
|
|
331
|
+
this.__calculateAndCacheIntrinsicWidths([col]);
|
|
332
|
+
}
|
|
333
|
+
return this.__intrinsicWidthCache.get(col);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** @private */
|
|
337
|
+
__getDistributedWidth(col, innerColumn) {
|
|
338
|
+
if (col == null || col === this) {
|
|
339
|
+
return 0;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const columnWidth = Math.max(this.__getIntrinsicWidth(col), this.__getDistributedWidth(col.parentElement, col));
|
|
343
|
+
|
|
344
|
+
// We're processing a regular grid-column and not a grid-column-group
|
|
345
|
+
if (!innerColumn) {
|
|
346
|
+
return columnWidth;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// At the end, the width of each vaadin-grid-column-group is determined by the sum of the width of its children.
|
|
350
|
+
// Here we determine how much space the vaadin-grid-column-group actually needs to render properly and then we distribute that space
|
|
351
|
+
// to its children, so when we actually do the summation it will be rendered properly.
|
|
352
|
+
// Check out vaadin-grid-column-group:_updateFlexAndWidth
|
|
353
|
+
const columnGroup = col;
|
|
354
|
+
const columnGroupWidth = columnWidth;
|
|
355
|
+
const sumOfWidthOfAllChildColumns = columnGroup._visibleChildColumns
|
|
356
|
+
.map((col) => this.__getIntrinsicWidth(col))
|
|
357
|
+
.reduce((sum, curr) => sum + curr, 0);
|
|
358
|
+
|
|
359
|
+
const extraNecessarySpaceForGridColumnGroup = Math.max(0, columnGroupWidth - sumOfWidthOfAllChildColumns);
|
|
360
|
+
|
|
361
|
+
// The distribution of the extra necessary space is done according to the intrinsic width of each child column.
|
|
362
|
+
// Lets say we need 100 pixels of extra space for the grid-column-group to render properly
|
|
363
|
+
// it has two grid-column children, |100px|300px| in total 400px
|
|
364
|
+
// the first column gets 25px of the additional space (100/400)*100 = 25
|
|
365
|
+
// the second column gets the 75px of the additional space (300/400)*100 = 75
|
|
366
|
+
const proportionOfExtraSpace = this.__getIntrinsicWidth(innerColumn) / sumOfWidthOfAllChildColumns;
|
|
367
|
+
const shareOfInnerColumnFromNecessaryExtraSpace = proportionOfExtraSpace * extraNecessarySpaceForGridColumnGroup;
|
|
368
|
+
|
|
369
|
+
return this.__getIntrinsicWidth(innerColumn) + shareOfInnerColumnFromNecessaryExtraSpace;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* @param {!Array<!GridColumn>} cols the columns to auto size based on their content width
|
|
374
|
+
* @private
|
|
375
|
+
*/
|
|
376
|
+
_recalculateColumnWidths(cols) {
|
|
377
|
+
// Flush to make sure DOM is up-to-date when measuring the column widths
|
|
378
|
+
this.__virtualizer.flush();
|
|
379
|
+
[...this.$.header.children, ...this.$.footer.children].forEach((row) => {
|
|
380
|
+
if (row.__debounceUpdateHeaderFooterRowVisibility) {
|
|
381
|
+
row.__debounceUpdateHeaderFooterRowVisibility.flush();
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Flush to account for any changes to the visibility of the columns
|
|
386
|
+
if (this._debouncerHiddenChanged) {
|
|
387
|
+
this._debouncerHiddenChanged.flush();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
this.__intrinsicWidthCache = new Map();
|
|
391
|
+
// Cache the viewport rows to avoid unnecessary reflows while measuring the column widths
|
|
392
|
+
const fvi = this._firstVisibleIndex;
|
|
393
|
+
const lvi = this._lastVisibleIndex;
|
|
394
|
+
this.__viewportRowsCache = this._getRenderedRows().filter((row) => row.index >= fvi && row.index <= lvi);
|
|
395
|
+
|
|
396
|
+
// Pre-cache the intrinsic width of each column
|
|
397
|
+
this.__calculateAndCacheIntrinsicWidths(cols);
|
|
398
|
+
|
|
399
|
+
cols.forEach((col) => {
|
|
400
|
+
col.width = `${this.__getDistributedWidth(col)}px`;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Toggles the cell content for the given column to use or not use auto width.
|
|
406
|
+
*
|
|
407
|
+
* While content for all the column cells uses auto width (instead of the default 100%),
|
|
408
|
+
* their offsetWidth can be used to calculate the collective intrinsic width of the column.
|
|
409
|
+
*
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
__setVisibleCellContentAutoWidth(col, autoWidth) {
|
|
413
|
+
col._allCells
|
|
414
|
+
.filter((cell) => {
|
|
415
|
+
if (this.$.items.contains(cell)) {
|
|
416
|
+
return this.__viewportRowsCache.includes(cell.parentElement);
|
|
417
|
+
}
|
|
418
|
+
return true;
|
|
419
|
+
})
|
|
420
|
+
.forEach((cell) => {
|
|
421
|
+
cell.__measuringAutoWidth = autoWidth;
|
|
422
|
+
cell._content.style.width = autoWidth ? 'auto' : '';
|
|
423
|
+
cell._content.style.position = autoWidth ? 'absolute' : '';
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Returns the maximum intrinsic width of the cell content in the given column.
|
|
429
|
+
* Only cells which are marked for measuring auto width are considered.
|
|
430
|
+
*
|
|
431
|
+
* @private
|
|
432
|
+
*/
|
|
433
|
+
__getAutoWidthCellsMaxWidth(col) {
|
|
434
|
+
// Note: _allCells only contains cells which are currently rendered in DOM
|
|
435
|
+
return col._allCells.reduce((width, cell) => {
|
|
436
|
+
// Add 1px buffer to the offset width to avoid too narrow columns (sub-pixel rendering)
|
|
437
|
+
return cell.__measuringAutoWidth ? Math.max(width, cell._content.offsetWidth + 1) : width;
|
|
438
|
+
}, 0);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Calculates and caches the intrinsic width of each given column.
|
|
443
|
+
*
|
|
444
|
+
* @private
|
|
445
|
+
*/
|
|
446
|
+
__calculateAndCacheIntrinsicWidths(cols) {
|
|
447
|
+
// Make all the columns use auto width at once before measuring to
|
|
448
|
+
// avoid reflows in between the measurements
|
|
449
|
+
cols.forEach((col) => this.__setVisibleCellContentAutoWidth(col, true));
|
|
450
|
+
// Measure and cache
|
|
451
|
+
cols.forEach((col) => {
|
|
452
|
+
const width = this.__getAutoWidthCellsMaxWidth(col);
|
|
453
|
+
this.__intrinsicWidthCache.set(col, width);
|
|
454
|
+
});
|
|
455
|
+
// Reset the columns to use 100% width
|
|
456
|
+
cols.forEach((col) => this.__setVisibleCellContentAutoWidth(col, false));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Updates the `width` of all columns which have `autoWidth` set to `true`.
|
|
461
|
+
*/
|
|
462
|
+
recalculateColumnWidths() {
|
|
463
|
+
if (!this._columnTree) {
|
|
464
|
+
return; // No columns
|
|
465
|
+
}
|
|
466
|
+
if (isElementHidden(this) || this._dataProviderController.isLoading()) {
|
|
467
|
+
this.__pendingRecalculateColumnWidths = true;
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const cols = this._getColumns().filter((col) => !col.hidden && col.autoWidth);
|
|
471
|
+
this._recalculateColumnWidths(cols);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** @private */
|
|
475
|
+
__tryToRecalculateColumnWidthsIfPending() {
|
|
476
|
+
if (
|
|
477
|
+
this.__pendingRecalculateColumnWidths &&
|
|
478
|
+
!isElementHidden(this) &&
|
|
479
|
+
!this._dataProviderController.isLoading() &&
|
|
480
|
+
this.__hasRowsWithClientHeight()
|
|
481
|
+
) {
|
|
482
|
+
this.__pendingRecalculateColumnWidths = false;
|
|
483
|
+
this.recalculateColumnWidths();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @protected
|
|
489
|
+
* @override
|
|
490
|
+
*/
|
|
491
|
+
_onDataProviderPageLoaded() {
|
|
492
|
+
super._onDataProviderPageLoaded();
|
|
493
|
+
this.__tryToRecalculateColumnWidthsIfPending();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** @private */
|
|
497
|
+
_createScrollerRows(count) {
|
|
498
|
+
const rows = [];
|
|
499
|
+
for (let i = 0; i < count; i++) {
|
|
500
|
+
const row = document.createElement('tr');
|
|
501
|
+
row.setAttribute('part', 'row');
|
|
502
|
+
row.setAttribute('role', 'row');
|
|
503
|
+
row.setAttribute('tabindex', '-1');
|
|
504
|
+
if (this._columnTree) {
|
|
505
|
+
this._updateRow(row, this._columnTree[this._columnTree.length - 1], 'body', false, true);
|
|
506
|
+
}
|
|
507
|
+
rows.push(row);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (this._columnTree) {
|
|
511
|
+
this._columnTree[this._columnTree.length - 1].forEach(
|
|
512
|
+
(c) => c.isConnected && c.notifyPath && c.notifyPath('_cells.*', c._cells),
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.__afterCreateScrollerRowsDebouncer = Debouncer.debounce(
|
|
517
|
+
this.__afterCreateScrollerRowsDebouncer,
|
|
518
|
+
animationFrame,
|
|
519
|
+
() => {
|
|
520
|
+
this._afterScroll();
|
|
521
|
+
this.__tryToRecalculateColumnWidthsIfPending();
|
|
522
|
+
},
|
|
523
|
+
);
|
|
524
|
+
return rows;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** @private */
|
|
528
|
+
_createCell(tagName, column) {
|
|
529
|
+
const contentId = (this._contentIndex = this._contentIndex + 1 || 0);
|
|
530
|
+
const slotName = `vaadin-grid-cell-content-${contentId}`;
|
|
531
|
+
|
|
532
|
+
const cellContent = document.createElement('vaadin-grid-cell-content');
|
|
533
|
+
cellContent.setAttribute('slot', slotName);
|
|
534
|
+
|
|
535
|
+
const cell = document.createElement(tagName);
|
|
536
|
+
cell.id = slotName.replace('-content-', '-');
|
|
537
|
+
cell.setAttribute('role', tagName === 'td' ? 'gridcell' : 'columnheader');
|
|
538
|
+
|
|
539
|
+
// For now we only support tooltip on desktop
|
|
540
|
+
if (!isAndroid && !isIOS) {
|
|
541
|
+
cell.addEventListener('mouseenter', (event) => {
|
|
542
|
+
if (!this.$.scroller.hasAttribute('scrolling')) {
|
|
543
|
+
this._showTooltip(event);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
cell.addEventListener('mouseleave', () => {
|
|
548
|
+
this._hideTooltip();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
cell.addEventListener('mousedown', () => {
|
|
552
|
+
this._hideTooltip(true);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const slot = document.createElement('slot');
|
|
557
|
+
slot.setAttribute('name', slotName);
|
|
558
|
+
|
|
559
|
+
if (column && column._focusButtonMode) {
|
|
560
|
+
const div = document.createElement('div');
|
|
561
|
+
div.setAttribute('role', 'button');
|
|
562
|
+
div.setAttribute('tabindex', '-1');
|
|
563
|
+
cell.appendChild(div);
|
|
564
|
+
|
|
565
|
+
// Patch `focus()` to use the button
|
|
566
|
+
cell._focusButton = div;
|
|
567
|
+
cell.focus = function () {
|
|
568
|
+
cell._focusButton.focus();
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
div.appendChild(slot);
|
|
572
|
+
} else {
|
|
573
|
+
cell.setAttribute('tabindex', '-1');
|
|
574
|
+
cell.appendChild(slot);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
cell._content = cellContent;
|
|
578
|
+
|
|
579
|
+
// With native Shadow DOM, mousedown on slotted element does not focus
|
|
580
|
+
// focusable slot wrapper, that is why cells are not focused with
|
|
581
|
+
// mousedown. Workaround: listen for mousedown and focus manually.
|
|
582
|
+
cellContent.addEventListener('mousedown', () => {
|
|
583
|
+
if (isChrome) {
|
|
584
|
+
// Chrome bug: focusing before mouseup prevents text selection, see http://crbug.com/771903
|
|
585
|
+
const mouseUpListener = (event) => {
|
|
586
|
+
// If focus is on element within the cell content - respect it, do not change
|
|
587
|
+
const contentContainsFocusedElement = cellContent.contains(this.getRootNode().activeElement);
|
|
588
|
+
// Only focus if mouse is released on cell content itself
|
|
589
|
+
const mouseUpWithinCell = event.composedPath().includes(cellContent);
|
|
590
|
+
if (!contentContainsFocusedElement && mouseUpWithinCell) {
|
|
591
|
+
cell.focus();
|
|
592
|
+
}
|
|
593
|
+
document.removeEventListener('mouseup', mouseUpListener, true);
|
|
594
|
+
};
|
|
595
|
+
document.addEventListener('mouseup', mouseUpListener, true);
|
|
596
|
+
} else {
|
|
597
|
+
// Focus on mouseup, on the other hand, removes selection on Safari.
|
|
598
|
+
// Watch out sync focus removal issue, only async focus works here.
|
|
599
|
+
setTimeout(() => {
|
|
600
|
+
if (!cellContent.contains(this.getRootNode().activeElement)) {
|
|
601
|
+
cell.focus();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
return cell;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @param {!HTMLTableRowElement} row
|
|
612
|
+
* @param {!Array<!GridColumn>} columns
|
|
613
|
+
* @param {?string} section
|
|
614
|
+
* @param {boolean} isColumnRow
|
|
615
|
+
* @param {boolean} noNotify
|
|
616
|
+
* @protected
|
|
617
|
+
*/
|
|
618
|
+
_updateRow(row, columns, section = 'body', isColumnRow = false, noNotify = false) {
|
|
619
|
+
const contentsFragment = document.createDocumentFragment();
|
|
620
|
+
|
|
621
|
+
iterateRowCells(row, (cell) => {
|
|
622
|
+
cell._vacant = true;
|
|
623
|
+
});
|
|
624
|
+
row.innerHTML = '';
|
|
625
|
+
if (section === 'body') {
|
|
626
|
+
// Clear the cached cell references
|
|
627
|
+
row.__cells = [];
|
|
628
|
+
row.__detailsCell = null;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
columns
|
|
632
|
+
.filter((column) => !column.hidden)
|
|
633
|
+
.forEach((column, index, cols) => {
|
|
634
|
+
let cell;
|
|
635
|
+
|
|
636
|
+
if (section === 'body') {
|
|
637
|
+
// Body
|
|
638
|
+
if (!column._cells) {
|
|
639
|
+
column._cells = [];
|
|
640
|
+
}
|
|
641
|
+
cell = column._cells.find((cell) => cell._vacant);
|
|
642
|
+
if (!cell) {
|
|
643
|
+
cell = this._createCell('td', column);
|
|
644
|
+
column._cells.push(cell);
|
|
645
|
+
}
|
|
646
|
+
cell.setAttribute('part', 'cell body-cell');
|
|
647
|
+
cell.__parentRow = row;
|
|
648
|
+
// Cache the cell reference
|
|
649
|
+
row.__cells.push(cell);
|
|
650
|
+
if (!column._bodyContentHidden) {
|
|
651
|
+
row.appendChild(cell);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (row === this.$.sizer) {
|
|
655
|
+
column._sizerCell = cell;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (index === cols.length - 1 && this.rowDetailsRenderer) {
|
|
659
|
+
// Add details cell as last cell to body rows
|
|
660
|
+
if (!this._detailsCells) {
|
|
661
|
+
this._detailsCells = [];
|
|
662
|
+
}
|
|
663
|
+
const detailsCell = this._detailsCells.find((cell) => cell._vacant) || this._createCell('td');
|
|
664
|
+
if (this._detailsCells.indexOf(detailsCell) === -1) {
|
|
665
|
+
this._detailsCells.push(detailsCell);
|
|
666
|
+
}
|
|
667
|
+
if (!detailsCell._content.parentElement) {
|
|
668
|
+
contentsFragment.appendChild(detailsCell._content);
|
|
669
|
+
}
|
|
670
|
+
this._configureDetailsCell(detailsCell);
|
|
671
|
+
row.appendChild(detailsCell);
|
|
672
|
+
// Cache the details cell reference
|
|
673
|
+
row.__detailsCell = detailsCell;
|
|
674
|
+
this._a11ySetRowDetailsCell(row, detailsCell);
|
|
675
|
+
detailsCell._vacant = false;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (column.notifyPath && !noNotify) {
|
|
679
|
+
column.notifyPath('_cells.*', column._cells);
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
// Header & footer
|
|
683
|
+
const tagName = section === 'header' ? 'th' : 'td';
|
|
684
|
+
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
685
|
+
cell = column[`_${section}Cell`] || this._createCell(tagName);
|
|
686
|
+
cell._column = column;
|
|
687
|
+
row.appendChild(cell);
|
|
688
|
+
column[`_${section}Cell`] = cell;
|
|
689
|
+
} else {
|
|
690
|
+
if (!column._emptyCells) {
|
|
691
|
+
column._emptyCells = [];
|
|
692
|
+
}
|
|
693
|
+
cell = column._emptyCells.find((cell) => cell._vacant) || this._createCell(tagName);
|
|
694
|
+
cell._column = column;
|
|
695
|
+
row.appendChild(cell);
|
|
696
|
+
if (column._emptyCells.indexOf(cell) === -1) {
|
|
697
|
+
column._emptyCells.push(cell);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
cell.setAttribute('part', `cell ${section}-cell`);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (!cell._content.parentElement) {
|
|
704
|
+
contentsFragment.appendChild(cell._content);
|
|
705
|
+
}
|
|
706
|
+
cell._vacant = false;
|
|
707
|
+
cell._column = column;
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
if (section !== 'body') {
|
|
711
|
+
this.__debounceUpdateHeaderFooterRowVisibility(row);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Might be empty if only cache was used
|
|
715
|
+
this.appendChild(contentsFragment);
|
|
716
|
+
|
|
717
|
+
this._frozenCellsChanged();
|
|
718
|
+
this._updateFirstAndLastColumnForRow(row);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* @param {HTMLTableRowElement} row
|
|
723
|
+
* @protected
|
|
724
|
+
*/
|
|
725
|
+
__debounceUpdateHeaderFooterRowVisibility(row) {
|
|
726
|
+
row.__debounceUpdateHeaderFooterRowVisibility = Debouncer.debounce(
|
|
727
|
+
row.__debounceUpdateHeaderFooterRowVisibility,
|
|
728
|
+
microTask,
|
|
729
|
+
() => this.__updateHeaderFooterRowVisibility(row),
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* @param {HTMLTableRowElement} row
|
|
735
|
+
* @protected
|
|
736
|
+
*/
|
|
737
|
+
__updateHeaderFooterRowVisibility(row) {
|
|
738
|
+
if (!row) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const visibleRowCells = Array.from(row.children).filter((cell) => {
|
|
743
|
+
const column = cell._column;
|
|
744
|
+
if (column._emptyCells && column._emptyCells.indexOf(cell) > -1) {
|
|
745
|
+
// The cell is an "empty cell" -> doesn't block hiding the row
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
if (row.parentElement === this.$.header) {
|
|
749
|
+
if (column.headerRenderer) {
|
|
750
|
+
// The cell is the header cell of a column that has a header renderer
|
|
751
|
+
// -> row should be visible
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
if (column.header === null) {
|
|
755
|
+
// The column header is explicilty set to null -> doesn't block hiding the row
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
if (column.path || column.header !== undefined) {
|
|
759
|
+
// The column has an explicit non-null header or a path that generates a header
|
|
760
|
+
// -> row should be visible
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
} else if (column.footerRenderer) {
|
|
764
|
+
// The cell is the footer cell of a column that has a footer renderer
|
|
765
|
+
// -> row should be visible
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
return false;
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
if (row.hidden !== !visibleRowCells.length) {
|
|
772
|
+
row.hidden = !visibleRowCells.length;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Make sure the section has a tabbable element
|
|
776
|
+
this._resetKeyboardNavigation();
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** @private */
|
|
780
|
+
_updateScrollerItem(row, index) {
|
|
781
|
+
this._preventScrollerRotatingCellFocus(row, index);
|
|
782
|
+
|
|
783
|
+
if (!this._columnTree) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
this._updateRowOrderParts(row, index);
|
|
788
|
+
|
|
789
|
+
this._a11yUpdateRowRowindex(row, index);
|
|
790
|
+
this._getItem(index, row);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/** @private */
|
|
794
|
+
_columnTreeChanged(columnTree) {
|
|
795
|
+
this._renderColumnTree(columnTree);
|
|
796
|
+
this.recalculateColumnWidths();
|
|
797
|
+
this.__updateColumnsBodyContentHidden();
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** @private */
|
|
801
|
+
_updateRowOrderParts(row, index = row.index) {
|
|
802
|
+
updateBooleanRowStates(row, {
|
|
803
|
+
first: index === 0,
|
|
804
|
+
last: index === this._flatSize - 1,
|
|
805
|
+
odd: index % 2 !== 0,
|
|
806
|
+
even: index % 2 === 0,
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** @private */
|
|
811
|
+
_updateRowStateParts(row, { expanded, selected, detailsOpened }) {
|
|
812
|
+
updateBooleanRowStates(row, {
|
|
813
|
+
expanded,
|
|
814
|
+
selected,
|
|
815
|
+
'details-opened': detailsOpened,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* @param {!Array<!GridColumn>} columnTree
|
|
821
|
+
* @protected
|
|
822
|
+
*/
|
|
823
|
+
_renderColumnTree(columnTree) {
|
|
824
|
+
iterateChildren(this.$.items, (row) => {
|
|
825
|
+
this._updateRow(row, columnTree[columnTree.length - 1], 'body', false, true);
|
|
826
|
+
|
|
827
|
+
const model = this.__getRowModel(row);
|
|
828
|
+
this._updateRowOrderParts(row);
|
|
829
|
+
this._updateRowStateParts(row, model);
|
|
830
|
+
this._filterDragAndDrop(row, model);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
while (this.$.header.children.length < columnTree.length) {
|
|
834
|
+
const headerRow = document.createElement('tr');
|
|
835
|
+
headerRow.setAttribute('part', 'row');
|
|
836
|
+
headerRow.setAttribute('role', 'row');
|
|
837
|
+
headerRow.setAttribute('tabindex', '-1');
|
|
838
|
+
this.$.header.appendChild(headerRow);
|
|
839
|
+
|
|
840
|
+
const footerRow = document.createElement('tr');
|
|
841
|
+
footerRow.setAttribute('part', 'row');
|
|
842
|
+
footerRow.setAttribute('role', 'row');
|
|
843
|
+
footerRow.setAttribute('tabindex', '-1');
|
|
844
|
+
this.$.footer.appendChild(footerRow);
|
|
845
|
+
}
|
|
846
|
+
while (this.$.header.children.length > columnTree.length) {
|
|
847
|
+
this.$.header.removeChild(this.$.header.firstElementChild);
|
|
848
|
+
this.$.footer.removeChild(this.$.footer.firstElementChild);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
iterateChildren(this.$.header, (headerRow, index, rows) => {
|
|
852
|
+
this._updateRow(headerRow, columnTree[index], 'header', index === columnTree.length - 1);
|
|
853
|
+
|
|
854
|
+
const cells = getBodyRowCells(headerRow);
|
|
855
|
+
updateCellsPart(cells, 'first-header-row-cell', index === 0);
|
|
856
|
+
updateCellsPart(cells, 'last-header-row-cell', index === rows.length - 1);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
iterateChildren(this.$.footer, (footerRow, index, rows) => {
|
|
860
|
+
this._updateRow(footerRow, columnTree[columnTree.length - 1 - index], 'footer', index === 0);
|
|
861
|
+
|
|
862
|
+
const cells = getBodyRowCells(footerRow);
|
|
863
|
+
updateCellsPart(cells, 'first-footer-row-cell', index === 0);
|
|
864
|
+
updateCellsPart(cells, 'last-footer-row-cell', index === rows.length - 1);
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// Sizer rows
|
|
868
|
+
this._updateRow(this.$.sizer, columnTree[columnTree.length - 1]);
|
|
869
|
+
|
|
870
|
+
this._resizeHandler();
|
|
871
|
+
this._frozenCellsChanged();
|
|
872
|
+
this._updateFirstAndLastColumn();
|
|
873
|
+
this._resetKeyboardNavigation();
|
|
874
|
+
this._a11yUpdateHeaderRows();
|
|
875
|
+
this._a11yUpdateFooterRows();
|
|
876
|
+
this.__updateFooterPositioning();
|
|
877
|
+
this.generateCellClassNames();
|
|
878
|
+
this.generateCellPartNames();
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/** @private */
|
|
882
|
+
__updateFooterPositioning() {
|
|
883
|
+
// TODO: fixed in Firefox 99, remove when we can drop Firefox ESR 91 support
|
|
884
|
+
if (this._firefox && parseFloat(navigator.userAgent.match(/Firefox\/(\d{2,3}.\d)/u)[1]) < 99) {
|
|
885
|
+
// Sticky (or translated) footer in a flexbox host doesn't get included in
|
|
886
|
+
// the scroll height calculation on FF. This is a workaround for the issue.
|
|
887
|
+
this.$.items.style.paddingBottom = 0;
|
|
888
|
+
if (!this.allRowsVisible) {
|
|
889
|
+
this.$.items.style.paddingBottom = `${this.$.footer.offsetHeight}px`;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* @param {!HTMLElement} row
|
|
896
|
+
* @param {GridItem} item
|
|
897
|
+
* @protected
|
|
898
|
+
*/
|
|
899
|
+
_updateItem(row, item) {
|
|
900
|
+
row._item = item;
|
|
901
|
+
const model = this.__getRowModel(row);
|
|
902
|
+
|
|
903
|
+
this._toggleDetailsCell(row, model.detailsOpened);
|
|
904
|
+
|
|
905
|
+
this._a11yUpdateRowLevel(row, model.level);
|
|
906
|
+
this._a11yUpdateRowSelected(row, model.selected);
|
|
907
|
+
|
|
908
|
+
this._updateRowStateParts(row, model);
|
|
909
|
+
|
|
910
|
+
this._generateCellClassNames(row, model);
|
|
911
|
+
this._generateCellPartNames(row, model);
|
|
912
|
+
this._filterDragAndDrop(row, model);
|
|
913
|
+
|
|
914
|
+
iterateChildren(row, (cell) => {
|
|
915
|
+
if (cell._renderer) {
|
|
916
|
+
const owner = cell._column || this;
|
|
917
|
+
cell._renderer.call(owner, cell._content, owner, model);
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
this._updateDetailsCellHeight(row);
|
|
922
|
+
|
|
923
|
+
this._a11yUpdateRowExpanded(row, model.expanded);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** @private */
|
|
927
|
+
_resizeHandler() {
|
|
928
|
+
this._updateDetailsCellHeights();
|
|
929
|
+
this.__updateFooterPositioning();
|
|
930
|
+
this.__updateHorizontalScrollPosition();
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/** @private */
|
|
934
|
+
_onAnimationEnd(e) {
|
|
935
|
+
// ShadyCSS applies scoping suffixes to animation names
|
|
936
|
+
if (e.animationName.indexOf('vaadin-grid-appear') === 0) {
|
|
937
|
+
e.stopPropagation();
|
|
938
|
+
this.__tryToRecalculateColumnWidthsIfPending();
|
|
939
|
+
|
|
940
|
+
requestAnimationFrame(() => {
|
|
941
|
+
this.__scrollToPendingIndexes();
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* @param {!HTMLTableRowElement} row
|
|
948
|
+
* @return {!GridItemModel}
|
|
949
|
+
* @protected
|
|
950
|
+
*/
|
|
951
|
+
__getRowModel(row) {
|
|
952
|
+
return {
|
|
953
|
+
index: row.index,
|
|
954
|
+
item: row._item,
|
|
955
|
+
level: this._getIndexLevel(row.index),
|
|
956
|
+
expanded: this._isExpanded(row._item),
|
|
957
|
+
selected: this._isSelected(row._item),
|
|
958
|
+
detailsOpened: !!this.rowDetailsRenderer && this._isDetailsOpened(row._item),
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* @param {Event} event
|
|
964
|
+
* @protected
|
|
965
|
+
*/
|
|
966
|
+
_showTooltip(event) {
|
|
967
|
+
// Check if there is a slotted vaadin-tooltip element.
|
|
968
|
+
const tooltip = this._tooltipController.node;
|
|
969
|
+
if (tooltip && tooltip.isConnected) {
|
|
970
|
+
this._tooltipController.setTarget(event.target);
|
|
971
|
+
this._tooltipController.setContext(this.getEventContext(event));
|
|
972
|
+
|
|
973
|
+
// Trigger opening using the corresponding delay.
|
|
974
|
+
tooltip._stateController.open({
|
|
975
|
+
focus: event.type === 'focusin',
|
|
976
|
+
hover: event.type === 'mouseenter',
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/** @protected */
|
|
982
|
+
_hideTooltip(immediate) {
|
|
983
|
+
const tooltip = this._tooltipController.node;
|
|
984
|
+
if (tooltip) {
|
|
985
|
+
tooltip._stateController.close(immediate);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Requests an update for the content of cells.
|
|
991
|
+
*
|
|
992
|
+
* While performing the update, the following renderers are invoked:
|
|
993
|
+
* - `Grid.rowDetailsRenderer`
|
|
994
|
+
* - `GridColumn.renderer`
|
|
995
|
+
* - `GridColumn.headerRenderer`
|
|
996
|
+
* - `GridColumn.footerRenderer`
|
|
997
|
+
*
|
|
998
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
999
|
+
*/
|
|
1000
|
+
requestContentUpdate() {
|
|
1001
|
+
if (this._columnTree) {
|
|
1002
|
+
// Header and footer renderers
|
|
1003
|
+
this._columnTree.forEach((level) => {
|
|
1004
|
+
level.forEach((column) => {
|
|
1005
|
+
if (column._renderHeaderAndFooter) {
|
|
1006
|
+
column._renderHeaderAndFooter();
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
// Body and row details renderers
|
|
1012
|
+
this.__updateVisibleRows();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/** @protected */
|
|
1017
|
+
__updateVisibleRows(start, end) {
|
|
1018
|
+
if (this.__virtualizer) {
|
|
1019
|
+
this.__virtualizer.update(start, end);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
};
|