@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.
Files changed (104) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +79 -0
  3. package/all-imports.js +1 -0
  4. package/package.json +58 -0
  5. package/src/all-imports.js +14 -0
  6. package/src/array-data-provider.js +145 -0
  7. package/src/interfaces.d.ts +75 -0
  8. package/src/vaadin-grid-a11y-mixin.js +158 -0
  9. package/src/vaadin-grid-active-item-mixin.d.ts +19 -0
  10. package/src/vaadin-grid-active-item-mixin.js +117 -0
  11. package/src/vaadin-grid-array-data-provider-mixin.d.ts +16 -0
  12. package/src/vaadin-grid-array-data-provider-mixin.js +75 -0
  13. package/src/vaadin-grid-column-group.d.ts +54 -0
  14. package/src/vaadin-grid-column-group.js +320 -0
  15. package/src/vaadin-grid-column-reordering-mixin.d.ts +19 -0
  16. package/src/vaadin-grid-column-reordering-mixin.js +387 -0
  17. package/src/vaadin-grid-column-resizing-mixin.js +111 -0
  18. package/src/vaadin-grid-column.d.ts +133 -0
  19. package/src/vaadin-grid-column.js +745 -0
  20. package/src/vaadin-grid-data-provider-mixin.d.ts +108 -0
  21. package/src/vaadin-grid-data-provider-mixin.js +520 -0
  22. package/src/vaadin-grid-drag-and-drop-mixin.d.ts +69 -0
  23. package/src/vaadin-grid-drag-and-drop-mixin.js +433 -0
  24. package/src/vaadin-grid-dynamic-columns-mixin.js +180 -0
  25. package/src/vaadin-grid-event-context-mixin.d.ts +33 -0
  26. package/src/vaadin-grid-event-context-mixin.js +57 -0
  27. package/src/vaadin-grid-filter-column.d.ts +35 -0
  28. package/src/vaadin-grid-filter-column.js +120 -0
  29. package/src/vaadin-grid-filter-mixin.js +76 -0
  30. package/src/vaadin-grid-filter.d.ts +67 -0
  31. package/src/vaadin-grid-filter.js +125 -0
  32. package/src/vaadin-grid-helpers.js +23 -0
  33. package/src/vaadin-grid-keyboard-navigation-mixin.js +891 -0
  34. package/src/vaadin-grid-row-details-mixin.d.ts +44 -0
  35. package/src/vaadin-grid-row-details-mixin.js +200 -0
  36. package/src/vaadin-grid-scroll-mixin.d.ts +18 -0
  37. package/src/vaadin-grid-scroll-mixin.js +202 -0
  38. package/src/vaadin-grid-selection-column.d.ts +71 -0
  39. package/src/vaadin-grid-selection-column.js +285 -0
  40. package/src/vaadin-grid-selection-mixin.d.ts +30 -0
  41. package/src/vaadin-grid-selection-mixin.js +93 -0
  42. package/src/vaadin-grid-sort-column.d.ts +63 -0
  43. package/src/vaadin-grid-sort-column.js +118 -0
  44. package/src/vaadin-grid-sort-mixin.d.ts +15 -0
  45. package/src/vaadin-grid-sort-mixin.js +139 -0
  46. package/src/vaadin-grid-sorter.d.ts +94 -0
  47. package/src/vaadin-grid-sorter.js +230 -0
  48. package/src/vaadin-grid-styles.js +297 -0
  49. package/src/vaadin-grid-styling-mixin.d.ts +37 -0
  50. package/src/vaadin-grid-styling-mixin.js +71 -0
  51. package/src/vaadin-grid-tree-column.d.ts +36 -0
  52. package/src/vaadin-grid-tree-column.js +119 -0
  53. package/src/vaadin-grid-tree-toggle.d.ts +104 -0
  54. package/src/vaadin-grid-tree-toggle.js +205 -0
  55. package/src/vaadin-grid.d.ts +397 -0
  56. package/src/vaadin-grid.js +1004 -0
  57. package/theme/lumo/all-imports.js +11 -0
  58. package/theme/lumo/vaadin-grid-column-group.js +1 -0
  59. package/theme/lumo/vaadin-grid-column.js +1 -0
  60. package/theme/lumo/vaadin-grid-filter-column.js +2 -0
  61. package/theme/lumo/vaadin-grid-filter.js +2 -0
  62. package/theme/lumo/vaadin-grid-selection-column.js +2 -0
  63. package/theme/lumo/vaadin-grid-sort-column.js +2 -0
  64. package/theme/lumo/vaadin-grid-sorter-styles.js +53 -0
  65. package/theme/lumo/vaadin-grid-sorter.js +2 -0
  66. package/theme/lumo/vaadin-grid-styles.js +378 -0
  67. package/theme/lumo/vaadin-grid-tree-column.js +2 -0
  68. package/theme/lumo/vaadin-grid-tree-toggle-styles.js +112 -0
  69. package/theme/lumo/vaadin-grid-tree-toggle.js +2 -0
  70. package/theme/lumo/vaadin-grid.js +9 -0
  71. package/theme/material/all-imports.js +11 -0
  72. package/theme/material/vaadin-grid-column-group.js +1 -0
  73. package/theme/material/vaadin-grid-column.js +1 -0
  74. package/theme/material/vaadin-grid-filter-column.js +2 -0
  75. package/theme/material/vaadin-grid-filter.js +2 -0
  76. package/theme/material/vaadin-grid-selection-column.js +2 -0
  77. package/theme/material/vaadin-grid-sort-column.js +2 -0
  78. package/theme/material/vaadin-grid-sorter-styles.js +72 -0
  79. package/theme/material/vaadin-grid-sorter.js +2 -0
  80. package/theme/material/vaadin-grid-styles.js +252 -0
  81. package/theme/material/vaadin-grid-tree-column.js +2 -0
  82. package/theme/material/vaadin-grid-tree-toggle-styles.js +42 -0
  83. package/theme/material/vaadin-grid-tree-toggle.js +2 -0
  84. package/theme/material/vaadin-grid.js +2 -0
  85. package/vaadin-grid-column-group.d.ts +1 -0
  86. package/vaadin-grid-column-group.js +3 -0
  87. package/vaadin-grid-column.d.ts +1 -0
  88. package/vaadin-grid-column.js +3 -0
  89. package/vaadin-grid-filter-column.d.ts +1 -0
  90. package/vaadin-grid-filter-column.js +3 -0
  91. package/vaadin-grid-filter.d.ts +1 -0
  92. package/vaadin-grid-filter.js +3 -0
  93. package/vaadin-grid-selection-column.d.ts +1 -0
  94. package/vaadin-grid-selection-column.js +3 -0
  95. package/vaadin-grid-sort-column.d.ts +1 -0
  96. package/vaadin-grid-sort-column.js +3 -0
  97. package/vaadin-grid-sorter.d.ts +1 -0
  98. package/vaadin-grid-sorter.js +3 -0
  99. package/vaadin-grid-tree-column.d.ts +1 -0
  100. package/vaadin-grid-tree-column.js +3 -0
  101. package/vaadin-grid-tree-toggle.d.ts +1 -0
  102. package/vaadin-grid-tree-toggle.js +3 -0
  103. package/vaadin-grid.d.ts +3 -0
  104. 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
+ };