@vaadin/crud 24.6.0-alpha9 → 24.6.0-rc1
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 +15 -15
- package/src/vaadin-crud-grid-mixin.d.ts +39 -0
- package/src/vaadin-crud-grid-mixin.js +266 -0
- package/src/vaadin-crud-grid.d.ts +2 -20
- package/src/vaadin-crud-grid.js +3 -249
- package/src/vaadin-crud-helpers.js +4 -0
- package/src/vaadin-crud-mixin.d.ts +269 -0
- package/src/vaadin-crud-mixin.js +1045 -0
- package/src/vaadin-crud.d.ts +13 -252
- package/src/vaadin-crud.js +3 -1033
- package/web-types.json +6 -6
- package/web-types.lit.json +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/crud",
|
|
3
|
-
"version": "24.6.0-
|
|
3
|
+
"version": "24.6.0-rc1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -37,21 +37,21 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
39
39
|
"@polymer/polymer": "^3.0.0",
|
|
40
|
-
"@vaadin/a11y-base": "24.6.0-
|
|
41
|
-
"@vaadin/button": "24.6.0-
|
|
42
|
-
"@vaadin/component-base": "24.6.0-
|
|
43
|
-
"@vaadin/confirm-dialog": "24.6.0-
|
|
44
|
-
"@vaadin/dialog": "24.6.0-
|
|
45
|
-
"@vaadin/form-layout": "24.6.0-
|
|
46
|
-
"@vaadin/grid": "24.6.0-
|
|
47
|
-
"@vaadin/overlay": "24.6.0-
|
|
48
|
-
"@vaadin/text-field": "24.6.0-
|
|
49
|
-
"@vaadin/vaadin-lumo-styles": "24.6.0-
|
|
50
|
-
"@vaadin/vaadin-material-styles": "24.6.0-
|
|
51
|
-
"@vaadin/vaadin-themable-mixin": "24.6.0-
|
|
40
|
+
"@vaadin/a11y-base": "24.6.0-rc1",
|
|
41
|
+
"@vaadin/button": "24.6.0-rc1",
|
|
42
|
+
"@vaadin/component-base": "24.6.0-rc1",
|
|
43
|
+
"@vaadin/confirm-dialog": "24.6.0-rc1",
|
|
44
|
+
"@vaadin/dialog": "24.6.0-rc1",
|
|
45
|
+
"@vaadin/form-layout": "24.6.0-rc1",
|
|
46
|
+
"@vaadin/grid": "24.6.0-rc1",
|
|
47
|
+
"@vaadin/overlay": "24.6.0-rc1",
|
|
48
|
+
"@vaadin/text-field": "24.6.0-rc1",
|
|
49
|
+
"@vaadin/vaadin-lumo-styles": "24.6.0-rc1",
|
|
50
|
+
"@vaadin/vaadin-material-styles": "24.6.0-rc1",
|
|
51
|
+
"@vaadin/vaadin-themable-mixin": "24.6.0-rc1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@vaadin/chai-plugins": "24.6.0-
|
|
54
|
+
"@vaadin/chai-plugins": "24.6.0-rc1",
|
|
55
55
|
"@vaadin/testing-helpers": "^1.0.0",
|
|
56
56
|
"sinon": "^18.0.0"
|
|
57
57
|
},
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"web-types.json",
|
|
61
61
|
"web-types.lit.json"
|
|
62
62
|
],
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "d62ba309e3286777ad3ea7e015d50a2c4976bb42"
|
|
64
64
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2000 - 2024 Vaadin Ltd.
|
|
4
|
+
*
|
|
5
|
+
* This program is available under Vaadin Commercial License and Service Terms.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* See https://vaadin.com/commercial-license-and-service-terms for the full
|
|
9
|
+
* license.
|
|
10
|
+
*/
|
|
11
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
12
|
+
import type { IncludedMixinClass } from './vaadin-crud-include-mixin.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A mixin providing common crud grid functionality.
|
|
16
|
+
*/
|
|
17
|
+
export declare function CrudGridMixin<T extends Constructor<HTMLElement>>(
|
|
18
|
+
base: T,
|
|
19
|
+
): Constructor<CrudGridMixinClass> & Constructor<IncludedMixinClass> & T;
|
|
20
|
+
|
|
21
|
+
export declare class CrudGridMixinClass {
|
|
22
|
+
/**
|
|
23
|
+
* Disable filtering in the generated columns.
|
|
24
|
+
* @attr {boolean} no-filter
|
|
25
|
+
*/
|
|
26
|
+
noFilter: boolean | null | undefined;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Disable sorting in the generated columns.
|
|
30
|
+
* @attr {boolean} no-sort
|
|
31
|
+
*/
|
|
32
|
+
noSort: boolean | null | undefined;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Do not add headers to columns.
|
|
36
|
+
* @attr {boolean} no-head
|
|
37
|
+
*/
|
|
38
|
+
noHead: boolean | null | undefined;
|
|
39
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2000 - 2024 Vaadin Ltd.
|
|
4
|
+
*
|
|
5
|
+
* This program is available under Vaadin Commercial License and Service Terms.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* See https://vaadin.com/commercial-license-and-service-terms for the full
|
|
9
|
+
* license.
|
|
10
|
+
*/
|
|
11
|
+
import { capitalize, getProperty } from './vaadin-crud-helpers.js';
|
|
12
|
+
import { IncludedMixin } from './vaadin-crud-include-mixin.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A mixin providing common crud-grid functionality.
|
|
16
|
+
*
|
|
17
|
+
* @polymerMixin
|
|
18
|
+
* @mixes IncludedMixin
|
|
19
|
+
*/
|
|
20
|
+
export const CrudGridMixin = (superClass) =>
|
|
21
|
+
class extends IncludedMixin(superClass) {
|
|
22
|
+
static get properties() {
|
|
23
|
+
return {
|
|
24
|
+
/**
|
|
25
|
+
* Disable filtering in the generated columns.
|
|
26
|
+
* @attr {boolean} no-filter
|
|
27
|
+
*/
|
|
28
|
+
noFilter: Boolean,
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Disable sorting in the generated columns.
|
|
32
|
+
* @attr {boolean} no-sort
|
|
33
|
+
*/
|
|
34
|
+
noSort: Boolean,
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Do not add headers to columns.
|
|
38
|
+
* @attr {boolean} no-head
|
|
39
|
+
*/
|
|
40
|
+
noHead: Boolean,
|
|
41
|
+
|
|
42
|
+
/** @private */
|
|
43
|
+
__hideEditColumn: Boolean,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static get observers() {
|
|
48
|
+
return ['__onItemsChange(items)', '__onHideEditColumnChange(hideEditColumn)'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @private */
|
|
52
|
+
__onItemsChange(items) {
|
|
53
|
+
if ((!this.dataProvider || this.dataProvider === this._arrayDataProvider) && !this.include && items && items[0]) {
|
|
54
|
+
this._configure(items[0]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** @private */
|
|
59
|
+
__onHideEditColumnChange() {
|
|
60
|
+
if (this.firstChild) {
|
|
61
|
+
this.__toggleEditColumn();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @private */
|
|
66
|
+
__toggleEditColumn() {
|
|
67
|
+
let editColumn = this.querySelector('vaadin-crud-edit-column');
|
|
68
|
+
if (this.hideEditColumn) {
|
|
69
|
+
if (editColumn) {
|
|
70
|
+
this.removeChild(editColumn);
|
|
71
|
+
}
|
|
72
|
+
} else if (!editColumn) {
|
|
73
|
+
editColumn = document.createElement('vaadin-crud-edit-column');
|
|
74
|
+
editColumn.frozenToEnd = true;
|
|
75
|
+
this.appendChild(editColumn);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @private */
|
|
80
|
+
__dataProviderWrapper(params, callback) {
|
|
81
|
+
this.__dataProvider(params, (items, size) => {
|
|
82
|
+
if (this.innerHTML === '' && !this.include && items[0]) {
|
|
83
|
+
this._configure(items[0]);
|
|
84
|
+
}
|
|
85
|
+
callback(items, size);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @override
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
_dataProviderChanged(dataProvider, oldDataProvider) {
|
|
94
|
+
if (this._arrayDataProvider === dataProvider) {
|
|
95
|
+
super._dataProviderChanged(dataProvider, oldDataProvider);
|
|
96
|
+
} else if (this.__dataProviderWrapper !== dataProvider) {
|
|
97
|
+
this.innerHTML = '';
|
|
98
|
+
this.__dataProvider = dataProvider;
|
|
99
|
+
this.dataProvider = this.__dataProviderWrapper;
|
|
100
|
+
super._dataProviderChanged(this.__dataProviderWrapper, oldDataProvider);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Auto-generate grid columns based on the JSON structure of the object provided.
|
|
106
|
+
*
|
|
107
|
+
* Method will be executed when items or dataProvider is assigned.
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
_configure(item) {
|
|
111
|
+
this.innerHTML = '';
|
|
112
|
+
this.__createColumns(this, item, undefined, this.__getPropertyDepth(item));
|
|
113
|
+
this.__toggleEditColumn();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Return the deepest property depth of the object
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
__getPropertyDepth(object) {
|
|
121
|
+
if (!object || typeof object !== 'object') {
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return Object.keys(object).reduce((deepest, prop) => {
|
|
126
|
+
if (this.exclude && this.exclude.test(prop)) {
|
|
127
|
+
return deepest;
|
|
128
|
+
}
|
|
129
|
+
return Math.max(deepest, 1 + this.__getPropertyDepth(object[prop]));
|
|
130
|
+
}, 0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse the camelCase column names into sentence case headers.
|
|
135
|
+
* @param {string} path
|
|
136
|
+
* @return {string}
|
|
137
|
+
* @protected
|
|
138
|
+
*/
|
|
139
|
+
_generateHeader(path) {
|
|
140
|
+
return path
|
|
141
|
+
.substr(path.lastIndexOf('.') + 1)
|
|
142
|
+
.replace(/([A-Z])/gu, '-$1')
|
|
143
|
+
.toLowerCase()
|
|
144
|
+
.replace(/-/gu, ' ')
|
|
145
|
+
.replace(/^./u, (match) => match.toUpperCase());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** @private */
|
|
149
|
+
__createColumn(parent, path) {
|
|
150
|
+
let col;
|
|
151
|
+
if (!this.noFilter && !this.noSort && !parent.__sortColumnGroup) {
|
|
152
|
+
// This crud-grid has both a sorter and a filter, but neither has yet been
|
|
153
|
+
// created => col should become the sorter group column
|
|
154
|
+
col = this.__createGroup(parent);
|
|
155
|
+
col.__sortColumnGroup = true;
|
|
156
|
+
// Create the filter column under this sorter group column
|
|
157
|
+
this.__createColumn(col, path);
|
|
158
|
+
} else {
|
|
159
|
+
// In all other cases, col should be a regular column with a renderer
|
|
160
|
+
col = document.createElement('vaadin-grid-column');
|
|
161
|
+
parent.appendChild(col);
|
|
162
|
+
col.renderer = (root, _column, model) => {
|
|
163
|
+
root.textContent = path ? getProperty(path, model.item) : model.item;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!this.noHead && path) {
|
|
168
|
+
// Create a header renderer for the column (or column group)
|
|
169
|
+
col.headerRenderer = (root) => {
|
|
170
|
+
if (root.firstElementChild) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const label = this._generateHeader(path);
|
|
175
|
+
|
|
176
|
+
if (col.__sortColumnGroup || (this.noFilter && !this.noSort)) {
|
|
177
|
+
// The column is either the sorter group column or the root level
|
|
178
|
+
// sort column (in case a filter isn't used at all) => add the sort indicator
|
|
179
|
+
const sorter = document.createElement('vaadin-grid-sorter');
|
|
180
|
+
sorter.setAttribute('path', path);
|
|
181
|
+
// TODO: Localize aria labels
|
|
182
|
+
sorter.setAttribute('aria-label', `Sort by ${label}`);
|
|
183
|
+
sorter.textContent = label;
|
|
184
|
+
root.appendChild(sorter);
|
|
185
|
+
} else if (!this.noFilter) {
|
|
186
|
+
// Filtering is enabled in this crud-grid, create the filter element
|
|
187
|
+
const filter = document.createElement('vaadin-grid-filter');
|
|
188
|
+
filter.setAttribute('path', path);
|
|
189
|
+
// TODO: Localize aria labels
|
|
190
|
+
filter.setAttribute('aria-label', `Filter by ${label}`);
|
|
191
|
+
filter.style.display = 'flex';
|
|
192
|
+
|
|
193
|
+
const textField = window.document.createElement('vaadin-text-field');
|
|
194
|
+
textField.setAttribute('theme', 'small');
|
|
195
|
+
textField.setAttribute('focus-target', true);
|
|
196
|
+
textField.style.width = '100%';
|
|
197
|
+
if (this.noSort) {
|
|
198
|
+
textField.placeholder = label;
|
|
199
|
+
}
|
|
200
|
+
textField.addEventListener('value-changed', (event) => {
|
|
201
|
+
filter.value = event.detail.value;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
filter.appendChild(textField);
|
|
205
|
+
root.appendChild(filter);
|
|
206
|
+
} else if (this.noSort && this.noFilter) {
|
|
207
|
+
// Neither sorter nor filter are enabled, just add the label
|
|
208
|
+
root.textContent = label;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Creates the column structure for the (sub)object.
|
|
216
|
+
*
|
|
217
|
+
* @param {HTMLElement} parent May be the crud-grid or a column group.
|
|
218
|
+
* @param {Object} object The object to create the sub-columns for.
|
|
219
|
+
* @param {string} path The property path from the root item to the object.
|
|
220
|
+
* @param {number} depth The depth of the object in the object hierarchy.
|
|
221
|
+
* @private
|
|
222
|
+
*/
|
|
223
|
+
__createColumns(parent, object, path, depth) {
|
|
224
|
+
if (object && typeof object === 'object') {
|
|
225
|
+
// Iterate over the object properties
|
|
226
|
+
Object.keys(object).forEach((prop) => {
|
|
227
|
+
if (!this.include && this.exclude && this.exclude.test(prop)) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Sub-object of the current object
|
|
231
|
+
const subObject = object[prop];
|
|
232
|
+
// Full path to the sub-object
|
|
233
|
+
const subObjectPath = path ? `${path}.${prop}` : prop;
|
|
234
|
+
|
|
235
|
+
// The column element for the sub-object
|
|
236
|
+
let subObjectColumn = parent;
|
|
237
|
+
if (!this.noHead && depth > 1) {
|
|
238
|
+
const isSubObject = subObject && typeof subObject === 'object';
|
|
239
|
+
// If the sub-object is an actual object, create a column group with the property
|
|
240
|
+
// name as the header text, otherwise create a group without a header
|
|
241
|
+
subObjectColumn = this.__createGroup(parent, isSubObject ? prop : undefined);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Run recursively for the sub-object level
|
|
245
|
+
this.__createColumns(subObjectColumn, subObject, subObjectPath, depth - 1);
|
|
246
|
+
});
|
|
247
|
+
} else if (depth > 1) {
|
|
248
|
+
// The object has been fully traversed, but empty wrapping column
|
|
249
|
+
// groups are still needed to complete the full object depth
|
|
250
|
+
this.__createColumns(this.__createGroup(parent), undefined, path, depth - 1);
|
|
251
|
+
} else {
|
|
252
|
+
// The column group depth is complete, create the actual leaf column
|
|
253
|
+
this.__createColumn(parent, path);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** @private */
|
|
258
|
+
__createGroup(parent, header) {
|
|
259
|
+
const grp = document.createElement('vaadin-grid-column-group');
|
|
260
|
+
if (header) {
|
|
261
|
+
grp.header = capitalize(header);
|
|
262
|
+
}
|
|
263
|
+
parent.appendChild(grp);
|
|
264
|
+
return grp;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
@@ -9,30 +9,12 @@
|
|
|
9
9
|
* license.
|
|
10
10
|
*/
|
|
11
11
|
import { Grid } from '@vaadin/grid/src/vaadin-grid.js';
|
|
12
|
-
import {
|
|
12
|
+
import { CrudGridMixin } from './vaadin-crud-grid-mixin.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* An element used internally by `<vaadin-crud>`. Not intended to be used separately.
|
|
16
16
|
*/
|
|
17
|
-
declare class CrudGrid extends
|
|
18
|
-
/**
|
|
19
|
-
* Disable filtering in the generated columns.
|
|
20
|
-
* @attr {boolean} no-filter
|
|
21
|
-
*/
|
|
22
|
-
noFilter: boolean | null | undefined;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Disable sorting in the generated columns.
|
|
26
|
-
* @attr {boolean} no-sort
|
|
27
|
-
*/
|
|
28
|
-
noSort: boolean | null | undefined;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Do not add headers to columns.
|
|
32
|
-
* @attr {boolean} no-head
|
|
33
|
-
*/
|
|
34
|
-
noHead: boolean | null | undefined;
|
|
35
|
-
}
|
|
17
|
+
declare class CrudGrid extends CrudGridMixin(Grid) {}
|
|
36
18
|
|
|
37
19
|
declare global {
|
|
38
20
|
interface HTMLElementTagNameMap {
|
package/src/vaadin-crud-grid.js
CHANGED
|
@@ -15,265 +15,19 @@ import '@vaadin/grid/src/vaadin-grid-sorter.js';
|
|
|
15
15
|
import './vaadin-crud-edit-column.js';
|
|
16
16
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
17
17
|
import { Grid } from '@vaadin/grid/src/vaadin-grid.js';
|
|
18
|
-
import {
|
|
19
|
-
import { IncludedMixin } from './vaadin-crud-include-mixin.js';
|
|
18
|
+
import { CrudGridMixin } from './vaadin-crud-grid-mixin.js';
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* An element used internally by `<vaadin-crud>`. Not intended to be used separately.
|
|
23
22
|
*
|
|
24
23
|
* @extends Grid
|
|
25
|
-
* @mixes
|
|
24
|
+
* @mixes CrudGridMixin
|
|
26
25
|
* @private
|
|
27
26
|
*/
|
|
28
|
-
class CrudGrid extends
|
|
27
|
+
class CrudGrid extends CrudGridMixin(Grid) {
|
|
29
28
|
static get is() {
|
|
30
29
|
return 'vaadin-crud-grid';
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
static get properties() {
|
|
34
|
-
return {
|
|
35
|
-
/**
|
|
36
|
-
* Disable filtering in the generated columns.
|
|
37
|
-
* @attr {boolean} no-filter
|
|
38
|
-
*/
|
|
39
|
-
noFilter: Boolean,
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Disable sorting in the generated columns.
|
|
43
|
-
* @attr {boolean} no-sort
|
|
44
|
-
*/
|
|
45
|
-
noSort: Boolean,
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Do not add headers to columns.
|
|
49
|
-
* @attr {boolean} no-head
|
|
50
|
-
*/
|
|
51
|
-
noHead: Boolean,
|
|
52
|
-
|
|
53
|
-
/** @private */
|
|
54
|
-
__hideEditColumn: Boolean,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static get observers() {
|
|
59
|
-
return ['__onItemsChange(items)', '__onHideEditColumnChange(hideEditColumn)'];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** @private */
|
|
63
|
-
__onItemsChange(items) {
|
|
64
|
-
if ((!this.dataProvider || this.dataProvider === this._arrayDataProvider) && !this.include && items && items[0]) {
|
|
65
|
-
this._configure(items[0]);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** @private */
|
|
70
|
-
__onHideEditColumnChange() {
|
|
71
|
-
if (this.firstChild) {
|
|
72
|
-
this.__toggleEditColumn();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** @private */
|
|
77
|
-
__toggleEditColumn() {
|
|
78
|
-
let editColumn = this.querySelector('vaadin-crud-edit-column');
|
|
79
|
-
if (this.hideEditColumn) {
|
|
80
|
-
if (editColumn) {
|
|
81
|
-
this.removeChild(editColumn);
|
|
82
|
-
}
|
|
83
|
-
} else if (!editColumn) {
|
|
84
|
-
editColumn = document.createElement('vaadin-crud-edit-column');
|
|
85
|
-
editColumn.frozenToEnd = true;
|
|
86
|
-
this.appendChild(editColumn);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @private */
|
|
91
|
-
__dataProviderWrapper(params, callback) {
|
|
92
|
-
this.__dataProvider(params, (items, size) => {
|
|
93
|
-
if (this.innerHTML === '' && !this.include && items[0]) {
|
|
94
|
-
this._configure(items[0]);
|
|
95
|
-
}
|
|
96
|
-
callback(items, size);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* @override
|
|
102
|
-
* @private
|
|
103
|
-
*/
|
|
104
|
-
_dataProviderChanged(dataProvider, oldDataProvider) {
|
|
105
|
-
if (this._arrayDataProvider === dataProvider) {
|
|
106
|
-
super._dataProviderChanged(dataProvider, oldDataProvider);
|
|
107
|
-
} else if (this.__dataProviderWrapper !== dataProvider) {
|
|
108
|
-
this.innerHTML = '';
|
|
109
|
-
this.__dataProvider = dataProvider;
|
|
110
|
-
this.dataProvider = this.__dataProviderWrapper;
|
|
111
|
-
super._dataProviderChanged(this.__dataProviderWrapper, oldDataProvider);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Auto-generate grid columns based on the JSON structure of the object provided.
|
|
117
|
-
*
|
|
118
|
-
* Method will be executed when items or dataProvider is assigned.
|
|
119
|
-
* @private
|
|
120
|
-
*/
|
|
121
|
-
_configure(item) {
|
|
122
|
-
this.innerHTML = '';
|
|
123
|
-
this.__createColumns(this, item, undefined, this.__getPropertyDepth(item));
|
|
124
|
-
this.__toggleEditColumn();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Return the deepest property depth of the object
|
|
129
|
-
* @private
|
|
130
|
-
*/
|
|
131
|
-
__getPropertyDepth(object) {
|
|
132
|
-
if (!object || typeof object !== 'object') {
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return Object.keys(object).reduce((deepest, prop) => {
|
|
137
|
-
if (this.exclude && this.exclude.test(prop)) {
|
|
138
|
-
return deepest;
|
|
139
|
-
}
|
|
140
|
-
return Math.max(deepest, 1 + this.__getPropertyDepth(object[prop]));
|
|
141
|
-
}, 0);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Parse the camelCase column names into sentence case headers.
|
|
146
|
-
* @param {string} path
|
|
147
|
-
* @return {string}
|
|
148
|
-
* @protected
|
|
149
|
-
*/
|
|
150
|
-
_generateHeader(path) {
|
|
151
|
-
return path
|
|
152
|
-
.substr(path.lastIndexOf('.') + 1)
|
|
153
|
-
.replace(/([A-Z])/gu, '-$1')
|
|
154
|
-
.toLowerCase()
|
|
155
|
-
.replace(/-/gu, ' ')
|
|
156
|
-
.replace(/^./u, (match) => match.toUpperCase());
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** @private */
|
|
160
|
-
__createColumn(parent, path) {
|
|
161
|
-
let col;
|
|
162
|
-
if (!this.noFilter && !this.noSort && !parent.__sortColumnGroup) {
|
|
163
|
-
// This crud-grid has both a sorter and a filter, but neither has yet been
|
|
164
|
-
// created => col should become the sorter group column
|
|
165
|
-
col = this.__createGroup(parent);
|
|
166
|
-
col.__sortColumnGroup = true;
|
|
167
|
-
// Create the filter column under this sorter group column
|
|
168
|
-
this.__createColumn(col, path);
|
|
169
|
-
} else {
|
|
170
|
-
// In all other cases, col should be a regular column with a renderer
|
|
171
|
-
col = document.createElement('vaadin-grid-column');
|
|
172
|
-
parent.appendChild(col);
|
|
173
|
-
col.renderer = (root, _column, model) => {
|
|
174
|
-
root.textContent = path ? getProperty(path, model.item) : model.item;
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (!this.noHead && path) {
|
|
179
|
-
// Create a header renderer for the column (or column group)
|
|
180
|
-
col.headerRenderer = (root) => {
|
|
181
|
-
if (root.firstElementChild) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const label = this._generateHeader(path);
|
|
186
|
-
|
|
187
|
-
if (col.__sortColumnGroup || (this.noFilter && !this.noSort)) {
|
|
188
|
-
// The column is either the sorter group column or the root level
|
|
189
|
-
// sort column (in case a filter isn't used at all) => add the sort indicator
|
|
190
|
-
const sorter = document.createElement('vaadin-grid-sorter');
|
|
191
|
-
sorter.setAttribute('path', path);
|
|
192
|
-
// TODO: Localize aria labels
|
|
193
|
-
sorter.setAttribute('aria-label', `Sort by ${label}`);
|
|
194
|
-
sorter.textContent = label;
|
|
195
|
-
root.appendChild(sorter);
|
|
196
|
-
} else if (!this.noFilter) {
|
|
197
|
-
// Filtering is enabled in this crud-grid, create the filter element
|
|
198
|
-
const filter = document.createElement('vaadin-grid-filter');
|
|
199
|
-
filter.setAttribute('path', path);
|
|
200
|
-
// TODO: Localize aria labels
|
|
201
|
-
filter.setAttribute('aria-label', `Filter by ${label}`);
|
|
202
|
-
filter.style.display = 'flex';
|
|
203
|
-
|
|
204
|
-
const textField = window.document.createElement('vaadin-text-field');
|
|
205
|
-
textField.setAttribute('theme', 'small');
|
|
206
|
-
textField.setAttribute('focus-target', true);
|
|
207
|
-
textField.style.width = '100%';
|
|
208
|
-
if (this.noSort) {
|
|
209
|
-
textField.placeholder = label;
|
|
210
|
-
}
|
|
211
|
-
textField.addEventListener('value-changed', (event) => {
|
|
212
|
-
filter.value = event.detail.value;
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
filter.appendChild(textField);
|
|
216
|
-
root.appendChild(filter);
|
|
217
|
-
} else if (this.noSort && this.noFilter) {
|
|
218
|
-
// Neither sorter nor filter are enabled, just add the label
|
|
219
|
-
root.textContent = label;
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Creates the column structure for the (sub)object.
|
|
227
|
-
*
|
|
228
|
-
* @param {HTMLElement} parent May be the crud-grid or a column group.
|
|
229
|
-
* @param {Object} object The object to create the sub-columns for.
|
|
230
|
-
* @param {string} path The property path from the root item to the object.
|
|
231
|
-
* @param {number} depth The depth of the object in the object hierarchy.
|
|
232
|
-
* @private
|
|
233
|
-
*/
|
|
234
|
-
__createColumns(parent, object, path, depth) {
|
|
235
|
-
if (object && typeof object === 'object') {
|
|
236
|
-
// Iterate over the object properties
|
|
237
|
-
Object.keys(object).forEach((prop) => {
|
|
238
|
-
if (!this.include && this.exclude && this.exclude.test(prop)) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
// Sub-object of the current object
|
|
242
|
-
const subObject = object[prop];
|
|
243
|
-
// Full path to the sub-object
|
|
244
|
-
const subObjectPath = path ? `${path}.${prop}` : prop;
|
|
245
|
-
|
|
246
|
-
// The column element for the sub-object
|
|
247
|
-
let subObjectColumn = parent;
|
|
248
|
-
if (!this.noHead && depth > 1) {
|
|
249
|
-
const isSubObject = subObject && typeof subObject === 'object';
|
|
250
|
-
// If the sub-object is an actual object, create a column group with the property
|
|
251
|
-
// name as the header text, otherwise create a group without a header
|
|
252
|
-
subObjectColumn = this.__createGroup(parent, isSubObject ? prop : undefined);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Run recursively for the sub-object level
|
|
256
|
-
this.__createColumns(subObjectColumn, subObject, subObjectPath, depth - 1);
|
|
257
|
-
});
|
|
258
|
-
} else if (depth > 1) {
|
|
259
|
-
// The object has been fully traversed, but empty wrapping column
|
|
260
|
-
// groups are still needed to complete the full object depth
|
|
261
|
-
this.__createColumns(this.__createGroup(parent), undefined, path, depth - 1);
|
|
262
|
-
} else {
|
|
263
|
-
// The column group depth is complete, create the actual leaf column
|
|
264
|
-
this.__createColumn(parent, path);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/** @private */
|
|
269
|
-
__createGroup(parent, header) {
|
|
270
|
-
const grp = document.createElement('vaadin-grid-column-group');
|
|
271
|
-
if (header) {
|
|
272
|
-
grp.header = capitalize(header);
|
|
273
|
-
}
|
|
274
|
-
parent.appendChild(grp);
|
|
275
|
-
return grp;
|
|
276
|
-
}
|
|
277
31
|
}
|
|
278
32
|
|
|
279
33
|
defineCustomElement(CrudGrid);
|