tablevu 0.3.0-beta.1 → 0.3.0-beta.3

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.
@@ -85,6 +85,7 @@
85
85
  Ready: 0,
86
86
  Loading: 1
87
87
  };
88
+
88
89
  const tablevu = {
89
90
  props: {
90
91
  settings: { type: Object, default: null },
@@ -92,14 +93,13 @@
92
93
  group: { type: [String, Object] },
93
94
  search: { type: String },
94
95
  filter: { type: [Array, Object] },
95
- command: { type: Array, default: () => [] },
96
96
  rowid: { type: String },
97
97
  onRowClick: { type: Function },
98
98
  onRowDblClick: { type: Function },
99
99
  class: { type: String, default: 'table' },
100
100
  style: { type: String, default: null }
101
101
  },
102
- emits: ['onRowClick', 'onRowDblClick'],
102
+ emits: ['onRowClick', 'onRowDblClick', 'selectionChange', 'columnVisibilityChange'],
103
103
  setup(props, { attrs, slots, emit, expose }) {
104
104
  let rows = [];
105
105
  let rowg = [];
@@ -109,16 +109,16 @@
109
109
  const spec = vue.reactive({
110
110
  caption: null,
111
111
  group: {
112
- value: (props.settings && props.settings.group) ? props.settings.group.value : props.group ? props.group : null,
113
- content: (props.settings && props.settings.group) ? props.settings.group.content : null,
112
+ value: (props.settings && props.settings.group) ? props.settings.group.value : props.group ? props.group : undefined,
113
+ content: (props.settings && props.settings.group) ? props.settings.group.content : undefined,
114
114
  expandAll: true,
115
115
  class: 'table-light'
116
116
  },
117
117
  page: {
118
118
  count: 0,
119
- size: (props.settings && props.settings.page) ? props.settings.page.size : 0,
119
+ size: (props.settings && props.settings.page) ? (props.settings.page.size || 0) : 0,
120
120
  current: 0,
121
- type: null
121
+ type: undefined
122
122
  },
123
123
  sort: (props.settings && props.settings.sort) ? props.settings.sort : [],
124
124
  columns: (props.settings && props.settings.columns) ? props.settings.columns : [],
@@ -131,7 +131,11 @@
131
131
  time: null,
132
132
  search: null,
133
133
  filter: null,
134
- onData: (props.settings && props.settings.onData) ? props.settings.onData : undefined
134
+ onData: (props.settings && props.settings.onData) ? props.settings.onData : undefined,
135
+ selectedRows: [],
136
+ columnWidths: {},
137
+ columnOrder: [],
138
+ columnVisibility: {}
135
139
  });
136
140
  if (props.data) {
137
141
  if (Array.isArray(props.data)) {
@@ -167,32 +171,6 @@
167
171
  spec.filter = null;
168
172
  refresh();
169
173
  }, { deep: true });
170
- vue.watch(() => props.command, (n, o) => {
171
- if (n && n.length > 0) {
172
- const c = props.command[n.length - 1];
173
- if (c && c.name) {
174
- switch (c.name.toLowerCase()) {
175
- case 'refresh':
176
- refresh();
177
- break;
178
- case 'first':
179
- first();
180
- break;
181
- case 'prior':
182
- prior();
183
- break;
184
- case 'next':
185
- next();
186
- break;
187
- case 'last':
188
- last();
189
- break;
190
- default: console.log('Unknown command');
191
- }
192
- }
193
- props.command.length = 0;
194
- }
195
- }, { deep: true });
196
174
  function prepareGroup(g, r) {
197
175
  grps = [];
198
176
  rowg = [];
@@ -231,28 +209,26 @@
231
209
  rowg.push(n);
232
210
  }
233
211
  }
234
- function first() { page(1); }
212
+ function first() { loadPage(1); }
235
213
  function prior() {
236
214
  if (spec.page.current > 1) {
237
- page(spec.page.current - 1);
215
+ loadPage(spec.page.current - 1);
238
216
  }
239
217
  else {
240
- page(spec.page.current);
218
+ loadPage(spec.page.current);
241
219
  }
242
220
  }
243
- function next() { page(spec.page.current + 1); }
244
- function last() { page(spec.page.count); }
245
- function go() {
246
- let p;
247
- let g = spec.page.current;
248
- do {
249
- p = prompt(undefined, String(spec.page.current));
250
- g = parseInt(p || '0');
251
- } while (p && isNaN(g));
252
- if (g && g > 0 && g != spec.page.current)
253
- page(g);
221
+ function next() { loadPage(spec.page.current + 1); }
222
+ function last() { loadPage(spec.page.count); }
223
+ // Navigate to specific page number
224
+ function page(currentPage) {
225
+ if (currentPage < 1)
226
+ currentPage = 1;
227
+ if (currentPage > spec.page.count && spec.page.count > 0)
228
+ currentPage = spec.page.count;
229
+ loadPage(currentPage);
254
230
  }
255
- function refresh() { page(spec.page.current); }
231
+ function refresh() { loadPage(spec.page.current); }
256
232
  function orderby(ind, dir) {
257
233
  if (spec.sort && spec.sort.length > 0) {
258
234
  let l = spec.sort.findIndex(f => f.index == ind);
@@ -276,9 +252,10 @@
276
252
  else {
277
253
  spec.sort.push({ index: ind, dir: dir ? dir : null });
278
254
  }
279
- page(spec.page.current);
255
+ loadPage(spec.page.current);
280
256
  }
281
- function page(currentPage) {
257
+ // Internal load function (alias for adapter call)
258
+ function loadPage(currentPage) {
282
259
  let dat = null;
283
260
  if (props.data) {
284
261
  if (Array.isArray(props.data))
@@ -288,8 +265,9 @@
288
265
  return;
289
266
  spec.status = TablevuStatus.Loading;
290
267
  if (adapter) {
268
+ const pageSize = spec.page.size || 20;
291
269
  adapter({
292
- pageSize: spec.page.size,
270
+ pageSize,
293
271
  currPage: currentPage,
294
272
  sort: spec.sort,
295
273
  cols: spec.columns,
@@ -303,7 +281,7 @@
303
281
  const groupValue = spec.group.value;
304
282
  if (groupValue && (typeof groupValue == 'string' || typeof groupValue == 'number' || typeof groupValue == 'bigint' || (typeof groupValue == 'object' && groupValue.value)))
305
283
  prepareGroup(spec.group, rows);
306
- spec.page.count = (ada.count > 0 && spec.page.size > 0) ? Math.ceil(ada.count / spec.page.size) : 0;
284
+ spec.page.count = (ada.count > 0 && pageSize > 0) ? Math.ceil(ada.count / pageSize) : 0;
307
285
  spec.page.current = currentPage;
308
286
  spec.status = TablevuStatus.Ready;
309
287
  spec.time = Date.now();
@@ -365,8 +343,8 @@
365
343
  o.style['text-align'] = col.align;
366
344
  if (col.width)
367
345
  o.style['max-width'] = col.width;
368
- const f = col.render(row);
369
- return vue.h('td', o, f);
346
+ const f = col.render?.(row) ?? '';
347
+ return vue.h('td', { ...o, innerHTML: f });
370
348
  }
371
349
  function renderGroup(grp) {
372
350
  const b = vue.h('button', {
@@ -425,6 +403,10 @@
425
403
  }
426
404
  };
427
405
  }
406
+ // Selection
407
+ if (spec.selectedRows.some(s => s[props.rowid || 'id'] === row[props.rowid || 'id'])) {
408
+ o.class = (o.class || '') + ' selected';
409
+ }
428
410
  return vue.h('tr', o, r);
429
411
  }
430
412
  function renderHeader() {
@@ -475,7 +457,10 @@
475
457
  else
476
458
  a = vue.h('a', { href: '#', onClick: (event) => { orderby(i); event.preventDefault(); }, style: cy }, cl);
477
459
  if (spec.columns[i].filter) ;
478
- ar.push(vue.h('th', { colspan: null, style: ch }, [a, f]));
460
+ ar.push(vue.h('th', {
461
+ colspan: null,
462
+ style: ch
463
+ }, [a, f]));
479
464
  }
480
465
  const tr = vue.h('tr', {}, ar);
481
466
  return [tr];
@@ -503,14 +488,34 @@
503
488
  }
504
489
  }
505
490
  }
491
+ else if (spec.settings?.emptyMessage) {
492
+ dr.push(vue.h('tr', {}, vue.h('td', { colspan: spec.columns.length, style: { textAlign: 'center', padding: '2rem', color: '#666' } }, spec.settings.emptyMessage)));
493
+ }
506
494
  return dr;
507
495
  }
496
+ // Prompt user for page number and navigate
497
+ function goToPage() {
498
+ const input = prompt('Page number:', String(spec.page.current));
499
+ if (input) {
500
+ const pageNum = parseInt(input, 10);
501
+ if (!isNaN(pageNum) && pageNum > 0 && pageNum !== spec.page.current) {
502
+ page(pageNum);
503
+ }
504
+ }
505
+ }
506
+ function makeHandler(fn) {
507
+ return (e) => {
508
+ e.preventDefault();
509
+ e.stopPropagation();
510
+ fn();
511
+ };
512
+ }
508
513
  function renderFooter() {
509
- const fr = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: first, disabled: spec.page.current == 1 }, '◀◀');
510
- const ls = vue.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 }, '▶▶');
511
- const pg = vue.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) : ''));
512
- const pr = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: prior, disabled: spec.page.current == 1 }, '◀');
513
- const nx = vue.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 }, '▶');
514
+ const fr = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(first), disabled: spec.page.current == 1 }, '◀◀');
515
+ const ls = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(last), disabled: spec.page.count == 0 || spec.page.current == spec.page.count }, '▶▶');
516
+ const pg = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(goToPage) }, spec.page.current + (spec.page.count > 0 ? (' / ' + spec.page.count) : ''));
517
+ const pr = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(prior), disabled: spec.page.current == 1 }, '◀');
518
+ const nx = vue.h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(next), disabled: spec.page.count > 0 && spec.page.current == spec.page.count }, '▶');
514
519
  const td = vue.h('td', { colspan: spec.columns.length, align: 'center' }, [fr, pr, pg, nx, ls]);
