@bexis2/bexis2-core-ui 0.4.95 → 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 (89) hide show
  1. package/README.md +105 -24
  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 +31 -5
  6. package/dist/components/Table/TableContent.svelte +226 -27
  7. package/dist/components/Table/TableFilter.svelte +25 -12
  8. package/dist/components/Table/TableFilterServer.svelte +51 -6
  9. package/dist/components/Table/TablePaginationServer.svelte +27 -8
  10. package/dist/components/Table/clientDB.d.ts +22 -0
  11. package/dist/components/Table/clientDB.js +236 -0
  12. package/dist/components/Table/filter.js +21 -2
  13. package/dist/components/Table/tableWorker.d.ts +1 -0
  14. package/dist/components/Table/tableWorker.js +179 -0
  15. package/dist/components/Table/utils.d.ts +1 -2
  16. package/dist/components/Table/utils.js +64 -31
  17. package/dist/components/form/CheckboxKvPList.svelte +15 -15
  18. package/dist/components/form/CheckboxList.svelte +1 -1
  19. package/dist/components/form/DateInput.svelte +12 -1
  20. package/dist/components/form/DatePickerInput.svelte +51 -29
  21. package/dist/components/form/Dropdown.svelte +14 -3
  22. package/dist/components/form/DropdownKvP.svelte +13 -2
  23. package/dist/components/form/InputContainer.svelte +17 -15
  24. package/dist/components/form/MultiSelect.svelte +30 -29
  25. package/dist/components/form/NumberInput.svelte +12 -1
  26. package/dist/components/form/TextArea.svelte +12 -1
  27. package/dist/components/form/TextInput.svelte +12 -1
  28. package/dist/components/page/Alert.svelte +5 -1
  29. package/dist/components/page/BackToTop.svelte +3 -1
  30. package/dist/components/page/Docs.svelte +9 -2
  31. package/dist/components/page/GoToTop.svelte +14 -15
  32. package/dist/components/page/Page.svelte +50 -56
  33. package/dist/components/page/breadcrumb/BreadcrumbDataCaller.js +11 -15
  34. package/dist/components/page/menu/MenuAccountBar.svelte +1 -5
  35. package/dist/components/page/menu/MenuDataCaller.js +1 -1
  36. package/dist/components/page/menu/MenuItem.svelte +9 -7
  37. package/dist/components/page/menu/MenuSublist.svelte +7 -5
  38. package/dist/components/page/menu/SettingsBar.svelte +4 -1
  39. package/dist/components/toggle/Toggle.svelte +9 -9
  40. package/dist/css/core.ui.postcss +1 -2
  41. package/dist/services/Api.js +4 -4
  42. package/dist/services/BaseCaller.js +1 -1
  43. package/package.json +114 -114
  44. package/src/lib/components/CodeEditor/CodeEditor.svelte +4 -4
  45. package/src/lib/components/Facets/Facets.svelte +2 -2
  46. package/src/lib/components/Facets/ShowMore.svelte +4 -6
  47. package/src/lib/components/File/FileUploader.svelte +17 -14
  48. package/src/lib/components/Table/Table.svelte +32 -5
  49. package/src/lib/components/Table/TableContent.svelte +261 -38
  50. package/src/lib/components/Table/TableFilter.svelte +34 -8
  51. package/src/lib/components/Table/TableFilterServer.svelte +65 -9
  52. package/src/lib/components/Table/TablePagination.svelte +6 -2
  53. package/src/lib/components/Table/TablePaginationServer.svelte +36 -10
  54. package/src/lib/components/Table/clientDB.js +236 -0
  55. package/src/lib/components/Table/filter.ts +44 -24
  56. package/src/lib/components/Table/tableWorker.js +179 -0
  57. package/src/lib/components/Table/utils.ts +80 -46
  58. package/src/lib/components/form/Checkbox.svelte +1 -1
  59. package/src/lib/components/form/CheckboxKvPList.svelte +15 -16
  60. package/src/lib/components/form/CheckboxList.svelte +1 -1
  61. package/src/lib/components/form/DateInput.svelte +13 -2
  62. package/src/lib/components/form/DatePickerInput.svelte +51 -29
  63. package/src/lib/components/form/Dropdown.svelte +14 -3
  64. package/src/lib/components/form/DropdownKvP.svelte +13 -2
  65. package/src/lib/components/form/InputContainer.svelte +18 -17
  66. package/src/lib/components/form/MultiSelect.svelte +30 -29
  67. package/src/lib/components/form/NumberInput.svelte +21 -12
  68. package/src/lib/components/form/TextArea.svelte +13 -2
  69. package/src/lib/components/form/TextInput.svelte +13 -2
  70. package/src/lib/components/page/Alert.svelte +5 -1
  71. package/src/lib/components/page/BackToTop.svelte +3 -1
  72. package/src/lib/components/page/Docs.svelte +9 -2
  73. package/src/lib/components/page/GoToTop.svelte +14 -15
  74. package/src/lib/components/page/Notification.svelte +1 -1
  75. package/src/lib/components/page/Page.svelte +67 -78
  76. package/src/lib/components/page/breadcrumb/Breadcrumb.svelte +2 -3
  77. package/src/lib/components/page/breadcrumb/BreadcrumbDataCaller.js +11 -15
  78. package/src/lib/components/page/menu/MenuAccountBar.svelte +1 -6
  79. package/src/lib/components/page/menu/MenuDataCaller.js +1 -1
  80. package/src/lib/components/page/menu/MenuItem.svelte +10 -8
  81. package/src/lib/components/page/menu/MenuSublist.svelte +35 -41
  82. package/src/lib/components/page/menu/SettingsBar.svelte +5 -2
  83. package/src/lib/components/toggle/Toggle.svelte +28 -30
  84. package/src/lib/css/core.ui.postcss +1 -2
  85. package/src/lib/index.ts +1 -2
  86. package/src/lib/services/Api.ts +21 -20
  87. package/src/lib/services/BaseCaller.js +1 -1
  88. package/src/lib/stores/apiStores.ts +1 -5
  89. 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}
