@bexis2/bexis2-core-ui 0.3.12 → 0.3.13

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 (30) hide show
  1. package/README.md +1 -0
  2. package/dist/components/File/FileUploader.svelte +3 -3
  3. package/dist/components/Table/TableContent.svelte +202 -119
  4. package/dist/components/Table/TableFilter.svelte +146 -102
  5. package/dist/components/Table/TableFilter.svelte.d.ts +2 -3
  6. package/dist/components/Table/TableFilterServer.svelte +274 -0
  7. package/dist/components/Table/TableFilterServer.svelte.d.ts +22 -0
  8. package/dist/components/Table/TablePagination.svelte +72 -39
  9. package/dist/components/Table/TablePaginationServer.svelte +125 -0
  10. package/dist/components/Table/TablePaginationServer.svelte.d.ts +21 -0
  11. package/dist/components/Table/filter.js +40 -78
  12. package/dist/components/Table/shared.d.ts +32 -0
  13. package/dist/components/Table/shared.js +117 -0
  14. package/dist/components/form/DropdownKvP.svelte.d.ts +4 -4
  15. package/dist/components/form/MultiSelect.svelte.d.ts +6 -6
  16. package/dist/models/Enums.d.ts +18 -0
  17. package/dist/models/Enums.js +20 -0
  18. package/dist/models/Models.d.ts +43 -2
  19. package/dist/models/Models.js +28 -1
  20. package/package.json +2 -2
  21. package/src/lib/components/Table/TableContent.svelte +227 -151
  22. package/src/lib/components/Table/TableFilter.svelte +166 -102
  23. package/src/lib/components/Table/TableFilterServer.svelte +310 -0
  24. package/src/lib/components/Table/TablePagination.svelte +75 -39
  25. package/src/lib/components/Table/TablePaginationServer.svelte +133 -0
  26. package/src/lib/components/Table/filter.ts +42 -86
  27. package/src/lib/components/Table/shared.ts +141 -0
  28. package/src/lib/components/file/FileUploader.svelte +3 -3
  29. package/src/lib/models/Enums.ts +22 -0
  30. package/src/lib/models/Models.ts +63 -2
package/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # bexis-core-ui
2
+ ## 0.3.13
2
3
  ## 0.3.12
3
4
  - table
4
5
  - Server-side data fetching
@@ -1,4 +1,4 @@
1
- <script>import DropZone from "svelte-file-dropzone/Dropzone.svelte";
1
+ <script>import Dropzone from "svelte-file-dropzone";
2
2
  import Fa from "svelte-fa/src/fa.svelte";
3
3
  import Spinner from "../page/Spinner.svelte";
4
4
  import { createEventDispatcher } from "svelte";
