@bexis2/bexis2-core-ui 0.4.23 → 0.4.25

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.
@@ -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>
@@ -10,6 +10,7 @@
10
10
  export let id; // Unique table ID
11
11
  export let pageIndex;
12
12
  export let pageSize;
13
+ export let pageIndexStringType;
13
14
  export let pageSizes; // Available page sizes
14
15
  export let serverItemCount; // Total number of items expected from the server. `serverSide` must be true on table config.
15
16
  export let updateTable; // Function to update table
@@ -20,6 +21,9 @@
20
21
  let goToNextPageDisabled = true;
21
22
  let goToPreviousPageDisabled = true;
22
23
 
24
+ // Index information string
25
+ let indexInformation = '';
26
+
23
27
  // Handles the input change event
24
28
  const handleChange = (e) => {
25
29
  const value = e.target.value;
@@ -58,12 +62,23 @@
58
62
  updateTable();
59
63
  };
60
64
 
65
+ const getIndexInfomationString = () => {
66
+ if (pageIndexStringType === 'pages') {
67
+ return pageCount > 0 ? `Page ${$pageIndex + 1} of ${pageCount}` : 'No pages';
68
+ } else {
69
+ return `Showing ${$pageIndex * $pageSize + 1} - ${($pageIndex + 1) * $pageSize} of ${
70
+ pageCount * $pageSize
71
+ } items`;
72
+ }
73
+ };
74
+
61
75
  $: pageCount = Math.ceil($serverItemCount / $pageSize);
62
76
  $: goToFirstPageDisabled = !$pageIndex;
63
77
  $: goToLastPageDisabled = $pageIndex == pageCount - 1;
64
78
  $: goToNextPageDisabled = $pageIndex == pageCount - 1;
65
79
  $: goToPreviousPageDisabled = !$pageIndex;
66
80
  $: $pageSize && updateTable(); // Update query when page size changes
81
+ $: pageCount, $pageIndex, $pageSize, (indexInformation = getIndexInfomationString());
67
82
 
68
83
  updateTable();
69
84
  </script>
@@ -127,15 +142,7 @@
127
142
  </div>
128
143
  <div class="flex justify-end items-center">
129
144
  <span class="text-sm text-gray-500">