515
520
  const tr = vue.h('tr', {}, [td]);
516
521
  return [tr];
@@ -518,19 +523,41 @@
518
523
  function renderCaption() {
519
524
  return null;
520
525
  }
526
+ // Initialize column metadata
521
527
  if (spec.columns.length == 0) {
522
528
  console.error('Columns not defined !');
523
529
  }
524
- for (let i = 0; i < spec.columns.length; i++)
530
+ for (let i = 0; i < spec.columns.length; i++) {
525
531
  if (spec.columns[i].name && spec.columns[i].name.indexOf('.') > -1) {
526
532
  spec.columns[i].depth = spec.columns[i].name.split('.');
527
533
  }
528
- page(1);
534
+ // Initialize visibility
535
+ if (spec.columns[i].visible === undefined) {
536
+ spec.columns[i].visible = true;
537
+ spec.columnVisibility[spec.columns[i].name] = true;
538
+ }
539
+ }
540
+ loadPage(1);
541
+ // Expose methods for external access via ref
542
+ expose({
543
+ first,
544
+ prior,
545
+ next,
546
+ last,
547
+ page,
548
+ refresh,
549
+ orderby,
550
+ getSelectedRows: () => spec.selectedRows,
551
+ setSelectedRows: (rows) => { spec.selectedRows = rows; }
552
+ });
529
553
  vue.onMounted(() => {
530
554
  // mounted hook
531
555
  });
