kempo 2.7.4 → 2.9.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/Table.js +1 -1
- package/docs/components/table.html +298 -151
- package/docs/icons/add.svg +1 -0
- package/docs/icons/refresh.svg +1 -0
- package/docs/kempo/components/Table.js +1 -1
- package/docs/table.html +0 -0
- package/icons/add.svg +1 -0
- package/icons/refresh.svg +1 -0
- package/package.json +1 -1
- package/src/components/Table.js +357 -35
- package/src/components/tableControls/DeleteSelected.js +58 -0
- package/src/components/tableControls/PageSelect.js +68 -0
- package/src/components/tableControls/PageSize.js +56 -0
- package/src/components/tableControls/SelectCount.js +46 -0
- package/README.md +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import Icon from"./Icon.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{
|
|
1
|
+
import Component from"./Component.js";import Icon from"./Icon.js";import{toTitleCase}from"../utils/string.js";import{onEvent,dispatchEvent}from"../utils/element.js";export default class Table extends Component{static controls={prevPage:{render:e=>{const t=document.createElement("button");return t.classList.add("pq"),t.appendChild(new Icon("chevron-left")),onEvent(t,"click",(()=>e.prevPage())),onEvent(e,"pageChange",(()=>{t.disabled=1===e.getCurrentPage()})),t}},nextPage:{render:e=>{const t=document.createElement("button");return t.classList.add("pq"),t.appendChild(new Icon("chevron-right")),onEvent(t,"click",(()=>e.nextPage())),onEvent(e,"pageChange",(()=>{t.disabled=e.getCurrentPage()===e.getTotalPages()})),t}},pageSelect:{render:e=>new PageSelect(e)},pageSize:{render:e=>new PageSize(e)},spacer:{html:'<div class="flex"></div>'}};constructor({records:e=[],fields:t=[],controls:n={before:[],after:[],top:[],bottom:[]},pageSize:s=100,pageSizeOptions:r=[10,25,50,100,500],currentPage:a=1}={}){super(),this.registerProps({fields:t,records:e,controls:n,pageSize:s,pageSizeOptions:r,currentPage:a})}async render(e){return!!await super.render(e)&&(this.setData({records:this.records,fields:this.fields,controls:this.controls,pageSize:this.pageSize,pageSizeOptions:this.pageSizeOptions,currentPage:this.currentPage}),!0)}renderFields(){if(!this.rendered)return;const e=this.controls?.before?.length?"<th></th>":"",t=this.controls?.after?.length?"<th></th>":"";this.shadowRoot.getElementById("fields").innerHTML=`${e}${this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}${t}`}renderRecords(){if(!this.rendered)return;const e=this.shadowRoot.getElementById("records");e.innerHTML="";const t=(this.currentPage-1)*this.pageSize,n=t+this.pageSize;this.records.slice(t,n).forEach(((s,r)=>{const a=document.createElement("tr");this.controls?.before?.length&&a.appendChild(this.renderRowControls(this.controls.before,s,t+r)),this.fields.forEach((({name:e,formatter:r,calculator:o})=>{const i=document.createElement("td");if(null===s)i.innerHTML="Loading...",dispatchEvent(this,"fetchRecords",{start:t,end:n});else{let t;o?t=o(s,this):(t=s[e]||"",Array.isArray(t)?t=r?t.map(r).join(", "):t.join(", "):r&&(t=r(t))),i.innerHTML=t}a.appendChild(i)})),this.controls?.after?.length&&a.appendChild(this.renderRowControls(this.controls.after,s,t+r)),e.appendChild(a)}))}renderRowControls(e=[],t,n){if(!this.rendered)return;const s=document.createElement("td");return e.forEach((({html:e,icon:r,action:a,render:o})=>{if(e)s.appendChild(document.createRange().createContextualFragment(e));else if(o&&"function"==typeof o){const e=o(this,t,n);e instanceof HTMLElement?s.appendChild(e):"string"==typeof e&&s.appendChild(document.createRange().createContextualFragment(e))}else if(r){const e=document.createElement("button");e.classList.add("pq"),e.appendChild(new Icon(r)),a&&onEvent(e,"click",(()=>a(this,t,n))),s.appendChild(e)}})),s}renderControls(e=[],t){if(!this.rendered)return;const n=this.shadowRoot.getElementById(t);n.innerHTML="",e.forEach((({html:e,icon:t,action:s,render:r})=>{if(e)n.appendChild(document.createRange().createContextualFragment(e));else if(r&&"function"==typeof r){const e=r(this);e instanceof HTMLElement?n.appendChild(e):"string"==typeof e&&n.appendChild(document.createRange().createContextualFragment(e))}else if(t){const e=document.createElement("button");e.classList.add("pq"),e.appendChild(new Icon(t)),s&&onEvent(e,"click",(()=>s(this))),n.appendChild(e)}}))}getCurrentPage(){return this.currentPage}getTotalPages(){return Math.ceil(this.records.length/this.pageSize)}setPage(e){e<1||e>this.getTotalPages()||(this.currentPage=e,this.renderRecords(),dispatchEvent(this,"pageChange"))}nextPage(){this.currentPage<this.getTotalPages()&&this.setPage(this.currentPage+1)}prevPage(){this.currentPage>1&&this.setPage(this.currentPage-1)}setPageSize(e){this.pageSize=e,this.currentPage=1,this.renderRecords(),dispatchEvent(this,"pageSizeChange")}setData({records:e=!1,fields:t=!1,controls:n=!1,pageSize:s=!1,pageSizeOptions:r=!1,currentPage:a=!1}={}){let o=!1,i=!1;e&&(this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),o=!0),s&&(this.pageSize=s,o=!0),r&&(this.pageSizeOptions=r),n&&(this.controls=n,i=!0),a&&(this.currentPage=a,o=!0),o&&(this.renderFields(),this.renderRecords()),i&&(this.renderControls(this.controls.top,"top"),this.renderControls(this.controls.bottom,"bottom"))}setRecords(e){this.records=e,this.currentPage=1,this.renderRecords(),dispatchEvent(this,"recordsSet",{records:e})}setupFetchRecords(e,t){const n=this.records.length;n<e&&(this.records.length=e,this.records.fill(null,n)),onEvent(this,"fetchRecords",(async e=>{const{start:n,end:s}=e.detail,r=await t(n,s-n);this.records.splice(n,r.length,...r),this.renderRecords()}))}addRecord(e){this.records.push(e),this.renderRecords(),dispatchEvent(this,"recordAdded",{record:e})}updateRecord(e,t){this.records[e]=t;const n=(this.currentPage-1)*this.pageSize,s=n+this.pageSize;e>=n&&e<s&&this.renderRecords()}deleteRecord(e){e>=0&&e<this.records.length&&(this.records.splice(e,1),this.renderRecords(),dispatchEvent(this,"recordDeleted",{index:e}))}get shadowTemplate(){return`\n ${super.shadowTemplate}\n <div class="responsive-table">\n <div id="top"></div>\n <table class="b0">\n <thead id="fields"></thead>\n <tbody id="records"></tbody>\n </table>\n <div id="bottom"></div>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n margin-bottom: var(--spacer);\n border: 1px solid var(--c_border);\n border-radius: var(--radius);\n }\n #top, #bottom {\n display: flex;\n }\n th {\n border-top: none;\n }\n tr:last-child td:first-child {\n border-bottom-left-radius: 0;\n }\n tr:last-child td:last-child {\n border-bottom-right-radius: 0;\n }\n tr:last-child td {\n border-bottom: none;\n }\n th:first-child, td:first-child {\n border-left: none;\n }\n th:last-child, td:last-child {\n border-right: none;\n }\n #top:not(:empty) {\n border-bottom: 1px solid var(--c_border);\n }\n #bottom:not(:empty) {\n border-top: 1px solid var(--c_border);\n }\n `}static extractFieldsFromRecords(e,t=100){const n=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>n.add(e)))})),[...n].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);class PageSelect extends Component{constructor(e){super(),this.table=e,this.classList.add("mxq")}connectedCallback(){this.render()}async render(){return!!await super.render()&&(this.updateOptions(),!0)}updateOptions(){const e=this.shadowRoot.getElementById("pageSelect");e.innerHTML="";for(let t=1;t<=this.table.getTotalPages();t++){const n=document.createElement("option");n.value=t,n.textContent=t,e.appendChild(n)}e.value=this.table.getCurrentPage();this.shadowRoot.getElementById("totalPages").textContent=this.table.getTotalPages(),onEvent(e,"change",(()=>{this.table.setPage(parseInt(e.value))})),onEvent(this.table,"pageSizeChange",(()=>{this.updateOptions()})),onEvent(this.table,"pageChange",(()=>{e.value=this.table.getCurrentPage()}))}get shadowTemplate(){return'\n <select id="pageSelect" class="mxq"></select>\n <label> out of <span id="totalPages"></span></label>\n '}get shadowStyles(){return"\n :host {\n display: inline-flex;\n width: max-content;\n align-items: baseline;\n }\n #pageSelect, label {\n display: inline;\n }\n label {\n white-space: nowrap;\n }\n "}}window.customElements.define("k-table-page-select",PageSelect);class PageSize extends Component{constructor(e){super(),this.table=e,this.classList.add("mxq")}connectedCallback(){this.render()}async render(){if(await super.render()){const e=this.shadowRoot.getElementById("pageSizeSelect");return this.table.pageSizeOptions.forEach((t=>{const n=document.createElement("option");n.value=t,n.textContent=t,e.appendChild(n)})),e.value=this.table.pageSize,onEvent(e,"change",(()=>{this.table.setPageSize(parseInt(e.value))})),onEvent(this.table,"pageSizeChange",(()=>{e.value=this.table.pageSize})),!0}return!1}get shadowTemplate(){return'\n <label for="pageSizeSelect">Page Size: </label>\n <select id="pageSizeSelect" class="mxq"></select>\n '}get shadowStyles(){return"\n :host {\n display: inline-flex;\n width: max-content;\n align-items: baseline;\n }\n label {\n white-space: nowrap;\n }\n "}}window.customElements.define("k-table-page-size",PageSize);
|
package/docs/table.html
ADDED
|
File without changes
|
package/icons/add.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z"/></svg>
|
package/package.json
CHANGED
package/src/components/Table.js
CHANGED
|
@@ -1,68 +1,196 @@
|
|
|
1
1
|
import Component from './Component.js';
|
|
2
|
-
import Icon from './Icon.js';
|
|
2
|
+
import Icon from './Icon.js';
|
|
3
|
+
import SelectCount from './tableControls/SelectCount.js';
|
|
4
|
+
import PageSelect from './tableControls/PageSelect.js';
|
|
5
|
+
import PageSize from './tableControls/PageSize.js';
|
|
6
|
+
import DeleteSelected from './tableControls/DeleteSelected.js';
|
|
3
7
|
import { toTitleCase } from '../utils/string.js';
|
|
8
|
+
import { onEvent, dispatchEvent } from '../utils/element.js';
|
|
4
9
|
|
|
5
10
|
export default class Table extends Component {
|
|
6
|
-
|
|
11
|
+
static controls = {
|
|
12
|
+
prevPage: {
|
|
13
|
+
render: (table) => {
|
|
14
|
+
const $button = document.createElement('button');
|
|
15
|
+
$button.classList.add('pq');
|
|
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');
|
|
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
|
+
};
|
|
58
|
+
|
|
59
|
+
constructor({
|
|
60
|
+
records = [],
|
|
61
|
+
fields = [],
|
|
62
|
+
controls = { before: [], after: [], top: [], bottom: [] },
|
|
63
|
+
pageSize = 100,
|
|
64
|
+
pageSizeOptions = [10, 25, 50, 100, 500],
|
|
65
|
+
currentPage = 1,
|
|
66
|
+
enableSelection = false,
|
|
67
|
+
} = {}) {
|
|
7
68
|
super();
|
|
8
69
|
|
|
9
70
|
this.registerProps({
|
|
10
|
-
fields:
|
|
11
|
-
records:
|
|
12
|
-
controls:
|
|
71
|
+
fields: fields,
|
|
72
|
+
records: records,
|
|
73
|
+
controls: controls,
|
|
74
|
+
pageSize,
|
|
75
|
+
pageSizeOptions,
|
|
76
|
+
currentPage,
|
|
77
|
+
enableSelection,
|
|
78
|
+
selectedIndexes: [], // Renamed property
|
|
13
79
|
});
|
|
14
80
|
|
|
15
81
|
/* Init */
|
|
16
|
-
|
|
17
|
-
this.setData(data, fields, controls);
|
|
18
|
-
}
|
|
82
|
+
//
|
|
19
83
|
}
|
|
20
84
|
async render(force){
|
|
21
85
|
if(await super.render(force)){
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
86
|
+
this.setData({
|
|
87
|
+
records: this.records,
|
|
88
|
+
fields: this.fields,
|
|
89
|
+
controls: this.controls,
|
|
90
|
+
pageSize: this.pageSize,
|
|
91
|
+
pageSizeOptions: this.pageSizeOptions,
|
|
92
|
+
currentPage: this.currentPage
|
|
93
|
+
});
|
|
26
94
|
return true;
|
|
27
95
|
}
|
|
28
96
|
return false;
|
|
29
97
|
}
|
|
30
98
|
renderFields(){
|
|
99
|
+
if(!this.rendered) return;
|
|
31
100
|
const beforeControls = this.controls?.before?.length ? '<th></th>' : '';
|
|
32
101
|
const afterControls = this.controls?.after?.length ? '<th></th>' : '';
|
|
33
|
-
this.
|
|
102
|
+
const selectionControl = this.enableSelection ? '<th class="selection"><input type="checkbox" id="select-all"></th>' : '';
|
|
103
|
+
this.shadowRoot.getElementById('fields').innerHTML = `${selectionControl}${beforeControls}${this.fields.map(({label})=>`<th>${label}</th>`).join('')}${afterControls}`;
|
|
104
|
+
|
|
105
|
+
if (this.enableSelection) {
|
|
106
|
+
const selectAllCheckbox = this.shadowRoot.getElementById('select-all');
|
|
107
|
+
selectAllCheckbox.addEventListener('change', () => {
|
|
108
|
+
if (selectAllCheckbox.checked) {
|
|
109
|
+
this.selectAllOnPage();
|
|
110
|
+
} else {
|
|
111
|
+
this.deselectAllOnPage();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
onEvent(this, 'selectionChange pageChange', () => {
|
|
115
|
+
if(this.allOnPageSelected()){
|
|
116
|
+
selectAllCheckbox.checked = true;
|
|
117
|
+
} else {
|
|
118
|
+
selectAllCheckbox.checked = false;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
34
122
|
}
|
|
35
123
|
renderRecords(){
|
|
124
|
+
if(!this.rendered) return;
|
|
36
125
|
const $records = this.shadowRoot.getElementById('records');
|
|
37
126
|
$records.innerHTML = '';
|
|
38
|
-
this.
|
|
127
|
+
const start = (this.currentPage - 1) * this.pageSize;
|
|
128
|
+
const end = start + this.pageSize;
|
|
129
|
+
const paginatedRecords = this.records.slice(start, end);
|
|
130
|
+
paginatedRecords.forEach((record, index) => {
|
|
39
131
|
const $tr = document.createElement('tr');
|
|
132
|
+
if (this.enableSelection) {
|
|
133
|
+
const $td = document.createElement('td');
|
|
134
|
+
$td.classList.add('selection');
|
|
135
|
+
const $checkbox = document.createElement('input');
|
|
136
|
+
$checkbox.type = 'checkbox';
|
|
137
|
+
$checkbox.checked = this.selectedIndexes.includes(start + index);
|
|
138
|
+
$checkbox.addEventListener('change', () => {
|
|
139
|
+
if ($checkbox.checked) {
|
|
140
|
+
this.selectedIndexes.push(start + index);
|
|
141
|
+
} else {
|
|
142
|
+
this.selectedIndexes = this.selectedIndexes.filter(i => i !== start + index);
|
|
143
|
+
}
|
|
144
|
+
dispatchEvent(this, 'selectionChange', { selectedIndexes: this.selectedIndexes });
|
|
145
|
+
});
|
|
146
|
+
$td.appendChild($checkbox);
|
|
147
|
+
$tr.appendChild($td);
|
|
148
|
+
}
|
|
40
149
|
if(this.controls?.before?.length){
|
|
41
|
-
$tr.appendChild(this.renderRowControls(this.controls.before, record));
|
|
150
|
+
$tr.appendChild(this.renderRowControls(this.controls.before, record, start + index));
|
|
42
151
|
}
|
|
43
|
-
this.fields.forEach(({name}) => {
|
|
152
|
+
this.fields.forEach(({name, formatter, calculator}) => {
|
|
44
153
|
const $td = document.createElement('td');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
154
|
+
if (record === null) {
|
|
155
|
+
$td.innerHTML = 'Loading...';
|
|
156
|
+
dispatchEvent(this, 'fetchRecords', { start, end });
|
|
157
|
+
} else {
|
|
158
|
+
let value;
|
|
159
|
+
if(calculator){
|
|
160
|
+
value = calculator(record, this);
|
|
161
|
+
} else {
|
|
162
|
+
value = record[name] || '';
|
|
163
|
+
if (Array.isArray(value)) {
|
|
164
|
+
if(formatter){
|
|
165
|
+
value = value.map(formatter).join(', ');
|
|
166
|
+
} else {
|
|
167
|
+
value = value.join(', ');
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
if(formatter){
|
|
171
|
+
value = formatter(value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
$td.innerHTML = value;
|
|
48
176
|
}
|
|
49
|
-
$td.innerHTML = value;
|
|
50
177
|
$tr.appendChild($td);
|
|
51
178
|
});
|
|
52
179
|
if(this.controls?.after?.length){
|
|
53
|
-
$tr.appendChild(this.renderRowControls(this.controls.after, record));
|
|
180
|
+
$tr.appendChild(this.renderRowControls(this.controls.after, record, start + index));
|
|
54
181
|
}
|
|
55
182
|
$records.appendChild($tr);
|
|
56
183
|
});
|
|
57
184
|
}
|
|
58
185
|
|
|
59
|
-
renderRowControls(controls = [], record) {
|
|
186
|
+
renderRowControls(controls = [], record, index) {
|
|
187
|
+
if(!this.rendered) return;
|
|
60
188
|
const $td = document.createElement('td');
|
|
61
189
|
controls.forEach(({ html, icon, action, render }) => {
|
|
62
190
|
if(html){
|
|
63
191
|
$td.appendChild(document.createRange().createContextualFragment(html));
|
|
64
192
|
} else if(render && typeof(render) === 'function'){
|
|
65
|
-
const rendered = render(this, record);
|
|
193
|
+
const rendered = render(this, record, index);
|
|
66
194
|
if(rendered instanceof HTMLElement){
|
|
67
195
|
$td.appendChild(rendered);
|
|
68
196
|
} else if(typeof(rendered) === 'string'){
|
|
@@ -70,9 +198,10 @@ export default class Table extends Component {
|
|
|
70
198
|
}
|
|
71
199
|
} else if(icon){
|
|
72
200
|
const $button = document.createElement('button');
|
|
201
|
+
$button.classList.add('pq');
|
|
73
202
|
$button.appendChild(new Icon(icon));
|
|
74
203
|
if (action) {
|
|
75
|
-
$button
|
|
204
|
+
onEvent($button, 'click', () => action(this, record, index));
|
|
76
205
|
}
|
|
77
206
|
$td.appendChild($button);
|
|
78
207
|
}
|
|
@@ -81,7 +210,9 @@ export default class Table extends Component {
|
|
|
81
210
|
}
|
|
82
211
|
|
|
83
212
|
renderControls(controls = [], position) {
|
|
84
|
-
|
|
213
|
+
if(!this.rendered) return;
|
|
214
|
+
const $container = this.shadowRoot.getElementById(position);
|
|
215
|
+
$container.innerHTML = '';
|
|
85
216
|
controls.forEach(({ html, icon, action, render }) => {
|
|
86
217
|
if(html){
|
|
87
218
|
$container.appendChild(document.createRange().createContextualFragment(html));
|
|
@@ -94,25 +225,178 @@ export default class Table extends Component {
|
|
|
94
225
|
}
|
|
95
226
|
} else if(icon){
|
|
96
227
|
const $button = document.createElement('button');
|
|
228
|
+
$button.classList.add('pq');
|
|
97
229
|
$button.appendChild(new Icon(icon));
|
|
98
230
|
if (action) {
|
|
99
|
-
$button
|
|
231
|
+
onEvent($button, 'click', () => action(this));
|
|
100
232
|
}
|
|
101
233
|
$container.appendChild($button);
|
|
102
234
|
}
|
|
103
235
|
});
|
|
104
|
-
this.shadowRoot.getElementById(position).innerHTML = '';
|
|
105
|
-
this.shadowRoot.getElementById(position).appendChild($container);
|
|
106
236
|
}
|
|
107
237
|
|
|
108
|
-
|
|
238
|
+
getCurrentPage() {
|
|
239
|
+
return this.currentPage;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
getTotalPages() {
|
|
243
|
+
return Math.ceil(this.records.length / this.pageSize);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
setPage(page) {
|
|
247
|
+
if (page < 1 || page > this.getTotalPages()) return;
|
|
248
|
+
this.currentPage = page;
|
|
249
|
+
this.renderRecords();
|
|
250
|
+
// Trigger page change event
|
|
251
|
+
dispatchEvent(this, 'pageChange');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
nextPage() {
|
|
255
|
+
if (this.currentPage < this.getTotalPages()) {
|
|
256
|
+
this.setPage(this.currentPage + 1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
prevPage() {
|
|
261
|
+
if (this.currentPage > 1) {
|
|
262
|
+
this.setPage(this.currentPage - 1);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
setPageSize(pageSize) {
|
|
267
|
+
this.pageSize = pageSize;
|
|
268
|
+
this.currentPage = 1;
|
|
269
|
+
this.renderRecords();
|
|
270
|
+
// Trigger page size change event
|
|
271
|
+
dispatchEvent(this, 'pageSizeChange');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
setData({
|
|
275
|
+
records = false,
|
|
276
|
+
fields = false,
|
|
277
|
+
controls = false,
|
|
278
|
+
pageSize = false,
|
|
279
|
+
pageSizeOptions = false,
|
|
280
|
+
currentPage = false,
|
|
281
|
+
enableSelection = false // Add enableSelection property
|
|
282
|
+
} = {}) {
|
|
283
|
+
let rerender = false;
|
|
284
|
+
let rerenderControls = false;
|
|
285
|
+
if(records){
|
|
286
|
+
this.records = records;
|
|
287
|
+
this.fields = fields || Table.extractFieldsFromRecords(this.records);
|
|
288
|
+
rerender = true;
|
|
289
|
+
}
|
|
290
|
+
if (pageSize) {
|
|
291
|
+
this.pageSize = pageSize;
|
|
292
|
+
rerender = true;
|
|
293
|
+
}
|
|
294
|
+
if (pageSizeOptions) {
|
|
295
|
+
this.pageSizeOptions = pageSizeOptions;
|
|
296
|
+
}
|
|
297
|
+
if (controls) {
|
|
298
|
+
this.controls = controls;
|
|
299
|
+
rerenderControls = true;
|
|
300
|
+
}
|
|
301
|
+
if(currentPage){
|
|
302
|
+
this.currentPage = currentPage;
|
|
303
|
+
rerender = true;
|
|
304
|
+
}
|
|
305
|
+
if(enableSelection !== undefined){
|
|
306
|
+
this.enableSelection = enableSelection;
|
|
307
|
+
rerender = true;
|
|
308
|
+
}
|
|
309
|
+
if(rerender){
|
|
310
|
+
this.renderFields();
|
|
311
|
+
this.renderRecords();
|
|
312
|
+
}
|
|
313
|
+
if(rerenderControls){
|
|
314
|
+
this.renderControls(this.controls.top, 'top');
|
|
315
|
+
this.renderControls(this.controls.bottom, 'bottom');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
setRecords(records) {
|
|
109
320
|
this.records = records;
|
|
110
|
-
this.
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
321
|
+
this.currentPage = 1;
|
|
322
|
+
this.renderRecords();
|
|
323
|
+
dispatchEvent(this, 'recordsSet', { records });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
setupFetchRecords(totalRecords, callback) {
|
|
327
|
+
const previousLength = this.records.length;
|
|
328
|
+
if (previousLength < totalRecords) {
|
|
329
|
+
this.records.length = totalRecords;
|
|
330
|
+
this.records.fill(null, previousLength);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
onEvent(this, 'fetchRecords', async (event) => {
|
|
334
|
+
const { start, end } = event.detail;
|
|
335
|
+
const records = await callback(start, end - start);
|
|
336
|
+
this.records.splice(start, records.length, ...records);
|
|
337
|
+
this.renderRecords();
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
addRecord(record) {
|
|
342
|
+
this.records.push(record);
|
|
343
|
+
this.renderRecords();
|
|
344
|
+
dispatchEvent(this, 'recordAdded', { record });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
updateRecord(index, newRecord) {
|
|
348
|
+
this.records[index] = newRecord;
|
|
349
|
+
const start = (this.currentPage - 1) * this.pageSize;
|
|
350
|
+
const end = start + this.pageSize;
|
|
351
|
+
if (index >= start && index < end) {
|
|
352
|
+
this.renderRecords();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
deleteRecord(index) {
|
|
357
|
+
if (index >= 0 && index < this.records.length) {
|
|
358
|
+
// Deselect the record before deleting
|
|
359
|
+
this.selectedIndexes = this.selectedIndexes.filter(i => i !== index);
|
|
360
|
+
dispatchEvent(this, 'selectionChange', { selectedIndexes: this.selectedIndexes });
|
|
361
|
+
this.records.splice(index, 1);
|
|
362
|
+
this.renderRecords();
|
|
363
|
+
dispatchEvent(this, 'recordDeleted', { index });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
getSelectedRecords() {
|
|
368
|
+
return this.selectedIndexes.map(index => this.records[index]);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
selectAllOnPage() {
|
|
372
|
+
const start = (this.currentPage - 1) * this.pageSize;
|
|
373
|
+
const end = Math.min(start + this.pageSize, this.records.length);
|
|
374
|
+
for (let i = start; i < end; i++) {
|
|
375
|
+
if (!this.selectedIndexes.includes(i)) {
|
|
376
|
+
this.selectedIndexes.push(i);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
114
379
|
this.renderRecords();
|
|
115
|
-
|
|
380
|
+
dispatchEvent(this, 'selectionChange', { selectedIndexes: this.selectedIndexes });
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
deselectAllOnPage() {
|
|
384
|
+
const start = (this.currentPage - 1) * this.pageSize;
|
|
385
|
+
const end = Math.min(start + this.pageSize, this.records.length);
|
|
386
|
+
this.selectedIndexes = this.selectedIndexes.filter(index => index < start || index >= end);
|
|
387
|
+
this.renderRecords();
|
|
388
|
+
dispatchEvent(this, 'selectionChange', { selectedIndexes: this.selectedIndexes });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
allOnPageSelected() {
|
|
392
|
+
const start = (this.currentPage - 1) * this.pageSize;
|
|
393
|
+
const end = Math.min(start + this.pageSize, this.records.length);
|
|
394
|
+
for (let i = start; i < end; i++) {
|
|
395
|
+
if (!this.selectedIndexes.includes(i)) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return true;
|
|
116
400
|
}
|
|
117
401
|
|
|
118
402
|
get shadowTemplate(){
|
|
@@ -120,7 +404,7 @@ export default class Table extends Component {
|
|
|
120
404
|
${super.shadowTemplate}
|
|
121
405
|
<div class="responsive-table">
|
|
122
406
|
<div id="top"></div>
|
|
123
|
-
<table>
|
|
407
|
+
<table class="b0">
|
|
124
408
|
<thead id="fields"></thead>
|
|
125
409
|
<tbody id="records"></tbody>
|
|
126
410
|
</table>
|
|
@@ -134,6 +418,44 @@ export default class Table extends Component {
|
|
|
134
418
|
:host {
|
|
135
419
|
display: block;
|
|
136
420
|
margin-bottom: var(--spacer);
|
|
421
|
+
border: 1px solid var(--c_border);
|
|
422
|
+
border-radius: var(--radius);
|
|
423
|
+
}
|
|
424
|
+
#top, #bottom {
|
|
425
|
+
display: flex;
|
|
426
|
+
}
|
|
427
|
+
th {
|
|
428
|
+
border-top: none;
|
|
429
|
+
}
|
|
430
|
+
tr:last-child td:first-child {
|
|
431
|
+
border-bottom-left-radius: 0;
|
|
432
|
+
}
|
|
433
|
+
tr:last-child td:last-child {
|
|
434
|
+
border-bottom-right-radius: 0;
|
|
435
|
+
}
|
|
436
|
+
tr:last-child td {
|
|
437
|
+
border-bottom: none;
|
|
438
|
+
}
|
|
439
|
+
th:first-child, td:first-child {
|
|
440
|
+
border-left: none;
|
|
441
|
+
}
|
|
442
|
+
th:last-child, td:last-child {
|
|
443
|
+
border-right: none;
|
|
444
|
+
}
|
|
445
|
+
#top:not(:empty) {
|
|
446
|
+
border-bottom: 1px solid var(--c_border);
|
|
447
|
+
}
|
|
448
|
+
#bottom:not(:empty) {
|
|
449
|
+
border-top: 1px solid var(--c_border);
|
|
450
|
+
}
|
|
451
|
+
th.selection, td.selection {
|
|
452
|
+
width: 50px;
|
|
453
|
+
}
|
|
454
|
+
th.selection input,
|
|
455
|
+
td.selection input {
|
|
456
|
+
margin: 0;
|
|
457
|
+
width: 1.35rem;
|
|
458
|
+
height: 1.35rem;
|
|
137
459
|
}
|
|
138
460
|
`;
|
|
139
461
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Component from '../Component.js';
|
|
2
|
+
import { onEvent } from '../../utils/element.js';
|
|
3
|
+
|
|
4
|
+
export default class DeleteSelected extends Component {
|
|
5
|
+
constructor(table) {
|
|
6
|
+
super();
|
|
7
|
+
this.table = table;
|
|
8
|
+
this.classList.add('mxq');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async render() {
|
|
12
|
+
if (await super.render()) {
|
|
13
|
+
this.updateButtonState();
|
|
14
|
+
const $button = this.shadowRoot.getElementById('deleteSelectedButton');
|
|
15
|
+
onEvent($button, 'click', () => this.deleteSelected());
|
|
16
|
+
onEvent(this.table, 'selectionChange', () => this.updateButtonState());
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
deleteSelected() {
|
|
23
|
+
const selectedIndexes = this.table.selectedIndexes;
|
|
24
|
+
selectedIndexes.sort((a, b) => b - a); // Sort in descending order
|
|
25
|
+
selectedIndexes.forEach(index => this.table.deleteRecord(index));
|
|
26
|
+
this.table.selectedIndexes = [];
|
|
27
|
+
this.table.renderRecords();
|
|
28
|
+
this.updateButtonState();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
updateButtonState() {
|
|
32
|
+
const $button = this.shadowRoot.getElementById('deleteSelectedButton');
|
|
33
|
+
$button.disabled = this.table.selectedIndexes.length === 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get shadowTemplate() {
|
|
37
|
+
return /*html*/`
|
|
38
|
+
<button id="deleteSelectedButton" class="pq">
|
|
39
|
+
<k-icon name="delete"></k-icon>
|
|
40
|
+
</button>
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get shadowStyles() {
|
|
45
|
+
return /*css*/`
|
|
46
|
+
:host {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
width: max-content;
|
|
49
|
+
align-items: baseline;
|
|
50
|
+
}
|
|
51
|
+
button {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
window.customElements.define('k-table-delete-selected', DeleteSelected);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import Component from '../Component.js';
|
|
2
|
+
import { onEvent } from '../../utils/element.js';
|
|
3
|
+
|
|
4
|
+
export default class PageSelect extends Component {
|
|
5
|
+
constructor(table) {
|
|
6
|
+
super();
|
|
7
|
+
this.table = table;
|
|
8
|
+
this.classList.add('mxq');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async render() {
|
|
12
|
+
if(await super.render()){
|
|
13
|
+
this.updateOptions();
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
updateOptions() {
|
|
20
|
+
const $select = this.shadowRoot.getElementById('pageSelect');
|
|
21
|
+
$select.innerHTML = '';
|
|
22
|
+
for (let i = 1; i <= this.table.getTotalPages(); i++) {
|
|
23
|
+
const $option = document.createElement('option');
|
|
24
|
+
$option.value = i;
|
|
25
|
+
$option.textContent = i;
|
|
26
|
+
$select.appendChild($option);
|
|
27
|
+
}
|
|
28
|
+
$select.value = this.table.getCurrentPage();
|
|
29
|
+
const $totalPages = this.shadowRoot.getElementById('totalPages');
|
|
30
|
+
$totalPages.textContent = this.table.getTotalPages();
|
|
31
|
+
|
|
32
|
+
onEvent($select, 'change', () => {
|
|
33
|
+
this.table.setPage(parseInt($select.value));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
onEvent(this.table, 'pageSizeChange', () => {
|
|
37
|
+
this.updateOptions();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
onEvent(this.table, 'pageChange', () => {
|
|
41
|
+
$select.value = this.table.getCurrentPage();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get shadowTemplate() {
|
|
46
|
+
return /*html*/`
|
|
47
|
+
<select id="pageSelect" class="mxq"></select>
|
|
48
|
+
<label> out of <span id="totalPages"></span></label>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get shadowStyles() {
|
|
53
|
+
return /*css*/`
|
|
54
|
+
:host {
|
|
55
|
+
display: inline-flex;
|
|
56
|
+
width: max-content;
|
|
57
|
+
align-items: baseline;
|
|
58
|
+
}
|
|
59
|
+
#pageSelect, label {
|
|
60
|
+
display: inline;
|
|
61
|
+
}
|
|
62
|
+
label {
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
window.customElements.define('k-table-page-select', PageSelect);
|