@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.
- package/custom-elements.json +248 -128
- package/package.json +12 -12
- package/src/directives/cell-content-directive.js +33 -0
- package/src/styles/vaadin-grid-base-styles.js +0 -4
- package/src/vaadin-grid-a11y-mixin.js +20 -31
- package/src/vaadin-grid-active-item-mixin.js +1 -1
- package/src/vaadin-grid-column-auto-width-mixin.js +4 -13
- package/src/vaadin-grid-column-group-mixin.js +5 -20
- package/src/vaadin-grid-column-mixin.d.ts +3 -3
- package/src/vaadin-grid-column-mixin.js +57 -98
- package/src/vaadin-grid-column-reordering-mixin.js +10 -5
- package/src/vaadin-grid-column-resizing-mixin.js +4 -0
- package/src/vaadin-grid-data-provider-mixin.js +1 -1
- package/src/vaadin-grid-drag-and-drop-mixin.js +2 -5
- package/src/vaadin-grid-dynamic-columns-mixin.js +0 -5
- package/src/vaadin-grid-filter-column-mixin.js +1 -1
- package/src/vaadin-grid-filter-element-mixin.js +1 -3
- package/src/vaadin-grid-filter.js +1 -0
- package/src/vaadin-grid-header-footer-rendering-mixin.js +296 -0
- package/src/vaadin-grid-helpers.js +13 -0
- package/src/vaadin-grid-keyboard-navigation-mixin.js +3 -7
- package/src/vaadin-grid-mixin.js +69 -228
- package/src/vaadin-grid-row-details-mixin.js +1 -1
- package/src/vaadin-grid-scroll-mixin.js +2 -11
- package/src/vaadin-grid-selection-column-base-mixin.js +66 -21
- package/src/vaadin-grid-sort-column-mixin.js +11 -2
- package/src/vaadin-grid-sorter-mixin.js +22 -2
- package/src/vaadin-grid-sorter.js +2 -1
- package/src/vaadin-grid-tree-toggle.js +1 -0
- package/src/vaadin-grid.d.ts +63 -1
- package/src/vaadin-grid.js +51 -2
- package/web-types.json +29 -94
- package/web-types.lit.json +17 -10
package/src/vaadin-grid-mixin.js
CHANGED
|
@@ -4,10 +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 {
|
|
8
|
-
import { isAndroid, isChrome, isFirefox, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
9
|
-
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
10
|
-
import { getClosestElement } from '@vaadin/component-base/src/dom-utils.js';
|
|
7
|
+
import { isAndroid, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
11
8
|
import { setTouchAction } from '@vaadin/component-base/src/gestures.js';
|
|
12
9
|
import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
|
|
13
10
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
@@ -23,8 +20,10 @@ import { DragAndDropMixin } from './vaadin-grid-drag-and-drop-mixin.js';
|
|
|
23
20
|
import { DynamicColumnsMixin } from './vaadin-grid-dynamic-columns-mixin.js';
|
|
24
21
|
import { EventContextMixin } from './vaadin-grid-event-context-mixin.js';
|
|
25
22
|
import { FilterMixin } from './vaadin-grid-filter-mixin.js';
|
|
23
|
+
import { HeaderFooterRenderingMixin } from './vaadin-grid-header-footer-rendering-mixin.js';
|
|
26
24
|
import {
|
|
27
25
|
getBodyRowCells,
|
|
26
|
+
getClosestCell,
|
|
28
27
|
iterateChildren,
|
|
29
28
|
iterateRowCells,
|
|
30
29
|
updateBooleanRowStates,
|
|
@@ -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
|
),
|
|
@@ -91,12 +94,6 @@ export const GridMixin = (superClass) =>
|
|
|
91
94
|
value: isIOS,
|
|
92
95
|
},
|
|
93
96
|
|
|
94
|
-
/** @private */
|
|
95
|
-
_firefox: {
|
|
96
|
-
type: Boolean,
|
|
97
|
-
value: isFirefox,
|
|
98
|
-
},
|
|
99
|
-
|
|
100
97
|
/** @private */
|
|
101
98
|
_android: {
|
|
102
99
|
type: Boolean,
|
|
@@ -155,13 +152,22 @@ export const GridMixin = (superClass) =>
|
|
|
155
152
|
/** @private */
|
|
156
153
|
get _firstVisibleIndex() {
|
|
157
154
|
const firstVisibleItem = this.__getFirstVisibleItem();
|
|
158
|
-
return firstVisibleItem
|
|
155
|
+
return firstVisibleItem?.index;
|
|
159
156
|
}
|
|
160
157
|
|
|
161
158
|
/** @private */
|
|
162
159
|
get _lastVisibleIndex() {
|
|
163
160
|
const lastVisibleItem = this.__getLastVisibleItem();
|
|
164
|
-
return lastVisibleItem
|
|
161
|
+
return lastVisibleItem?.index;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
constructor() {
|
|
165
|
+
super();
|
|
166
|
+
|
|
167
|
+
this.__onCellMouseEnter = this.__onCellMouseEnter.bind(this);
|
|
168
|
+
this.__onCellMouseLeave = this.__onCellMouseLeave.bind(this);
|
|
169
|
+
this.__onCellMouseDown = this.__onCellMouseDown.bind(this);
|
|
170
|
+
this.__onCellKeyDown = this.__onCellKeyDown.bind(this);
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
/** @protected */
|
|
@@ -210,13 +216,7 @@ export const GridMixin = (superClass) =>
|
|
|
210
216
|
|
|
211
217
|
/** @protected */
|
|
212
218
|
_getRowContainingNode(node) {
|
|
213
|
-
|
|
214
|
-
if (!content) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const cell = content.assignedSlot.parentElement;
|
|
219
|
-
return cell.parentElement;
|
|
219
|
+
return getClosestCell(node)?.parentElement;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/** @protected */
|
|
@@ -298,9 +298,7 @@ export const GridMixin = (superClass) =>
|
|
|
298
298
|
__focusBodyCell({ item, column }) {
|
|
299
299
|
const row = this._getRenderedRows().find((row) => row._item === item);
|
|
300
300
|
const cell = row && [...row.children].find((cell) => cell._column === column);
|
|
301
|
-
|
|
302
|
-
cell.focus();
|
|
303
|
-
}
|
|
301
|
+
cell?.focus();
|
|
304
302
|
}
|
|
305
303
|
|
|
306
304
|
/** @protected */
|
|
@@ -347,7 +345,7 @@ export const GridMixin = (superClass) =>
|
|
|
347
345
|
updatePart(row, 'row', true);
|
|
348
346
|
updatePart(row, 'body-row', true);
|
|
349
347
|
if (this._columnTree) {
|
|
350
|
-
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body',
|
|
348
|
+
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body', true);
|
|
351
349
|
}
|
|
352
350
|
rows.push(row);
|
|
353
351
|
}
|
|
@@ -363,6 +361,36 @@ export const GridMixin = (superClass) =>
|
|
|
363
361
|
return rows;
|
|
364
362
|
}
|
|
365
363
|
|
|
364
|
+
/** @private */
|
|
365
|
+
__onCellKeyDown(event) {
|
|
366
|
+
const cell = event.currentTarget;
|
|
367
|
+
cell._column?.__onCellKeyDown?.(event);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** @private */
|
|
371
|
+
__onCellMouseEnter(event) {
|
|
372
|
+
// For now we only support tooltip on desktop
|
|
373
|
+
if (!isAndroid && !isIOS && !this.$.scroller.hasAttribute('scrolling')) {
|
|
374
|
+
this._showTooltip(event);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** @private */
|
|
379
|
+
__onCellMouseLeave() {
|
|
380
|
+
// For now we only support tooltip on desktop
|
|
381
|
+
if (!isAndroid && !isIOS) {
|
|
382
|
+
this._hideTooltip();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** @private */
|
|
387
|
+
__onCellMouseDown() {
|
|
388
|
+
// For now we only support tooltip on desktop
|
|
389
|
+
if (!isAndroid && !isIOS) {
|
|
390
|
+
this._hideTooltip(true);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
366
394
|
/** @private */
|
|
367
395
|
_createCell(tagName, column) {
|
|
368
396
|
const contentId = (this._contentIndex = this._contentIndex + 1 || 0);
|
|
@@ -375,22 +403,10 @@ export const GridMixin = (superClass) =>
|
|
|
375
403
|
cell.id = slotName.replace('-content-', '-');
|
|
376
404
|
cell.setAttribute('role', tagName === 'td' ? 'gridcell' : 'columnheader');
|
|
377
405
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
this._showTooltip(event);
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
cell.addEventListener('mouseleave', () => {
|
|
387
|
-
this._hideTooltip();
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
cell.addEventListener('mousedown', () => {
|
|
391
|
-
this._hideTooltip(true);
|
|
392
|
-
});
|
|
393
|
-
}
|
|
406
|
+
cell.addEventListener('mouseenter', this.__onCellMouseEnter);
|
|
407
|
+
cell.addEventListener('mouseleave', this.__onCellMouseLeave);
|
|
408
|
+
cell.addEventListener('mousedown', this.__onCellMouseDown);
|
|
409
|
+
cell.addEventListener('keydown', this.__onCellKeyDown);
|
|
394
410
|
|
|
395
411
|
const slot = document.createElement('slot');
|
|
396
412
|
slot.setAttribute('name', slotName);
|
|
@@ -415,34 +431,6 @@ export const GridMixin = (superClass) =>
|
|
|
415
431
|
|
|
416
432
|
cell._content = cellContent;
|
|
417
433
|
|
|
418
|
-
// With native Shadow DOM, mousedown on slotted element does not focus
|
|
419
|
-
// focusable slot wrapper, that is why cells are not focused with
|
|
420
|
-
// mousedown. Workaround: listen for mousedown and focus manually.
|
|
421
|
-
cellContent.addEventListener('mousedown', () => {
|
|
422
|
-
if (isChrome) {
|
|
423
|
-
// Chrome bug: focusing before mouseup prevents text selection, see http://crbug.com/771903
|
|
424
|
-
const mouseUpListener = (event) => {
|
|
425
|
-
// If focus is on element within the cell content - respect it, do not change
|
|
426
|
-
const contentContainsFocusedElement = cellContent.contains(this.getRootNode().activeElement);
|
|
427
|
-
// Only focus if mouse is released on cell content itself
|
|
428
|
-
const mouseUpWithinCell = event.composedPath().includes(cellContent);
|
|
429
|
-
if (!contentContainsFocusedElement && mouseUpWithinCell) {
|
|
430
|
-
cell.focus({ preventScroll: true });
|
|
431
|
-
}
|
|
432
|
-
document.removeEventListener('mouseup', mouseUpListener, true);
|
|
433
|
-
};
|
|
434
|
-
document.addEventListener('mouseup', mouseUpListener, true);
|
|
435
|
-
} else {
|
|
436
|
-
// Focus on mouseup, on the other hand, removes selection on Safari.
|
|
437
|
-
// Watch out sync focus removal issue, only async focus works here.
|
|
438
|
-
setTimeout(() => {
|
|
439
|
-
if (!cellContent.contains(this.getRootNode().activeElement)) {
|
|
440
|
-
cell.focus({ preventScroll: true });
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
|
|
446
434
|
return cell;
|
|
447
435
|
}
|
|
448
436
|
|
|
@@ -450,11 +438,10 @@ export const GridMixin = (superClass) =>
|
|
|
450
438
|
* @param {!HTMLTableRowElement} row
|
|
451
439
|
* @param {!Array<!GridColumn>} columns
|
|
452
440
|
* @param {?string} section
|
|
453
|
-
* @param {boolean} isColumnRow
|
|
454
441
|
* @param {boolean} noNotify
|
|
455
442
|
* @private
|
|
456
443
|
*/
|
|
457
|
-
__initRow(row, columns, section = 'body',
|
|
444
|
+
__initRow(row, columns, section = 'body', noNotify = false) {
|
|
458
445
|
const contentsFragment = document.createDocumentFragment();
|
|
459
446
|
|
|
460
447
|
iterateRowCells(row, (cell) => {
|
|
@@ -481,9 +468,6 @@ export const GridMixin = (superClass) =>
|
|
|
481
468
|
cell = column._cells.find((cell) => cell._vacant);
|
|
482
469
|
if (!cell) {
|
|
483
470
|
cell = this._createCell('td', column);
|
|
484
|
-
if (column._onCellKeyDown) {
|
|
485
|
-
cell.addEventListener('keydown', column._onCellKeyDown.bind(column));
|
|
486
|
-
}
|
|
487
471
|
column._cells.push(cell);
|
|
488
472
|
}
|
|
489
473
|
updatePart(cell, 'cell', true);
|
|
@@ -525,33 +509,6 @@ export const GridMixin = (superClass) =>
|
|
|
525
509
|
if (!noNotify) {
|
|
526
510
|
column._cells = [...column._cells];
|
|
527
511
|
}
|
|
528
|
-
} else {
|
|
529
|
-
// Header & footer
|
|
530
|
-
const tagName = section === 'header' ? 'th' : 'td';
|
|
531
|
-
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
532
|
-
cell = column[`_${section}Cell`];
|
|
533
|
-
if (!cell) {
|
|
534
|
-
cell = this._createCell(tagName);
|
|
535
|
-
if (column._onCellKeyDown) {
|
|
536
|
-
cell.addEventListener('keydown', column._onCellKeyDown.bind(column));
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
cell._column = column;
|
|
540
|
-
row.appendChild(cell);
|
|
541
|
-
column[`_${section}Cell`] = cell;
|
|
542
|
-
} else {
|
|
543
|
-
if (!column._emptyCells) {
|
|
544
|
-
column._emptyCells = [];
|
|
545
|
-
}
|
|
546
|
-
cell = column._emptyCells.find((cell) => cell._vacant) || this._createCell(tagName);
|
|
547
|
-
cell._column = column;
|
|
548
|
-
row.appendChild(cell);
|
|
549
|
-
if (column._emptyCells.indexOf(cell) === -1) {
|
|
550
|
-
column._emptyCells.push(cell);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
updatePart(cell, 'cell', true);
|
|
554
|
-
updatePart(cell, `${section}-cell`, true);
|
|
555
512
|
}
|
|
556
513
|
|
|
557
514
|
if (!cell._content.parentElement) {
|
|
@@ -561,10 +518,6 @@ export const GridMixin = (superClass) =>
|
|
|
561
518
|
cell._column = column;
|
|
562
519
|
});
|
|
563
520
|
|
|
564
|
-
if (section !== 'body') {
|
|
565
|
-
this.__debounceUpdateHeaderFooterRowVisibility(row);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
521
|
// Might be empty if only cache was used
|
|
569
522
|
this.appendChild(contentsFragment);
|
|
570
523
|
|
|
@@ -572,75 +525,6 @@ export const GridMixin = (superClass) =>
|
|
|
572
525
|
this._updateFirstAndLastColumnForRow(row);
|
|
573
526
|
}
|
|
574
527
|
|
|
575
|
-
/**
|
|
576
|
-
* @param {HTMLTableRowElement} row
|
|
577
|
-
* @protected
|
|
578
|
-
*/
|
|
579
|
-
__debounceUpdateHeaderFooterRowVisibility(row) {
|
|
580
|
-
row.__debounceUpdateHeaderFooterRowVisibility = Debouncer.debounce(
|
|
581
|
-
row.__debounceUpdateHeaderFooterRowVisibility,
|
|
582
|
-
microTask,
|
|
583
|
-
() => this.__updateHeaderFooterRowVisibility(row),
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* @param {HTMLTableRowElement} row
|
|
589
|
-
* @protected
|
|
590
|
-
*/
|
|
591
|
-
__updateHeaderFooterRowVisibility(row) {
|
|
592
|
-
if (!row) {
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
const visibleRowCells = Array.from(row.children).filter((cell) => {
|
|
597
|
-
const column = cell._column;
|
|
598
|
-
if (column._emptyCells && column._emptyCells.indexOf(cell) > -1) {
|
|
599
|
-
// The cell is an "empty cell" -> doesn't block hiding the row
|
|
600
|
-
return false;
|
|
601
|
-
}
|
|
602
|
-
if (row.parentElement === this.$.header) {
|
|
603
|
-
if (column.headerRenderer) {
|
|
604
|
-
// The cell is the header cell of a column that has a header renderer
|
|
605
|
-
// -> row should be visible
|
|
606
|
-
return true;
|
|
607
|
-
}
|
|
608
|
-
if (column.header === null) {
|
|
609
|
-
// The column header is explicilty set to null -> doesn't block hiding the row
|
|
610
|
-
return false;
|
|
611
|
-
}
|
|
612
|
-
if (column.path || column.header !== undefined) {
|
|
613
|
-
// The column has an explicit non-null header or a path that generates a header
|
|
614
|
-
// -> row should be visible
|
|
615
|
-
return true;
|
|
616
|
-
}
|
|
617
|
-
} else if (column.footerRenderer) {
|
|
618
|
-
// The cell is the footer cell of a column that has a footer renderer
|
|
619
|
-
// -> row should be visible
|
|
620
|
-
return true;
|
|
621
|
-
}
|
|
622
|
-
return false;
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
if (row.hidden !== !visibleRowCells.length) {
|
|
626
|
-
row.hidden = !visibleRowCells.length;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
if (row.parentElement === this.$.header) {
|
|
630
|
-
this.$.table.toggleAttribute('has-header', this.$.header.querySelector('tr:not([hidden])'));
|
|
631
|
-
this.__updateHeaderFooterRowParts('header');
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
if (row.parentElement === this.$.footer) {
|
|
635
|
-
this.$.table.toggleAttribute('has-footer', this.$.footer.querySelector('tr:not([hidden])'));
|
|
636
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Make sure the section has a tabbable element
|
|
640
|
-
this._resetKeyboardNavigation();
|
|
641
|
-
this.__a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
528
|
/** @private */
|
|
645
529
|
__updateVirtualizerElement(row, index) {
|
|
646
530
|
this._preventScrollerRotatingCellFocus(row, index);
|
|
@@ -693,46 +577,17 @@ export const GridMixin = (superClass) =>
|
|
|
693
577
|
*/
|
|
694
578
|
_renderColumnTree(columnTree) {
|
|
695
579
|
iterateChildren(this.$.items, (row) => {
|
|
696
|
-
this.__initRow(row, columnTree[columnTree.length - 1], 'body',
|
|
580
|
+
this.__initRow(row, columnTree[columnTree.length - 1], 'body', true);
|
|
697
581
|
this.__updateRow(row);
|
|
698
582
|
});
|
|
699
583
|
|
|
700
|
-
|
|
701
|
-
const headerRow = document.createElement('tr');
|
|
702
|
-
headerRow.setAttribute('role', 'row');
|
|
703
|
-
headerRow.setAttribute('tabindex', '-1');
|
|
704
|
-
updatePart(headerRow, 'row', true);
|
|
705
|
-
updatePart(headerRow, 'header-row', true);
|
|
706
|
-
this.$.header.appendChild(headerRow);
|
|
707
|
-
|
|
708
|
-
const footerRow = document.createElement('tr');
|
|
709
|
-
footerRow.setAttribute('role', 'row');
|
|
710
|
-
footerRow.setAttribute('tabindex', '-1');
|
|
711
|
-
updatePart(footerRow, 'row', true);
|
|
712
|
-
updatePart(footerRow, 'footer-row', true);
|
|
713
|
-
this.$.footer.appendChild(footerRow);
|
|
714
|
-
}
|
|
715
|
-
while (this.$.header.children.length > columnTree.length) {
|
|
716
|
-
this.$.header.removeChild(this.$.header.firstElementChild);
|
|
717
|
-
this.$.footer.removeChild(this.$.footer.firstElementChild);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
iterateChildren(this.$.header, (headerRow, index) => {
|
|
721
|
-
this.__initRow(headerRow, columnTree[index], 'header', index === columnTree.length - 1);
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
iterateChildren(this.$.footer, (footerRow, index) => {
|
|
725
|
-
this.__initRow(footerRow, columnTree[columnTree.length - 1 - index], 'footer', index === 0);
|
|
726
|
-
});
|
|
584
|
+
this.__renderHeaderFooter();
|
|
727
585
|
|
|
728
586
|
// Sizer rows
|
|
729
587
|
this.__initRow(this.$.sizer, columnTree[columnTree.length - 1]);
|
|
730
588
|
|
|
731
|
-
this.__updateHeaderFooterRowParts('header');
|
|
732
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
733
589
|
this._resizeHandler();
|
|
734
590
|
this._frozenCellsChanged();
|
|
735
|
-
this._updateFirstAndLastColumn();
|
|
736
591
|
this._resetKeyboardNavigation();
|
|
737
592
|
this.__a11yUpdateHeaderRows();
|
|
738
593
|
this.__a11yUpdateFooterRows();
|
|
@@ -740,20 +595,6 @@ export const GridMixin = (superClass) =>
|
|
|
740
595
|
this.__updateHeaderAndFooter();
|
|
741
596
|
}
|
|
742
597
|
|
|
743
|
-
/** @private */
|
|
744
|
-
__updateHeaderFooterRowParts(section) {
|
|
745
|
-
const visibleRows = [...this.$[section].querySelectorAll('tr:not([hidden])')];
|
|
746
|
-
[...this.$[section].children].forEach((row) => {
|
|
747
|
-
updatePart(row, `first-${section}-row`, row === visibleRows.at(0));
|
|
748
|
-
updatePart(row, `last-${section}-row`, row === visibleRows.at(-1));
|
|
749
|
-
|
|
750
|
-
getBodyRowCells(row).forEach((cell) => {
|
|
751
|
-
updatePart(cell, `first-${section}-row-cell`, row === visibleRows.at(0));
|
|
752
|
-
updatePart(cell, `last-${section}-row-cell`, row === visibleRows.at(-1));
|
|
753
|
-
});
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
|
|
757
598
|
/**
|
|
758
599
|
* @param {!HTMLElement} row
|
|
759
600
|
* @param {boolean} loading
|
|
@@ -180,7 +180,7 @@ export const RowDetailsMixin = (superClass) =>
|
|
|
180
180
|
* @protected
|
|
181
181
|
*/
|
|
182
182
|
_isDetailsOpened(item) {
|
|
183
|
-
return this.__detailsOpenedKeys
|
|
183
|
+
return this.__detailsOpenedKeys?.has(this.getItemId(item));
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/** @private */
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { microTask, timeOut } from '@vaadin/component-base/src/async.js';
|
|
7
7
|
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
8
8
|
import { getNormalizedScrollLeft } from '@vaadin/component-base/src/dir-utils.js';
|
|
9
|
+
import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
9
10
|
import { OverflowController } from '@vaadin/component-base/src/overflow-controller.js';
|
|
10
11
|
|
|
11
12
|
const timeouts = {
|
|
@@ -135,12 +136,6 @@ export const ScrollMixin = (superClass) =>
|
|
|
135
136
|
|
|
136
137
|
this.__scrollIntoViewport(scrollTarget);
|
|
137
138
|
}
|
|
138
|
-
|
|
139
|
-
if (!this.$.table.contains(e.relatedTarget)) {
|
|
140
|
-
// Virtualizer can't catch the event because if orginates from the light DOM.
|
|
141
|
-
// Dispatch a virtualizer-element-focused event for virtualizer to catch.
|
|
142
|
-
this.$.table.dispatchEvent(new CustomEvent('virtualizer-element-focused', { detail: { element: row } }));
|
|
143
|
-
}
|
|
144
139
|
}
|
|
145
140
|
});
|
|
146
141
|
|
|
@@ -377,11 +372,7 @@ export const ScrollMixin = (superClass) =>
|
|
|
377
372
|
|
|
378
373
|
/** @private */
|
|
379
374
|
__columnRenderingChanged(_columnTree, columnRendering) {
|
|
380
|
-
|
|
381
|
-
this.$.scroller.removeAttribute('column-rendering');
|
|
382
|
-
} else {
|
|
383
|
-
this.$.scroller.setAttribute('column-rendering', columnRendering);
|
|
384
|
-
}
|
|
375
|
+
setOrRemoveAttribute(this.$.scroller, 'column-rendering', columnRendering !== 'eager' && columnRendering);
|
|
385
376
|
|
|
386
377
|
this.__updateColumnsBodyContentHidden();
|
|
387
378
|
}
|
|
@@ -107,12 +107,12 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
107
107
|
|
|
108
108
|
constructor() {
|
|
109
109
|
super();
|
|
110
|
-
this.
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
110
|
+
this.__onCellContentTrack = this.__onCellContentTrack.bind(this);
|
|
111
|
+
this.__onCellContentClick = this.__onCellContentClick.bind(this);
|
|
112
|
+
this.__onCellContentMouseDown = this.__onCellContentMouseDown.bind(this);
|
|
113
113
|
this.__onGridInteraction = this.__onGridInteraction.bind(this);
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
114
|
+
this.__onGridActiveItemChanged = this.__onGridActiveItemChanged.bind(this);
|
|
115
|
+
this.__onGridTouchStart = this.__onGridTouchStart.bind(this);
|
|
116
116
|
this.__onSelectRowCheckboxChange = this.__onSelectRowCheckboxChange.bind(this);
|
|
117
117
|
this.__onSelectAllCheckboxChange = this.__onSelectAllCheckboxChange.bind(this);
|
|
118
118
|
}
|
|
@@ -124,8 +124,8 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
124
124
|
this._grid.addEventListener('keyup', this.__onGridInteraction);
|
|
125
125
|
this._grid.addEventListener('keydown', this.__onGridInteraction, { capture: true });
|
|
126
126
|
this._grid.addEventListener('mousedown', this.__onGridInteraction);
|
|
127
|
-
this._grid.addEventListener('active-item-changed', this.
|
|
128
|
-
this._grid.addEventListener('touchstart', this.
|
|
127
|
+
this._grid.addEventListener('active-item-changed', this.__onGridActiveItemChanged);
|
|
128
|
+
this._grid.addEventListener('touchstart', this.__onGridTouchStart);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -136,8 +136,8 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
136
136
|
this._grid.removeEventListener('keyup', this.__onGridInteraction);
|
|
137
137
|
this._grid.removeEventListener('keydown', this.__onGridInteraction, { capture: true });
|
|
138
138
|
this._grid.removeEventListener('mousedown', this.__onGridInteraction);
|
|
139
|
-
this._grid.removeEventListener('active-item-changed', this.
|
|
140
|
-
this._grid.removeEventListener('touchstart', this.
|
|
139
|
+
this._grid.removeEventListener('active-item-changed', this.__onGridActiveItemChanged);
|
|
140
|
+
this._grid.removeEventListener('touchstart', this.__onGridTouchStart);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -150,16 +150,33 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
150
150
|
let checkbox = root.firstElementChild;
|
|
151
151
|
if (!checkbox) {
|
|
152
152
|
checkbox = document.createElement('vaadin-checkbox');
|
|
153
|
-
checkbox.accessibleName = 'Select All';
|
|
154
153
|
checkbox.classList.add('vaadin-grid-select-all-checkbox');
|
|
155
154
|
checkbox.addEventListener('change', this.__onSelectAllCheckboxChange);
|
|
156
155
|
root.appendChild(checkbox);
|
|
157
156
|
}
|
|
158
157
|
|
|
158
|
+
const { selectAll, selectAllUnavailable } = this._grid.__effectiveI18n;
|
|
159
|
+
checkbox.accessibleName = selectAll;
|
|
160
|
+
|
|
159
161
|
const checked = this.__isChecked(this.selectAll, this._indeterminate);
|
|
160
162
|
checkbox.checked = checked;
|
|
161
163
|
checkbox.indeterminate = this._indeterminate;
|
|
162
164
|
checkbox.style.visibility = this._selectAllHidden ? 'hidden' : '';
|
|
165
|
+
|
|
166
|
+
// When the Select All checkbox is hidden, the header cell would otherwise
|
|
167
|
+
// be announced as blank. Render a screen-reader-only label into the cell
|
|
168
|
+
// so that screen readers announce the `selectAllUnavailable` text.
|
|
169
|
+
let label = this._headerCell.querySelector(':scope > .sr-only');
|
|
170
|
+
if (this._selectAllHidden && selectAllUnavailable) {
|
|
171
|
+
if (!label) {
|
|
172
|
+
label = document.createElement('span');
|
|
173
|
+
label.classList.add('sr-only');
|
|
174
|
+
this._headerCell.appendChild(label);
|
|
175
|
+
}
|
|
176
|
+
label.textContent = selectAllUnavailable;
|
|
177
|
+
} else {
|
|
178
|
+
label?.remove();
|
|
179
|
+
}
|
|
163
180
|
}
|
|
164
181
|
|
|
165
182
|
/**
|
|
@@ -171,15 +188,15 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
171
188
|
let checkbox = root.firstElementChild;
|
|
172
189
|
if (!checkbox) {
|
|
173
190
|
checkbox = document.createElement('vaadin-checkbox');
|
|
174
|
-
checkbox.accessibleName = 'Select Row';
|
|
175
191
|
checkbox.addEventListener('change', this.__onSelectRowCheckboxChange);
|
|
176
192
|
root.appendChild(checkbox);
|
|
177
|
-
addListener(root, 'track', this.
|
|
193
|
+
addListener(root, 'track', this.__onCellContentTrack);
|
|
178
194
|
setTouchAction(root, 'pinch-zoom');
|
|
179
|
-
root.addEventListener('mousedown', this.
|
|
180
|
-
root.addEventListener('click', this.
|
|
195
|
+
root.addEventListener('mousedown', this.__onCellContentMouseDown);
|
|
196
|
+
root.addEventListener('click', this.__onCellContentClick);
|
|
181
197
|
}
|
|
182
198
|
|
|
199
|
+
this.__updateSelectRowAccessibleName(checkbox, root);
|
|
183
200
|
checkbox.__item = item;
|
|
184
201
|
checkbox.checked = selected;
|
|
185
202
|
|
|
@@ -190,6 +207,34 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
190
207
|
checkbox.style.visibility = isHidden ? 'hidden' : '';
|
|
191
208
|
}
|
|
192
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Sets the Select Row checkbox accessible name based on the grid i18n.
|
|
212
|
+
* The `{rowHeader}` placeholder is replaced with the row header cell text content
|
|
213
|
+
* or row index if there is no row header column or it's cell is empty.
|
|
214
|
+
*
|
|
215
|
+
* @private
|
|
216
|
+
*/
|
|
217
|
+
__updateSelectRowAccessibleName(checkbox, root) {
|
|
218
|
+
const ariaLabel = this._grid?.__effectiveI18n?.selectRow;
|
|
219
|
+
if (!ariaLabel || !ariaLabel.includes('{rowHeader}')) {
|
|
220
|
+
checkbox.accessibleName = ariaLabel;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Defer reading the row header cell text until all cell renderers of
|
|
225
|
+
// the row have run in the current task, as the selection column may
|
|
226
|
+
// render before the row header column.
|
|
227
|
+
queueMicrotask(() => {
|
|
228
|
+
const row = root.assignedSlot?.parentElement?.__parentRow;
|
|
229
|
+
if (!checkbox.isConnected || !row) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const rowHeaderCell = row.__cells.find((cell) => cell._column?.rowHeader);
|
|
233
|
+
const value = rowHeaderCell?._content?.textContent.trim() || String(row.index + 1);
|
|
234
|
+
checkbox.accessibleName = ariaLabel.replace('{rowHeader}', value);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
193
238
|
/**
|
|
194
239
|
* Updates the select all state when the Select All checkbox is switched.
|
|
195
240
|
* The listener handles only user-fired events.
|
|
@@ -225,7 +270,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
225
270
|
}
|
|
226
271
|
|
|
227
272
|
/** @private */
|
|
228
|
-
|
|
273
|
+
__onCellContentTrack(event) {
|
|
229
274
|
if (!this.dragSelect) {
|
|
230
275
|
return;
|
|
231
276
|
}
|
|
@@ -261,7 +306,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
261
306
|
}
|
|
262
307
|
|
|
263
308
|
/** @private */
|
|
264
|
-
|
|
309
|
+
__onCellContentMouseDown(e) {
|
|
265
310
|
if (this.dragSelect) {
|
|
266
311
|
// Prevent text selection when starting to drag
|
|
267
312
|
e.preventDefault();
|
|
@@ -269,7 +314,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
269
314
|
}
|
|
270
315
|
|
|
271
316
|
/** @private */
|
|
272
|
-
|
|
317
|
+
__onGridTouchStart(e) {
|
|
273
318
|
if (e.touches.length > 1) {
|
|
274
319
|
this.__multiTouchActive = true;
|
|
275
320
|
// Cancel in-progress drag-select on multi-touch (e.g. pinch-zoom)
|
|
@@ -281,7 +326,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
281
326
|
}
|
|
282
327
|
|
|
283
328
|
/** @private */
|
|
284
|
-
|
|
329
|
+
__onCellContentClick(e) {
|
|
285
330
|
if (this.__dragStartIndex !== undefined) {
|
|
286
331
|
// Stop the click event if drag was enabled. This click event should
|
|
287
332
|
// only occur if drag started and stopped on the same item. In that case
|
|
@@ -293,7 +338,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
293
338
|
}
|
|
294
339
|
|
|
295
340
|
/** @private */
|
|
296
|
-
|
|
341
|
+
__onCellKeyDown(e) {
|
|
297
342
|
const target = e.composedPath()[0];
|
|
298
343
|
// Toggle on Space without having to enter interaction mode first
|
|
299
344
|
if (e.keyCode !== 32) {
|
|
@@ -312,7 +357,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
312
357
|
}
|
|
313
358
|
|
|
314
359
|
/** @private */
|
|
315
|
-
|
|
360
|
+
__onGridActiveItemChanged(e) {
|
|
316
361
|
const activeItem = e.detail.value;
|
|
317
362
|
if (this.autoSelect) {
|
|
318
363
|
const item = activeItem || this.__previousActiveItem;
|
|
@@ -338,7 +383,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
338
383
|
|
|
339
384
|
// Get the index of the row being hovered over or the first/last
|
|
340
385
|
// visible row if hovering outside the grid
|
|
341
|
-
let hoveredIndex = hoveredRow
|
|
386
|
+
let hoveredIndex = hoveredRow?.index;
|
|
342
387
|
const scrollableArea = this.__getScrollableArea();
|
|
343
388
|
if (this.__dragCurrentY < scrollableArea.top) {
|
|
344
389
|
hoveredIndex = this._grid._firstVisibleIndex;
|