kempo 2.9.2 → 2.10.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/dist/components/Accordion.js +1 -1
- package/dist/components/Dialog.js +1 -1
- package/dist/components/DirectoryViewer.js +1 -1
- package/dist/components/FocusCapture.js +1 -1
- package/dist/components/Icon.js +1 -1
- package/dist/components/Search.js +1 -1
- package/dist/components/Table.js +1 -1
- package/dist/components/Timestamp.js +1 -1
- package/dist/components/Toggle.js +1 -1
- package/dist/components/tableControls/DeleteSelected.js +1 -1
- package/dist/components/tableControls/Edit.js +1 -0
- package/dist/components/tableControls/Filters.js +1 -0
- package/dist/components/tableControls/HiddenCount.js +1 -0
- package/dist/components/tableControls/Hide.js +1 -0
- package/dist/components/tableControls/PageSelect.js +1 -1
- package/dist/components/tableControls/PageSize.js +1 -1
- package/dist/components/tableControls/ShowAll.js +1 -0
- package/dist/index.js +1 -1
- package/dist/kempo-styles.css +1 -1
- package/dist/kempo-vars.css +1 -1
- package/dist/utils/formatTimestamp.js +1 -1
- package/docs/components/_template.html +9 -0
- package/docs/components/accordion.html +52 -12
- package/docs/components/card.html +68 -8
- package/docs/components/component.html +6 -0
- package/docs/components/content-slider.html +8 -0
- package/docs/components/dialog.html +145 -5
- package/docs/components/directory-viewer.html +51 -40
- package/docs/components/focus-capture.html +68 -0
- package/docs/components/icon.html +76 -5
- package/docs/components/import.html +47 -0
- package/docs/components/lazy-component.html +45 -15
- package/docs/components/reactive-component.html +69 -0
- package/docs/components/reactive-lazy-component.html +69 -0
- package/docs/components/resize.html +52 -3
- package/docs/components/search.html +102 -0
- package/docs/components/show-more.html +54 -5
- package/docs/components/{sidemenu.html → side-menu.html} +56 -3
- package/docs/components/split.html +57 -16
- package/docs/components/table.html +90 -189
- package/docs/components/tableControls.html +91 -0
- package/docs/components/tableCustomFields.html +112 -0
- package/docs/components/tablePagination.html +92 -0
- package/docs/components/tableRecordEditing.html +144 -0
- package/docs/components/tableRecordFiltering.html +82 -0
- package/docs/components/tableRecordHiding.html +84 -0
- package/docs/components/tableRecordSelection.html +86 -0
- package/docs/components/tableRowControls.html +103 -0
- package/docs/components/tabs.html +72 -4
- package/docs/components/tags.html +94 -0
- package/docs/components/theme-switcher.html +74 -0
- package/docs/components/timestamp.html +86 -0
- package/docs/components/toggle.html +37 -34
- package/docs/icons/check.svg +1 -0
- package/docs/icons/hide.svg +1 -0
- package/docs/icons/show.svg +1 -0
- package/docs/index.html +5 -0
- package/docs/kempo/components/Accordion.js +1 -1
- package/docs/kempo/components/Dialog.js +1 -1
- package/docs/kempo/components/DirectoryViewer.js +1 -1
- package/docs/kempo/components/FocusCapture.js +1 -1
- package/docs/kempo/components/Icon.js +1 -1
- package/docs/kempo/components/Search.js +1 -1
- package/docs/kempo/components/Table.js +1 -1
- package/docs/kempo/components/Timestamp.js +1 -1
- package/docs/kempo/components/Toggle.js +1 -1
- package/docs/kempo/components/tableControls/DeleteSelected.js +1 -1
- package/docs/kempo/components/tableControls/Edit.js +1 -0
- package/docs/kempo/components/tableControls/Filters.js +1 -0
- package/docs/kempo/components/tableControls/HiddenCount.js +1 -0
- package/docs/kempo/components/tableControls/Hide.js +1 -0
- package/docs/kempo/components/tableControls/PageSelect.js +1 -1
- package/docs/kempo/components/tableControls/PageSize.js +1 -1
- package/docs/kempo/components/tableControls/ShowAll.js +1 -0
- package/docs/kempo/index.js +1 -1
- package/docs/kempo/kempo-styles.css +1 -1
- package/docs/kempo/kempo-vars.css +1 -1
- package/docs/kempo/utils/formatTimestamp.js +1 -1
- package/docs/nav-1.inc.html +16 -2
- package/docs/nav.inc.html +10 -2
- package/docs/search.json +281 -23
- package/docs/styles/_template.html +6 -0
- package/docs/styles/borders-spacing.html +6 -0
- package/docs/styles/buttons.html +6 -0
- package/docs/styles/colors.html +6 -0
- package/docs/styles/displays-layouts.html +6 -0
- package/docs/styles/embed-1.html +6 -0
- package/docs/styles/inputs.html +6 -0
- package/docs/styles/tables.html +6 -0
- package/docs/styles/typography.html +6 -0
- package/docs/{components/stacks.html → utils/_template.html} +11 -30
- package/docs/utils/cli.html +84 -0
- package/docs/utils/cookie.html +87 -0
- package/docs/utils/debounce.html +61 -0
- package/docs/utils/drag.html +85 -0
- package/docs/utils/element.html +124 -0
- package/docs/utils/format-timestamp.html +64 -0
- package/docs/utils/fuzzy-match.html +60 -0
- package/icons/check.svg +1 -0
- package/icons/hide.svg +1 -0
- package/icons/show.svg +1 -0
- package/package.json +1 -1
- package/src/components/Accordion.js +0 -1
- package/src/components/Dialog.js +15 -6
- package/src/components/DirectoryViewer.js +21 -92
- package/src/components/FocusCapture.js +2 -1
- package/src/components/Icon.js +3 -1
- package/src/components/Search.js +4 -1
- package/src/components/Table.js +469 -139
- package/src/components/Timestamp.js +7 -4
- package/src/components/Toggle.js +3 -0
- package/src/components/tableControls/DeleteSelected.js +36 -22
- package/src/components/tableControls/Edit.js +88 -0
- package/src/components/tableControls/Filters.js +128 -0
- package/src/components/tableControls/HiddenCount.js +42 -0
- package/src/components/tableControls/Hide.js +43 -0
- package/src/components/tableControls/PageSelect.js +54 -30
- package/src/components/tableControls/PageSize.js +33 -16
- package/src/components/tableControls/ShowAll.js +40 -0
- package/src/kempo-styles.css +7 -0
- package/src/kempo-vars.css +2 -0
- package/src/utils/formatTimestamp.js +26 -10
- package/docs/table.html +0 -0
- package/src/components/Stacks.js +0 -79
- package/src/utils/files.js +0 -15
package/src/components/Table.js
CHANGED
|
@@ -4,62 +4,26 @@ import SelectCount from './tableControls/SelectCount.js';
|
|
|
4
4
|
import PageSelect from './tableControls/PageSelect.js';
|
|
5
5
|
import PageSize from './tableControls/PageSize.js';
|
|
6
6
|
import DeleteSelected from './tableControls/DeleteSelected.js';
|
|
7
|
+
import Edit from './tableControls/Edit.js';
|
|
8
|
+
import Hide from './tableControls/Hide.js';
|
|
9
|
+
import HiddenCount from './tableControls/HiddenCount.js';
|
|
10
|
+
import ShowAll from './tableControls/ShowAll.js';
|
|
11
|
+
import Filters from './tableControls/Filters.js';
|
|
7
12
|
import { toTitleCase } from '../utils/string.js';
|
|
8
|
-
import { onEvent, dispatchEvent } from '../utils/element.js';
|
|
13
|
+
import { onEvent, offEvent, dispatchEvent } from '../utils/element.js';
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const $button = document.createElement('button');
|
|
15
|
-
$button.classList.add('pq', 'no-btn');
|
|
16
|
-
$button.appendChild(new Icon('chevron-left'));
|
|
17
|
-
onEvent($button, 'click', () => table.prevPage());
|
|
18
|
-
|
|
19
|
-
// Disable button if on the first page
|
|
20
|
-
onEvent(table, 'pageChange', () => {
|
|
21
|
-
$button.disabled = table.getCurrentPage() === 1;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
return $button;
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
nextPage: {
|
|
28
|
-
render: (table) => {
|
|
29
|
-
const $button = document.createElement('button');
|
|
30
|
-
$button.classList.add('pq', 'no-btn');
|
|
31
|
-
$button.appendChild(new Icon('chevron-right'));
|
|
32
|
-
onEvent($button, 'click', () => table.nextPage());
|
|
33
|
-
|
|
34
|
-
// Disable button if on the last page
|
|
35
|
-
onEvent(table, 'pageChange', () => {
|
|
36
|
-
$button.disabled = table.getCurrentPage() === table.getTotalPages();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
return $button;
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
pageSelect: {
|
|
43
|
-
render: (table) => new PageSelect(table)
|
|
44
|
-
},
|
|
45
|
-
pageSize: {
|
|
46
|
-
render: (table) => new PageSize(table)
|
|
47
|
-
},
|
|
48
|
-
selectCount: {
|
|
49
|
-
render: (table) => new SelectCount(table)
|
|
50
|
-
},
|
|
51
|
-
deleteSelected: {
|
|
52
|
-
render: (table) => new DeleteSelected(table)
|
|
53
|
-
},
|
|
54
|
-
spacer: {
|
|
55
|
-
html: '<div class="flex"></div>'
|
|
56
|
-
}
|
|
57
|
-
};
|
|
15
|
+
const selected = Symbol('selected');
|
|
16
|
+
const hidden = Symbol('hidden');
|
|
17
|
+
const index = Symbol('index');
|
|
18
|
+
const editing = Symbol('editing');
|
|
58
19
|
|
|
20
|
+
export default class Table extends Component {
|
|
59
21
|
constructor({
|
|
60
22
|
records = [],
|
|
61
23
|
fields = [],
|
|
24
|
+
filters = [],
|
|
62
25
|
controls = { before: [], after: [], top: [], bottom: [] },
|
|
26
|
+
enablePages = false,
|
|
63
27
|
pageSize = 100,
|
|
64
28
|
pageSizeOptions = [10, 25, 50, 100, 500],
|
|
65
29
|
currentPage = 1,
|
|
@@ -71,11 +35,13 @@ export default class Table extends Component {
|
|
|
71
35
|
fields: fields,
|
|
72
36
|
records: records,
|
|
73
37
|
controls: controls,
|
|
38
|
+
filters: filters,
|
|
74
39
|
pageSize,
|
|
75
40
|
pageSizeOptions,
|
|
76
41
|
currentPage,
|
|
77
42
|
enableSelection,
|
|
78
|
-
|
|
43
|
+
enablePages,
|
|
44
|
+
sort: []
|
|
79
45
|
});
|
|
80
46
|
|
|
81
47
|
/* Init */
|
|
@@ -87,6 +53,7 @@ export default class Table extends Component {
|
|
|
87
53
|
records: this.records,
|
|
88
54
|
fields: this.fields,
|
|
89
55
|
controls: this.controls,
|
|
56
|
+
filters: this.filters,
|
|
90
57
|
pageSize: this.pageSize,
|
|
91
58
|
pageSizeOptions: this.pageSizeOptions,
|
|
92
59
|
currentPage: this.currentPage
|
|
@@ -95,6 +62,7 @@ export default class Table extends Component {
|
|
|
95
62
|
}
|
|
96
63
|
return false;
|
|
97
64
|
}
|
|
65
|
+
|
|
98
66
|
renderFields(){
|
|
99
67
|
if(!this.rendered) return;
|
|
100
68
|
const beforeControls = this.controls?.before?.length ? '<th></th>' : '';
|
|
@@ -120,84 +88,128 @@ export default class Table extends Component {
|
|
|
120
88
|
});
|
|
121
89
|
}
|
|
122
90
|
}
|
|
123
|
-
|
|
124
|
-
|
|
91
|
+
|
|
92
|
+
renderRecords() {
|
|
93
|
+
if (!this.rendered) return;
|
|
125
94
|
const $records = this.shadowRoot.getElementById('records');
|
|
126
95
|
$records.innerHTML = '';
|
|
127
96
|
const start = (this.currentPage - 1) * this.pageSize;
|
|
128
97
|
const end = start + this.pageSize;
|
|
129
|
-
const paginatedRecords = this.
|
|
98
|
+
const paginatedRecords = this.getDisplayedRecords().slice(start, end);
|
|
130
99
|
let fetchRecords = false;
|
|
131
|
-
paginatedRecords.forEach((record
|
|
132
|
-
|
|
133
|
-
|
|
100
|
+
paginatedRecords.forEach((record) => {
|
|
101
|
+
$records.appendChild(this.renderRecord(record));
|
|
102
|
+
});
|
|
103
|
+
if (fetchRecords) {
|
|
104
|
+
dispatchEvent(this, 'fetchRecords', { start, end });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
renderRecord(record){
|
|
109
|
+
const $tr = document.createElement('tr');
|
|
110
|
+
$tr.dataset.index = record[index];
|
|
111
|
+
if (this.enableSelection) {
|
|
112
|
+
const $td = document.createElement('td');
|
|
113
|
+
$td.classList.add('selection');
|
|
114
|
+
const $checkbox = document.createElement('input');
|
|
115
|
+
$checkbox.type = 'checkbox';
|
|
116
|
+
$checkbox.checked = record[selected];
|
|
117
|
+
$checkbox.addEventListener('change', (event) => {
|
|
118
|
+
record[selected] = !!event.target.checked;
|
|
119
|
+
dispatchEvent(this, 'selectionChange');
|
|
120
|
+
});
|
|
121
|
+
$td.appendChild($checkbox);
|
|
122
|
+
$tr.appendChild($td);
|
|
123
|
+
}
|
|
124
|
+
if (this.controls?.before?.length) {
|
|
125
|
+
$tr.appendChild(this.renderRowControls(this.controls.before, record));
|
|
126
|
+
}
|
|
127
|
+
if (record === null) {
|
|
128
|
+
const $td = document.createElement('td');
|
|
129
|
+
$td.colSpan = this.fields.length;
|
|
130
|
+
$td.innerHTML = '<i>Loading...</i>';
|
|
131
|
+
fetchRecords = true;
|
|
132
|
+
$tr.appendChild($td);
|
|
133
|
+
} else {
|
|
134
|
+
this.fields.forEach(({ name, formatter, calculator, type, editor }) => {
|
|
134
135
|
const $td = document.createElement('td');
|
|
135
|
-
$td.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
$td.dataset.field = name;
|
|
137
|
+
let value = record[name] || '';
|
|
138
|
+
if(record[editing]){
|
|
139
|
+
$tr.setAttribute('editing', true);
|
|
140
|
+
if (calculator) {
|
|
141
|
+
$td.appendChild(Table.editors.calculated(calculator(record, this)));
|
|
142
|
+
} else if(editor){
|
|
143
|
+
$td.appendChild(editor(value));
|
|
142
144
|
} else {
|
|
143
|
-
|
|
145
|
+
let editorGen = Table.editors[type || typeof value];
|
|
146
|
+
if(!editorGen){
|
|
147
|
+
editorGen = Table.editors.string;
|
|
148
|
+
}
|
|
149
|
+
$td.appendChild(editorGen(value));
|
|
144
150
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if(this.controls?.before?.length){
|
|
151
|
-
$tr.appendChild(this.renderRowControls(this.controls.before, record, start + index));
|
|
152
|
-
}
|
|
153
|
-
if(record === null){
|
|
154
|
-
const $td = document.createElement('td');
|
|
155
|
-
$td.colSpan = this.fields.length;
|
|
156
|
-
$td.innerHTML = '<i>Loading...</i>';
|
|
157
|
-
fetchRecords = true;
|
|
158
|
-
$tr.appendChild($td);
|
|
159
|
-
} else {
|
|
160
|
-
this.fields.forEach(({name, formatter, calculator}, index) => {
|
|
161
|
-
const $td = document.createElement('td');
|
|
162
|
-
let value;
|
|
163
|
-
if(calculator){
|
|
164
|
-
value = calculator(record, this);
|
|
151
|
+
} else {
|
|
152
|
+
if (calculator) {
|
|
153
|
+
$td.innerHTML = calculator(record, this);
|
|
154
|
+
} else if (formatter) {
|
|
155
|
+
$td.innerHTML = formatter(value);
|
|
165
156
|
} else {
|
|
166
|
-
|
|
167
|
-
if (Array.isArray(value)) {
|
|
168
|
-
if(formatter){
|
|
169
|
-
value = value.map(formatter).join(', ');
|
|
170
|
-
} else {
|
|
171
|
-
value = value.join(', ');
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
if(formatter){
|
|
175
|
-
value = formatter(value);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
157
|
+
$td.innerHTML = value;
|
|
178
158
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
159
|
+
}
|
|
160
|
+
$tr.appendChild($td);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (this.controls?.after?.length) {
|
|
164
|
+
$tr.appendChild(this.renderRowControls(this.controls.after, record));
|
|
165
|
+
}
|
|
166
|
+
return $tr;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
editRecord(record){
|
|
170
|
+
record[editing] = true;
|
|
171
|
+
const $currentTr = this.shadowRoot.querySelector(`tr[data-index="${record[index]}"]`);
|
|
172
|
+
const $newTr = this.renderRecord(record);
|
|
173
|
+
$currentTr.replaceWith($newTr);
|
|
174
|
+
dispatchEvent(this, 'editingChange');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
saveEditedRecord(record){
|
|
178
|
+
record[editing] = false;
|
|
179
|
+
const $currentTr = this.shadowRoot.querySelector(`tr[data-index="${record[index]}"]`);
|
|
180
|
+
$currentTr.querySelectorAll('td').forEach(($td) => {
|
|
181
|
+
const field = $td.dataset.field;
|
|
182
|
+
const fieldDef = this.fields.find(f => f.name === field);
|
|
183
|
+
if(fieldDef && !fieldDef.calculator){ // calculated fields cannot be udated
|
|
184
|
+
const $input = $td.children.length === 1 ? $td.firstChild : $td.querySelector('input, select');
|
|
185
|
+
if ($input) {
|
|
186
|
+
record[field] = $input.value;
|
|
187
|
+
}
|
|
185
188
|
}
|
|
186
|
-
$records.appendChild($tr);
|
|
187
189
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
const $newTr = this.renderRecord(record);
|
|
191
|
+
$currentTr.replaceWith($newTr);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
cancelEditedRecord(record){
|
|
195
|
+
record[editing] = false;
|
|
196
|
+
const $currentTr = this.shadowRoot.querySelector(`tr[data-index="${record[index]}"]`);
|
|
197
|
+
const $newTr = this.renderRecord(record);
|
|
198
|
+
$currentTr.replaceWith($newTr);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
recordIsEditing(record){
|
|
202
|
+
return record[editing];
|
|
191
203
|
}
|
|
192
204
|
|
|
193
|
-
renderRowControls(controls = [], record
|
|
205
|
+
renderRowControls(controls = [], record) {
|
|
194
206
|
if(!this.rendered) return;
|
|
195
207
|
const $td = document.createElement('td');
|
|
196
|
-
controls.forEach(({ html, icon, action, render }) => {
|
|
208
|
+
controls.filter(c=>!!c).forEach(({ html, icon, action, render }) => {
|
|
197
209
|
if(html){
|
|
198
210
|
$td.appendChild(document.createRange().createContextualFragment(html));
|
|
199
211
|
} else if(render && typeof(render) === 'function'){
|
|
200
|
-
const rendered = render(this, record
|
|
212
|
+
const rendered = render(this, record);
|
|
201
213
|
if(rendered instanceof HTMLElement){
|
|
202
214
|
$td.appendChild(rendered);
|
|
203
215
|
} else if(typeof(rendered) === 'string'){
|
|
@@ -208,7 +220,7 @@ export default class Table extends Component {
|
|
|
208
220
|
$button.classList.add('pq', 'no-btn');
|
|
209
221
|
$button.appendChild(new Icon(icon));
|
|
210
222
|
if (action) {
|
|
211
|
-
onEvent($button, 'click', () => action(this, record
|
|
223
|
+
onEvent($button, 'click', () => action(this, record));
|
|
212
224
|
}
|
|
213
225
|
$td.appendChild($button);
|
|
214
226
|
}
|
|
@@ -220,7 +232,7 @@ export default class Table extends Component {
|
|
|
220
232
|
if(!this.rendered) return;
|
|
221
233
|
const $container = this.shadowRoot.getElementById(position);
|
|
222
234
|
$container.innerHTML = '';
|
|
223
|
-
controls.forEach(({ html, icon, action, render }) => {
|
|
235
|
+
controls.filter(c=>!!c).forEach(({ html, icon, action, render }) => {
|
|
224
236
|
if(html){
|
|
225
237
|
$container.appendChild(document.createRange().createContextualFragment(html));
|
|
226
238
|
} else if(render && typeof(render) === 'function'){
|
|
@@ -247,7 +259,7 @@ export default class Table extends Component {
|
|
|
247
259
|
}
|
|
248
260
|
|
|
249
261
|
getTotalPages() {
|
|
250
|
-
return Math.ceil(this.
|
|
262
|
+
return Math.ceil(this.getDisplayedRecords().length / this.pageSize);
|
|
251
263
|
}
|
|
252
264
|
|
|
253
265
|
setPage(page) {
|
|
@@ -287,10 +299,18 @@ export default class Table extends Component {
|
|
|
287
299
|
currentPage = false,
|
|
288
300
|
enableSelection = false // Add enableSelection property
|
|
289
301
|
} = {}) {
|
|
290
|
-
let rerender = false
|
|
291
|
-
|
|
302
|
+
let rerender = false,
|
|
303
|
+
rerenderControls = false,
|
|
304
|
+
pageCountBefore = this.getTotalPages(),
|
|
305
|
+
pageBefore = this.currentPage;
|
|
292
306
|
if(records){
|
|
293
|
-
this.records = records;
|
|
307
|
+
this.records = records.map(r=>({...r}));
|
|
308
|
+
this.records.forEach((record, idx) => {
|
|
309
|
+
record[index] = idx;
|
|
310
|
+
record[selected] = false;
|
|
311
|
+
record[hidden] = false;
|
|
312
|
+
record[editing] = false;
|
|
313
|
+
});
|
|
294
314
|
this.fields = fields || Table.extractFieldsFromRecords(this.records);
|
|
295
315
|
rerender = true;
|
|
296
316
|
}
|
|
@@ -321,13 +341,34 @@ export default class Table extends Component {
|
|
|
321
341
|
this.renderControls(this.controls.top, 'top');
|
|
322
342
|
this.renderControls(this.controls.bottom, 'bottom');
|
|
323
343
|
}
|
|
344
|
+
const newPageCount = this.getTotalPages();
|
|
345
|
+
if(newPageCount !== pageCountBefore){
|
|
346
|
+
dispatchEvent(this, 'pageCountChanged', { totalPages: this.getTotalPages() });
|
|
347
|
+
}
|
|
348
|
+
if(pageBefore > newPageCount){
|
|
349
|
+
this.setPage(newPageCount);
|
|
350
|
+
}
|
|
324
351
|
}
|
|
325
352
|
|
|
326
353
|
setRecords(records) {
|
|
327
|
-
|
|
328
|
-
|
|
354
|
+
let pageCountBefore = this.getTotalPages(),
|
|
355
|
+
pageBefore = this.currentPage;
|
|
356
|
+
this.records = records.map(r=>({...r}));
|
|
357
|
+
this.records.forEach((record, idx) => {
|
|
358
|
+
record[index] = idx;
|
|
359
|
+
record[selected] = false;
|
|
360
|
+
record[hidden] = false;
|
|
361
|
+
record[editing] = false;
|
|
362
|
+
});
|
|
329
363
|
this.renderRecords();
|
|
330
364
|
dispatchEvent(this, 'recordsSet', { records });
|
|
365
|
+
const newPageCount = this.getTotalPages();
|
|
366
|
+
if(newPageCount !== pageCountBefore){
|
|
367
|
+
dispatchEvent(this, 'pageCountChanged', { totalPages: this.getTotalPages() });
|
|
368
|
+
}
|
|
369
|
+
if(pageBefore > newPageCount){
|
|
370
|
+
this.setPage(newPageCount);
|
|
371
|
+
}
|
|
331
372
|
}
|
|
332
373
|
|
|
333
374
|
setupFetchRecords(totalRecords, callback) {
|
|
@@ -346,66 +387,232 @@ export default class Table extends Component {
|
|
|
346
387
|
}
|
|
347
388
|
|
|
348
389
|
addRecord(record) {
|
|
390
|
+
record[selected] = false;
|
|
391
|
+
record[hidden] = false;
|
|
392
|
+
record[index] = this.records.length;
|
|
349
393
|
this.records.push(record);
|
|
350
394
|
this.renderRecords();
|
|
351
395
|
dispatchEvent(this, 'recordAdded', { record });
|
|
352
396
|
}
|
|
353
397
|
|
|
354
|
-
updateRecord(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
398
|
+
updateRecord(record, newData) {
|
|
399
|
+
let updated = false;
|
|
400
|
+
let originalRecord = this.records.find(r => r === record);
|
|
401
|
+
if (!originalRecord && record[index] !== undefined) {
|
|
402
|
+
originalRecord = this.records[record[index]];
|
|
403
|
+
}
|
|
404
|
+
Object.keys(newData).forEach(key => {
|
|
405
|
+
if (originalRecord.hasOwnProperty(key)) {
|
|
406
|
+
originalRecord[key] = newData[key];
|
|
407
|
+
updated = true;
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
if(updated){
|
|
411
|
+
const start = (this.currentPage - 1) * this.pageSize;
|
|
412
|
+
const end = start + this.pageSize;
|
|
413
|
+
if (
|
|
414
|
+
!this.enablePages ||
|
|
415
|
+
(originalRecord[index] >= start && originalRecord[index] < end)
|
|
416
|
+
) {
|
|
417
|
+
this.renderRecords();
|
|
418
|
+
}
|
|
360
419
|
}
|
|
361
420
|
}
|
|
362
421
|
|
|
363
|
-
deleteRecord(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
422
|
+
deleteRecord(record) {
|
|
423
|
+
let originalRecord = this.records.find(r => r === record),
|
|
424
|
+
totalPagesBefore = this.getTotalPages();
|
|
425
|
+
if (!originalRecord && record[index] !== undefined) {
|
|
426
|
+
originalRecord = this.records[record[index]];
|
|
427
|
+
}
|
|
428
|
+
if (originalRecord) {
|
|
429
|
+
const recordIndex = this.records.indexOf(originalRecord);
|
|
430
|
+
this.records.splice(recordIndex, 1);
|
|
431
|
+
// Update the index of remaining records
|
|
432
|
+
this.records.forEach((rec, idx) => {
|
|
433
|
+
rec[index] = idx;
|
|
434
|
+
});
|
|
369
435
|
this.renderRecords();
|
|
370
|
-
dispatchEvent(this, '
|
|
436
|
+
dispatchEvent(this, 'selectionChange');
|
|
437
|
+
dispatchEvent(this, 'recordDeleted', { index: recordIndex });
|
|
438
|
+
const totalPages = this.getTotalPages();
|
|
439
|
+
if (this.currentPage > totalPages) {
|
|
440
|
+
this.setPage(totalPages);
|
|
441
|
+
}
|
|
442
|
+
if(totalPages !== totalPagesBefore){
|
|
443
|
+
dispatchEvent(this, 'pageCountChanged', { totalPages });
|
|
444
|
+
}
|
|
371
445
|
}
|
|
372
446
|
}
|
|
373
447
|
|
|
448
|
+
deleteSelected() {
|
|
449
|
+
let totalPagesBefore = this.getTotalPages();
|
|
450
|
+
const selectedRecords = this.getSelectedRecords();
|
|
451
|
+
selectedRecords.forEach(record => {
|
|
452
|
+
let originalRecord = this.records.find(r => r === record);
|
|
453
|
+
if (!originalRecord && record[index] !== undefined) {
|
|
454
|
+
originalRecord = this.records[record[index]];
|
|
455
|
+
}
|
|
456
|
+
if (originalRecord) {
|
|
457
|
+
const recordIndex = this.records.indexOf(originalRecord);
|
|
458
|
+
this.records.splice(recordIndex, 1);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
this.records.forEach((rec, idx) => {
|
|
462
|
+
rec[index] = idx;
|
|
463
|
+
});
|
|
464
|
+
this.renderRecords();
|
|
465
|
+
const totalPages = this.getTotalPages();
|
|
466
|
+
if (this.currentPage > totalPages) {
|
|
467
|
+
this.setPage(totalPages);
|
|
468
|
+
}
|
|
469
|
+
if(totalPages !== totalPagesBefore){
|
|
470
|
+
dispatchEvent(this, 'pageCountChanged', { totalPages });
|
|
471
|
+
}
|
|
472
|
+
dispatchEvent(this, 'selectionChange');
|
|
473
|
+
}
|
|
474
|
+
|
|
374
475
|
getSelectedRecords() {
|
|
375
|
-
|
|
476
|
+
const selectedRecords = this.records.filter(record => {
|
|
477
|
+
const isSelected = record[selected];
|
|
478
|
+
return isSelected;
|
|
479
|
+
});
|
|
480
|
+
return selectedRecords;
|
|
376
481
|
}
|
|
377
482
|
|
|
378
483
|
selectAllOnPage() {
|
|
379
484
|
const start = (this.currentPage - 1) * this.pageSize;
|
|
380
485
|
const end = Math.min(start + this.pageSize, this.records.length);
|
|
381
486
|
for (let i = start; i < end; i++) {
|
|
382
|
-
|
|
383
|
-
this.selectedIndexes.push(i);
|
|
384
|
-
}
|
|
487
|
+
this.records[i][selected] = true;
|
|
385
488
|
}
|
|
386
489
|
this.renderRecords();
|
|
387
|
-
|
|
490
|
+
setTimeout(() => {
|
|
491
|
+
dispatchEvent(this, 'selectionChange');
|
|
492
|
+
}, 0);
|
|
388
493
|
}
|
|
389
494
|
|
|
390
495
|
deselectAllOnPage() {
|
|
391
496
|
const start = (this.currentPage - 1) * this.pageSize;
|
|
392
497
|
const end = Math.min(start + this.pageSize, this.records.length);
|
|
393
|
-
|
|
498
|
+
for (let i = start; i < end; i++) {
|
|
499
|
+
this.records[i][selected] = false;
|
|
500
|
+
}
|
|
394
501
|
this.renderRecords();
|
|
395
|
-
|
|
502
|
+
setTimeout(() => {
|
|
503
|
+
dispatchEvent(this, 'selectionChange');
|
|
504
|
+
}, 0);
|
|
396
505
|
}
|
|
397
506
|
|
|
398
507
|
allOnPageSelected() {
|
|
399
508
|
const start = (this.currentPage - 1) * this.pageSize;
|
|
400
509
|
const end = Math.min(start + this.pageSize, this.records.length);
|
|
401
510
|
for (let i = start; i < end; i++) {
|
|
402
|
-
if (!this.
|
|
511
|
+
if (!this.records[i][selected]) {
|
|
403
512
|
return false;
|
|
404
513
|
}
|
|
405
514
|
}
|
|
406
515
|
return true;
|
|
407
516
|
}
|
|
408
517
|
|
|
518
|
+
sortBy(field, asc = true) {
|
|
519
|
+
this.sort = this.sort.filter(item => item.name !== field);
|
|
520
|
+
this.sort.push({ name: field, asc });
|
|
521
|
+
this.renderRecords();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
hideRecord(record) {
|
|
525
|
+
let originalRecord = this.records.find(r => r === record);
|
|
526
|
+
if (!originalRecord && record[index] !== undefined) {
|
|
527
|
+
originalRecord = this.records[record[index]];
|
|
528
|
+
}
|
|
529
|
+
if (originalRecord) {
|
|
530
|
+
originalRecord[hidden] = true;
|
|
531
|
+
this.renderRecords();
|
|
532
|
+
dispatchEvent(this, 'recordHidden');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
showRecord(record) {
|
|
537
|
+
let originalRecord = this.records.find(r => r === record);
|
|
538
|
+
if (!originalRecord && record[index] !== undefined) {
|
|
539
|
+
originalRecord = this.records[record[index]];
|
|
540
|
+
}
|
|
541
|
+
if (originalRecord) {
|
|
542
|
+
originalRecord[hidden] = false;
|
|
543
|
+
this.renderRecords();
|
|
544
|
+
dispatchEvent(this, 'recordShown');
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
showAllRecords() {
|
|
549
|
+
this.records.forEach(record => {
|
|
550
|
+
record[hidden] = false;
|
|
551
|
+
});
|
|
552
|
+
this.renderRecords();
|
|
553
|
+
dispatchEvent(this, 'recordShown allRecordsShown');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
addFilter(field, condition, value) {
|
|
557
|
+
this.filters.push({ field, condition, value });
|
|
558
|
+
dispatchEvent(this, 'filterAdded filterChange');
|
|
559
|
+
this.renderRecords();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
removeFilter(field, condition, value) {
|
|
563
|
+
this.filters = this.filters.filter(f => f.field !== field || f.condition !== condition || f.value !== value);
|
|
564
|
+
dispatchEvent(this, 'filterRemoved filterChange');
|
|
565
|
+
this.renderRecords();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
getDisplayedRecords() {
|
|
569
|
+
let displayedRecords = this.records.filter(record => !record[hidden]);
|
|
570
|
+
|
|
571
|
+
this.filters.forEach(({ field, condition, value }) => {
|
|
572
|
+
displayedRecords = displayedRecords.filter(record => {
|
|
573
|
+
if (condition === 'eq') {
|
|
574
|
+
return record[field] === value;
|
|
575
|
+
}
|
|
576
|
+
if (condition === 'neq') {
|
|
577
|
+
return record[field] !== value;
|
|
578
|
+
}
|
|
579
|
+
if (condition === 'gt') {
|
|
580
|
+
return record[field] > value;
|
|
581
|
+
}
|
|
582
|
+
if (condition === 'lt') {
|
|
583
|
+
return record[field] < value;
|
|
584
|
+
}
|
|
585
|
+
if (condition === 'gte') {
|
|
586
|
+
return record[field] >= value;
|
|
587
|
+
}
|
|
588
|
+
if (condition === 'lte') {
|
|
589
|
+
return record[field] <= value;
|
|
590
|
+
}
|
|
591
|
+
if (condition === 'contains') {
|
|
592
|
+
return record[field].includes(value);
|
|
593
|
+
}
|
|
594
|
+
if (condition === 'ncontains') {
|
|
595
|
+
return !record[field].includes(value);
|
|
596
|
+
}
|
|
597
|
+
return true;
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
this.sort.forEach(({ name, asc }) => {
|
|
602
|
+
displayedRecords.sort((a, b) => {
|
|
603
|
+
if (a[name] < b[name]) return asc ? -1 : 1;
|
|
604
|
+
if (a[name] > b[name]) return asc ? 1 : -1;
|
|
605
|
+
return 0;
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
return displayedRecords;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
getHiddenRecords() {
|
|
613
|
+
return this.records.filter(record => record[hidden]);
|
|
614
|
+
}
|
|
615
|
+
|
|
409
616
|
get shadowTemplate(){
|
|
410
617
|
return /*html*/`
|
|
411
618
|
${super.shadowTemplate}
|
|
@@ -419,6 +626,7 @@ export default class Table extends Component {
|
|
|
419
626
|
</div>
|
|
420
627
|
`;
|
|
421
628
|
}
|
|
629
|
+
|
|
422
630
|
get shadowStyles(){
|
|
423
631
|
return /*css*/`
|
|
424
632
|
${super.shadowStyles}
|
|
@@ -464,6 +672,15 @@ export default class Table extends Component {
|
|
|
464
672
|
width: 1.35rem;
|
|
465
673
|
height: 1.35rem;
|
|
466
674
|
}
|
|
675
|
+
tr[editing] td {
|
|
676
|
+
padding: 0;
|
|
677
|
+
}
|
|
678
|
+
tr[editing] input,
|
|
679
|
+
tr[editing] select,
|
|
680
|
+
tr[editing] .input {
|
|
681
|
+
padding: calc(0.75 * var(--spacer)) var(--spacer) !important;
|
|
682
|
+
width: 100%;
|
|
683
|
+
}
|
|
467
684
|
`;
|
|
468
685
|
}
|
|
469
686
|
|
|
@@ -473,6 +690,119 @@ export default class Table extends Component {
|
|
|
473
690
|
Object.keys(record).forEach( name => names.add(name) )
|
|
474
691
|
});
|
|
475
692
|
return [...names].map(name=>({name,label:toTitleCase(name)}));
|
|
476
|
-
}
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
static controls = {
|
|
696
|
+
prevPage: {
|
|
697
|
+
render: (table) => {
|
|
698
|
+
const $button = document.createElement('button');
|
|
699
|
+
$button.classList.add('pq', 'no-btn');
|
|
700
|
+
$button.appendChild(new Icon('chevron-left'));
|
|
701
|
+
onEvent($button, 'click', () => table.prevPage());
|
|
702
|
+
|
|
703
|
+
// Disable button if on the first page
|
|
704
|
+
onEvent(table, 'pageChange', () => {
|
|
705
|
+
$button.disabled = table.getCurrentPage() === 1;
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
return $button;
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
nextPage: {
|
|
712
|
+
render: (table) => {
|
|
713
|
+
const $button = document.createElement('button');
|
|
714
|
+
$button.classList.add('pq', 'no-btn');
|
|
715
|
+
$button.appendChild(new Icon('chevron-right'));
|
|
716
|
+
onEvent($button, 'click', () => table.nextPage());
|
|
717
|
+
|
|
718
|
+
// Disable button if on the last page
|
|
719
|
+
onEvent(table, 'pageChange', () => {
|
|
720
|
+
$button.disabled = table.getCurrentPage() === table.getTotalPages();
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
return $button;
|
|
724
|
+
}
|
|
725
|
+
},
|
|
726
|
+
pageSelect: {
|
|
727
|
+
render: (table) => new PageSelect(table)
|
|
728
|
+
},
|
|
729
|
+
pageSize: {
|
|
730
|
+
render: (table) => new PageSize(table)
|
|
731
|
+
},
|
|
732
|
+
selectCount: {
|
|
733
|
+
render: (table) => new SelectCount(table)
|
|
734
|
+
},
|
|
735
|
+
deleteSelected: {
|
|
736
|
+
render: (table) => new DeleteSelected(table)
|
|
737
|
+
},
|
|
738
|
+
spacer: {
|
|
739
|
+
html: '<div class="flex"></div>'
|
|
740
|
+
},
|
|
741
|
+
edit: {
|
|
742
|
+
render: (table, record) => new Edit(table, record)
|
|
743
|
+
},
|
|
744
|
+
hide: {
|
|
745
|
+
render: (table, record) => new Hide(table, record)
|
|
746
|
+
},
|
|
747
|
+
hiddenCount: {
|
|
748
|
+
render: (table) => new HiddenCount(table)
|
|
749
|
+
},
|
|
750
|
+
showAll: {
|
|
751
|
+
render: (table) => new ShowAll(table)
|
|
752
|
+
},
|
|
753
|
+
filters: {
|
|
754
|
+
render: (table) => new Filters(table)
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
static format(value){
|
|
759
|
+
const f = Array.isArray(value) ? Table.formatters.array : Table.formatters[typeof value];
|
|
760
|
+
return f(value);
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
static formatters = {
|
|
764
|
+
string: v=>v,
|
|
765
|
+
number: v=>`${v}`,
|
|
766
|
+
date: v=>v.toLocaleDateString(),
|
|
767
|
+
boolean: v=>v ? 'True' : 'False',
|
|
768
|
+
array: v=>v.map(i=>Table.format(i)).join(', '),
|
|
769
|
+
'undefined': v=>'',
|
|
770
|
+
'null': v=>'<code>null</code>'
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
static editors = {
|
|
774
|
+
string: (value) => {
|
|
775
|
+
const $i = document.createElement('input');
|
|
776
|
+
$i.value = value;
|
|
777
|
+
return $i;
|
|
778
|
+
},
|
|
779
|
+
number: (value) => {
|
|
780
|
+
const $i = document.createElement('input');
|
|
781
|
+
$i.type = 'number';
|
|
782
|
+
$i.value = value;
|
|
783
|
+
return $i;
|
|
784
|
+
},
|
|
785
|
+
date: (value) => {
|
|
786
|
+
const $i = document.createElement('input');
|
|
787
|
+
$i.type = 'date';
|
|
788
|
+
$i.value = value;
|
|
789
|
+
return $i;
|
|
790
|
+
},
|
|
791
|
+
boolean: (value) => {
|
|
792
|
+
const $i = document.createElement('select');
|
|
793
|
+
$i.innerHTML = `
|
|
794
|
+
<option value="true" ${value ? 'selected' : ''}>True</option>
|
|
795
|
+
<option value="false" ${!value ? 'selected' : ''}>False</option>
|
|
796
|
+
`;
|
|
797
|
+
$i.value = value;
|
|
798
|
+
return $i;
|
|
799
|
+
},
|
|
800
|
+
calculated: (value) => {
|
|
801
|
+
const $i = document.createElement('input');
|
|
802
|
+
$i.disabled = true;
|
|
803
|
+
$i.value = value;
|
|
804
|
+
return $i;
|
|
805
|
+
}
|
|
806
|
+
};
|
|
477
807
|
}
|
|
478
808
|
window.customElements.define('k-table', Table);
|