@vaadin/grid 22.0.0-alpha7
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/LICENSE +190 -0
- package/README.md +79 -0
- package/all-imports.js +1 -0
- package/package.json +58 -0
- package/src/all-imports.js +14 -0
- package/src/array-data-provider.js +145 -0
- package/src/interfaces.d.ts +75 -0
- package/src/vaadin-grid-a11y-mixin.js +158 -0
- package/src/vaadin-grid-active-item-mixin.d.ts +19 -0
- package/src/vaadin-grid-active-item-mixin.js +117 -0
- package/src/vaadin-grid-array-data-provider-mixin.d.ts +16 -0
- package/src/vaadin-grid-array-data-provider-mixin.js +75 -0
- package/src/vaadin-grid-column-group.d.ts +54 -0
- package/src/vaadin-grid-column-group.js +320 -0
- package/src/vaadin-grid-column-reordering-mixin.d.ts +19 -0
- package/src/vaadin-grid-column-reordering-mixin.js +387 -0
- package/src/vaadin-grid-column-resizing-mixin.js +111 -0
- package/src/vaadin-grid-column.d.ts +133 -0
- package/src/vaadin-grid-column.js +745 -0
- package/src/vaadin-grid-data-provider-mixin.d.ts +108 -0
- package/src/vaadin-grid-data-provider-mixin.js +520 -0
- package/src/vaadin-grid-drag-and-drop-mixin.d.ts +69 -0
- package/src/vaadin-grid-drag-and-drop-mixin.js +433 -0
- package/src/vaadin-grid-dynamic-columns-mixin.js +180 -0
- package/src/vaadin-grid-event-context-mixin.d.ts +33 -0
- package/src/vaadin-grid-event-context-mixin.js +57 -0
- package/src/vaadin-grid-filter-column.d.ts +35 -0
- package/src/vaadin-grid-filter-column.js +120 -0
- package/src/vaadin-grid-filter-mixin.js +76 -0
- package/src/vaadin-grid-filter.d.ts +67 -0
- package/src/vaadin-grid-filter.js +125 -0
- package/src/vaadin-grid-helpers.js +23 -0
- package/src/vaadin-grid-keyboard-navigation-mixin.js +891 -0
- package/src/vaadin-grid-row-details-mixin.d.ts +44 -0
- package/src/vaadin-grid-row-details-mixin.js +200 -0
- package/src/vaadin-grid-scroll-mixin.d.ts +18 -0
- package/src/vaadin-grid-scroll-mixin.js +202 -0
- package/src/vaadin-grid-selection-column.d.ts +71 -0
- package/src/vaadin-grid-selection-column.js +285 -0
- package/src/vaadin-grid-selection-mixin.d.ts +30 -0
- package/src/vaadin-grid-selection-mixin.js +93 -0
- package/src/vaadin-grid-sort-column.d.ts +63 -0
- package/src/vaadin-grid-sort-column.js +118 -0
- package/src/vaadin-grid-sort-mixin.d.ts +15 -0
- package/src/vaadin-grid-sort-mixin.js +139 -0
- package/src/vaadin-grid-sorter.d.ts +94 -0
- package/src/vaadin-grid-sorter.js +230 -0
- package/src/vaadin-grid-styles.js +297 -0
- package/src/vaadin-grid-styling-mixin.d.ts +37 -0
- package/src/vaadin-grid-styling-mixin.js +71 -0
- package/src/vaadin-grid-tree-column.d.ts +36 -0
- package/src/vaadin-grid-tree-column.js +119 -0
- package/src/vaadin-grid-tree-toggle.d.ts +104 -0
- package/src/vaadin-grid-tree-toggle.js +205 -0
- package/src/vaadin-grid.d.ts +397 -0
- package/src/vaadin-grid.js +1004 -0
- package/theme/lumo/all-imports.js +11 -0
- package/theme/lumo/vaadin-grid-column-group.js +1 -0
- package/theme/lumo/vaadin-grid-column.js +1 -0
- package/theme/lumo/vaadin-grid-filter-column.js +2 -0
- package/theme/lumo/vaadin-grid-filter.js +2 -0
- package/theme/lumo/vaadin-grid-selection-column.js +2 -0
- package/theme/lumo/vaadin-grid-sort-column.js +2 -0
- package/theme/lumo/vaadin-grid-sorter-styles.js +53 -0
- package/theme/lumo/vaadin-grid-sorter.js +2 -0
- package/theme/lumo/vaadin-grid-styles.js +378 -0
- package/theme/lumo/vaadin-grid-tree-column.js +2 -0
- package/theme/lumo/vaadin-grid-tree-toggle-styles.js +112 -0
- package/theme/lumo/vaadin-grid-tree-toggle.js +2 -0
- package/theme/lumo/vaadin-grid.js +9 -0
- package/theme/material/all-imports.js +11 -0
- package/theme/material/vaadin-grid-column-group.js +1 -0
- package/theme/material/vaadin-grid-column.js +1 -0
- package/theme/material/vaadin-grid-filter-column.js +2 -0
- package/theme/material/vaadin-grid-filter.js +2 -0
- package/theme/material/vaadin-grid-selection-column.js +2 -0
- package/theme/material/vaadin-grid-sort-column.js +2 -0
- package/theme/material/vaadin-grid-sorter-styles.js +72 -0
- package/theme/material/vaadin-grid-sorter.js +2 -0
- package/theme/material/vaadin-grid-styles.js +252 -0
- package/theme/material/vaadin-grid-tree-column.js +2 -0
- package/theme/material/vaadin-grid-tree-toggle-styles.js +42 -0
- package/theme/material/vaadin-grid-tree-toggle.js +2 -0
- package/theme/material/vaadin-grid.js +2 -0
- package/vaadin-grid-column-group.d.ts +1 -0
- package/vaadin-grid-column-group.js +3 -0
- package/vaadin-grid-column.d.ts +1 -0
- package/vaadin-grid-column.js +3 -0
- package/vaadin-grid-filter-column.d.ts +1 -0
- package/vaadin-grid-filter-column.js +3 -0
- package/vaadin-grid-filter.d.ts +1 -0
- package/vaadin-grid-filter.js +3 -0
- package/vaadin-grid-selection-column.d.ts +1 -0
- package/vaadin-grid-selection-column.js +3 -0
- package/vaadin-grid-sort-column.d.ts +1 -0
- package/vaadin-grid-sort-column.js +3 -0
- package/vaadin-grid-sorter.d.ts +1 -0
- package/vaadin-grid-sorter.js +3 -0
- package/vaadin-grid-tree-column.d.ts +1 -0
- package/vaadin-grid-tree-column.js +3 -0
- package/vaadin-grid-tree-toggle.d.ts +1 -0
- package/vaadin-grid-tree-toggle.js +3 -0
- package/vaadin-grid.d.ts +3 -0
- package/vaadin-grid.js +4 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
const DropMode = {
|
|
7
|
+
BETWEEN: 'between',
|
|
8
|
+
ON_TOP: 'on-top',
|
|
9
|
+
ON_TOP_OR_BETWEEN: 'on-top-or-between',
|
|
10
|
+
ON_GRID: 'on-grid'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const DropLocation = {
|
|
14
|
+
ON_TOP: 'on-top',
|
|
15
|
+
ABOVE: 'above',
|
|
16
|
+
BELOW: 'below',
|
|
17
|
+
EMPTY: 'empty'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @polymerMixin
|
|
22
|
+
*/
|
|
23
|
+
export const DragAndDropMixin = (superClass) =>
|
|
24
|
+
class DragAndDropMixin extends superClass {
|
|
25
|
+
static get properties() {
|
|
26
|
+
return {
|
|
27
|
+
/**
|
|
28
|
+
* Defines the locations within the Grid row where an element can be dropped.
|
|
29
|
+
*
|
|
30
|
+
* Possible values are:
|
|
31
|
+
* - `between`: The drop event can happen between Grid rows.
|
|
32
|
+
* - `on-top`: The drop event can happen on top of Grid rows.
|
|
33
|
+
* - `on-top-or-between`: The drop event can happen either on top of or between Grid rows.
|
|
34
|
+
* - `on-grid`: The drop event will not happen on any specific row, it will show the drop target outline around the whole grid.
|
|
35
|
+
* @attr {between|on-top|on-top-or-between|on-grid} drop-mode
|
|
36
|
+
* @type {GridDropMode | null | undefined}
|
|
37
|
+
*/
|
|
38
|
+
dropMode: String,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Marks the grid's rows to be available for dragging.
|
|
42
|
+
* @attr {boolean} rows-draggable
|
|
43
|
+
*/
|
|
44
|
+
rowsDraggable: Boolean,
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A function that filters dragging of specific grid rows. The return value should be false
|
|
48
|
+
* if dragging of the row should be disabled.
|
|
49
|
+
*
|
|
50
|
+
* Receives one argument:
|
|
51
|
+
* - `model` The object with the properties related with
|
|
52
|
+
* the rendered item, contains:
|
|
53
|
+
* - `model.index` The index of the item.
|
|
54
|
+
* - `model.item` The item.
|
|
55
|
+
* - `model.expanded` Sublevel toggle state.
|
|
56
|
+
* - `model.level` Level of the tree represented with a horizontal offset of the toggle button.
|
|
57
|
+
* - `model.selected` Selected state.
|
|
58
|
+
*
|
|
59
|
+
* @type {GridDragAndDropFilter | null | undefined}
|
|
60
|
+
*/
|
|
61
|
+
dragFilter: Function,
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A function that filters dropping on specific grid rows. The return value should be false
|
|
65
|
+
* if dropping on the row should be disabled.
|
|
66
|
+
*
|
|
67
|
+
* Receives one argument:
|
|
68
|
+
* - `model` The object with the properties related with
|
|
69
|
+
* the rendered item, contains:
|
|
70
|
+
* - `model.index` The index of the item.
|
|
71
|
+
* - `model.item` The item.
|
|
72
|
+
* - `model.expanded` Sublevel toggle state.
|
|
73
|
+
* - `model.level` Level of the tree represented with a horizontal offset of the toggle button.
|
|
74
|
+
* - `model.selected` Selected state.
|
|
75
|
+
*
|
|
76
|
+
* @type {GridDragAndDropFilter | null | undefined}
|
|
77
|
+
*/
|
|
78
|
+
dropFilter: Function,
|
|
79
|
+
|
|
80
|
+
/** @private */
|
|
81
|
+
__dndAutoScrollThreshold: {
|
|
82
|
+
value: 50
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static get observers() {
|
|
88
|
+
return ['_dragDropAccessChanged(rowsDraggable, dropMode, dragFilter, dropFilter)'];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** @protected */
|
|
92
|
+
ready() {
|
|
93
|
+
super.ready();
|
|
94
|
+
this.$.table.addEventListener('dragstart', this._onDragStart.bind(this));
|
|
95
|
+
this.$.table.addEventListener('dragend', this._onDragEnd.bind(this));
|
|
96
|
+
this.$.table.addEventListener('dragover', this._onDragOver.bind(this));
|
|
97
|
+
this.$.table.addEventListener('dragleave', this._onDragLeave.bind(this));
|
|
98
|
+
this.$.table.addEventListener('drop', this._onDrop.bind(this));
|
|
99
|
+
this.$.table.addEventListener('dragenter', (e) => {
|
|
100
|
+
if (this.dropMode) {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @private */
|
|
108
|
+
_onDragStart(e) {
|
|
109
|
+
if (this.rowsDraggable) {
|
|
110
|
+
let row = e.target;
|
|
111
|
+
if (row.localName === 'vaadin-grid-cell-content') {
|
|
112
|
+
// The draggable node is the cell content element on browsers that support native shadow
|
|
113
|
+
row = row.assignedSlot.parentNode.parentNode;
|
|
114
|
+
}
|
|
115
|
+
if (row.parentNode !== this.$.items) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
e.stopPropagation();
|
|
120
|
+
this.toggleAttribute('dragging-rows', true);
|
|
121
|
+
|
|
122
|
+
if (this._safari) {
|
|
123
|
+
// Safari doesn't position drag images from transformed
|
|
124
|
+
// elements properly so we need to switch to use top temporarily
|
|
125
|
+
const transform = row.style.transform;
|
|
126
|
+
row.style.top = /translateY\((.*)\)/.exec(transform)[1];
|
|
127
|
+
row.style.transform = 'none';
|
|
128
|
+
requestAnimationFrame(() => {
|
|
129
|
+
row.style.top = '';
|
|
130
|
+
row.style.transform = transform;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const rowRect = row.getBoundingClientRect();
|
|
135
|
+
|
|
136
|
+
if (this._ios) {
|
|
137
|
+
e.dataTransfer.setDragImage(row);
|
|
138
|
+
} else {
|
|
139
|
+
e.dataTransfer.setDragImage(row, e.clientX - rowRect.left, e.clientY - rowRect.top);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let rows = [row];
|
|
143
|
+
if (this._isSelected(row._item)) {
|
|
144
|
+
rows = this.__getViewportRows()
|
|
145
|
+
.filter((row) => this._isSelected(row._item))
|
|
146
|
+
.filter((row) => !this.dragFilter || this.dragFilter(this.__getRowModel(row)));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Set the default transfer data
|
|
150
|
+
e.dataTransfer.setData('text', this.__formatDefaultTransferData(rows));
|
|
151
|
+
|
|
152
|
+
row.setAttribute('dragstart', rows.length > 1 ? rows.length : '');
|
|
153
|
+
this.style.setProperty('--_grid-drag-start-x', `${e.clientX - rowRect.left + 20}px`);
|
|
154
|
+
this.style.setProperty('--_grid-drag-start-y', `${e.clientY - rowRect.top + 10}px`);
|
|
155
|
+
|
|
156
|
+
requestAnimationFrame(() => {
|
|
157
|
+
row.removeAttribute('dragstart');
|
|
158
|
+
this.updateStyles({ '--_grid-drag-start-x': '', '--_grid-drag-start-y': '' });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const event = new CustomEvent('grid-dragstart', {
|
|
162
|
+
detail: {
|
|
163
|
+
draggedItems: rows.map((row) => row._item),
|
|
164
|
+
setDragData: (type, data) => e.dataTransfer.setData(type, data),
|
|
165
|
+
setDraggedItemsCount: (count) => row.setAttribute('dragstart', count)
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
event.originalEvent = e;
|
|
169
|
+
this.dispatchEvent(event);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** @private */
|
|
174
|
+
_onDragEnd(e) {
|
|
175
|
+
this.toggleAttribute('dragging-rows', false);
|
|
176
|
+
e.stopPropagation();
|
|
177
|
+
const event = new CustomEvent('grid-dragend');
|
|
178
|
+
event.originalEvent = e;
|
|
179
|
+
this.dispatchEvent(event);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** @private */
|
|
183
|
+
_onDragLeave(e) {
|
|
184
|
+
e.stopPropagation();
|
|
185
|
+
this._clearDragStyles();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** @private */
|
|
189
|
+
_onDragOver(e) {
|
|
190
|
+
if (this.dropMode) {
|
|
191
|
+
this._dropLocation = undefined;
|
|
192
|
+
this._dragOverItem = undefined;
|
|
193
|
+
|
|
194
|
+
if (this.__dndAutoScroll(e.clientY)) {
|
|
195
|
+
this._clearDragStyles();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let row = e.composedPath().filter((node) => node.localName === 'tr')[0];
|
|
200
|
+
|
|
201
|
+
if (!this._effectiveSize || this.dropMode === DropMode.ON_GRID) {
|
|
202
|
+
// The grid is empty or "on-grid" drop mode was used, always default to "empty"
|
|
203
|
+
this._dropLocation = DropLocation.EMPTY;
|
|
204
|
+
} else if (!row || row.parentNode !== this.$.items) {
|
|
205
|
+
// The dragover didn't occur on a body row but the grid has items
|
|
206
|
+
if (row) {
|
|
207
|
+
// The dragover occurred over a header/footer row
|
|
208
|
+
return;
|
|
209
|
+
} else if (this.dropMode === DropMode.BETWEEN || this.dropMode === DropMode.ON_TOP_OR_BETWEEN) {
|
|
210
|
+
// The drop mode allows setting the last row as the drag over item
|
|
211
|
+
row = Array.from(this.$.items.children)
|
|
212
|
+
.filter((row) => !row.hidden)
|
|
213
|
+
.pop();
|
|
214
|
+
this._dropLocation = DropLocation.BELOW;
|
|
215
|
+
} else {
|
|
216
|
+
// Drop mode on-top used but the dragover didn't occur over one of the existing rows
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
// The dragover occurred on a body row, determine the drop location from coordinates
|
|
221
|
+
const rowRect = row.getBoundingClientRect();
|
|
222
|
+
|
|
223
|
+
this._dropLocation = DropLocation.ON_TOP;
|
|
224
|
+
|
|
225
|
+
if (this.dropMode === DropMode.BETWEEN) {
|
|
226
|
+
const dropAbove = e.clientY - rowRect.top < rowRect.bottom - e.clientY;
|
|
227
|
+
this._dropLocation = dropAbove ? DropLocation.ABOVE : DropLocation.BELOW;
|
|
228
|
+
} else if (this.dropMode === DropMode.ON_TOP_OR_BETWEEN) {
|
|
229
|
+
if (e.clientY - rowRect.top < rowRect.height / 3) {
|
|
230
|
+
this._dropLocation = DropLocation.ABOVE;
|
|
231
|
+
} else if (e.clientY - rowRect.top > (rowRect.height / 3) * 2) {
|
|
232
|
+
this._dropLocation = DropLocation.BELOW;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (row && row.hasAttribute('drop-disabled')) {
|
|
238
|
+
this._dropLocation = undefined;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
e.stopPropagation();
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
|
|
245
|
+
if (this._dropLocation === DropLocation.EMPTY) {
|
|
246
|
+
this.toggleAttribute('dragover', true);
|
|
247
|
+
} else if (row) {
|
|
248
|
+
this._dragOverItem = row._item;
|
|
249
|
+
if (row.getAttribute('dragover') !== this._dropLocation) {
|
|
250
|
+
row.setAttribute('dragover', this._dropLocation);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
this._clearDragStyles();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** @private */
|
|
259
|
+
__dndAutoScroll(clientY) {
|
|
260
|
+
if (this.__dndAutoScrolling) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const headerBottom = this.$.header.getBoundingClientRect().bottom;
|
|
265
|
+
const footerTop = this.$.footer.getBoundingClientRect().top;
|
|
266
|
+
const topDiff = headerBottom - clientY + this.__dndAutoScrollThreshold;
|
|
267
|
+
const bottomDiff = clientY - footerTop + this.__dndAutoScrollThreshold;
|
|
268
|
+
let scrollTopDelta = 0;
|
|
269
|
+
|
|
270
|
+
if (bottomDiff > 0) {
|
|
271
|
+
scrollTopDelta = bottomDiff * 2;
|
|
272
|
+
} else if (topDiff > 0) {
|
|
273
|
+
scrollTopDelta = -topDiff * 2;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (scrollTopDelta) {
|
|
277
|
+
const scrollTop = this.$.table.scrollTop;
|
|
278
|
+
this.$.table.scrollTop += scrollTopDelta;
|
|
279
|
+
const scrollTopChanged = scrollTop !== this.$.table.scrollTop;
|
|
280
|
+
if (scrollTopChanged) {
|
|
281
|
+
this.__dndAutoScrolling = true;
|
|
282
|
+
// Disallow more auto-scrolls within 20ms
|
|
283
|
+
setTimeout(() => (this.__dndAutoScrolling = false), 20);
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** @private */
|
|
290
|
+
__getViewportRows() {
|
|
291
|
+
const headerBottom = this.$.header.getBoundingClientRect().bottom;
|
|
292
|
+
const footerTop = this.$.footer.getBoundingClientRect().top;
|
|
293
|
+
return Array.from(this.$.items.children).filter((row) => {
|
|
294
|
+
const rowRect = row.getBoundingClientRect();
|
|
295
|
+
return rowRect.bottom > headerBottom && rowRect.top < footerTop;
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** @protected */
|
|
300
|
+
_clearDragStyles() {
|
|
301
|
+
this.removeAttribute('dragover');
|
|
302
|
+
Array.from(this.$.items.children).forEach((row) => row.removeAttribute('dragover'));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** @private */
|
|
306
|
+
_onDrop(e) {
|
|
307
|
+
if (this.dropMode) {
|
|
308
|
+
e.stopPropagation();
|
|
309
|
+
e.preventDefault();
|
|
310
|
+
|
|
311
|
+
const dragData =
|
|
312
|
+
e.dataTransfer.types &&
|
|
313
|
+
Array.from(e.dataTransfer.types).map((type) => {
|
|
314
|
+
return {
|
|
315
|
+
type,
|
|
316
|
+
data: e.dataTransfer.getData(type)
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
this._clearDragStyles();
|
|
321
|
+
|
|
322
|
+
const event = new CustomEvent('grid-drop', {
|
|
323
|
+
bubbles: e.bubbles,
|
|
324
|
+
cancelable: e.cancelable,
|
|
325
|
+
detail: {
|
|
326
|
+
dropTargetItem: this._dragOverItem,
|
|
327
|
+
dropLocation: this._dropLocation,
|
|
328
|
+
dragData
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
event.originalEvent = e;
|
|
332
|
+
this.dispatchEvent(event);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** @private */
|
|
337
|
+
__formatDefaultTransferData(rows) {
|
|
338
|
+
return rows
|
|
339
|
+
.map((row) => {
|
|
340
|
+
return Array.from(row.children)
|
|
341
|
+
.filter((cell) => !cell.hidden && cell.getAttribute('part').indexOf('details-cell') === -1)
|
|
342
|
+
.sort((a, b) => {
|
|
343
|
+
return a._column._order > b._column._order ? 1 : -1;
|
|
344
|
+
})
|
|
345
|
+
.map((cell) => cell._content.textContent.trim())
|
|
346
|
+
.filter((content) => content)
|
|
347
|
+
.join('\t');
|
|
348
|
+
})
|
|
349
|
+
.join('\n');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** @private */
|
|
353
|
+
_dragDropAccessChanged() {
|
|
354
|
+
this.filterDragAndDrop();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Runs the `dragFilter` and `dropFilter` hooks for the visible cells.
|
|
359
|
+
* If the filter depends on varying conditions, you may need to
|
|
360
|
+
* call this function manually in order to update the draggability when
|
|
361
|
+
* the conditions change.
|
|
362
|
+
*/
|
|
363
|
+
filterDragAndDrop() {
|
|
364
|
+
Array.from(this.$.items.children)
|
|
365
|
+
.filter((row) => !row.hidden)
|
|
366
|
+
.forEach((row) => {
|
|
367
|
+
this._filterDragAndDrop(row, this.__getRowModel(row));
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* @param {!HTMLElement} row
|
|
373
|
+
* @param {!GridItemModel} model
|
|
374
|
+
* @protected
|
|
375
|
+
*/
|
|
376
|
+
_filterDragAndDrop(row, model) {
|
|
377
|
+
const dragDisabled = !this.rowsDraggable || (this.dragFilter && !this.dragFilter(model));
|
|
378
|
+
const dropDisabled = !this.dropMode || (this.dropFilter && !this.dropFilter(model));
|
|
379
|
+
|
|
380
|
+
const draggableElements = Array.from(row.children).map((cell) => cell._content);
|
|
381
|
+
|
|
382
|
+
draggableElements.forEach((e) => {
|
|
383
|
+
if (dragDisabled) {
|
|
384
|
+
e.removeAttribute('draggable');
|
|
385
|
+
} else {
|
|
386
|
+
e.setAttribute('draggable', true);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
row.toggleAttribute('drag-disabled', !!dragDisabled);
|
|
391
|
+
row.toggleAttribute('drop-disabled', !!dropDisabled);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Fired when starting to drag grid rows.
|
|
396
|
+
*
|
|
397
|
+
* @event grid-dragstart
|
|
398
|
+
* @param {Object} originalEvent The native dragstart event
|
|
399
|
+
* @param {Object} detail
|
|
400
|
+
* @param {Object} detail.draggedItems the items in the visible viewport that are dragged
|
|
401
|
+
* @param {Function} detail.setDraggedItemsCount Overrides the default number shown in the drag image on multi row drag.
|
|
402
|
+
* Parameter is of type number.
|
|
403
|
+
* @param {Function} detail.setDragData Sets dataTransfer data for the drag operation.
|
|
404
|
+
* Note that "text" is the only data type supported by all the browsers the grid currently supports (including IE11).
|
|
405
|
+
* The function takes two parameters:
|
|
406
|
+
* - type:string The type of the data
|
|
407
|
+
* - data:string The data
|
|
408
|
+
*/
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Fired when the dragging of the rows ends.
|
|
412
|
+
*
|
|
413
|
+
* @event grid-dragend
|
|
414
|
+
* @param {Object} originalEvent The native dragend event
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Fired when a drop occurs on top of the grid.
|
|
419
|
+
*
|
|
420
|
+
* @event grid-drop
|
|
421
|
+
* @param {Object} originalEvent The native drop event
|
|
422
|
+
* @param {Object} detail
|
|
423
|
+
* @param {Object} detail.dropTargetItem The item of the grid row on which the drop occurred.
|
|
424
|
+
* @param {string} detail.dropLocation The position at which the drop event took place relative to a row.
|
|
425
|
+
* Depending on the dropMode value, the drop location can be one of the following
|
|
426
|
+
* - `on-top`: when the drop occurred on top of the row
|
|
427
|
+
* - `above`: when the drop occurred above the row
|
|
428
|
+
* - `below`: when the drop occurred below the row
|
|
429
|
+
* - `empty`: when the drop occurred over the grid, not relative to any specific row
|
|
430
|
+
* @param {string} detail.dragData An array of items with the payload as a string representation as the
|
|
431
|
+
* `data` property and the type of the data as `type` property.
|
|
432
|
+
*/
|
|
433
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
7
|
+
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
8
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
9
|
+
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @polymerMixin
|
|
13
|
+
*/
|
|
14
|
+
export const DynamicColumnsMixin = (superClass) =>
|
|
15
|
+
class DynamicColumnsMixin extends superClass {
|
|
16
|
+
static get properties() {
|
|
17
|
+
return {
|
|
18
|
+
/**
|
|
19
|
+
* @protected
|
|
20
|
+
*/
|
|
21
|
+
_columnTree: Object
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @protected */
|
|
26
|
+
ready() {
|
|
27
|
+
super.ready();
|
|
28
|
+
this._addNodeObserver();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @private */
|
|
32
|
+
_hasColumnGroups(columns) {
|
|
33
|
+
for (let i = 0; i < columns.length; i++) {
|
|
34
|
+
if (columns[i].localName === 'vaadin-grid-column-group') {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {!GridColumnGroup} el
|
|
44
|
+
* @return {!Array<!GridColumn>}
|
|
45
|
+
* @protected
|
|
46
|
+
*/
|
|
47
|
+
_getChildColumns(el) {
|
|
48
|
+
return FlattenedNodesObserver.getFlattenedNodes(el).filter(this._isColumnElement);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @private */
|
|
52
|
+
_flattenColumnGroups(columns) {
|
|
53
|
+
return columns
|
|
54
|
+
.map((col) => {
|
|
55
|
+
if (col.localName === 'vaadin-grid-column-group') {
|
|
56
|
+
return this._getChildColumns(col);
|
|
57
|
+
} else {
|
|
58
|
+
return [col];
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.reduce((prev, curr) => {
|
|
62
|
+
return prev.concat(curr);
|
|
63
|
+
}, []);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** @private */
|
|
67
|
+
_getColumnTree() {
|
|
68
|
+
const rootColumns = FlattenedNodesObserver.getFlattenedNodes(this).filter(this._isColumnElement);
|
|
69
|
+
const columnTree = [];
|
|
70
|
+
|
|
71
|
+
for (let c = rootColumns; ; ) {
|
|
72
|
+
columnTree.push(c);
|
|
73
|
+
if (!this._hasColumnGroups(c)) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
c = this._flattenColumnGroups(c);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return columnTree;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** @protected */
|
|
83
|
+
_updateColumnTree() {
|
|
84
|
+
const columnTree = this._getColumnTree();
|
|
85
|
+
if (!this._arrayEquals(columnTree, this._columnTree)) {
|
|
86
|
+
this._columnTree = columnTree;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** @private */
|
|
91
|
+
_addNodeObserver() {
|
|
92
|
+
this._observer = new FlattenedNodesObserver(this, (info) => {
|
|
93
|
+
const hasColumnElements = (nodeCollection) => nodeCollection.filter(this._isColumnElement).length > 0;
|
|
94
|
+
if (hasColumnElements(info.addedNodes) || hasColumnElements(info.removedNodes)) {
|
|
95
|
+
const allRemovedCells = info.removedNodes.flatMap((c) => c._allCells);
|
|
96
|
+
const filterNotConnected = (element) =>
|
|
97
|
+
allRemovedCells.filter((cell) => cell._content.contains(element)).length;
|
|
98
|
+
|
|
99
|
+
this.__removeSorters(this._sorters.filter(filterNotConnected));
|
|
100
|
+
this.__removeFilters(this._filters.filter(filterNotConnected));
|
|
101
|
+
this._updateColumnTree();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this._debouncerCheckImports = Debouncer.debounce(
|
|
105
|
+
this._debouncerCheckImports,
|
|
106
|
+
timeOut.after(2000),
|
|
107
|
+
this._checkImports.bind(this)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
this._ensureFirstPageLoaded();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @private */
|
|
115
|
+
_arrayEquals(arr1, arr2) {
|
|
116
|
+
if (!arr1 || !arr2 || arr1.length != arr2.length) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (let i = 0, l = arr1.length; i < l; i++) {
|
|
121
|
+
// Check if we have nested arrays
|
|
122
|
+
if (arr1[i] instanceof Array && arr2[i] instanceof Array) {
|
|
123
|
+
// recurse into the nested arrays
|
|
124
|
+
if (!this._arrayEquals(arr1[i], arr2[i])) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
} else if (arr1[i] != arr2[i]) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @protected */
|
|
135
|
+
_checkImports() {
|
|
136
|
+
[
|
|
137
|
+
'vaadin-grid-column-group',
|
|
138
|
+
'vaadin-grid-filter',
|
|
139
|
+
'vaadin-grid-filter-column',
|
|
140
|
+
'vaadin-grid-tree-toggle',
|
|
141
|
+
'vaadin-grid-selection-column',
|
|
142
|
+
'vaadin-grid-sort-column',
|
|
143
|
+
'vaadin-grid-sorter'
|
|
144
|
+
].forEach((elementName) => {
|
|
145
|
+
const element = this.querySelector(elementName);
|
|
146
|
+
if (element && !(element instanceof PolymerElement)) {
|
|
147
|
+
console.warn(`Make sure you have imported the required module for <${elementName}> element.`);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @protected */
|
|
153
|
+
_updateFirstAndLastColumn() {
|
|
154
|
+
Array.from(this.shadowRoot.querySelectorAll('tr')).forEach((row) => this._updateFirstAndLastColumnForRow(row));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {!HTMLElement} row
|
|
159
|
+
* @protected
|
|
160
|
+
*/
|
|
161
|
+
_updateFirstAndLastColumnForRow(row) {
|
|
162
|
+
Array.from(row.querySelectorAll('[part~="cell"]:not([part~="details-cell"])'))
|
|
163
|
+
.sort((a, b) => {
|
|
164
|
+
return a._column._order - b._column._order;
|
|
165
|
+
})
|
|
166
|
+
.forEach((cell, cellIndex, children) => {
|
|
167
|
+
cell.toggleAttribute('first-column', cellIndex === 0);
|
|
168
|
+
cell.toggleAttribute('last-column', cellIndex === children.length - 1);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {!Node} node
|
|
174
|
+
* @return {boolean}
|
|
175
|
+
* @protected
|
|
176
|
+
*/
|
|
177
|
+
_isColumnElement(node) {
|
|
178
|
+
return node.nodeType === Node.ELEMENT_NODE && /\bcolumn\b/.test(node.localName);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GridEventContext } from './interfaces';
|
|
2
|
+
|
|
3
|
+
declare function EventContextMixin<TItem, T extends new (...args: any[]) => {}>(
|
|
4
|
+
base: T
|
|
5
|
+
): T & EventContextMixinConstructor<TItem>;
|
|
6
|
+
|
|
7
|
+
interface EventContextMixinConstructor<TItem> {
|
|
8
|
+
new (...args: any[]): EventContextMixin<TItem>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface EventContextMixin<TItem> {
|
|
12
|
+
/**
|
|
13
|
+
* Returns an object with context information about the event target:
|
|
14
|
+
* - `item`: the data object corresponding to the targeted row (not specified when targeting header or footer)
|
|
15
|
+
* - `column`: the column element corresponding to the targeted cell (not specified when targeting row details)
|
|
16
|
+
* - `section`: whether the event targeted the body, header, footer or details of the grid
|
|
17
|
+
*
|
|
18
|
+
* These additional properties are included when `item` is specified:
|
|
19
|
+
* - `index`: the index of the item
|
|
20
|
+
* - `selected`: the selected state of the item
|
|
21
|
+
* - `detailsOpened`: whether the row details are open for the item
|
|
22
|
+
* - `expanded`: the expanded state of the tree toggle
|
|
23
|
+
* - `level`: the tree hierarchy level
|
|
24
|
+
*
|
|
25
|
+
* The returned object is populated only when a grid cell, header, footer or row details is found in `event.composedPath()`.
|
|
26
|
+
* This means mostly mouse and keyboard events. If such a grid part is not found in the path, an empty object is returned.
|
|
27
|
+
* This may be the case eg. if the event is fired on the `<vaadin-grid>` element and not any deeper in the DOM, or if
|
|
28
|
+
* the event targets the empty part of the grid body.
|
|
29
|
+
*/
|
|
30
|
+
getEventContext(event: Event): GridEventContext<TItem>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { EventContextMixin, EventContextMixinConstructor };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @polymerMixin
|
|
9
|
+
*/
|
|
10
|
+
export const EventContextMixin = (superClass) =>
|
|
11
|
+
class EventContextMixin extends superClass {
|
|
12
|
+
/**
|
|
13
|
+
* Returns an object with context information about the event target:
|
|
14
|
+
* - `item`: the data object corresponding to the targeted row (not specified when targeting header or footer)
|
|
15
|
+
* - `column`: the column element corresponding to the targeted cell (not specified when targeting row details)
|
|
16
|
+
* - `section`: whether the event targeted the body, header, footer or details of the grid
|
|
17
|
+
*
|
|
18
|
+
* These additional properties are included when `item` is specified:
|
|
19
|
+
* - `index`: the index of the item
|
|
20
|
+
* - `selected`: the selected state of the item
|
|
21
|
+
* - `detailsOpened`: whether the row details are open for the item
|
|
22
|
+
* - `expanded`: the expanded state of the tree toggle
|
|
23
|
+
* - `level`: the tree hierarchy level
|
|
24
|
+
*
|
|
25
|
+
* The returned object is populated only when a grid cell, header, footer or row details is found in `event.composedPath()`.
|
|
26
|
+
* This means mostly mouse and keyboard events. If such a grid part is not found in the path, an empty object is returned.
|
|
27
|
+
* This may be the case eg. if the event is fired on the `<vaadin-grid>` element and not any deeper in the DOM, or if
|
|
28
|
+
* the event targets the empty part of the grid body.
|
|
29
|
+
*
|
|
30
|
+
* @param {!Event} event
|
|
31
|
+
* @return {GridEventContext}
|
|
32
|
+
*/
|
|
33
|
+
getEventContext(event) {
|
|
34
|
+
const context = {};
|
|
35
|
+
|
|
36
|
+
const path = event.composedPath();
|
|
37
|
+
const cell = path[path.indexOf(this.$.table) - 3];
|
|
38
|
+
|
|
39
|
+
if (!cell) {
|
|
40
|
+
return context;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
context.section = ['body', 'header', 'footer', 'details'].filter(
|
|
44
|
+
(section) => cell.getAttribute('part').indexOf(section) > -1
|
|
45
|
+
)[0];
|
|
46
|
+
|
|
47
|
+
if (cell._column) {
|
|
48
|
+
context.column = cell._column;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (context.section === 'body' || context.section === 'details') {
|
|
52
|
+
Object.assign(context, this.__getRowModel(cell.parentElement));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return context;
|
|
56
|
+
}
|
|
57
|
+
};
|