@@ -28,7 +28,7 @@
28
28
 
29
29
  // Check the type of the column
30
30
  $values.forEach((item) => {
31
- if (item) {
31
+ if (item !== null && item !== undefined) {
32
32
  type = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
33
33
 
34
34
  if (type === 'object') {
@@ -119,6 +119,16 @@
119
119
  value: FilterOptionsEnum.no,
120
120
  label: 'Is not on'
121
121
  }
122
+ ],
123
+ boolean: [
124
+ {
125
+ value: FilterOptionsEnum.e,
126
+ label: 'Is equal to'
127
+ },
128
+ {
129
+ value: FilterOptionsEnum.ne,
130
+ label: 'Is not equal to'
131
+ }
122
132
  ]
123
133
  };
124
134
 
@@ -156,13 +166,15 @@
156
166
 
157
167
  const optionChangeHandler = (e, index) => {
158
168
  delete $filters[id][dropdowns[index].option];
169
+ const val = dropdowns[index].value;
159
170
  $filters[id] = {
160
171
  ...$filters[id],
161
172
  [e.target.value]:
162
- // type === 'number'
163
- // ?
164
- getMissingValue(dropdowns[index].value as string)
165
- // : dropdowns[index].value
173
+ type === 'boolean'
174
+ ? val === '' || val === undefined
175
+ ? undefined
176
+ : getMissingValue(val as string) === 'true' || getMissingValue(val as string) === true
177
+ : getMissingValue(val as string)
166
178
  };
167
179
  $filters = $filters;
168
180
 
@@ -184,9 +196,12 @@
184
196
  [id]: {
185
197
  ...$filters[id],
186
198
  [dropdowns[index].option]:
187
- // type === 'number' ?
188
- getMissingValue(e.target.value)
189
- // : dropdowns[index].value
199
+ type === 'boolean'
200
+ ? e.target.value === ''
201
+ ? undefined
202
+ : getMissingValue(e.target.value) === 'true' ||
203
+ getMissingValue(e.target.value) === true
204
+ : getMissingValue(e.target.value)
190
205
  }
191
206
  };
192
207
  };
@@ -304,6 +319,17 @@
304
319
  bind:value={dropdown.value}
305
320
  aria-label="Filter value"
306
321
  />
322
+ {:else if type === 'boolean'}
323
+ <select
324
+ class="select border border-primary-500 text-sm p-1"
325
+ on:change={(e) => valueChangeHandler(e, index)}
326
+ bind:value={dropdown.value}
327
+ aria-label="Filter value"
328
+ >
329
+ <option value="" disabled selected>Select value...</option>
330
+ <option value="true">True</option>
331
+ <option value="false">False</option>
332
+ </select>
307
333
  {:else}
308
334
  <input
309
335
  type="date"
@@ -28,8 +28,11 @@
28
28
 
29
29
  // Check the type of the column
30
30
  $values.forEach((item) => {
31
- if (item) {
32
- type = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
31
+ if (item !== null && item !== undefined) {
32
+ const rawType = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
33
+
34
+ // Treat bigint exactly as 'number' for layout options
35
+ type = rawType === 'bigint' ? 'number' : rawType;
33
36
 
34
37
  if (type === 'object') {
35
38
  if ((toFilterableValueFn ? toFilterableValueFn(item) : item) instanceof Date) {
@@ -119,6 +122,16 @@
119
122
  // value: FilterOptionsEnum.no,
120
123
  // label: 'Is not on'
121
124
  // }
125
+ ],
126
+ boolean: [
127
+ {
128
+ value: FilterOptionsEnum.e,
129
+ label: 'Is equal to'
130
+ },
131
+ {
132
+ value: FilterOptionsEnum.ne,
133
+ label: 'Is not equal to'
134
+ }
122
135
  ]
123
136
  };
124
137
 
@@ -134,7 +147,9 @@
134
147
  const stringValues = $values.map((item) => (toStringFn ? toStringFn(item) : item));
135
148
 
136
149
  const missingValues = stringValues.reduce((acc, item, index) => {
137
- acc[typeof item === 'string' ? item.toLowerCase() : item] = $values[index];
150
+ // Convert everything (including BigInt values) to string first
151
+ const strRepresentation = String(item).toLowerCase();
152
+ acc[strRepresentation] = $values[index];
138
153
  return acc;
139
154
  }, {});
140
155
 
@@ -146,9 +161,15 @@
146
161
 
147
162
  const optionChangeHandler = (e, index) => {
148
163
  delete $filters[id][dropdowns[index].option];
164
+ const val = dropdowns[index].value;
149
165
  $filters[id] = {
150
166
  ...$filters[id],
151
- [e.target.value]: getMissingValue(dropdowns[index].value as string)
167
+ [e.target.value]:
168
+ type === 'boolean'
169
+ ? val === '' || val === undefined
170
+ ? undefined
171
+ : getMissingValue(val as string) === 'true' || getMissingValue(val as string) === true
172
+ : getMissingValue(dropdowns[index].value as string)
152
173
  };
153
174
  $filters = $filters;
154
175
 
@@ -169,10 +190,34 @@
169
190
  ...$filters,
170
191
  [id]: {
171
192
  ...$filters[id],
172
- [dropdowns[index].option]:
173
- type === 'number'
174
- ? parseFloat(getMissingValue(e.target.value)) || undefined
175
- : getMissingValue(e.target.value)
193
+ [dropdowns[index].option]: (() => {
194
+ const rawVal = getMissingValue(e.target.value);
195
+
196
+ if (rawVal === '' || rawVal === undefined || rawVal === null) {
197
+ return undefined;
198
+ }
199
+
200
+ if (type === 'number') {
201
+ const cleanStr = String(rawVal).trim();
202
+ // If it's a 16+ digit integer, parse as BigInt to keep precision
203
+ const isBigInt = /^\d+$/.test(cleanStr) && cleanStr.length >= 16;
204
+
205
+ if (isBigInt) {
206
+ try {
207
+ return BigInt(cleanStr);
208
+ } catch {
209
+ return cleanStr;
210
+ }
211
+ }
212
+ return parseFloat(cleanStr) || undefined;
213
+ }
214
+
215
+ if (type === 'boolean') {
216
+ return rawVal === 'true' || rawVal === true;
217
+ }
218
+
219
+ return rawVal;
220
+ })()
176
221
  }
177
222
  };
178
223
  };
@@ -284,7 +329,7 @@
284
329
  on:click|preventDefault={() => removeFilter(dropdown.option)}
285
330
  on:keydown|preventDefault={() => removeFilter(dropdown.option)}
286
331
  aria-label="Remove filter"
287
- >
332
+ >
288
333
  <Fa icon={faXmark} />
289
334
  </div>
290
335
  {/if}
@@ -305,6 +350,17 @@
305
350
  bind:value={dropdown.value}
306
351
  aria-label="Filter value"
307
352
  />
353
+ {:else if type === 'boolean'}
354
+ <select
355
+ class="select border border-primary-500 text-sm p-1"
356
+ on:change={(e) => valueChangeHandler(e, index)}
357
+ bind:value={dropdown.value}
358
+ aria-label="Filter value"
359
+ >
360
+ <option value="" disabled selected>Select value...</option>
361
+ <option value="true">True</option>
362
+ <option value="false">False</option>
363
+ </select>
308
364
  {:else}
309
365
  <input
310
366
  type="date"
@@ -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">