kempo-ui 0.0.77 → 0.0.79
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/.github/skills/highlight-code/SKILL.md +17 -30
- package/bin/highlight_code.js +34 -7
- package/dist/components/Table.js +120 -77
- package/dist/components/tableControls/DeleteSelected.js +7 -0
- package/docs/components/table.html +2 -1
- package/docs/components/tableServerSync.html +143 -0
- package/docs/icons/delete_sweep.svg +1 -0
- package/docs/src/components/Table.js +120 -77
- package/docs/src/components/tableControls/DeleteSelected.js +7 -0
- package/icons/delete_sweep.svg +1 -0
- package/package.json +2 -1
- package/src/components/Table.js +270 -253
- package/src/components/tableControls/DeleteSelected.js +27 -0
package/src/components/Table.js
CHANGED
|
@@ -17,6 +17,8 @@ export default class Table extends ShadowComponent {
|
|
|
17
17
|
enableSelection: { type: Boolean, reflect: true, converter: boolExists, attribute: 'enable-selection' },
|
|
18
18
|
enableSorting: { type: Boolean, reflect: true, converter: boolExists, attribute: 'enable-sorting' },
|
|
19
19
|
caseSensitiveFilters: { type: Boolean, reflect: true, converter: boolExists, attribute: 'case-sensitive-filters' },
|
|
20
|
+
requestEdit: { type: Boolean, reflect: true, converter: boolExists, attribute: 'request-edit' },
|
|
21
|
+
requestDelete: { type: Boolean, reflect: true, converter: boolExists, attribute: 'request-delete' },
|
|
20
22
|
fields: { type: Array },
|
|
21
23
|
records: { type: Array },
|
|
22
24
|
filters: { type: Array },
|
|
@@ -38,6 +40,8 @@ export default class Table extends ShadowComponent {
|
|
|
38
40
|
if(this.sort === undefined) this.sort = [];
|
|
39
41
|
if(this.columnSizes === undefined) this.columnSizes = {};
|
|
40
42
|
if(this.fetchPending === undefined) this.fetchPending = false;
|
|
43
|
+
if(this.requestEdit === undefined) this.requestEdit = false;
|
|
44
|
+
if(this.requestDelete === undefined) this.requestDelete = false;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
/*
|
|
@@ -80,86 +84,72 @@ export default class Table extends ShadowComponent {
|
|
|
80
84
|
|
|
81
85
|
updated(changedProperties) {
|
|
82
86
|
super.updated(changedProperties);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (this.enableSelection) {
|
|
87
|
+
|
|
88
|
+
if(this.enableSelection) {
|
|
86
89
|
const selectAllCheckbox = this.shadowRoot.getElementById('select-all');
|
|
87
|
-
if
|
|
90
|
+
if(selectAllCheckbox) {
|
|
88
91
|
selectAllCheckbox.checked = this.allOnPageSelected();
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
|
-
|
|
92
|
-
// Update container widths
|
|
93
|
-
this.updateContainerWidths();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
updateContainerWidths() {
|
|
97
|
-
const totalWidth = this.columnSizes.total + 'px';
|
|
98
|
-
const fieldsEl = this.shadowRoot.getElementById('fields');
|
|
99
|
-
const topEl = this.shadowRoot.getElementById('top');
|
|
100
|
-
const bottomEl = this.shadowRoot.getElementById('bottom');
|
|
101
|
-
const recordsEl = this.shadowRoot.getElementById('records');
|
|
102
|
-
|
|
103
|
-
if (fieldsEl) fieldsEl.style.width = totalWidth;
|
|
104
|
-
if (topEl) topEl.style.width = totalWidth;
|
|
105
|
-
if (bottomEl) bottomEl.style.width = totalWidth;
|
|
106
|
-
if (recordsEl) recordsEl.style.width = totalWidth;
|
|
107
94
|
}
|
|
108
95
|
|
|
109
96
|
/*
|
|
110
97
|
Rendering Functions
|
|
111
98
|
*/
|
|
112
99
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
100
|
+
renderColgroupTemplate() {
|
|
101
|
+
const cols = [];
|
|
102
|
+
if(this.enableSelection) cols.push(html`<col style="width: 40px" />`);
|
|
103
|
+
if(this.hasBeforeControls()) cols.push(html`<col style="width: ${this.columnSizes.beforeControls}px" />`);
|
|
104
|
+
this.fields.forEach(({ size, hidden }) => {
|
|
105
|
+
if(hidden) return;
|
|
106
|
+
cols.push(size ? html`<col style="width: ${size}px" />` : html`<col />`);
|
|
107
|
+
});
|
|
108
|
+
if(this.hasAfterControls()) cols.push(html`<col style="width: ${this.columnSizes.afterControls}px" />`);
|
|
109
|
+
return cols;
|
|
110
|
+
}
|
|
117
111
|
|
|
118
|
-
|
|
112
|
+
getColumnCount() {
|
|
113
|
+
let count = 0;
|
|
114
|
+
if(this.enableSelection) count++;
|
|
115
|
+
if(this.hasBeforeControls()) count++;
|
|
116
|
+
this.fields.forEach(({ hidden }) => { if(!hidden) count++; });
|
|
117
|
+
if(this.hasAfterControls()) count++;
|
|
118
|
+
return count;
|
|
119
|
+
}
|
|
119
120
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
/>
|
|
128
|
-
</div>
|
|
121
|
+
renderFieldsTemplate() {
|
|
122
|
+
const headers = [];
|
|
123
|
+
if(this.enableSelection) {
|
|
124
|
+
headers.push(html`
|
|
125
|
+
<th class="controls field-select">
|
|
126
|
+
<input type="checkbox" id="select-all" @change=${this.handleSelectAllChange} />
|
|
127
|
+
</th>
|
|
129
128
|
`);
|
|
130
129
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
fieldCells.push(html`
|
|
134
|
-
<div class="field cell field-before-controls" style="width: ${this.columnSizes.beforeControls}px"></div>
|
|
135
|
-
`);
|
|
130
|
+
if(this.hasBeforeControls()) {
|
|
131
|
+
headers.push(html`<th class="controls field-before-controls"></th>`);
|
|
136
132
|
}
|
|
137
|
-
|
|
138
133
|
this.fields.forEach(({ name, label, hidden }) => {
|
|
139
|
-
if
|
|
134
|
+
if(hidden) return;
|
|
140
135
|
const sortItem = this.sort.find(item => item.name === name);
|
|
141
136
|
const isCurrentSort = this.sort.length > 0 && this.sort[this.sort.length - 1].name === name;
|
|
142
137
|
const sortClass = sortItem ? (sortItem.asc ? 'sort-asc' : 'sort-desc') : '';
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
style="width: ${this.columnSizes[name]}px; ${this.enableSorting ? 'cursor: pointer;' : ''}"
|
|
138
|
+
headers.push(html`
|
|
139
|
+
<th
|
|
140
|
+
class="${sortClass}"
|
|
141
|
+
style="${this.enableSorting ? 'cursor: pointer;' : ''}"
|
|
148
142
|
@click=${this.enableSorting ? () => this.handleFieldClick(name) : null}
|
|
149
143
|
>
|
|
150
144
|
${label}
|
|
151
145
|
${isCurrentSort ? html`<k-icon name="arrow" direction="${sortItem.asc ? 'down' : 'up'}" class="icon-sort"></k-icon>` : ''}
|
|
152
|
-
</
|
|
146
|
+
</th>
|
|
153
147
|
`);
|
|
154
148
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
fieldCells.push(html`
|
|
158
|
-
<div class="field cell field-after-controls" style="width: ${this.columnSizes.afterControls}px"></div>
|
|
159
|
-
`);
|
|
149
|
+
if(this.hasAfterControls()) {
|
|
150
|
+
headers.push(html`<th class="controls field-after-controls"></th>`);
|
|
160
151
|
}
|
|
161
|
-
|
|
162
|
-
return fieldCells;
|
|
152
|
+
return headers;
|
|
163
153
|
}
|
|
164
154
|
|
|
165
155
|
renderRecordsTemplate() {
|
|
@@ -182,7 +172,7 @@ export default class Table extends ShadowComponent {
|
|
|
182
172
|
} else {
|
|
183
173
|
if (fetchStart === null) fetchStart = start + idx;
|
|
184
174
|
fetchCount++;
|
|
185
|
-
return html`<
|
|
175
|
+
return html`<tr class="record fetching"><td class="cell" colspan="${this.getColumnCount()}">Loading...</td></tr>`;
|
|
186
176
|
}
|
|
187
177
|
});
|
|
188
178
|
|
|
@@ -202,42 +192,41 @@ export default class Table extends ShadowComponent {
|
|
|
202
192
|
|
|
203
193
|
renderRecordTemplate(record) {
|
|
204
194
|
const recordCells = [];
|
|
205
|
-
|
|
206
|
-
if
|
|
195
|
+
|
|
196
|
+
if(this.enableSelection) {
|
|
207
197
|
recordCells.push(html`
|
|
208
|
-
<
|
|
209
|
-
<input
|
|
210
|
-
type="checkbox"
|
|
198
|
+
<td class="cell selection controls">
|
|
199
|
+
<input
|
|
200
|
+
type="checkbox"
|
|
211
201
|
.checked=${record[selected]}
|
|
212
202
|
@change=${(e) => this.handleRecordSelectionChange(record, e)}
|
|
213
203
|
/>
|
|
214
|
-
</
|
|
204
|
+
</td>
|
|
215
205
|
`);
|
|
216
206
|
}
|
|
217
|
-
|
|
218
|
-
if
|
|
207
|
+
|
|
208
|
+
if(this.hasBeforeControls()) {
|
|
219
209
|
recordCells.push(this.renderBeforeControlsTemplate());
|
|
220
210
|
}
|
|
221
|
-
|
|
211
|
+
|
|
222
212
|
this.fields.forEach(({ name, formatter, calculator, type, editor, hidden }) => {
|
|
223
|
-
if
|
|
213
|
+
if(hidden) return;
|
|
224
214
|
let value = record[name] || '';
|
|
225
|
-
|
|
226
215
|
recordCells.push(html`
|
|
227
|
-
<
|
|
216
|
+
<td class="cell" data-field=${name}>
|
|
228
217
|
${record[editing] ? this.renderEditingCell(record, name, value, calculator, editor, type) : this.renderDisplayCell(record, name, value, calculator, formatter)}
|
|
229
|
-
</
|
|
218
|
+
</td>
|
|
230
219
|
`);
|
|
231
220
|
});
|
|
232
|
-
|
|
233
|
-
if
|
|
221
|
+
|
|
222
|
+
if(this.hasAfterControls()) {
|
|
234
223
|
recordCells.push(this.renderAfterControlsTemplate());
|
|
235
224
|
}
|
|
236
|
-
|
|
225
|
+
|
|
237
226
|
return html`
|
|
238
|
-
<
|
|
227
|
+
<tr class="record ${record[editing] ? 'editing' : ''}" data-index=${record[index]}>
|
|
239
228
|
${recordCells}
|
|
240
|
-
</
|
|
229
|
+
</tr>
|
|
241
230
|
`;
|
|
242
231
|
}
|
|
243
232
|
|
|
@@ -280,55 +269,37 @@ export default class Table extends ShadowComponent {
|
|
|
280
269
|
|
|
281
270
|
renderBeforeControlsTemplate() {
|
|
282
271
|
const controls = [];
|
|
283
|
-
|
|
284
272
|
this.querySelectorAll('[slot="before"]').forEach(control => {
|
|
285
273
|
const tagName = control.tagName.toLowerCase();
|
|
286
274
|
const newControl = document.createElement(tagName);
|
|
287
|
-
|
|
288
275
|
Array.from(control.attributes).forEach(attr => {
|
|
289
|
-
if(attr.name !== 'slot')
|
|
290
|
-
newControl.setAttribute(attr.name, attr.value);
|
|
291
|
-
}
|
|
276
|
+
if(attr.name !== 'slot') newControl.setAttribute(attr.name, attr.value);
|
|
292
277
|
});
|
|
293
|
-
|
|
294
|
-
if(control.innerHTML){
|
|
295
|
-
newControl.innerHTML = control.innerHTML;
|
|
296
|
-
}
|
|
297
|
-
|
|
278
|
+
if(control.innerHTML) newControl.innerHTML = control.innerHTML;
|
|
298
279
|
controls.push(newControl);
|
|
299
280
|
});
|
|
300
|
-
|
|
301
281
|
return html`
|
|
302
|
-
<
|
|
282
|
+
<td class="cell controls controls-before">
|
|
303
283
|
${controls}
|
|
304
|
-
</
|
|
284
|
+
</td>
|
|
305
285
|
`;
|
|
306
286
|
}
|
|
307
287
|
|
|
308
288
|
renderAfterControlsTemplate() {
|
|
309
289
|
const controls = [];
|
|
310
|
-
|
|
311
290
|
this.querySelectorAll('[slot="after"]').forEach(control => {
|
|
312
291
|
const tagName = control.tagName.toLowerCase();
|
|
313
292
|
const newControl = document.createElement(tagName);
|
|
314
|
-
|
|
315
293
|
Array.from(control.attributes).forEach(attr => {
|
|
316
|
-
if(attr.name !== 'slot')
|
|
317
|
-
newControl.setAttribute(attr.name, attr.value);
|
|
318
|
-
}
|
|
294
|
+
if(attr.name !== 'slot') newControl.setAttribute(attr.name, attr.value);
|
|
319
295
|
});
|
|
320
|
-
|
|
321
|
-
if(control.innerHTML){
|
|
322
|
-
newControl.innerHTML = control.innerHTML;
|
|
323
|
-
}
|
|
324
|
-
|
|
296
|
+
if(control.innerHTML) newControl.innerHTML = control.innerHTML;
|
|
325
297
|
controls.push(newControl);
|
|
326
298
|
});
|
|
327
|
-
|
|
328
299
|
return html`
|
|
329
|
-
<
|
|
300
|
+
<td class="cell controls controls-after">
|
|
330
301
|
${controls}
|
|
331
|
-
</
|
|
302
|
+
</td>
|
|
332
303
|
`;
|
|
333
304
|
}
|
|
334
305
|
|
|
@@ -391,39 +362,59 @@ export default class Table extends ShadowComponent {
|
|
|
391
362
|
|
|
392
363
|
saveEditedRecord(record) {
|
|
393
364
|
const recordEl = this.shadowRoot.querySelector(`.record[data-index="${record[index]}"]`);
|
|
365
|
+
const newData = {};
|
|
394
366
|
if(recordEl){
|
|
395
367
|
recordEl.querySelectorAll('.cell[data-field]').forEach($cell => {
|
|
396
368
|
const field = $cell.dataset.field;
|
|
397
369
|
const fieldDef = this.fields.find(f => f.name === field);
|
|
398
370
|
if(fieldDef && !fieldDef.calculator){
|
|
399
371
|
const $input = $cell.querySelector('input, select');
|
|
400
|
-
if($input)
|
|
401
|
-
record[field] = $input.value;
|
|
402
|
-
}
|
|
372
|
+
if($input) newData[field] = $input.value;
|
|
403
373
|
}
|
|
404
374
|
});
|
|
375
|
+
}
|
|
376
|
+
const commit = () => {
|
|
377
|
+
Object.assign(record, newData);
|
|
405
378
|
record[editing] = false;
|
|
406
|
-
recordEl
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
379
|
+
if(recordEl){
|
|
380
|
+
recordEl.classList.remove('editing');
|
|
381
|
+
recordEl.removeAttribute('editing');
|
|
382
|
+
recordEl.querySelectorAll('.cell[data-field]').forEach($cell => {
|
|
383
|
+
const field = $cell.dataset.field;
|
|
384
|
+
const fieldDef = this.fields.find(f => f.name === field);
|
|
385
|
+
if(fieldDef){
|
|
386
|
+
const value = record[field] || '';
|
|
387
|
+
if(fieldDef.calculator){
|
|
388
|
+
$cell.textContent = fieldDef.calculator(record, this);
|
|
389
|
+
} else if(fieldDef.formatter){
|
|
390
|
+
$cell.innerHTML = fieldDef.formatter(value);
|
|
391
|
+
} else {
|
|
392
|
+
$cell.textContent = value;
|
|
393
|
+
}
|
|
419
394
|
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
this.dispatchEvent(new CustomEvent('editingChange', {
|
|
398
|
+
detail: { record, editing: false },
|
|
399
|
+
bubbles: true
|
|
400
|
+
}));
|
|
401
|
+
};
|
|
402
|
+
const hasChanges = Object.keys(newData).some(key => String(record[key] ?? '') !== newData[key]);
|
|
403
|
+
if(!hasChanges){
|
|
404
|
+
this.cancelEditedRecord(record);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if(this.requestEdit){
|
|
408
|
+
recordEl?.classList.add('pending');
|
|
409
|
+
const wrappedCommit = () => { recordEl?.classList.remove('pending'); commit(); };
|
|
410
|
+
const reject = () => { recordEl?.classList.remove('pending'); };
|
|
411
|
+
this.dispatchEvent(new CustomEvent('requestSave', {
|
|
412
|
+
detail: { record, newData, approve: wrappedCommit, reject },
|
|
413
|
+
bubbles: true
|
|
414
|
+
}));
|
|
415
|
+
} else {
|
|
416
|
+
commit();
|
|
422
417
|
}
|
|
423
|
-
this.dispatchEvent(new CustomEvent('editingChange', {
|
|
424
|
-
detail: { record, editing: false },
|
|
425
|
-
bubbles: true
|
|
426
|
-
}));
|
|
427
418
|
}
|
|
428
419
|
|
|
429
420
|
cancelEditedRecord(record) {
|
|
@@ -682,69 +673,79 @@ export default class Table extends ShadowComponent {
|
|
|
682
673
|
|
|
683
674
|
deleteRecord(record) {
|
|
684
675
|
let originalRecord = this.records.find(r => r === record);
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
if
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
if (originalRecord) {
|
|
676
|
+
const totalPagesBefore = this.getTotalPages();
|
|
677
|
+
if(!originalRecord && record[index] !== undefined) originalRecord = this.records[record[index]];
|
|
678
|
+
if(!originalRecord) return;
|
|
679
|
+
const commit = () => {
|
|
692
680
|
const recordIndex = this.records.indexOf(originalRecord);
|
|
693
681
|
this.records.splice(recordIndex, 1);
|
|
694
|
-
this.records.forEach((rec, idx) => {
|
|
695
|
-
rec[index] = idx;
|
|
696
|
-
});
|
|
682
|
+
this.records.forEach((rec, idx) => { rec[index] = idx; });
|
|
697
683
|
this.requestUpdate();
|
|
698
684
|
this.dispatchEvent(new CustomEvent('selectionChange', { bubbles: true }));
|
|
699
|
-
this.dispatchEvent(new CustomEvent('recordDeleted', {
|
|
685
|
+
this.dispatchEvent(new CustomEvent('recordDeleted', {
|
|
700
686
|
detail: { index: recordIndex },
|
|
701
|
-
bubbles: true
|
|
687
|
+
bubbles: true
|
|
702
688
|
}));
|
|
703
|
-
|
|
704
689
|
const totalPages = this.getTotalPages();
|
|
705
|
-
if
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if (totalPages !== totalPagesBefore) {
|
|
709
|
-
this.dispatchEvent(new CustomEvent('pageCountChanged', {
|
|
690
|
+
if(this.currentPage > totalPages) this.setPage(totalPages);
|
|
691
|
+
if(totalPages !== totalPagesBefore){
|
|
692
|
+
this.dispatchEvent(new CustomEvent('pageCountChanged', {
|
|
710
693
|
detail: { totalPages },
|
|
711
|
-
bubbles: true
|
|
694
|
+
bubbles: true
|
|
712
695
|
}));
|
|
713
696
|
}
|
|
697
|
+
};
|
|
698
|
+
if(this.requestDelete){
|
|
699
|
+
const recordEl = this.shadowRoot.querySelector(`.record[data-index="${originalRecord[index]}"]`);
|
|
700
|
+
recordEl?.classList.add('pending');
|
|
701
|
+
const wrappedCommit = () => { recordEl?.classList.remove('pending'); commit(); };
|
|
702
|
+
const reject = () => { recordEl?.classList.remove('pending'); };
|
|
703
|
+
this.dispatchEvent(new CustomEvent('requestDelete', {
|
|
704
|
+
detail: { records: [originalRecord], approve: wrappedCommit, reject },
|
|
705
|
+
bubbles: true
|
|
706
|
+
}));
|
|
707
|
+
} else {
|
|
708
|
+
commit();
|
|
714
709
|
}
|
|
715
710
|
}
|
|
716
711
|
|
|
717
712
|
deleteSelected() {
|
|
718
|
-
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
713
|
+
const totalPagesBefore = this.getTotalPages();
|
|
714
|
+
const originalRecords = this.getSelectedRecords()
|
|
715
|
+
.map(record => this.records.find(r => r === record) ?? (record[index] !== undefined ? this.records[record[index]] : null))
|
|
716
|
+
.filter(Boolean);
|
|
717
|
+
if(!originalRecords.length) return;
|
|
718
|
+
const commit = () => {
|
|
719
|
+
originalRecords.forEach(record => {
|
|
720
|
+
const i = this.records.indexOf(record);
|
|
721
|
+
if(i !== -1) this.records.splice(i, 1);
|
|
722
|
+
});
|
|
723
|
+
this.records.forEach((rec, idx) => { rec[index] = idx; });
|
|
724
|
+
this.requestUpdate();
|
|
725
|
+
const totalPages = this.getTotalPages();
|
|
726
|
+
if(this.currentPage > totalPages) this.setPage(totalPages);
|
|
727
|
+
if(totalPages !== totalPagesBefore){
|
|
728
|
+
this.dispatchEvent(new CustomEvent('pageCountChanged', {
|
|
729
|
+
detail: { totalPages },
|
|
730
|
+
bubbles: true
|
|
731
|
+
}));
|
|
729
732
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
this.
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
this.
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
this.dispatchEvent(new CustomEvent('pageCountChanged', {
|
|
743
|
-
detail: { totalPages },
|
|
744
|
-
bubbles: true
|
|
733
|
+
this.dispatchEvent(new CustomEvent('selectionChange', { bubbles: true }));
|
|
734
|
+
};
|
|
735
|
+
if(this.requestDelete){
|
|
736
|
+
const rowEls = originalRecords
|
|
737
|
+
.map(r => this.shadowRoot.querySelector(`.record[data-index="${r[index]}"]`))
|
|
738
|
+
.filter(Boolean);
|
|
739
|
+
rowEls.forEach(el => el.classList.add('pending'));
|
|
740
|
+
const wrappedCommit = () => { rowEls.forEach(el => el.classList.remove('pending')); commit(); };
|
|
741
|
+
const reject = () => { rowEls.forEach(el => el.classList.remove('pending')); };
|
|
742
|
+
this.dispatchEvent(new CustomEvent('requestDelete', {
|
|
743
|
+
detail: { records: originalRecords, approve: wrappedCommit, reject },
|
|
744
|
+
bubbles: true
|
|
745
745
|
}));
|
|
746
|
+
} else {
|
|
747
|
+
commit();
|
|
746
748
|
}
|
|
747
|
-
this.dispatchEvent(new CustomEvent('selectionChange', { bubbles: true }));
|
|
748
749
|
}
|
|
749
750
|
|
|
750
751
|
getSelectedRecords() {
|
|
@@ -949,54 +950,19 @@ export default class Table extends ShadowComponent {
|
|
|
949
950
|
}
|
|
950
951
|
|
|
951
952
|
calculateColumnSizes() {
|
|
952
|
-
const newSizes = {};
|
|
953
|
-
newSizes.total = 0;
|
|
954
|
-
|
|
955
|
-
if (this.enableSelection) newSizes.total += 40;
|
|
956
|
-
|
|
957
953
|
const beforeEls = Array.from(this.querySelectorAll('[slot="before"]'));
|
|
958
954
|
const afterEls = Array.from(this.querySelectorAll('[slot="after"]'));
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
if (this.hasBeforeControls()) newSizes.total += newSizes.beforeControls;
|
|
964
|
-
if (this.hasAfterControls()) newSizes.total += newSizes.afterControls;
|
|
965
|
-
|
|
966
|
-
this.fields.forEach(field => {
|
|
967
|
-
if (field.size) {
|
|
968
|
-
newSizes[field.name] = field.size;
|
|
969
|
-
newSizes.total += field.size;
|
|
970
|
-
} else {
|
|
971
|
-
let maxLength = 0;
|
|
972
|
-
this.records.slice(0, 100).forEach(record => {
|
|
973
|
-
if(record === null) return;
|
|
974
|
-
let value = record[field.name];
|
|
975
|
-
if (field.calculator) {
|
|
976
|
-
value = field.calculator(record, this);
|
|
977
|
-
}
|
|
978
|
-
if (field.formatter) {
|
|
979
|
-
value = field.formatter(value);
|
|
980
|
-
}
|
|
981
|
-
if (value && value.toString().length > maxLength) {
|
|
982
|
-
maxLength = value.toString().length;
|
|
983
|
-
}
|
|
984
|
-
});
|
|
985
|
-
newSizes[field.name] = Math.max((maxLength * 10 + 32), 128);
|
|
986
|
-
if (!field.hidden) newSizes.total += newSizes[field.name];
|
|
987
|
-
}
|
|
988
|
-
});
|
|
989
|
-
|
|
955
|
+
const newSizes = {
|
|
956
|
+
beforeControls: beforeEls.reduce((total, el) => total + (el.maxWidth || 40), 0),
|
|
957
|
+
afterControls: afterEls.reduce((total, el) => total + (el.maxWidth || 40), 0)
|
|
958
|
+
};
|
|
990
959
|
const hasUndefinedMaxWidth = [...beforeEls, ...afterEls].some(el => el.maxWidth === undefined);
|
|
991
|
-
|
|
992
|
-
if (JSON.stringify(this.columnSizes) !== JSON.stringify(newSizes)) {
|
|
960
|
+
if(JSON.stringify(this.columnSizes) !== JSON.stringify(newSizes)) {
|
|
993
961
|
this.columnSizes = newSizes;
|
|
994
962
|
}
|
|
995
|
-
|
|
996
|
-
if (hasUndefinedMaxWidth) {
|
|
963
|
+
if(hasUndefinedMaxWidth) {
|
|
997
964
|
setTimeout(() => this.calculateColumnSizes(), 0);
|
|
998
965
|
}
|
|
999
|
-
|
|
1000
966
|
return this.columnSizes;
|
|
1001
967
|
}
|
|
1002
968
|
|
|
@@ -1040,13 +1006,12 @@ export default class Table extends ShadowComponent {
|
|
|
1040
1006
|
/* Rendering */
|
|
1041
1007
|
|
|
1042
1008
|
render() {
|
|
1043
|
-
if
|
|
1009
|
+
if(!this.records || !this.fields) {
|
|
1044
1010
|
return html`
|
|
1045
1011
|
<div id="wrapper">
|
|
1046
1012
|
<div id="top"><slot name="top"></slot></div>
|
|
1047
|
-
<div id="table">
|
|
1048
|
-
<
|
|
1049
|
-
<div id="records"></div>
|
|
1013
|
+
<div id="table-container">
|
|
1014
|
+
<table><thead><tr></tr></thead><tbody></tbody></table>
|
|
1050
1015
|
</div>
|
|
1051
1016
|
<div id="bottom"><slot></slot></div>
|
|
1052
1017
|
</div>
|
|
@@ -1063,16 +1028,23 @@ export default class Table extends ShadowComponent {
|
|
|
1063
1028
|
|
|
1064
1029
|
return html`
|
|
1065
1030
|
<div id="wrapper">
|
|
1066
|
-
<div id="top"
|
|
1067
|
-
<div id="table">
|
|
1068
|
-
<
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1031
|
+
<div id="top"><slot name="top"></slot></div>
|
|
1032
|
+
<div id="table-container">
|
|
1033
|
+
<table>
|
|
1034
|
+
<colgroup>
|
|
1035
|
+
${this.renderColgroupTemplate()}
|
|
1036
|
+
</colgroup>
|
|
1037
|
+
<thead>
|
|
1038
|
+
<tr>
|
|
1039
|
+
${this.renderFieldsTemplate()}
|
|
1040
|
+
</tr>
|
|
1041
|
+
</thead>
|
|
1042
|
+
<tbody>
|
|
1043
|
+
${this.renderRecordsTemplate()}
|
|
1044
|
+
</tbody>
|
|
1045
|
+
</table>
|
|
1074
1046
|
</div>
|
|
1075
|
-
<div id="bottom"
|
|
1047
|
+
<div id="bottom"><slot></slot></div>
|
|
1076
1048
|
</div>
|
|
1077
1049
|
<div style="display: none">
|
|
1078
1050
|
<slot name="before"></slot>
|
|
@@ -1084,39 +1056,99 @@ export default class Table extends ShadowComponent {
|
|
|
1084
1056
|
static styles = css`
|
|
1085
1057
|
:host {
|
|
1086
1058
|
display: block;
|
|
1087
|
-
width: 100%;
|
|
1088
|
-
overflow: auto;
|
|
1089
1059
|
margin-bottom: var(--spacer);
|
|
1090
1060
|
}
|
|
1091
1061
|
#wrapper {
|
|
1092
|
-
width: min-content;
|
|
1093
1062
|
border: 1px solid var(--c_border);
|
|
1094
1063
|
border-radius: var(--radius);
|
|
1064
|
+
overflow: hidden;
|
|
1095
1065
|
}
|
|
1096
|
-
#table {
|
|
1097
|
-
|
|
1066
|
+
#table-container {
|
|
1067
|
+
overflow-x: auto;
|
|
1098
1068
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1069
|
+
table {
|
|
1070
|
+
width: 100%;
|
|
1071
|
+
border-collapse: collapse;
|
|
1102
1072
|
}
|
|
1103
|
-
|
|
1073
|
+
thead tr {
|
|
1104
1074
|
background-color: var(--c_bg__alt);
|
|
1105
|
-
border-bottom: 1px solid var(--c_border);
|
|
1106
1075
|
}
|
|
1107
|
-
|
|
1108
|
-
#fields .cell:not(.controls) {
|
|
1076
|
+
th, td {
|
|
1109
1077
|
padding: calc(0.5 * var(--spacer)) var(--spacer);
|
|
1078
|
+
vertical-align: middle;
|
|
1079
|
+
}
|
|
1080
|
+
th:not(:last-child),
|
|
1081
|
+
td:not(:last-child) {
|
|
1082
|
+
border-right: 1px solid var(--c_border);
|
|
1083
|
+
}
|
|
1084
|
+
th:first-child,
|
|
1085
|
+
td:first-child {
|
|
1086
|
+
border-left: none;
|
|
1087
|
+
}
|
|
1088
|
+
th:last-child,
|
|
1089
|
+
td:last-child {
|
|
1090
|
+
border-right: none;
|
|
1091
|
+
}
|
|
1092
|
+
thead tr th {
|
|
1093
|
+
border-top: none;
|
|
1094
|
+
border-bottom: 1px solid var(--c_border);
|
|
1110
1095
|
}
|
|
1111
|
-
|
|
1096
|
+
tbody tr:not(:last-child) td {
|
|
1097
|
+
border-bottom: 1px solid var(--c_border);
|
|
1098
|
+
}
|
|
1099
|
+
tbody tr:last-child td {
|
|
1100
|
+
border-bottom: none;
|
|
1101
|
+
}
|
|
1102
|
+
tr.editing td.cell[data-field] {
|
|
1103
|
+
padding: 0;
|
|
1104
|
+
}
|
|
1105
|
+
tr.editing td.cell[data-field] input,
|
|
1106
|
+
tr.editing td.cell[data-field] select {
|
|
1107
|
+
width: 100%;
|
|
1108
|
+
height: 100%;
|
|
1109
|
+
box-sizing: border-box;
|
|
1110
|
+
}
|
|
1111
|
+
tr.pending {
|
|
1112
|
+
pointer-events: none;
|
|
1113
|
+
}
|
|
1114
|
+
tr.pending td {
|
|
1115
|
+
position: relative;
|
|
1116
|
+
overflow: hidden;
|
|
1117
|
+
}
|
|
1118
|
+
tr.pending td::before {
|
|
1119
|
+
content: '';
|
|
1120
|
+
position: absolute;
|
|
1121
|
+
inset: 0;
|
|
1122
|
+
background: linear-gradient(90deg, transparent, rgba(128, 128, 128, 0.15), transparent);
|
|
1123
|
+
transform: translateX(-100%);
|
|
1124
|
+
animation: row-pending 1.2s ease-in-out infinite;
|
|
1125
|
+
}
|
|
1126
|
+
@keyframes row-pending {
|
|
1127
|
+
0% { transform: translateX(-100%); }
|
|
1128
|
+
100% { transform: translateX(100%); }
|
|
1129
|
+
}
|
|
1130
|
+
th.controls,
|
|
1131
|
+
td.controls {
|
|
1132
|
+
padding: 0;
|
|
1133
|
+
}
|
|
1134
|
+
td.controls-after,
|
|
1135
|
+
td.controls-before {
|
|
1112
1136
|
display: flex;
|
|
1113
1137
|
align-items: center;
|
|
1114
1138
|
}
|
|
1115
|
-
.
|
|
1116
|
-
|
|
1139
|
+
.field-select,
|
|
1140
|
+
.selection {
|
|
1141
|
+
width: 40px;
|
|
1142
|
+
text-align: center;
|
|
1117
1143
|
}
|
|
1118
|
-
.
|
|
1119
|
-
|
|
1144
|
+
.field-select input,
|
|
1145
|
+
.selection input {
|
|
1146
|
+
width: 1.25rem;
|
|
1147
|
+
height: 1.25rem;
|
|
1148
|
+
}
|
|
1149
|
+
.icon-sort {
|
|
1150
|
+
float: right;
|
|
1151
|
+
opacity: 0.5;
|
|
1120
1152
|
}
|
|
1121
1153
|
#top, #bottom {
|
|
1122
1154
|
display: flex;
|
|
@@ -1136,21 +1168,6 @@ export default class Table extends ShadowComponent {
|
|
|
1136
1168
|
:host(:not([bottom-controls])) #bottom {
|
|
1137
1169
|
display: none;
|
|
1138
1170
|
}
|
|
1139
|
-
.field-select,
|
|
1140
|
-
.selection {
|
|
1141
|
-
display: flex;
|
|
1142
|
-
justify-content: center;
|
|
1143
|
-
align-items: center;
|
|
1144
|
-
}
|
|
1145
|
-
.field-select input,
|
|
1146
|
-
.selection input {
|
|
1147
|
-
width: 1.25rem;
|
|
1148
|
-
height: 1.25rem;
|
|
1149
|
-
}
|
|
1150
|
-
.icon-sort {
|
|
1151
|
-
float: right;
|
|
1152
|
-
opacity: 0.5;
|
|
1153
|
-
}
|
|
1154
1171
|
`;
|
|
1155
1172
|
|
|
1156
1173
|
/*
|