130
- {#if pageCount > 0}
131
- {#if pageCount == 1}
132
- 1 page
133
- {:else}
134
- {pageCount} pages
135
- {/if}
136
- {:else}
137
- No pages
138
- {/if}
145
+ {indexInformation}
139
146
  </span>
140
147
  </div>
141
148
  </div>
@@ -1,7 +1,10 @@
1
1
  import dateFormat from 'dateformat';
2
+ import { SvelteComponent } from 'svelte';
3
+ import type { Writable } from 'svelte/store';
2
4
 
5
+ import { Send, Receive } from '$models/Models';
3
6
  import type { FilterOptionsEnum } from '$models/Enums';
4
- import type { Columns, Filter, ServerColumn } from '$models/Models';
7
+ import type { Columns, Filter, ServerColumn, ServerConfig } from '$models/Models';
5
8
 
6
9
  // Function to determine minWidth for a column to simplify the logic in the HTML
7
10
  export const minWidth = (id: string, columns: Columns | undefined) => {
@@ -158,6 +161,76 @@ export const missingValuesFn = (
158
161
  return foundKey ? missingValues[foundKey] : key;
159
162
  };
160
163
 
164
+ // Function to update the server-side table data
165
+ export const updateTable = async (
166
+ pageSize: number,
167
+ pageIndex: number,
168
+ server: ServerConfig | undefined,
169
+ filters: {
170
+ [key: string]: { [key in FilterOptionsEnum]?: number | string | Date }
171
+ },
172
+ data: Writable<any[]>,
173
+ serverItems: Writable<number> | undefined,
174
+ columns: Columns | undefined,
175
+ dispatch: any
176
+ ) => {
177
+ const { baseUrl, entityId, versionId, sendModel = new Send() } = server ?? {};
178
+
179
+ if (!sendModel) throw new Error('Server-side configuration is missing');
180
+
181
+ sendModel.limit = pageSize;
182
+ sendModel.offset = pageSize * pageIndex;
183
+ sendModel.version = versionId || -1;
184
+ sendModel.id = entityId || -1;
185
+ sendModel.filter = normalizeFilters(filters);
186
+
187
+ let fetchData;
188
+
189
+ try {
190
+ fetchData = await fetch(baseUrl || '', {
191
+ headers: {
192
+ 'Content-Type': 'application/json'
193
+ },
194
+ method: 'POST',
195
+ body: JSON.stringify(sendModel)
196
+ });
197
+ } catch (error) {
198
+ throw new Error(`Network error: ${(error as Error).message}`);
199
+ }
200
+
201
+ if (!fetchData.ok) {
202
+ throw new Error('Failed to fetch data');
203
+ }
204
+
205
+ const response: Receive = await fetchData.json();
206
+
207
+ // Format server columns to the client columns
208
+ if (response.columns !== undefined) {
209
+ columns = convertServerColumns(response.columns, columns);
210
+
211
+ const clientCols = response.columns.reduce((acc, col) => {
212
+ acc[col.key] = col.column;
213
+ return acc;
214
+ }, {});
215
+
216
+ const tmpArr: any[] = [];
217
+
218
+ response.data.forEach((row, index) => {
219
+ const tmp: { [key: string]: any } = {};
220
+ Object.keys(row).forEach((key) => {
221
+ tmp[clientCols[key]] = row[key];
222
+ });
223
+ tmpArr.push(tmp);
224
+ });
225
+ dispatch('fetch', columns);
226
+ data.set(tmpArr);
227
+ }
228
+
229
+ serverItems?.set(response.count);
230
+
231
+ return response;
232
+ };
233
+
161
234
  export const convertServerColumns = (
162
235
  serverColumns: ServerColumn[],
163
236
  columns: Columns | undefined
@@ -215,3 +288,86 @@ export const convertServerColumns = (
215
288
 
216
289
  return columnsConfig;
217
290
  };
291
+
292
+ // Calculates the maximum height of the cells in each row
293
+ export const getMaxCellHeightInRow = (
294
+ tableRef: HTMLTableElement,
295
+ resizable: 'columns' | 'rows' | 'none' | 'both',
296
+ optionsComponent: typeof SvelteComponent | undefined,
297
+ rowHeights: Writable<{ [key: number]: { max: number; min: number } }>,
298
+ tableId: string,
299
+ rowHeight: number | null
300
+ ) => {
301
+ if (!tableRef || resizable === 'columns' || resizable === 'none') return;
302
+
303
+ tableRef.querySelectorAll('tbody tr').forEach((row, index) => {
304
+ const cells = row.querySelectorAll('td');
305
+
306
+ let maxHeight = optionsComponent ? 56 : 44;
307
+ let minHeight = optionsComponent ? 56 : 44;
308
+
309
+ cells.forEach((cell) => {
310
+ const cellHeight = cell.getBoundingClientRect().height;
311
+ // + 2 pixels for rendering borders correctly
312
+ if (cellHeight > maxHeight) {
313
+ maxHeight = cellHeight + 2;
314
+ }
315
+ if (cellHeight < minHeight) {
316
+ minHeight = cellHeight + 2;
317
+ }
318
+ });
319
+
320
+ rowHeights.update((rh) => {
321
+ const id = +row.id.split(`${tableId}-row-`)[1];
322
+ return {
323
+ ...rh,
324
+ [id]: {
325
+ max: maxHeight - 24,
326
+ min: Math.max(minHeight - 24, rowHeight ?? 20)
327
+ }
328
+ };
329
+ });
330
+ });
331
+ };
332
+
333
+ // Calculates the minimum width of the cells in each column
334
+ export const getMinCellWidthInColumn = (
335
+ tableRef: HTMLTableElement,
336
+ colWidths: Writable<number[]>,
337
+ headerRowsLength: number,
338
+ resizable: 'columns' | 'rows' | 'none' | 'both'
339
+ ) => {
340
+ if (!tableRef || resizable === 'rows' || resizable === 'none') return;
341
+
342
+ // Initialize the column widths if they are not already initialized
343
+ colWidths.update((cw) => {
344
+ if (cw.length === 0) {
345
+ return Array.from({ length: headerRowsLength }, () => 100);
346
+ }
347
+ return cw;
348
+ });
349
+
350
+ colWidths.update((cw) => {
351
+ tableRef?.querySelectorAll('thead tr th span').forEach((cell, index) => {
352
+ // + 12 pixels for padding and + 32 pixels for filter icon
353
+ // If the column width is 100, which means it has not been initialized, then calculate the width
354
+ cw[index] = cw[index] === 100 ? cell.getBoundingClientRect().width + 12 + 32 : cw[index];
355
+ });
356
+ return cw;
357
+ });
358
+ };
359
+
360
+ export const getResizeStyles = (
361
+ rowHeights: { [key: number]: { max: number; min: number } },
362
+ id: string | number,
363
+ index: number
364
+ ) => {
365
+ return `
366
+ min-height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
367
+ max-height: ${index !== 0 && rowHeights && rowHeights[+id]
368
+ ? `${rowHeights[+id].max}px`
369
+ : 'auto'
370
+ };
371
+ height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
372
+ `;
373
+ }
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { ListBox, ListBoxItem } from '@skeletonlabs/skeleton';
2
+ import { ListBox, ListBoxItem, TreeViewItem } from '@skeletonlabs/skeleton';
3
3
  import type { menuItemType } from '../../../models/Page';
4
4
  import { goTo } from '../../../services/BaseCaller';
5
5
 
@@ -31,7 +31,7 @@
31
31
  return;
32
32
  }
33
33
  else{
34
- goTo(item.Url)
34
+ goTo(item.Url, item.Internal, item.Target);
35
35
  }
36
36
  }
37
37
 
@@ -117,6 +117,7 @@ export interface TableConfig<T> {
117
117
  id: string;
118
118
  data: Writable<T[]>;
119
119
  resizable?: 'none' | 'rows' | 'columns' | 'both'; // none by default
120
+ showColumnsMenu?: boolean; // false by default
120
121
  toggle?: boolean; // false by default
121
122
  search?: boolean; // true by default
122
123
  fitToScreen?: boolean; // true by default
@@ -126,6 +127,7 @@ export interface TableConfig<T> {
126
127
  exportable?: boolean; // false by default
127
128
  pageSizes?: number[]; // [5, 10, 20, 50, 100] by default
128
129
  defaultPageSize?: number; // 10 by default
130
+ pageIndexStringType?: 'items' | 'pages'; // pages by default
129
131
  optionsComponent?: typeof SvelteComponent;
130
132
 
131
133
  server?: ServerConfig;
@@ -29,6 +29,7 @@ export interface menuItemType {
29
29
  Url: string;
30
30
  Target: string;
31
31
  Module: string;
32
+ Internal: boolean;
32
33
  Items: menuItemType[];
33
34
  }
34
35
 
@@ -3,11 +3,11 @@
3
3
  import { host } from '$store/apiStores';
4
4
 
5
5
  // go to a internal action
6
- export const goTo = async (url, intern = true) => {
6
+ export const goTo = async (url, intern = true, target="_self") => {
7
7
  if (intern == true) {
8
8
  // go to inside bexis2
9
9
  if (window != null && host != null && url != null) {
10
- window.open(host + url, '_self')?.focus();
10
+ window.open(host + url, target)?.focus();
11
11
  }
12
12
  } // go to a external page
13
13
  else {
@@ -1,15 +0,0 @@
1
- import type { FilterOptionsEnum } from '../../models/Enums';
2
- import type { Columns, Filter, ServerColumn } from '../../models/Models';
3
- export declare const minWidth: (id: string, columns: Columns | undefined) => number;
4
- export declare const fixedWidth: (id: string, columns: Columns | undefined) => number;
5
- export declare const cellStyle: (id: string, columns: Columns | undefined) => string;
6
- export declare const normalizeFilters: (filters: {
7
- [key: string]: { [key in FilterOptionsEnum]?: number | string | Date; };
8
- }) => Filter[];
9
- export declare const exportAsCsv: (tableId: string, exportedData: string) => void;
10
- export declare const jsonToCsv: (data: string) => string;
11
- export declare const resetResize: (headerRows: any, pageRows: any, tableId: string, columns: Columns | undefined, resizable: "none" | "rows" | "columns" | "both") => void;
12
- export declare const missingValuesFn: (key: number | string, missingValues: {
13
- [key: string | number]: string;
14
- }) => string | number;
15
- export declare const convertServerColumns: (serverColumns: ServerColumn[], columns: Columns | undefined) => Columns;