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