@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.
Files changed (86) hide show
  1. package/README.md +104 -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 +222 -22
  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 +258 -33
  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,14 +106,13 @@
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 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: 'optionsColumn',
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' }[] | undefined = undefined;
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) && prevSort !== undefined) {
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
- if (order === undefined) {
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
- if ($data.length > 0 && serverSide) {
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
- onMount(() => {
437
- if (serverSide) {
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={$serverItemCount}
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
- ? (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"