@vaadin/grid 24.2.3 → 24.3.0-alpha10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -13
- package/src/vaadin-grid-a11y-mixin.js +1 -1
- package/src/vaadin-grid-active-item-mixin.js +1 -0
- package/src/vaadin-grid-array-data-provider-mixin.js +14 -17
- package/src/vaadin-grid-column-group-mixin.d.ts +20 -0
- package/src/vaadin-grid-column-group-mixin.js +364 -0
- package/src/vaadin-grid-column-group.d.ts +4 -14
- package/src/vaadin-grid-column-group.js +5 -355
- package/src/vaadin-grid-column-mixin.d.ts +170 -0
- package/src/vaadin-grid-column-mixin.js +958 -0
- package/src/vaadin-grid-column.d.ts +11 -138
- package/src/vaadin-grid-column.js +5 -876
- package/src/vaadin-grid-data-provider-mixin.d.ts +6 -30
- package/src/vaadin-grid-data-provider-mixin.js +122 -246
- package/src/vaadin-grid-drag-and-drop-mixin.js +17 -5
- package/src/vaadin-grid-dynamic-columns-mixin.js +22 -17
- package/src/vaadin-grid-filter-column-mixin.d.ts +22 -0
- package/src/vaadin-grid-filter-column-mixin.js +106 -0
- package/src/vaadin-grid-filter-column.d.ts +9 -11
- package/src/vaadin-grid-filter-column.js +3 -90
- package/src/vaadin-grid-filter-element-mixin.d.ts +34 -0
- package/src/vaadin-grid-filter-element-mixin.js +108 -0
- package/src/vaadin-grid-filter-mixin.js +4 -4
- package/src/vaadin-grid-filter.d.ts +4 -21
- package/src/vaadin-grid-filter.js +5 -84
- package/src/vaadin-grid-helpers.js +94 -0
- package/src/vaadin-grid-keyboard-navigation-mixin.js +11 -4
- package/src/vaadin-grid-mixin.js +21 -37
- package/src/vaadin-grid-row-details-mixin.js +7 -8
- package/src/vaadin-grid-scroll-mixin.js +2 -1
- package/src/vaadin-grid-selection-column-base-mixin.js +12 -4
- package/src/vaadin-grid-selection-column-mixin.d.ts +24 -0
- package/src/vaadin-grid-selection-column-mixin.js +194 -0
- package/src/vaadin-grid-selection-column.d.ts +13 -17
- package/src/vaadin-grid-selection-column.js +4 -186
- package/src/vaadin-grid-selection-mixin.js +4 -3
- package/src/vaadin-grid-sort-column-mixin.d.ts +36 -0
- package/src/vaadin-grid-sort-column-mixin.js +101 -0
- package/src/vaadin-grid-sort-column.d.ts +8 -26
- package/src/vaadin-grid-sort-column.js +3 -87
- package/src/vaadin-grid-sorter-mixin.d.ts +44 -0
- package/src/vaadin-grid-sorter-mixin.js +200 -0
- package/src/vaadin-grid-sorter.d.ts +3 -32
- package/src/vaadin-grid-sorter.js +5 -181
- package/src/vaadin-grid-styles.js +341 -345
- package/src/vaadin-grid-styling-mixin.js +8 -2
- package/src/vaadin-grid-tree-column-mixin.d.ts +18 -0
- package/src/vaadin-grid-tree-column-mixin.js +99 -0
- package/src/vaadin-grid-tree-column.d.ts +9 -7
- package/src/vaadin-grid-tree-column.js +3 -82
- package/src/vaadin-grid-tree-toggle-mixin.d.ts +39 -0
- package/src/vaadin-grid-tree-toggle-mixin.js +153 -0
- package/src/vaadin-grid-tree-toggle.d.ts +4 -27
- package/src/vaadin-grid-tree-toggle.js +9 -141
- package/src/vaadin-grid.d.ts +3 -0
- package/src/vaadin-grid.js +7 -2
- package/theme/lumo/vaadin-grid-sorter-styles.js +1 -1
- package/theme/lumo/vaadin-grid-styles.js +15 -14
- package/theme/material/vaadin-grid-styles.js +15 -10
- package/web-types.json +331 -126
- package/web-types.lit.json +114 -58
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2016 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { animationFrame } from '@vaadin/component-base/src/async.js';
|
|
7
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
8
|
+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
9
|
+
import { get } from '@vaadin/component-base/src/path-utils.js';
|
|
10
|
+
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
11
|
+
import { updateCellState } from './vaadin-grid-helpers.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @polymerMixin
|
|
15
|
+
*/
|
|
16
|
+
export const ColumnBaseMixin = (superClass) =>
|
|
17
|
+
class ColumnBaseMixin extends superClass {
|
|
18
|
+
static get properties() {
|
|
19
|
+
return {
|
|
20
|
+
/**
|
|
21
|
+
* When set to true, the column is user-resizable.
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
resizable: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
sync: true,
|
|
27
|
+
value() {
|
|
28
|
+
if (this.localName === 'vaadin-grid-column-group') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parent = this.parentNode;
|
|
33
|
+
if (parent && parent.localName === 'vaadin-grid-column-group') {
|
|
34
|
+
return parent.resizable || false;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* When true, the column is frozen. When a column inside of a column group is frozen,
|
|
42
|
+
* all of the sibling columns inside the group will get frozen also.
|
|
43
|
+
* @type {boolean}
|
|
44
|
+
*/
|
|
45
|
+
frozen: {
|
|
46
|
+
type: Boolean,
|
|
47
|
+
value: false,
|
|
48
|
+
sync: true,
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* When true, the column is frozen to end of grid.
|
|
53
|
+
*
|
|
54
|
+
* When a column inside of a column group is frozen to end, all of the sibling columns
|
|
55
|
+
* inside the group will get frozen to end also.
|
|
56
|
+
*
|
|
57
|
+
* Column can not be set as `frozen` and `frozenToEnd` at the same time.
|
|
58
|
+
* @attr {boolean} frozen-to-end
|
|
59
|
+
* @type {boolean}
|
|
60
|
+
*/
|
|
61
|
+
frozenToEnd: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
value: false,
|
|
64
|
+
sync: true,
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* When true, the cells for this column will be rendered with the `role` attribute
|
|
69
|
+
* set as `rowheader`, instead of the `gridcell` role value used by default.
|
|
70
|
+
*
|
|
71
|
+
* When a column is set as row header, its cells will be announced by screen readers
|
|
72
|
+
* while navigating to help user identify the current row as uniquely as possible.
|
|
73
|
+
*
|
|
74
|
+
* @attr {boolean} row-header
|
|
75
|
+
* @type {boolean}
|
|
76
|
+
*/
|
|
77
|
+
rowHeader: {
|
|
78
|
+
type: Boolean,
|
|
79
|
+
value: false,
|
|
80
|
+
sync: true,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* When set to true, the cells for this column are hidden.
|
|
85
|
+
*/
|
|
86
|
+
hidden: {
|
|
87
|
+
type: Boolean,
|
|
88
|
+
value: false,
|
|
89
|
+
sync: true,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Text content to display in the header cell of the column.
|
|
94
|
+
*/
|
|
95
|
+
header: {
|
|
96
|
+
type: String,
|
|
97
|
+
sync: true,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Aligns the columns cell content horizontally.
|
|
102
|
+
* Supported values: "start", "center" and "end".
|
|
103
|
+
* @attr {start|center|end} text-align
|
|
104
|
+
* @type {GridColumnTextAlign | null | undefined}
|
|
105
|
+
*/
|
|
106
|
+
textAlign: {
|
|
107
|
+
type: String,
|
|
108
|
+
sync: true,
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Custom part name for the header cell.
|
|
113
|
+
*
|
|
114
|
+
* @attr {string} header-part-name
|
|
115
|
+
*/
|
|
116
|
+
headerPartName: {
|
|
117
|
+
type: String,
|
|
118
|
+
sync: true,
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Custom part name for the footer cell.
|
|
123
|
+
*
|
|
124
|
+
* @attr {string} footer-part-name
|
|
125
|
+
*/
|
|
126
|
+
footerPartName: {
|
|
127
|
+
type: String,
|
|
128
|
+
sync: true,
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @type {boolean}
|
|
133
|
+
* @protected
|
|
134
|
+
*/
|
|
135
|
+
_lastFrozen: {
|
|
136
|
+
type: Boolean,
|
|
137
|
+
value: false,
|
|
138
|
+
sync: true,
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @type {boolean}
|
|
143
|
+
* @protected
|
|
144
|
+
*/
|
|
145
|
+
_bodyContentHidden: {
|
|
146
|
+
type: Boolean,
|
|
147
|
+
value: false,
|
|
148
|
+
sync: true,
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @type {boolean}
|
|
153
|
+
* @protected
|
|
154
|
+
*/
|
|
155
|
+
_firstFrozenToEnd: {
|
|
156
|
+
type: Boolean,
|
|
157
|
+
value: false,
|
|
158
|
+
sync: true,
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
/** @protected */
|
|
162
|
+
_order: {
|
|
163
|
+
type: Number,
|
|
164
|
+
sync: true,
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
/** @private */
|
|
168
|
+
_reorderStatus: {
|
|
169
|
+
type: Boolean,
|
|
170
|
+
sync: true,
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @type {Array<!HTMLElement>}
|
|
175
|
+
* @protected
|
|
176
|
+
*/
|
|
177
|
+
_emptyCells: Array,
|
|
178
|
+
|
|
179
|
+
/** @private */
|
|
180
|
+
_headerCell: Object,
|
|
181
|
+
|
|
182
|
+
/** @private */
|
|
183
|
+
_footerCell: Object,
|
|
184
|
+
|
|
185
|
+
/** @protected */
|
|
186
|
+
_grid: Object,
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* By default, the Polymer doesn't invoke the observer
|
|
190
|
+
* during initialization if all of its dependencies are `undefined`.
|
|
191
|
+
* This internal property can be used to force initial invocation of an observer
|
|
192
|
+
* even the other dependencies of the observer are `undefined`.
|
|
193
|
+
*
|
|
194
|
+
* @private
|
|
195
|
+
*/
|
|
196
|
+
__initialized: {
|
|
197
|
+
type: Boolean,
|
|
198
|
+
value: true,
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Custom function for rendering the header content.
|
|
203
|
+
* Receives two arguments:
|
|
204
|
+
*
|
|
205
|
+
* - `root` The header cell content DOM element. Append your content to it.
|
|
206
|
+
* - `column` The `<vaadin-grid-column>` element.
|
|
207
|
+
*
|
|
208
|
+
* @type {GridHeaderFooterRenderer | null | undefined}
|
|
209
|
+
*/
|
|
210
|
+
headerRenderer: {
|
|
211
|
+
type: Function,
|
|
212
|
+
sync: true,
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Represents the final header renderer computed on the set of observable arguments.
|
|
217
|
+
* It is supposed to be used internally when rendering the header cell content.
|
|
218
|
+
*
|
|
219
|
+
* @protected
|
|
220
|
+
* @type {GridHeaderFooterRenderer | undefined}
|
|
221
|
+
*/
|
|
222
|
+
_headerRenderer: {
|
|
223
|
+
type: Function,
|
|
224
|
+
computed: '_computeHeaderRenderer(headerRenderer, header, __initialized)',
|
|
225
|
+
sync: true,
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Custom function for rendering the footer content.
|
|
230
|
+
* Receives two arguments:
|
|
231
|
+
*
|
|
232
|
+
* - `root` The footer cell content DOM element. Append your content to it.
|
|
233
|
+
* - `column` The `<vaadin-grid-column>` element.
|
|
234
|
+
*
|
|
235
|
+
* @type {GridHeaderFooterRenderer | null | undefined}
|
|
236
|
+
*/
|
|
237
|
+
footerRenderer: {
|
|
238
|
+
type: Function,
|
|
239
|
+
sync: true,
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Represents the final footer renderer computed on the set of observable arguments.
|
|
244
|
+
* It is supposed to be used internally when rendering the footer cell content.
|
|
245
|
+
*
|
|
246
|
+
* @protected
|
|
247
|
+
* @type {GridHeaderFooterRenderer | undefined}
|
|
248
|
+
*/
|
|
249
|
+
_footerRenderer: {
|
|
250
|
+
type: Function,
|
|
251
|
+
computed: '_computeFooterRenderer(footerRenderer, __initialized)',
|
|
252
|
+
sync: true,
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* An internal property that is mainly used by `vaadin-template-renderer`
|
|
257
|
+
* to identify grid column elements.
|
|
258
|
+
*
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
__gridColumnElement: {
|
|
262
|
+
type: Boolean,
|
|
263
|
+
value: true,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
static get observers() {
|
|
269
|
+
return [
|
|
270
|
+
'_widthChanged(width, _headerCell, _footerCell, _cells)',
|
|
271
|
+
'_frozenChanged(frozen, _headerCell, _footerCell, _cells)',
|
|
272
|
+
'_frozenToEndChanged(frozenToEnd, _headerCell, _footerCell, _cells)',
|
|
273
|
+
'_flexGrowChanged(flexGrow, _headerCell, _footerCell, _cells)',
|
|
274
|
+
'_textAlignChanged(textAlign, _cells, _headerCell, _footerCell)',
|
|
275
|
+
'_orderChanged(_order, _headerCell, _footerCell, _cells)',
|
|
276
|
+
'_lastFrozenChanged(_lastFrozen)',
|
|
277
|
+
'_firstFrozenToEndChanged(_firstFrozenToEnd)',
|
|
278
|
+
'_onRendererOrBindingChanged(_renderer, _cells, _bodyContentHidden, path)',
|
|
279
|
+
'_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header)',
|
|
280
|
+
'_onFooterRendererOrBindingChanged(_footerRenderer, _footerCell)',
|
|
281
|
+
'_resizableChanged(resizable, _headerCell)',
|
|
282
|
+
'_reorderStatusChanged(_reorderStatus, _headerCell, _footerCell, _cells)',
|
|
283
|
+
'_hiddenChanged(hidden, _headerCell, _footerCell, _cells)',
|
|
284
|
+
'_rowHeaderChanged(rowHeader, _cells)',
|
|
285
|
+
'__headerFooterPartNameChanged(_headerCell, _footerCell, headerPartName, footerPartName)',
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @return {!Grid | undefined}
|
|
291
|
+
* @protected
|
|
292
|
+
*/
|
|
293
|
+
get _grid() {
|
|
294
|
+
if (!this._gridValue) {
|
|
295
|
+
this._gridValue = this._findHostGrid();
|
|
296
|
+
}
|
|
297
|
+
return this._gridValue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @return {!Array<!HTMLElement>}
|
|
302
|
+
* @protected
|
|
303
|
+
*/
|
|
304
|
+
get _allCells() {
|
|
305
|
+
return []
|
|
306
|
+
.concat(this._cells || [])
|
|
307
|
+
.concat(this._emptyCells || [])
|
|
308
|
+
.concat(this._headerCell)
|
|
309
|
+
.concat(this._footerCell)
|
|
310
|
+
.filter((cell) => cell);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** @protected */
|
|
314
|
+
connectedCallback() {
|
|
315
|
+
super.connectedCallback();
|
|
316
|
+
|
|
317
|
+
// Adds the column cells to the grid after the column is attached
|
|
318
|
+
requestAnimationFrame(() => {
|
|
319
|
+
// Skip if the column has been detached
|
|
320
|
+
if (!this._grid) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this._allCells.forEach((cell) => {
|
|
325
|
+
if (!cell._content.parentNode) {
|
|
326
|
+
this._grid.appendChild(cell._content);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** @protected */
|
|
333
|
+
disconnectedCallback() {
|
|
334
|
+
super.disconnectedCallback();
|
|
335
|
+
|
|
336
|
+
// Removes the column cells from the grid after the column is detached
|
|
337
|
+
requestAnimationFrame(() => {
|
|
338
|
+
// Skip if the column has been attached again
|
|
339
|
+
if (this._grid) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
this._allCells.forEach((cell) => {
|
|
344
|
+
if (cell._content.parentNode) {
|
|
345
|
+
cell._content.parentNode.removeChild(cell._content);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
this._gridValue = undefined;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** @protected */
|
|
354
|
+
ready() {
|
|
355
|
+
super.ready();
|
|
356
|
+
|
|
357
|
+
processTemplates(this);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @return {!Grid | undefined}
|
|
362
|
+
* @protected
|
|
363
|
+
*/
|
|
364
|
+
_findHostGrid() {
|
|
365
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
|
|
366
|
+
let el = this;
|
|
367
|
+
// Custom elements extending grid must have a specific localName
|
|
368
|
+
while (el && !/^vaadin.*grid(-pro)?$/u.test(el.localName)) {
|
|
369
|
+
el = el.assignedSlot ? el.assignedSlot.parentNode : el.parentNode;
|
|
370
|
+
}
|
|
371
|
+
return el || undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** @protected */
|
|
375
|
+
_renderHeaderAndFooter() {
|
|
376
|
+
this._renderHeaderCellContent(this._headerRenderer, this._headerCell);
|
|
377
|
+
this._renderFooterCellContent(this._footerRenderer, this._footerCell);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** @private */
|
|
381
|
+
_flexGrowChanged(flexGrow) {
|
|
382
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
383
|
+
this.parentElement._columnPropChanged('flexGrow');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
this._allCells.forEach((cell) => {
|
|
387
|
+
cell.style.flexGrow = flexGrow;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** @private */
|
|
392
|
+
_orderChanged(order) {
|
|
393
|
+
this._allCells.forEach((cell) => {
|
|
394
|
+
cell.style.order = order;
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** @private */
|
|
399
|
+
_widthChanged(width) {
|
|
400
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
401
|
+
this.parentElement._columnPropChanged('width');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
this._allCells.forEach((cell) => {
|
|
405
|
+
cell.style.width = width;
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** @private */
|
|
410
|
+
_frozenChanged(frozen) {
|
|
411
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
412
|
+
this.parentElement._columnPropChanged('frozen', frozen);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
this._allCells.forEach((cell) => {
|
|
416
|
+
updateCellState(cell, 'frozen', frozen);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
if (this._grid && this._grid._frozenCellsChanged) {
|
|
420
|
+
this._grid._frozenCellsChanged();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** @private */
|
|
425
|
+
_frozenToEndChanged(frozenToEnd) {
|
|
426
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
427
|
+
this.parentElement._columnPropChanged('frozenToEnd', frozenToEnd);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
this._allCells.forEach((cell) => {
|
|
431
|
+
// Skip sizer cells to keep correct scrollWidth.
|
|
432
|
+
if (this._grid && cell.parentElement === this._grid.$.sizer) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
updateCellState(cell, 'frozen-to-end', frozenToEnd);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if (this._grid && this._grid._frozenCellsChanged) {
|
|
440
|
+
this._grid._frozenCellsChanged();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** @private */
|
|
445
|
+
_lastFrozenChanged(lastFrozen) {
|
|
446
|
+
this._allCells.forEach((cell) => {
|
|
447
|
+
updateCellState(cell, 'last-frozen', lastFrozen);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
451
|
+
this.parentElement._lastFrozen = lastFrozen;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** @private */
|
|
456
|
+
_firstFrozenToEndChanged(firstFrozenToEnd) {
|
|
457
|
+
this._allCells.forEach((cell) => {
|
|
458
|
+
// Skip sizer cells to keep correct scrollWidth.
|
|
459
|
+
if (this._grid && cell.parentElement === this._grid.$.sizer) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
updateCellState(cell, 'first-frozen-to-end', firstFrozenToEnd);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
467
|
+
this.parentElement._firstFrozenToEnd = firstFrozenToEnd;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** @private */
|
|
472
|
+
_rowHeaderChanged(rowHeader, cells) {
|
|
473
|
+
if (!cells) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
cells.forEach((cell) => {
|
|
478
|
+
cell.setAttribute('role', rowHeader ? 'rowheader' : 'gridcell');
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* @param {string} path
|
|
484
|
+
* @return {string}
|
|
485
|
+
* @protected
|
|
486
|
+
*/
|
|
487
|
+
_generateHeader(path) {
|
|
488
|
+
return path
|
|
489
|
+
.substr(path.lastIndexOf('.') + 1)
|
|
490
|
+
.replace(/([A-Z])/gu, '-$1')
|
|
491
|
+
.toLowerCase()
|
|
492
|
+
.replace(/-/gu, ' ')
|
|
493
|
+
.replace(/^./u, (match) => match.toUpperCase());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** @private */
|
|
497
|
+
_reorderStatusChanged(reorderStatus) {
|
|
498
|
+
const prevStatus = this.__previousReorderStatus;
|
|
499
|
+
const oldPart = prevStatus ? `reorder-${prevStatus}-cell` : '';
|
|
500
|
+
const newPart = `reorder-${reorderStatus}-cell`;
|
|
501
|
+
|
|
502
|
+
this._allCells.forEach((cell) => {
|
|
503
|
+
updateCellState(cell, 'reorder-status', reorderStatus, newPart, oldPart);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
this.__previousReorderStatus = reorderStatus;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/** @private */
|
|
510
|
+
_resizableChanged(resizable, headerCell) {
|
|
511
|
+
if (resizable === undefined || headerCell === undefined) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (headerCell) {
|
|
516
|
+
[headerCell].concat(this._emptyCells).forEach((cell) => {
|
|
517
|
+
if (cell) {
|
|
518
|
+
const existingHandle = cell.querySelector('[part~="resize-handle"]');
|
|
519
|
+
if (existingHandle) {
|
|
520
|
+
cell.removeChild(existingHandle);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (resizable) {
|
|
524
|
+
const handle = document.createElement('div');
|
|
525
|
+
handle.setAttribute('part', 'resize-handle');
|
|
526
|
+
cell.appendChild(handle);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** @private */
|
|
534
|
+
_textAlignChanged(textAlign) {
|
|
535
|
+
if (textAlign === undefined || this._grid === undefined) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (['start', 'end', 'center'].indexOf(textAlign) === -1) {
|
|
539
|
+
console.warn('textAlign can only be set as "start", "end" or "center"');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
let textAlignFallback;
|
|
544
|
+
if (getComputedStyle(this._grid).direction === 'ltr') {
|
|
545
|
+
if (textAlign === 'start') {
|
|
546
|
+
textAlignFallback = 'left';
|
|
547
|
+
} else if (textAlign === 'end') {
|
|
548
|
+
textAlignFallback = 'right';
|
|
549
|
+
}
|
|
550
|
+
} else if (textAlign === 'start') {
|
|
551
|
+
textAlignFallback = 'right';
|
|
552
|
+
} else if (textAlign === 'end') {
|
|
553
|
+
textAlignFallback = 'left';
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
this._allCells.forEach((cell) => {
|
|
557
|
+
cell._content.style.textAlign = textAlign;
|
|
558
|
+
if (getComputedStyle(cell._content).textAlign !== textAlign) {
|
|
559
|
+
cell._content.style.textAlign = textAlignFallback;
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** @private */
|
|
565
|
+
_hiddenChanged(hidden) {
|
|
566
|
+
if (this.parentElement && this.parentElement._columnPropChanged) {
|
|
567
|
+
this.parentElement._columnPropChanged('hidden', hidden);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (!!hidden !== !!this._previousHidden && this._grid) {
|
|
571
|
+
if (hidden === true) {
|
|
572
|
+
this._allCells.forEach((cell) => {
|
|
573
|
+
if (cell._content.parentNode) {
|
|
574
|
+
cell._content.parentNode.removeChild(cell._content);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
this._grid._debouncerHiddenChanged = Debouncer.debounce(
|
|
579
|
+
this._grid._debouncerHiddenChanged,
|
|
580
|
+
animationFrame,
|
|
581
|
+
() => {
|
|
582
|
+
if (this._grid && this._grid._renderColumnTree) {
|
|
583
|
+
this._grid._renderColumnTree(this._grid._columnTree);
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
if (this._grid._debounceUpdateFrozenColumn) {
|
|
589
|
+
this._grid._debounceUpdateFrozenColumn();
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (this._grid._resetKeyboardNavigation) {
|
|
593
|
+
this._grid._resetKeyboardNavigation();
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
this._previousHidden = hidden;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** @protected */
|
|
600
|
+
_runRenderer(renderer, cell, model) {
|
|
601
|
+
const args = [cell._content, this];
|
|
602
|
+
if (model && model.item) {
|
|
603
|
+
args.push(model);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
renderer.apply(this, args);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Renders the content to the given cells using a renderer.
|
|
611
|
+
*
|
|
612
|
+
* @private
|
|
613
|
+
*/
|
|
614
|
+
__renderCellsContent(renderer, cells) {
|
|
615
|
+
// Skip if the column is hidden or not attached to a grid.
|
|
616
|
+
if (this.hidden || !this._grid) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
cells.forEach((cell) => {
|
|
621
|
+
if (!cell.parentElement) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const model = this._grid.__getRowModel(cell.parentElement);
|
|
626
|
+
|
|
627
|
+
if (!renderer) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (cell._renderer !== renderer) {
|
|
632
|
+
this._clearCellContent(cell);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
cell._renderer = renderer;
|
|
636
|
+
|
|
637
|
+
if (model.item || renderer === this._headerRenderer || renderer === this._footerRenderer) {
|
|
638
|
+
this._runRenderer(renderer, cell, model);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Clears the content of a cell.
|
|
645
|
+
*
|
|
646
|
+
* @protected
|
|
647
|
+
*/
|
|
648
|
+
_clearCellContent(cell) {
|
|
649
|
+
cell._content.innerHTML = '';
|
|
650
|
+
// Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
|
|
651
|
+
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
652
|
+
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
653
|
+
delete cell._content._$litPart$;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Renders the header cell content using a renderer,
|
|
658
|
+
* and then updates the visibility of the parent row depending on
|
|
659
|
+
* whether all its children cells are empty or not.
|
|
660
|
+
*
|
|
661
|
+
* @protected
|
|
662
|
+
*/
|
|
663
|
+
_renderHeaderCellContent(headerRenderer, headerCell) {
|
|
664
|
+
if (!headerCell || !headerRenderer) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
this.__renderCellsContent(headerRenderer, [headerCell]);
|
|
669
|
+
if (this._grid && headerCell.parentElement) {
|
|
670
|
+
this._grid.__debounceUpdateHeaderFooterRowVisibility(headerCell.parentElement);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/** @protected */
|
|
675
|
+
_onHeaderRendererOrBindingChanged(headerRenderer, headerCell, ..._bindings) {
|
|
676
|
+
this._renderHeaderCellContent(headerRenderer, headerCell);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/** @private */
|
|
680
|
+
__headerFooterPartNameChanged(headerCell, footerCell, headerPartName, footerPartName) {
|
|
681
|
+
[
|
|
682
|
+
{ cell: headerCell, partName: headerPartName },
|
|
683
|
+
{ cell: footerCell, partName: footerPartName },
|
|
684
|
+
].forEach(({ cell, partName }) => {
|
|
685
|
+
if (cell) {
|
|
686
|
+
const customParts = cell.__customParts || [];
|
|
687
|
+
cell.part.remove(...customParts);
|
|
688
|
+
|
|
689
|
+
cell.__customParts = partName ? partName.trim().split(' ') : [];
|
|
690
|
+
cell.part.add(...cell.__customParts);
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Renders the content of body cells using a renderer.
|
|
697
|
+
*
|
|
698
|
+
* @protected
|
|
699
|
+
*/
|
|
700
|
+
_renderBodyCellsContent(renderer, cells) {
|
|
701
|
+
if (!cells || !renderer) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
this.__renderCellsContent(renderer, cells);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** @protected */
|
|
709
|
+
_onRendererOrBindingChanged(renderer, cells, ..._bindings) {
|
|
710
|
+
this._renderBodyCellsContent(renderer, cells);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Renders the footer cell content using a renderer
|
|
715
|
+
* and then updates the visibility of the parent row depending on
|
|
716
|
+
* whether all its children cells are empty or not.
|
|
717
|
+
*
|
|
718
|
+
* @protected
|
|
719
|
+
*/
|
|
720
|
+
_renderFooterCellContent(footerRenderer, footerCell) {
|
|
721
|
+
if (!footerCell || !footerRenderer) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
this.__renderCellsContent(footerRenderer, [footerCell]);
|
|
726
|
+
if (this._grid && footerCell.parentElement) {
|
|
727
|
+
this._grid.__debounceUpdateHeaderFooterRowVisibility(footerCell.parentElement);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/** @protected */
|
|
732
|
+
_onFooterRendererOrBindingChanged(footerRenderer, footerCell) {
|
|
733
|
+
this._renderFooterCellContent(footerRenderer, footerCell);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/** @private */
|
|
737
|
+
__setTextContent(node, textContent) {
|
|
738
|
+
if (node.textContent !== textContent) {
|
|
739
|
+
node.textContent = textContent;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Renders the text header to the header cell.
|
|
745
|
+
*
|
|
746
|
+
* @private
|
|
747
|
+
*/
|
|
748
|
+
__textHeaderRenderer() {
|
|
749
|
+
this.__setTextContent(this._headerCell._content, this.header);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Computes the property name based on the path and renders it to the header cell.
|
|
754
|
+
* If the path is not defined, then nothing is rendered.
|
|
755
|
+
*
|
|
756
|
+
* @protected
|
|
757
|
+
*/
|
|
758
|
+
_defaultHeaderRenderer() {
|
|
759
|
+
if (!this.path) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
this.__setTextContent(this._headerCell._content, this._generateHeader(this.path));
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Computes the item property value based on the path and renders it to the body cell.
|
|
768
|
+
* If the path is not defined, then nothing is rendered.
|
|
769
|
+
*
|
|
770
|
+
* @protected
|
|
771
|
+
*/
|
|
772
|
+
_defaultRenderer(root, _owner, { item }) {
|
|
773
|
+
if (!this.path) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
this.__setTextContent(root, get(this.path, item));
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* By default, nothing is rendered to the footer cell.
|
|
782
|
+
*
|
|
783
|
+
* @protected
|
|
784
|
+
*/
|
|
785
|
+
_defaultFooterRenderer() {}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Computes the final header renderer for the `_headerRenderer` computed property.
|
|
789
|
+
* All the arguments are observable by the Polymer, it re-calls the method
|
|
790
|
+
* once an argument is changed to update the property value.
|
|
791
|
+
*
|
|
792
|
+
* @protected
|
|
793
|
+
* @return {GridHeaderFooterRenderer | undefined}
|
|
794
|
+
*/
|
|
795
|
+
_computeHeaderRenderer(headerRenderer, header) {
|
|
796
|
+
if (headerRenderer) {
|
|
797
|
+
return headerRenderer;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (header !== undefined && header !== null) {
|
|
801
|
+
return this.__textHeaderRenderer;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
return this._defaultHeaderRenderer;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Computes the final renderer for the `_renderer` property.
|
|
809
|
+
* All the arguments are observable by the Polymer, it re-calls the method
|
|
810
|
+
* once an argument is changed to update the property value.
|
|
811
|
+
*
|
|
812
|
+
* @protected
|
|
813
|
+
* @return {GridBodyRenderer | undefined}
|
|
814
|
+
*/
|
|
815
|
+
_computeRenderer(renderer) {
|
|
816
|
+
if (renderer) {
|
|
817
|
+
return renderer;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
return this._defaultRenderer;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Computes the final footer renderer for the `_footerRenderer` property.
|
|
825
|
+
* All the arguments are observable by the Polymer, it re-calls the method
|
|
826
|
+
* once an argument is changed to update the property value.
|
|
827
|
+
*
|
|
828
|
+
* @protected
|
|
829
|
+
* @return {GridHeaderFooterRenderer | undefined}
|
|
830
|
+
*/
|
|
831
|
+
_computeFooterRenderer(footerRenderer) {
|
|
832
|
+
if (footerRenderer) {
|
|
833
|
+
return footerRenderer;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
return this._defaultFooterRenderer;
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* @polymerMixin
|
|
842
|
+
* @mixes ColumnBaseMixin
|
|
843
|
+
* @mixes DirMixin
|
|
844
|
+
*/
|
|
845
|
+
export const GridColumnMixin = (superClass) =>
|
|
846
|
+
class extends ColumnBaseMixin(DirMixin(superClass)) {
|
|
847
|
+
static get properties() {
|
|
848
|
+
return {
|
|
849
|
+
/**
|
|
850
|
+
* Width of the cells for this column.
|
|
851
|
+
*/
|
|
852
|
+
width: {
|
|
853
|
+
type: String,
|
|
854
|
+
value: '100px',
|
|
855
|
+
sync: true,
|
|
856
|
+
},
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Flex grow ratio for the cell widths. When set to 0, cell width is fixed.
|
|
860
|
+
* @attr {number} flex-grow
|
|
861
|
+
* @type {number}
|
|
862
|
+
*/
|
|
863
|
+
flexGrow: {
|
|
864
|
+
type: Number,
|
|
865
|
+
value: 1,
|
|
866
|
+
sync: true,
|
|
867
|
+
},
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Custom function for rendering the cell content.
|
|
871
|
+
* Receives three arguments:
|
|
872
|
+
*
|
|
873
|
+
* - `root` The cell content DOM element. Append your content to it.
|
|
874
|
+
* - `column` The `<vaadin-grid-column>` element.
|
|
875
|
+
* - `model` The object with the properties related with
|
|
876
|
+
* the rendered item, contains:
|
|
877
|
+
* - `model.index` The index of the item.
|
|
878
|
+
* - `model.item` The item.
|
|
879
|
+
* - `model.expanded` Sublevel toggle state.
|
|
880
|
+
* - `model.level` Level of the tree represented with a horizontal offset of the toggle button.
|
|
881
|
+
* - `model.selected` Selected state.
|
|
882
|
+
* - `model.detailsOpened` Details opened state.
|
|
883
|
+
*
|
|
884
|
+
* @type {GridBodyRenderer | null | undefined}
|
|
885
|
+
*/
|
|
886
|
+
renderer: {
|
|
887
|
+
type: Function,
|
|
888
|
+
sync: true,
|
|
889
|
+
},
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Represents the final renderer computed on the set of observable arguments.
|
|
893
|
+
* It is supposed to be used internally when rendering the content of a body cell.
|
|
894
|
+
*
|
|
895
|
+
* @protected
|
|
896
|
+
* @type {GridBodyRenderer | undefined}
|
|
897
|
+
*/
|
|
898
|
+
_renderer: {
|
|
899
|
+
type: Function,
|
|
900
|
+
computed: '_computeRenderer(renderer, __initialized)',
|
|
901
|
+
sync: true,
|
|
902
|
+
},
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Path to an item sub-property whose value gets displayed in the column body cells.
|
|
906
|
+
* The property name is also shown in the column header if an explicit header or renderer isn't defined.
|
|
907
|
+
*/
|
|
908
|
+
path: {
|
|
909
|
+
type: String,
|
|
910
|
+
sync: true,
|
|
911
|
+
},
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Automatically sets the width of the column based on the column contents when this is set to `true`.
|
|
915
|
+
*
|
|
916
|
+
* For performance reasons the column width is calculated automatically only once when the grid items
|
|
917
|
+
* are rendered for the first time and the calculation only considers the rows which are currently
|
|
918
|
+
* rendered in DOM (a bit more than what is currently visible). If the grid is scrolled, or the cell
|
|
919
|
+
* content changes, the column width might not match the contents anymore.
|
|
920
|
+
*
|
|
921
|
+
* Hidden columns are ignored in the calculation and their widths are not automatically updated when
|
|
922
|
+
* you show a column that was initially hidden.
|
|
923
|
+
*
|
|
924
|
+
* You can manually trigger the auto sizing behavior again by calling `grid.recalculateColumnWidths()`.
|
|
925
|
+
*
|
|
926
|
+
* The column width may still grow larger when `flexGrow` is not 0.
|
|
927
|
+
* @attr {boolean} auto-width
|
|
928
|
+
* @type {boolean}
|
|
929
|
+
*/
|
|
930
|
+
autoWidth: {
|
|
931
|
+
type: Boolean,
|
|
932
|
+
value: false,
|
|
933
|
+
},
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* When true, wraps the cell's slot into an element with role="button", and sets
|
|
937
|
+
* the tabindex attribute on the button element, instead of the cell itself.
|
|
938
|
+
* This is needed to keep focus in sync with VoiceOver cursor when navigating
|
|
939
|
+
* with Control + Option + arrow keys: focusing the `<td>` element does not fire
|
|
940
|
+
* a focus event, but focusing an element with role="button" inside a cell fires it.
|
|
941
|
+
* @protected
|
|
942
|
+
*/
|
|
943
|
+
_focusButtonMode: {
|
|
944
|
+
type: Boolean,
|
|
945
|
+
value: false,
|
|
946
|
+
},
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* @type {Array<!HTMLElement>}
|
|
950
|
+
* @protected
|
|
951
|
+
*/
|
|
952
|
+
_cells: {
|
|
953
|
+
type: Array,
|
|
954
|
+
sync: true,
|
|
955
|
+
},
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
};
|