@dile/crud 2.6.4 → 2.7.1
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/components/list/__screenshots__/crud-data-grid.spec.js/dile-crud-data-grid-always-forces-sort-mode-to-external-regardless-of-config-1.png +0 -0
- package/components/list/__screenshots__/crud-data-grid.spec.js/dile-crud-data-grid-forwards-stickyFirstColumn--striped--responsiveMode-and-emptyMessage-with-sensible-defaults-1.png +0 -0
- package/components/list/__screenshots__/crud-data-grid.spec.js/dile-crud-data-grid-leaves-computeRowId-unset-when-config-computeItemId-is-not-a-function-1.png +0 -0
- package/components/list/crud-data-grid.js +5 -0
- package/components/list/crud-data-grid.spec.js +192 -0
- package/components/list/crud-item-actions.js +5 -0
- package/components/list/crud-item-actions.spec.js +122 -0
- package/components/list/crud-list.spec.js +255 -0
- package/components/list/index.js +4 -2
- package/components/list/src/DileCrudDataGrid.js +89 -0
- package/components/list/src/DileCrudItemActions.js +191 -0
- package/components/list/src/DileCrudList.js +55 -10
- package/components/list/src/DileCrudListItem.js +78 -124
- package/lib/CrudConfigBuilder.spec.js +32 -0
- package/lib/deepMerge.spec.js +45 -0
- package/lib/defaultConfig.js +4 -1
- package/package.json +3 -3
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { LitElement, html, css, nothing } from 'lit';
|
|
2
|
+
import '@dile/ui/components/button/button.js';
|
|
3
|
+
import '@dile/iconlib/material-icons/edit.js';
|
|
4
|
+
import '@dile/iconlib/material-icons/delete.js';
|
|
5
|
+
import '@dile/iconlib/material-icons/restore-from-trash.js';
|
|
6
|
+
|
|
7
|
+
export class DileCrudItemActions extends LitElement {
|
|
8
|
+
static styles = [
|
|
9
|
+
css`
|
|
10
|
+
:host {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: var(--dile-crud-item-actions-gap, 0.25rem);
|
|
14
|
+
}
|
|
15
|
+
dile-button.action-button {
|
|
16
|
+
--dile-button-background-color: var(--dile-crud-list-item-action-button-background-color, transparent);
|
|
17
|
+
--dile-button-border-color: var(--dile-crud-list-item-action-button-border-color, transparent);
|
|
18
|
+
--dile-button-hover-background-color: var(--dile-crud-list-item-action-button-hover-background-color, var(--dile-neutral-color, #f1f5f9));
|
|
19
|
+
--dile-button-hover-border-color: var(--dile-crud-list-item-action-button-hover-border-color, transparent);
|
|
20
|
+
--dile-button-padding-y: var(--dile-crud-list-item-action-button-padding-y, 0.25rem);
|
|
21
|
+
--dile-button-padding-x: var(--dile-crud-list-item-action-button-padding-x, 0.25rem);
|
|
22
|
+
--dile-button-icon-size: var(--dile-crud-list-item-action-button-icon-size, 24px);
|
|
23
|
+
--dile-button-border-radius: var(--dile-crud-list-item-action-button-border-radius, 2rem);
|
|
24
|
+
}
|
|
25
|
+
dile-button.action-button.edit {
|
|
26
|
+
--dile-button-icon-color: var(--edit-icon-color, var(--dile-alert-neutral-color, #0c7595));
|
|
27
|
+
--dile-button-icon-hover-color: var(--dile-crud-list-item-edit-icon-hover-color, var(--edit-icon-color, var(--dile-alert-neutral-color, #2889a7)));
|
|
28
|
+
}
|
|
29
|
+
dile-button.action-button.delete {
|
|
30
|
+
--dile-button-icon-color: var(--delete-icon-color, var(--dile-danger-color, #e33));
|
|
31
|
+
--dile-button-icon-hover-color: var(--dile-crud-list-item-delete-icon-hover-color, var(--delete-icon-color, var(--dile-danger-color, #e33)));
|
|
32
|
+
}
|
|
33
|
+
dile-button.action-button.restore {
|
|
34
|
+
--dile-button-icon-color: var(--restore-icon-color, var(--dile-alert-success-color, #00900f));
|
|
35
|
+
--dile-button-icon-hover-color: var(--dile-crud-list-item-restore-icon-hover-color, var(--restore-icon-color, var(--dile-alert-success-color, #00900f)));
|
|
36
|
+
}
|
|
37
|
+
`
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
static get properties() {
|
|
41
|
+
return {
|
|
42
|
+
item: { type: Object },
|
|
43
|
+
itemId: { type: String },
|
|
44
|
+
config: { type: Object },
|
|
45
|
+
disableEdit: { type: Boolean },
|
|
46
|
+
disableDelete: { type: Boolean },
|
|
47
|
+
disableRestore: { type: Boolean },
|
|
48
|
+
isDeleted: { type: Boolean },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
super();
|
|
54
|
+
this.config = null;
|
|
55
|
+
this.disableEdit = false;
|
|
56
|
+
this.disableDelete = false;
|
|
57
|
+
this.disableRestore = false;
|
|
58
|
+
this.isDeleted = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static hasActions(config) {
|
|
62
|
+
if (!config) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (config.customization?.disableListActions) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const editDisabled = Boolean(config.customization?.disableEdit);
|
|
69
|
+
const deleteDisabled = Boolean(config.customization?.disableDelete);
|
|
70
|
+
const restoreDisabled = Boolean(config.customization?.disableRestore);
|
|
71
|
+
|
|
72
|
+
return !(editDisabled && deleteDisabled && restoreDisabled);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get computedIsDeleted() {
|
|
76
|
+
if (this.isDeleted) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return Boolean(this.item?.deleted_at);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get computedItemId() {
|
|
83
|
+
if (this.itemId !== undefined && this.itemId !== null && this.itemId !== '') {
|
|
84
|
+
return this.itemId;
|
|
85
|
+
}
|
|
86
|
+
if (this.config?.computeItemId && typeof this.config.computeItemId === 'function' && this.item) {
|
|
87
|
+
return this.config.computeItemId(this.item);
|
|
88
|
+
}
|
|
89
|
+
return this.item?.id;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get isEditable() {
|
|
93
|
+
if (this.disableEdit) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (this.config) {
|
|
97
|
+
if (this.config.customization?.disableListActions || this.config.customization?.disableEdit) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (typeof this.config.isItemEditable === 'function' && this.item) {
|
|
101
|
+
return this.config.isItemEditable(this.item);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get isDeletable() {
|
|
108
|
+
if (this.disableDelete) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (this.config) {
|
|
112
|
+
if (this.config.customization?.disableListActions || this.config.customization?.disableDelete) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (typeof this.config.isItemDeletable === 'function' && this.item) {
|
|
116
|
+
return this.config.isItemDeletable(this.item);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get isRestorable() {
|
|
123
|
+
if (this.disableRestore) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (this.config?.customization?.disableListActions || this.config?.customization?.disableRestore) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
render() {
|
|
133
|
+
return this.computedIsDeleted ? this.restoreActionsTemplate : this.regularActionsTemplate;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
get regularActionsTemplate() {
|
|
137
|
+
const showEdit = this.isEditable;
|
|
138
|
+
const showDelete = this.isDeletable;
|
|
139
|
+
|
|
140
|
+
if (!showEdit && !showDelete) {
|
|
141
|
+
return nothing;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return html`
|
|
145
|
+
${showEdit
|
|
146
|
+
? html`<dile-button class="action-button edit" icon="material.edit" label="Edit" @click=${this.editClick}></dile-button>`
|
|
147
|
+
: nothing
|
|
148
|
+
}
|
|
149
|
+
${showDelete
|
|
150
|
+
? html`<dile-button class="action-button delete" icon="material.delete" label="Delete" @click=${this.deleteClick}></dile-button>`
|
|
151
|
+
: nothing
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get restoreActionsTemplate() {
|
|
157
|
+
if (!this.isRestorable) {
|
|
158
|
+
return nothing;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return html`
|
|
162
|
+
<dile-button class="action-button restore" icon="material.restore-from-trash" label="Restore" @click=${this.restoreClick}></dile-button>
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_dispatchItemEvent(eventName) {
|
|
167
|
+
this.dispatchEvent(new CustomEvent(eventName, {
|
|
168
|
+
bubbles: true,
|
|
169
|
+
composed: true,
|
|
170
|
+
detail: {
|
|
171
|
+
item: this.item,
|
|
172
|
+
itemId: this.computedItemId,
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
editClick(e) {
|
|
178
|
+
e?.stopPropagation();
|
|
179
|
+
this._dispatchItemEvent('crud-item-edit');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
deleteClick(e) {
|
|
183
|
+
e?.stopPropagation();
|
|
184
|
+
this._dispatchItemEvent('crud-item-delete');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
restoreClick(e) {
|
|
188
|
+
e?.stopPropagation();
|
|
189
|
+
this._dispatchItemEvent('crud-item-restore');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -7,6 +7,7 @@ import '../../list/crud-list-pagination-links.js';
|
|
|
7
7
|
import '../crud-list-item.js';
|
|
8
8
|
import '../crud-select-all.js';
|
|
9
9
|
import '../crud-list-service.js';
|
|
10
|
+
import '../crud-data-grid.js';
|
|
10
11
|
import { DileI18nMixin } from '../../../lib/DileI18nMixin.js';
|
|
11
12
|
|
|
12
13
|
export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
@@ -67,9 +68,14 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
67
68
|
}
|
|
68
69
|
.elements-container {
|
|
69
70
|
display: grid;
|
|
70
|
-
grid-template-columns: var(--dile-crud-list-elements-container-template-columns,
|
|
71
|
+
grid-template-columns: var(--dile-crud-list-elements-container-template-columns, minmax(0, 1fr));
|
|
71
72
|
gap: var(--dile-crud-list-elements-container-gap, 0);
|
|
72
73
|
}
|
|
74
|
+
.grid-container {
|
|
75
|
+
display: block;
|
|
76
|
+
width: 100%;
|
|
77
|
+
margin: var(--dile-crud-list-grid-container-margin, 0 0 1rem 0);
|
|
78
|
+
}
|
|
73
79
|
`
|
|
74
80
|
];
|
|
75
81
|
|
|
@@ -94,7 +100,7 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
94
100
|
|
|
95
101
|
constructor() {
|
|
96
102
|
super();
|
|
97
|
-
this.paginationData = {}
|
|
103
|
+
this.paginationData = {};
|
|
98
104
|
this.elements = [];
|
|
99
105
|
this.pageSize = 10;
|
|
100
106
|
this.keyword = '';
|
|
@@ -207,6 +213,7 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
207
213
|
></dile-ajax>
|
|
208
214
|
`;
|
|
209
215
|
}
|
|
216
|
+
|
|
210
217
|
get emptyTemplate() {
|
|
211
218
|
return html`
|
|
212
219
|
<div class="empty">
|
|
@@ -243,6 +250,32 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
243
250
|
}
|
|
244
251
|
|
|
245
252
|
get elementsTemplate() {
|
|
253
|
+
if (this.config?.templates?.grid && typeof this.config.templates.grid === 'function') {
|
|
254
|
+
return html`
|
|
255
|
+
<div
|
|
256
|
+
class="grid-container"
|
|
257
|
+
@item-checkbox-changed=${this.onItemsCheckboxChanged}
|
|
258
|
+
@dile-data-grid-sort=${this.onGridSort}
|
|
259
|
+
>
|
|
260
|
+
${this.config.templates.grid(this.elements, this.actionIds, this.config)}
|
|
261
|
+
</div>
|
|
262
|
+
`;
|
|
263
|
+
}
|
|
264
|
+
if (this.config?.grid?.columns && Array.isArray(this.config.grid.columns)) {
|
|
265
|
+
return html`
|
|
266
|
+
<div
|
|
267
|
+
class="grid-container"
|
|
268
|
+
@item-checkbox-changed=${this.onItemsCheckboxChanged}
|
|
269
|
+
@dile-data-grid-sort=${this.onGridSort}
|
|
270
|
+
>
|
|
271
|
+
<dile-crud-data-grid
|
|
272
|
+
.items=${this.elements}
|
|
273
|
+
.selectedIds=${this.actionIds}
|
|
274
|
+
.config=${this.config}
|
|
275
|
+
></dile-crud-data-grid>
|
|
276
|
+
</div>
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
246
279
|
return html`
|
|
247
280
|
<div class="elements-container">
|
|
248
281
|
${this.elements.map(element => html`
|
|
@@ -252,7 +285,7 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
252
285
|
.actionIds="${this.actionIds}"
|
|
253
286
|
?disableEdit=${!this.isItemEditable(element)}
|
|
254
287
|
?disableDelete=${!this.isItemDeletable(element)}
|
|
255
|
-
?disableRestore=${this.config?.customization?.disableRestore}
|
|
288
|
+
?disableRestore=${this.config?.customization?.disableRestore || this.config?.customization?.disableListActions}
|
|
256
289
|
?hideCheckboxSelection="${this.config?.customization?.hideCheckboxSelection}"
|
|
257
290
|
@item-checkbox-changed=${this.onItemsCheckboxChanged}
|
|
258
291
|
?isDeleted=${element.deleted_at}
|
|
@@ -276,13 +309,13 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
276
309
|
}
|
|
277
310
|
|
|
278
311
|
isItemEditable(element) {
|
|
279
|
-
const isGloballyDisabled = this.config?.customization?.disableEdit;
|
|
312
|
+
const isGloballyDisabled = this.config?.customization?.disableListActions || this.config?.customization?.disableEdit;
|
|
280
313
|
const isItemEditable = this.config?.isItemEditable(element) ?? true;
|
|
281
314
|
return !isGloballyDisabled && isItemEditable;
|
|
282
315
|
}
|
|
283
316
|
|
|
284
317
|
isItemDeletable(element) {
|
|
285
|
-
const isGloballyDisabled = this.config?.customization?.disableDelete;
|
|
318
|
+
const isGloballyDisabled = this.config?.customization?.disableListActions || this.config?.customization?.disableDelete;
|
|
286
319
|
const isItemDeletable = this.config?.isItemDeletable(element) ?? true;
|
|
287
320
|
return !isGloballyDisabled && isItemDeletable;
|
|
288
321
|
}
|
|
@@ -314,11 +347,11 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
314
347
|
}
|
|
315
348
|
|
|
316
349
|
goNext() {
|
|
317
|
-
this.elservice.goNext()
|
|
350
|
+
this.elservice.goNext();
|
|
318
351
|
}
|
|
319
352
|
|
|
320
353
|
goPrev() {
|
|
321
|
-
this.elservice.goPrev()
|
|
354
|
+
this.elservice.goPrev();
|
|
322
355
|
}
|
|
323
356
|
|
|
324
357
|
refresh() {
|
|
@@ -332,7 +365,6 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
332
365
|
this.shadowRoot.querySelector('dile-crud-select-all').reset();
|
|
333
366
|
}
|
|
334
367
|
this.elservice.refresh();
|
|
335
|
-
|
|
336
368
|
}
|
|
337
369
|
|
|
338
370
|
setKeyword(keyword) {
|
|
@@ -347,6 +379,20 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
347
379
|
this.elservice.setSort(sortObject);
|
|
348
380
|
}
|
|
349
381
|
|
|
382
|
+
onGridSort(e) {
|
|
383
|
+
if (e.detail?.field) {
|
|
384
|
+
this.setSort({
|
|
385
|
+
sortField: e.detail.field,
|
|
386
|
+
sortDirection: e.detail.direction || 'asc',
|
|
387
|
+
});
|
|
388
|
+
} else {
|
|
389
|
+
this.setSort({
|
|
390
|
+
sortField: null,
|
|
391
|
+
sortDirection: null,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
350
396
|
setPageSize(size) {
|
|
351
397
|
this.loading = true;
|
|
352
398
|
this.pageSize = size;
|
|
@@ -389,7 +435,7 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
389
435
|
if(this.config.customization?.disablePagination) {
|
|
390
436
|
let ids = this.elements.map(item => this.config.computeItemId(item));
|
|
391
437
|
this.dispactSelectAll(ids);
|
|
392
|
-
} else{
|
|
438
|
+
} else {
|
|
393
439
|
let data = {
|
|
394
440
|
keyword: this.keyword,
|
|
395
441
|
filters: this.filters,
|
|
@@ -413,7 +459,6 @@ export class DileCrudList extends DileI18nMixin(DileLoading(LitElement)) {
|
|
|
413
459
|
if (!e.detail.checked && this.isSelectAllActive) {
|
|
414
460
|
this.shadowRoot.querySelector('dile-crud-select-all').resetWithoutDispatch();
|
|
415
461
|
}
|
|
416
|
-
|
|
417
462
|
}
|
|
418
463
|
|
|
419
464
|
onListError(e) {
|
|
@@ -1,67 +1,42 @@
|
|
|
1
|
-
import { LitElement, html, css } from 'lit';
|
|
1
|
+
import { LitElement, html, css, nothing } from 'lit';
|
|
2
2
|
import '@dile/ui/components/checkbox/checkbox.js';
|
|
3
|
-
import '
|
|
4
|
-
import '@dile/iconlib/material-icons/edit.js';
|
|
5
|
-
import '@dile/iconlib/material-icons/delete.js';
|
|
6
|
-
import '@dile/iconlib/material-icons/restore-from-trash.js';
|
|
3
|
+
import '../crud-item-actions.js';
|
|
7
4
|
|
|
8
5
|
export class DileCrudListItem extends LitElement {
|
|
9
6
|
static styles = [
|
|
10
7
|
css`
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
:host {
|
|
9
|
+
display: var(--dile-crud-list-item-display, block);
|
|
10
|
+
max-width: var(--dile-crud-list-item-max-width, 100%);
|
|
11
|
+
width: var(--dile-crud-list-item-width, 100%);
|
|
12
|
+
}
|
|
13
|
+
section {
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
padding: var(--dile-crud-list-item-padding, 0.5rem 0.5rem);
|
|
19
|
+
border-bottom: var(--dile-crud-list-item-line-separator, 1px solid #ddd);
|
|
20
|
+
}
|
|
21
|
+
dile-checkbox {
|
|
22
|
+
margin-right: 0.5rem;
|
|
23
|
+
--dile-checkbox-unchecked-color: #888;
|
|
24
|
+
}
|
|
25
|
+
main {
|
|
26
|
+
flex-grow: 1;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
}
|
|
29
|
+
.actions {
|
|
30
|
+
margin-left: 0.5rem;
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
text-align: right;
|
|
34
|
+
}
|
|
35
|
+
@media (min-width: 550px) {
|
|
16
36
|
section {
|
|
17
|
-
|
|
18
|
-
overflow: hidden;
|
|
19
|
-
display: flex;
|
|
20
|
-
align-items: center;
|
|
21
|
-
padding: var(--dile-crud-list-item-padding, 0.5rem 0.5rem);
|
|
22
|
-
border-bottom: var(--dile-crud-list-item-line-separator, 1px solid #ddd);
|
|
23
|
-
}
|
|
24
|
-
dile-checkbox {
|
|
25
|
-
margin-right: 0.5rem;
|
|
26
|
-
--dile-checkbox-unchecked-color: #888;
|
|
27
|
-
}
|
|
28
|
-
main {
|
|
29
|
-
flex-grow: 1;
|
|
30
|
-
overflow: hidden;
|
|
31
|
-
}
|
|
32
|
-
.actions {
|
|
33
|
-
margin-left: 0.5rem;
|
|
34
|
-
display: flex;
|
|
35
|
-
align-items: center;
|
|
36
|
-
text-align: right;
|
|
37
|
-
}
|
|
38
|
-
dile-button.action-button {
|
|
39
|
-
--dile-button-background-color: var(--dile-crud-list-item-action-button-background-color, transparent);
|
|
40
|
-
--dile-button-border-color: var(--dile-crud-list-item-action-button-border-color, transparent);
|
|
41
|
-
--dile-button-hover-background-color: var(--dile-crud-list-item-action-button-hover-background-color, var(--dile-neutral-color, transparent));
|
|
42
|
-
--dile-button-hover-border-color: var(--dile-crud-list-item-action-button-hover-border-color, transparent);
|
|
43
|
-
--dile-button-padding-y: var(--dile-crud-list-item-action-button-padding-y, 0.25rem);
|
|
44
|
-
--dile-button-padding-x: var(--dile-crud-list-item-action-button-padding-x, 0.25rem);
|
|
45
|
-
--dile-button-icon-size: var(--dile-crud-list-item-action-button-icon-size, 24px);
|
|
46
|
-
--dile-button-border-radius: var(--dile-crud-list-item-action-button-border-radius, 2rem);
|
|
47
|
-
}
|
|
48
|
-
dile-button.action-button.edit {
|
|
49
|
-
--dile-button-icon-color: var(--edit-icon-color, var(--dile-alert-neutral-color, #2889a7));
|
|
50
|
-
--dile-button-icon-hover-color: var(--dile-crud-list-item-edit-icon-hover-color, var(--edit-icon-color, var(--dile-alert-neutral-color, #2889a7)));
|
|
51
|
-
}
|
|
52
|
-
dile-button.action-button.delete {
|
|
53
|
-
--dile-button-icon-color: var(--delete-icon-color, var(--dile-danger-color, #e33));
|
|
54
|
-
--dile-button-icon-hover-color: var(--dile-crud-list-item-delete-icon-hover-color, var(--delete-icon-color, var(--dile-danger-color, #e33)));
|
|
55
|
-
}
|
|
56
|
-
dile-button.action-button.restore {
|
|
57
|
-
--dile-button-icon-color: var(--restore-icon-color, var(--dile-alert-success-color, #00900f));
|
|
58
|
-
--dile-button-icon-hover-color: var(--dile-crud-list-item-restore-icon-hover-color, var(--restore-icon-color, var(--dile-alert-success-color, #00900f)));
|
|
59
|
-
}
|
|
60
|
-
@media(min-width: 550px) {
|
|
61
|
-
section {
|
|
62
|
-
padding: var(--dile-crud-list-item-padding, 0.5rem 1rem);
|
|
63
|
-
}
|
|
37
|
+
padding: var(--dile-crud-list-item-padding, 0.5rem 1rem);
|
|
64
38
|
}
|
|
39
|
+
}
|
|
65
40
|
`
|
|
66
41
|
];
|
|
67
42
|
|
|
@@ -72,6 +47,7 @@ export class DileCrudListItem extends LitElement {
|
|
|
72
47
|
actionIds: { type: Array },
|
|
73
48
|
disableEdit: { type: Boolean },
|
|
74
49
|
disableDelete: { type: Boolean },
|
|
50
|
+
disableRestore: { type: Boolean },
|
|
75
51
|
/** Hide checkboxes on the item list */
|
|
76
52
|
hideCheckboxSelection: { type: Boolean },
|
|
77
53
|
isDeleted: { type: Boolean },
|
|
@@ -85,86 +61,64 @@ export class DileCrudListItem extends LitElement {
|
|
|
85
61
|
this.disableDelete = false;
|
|
86
62
|
this.disableRestore = false;
|
|
87
63
|
this.hideCheckboxSelection = false;
|
|
64
|
+
this.isDeleted = false;
|
|
65
|
+
}
|
|
88
66
|
|
|
67
|
+
get hasActions() {
|
|
68
|
+
if (this.isDeleted) {
|
|
69
|
+
return !this.disableRestore;
|
|
70
|
+
}
|
|
71
|
+
return !this.disableEdit || !this.disableDelete;
|
|
89
72
|
}
|
|
90
73
|
|
|
91
74
|
render() {
|
|
92
75
|
return html`
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
76
|
+
<section>
|
|
77
|
+
${this.hideCheckboxSelection
|
|
78
|
+
? ''
|
|
79
|
+
: html`
|
|
80
|
+
<dile-checkbox
|
|
81
|
+
?checked="${this.includes(this.actionIds, this.itemId)}"
|
|
82
|
+
@dile-checkbox-changed=${this.checkboxChanged}
|
|
83
|
+
></dile-checkbox>
|
|
96
84
|
`}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
85
|
+
<main>
|
|
86
|
+
<slot></slot>
|
|
87
|
+
</main>
|
|
88
|
+
${this.hasActions
|
|
89
|
+
? html`
|
|
90
|
+
<div class="actions">
|
|
91
|
+
<dile-crud-item-actions
|
|
92
|
+
.item=${this.item}
|
|
93
|
+
itemId="${this.itemId}"
|
|
94
|
+
?disableEdit=${this.disableEdit}
|
|
95
|
+
?disableDelete=${this.disableDelete}
|
|
96
|
+
?disableRestore=${this.disableRestore}
|
|
97
|
+
?isDeleted=${this.isDeleted}
|
|
98
|
+
></dile-crud-item-actions>
|
|
99
|
+
</div>
|
|
100
|
+
`
|
|
101
|
+
: nothing
|
|
102
|
+
}
|
|
103
|
+
</section>
|
|
107
104
|
`;
|
|
108
105
|
}
|
|
109
106
|
|
|
110
|
-
get regularActionsTemplate() {
|
|
111
|
-
return html`
|
|
112
|
-
${this.disableEdit
|
|
113
|
-
? ''
|
|
114
|
-
: html`<dile-button class="action-button edit" icon="material.edit" label="Edit" @click=${this.editClick}></dile-button>`
|
|
115
|
-
}
|
|
116
|
-
${this.disableDelete
|
|
117
|
-
? ''
|
|
118
|
-
: html`<dile-button class="action-button delete" icon="material.delete" label="Delete" @click=${this.deleteClick}></dile-button>`
|
|
119
|
-
}
|
|
120
|
-
`
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
get restoreActionsTemplate() {
|
|
124
|
-
return html`
|
|
125
|
-
${this.disableRestore
|
|
126
|
-
? ''
|
|
127
|
-
: html`<dile-button class="action-button restore" icon="material.restore-from-trash" label="Restore" @click=${this.restoreClick}></dile-button>`
|
|
128
|
-
}
|
|
129
|
-
`
|
|
130
|
-
}
|
|
131
|
-
|
|
132
107
|
includes(actionIds, itemId) {
|
|
133
108
|
const stringIds = actionIds.map(String);
|
|
134
109
|
return stringIds.includes(String(itemId));
|
|
135
110
|
}
|
|
136
111
|
|
|
137
112
|
checkboxChanged(e) {
|
|
138
|
-
this.dispatchEvent(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
_dispatchItemEvent(eventName) {
|
|
149
|
-
this.dispatchEvent(new CustomEvent(eventName, {
|
|
150
|
-
bubbles: true,
|
|
151
|
-
composed: true,
|
|
152
|
-
detail: {
|
|
153
|
-
item: this.item,
|
|
154
|
-
itemId: this.itemId,
|
|
155
|
-
}
|
|
156
|
-
}));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
editClick() {
|
|
160
|
-
this._dispatchItemEvent('crud-item-edit');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
deleteClick() {
|
|
164
|
-
this._dispatchItemEvent('crud-item-delete');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
restoreClick() {
|
|
168
|
-
this._dispatchItemEvent('crud-item-restore');
|
|
113
|
+
this.dispatchEvent(
|
|
114
|
+
new CustomEvent('item-checkbox-changed', {
|
|
115
|
+
bubbles: true,
|
|
116
|
+
composed: true,
|
|
117
|
+
detail: {
|
|
118
|
+
checked: e.detail.checked,
|
|
119
|
+
itemId: this.itemId,
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
);
|
|
169
123
|
}
|
|
170
|
-
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { CrudConfigBuilder } from './CrudConfigBuilder.js';
|
|
3
|
+
|
|
4
|
+
describe('CrudConfigBuilder', () => {
|
|
5
|
+
it('merges customConfig.grid.columns over the default null grid', () => {
|
|
6
|
+
const columns = [{ field: 'id', header: 'ID' }];
|
|
7
|
+
|
|
8
|
+
const config = new CrudConfigBuilder('https://example.test/api/items', {
|
|
9
|
+
grid: { columns },
|
|
10
|
+
}).getConfig();
|
|
11
|
+
|
|
12
|
+
expect(config.grid.columns).toEqual(columns);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('sets the endpoint from the constructor argument', () => {
|
|
16
|
+
const config = new CrudConfigBuilder('https://example.test/api/items', {}).getConfig();
|
|
17
|
+
|
|
18
|
+
expect(config.endpoint).toBe('https://example.test/api/items');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('getConfig(endpoint) returns a copy with an overridden endpoint without mutating the original config', () => {
|
|
22
|
+
const builder = new CrudConfigBuilder('https://example.test/api/items', {
|
|
23
|
+
grid: { columns: [{ field: 'id' }] },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const relatedConfig = builder.getConfig('https://example.test/api/items/1/related');
|
|
27
|
+
|
|
28
|
+
expect(relatedConfig.endpoint).toBe('https://example.test/api/items/1/related');
|
|
29
|
+
expect(builder.getConfig().endpoint).toBe('https://example.test/api/items');
|
|
30
|
+
expect(relatedConfig.grid.columns).toEqual([{ field: 'id' }]);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { deepMerge } from './deepMerge.js';
|
|
3
|
+
|
|
4
|
+
describe('deepMerge', () => {
|
|
5
|
+
it('replaces a null target property with a plain object from source', () => {
|
|
6
|
+
const target = { grid: null };
|
|
7
|
+
const source = { grid: { columns: [{ field: 'id' }] } };
|
|
8
|
+
|
|
9
|
+
const result = deepMerge(target, source);
|
|
10
|
+
|
|
11
|
+
expect(result.grid).toEqual({ columns: [{ field: 'id' }] });
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('preserves array order and content when merging a columns array under a previously-null key', () => {
|
|
15
|
+
const columns = [
|
|
16
|
+
{ field: 'id', header: 'ID' },
|
|
17
|
+
{ field: 'name', header: 'Name' },
|
|
18
|
+
{ field: 'continent', header: 'Continent' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const result = deepMerge({ grid: null }, { grid: { columns } });
|
|
22
|
+
|
|
23
|
+
expect(result.grid.columns).toEqual(columns);
|
|
24
|
+
expect(result.grid.columns).not.toBe(columns);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('does not mutate the source when merging into a null target key', () => {
|
|
28
|
+
const source = { grid: { columns: [{ field: 'id' }] } };
|
|
29
|
+
|
|
30
|
+
const result = deepMerge({ grid: null }, source);
|
|
31
|
+
result.grid.columns.push({ field: 'extra' });
|
|
32
|
+
|
|
33
|
+
expect(source.grid.columns.length).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('overwrites configured overwriteProperties instances as-is instead of merging them', () => {
|
|
37
|
+
class Adapter {}
|
|
38
|
+
const targetAdapter = new Adapter();
|
|
39
|
+
const sourceAdapter = new Adapter();
|
|
40
|
+
|
|
41
|
+
const result = deepMerge({ responseAdapter: targetAdapter }, { responseAdapter: sourceAdapter });
|
|
42
|
+
|
|
43
|
+
expect(result.responseAdapter).toBe(sourceAdapter);
|
|
44
|
+
});
|
|
45
|
+
});
|
package/lib/defaultConfig.js
CHANGED
|
@@ -16,6 +16,7 @@ export const defaultConfig = {
|
|
|
16
16
|
initial: 25,
|
|
17
17
|
},
|
|
18
18
|
maxBatchActionItems: 100,
|
|
19
|
+
grid: null,
|
|
19
20
|
customization: {
|
|
20
21
|
disableKeywordSearch: true,
|
|
21
22
|
hideCountSummary: false,
|
|
@@ -26,6 +27,7 @@ export const defaultConfig = {
|
|
|
26
27
|
disableEdit: false,
|
|
27
28
|
disableDelete: false,
|
|
28
29
|
disableRestore: false,
|
|
30
|
+
disableListActions: false,
|
|
29
31
|
disablePagination: true,
|
|
30
32
|
disableSort: true,
|
|
31
33
|
disableFilter: true,
|
|
@@ -62,6 +64,7 @@ export const defaultConfig = {
|
|
|
62
64
|
},
|
|
63
65
|
templates: {
|
|
64
66
|
item: () => templatePlaceholder('item'),
|
|
67
|
+
grid: null,
|
|
65
68
|
insertForm: (belongsTo, relationId) => templatePlaceholder('insertForm'),
|
|
66
69
|
updateForm: () => templatePlaceholder('updateForm'),
|
|
67
70
|
help: () => templatePlaceholder('help'),
|
|
@@ -84,4 +87,4 @@ export const defaultConfig = {
|
|
|
84
87
|
onActionListSuccess(actionSuccessDetail) {},
|
|
85
88
|
onActionSingleSuccess(actionSuccessDetail) {},
|
|
86
89
|
destructiveActionNames: ['DeleteAction'],
|
|
87
|
-
}
|
|
90
|
+
}
|