@bexis2/bexis2-core-ui 0.4.23 → 0.4.24

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.
@@ -1,4 +1,6 @@
1
1
  import dateFormat from 'dateformat';
2
+ import { SvelteComponent } from 'svelte';
3
+ import { Send, Receive } from '../../models/Models';
2
4
  // Function to determine minWidth for a column to simplify the logic in the HTML
3
5
  export const minWidth = (id, columns) => {
4
6
  if (columns && id in columns) {
@@ -125,6 +127,54 @@ export const missingValuesFn = (key, missingValues) => {
125
127
  : undefined;
126
128
  return foundKey ? missingValues[foundKey] : key;
127
129
  };
130
+ // Function to update the server-side table data
131
+ export const updateTable = async (pageSize, pageIndex, server, filters, data, serverItems, columns, dispatch) => {
132
+ const { baseUrl, entityId, versionId, sendModel = new Send() } = server ?? {};
133
+ if (!sendModel)
134
+ throw new Error('Server-side configuration is missing');
135
+ sendModel.limit = pageSize;
136
+ sendModel.offset = pageSize * pageIndex;
137
+ sendModel.version = versionId || -1;
138
+ sendModel.id = entityId || -1;
139
+ sendModel.filter = normalizeFilters(filters);
140
+ let fetchData;
141
+ try {
142
+ fetchData = await fetch(baseUrl || '', {
143
+ headers: {
144
+ 'Content-Type': 'application/json'
145
+ },
146
+ method: 'POST',
147
+ body: JSON.stringify(sendModel)
148
+ });
149
+ }
150
+ catch (error) {
151
+ throw new Error(`Network error: ${error.message}`);
152
+ }
153
+ if (!fetchData.ok) {
154
+ throw new Error('Failed to fetch data');
155
+ }
156
+ const response = await fetchData.json();
157
+ // Format server columns to the client columns
158
+ if (response.columns !== undefined) {
159
+ columns = convertServerColumns(response.columns, columns);
160
+ const clientCols = response.columns.reduce((acc, col) => {
161
+ acc[col.key] = col.column;
162
+ return acc;
163
+ }, {});
164
+ const tmpArr = [];
165
+ response.data.forEach((row, index) => {
166
+ const tmp = {};
167
+ Object.keys(row).forEach((key) => {
168
+ tmp[clientCols[key]] = row[key];
169
+ });
170
+ tmpArr.push(tmp);
171
+ });
172
+ dispatch('fetch', columns);
173
+ data.set(tmpArr);
174
+ }
175
+ serverItems?.set(response.count);
176
+ return response;
177
+ };
128
178
  export const convertServerColumns = (serverColumns, columns) => {
129
179
  const columnsConfig = {};
130
180
  serverColumns.forEach((col) => {
@@ -177,3 +227,62 @@ export const convertServerColumns = (serverColumns, columns) => {
177
227
  });
178
228
  return columnsConfig;
179
229
  };
230
+ // Calculates the maximum height of the cells in each row
231
+ export const getMaxCellHeightInRow = (tableRef, resizable, optionsComponent, rowHeights, tableId, rowHeight) => {
232
+ if (!tableRef || resizable === 'columns' || resizable === 'none')
233
+ return;
234
+ tableRef.querySelectorAll('tbody tr').forEach((row, index) => {
235
+ const cells = row.querySelectorAll('td');
236
+ let maxHeight = optionsComponent ? 56 : 44;
237
+ let minHeight = optionsComponent ? 56 : 44;
238
+ cells.forEach((cell) => {
239
+ const cellHeight = cell.getBoundingClientRect().height;
240
+ // + 2 pixels for rendering borders correctly
241
+ if (cellHeight > maxHeight) {
242
+ maxHeight = cellHeight + 2;
243
+ }
244
+ if (cellHeight < minHeight) {
245
+ minHeight = cellHeight + 2;
246
+ }
247
+ });
248
+ rowHeights.update((rh) => {
249
+ const id = +row.id.split(`${tableId}-row-`)[1];
250
+ return {
251
+ ...rh,
252
+ [id]: {
253
+ max: maxHeight - 24,
254
+ min: Math.max(minHeight - 24, rowHeight ?? 20)
255
+ }
256
+ };
257
+ });
258
+ });
259
+ };
260
+ // Calculates the minimum width of the cells in each column
261
+ export const getMinCellWidthInColumn = (tableRef, colWidths, headerRowsLength, resizable) => {
262
+ if (!tableRef || resizable === 'rows' || resizable === 'none')
263
+ return;
264
+ // Initialize the column widths if they are not already initialized
265
+ colWidths.update((cw) => {
266
+ if (cw.length === 0) {
267
+ return Array.from({ length: headerRowsLength }, () => 100);
268
+ }
269
+ return cw;
270
+ });
271
+ colWidths.update((cw) => {
272
+ tableRef?.querySelectorAll('thead tr th span').forEach((cell, index) => {
273
+ // + 12 pixels for padding and + 32 pixels for filter icon
274
+ // If the column width is 100, which means it has not been initialized, then calculate the width
275
+ cw[index] = cw[index] === 100 ? cell.getBoundingClientRect().width + 12 + 32 : cw[index];
276
+ });
277
+ return cw;
278
+ });
279
+ };
280
+ export const getResizeStyles = (rowHeights, id, index) => {
281
+ return `
282
+ min-height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
283
+ max-height: ${index !== 0 && rowHeights && rowHeights[+id]
284
+ ? `${rowHeights[+id].max}px`
285
+ : 'auto'};
286
+ height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
287
+ `;
288
+ };
@@ -90,6 +90,7 @@ export interface TableConfig<T> {
90
90
  id: string;
91
91
  data: Writable<T[]>;
92
92
  resizable?: 'none' | 'rows' | 'columns' | 'both';
93
+ showColumnsMenu?: boolean;
93
94
  toggle?: boolean;
94
95
  search?: boolean;
95
96
  fitToScreen?: boolean;
@@ -99,6 +100,7 @@ export interface TableConfig<T> {
99
100
  exportable?: boolean;
100
101
  pageSizes?: number[];
101
102
  defaultPageSize?: number;
103
+ pageIndexStringType?: 'items' | 'pages';
102
104
  optionsComponent?: typeof SvelteComponent;
103
105
  server?: ServerConfig;
104
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bexis2/bexis2-core-ui",
3
- "version": "0.4.23",
3
+ "version": "0.4.24",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -191,10 +191,14 @@
191
191
  hyphenOpacity="opacity-0"
192
192
  bind:checked={selectedGroups[group.name]}
193
193
  bind:group={selectedGroups}
194
+ regionSymbol="shrink-0"
194
195
  >
195
- <p class="font-semibold whitespace-nowrap">
196
- {group.displayName}{group.count !== undefined ? ` (${group.count})` : ''}
197
- </p>
196
+ <div class="grid grid-flow-col gap-2">
197
+ <p class="font-semibold whitespace-nowrap truncate" title={group.displayName}>
198
+ {group.displayName}
199
+ </p>
200
+ <span>{group.count !== undefined ? ` (${group.count})` : ''}</span>
201
+ </div>
198
202
 
199
203
  <svelte:fragment slot="children">
200
204
  <!-- If more than 5 choices, show the remaining in the Modal -->
@@ -210,9 +214,9 @@
210
214
  selection
211
215
  multiple
212
216
  >
213
- <div class="flex gap-2">
214
- <p class="w-max grow truncate">
215
- <span title={item.displayName}>{item.displayName}</span>
217
+ <div class="grid grid-flow-col gap-2">
218
+ <p class="truncate">
219
+ <span class="" title={item.displayName}>{item.displayName}</span>
216
220
  </p>
217
221
  <span>({item.count})</span>
218
222
  </div>
@@ -237,9 +241,9 @@
237
241
  selection
238
242
  multiple
239
243
  >
240
- <div class="flex gap-2">
241
- <p class="w-max grow truncate">
242
- <span title={item.displayName}>{item.displayName}</span>
244
+ <div class="grid grid-flow-col gap-2">
245
+ <p class="truncate">
246
+ <span class="" title={item.displayName}>{item.displayName}</span>
243
247
  </p>
244
248
  <span>({item.count})</span>
245
249
  </div>
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { afterUpdate, onDestroy, createEventDispatcher } from 'svelte';
2
+ import { afterUpdate, createEventDispatcher, onDestroy } from 'svelte';
3
3
  import { readable, writable } from 'svelte/store';
4
4
 
5
5
  import Fa from 'svelte-fa';
@@ -26,18 +26,9 @@
26
26
  import TablePagination from './TablePagination.svelte';
27
27
  import TablePaginationServer from './TablePaginationServer.svelte';
28
28
  import ColumnsMenu from './ColumnsMenu.svelte';
29
+ import * as utils from './utils';
29
30
  import { columnFilter, searchFilter } from './filter';
30
- import {
31
- cellStyle,
32
- exportAsCsv,
33
- jsonToCsv,
34
- fixedWidth,
35
- normalizeFilters,
36
- resetResize,
37
- convertServerColumns,
38
- minWidth
39
- } from './shared';
40
- import { Receive, Send } from '$models/Models';
31
+ import { Send } from '$models/Models';
41
32
  import type { TableConfig } from '$models/Models';
42
33
  import type { FilterOptionsEnum } from '$models/Enums';
43
34
 
@@ -48,6 +39,7 @@
48
39
  id: tableId, // Unique table ID
49
40
  data, // Data store
50
41
  columns, // Column configuration
42
+ showColumnsMenu = false, // Whether to display the columns menu
51
43
  resizable = 'none', // Resizability config
52
44
  height = null, // Table height
53
45
  rowHeight = null, // Row height
@@ -56,6 +48,7 @@
56
48
  toggle = false, // Whether to display the fitToScreen toggle
57
49
  search = true, // Whether to display the search input
58
50
  pageSizes = [5, 10, 20, 50, 100], // Page sizes to display in the pagination component
51
+ pageIndexStringType = 'pages', // pages by default
59
52
  fitToScreen = true, // Whether to fit the table to the screen,
60
53
  exportable = false, // Whether to display the export button and enable export functionality
61
54
  server
@@ -66,7 +59,7 @@
66
59
  let tableRef: HTMLTableElement;
67
60
 
68
61
  const serverSide = server !== undefined;
69
- const { baseUrl, entityId, versionId, sendModel = new Send() } = server ?? {};
62
+ const { sendModel = new Send() } = server ?? {};
70
63
 
71
64
  const filters = writable<{
72
65
  [key: string]: { [key in FilterOptionsEnum]?: number | string | Date };
@@ -84,7 +77,7 @@
84
77
  const colWidths = writable<number[]>([]);
85
78
 
86
79
  // Server-side variables
87
- const serverItems = serverSide ? writable<Number>(0) : undefined;
80
+ const serverItems = serverSide ? writable<number>(0) : undefined;
88
81
  const serverItemCount = serverSide
89
82
  ? readable<Number>(0, (set) => {
90
83
  serverItems!.subscribe((val) => set(val));
@@ -207,7 +200,7 @@
207
200
  id,
208
201
  tableId,
209
202
  values,
210
- updateTable,
203
+ updateTable: updateTableWithParams,
211
204
  pageIndex,
212
205
  toFilterableValueFn,
213
206
  filters,
@@ -241,7 +234,7 @@
241
234
  accessor: accessor,
242
235
  cell: ({ value }) => {
243
236
  // If null or undefined, return an empty string
244
- return value ? value : '';
237
+ return value ?? '';
245
238
  },
246
239
  plugins: {
247
240
  // Sorting enabled by default
@@ -255,7 +248,7 @@
255
248
  id,
256
249
  tableId,
257
250
  values,
258
- updateTable,
251
+ updateTable: updateTableWithParams,
259
252
  pageIndex,
260
253
  filters
261
254
  })
@@ -299,7 +292,7 @@
299
292
  // Creating the table columns
300
293
  const createdTableColumns = table.createColumns(tableColumns);
301
294
  // Creating the table view model
302
- const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } =
295
+ const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates, rows } =
303
296
  table.createViewModel(createdTableColumns);
304
297
  // Extracting filterValue to bind it for the search input and search immediately on input
305
298
  const { filterValue } = pluginStates.tableFilter;
@@ -310,65 +303,6 @@
310
303
  // Column visibility configuration
311
304
  const { hiddenColumnIds } = pluginStates.hideColumns;
312
305
 
313
- const updateTable = async () => {
314
- if (!sendModel) throw new Error('Server-side configuration is missing');
315
-
316
- sendModel.limit = $pageSize;
317
- sendModel.offset = $pageSize * $pageIndex;
318
- sendModel.version = versionId || -1;
319
- sendModel.id = entityId || -1;
320
- sendModel.filter = normalizeFilters($filters);
321
-
322
- let fetchData;
323
-
324
- try {
325
- isFetching = true;
326
- fetchData = await fetch(baseUrl || '', {
327
- headers: {
328
- 'Content-Type': 'application/json'
329
- },
330
- method: 'POST',
331
- body: JSON.stringify(sendModel)
332
- });
333
- } catch (error) {
334
- throw new Error(`Network error: ${(error as Error).message}`);
335
- } finally {
336
- isFetching = false;
337
- }
338
-
339
- if (!fetchData.ok) {
340
- throw new Error('Failed to fetch data');
341
- }
342
-
343
- const response: Receive = await fetchData.json();
344
-
345
- // Format server columns to the client columns
346
- if (response.columns !== undefined) {
347
- columns = convertServerColumns(response.columns, columns);
348
-
349
- const clientCols = response.columns.reduce((acc, col) => {
350
- acc[col.key] = col.column;
351
- return acc;
352
- }, {});
353
-
354
- const tmpArr: any[] = [];
355
-
356
- response.data.forEach((row, index) => {
357
- const tmp: { [key: string]: any } = {};
358
- Object.keys(row).forEach((key) => {
359
- tmp[clientCols[key]] = row[key];
360
- });
361
- tmpArr.push(tmp);
362
- });
363
- dispatch('fetch', columns);
364
- $data = tmpArr;
365
- }
366
-
367
- $serverItems = response.count;
368
-
369
- return response;
370
- };
371
-
372
306
  const sortServer = (order: 'asc' | 'desc' | undefined, id: string) => {
373
307
  if (!sendModel) throw new Error('Server-side configuration is missing');
374
308
  // Set parameter for sorting
@@ -381,83 +315,70 @@
381
315
  // Reset pagination
382
316
  $pageIndex = 0;
383
317
 
384
- updateTable();
318
+ updateTableWithParams();
385
319
  };
386
320
 
387
- const getMaxCellHeightInRow = () => {
388
- if (!tableRef || resizable === 'columns' || resizable === 'none') return;
389
-
390
- tableRef.querySelectorAll('tbody tr').forEach((row, index) => {
391
- const cells = row.querySelectorAll('td');
392
-
393
- let maxHeight = optionsComponent ? 56 : 44;
394
- let minHeight = optionsComponent ? 56 : 44;
321
+ // Function to update the table with the provided parameters for easily passing to other components
322
+ const updateTableWithParams = async () => {
323
+ isFetching = true;
324
+ const result = await utils.updateTable(
325
+ $pageSize,
326
+ $pageIndex,
327
+ server,
328
+ $filters,
329
+ data,
330
+ serverItems,
331
+ columns,
332
+ dispatch
333
+ );
334
+ isFetching = false;
395
335
 
396
- cells.forEach((cell) => {
397
- const cellHeight = cell.getBoundingClientRect().height;
398
- // + 2 pixels for rendering borders correctly
399
- if (cellHeight > maxHeight) {
400
- maxHeight = cellHeight + 2;
401
- }
402
- if (cellHeight < minHeight) {
403
- minHeight = cellHeight + 2;
404
- }
405
- });
406
-
407
- rowHeights.update((rh) => {
408
- const id = +row.id.split(`${tableId}-row-`)[1];
409
- return {
410
- ...rh,
411
- [id]: {
412
- max: maxHeight - 24,
413
- min: Math.max(minHeight - 24, rowHeight ?? 20)
414
- }
415
- };
416
- });
417
- });
336
+ return result;
418
337
  };
419
338
 
420
- const getMinCellWidthInColumn = () => {
421
- if (!tableRef || resizable === 'rows' || resizable === 'none') return;
422
-
423
- // Initialize the colWidths array if it is empty
424
- if ($colWidths.length === 0) {
425
- $colWidths = Array.from({ length: $headerRows[0].cells.length }, () => 100);
339
+ // Initializes observers for rows and columns
340
+ const getDimensions = () => {
341
+ if (!tableRef) return;
342
+ if (resizable === 'none') return;
343
+ else if (resizable === 'columns') {
344
+ observeHeaderColumns();
345
+ } else if (resizable === 'rows') {
346
+ observeFirstCells();
347
+ } else {
348
+ observeHeaderColumns();
349
+ observeFirstCells();
426
350
  }
427
-
428
- colWidths.update((cw) => {
429
- tableRef?.querySelectorAll('thead tr th span').forEach((cell, index) => {
430
- // + 12 pixels for padding and + 32 pixels for filter icon
431
- // If the column width is 100, which means it has not been initialized, then calculate the width
432
- cw[index] = cw[index] === 100 ? cell.getBoundingClientRect().width + 12 + 32 : cw[index];
433
- });
434
- return cw;
435
- });
436
351
  };
437
352
 
353
+ // Resize observer for rows
438
354
  const resizeRowsObserver = new ResizeObserver(() => {
439
- getMaxCellHeightInRow();
355
+ utils.getMaxCellHeightInRow(
356
+ tableRef,
357
+ resizable,
358
+ optionsComponent,
359
+ rowHeights,
360
+ tableId,
361
+ rowHeight
362
+ );
440
363
  });
441
364
 
365
+ // Resize observers for columns
442
366
  const resizeColumnsObserver = new ResizeObserver(() => {
443
- getMinCellWidthInColumn();
367
+ utils.getMinCellWidthInColumn(tableRef, colWidths, $headerRows[0].cells.length, resizable);
444
368
  });
445
369
 
370
+ // Adds observers on the first cells of each row to resize rows
446
371
  const observeFirstCells = () => {
447
372
  if (!tableRef) return;
448
373
 
449
- $pageRows.forEach((row) => {
450
- const cell = tableRef.querySelector(`#${tableId}-row-${row.id}`);
451
- if (cell) {
452
- resizeRowsObserver.observe(cell);
453
- }
454
- });
455
-
456
374
  tableRef.querySelectorAll('tbody tr td:first-child').forEach((cell) => {
457
375
  resizeRowsObserver.observe(cell);
458
376
  });
377
+
378
+ return resizeRowsObserver;
459
379
  };
460
380
 
381
+ // Adds observers on the header columns to resize columns
461
382
  const observeHeaderColumns = () => {
462
383
  if (!tableRef) return;
463
384
 
@@ -467,6 +388,7 @@
467
388
  };
468
389
 
469
390
  afterUpdate(() => {
391
+ // If not resizable, return
470
392
  if (resizable !== 'rows' && resizable !== 'both') {
471
393
  return;
472
394
  }
@@ -478,27 +400,13 @@
478
400
  }
479
401
  });
480
402
 
481
- // Remove the resize observer when the component is destroyed for performance reasons
482
403
  onDestroy(() => {
483
- resizeRowsObserver.disconnect();
484
404
  resizeColumnsObserver.disconnect();
405
+ resizeRowsObserver.disconnect();
485
406
  });
486
407
 
487
- const getDimensions = () => {
488
- if (!tableRef) return;
489
- if (resizable === 'none') return;
490
- else if (resizable === 'columns') {
491
- observeHeaderColumns();
492
- } else if (resizable === 'rows') {
493
- observeFirstCells();
494
- } else {
495
- observeHeaderColumns();
496
- observeFirstCells();
497
- }
498
- };
499
-
500
408
  $: sortKeys = pluginStates.sort.sortKeys;
501
- $: serverSide && updateTable();
409
+ $: serverSide && updateTableWithParams();
502
410
  $: serverSide && sortServer($sortKeys[0]?.order, $sortKeys[0]?.id);
503
411
  $: $hiddenColumnIds = shownColumns.filter((col) => !col.visible).map((col) => col.id);
504
412
  </script>
@@ -598,20 +506,23 @@
598
506
  class="btn btn-sm variant-filled-primary rounded-full order-last flex gap-2 items-center"
599
507
  aria-label="Reset sizing of columns and rows"
600
508
  on:click|preventDefault={() =>
601
- resetResize($headerRows, $pageRows, tableId, columns, resizable)}
509
+ utils.resetResize($headerRows, $pageRows, tableId, columns, resizable)}
602
510
  ><Fa icon={faCompress} /> Reset sizing</button
603
511
  >
604
512
  {/if}
513
+ <!-- Enable export as CSV button if exportable === true -->
605
514
  {#if exportable}
606
515
  <button
607
516
  type="button"
608
517
  class="btn btn-sm variant-filled-primary rounded-full order-last flex items-center gap-2"
609
518
  aria-label="Export table data as CSV"
610
- on:click|preventDefault={() => exportAsCsv(tableId, jsonToCsv($exportedData))}
519
+ on:click|preventDefault={() =>
520
+ utils.exportAsCsv(tableId, utils.jsonToCsv($exportedData))}
611
521
  ><Fa icon={faDownload} /> Export as CSV</button
612
522
  >
613
523
  {/if}
614
- {#if shownColumns.length > 0}
524
+ <!-- Enable show/hide columns menu if showColumnsMenu === true -->
525
+ {#if showColumnsMenu && shownColumns.length > 0}
615
526
  <ColumnsMenu bind:columns={shownColumns} {tableId} />
616
527
  {/if}
617
528
  </div>
@@ -644,16 +555,20 @@
644
555
  {...attrs}
645
556
  style={`
646
557
  width: ${cell.isData() ? 'auto' : '0'};
647
- ${cellStyle(cell.id, columns)}
558
+ ${utils.cellStyle(cell.id, columns)}
648
559
  `}
649
560
  >
650
561
  <div
651
562
  class="overflow-auto"
652
563
  class:resize-x={(resizable === 'columns' || resizable === 'both') &&
653
- !fixedWidth(cell.id, columns)}
564
+ !utils.fixedWidth(cell.id, columns)}
654
565
  id="th-{tableId}-{cell.id}"
655
566
  style={`
656
- min-width: ${minWidth(cell.id, columns) ? minWidth(cell.id, columns) : $colWidths[index]}px;
567
+ min-width: ${
568
+ utils.minWidth(cell.id, columns)
569
+ ? utils.minWidth(cell.id, columns)
570
+ : $colWidths[index]
571
+ }px;
657
572
  `}
658
573
  >
659
574
  <div class="flex justify-between items-center">
@@ -712,15 +627,7 @@
712
627
  ? 'resize-y overflow-auto'
713
628
  : 'block'}"
714
629
  id="{tableId}-{cell.id}-{row.id}"
715
- style={`
716
- min-height: ${$rowHeights && $rowHeights[+row.id] ? `${$rowHeights[+row.id].min}px` : 'auto'};
717
- max-height: ${
718
- index !== 0 && $rowHeights && $rowHeights[+row.id]
719
- ? `${$rowHeights[+row.id].max}px`
720
- : 'auto'
721
- };
722
- height: ${$rowHeights && $rowHeights[+row.id] ? `${$rowHeights[+row.id].min}px` : 'auto'};
723
- `}
630
+ style={utils.getResizeStyles($rowHeights, row.id, index)}
724
631
  >
725
632
  <!-- Adding config for initial rowHeight, if provided -->
726
633
  <div
@@ -733,8 +640,8 @@
733
640
  class="grow overflow-auto"
734
641
  style={cell.isData()
735
642
  ? `width: ${
736
- minWidth(cell.id, columns)
737
- ? minWidth(cell.id, columns)
643
+ utils.minWidth(cell.id, columns)
644
+ ? utils.minWidth(cell.id, columns)
738
645
  : $colWidths[index]
739
646
  }px;`
740
647
  : 'max-width: min-content;'}
@@ -777,12 +684,19 @@
777
684
  {pageIndex}
778
685
  {pageSize}
779
686
  {serverItemCount}
780
- {updateTable}
687
+ updateTable={updateTableWithParams}
781
688
  {pageSizes}
689
+ {pageIndexStringType}
782
690
  id={tableId}
783
691
  />
784
692
  {:else}
785
- <TablePagination pageConfig={pluginStates.page} {pageSizes} id={tableId} />
693
+ <TablePagination
694
+ itemCount={$rows.length}
695
+ pageConfig={pluginStates.page}
696
+ {pageSizes}
697
+ id={tableId}
698
+ {pageIndexStringType}
699
+ />
786
700
  {/if}
787
701
  {/if}
788
702
  </div>
@@ -9,10 +9,14 @@
9
9
  } from '@fortawesome/free-solid-svg-icons';
10
10
  import { ListBox, ListBoxItem, popup, type PopupSettings } from '@skeletonlabs/skeleton';
11
11
 
12
+ export let itemCount;
12
13
  export let pageConfig;
13
14
  export let pageSizes;
15
+ export let pageIndexStringType;
14
16
  export let id;
15
17
 
18
+ let indexInformation = '';
19
+
16
20
  const { pageIndex, pageCount, pageSize, hasNextPage, hasPreviousPage } = pageConfig;
17
21
 
18
22
  const goToFirstPage = () => ($pageIndex = 0);
@@ -42,11 +46,23 @@
42
46
  closeQuery: '.listbox-item'
43
47
  };
44
48
 
49
+ const getIndexInfomationString = () => {
50
+ if (pageIndexStringType === 'pages') {
51
+ return $pageCount > 0 ? `Page ${$pageIndex + 1} of ${$pageCount}` : 'No pages';
52
+ } else {
53
+ return itemCount === 0 ? 'No items' : `Displaying items ${$pageIndex * $pageSize + 1} - ${Math.min(
54
+ ($pageIndex + 1) * $pageSize,
55
+ itemCount
56
+ )} of ${Math.min($pageCount * $pageSize, itemCount)}`;
57
+ }
58
+ };
59
+
45
60
  $: goToFirstPageDisabled = !$pageIndex;
46
61
  $: goToLastPageDisabled = $pageIndex == $pageCount - 1;
47
62
  $: goToNextPageDisabled = !$hasNextPage;
48
63
  $: goToPreviousPageDisabled = !$hasPreviousPage;
49
64
  $: $pageSize = pageSizeDropdownValue;
65
+ $: $pageCount, $pageIndex, $pageSize, (indexInformation = getIndexInfomationString());
50
66
  </script>
51
67
 
52
68
  <div class="flex justify-between w-full items-stretch gap-10">
@@ -124,12 +140,6 @@
124
140
  >
125
141
  </div>
126
142
  <div class="flex justify-end items-center">
127
- <span class="text-sm text-gray-500">
128
- {#if $pageCount > 0}
129
- Page {$pageIndex + 1} of {$pageCount}
130
- {:else}
131
- No pages
132
- {/if}
133
- </span>
143
+ <span class="text-sm text-gray-500">{indexInformation}</span>
134
144
  </div>
135
145
  </div>