@dile/crud 2.6.4 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/components/crud/crud.spec.js +142 -0
  2. package/components/crud/src/DileCrud.js +23 -3
  3. 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
  4. 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
  5. 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
  6. package/components/list/crud-data-grid.js +5 -0
  7. package/components/list/crud-data-grid.spec.js +207 -0
  8. package/components/list/crud-item-actions.js +5 -0
  9. package/components/list/crud-item-actions.spec.js +122 -0
  10. package/components/list/crud-list.spec.js +319 -0
  11. package/components/list/index.js +4 -2
  12. package/components/list/src/DileCrudDataGrid.js +93 -0
  13. package/components/list/src/DileCrudItemActions.js +191 -0
  14. package/components/list/src/DileCrudList.js +62 -11
  15. package/components/list/src/DileCrudListItem.js +78 -124
  16. package/components/ui/__screenshots__/crud-sort-form.spec.js/dile-crud-sort-form-syncSort-suppresses-exactly-one-radio-group-echo--preserving-the-synced-direction-instead-of-falling-back-to-the-option-static-default-1.png +0 -0
  17. package/components/ui/crud-sort-form.spec.js +132 -0
  18. package/components/ui/src/DileCrudSortForm.js +24 -5
  19. package/lib/CrudConfigBuilder.spec.js +32 -0
  20. package/lib/deepMerge.spec.js +45 -0
  21. package/lib/defaultConfig.js +4 -1
  22. package/package.json +3 -3