532
556
  return {
533
- spec, renderHeader, renderBody, renderFooter, renderCaption
557
+ spec, renderHeader, renderBody, renderFooter, renderCaption,
558
+ first, prior, next, last, page, refresh, orderby,
559
+ getSelectedRows: () => spec.selectedRows,
560
+ setSelectedRows: (rows) => { spec.selectedRows = rows; }
534
561
  };
535
562
  },
536
563
  render(props) {
@@ -687,18 +714,18 @@
687
714
  * Logical
688
715
  * ========================================================================== */
689
716
  function buildLogical(filter, operator) {
690
- const filters = requireFilters(filter);
717
+ const filters = filter.filters || [];
691
718
  if (filters.length === 0)
692
- throw new Error(`Operator '${filter.operator}' requires at least one filter.`);
719
+ return '';
693
720
  return filters
694
721
  .map(buildFilter)
695
722
  .map(expression => `(${expression})`)
696
723
  .join(` ${operator} `);
697
724
  }
698
725
  function buildNot(filter) {
699
- const filters = requireFilters(filter);
726
+ const filters = filter.filters || [];
700
727
  if (filters.length !== 1)
701
- throw new Error("Operator 'not' requires exactly one filter.");
728
+ return '';
702
729
  return `not (${buildFilter(filters[0])})`;
703
730
  }
704
731
  /* ==========================================================================
@@ -789,9 +816,9 @@
789
816
  * ========================================================================== */
790
817
  function buildAll(filter) {
791
818
  const column = getColumn(filter);
792
- const filters = requireFilters(filter);
819
+ const filters = filter.filters || [];
793
820
  if (filters.length !== 1)
794
- throw new Error("Operator 'all' requires exactly one filter.");
821
+ return '';
795
822
  return (`${column}/all(x: ` +
796
823
  `${buildLambdaFilter(filters[0], 'x')}` +
797
824
  `)`);
@@ -908,9 +935,7 @@
908
935
  `${getColumn(filter)}`);
909
936
  }
