@bexis2/bexis2-core-ui 0.4.96 → 0.4.98
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/README.md +104 -25
- package/dist/components/CodeEditor/CodeEditor.svelte +2 -2
- package/dist/components/Facets/ShowMore.svelte +4 -6
- package/dist/components/File/FileUploader.svelte +6 -3
- package/dist/components/Table/Table.svelte +26 -5
- package/dist/components/Table/TableContent.svelte +222 -22
- package/dist/components/Table/TablePaginationServer.svelte +27 -8
- package/dist/components/Table/clientDB.d.ts +22 -0
- package/dist/components/Table/clientDB.js +236 -0
- package/dist/components/Table/filter.js +5 -1
- package/dist/components/Table/tableWorker.d.ts +1 -0
- package/dist/components/Table/tableWorker.js +179 -0
- package/dist/components/Table/utils.d.ts +1 -2
- package/dist/components/Table/utils.js +45 -26
- package/dist/components/form/CheckboxKvPList.svelte +15 -15
- package/dist/components/form/CheckboxList.svelte +1 -1
- package/dist/components/form/DateInput.svelte +12 -1
- package/dist/components/form/DatePickerInput.svelte +51 -29
- package/dist/components/form/Dropdown.svelte +14 -3
- package/dist/components/form/DropdownKvP.svelte +13 -2
- package/dist/components/form/InputContainer.svelte +17 -15
- package/dist/components/form/MultiSelect.svelte +30 -29
- package/dist/components/form/NumberInput.svelte +12 -1
- package/dist/components/form/TextArea.svelte +12 -1
- package/dist/components/form/TextInput.svelte +12 -1
- package/dist/components/page/Alert.svelte +5 -1
- package/dist/components/page/BackToTop.svelte +3 -1
- package/dist/components/page/Docs.svelte +9 -2
- package/dist/components/page/GoToTop.svelte +14 -15
- package/dist/components/page/Page.svelte +50 -56
- package/dist/components/page/breadcrumb/BreadcrumbDataCaller.js +11 -15
- package/dist/components/page/menu/MenuAccountBar.svelte +1 -5
- package/dist/components/page/menu/MenuDataCaller.js +1 -1
- package/dist/components/page/menu/MenuItem.svelte +9 -7
- package/dist/components/page/menu/MenuSublist.svelte +7 -5
- package/dist/components/page/menu/SettingsBar.svelte +4 -1
- package/dist/components/toggle/Toggle.svelte +9 -9
- package/dist/css/core.ui.postcss +1 -2
- package/dist/services/Api.js +4 -4
- package/dist/services/BaseCaller.js +1 -1
- package/package.json +114 -114
- package/src/lib/components/CodeEditor/CodeEditor.svelte +4 -4
- package/src/lib/components/Facets/Facets.svelte +2 -2
- package/src/lib/components/Facets/ShowMore.svelte +4 -6
- package/src/lib/components/File/FileUploader.svelte +17 -14
- package/src/lib/components/Table/Table.svelte +31 -10
- package/src/lib/components/Table/TableContent.svelte +258 -33
- package/src/lib/components/Table/TableFilter.svelte +7 -2
- package/src/lib/components/Table/TablePagination.svelte +6 -2
- package/src/lib/components/Table/TablePaginationServer.svelte +36 -10
- package/src/lib/components/Table/clientDB.js +236 -0
- package/src/lib/components/Table/filter.ts +27 -23
- package/src/lib/components/Table/tableWorker.js +179 -0
- package/src/lib/components/Table/utils.ts +75 -56
- package/src/lib/components/form/Checkbox.svelte +1 -1
- package/src/lib/components/form/CheckboxKvPList.svelte +15 -16
- package/src/lib/components/form/CheckboxList.svelte +1 -1
- package/src/lib/components/form/DateInput.svelte +13 -2
- package/src/lib/components/form/DatePickerInput.svelte +51 -29
- package/src/lib/components/form/Dropdown.svelte +14 -3
- package/src/lib/components/form/DropdownKvP.svelte +13 -2
- package/src/lib/components/form/InputContainer.svelte +18 -17
- package/src/lib/components/form/MultiSelect.svelte +30 -29
- package/src/lib/components/form/NumberInput.svelte +21 -12
- package/src/lib/components/form/TextArea.svelte +13 -2
- package/src/lib/components/form/TextInput.svelte +13 -2
- package/src/lib/components/page/Alert.svelte +5 -1
- package/src/lib/components/page/BackToTop.svelte +3 -1
- package/src/lib/components/page/Docs.svelte +9 -2
- package/src/lib/components/page/GoToTop.svelte +14 -15
- package/src/lib/components/page/Notification.svelte +1 -1
- package/src/lib/components/page/Page.svelte +67 -78
- package/src/lib/components/page/breadcrumb/Breadcrumb.svelte +2 -3
- package/src/lib/components/page/breadcrumb/BreadcrumbDataCaller.js +11 -15
- package/src/lib/components/page/menu/MenuAccountBar.svelte +1 -6
- package/src/lib/components/page/menu/MenuDataCaller.js +1 -1
- package/src/lib/components/page/menu/MenuItem.svelte +10 -8
- package/src/lib/components/page/menu/MenuSublist.svelte +35 -41
- package/src/lib/components/page/menu/SettingsBar.svelte +5 -2
- package/src/lib/components/toggle/Toggle.svelte +28 -30
- package/src/lib/css/core.ui.postcss +1 -2
- package/src/lib/index.ts +1 -2
- package/src/lib/services/Api.ts +21 -20
- package/src/lib/services/BaseCaller.js +1 -1
- package/src/lib/stores/apiStores.ts +1 -5
- package/src/lib/stores/pageStores.ts +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { afterUpdate, createEventDispatcher, onDestroy, onMount } from 'svelte';
|
|
3
|
-
import {
|
|
3
|
+
import { writable } from 'svelte/store';
|
|
4
4
|
|
|
5
5
|
import Fa from 'svelte-fa';
|
|
6
6
|
import { faCompress, faDownload, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
} from 'svelte-headless-table/plugins';
|
|
17
17
|
import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
|
|
18
18
|
import { SlideToggle, storePopup } from '@skeletonlabs/skeleton';
|
|
19
|
-
import type { PaginationConfig } from 'svelte-headless-table/lib/plugins/addPagination';
|
|
20
19
|
|
|
21
20
|
storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
|
|
22
21
|
|
|
@@ -26,6 +25,7 @@
|
|
|
26
25
|
import TablePagination from './TablePagination.svelte';
|
|
27
26
|
import TablePaginationServer from './TablePaginationServer.svelte';
|
|
28
27
|
import ColumnsMenu from './ColumnsMenu.svelte';
|
|
28
|
+
import ClientDB from './clientDB.js';
|
|
29
29
|
import * as utils from './utils';
|
|
30
30
|
import { columnFilter, searchFilter } from './filter';
|
|
31
31
|
import { Send } from '$models/Models';
|
|
@@ -53,19 +53,30 @@
|
|
|
53
53
|
server
|
|
54
54
|
} = config;
|
|
55
55
|
|
|
56
|
+
// `clientDb` is optional and may not be part of the `TableConfig` type
|
|
57
|
+
const clientDb = (config as any).clientDb ?? false;
|
|
58
|
+
const clientDbSeedData = (config as any).clientDbSeedData ?? [];
|
|
59
|
+
const initialServerCount = Number((config as any).__initialServerCount ?? 0);
|
|
60
|
+
|
|
56
61
|
let searchValue = '';
|
|
57
62
|
let isFetching = false;
|
|
58
63
|
let tableRef: HTMLTableElement;
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
let serverItemCountValue = Number.isFinite(initialServerCount) ? initialServerCount : 0;
|
|
65
|
+
|
|
66
|
+
const serverSide = server !== undefined || clientDb;
|
|
67
|
+
let _clientDbInstance: any = null;
|
|
68
|
+
let clientDbEnabled = clientDb;
|
|
69
|
+
let clientDbReady = false;
|
|
70
|
+
let clientDbInitSource: any[] = [];
|
|
71
|
+
let clientDbSeedSize = 0;
|
|
61
72
|
const { sendModel = new Send() } = server ?? {};
|
|
62
73
|
|
|
63
74
|
const filters = writable<{
|
|
64
75
|
[key: string]: { [key in FilterOptionsEnum]?: number | string | Date };
|
|
65
76
|
}>({});
|
|
66
77
|
|
|
67
|
-
//
|
|
68
|
-
type AccessorType =
|
|
78
|
+
// Accessor keys are handled as string keys to avoid heavy type coupling to full dataset shape
|
|
79
|
+
type AccessorType = string;
|
|
69
80
|
|
|
70
81
|
// Creating a dispatcher to dispatch actions to the parent component
|
|
71
82
|
const dispatch = createEventDispatcher();
|
|
@@ -76,12 +87,8 @@
|
|
|
76
87
|
const colWidths = writable<number[]>([]);
|
|
77
88
|
|
|
78
89
|
// Server-side variables
|
|
79
|
-
const serverItems = serverSide ? writable<number>(0) : undefined;
|
|
80
|
-
const serverItemCount =
|
|
81
|
-
? readable<Number>(0, (set) => {
|
|
82
|
-
serverItems!.subscribe((val) => set(val));
|
|
83
|
-
})
|
|
84
|
-
: undefined;
|
|
90
|
+
const serverItems = serverSide ? writable<number>(Number.isFinite(initialServerCount) ? initialServerCount : 0) : undefined;
|
|
91
|
+
const serverItemCount = serverItems;
|
|
85
92
|
|
|
86
93
|
// Initializing the table
|
|
87
94
|
const table = createTable(data, {
|
|
@@ -99,14 +106,13 @@
|
|
|
99
106
|
initialPageSize: defaultPageSize,
|
|
100
107
|
serverSide,
|
|
101
108
|
serverItemCount
|
|
102
|
-
} as
|
|
109
|
+
} as any),
|
|
103
110
|
expand: addExpandedRows(),
|
|
104
111
|
export: addDataExport({ format: 'json' })
|
|
105
112
|
});
|
|
106
113
|
|
|
107
|
-
//
|
|
114
|
+
// Build column keys from the incoming data model and merge explicit config as extras.
|
|
108
115
|
const allCols: { [key: string]: any } = {};
|
|
109
|
-
// Iterating over each item to get all the possible keys
|
|
110
116
|
$data.forEach((item) => {
|
|
111
117
|
Object.keys(item).forEach((key) => {
|
|
112
118
|
if (!allCols[key]) {
|
|
@@ -115,6 +121,17 @@
|
|
|
115
121
|
});
|
|
116
122
|
});
|
|
117
123
|
|
|
124
|
+
if (columns && Object.keys(columns).length > 0) {
|
|
125
|
+
Object.keys(columns).forEach((key) => {
|
|
126
|
+
if (optionsComponent !== undefined && key === 'optionsColumn') {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!allCols[key]) {
|
|
130
|
+
allCols[key] = {};
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
Object.keys(allCols).forEach((key) => {
|
|
119
136
|
$filters = { ...$filters, [key]: {} };
|
|
120
137
|
});
|
|
@@ -269,11 +286,23 @@
|
|
|
269
286
|
})
|
|
270
287
|
];
|
|
271
288
|
|
|
289
|
+
const getUniqueOptionsColumnId = () => {
|
|
290
|
+
const used = new Set<string>(unexcludedColumns.map((col) => String(col)));
|
|
291
|
+
let id = 'optionsColumn';
|
|
292
|
+
let i = 1;
|
|
293
|
+
while (used.has(id)) {
|
|
294
|
+
id = `optionsColumn_${i}`;
|
|
295
|
+
i += 1;
|
|
296
|
+
}
|
|
297
|
+
return id;
|
|
298
|
+
};
|
|
299
|
+
|
|
272
300
|
// If optionsComponent is provided, add a column for it at the end
|
|
273
301
|
if (optionsComponent !== undefined) {
|
|
302
|
+
const optionsColumnId = getUniqueOptionsColumnId();
|
|
274
303
|
tableColumns.push(
|
|
275
304
|
table.display({
|
|
276
|
-
id:
|
|
305
|
+
id: optionsColumnId,
|
|
277
306
|
header: '',
|
|
278
307
|
plugins: {
|
|
279
308
|
export: {
|
|
@@ -304,7 +333,7 @@
|
|
|
304
333
|
// Column visibility configuration
|
|
305
334
|
const { hiddenColumnIds } = pluginStates.hideColumns;
|
|
306
335
|
|
|
307
|
-
let prevSort: { column: string; direction: 'asc' | 'desc' }[]
|
|
336
|
+
let prevSort: { column: string; direction: 'asc' | 'desc' }[] = [];
|
|
308
337
|
const sortServer = (order: 'asc' | 'desc' | undefined, id: string) => {
|
|
309
338
|
// console.log('Sorting server with', { order, id, sendModel, prevSort });
|
|
310
339
|
if (!sendModel) throw new Error('Server-side configuration is missing');
|
|
@@ -315,18 +344,14 @@
|
|
|
315
344
|
sendModel.order = [{ column: id, direction: order }];
|
|
316
345
|
}
|
|
317
346
|
|
|
318
|
-
if (JSON.stringify(sendModel.order) !== JSON.stringify(prevSort)
|
|
347
|
+
if (JSON.stringify(sendModel.order) !== JSON.stringify(prevSort)) {
|
|
319
348
|
// Reset pagination
|
|
320
349
|
$pageIndex = 0;
|
|
321
350
|
// console.log('Sorting server with', { order, id, sendModel, prevSort });
|
|
322
351
|
$data = [];
|
|
323
352
|
updateTableWithParams();
|
|
324
353
|
}
|
|
325
|
-
|
|
326
|
-
prevSort = [];
|
|
327
|
-
} else {
|
|
328
|
-
prevSort = sendModel.order;
|
|
329
|
-
}
|
|
354
|
+
prevSort = sendModel.order ? [...sendModel.order] : [];
|
|
330
355
|
};
|
|
331
356
|
|
|
332
357
|
// Function to update the table with the provided parameters for easily passing to other components
|
|
@@ -339,13 +364,147 @@
|
|
|
339
364
|
// sendModel
|
|
340
365
|
// });
|
|
341
366
|
|
|
342
|
-
|
|
367
|
+
// If we're using a client-side DB (worker + IndexedDB), route queries there
|
|
368
|
+
if (clientDbEnabled) {
|
|
369
|
+
isFetching = true;
|
|
370
|
+
const filtersNormalized = utils.normalizeFilters($filters);
|
|
371
|
+
const q = sendModel?.q || '';
|
|
372
|
+
const offset = $pageSize * $pageIndex;
|
|
373
|
+
const limit = $pageSize;
|
|
374
|
+
|
|
375
|
+
if (!_clientDbInstance) {
|
|
376
|
+
_clientDbInstance = new ClientDB(tableId);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!clientDbReady) {
|
|
380
|
+
// Fallback while DB is still importing: apply local search/filter/sort for responsiveness.
|
|
381
|
+
const source = clientDbInitSource.length > 0 ? clientDbInitSource : $data;
|
|
382
|
+
|
|
383
|
+
const getValue = (row, column) => {
|
|
384
|
+
const dot = String(column || '').replaceAll('%%%', '.');
|
|
385
|
+
return row[dot] ?? row[column] ?? row[dot.replaceAll('.', '%%%')];
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const localMatches = (row) => {
|
|
389
|
+
if (q && q.length > 0) {
|
|
390
|
+
const ql = String(q).toLowerCase();
|
|
391
|
+
let found = false;
|
|
392
|
+
for (const k in row) {
|
|
393
|
+
const v = row[k];
|
|
394
|
+
if (v == null) continue;
|
|
395
|
+
if (String(v).toLowerCase().includes(ql)) {
|
|
396
|
+
found = true;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (!found) return false;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
for (const f of filtersNormalized) {
|
|
404
|
+
const actual = getValue(row, f.column);
|
|
405
|
+
const val = f.value;
|
|
406
|
+
if (val == null) continue;
|
|
407
|
+
|
|
408
|
+
const aStr = String(actual ?? '').toLowerCase();
|
|
409
|
+
const vStr = String(val ?? '').toLowerCase();
|
|
410
|
+
const aNum = Number(actual);
|
|
411
|
+
const vNum = Number(val);
|
|
412
|
+
const aDate = new Date(actual as any).getTime();
|
|
413
|
+
const vDate = new Date(val as any).getTime();
|
|
414
|
+
|
|
415
|
+
switch (f.filterBy) {
|
|
416
|
+
case 'c':
|
|
417
|
+
if (!aStr.includes(vStr)) return false;
|
|
418
|
+
break;
|
|
419
|
+
case 'nc':
|
|
420
|
+
if (aStr.includes(vStr)) return false;
|
|
421
|
+
break;
|
|
422
|
+
case 'sw':
|
|
423
|
+
if (!aStr.startsWith(vStr)) return false;
|
|
424
|
+
break;
|
|
425
|
+
case 'ew':
|
|
426
|
+
if (!aStr.endsWith(vStr)) return false;
|
|
427
|
+
break;
|
|
428
|
+
case 'e':
|
|
429
|
+
if (actual != val) return false;
|
|
430
|
+
break;
|
|
431
|
+
case 'ne':
|
|
432
|
+
if (actual == val) return false;
|
|
433
|
+
break;
|
|
434
|
+
case 'gt':
|
|
435
|
+
if (Number.isNaN(aNum) || Number.isNaN(vNum) || !(aNum > vNum)) return false;
|
|
436
|
+
break;
|
|
437
|
+
case 'lt':
|
|
438
|
+
if (Number.isNaN(aNum) || Number.isNaN(vNum) || !(aNum < vNum)) return false;
|
|
439
|
+
break;
|
|
440
|
+
case 'gte':
|
|
441
|
+
if (Number.isNaN(aNum) || Number.isNaN(vNum) || !(aNum >= vNum)) return false;
|
|
442
|
+
break;
|
|
443
|
+
case 'lte':
|
|
444
|
+
if (Number.isNaN(aNum) || Number.isNaN(vNum) || !(aNum <= vNum)) return false;
|
|
445
|
+
break;
|
|
446
|
+
case 'o':
|
|
447
|
+
if (Number.isNaN(aDate) || Number.isNaN(vDate) || aDate !== vDate) return false;
|
|
448
|
+
break;
|
|
449
|
+
case 'sf':
|
|
450
|
+
case 'a':
|
|
451
|
+
if (Number.isNaN(aDate) || Number.isNaN(vDate) || !(aDate >= vDate)) return false;
|
|
452
|
+
break;
|
|
453
|
+
case 'u':
|
|
454
|
+
case 'b':
|
|
455
|
+
if (Number.isNaN(aDate) || Number.isNaN(vDate) || !(aDate <= vDate)) return false;
|
|
456
|
+
break;
|
|
457
|
+
case 'no':
|
|
458
|
+
if (Number.isNaN(aDate) || Number.isNaN(vDate) || aDate === vDate) return false;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return true;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const filtered = source.filter(localMatches);
|
|
467
|
+
const order = sendModel?.order || [];
|
|
468
|
+
if (order.length > 0) {
|
|
469
|
+
const { column, direction } = order[0];
|
|
470
|
+
const dir = direction === 'desc' ? -1 : 1;
|
|
471
|
+
filtered.sort((a, b) => {
|
|
472
|
+
const av = getValue(a, column);
|
|
473
|
+
const bv = getValue(b, column);
|
|
474
|
+
if (av == null && bv == null) return 0;
|
|
475
|
+
if (av == null) return -1 * dir;
|
|
476
|
+
if (bv == null) return 1 * dir;
|
|
477
|
+
if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir;
|
|
478
|
+
return String(av).localeCompare(String(bv)) * dir;
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const localRows = filtered.slice(offset, offset + limit);
|
|
483
|
+
serverItems?.set(filtered.length);
|
|
484
|
+
data.set(localRows);
|
|
485
|
+
isFetching = false;
|
|
486
|
+
return { rows: localRows, total: filtered.length };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const res = await _clientDbInstance.query({
|
|
490
|
+
q,
|
|
491
|
+
filters: filtersNormalized,
|
|
492
|
+
order: sendModel?.order || [],
|
|
493
|
+
offset,
|
|
494
|
+
limit
|
|
495
|
+
});
|
|
496
|
+
const payload = res.payload ? res.payload : res;
|
|
497
|
+
serverItems?.set(payload.total ?? payload.totalCount ?? 0);
|
|
498
|
+
data.set(payload.rows ?? []);
|
|
499
|
+
isFetching = false;
|
|
500
|
+
return payload;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if ($data.length > 0 && serverSide && Number($serverItemCount) > 0) {
|
|
343
504
|
console.log('Table is not empty');
|
|
344
|
-
// $data = [];
|
|
345
505
|
return;
|
|
346
506
|
}
|
|
347
507
|
|
|
348
|
-
// throw new Error("Stack trace inspection");
|
|
349
508
|
isFetching = true;
|
|
350
509
|
const result = await utils.updateTable(
|
|
351
510
|
$pageSize,
|
|
@@ -419,6 +578,9 @@
|
|
|
419
578
|
if (resizable !== 'rows' && resizable !== 'both') {
|
|
420
579
|
return;
|
|
421
580
|
}
|
|
581
|
+
if (!$pageRows || $pageRows.length === 0) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
422
584
|
// Making sure tableRef is up to date and contains the new rows
|
|
423
585
|
// If it contains even one element, it means it contains them all
|
|
424
586
|
const e = tableRef?.querySelector(`#${tableId}-row-${$pageRows[0].id}`);
|
|
@@ -430,11 +592,59 @@
|
|
|
430
592
|
onDestroy(() => {
|
|
431
593
|
resizeColumnsObserver.disconnect();
|
|
432
594
|
resizeRowsObserver.disconnect();
|
|
595
|
+
if (_clientDbInstance) {
|
|
596
|
+
try {
|
|
597
|
+
_clientDbInstance.destroy();
|
|
598
|
+
} catch (e) {
|
|
599
|
+
// ignore
|
|
600
|
+
}
|
|
601
|
+
}
|
|
433
602
|
});
|
|
434
603
|
|
|
435
604
|
$: sortKeys = pluginStates.sort.sortKeys;
|
|
436
|
-
|
|
437
|
-
|
|
605
|
+
$: serverItemCountValue = clientDbEnabled
|
|
606
|
+
? Number($serverItemCount) > 0
|
|
607
|
+
? Math.max(
|
|
608
|
+
Number($serverItemCount),
|
|
609
|
+
initialServerCount,
|
|
610
|
+
clientDbSeedSize,
|
|
611
|
+
clientDbInitSource.length,
|
|
612
|
+
$data.length
|
|
613
|
+
)
|
|
614
|
+
: Math.max(initialServerCount, clientDbSeedSize, clientDbInitSource.length, $data.length)
|
|
615
|
+
: Number($serverItemCount);
|
|
616
|
+
onMount(async () => {
|
|
617
|
+
if (clientDbEnabled) {
|
|
618
|
+
const seed = Array.isArray(clientDbSeedData) ? clientDbSeedData : [];
|
|
619
|
+
const hasSeed = seed.length > 0;
|
|
620
|
+
clientDbSeedSize = seed.length;
|
|
621
|
+
if (hasSeed || ($data && $data.length > 0)) {
|
|
622
|
+
// clone source to avoid mutating config-provided arrays
|
|
623
|
+
clientDbInitSource = hasSeed ? [...seed] : [...$data];
|
|
624
|
+
const firstPage = clientDbInitSource.slice(0, $pageSize);
|
|
625
|
+
serverItems?.set(clientDbInitSource.length);
|
|
626
|
+
data.set(firstPage);
|
|
627
|
+
|
|
628
|
+
_clientDbInstance = new ClientDB(tableId);
|
|
629
|
+
_clientDbInstance
|
|
630
|
+
.init(clientDbInitSource)
|
|
631
|
+
.then(async () => {
|
|
632
|
+
// Only mark ready and release seed after the first DB-backed query succeeds.
|
|
633
|
+
const result = await updateTableWithParams();
|
|
634
|
+
clientDbReady = true;
|
|
635
|
+
clientDbInitSource = [];
|
|
636
|
+
return result;
|
|
637
|
+
})
|
|
638
|
+
.catch(() => {
|
|
639
|
+
clientDbReady = false;
|
|
640
|
+
});
|
|
641
|
+
} else if (!_clientDbInstance) {
|
|
642
|
+
_clientDbInstance = new ClientDB(tableId);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Only call server update when a real server config is present
|
|
647
|
+
if (server !== undefined) {
|
|
438
648
|
updateTableWithParams();
|
|
439
649
|
}
|
|
440
650
|
});
|
|
@@ -449,14 +659,19 @@
|
|
|
449
659
|
{#if search}
|
|
450
660
|
<form
|
|
451
661
|
class="flex gap-2"
|
|
452
|
-
on:submit|preventDefault={() => {
|
|
662
|
+
on:submit|preventDefault={async () => {
|
|
453
663
|
if (serverSide && !sendModel) {
|
|
454
664
|
throw new Error('Server-side configuration is missing');
|
|
455
665
|
} else {
|
|
456
666
|
sendModel.q = searchValue;
|
|
457
667
|
}
|
|
458
668
|
|
|
669
|
+
$pageIndex = 0;
|
|
459
670
|
$filterValue = searchValue;
|
|
671
|
+
if (serverSide) {
|
|
672
|
+
$data = [];
|
|
673
|
+
await updateTableWithParams();
|
|
674
|
+
}
|
|
460
675
|
}}
|
|
461
676
|
>
|
|
462
677
|
<div class="relative w-full flex items-center">
|
|
@@ -474,15 +689,20 @@
|
|
|
474
689
|
id="{tableId}-searchReset"
|
|
475
690
|
class="absolute right-3 items-center"
|
|
476
691
|
aria-label="Clear search"
|
|
477
|
-
on:click|preventDefault={() => {
|
|
692
|
+
on:click|preventDefault={async () => {
|
|
478
693
|
if (serverSide && !sendModel) {
|
|
479
694
|
throw new Error('Server-side configuration is missing');
|
|
480
695
|
} else {
|
|
481
696
|
sendModel.q = '';
|
|
482
697
|
}
|
|
483
698
|
|
|
699
|
+
$pageIndex = 0;
|
|
484
700
|
searchValue = '';
|
|
485
701
|
$filterValue = '';
|
|
702
|
+
if (serverSide) {
|
|
703
|
+
$data = [];
|
|
704
|
+
await updateTableWithParams();
|
|
705
|
+
}
|
|
486
706
|
}}><Fa icon={faXmark} /></button
|
|
487
707
|
>
|
|
488
708
|
</div>
|
|
@@ -492,14 +712,19 @@
|
|
|
492
712
|
id="{tableId}-searchSubmit"
|
|
493
713
|
class="btn variant-filled-primary"
|
|
494
714
|
aria-label="Search"
|
|
495
|
-
on:click|preventDefault={() => {
|
|
715
|
+
on:click|preventDefault={async () => {
|
|
496
716
|
if (serverSide && !sendModel) {
|
|
497
717
|
throw new Error('Server-side configuration is missing');
|
|
498
718
|
} else {
|
|
499
719
|
sendModel.q = searchValue;
|
|
500
720
|
}
|
|
501
721
|
|
|
722
|
+
$pageIndex = 0;
|
|
502
723
|
$filterValue = searchValue;
|
|
724
|
+
if (serverSide) {
|
|
725
|
+
$data = [];
|
|
726
|
+
await updateTableWithParams();
|
|
727
|
+
}
|
|
503
728
|
}}>Search</button
|
|
504
729
|
>
|
|
505
730
|
</form>
|
|
@@ -621,7 +846,7 @@
|
|
|
621
846
|
? `Remove sorting by ${cell.label} column`
|
|
622
847
|
: `Sort by ${cell.label} column in ascending order`}
|
|
623
848
|
>
|
|
624
|
-
{cell.render().replaceAll('%%%', '.')}
|
|
849
|
+
{String(cell.render()).replaceAll('%%%', '.')}
|
|
625
850
|
</span>
|
|
626
851
|
<div class="w-2">
|
|
627
852
|
{#if props.sort.order === 'asc'}
|
|
@@ -716,7 +941,7 @@
|
|
|
716
941
|
<TablePaginationServer
|
|
717
942
|
pageConfig={pluginStates.page}
|
|
718
943
|
{pageSizes}
|
|
719
|
-
itemCount={
|
|
944
|
+
itemCount={serverItemCountValue}
|
|
720
945
|
updateTable={updateTableWithParams}
|
|
721
946
|
id={tableId}
|
|
722
947
|
{data}
|
|
@@ -171,7 +171,9 @@
|
|
|
171
171
|
...$filters[id],
|
|
172
172
|
[e.target.value]:
|
|
173
173
|
type === 'boolean'
|
|
174
|
-
?
|
|
174
|
+
? val === '' || val === undefined
|
|
175
|
+
? undefined
|
|
176
|
+
: getMissingValue(val as string) === 'true' || getMissingValue(val as string) === true
|
|
175
177
|
: getMissingValue(val as string)
|
|
176
178
|
};
|
|
177
179
|
$filters = $filters;
|
|
@@ -195,7 +197,10 @@
|
|
|
195
197
|
...$filters[id],
|
|
196
198
|
[dropdowns[index].option]:
|
|
197
199
|
type === 'boolean'
|
|
198
|
-
?
|
|
200
|
+
? e.target.value === ''
|
|
201
|
+
? undefined
|
|
202
|
+
: getMissingValue(e.target.value) === 'true' ||
|
|
203
|
+
getMissingValue(e.target.value) === true
|
|
199
204
|
: getMissingValue(e.target.value)
|
|
200
205
|
}
|
|
201
206
|
};
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
: `Displaying items ${$pageIndex * $pageSize + 1} - ${Math.min(
|
|
29
29
|
($pageIndex + 1) * $pageSize,
|
|
30
30
|
itemCount
|
|
31
|
-
|
|
31
|
+
)} of ${Math.min($pageCount * $pageSize, itemCount)}`;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
$: paginationSettings = {
|
|
@@ -38,7 +38,11 @@
|
|
|
38
38
|
amounts: pageSizes
|
|
39
39
|
};
|
|
40
40
|
$: $pageSize = pageSizeDropdownValue;
|
|
41
|
-
$: $pageCount,
|
|
41
|
+
$: ($pageCount,
|
|
42
|
+
$pageIndex,
|
|
43
|
+
$pageSize,
|
|
44
|
+
itemCount,
|
|
45
|
+
(indexInformation = getIndexInfomationString()));
|
|
42
46
|
</script>
|
|
43
47
|
|
|
44
48
|
<div class="grid grid-cols-3 w-full items-stretch gap-10">
|
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
export let updateTable; // Function to update table
|
|
12
12
|
export let data;
|
|
13
13
|
|
|
14
|
+
const toCount = (value) => {
|
|
15
|
+
if (typeof value === 'number') return Number.isNaN(value) ? 0 : value;
|
|
16
|
+
const parsed = Number(String(value ?? '').replace(/[^0-9.-]/g, ''));
|
|
17
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getEffectiveCount = () => {
|
|
21
|
+
const count = toCount(itemCount);
|
|
22
|
+
if (count > 0) return count;
|
|
23
|
+
// Fallback for edge cases where server count has not propagated yet.
|
|
24
|
+
return Array.isArray($data) ? $data.length : 0;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let effectiveCount = 0;
|
|
28
|
+
|
|
14
29
|
let indexInformation = '';
|
|
15
30
|
|
|
16
31
|
const { pageIndex, pageCount, pageSize } = pageConfig;
|
|
@@ -25,12 +40,13 @@
|
|
|
25
40
|
};
|
|
26
41
|
|
|
27
42
|
const getIndexInfomationString = () => {
|
|
28
|
-
|
|
43
|
+
const count = effectiveCount;
|
|
44
|
+
return count === 0
|
|
29
45
|
? 'No items'
|
|
30
46
|
: `Displaying items ${$pageIndex * $pageSize + 1} - ${Math.min(
|
|
31
47
|
($pageIndex + 1) * $pageSize,
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
count
|
|
49
|
+
)} of ${count}`;
|
|
34
50
|
};
|
|
35
51
|
|
|
36
52
|
function updateTableServer() {
|
|
@@ -38,17 +54,21 @@
|
|
|
38
54
|
updateTable();
|
|
39
55
|
}
|
|
40
56
|
|
|
57
|
+
$: effectiveCount = getEffectiveCount();
|
|
41
58
|
$: paginationSettings = {
|
|
42
|
-
size:
|
|
59
|
+
size: effectiveCount,
|
|
43
60
|
limit: $pageSize,
|
|
44
61
|
page: $pageIndex,
|
|
45
62
|
amounts: pageSizes
|
|
46
63
|
};
|
|
47
64
|
$: $pageSize = pageSizeDropdownValue;
|
|
48
|
-
$: $pageCount,
|
|
65
|
+
$: ($pageCount,
|
|
66
|
+
$pageIndex,
|
|
67
|
+
$pageSize,
|
|
68
|
+
itemCount,
|
|
69
|
+
(indexInformation = getIndexInfomationString()));
|
|
49
70
|
|
|
50
71
|
// updateTable();
|
|
51
|
-
|
|
52
72
|
</script>
|
|
53
73
|
|
|
54
74
|
<div class="grid grid-cols-3 w-full items-stretch gap-10">
|
|
@@ -67,9 +87,12 @@
|
|
|
67
87
|
{#each pageSizes as size}
|
|
68
88
|
<ListBoxItem
|
|
69
89
|
bind:group={pageSizeDropdownValue}
|
|
70
|
-
name="medium"
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
name="medium"
|
|
91
|
+
value={size}
|
|
92
|
+
on:click={() => {
|
|
93
|
+
$pageSize = size;
|
|
94
|
+
updateTableServer();
|
|
95
|
+
}}>{size}</ListBoxItem
|
|
73
96
|
>
|
|
74
97
|
{/each}
|
|
75
98
|
</ListBox>
|
|
@@ -78,7 +101,10 @@
|
|
|
78
101
|
</div>
|
|
79
102
|
<div class="flex justify-center">
|
|
80
103
|
<Paginator
|
|
81
|
-
on:page={(page) => {
|
|
104
|
+
on:page={(page) => {
|
|
105
|
+
$pageIndex = page.detail;
|
|
106
|
+
updateTableServer();
|
|
107
|
+
}}
|
|
82
108
|
settings={paginationSettings}
|
|
83
109
|
select="hidden"
|
|
84
110
|
active="!variant-filled-secondary !text-on-secondary-token"
|