@@ -0,0 +1,319 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { html } from 'lit';
3
+ import { CrudConfigBuilder } from '../../lib/CrudConfigBuilder.js';
4
+ import './crud-list.js';
5
+ import './crud-data-grid.js';
6
+ import '@dile/ui/components/data-grid/data-grid.js';
7
+
8
+ describe('dile-crud-list grid template integration', () => {
9
+ afterEach(() => {
10
+ document.body.innerHTML = '';
11
+ });
12
+
13
+ const sampleElements = [
14
+ { id: 1, name: 'Customer A', email: 'a@example.com' },
15
+ { id: 2, name: 'Customer B', email: 'b@example.com' },
16
+ ];
17
+
18
+ it('renders default dile-crud-list-item when templates.grid is not provided', async () => {
19
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
20
+ templates: {
21
+ item: (item) => html`<span class="item-name">${item.name}</span>`,
22
+ },
23
+ }).getConfig();
24
+
25
+ const el = document.createElement('dile-crud-list');
26
+ el.disableLoadOnStart = true;
27
+ el.config = config;
28
+ el.elements = sampleElements;
29
+ el.loading = false;
30
+ document.body.appendChild(el);
31
+ await el.updateComplete;
32
+
33
+ const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
34
+ expect(listItems.length).toBe(2);
35
+
36
+ const gridContainer = el.shadowRoot.querySelector('.grid-container');
37
+ expect(gridContainer).toBeNull();
38
+ });
39
+
40
+ it('renders generic dile-crud-data-grid when config.grid.columns is defined', async () => {
41
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
42
+ grid: {
43
+ columns: [
44
+ { field: 'id', header: 'ID' },
45
+ { field: 'name', header: 'Customer Name' },
46
+ ],
47
+ striped: true,
48
+ },
49
+ }).getConfig();
50
+
51
+ const el = document.createElement('dile-crud-list');
52
+ el.disableLoadOnStart = true;
53
+ el.config = config;
54
+ el.elements = sampleElements;
55
+ el.sort = { sortField: 'name', sortDirection: 'desc' };
56
+ el.loading = false;
57
+ document.body.appendChild(el);
58
+ await el.updateComplete;
59
+
60
+ const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
61
+ expect(listItems.length).toBe(0);
62
+
63
+ const crudDataGrid = el.shadowRoot.querySelector('dile-crud-data-grid');
64
+ expect(crudDataGrid).toBeTruthy();
65
+ expect(crudDataGrid.items).toEqual(sampleElements);
66
+ expect(crudDataGrid.columns.length).toBe(3); // id, name + automatically injected Actions
67
+ expect(crudDataGrid.columns[2].header).toBe('Actions');
68
+ expect(crudDataGrid.sort).toEqual({ sortField: 'name', sortDirection: 'desc' });
69
+ });
70
+
71
+ it('hides actions in traditional list items when disableListActions is true', async () => {
72
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
73
+ customization: {
74
+ disableListActions: true,
75
+ },
76
+ templates: {
77
+ item: (item) => html`<span class="item-name">${item.name}</span>`,
78
+ },
79
+ }).getConfig();
80
+
81
+ const el = document.createElement('dile-crud-list');
82
+ el.disableLoadOnStart = true;
83
+ el.config = config;
84
+ el.elements = sampleElements;
85
+ el.loading = false;
86
+ document.body.appendChild(el);
87
+ await el.updateComplete;
88
+
89
+ const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
90
+ expect(listItems.length).toBe(2);
91
+
92
+ for (const item of listItems) {
93
+ const actionsDiv = item.shadowRoot.querySelector('.actions');
94
+ expect(actionsDiv).toBeNull();
95
+ }
96
+ });
97
+
98
+ it('renders custom grid template when templates.grid is configured', async () => {
99
+ let receivedElements = null;
100
+ let receivedActionIds = null;
101
+ let receivedSort = null;
102
+
103
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
104
+ templates: {
105
+ grid: (elements, actionIds, config, sort) => {
106
+ receivedElements = elements;
107
+ receivedActionIds = actionIds;
108
+ receivedSort = sort;
109
+ return html`
110
+ <dile-data-grid
111
+ .items="${elements}"
112
+ .selectedIds="${actionIds}"
113
+ selectable
114
+ .columns="${[
115
+ { field: 'id', header: 'ID' },
116
+ { field: 'name', header: 'Name' },
117
+ ]}"
118
+ ></dile-data-grid>
119
+ `;
120
+ },
121
+ },
122
+ }).getConfig();
123
+
124
+ const el = document.createElement('dile-crud-list');
125
+ el.disableLoadOnStart = true;
126
+ el.config = config;
127
+ el.elements = sampleElements;
128
+ el.actionIds = [2];
129
+ el.sort = { sortField: 'name', sortDirection: 'asc' };
130
+ el.loading = false;
131
+ document.body.appendChild(el);
132
+ await el.updateComplete;
133
+
134
+ const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
135
+ expect(listItems.length).toBe(0);
136
+
137
+ const gridContainer = el.shadowRoot.querySelector('.grid-container');
138
+ expect(gridContainer).toBeTruthy();
139
+
140
+ const dataGrid = el.shadowRoot.querySelector('dile-data-grid');
141
+ expect(dataGrid).toBeTruthy();
142
+ expect(receivedElements).toEqual(sampleElements);
143
+ expect(receivedActionIds).toEqual([2]);
144
+ expect(receivedSort).toEqual({ sortField: 'name', sortDirection: 'asc' });
145
+ });
146
+
147
+ it('updates sort when dile-data-grid-sort is dispatched', async () => {
148
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
149
+ templates: {
150
+ grid: (elements, actionIds) => html`
151
+ <dile-data-grid
152
+ .items="${elements}"
153
+ sort-mode="external"
154
+ .columns="${[
155
+ { field: 'name', header: 'Name', sortable: true },
156
+ ]}"
157
+ ></dile-data-grid>
158
+ `,
159
+ },
160
+ }).getConfig();
161
+
162
+ const el = document.createElement('dile-crud-list');
163
+ el.disableLoadOnStart = true;
164
+ el.config = config;
165
+ el.elements = sampleElements;
166
+ el.loading = false;
167
+ document.body.appendChild(el);
168
+ await el.updateComplete;
169
+
170
+ let sortApplied = null;
171
+ el.setSort = (sortObj) => {
172
+ sortApplied = sortObj;
173
+ };
174
+
175
+ const gridContainer = el.shadowRoot.querySelector('.grid-container');
176
+ gridContainer.dispatchEvent(
177
+ new CustomEvent('dile-data-grid-sort', {
178
+ bubbles: true,
179
+ composed: true,
180
+ detail: {
181
+ field: 'name',
182
+ direction: 'asc',
183
+ },
184
+ })
185
+ );
186
+
187
+ expect(sortApplied).toEqual({
188
+ sortField: 'name',
189
+ sortDirection: 'asc',
190
+ });
191
+ });
192
+
193
+ it('updates sort when dile-data-grid-sort is dispatched from the config.grid.columns branch', async () => {
194
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
195
+ grid: {
196
+ columns: [
197
+ { field: 'name', header: 'Name', sortable: true },
198
+ ],
199
+ },
200
+ }).getConfig();
201
+
202
+ const el = document.createElement('dile-crud-list');
203
+ el.disableLoadOnStart = true;
204
+ el.config = config;
205
+ el.elements = sampleElements;
206
+ el.loading = false;
207
+ document.body.appendChild(el);
208
+ await el.updateComplete;
209
+
210
+ let sortApplied = null;
211
+ el.setSort = (sortObj) => {
212
+ sortApplied = sortObj;
213
+ };
214
+
215
+ const gridContainer = el.shadowRoot.querySelector('.grid-container');
216
+ gridContainer.dispatchEvent(
217
+ new CustomEvent('dile-data-grid-sort', {
218
+ bubbles: true,
219
+ composed: true,
220
+ detail: {
221
+ field: 'name',
222
+ direction: 'desc',
223
+ },
224
+ })
225
+ );
226
+
227
+ expect(sortApplied).toEqual({
228
+ sortField: 'name',
229
+ sortDirection: 'desc',
230
+ });
231
+ });
232
+
233
+ it('prefers templates.grid over config.grid.columns when both are configured', async () => {
234
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
235
+ grid: {
236
+ columns: [
237
+ { field: 'id', header: 'ID' },
238
+ ],
239
+ },
240
+ templates: {
241
+ grid: (elements, actionIds) => html`
242
+ <dile-data-grid
243
+ .items="${elements}"
244
+ .columns="${[{ field: 'name', header: 'Name' }]}"
245
+ ></dile-data-grid>
246
+ `,
247
+ },
248
+ }).getConfig();
249
+
250
+ const el = document.createElement('dile-crud-list');
251
+ el.disableLoadOnStart = true;
252
+ el.config = config;
253
+ el.elements = sampleElements;
254
+ el.loading = false;
255
+ document.body.appendChild(el);
256
+ await el.updateComplete;
257
+
258
+ expect(el.shadowRoot.querySelector('dile-crud-data-grid')).toBeNull();
259
+ expect(el.shadowRoot.querySelector('dile-data-grid')).toBeTruthy();
260
+ });
261
+
262
+ it('dispatches crud-list-sort-changed with the new sort whenever setSort is called', async () => {
263
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
264
+ grid: { columns: [{ field: 'name', header: 'Name', sortable: true }] },
265
+ }).getConfig();
266
+
267
+ const el = document.createElement('dile-crud-list');
268
+ el.disableLoadOnStart = true;
269
+ el.config = config;
270
+ el.elements = sampleElements;
271
+ el.loading = false;
272
+ document.body.appendChild(el);
273
+ await el.updateComplete;
274
+
275
+ let receivedDetail = null;
276
+ el.addEventListener('crud-list-sort-changed', (e) => {
277
+ receivedDetail = e.detail;
278
+ });
279
+
280
+ el.setSort({ sortField: 'name', sortDirection: 'desc' });
281
+
282
+ expect(receivedDetail).toEqual({ sortField: 'name', sortDirection: 'desc' });
283
+ });
284
+
285
+ it('keeps reporting the correct sort state to a freshly remounted grid after a loading round-trip', async () => {
286
+ // Reproduces the real scenario: setSort() sets loading=true, which unmounts
287
+ // elementsTemplate (and therefore <dile-crud-data-grid>/<dile-data-grid>) while
288
+ // the request is in flight, then getSuccess() remounts it from scratch.
289
+ const config = new CrudConfigBuilder('https://example.test/api/customers', {
290
+ grid: { columns: [{ field: 'name', header: 'Name', sortable: true }] },
291
+ }).getConfig();
292
+
293
+ const el = document.createElement('dile-crud-list');
294
+ el.disableLoadOnStart = true;
295
+ el.config = config;
296
+ el.elements = sampleElements;
297
+ el.loading = false;
298
+ document.body.appendChild(el);
299
+ await el.updateComplete;
300
+
301
+ el.elservice.setSort = () => {}; // avoid a real network round-trip
302
+ el.setSort({ sortField: 'name', sortDirection: 'asc' });
303
+ await el.updateComplete;
304
+
305
+ expect(el.shadowRoot.querySelector('.grid-container')).toBeNull(); // unmounted while loading
306
+
307
+ el.getSuccess({ detail: { elements: sampleElements, numItems: 2, paginationData: {} } });
308
+ await el.updateComplete;
309
+
310
+ const freshDataGrid = el.shadowRoot.querySelector('dile-crud-data-grid');
311
+ expect(freshDataGrid).toBeTruthy();
312
+ expect(freshDataGrid.sort).toEqual({ sortField: 'name', sortDirection: 'asc' });
313
+
314
+ const innerGrid = freshDataGrid.shadowRoot.querySelector('dile-data-grid');
315
+ await innerGrid.updateComplete;
316
+ expect(innerGrid.sortField).toBe('name');
317
+ expect(innerGrid.sortDirection).toBe('asc');
318
+ });
319
+ });
@@ -1,5 +1,7 @@
1
1
  export { DileCrudList } from "./src/DileCrudList.js";
