tablevu 0.3.0-beta.2 → 0.3.0-beta.4

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.
@@ -1,551 +0,0 @@
1
- import { h, reactive, watch, onMounted, ref } from 'vue';
2
-
3
- function adaptArray(props, data) {
4
- const d = Array.isArray(data) ? [...data] : [];
5
- return new Promise((resolve, reject) => {
6
- try {
7
- // Filter & Search //
8
- if (props.filter || props.search) {
9
- const searchableCols = props.cols
10
- ?.map((m, i) => ({ index: i, searchable: m.searchable !== false }))
11
- .filter(q => q.searchable)
12
- .map(m => m.index) || [];
13
- let filtered = d;
14
- if (props.filter) {
15
- filtered = filtered.filter(row => {
16
- return Object.entries(props.filter).every(([key, value]) => {
17
- return row[key] === value;
18
- });
19
- });
20
- }
21
- if (props.search && searchableCols.length > 0) {
22
- const searchLower = props.search.toLowerCase();
23
- filtered = filtered.filter(row => {
24
- return searchableCols.some(colIndex => {
25
- const col = props.cols?.[colIndex];
26
- if (!col)
27
- return false;
28
- const val = row[col.name];
29
- return val && String(val).toLowerCase().includes(searchLower);
30
- });
31
- });
32
- }
33
- // Update d with filtered results
34
- d.length = 0;
35
- d.push(...filtered);
36
- }
37
- // Sort //
38
- if (props.sort && props.sort.length > 0) {
39
- for (let i = props.sort.length - 1; i >= 0; i--) {
40
- const s = props.sort[i];
41
- let field;
42
- if (s.index !== undefined && s.index >= 0 && props.cols?.[s.index]) {
43
- field = props.cols[s.index].name;
44
- }
45
- else {
46
- field = s.name || '';
47
- }
48
- if (!field)
49
- continue;
50
- const dir = s.dir === 'desc' ? -1 : 1;
51
- d.sort((a, b) => {
52
- const av = a[field];
53
- const bv = b[field];
54
- if (av === bv)
55
- return 0;
56
- if (av === null || av === undefined)
57
- return 1;
58
- if (bv === null || bv === undefined)
59
- return -1;
60
- return (av > bv ? 1 : -1) * dir;
61
- });
62
- }
63
- }
64
- // Paging //
65
- let result = d;
66
- if (props.pageSize && props.pageSize > 0) {
67
- const start = (props.currPage - 1) * props.pageSize;
68
- const end = props.currPage * props.pageSize;
69
- result = d.slice(start, end);
70
- }
71
- resolve({ rows: result, count: d.length });
72
- }
73
- catch (err) {
74
- reject(err);
75
- }
76
- });
77
- }
78
-
79
- const TablevuStatus = {
80
- Error: -1,
81
- Ready: 0,
82
- Loading: 1
83
- };
84
- const tablevu = {
85
- props: {
86
- settings: { type: Object, default: null },
87
- data: { type: [Array, Function] },
88
- group: { type: [String, Object] },
89
- search: { type: String },
90
- filter: { type: [Array, Object] },
91
- command: { type: Array, default: () => [] },
92
- rowid: { type: String },
93
- onRowClick: { type: Function },
94
- onRowDblClick: { type: Function },
95
- class: { type: String, default: 'table' },
96
- style: { type: String, default: null }
97
- },
98
- emits: ['onRowClick', 'onRowDblClick'],
99
- setup(props, { attrs, slots, emit, expose }) {
100
- let rows = [];
101
- let rowg = [];
102
- let grps = [];
103
- let adapter = null;
104
- let timer = null;
105
- const spec = reactive({
106
- caption: null,
107
- group: {
108
- value: (props.settings && props.settings.group) ? props.settings.group.value : props.group ? props.group : null,
109
- content: (props.settings && props.settings.group) ? props.settings.group.content : null,
110
- expandAll: true,
111
- class: 'table-light'
112
- },
113
- page: {
114
- count: 0,
115
- size: (props.settings && props.settings.page) ? props.settings.page.size : 0,
116
- current: 0,
117
- type: null
118
- },
119
- sort: (props.settings && props.settings.sort) ? props.settings.sort : [],
120
- columns: (props.settings && props.settings.columns) ? props.settings.columns : [],
121
- footer: {
122
- button: {
123
- class: (props.settings && props.settings.footer && props.settings.footer.button && props.settings.footer.button.class) || 'btn'
124
- }
125
- },
126
- status: TablevuStatus.Ready,
127
- time: null,
128
- search: null,
129
- filter: null,
130
- onData: (props.settings && props.settings.onData) ? props.settings.onData : undefined
131
- });
132
- if (props.data) {
133
- if (Array.isArray(props.data)) {
134
- adapter = adaptArray;
135
- }
136
- else {
137
- adapter = props.data;
138
- }
139
- }
140
- if (props.search)
141
- spec.search = props.search;
142
- watch(() => props.search, (n, o) => {
143
- const x = parseInt(String(props.settings?.searchMinChar)) || 3;
144
- if (timer) {
145
- clearTimeout(timer);
146
- timer = null;
147
- }
148
- if (n && n.length > x) {
149
- spec.search = n;
150
- timer = setTimeout(() => { refresh(); timer = null; }, 444);
151
- }
152
- else if (o && o.length > x && (n == undefined || n == null || n.length <= x)) {
153
- spec.search = null;
154
- timer = setTimeout(() => { refresh(); timer = null; }, 444);
155
- }
156
- });
157
- if (props.filter)
158
- spec.filter = props.filter;
159
- watch(() => props.filter, () => {
160
- if (props.filter)
161
- spec.filter = props.filter;
162
- else
163
- spec.filter = null;
164
- refresh();
165
- }, { deep: true });
166
- watch(() => props.command, (n, o) => {
167
- if (n && n.length > 0) {
168
- const c = props.command[n.length - 1];
169
- if (c && c.name) {
170
- switch (c.name.toLowerCase()) {
171
- case 'refresh':
172
- refresh();
173
- break;
174
- case 'first':
175
- first();
176
- break;
177
- case 'prior':
178
- prior();
179
- break;
180
- case 'next':
181
- next();
182
- break;
183
- case 'last':
184
- last();
185
- break;
186
- default: console.log('Unknown command');
187
- }
188
- }
189
- props.command.length = 0;
190
- }
191
- }, { deep: true });
192
- function prepareGroup(g, r) {
193
- grps = [];
194
- rowg = [];
195
- for (let i = 0; i < r.length; i++) {
196
- let v = null;
197
- let c = null;
198
- const o = r[i];
199
- if (g.value) {
200
- if (typeof g.value == 'function')
201
- v = g.value(o);
202
- else
203
- v = o[g.value];
204
- }
205
- else if (typeof g == 'function') {
206
- v = g(o);
207
- }
208
- else {
209
- if (typeof g == 'bigint' || typeof g == 'number') {
210
- const l = parseFloat(String(g));
211
- if (l)
212
- v = o[l];
213
- }
214
- else if (typeof g == 'string')
215
- v = o[g];
216
- }
217
- if (g.content) {
218
- if (typeof g.content == 'function')
219
- c = g.content(o);
220
- else
221
- c = o[g.content];
222
- }
223
- let n = grps.findIndex(f => f.group == v);
224
- if (n == -1) {
225
- n = grps.push({ group: v, content: c, expand: spec.group.expandAll }) - 1;
226
- }
227
- rowg.push(n);
228
- }
229
- }
230
- function first() { page(1); }
231
- function prior() {
232
- if (spec.page.current > 1) {
233
- page(spec.page.current - 1);
234
- }
235
- else {
236
- page(spec.page.current);
237
- }
238
- }
239
- function next() { page(spec.page.current + 1); }
240
- function last() { page(spec.page.count); }
241
- function go() {
242
- let p;
243
- let g = spec.page.current;
244
- do {
245
- p = prompt(undefined, String(spec.page.current));
246
- g = parseInt(p || '0');
247
- } while (p && isNaN(g));
248
- if (g && g > 0 && g != spec.page.current)
249
- page(g);
250
- }
251
- function refresh() { page(spec.page.current); }
252
- function orderby(ind, dir) {
253
- if (spec.sort && spec.sort.length > 0) {
254
- let l = spec.sort.findIndex(f => f.index == ind);
255
- if (l < 0) {
256
- let n = spec.columns[ind]?.name;
257
- l = spec.sort.findIndex(f => f.name == n);
258
- }
259
- if (l == 0) {
260
- spec.sort[l].dir = dir ? dir : (spec.sort[0].dir ? null : 'desc');
261
- }
262
- else if (l > 0) {
263
- spec.sort.splice(l, 1);
264
- }
265
- if (l != 0) {
266
- spec.sort.unshift({ index: ind, dir: dir ? dir : null });
267
- }
268
- if (spec.sort.length > 2) {
269
- spec.sort.pop();
270
- }
271
- }
272
- else {
273
- spec.sort.push({ index: ind, dir: dir ? dir : null });
274
- }
275
- page(spec.page.current);
276
- }
277
- function page(currentPage) {
278
- let dat = null;
279
- if (props.data) {
280
- if (Array.isArray(props.data))
281
- dat = props.data;
282
- }
283
- else
284
- return;
285
- spec.status = TablevuStatus.Loading;
286
- if (adapter) {
287
- adapter({
288
- pageSize: spec.page.size,
289
- currPage: currentPage,
290
- sort: spec.sort,
291
- cols: spec.columns,
292
- filter: spec.filter,
293
- search: spec.search
294
- }, dat).then(ada => {
295
- if (spec.onData)
296
- rows = spec.onData(ada.rows);
297
- else
298
- rows = ada.rows;
299
- const groupValue = spec.group.value;
300
- if (groupValue && (typeof groupValue == 'string' || typeof groupValue == 'number' || typeof groupValue == 'bigint' || (typeof groupValue == 'object' && groupValue.value)))
301
- prepareGroup(spec.group, rows);
302
- spec.page.count = (ada.count > 0 && spec.page.size > 0) ? Math.ceil(ada.count / spec.page.size) : 0;
303
- spec.page.current = currentPage;
304
- spec.status = TablevuStatus.Ready;
305
- spec.time = Date.now();
306
- }).catch(err => {
307
- console.error(err);
308
- spec.status = TablevuStatus.Error;
309
- });
310
- }
311
- }
312
- function renderValue(col, val) {
313
- if (val) {
314
- let v;
315
- if (col.type == 'datetime' || col.type == 'date' || col.type == 'time') {
316
- const t = new Date(val);
317
- if (col.type == 'date')
318
- v = t.toLocaleDateString();
319
- else if (col.type == 'time')
320
- v = t.toLocaleTimeString();
321
- else
322
- v = t.toLocaleString();
323
- }
324
- else {
325
- if (col.type == 'numeric') {
326
- v = parseFloat(val);
327
- }
328
- else {
329
- if (col.type == 'integer') {
330
- v = parseInt(val);
331
- }
332
- else
333
- v = val;
334
- }
335
- }
336
- return v;
337
- }
338
- else
339
- return '';
340
- }
341
- function renderCell(col, val) {
342
- const o = { style: {} };
343
- if (col.colspan && col.colspan > 1)
344
- o.colspan = col.colspan;
345
- if (col.style)
346
- o.style = { ...o.style, ...col.style };
347
- if (col.align)
348
- o.style['text-align'] = col.align;
349
- if (col.width)
350
- o.style['max-width'] = col.width;
351
- const v = renderValue(col, val);
352
- return h('td', o, v);
353
- }
354
- function renderCustomCell(col, row) {
355
- const o = { style: {} };
356
- if (col.colspan && col.colspan > 1)
357
- o.colspan = col.colspan;
358
- if (col.style)
359
- o.style = { ...o.style, ...col.style };
360
- if (col.align)
361
- o.style['text-align'] = col.align;
362
- if (col.width)
363
- o.style['max-width'] = col.width;
364
- const f = col.render(row);
365
- return h('td', o, f);
366
- }
367
- function renderGroup(grp) {
368
- const b = h('button', {
369
- title: String(grp.expand),
370
- onClick: (event) => {
371
- grp.expand = !grp.expand;
372
- event.currentTarget.value = grp.expand ? '▼' : '►';
373
- spec.time = Date.now();
374
- event.preventDefault();
375
- },
376
- style: { padding: 0, margin: '0 6px 0 0', width: '12px', height: '100%', border: 'none', outline: '0', backgroundColor: 'transparent' }
377
- }, grp.expand ? '▼' : '►');
378
- const v = grp.content || grp.group;
379
- return h('tr', {}, h('td', { colspan: spec.columns.length, class: spec.group.class }, [b, v]));
380
- }
381
- function renderRow(index, row) {
382
- const o = { key: index, index: index };
383
- if (props.rowid)
384
- o.rowid = row[props.rowid];
385
- const r = [];
386
- for (let i = 0; i < spec.columns.length; i++) {
387
- const c = spec.columns[i];
388
- if (c.render) {
389
- r.push(renderCustomCell(c, row));
390
- }
391
- else {
392
- let v;
393
- if (c.depth) {
394
- v = row;
395
- for (let j = 0; j < c.depth.length; j++) {
396
- if (v)
397
- v = v[c.depth[j]];
398
- }
399
- }
400
- else {
401
- v = row[c.name];
402
- }
403
- r.push(renderCell(c, v));
404
- }
405
- }
406
- o.ondblclick = (event) => {
407
- const target = event.currentTarget;
408
- const idx = target.getAttribute('index');
409
- if (idx !== null) {
410
- const rowData = rows[parseInt(idx)];
411
- emit('onRowDblClick', event, rowData);
412
- }
413
- };
414
- if (props.onRowClick) {
415
- o.onClick = (event) => {
416
- const target = event.currentTarget;
417
- const idx = target.getAttribute('index');
418
- if (idx !== null) {
419
- const rowData = rows[parseInt(idx)];
420
- props.onRowClick(event, rowData);
421
- }
422
- };
423
- }
424
- return h('tr', o, r);
425
- }
426
- function renderHeader() {
427
- const ar = [];
428
- for (let i = 0; i < spec.columns.length; i++) {
429
- const cl = [];
430
- if (spec.columns[i].label)
431
- cl.push(h('span', spec.columns[i].label));
432
- else
433
- cl.push(h('span', spec.columns[i].name));
434
- if (spec.sort && spec.sort.length > 0 && spec.sort[0].index == i) {
435
- if (spec.sort[0].dir == 'desc')
436
- cl.push(h('span', { style: { float: 'right' } }, '▼'));
437
- else
438
- cl.push(h('span', { style: { float: 'right' } }, '▲'));
439
- }
440
- const ch = { position: 'relative' };
441
- if (spec.columns[i].width) {
442
- ch.width = spec.columns[i].width;
443
- ch.minWidth = spec.columns[i].width;
444
- ch.maxWidth = spec.columns[i].width;
445
- }
446
- const cy = { display: 'block', 'font-weight': 500, textDecoration: 'none', textAlign: 'center' };
447
- let a;
448
- let f;
449
- if (spec.columns[i].popup) {
450
- const r = ref(null);
451
- const c = [
452
- h('a', { href: '#', onClick: (event) => { orderby(i); event.preventDefault(); }, style: { display: 'block', textDecoration: 'none', color: 'black', fontWeight: 'initial', padding: '.25rem' } }, 'Order by Ascending'),
453
- h('a', { href: '#', onClick: (event) => { orderby(i, 'desc'); event.preventDefault(); }, style: { display: 'block', textDecoration: 'none', color: 'black', fontWeight: 'initial', padding: '.25rem' } }, 'Order by Descending'),
454
- ];
455
- a = h('a', {
456
- href: '#',
457
- onClick: (event) => {
458
- if (r.value && r.value.style && r.value.style.display && r.value.style.display == 'block')
459
- r.value.style.display = 'none';
460
- else if (r.value && r.value.style)
461
- r.value.style.display = 'block';
462
- event.preventDefault();
463
- },
464
- style: cy
465
- }, cl);
466
- f = h('div', {
467
- ref: r,
468
- style: { position: 'absolute', display: 'none', width: spec.columns[i].width || '12rem', height: 'fit-content', border: '1px solid lightgray', backgroundColor: 'whitesmoke', zIndex: 1, left: 0, top: '2rem', bottom: 0, right: 0, padding: '.25rem' }
469
- }, c);
470
- }
471
- else
472
- a = h('a', { href: '#', onClick: (event) => { orderby(i); event.preventDefault(); }, style: cy }, cl);
473
- if (spec.columns[i].filter) ;
474
- ar.push(h('th', { colspan: null, style: ch }, [a, f]));
475
- }
476
- const tr = h('tr', {}, ar);
477
- return [tr];
478
- }
479
- function renderBody() {
480
- const dr = [];
481
- if (rows && rows.length > 0) {
482
- if (grps && grps.length > 0) {
483
- const r = [];
484
- for (let g = 0; g < grps.length; g++)
485
- r.push({ index: g, childs: [] });
486
- for (let i = 0; i < rows.length; i++) {
487
- const x = rowg[i];
488
- r[x].childs.push(renderRow(i, rows[i]));
489
- }
490
- for (let g = 0; g < grps.length; g++) {
491
- dr.push(renderGroup(grps[g]));
492
- if (grps[g].expand)
493
- dr.push(r[g].childs);
494
- }
495
- }
496
- else {
497
- for (let i = 0; i < rows.length; i++) {
498
- dr.push(renderRow(i, rows[i]));
499
- }
500
- }
501
- }
502
- return dr;
503
- }
504
- function renderFooter() {
505
- const fr = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: first, disabled: spec.page.current == 1 }, '◀◀');
506
- const ls = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: last, disabled: spec.page.count == 0 || spec.page.current == spec.page.count }, '▶▶');
507
- const pg = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: go }, spec.page.current + (spec.page.count > 0 ? (' / ' + spec.page.count) : ''));
508
- const pr = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: prior, disabled: spec.page.current == 1 }, '◀');
509
- const nx = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: next, disabled: spec.page.count > 0 && spec.page.current == spec.page.count }, '▶');
510
- const td = h('td', { colspan: spec.columns.length, align: 'center' }, [fr, pr, pg, nx, ls]);
511
- const tr = h('tr', {}, [td]);
512
- return [tr];
513
- }
514
- function renderCaption() {
515
- return null;
516
- }
517
- if (spec.columns.length == 0) {
518
- console.error('Columns not defined !');
519
- }
520
- for (let i = 0; i < spec.columns.length; i++)
521
- if (spec.columns[i].name && spec.columns[i].name.indexOf('.') > -1) {
522
- spec.columns[i].depth = spec.columns[i].name.split('.');
523
- }
524
- page(1);
525
- onMounted(() => {
526
- // mounted hook
527
- });
528
- return {
529
- spec, renderHeader, renderBody, renderFooter, renderCaption
530
- };
531
- },
532
- render(props) {
533
- let prps = { class: 'tablevu' };
534
- if (props.class)
535
- prps.class = 'tablevu ' + props.class;
536
- if (props.style)
537
- prps.style = props.style;
538
- const cntn = [];
539
- cntn.push(h('thead', {}, this.renderHeader()));
540
- if (this.spec.status == TablevuStatus.Ready)
541
- cntn.push(h('tbody', { 'data-time': this.spec.time }, this.renderBody()));
542
- if (this.spec.caption)
543
- cntn.push(h('caption', {}, this.renderCaption()));
544
- if (this.spec.status == TablevuStatus.Ready)
545
- if (this.spec.page.size > 0)
546
- cntn.push(h('tfoot', {}, this.renderFooter()));
547
- return h('table', prps, cntn);
548
- }
549
- };
550
-
551
- export { TablevuStatus, tablevu as default };