@vaadin/grid 24.3.0-alpha1 → 24.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/package.json +18 -13
- package/src/vaadin-grid-a11y-mixin.js +1 -1
- package/src/vaadin-grid-active-item-mixin.js +1 -0
- package/src/vaadin-grid-array-data-provider-mixin.js +14 -17
- package/src/vaadin-grid-column-group-mixin.d.ts +20 -0
- package/src/vaadin-grid-column-group-mixin.js +364 -0
- package/src/vaadin-grid-column-group.d.ts +4 -14
- package/src/vaadin-grid-column-group.js +5 -355
- package/src/vaadin-grid-column-mixin.d.ts +170 -0
- package/src/vaadin-grid-column-mixin.js +958 -0
- package/src/vaadin-grid-column.d.ts +11 -138
- package/src/vaadin-grid-column.js +5 -876
- package/src/vaadin-grid-data-provider-mixin.d.ts +6 -5
- package/src/vaadin-grid-data-provider-mixin.js +58 -11
- package/src/vaadin-grid-drag-and-drop-mixin.js +17 -5
- package/src/vaadin-grid-dynamic-columns-mixin.js +22 -17
- package/src/vaadin-grid-filter-column-mixin.d.ts +22 -0
- package/src/vaadin-grid-filter-column-mixin.js +106 -0
- package/src/vaadin-grid-filter-column.d.ts +9 -11
- package/src/vaadin-grid-filter-column.js +3 -90
- package/src/vaadin-grid-filter-element-mixin.d.ts +34 -0
- package/src/vaadin-grid-filter-element-mixin.js +108 -0
- package/src/vaadin-grid-filter-mixin.js +4 -4
- package/src/vaadin-grid-filter.d.ts +4 -21
- package/src/vaadin-grid-filter.js +5 -84
- package/src/vaadin-grid-helpers.js +94 -0
- package/src/vaadin-grid-keyboard-navigation-mixin.js +26 -6
- package/src/vaadin-grid-mixin.js +37 -46
- package/src/vaadin-grid-row-details-mixin.js +14 -8
- package/src/vaadin-grid-scroll-mixin.js +9 -3
- package/src/vaadin-grid-selection-column-base-mixin.js +12 -4
- package/src/vaadin-grid-selection-column-mixin.d.ts +24 -0
- package/src/vaadin-grid-selection-column-mixin.js +194 -0
- package/src/vaadin-grid-selection-column.d.ts +13 -17
- package/src/vaadin-grid-selection-column.js +4 -186
- package/src/vaadin-grid-selection-mixin.js +4 -3
- package/src/vaadin-grid-sort-column-mixin.d.ts +36 -0
- package/src/vaadin-grid-sort-column-mixin.js +101 -0
- package/src/vaadin-grid-sort-column.d.ts +8 -26
- package/src/vaadin-grid-sort-column.js +3 -87
- package/src/vaadin-grid-sorter-mixin.d.ts +44 -0
- package/src/vaadin-grid-sorter-mixin.js +200 -0
- package/src/vaadin-grid-sorter.d.ts +3 -32
- package/src/vaadin-grid-sorter.js +5 -181
- package/src/vaadin-grid-styles.js +341 -345
- package/src/vaadin-grid-styling-mixin.js +8 -2
- package/src/vaadin-grid-tree-column-mixin.d.ts +18 -0
- package/src/vaadin-grid-tree-column-mixin.js +99 -0
- package/src/vaadin-grid-tree-column.d.ts +9 -7
- package/src/vaadin-grid-tree-column.js +3 -82
- package/src/vaadin-grid-tree-toggle-mixin.d.ts +39 -0
- package/src/vaadin-grid-tree-toggle-mixin.js +153 -0
- package/src/vaadin-grid-tree-toggle.d.ts +4 -27
- package/src/vaadin-grid-tree-toggle.js +9 -141
- package/src/vaadin-grid.d.ts +3 -0
- package/src/vaadin-grid.js +7 -2
- package/theme/lumo/vaadin-grid-sorter-styles.js +1 -1
- package/theme/lumo/vaadin-grid-styles.js +15 -14
- package/theme/material/vaadin-grid-styles.js +15 -10
- package/web-types.json +331 -126
- package/web-types.lit.json +114 -58
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2016 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
7
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
8
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
9
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
10
|
+
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin';
|
|
11
|
+
|
|
12
|
+
registerStyles(
|
|
13
|
+
'vaadin-grid-filter',
|
|
14
|
+
css`
|
|
15
|
+
:host {
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
max-width: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
::slotted(*) {
|
|
21
|
+
width: 100%;
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
`,
|
|
25
|
+
{ moduleId: 'vaadin-grid-filter-styles' },
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @polymerMixin
|
|
30
|
+
*
|
|
31
|
+
* @mixes ControllerMixin
|
|
32
|
+
*/
|
|
33
|
+
export const GridFilterElementMixin = (superClass) =>
|
|
34
|
+
class extends ControllerMixin(superClass) {
|
|
35
|
+
static get properties() {
|
|
36
|
+
return {
|
|
37
|
+
/**
|
|
38
|
+
* JS Path of the property in the item used for filtering the data.
|
|
39
|
+
*/
|
|
40
|
+
path: {
|
|
41
|
+
type: String,
|
|
42
|
+
sync: true,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Current filter value.
|
|
47
|
+
*/
|
|
48
|
+
value: {
|
|
49
|
+
type: String,
|
|
50
|
+
notify: true,
|
|
51
|
+
sync: true,
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/** @private */
|
|
55
|
+
_textField: {
|
|
56
|
+
type: Object,
|
|
57
|
+
sync: true,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static get observers() {
|
|
63
|
+
return ['_filterChanged(path, value, _textField)'];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** @protected */
|
|
67
|
+
ready() {
|
|
68
|
+
super.ready();
|
|
69
|
+
|
|
70
|
+
this._filterController = new SlotController(this, '', 'vaadin-text-field', {
|
|
71
|
+
initializer: (field) => {
|
|
72
|
+
field.addEventListener('value-changed', (e) => {
|
|
73
|
+
if (field.__previousValue === undefined && e.detail.value === '') {
|
|
74
|
+
field.__previousValue = e.detail.value;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.value = e.detail.value;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
this._textField = field;
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
this.addController(this._filterController);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @private */
|
|
87
|
+
_filterChanged(path, value, textField) {
|
|
88
|
+
if (path === undefined || value === undefined || !textField) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (this._previousValue === undefined && value === '') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
textField.value = value;
|
|
96
|
+
this._previousValue = value;
|
|
97
|
+
|
|
98
|
+
this._debouncerFilterChanged = Debouncer.debounce(this._debouncerFilterChanged, timeOut.after(200), () => {
|
|
99
|
+
this.dispatchEvent(new CustomEvent('filter-changed', { bubbles: true }));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
focus() {
|
|
104
|
+
if (this._textField) {
|
|
105
|
+
this._textField.focus();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
@@ -19,10 +19,10 @@ export const FilterMixin = (superClass) =>
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.addEventListener('filter-changed', this._filterChanged
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
this._filterChanged = this._filterChanged.bind(this);
|
|
25
|
+
this.addEventListener('filter-changed', this._filterChanged);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** @private */
|
|
@@ -3,18 +3,11 @@
|
|
|
3
3
|
* Copyright (c) 2016 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
export type GridFilterValueChangedEvent = CustomEvent<{ value: string }>;
|
|
12
|
-
|
|
13
|
-
export interface GridFilterCustomEventMap {
|
|
14
|
-
'value-changed': GridFilterValueChangedEvent;
|
|
15
|
-
}
|
|
7
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
|
|
8
|
+
import { GridFilterElementMixin, type GridFilterEventMap } from './vaadin-grid-filter-element-mixin.js';
|
|
16
9
|
|
|
17
|
-
export
|
|
10
|
+
export * from './vaadin-grid-filter-element-mixin.js';
|
|
18
11
|
|
|
19
12
|
/**
|
|
20
13
|
* `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
|
|
@@ -41,17 +34,7 @@ export interface GridFilterEventMap extends HTMLElementEventMap, GridFilterCusto
|
|
|
41
34
|
*
|
|
42
35
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
43
36
|
*/
|
|
44
|
-
declare class GridFilter extends
|
|
45
|
-
/**
|
|
46
|
-
* JS Path of the property in the item used for filtering the data.
|
|
47
|
-
*/
|
|
48
|
-
path: string | null | undefined;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Current filter value.
|
|
52
|
-
*/
|
|
53
|
-
value: string | null | undefined;
|
|
54
|
-
|
|
37
|
+
declare class GridFilter extends GridFilterElementMixin(ThemableMixin(HTMLElement)) {
|
|
55
38
|
addEventListener<K extends keyof GridFilterEventMap>(
|
|
56
39
|
type: K,
|
|
57
40
|
listener: (this: GridFilter, ev: GridFilterEventMap[K]) => void,
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import '@vaadin/text-field/src/vaadin-text-field.js';
|
|
7
7
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
8
|
-
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
9
|
-
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
10
|
-
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
11
8
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
12
|
-
import {
|
|
9
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
|
|
10
|
+
import { GridFilterElementMixin } from './vaadin-grid-filter-element-mixin.js';
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
|
|
@@ -38,93 +36,16 @@ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
|
38
36
|
*
|
|
39
37
|
* @customElement
|
|
40
38
|
* @extends HTMLElement
|
|
39
|
+
* @mixes GridFilterElementMixin
|
|
41
40
|
*/
|
|
42
|
-
class GridFilter extends
|
|
41
|
+
class GridFilter extends GridFilterElementMixin(ThemableMixin(PolymerElement)) {
|
|
43
42
|
static get template() {
|
|
44
|
-
return html
|
|
45
|
-
<style>
|
|
46
|
-
:host {
|
|
47
|
-
display: inline-flex;
|
|
48
|
-
max-width: 100%;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
::slotted(*) {
|
|
52
|
-
width: 100%;
|
|
53
|
-
box-sizing: border-box;
|
|
54
|
-
}
|
|
55
|
-
</style>
|
|
56
|
-
<slot></slot>
|
|
57
|
-
`;
|
|
43
|
+
return html`<slot></slot>`;
|
|
58
44
|
}
|
|
59
45
|
|
|
60
46
|
static get is() {
|
|
61
47
|
return 'vaadin-grid-filter';
|
|
62
48
|
}
|
|
63
|
-
|
|
64
|
-
static get properties() {
|
|
65
|
-
return {
|
|
66
|
-
/**
|
|
67
|
-
* JS Path of the property in the item used for filtering the data.
|
|
68
|
-
*/
|
|
69
|
-
path: String,
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Current filter value.
|
|
73
|
-
*/
|
|
74
|
-
value: {
|
|
75
|
-
type: String,
|
|
76
|
-
notify: true,
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
/** @private */
|
|
80
|
-
_textField: {
|
|
81
|
-
type: Object,
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static get observers() {
|
|
87
|
-
return ['_filterChanged(path, value, _textField)'];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @protected */
|
|
91
|
-
ready() {
|
|
92
|
-
super.ready();
|
|
93
|
-
|
|
94
|
-
this._filterController = new SlotController(this, '', 'vaadin-text-field', {
|
|
95
|
-
initializer: (field) => {
|
|
96
|
-
field.addEventListener('value-changed', (e) => {
|
|
97
|
-
this.value = e.detail.value;
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
this._textField = field;
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
this.addController(this._filterController);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** @private */
|
|
107
|
-
_filterChanged(path, value, textField) {
|
|
108
|
-
if (path === undefined || value === undefined || !textField) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (this._previousValue === undefined && value === '') {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
textField.value = value;
|
|
116
|
-
this._previousValue = value;
|
|
117
|
-
|
|
118
|
-
this._debouncerFilterChanged = Debouncer.debounce(this._debouncerFilterChanged, timeOut.after(200), () => {
|
|
119
|
-
this.dispatchEvent(new CustomEvent('filter-changed', { bubbles: true }));
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
focus() {
|
|
124
|
-
if (this._textField) {
|
|
125
|
-
this._textField.focus();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
49
|
}
|
|
129
50
|
|
|
130
51
|
defineCustomElement(GridFilter);
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Copyright (c) 2016 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
+
import { microTask } from '@vaadin/component-base/src/async.js';
|
|
7
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
6
8
|
import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -170,3 +172,95 @@ export function updateCellState(cell, attribute, value, part, oldPart) {
|
|
|
170
172
|
// Add new part to the cell attribute
|
|
171
173
|
updatePart(cell, value, part || `${attribute}-cell`);
|
|
172
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* A helper for observing flattened child column list of an element.
|
|
178
|
+
*/
|
|
179
|
+
export class ColumnObserver {
|
|
180
|
+
constructor(host, callback) {
|
|
181
|
+
this.__host = host;
|
|
182
|
+
this.__callback = callback;
|
|
183
|
+
this.__currentSlots = [];
|
|
184
|
+
|
|
185
|
+
this.__onMutation = this.__onMutation.bind(this);
|
|
186
|
+
this.__observer = new MutationObserver(this.__onMutation);
|
|
187
|
+
this.__observer.observe(host, {
|
|
188
|
+
childList: true,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// The observer callback is invoked once initially.
|
|
192
|
+
this.__initialCallDebouncer = Debouncer.debounce(this.__initialCallDebouncer, microTask, () => this.__onMutation());
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
disconnect() {
|
|
196
|
+
this.__observer.disconnect();
|
|
197
|
+
this.__initialCallDebouncer.cancel();
|
|
198
|
+
this.__toggleSlotChangeListeners(false);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
flush() {
|
|
202
|
+
this.__onMutation();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
__toggleSlotChangeListeners(add) {
|
|
206
|
+
this.__currentSlots.forEach((slot) => {
|
|
207
|
+
if (add) {
|
|
208
|
+
slot.addEventListener('slotchange', this.__onMutation);
|
|
209
|
+
} else {
|
|
210
|
+
slot.removeEventListener('slotchange', this.__onMutation);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
__onMutation() {
|
|
216
|
+
// Detect if this is the initial call
|
|
217
|
+
const initialCall = !this.__currentColumns;
|
|
218
|
+
this.__currentColumns ||= [];
|
|
219
|
+
|
|
220
|
+
// Detect added and removed columns or if the columns order has changed
|
|
221
|
+
const columns = ColumnObserver.getColumns(this.__host);
|
|
222
|
+
const addedColumns = columns.filter((column) => !this.__currentColumns.includes(column));
|
|
223
|
+
const removedColumns = this.__currentColumns.filter((column) => !columns.includes(column));
|
|
224
|
+
const orderChanged = this.__currentColumns.some((column, index) => column !== columns[index]);
|
|
225
|
+
this.__currentColumns = columns;
|
|
226
|
+
|
|
227
|
+
// Update the list of child slots and toggle their slotchange listeners
|
|
228
|
+
this.__toggleSlotChangeListeners(false);
|
|
229
|
+
this.__currentSlots = [...this.__host.children].filter((child) => child instanceof HTMLSlotElement);
|
|
230
|
+
this.__toggleSlotChangeListeners(true);
|
|
231
|
+
|
|
232
|
+
// Invoke the callback if there are changes in the child columns or if this is the initial call
|
|
233
|
+
const invokeCallback = initialCall || addedColumns.length || removedColumns.length || orderChanged;
|
|
234
|
+
if (invokeCallback) {
|
|
235
|
+
this.__callback(addedColumns, removedColumns);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Default filter for column elements.
|
|
241
|
+
*/
|
|
242
|
+
static __isColumnElement(node) {
|
|
243
|
+
return node.nodeType === Node.ELEMENT_NODE && /\bcolumn\b/u.test(node.localName);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static getColumns(host) {
|
|
247
|
+
const columns = [];
|
|
248
|
+
|
|
249
|
+
// A temporary workaround for backwards compatibility
|
|
250
|
+
const isColumnElement = host._isColumnElement || ColumnObserver.__isColumnElement;
|
|
251
|
+
|
|
252
|
+
[...host.children].forEach((child) => {
|
|
253
|
+
if (isColumnElement(child)) {
|
|
254
|
+
// The child is a column element, add it to the list
|
|
255
|
+
columns.push(child);
|
|
256
|
+
} else if (child instanceof HTMLSlotElement) {
|
|
257
|
+
// The child is a slot, add all assigned column elements to the list
|
|
258
|
+
[...child.assignedElements({ flatten: true })]
|
|
259
|
+
.filter((assignedElement) => isColumnElement(assignedElement))
|
|
260
|
+
.forEach((assignedElement) => columns.push(assignedElement));
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return columns;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -18,6 +18,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
18
18
|
_headerFocusable: {
|
|
19
19
|
type: Object,
|
|
20
20
|
observer: '_focusableChanged',
|
|
21
|
+
sync: true,
|
|
21
22
|
},
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -27,12 +28,14 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
27
28
|
_itemsFocusable: {
|
|
28
29
|
type: Object,
|
|
29
30
|
observer: '_focusableChanged',
|
|
31
|
+
sync: true,
|
|
30
32
|
},
|
|
31
33
|
|
|
32
34
|
/** @private */
|
|
33
35
|
_footerFocusable: {
|
|
34
36
|
type: Object,
|
|
35
37
|
observer: '_focusableChanged',
|
|
38
|
+
sync: true,
|
|
36
39
|
},
|
|
37
40
|
|
|
38
41
|
/** @private */
|
|
@@ -54,6 +57,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
54
57
|
_focusedCell: {
|
|
55
58
|
type: Object,
|
|
56
59
|
observer: '_focusedCellChanged',
|
|
60
|
+
sync: true,
|
|
57
61
|
},
|
|
58
62
|
|
|
59
63
|
/**
|
|
@@ -267,7 +271,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
267
271
|
__isRowExpandable(row) {
|
|
268
272
|
if (this.itemHasChildrenPath) {
|
|
269
273
|
const item = row._item;
|
|
270
|
-
return item && get(this.itemHasChildrenPath, item) && !this._isExpanded(item);
|
|
274
|
+
return !!(item && get(this.itemHasChildrenPath, item) && !this._isExpanded(item));
|
|
271
275
|
}
|
|
272
276
|
}
|
|
273
277
|
|
|
@@ -443,7 +447,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
443
447
|
__navigateRows(dy, activeRow, activeCell) {
|
|
444
448
|
const currentRowIndex = this.__getIndexInGroup(activeRow, this._focusedItemIndex);
|
|
445
449
|
const activeRowGroup = activeRow.parentNode;
|
|
446
|
-
const maxRowIndex = (activeRowGroup === this.$.items ? this.
|
|
450
|
+
const maxRowIndex = (activeRowGroup === this.$.items ? this._flatSize : activeRowGroup.children.length) - 1;
|
|
447
451
|
|
|
448
452
|
// Index of the destination row
|
|
449
453
|
let dstRowIndex = Math.max(0, Math.min(currentRowIndex + dy, maxRowIndex));
|
|
@@ -687,7 +691,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
687
691
|
// If the target focusable is tied to a column that is not visible,
|
|
688
692
|
// find the first visible column and update the target in order to
|
|
689
693
|
// prevent scrolling to the start of the row.
|
|
690
|
-
if (focusStepTarget &&
|
|
694
|
+
if (focusStepTarget && !this.__isHorizontallyInViewport(focusStepTarget)) {
|
|
691
695
|
const firstVisibleColumn = this._getColumnsInOrder().find((column) => this.__isColumnInViewport(column));
|
|
692
696
|
if (firstVisibleColumn) {
|
|
693
697
|
if (focusStepTarget === this._headerFocusable) {
|
|
@@ -842,9 +846,12 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
842
846
|
}
|
|
843
847
|
|
|
844
848
|
if (cell) {
|
|
845
|
-
// Fire a public event for cell.
|
|
846
849
|
const context = this.getEventContext(e);
|
|
847
|
-
|
|
850
|
+
this.__pendingBodyCellFocus = this.loading && context.section === 'body';
|
|
851
|
+
if (!this.__pendingBodyCellFocus) {
|
|
852
|
+
// Fire a cell-focus event for the cell
|
|
853
|
+
cell.dispatchEvent(new CustomEvent('cell-focus', { bubbles: true, composed: true, detail: { context } }));
|
|
854
|
+
}
|
|
848
855
|
this._focusedCell = cell._focusButton || cell;
|
|
849
856
|
|
|
850
857
|
if (isKeyboardActive() && e.target === cell) {
|
|
@@ -858,6 +865,16 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
858
865
|
this._detectFocusedItemIndex(e);
|
|
859
866
|
}
|
|
860
867
|
|
|
868
|
+
/**
|
|
869
|
+
* @private
|
|
870
|
+
*/
|
|
871
|
+
__dispatchPendingBodyCellFocus() {
|
|
872
|
+
// If the body cell focus was pending, dispatch the event once loading is done
|
|
873
|
+
if (this.__pendingBodyCellFocus && this.shadowRoot.activeElement === this._itemsFocusable) {
|
|
874
|
+
this._itemsFocusable.dispatchEvent(new Event('focusin', { bubbles: true, composed: true }));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
861
878
|
/**
|
|
862
879
|
* Get the focusable element depending on the current focus mode.
|
|
863
880
|
* It can be a row, a cell, or a focusable div inside a cell.
|
|
@@ -954,6 +971,9 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
954
971
|
|
|
955
972
|
/** @protected */
|
|
956
973
|
_resetKeyboardNavigation() {
|
|
974
|
+
if (!this.$ && this.performUpdate) {
|
|
975
|
+
this.performUpdate();
|
|
976
|
+
}
|
|
957
977
|
// Header / footer
|
|
958
978
|
['header', 'footer'].forEach((section) => {
|
|
959
979
|
if (!this.__isValidFocusable(this[`_${section}Focusable`])) {
|
|
@@ -972,7 +992,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
972
992
|
|
|
973
993
|
if (firstVisibleCell && firstVisibleRow) {
|
|
974
994
|
// Reset memoized column
|
|
975
|
-
|
|
995
|
+
this._focusedColumnOrder = undefined;
|
|
976
996
|
this._itemsFocusable = this.__getFocusable(firstVisibleRow, firstVisibleCell);
|
|
977
997
|
}
|
|
978
998
|
} else {
|
package/src/vaadin-grid-mixin.js
CHANGED
|
@@ -86,10 +86,7 @@ export const GridMixin = (superClass) =>
|
|
|
86
86
|
),
|
|
87
87
|
) {
|
|
88
88
|
static get observers() {
|
|
89
|
-
return [
|
|
90
|
-
'_columnTreeChanged(_columnTree, _columnTree.*)',
|
|
91
|
-
'_effectiveSizeChanged(_effectiveSize, __virtualizer, _hasData, _columnTree)',
|
|
92
|
-
];
|
|
89
|
+
return ['_columnTreeChanged(_columnTree)', '_flatSizeChanged(_flatSize, __virtualizer, _hasData, _columnTree)'];
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
static get properties() {
|
|
@@ -254,7 +251,6 @@ export const GridMixin = (superClass) =>
|
|
|
254
251
|
|
|
255
252
|
new ResizeObserver(() =>
|
|
256
253
|
setTimeout(() => {
|
|
257
|
-
this.__updateFooterPositioning();
|
|
258
254
|
this.__updateColumnsBodyContentHidden();
|
|
259
255
|
this.__tryToRecalculateColumnWidthsIfPending();
|
|
260
256
|
}),
|
|
@@ -294,20 +290,20 @@ export const GridMixin = (superClass) =>
|
|
|
294
290
|
}
|
|
295
291
|
|
|
296
292
|
/** @private */
|
|
297
|
-
|
|
293
|
+
_flatSizeChanged(flatSize, virtualizer, hasData, columnTree) {
|
|
298
294
|
if (virtualizer && hasData && columnTree) {
|
|
299
295
|
// Changing the virtualizer size may result in the row with focus getting hidden
|
|
300
296
|
const cell = this.shadowRoot.activeElement;
|
|
301
297
|
const cellCoordinates = this.__getBodyCellCoordinates(cell);
|
|
302
298
|
|
|
303
299
|
const previousSize = virtualizer.size || 0;
|
|
304
|
-
virtualizer.size =
|
|
300
|
+
virtualizer.size = flatSize;
|
|
305
301
|
|
|
306
302
|
// Request an update for the previous last row to have the "last" state removed
|
|
307
303
|
virtualizer.update(previousSize - 1, previousSize - 1);
|
|
308
|
-
if (
|
|
304
|
+
if (flatSize < previousSize) {
|
|
309
305
|
// Size was decreased, so the new last row requires an explicit update
|
|
310
|
-
virtualizer.update(
|
|
306
|
+
virtualizer.update(flatSize - 1, flatSize - 1);
|
|
311
307
|
}
|
|
312
308
|
|
|
313
309
|
// If the focused cell's parent row got hidden by the size change, focus the corresponding new cell
|
|
@@ -339,7 +335,10 @@ export const GridMixin = (superClass) =>
|
|
|
339
335
|
return 0;
|
|
340
336
|
}
|
|
341
337
|
|
|
342
|
-
const columnWidth = Math.max(
|
|
338
|
+
const columnWidth = Math.max(
|
|
339
|
+
this.__getIntrinsicWidth(col),
|
|
340
|
+
this.__getDistributedWidth((col.assignedSlot || col).parentElement, col),
|
|
341
|
+
);
|
|
343
342
|
|
|
344
343
|
// We're processing a regular grid-column and not a grid-column-group
|
|
345
344
|
if (!innerColumn) {
|
|
@@ -498,7 +497,7 @@ export const GridMixin = (superClass) =>
|
|
|
498
497
|
const rows = [];
|
|
499
498
|
for (let i = 0; i < count; i++) {
|
|
500
499
|
const row = document.createElement('tr');
|
|
501
|
-
row.setAttribute('part', 'row');
|
|
500
|
+
row.setAttribute('part', 'row body-row');
|
|
502
501
|
row.setAttribute('role', 'row');
|
|
503
502
|
row.setAttribute('tabindex', '-1');
|
|
504
503
|
if (this._columnTree) {
|
|
@@ -508,9 +507,11 @@ export const GridMixin = (superClass) =>
|
|
|
508
507
|
}
|
|
509
508
|
|
|
510
509
|
if (this._columnTree) {
|
|
511
|
-
this._columnTree[this._columnTree.length - 1].forEach(
|
|
512
|
-
(c
|
|
513
|
-
|
|
510
|
+
this._columnTree[this._columnTree.length - 1].forEach((c) => {
|
|
511
|
+
if (c.isConnected && c._cells) {
|
|
512
|
+
c._cells = [...c._cells];
|
|
513
|
+
}
|
|
514
|
+
});
|
|
514
515
|
}
|
|
515
516
|
|
|
516
517
|
this.__afterCreateScrollerRowsDebouncer = Debouncer.debounce(
|
|
@@ -675,8 +676,8 @@ export const GridMixin = (superClass) =>
|
|
|
675
676
|
detailsCell._vacant = false;
|
|
676
677
|
}
|
|
677
678
|
|
|
678
|
-
if (
|
|
679
|
-
column.
|
|
679
|
+
if (!noNotify) {
|
|
680
|
+
column._cells = [...column._cells];
|
|
680
681
|
}
|
|
681
682
|
} else {
|
|
682
683
|
// Header & footer
|
|
@@ -697,7 +698,7 @@ export const GridMixin = (superClass) =>
|
|
|
697
698
|
column._emptyCells.push(cell);
|
|
698
699
|
}
|
|
699
700
|
}
|
|
700
|
-
cell.
|
|
701
|
+
cell.part.add('cell', `${section}-cell`);
|
|
701
702
|
}
|
|
702
703
|
|
|
703
704
|
if (!cell._content.parentElement) {
|
|
@@ -801,7 +802,7 @@ export const GridMixin = (superClass) =>
|
|
|
801
802
|
_updateRowOrderParts(row, index = row.index) {
|
|
802
803
|
updateBooleanRowStates(row, {
|
|
803
804
|
first: index === 0,
|
|
804
|
-
last: index === this.
|
|
805
|
+
last: index === this._flatSize - 1,
|
|
805
806
|
odd: index % 2 !== 0,
|
|
806
807
|
even: index % 2 === 0,
|
|
807
808
|
});
|
|
@@ -811,6 +812,7 @@ export const GridMixin = (superClass) =>
|
|
|
811
812
|
_updateRowStateParts(row, { expanded, selected, detailsOpened }) {
|
|
812
813
|
updateBooleanRowStates(row, {
|
|
813
814
|
expanded,
|
|
815
|
+
collapsed: this.__isRowExpandable(row),
|
|
814
816
|
selected,
|
|
815
817
|
'details-opened': detailsOpened,
|
|
816
818
|
});
|
|
@@ -873,22 +875,9 @@ export const GridMixin = (superClass) =>
|
|
|
873
875
|
this._resetKeyboardNavigation();
|
|
874
876
|
this._a11yUpdateHeaderRows();
|
|
875
877
|
this._a11yUpdateFooterRows();
|
|
876
|
-
this.__updateFooterPositioning();
|
|
877
878
|
this.generateCellClassNames();
|
|
878
879
|
this.generateCellPartNames();
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
/** @private */
|
|
882
|
-
__updateFooterPositioning() {
|
|
883
|
-
// TODO: fixed in Firefox 99, remove when we can drop Firefox ESR 91 support
|
|
884
|
-
if (this._firefox && parseFloat(navigator.userAgent.match(/Firefox\/(\d{2,3}.\d)/u)[1]) < 99) {
|
|
885
|
-
// Sticky (or translated) footer in a flexbox host doesn't get included in
|
|
886
|
-
// the scroll height calculation on FF. This is a workaround for the issue.
|
|
887
|
-
this.$.items.style.paddingBottom = 0;
|
|
888
|
-
if (!this.allRowsVisible) {
|
|
889
|
-
this.$.items.style.paddingBottom = `${this.$.footer.offsetHeight}px`;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
880
|
+
this.__updateHeaderAndFooter();
|
|
892
881
|
}
|
|
893
882
|
|
|
894
883
|
/**
|
|
@@ -926,7 +915,6 @@ export const GridMixin = (superClass) =>
|
|
|
926
915
|
/** @private */
|
|
927
916
|
_resizeHandler() {
|
|
928
917
|
this._updateDetailsCellHeights();
|
|
929
|
-
this.__updateFooterPositioning();
|
|
930
918
|
this.__updateHorizontalScrollPosition();
|
|
931
919
|
}
|
|
932
920
|
|
|
@@ -980,7 +968,7 @@ export const GridMixin = (superClass) =>
|
|
|
980
968
|
|
|
981
969
|
/** @protected */
|
|
982
970
|
_hideTooltip(immediate) {
|
|
983
|
-
const tooltip = this._tooltipController.node;
|
|
971
|
+
const tooltip = this._tooltipController && this._tooltipController.node;
|
|
984
972
|
if (tooltip) {
|
|
985
973
|
tooltip._stateController.close(immediate);
|
|
986
974
|
}
|
|
@@ -998,19 +986,22 @@ export const GridMixin = (superClass) =>
|
|
|
998
986
|
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
999
987
|
*/
|
|
1000
988
|
requestContentUpdate() {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
this._columnTree.forEach((level) => {
|
|
1004
|
-
level.forEach((column) => {
|
|
1005
|
-
if (column._renderHeaderAndFooter) {
|
|
1006
|
-
column._renderHeaderAndFooter();
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
});
|
|
989
|
+
// Header and footer renderers
|
|
990
|
+
this.__updateHeaderAndFooter();
|
|
1010
991
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
992
|
+
// Body and row details renderers
|
|
993
|
+
this.__updateVisibleRows();
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/** @private */
|
|
997
|
+
__updateHeaderAndFooter() {
|
|
998
|
+
(this._columnTree || []).forEach((level) => {
|
|
999
|
+
level.forEach((column) => {
|
|
1000
|
+
if (column._renderHeaderAndFooter) {
|
|
1001
|
+
column._renderHeaderAndFooter();
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1014
1005
|
}
|
|
1015
1006
|
|
|
1016
1007
|
/** @protected */
|