@vaadin/grid 25.3.0-alpha1 → 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 +7 -0
- 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 +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-helpers.js +13 -0
- package/src/vaadin-grid-keyboard-navigation-mixin.js +3 -7
- package/src/vaadin-grid-mixin.js +70 -225
- 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, 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
|
),
|
|
@@ -149,13 +152,22 @@ export const GridMixin = (superClass) =>
|
|
|
149
152
|
/** @private */
|
|
150
153
|
get _firstVisibleIndex() {
|
|
151
154
|
const firstVisibleItem = this.__getFirstVisibleItem();
|
|
152
|
-
return firstVisibleItem
|
|
155
|
+
return firstVisibleItem?.index;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
/** @private */
|
|
156
159
|
get _lastVisibleIndex() {
|
|
157
160
|
const lastVisibleItem = this.__getLastVisibleItem();
|
|
158
|
-
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);
|
|
159
171
|
}
|
|
160
172
|
|
|
161
173
|
/** @protected */
|
|
@@ -204,13 +216,7 @@ export const GridMixin = (superClass) =>
|
|
|
204
216
|
|
|
205
217
|
/** @protected */
|
|
206
218
|
_getRowContainingNode(node) {
|
|
207
|
-
|
|
208
|
-
if (!content) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const cell = content.assignedSlot.parentElement;
|
|
213
|
-
return cell.parentElement;
|
|
219
|
+
return getClosestCell(node)?.parentElement;
|
|
214
220
|
}
|
|
215
221
|
|
|
216
222
|
/** @protected */
|
|
@@ -292,9 +298,7 @@ export const GridMixin = (superClass) =>
|
|
|
292
298
|
__focusBodyCell({ item, column }) {
|
|
293
299
|
const row = this._getRenderedRows().find((row) => row._item === item);
|
|
294
300
|
const cell = row && [...row.children].find((cell) => cell._column === column);
|
|
295
|
-
|
|
296
|
-
cell.focus();
|
|
297
|
-
}
|
|
301
|
+
cell?.focus();
|
|
298
302
|
}
|
|
299
303
|
|
|
300
304
|
/** @protected */
|
|
@@ -341,7 +345,7 @@ export const GridMixin = (superClass) =>
|
|
|
341
345
|
updatePart(row, 'row', true);
|
|
342
346
|
updatePart(row, 'body-row', true);
|
|
343
347
|
if (this._columnTree) {
|
|
344
|
-
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body',
|
|
348
|
+
this.__initRow(row, this._columnTree[this._columnTree.length - 1], 'body', true);
|
|
345
349
|
}
|
|
346
350
|
rows.push(row);
|
|
347
351
|
}
|
|
@@ -357,6 +361,36 @@ export const GridMixin = (superClass) =>
|
|
|
357
361
|
return rows;
|
|
358
362
|
}
|
|
359
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
|
+
|
|
360
394
|
/** @private */
|
|
361
395
|
_createCell(tagName, column) {
|
|
362
396
|
const contentId = (this._contentIndex = this._contentIndex + 1 || 0);
|
|
@@ -369,22 +403,10 @@ export const GridMixin = (superClass) =>
|
|
|
369
403
|
cell.id = slotName.replace('-content-', '-');
|
|
370
404
|
cell.setAttribute('role', tagName === 'td' ? 'gridcell' : 'columnheader');
|
|
371
405
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
this._showTooltip(event);
|
|
377
|
-
}
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
cell.addEventListener('mouseleave', () => {
|
|
381
|
-
this._hideTooltip();
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
cell.addEventListener('mousedown', () => {
|
|
385
|
-
this._hideTooltip(true);
|
|
386
|
-
});
|
|
387
|
-
}
|
|
406
|
+
cell.addEventListener('mouseenter', this.__onCellMouseEnter);
|
|
407
|
+
cell.addEventListener('mouseleave', this.__onCellMouseLeave);
|
|
408
|
+
cell.addEventListener('mousedown', this.__onCellMouseDown);
|
|
409
|
+
cell.addEventListener('keydown', this.__onCellKeyDown);
|
|
388
410
|
|
|
389
411
|
const slot = document.createElement('slot');
|
|
390
412
|
slot.setAttribute('name', slotName);
|
|
@@ -409,34 +431,6 @@ export const GridMixin = (superClass) =>
|
|
|
409
431
|
|
|
410
432
|
cell._content = cellContent;
|
|
411
433
|
|
|
412
|
-
// With native Shadow DOM, mousedown on slotted element does not focus
|
|
413
|
-
// focusable slot wrapper, that is why cells are not focused with
|
|
414
|
-
// mousedown. Workaround: listen for mousedown and focus manually.
|
|
415
|
-
cellContent.addEventListener('mousedown', () => {
|
|
416
|
-
if (isChrome) {
|
|
417
|
-
// Chrome bug: focusing before mouseup prevents text selection, see http://crbug.com/771903
|
|
418
|
-
const mouseUpListener = (event) => {
|
|
419
|
-
// If focus is on element within the cell content - respect it, do not change
|
|
420
|
-
const contentContainsFocusedElement = cellContent.contains(this.getRootNode().activeElement);
|
|
421
|
-
// Only focus if mouse is released on cell content itself
|
|
422
|
-
const mouseUpWithinCell = event.composedPath().includes(cellContent);
|
|
423
|
-
if (!contentContainsFocusedElement && mouseUpWithinCell) {
|
|
424
|
-
cell.focus({ preventScroll: true });
|
|
425
|
-
}
|
|
426
|
-
document.removeEventListener('mouseup', mouseUpListener, true);
|
|
427
|
-
};
|
|
428
|
-
document.addEventListener('mouseup', mouseUpListener, true);
|
|
429
|
-
} else {
|
|
430
|
-
// Focus on mouseup, on the other hand, removes selection on Safari.
|
|
431
|
-
// Watch out sync focus removal issue, only async focus works here.
|
|
432
|
-
setTimeout(() => {
|
|
433
|
-
if (!cellContent.contains(this.getRootNode().activeElement)) {
|
|
434
|
-
cell.focus({ preventScroll: true });
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
|
|
440
434
|
return cell;
|
|
441
435
|
}
|
|
442
436
|
|
|
@@ -444,11 +438,10 @@ export const GridMixin = (superClass) =>
|
|
|
444
438
|
* @param {!HTMLTableRowElement} row
|
|
445
439
|
* @param {!Array<!GridColumn>} columns
|
|
446
440
|
* @param {?string} section
|
|
447
|
-
* @param {boolean} isColumnRow
|
|
448
441
|
* @param {boolean} noNotify
|
|
449
442
|
* @private
|
|
450
443
|
*/
|
|
451
|
-
__initRow(row, columns, section = 'body',
|
|
444
|
+
__initRow(row, columns, section = 'body', noNotify = false) {
|
|
452
445
|
const contentsFragment = document.createDocumentFragment();
|
|
453
446
|
|
|
454
447
|
iterateRowCells(row, (cell) => {
|
|
@@ -475,9 +468,6 @@ export const GridMixin = (superClass) =>
|
|
|
475
468
|
cell = column._cells.find((cell) => cell._vacant);
|
|
476
469
|
if (!cell) {
|
|
477
470
|
cell = this._createCell('td', column);
|
|
478
|
-
if (column._onCellKeyDown) {
|
|
479
|
-
cell.addEventListener('keydown', column._onCellKeyDown.bind(column));
|
|
480
|
-
}
|
|
481
471
|
column._cells.push(cell);
|
|
482
472
|
}
|
|
483
473
|
updatePart(cell, 'cell', true);
|
|
@@ -519,33 +509,6 @@ export const GridMixin = (superClass) =>
|
|
|
519
509
|
if (!noNotify) {
|
|
520
510
|
column._cells = [...column._cells];
|
|
521
511
|
}
|
|
522
|
-
} else {
|
|
523
|
-
// Header & footer
|
|
524
|
-
const tagName = section === 'header' ? 'th' : 'td';
|
|
525
|
-
if (isColumnRow || column.localName === 'vaadin-grid-column-group') {
|
|
526
|
-
cell = column[`_${section}Cell`];
|
|
527
|
-
if (!cell) {
|
|
528
|
-
cell = this._createCell(tagName);
|
|
529
|
-
if (column._onCellKeyDown) {
|
|
530
|
-
cell.addEventListener('keydown', column._onCellKeyDown.bind(column));
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
cell._column = column;
|
|
534
|
-
row.appendChild(cell);
|
|
535
|
-
column[`_${section}Cell`] = cell;
|
|
536
|
-
} else {
|
|
537
|
-
if (!column._emptyCells) {
|
|
538
|
-
column._emptyCells = [];
|
|
539
|
-
}
|
|
540
|
-
cell = column._emptyCells.find((cell) => cell._vacant) || this._createCell(tagName);
|
|
541
|
-
cell._column = column;
|
|
542
|
-
row.appendChild(cell);
|
|
543
|
-
if (column._emptyCells.indexOf(cell) === -1) {
|
|
544
|
-
column._emptyCells.push(cell);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
updatePart(cell, 'cell', true);
|
|
548
|
-
updatePart(cell, `${section}-cell`, true);
|
|
549
512
|
}
|
|
550
513
|
|
|
551
514
|
if (!cell._content.parentElement) {
|
|
@@ -555,10 +518,6 @@ export const GridMixin = (superClass) =>
|
|
|
555
518
|
cell._column = column;
|
|
556
519
|
});
|
|
557
520
|
|
|
558
|
-
if (section !== 'body') {
|
|
559
|
-
this.__debounceUpdateHeaderFooterRowVisibility(row);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
521
|
// Might be empty if only cache was used
|
|
563
522
|
this.appendChild(contentsFragment);
|
|
564
523
|
|
|
@@ -566,75 +525,6 @@ export const GridMixin = (superClass) =>
|
|
|
566
525
|
this._updateFirstAndLastColumnForRow(row);
|
|
567
526
|
}
|
|
568
527
|
|
|
569
|
-
/**
|
|
570
|
-
* @param {HTMLTableRowElement} row
|
|
571
|
-
* @protected
|
|
572
|
-
*/
|
|
573
|
-
__debounceUpdateHeaderFooterRowVisibility(row) {
|
|
574
|
-
row.__debounceUpdateHeaderFooterRowVisibility = Debouncer.debounce(
|
|
575
|
-
row.__debounceUpdateHeaderFooterRowVisibility,
|
|
576
|
-
microTask,
|
|
577
|
-
() => this.__updateHeaderFooterRowVisibility(row),
|
|
578
|
-
);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* @param {HTMLTableRowElement} row
|
|
583
|
-
* @protected
|
|
584
|
-
*/
|
|
585
|
-
__updateHeaderFooterRowVisibility(row) {
|
|
586
|
-
if (!row) {
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
const visibleRowCells = Array.from(row.children).filter((cell) => {
|
|
591
|
-
const column = cell._column;
|
|
592
|
-
if (column._emptyCells && column._emptyCells.indexOf(cell) > -1) {
|
|
593
|
-
// The cell is an "empty cell" -> doesn't block hiding the row
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
if (row.parentElement === this.$.header) {
|
|
597
|
-
if (column.headerRenderer) {
|
|
598
|
-
// The cell is the header cell of a column that has a header renderer
|
|
599
|
-
// -> row should be visible
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
if (column.header === null) {
|
|
603
|
-
// The column header is explicilty set to null -> doesn't block hiding the row
|
|
604
|
-
return false;
|
|
605
|
-
}
|
|
606
|
-
if (column.path || column.header !== undefined) {
|
|
607
|
-
// The column has an explicit non-null header or a path that generates a header
|
|
608
|
-
// -> row should be visible
|
|
609
|
-
return true;
|
|
610
|
-
}
|
|
611
|
-
} else if (column.footerRenderer) {
|
|
612
|
-
// The cell is the footer cell of a column that has a footer renderer
|
|
613
|
-
// -> row should be visible
|
|
614
|
-
return true;
|
|
615
|
-
}
|
|
616
|
-
return false;
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
if (row.hidden !== !visibleRowCells.length) {
|
|
620
|
-
row.hidden = !visibleRowCells.length;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
if (row.parentElement === this.$.header) {
|
|
624
|
-
this.$.table.toggleAttribute('has-header', this.$.header.querySelector('tr:not([hidden])'));
|
|
625
|
-
this.__updateHeaderFooterRowParts('header');
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
if (row.parentElement === this.$.footer) {
|
|
629
|
-
this.$.table.toggleAttribute('has-footer', this.$.footer.querySelector('tr:not([hidden])'));
|
|
630
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// Make sure the section has a tabbable element
|
|
634
|
-
this._resetKeyboardNavigation();
|
|
635
|
-
this.__a11yUpdateGridSize(this.size, this._columnTree, this.__emptyState);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
528
|
/** @private */
|
|
639
529
|
__updateVirtualizerElement(row, index) {
|
|
640
530
|
this._preventScrollerRotatingCellFocus(row, index);
|
|
@@ -687,46 +577,17 @@ export const GridMixin = (superClass) =>
|
|
|
687
577
|
*/
|
|
688
578
|
_renderColumnTree(columnTree) {
|
|
689
579
|
iterateChildren(this.$.items, (row) => {
|
|
690
|
-
this.__initRow(row, columnTree[columnTree.length - 1], 'body',
|
|
580
|
+
this.__initRow(row, columnTree[columnTree.length - 1], 'body', true);
|
|
691
581
|
this.__updateRow(row);
|
|
692
582
|
});
|
|
693
583
|
|
|
694
|
-
|
|
695
|
-
const headerRow = document.createElement('tr');
|
|
696
|
-
headerRow.setAttribute('role', 'row');
|
|
697
|
-
headerRow.setAttribute('tabindex', '-1');
|
|
698
|
-
updatePart(headerRow, 'row', true);
|
|
699
|
-
updatePart(headerRow, 'header-row', true);
|
|
700
|
-
this.$.header.appendChild(headerRow);
|
|
701
|
-
|
|
702
|
-
const footerRow = document.createElement('tr');
|
|
703
|
-
footerRow.setAttribute('role', 'row');
|
|
704
|
-
footerRow.setAttribute('tabindex', '-1');
|
|
705
|
-
updatePart(footerRow, 'row', true);
|
|
706
|
-
updatePart(footerRow, 'footer-row', true);
|
|
707
|
-
this.$.footer.appendChild(footerRow);
|
|
708
|
-
}
|
|
709
|
-
while (this.$.header.children.length > columnTree.length) {
|
|
710
|
-
this.$.header.removeChild(this.$.header.firstElementChild);
|
|
711
|
-
this.$.footer.removeChild(this.$.footer.firstElementChild);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
iterateChildren(this.$.header, (headerRow, index) => {
|
|
715
|
-
this.__initRow(headerRow, columnTree[index], 'header', index === columnTree.length - 1);
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
iterateChildren(this.$.footer, (footerRow, index) => {
|
|
719
|
-
this.__initRow(footerRow, columnTree[columnTree.length - 1 - index], 'footer', index === 0);
|
|
720
|
-
});
|
|
584
|
+
this.__renderHeaderFooter();
|
|
721
585
|
|
|
722
586
|
// Sizer rows
|
|
723
587
|
this.__initRow(this.$.sizer, columnTree[columnTree.length - 1]);
|
|
724
588
|
|
|
725
|
-
this.__updateHeaderFooterRowParts('header');
|
|
726
|
-
this.__updateHeaderFooterRowParts('footer');
|
|
727
589
|
this._resizeHandler();
|
|
728
590
|
this._frozenCellsChanged();
|
|
729
|
-
this._updateFirstAndLastColumn();
|
|
730
591
|
this._resetKeyboardNavigation();
|
|
731
592
|
this.__a11yUpdateHeaderRows();
|
|
732
593
|
this.__a11yUpdateFooterRows();
|
|
@@ -734,20 +595,6 @@ export const GridMixin = (superClass) =>
|
|
|
734
595
|
this.__updateHeaderAndFooter();
|
|
735
596
|
}
|
|
736
597
|
|
|
737
|
-
/** @private */
|
|
738
|
-
__updateHeaderFooterRowParts(section) {
|
|
739
|
-
const visibleRows = [...this.$[section].querySelectorAll('tr:not([hidden])')];
|
|
740
|
-
[...this.$[section].children].forEach((row) => {
|
|
741
|
-
updatePart(row, `first-${section}-row`, row === visibleRows.at(0));
|
|
742
|
-
updatePart(row, `last-${section}-row`, row === visibleRows.at(-1));
|
|
743
|
-
|
|
744
|
-
getBodyRowCells(row).forEach((cell) => {
|
|
745
|
-
updatePart(cell, `first-${section}-row-cell`, row === visibleRows.at(0));
|
|
746
|
-
updatePart(cell, `last-${section}-row-cell`, row === visibleRows.at(-1));
|
|
747
|
-
});
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
|
|
751
598
|
/**
|
|
752
599
|
* @param {!HTMLElement} row
|
|
753
600
|
* @param {boolean} loading
|
|
@@ -925,9 +772,7 @@ export const GridMixin = (superClass) =>
|
|
|
925
772
|
|
|
926
773
|
/** @protected */
|
|
927
774
|
__updateVisibleRows(start, end) {
|
|
928
|
-
|
|
929
|
-
this.__virtualizer.update(start, end);
|
|
930
|
-
}
|
|
775
|
+
this.__virtualizer?.update(start, end);
|
|
931
776
|
}
|
|
932
777
|
|
|
933
778
|
/** @private */
|
|
@@ -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;
|