@bexis2/bexis2-core-ui 0.4.96 → 0.4.97

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