@es-labs/jslib 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +42 -0
- package/__test__/services.test.js +32 -0
- package/auth/index.js +226 -0
- package/auth/keyv.js +23 -0
- package/auth/knex.js +29 -0
- package/auth/redis.js +23 -0
- package/comms/email.js +123 -0
- package/comms/nexmo.js +44 -0
- package/comms/telegram.js +43 -0
- package/comms/telegram2/inbound.js +314 -0
- package/comms/telegram2/outbound.js +574 -0
- package/comms/webpush.js +60 -0
- package/config.js +37 -0
- package/express/controller/auth/oauth.js +39 -0
- package/express/controller/auth/oidc.js +87 -0
- package/express/controller/auth/own.js +100 -0
- package/express/controller/auth/saml.js +74 -0
- package/express/upload.js +48 -0
- package/index.js +1 -0
- package/iso/README.md +4 -0
- package/iso/__tests__/csv-utils.spec.js +128 -0
- package/iso/__tests__/datetime.spec.js +101 -0
- package/iso/__tests__/fetch.spec.js +270 -0
- package/iso/csv-utils.js +206 -0
- package/iso/datetime.js +103 -0
- package/iso/fetch.js +129 -0
- package/iso/fetch2.js +180 -0
- package/iso/log-filter.js +17 -0
- package/iso/sleep.js +6 -0
- package/iso/ws.js +63 -0
- package/node/oss-files/oss-uploader-client-fetch.js +258 -0
- package/node/oss-files/oss-uploader-client-fetch.md +31 -0
- package/node/oss-files/oss-uploader-client.js +219 -0
- package/node/oss-files/oss-uploader-server.js +199 -0
- package/node/oss-files/oss-uploader-usage.js +121 -0
- package/node/oss-files/oss-uploader-usage.md +34 -0
- package/node/oss-files/s3-uploader-client.js +217 -0
- package/node/oss-files/s3-uploader-server.js +123 -0
- package/node/oss-files/s3-uploader-usage.js +77 -0
- package/node/oss-files/s3-uploader-usage.md +34 -0
- package/package.json +53 -0
- package/packageInfo.js +9 -0
- package/services/ali.js +279 -0
- package/services/aws.js +194 -0
- package/services/db/__tests__/keyv.spec.js +31 -0
- package/services/db/keyv.js +14 -0
- package/services/db/knex.js +67 -0
- package/services/db/redis.js +51 -0
- package/services/index.js +57 -0
- package/services/mq/README.md +8 -0
- package/services/websocket.js +139 -0
- package/t4t/README.md +1 -0
- package/traps.js +20 -0
- package/utils/__tests__/aes.spec.js +52 -0
- package/utils/aes.js +23 -0
- package/web/UI.md +71 -0
- package/web/bwc-autocomplete.js +211 -0
- package/web/bwc-combobox.js +343 -0
- package/web/bwc-fileupload.js +87 -0
- package/web/bwc-loading-overlay.js +54 -0
- package/web/bwc-t4t-form.js +511 -0
- package/web/bwc-table.js +756 -0
- package/web/fetch.js +129 -0
- package/web/i18n.js +24 -0
- package/web/idle.js +49 -0
- package/web/parse-jwt.js +15 -0
- package/web/pwa.js +84 -0
- package/web/sign-pad.js +164 -0
- package/web/t4t-fe.js +164 -0
- package/web/util.js +126 -0
- package/web/web-cam.js +182 -0
package/web/bwc-table.js
ADDED
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
// TODO
|
|
2
|
+
// inline edit?
|
|
3
|
+
|
|
4
|
+
// FEATURES
|
|
5
|
+
// handle columns and items
|
|
6
|
+
// row select
|
|
7
|
+
// pagination (optional)
|
|
8
|
+
// filters (optional)
|
|
9
|
+
// sorter single column (optional)
|
|
10
|
+
// checkbox (optional)
|
|
11
|
+
// sticky header (optional)
|
|
12
|
+
// sticky coloumn (optional - currently only for 1st column)
|
|
13
|
+
// checkbox & check all (optional)
|
|
14
|
+
// custom render columns
|
|
15
|
+
|
|
16
|
+
// STYLING...
|
|
17
|
+
// --bwc-table-width: 100%
|
|
18
|
+
// --bwc-table-overflow: auto
|
|
19
|
+
// --bwc-table-height: 100%
|
|
20
|
+
// --bwc-table-navbar-bgcolor: white
|
|
21
|
+
// --bwc-table-filter-bgcolor: white
|
|
22
|
+
// --bwc-table-filter-color: black
|
|
23
|
+
// --bwc-table-filter-top: 56px
|
|
24
|
+
// --bwc-table-th-bgcolor: white
|
|
25
|
+
// --bwc-table-th-color: black
|
|
26
|
+
// --bwc-table-td-bgcolor: transparent
|
|
27
|
+
// --bwc-table-td-color: black
|
|
28
|
+
// --bwc-table-td-select-bgcolor: black
|
|
29
|
+
// --bwc-table-td-select-color: black
|
|
30
|
+
// --bwc-table-sticky-header-top: 56px
|
|
31
|
+
|
|
32
|
+
// PROPERTIES
|
|
33
|
+
// commands="reload,filter"
|
|
34
|
+
// :pagination="true"
|
|
35
|
+
// :sort="true"
|
|
36
|
+
// :page="page"
|
|
37
|
+
// :pageSize="pageSize"
|
|
38
|
+
// :pageSizeList="pageSizeList"
|
|
39
|
+
// :columns="columns"
|
|
40
|
+
// :items="table.items"
|
|
41
|
+
// :total="total"
|
|
42
|
+
// style="--bwc-table-height: calc(100vh - 360px);--bwc-table-width: 200%;"
|
|
43
|
+
// class="sticky-header sticky-column"
|
|
44
|
+
|
|
45
|
+
// TODO change some properties to attributes? handle multiple UI frameworks
|
|
46
|
+
|
|
47
|
+
// EVENTS
|
|
48
|
+
// rowclick { detail: { row, col, data }
|
|
49
|
+
// triggered = sort / page / page-size / reload { detail: { name, sortKey, sortDir, page, pageSize, filters: [ { key, op, val, andOr } ] } }
|
|
50
|
+
// cmd = show/hide filter, reload, add, del, import, export, goback (if parentKey != null)
|
|
51
|
+
// checked = [indexes checked...]
|
|
52
|
+
|
|
53
|
+
// COLUMN PROPERTIES
|
|
54
|
+
// for hidden table columns, please remove before passing it to component
|
|
55
|
+
// label: 'ID',
|
|
56
|
+
// key: 'id',
|
|
57
|
+
// filter: false,
|
|
58
|
+
// sort: false,
|
|
59
|
+
// render: ({val, key, row, idx}) => `<a class='button' onclick='this.dispatchEvent(new CustomEvent("testevent", { detail: ${JSON.stringify({ val, key, row, idx })} }))'>${val}</a>`
|
|
60
|
+
// cell value, column key, row data, row index (0-based)
|
|
61
|
+
// try not to include row property in event detail... can be too much data
|
|
62
|
+
|
|
63
|
+
// NOT NEEDED
|
|
64
|
+
// loading state and loading spinner
|
|
65
|
+
|
|
66
|
+
// NOTES
|
|
67
|
+
// do not use document.querySelector, use this.querySelector
|
|
68
|
+
|
|
69
|
+
const template = document.createElement('template')
|
|
70
|
+
template.innerHTML = /*html*/`
|
|
71
|
+
<style>
|
|
72
|
+
#table-wrapper {
|
|
73
|
+
overflow: var(--bwc-table-overflow, auto);
|
|
74
|
+
height: var(--bwc-table-height, 100%);
|
|
75
|
+
}
|
|
76
|
+
#table-wrapper table {
|
|
77
|
+
table-layout: initial;
|
|
78
|
+
width: var(--bwc-table-width, 100%);
|
|
79
|
+
}
|
|
80
|
+
#table-wrapper > nav {
|
|
81
|
+
position: -webkit-sticky;
|
|
82
|
+
position: sticky;
|
|
83
|
+
top: 0px;
|
|
84
|
+
left: 0px;
|
|
85
|
+
z-index: 2;
|
|
86
|
+
background-color: var(--bwc-table-navbar-bgcolor, lightslategray) !important;
|
|
87
|
+
}
|
|
88
|
+
#table-wrapper #filters {
|
|
89
|
+
position: -webkit-sticky;
|
|
90
|
+
position: sticky;
|
|
91
|
+
top: var(--bwc-table-filter-top, 56px);
|
|
92
|
+
left: 0px;
|
|
93
|
+
z-index: 2;
|
|
94
|
+
background-color: var(--bwc-table-filter-bgcolor, white);
|
|
95
|
+
color: var(--bwc-table-filter-color, black);
|
|
96
|
+
}
|
|
97
|
+
#table-wrapper th {
|
|
98
|
+
background-color: var(--bwc-table-th-bgcolor, white);
|
|
99
|
+
color: var(--bwc-table-th-color, black);
|
|
100
|
+
}
|
|
101
|
+
#table-wrapper tr td {
|
|
102
|
+
background-color: var(--bwc-table-td-bgcolor, transparent);
|
|
103
|
+
color: var(--bwc-table-td-color, black);
|
|
104
|
+
}
|
|
105
|
+
#table-wrapper tr.is-selected td {
|
|
106
|
+
background-color: var(--bwc-table-td-select-bgcolor, lightgrey);
|
|
107
|
+
color: var(--bwc-table-td-select-color, black);
|
|
108
|
+
}
|
|
109
|
+
.sticky-header #table-wrapper th {
|
|
110
|
+
position: -webkit-sticky;
|
|
111
|
+
position: sticky;
|
|
112
|
+
top: var(--bwc-table-sticky-header-top, 56px); /* nav height - TODO filter height*/
|
|
113
|
+
z-index: 2;
|
|
114
|
+
}
|
|
115
|
+
.sticky-column #table-wrapper th[scope=row] {
|
|
116
|
+
position: -webkit-sticky;
|
|
117
|
+
position: sticky;
|
|
118
|
+
left: 0;
|
|
119
|
+
z-index: 3;
|
|
120
|
+
}
|
|
121
|
+
.sticky-column #table-wrapper th:not([scope=row]) {
|
|
122
|
+
}
|
|
123
|
+
.sticky-column #table-wrapper td[scope=row] {
|
|
124
|
+
position: -webkit-sticky;
|
|
125
|
+
position: sticky;
|
|
126
|
+
left: 0;
|
|
127
|
+
z-index: 1;
|
|
128
|
+
}
|
|
129
|
+
input::-webkit-outer-spin-button, /* to remove up and down arrows */
|
|
130
|
+
input::-webkit-inner-spin-button {
|
|
131
|
+
-webkit-appearance: none;
|
|
132
|
+
margin: 0;
|
|
133
|
+
}
|
|
134
|
+
input[type="number"] {
|
|
135
|
+
-moz-appearance: textfield;
|
|
136
|
+
}
|
|
137
|
+
</style>
|
|
138
|
+
<div id="table-wrapper">
|
|
139
|
+
<nav id="table-navbar" class="navbar" role="navigation" aria-label="main navigation">
|
|
140
|
+
<div class="navbar-brand">
|
|
141
|
+
<a id="table-navbar-burger" role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="table-navbar-menu">
|
|
142
|
+
<span aria-hidden="true"></span>
|
|
143
|
+
<span aria-hidden="true"></span>
|
|
144
|
+
<span aria-hidden="true"></span>
|
|
145
|
+
</a>
|
|
146
|
+
</div>
|
|
147
|
+
<div id="table-navbar-menu" class="navbar-menu">
|
|
148
|
+
<div class="navbar-start">
|
|
149
|
+
<div id="commands" class="navbar-item">
|
|
150
|
+
<a id="cmd-goback" class="button">↶</a>
|
|
151
|
+
<a id="cmd-filter" class="button">o</a><!-- need to make this configurable -->
|
|
152
|
+
<a id="cmd-reload" class="button">↻</a>
|
|
153
|
+
<a id="cmd-add" class="button">+</a>
|
|
154
|
+
<a id="cmd-del" class="button">-</a>
|
|
155
|
+
<a id="cmd-import" class="button">↑</a>
|
|
156
|
+
<a id="cmd-export" class="button">↓</a>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div class="navbar-end pagination">
|
|
161
|
+
<div class="navbar-item">
|
|
162
|
+
<a id="page-dec" class="button"><</a>
|
|
163
|
+
<a><input id="page-input" class="input" type="number" min="1" style="width: auto;"/></a>
|
|
164
|
+
<a class="button is-static"> / <span id="pages-span"></span></a>
|
|
165
|
+
<a id="page-inc" class="button">></a>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="navbar-item">
|
|
168
|
+
<a>
|
|
169
|
+
<span class="select">
|
|
170
|
+
<select id="page-select">
|
|
171
|
+
</select>
|
|
172
|
+
</span>
|
|
173
|
+
</a>
|
|
174
|
+
<a class="button is-static">Rows/Page</a>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</nav>
|
|
179
|
+
<div id="filters"></div>
|
|
180
|
+
</div>
|
|
181
|
+
`
|
|
182
|
+
|
|
183
|
+
class Table extends HTMLElement {
|
|
184
|
+
// basic
|
|
185
|
+
#columns = []
|
|
186
|
+
#items = []
|
|
187
|
+
|
|
188
|
+
// enable pagination
|
|
189
|
+
#pagination = true
|
|
190
|
+
#page = 1 // one based index
|
|
191
|
+
#pageSize = 10
|
|
192
|
+
#pageSizeList = [5, 10, 15]
|
|
193
|
+
#pages = 0 // computed Math.ceil(total / pageSize)
|
|
194
|
+
#total = 0
|
|
195
|
+
|
|
196
|
+
// enable sorting
|
|
197
|
+
#sort = true
|
|
198
|
+
#sortKey = ''
|
|
199
|
+
#sortDir = '' // blank, asc, desc
|
|
200
|
+
|
|
201
|
+
// checkbox
|
|
202
|
+
#checkboxes = true
|
|
203
|
+
#checkedRows = []
|
|
204
|
+
|
|
205
|
+
// selected
|
|
206
|
+
#selectedIndex = -1
|
|
207
|
+
#selectedNode = null
|
|
208
|
+
#selectedItem = null
|
|
209
|
+
|
|
210
|
+
// enable commands menu
|
|
211
|
+
#commands = ''
|
|
212
|
+
|
|
213
|
+
// filters
|
|
214
|
+
#filters = []
|
|
215
|
+
#filterCols = []
|
|
216
|
+
#filterOps = ['=', 'like', '!=', '>=', '>', '<=', '<']
|
|
217
|
+
#filterShow = false
|
|
218
|
+
|
|
219
|
+
// heights
|
|
220
|
+
#navbarHeight = 56 // #table-navbar
|
|
221
|
+
#filterHeight = 0 // #filters
|
|
222
|
+
|
|
223
|
+
constructor() {
|
|
224
|
+
super()
|
|
225
|
+
// this.input = this.input.bind(this)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_setHeights () {
|
|
229
|
+
// console.log(this.#navbarHeight, this.#filterHeight)
|
|
230
|
+
const el = this.querySelector('#filters')
|
|
231
|
+
if (!el) return
|
|
232
|
+
el.style.top = `${this.#navbarHeight}px`
|
|
233
|
+
const nodes = this.querySelectorAll('.sticky-header #table-wrapper th')
|
|
234
|
+
for (let i = 0; i<nodes.length; i++) {
|
|
235
|
+
// console.log('nodes', nodes[i])
|
|
236
|
+
nodes[i].style.top = `${this.#navbarHeight + this.#filterHeight}px`
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
_eventPageInputEL(e) {
|
|
241
|
+
const page = Number(e.target.value)
|
|
242
|
+
if (page >= 1 && page <= this.#pages && Number(page) !== Number(this.page)) {
|
|
243
|
+
this.page = page
|
|
244
|
+
this._trigger('page')
|
|
245
|
+
} else {
|
|
246
|
+
this._renderPageInput()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
connectedCallback() {
|
|
251
|
+
console.log('connected callback')
|
|
252
|
+
|
|
253
|
+
// console.log(this.value, this.required, typeof this.required)
|
|
254
|
+
this.appendChild(template.content.cloneNode(true))
|
|
255
|
+
|
|
256
|
+
// this.querySelector('input').addEventListener('input', this.input)
|
|
257
|
+
// if (this.required !== null) el.setAttribute('required', '')
|
|
258
|
+
|
|
259
|
+
// Check for click events on the navbar burger icon
|
|
260
|
+
this.querySelector('.navbar-burger').onclick = () => {
|
|
261
|
+
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
|
262
|
+
this.querySelector('#table-navbar-burger').classList.toggle('is-active') // navbar-burger
|
|
263
|
+
this.querySelector('#table-navbar-menu').classList.toggle('is-active') // navbar-menu
|
|
264
|
+
}
|
|
265
|
+
this.querySelector('#page-input').onkeypress = (e) => {
|
|
266
|
+
e.code === 'Enter' && this._eventPageInputEL(e)
|
|
267
|
+
}
|
|
268
|
+
this.querySelector('#page-input').onblur = (e) => {
|
|
269
|
+
this._eventPageInputEL(e)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
this.querySelector('#cmd-filter').onclick = () => {
|
|
273
|
+
this.#filterShow = !this.#filterShow
|
|
274
|
+
this.querySelector('#filters').style.display = this.#filterShow ? 'block': 'none'
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
new ResizeObserver(entries => {
|
|
278
|
+
this.#navbarHeight = entries[0].target.clientHeight
|
|
279
|
+
this._setHeights()
|
|
280
|
+
}).observe(this.querySelector('#table-navbar'))
|
|
281
|
+
|
|
282
|
+
new ResizeObserver(entries => {
|
|
283
|
+
this.#filterHeight = entries[0].target.clientHeight
|
|
284
|
+
this._setHeights()
|
|
285
|
+
}).observe(this.querySelector('#filters')) // start observing a DOM node
|
|
286
|
+
|
|
287
|
+
this.querySelector('#cmd-reload').onclick = () => this._trigger('reload')
|
|
288
|
+
this.querySelector('#cmd-goback').onclick = () => this.dispatchEvent(new CustomEvent('cmd', { detail: { cmd: 'goback' } }))
|
|
289
|
+
this.querySelector('#cmd-add').onclick = () => this.dispatchEvent(new CustomEvent('cmd', { detail: { cmd: 'add' } }))
|
|
290
|
+
this.querySelector('#cmd-del').onclick = () => this.dispatchEvent(new CustomEvent('cmd', { detail: { cmd: 'del', checkedRows: this.#checkedRows } }))
|
|
291
|
+
this.querySelector('#cmd-import').onclick = () => this.dispatchEvent(new CustomEvent('cmd', { detail: { cmd: 'import' } }))
|
|
292
|
+
this.querySelector('#cmd-export').onclick = () => this.dispatchEvent(new CustomEvent('cmd', { detail: { cmd: 'export', checkedRows: this.#checkedRows } }))
|
|
293
|
+
this.querySelector('#page-dec').onclick = (e) => {
|
|
294
|
+
let numPage = Number(this.page)
|
|
295
|
+
if (numPage > 1 && numPage <= this.#pages) {
|
|
296
|
+
numPage -= 1
|
|
297
|
+
this.page = numPage
|
|
298
|
+
this._trigger('page')
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
this.querySelector('#page-inc').onclick = (e) => {
|
|
302
|
+
// console.log('inc page', this.page, this.#pages)
|
|
303
|
+
let numPage = Number(this.page)
|
|
304
|
+
if (numPage < this.#pages) {
|
|
305
|
+
numPage += 1
|
|
306
|
+
this.page = numPage
|
|
307
|
+
this._trigger('page')
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
this.querySelector('#page-select').onchange = (e) => {
|
|
311
|
+
this.pageSize = e.target.value
|
|
312
|
+
this._trigger('page-size')
|
|
313
|
+
if (this.page > this.#pages){
|
|
314
|
+
this.page = this.#pages
|
|
315
|
+
this._trigger('page-size')
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// console.log('connectedCallback 0')
|
|
320
|
+
|
|
321
|
+
// initialize non-required properties that are undefined
|
|
322
|
+
if (!this.#sortKey) this.#sortKey = ''
|
|
323
|
+
if (!this.#sortDir) this.#sortDir = ''
|
|
324
|
+
|
|
325
|
+
this.querySelector('#filters').style.display = this.#filterShow ? 'block': 'none'
|
|
326
|
+
if (!this.#pagination) this.querySelector('.pagination').style.display = 'none'
|
|
327
|
+
if (!this.#commands || typeof this.#commands !== 'string') {
|
|
328
|
+
this.querySelector('#commands').style.display = 'none'
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
this.querySelector('#cmd-reload').style.display = this.#commands.includes('reload') ? 'block' : 'none'
|
|
332
|
+
this.querySelector('#cmd-filter').style.display = this.#commands.includes('filter') ? 'block' : 'none'
|
|
333
|
+
this.querySelector('#cmd-add').style.display = this.#commands.includes('add') ? 'block' : 'none'
|
|
334
|
+
this.querySelector('#cmd-del').style.display = this.#commands.includes('del') ? 'block' : 'none'
|
|
335
|
+
this.querySelector('#cmd-import').style.display = this.#commands.includes('import') ? 'block' : 'none'
|
|
336
|
+
this.querySelector('#cmd-export').style.display = this.#commands.includes('export') ? 'block' : 'none'
|
|
337
|
+
this.querySelector('#cmd-goback').style.display = this.#commands.includes('goback') ? 'block' : 'none'
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
this._render()
|
|
341
|
+
this._renderPageSelect()
|
|
342
|
+
this._renderPageInput()
|
|
343
|
+
this._renderPages()
|
|
344
|
+
this._renderFilters()
|
|
345
|
+
|
|
346
|
+
// console.log('connectedCallback 1')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
disconnectedCallback() {
|
|
350
|
+
// this.querySelector('input').removeEventListener('input', this.input)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// attributeChangedCallback(name, oldVal, newVal) {
|
|
354
|
+
// switch (name) {
|
|
355
|
+
// case 'page': { break }
|
|
356
|
+
// }
|
|
357
|
+
// }
|
|
358
|
+
// static get observedAttributes() {
|
|
359
|
+
// return ['page']
|
|
360
|
+
// }
|
|
361
|
+
|
|
362
|
+
get checkboxes () { return this.#checkboxes }
|
|
363
|
+
set checkboxes (val) { this.#checkboxes = val }
|
|
364
|
+
get pagination () { return this.#pagination }
|
|
365
|
+
set pagination (val) { this.#pagination = val }
|
|
366
|
+
get commands () { return this.#commands }
|
|
367
|
+
set commands (val) { this.#commands = val }
|
|
368
|
+
get sort () { return this.#sort }
|
|
369
|
+
set sort (val) { this.#sort = val }
|
|
370
|
+
|
|
371
|
+
get page () { return this.#page }
|
|
372
|
+
set page (val) { this.#page = val } // DONE ELSEWHERE emit event
|
|
373
|
+
|
|
374
|
+
get pageSize () { return this.#pageSize }
|
|
375
|
+
set pageSize (val) {
|
|
376
|
+
console.log('set pageSize', this.total , this.pageSize)
|
|
377
|
+
this.#pageSize = val
|
|
378
|
+
this._renderPages()
|
|
379
|
+
} // DONE ELSEWHERE emit event
|
|
380
|
+
|
|
381
|
+
get pageSizeList () { return this.#pageSizeList }
|
|
382
|
+
set pageSizeList (val) { this.#pageSizeList = val } // TODO emit event
|
|
383
|
+
get items() { return this.#items }
|
|
384
|
+
set items(val) {
|
|
385
|
+
// console.log('set items')
|
|
386
|
+
this.#items = val
|
|
387
|
+
this._render()
|
|
388
|
+
this._renderPageSelect()
|
|
389
|
+
this._renderPageInput()
|
|
390
|
+
this._renderPages()
|
|
391
|
+
} // if columns do something
|
|
392
|
+
|
|
393
|
+
get total () { return this.#total }
|
|
394
|
+
set total (val) {
|
|
395
|
+
this.#total = val
|
|
396
|
+
this._renderPages()
|
|
397
|
+
} // emit event ?
|
|
398
|
+
|
|
399
|
+
get selectedItem () { return this.#selectedItem }
|
|
400
|
+
set selectedItem (val) { this.#selectedItem = val }
|
|
401
|
+
get columns() { return this.#columns }
|
|
402
|
+
set columns(val) {
|
|
403
|
+
this.#columns = val
|
|
404
|
+
this._render()
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
_renderPages () {
|
|
408
|
+
this.#pages = Math.ceil(this.total / this.pageSize)
|
|
409
|
+
const el = this.querySelector('#pages-span')
|
|
410
|
+
if (el) el.textContent = this.#pages
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
_renderPageSelect () {
|
|
414
|
+
const el = this.querySelector('#page-select')
|
|
415
|
+
if (!el) return
|
|
416
|
+
el.textContent = '' // remove all children
|
|
417
|
+
this.pageSizeList.forEach(item => {
|
|
418
|
+
const option = document.createElement('option')
|
|
419
|
+
option.value = item
|
|
420
|
+
option.textContent = item
|
|
421
|
+
if (Number(item) === Number(this.pageSize)) option.selected = true
|
|
422
|
+
el.appendChild(option)
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
_renderPageInput () {
|
|
427
|
+
const el = this.querySelector('#page-input')
|
|
428
|
+
if (!el) return
|
|
429
|
+
el.value = this.page
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
_createSelect (items, filter, prop) {
|
|
433
|
+
const p = document.createElement('p')
|
|
434
|
+
p.classList.add('control', 'm-0')
|
|
435
|
+
const span = document.createElement('span')
|
|
436
|
+
span.classList.add('select')
|
|
437
|
+
const select = document.createElement('select')
|
|
438
|
+
items.forEach(item => {
|
|
439
|
+
const option = document.createElement('option')
|
|
440
|
+
if (item.key) {
|
|
441
|
+
option.textContent = item.label
|
|
442
|
+
option.value = item.key
|
|
443
|
+
} else {
|
|
444
|
+
option.textContent = item
|
|
445
|
+
option.value = item
|
|
446
|
+
}
|
|
447
|
+
select.appendChild(option)
|
|
448
|
+
})
|
|
449
|
+
select.value = filter[prop]
|
|
450
|
+
select.onchange = e => filter[prop] = e.target.value
|
|
451
|
+
span.appendChild(select)
|
|
452
|
+
p.appendChild(span)
|
|
453
|
+
return p
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
_renderFilters () {
|
|
457
|
+
const el = this.querySelector('#filters')
|
|
458
|
+
el.textContent = ''
|
|
459
|
+
if (this.#filters.length) {
|
|
460
|
+
for (let i=0; i < this.#filters.length; i++) {
|
|
461
|
+
const filter = this.#filters[i]
|
|
462
|
+
const div = document.createElement('div')
|
|
463
|
+
div.classList.add('field', 'has-addons', 'm-0', 'p-1')
|
|
464
|
+
|
|
465
|
+
div.appendChild( this._createSelect (this.#filterCols, filter, 'key') ) // TODO set input type and pattern based on column UI change event
|
|
466
|
+
div.appendChild( this._createSelect (this.#filterOps, filter, 'op') )
|
|
467
|
+
|
|
468
|
+
const p = document.createElement('p')
|
|
469
|
+
p.classList.add('control', 'm-0')
|
|
470
|
+
const filterInput = document.createElement('input')
|
|
471
|
+
filterInput.classList.add('input')
|
|
472
|
+
filterInput.value = filter.val
|
|
473
|
+
filterInput.oninput = e => filter.val = e.target.value // so that we can keep the filter value
|
|
474
|
+
p.appendChild(filterInput)
|
|
475
|
+
div.appendChild(p)
|
|
476
|
+
|
|
477
|
+
const pf = document.createElement('p')
|
|
478
|
+
pf.classList.add('control', 'm-0')
|
|
479
|
+
pf.innerHTML = `<span class="select">
|
|
480
|
+
<select id="filter-and-or">
|
|
481
|
+
<option value="and">And</option>
|
|
482
|
+
<option value="or">Or</option>
|
|
483
|
+
</select>
|
|
484
|
+
</span>`
|
|
485
|
+
pf.querySelector('#filter-and-or').value = filter.andOr
|
|
486
|
+
pf.querySelector('#filter-and-or').onchange = e => filter.andOr = e.target.value
|
|
487
|
+
div.appendChild(pf)
|
|
488
|
+
|
|
489
|
+
const p1 = document.createElement('p')
|
|
490
|
+
p1.classList.add('control', 'm-0')
|
|
491
|
+
const delBtn = document.createElement('button')
|
|
492
|
+
delBtn.classList.add('button')
|
|
493
|
+
delBtn.textContent = '-'
|
|
494
|
+
delBtn.onclick = () => this._delFilter(i)
|
|
495
|
+
p1.appendChild(delBtn)
|
|
496
|
+
div.appendChild(p1)
|
|
497
|
+
|
|
498
|
+
const p2 = document.createElement('p')
|
|
499
|
+
p2.classList.add('control', 'm-0')
|
|
500
|
+
const addBtn = document.createElement('button')
|
|
501
|
+
addBtn.classList.add('button')
|
|
502
|
+
addBtn.textContent = '+'
|
|
503
|
+
addBtn.onclick = () => this._addFilter(i + 1)
|
|
504
|
+
p2.appendChild(addBtn)
|
|
505
|
+
div.appendChild(p2)
|
|
506
|
+
|
|
507
|
+
el.appendChild(div)
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
const div = document.createElement('div')
|
|
511
|
+
div.classList.add('field', 'p-1')
|
|
512
|
+
const p = document.createElement('p')
|
|
513
|
+
p.classList.add('control')
|
|
514
|
+
const btn = document.createElement('button')
|
|
515
|
+
btn.classList.add('button')
|
|
516
|
+
btn.textContent = '+'
|
|
517
|
+
btn.onclick = () => this._addFilter(0)
|
|
518
|
+
p.appendChild(btn)
|
|
519
|
+
div.appendChild(p)
|
|
520
|
+
el.appendChild(div)
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
_trigger (name) {
|
|
525
|
+
const filters = []
|
|
526
|
+
const el = this.querySelector('#filters')
|
|
527
|
+
for (let i=0; i<el.children.length; i++) {
|
|
528
|
+
const div = el.children[i]
|
|
529
|
+
if (div.children.length >= 4) {
|
|
530
|
+
filters.push({
|
|
531
|
+
key: div.children[0].querySelector('select').value,
|
|
532
|
+
op: div.children[1].querySelector('select').value,
|
|
533
|
+
val: div.children[2].querySelector('input').value,
|
|
534
|
+
andOr: div.children[3].querySelector('select').value
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
this.dispatchEvent(new CustomEvent('triggered', {
|
|
539
|
+
// get filter information
|
|
540
|
+
detail: {
|
|
541
|
+
name, // page, sort
|
|
542
|
+
sortKey: this.#sortKey,
|
|
543
|
+
sortDir: this.#sortDir,
|
|
544
|
+
page: this.page || 0,
|
|
545
|
+
pageSize: this.pageSize || 0,
|
|
546
|
+
filters
|
|
547
|
+
}
|
|
548
|
+
}))
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// filters
|
|
552
|
+
_delFilter (index) {
|
|
553
|
+
this.#filters.splice(index, 1) // console.log('remove filter', index)
|
|
554
|
+
this._renderFilters()
|
|
555
|
+
}
|
|
556
|
+
_addFilter (index) {
|
|
557
|
+
this.#filters.splice(index, 0, { key: this.#filterCols[0].key, label: this.#filterCols[0].label, op: this.#filterOps[0], val: '', andOr: 'and' })
|
|
558
|
+
this._renderFilters()
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
_render() {
|
|
562
|
+
// console.log('bwc-table render fired')
|
|
563
|
+
try {
|
|
564
|
+
const el = this.querySelector('#table-wrapper')
|
|
565
|
+
if (!el) return
|
|
566
|
+
//<tfoot><tr><th><abbr title="Position">Pos</abbr></th>
|
|
567
|
+
|
|
568
|
+
let table = el.querySelector('table')
|
|
569
|
+
if (table) {
|
|
570
|
+
// const cNode = table.cloneNode(false)
|
|
571
|
+
// table.parentNode.replaceChild(cNode, table)
|
|
572
|
+
// table.innerHTML = ''
|
|
573
|
+
const parent = el.querySelector('table') // WORKS!
|
|
574
|
+
while (parent.firstChild) {
|
|
575
|
+
parent.firstChild.remove()
|
|
576
|
+
}
|
|
577
|
+
parent.remove()
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (this.#columns && typeof this.#columns === 'object') {
|
|
581
|
+
// console.log('render thead')
|
|
582
|
+
table = document.createElement('table')
|
|
583
|
+
table.setAttribute('id', 'table')
|
|
584
|
+
el.appendChild(table)
|
|
585
|
+
const thead = document.createElement('thead')
|
|
586
|
+
thead.onclick = (e) => {
|
|
587
|
+
let target = e.target
|
|
588
|
+
if (this.#checkboxes && !target.cellIndex) { // checkbox clicked - target.type === 'checkbox' // e.stopPropagation()?
|
|
589
|
+
this.#checkedRows = [] // clear first
|
|
590
|
+
const tbody = this.querySelector('table tbody')
|
|
591
|
+
if (tbody && tbody.children) {
|
|
592
|
+
for (let i = 0; i < tbody.children.length; i++) {
|
|
593
|
+
const tr = tbody.children[i]
|
|
594
|
+
const td = tr.firstChild
|
|
595
|
+
if (td) {
|
|
596
|
+
const checkbox = td.firstChild
|
|
597
|
+
if (checkbox.type === 'checkbox') {
|
|
598
|
+
checkbox.checked = target.checked
|
|
599
|
+
if (target.checked) this.#checkedRows.push(i)
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
this.dispatchEvent(new CustomEvent('checked', { detail: this.#checkedRows }))
|
|
605
|
+
} else { // sort
|
|
606
|
+
if (!this.sort) return
|
|
607
|
+
const offset = this.#checkboxes ? 1 : 0 // column offset
|
|
608
|
+
const col = target.cellIndex - offset // TD 0-index based column
|
|
609
|
+
if (!this.#columns[col].sort) return
|
|
610
|
+
const key = this.#columns[col].key
|
|
611
|
+
|
|
612
|
+
if (key !== this.#sortKey) {
|
|
613
|
+
this.#sortKey = key
|
|
614
|
+
this.#sortDir = 'asc'
|
|
615
|
+
} else {
|
|
616
|
+
if (this.#sortDir === 'asc') {
|
|
617
|
+
this.#sortDir = 'desc'
|
|
618
|
+
} else if (this.#sortDir === 'desc') {
|
|
619
|
+
this.#sortKey = ''
|
|
620
|
+
this.#sortDir = ''
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
this._trigger('sort') // header is re-rendered, checkboxes are also cleared...
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
table.appendChild(thead)
|
|
628
|
+
table.classList.add('table')
|
|
629
|
+
const tr = document.createElement('tr')
|
|
630
|
+
thead.appendChild(tr)
|
|
631
|
+
if (this.#checkboxes) { // check all
|
|
632
|
+
const th = document.createElement('th')
|
|
633
|
+
th.style.width = '50px' // TODO do not hardcode
|
|
634
|
+
const checkbox = document.createElement('input')
|
|
635
|
+
checkbox.type = 'checkbox' // value
|
|
636
|
+
th.setAttribute('scope', 'row')
|
|
637
|
+
th.appendChild(checkbox)
|
|
638
|
+
tr.appendChild(th)
|
|
639
|
+
}
|
|
640
|
+
this.#filterCols = [] // clear this first
|
|
641
|
+
for (const col of this.#columns) {
|
|
642
|
+
const th = document.createElement('th')
|
|
643
|
+
if (col.sort) th.style.cursor = 'pointer'
|
|
644
|
+
let label = col.label
|
|
645
|
+
if (col.sort) {
|
|
646
|
+
if (this.#sortKey === col.key) {
|
|
647
|
+
// ∧ (up) & ∨ (down)
|
|
648
|
+
label += this.#sortDir === 'asc' ? '↑' : (this.#sortDir === 'desc' ? '↓' : '↕')
|
|
649
|
+
} else {
|
|
650
|
+
label += '↕'
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (col.width) th.style.width = `${col.width}px`
|
|
654
|
+
if (col.sticky) th.setAttribute('scope', 'row')
|
|
655
|
+
|
|
656
|
+
th.appendChild(document.createTextNode(label))
|
|
657
|
+
tr.appendChild(th)
|
|
658
|
+
|
|
659
|
+
// set filters...
|
|
660
|
+
if (col.filter) this.#filterCols.push({
|
|
661
|
+
key: col.key,
|
|
662
|
+
label: col.label
|
|
663
|
+
}) // process filters (col is key)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// populate the data
|
|
667
|
+
if (this.#items && typeof this.#items === 'object' && this.#items.length) {
|
|
668
|
+
// console.log('render tbody')
|
|
669
|
+
const tbody = document.createElement('tbody')
|
|
670
|
+
// TODO function to get checked rows...
|
|
671
|
+
tbody.onclick = (e) => {
|
|
672
|
+
let target = e.target
|
|
673
|
+
if (this.#checkboxes && !target.cellIndex) { // checkbox clicked - target.type === 'checkbox' // e.stopPropagation()?
|
|
674
|
+
if (target.type === 'checkbox') {
|
|
675
|
+
this.#checkedRows = [] // clear first
|
|
676
|
+
for (let i = 0; i < tbody.children.length; i++) {
|
|
677
|
+
const tr = tbody.children[i]
|
|
678
|
+
const td = tr.firstChild
|
|
679
|
+
if (td) {
|
|
680
|
+
const checkbox = td.firstChild
|
|
681
|
+
if (checkbox.type === 'checkbox' && checkbox.checked) {
|
|
682
|
+
this.#checkedRows.push(i)
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
this.dispatchEvent(new CustomEvent('checked', { detail: this.#checkedRows }))
|
|
687
|
+
}
|
|
688
|
+
} else {
|
|
689
|
+
const offset = this.#checkboxes ? 1 : 0 // column offset
|
|
690
|
+
const col = target.cellIndex - offset // TD 0-index based column
|
|
691
|
+
|
|
692
|
+
while (target && target.nodeName !== "TR") {
|
|
693
|
+
target = target.parentNode
|
|
694
|
+
}
|
|
695
|
+
const row = target.rowIndex - 1 // TR 1-index based row
|
|
696
|
+
let data = null
|
|
697
|
+
if (target) { // TODO - To handle multiple UI frameworks
|
|
698
|
+
if (this.#selectedNode) { // clear class is-selected
|
|
699
|
+
this.#selectedNode.classList.remove('is-selected')
|
|
700
|
+
}
|
|
701
|
+
if (this.#selectedIndex === row && this.#selectedIndex !== -1) { // unselect
|
|
702
|
+
this.#selectedIndex = -1
|
|
703
|
+
this.selectedItem = null
|
|
704
|
+
} else {
|
|
705
|
+
data = { ...this.#items[row] }
|
|
706
|
+
this.#selectedNode = target // set selected
|
|
707
|
+
this.#selectedIndex = row
|
|
708
|
+
this.selectedItem = { row, col, data }
|
|
709
|
+
target.classList.add('is-selected')
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
this.dispatchEvent(new CustomEvent('rowclick', { detail: { row, col, data } }))
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
table.appendChild(tbody)
|
|
717
|
+
for (const [idx, row] of this.#items.entries()) {
|
|
718
|
+
const tr = document.createElement('tr')
|
|
719
|
+
tbody.appendChild(tr)
|
|
720
|
+
|
|
721
|
+
if (this.#checkboxes) { // add checkbox
|
|
722
|
+
const td = document.createElement('td')
|
|
723
|
+
const checkbox = document.createElement('input')
|
|
724
|
+
checkbox.type = 'checkbox' // value
|
|
725
|
+
td.setAttribute('scope', 'row')
|
|
726
|
+
td.appendChild(checkbox)
|
|
727
|
+
tr.appendChild(td)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
for (const col of this.#columns) {
|
|
731
|
+
const { key, sticky, width, render } = col
|
|
732
|
+
const td = document.createElement('td')
|
|
733
|
+
// if (sticky) td.setAttribute('scope', 'row') // not used yet, need to calculate left property value
|
|
734
|
+
if (width) td.style.width = `${width}px`
|
|
735
|
+
if (render) {
|
|
736
|
+
td.innerHTML = render({
|
|
737
|
+
val: row[key],
|
|
738
|
+
key,
|
|
739
|
+
row,
|
|
740
|
+
idx
|
|
741
|
+
}) // value, key, row - need to sanitize, el (the td element)
|
|
742
|
+
} else {
|
|
743
|
+
td.appendChild(document.createTextNode(row[key]))
|
|
744
|
+
}
|
|
745
|
+
tr.appendChild(td)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
} catch (e) {
|
|
751
|
+
console.log(e)
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
customElements.define('bwc-table', Table)
|