910
937
  function requireFilters(filter) {
911
- if (!filter.filters)
912
- throw new Error(`Operator '${filter.operator}' requires filters.`);
913
- return filter.filters;
938
+ return filter.filters || [];
914
939
  }
915
940
  /* ==========================================================================
916
941
  * OData Request
@@ -974,7 +999,7 @@
974
999
  * Static filter from options
975
1000
  */
976
1001
  if (options.filter &&
977
- (options.filter.filters ||
1002
+ ((options.filter.filters && options.filter.filters.length > 0) ||
978
1003
  options.filter.column ||
979
1004
  options.filter.func)) {
980
1005
  const expression = toODataFilter(options.filter);
@@ -985,7 +1010,7 @@
985
1010
  * Runtime filter from DataTable
986
1011
  */
987
1012
  if (props.filter &&
988
- (props.filter.filters ||
1013
+ ((props.filter.filters && props.filter.filters.length > 0) ||
989
1014
  props.filter.column ||
990
1015
  props.filter.func)) {
991
1016
  const expression = toODataFilter(props.filter);
@@ -1056,7 +1081,8 @@
1056
1081
  /* ---------------------------------------------------------------------- */
1057
1082
  /* Request */
1058
1083
  /* ---------------------------------------------------------------------- */
1059
- return new Request(url, options.header);
1084
+ const requestInit = options.headers ? { headers: options.headers } : undefined;
1085
+ return new Request(url, requestInit);
1060
1086
  }
1061
1087
 
1062
1088
  const param = (uri, header, props) => {
@@ -1078,7 +1104,6 @@
1078
1104
  };
1079
1105
 
1080
1106
  exports.TableVu = tablevu;
1081
- exports.TablevuStatus = TablevuStatus;
1082
1107
  exports.adaptArray = adaptArray;
1083
1108
  exports.buildFilter = toODataFilter;
1084
1109
  exports.odata = odata;
@@ -133,18 +133,18 @@ function buildFilter(filter) {
133
133
  * Logical
134
134
  * ========================================================================== */
135
135
  function buildLogical(filter, operator) {
136
- const filters = requireFilters(filter);
136
+ const filters = filter.filters || [];
137
137
  if (filters.length === 0)
138
- throw new Error(`Operator '${filter.operator}' requires at least one filter.`);
138
+ return '';
139
139
  return filters
140
140
  .map(buildFilter)
141
141
  .map(expression => `(${expression})`)
142
142
  .join(` ${operator} `);
143
143
  }
144
144
  function buildNot(filter) {
145
- const filters = requireFilters(filter);
145
+ const filters = filter.filters || [];
146
146
  if (filters.length !== 1)
147
- throw new Error("Operator 'not' requires exactly one filter.");
147
+ return '';
148
148
  return `not (${buildFilter(filters[0])})`;
149
149
  }
150
150
  /* ==========================================================================
@@ -235,9 +235,9 @@ function buildAny(filter) {
235
235
  * ========================================================================== */
236
236
  function buildAll(filter) {
237
237
  const column = getColumn(filter);
238
- const filters = requireFilters(filter);
238
+ const filters = filter.filters || [];
239
239
  if (filters.length !== 1)
240
- throw new Error("Operator 'all' requires exactly one filter.");
240
+ return '';
241
241
  return (`${column}/all(x: ` +
242
242
  `${buildLambdaFilter(filters[0], 'x')}` +
243
243
  `)`);
@@ -354,9 +354,7 @@ function getLambdaColumn(filter, variable) {
354
354
  `${getColumn(filter)}`);
355
355
  }
356
356
  function requireFilters(filter) {
357
- if (!filter.filters)
358
- throw new Error(`Operator '${filter.operator}' requires filters.`);
359
- return filter.filters;
357
+ return filter.filters || [];
360
358
  }
361
359
  /* ==========================================================================
362
360
  * OData Request
@@ -420,7 +418,7 @@ function odata(options, props = {}) {
420
418
  * Static filter from options
421
419
  */
422
420
  if (options.filter &&
423
- (options.filter.filters ||
421
+ ((options.filter.filters && options.filter.filters.length > 0) ||
424
422
  options.filter.column ||
425
423
  options.filter.func)) {
426
424
  const expression = toODataFilter(options.filter);
@@ -431,7 +429,7 @@ function odata(options, props = {}) {
431
429
  * Runtime filter from DataTable
432
430
  */
433
431
  if (props.filter &&
434
- (props.filter.filters ||
432
+ ((props.filter.filters && props.filter.filters.length > 0) ||
435
433
  props.filter.column ||
436
434
  props.filter.func)) {
437
435
  const expression = toODataFilter(props.filter);
@@ -502,7 +500,8 @@ function odata(options, props = {}) {
502
500
  /* ---------------------------------------------------------------------- */
503
501
  /* Request */
504
502
  /* ---------------------------------------------------------------------- */
505
- return new Request(url, options.header);
503
+ const requestInit = options.headers ? { headers: options.headers } : undefined;
504
+ return new Request(url, requestInit);
506
505
  }
507
506
 
508
507
  export { odata as default, toODataFilter };
@@ -139,18 +139,18 @@
139
139
  * Logical
140
140
  * ========================================================================== */
141
141
  function buildLogical(filter, operator) {
142
- const filters = requireFilters(filter);
142
+ const filters = filter.filters || [];
143
143
  if (filters.length === 0)
144
- throw new Error(`Operator '${filter.operator}' requires at least one filter.`);
144
+ return '';
145
145
  return filters
146
146
  .map(buildFilter)
147
147
  .map(expression => `(${expression})`)
148
148
  .join(` ${operator} `);
149
149
  }
150
150
  function buildNot(filter) {
151
- const filters = requireFilters(filter);
151
+ const filters = filter.filters || [];
152
152
  if (filters.length !== 1)
153
- throw new Error("Operator 'not' requires exactly one filter.");
153
+ return '';
154
154
  return `not (${buildFilter(filters[0])})`;
155
155
  }
156
156
  /* ==========================================================================
@@ -241,9 +241,9 @@
241
241
  * ========================================================================== */
242
242
  function buildAll(filter) {
243
243
  const column = getColumn(filter);
244
- const filters = requireFilters(filter);
244
+ const filters = filter.filters || [];
245
245
  if (filters.length !== 1)
246
- throw new Error("Operator 'all' requires exactly one filter.");
246
+ return '';
247
247
  return (`${column}/all(x: ` +
248
248
  `${buildLambdaFilter(filters[0], 'x')}` +
249
249
  `)`);
@@ -360,9 +360,7 @@
360
360
  `${getColumn(filter)}`);
361
361
  }
362
362
  function requireFilters(filter) {
363
- if (!filter.filters)
364
- throw new Error(`Operator '${filter.operator}' requires filters.`);
365
- return filter.filters;
363
+ return filter.filters || [];
366
364
  }
367
365
  /* ==========================================================================
368
366
  * OData Request
@@ -426,7 +424,7 @@
426
424
  * Static filter from options
427
425
  */
428
426
  if (options.filter &&
429
- (options.filter.filters ||
427
+ ((options.filter.filters && options.filter.filters.length > 0) ||
430
428
  options.filter.column ||
431
429
  options.filter.func)) {
432
430
  const expression = toODataFilter(options.filter);
@@ -437,7 +435,7 @@
437
435
  * Runtime filter from DataTable
438
436
  */
439
437
  if (props.filter &&
440
- (props.filter.filters ||
438
+ ((props.filter.filters && props.filter.filters.length > 0) ||
441
439
  props.filter.column ||
442
440
  props.filter.func)) {
443
441
  const expression = toODataFilter(props.filter);
@@ -508,7 +506,8 @@
508
506
  /* ---------------------------------------------------------------------- */
509
507
  /* Request */
510
508
  /* ---------------------------------------------------------------------- */
511
- return new Request(url, options.header);
509
+ const requestInit = options.headers ? { headers: options.headers } : undefined;
510
+ return new Request(url, requestInit);
512
511
  }
513
512
 
514
513
  exports.default = odata;