@@ -100,7 +100,7 @@ async function handleSubmit() {
100
100
  {#if model}
101
101
  <!--if model exist -->
102
102
  <div>
103
- <DropZone
103
+ <Dropzone
104
104
  on:drop={handleFilesSelect}
105
105
  accept={model.accept}
106
106
  multiple={model.multiple}
@@ -118,7 +118,7 @@ async function handleSubmit() {
118
118
  {/each}
119
119
  {/if}
120
120
  </p>
121
- </DropZone>
121
+ </Dropzone>
122
122
  {#if isUploading}
123
123
  <ProgressBar value={undefined}/>
124
124
  {/if}
@@ -1,4 +1,7 @@
1
1
  <script>import { createEventDispatcher } from "svelte";
2
+ import { readable, writable } from "svelte/store";
3
+ import Fa from "svelte-fa";
4
+ import { faXmark } from "@fortawesome/free-solid-svg-icons";
2
5
  import { createTable, Subscribe, Render, createRender } from "svelte-headless-table";
3
6
  import {
4
7
  addSortBy,
@@ -12,8 +15,19 @@ import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floati
12
15
  import { SlideToggle, storePopup } from "@skeletonlabs/skeleton";
13
16
  storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
14
17
  import TableFilter from "./TableFilter.svelte";
18
+ import TableFilterServer from "./TableFilterServer.svelte";
15
19
  import TablePagination from "./TablePagination.svelte";
20
+ import TablePaginationServer from "./TablePaginationServer.svelte";
16
21
  import { columnFilter, searchFilter } from "./filter";
22
+ import {
23
+ cellStyle,
24
+ exportAsCsv,
25
+ fixedWidth,
26
+ normalizeFilters,
27
+ resetResize,
28
+ convertServerColumns
29
+ } from "./shared";
30
+ import { Receive, Send } from "../../models/Models";
17
31
  export let config;
18
32
  let {
19
33
  id: tableId,
@@ -38,18 +52,44 @@ let {
38
52
  // Page sizes to display in the pagination component
39
53
  fitToScreen = true,
40
54
  // Whether to fit the table to the screen,
41
- exportable = false
55
+ exportable = false,
42
56
  // Whether to display the export button and enable export functionality
57
+ serverSide = false,
58
+ // Whether the table is client or server-side
59
+ URL = "",
60
+ // URL to fetch data from
61
+ token = "",
62
+ // Bearer token to authenticate the request
63
+ sendModel = new Send(),
64
+ // Model to send requests
65
+ entityId = 0,
66
+ // Entity ID to send with the request
67
+ versionId = 0
68
+ // Version ID to send with the request
43
69
  } = config;
70
+ let searchValue = "";
71
+ const filters = writable({});
44
72
  const dispatch = createEventDispatcher();
45
73
  const actionDispatcher = (obj) => dispatch("action", obj);
74
+ const serverItems = serverSide ? writable(0) : void 0;
75
+ const serverItemCount = serverSide ? readable(0, (set) => {
76
+ serverItems.subscribe((val) => set(val));
77
+ }) : void 0;
46
78
  const table = createTable(data, {
47
79
  colFilter: addColumnFilters(),
48
80
  tableFilter: addTableFilter({
49
- fn: searchFilter
81
+ fn: searchFilter,
82
+ serverSide
83
+ }),
84
+ sort: addSortBy({
85
+ disableMultiSort: true,
86
+ serverSide
87
+ }),
88
+ page: addPagination({
89
+ initialPageSize: defaultPageSize,
90
+ serverSide,
91
+ serverItemCount
50
92
  }),
51
- sort: addSortBy({ disableMultiSort: true }),
52
- page: addPagination({ initialPageSize: defaultPageSize }),
53
93
  expand: addExpandedRows(),
54
94
  export: addDataExport({ format: "csv" })
55
95
  });
@@ -61,6 +101,9 @@ $data.forEach((item) => {
61
101
  }
62
102
  });
63
103
  });
104
+ Object.keys(allCols).forEach((key) => {
105
+ $filters = { ...$filters, [key]: {} };
106
+ });
64
107
  const accessors = Object.keys(allCols);
65
108
  const tableColumns = [
66
109
  ...accessors.filter((accessor) => {
@@ -99,7 +142,6 @@ const tableColumns = [
99
142
  // Sorting config
100
143
  sort: {
101
144
  disable: disableSorting,
102
- invert: true,
103
145
  getSortValue: (row) => {
104
146
  return toSortableValueFn ? toSortableValueFn(row) : row;
105
147
  }
@@ -110,12 +152,22 @@ const tableColumns = [
110
152
  return colFilterFn ? colFilterFn({ filterValue: filterValue2, value: val }) : columnFilter({ filterValue: filterValue2, value: val });
111
153
  },
112
154
  render: ({ filterValue: filterValue2, values, id }) => {
113
- return createRender(colFilterComponent ?? TableFilter, {
155
+ filterValue2.set($filters[key]);
156
+ return serverSide ? createRender(TableFilterServer, {
157
+ id,
158
+ tableId,
159
+ values,
160
+ updateTable,
161
+ pageIndex,
162
+ toFilterableValueFn,
163
+ filters
164
+ }) : createRender(colFilterComponent ?? TableFilter, {
114
165
  filterValue: filterValue2,
115
166
  id,
116
167
  tableId,
117
168
  values,
118
- toFilterableValueFn
169
+ toFilterableValueFn,
170
+ filters
119
171
  });
120
172
  }
121
173
  } : void 0,
@@ -136,18 +188,26 @@ const tableColumns = [
136
188
  },
137
189
  plugins: {
138
190
  // Sorting enabled by default
139
- sort: {
140
- invert: true
141
- },
191
+ sort: {},
142
192
  // Filtering enabled by default
143
193
  colFilter: {
144
194
  fn: columnFilter,
145
- render: ({ filterValue: filterValue2, values, id }) => createRender(TableFilter, {
146
- filterValue: filterValue2,
147
- id,
148
- tableId,
149
- values
150
- })
195
+ render: ({ filterValue: filterValue2, values, id }) => {
196
+ return serverSide ? createRender(TableFilterServer, {
197
+ id,
198
+ tableId,
199
+ values,
200
+ updateTable,
201
+ pageIndex,
202
+ filters
203
+ }) : createRender(TableFilter, {
204
+ filterValue: filterValue2,
205
+ id,
206
+ tableId,
207
+ values,
208
+ filters
209
+ });
210
+ }
151
211
  }
152
212
  }
153
213
  });
@@ -177,68 +237,77 @@ const createdTableColumns = table.createColumns(tableColumns);
177
237
  const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(createdTableColumns);
178
238
  const { filterValue } = pluginStates.tableFilter;
179
239
  const { exportedData } = pluginStates.export;
180
- const minWidth = (id) => {
181
- if (columns && id in columns) {
182
- return columns[id].minWidth ?? 0;
183
- }
184
- return 0;
185
- };
186
- const fixedWidth = (id) => {
187
- if (columns && id in columns) {
188
- return columns[id].fixedWidth ?? 0;
240
+ const { pageIndex, pageSize } = pluginStates.page;
241
+ const updateTable = async () => {
242
+ sendModel.limit = $pageSize;
243
+ sendModel.offset = $pageSize * $pageIndex;
244
+ sendModel.version = versionId;
245
+ sendModel.id = entityId;
246
+ sendModel.filter = normalizeFilters($filters);
247
+ const fetchData = await fetch(URL, {
248
+ headers: {
249
+ "Content-Type": "application/json",
250
+ Authorization: `Bearer ${token}`
251
+ },
252
+ method: "POST",
253
+ body: JSON.stringify(sendModel)
254
+ });
255
+ const response = await fetchData.json();
256
+ if (response.columns !== void 0) {
257
+ columns = convertServerColumns(response.columns);
189
258
  }
190
- return 0;
191
- };
192
- const cellStyle = (id) => {
193
- const minW = minWidth(id);
194
- const fixedW = fixedWidth(id);
195
- const styles = [];
196
- minW && styles.push(`min-width: ${minW}px`);
197
- fixedW && styles.push(`width: ${fixedW}px`);
198
- return styles.join(";");
259
+ $data = response.data;
260
+ $serverItems = response.count;
261
+ return response;
199
262
  };
200
- const resetResize = () => {
201
- if (resizable === "columns" || resizable === "both") {
202
- $headerRows.forEach((row) => {
203
- row.cells.forEach((cell) => {
204
- const minW = minWidth(cell.id);
205
- const fixedW = fixedWidth(cell.id);
206
- fixedW && document.getElementById(`th-${tableId}-${cell.id}`)?.style.setProperty("width", `${fixedW}px`);
207
- minW && document.getElementById(`th-${tableId}-${cell.id}`)?.style.setProperty("min-width", `${minW}px`);
208
- !minW && !fixedW && document.getElementById(`th-${tableId}-${cell.id}`)?.style.setProperty("width", "auto");
209
- });
210
- });
263
+ const sortServer = (order, id) => {
264
+ if (order === void 0) {
265
+ sendModel.order = [];
266
+ } else {
267
+ sendModel.order = [{ column: id, direction: order }];
211
268
  }
212
- if (resizable === "rows" || resizable === "both") {
213
- $pageRows.forEach((row) => {
214
- row.cells.forEach((cell) => {
215
- document.getElementById(`${tableId}-${cell.id}-${row.id}`)?.style.setProperty("height", "auto");
216
- });
217
- });
218
- }
219
- };
220
- const exportAsCsv = () => {
221
- const anchor = document.createElement("a");
222
- anchor.style.display = "none";
223
- anchor.href = `data:text/csv;charset=utf-8,${encodeURIComponent($exportedData)}`;
224
- anchor.download = `${tableId}.csv`;
225
- document.body.appendChild(anchor);
226
- anchor.click();
227
- document.body.removeChild(anchor);
269
+ $pageIndex = 0;
270
+ updateTable();
228
271
  };
272
+ $:
273
+ sortKeys = pluginStates.sort.sortKeys;
274
+ $:
275
+ serverSide && updateTable();
276
+ $:
277
+ serverSide && sortServer($sortKeys[0]?.order, $sortKeys[0]?.id);
229
278
  </script>
230
279
 
231
280
  <div class="grid gap-2 overflow-auto" class:w-fit={!fitToScreen} class:w-full={fitToScreen}>
232
281
  <div class="table-container">
233
282
  <!-- Enable the search filter if table is not empty -->
234
283
  {#if $data.length > 0}
235
- <input
236
- class="input p-2 border border-primary-500"
237
- type="text"
238
- bind:value={$filterValue}
239
- placeholder="Search rows..."
240
- id="{tableId}-search"
241
- />
284
+ {#if !serverSide}
285
+ <div class="flex gap-2">
286
+ <div class="relative w-full flex items-center">
287
+ <input
288
+ class="input p-2 border border-primary-500"
289
+ type="text"
290
+ bind:value={searchValue}
291
+ placeholder="Search rows..."
292
+ id="{tableId}-search"
293
+ /><button
294
+ type="reset"
295
+ class="absolute right-3 items-center"
296
+ on:click|preventDefault={() => {
297
+ searchValue = '';
298
+ $filterValue = '';
299
+ }}><Fa icon={faXmark} /></button
300
+ >
301
+ </div>
302
+ <button
303
+ type="button"
304
+ class="btn variant-filled-primary"
305
+ on:click|preventDefault={() => {
306
+ $filterValue = searchValue;
307
+ }}>Search</button
308
+ >
309
+ </div>
310
+ {/if}
242
311
  <div class="flex justify-between items-center py-2 w-full">
243
312
  <div>
244
313
  <!-- Enable the fitToScreen toggle if toggle === true -->
@@ -259,14 +328,17 @@ const exportAsCsv = () => {
259
328
  <button
260
329
  type="button"
261
330
  class="btn btn-sm variant-filled-primary rounded-full order-last"
262
- on:click|preventDefault={resetResize}>Reset sizing</button
331
+ on:click|preventDefault={() =>
332
+ resetResize($headerRows, $pageRows, tableId, columns, resizable)}
333
+ >Reset sizing</button
263
334
  >
264
335
  {/if}
265
336
  {#if exportable}
266
337
  <button
267
338
  type="button"
268
339
  class="btn btn-sm variant-filled-primary rounded-full order-last"
269
- on:click|preventDefault={exportAsCsv}>Export as CSV</button
340
+ on:click|preventDefault={() => exportAsCsv(tableId, $exportedData)}
341
+ >Export as CSV</button
270
342
  >
271
343
  {/if}
272
344
  </div>
@@ -289,46 +361,45 @@ const exportAsCsv = () => {
289
361
  rowProps={headerRow.props()}
290
362
  let:rowProps
291
363
  >
292
- <tr {...rowAttrs} class="bg-primary-300 dark:bg-primary-500 items-stretch">
364
+ <tr {...rowAttrs} class="bg-primary-300 dark:bg-primary-500">
293
365
  {#each headerRow.cells as cell (cell.id)}
294
366
  <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
295
- <th
296
- scope="col"
297
- class="!p-2 overflow-auto"
298
- class:resize-x={(resizable === 'columns' || resizable === 'both') &&
299
- !fixedWidth(cell.id)}
300
- {...attrs}
301
- id="th-{tableId}-{cell.id}"
302
- style={cellStyle(cell.id)}
303
- >
304
- <div class="flex justify-between items-center">
305
- <div class="flex gap-1 whitespace-pre-wrap">
306
- <!-- Adding sorting config and styling -->
307
- <span
308
- class:underline={props.sort.order}
309
- class:normal-case={cell.id !== cell.label}
310
- class:cursor-pointer={!props.sort.disabled}
311
- on:click={props.sort.toggle}
312
- on:keydown={props.sort.toggle}
313
- >
314
- {cell.render()}
315
- </span>
316
- <div class="w-2">
317
- {#if props.sort.order === 'asc'}
318
-
319
- {:else if props.sort.order === 'desc'}
320
-
321
- {/if}
322
- </div>
323
- </div>
324
- <!-- Adding column filter config -->
325
- {#if cell.isData()}
326
- {#if props.colFilter?.render}
327
- <div class="">
328
- <Render of={props.colFilter.render} />
367
+ <th scope="col" class="!p-2" {...attrs} style={cellStyle(cell.id, columns)}>
368
+ <div
369
+ class="overflow-auto"
370
+ class:resize-x={(resizable === 'columns' || resizable === 'both') &&
371
+ !fixedWidth(cell.id, columns)}
372
+ id="th-{tableId}-{cell.id}"
373
+ >
374
+ <div class="flex justify-between items-center">
375
+ <div class="flex gap-1 whitespace-pre-wrap">
376
+ <!-- Adding sorting config and styling -->
377
+ <span
378
+ class:underline={props.sort.order}
379
+ class:normal-case={cell.id !== cell.label}
380
+ class:cursor-pointer={!props.sort.disabled}
381
+ on:click={props.sort.toggle}
382
+ on:keydown={props.sort.toggle}
383
+ >
384
+ {cell.render()}
385
+ </span>
386
+ <div class="w-2">
387
+ {#if props.sort.order === 'asc'}
388
+
389
+ {:else if props.sort.order === 'desc'}
390
+
391
+ {/if}
329
392
  </div>
393
+ </div>
394
+ <!-- Adding column filter config -->
395
+ {#if cell.isData()}
396
+ {#if props.colFilter?.render}
397
+ <div class="">
398
+ <Render of={props.colFilter.render} />
399
+ </div>
400
+ {/if}
330
401
  {/if}
331
- {/if}
402
+ </div>
332
403
  </div>
333
404
  </th>
334
405
  </Subscribe>
@@ -349,20 +420,21 @@ const exportAsCsv = () => {
349
420
  <tr {...rowAttrs} id="{tableId}-row-{row.id}" class="">
350
421
  {#each row.cells as cell, index (cell?.id)}
351
422
  <Subscribe attrs={cell.attrs()} let:attrs>
352
- <td
353
- {...attrs}
354
- class="!p-2 overflow-auto {index === 0 &&
355
- (resizable === 'rows' || resizable === 'both')
356
- ? 'resize-y'
357
- : ''}"
358
- id="{tableId}-{cell.id}-{row.id}"
359
- >
360
- <!-- Adding config for initial rowHeight, if provided -->
423
+ <td {...attrs} class="!p-2">
361
424
  <div
362
- class="flex items-center"
363
- style="height: {rowHeight ? `${rowHeight}px` : 'auto'};"
425
+ class=" overflow-auto h-max {index === 0 &&
426
+ (resizable === 'rows' || resizable === 'both')
427
+ ? 'resize-y'
428
+ : ''}"
429
+ id="{tableId}-{cell.id}-{row.id}"
364
430
  >
365
- <div class="grow h-full"><Render of={cell.render()} /></div>
431
+ <!-- Adding config for initial rowHeight, if provided -->
432
+ <div
433
+ class="flex items-center overflow-auto"
434
+ style="height: {rowHeight ? `${rowHeight}px` : 'auto'};"
435
+ >
436
+ <div class="grow h-full"><Render of={cell.render()} /></div>
437
+ </div>
366
438
  </div>
367
439
  </td>
368
440
  </Subscribe>
@@ -377,6 +449,17 @@ const exportAsCsv = () => {
377
449
  </div>
378
450
  {#if $data.length > 0}
379
451
  <!-- Adding pagination, if table is not empty -->
380
- <TablePagination pageConfig={pluginStates.page} {pageSizes} id={tableId} />
452
+ {#if serverSide}
453
+ <TablePaginationServer
454
+ {pageIndex}
455
+ {pageSize}
456
+ {serverItemCount}
457
+ {updateTable}
458
+ {pageSizes}
459
+ id={tableId}
460
+ />
461
+ {:else}
462
+ <TablePagination pageConfig={pluginStates.page} {pageSizes} id={tableId} />
463
+ {/if}
381
464
  {/if}
382
465
  </div>