@vaadin/crud 24.6.0-beta1 → 24.6.0
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
|
@@ -0,0 +1,1045 @@
|
|
|
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 { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
12
|
+
import { FocusRestorationController } from '@vaadin/a11y-base/src/focus-restoration-controller.js';
|
|
13
|
+
import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
|
|
14
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
15
|
+
import { ButtonSlotController, FormSlotController, GridSlotController } from './vaadin-crud-controllers.js';
|
|
16
|
+
import { getProperty, isValidEditorPosition, setProperty } from './vaadin-crud-helpers.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A mixin providing common crud functionality.
|
|
20
|
+
*
|
|
21
|
+
* @polymerMixin
|
|
22
|
+
*/
|
|
23
|
+
export const CrudMixin = (superClass) =>
|
|
24
|
+
class extends superClass {
|
|
25
|
+
static get properties() {
|
|
26
|
+
return {
|
|
27
|
+
/**
|
|
28
|
+
* A reference to the grid used for displaying the item list
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
_grid: {
|
|
32
|
+
type: Object,
|
|
33
|
+
observer: '__gridChanged',
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A reference to the editor component which will be teleported to the dialog
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
_form: {
|
|
41
|
+
type: Object,
|
|
42
|
+
observer: '__formChanged',
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A reference to the save button which will be teleported to the dialog
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
_saveButton: {
|
|
50
|
+
type: Object,
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A reference to the delete button which will be teleported to the dialog
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
_deleteButton: {
|
|
58
|
+
type: Object,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A reference to the cancel button which will be teleported to the dialog
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
_cancelButton: {
|
|
66
|
+
type: Object,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A reference to the default editor header element created by the CRUD
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
_defaultHeader: {
|
|
74
|
+
type: Object,
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A reference to the "New item" button
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
_newButton: {
|
|
82
|
+
type: Object,
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* An array containing the items which will be stamped to the column template instances.
|
|
87
|
+
* @type {Array<unknown> | undefined}
|
|
88
|
+
*/
|
|
89
|
+
items: {
|
|
90
|
+
type: Array,
|
|
91
|
+
notify: true,
|
|
92
|
+
observer: '__itemsChanged',
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The item being edited in the dialog.
|
|
97
|
+
* @type {unknown}
|
|
98
|
+
*/
|
|
99
|
+
editedItem: {
|
|
100
|
+
type: Object,
|
|
101
|
+
observer: '__editedItemChanged',
|
|
102
|
+
notify: true,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Sets how editor will be presented on desktop screen.
|
|
107
|
+
*
|
|
108
|
+
* Accepted values are:
|
|
109
|
+
* - `` (default) - form will open as overlay
|
|
110
|
+
* - `bottom` - form will open below the grid
|
|
111
|
+
* - `aside` - form will open on the grid side (_right_, if lft and _left_ if rtl)
|
|
112
|
+
* @attr {bottom|aside} editor-position
|
|
113
|
+
* @type {!CrudEditorPosition}
|
|
114
|
+
*/
|
|
115
|
+
editorPosition: {
|
|
116
|
+
type: String,
|
|
117
|
+
value: '',
|
|
118
|
+
reflectToAttribute: true,
|
|
119
|
+
observer: '__editorPositionChanged',
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Enables user to click on row to edit it.
|
|
124
|
+
* Note: When enabled, auto-generated grid won't show the edit column.
|
|
125
|
+
* @attr {boolean} edit-on-click
|
|
126
|
+
* @type {boolean}
|
|
127
|
+
*/
|
|
128
|
+
editOnClick: {
|
|
129
|
+
type: Boolean,
|
|
130
|
+
value: false,
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Function that provides items lazily. Receives arguments `params`, `callback`
|
|
135
|
+
*
|
|
136
|
+
* `params.page` Requested page index
|
|
137
|
+
* `params.pageSize` Current page size
|
|
138
|
+
* `params.filters` Currently applied filters
|
|
139
|
+
* `params.sortOrders` Currently applied sorting orders
|
|
140
|
+
*
|
|
141
|
+
* `callback(items, size)` Callback function with arguments:
|
|
142
|
+
* - `items` Current page of items
|
|
143
|
+
* - `size` Total number of items
|
|
144
|
+
* @type {CrudDataProvider | undefined}
|
|
145
|
+
*/
|
|
146
|
+
dataProvider: {
|
|
147
|
+
type: Function,
|
|
148
|
+
observer: '__dataProviderChanged',
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Disable filtering when grid is autoconfigured.
|
|
153
|
+
* @attr {boolean} no-filter
|
|
154
|
+
*/
|
|
155
|
+
noFilter: Boolean,
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Disable sorting when grid is autoconfigured.
|
|
159
|
+
* @attr {boolean} no-sort
|
|
160
|
+
*/
|
|
161
|
+
noSort: Boolean,
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Remove grid headers when it is autoconfigured.
|
|
165
|
+
* @attr {boolean} no-head
|
|
166
|
+
*/
|
|
167
|
+
noHead: Boolean,
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* A comma-separated list of fields to include in the generated grid and the generated editor.
|
|
171
|
+
*
|
|
172
|
+
* It can be used to explicitly define the field order.
|
|
173
|
+
*
|
|
174
|
+
* When it is defined [`exclude`](#/elements/vaadin-crud#property-exclude) is ignored.
|
|
175
|
+
*
|
|
176
|
+
* Default is undefined meaning that all properties in the object should be mapped to fields.
|
|
177
|
+
*/
|
|
178
|
+
include: String,
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A comma-separated list of fields to be excluded from the generated grid and the generated editor.
|
|
182
|
+
*
|
|
183
|
+
* When [`include`](#/elements/vaadin-crud#property-include) is defined, this parameter is ignored.
|
|
184
|
+
*
|
|
185
|
+
* Default is to exclude all private fields (those properties starting with underscore)
|
|
186
|
+
*/
|
|
187
|
+
exclude: String,
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Reflects the opened status of the editor.
|
|
191
|
+
*/
|
|
192
|
+
editorOpened: {
|
|
193
|
+
type: Boolean,
|
|
194
|
+
reflectToAttribute: true,
|
|
195
|
+
notify: true,
|
|
196
|
+
observer: '__editorOpenedChanged',
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Number of items in the data set which is reported by the grid.
|
|
201
|
+
* Typically it reflects the number of filtered items displayed in the grid.
|
|
202
|
+
*
|
|
203
|
+
* Note: As with `<vaadin-grid>`, this property updates automatically only
|
|
204
|
+
* if `items` is used for data.
|
|
205
|
+
*/
|
|
206
|
+
size: {
|
|
207
|
+
type: Number,
|
|
208
|
+
readOnly: true,
|
|
209
|
+
notify: true,
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Controls visibility state of toolbar.
|
|
214
|
+
* When set to false toolbar is hidden and shown when set to true.
|
|
215
|
+
* @attr {boolean} no-toolbar
|
|
216
|
+
*/
|
|
217
|
+
noToolbar: {
|
|
218
|
+
type: Boolean,
|
|
219
|
+
value: false,
|
|
220
|
+
reflectToAttribute: true,
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The object used to localize this component.
|
|
225
|
+
* For changing the default localization, change the entire
|
|
226
|
+
* _i18n_ object or just the property you want to modify.
|
|
227
|
+
*
|
|
228
|
+
* The object has the following JSON structure and default values:
|
|
229
|
+
*
|
|
230
|
+
* ```
|
|
231
|
+
* {
|
|
232
|
+
* newItem: 'New item',
|
|
233
|
+
* editItem: 'Edit item',
|
|
234
|
+
* saveItem: 'Save',
|
|
235
|
+
* cancel: 'Cancel',
|
|
236
|
+
* deleteItem: 'Delete...',
|
|
237
|
+
* editLabel: 'Edit',
|
|
238
|
+
* confirm: {
|
|
239
|
+
* delete: {
|
|
240
|
+
* title: 'Confirm delete',
|
|
241
|
+
* content: 'Are you sure you want to delete the selected item? This action cannot be undone.',
|
|
242
|
+
* button: {
|
|
243
|
+
* confirm: 'Delete',
|
|
244
|
+
* dismiss: 'Cancel'
|
|
245
|
+
* }
|
|
246
|
+
* },
|
|
247
|
+
* cancel: {
|
|
248
|
+
* title: 'Unsaved changes',
|
|
249
|
+
* content: 'There are unsaved modifications to the item.',
|
|
250
|
+
* button: {
|
|
251
|
+
* confirm: 'Discard',
|
|
252
|
+
* dismiss: 'Continue editing'
|
|
253
|
+
* }
|
|
254
|
+
* }
|
|
255
|
+
* }
|
|
256
|
+
* }
|
|
257
|
+
* ```
|
|
258
|
+
*
|
|
259
|
+
* @type {!CrudI18n}
|
|
260
|
+
* @default {English/US}
|
|
261
|
+
*/
|
|
262
|
+
i18n: {
|
|
263
|
+
type: Object,
|
|
264
|
+
value() {
|
|
265
|
+
return {
|
|
266
|
+
newItem: 'New item',
|
|
267
|
+
editItem: 'Edit item',
|
|
268
|
+
saveItem: 'Save',
|
|
269
|
+
cancel: 'Cancel',
|
|
270
|
+
deleteItem: 'Delete...',
|
|
271
|
+
editLabel: 'Edit',
|
|
272
|
+
confirm: {
|
|
273
|
+
delete: {
|
|
274
|
+
title: 'Delete item',
|
|
275
|
+
content: 'Are you sure you want to delete this item? This action cannot be undone.',
|
|
276
|
+
button: {
|
|
277
|
+
confirm: 'Delete',
|
|
278
|
+
dismiss: 'Cancel',
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
cancel: {
|
|
282
|
+
title: 'Discard changes',
|
|
283
|
+
content: 'There are unsaved changes to this item.',
|
|
284
|
+
button: {
|
|
285
|
+
confirm: 'Discard',
|
|
286
|
+
dismiss: 'Cancel',
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/** @private */
|
|
295
|
+
__dialogAriaLabel: String,
|
|
296
|
+
|
|
297
|
+
/** @private */
|
|
298
|
+
__isDirty: Boolean,
|
|
299
|
+
|
|
300
|
+
/** @private */
|
|
301
|
+
__isNew: Boolean,
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @type {boolean}
|
|
305
|
+
* @protected
|
|
306
|
+
*/
|
|
307
|
+
_fullscreen: {
|
|
308
|
+
type: Boolean,
|
|
309
|
+
observer: '__fullscreenChanged',
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @type {string}
|
|
314
|
+
* @protected
|
|
315
|
+
*/
|
|
316
|
+
_fullscreenMediaQuery: {
|
|
317
|
+
value: '(max-width: 600px), (max-height: 600px)',
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
static get observers() {
|
|
323
|
+
return [
|
|
324
|
+
'__headerPropsChanged(_defaultHeader, __isNew, i18n)',
|
|
325
|
+
'__formPropsChanged(_form, _theme, include, exclude)',
|
|
326
|
+
'__gridPropsChanged(_grid, _theme, include, exclude, noFilter, noHead, noSort, items)',
|
|
327
|
+
'__i18nChanged(i18n, _grid)',
|
|
328
|
+
'__editOnClickChanged(editOnClick, _grid)',
|
|
329
|
+
'__saveButtonPropsChanged(_saveButton, i18n, __isDirty)',
|
|
330
|
+
'__cancelButtonPropsChanged(_cancelButton, i18n)',
|
|
331
|
+
'__deleteButtonPropsChanged(_deleteButton, i18n, __isNew)',
|
|
332
|
+
'__newButtonPropsChanged(_newButton, i18n)',
|
|
333
|
+
];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
constructor() {
|
|
337
|
+
super();
|
|
338
|
+
|
|
339
|
+
this.__cancel = this.__cancel.bind(this);
|
|
340
|
+
this.__delete = this.__delete.bind(this);
|
|
341
|
+
this.__save = this.__save.bind(this);
|
|
342
|
+
this.__new = this.__new.bind(this);
|
|
343
|
+
this.__onFormChange = this.__onFormChange.bind(this);
|
|
344
|
+
this.__onGridEdit = this.__onGridEdit.bind(this);
|
|
345
|
+
this.__onGridSizeChanged = this.__onGridSizeChanged.bind(this);
|
|
346
|
+
this.__onGridActiveItemChanged = this.__onGridActiveItemChanged.bind(this);
|
|
347
|
+
|
|
348
|
+
this.__focusRestorationController = new FocusRestorationController();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** @protected */
|
|
352
|
+
get _headerNode() {
|
|
353
|
+
return this._headerController && this._headerController.node;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* A reference to all fields inside the [`_form`](#/elements/vaadin-crud#property-_form) element
|
|
358
|
+
* @return {!Array<!HTMLElement>}
|
|
359
|
+
* @protected
|
|
360
|
+
*/
|
|
361
|
+
get _fields() {
|
|
362
|
+
if (!this.__fields || !this.__fields.length) {
|
|
363
|
+
this.__fields = Array.from(this._form.querySelectorAll('*')).filter((e) => e.validate || e.checkValidity);
|
|
364
|
+
}
|
|
365
|
+
return this.__fields;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** @protected */
|
|
369
|
+
ready() {
|
|
370
|
+
super.ready();
|
|
371
|
+
|
|
372
|
+
this.$.dialog.$.overlay.addEventListener('vaadin-overlay-outside-click', this.__cancel);
|
|
373
|
+
this.$.dialog.$.overlay.addEventListener('vaadin-overlay-escape-press', this.__cancel);
|
|
374
|
+
|
|
375
|
+
this._headerController = new SlotController(this, 'header', 'h3', {
|
|
376
|
+
initializer: (node) => {
|
|
377
|
+
this._defaultHeader = node;
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
this.addController(this._headerController);
|
|
381
|
+
|
|
382
|
+
this._gridController = new GridSlotController(this);
|
|
383
|
+
this.addController(this._gridController);
|
|
384
|
+
|
|
385
|
+
this.addController(new FormSlotController(this));
|
|
386
|
+
|
|
387
|
+
// Init controllers in `ready()` (not constructor) so that Flow can set `_noDefaultButtons`
|
|
388
|
+
this._newButtonController = new ButtonSlotController(this, 'new', 'primary', this._noDefaultButtons);
|
|
389
|
+
this._saveButtonController = new ButtonSlotController(this, 'save', 'primary', this._noDefaultButtons);
|
|
390
|
+
this._cancelButtonController = new ButtonSlotController(this, 'cancel', 'tertiary', this._noDefaultButtons);
|
|
391
|
+
this._deleteButtonController = new ButtonSlotController(this, 'delete', 'tertiary error', this._noDefaultButtons);
|
|
392
|
+
|
|
393
|
+
this.addController(this._newButtonController);
|
|
394
|
+
|
|
395
|
+
// NOTE: order in which buttons are added should match the order of slots in template
|
|
396
|
+
this.addController(this._saveButtonController);
|
|
397
|
+
this.addController(this._cancelButtonController);
|
|
398
|
+
this.addController(this._deleteButtonController);
|
|
399
|
+
|
|
400
|
+
this.addController(
|
|
401
|
+
new MediaQueryController(this._fullscreenMediaQuery, (matches) => {
|
|
402
|
+
this._fullscreen = matches;
|
|
403
|
+
}),
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
this.addController(this.__focusRestorationController);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @param {boolean} isDirty
|
|
411
|
+
* @private
|
|
412
|
+
*/
|
|
413
|
+
__isSaveBtnDisabled(isDirty) {
|
|
414
|
+
// Used instead of isDirty property binding in order to enable overriding of the behavior
|
|
415
|
+
// by overriding the method (i.e. from Flow component)
|
|
416
|
+
return !isDirty;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @param {HTMLElement | undefined} headerNode
|
|
421
|
+
* @param {boolean} isNew
|
|
422
|
+
* @param {string} i18nNewItem
|
|
423
|
+
* @param {string} i18nEditItem
|
|
424
|
+
* @private
|
|
425
|
+
*/
|
|
426
|
+
__headerPropsChanged(headerNode, isNew, i18n) {
|
|
427
|
+
if (headerNode) {
|
|
428
|
+
headerNode.textContent = isNew ? i18n.newItem : i18n.editItem;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* @param {CrudI18n} i18n
|
|
434
|
+
* @param {CrudGrid | Grid} grid
|
|
435
|
+
* @private
|
|
436
|
+
*/
|
|
437
|
+
__i18nChanged(i18n, grid) {
|
|
438
|
+
if (!grid) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
afterNextRender(grid, () => {
|
|
443
|
+
Array.from(grid.querySelectorAll('vaadin-crud-edit-column')).forEach((column) => {
|
|
444
|
+
column.ariaLabel = i18n.editLabel;
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** @private */
|
|
450
|
+
__editorPositionChanged(editorPosition) {
|
|
451
|
+
if (isValidEditorPosition(editorPosition)) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
this.editorPosition = '';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** @private */
|
|
458
|
+
__editorOpenedChanged(opened, oldOpened) {
|
|
459
|
+
if (!opened && oldOpened) {
|
|
460
|
+
this.__closeEditor();
|
|
461
|
+
} else {
|
|
462
|
+
this.__formChanged(this._form);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (opened) {
|
|
466
|
+
this.__ensureChildren();
|
|
467
|
+
|
|
468
|
+
// When using bottom / aside editor position,
|
|
469
|
+
// auto-focus the editor element on open.
|
|
470
|
+
if (this._form.parentElement === this) {
|
|
471
|
+
this.$.editor.setAttribute('tabindex', '0');
|
|
472
|
+
this.$.editor.focus();
|
|
473
|
+
} else {
|
|
474
|
+
this.$.editor.removeAttribute('tabindex');
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
this.__toggleToolbar();
|
|
479
|
+
|
|
480
|
+
// Make sure to reset scroll position
|
|
481
|
+
this.$.scroller.scrollTop = 0;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** @private */
|
|
485
|
+
__fullscreenChanged(fullscreen, oldFullscreen) {
|
|
486
|
+
if (fullscreen || oldFullscreen) {
|
|
487
|
+
this.__toggleToolbar();
|
|
488
|
+
|
|
489
|
+
this.__ensureChildren();
|
|
490
|
+
|
|
491
|
+
this.toggleAttribute('fullscreen', fullscreen);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** @private */
|
|
496
|
+
__toggleToolbar() {
|
|
497
|
+
// Hide toolbar to give more room for the editor when it's positioned below the grid
|
|
498
|
+
if (this.editorPosition === 'bottom' && !this._fullscreen) {
|
|
499
|
+
this.$.toolbar.style.display = this.editorOpened ? 'none' : '';
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** @private */
|
|
504
|
+
__moveChildNodes(target) {
|
|
505
|
+
const nodes = [this._headerNode, this._form, this._saveButton, this._cancelButton, this._deleteButton];
|
|
506
|
+
if (!nodes.every((node) => node instanceof HTMLElement)) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Teleport header node, form, and the buttons to corresponding slots.
|
|
511
|
+
// NOTE: order in which buttons are moved matches the order of slots.
|
|
512
|
+
nodes.forEach((node) => {
|
|
513
|
+
target.appendChild(node);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Wait to set label until slotted element has been moved.
|
|
517
|
+
setTimeout(() => {
|
|
518
|
+
this.__dialogAriaLabel = this._headerNode.textContent.trim();
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** @private */
|
|
523
|
+
__shouldOpenDialog(fullscreen, editorPosition) {
|
|
524
|
+
return editorPosition === '' || fullscreen;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** @private */
|
|
528
|
+
__ensureChildren() {
|
|
529
|
+
if (this.__shouldOpenDialog(this._fullscreen, this.editorPosition)) {
|
|
530
|
+
// Move form to dialog
|
|
531
|
+
this.__moveChildNodes(this.$.dialog.$.overlay);
|
|
532
|
+
} else {
|
|
533
|
+
// Move form to crud
|
|
534
|
+
this.__moveChildNodes(this);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** @private */
|
|
539
|
+
__computeDialogOpened(opened, fullscreen, editorPosition) {
|
|
540
|
+
// Only open dialog when editorPosition is "" or fullscreen is set
|
|
541
|
+
return this.__shouldOpenDialog(fullscreen, editorPosition) ? opened : false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** @private */
|
|
545
|
+
__computeEditorHidden(opened, fullscreen, editorPosition) {
|
|
546
|
+
// Only show editor when editorPosition is "bottom" or "aside"
|
|
547
|
+
if (['aside', 'bottom'].includes(editorPosition) && !fullscreen) {
|
|
548
|
+
return !opened;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/** @private */
|
|
555
|
+
__onDialogOpened(event) {
|
|
556
|
+
this.editorOpened = event.detail.value;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** @private */
|
|
560
|
+
__onGridEdit(event) {
|
|
561
|
+
event.stopPropagation();
|
|
562
|
+
this.__confirmBeforeChangingEditedItem(event.detail.item);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** @private */
|
|
566
|
+
__onFormChange() {
|
|
567
|
+
this.__isDirty = true;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** @private */
|
|
571
|
+
__onGridSizeChanged() {
|
|
572
|
+
this._setSize(this._grid.size);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* @param {CrudGrid | Grid} grid
|
|
577
|
+
* @param {CrudGrid | Grid | undefined} oldGrid
|
|
578
|
+
* @private
|
|
579
|
+
*/
|
|
580
|
+
__gridChanged(grid, oldGrid) {
|
|
581
|
+
if (oldGrid) {
|
|
582
|
+
oldGrid.removeEventListener('edit', this.__onGridEdit);
|
|
583
|
+
oldGrid.removeEventListener('size-changed', this.__onGridSizeChanged);
|
|
584
|
+
}
|
|
585
|
+
if (this.dataProvider) {
|
|
586
|
+
this.__dataProviderChanged(this.dataProvider);
|
|
587
|
+
}
|
|
588
|
+
if (this.editedItem) {
|
|
589
|
+
this.__editedItemChanged(this.editedItem);
|
|
590
|
+
}
|
|
591
|
+
grid.addEventListener('edit', this.__onGridEdit);
|
|
592
|
+
grid.addEventListener('size-changed', this.__onGridSizeChanged);
|
|
593
|
+
this.__onGridSizeChanged();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* @param {HTMLElement | undefined | null} form
|
|
598
|
+
* @param {HTMLElement | undefined | null} oldForm
|
|
599
|
+
* @private
|
|
600
|
+
*/
|
|
601
|
+
__formChanged(form, oldForm) {
|
|
602
|
+
if (oldForm && oldForm.parentElement) {
|
|
603
|
+
oldForm.parentElement.removeChild(oldForm);
|
|
604
|
+
oldForm.removeEventListener('change', this.__onFormChange);
|
|
605
|
+
oldForm.removeEventListener('input', this.__onFormChange);
|
|
606
|
+
}
|
|
607
|
+
if (!form) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (this.items) {
|
|
611
|
+
this.__itemsChanged(this.items);
|
|
612
|
+
}
|
|
613
|
+
if (this.editedItem) {
|
|
614
|
+
this.__editedItemChanged(this.editedItem);
|
|
615
|
+
}
|
|
616
|
+
form.addEventListener('change', this.__onFormChange);
|
|
617
|
+
form.addEventListener('input', this.__onFormChange);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* @param {HTMLElement | undefined} form
|
|
622
|
+
* @param {string} theme
|
|
623
|
+
* @param {string | string[] | undefined} include
|
|
624
|
+
* @param {string | RegExp} exclude
|
|
625
|
+
* @private
|
|
626
|
+
*/
|
|
627
|
+
__formPropsChanged(form, theme, include, exclude) {
|
|
628
|
+
if (form) {
|
|
629
|
+
form.include = include;
|
|
630
|
+
form.exclude = exclude;
|
|
631
|
+
|
|
632
|
+
if (theme) {
|
|
633
|
+
form.setAttribute('theme', theme);
|
|
634
|
+
} else {
|
|
635
|
+
form.removeAttribute('theme');
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* @param {HTMLElement | undefined} grid
|
|
642
|
+
* @param {string} theme
|
|
643
|
+
* @param {string | string[] | undefined} include
|
|
644
|
+
* @param {string | RegExp} exclude
|
|
645
|
+
* @param {boolean} noFilter
|
|
646
|
+
* @param {boolean} noHead
|
|
647
|
+
* @param {boolean} noSort
|
|
648
|
+
* @param {Array<unknown> | undefined} items
|
|
649
|
+
* @private
|
|
650
|
+
*/
|
|
651
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
652
|
+
__gridPropsChanged(grid, theme, include, exclude, noFilter, noHead, noSort, items) {
|
|
653
|
+
if (!grid) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (grid === this._gridController.defaultNode) {
|
|
658
|
+
grid.noFilter = noFilter;
|
|
659
|
+
grid.noHead = noHead;
|
|
660
|
+
grid.noSort = noSort;
|
|
661
|
+
grid.include = include;
|
|
662
|
+
grid.exclude = exclude;
|
|
663
|
+
|
|
664
|
+
if (theme) {
|
|
665
|
+
grid.setAttribute('theme', theme);
|
|
666
|
+
} else {
|
|
667
|
+
grid.removeAttribute('theme');
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
grid.items = items;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* @param {HTMLElement | undefined} saveButton
|
|
676
|
+
* @param {string} i18nLabel
|
|
677
|
+
* @param {boolean} isDirty
|
|
678
|
+
* @private
|
|
679
|
+
*/
|
|
680
|
+
__saveButtonPropsChanged(saveButton, i18n, isDirty) {
|
|
681
|
+
if (saveButton) {
|
|
682
|
+
saveButton.toggleAttribute('disabled', this.__isSaveBtnDisabled(isDirty));
|
|
683
|
+
|
|
684
|
+
if (saveButton === this._saveButtonController.defaultNode) {
|
|
685
|
+
saveButton.textContent = i18n.saveItem;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* @param {HTMLElement | undefined} deleteButton
|
|
692
|
+
* @param {string} i18nLabel
|
|
693
|
+
* @param {boolean} isNew
|
|
694
|
+
* @private
|
|
695
|
+
*/
|
|
696
|
+
__deleteButtonPropsChanged(deleteButton, i18n, isNew) {
|
|
697
|
+
if (deleteButton) {
|
|
698
|
+
deleteButton.toggleAttribute('hidden', isNew);
|
|
699
|
+
|
|
700
|
+
if (deleteButton === this._deleteButtonController.defaultNode) {
|
|
701
|
+
deleteButton.textContent = i18n.deleteItem;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* @param {HTMLElement | undefined} cancelButton
|
|
708
|
+
* @param {string} i18nLabel
|
|
709
|
+
* @private
|
|
710
|
+
*/
|
|
711
|
+
__cancelButtonPropsChanged(cancelButton, i18n) {
|
|
712
|
+
if (cancelButton && cancelButton === this._cancelButtonController.defaultNode) {
|
|
713
|
+
cancelButton.textContent = i18n.cancel;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* @param {HTMLElement | undefined} newButton
|
|
719
|
+
* @param {string} i18nNewItem
|
|
720
|
+
* @private
|
|
721
|
+
*/
|
|
722
|
+
__newButtonPropsChanged(newButton, i18n) {
|
|
723
|
+
if (newButton && newButton === this._newButtonController.defaultNode) {
|
|
724
|
+
newButton.textContent = i18n.newItem;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** @private */
|
|
729
|
+
__dataProviderChanged(dataProvider) {
|
|
730
|
+
if (this._grid) {
|
|
731
|
+
this._grid.dataProvider = this.__createDataProviderProxy(dataProvider);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/** @private */
|
|
736
|
+
__editOnClickChanged(editOnClick, grid) {
|
|
737
|
+
if (!grid) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
grid.hideEditColumn = editOnClick;
|
|
742
|
+
|
|
743
|
+
if (editOnClick) {
|
|
744
|
+
grid.addEventListener('active-item-changed', this.__onGridActiveItemChanged);
|
|
745
|
+
} else {
|
|
746
|
+
grid.removeEventListener('active-item-changed', this.__onGridActiveItemChanged);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/** @private */
|
|
751
|
+
__onGridActiveItemChanged(event) {
|
|
752
|
+
const item = event.detail.value;
|
|
753
|
+
if (this.editorOpened && this.__isDirty) {
|
|
754
|
+
this.__confirmBeforeChangingEditedItem(item);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (item) {
|
|
758
|
+
this.__edit(item);
|
|
759
|
+
} else if (!this.__keepOpened) {
|
|
760
|
+
this.__closeEditor();
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/** @private */
|
|
765
|
+
__confirmBeforeChangingEditedItem(item, keepOpened) {
|
|
766
|
+
if (
|
|
767
|
+
this.editorOpened && // Editor opened
|
|
768
|
+
this.__isDirty && // Form change has been made
|
|
769
|
+
this.editedItem !== item // Item is different
|
|
770
|
+
) {
|
|
771
|
+
this.$.confirmCancel.opened = true;
|
|
772
|
+
this.addEventListener(
|
|
773
|
+
'cancel',
|
|
774
|
+
(event) => {
|
|
775
|
+
event.preventDefault(); // Prevent closing the editor
|
|
776
|
+
if (item || keepOpened) {
|
|
777
|
+
this.__edit(item);
|
|
778
|
+
this.__clearItemAndKeepEditorOpened(item, keepOpened);
|
|
779
|
+
} else {
|
|
780
|
+
this.__closeEditor();
|
|
781
|
+
}
|
|
782
|
+
},
|
|
783
|
+
{ once: true },
|
|
784
|
+
);
|
|
785
|
+
} else {
|
|
786
|
+
this.__edit(item);
|
|
787
|
+
this.__clearItemAndKeepEditorOpened(item, keepOpened);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/** @private */
|
|
792
|
+
__clearItemAndKeepEditorOpened(item, keepOpened) {
|
|
793
|
+
if (!item) {
|
|
794
|
+
setTimeout(() => {
|
|
795
|
+
this.__keepOpened = keepOpened;
|
|
796
|
+
this.editedItem = this._grid.activeItem = undefined;
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/** @private */
|
|
802
|
+
__createDataProviderProxy(dataProvider) {
|
|
803
|
+
return (params, callback) => {
|
|
804
|
+
const callbackProxy = (chunk, size) => {
|
|
805
|
+
if (chunk && chunk[0]) {
|
|
806
|
+
this.__model = chunk[0];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
callback(chunk, size);
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
dataProvider(params, callbackProxy);
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/** @private */
|
|
817
|
+
__itemsChanged(items) {
|
|
818
|
+
if (this.items && this.items[0]) {
|
|
819
|
+
this.__model = items[0];
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/** @private */
|
|
824
|
+
__editedItemChanged(item) {
|
|
825
|
+
if (!this._form) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (item) {
|
|
829
|
+
if (!this._fields.length && this._form._configure) {
|
|
830
|
+
if (this.__model) {
|
|
831
|
+
this._form._configure(this.__model);
|
|
832
|
+
} else {
|
|
833
|
+
console.warn(
|
|
834
|
+
'<vaadin-crud> Unable to autoconfigure form because the data structure is unknown. ' +
|
|
835
|
+
'Either specify `include` or ensure at least one item is available beforehand.',
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
this._form.item = item;
|
|
840
|
+
this._fields.forEach((e) => {
|
|
841
|
+
const path = e.path || e.getAttribute('path');
|
|
842
|
+
if (path) {
|
|
843
|
+
e.value = getProperty(path, item);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
this.__isNew = !!(this.__isNew || (this.items && this.items.indexOf(item) < 0));
|
|
848
|
+
this.editorOpened = true;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/** @private */
|
|
853
|
+
__validate() {
|
|
854
|
+
return this._fields.every((e) => (e.validate || e.checkValidity).call(e));
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/** @private */
|
|
858
|
+
__setHighlightedItem(item) {
|
|
859
|
+
if (this._grid === this._gridController.defaultNode) {
|
|
860
|
+
this._grid.selectedItems = item ? [item] : [];
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/** @private */
|
|
865
|
+
__closeEditor() {
|
|
866
|
+
this.editorOpened = false;
|
|
867
|
+
this.__isDirty = false;
|
|
868
|
+
this.__setHighlightedItem(null);
|
|
869
|
+
|
|
870
|
+
// Delay changing the item in order not to modify editor while closing
|
|
871
|
+
setTimeout(() => this.__clearItemAndKeepEditorOpened(null, false));
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/** @private */
|
|
875
|
+
__new() {
|
|
876
|
+
this.__confirmBeforeChangingEditedItem(null, true);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** @private */
|
|
880
|
+
__edit(item) {
|
|
881
|
+
if (this.editedItem === item) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
this.__setHighlightedItem(item);
|
|
885
|
+
this.__openEditor(item);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/** @private */
|
|
889
|
+
__fireEvent(type, item) {
|
|
890
|
+
const event = new CustomEvent(type, { detail: { item }, cancelable: true });
|
|
891
|
+
this.dispatchEvent(event);
|
|
892
|
+
return event.defaultPrevented === false;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/** @private */
|
|
896
|
+
__openEditor(item) {
|
|
897
|
+
this.__focusRestorationController.saveFocus();
|
|
898
|
+
|
|
899
|
+
this.__isDirty = false;
|
|
900
|
+
this.__isNew = !item;
|
|
901
|
+
const result = this.__fireEvent(this.__isNew ? 'new' : 'edit', item);
|
|
902
|
+
if (result) {
|
|
903
|
+
this.editedItem = item || {};
|
|
904
|
+
} else {
|
|
905
|
+
this.editorOpened = true;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/** @private */
|
|
910
|
+
__restoreFocusOnDelete() {
|
|
911
|
+
if (this._grid._flatSize === 1) {
|
|
912
|
+
this._newButton.focus();
|
|
913
|
+
} else {
|
|
914
|
+
this._grid._focusFirstVisibleRow();
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** @private */
|
|
919
|
+
__restoreFocusOnSaveOrCancel() {
|
|
920
|
+
const focusNode = this.__focusRestorationController.focusNode;
|
|
921
|
+
const row = this._grid._getRowContainingNode(focusNode);
|
|
922
|
+
if (!row) {
|
|
923
|
+
this.__focusRestorationController.restoreFocus();
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
if (this._grid._isItemAssignedToRow(this.editedItem, row) && this._grid._isInViewport(row)) {
|
|
928
|
+
this.__focusRestorationController.restoreFocus();
|
|
929
|
+
} else {
|
|
930
|
+
this._grid._focusFirstVisibleRow();
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/** @private */
|
|
935
|
+
__save() {
|
|
936
|
+
if (!this.__validate()) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const item = { ...this.editedItem };
|
|
941
|
+
this._fields.forEach((e) => {
|
|
942
|
+
const path = e.path || e.getAttribute('path');
|
|
943
|
+
if (path) {
|
|
944
|
+
setProperty(path, e.value, item);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
const result = this.__fireEvent('save', item);
|
|
948
|
+
if (result) {
|
|
949
|
+
if (this.__isNew && !this.dataProvider) {
|
|
950
|
+
if (!this.items) {
|
|
951
|
+
this.items = [item];
|
|
952
|
+
} else {
|
|
953
|
+
this.items.push(item);
|
|
954
|
+
}
|
|
955
|
+
} else {
|
|
956
|
+
if (!this.editedItem) {
|
|
957
|
+
this.editedItem = {};
|
|
958
|
+
}
|
|
959
|
+
Object.assign(this.editedItem, item);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
this.__restoreFocusOnSaveOrCancel();
|
|
963
|
+
this._grid.clearCache();
|
|
964
|
+
this.__closeEditor();
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** @private */
|
|
969
|
+
__cancel() {
|
|
970
|
+
if (this.__isDirty) {
|
|
971
|
+
this.$.confirmCancel.opened = true;
|
|
972
|
+
} else {
|
|
973
|
+
this.__confirmCancel();
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/** @private */
|
|
978
|
+
__confirmCancel() {
|
|
979
|
+
const result = this.__fireEvent('cancel', this.editedItem);
|
|
980
|
+
if (result) {
|
|
981
|
+
this.__restoreFocusOnSaveOrCancel();
|
|
982
|
+
this.__closeEditor();
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/** @private */
|
|
987
|
+
__delete() {
|
|
988
|
+
this.$.confirmDelete.opened = true;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/** @private */
|
|
992
|
+
__confirmDelete() {
|
|
993
|
+
const result = this.__fireEvent('delete', this.editedItem);
|
|
994
|
+
if (result) {
|
|
995
|
+
if (this.items && this.items.indexOf(this.editedItem) >= 0) {
|
|
996
|
+
this.items.splice(this.items.indexOf(this.editedItem), 1);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
this.__restoreFocusOnDelete();
|
|
1000
|
+
this._grid.clearCache();
|
|
1001
|
+
this.__closeEditor();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Fired when user wants to edit an existing item. If the default is prevented, then
|
|
1007
|
+
* a new item is not assigned to the form, giving that responsibility to the app, though
|
|
1008
|
+
* dialog is always opened.
|
|
1009
|
+
*
|
|
1010
|
+
* @event edit
|
|
1011
|
+
* @param {Object} detail.item the item to edit
|
|
1012
|
+
*/
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Fired when user wants to create a new item.
|
|
1016
|
+
*
|
|
1017
|
+
* @event new
|
|
1018
|
+
*/
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Fired when user wants to delete item. If the default is prevented, then
|
|
1022
|
+
* no action is performed, items array is not modified nor dialog closed
|
|
1023
|
+
*
|
|
1024
|
+
* @event delete
|
|
1025
|
+
* @param {Object} detail.item the item to delete
|
|
1026
|
+
*/
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Fired when user discards edition. If the default is prevented, then
|
|
1030
|
+
* no action is performed, user is responsible to close dialog and reset
|
|
1031
|
+
* item and grid.
|
|
1032
|
+
*
|
|
1033
|
+
* @event cancel
|
|
1034
|
+
* @param {Object} detail.item the item to delete
|
|
1035
|
+
*/
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Fired when user wants to save a new or an existing item. If the default is prevented, then
|
|
1039
|
+
* no action is performed, items array is not modified nor dialog closed
|
|
1040
|
+
*
|
|
1041
|
+
* @event save
|
|
1042
|
+
* @param {Object} detail.item the item to save
|
|
1043
|
+
* @param {Object} detail.new whether the item is a new one
|
|
1044
|
+
*/
|
|
1045
|
+
};
|