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.
package/dist/array.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TablevuSort, TablevuColumn } from './table';
1
+ import type { TablevuSort, TablevuColumn } from './types';
2
2
  export interface ArrayAdapterParams {
3
3
  pageSize?: number;
4
4
  currPage?: number;
package/dist/odata.d.ts CHANGED
@@ -30,7 +30,7 @@ export interface DataTableProps {
30
30
  }
31
31
  export interface ODataOptions {
32
32
  url: string;
33
- header?: RequestInit;
33
+ headers?: Record<string, string>;
34
34
  select?: string[];
35
35
  expand?: Expand[];
36
36
  filter?: Filter;
package/dist/table.d.ts CHANGED
@@ -1,115 +1,5 @@
1
1
  import { PropType, EmitsOptions } from 'vue';
2
- export type TablevuStatus = -1 | 0 | 1;
3
- export declare const TablevuStatus: {
4
- Error: TablevuStatus;
5
- Ready: TablevuStatus;
6
- Loading: TablevuStatus;
7
- };
8
- export interface TablevuColumn {
9
- name: string;
10
- label?: string;
11
- type?: 'string' | 'number' | 'numeric' | 'integer' | 'date' | 'time' | 'datetime' | 'boolean';
12
- align?: 'left' | 'center' | 'right';
13
- width?: string;
14
- style?: Record<string, string>;
15
- render?: (row: any) => any;
16
- colspan?: number;
17
- depth?: string[];
18
- header?: {
19
- colspan?: number;
20
- style?: Record<string, string>;
21
- align?: string;
22
- };
23
- popup?: boolean;
24
- filter?: any;
25
- }
26
- export interface TablevuSort {
27
- index?: number;
28
- name?: string;
29
- dir?: 'asc' | 'desc' | null | string;
30
- }
31
- export interface TablevuGroup {
32
- value?: string | number | bigint | ((row: any) => any);
33
- content?: string | number | ((row: any) => any);
34
- expandAll?: boolean;
35
- class?: string;
36
- }
37
- export interface TablevuPageSettings {
38
- size?: number;
39
- type?: 'incremental' | 'dynamic';
40
- }
41
- export interface TablevuFooterSettings {
42
- button?: {
43
- class?: string;
44
- };
45
- }
46
- export interface TablevuSettings {
47
- group?: TablevuGroup;
48
- page?: TablevuPageSettings;
49
- sort?: TablevuSort[];
50
- columns?: TablevuColumn[];
51
- searchMinChar?: number;
52
- footer?: TablevuFooterSettings;
53
- onData?: (rows: any[]) => any[];
54
- }
55
- export interface TablevuProps {
56
- settings?: TablevuSettings;
57
- data?: any[] | ((params: any, data: any[]) => Promise<any>);
58
- group?: string | object;
59
- search?: string;
60
- filter?: any;
61
- command?: Array<{
62
- name: string;
63
- }>;
64
- rowid?: string;
65
- onRowClick?: (event: MouseEvent, row: any) => void;
66
- onRowDblClick?: (event: MouseEvent, row: any) => void;
67
- class?: string;
68
- style?: string;
69
- }
70
- export interface TablevuEmits {
71
- (e: 'onRowClick', event: MouseEvent, row: any): void;
72
- (e: 'onRowDblClick', event: MouseEvent, row: any): void;
73
- }
74
- interface AdapterResult {
75
- rows: any[];
76
- count: number;
77
- }
78
- type AdapterFunction = (params: {
79
- pageSize: number;
80
- currPage: number;
81
- sort: TablevuSort[];
82
- cols: TablevuColumn[];
83
- filter: any;
84
- search: string | null;
85
- }, data: any[]) => Promise<AdapterResult>;
86
- interface TablevuSpec {
87
- caption: any;
88
- group: TablevuGroup & {
89
- value: any;
90
- content: any;
91
- expandAll: boolean;
92
- class: string;
93
- };
94
- page: {
95
- count: number;
96
- size: number;
97
- current: number;
98
- type: string | null;
99
- };
100
- sort: TablevuSort[];
101
- columns: TablevuColumn[];
102
- footer: {
103
- button: {
104
- class: string;
105
- };
106
- };
107
- status: TablevuStatus;
108
- time: number | null;
109
- search: string | null;
110
- filter: any;
111
- onData?: (rows: any[]) => any[];
112
- }
2
+ import type { TablevuSettings, TablevuProps, TablevuSpec, AdapterFunction } from './types';
113
3
  interface TableSetupReturn {
114
4
  spec: TablevuSpec;
115
5
  renderHeader: () => any[];
@@ -135,12 +25,6 @@ declare const tablevu: {
135
25
  readonly filter: {
136
26
  readonly type: PropType<any>;
137
27
  };
138
- readonly command: {
139
- readonly type: PropType<Array<{
140
- name: string;
141
- }>>;
142
- readonly default: () => never[];
143
- };
144
28
  readonly rowid: {
145
29
  readonly type: PropType<string>;
146
30
  };
@@ -160,12 +44,7 @@ declare const tablevu: {
160
44
  };
161
45
  };
162
46
  emits: EmitsOptions;
163
- setup(props: TablevuProps, { attrs, slots, emit, expose }: {
164
- attrs: any;
165
- slots: any;
166
- emit: any;
167
- expose: any;
168
- }): TableSetupReturn;
47
+ setup(props: TablevuProps, { attrs, slots, emit, expose }: any): TableSetupReturn;
169
48
  render(props: TablevuProps): import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
170
49
  [key: string]: any;
171
50
  }>;
@@ -81,6 +81,7 @@ const TablevuStatus = {
81
81
  Ready: 0,
82
82
  Loading: 1
83
83
  };
84
+
84
85
  const tablevu = {
85
86
  props: {
86
87
  settings: { type: Object, default: null },
@@ -88,14 +89,13 @@ const tablevu = {
88
89
  group: { type: [String, Object] },
89
90
  search: { type: String },
90
91
  filter: { type: [Array, Object] },
91
- command: { type: Array, default: () => [] },
92
92
  rowid: { type: String },
93
93
  onRowClick: { type: Function },
94
94
  onRowDblClick: { type: Function },
95
95
  class: { type: String, default: 'table' },
96
96
  style: { type: String, default: null }
97
97
  },
98
- emits: ['onRowClick', 'onRowDblClick'],
98
+ emits: ['onRowClick', 'onRowDblClick', 'selectionChange', 'columnVisibilityChange'],
99
99
  setup(props, { attrs, slots, emit, expose }) {
100
100
  let rows = [];
101
101
  let rowg = [];
@@ -105,16 +105,16 @@ const tablevu = {
105
105
  const spec = reactive({
106
106
  caption: null,
107
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,
108
+ value: (props.settings && props.settings.group) ? props.settings.group.value : props.group ? props.group : undefined,
109
+ content: (props.settings && props.settings.group) ? props.settings.group.content : undefined,
110
110
  expandAll: true,
111
111
  class: 'table-light'
112
112
  },
113
113
  page: {
114
114
  count: 0,
115
- size: (props.settings && props.settings.page) ? props.settings.page.size : 0,
115
+ size: (props.settings && props.settings.page) ? (props.settings.page.size || 0) : 0,
116
116
  current: 0,
117
- type: null
117
+ type: undefined
118
118
  },
119
119
  sort: (props.settings && props.settings.sort) ? props.settings.sort : [],
120
120
  columns: (props.settings && props.settings.columns) ? props.settings.columns : [],
@@ -127,7 +127,11 @@ const tablevu = {
127
127
  time: null,
128
128
  search: null,
129
129
  filter: null,
130
- onData: (props.settings && props.settings.onData) ? props.settings.onData : undefined
130
+ onData: (props.settings && props.settings.onData) ? props.settings.onData : undefined,
131
+ selectedRows: [],
132
+ columnWidths: {},
133
+ columnOrder: [],
134
+ columnVisibility: {}
131
135
  });
132
136
  if (props.data) {
133
137
  if (Array.isArray(props.data)) {
@@ -163,32 +167,6 @@ const tablevu = {
163
167
  spec.filter = null;
164
168
  refresh();
165
169
  }, { 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
170
  function prepareGroup(g, r) {
193
171
  grps = [];
194
172
  rowg = [];
@@ -227,28 +205,26 @@ const tablevu = {
227
205
  rowg.push(n);
228
206
  }
229
207
  }
230
- function first() { page(1); }
208
+ function first() { loadPage(1); }
231
209
  function prior() {
232
210
  if (spec.page.current > 1) {
233
- page(spec.page.current - 1);
211
+ loadPage(spec.page.current - 1);
234
212
  }
235
213
  else {
236
- page(spec.page.current);
214
+ loadPage(spec.page.current);
237
215
  }
238
216
  }
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);
217
+ function next() { loadPage(spec.page.current + 1); }
218
+ function last() { loadPage(spec.page.count); }
219
+ // Navigate to specific page number
220
+ function page(currentPage) {
221
+ if (currentPage < 1)
222
+ currentPage = 1;
223
+ if (currentPage > spec.page.count && spec.page.count > 0)
224
+ currentPage = spec.page.count;
225
+ loadPage(currentPage);
250
226
  }
251
- function refresh() { page(spec.page.current); }
227
+ function refresh() { loadPage(spec.page.current); }
252
228
  function orderby(ind, dir) {
253
229
  if (spec.sort && spec.sort.length > 0) {
254
230
  let l = spec.sort.findIndex(f => f.index == ind);
@@ -272,9 +248,10 @@ const tablevu = {
272
248
  else {
273
249
  spec.sort.push({ index: ind, dir: dir ? dir : null });
274
250
  }
275
- page(spec.page.current);
251
+ loadPage(spec.page.current);
276
252
  }
277
- function page(currentPage) {
253
+ // Internal load function (alias for adapter call)
254
+ function loadPage(currentPage) {
278
255
  let dat = null;
279
256
  if (props.data) {
280
257
  if (Array.isArray(props.data))
@@ -284,8 +261,9 @@ const tablevu = {
284
261
  return;
285
262
  spec.status = TablevuStatus.Loading;
286
263
  if (adapter) {
264
+ const pageSize = spec.page.size || 20;
287
265
  adapter({
288
- pageSize: spec.page.size,
266
+ pageSize,
289
267
  currPage: currentPage,
290
268
  sort: spec.sort,
291
269
  cols: spec.columns,
@@ -299,7 +277,7 @@ const tablevu = {
299
277
  const groupValue = spec.group.value;
300
278
  if (groupValue && (typeof groupValue == 'string' || typeof groupValue == 'number' || typeof groupValue == 'bigint' || (typeof groupValue == 'object' && groupValue.value)))
301
279
  prepareGroup(spec.group, rows);
302
- spec.page.count = (ada.count > 0 && spec.page.size > 0) ? Math.ceil(ada.count / spec.page.size) : 0;
280
+ spec.page.count = (ada.count > 0 && pageSize > 0) ? Math.ceil(ada.count / pageSize) : 0;
303
281
  spec.page.current = currentPage;
304
282
  spec.status = TablevuStatus.Ready;
305
283
  spec.time = Date.now();
@@ -361,8 +339,8 @@ const tablevu = {
361
339
  o.style['text-align'] = col.align;
362
340
  if (col.width)
363
341
  o.style['max-width'] = col.width;
364
- const f = col.render(row);
365
- return h('td', o, f);
342
+ const f = col.render?.(row) ?? '';
343
+ return h('td', { ...o, innerHTML: f });
366
344
  }
367
345
  function renderGroup(grp) {
368
346
  const b = h('button', {
@@ -421,6 +399,10 @@ const tablevu = {
421
399
  }
422
400
  };
423
401
  }
402
+ // Selection
403
+ if (spec.selectedRows.some(s => s[props.rowid || 'id'] === row[props.rowid || 'id'])) {
404
+ o.class = (o.class || '') + ' selected';
405
+ }
424
406
  return h('tr', o, r);
425
407
  }
426
408
  function renderHeader() {
@@ -471,7 +453,10 @@ const tablevu = {
471
453
  else
472
454
  a = h('a', { href: '#', onClick: (event) => { orderby(i); event.preventDefault(); }, style: cy }, cl);
473
455
  if (spec.columns[i].filter) ;
474
- ar.push(h('th', { colspan: null, style: ch }, [a, f]));
456
+ ar.push(h('th', {
457
+ colspan: null,
458
+ style: ch
459
+ }, [a, f]));
475
460
  }
476
461
  const tr = h('tr', {}, ar);
477
462
  return [tr];
@@ -499,14 +484,34 @@ const tablevu = {
499
484
  }
500
485
  }
501
486
  }
487
+ else if (spec.settings?.emptyMessage) {
488
+ dr.push(h('tr', {}, h('td', { colspan: spec.columns.length, style: { textAlign: 'center', padding: '2rem', color: '#666' } }, spec.settings.emptyMessage)));
489
+ }
502
490
  return dr;
503
491
  }
492
+ // Prompt user for page number and navigate
493
+ function goToPage() {
494
+ const input = prompt('Page number:', String(spec.page.current));
495
+ if (input) {
496
+ const pageNum = parseInt(input, 10);
497
+ if (!isNaN(pageNum) && pageNum > 0 && pageNum !== spec.page.current) {
498
+ page(pageNum);
499
+ }
500
+ }
501
+ }
502
+ function makeHandler(fn) {
503
+ return (e) => {
504
+ e.preventDefault();
505
+ e.stopPropagation();
506
+ fn();
507
+ };
508
+ }
504
509
  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 fr = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(first), disabled: spec.page.current == 1 }, '◀◀');
511
+ const ls = 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 }, '▶▶');
512
+ const pg = 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) : ''));
513
+ const pr = h('button', { class: spec.footer.button.class, style: 'font: initial; margin-right: .5rem', onClick: makeHandler(prior), disabled: spec.page.current == 1 }, '◀');
514
+ const nx = 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 }, '▶');
510
515
  const td = h('td', { colspan: spec.columns.length, align: 'center' }, [fr, pr, pg, nx, ls]);
511
516
  const tr = h('tr', {}, [td]);
512
517
  return [tr];
@@ -514,19 +519,41 @@ const tablevu = {
514
519
  function renderCaption() {
515
520
  return null;
516
521
  }
522
+ // Initialize column metadata
517
523
  if (spec.columns.length == 0) {
518
524
  console.error('Columns not defined !');
519
525
  }
520
- for (let i = 0; i < spec.columns.length; i++)
526
+ for (let i = 0; i < spec.columns.length; i++) {
521
527
  if (spec.columns[i].name && spec.columns[i].name.indexOf('.') > -1) {
522
528
  spec.columns[i].depth = spec.columns[i].name.split('.');
523
529
  }
524
- page(1);
530
+ // Initialize visibility
531
+ if (spec.columns[i].visible === undefined) {
532
+ spec.columns[i].visible = true;
533
+ spec.columnVisibility[spec.columns[i].name] = true;
534
+ }
535
+ }
536
+ loadPage(1);
537
+ // Expose methods for external access via ref
538
+ expose({
539
+ first,
540
+ prior,
541
+ next,
542
+ last,
543
+ page,
544
+ refresh,
545
+ orderby,
546
+ getSelectedRows: () => spec.selectedRows,
547
+ setSelectedRows: (rows) => { spec.selectedRows = rows; }
548
+ });
525
549
  onMounted(() => {
526
550
  // mounted hook
527
551
  });
528
552
  return {
529
- spec, renderHeader, renderBody, renderFooter, renderCaption
553
+ spec, renderHeader, renderBody, renderFooter, renderCaption,
554
+ first, prior, next, last, page, refresh, orderby,
555
+ getSelectedRows: () => spec.selectedRows,
556
+ setSelectedRows: (rows) => { spec.selectedRows = rows; }
530
557
  };
531
558
  },
532
559
  render(props) {
@@ -683,18 +710,18 @@ function buildFilter(filter) {
683
710
  * Logical
684
711
  * ========================================================================== */
685
712
  function buildLogical(filter, operator) {
686
- const filters = requireFilters(filter);
713
+ const filters = filter.filters || [];
687
714
  if (filters.length === 0)
688
- throw new Error(`Operator '${filter.operator}' requires at least one filter.`);
715
+ return '';
689
716
  return filters
690
717
  .map(buildFilter)
691
718
  .map(expression => `(${expression})`)
692
719
  .join(` ${operator} `);
693
720
  }
694
721
  function buildNot(filter) {
695
- const filters = requireFilters(filter);
722
+ const filters = filter.filters || [];
696
723
  if (filters.length !== 1)
697
- throw new Error("Operator 'not' requires exactly one filter.");
724
+ return '';
698
725
  return `not (${buildFilter(filters[0])})`;
699
726
  }
700
727
  /* ==========================================================================
@@ -785,9 +812,9 @@ function buildAny(filter) {
785
812
  * ========================================================================== */
786
813
  function buildAll(filter) {
787
814
  const column = getColumn(filter);
788
- const filters = requireFilters(filter);
815
+ const filters = filter.filters || [];
789
816
  if (filters.length !== 1)
790
- throw new Error("Operator 'all' requires exactly one filter.");
817
+ return '';
791
818
  return (`${column}/all(x: ` +
792
819
  `${buildLambdaFilter(filters[0], 'x')}` +
793
820
  `)`);
@@ -904,9 +931,7 @@ function getLambdaColumn(filter, variable) {
904
931
  `${getColumn(filter)}`);
905
932
  }
906
933
  function requireFilters(filter) {
907
- if (!filter.filters)
908
- throw new Error(`Operator '${filter.operator}' requires filters.`);
909
- return filter.filters;
934
+ return filter.filters || [];
910
935
  }
911
936
  /* ==========================================================================
912
937
  * OData Request
@@ -970,7 +995,7 @@ function odata(options, props = {}) {
970
995
  * Static filter from options
971
996
  */
972
997
  if (options.filter &&
973
- (options.filter.filters ||
998
+ ((options.filter.filters && options.filter.filters.length > 0) ||
974
999
  options.filter.column ||
975
1000
  options.filter.func)) {
976
1001
  const expression = toODataFilter(options.filter);
@@ -981,7 +1006,7 @@ function odata(options, props = {}) {
981
1006
  * Runtime filter from DataTable
982
1007
  */
983
1008
  if (props.filter &&
984
- (props.filter.filters ||
1009
+ ((props.filter.filters && props.filter.filters.length > 0) ||
985
1010
  props.filter.column ||
986
1011
  props.filter.func)) {
987
1012
  const expression = toODataFilter(props.filter);
@@ -1052,7 +1077,8 @@ function odata(options, props = {}) {
1052
1077
  /* ---------------------------------------------------------------------- */
1053
1078
  /* Request */
1054
1079
  /* ---------------------------------------------------------------------- */
1055
- return new Request(url, options.header);
1080
+ const requestInit = options.headers ? { headers: options.headers } : undefined;
1081
+ return new Request(url, requestInit);
1056
1082
  }
1057
1083
 
1058
1084
  const param = (uri, header, props) => {
@@ -1073,4 +1099,4 @@ const param = (uri, header, props) => {
1073
1099
  return new Request(url, header);
1074
1100
  };
1075
1101
 
1076
- export { tablevu as TableVu, TablevuStatus, adaptArray, toODataFilter as buildFilter, odata, param as webapi };
1102
+ export { tablevu as TableVu, adaptArray, toODataFilter as buildFilter, odata, param as webapi };