@vaadin/grid 25.3.0-dev.3a3c2d7d2a → 25.3.0-rc1
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 +194 -135
- package/package.json +12 -12
- package/src/directives/cell-content-directive.js +33 -0
- package/src/styles/vaadin-grid-base-styles.js +7 -0
- package/src/vaadin-grid-a11y-mixin.js +15 -28
- 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 -109
- 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 +2 -2
- package/src/vaadin-grid-drag-and-drop-mixin.js +14 -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-keyboard-navigation-mixin.js +3 -7
- package/src/vaadin-grid-mixin.js +67 -229
- package/src/vaadin-grid-row-details-mixin.js +1 -1
- package/src/vaadin-grid-scroll-mixin.js +7 -32
- package/src/vaadin-grid-selection-column-base-mixin.js +36 -20
- package/src/vaadin-grid-sort-column-mixin.js +11 -2
- package/src/vaadin-grid-sorter-mixin.js +3 -7
- package/src/vaadin-grid-sorter.js +1 -0
- package/src/vaadin-grid-tree-toggle.d.ts +5 -3
- package/src/vaadin-grid-tree-toggle.js +6 -3
- package/src/vaadin-grid.d.ts +33 -0
- package/src/vaadin-grid.js +30 -1
- package/web-types.json +22 -96
- package/web-types.lit.json +12 -12
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 {
|
|
8
|
-
import { isAndroid, isChrome, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
9
|
-
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
7
|
+
import { isAndroid, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.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
|
),
|
|
@@ -121,17 +124,6 @@ export const GridMixin = (superClass) =>
|
|
|
121
124
|
value: false,
|
|
122
125
|
},
|
|
123
126
|
|
|
124
|
-
/**
|
|
125
|
-
* An internal property that is mainly used by `vaadin-template-renderer`
|
|
126
|
-
* to identify grid elements.
|
|
127
|
-
*
|
|
128
|
-
* @private
|
|
129
|
-
*/
|
|
130
|
-
__gridElement: {
|
|
131
|
-
type: Boolean,
|
|
132
|
-
value: true,
|
|
133
|
-
},
|
|
134
|
-
|
|
135
127
|
/** @private */
|
|
136
128
|
__hasEmptyStateContent: {
|
|
137
129
|
type: Boolean,
|
|
@@ -149,13 +141,22 @@ export const GridMixin = (superClass) =>
|
|
|
149
141
|
/** @private */
|
|
150
142
|
get _firstVisibleIndex() {
|
|
151
143
|
const firstVisibleItem = this.__getFirstVisibleItem();
|
|
152
|
-
return firstVisibleItem
|
|
144
|
+
return firstVisibleItem?.index;
|
|
153
145
|
}
|
|
154
146
|
|
|
155
147
|
/** @private */
|
|
156
148
|
get _lastVisibleIndex() {
|
|
157
149
|
const lastVisibleItem = this.__getLastVisibleItem();
|
|
158
|
-
return lastVisibleItem
|
|
150
|
+
return lastVisibleItem?.index;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
constructor() {
|
|
154
|
+
super();
|
|
155
|
+
|
|
156
|
+
this.__onCellMouseEnter = this.__onCellMouseEnter.bind(this);
|
|
157
|
+
this.__onCellMouseLeave = this.__onCellMouseLeave.bind(this);
|
|
158
|
+
this.__onCellMouseDown = this.__onCellMouseDown.bind(this);
|
|
159
|
+
this.__onCellKeyDown = this.__onCellKeyDown.bind(this);
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
/** @protected */
|
|
@@ -286,9 +287,7 @@ export const GridMixin = (superClass) =>
|
|
|
286
287
|
__focusBodyCell({ item, column }) {
|
|
287
288
|
const row = this._getRenderedRows().find((row) => row._item === item);
|
|
288
289
|
const cell = row && [...row.children].find((cell) => cell._column === column);
|
|
289
|
-
|
|
290
|
-
cell.focus();
|
|
291
|
-
}
|
|
290
|
+
cell?.focus();
|
|
292
291
|
}
|
|
293
292
|
|
|
294
293
|
/** @protected */
|
|
@@ -335,7 +334,7 @@ export const GridMixin = (superClass) =>
|
|
|
335
334
|
updatePart(row, 'row', true);
|
|
336
335
|
updatePart(row, 'body-row', true);
|
|
337
336
|
if (this._columnTree) {
|
|
338
|
-
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body',
|
|
337
|
+
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body', true);
|
|
339
338
|
}
|
|
340
339
|
rows.push(row);
|
|
341
340
|
}
|
|
@@ -351,6 +350,36 @@ export const GridMixin = (superClass) =>
|
|
|
351
350
|
return rows;
|
|
352
351
|
}
|
|
353
352
|
|
|
353
|
+
/** @private */
|
|
354
|
+
__onCellKeyDown(event) {
|
|
355
|
+
const cell = event.currentTarget;
|
|
356
|
+
cell._column?.__onCellKeyDown?.(event);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** @private */
|
|
360
|
+
__onCellMouseEnter(event) {
|
|
361
|
+
// For now we only support tooltip on desktop
|
|
362
|
+
if (!isAndroid && !isIOS && !this.$.scroller.hasAttribute('scrolling')) {
|
|
363
|
+
this._showTooltip(event);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** @private */
|
|
368
|
+
__onCellMouseLeave() {
|
|
369
|
+
// For now we only support tooltip on desktop
|
|
370
|
+
if (!isAndroid && !isIOS) {
|
|
371
|
+
this._hideTooltip();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** @private */
|
|
376
|
+
__onCellMouseDown() {
|
|
377
|
+
// For now we only support tooltip on desktop
|
|
378
|
+
if (!isAndroid && !isIOS) {
|
|
379
|
+
this._hideTooltip(true);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
354
383
|
/** @private */
|
|
355
384
|
_createCell(tagName, column) {
|
|
356
385
|
const contentId = (this._contentIndex = this._contentIndex + 1 || 0);
|
|
@@ -363,22 +392,10 @@ export const GridMixin = (superClass) =>
|
|
|
363
392
|
cell.id = slotName.replace('-content-', '-');
|
|
364
393
|
cell.setAttribute('role', tagName === 'td' ? 'gridcell' : 'columnheader');
|
|
365
394
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this._showTooltip(event);
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
cell.addEventListener('mouseleave', () => {
|
|
375
|
-
this._hideTooltip();
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
cell.addEventListener('mousedown', () => {
|
|
379
|
-
this._hideTooltip(true);
|
|
380
|
-
});
|
|
381
|
-
}
|
|
395
|
+
cell.addEventListener('mouseenter', this.__onCellMouseEnter);
|
|
396
|
+
cell.addEventListener('mouseleave', this.__onCellMouseLeave);
|
|
397
|
+
cell.addEventListener('mousedown', this.__onCellMouseDown);
|
|
398
|
+
cell.addEventListener('keydown', this.__onCellKeyDown);
|
|
382
399
|
|
|
383
400
|
const slot = document.createElement('slot');
|
|
384
401
|
slot.setAttribute('name', slotName);
|
|
@@ -403,34 +420,6 @@ export const GridMixin = (superClass) =>
|
|
|
403
420
|
|
|
404
421
|
cell._content = cellContent;
|
|
405
422
|
|
|
406
|
-
// With native Shadow DOM, mousedown on slotted element does not focus
|
|
407
|
-
// focusable slot wrapper, that is why cells are not focused with
|
|
408
|
-
// mousedown. Workaround: listen for mousedown and focus manually.
|
|
409
|
-
cellContent.addEventListener('mousedown', () => {
|
|
410
|
-
if (isChrome) {
|
|
411
|
-
// Chrome bug: focusing before mouseup prevents text selection, see http://crbug.com/771903
|
|
412
|
-
const mouseUpListener = (event) => {
|
|
413
|
-
// If focus is on element within the cell content - respect it, do not change
|
|
414
|
-
const contentContainsFocusedElement = cellContent.contains(this.getRootNode().activeElement);
|
|
415
|
-
// Only focus if mouse is released on cell content itself
|
|
416
|
-
const mouseUpWithinCell = event.composedPath().includes(cellContent);
|
|
417
|
-
if (!contentContainsFocusedElement && mouseUpWithinCell) {
|
|
418
|
-
cell.focus({ preventScroll: true });
|
|
419
|
-
}
|
|
420
|
-
document.removeEventListener('mouseup', mouseUpListener, true);
|
|
421
|
-
};
|
|
422
|
-
document.addEventListener('mouseup', mouseUpListener, true);
|
|
423
|
-
} else {
|
|
424
|
-
// Focus on mouseup, on the other hand, removes selection on Safari.
|
|
425
|
-
// Watch out sync focus removal issue, only async focus works here.
|
|
426
|
-
setTimeout(() => {
|
|
427
|
-
if (!cellContent.contains(this.getRootNode().activeElement)) {
|
|
428
|
-
cell.focus({ preventScroll: true });
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
|
|
434
423
|
return cell;
|
|
435
424
|
}
|
|
436
425
|
|
|
@@ -438,11 +427,10 @@ export const GridMixin = (superClass) =>
|
|
|
438
427
|
* @param {!HTMLTableRowElement} row
|
|
439
428
|
* @param {!Array<!GridColumn>} columns
|
|
440
429
|
* @param {?string} section
|
|
441
|
-
* @param {boolean} isColumnRow
|
|
442
430
|
* @param {boolean} noNotify
|
|
443
431
|
* @private
|
|
444
432
|
*/
|
|
445
|
-
__initRow(row, columns, section = 'body',
|
|
433
|
+
__initRow(row, columns, section = 'body', noNotify = false) {
|
|
446
434
|
const contentsFragment = document.createDocumentFragment();
|
|
447
435
|
|
|
448
436
|
iterateRowCells(row, (cell) => {
|
|
@@ -469,9 +457,6 @@ export const GridMixin = (superClass) =>
|
|
|
469
457
|
cell = column._cells.find((cell) => cell._vacant);
|
|
470
458
|
if (!cell) {
|
|
471
459
|
cell = this._createCell('td', column);
|
|
472
|
-
if (column._onCellKeyDown) {
|
|
473
|
-
cell.addEventListener('keydown', column._onCellKeyDown.bind(column));
|
|
474
|
-
}
|
|
475
460
|
column._cells.push(cell);
|
|
476
461
|
}
|
|
477
462
|
updatePart(cell, 'cell', true);
|
|
@@ -513,33 +498,6 @@ export const GridMixin = (superClass) =>
|
|
|
513
498
|
if (!noNotify) {
|
|
514
499
|
column._cells = [...column._cells];
|
|
515
500
|
}
|
|
516
|
-
} else {
|
|
517
|
-
// Header & footer
|
|
518
|
-
const tagName = section === 'header' ? 'th' : 'td';
|
|
519
|
-
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
520
|
-
cell = column[`_${section}Cell`];
|
|
521
|
-
if (!cell) {
|
|
522
|
-
cell = this._createCell(tagName);
|
|
523
|
-
if (column._onCellKeyDown) {
|
|
524
|
-
cell.addEventListener('keydown', column._onCellKeyDown.bind(column));
|
|
525
|
-
}
|
|
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
501
|
}
|
|
544
502
|
|
|
545
503
|
if (!cell._content.parentElement) {
|
|
@@ -549,86 +507,12 @@ export const GridMixin = (superClass) =>
|
|
|
549
507
|
cell._column = column;
|
|
550
508
|
});
|
|
551
509
|
|
|
552
|
-
if (section !== 'body') {
|
|
553
|
-
this.__debounceUpdateHeaderFooterRowVisibility(row);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
510
|
// Might be empty if only cache was used
|
|
557
511
|
this.appendChild(contentsFragment);
|
|
558
512
|
|
|
559
|
-
this._frozenCellsChanged();
|
|
560
513
|
this._updateFirstAndLastColumnForRow(row);
|
|
561
514
|
}
|
|
562
515
|
|
|
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
516
|
/** @private */
|
|
633
517
|
__updateVirtualizerElement(row, index) {
|
|
634
518
|
this._preventScrollerRotatingCellFocus(row, index);
|
|
@@ -681,67 +565,21 @@ export const GridMixin = (superClass) =>
|
|
|
681
565
|
*/
|
|
682
566
|
_renderColumnTree(columnTree) {
|
|
683
567
|
iterateChildren(this.$.items, (row) => {
|
|
684
|
-
this.__initRow(row, columnTree[columnTree.length - 1], 'body',
|
|
568
|
+
this.__initRow(row, columnTree[columnTree.length - 1], 'body', true);
|
|
685
569
|
this.__updateRow(row);
|
|
686
570
|
});
|
|
687
571
|
|
|
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
|
-
});
|
|
572
|
+
this.__renderHeaderFooter();
|
|
715
573
|
|
|
716
574
|
// Sizer rows
|
|
717
575
|
this.__initRow(this.$.sizer, columnTree[columnTree.length - 1]);
|
|
718
576
|
|
|
719
|
-
this.__updateHeaderFooterRowParts('header');
|
|
720
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
721
577
|
this._resizeHandler();
|
|
722
|
-
this._frozenCellsChanged();
|
|
723
|
-
this._updateFirstAndLastColumn();
|
|
724
|
-
this._resetKeyboardNavigation();
|
|
725
578
|
this.__a11yUpdateHeaderRows();
|
|
726
579
|
this.__a11yUpdateFooterRows();
|
|
727
|
-
this.generateCellPartNames();
|
|
728
580
|
this.__updateHeaderAndFooter();
|
|
729
581
|
}
|
|
730
582
|
|
|
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
583
|
/**
|
|
746
584
|
* @param {!HTMLElement} row
|
|
747
585
|
* @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 = {
|
|
@@ -88,19 +89,6 @@ export const ScrollMixin = (superClass) =>
|
|
|
88
89
|
return this.$.table.scrollLeft;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
/** @private */
|
|
92
|
-
get _scrollTop() {
|
|
93
|
-
return this.$.table.scrollTop;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Override (from iron-scroll-target-behavior) to avoid document scroll
|
|
98
|
-
* @private
|
|
99
|
-
*/
|
|
100
|
-
set _scrollTop(top) {
|
|
101
|
-
this.$.table.scrollTop = top;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
92
|
/** @protected */
|
|
105
93
|
get _lazyColumns() {
|
|
106
94
|
return this.columnRendering === 'lazy';
|
|
@@ -123,24 +111,20 @@ export const ScrollMixin = (superClass) =>
|
|
|
123
111
|
// is inside the viewport. If the whole row fits into the viewport, then scroll
|
|
124
112
|
// the row into view. This ensures that labels, helper texts and other related
|
|
125
113
|
// elements of focusable elements within cells also become visible. When the row
|
|
126
|
-
// is larger than the viewport, scroll the
|
|
114
|
+
// is larger than the viewport, scroll the actually focused element into the viewport.
|
|
127
115
|
// This works better when focusing elements within cells, which could otherwise
|
|
128
116
|
// still be outside the viewport when scrolling to the top or bottom of the row.
|
|
117
|
+
// The composed path is used instead of the event target, which would be
|
|
118
|
+
// retargeted to the host element when focus lands inside a nested shadow root.
|
|
129
119
|
const tableHeight = this.$.table.clientHeight;
|
|
130
120
|
const headerHeight = this.$.header.clientHeight;
|
|
131
121
|
const footerHeight = this.$.footer.clientHeight;
|
|
132
122
|
const viewportHeight = tableHeight - headerHeight - footerHeight;
|
|
133
123
|
const isRowLargerThanViewport = row.clientHeight > viewportHeight;
|
|
134
|
-
const scrollTarget = isRowLargerThanViewport ?
|
|
124
|
+
const scrollTarget = isRowLargerThanViewport ? composedPath[0] : row;
|
|
135
125
|
|
|
136
126
|
this.__scrollIntoViewport(scrollTarget);
|
|
137
127
|
}
|
|
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
128
|
}
|
|
145
129
|
});
|
|
146
130
|
|
|
@@ -244,13 +228,8 @@ export const ScrollMixin = (superClass) =>
|
|
|
244
228
|
|
|
245
229
|
/** @private */
|
|
246
230
|
_scheduleScrolling() {
|
|
247
|
-
|
|
248
|
-
// Defer setting state attributes to avoid Edge hiccups
|
|
249
|
-
this._scrollingFrame = requestAnimationFrame(() => this.$.scroller.toggleAttribute('scrolling', true));
|
|
250
|
-
}
|
|
231
|
+
this.$.scroller.toggleAttribute('scrolling', true);
|
|
251
232
|
this._debounceScrolling = Debouncer.debounce(this._debounceScrolling, timeOut.after(timeouts.SCROLLING), () => {
|
|
252
|
-
cancelAnimationFrame(this._scrollingFrame);
|
|
253
|
-
delete this._scrollingFrame;
|
|
254
233
|
this.$.scroller.toggleAttribute('scrolling', false);
|
|
255
234
|
});
|
|
256
235
|
}
|
|
@@ -377,11 +356,7 @@ export const ScrollMixin = (superClass) =>
|
|
|
377
356
|
|
|
378
357
|
/** @private */
|
|
379
358
|
__columnRenderingChanged(_columnTree, columnRendering) {
|
|
380
|
-
|
|
381
|
-
this.$.scroller.removeAttribute('column-rendering');
|
|
382
|
-
} else {
|
|
383
|
-
this.$.scroller.setAttribute('column-rendering', columnRendering);
|
|
384
|
-
}
|
|
359
|
+
setOrRemoveAttribute(this.$.scroller, 'column-rendering', columnRendering !== 'eager' && columnRendering);
|
|
385
360
|
|
|
386
361
|
this.__updateColumnsBodyContentHidden();
|
|
387
362
|
}
|
|
@@ -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
|
|
|
@@ -155,12 +155,28 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
155
155
|
root.appendChild(checkbox);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
const { selectAll, selectAllUnavailable } = this._grid.__effectiveI18n;
|
|
159
|
+
checkbox.accessibleName = selectAll;
|
|
159
160
|
|
|
160
161
|
const checked = this.__isChecked(this.selectAll, this._indeterminate);
|
|
161
162
|
checkbox.checked = checked;
|
|
162
163
|
checkbox.indeterminate = this._indeterminate;
|
|
163
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
|
+
}
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
/**
|
|
@@ -174,10 +190,10 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
174
190
|
checkbox = document.createElement('vaadin-checkbox');
|
|
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
|
|
|
183
199
|
this.__updateSelectRowAccessibleName(checkbox, root);
|
|
@@ -254,7 +270,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
254
270
|
}
|
|
255
271
|
|
|
256
272
|
/** @private */
|
|
257
|
-
|
|
273
|
+
__onCellContentTrack(event) {
|
|
258
274
|
if (!this.dragSelect) {
|
|
259
275
|
return;
|
|
260
276
|
}
|
|
@@ -290,7 +306,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
290
306
|
}
|
|
291
307
|
|
|
292
308
|
/** @private */
|
|
293
|
-
|
|
309
|
+
__onCellContentMouseDown(e) {
|
|
294
310
|
if (this.dragSelect) {
|
|
295
311
|
// Prevent text selection when starting to drag
|
|
296
312
|
e.preventDefault();
|
|
@@ -298,7 +314,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
298
314
|
}
|
|
299
315
|
|
|
300
316
|
/** @private */
|
|
301
|
-
|
|
317
|
+
__onGridTouchStart(e) {
|
|
302
318
|
if (e.touches.length > 1) {
|
|
303
319
|
this.__multiTouchActive = true;
|
|
304
320
|
// Cancel in-progress drag-select on multi-touch (e.g. pinch-zoom)
|
|
@@ -310,7 +326,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
310
326
|
}
|
|
311
327
|
|
|
312
328
|
/** @private */
|
|
313
|
-
|
|
329
|
+
__onCellContentClick(e) {
|
|
314
330
|
if (this.__dragStartIndex !== undefined) {
|
|
315
331
|
// Stop the click event if drag was enabled. This click event should
|
|
316
332
|
// only occur if drag started and stopped on the same item. In that case
|
|
@@ -322,7 +338,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
322
338
|
}
|
|
323
339
|
|
|
324
340
|
/** @private */
|
|
325
|
-
|
|
341
|
+
__onCellKeyDown(e) {
|
|
326
342
|
const target = e.composedPath()[0];
|
|
327
343
|
// Toggle on Space without having to enter interaction mode first
|
|
328
344
|
if (e.keyCode !== 32) {
|
|
@@ -341,7 +357,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
341
357
|
}
|
|
342
358
|
|
|
343
359
|
/** @private */
|
|
344
|
-
|
|
360
|
+
__onGridActiveItemChanged(e) {
|
|
345
361
|
const activeItem = e.detail.value;
|
|
346
362
|
if (this.autoSelect) {
|
|
347
363
|
const item = activeItem || this.__previousActiveItem;
|
|
@@ -367,7 +383,7 @@ export const GridSelectionColumnBaseMixin = (superClass) =>
|
|
|
367
383
|
|
|
368
384
|
// Get the index of the row being hovered over or the first/last
|
|
369
385
|
// visible row if hovering outside the grid
|
|
370
|
-
let hoveredIndex = hoveredRow
|
|
386
|
+
let hoveredIndex = hoveredRow?.index;
|
|
371
387
|
const scrollableArea = this.__getScrollableArea();
|
|
372
388
|
if (this.__dragCurrentY < scrollableArea.top) {
|
|
373
389
|
hoveredIndex = this._grid._firstVisibleIndex;
|