2
- export { DileCrudListItem } from ".src/DileCrudListItem.js";
3
- export { DileCrudListPaginationLinks } from ".src/DileCrudListPaginationLinks.js";
2
+ export { DileCrudListItem } from "./src/DileCrudListItem.js";
3
+ export { DileCrudItemActions } from "./src/DileCrudItemActions.js";
4
+ export { DileCrudDataGrid } from "./src/DileCrudDataGrid.js";
5
+ export { DileCrudListPaginationLinks } from "./src/DileCrudListPaginationLinks.js";
4
6
  export { DileCrudListService } from "./src/DileCrudListService.js";
5
7
  export { DileCrudSelectAll } from "./src/DileCrudSelectAll.js";
@@ -0,0 +1,93 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import '@dile/ui/components/data-grid/data-grid.js';
3
+ import { DileCrudItemActions } from './DileCrudItemActions.js';
4
+ import '../crud-item-actions.js';
5
+
6
+ export class DileCrudDataGrid extends LitElement {
7
+ static styles = [
8
+ css`
9
+ :host {
10
+ display: block;
11
+ }
12
+ `
13
+ ];
14
+
15
+ static get properties() {
16
+ return {
17
+ items: { type: Array },
18
+ selectedIds: { type: Array },
19
+ config: { type: Object },
20
+ sort: { type: Object },
21
+ };
22
+ }
23
+
24
+ constructor() {
25
+ super();
26
+ this.items = [];
27
+ this.selectedIds = [];
28
+ this.config = null;
29
+ this.sort = null;
30
+ }
31
+
32
+ get columns() {
33
+ const configuredCols = this.config?.grid?.columns || [];
34
+ const cols = [...configuredCols];
35
+
36
+ const hideActions = Boolean(this.config?.grid?.hideActionsColumn);
37
+
38
+ if (!hideActions && DileCrudItemActions.hasActions(this.config)) {
39
+ const customActionsHeader = this.config?.grid?.actionsColumnHeader || this.config?.labels?.actionsHeader || 'Actions';
40
+ const customActionsWidth = this.config?.grid?.actionsColumnWidth || '110px';
41
+ const customActionsAlign = this.config?.grid?.actionsColumnAlign || 'right';
42
+
43
+ cols.push({
44
+ header: customActionsHeader,
45
+ align: customActionsAlign,
46
+ width: customActionsWidth,
47
+ hideCardLabel: true,
48
+ render: (item) => html`
49
+ <dile-crud-item-actions
50
+ .item=${item}
51
+ .config=${this.config}
52
+ ></dile-crud-item-actions>
53
+ `,
54
+ });
55
+ }
56
+
57
+ return cols;
58
+ }
59
+
60
+ get computeRowId() {
61
+ const computeItemId = this.config?.computeItemId;
62
+ if (typeof computeItemId !== 'function') {
63
+ return null;
64
+ }
65
+ return (row) => computeItemId(row);
66
+ }
67
+
68
+ get rowClass() {
69
+ return (row) => (row?.deleted_at ? 'is-deleted' : '');
70
+ }
71
+
72
+ render() {
73
+ const gridConfig = this.config?.grid || {};
74
+
75
+ return html`
76
+ <dile-data-grid
77
+ .items=${this.items}
78
+ .columns=${this.columns}
79
+ .selectedIds=${this.selectedIds}
80
+ .computeRowId=${this.computeRowId}
81
+ .rowClass=${this.rowClass}
82
+ .sortField=${this.sort?.sortField || ''}
83
+ .sortDirection=${this.sort?.sortDirection || ''}
84
+ ?selectable=${gridConfig.selectable !== undefined ? gridConfig.selectable : !this.config?.customization?.hideCheckboxSelection}
85
+ ?sticky-first-column=${gridConfig.stickyFirstColumn !== undefined ? gridConfig.stickyFirstColumn : true}
86
+ ?striped=${gridConfig.striped !== undefined ? gridConfig.striped : true}
87
+ responsive-mode=${gridConfig.responsiveMode || 'auto'}
88
+ empty-message=${gridConfig.emptyMessage || 'No data available'}
89
+ sort-mode="external"
90
+ ></dile-data-grid>
91
+ `;
92
+ }
93
+ }
@@ -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
+ }