@vaadin/grid 23.0.2 → 23.1.0-alpha1
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 +9 -9
- package/src/vaadin-grid-column-group.js +23 -0
- package/src/vaadin-grid-column-reordering-mixin.js +5 -2
- package/src/vaadin-grid-column-resizing-mixin.js +27 -6
- package/src/vaadin-grid-column.d.ts +11 -0
- package/src/vaadin-grid-column.js +60 -1
- package/src/vaadin-grid-keyboard-navigation-mixin.js +4 -3
- package/src/vaadin-grid-scroll-mixin.js +90 -15
- package/src/vaadin-grid-styles.js +32 -8
- package/src/vaadin-grid.d.ts +1 -1
- package/src/vaadin-grid.js +10 -6
- package/theme/lumo/vaadin-grid-styles.js +19 -2
- package/theme/material/vaadin-grid-styles.js +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/grid",
|
|
3
|
-
"version": "23.0
|
|
3
|
+
"version": "23.1.0-alpha1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -41,19 +41,19 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
43
43
|
"@polymer/polymer": "^3.0.0",
|
|
44
|
-
"@vaadin/checkbox": "
|
|
45
|
-
"@vaadin/component-base": "
|
|
46
|
-
"@vaadin/text-field": "
|
|
47
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
48
|
-
"@vaadin/vaadin-material-styles": "
|
|
49
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
44
|
+
"@vaadin/checkbox": "23.1.0-alpha1",
|
|
45
|
+
"@vaadin/component-base": "23.1.0-alpha1",
|
|
46
|
+
"@vaadin/text-field": "23.1.0-alpha1",
|
|
47
|
+
"@vaadin/vaadin-lumo-styles": "23.1.0-alpha1",
|
|
48
|
+
"@vaadin/vaadin-material-styles": "23.1.0-alpha1",
|
|
49
|
+
"@vaadin/vaadin-themable-mixin": "23.1.0-alpha1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@esm-bundle/chai": "^4.3.4",
|
|
53
|
-
"@vaadin/polymer-legacy-adapter": "
|
|
53
|
+
"@vaadin/polymer-legacy-adapter": "23.1.0-alpha1",
|
|
54
54
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
55
55
|
"lit": "^2.0.0",
|
|
56
56
|
"sinon": "^9.2.0"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "5d0cdee069f866037c507265fafb4d0476795333"
|
|
59
59
|
}
|
|
@@ -88,6 +88,7 @@ class GridColumnGroup extends ColumnBaseMixin(PolymerElement) {
|
|
|
88
88
|
'_updateVisibleChildColumns(_childColumns)',
|
|
89
89
|
'_childColumnsChanged(_childColumns)',
|
|
90
90
|
'_groupFrozenChanged(frozen, _rootColumns)',
|
|
91
|
+
'_groupFrozenToEndChanged(frozenToEnd, _rootColumns)',
|
|
91
92
|
'_groupHiddenChanged(hidden, _rootColumns)',
|
|
92
93
|
'_visibleChildColumnsChanged(_visibleChildColumns)',
|
|
93
94
|
'_colSpanChanged(_colSpan, _headerCell, _footerCell)',
|
|
@@ -135,6 +136,16 @@ class GridColumnGroup extends ColumnBaseMixin(PolymerElement) {
|
|
|
135
136
|
// Don’t unfreeze the frozen group because of a non-frozen child
|
|
136
137
|
this._lastFrozen = this._lastFrozen || value;
|
|
137
138
|
}
|
|
139
|
+
|
|
140
|
+
if (path === 'frozenToEnd') {
|
|
141
|
+
// Don’t unfreeze the frozen group because of a non-frozen child
|
|
142
|
+
this.frozenToEnd = this.frozenToEnd || value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (path === 'firstFrozenToEnd') {
|
|
146
|
+
// Don’t unfreeze the frozen group because of a non-frozen child
|
|
147
|
+
this._firstFrozenToEnd = this._firstFrozenToEnd || value;
|
|
148
|
+
}
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
/** @private */
|
|
@@ -239,6 +250,18 @@ class GridColumnGroup extends ColumnBaseMixin(PolymerElement) {
|
|
|
239
250
|
}
|
|
240
251
|
}
|
|
241
252
|
|
|
253
|
+
/** @private */
|
|
254
|
+
_groupFrozenToEndChanged(frozenToEnd, rootColumns) {
|
|
255
|
+
if (rootColumns === undefined || frozenToEnd === undefined) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Don’t propagate the default `false` value.
|
|
260
|
+
if (frozenToEnd !== false) {
|
|
261
|
+
Array.from(rootColumns).forEach((col) => (col.frozenToEnd = frozenToEnd));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
242
265
|
/** @private */
|
|
243
266
|
_groupHiddenChanged(hidden, rootColumns) {
|
|
244
267
|
if (rootColumns && !this._preventHiddenCascade) {
|
|
@@ -317,7 +317,10 @@ export const ColumnReorderingMixin = (superClass) =>
|
|
|
317
317
|
if (column1 && column2) {
|
|
318
318
|
const differentColumns = column1 !== column2;
|
|
319
319
|
const sameParent = column1.parentElement === column2.parentElement;
|
|
320
|
-
const sameFrozen =
|
|
320
|
+
const sameFrozen =
|
|
321
|
+
(column1.frozen && column2.frozen) || // both columns are frozen
|
|
322
|
+
(column1.frozenToEnd && column2.frozenToEnd) || // both columns are frozen to end
|
|
323
|
+
(!column1.frozen && !column1.frozenToEnd && !column2.frozen && !column2.frozenToEnd);
|
|
321
324
|
return differentColumns && sameParent && sameFrozen;
|
|
322
325
|
}
|
|
323
326
|
}
|
|
@@ -351,7 +354,7 @@ export const ColumnReorderingMixin = (superClass) =>
|
|
|
351
354
|
const _order = column1._order;
|
|
352
355
|
column1._order = column2._order;
|
|
353
356
|
column2._order = _order;
|
|
354
|
-
this.
|
|
357
|
+
this._updateFrozenColumn();
|
|
355
358
|
this._updateFirstAndLastColumn();
|
|
356
359
|
}
|
|
357
360
|
|
|
@@ -53,11 +53,12 @@ export const ColumnResizingMixin = (superClass) =>
|
|
|
53
53
|
.pop();
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const eventX = e.detail.x;
|
|
56
57
|
const columnRowCells = Array.from(this.$.header.querySelectorAll('[part~="row"]:last-child [part~="cell"]'));
|
|
57
58
|
const targetCell = columnRowCells.filter((cell) => cell._column === column)[0];
|
|
58
59
|
// Resize the target column
|
|
59
60
|
if (targetCell.offsetWidth) {
|
|
60
|
-
const style =
|
|
61
|
+
const style = getComputedStyle(targetCell._content);
|
|
61
62
|
const minWidth =
|
|
62
63
|
10 +
|
|
63
64
|
parseInt(style.paddingLeft) +
|
|
@@ -66,11 +67,19 @@ export const ColumnResizingMixin = (superClass) =>
|
|
|
66
67
|
parseInt(style.borderRightWidth) +
|
|
67
68
|
parseInt(style.marginLeft) +
|
|
68
69
|
parseInt(style.marginRight);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
|
|
71
|
+
let maxWidth;
|
|
72
|
+
|
|
73
|
+
const cellWidth = targetCell.offsetWidth;
|
|
74
|
+
const cellRect = targetCell.getBoundingClientRect();
|
|
75
|
+
|
|
76
|
+
// For cells frozen to end, resize handle is flipped horizontally.
|
|
77
|
+
if (targetCell.hasAttribute('frozen-to-end')) {
|
|
78
|
+
maxWidth = cellWidth + (this.__isRTL ? eventX - cellRect.right : cellRect.left - eventX);
|
|
79
|
+
} else {
|
|
80
|
+
maxWidth = cellWidth + (this.__isRTL ? cellRect.left - eventX : eventX - cellRect.right);
|
|
81
|
+
}
|
|
82
|
+
|
|
74
83
|
column.width = Math.max(minWidth, maxWidth) + 'px';
|
|
75
84
|
column.flexGrow = 0;
|
|
76
85
|
}
|
|
@@ -86,6 +95,18 @@ export const ColumnResizingMixin = (superClass) =>
|
|
|
86
95
|
}
|
|
87
96
|
});
|
|
88
97
|
|
|
98
|
+
const cellFrozenToEnd = this._frozenToEndCells[0];
|
|
99
|
+
|
|
100
|
+
// When handle moves below the cell frozen to end, scroll into view.
|
|
101
|
+
if (cellFrozenToEnd && this.$.table.scrollWidth > this.$.table.offsetWidth) {
|
|
102
|
+
const frozenRect = cellFrozenToEnd.getBoundingClientRect();
|
|
103
|
+
const offset = eventX - (this.__isRTL ? frozenRect.right : frozenRect.left);
|
|
104
|
+
|
|
105
|
+
if ((this.__isRTL && offset <= 0) || (!this.__isRTL && offset >= 0)) {
|
|
106
|
+
this.$.table.scrollLeft += offset;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
89
110
|
if (e.detail.state === 'end') {
|
|
90
111
|
this.$.scroller.toggleAttribute('column-resizing', false);
|
|
91
112
|
this.dispatchEvent(
|
|
@@ -32,6 +32,17 @@ export declare class ColumnBaseMixinClass<TItem> {
|
|
|
32
32
|
*/
|
|
33
33
|
frozen: boolean;
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* When true, the column is frozen to end of grid.
|
|
37
|
+
*
|
|
38
|
+
* When a column inside of a column group is frozen to end, all of the sibling columns
|
|
39
|
+
* inside the group will get frozen to end also.
|
|
40
|
+
*
|
|
41
|
+
* Column can not be set as `frozen` and `frozenToEnd` at the same time.
|
|
42
|
+
* @attr {boolean} frozen-to-end
|
|
43
|
+
*/
|
|
44
|
+
frozenToEnd: boolean;
|
|
45
|
+
|
|
35
46
|
/**
|
|
36
47
|
* When set to true, the cells for this column are hidden.
|
|
37
48
|
*/
|
|
@@ -45,6 +45,21 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
45
45
|
value: false
|
|
46
46
|
},
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* When true, the column is frozen to end of grid.
|
|
50
|
+
*
|
|
51
|
+
* When a column inside of a column group is frozen to end, all of the sibling columns
|
|
52
|
+
* inside the group will get frozen to end also.
|
|
53
|
+
*
|
|
54
|
+
* Column can not be set as `frozen` and `frozenToEnd` at the same time.
|
|
55
|
+
* @attr {boolean} frozen-to-end
|
|
56
|
+
* @type {boolean}
|
|
57
|
+
*/
|
|
58
|
+
frozenToEnd: {
|
|
59
|
+
type: Boolean,
|
|
60
|
+
value: false
|
|
61
|
+
},
|
|
62
|
+
|
|
48
63
|
/**
|
|
49
64
|
* When set to true, the cells for this column are hidden.
|
|
50
65
|
*/
|
|
@@ -79,6 +94,15 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
79
94
|
value: false
|
|
80
95
|
},
|
|
81
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @type {boolean}
|
|
99
|
+
* @protected
|
|
100
|
+
*/
|
|
101
|
+
_firstFrozenToEnd: {
|
|
102
|
+
type: Boolean,
|
|
103
|
+
value: false
|
|
104
|
+
},
|
|
105
|
+
|
|
82
106
|
/** @protected */
|
|
83
107
|
_order: Number,
|
|
84
108
|
|
|
@@ -176,10 +200,12 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
176
200
|
return [
|
|
177
201
|
'_widthChanged(width, _headerCell, _footerCell, _cells.*)',
|
|
178
202
|
'_frozenChanged(frozen, _headerCell, _footerCell, _cells.*)',
|
|
203
|
+
'_frozenToEndChanged(frozenToEnd, _headerCell, _footerCell, _cells.*)',
|
|
179
204
|
'_flexGrowChanged(flexGrow, _headerCell, _footerCell, _cells.*)',
|
|
180
205
|
'_textAlignChanged(textAlign, _cells.*, _headerCell, _footerCell)',
|
|
181
206
|
'_orderChanged(_order, _headerCell, _footerCell, _cells.*)',
|
|
182
207
|
'_lastFrozenChanged(_lastFrozen)',
|
|
208
|
+
'_firstFrozenToEndChanged(_firstFrozenToEnd)',
|
|
183
209
|
'_onRendererOrBindingChanged(_renderer, _cells, _cells.*, path)',
|
|
184
210
|
'_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header)',
|
|
185
211
|
'_onFooterRendererOrBindingChanged(_footerRenderer, _footerCell)',
|
|
@@ -314,6 +340,23 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
314
340
|
this._grid && this._grid._frozenCellsChanged && this._grid._frozenCellsChanged();
|
|
315
341
|
}
|
|
316
342
|
|
|
343
|
+
/** @private */
|
|
344
|
+
_frozenToEndChanged(frozenToEnd) {
|
|
345
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
346
|
+
this.parentElement._columnPropChanged('frozenToEnd', frozenToEnd);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
this._allCells.forEach((cell) => {
|
|
350
|
+
// Skip sizer cells to keep correct scrollWidth.
|
|
351
|
+
if (this._grid && cell.parentElement === this._grid.$.sizer) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
cell.toggleAttribute('frozen-to-end', frozenToEnd);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
this._grid && this._grid._frozenCellsChanged && this._grid._frozenCellsChanged();
|
|
358
|
+
}
|
|
359
|
+
|
|
317
360
|
/** @private */
|
|
318
361
|
_lastFrozenChanged(lastFrozen) {
|
|
319
362
|
this._allCells.forEach((cell) => cell.toggleAttribute('last-frozen', lastFrozen));
|
|
@@ -323,6 +366,22 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
323
366
|
}
|
|
324
367
|
}
|
|
325
368
|
|
|
369
|
+
/** @private */
|
|
370
|
+
_firstFrozenToEndChanged(firstFrozenToEnd) {
|
|
371
|
+
this._allCells.forEach((cell) => {
|
|
372
|
+
// Skip sizer cells to keep correct scrollWidth.
|
|
373
|
+
if (this._grid && cell.parentElement === this._grid.$.sizer) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
cell.toggleAttribute('first-frozen-to-end', firstFrozenToEnd);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
381
|
+
this.parentElement._firstFrozenToEnd = firstFrozenToEnd;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
326
385
|
/**
|
|
327
386
|
* @param {string} path
|
|
328
387
|
* @return {string}
|
|
@@ -421,7 +480,7 @@ export const ColumnBaseMixin = (superClass) =>
|
|
|
421
480
|
}
|
|
422
481
|
);
|
|
423
482
|
|
|
424
|
-
this._grid.
|
|
483
|
+
this._grid._updateFrozenColumn && this._grid._updateFrozenColumn();
|
|
425
484
|
this._grid._resetKeyboardNavigation && this._grid._resetKeyboardNavigation();
|
|
426
485
|
}
|
|
427
486
|
this._previousHidden = hidden;
|
|
@@ -751,6 +751,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
751
751
|
_detectInteracting(e) {
|
|
752
752
|
const isInteracting = e.composedPath().some((el) => el.localName === 'vaadin-grid-cell-content');
|
|
753
753
|
this._setInteracting(isInteracting);
|
|
754
|
+
this.__updateHorizontalScrollPosition();
|
|
754
755
|
}
|
|
755
756
|
|
|
756
757
|
/** @private */
|
|
@@ -854,7 +855,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
854
855
|
* @protected
|
|
855
856
|
*/
|
|
856
857
|
_scrollHorizontallyToCell(dstCell) {
|
|
857
|
-
if (dstCell.hasAttribute('frozen') || this.__isDetailsCell(dstCell)) {
|
|
858
|
+
if (dstCell.hasAttribute('frozen') || dstCell.hasAttribute('frozen-to-end') || this.__isDetailsCell(dstCell)) {
|
|
858
859
|
// These cells are, by design, always visible, no need to scroll.
|
|
859
860
|
return;
|
|
860
861
|
}
|
|
@@ -870,7 +871,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
870
871
|
if (cell.hasAttribute('hidden') || this.__isDetailsCell(cell)) {
|
|
871
872
|
continue;
|
|
872
873
|
}
|
|
873
|
-
if (cell.hasAttribute('frozen')) {
|
|
874
|
+
if (cell.hasAttribute('frozen') || cell.hasAttribute('frozen-to-end')) {
|
|
874
875
|
leftBoundary = cell.getBoundingClientRect().right;
|
|
875
876
|
break;
|
|
876
877
|
}
|
|
@@ -880,7 +881,7 @@ export const KeyboardNavigationMixin = (superClass) =>
|
|
|
880
881
|
if (cell.hasAttribute('hidden') || this.__isDetailsCell(cell)) {
|
|
881
882
|
continue;
|
|
882
883
|
}
|
|
883
|
-
if (cell.hasAttribute('frozen')) {
|
|
884
|
+
if (cell.hasAttribute('frozen') || cell.hasAttribute('frozen-to-end')) {
|
|
884
885
|
rightBoundary = cell.getBoundingClientRect().left;
|
|
885
886
|
break;
|
|
886
887
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { animationFrame, microTask, timeOut } from '@vaadin/component-base/src/async.js';
|
|
7
7
|
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
8
|
+
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
8
9
|
|
|
9
10
|
const timeouts = {
|
|
10
11
|
SCROLLING: 500
|
|
@@ -14,7 +15,7 @@ const timeouts = {
|
|
|
14
15
|
* @polymerMixin
|
|
15
16
|
*/
|
|
16
17
|
export const ScrollMixin = (superClass) =>
|
|
17
|
-
class ScrollMixin extends superClass {
|
|
18
|
+
class ScrollMixin extends ResizeMixin(superClass) {
|
|
18
19
|
static get properties() {
|
|
19
20
|
return {
|
|
20
21
|
/**
|
|
@@ -26,6 +27,15 @@ export const ScrollMixin = (superClass) =>
|
|
|
26
27
|
value: () => []
|
|
27
28
|
},
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Cached array of cells frozen to end
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
_frozenToEndCells: {
|
|
35
|
+
type: Array,
|
|
36
|
+
value: () => []
|
|
37
|
+
},
|
|
38
|
+
|
|
29
39
|
/** @private */
|
|
30
40
|
_rowWithFocusedElement: Element
|
|
31
41
|
};
|
|
@@ -67,6 +77,15 @@ export const ScrollMixin = (superClass) =>
|
|
|
67
77
|
this.$.table.addEventListener('scroll', () => this._afterScroll());
|
|
68
78
|
}
|
|
69
79
|
|
|
80
|
+
/**
|
|
81
|
+
* @protected
|
|
82
|
+
* @override
|
|
83
|
+
*/
|
|
84
|
+
_onResize() {
|
|
85
|
+
this._updateOverflow();
|
|
86
|
+
this.__updateHorizontalScrollPosition();
|
|
87
|
+
}
|
|
88
|
+
|
|
70
89
|
/**
|
|
71
90
|
* Scroll to a specific row index in the virtual list. Note that the row index is
|
|
72
91
|
* not always the same for any particular item. For example, sorting/filtering/expanding
|
|
@@ -136,6 +155,22 @@ export const ScrollMixin = (superClass) =>
|
|
|
136
155
|
overflow += ' top';
|
|
137
156
|
}
|
|
138
157
|
|
|
158
|
+
const scrollLeft = this.__getNormalizedScrollLeft(table);
|
|
159
|
+
if (scrollLeft > 0) {
|
|
160
|
+
overflow += ' start';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (scrollLeft < table.scrollWidth - table.clientWidth) {
|
|
164
|
+
overflow += ' end';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.__isRTL) {
|
|
168
|
+
overflow = overflow.replace(/start|end/gi, (matched) => {
|
|
169
|
+
return matched === 'start' ? 'end' : 'start';
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// TODO: Remove "right" and "left" values in the next major.
|
|
139
174
|
if (table.scrollLeft < table.scrollWidth - table.clientWidth) {
|
|
140
175
|
overflow += ' right';
|
|
141
176
|
}
|
|
@@ -161,13 +196,14 @@ export const ScrollMixin = (superClass) =>
|
|
|
161
196
|
cell.style.transform = '';
|
|
162
197
|
});
|
|
163
198
|
this._frozenCells = Array.prototype.slice.call(this.$.table.querySelectorAll('[frozen]'));
|
|
199
|
+
this._frozenToEndCells = Array.prototype.slice.call(this.$.table.querySelectorAll('[frozen-to-end]'));
|
|
164
200
|
this.__updateHorizontalScrollPosition();
|
|
165
201
|
});
|
|
166
|
-
this.
|
|
202
|
+
this._updateFrozenColumn();
|
|
167
203
|
}
|
|
168
204
|
|
|
169
205
|
/** @protected */
|
|
170
|
-
|
|
206
|
+
_updateFrozenColumn() {
|
|
171
207
|
if (!this._columnTree) {
|
|
172
208
|
return;
|
|
173
209
|
}
|
|
@@ -176,27 +212,66 @@ export const ScrollMixin = (superClass) =>
|
|
|
176
212
|
columnsRow.sort((a, b) => {
|
|
177
213
|
return a._order - b._order;
|
|
178
214
|
});
|
|
179
|
-
|
|
215
|
+
|
|
216
|
+
let lastFrozen;
|
|
217
|
+
let firstFrozenToEnd;
|
|
218
|
+
|
|
219
|
+
// Use for loop to only iterate columns once
|
|
220
|
+
for (let i = 0; i < columnsRow.length; i++) {
|
|
221
|
+
const col = columnsRow[i];
|
|
222
|
+
|
|
180
223
|
col._lastFrozen = false;
|
|
181
|
-
|
|
182
|
-
|
|
224
|
+
col._firstFrozenToEnd = false;
|
|
225
|
+
|
|
226
|
+
if (firstFrozenToEnd === undefined && col.frozenToEnd && !col.hidden) {
|
|
227
|
+
firstFrozenToEnd = i;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (col.frozen && !col.hidden) {
|
|
231
|
+
lastFrozen = i;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
183
235
|
if (lastFrozen !== undefined) {
|
|
184
236
|
columnsRow[lastFrozen]._lastFrozen = true;
|
|
185
237
|
}
|
|
238
|
+
|
|
239
|
+
if (firstFrozenToEnd !== undefined) {
|
|
240
|
+
columnsRow[firstFrozenToEnd]._firstFrozenToEnd = true;
|
|
241
|
+
}
|
|
186
242
|
}
|
|
187
243
|
|
|
188
244
|
/** @private */
|
|
189
245
|
__updateHorizontalScrollPosition() {
|
|
190
|
-
this.$.table.
|
|
246
|
+
const scrollWidth = this.$.table.scrollWidth;
|
|
247
|
+
const clientWidth = this.$.table.clientWidth;
|
|
248
|
+
const scrollLeft = Math.max(0, this.$.table.scrollLeft);
|
|
249
|
+
const normalizedScrollLeft = this.__getNormalizedScrollLeft(this.$.table);
|
|
191
250
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
251
|
+
// Position header, footer and items container
|
|
252
|
+
const transform = `translate(${-scrollLeft}px, 0)`;
|
|
253
|
+
this.$.header.style.transform = transform;
|
|
254
|
+
this.$.footer.style.transform = transform;
|
|
255
|
+
this.$.items.style.transform = transform;
|
|
256
|
+
|
|
257
|
+
// Position frozen cells
|
|
258
|
+
const x = this.__isRTL ? normalizedScrollLeft + clientWidth - scrollWidth : scrollLeft;
|
|
259
|
+
const transformFrozen = `translate(${x}px, 0)`;
|
|
260
|
+
for (let i = 0; i < this._frozenCells.length; i++) {
|
|
261
|
+
this._frozenCells[i].style.transform = transformFrozen;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Position cells frozen to end
|
|
265
|
+
const remaining = this.__isRTL ? normalizedScrollLeft : scrollLeft + clientWidth - scrollWidth;
|
|
266
|
+
const transformFrozenToEnd = `translate(${remaining}px, 0)`;
|
|
267
|
+
for (let i = 0; i < this._frozenToEndCells.length; i++) {
|
|
268
|
+
this._frozenToEndCells[i].style.transform = transformFrozenToEnd;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Only update the --_grid-horizontal-scroll-position custom property when navigating
|
|
272
|
+
// on row focus mode to avoid performance issues.
|
|
273
|
+
if (this.hasAttribute('navigating') && this.__rowFocusMode) {
|
|
274
|
+
this.$.table.style.setProperty('--_grid-horizontal-scroll-position', -x + 'px');
|
|
200
275
|
}
|
|
201
276
|
}
|
|
202
277
|
};
|
|
@@ -74,7 +74,6 @@ registerStyles(
|
|
|
74
74
|
|
|
75
75
|
#header,
|
|
76
76
|
#footer {
|
|
77
|
-
transform: translateX(var(--_grid-horizontal-scroll-position));
|
|
78
77
|
display: block;
|
|
79
78
|
position: -webkit-sticky;
|
|
80
79
|
position: sticky;
|
|
@@ -102,7 +101,6 @@ registerStyles(
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
#items {
|
|
105
|
-
transform: translateX(var(--_grid-horizontal-scroll-position));
|
|
106
104
|
flex-grow: 1;
|
|
107
105
|
flex-shrink: 0;
|
|
108
106
|
display: block;
|
|
@@ -164,9 +162,9 @@ registerStyles(
|
|
|
164
162
|
display: none !important;
|
|
165
163
|
}
|
|
166
164
|
|
|
167
|
-
[frozen]
|
|
165
|
+
[frozen],
|
|
166
|
+
[frozen-to-end] {
|
|
168
167
|
z-index: 2;
|
|
169
|
-
transform: translateX(calc(-1 * var(--_grid-horizontal-scroll-position)));
|
|
170
168
|
will-change: transform;
|
|
171
169
|
}
|
|
172
170
|
|
|
@@ -224,6 +222,26 @@ registerStyles(
|
|
|
224
222
|
right: 0;
|
|
225
223
|
}
|
|
226
224
|
|
|
225
|
+
[frozen-to-end] [part~='resize-handle'] {
|
|
226
|
+
left: 0;
|
|
227
|
+
right: auto;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[frozen-to-end] [part~='resize-handle']::before {
|
|
231
|
+
left: 0;
|
|
232
|
+
right: auto;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[first-frozen-to-end] [part~='resize-handle']::before {
|
|
236
|
+
width: 18px;
|
|
237
|
+
transform: none;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Hide resize handle if scrolled to end */
|
|
241
|
+
:host(:not([overflow~='end'])) [first-frozen-to-end] [part~='resize-handle'] {
|
|
242
|
+
display: none;
|
|
243
|
+
}
|
|
244
|
+
|
|
227
245
|
#scroller[column-resizing] {
|
|
228
246
|
-ms-user-select: none;
|
|
229
247
|
-moz-user-select: none;
|
|
@@ -267,10 +285,6 @@ registerStyles(
|
|
|
267
285
|
|
|
268
286
|
/* RTL specific styles */
|
|
269
287
|
|
|
270
|
-
:host([dir='rtl']) *:is(#items, #header, #footer, [frozen]) {
|
|
271
|
-
transform: none;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
288
|
:host([dir='rtl']) #items,
|
|
275
289
|
:host([dir='rtl']) #header,
|
|
276
290
|
:host([dir='rtl']) #footer {
|
|
@@ -296,6 +310,16 @@ registerStyles(
|
|
|
296
310
|
left: 0;
|
|
297
311
|
right: auto;
|
|
298
312
|
}
|
|
313
|
+
|
|
314
|
+
:host([dir='rtl']) [frozen-to-end] [part~='resize-handle'] {
|
|
315
|
+
right: 0;
|
|
316
|
+
left: auto;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
:host([dir='rtl']) [frozen-to-end] [part~='resize-handle']::before {
|
|
320
|
+
right: 0;
|
|
321
|
+
left: auto;
|
|
322
|
+
}
|
|
299
323
|
`,
|
|
300
324
|
{ moduleId: 'vaadin-grid-styles' }
|
|
301
325
|
);
|
package/src/vaadin-grid.d.ts
CHANGED
|
@@ -302,7 +302,7 @@ export interface GridEventMap<TItem> extends HTMLElementEventMap, GridCustomEven
|
|
|
302
302
|
* `loading` | Set when the grid is loading data from data provider | :host
|
|
303
303
|
* `interacting` | Keyboard navigation in interaction mode | :host
|
|
304
304
|
* `navigating` | Keyboard navigation in navigation mode | :host
|
|
305
|
-
* `overflow` | Set when rows are overflowing the grid viewport. Possible values: `top`, `bottom`, `
|
|
305
|
+
* `overflow` | Set when rows are overflowing the grid viewport. Possible values: `top`, `bottom`, `start`, `end` | :host
|
|
306
306
|
* `reordering` | Set when the grid's columns are being reordered | :host
|
|
307
307
|
* `dragover` | Set when the grid (not a specific row) is dragged over | :host
|
|
308
308
|
* `dragging-rows` : Set when grid rows are dragged | :host
|
package/src/vaadin-grid.js
CHANGED
|
@@ -7,7 +7,7 @@ import './vaadin-grid-column.js';
|
|
|
7
7
|
import './vaadin-grid-styles.js';
|
|
8
8
|
import { beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
9
9
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
10
|
-
import { isAndroid, isFirefox, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
10
|
+
import { isAndroid, isChrome, isFirefox, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
11
11
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
12
12
|
import { TabindexMixin } from '@vaadin/component-base/src/tabindex-mixin.js';
|
|
13
13
|
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
@@ -188,7 +188,7 @@ import { StylingMixin } from './vaadin-grid-styling-mixin.js';
|
|
|
188
188
|
* `loading` | Set when the grid is loading data from data provider | :host
|
|
189
189
|
* `interacting` | Keyboard navigation in interaction mode | :host
|
|
190
190
|
* `navigating` | Keyboard navigation in navigation mode | :host
|
|
191
|
-
* `overflow` | Set when rows are overflowing the grid viewport. Possible values: `top`, `bottom`, `
|
|
191
|
+
* `overflow` | Set when rows are overflowing the grid viewport. Possible values: `top`, `bottom`, `start`, `end` | :host
|
|
192
192
|
* `reordering` | Set when the grid's columns are being reordered | :host
|
|
193
193
|
* `dragover` | Set when the grid (not a specific row) is dragged over | :host
|
|
194
194
|
* `dragging-rows` : Set when grid rows are dragged | :host
|
|
@@ -685,13 +685,16 @@ class Grid extends ElementMixin(
|
|
|
685
685
|
// focusable slot wrapper, that is why cells are not focused with
|
|
686
686
|
// mousedown. Workaround: listen for mousedown and focus manually.
|
|
687
687
|
cellContent.addEventListener('mousedown', () => {
|
|
688
|
-
if (
|
|
688
|
+
if (isChrome) {
|
|
689
689
|
// Chrome bug: focusing before mouseup prevents text selection, see http://crbug.com/771903
|
|
690
|
-
const mouseUpListener = () => {
|
|
691
|
-
|
|
690
|
+
const mouseUpListener = (event) => {
|
|
691
|
+
// If focus is on element within the cell content — respect it, do not change
|
|
692
|
+
const contentContainsFocusedElement = cellContent.contains(this.getRootNode().activeElement);
|
|
693
|
+
// Only focus if mouse is released on cell content itself
|
|
694
|
+
const mouseUpWithinCell = cellContent.contains(event.target);
|
|
695
|
+
if (!contentContainsFocusedElement && mouseUpWithinCell) {
|
|
692
696
|
cell.focus();
|
|
693
697
|
}
|
|
694
|
-
// If focus is in the cell content — respect it, do not change.
|
|
695
698
|
document.removeEventListener('mouseup', mouseUpListener, true);
|
|
696
699
|
};
|
|
697
700
|
document.addEventListener('mouseup', mouseUpListener, true);
|
|
@@ -959,6 +962,7 @@ class Grid extends ElementMixin(
|
|
|
959
962
|
_resizeHandler() {
|
|
960
963
|
this._updateDetailsCellHeights();
|
|
961
964
|
this.__updateFooterPositioning();
|
|
965
|
+
this.__updateHorizontalScrollPosition();
|
|
962
966
|
}
|
|
963
967
|
|
|
964
968
|
/** @private */
|
|
@@ -299,10 +299,18 @@ registerStyles(
|
|
|
299
299
|
overflow: hidden;
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
:host([overflow~='
|
|
302
|
+
:host([overflow~='start']) [part~='cell'][last-frozen]:not([part~='details-cell']) {
|
|
303
303
|
border-right-color: var(--_lumo-grid-border-color);
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
[first-frozen-to-end] {
|
|
307
|
+
border-left: var(--_lumo-grid-border-width) solid transparent;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
:host([overflow~='end']) [part~='cell'][first-frozen-to-end]:not([part~='details-cell']) {
|
|
311
|
+
border-left-color: var(--_lumo-grid-border-color);
|
|
312
|
+
}
|
|
313
|
+
|
|
306
314
|
/* Row stripes */
|
|
307
315
|
|
|
308
316
|
:host([theme~='row-stripes']) [part~='row']:not([odd]) [part~='body-cell'],
|
|
@@ -372,9 +380,18 @@ registerStyles(
|
|
|
372
380
|
border-left: var(--_lumo-grid-border-width) solid transparent;
|
|
373
381
|
}
|
|
374
382
|
|
|
375
|
-
:host([dir='rtl']
|
|
383
|
+
:host([dir='rtl']) [first-frozen-to-end] {
|
|
384
|
+
border-left: none;
|
|
385
|
+
border-right: var(--_lumo-grid-border-width) solid transparent;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
:host([dir='rtl'][overflow~='start']) [part~='cell'][last-frozen]:not([part~='details-cell']) {
|
|
376
389
|
border-left-color: var(--_lumo-grid-border-color);
|
|
377
390
|
}
|
|
391
|
+
|
|
392
|
+
:host([dir='rtl'][overflow~='end']) [part~='cell'][first-frozen-to-end]:not([part~='details-cell']) {
|
|
393
|
+
border-right-color: var(--_lumo-grid-border-color);
|
|
394
|
+
}
|
|
378
395
|
`,
|
|
379
396
|
{ moduleId: 'lumo-grid' }
|
|
380
397
|
);
|
|
@@ -114,6 +114,10 @@ registerStyles(
|
|
|
114
114
|
border-right: 1px solid var(--material-divider-color);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
[part~='cell'][first-frozen-to-end] {
|
|
118
|
+
border-left: 1px solid var(--material-divider-color);
|
|
119
|
+
}
|
|
120
|
+
|
|
117
121
|
/* Column resizing */
|
|
118
122
|
|
|
119
123
|
[part~='cell']:not([last-frozen]) [part='resize-handle'] {
|
|
@@ -248,6 +252,11 @@ registerStyles(
|
|
|
248
252
|
border-left: 1px solid var(--material-divider-color);
|
|
249
253
|
}
|
|
250
254
|
|
|
255
|
+
:host([dir='rtl']) [part~='cell'][first-frozen-to-end] {
|
|
256
|
+
border-left: none;
|
|
257
|
+
border-right: 1px solid var(--material-divider-color);
|
|
258
|
+
}
|
|
259
|
+
|
|
251
260
|
:host([dir='rtl']) [part~='cell']:not([last-frozen]) [part='resize-handle'] {
|
|
252
261
|
border-right: none;
|
|
253
262
|
border-left: 1px solid var(--material-divider-color);
|