@bexis2/bexis2-core-ui 0.3.2 → 0.3.3
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.
- package/README.md +11 -0
- package/dist/components/Table/Table.svelte +165 -100
- package/dist/components/Table/TableFilter.svelte +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/models/Models.d.ts +2 -0
- package/dist/themes/theme-bexis2.js +13 -0
- package/package.json +2 -2
- package/src/lib/components/Table/Table.svelte +198 -125
- package/src/lib/components/Table/TableFilter.svelte +7 -3
- package/src/lib/index.ts +6 -5
- package/src/lib/models/Models.ts +6 -4
- package/src/lib/stores/pageStores.ts +0 -1
- package/src/lib/themes/theme-bexis2.ts +13 -0
package/README.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# bexis-core-ui
|
|
2
2
|
|
|
3
|
+
## 0.3.3
|
|
4
|
+
|
|
5
|
+
- Table
|
|
6
|
+
- `rowHeight (number)`: fixed height for rows in pixels. (if combined with resizability, acts as minimum height)
|
|
7
|
+
- `exportability (boolean)`: if toggled true, lets user to export the current state of the table as CSV.
|
|
8
|
+
- Optimized styles for dark mode.
|
|
9
|
+
- Bug fix related to empty tables.
|
|
10
|
+
- Migrated to new TableOfContents (Beta) on documentation page.
|
|
11
|
+
- LightSwitch for toggling dark mode on documentation pages.
|
|
12
|
+
- Optimized surface colors in dark mode.
|
|
13
|
+
|
|
3
14
|
## 0.3.1
|
|
4
15
|
|
|
5
16
|
- add bexis2theme
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
addPagination,
|
|
6
6
|
addExpandedRows,
|
|
7
7
|
addColumnFilters,
|
|
8
|
-
addTableFilter
|
|
8
|
+
addTableFilter,
|
|
9
|
+
addDataExport
|
|
9
10
|
} from "svelte-headless-table/plugins";
|
|
10
11
|
import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
|
|
11
12
|
import { SlideToggle, storePopup } from "@skeletonlabs/skeleton";
|
|
@@ -16,15 +17,29 @@ import { columnFilter, searchFilter } from "./filter";
|
|
|
16
17
|
export let config;
|
|
17
18
|
let {
|
|
18
19
|
id: tableId,
|
|
20
|
+
// Unique table ID
|
|
19
21
|
data,
|
|
22
|
+
// Data store
|
|
20
23
|
columns,
|
|
24
|
+
// Column configuration
|
|
21
25
|
resizable = "none",
|
|
26
|
+
// Resizability config
|
|
22
27
|
height = null,
|
|
28
|
+
// Table height
|
|
29
|
+
rowHeight = null,
|
|
30
|
+
// Row height
|
|
23
31
|
optionsComponent,
|
|
32
|
+
// Custom component to render in the last column
|
|
24
33
|
defaultPageSize = 10,
|
|
34
|
+
// Default page size - number of rows to display per page
|
|
25
35
|
toggle = false,
|
|
36
|
+
// Whether to display the fitToScreen toggle
|
|
26
37
|
pageSizes = [5, 10, 15, 20],
|
|
27
|
-
|
|
38
|
+
// Page sizes to display in the pagination component
|
|
39
|
+
fitToScreen = true,
|
|
40
|
+
// Whether to fit the table to the screen,
|
|
41
|
+
exportable = false
|
|
42
|
+
// Whether to display the export button and enable export functionality
|
|
28
43
|
} = config;
|
|
29
44
|
const dispatch = createEventDispatcher();
|
|
30
45
|
const actionDispatcher = (obj) => dispatch("action", obj);
|
|
@@ -35,7 +50,8 @@ const table = createTable(data, {
|
|
|
35
50
|
}),
|
|
36
51
|
sort: addSortBy({ disableMultiSort: true }),
|
|
37
52
|
page: addPagination({ initialPageSize: defaultPageSize }),
|
|
38
|
-
expand: addExpandedRows()
|
|
53
|
+
expand: addExpandedRows(),
|
|
54
|
+
export: addDataExport({ format: "csv" })
|
|
39
55
|
});
|
|
40
56
|
const allCols = {};
|
|
41
57
|
$data.forEach((item) => {
|
|
@@ -58,20 +74,29 @@ const tableColumns = [
|
|
|
58
74
|
if (columns !== void 0 && key in columns) {
|
|
59
75
|
const {
|
|
60
76
|
header,
|
|
77
|
+
// Custom header to display
|
|
61
78
|
colFilterFn,
|
|
79
|
+
// Custom column filter function
|
|
62
80
|
colFilterComponent,
|
|
81
|
+
// Custom column filter component
|
|
63
82
|
instructions,
|
|
83
|
+
// Custom instructions for the column cells (sorting, filtering, searching, rendering)
|
|
64
84
|
disableFiltering = false,
|
|
85
|
+
// Whether to disable filtering for the column
|
|
65
86
|
disableSorting = false
|
|
87
|
+
// Whether to disable sorting for the column
|
|
66
88
|
} = columns[key];
|
|
67
89
|
const { toSortableValueFn, toFilterableValueFn, toStringFn, renderComponent } = instructions ?? {};
|
|
68
90
|
return table.column({
|
|
91
|
+
// If header is not provided, use the key as the header
|
|
69
92
|
header: header ?? key,
|
|
70
93
|
accessor,
|
|
94
|
+
// Render the cell with the provided component, or use the toStringFn if provided, or just use the value
|
|
71
95
|
cell: ({ value, row }) => {
|
|
72
96
|
return renderComponent ? createRender(renderComponent, { value, row }) : toStringFn ? toStringFn(value) : value;
|
|
73
97
|
},
|
|
74
98
|
plugins: {
|
|
99
|
+
// Sorting config
|
|
75
100
|
sort: {
|
|
76
101
|
disable: disableSorting,
|
|
77
102
|
invert: true,
|
|
@@ -95,6 +120,7 @@ const tableColumns = [
|
|
|
95
120
|
}
|
|
96
121
|
} : void 0,
|
|
97
122
|
tableFilter: {
|
|
123
|
+
// Search filter config
|
|
98
124
|
getFilterValue: (row) => {
|
|
99
125
|
return toStringFn ? toStringFn(row) : row;
|
|
100
126
|
}
|
|
@@ -109,9 +135,11 @@ const tableColumns = [
|
|
|
109
135
|
return value ? value : "";
|
|
110
136
|
},
|
|
111
137
|
plugins: {
|
|
138
|
+
// Sorting enabled by default
|
|
112
139
|
sort: {
|
|
113
140
|
invert: true
|
|
114
141
|
},
|
|
142
|
+
// Filtering enabled by default
|
|
115
143
|
colFilter: {
|
|
116
144
|
fn: columnFilter,
|
|
117
145
|
render: ({ filterValue: filterValue2, values, id }) => createRender(TableFilter, {
|
|
@@ -131,6 +159,11 @@ if (optionsComponent !== void 0) {
|
|
|
131
159
|
table.display({
|
|
132
160
|
id: "optionsColumn",
|
|
133
161
|
header: "",
|
|
162
|
+
plugins: {
|
|
163
|
+
export: {
|
|
164
|
+
exclude: true
|
|
165
|
+
}
|
|
166
|
+
},
|
|
134
167
|
cell: ({ row }, _) => {
|
|
135
168
|
return createRender(optionsComponent, {
|
|
136
169
|
row: row.isData() ? row.original : null,
|
|
@@ -143,6 +176,7 @@ if (optionsComponent !== void 0) {
|
|
|
143
176
|
const createdTableColumns = table.createColumns(tableColumns);
|
|
144
177
|
const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(createdTableColumns);
|
|
145
178
|
const { filterValue } = pluginStates.tableFilter;
|
|
179
|
+
const { exportedData } = pluginStates.export;
|
|
146
180
|
const minWidth = (id) => {
|
|
147
181
|
if (columns && id in columns) {
|
|
148
182
|
return columns[id].minWidth ?? 0;
|
|
@@ -183,10 +217,20 @@ const resetResize = () => {
|
|
|
183
217
|
});
|
|
184
218
|
}
|
|
185
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);
|
|
228
|
+
};
|
|
186
229
|
</script>
|
|
187
230
|
|
|
188
231
|
<div class="grid gap-2 overflow-auto" class:w-fit={!fitToScreen} class:w-full={fitToScreen}>
|
|
189
232
|
<div class="table-container">
|
|
233
|
+
<!-- Enable the search filter if table is not empty -->
|
|
190
234
|
{#if $data.length > 0}
|
|
191
235
|
<input
|
|
192
236
|
class="input p-2 border border-primary-500"
|
|
@@ -195,123 +239,144 @@ const resetResize = () => {
|
|
|
195
239
|
placeholder="Search rows..."
|
|
196
240
|
id="{tableId}-search"
|
|
197
241
|
/>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
242
|
+
<div class="flex justify-between items-center py-2 w-full">
|
|
243
|
+
<div>
|
|
244
|
+
<!-- Enable the fitToScreen toggle if toggle === true -->
|
|
245
|
+
{#if toggle}
|
|
246
|
+
<SlideToggle
|
|
247
|
+
name="slider-label"
|
|
248
|
+
active="bg-primary-500"
|
|
249
|
+
size="sm"
|
|
250
|
+
checked={fitToScreen}
|
|
251
|
+
id="{tableId}-toggle"
|
|
252
|
+
on:change={() => (fitToScreen = !fitToScreen)}>Fit to screen</SlideToggle
|
|
253
|
+
>
|
|
254
|
+
{/if}
|
|
255
|
+
</div>
|
|
256
|
+
<div class="flex gap-2">
|
|
257
|
+
<!-- Enable the resetResize button if resizable !== 'none' -->
|
|
258
|
+
{#if resizable !== 'none'}
|
|
259
|
+
<button
|
|
260
|
+
type="button"
|
|
261
|
+
class="btn btn-sm variant-filled-primary rounded-full order-last"
|
|
262
|
+
on:click|preventDefault={resetResize}>Reset sizing</button
|
|
263
|
+
>
|
|
264
|
+
{/if}
|
|
265
|
+
{#if exportable}
|
|
266
|
+
<button
|
|
267
|
+
type="button"
|
|
268
|
+
class="btn btn-sm variant-filled-primary rounded-full order-last"
|
|
269
|
+
on:click|preventDefault={exportAsCsv}>Export as CSV</button
|
|
270
|
+
>
|
|
271
|
+
{/if}
|
|
272
|
+
</div>
|
|
221
273
|
</div>
|
|
222
|
-
|
|
274
|
+
{/if}
|
|
223
275
|
|
|
224
276
|
<div class="overflow-auto" style="height: {height}px">
|
|
225
277
|
<table
|
|
226
278
|
{...$tableAttrs}
|
|
227
|
-
class="table table-auto table-compact bg-tertiary-500/30 overflow-clip"
|
|
279
|
+
class="table table-auto table-compact bg-tertiary-500/30 dark:bg-tertiary-900/10 overflow-clip"
|
|
228
280
|
id="{tableId}-table"
|
|
229
281
|
>
|
|
282
|
+
<!-- If table height is provided, making the top row sticky -->
|
|
230
283
|
<thead class=" {height != null ? `sticky top-0` : ''}">
|
|
231
|
-
{#
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
|
|
241
|
-
<
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
<div class="flex
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
{#if cell.isData()}
|
|
270
|
-
{#if props.colFilter?.render}
|
|
271
|
-
<div class="">
|
|
272
|
-
<Render of={props.colFilter.render} />
|
|
284
|
+
{#if $data.length > 0}
|
|
285
|
+
{#each $headerRows as headerRow (headerRow.id)}
|
|
286
|
+
<Subscribe
|
|
287
|
+
rowAttrs={headerRow.attrs()}
|
|
288
|
+
let:rowAttrs
|
|
289
|
+
rowProps={headerRow.props()}
|
|
290
|
+
let:rowProps
|
|
291
|
+
>
|
|
292
|
+
<tr {...rowAttrs} class="bg-primary-300 dark:bg-primary-500 items-stretch">
|
|
293
|
+
{#each headerRow.cells as cell (cell.id)}
|
|
294
|
+
<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}
|
|
273
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} />
|
|
329
|
+
</div>
|
|
330
|
+
{/if}
|
|
274
331
|
{/if}
|
|
275
|
-
|
|
276
|
-
</
|
|
277
|
-
</
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
</
|
|
281
|
-
|
|
332
|
+
</div>
|
|
333
|
+
</th>
|
|
334
|
+
</Subscribe>
|
|
335
|
+
{/each}
|
|
336
|
+
</tr>
|
|
337
|
+
</Subscribe>
|
|
338
|
+
{/each}
|
|
282
339
|
{:else}
|
|
340
|
+
<!-- Table is empty -->
|
|
283
341
|
<p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
|
|
284
|
-
{/
|
|
342
|
+
{/if}
|
|
285
343
|
</thead>
|
|
286
344
|
|
|
287
345
|
<tbody class="overflow-auto" {...$tableBodyAttrs}>
|
|
288
|
-
{#
|
|
289
|
-
|
|
290
|
-
<
|
|
291
|
-
{
|
|
292
|
-
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
346
|
+
{#if $data.length > 0}
|
|
347
|
+
{#each $pageRows as row (row.id)}
|
|
348
|
+
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
|
|
349
|
+
<tr {...rowAttrs} id="{tableId}-row-{row.id}" class="">
|
|
350
|
+
{#each row.cells as cell, index (cell?.id)}
|
|
351
|
+
<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 -->
|
|
361
|
+
<div
|
|
362
|
+
class="flex items-center"
|
|
363
|
+
style="height: {rowHeight ? `${rowHeight}px` : 'auto'};"
|
|
364
|
+
>
|
|
365
|
+
<div class="grow h-full"><Render of={cell.render()} /></div>
|
|
366
|
+
</div>
|
|
367
|
+
</td>
|
|
368
|
+
</Subscribe>
|
|
369
|
+
{/each}
|
|
370
|
+
</tr>
|
|
371
|
+
</Subscribe>
|
|
372
|
+
{/each}
|
|
373
|
+
{/if}
|
|
310
374
|
</tbody>
|
|
311
375
|
</table>
|
|
312
376
|
</div>
|
|
313
377
|
</div>
|
|
314
378
|
{#if $data.length > 0}
|
|
379
|
+
<!-- Adding pagination, if table is not empty -->
|
|
315
380
|
<TablePagination pageConfig={pluginStates.page} {pageSizes} id={tableId} />
|
|
316
381
|
{/if}
|
|
317
382
|
</div>
|
|
@@ -129,6 +129,7 @@ type = isDate ? "date" : type;
|
|
|
129
129
|
class="btn variant-filled-primary btn-sm"
|
|
130
130
|
type="button"
|
|
131
131
|
on:click|preventDefault={() => {
|
|
132
|
+
// Set the defaults when cleared
|
|
132
133
|
firstOption = 'isequal';
|
|
133
134
|
firstValue = undefined;
|
|
134
135
|
secondOption = 'isequal';
|
package/dist/index.d.ts
CHANGED
|
@@ -22,12 +22,12 @@ import type { TableConfig, Columns, Column } from './models/Models';
|
|
|
22
22
|
import CodeEditor from './components/CodeEditor/CodeEditor.svelte';
|
|
23
23
|
import Notification from './components/page/Notification.svelte';
|
|
24
24
|
import TablePlaceholder from './components/page/TablePlaceholder.svelte';
|
|
25
|
+
import { bexis2theme } from './themes/theme-bexis2';
|
|
25
26
|
export { Checkbox, CheckboxKVPList, CheckboxList, DateInput, DropdownKVP, MultiSelect, NumberInput, TextArea, TextInput };
|
|
26
27
|
export { FileInfo, FileIcon, FileUploader };
|
|
27
28
|
export { Spinner, Page, Alert, Menu, ErrorMessage };
|
|
28
29
|
export { Api } from './services/Api.js';
|
|
29
30
|
export { host, username, password, setApiConfig } from './stores/apiStores.js';
|
|
30
|
-
export { bexis2theme } from './themes/theme-bexis2';
|
|
31
31
|
export type { userType, inputType, fileUploaderType, linkType, listItemType, keyValuePairType, fileInfoType, fileReaderInfoType, asciiFileReaderInfoType } from './models/Models.js';
|
|
32
32
|
export { helpStore } from './stores/pageStores';
|
|
33
33
|
export type { helpStoreType, helpItemType } from './models/Models';
|
|
@@ -40,3 +40,4 @@ export { positionType, pageContentLayoutType, decimalCharacterType, orientationT
|
|
|
40
40
|
export { Table, TableFilter, columnFilter, searchFilter };
|
|
41
41
|
export { CodeEditor };
|
|
42
42
|
export type { TableConfig, Columns, Column };
|
|
43
|
+
export { bexis2theme };
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,8 @@ import CodeEditor from './components/CodeEditor/CodeEditor.svelte';
|
|
|
30
30
|
import Notification from './components/page/Notification.svelte';
|
|
31
31
|
//table placeholder
|
|
32
32
|
import TablePlaceholder from './components/page/TablePlaceholder.svelte';
|
|
33
|
+
// theme
|
|
34
|
+
import { bexis2theme } from './themes/theme-bexis2';
|
|
33
35
|
//Form
|
|
34
36
|
export { Checkbox, CheckboxKVPList, CheckboxList, DateInput, DropdownKVP, MultiSelect, NumberInput, TextArea, TextInput };
|
|
35
37
|
//File
|
|
@@ -39,8 +41,6 @@ export { Spinner, Page, Alert, Menu, ErrorMessage };
|
|
|
39
41
|
//Api
|
|
40
42
|
export { Api } from './services/Api.js';
|
|
41
43
|
export { host, username, password, setApiConfig } from './stores/apiStores.js';
|
|
42
|
-
// theme
|
|
43
|
-
export { bexis2theme } from './themes/theme-bexis2';
|
|
44
44
|
//help
|
|
45
45
|
export { helpStore } from './stores/pageStores';
|
|
46
46
|
//notification
|
|
@@ -55,3 +55,5 @@ export { positionType, pageContentLayoutType, decimalCharacterType, orientationT
|
|
|
55
55
|
export { Table, TableFilter, columnFilter, searchFilter };
|
|
56
56
|
// CodeEditor
|
|
57
57
|
export { CodeEditor };
|
|
58
|
+
// theme
|
|
59
|
+
export { bexis2theme };
|
package/dist/models/Models.d.ts
CHANGED
|
@@ -87,7 +87,9 @@ export interface TableConfig<T> {
|
|
|
87
87
|
toggle?: boolean;
|
|
88
88
|
fitToScreen?: boolean;
|
|
89
89
|
height?: null | number;
|
|
90
|
+
rowHeight?: number;
|
|
90
91
|
columns?: Columns;
|
|
92
|
+
exportable?: boolean;
|
|
91
93
|
pageSizes?: number[];
|
|
92
94
|
defaultPageSize?: number;
|
|
93
95
|
optionsComponent?: typeof SvelteComponent;
|
|
@@ -95,5 +95,18 @@ export const bexis2theme = {
|
|
|
95
95
|
'--color-surface-700': '149 149 149',
|
|
96
96
|
'--color-surface-800': '119 119 119',
|
|
97
97
|
'--color-surface-900': '98 98 98' // #626262
|
|
98
|
+
},
|
|
99
|
+
properties_dark: {
|
|
100
|
+
// surface | #2e2e2e
|
|
101
|
+
'--color-surface-50': '224 224 224',
|
|
102
|
+
'--color-surface-100': '213 213 213',
|
|
103
|
+
'--color-surface-200': '203 203 203',
|
|
104
|
+
'--color-surface-300': '171 171 171',
|
|
105
|
+
'--color-surface-400': '109 109 109',
|
|
106
|
+
'--color-surface-500': '46 46 46',
|
|
107
|
+
'--color-surface-600': '41 41 41',
|
|
108
|
+
'--color-surface-700': '35 35 35',
|
|
109
|
+
'--color-surface-800': '28 28 28',
|
|
110
|
+
'--color-surface-900': '23 23 23' // #171717
|
|
98
111
|
}
|
|
99
112
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bexis2/bexis2-core-ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"delay": "^6.0.0",
|
|
75
75
|
"dotenv": "^16.3.1",
|
|
76
76
|
"eslint4b-prebuilt": "^6.7.2",
|
|
77
|
-
"highlight.js": "^11.
|
|
77
|
+
"highlight.js": "^11.9.0",
|
|
78
78
|
"highlightjs-svelte": "^1.0.6",
|
|
79
79
|
"svelte": "^3.54.0",
|
|
80
80
|
"svelte-codemirror-editor": "^1.1.0",
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
addPagination,
|
|
7
7
|
addExpandedRows,
|
|
8
8
|
addColumnFilters,
|
|
9
|
-
addTableFilter
|
|
9
|
+
addTableFilter,
|
|
10
|
+
addDataExport
|
|
10
11
|
} from 'svelte-headless-table/plugins';
|
|
11
12
|
import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
|
|
12
13
|
import { SlideToggle, storePopup } from '@skeletonlabs/skeleton';
|
|
@@ -20,24 +21,30 @@
|
|
|
20
21
|
|
|
21
22
|
export let config: TableConfig<any>;
|
|
22
23
|
|
|
24
|
+
// Destructuring the config object and setting default values
|
|
23
25
|
let {
|
|
24
|
-
id: tableId,
|
|
25
|
-
data,
|
|
26
|
-
columns,
|
|
27
|
-
resizable = 'none',
|
|
28
|
-
height = null,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
id: tableId, // Unique table ID
|
|
27
|
+
data, // Data store
|
|
28
|
+
columns, // Column configuration
|
|
29
|
+
resizable = 'none', // Resizability config
|
|
30
|
+
height = null, // Table height
|
|
31
|
+
rowHeight = null, // Row height
|
|
32
|
+
optionsComponent, // Custom component to render in the last column
|
|
33
|
+
defaultPageSize = 10, // Default page size - number of rows to display per page
|
|
34
|
+
toggle = false, // Whether to display the fitToScreen toggle
|
|
35
|
+
pageSizes = [5, 10, 15, 20], // Page sizes to display in the pagination component
|
|
36
|
+
fitToScreen = true, // Whether to fit the table to the screen,
|
|
37
|
+
exportable = false // Whether to display the export button and enable export functionality
|
|
34
38
|
} = config;
|
|
35
39
|
|
|
40
|
+
// Creatign a type to access keys of the objects in the data store
|
|
36
41
|
type AccessorType = keyof (typeof $data)[number];
|
|
37
42
|
|
|
43
|
+
// Creating a dispatcher to dispatch actions to the parent component
|
|
38
44
|
const dispatch = createEventDispatcher();
|
|
39
45
|
const actionDispatcher = (obj) => dispatch('action', obj);
|
|
40
46
|
|
|
47
|
+
// Initializing the table
|
|
41
48
|
const table = createTable(data, {
|
|
42
49
|
colFilter: addColumnFilters(),
|
|
43
50
|
tableFilter: addTableFilter({
|
|
@@ -45,11 +52,13 @@
|
|
|
45
52
|
}),
|
|
46
53
|
sort: addSortBy({ disableMultiSort: true }),
|
|
47
54
|
page: addPagination({ initialPageSize: defaultPageSize }),
|
|
48
|
-
expand: addExpandedRows()
|
|
55
|
+
expand: addExpandedRows(),
|
|
56
|
+
export: addDataExport({ format: 'csv' })
|
|
49
57
|
});
|
|
50
58
|
|
|
59
|
+
// A variable to hold all the keys
|
|
51
60
|
const allCols: { [key: string]: any } = {};
|
|
52
|
-
|
|
61
|
+
// Iterating over each item to get all the possible keys
|
|
53
62
|
$data.forEach((item) => {
|
|
54
63
|
Object.keys(item).forEach((key) => {
|
|
55
64
|
if (!allCols[key]) {
|
|
@@ -57,12 +66,13 @@
|
|
|
57
66
|
}
|
|
58
67
|
});
|
|
59
68
|
});
|
|
60
|
-
|
|
69
|
+
// Creating an array of all the keys
|
|
61
70
|
const accessors: AccessorType[] = Object.keys(allCols) as AccessorType[];
|
|
62
|
-
|
|
71
|
+
// Configuring every table column with the provided options
|
|
63
72
|
const tableColumns = [
|
|
64
73
|
...accessors
|
|
65
74
|
.filter((accessor) => {
|
|
75
|
+
// Filtering only unexcluded columns
|
|
66
76
|
const key = accessor as string;
|
|
67
77
|
if (columns !== undefined && key in columns && columns[key].exclude === true) {
|
|
68
78
|
return false;
|
|
@@ -71,22 +81,25 @@
|
|
|
71
81
|
})
|
|
72
82
|
.map((accessor) => {
|
|
73
83
|
const key = accessor as string;
|
|
84
|
+
// Applying configuration options for configured columns
|
|
74
85
|
if (columns !== undefined && key in columns) {
|
|
75
86
|
const {
|
|
76
|
-
header,
|
|
77
|
-
colFilterFn,
|
|
78
|
-
colFilterComponent,
|
|
79
|
-
instructions,
|
|
80
|
-
disableFiltering = false,
|
|
81
|
-
disableSorting = false
|
|
87
|
+
header, // Custom header to display
|
|
88
|
+
colFilterFn, // Custom column filter function
|
|
89
|
+
colFilterComponent, // Custom column filter component
|
|
90
|
+
instructions, // Custom instructions for the column cells (sorting, filtering, searching, rendering)
|
|
91
|
+
disableFiltering = false, // Whether to disable filtering for the column
|
|
92
|
+
disableSorting = false // Whether to disable sorting for the column
|
|
82
93
|
} = columns[key];
|
|
83
94
|
|
|
84
95
|
const { toSortableValueFn, toFilterableValueFn, toStringFn, renderComponent } =
|
|
85
96
|
instructions ?? {};
|
|
86
97
|
|
|
87
98
|
return table.column({
|
|
99
|
+
// If header is not provided, use the key as the header
|
|
88
100
|
header: header ?? key,
|
|
89
101
|
accessor: accessor,
|
|
102
|
+
// Render the cell with the provided component, or use the toStringFn if provided, or just use the value
|
|
90
103
|
cell: ({ value, row }) => {
|
|
91
104
|
return renderComponent
|
|
92
105
|
? createRender(renderComponent, { value, row })
|
|
@@ -95,23 +108,27 @@
|
|
|
95
108
|
: value;
|
|
96
109
|
},
|
|
97
110
|
plugins: {
|
|
111
|
+
// Sorting config
|
|
98
112
|
sort: {
|
|
99
113
|
disable: disableSorting,
|
|
100
114
|
invert: true,
|
|
101
115
|
getSortValue: (row) => {
|
|
116
|
+
// If provided, use the custom sorting function toSortableValueFn(), or just use the value
|
|
102
117
|
return toSortableValueFn ? toSortableValueFn(row) : row;
|
|
103
118
|
}
|
|
104
119
|
},
|
|
105
120
|
colFilter: !disableFiltering
|
|
106
121
|
? {
|
|
107
122
|
fn: ({ filterValue, value }) => {
|
|
123
|
+
// If provided, use the custom filtering function toFilterableValueFn(), or just use the value
|
|
108
124
|
const val = toFilterableValueFn ? toFilterableValueFn(value) : value;
|
|
109
|
-
|
|
125
|
+
// If provided, use the custom filtering function colFilterFn(), or just use the default columnFilter()
|
|
110
126
|
return colFilterFn
|
|
111
127
|
? colFilterFn({ filterValue, value: val })
|
|
112
128
|
: columnFilter({ filterValue, value: val });
|
|
113
129
|
},
|
|
114
130
|
render: ({ filterValue, values, id }) => {
|
|
131
|
+
// If provided, use the custom filter component, or use the default TableFilter component
|
|
115
132
|
return createRender(colFilterComponent ?? TableFilter, {
|
|
116
133
|
filterValue,
|
|
117
134
|
id,
|
|
@@ -123,23 +140,29 @@
|
|
|
123
140
|
}
|
|
124
141
|
: undefined,
|
|
125
142
|
tableFilter: {
|
|
143
|
+
// Search filter config
|
|
126
144
|
getFilterValue: (row) => {
|
|
145
|
+
// If provided, use the custom toString function toStringFn(), or just use the value
|
|
127
146
|
return toStringFn ? toStringFn(row) : row;
|
|
128
147
|
}
|
|
129
148
|
}
|
|
130
149
|
}
|
|
131
150
|
});
|
|
132
151
|
} else {
|
|
152
|
+
// Default configuration for unconfigured columns
|
|
133
153
|
return table.column({
|
|
134
154
|
header: key,
|
|
135
155
|
accessor: accessor,
|
|
136
156
|
cell: ({ value }) => {
|
|
157
|
+
// If null or undefined, return an empty string
|
|
137
158
|
return value ? value : '';
|
|
138
159
|
},
|
|
139
160
|
plugins: {
|
|
161
|
+
// Sorting enabled by default
|
|
140
162
|
sort: {
|
|
141
163
|
invert: true
|
|
142
164
|
},
|
|
165
|
+
// Filtering enabled by default
|
|
143
166
|
colFilter: {
|
|
144
167
|
fn: columnFilter,
|
|
145
168
|
render: ({ filterValue, values, id }) =>
|
|
@@ -156,11 +179,17 @@
|
|
|
156
179
|
})
|
|
157
180
|
];
|
|
158
181
|
|
|
182
|
+
// If optionsComponent is provided, add a column for it at the end
|
|
159
183
|
if (optionsComponent !== undefined) {
|
|
160
184
|
tableColumns.push(
|
|
161
185
|
table.display({
|
|
162
186
|
id: 'optionsColumn',
|
|
163
187
|
header: '',
|
|
188
|
+
plugins: {
|
|
189
|
+
export: {
|
|
190
|
+
exclude: true
|
|
191
|
+
}
|
|
192
|
+
},
|
|
164
193
|
cell: ({ row }, _) => {
|
|
165
194
|
return createRender(optionsComponent!, {
|
|
166
195
|
row: row.isData() ? row.original : null,
|
|
@@ -171,53 +200,63 @@
|
|
|
171
200
|
);
|
|
172
201
|
}
|
|
173
202
|
|
|
203
|
+
// Creating the table columns
|
|
174
204
|
const createdTableColumns = table.createColumns(tableColumns);
|
|
175
|
-
|
|
205
|
+
// Creating the table view model
|
|
176
206
|
const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } =
|
|
177
207
|
table.createViewModel(createdTableColumns);
|
|
208
|
+
// Extracting filterValue to bind it for the search input and search immediately on input
|
|
178
209
|
const { filterValue } = pluginStates.tableFilter;
|
|
210
|
+
// CSV content to be exported. If unexportable, then null
|
|
211
|
+
const { exportedData } = pluginStates.export;
|
|
179
212
|
|
|
213
|
+
// Function to determine minWidth for a column to simplify the logic in the HTML
|
|
180
214
|
const minWidth = (id: string) => {
|
|
181
215
|
if (columns && id in columns) {
|
|
182
216
|
return columns[id].minWidth ?? 0;
|
|
183
217
|
}
|
|
184
218
|
return 0;
|
|
185
219
|
};
|
|
186
|
-
|
|
220
|
+
// Function to determine fixedWidth for a column to simplify the logic in the HTML
|
|
187
221
|
const fixedWidth = (id: string) => {
|
|
188
222
|
if (columns && id in columns) {
|
|
189
223
|
return columns[id].fixedWidth ?? 0;
|
|
190
224
|
}
|
|
191
225
|
return 0;
|
|
192
226
|
};
|
|
193
|
-
|
|
227
|
+
// Function to create custom styles for the columns to simplify the logic in the HTML
|
|
194
228
|
const cellStyle = (id: string) => {
|
|
195
229
|
const minW = minWidth(id);
|
|
196
230
|
const fixedW = fixedWidth(id);
|
|
197
231
|
const styles: string[] = [];
|
|
198
232
|
|
|
233
|
+
// If minWidth is provided, add to styles
|
|
199
234
|
minW && styles.push(`min-width: ${minW}px`);
|
|
235
|
+
// If fixedWidth is provided, add to styles
|
|
200
236
|
fixedW && styles.push(`width: ${fixedW}px`);
|
|
201
|
-
|
|
237
|
+
// Create and return styles separated by ';'
|
|
202
238
|
return styles.join(';');
|
|
203
239
|
};
|
|
204
240
|
|
|
241
|
+
// Resetting the resized columns and/or rows
|
|
205
242
|
const resetResize = () => {
|
|
243
|
+
// Run only if resizable is not none
|
|
206
244
|
if (resizable === 'columns' || resizable === 'both') {
|
|
207
245
|
$headerRows.forEach((row) => {
|
|
208
246
|
row.cells.forEach((cell) => {
|
|
209
247
|
const minW = minWidth(cell.id);
|
|
210
248
|
const fixedW = fixedWidth(cell.id);
|
|
211
|
-
|
|
249
|
+
// If a fixedWidth is provided for a column, then reset the width to that value
|
|
212
250
|
fixedW &&
|
|
213
251
|
document
|
|
214
252
|
.getElementById(`th-${tableId}-${cell.id}`)
|
|
215
253
|
?.style.setProperty('width', `${fixedW}px`);
|
|
254
|
+
// If a minWidth is provided for a column, then reset the width to that value
|
|
216
255
|
minW &&
|
|
217
256
|
document
|
|
218
257
|
.getElementById(`th-${tableId}-${cell.id}`)
|
|
219
258
|
?.style.setProperty('min-width', `${minW}px`);
|
|
220
|
-
|
|
259
|
+
// If neither minWidth nor fixedWidth provided for a column, then reset the width to auto
|
|
221
260
|
!minW &&
|
|
222
261
|
!fixedW &&
|
|
223
262
|
document.getElementById(`th-${tableId}-${cell.id}`)?.style.setProperty('width', 'auto');
|
|
@@ -228,6 +267,7 @@
|
|
|
228
267
|
if (resizable === 'rows' || resizable === 'both') {
|
|
229
268
|
$pageRows.forEach((row) => {
|
|
230
269
|
row.cells.forEach((cell) => {
|
|
270
|
+
// Reset all row heights to auto
|
|
231
271
|
document
|
|
232
272
|
.getElementById(`${tableId}-${cell.id}-${row.id}`)
|
|
233
273
|
?.style.setProperty('height', 'auto');
|
|
@@ -235,10 +275,22 @@
|
|
|
235
275
|
});
|
|
236
276
|
}
|
|
237
277
|
};
|
|
278
|
+
|
|
279
|
+
const exportAsCsv = () => {
|
|
280
|
+
// Creating a hidden anchor element to download the CSV file
|
|
281
|
+
const anchor = document.createElement('a');
|
|
282
|
+
anchor.style.display = 'none';
|
|
283
|
+
anchor.href = `data:text/csv;charset=utf-8,${encodeURIComponent($exportedData)}`;
|
|
284
|
+
anchor.download = `${tableId}.csv`;
|
|
285
|
+
document.body.appendChild(anchor);
|
|
286
|
+
anchor.click();
|
|
287
|
+
document.body.removeChild(anchor);
|
|
288
|
+
};
|
|
238
289
|
</script>
|
|
239
290
|
|
|
240
291
|
<div class="grid gap-2 overflow-auto" class:w-fit={!fitToScreen} class:w-full={fitToScreen}>
|
|
241
292
|
<div class="table-container">
|
|
293
|
+
<!-- Enable the search filter if table is not empty -->
|
|
242
294
|
{#if $data.length > 0}
|
|
243
295
|
<input
|
|
244
296
|
class="input p-2 border border-primary-500"
|
|
@@ -247,123 +299,144 @@
|
|
|
247
299
|
placeholder="Search rows..."
|
|
248
300
|
id="{tableId}-search"
|
|
249
301
|
/>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
302
|
+
<div class="flex justify-between items-center py-2 w-full">
|
|
303
|
+
<div>
|
|
304
|
+
<!-- Enable the fitToScreen toggle if toggle === true -->
|
|
305
|
+
{#if toggle}
|
|
306
|
+
<SlideToggle
|
|
307
|
+
name="slider-label"
|
|
308
|
+
active="bg-primary-500"
|
|
309
|
+
size="sm"
|
|
310
|
+
checked={fitToScreen}
|
|
311
|
+
id="{tableId}-toggle"
|
|
312
|
+
on:change={() => (fitToScreen = !fitToScreen)}>Fit to screen</SlideToggle
|
|
313
|
+
>
|
|
314
|
+
{/if}
|
|
315
|
+
</div>
|
|
316
|
+
<div class="flex gap-2">
|
|
317
|
+
<!-- Enable the resetResize button if resizable !== 'none' -->
|
|
318
|
+
{#if resizable !== 'none'}
|
|
319
|
+
<button
|
|
320
|
+
type="button"
|
|
321
|
+
class="btn btn-sm variant-filled-primary rounded-full order-last"
|
|
322
|
+
on:click|preventDefault={resetResize}>Reset sizing</button
|
|
323
|
+
>
|
|
324
|
+
{/if}
|
|
325
|
+
{#if exportable}
|
|
326
|
+
<button
|
|
327
|
+
type="button"
|
|
328
|
+
class="btn btn-sm variant-filled-primary rounded-full order-last"
|
|
329
|
+
on:click|preventDefault={exportAsCsv}>Export as CSV</button
|
|
330
|
+
>
|
|
331
|
+
{/if}
|
|
332
|
+
</div>
|
|
273
333
|
</div>
|
|
274
|
-
|
|
334
|
+
{/if}
|
|
275
335
|
|
|
276
336
|
<div class="overflow-auto" style="height: {height}px">
|
|
277
337
|
<table
|
|
278
338
|
{...$tableAttrs}
|
|
279
|
-
class="table table-auto table-compact bg-tertiary-500/30 overflow-clip"
|
|
339
|
+
class="table table-auto table-compact bg-tertiary-500/30 dark:bg-tertiary-900/10 overflow-clip"
|
|
280
340
|
id="{tableId}-table"
|
|
281
341
|
>
|
|
342
|
+
<!-- If table height is provided, making the top row sticky -->
|
|
282
343
|
<thead class=" {height != null ? `sticky top-0` : ''}">
|
|
283
|
-
{#
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
{
|
|
292
|
-
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
<div class="flex
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
{#if cell.isData()}
|
|
322
|
-
{#if props.colFilter?.render}
|
|
323
|
-
<div class="">
|
|
324
|
-
<Render of={props.colFilter.render} />
|
|
344
|
+
{#if $data.length > 0}
|
|
345
|
+
{#each $headerRows as headerRow (headerRow.id)}
|
|
346
|
+
<Subscribe
|
|
347
|
+
rowAttrs={headerRow.attrs()}
|
|
348
|
+
let:rowAttrs
|
|
349
|
+
rowProps={headerRow.props()}
|
|
350
|
+
let:rowProps
|
|
351
|
+
>
|
|
352
|
+
<tr {...rowAttrs} class="bg-primary-300 dark:bg-primary-500 items-stretch">
|
|
353
|
+
{#each headerRow.cells as cell (cell.id)}
|
|
354
|
+
<Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
|
|
355
|
+
<th
|
|
356
|
+
scope="col"
|
|
357
|
+
class="!p-2 overflow-auto"
|
|
358
|
+
class:resize-x={(resizable === 'columns' || resizable === 'both') &&
|
|
359
|
+
!fixedWidth(cell.id)}
|
|
360
|
+
{...attrs}
|
|
361
|
+
id="th-{tableId}-{cell.id}"
|
|
362
|
+
style={cellStyle(cell.id)}
|
|
363
|
+
>
|
|
364
|
+
<div class="flex justify-between items-center">
|
|
365
|
+
<div class="flex gap-1 whitespace-pre-wrap">
|
|
366
|
+
<!-- Adding sorting config and styling -->
|
|
367
|
+
<span
|
|
368
|
+
class:underline={props.sort.order}
|
|
369
|
+
class:normal-case={cell.id !== cell.label}
|
|
370
|
+
class:cursor-pointer={!props.sort.disabled}
|
|
371
|
+
on:click={props.sort.toggle}
|
|
372
|
+
on:keydown={props.sort.toggle}
|
|
373
|
+
>
|
|
374
|
+
{cell.render()}
|
|
375
|
+
</span>
|
|
376
|
+
<div class="w-2">
|
|
377
|
+
{#if props.sort.order === 'asc'}
|
|
378
|
+
▾
|
|
379
|
+
{:else if props.sort.order === 'desc'}
|
|
380
|
+
▴
|
|
381
|
+
{/if}
|
|
325
382
|
</div>
|
|
383
|
+
</div>
|
|
384
|
+
<!-- Adding column filter config -->
|
|
385
|
+
{#if cell.isData()}
|
|
386
|
+
{#if props.colFilter?.render}
|
|
387
|
+
<div class="">
|
|
388
|
+
<Render of={props.colFilter.render} />
|
|
389
|
+
</div>
|
|
390
|
+
{/if}
|
|
326
391
|
{/if}
|
|
327
|
-
|
|
328
|
-
</
|
|
329
|
-
</
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
</
|
|
333
|
-
|
|
392
|
+
</div>
|
|
393
|
+
</th>
|
|
394
|
+
</Subscribe>
|
|
395
|
+
{/each}
|
|
396
|
+
</tr>
|
|
397
|
+
</Subscribe>
|
|
398
|
+
{/each}
|
|
334
399
|
{:else}
|
|
400
|
+
<!-- Table is empty -->
|
|
335
401
|
<p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
|
|
336
|
-
{/
|
|
402
|
+
{/if}
|
|
337
403
|
</thead>
|
|
338
404
|
|
|
339
405
|
<tbody class="overflow-auto" {...$tableBodyAttrs}>
|
|
340
|
-
{#
|
|
341
|
-
|
|
342
|
-
<
|
|
343
|
-
{
|
|
344
|
-
|
|
345
|
-
<
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
406
|
+
{#if $data.length > 0}
|
|
407
|
+
{#each $pageRows as row (row.id)}
|
|
408
|
+
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
|
|
409
|
+
<tr {...rowAttrs} id="{tableId}-row-{row.id}" class="">
|
|
410
|
+
{#each row.cells as cell, index (cell?.id)}
|
|
411
|
+
<Subscribe attrs={cell.attrs()} let:attrs>
|
|
412
|
+
<td
|
|
413
|
+
{...attrs}
|
|
414
|
+
class="!p-2 overflow-auto {index === 0 &&
|
|
415
|
+
(resizable === 'rows' || resizable === 'both')
|
|
416
|
+
? 'resize-y'
|
|
417
|
+
: ''}"
|
|
418
|
+
id="{tableId}-{cell.id}-{row.id}"
|
|
419
|
+
>
|
|
420
|
+
<!-- Adding config for initial rowHeight, if provided -->
|
|
421
|
+
<div
|
|
422
|
+
class="flex items-center"
|
|
423
|
+
style="height: {rowHeight ? `${rowHeight}px` : 'auto'};"
|
|
424
|
+
>
|
|
425
|
+
<div class="grow h-full"><Render of={cell.render()} /></div>
|
|
426
|
+
</div>
|
|
427
|
+
</td>
|
|
428
|
+
</Subscribe>
|
|
429
|
+
{/each}
|
|
430
|
+
</tr>
|
|
431
|
+
</Subscribe>
|
|
432
|
+
{/each}
|
|
433
|
+
{/if}
|
|
362
434
|
</tbody>
|
|
363
435
|
</table>
|
|
364
436
|
</div>
|
|
365
437
|
</div>
|
|
366
438
|
{#if $data.length > 0}
|
|
439
|
+
<!-- Adding pagination, if table is not empty -->
|
|
367
440
|
<TablePagination pageConfig={pluginStates.page} {pageSizes} id={tableId} />
|
|
368
441
|
{/if}
|
|
369
442
|
</div>
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
let firstValue;
|
|
15
15
|
let secondOption;
|
|
16
16
|
let secondValue;
|
|
17
|
+
// If the filter is applied and the displayed values are filtered
|
|
17
18
|
let active = false;
|
|
18
19
|
|
|
20
|
+
// Options for different types of values
|
|
19
21
|
const options = {
|
|
20
22
|
number: [
|
|
21
23
|
{
|
|
@@ -97,8 +99,9 @@
|
|
|
97
99
|
]
|
|
98
100
|
};
|
|
99
101
|
|
|
102
|
+
// Unique ID for the column filter popup
|
|
100
103
|
const popupId = `${tableId}-${id}`;
|
|
101
|
-
|
|
104
|
+
// Popup config
|
|
102
105
|
const popupFeatured: PopupSettings = {
|
|
103
106
|
event: 'click',
|
|
104
107
|
target: popupId,
|
|
@@ -107,7 +110,7 @@
|
|
|
107
110
|
|
|
108
111
|
let type: string = 'string';
|
|
109
112
|
let isDate = false;
|
|
110
|
-
|
|
113
|
+
// Check the type of the column
|
|
111
114
|
$values.forEach((item) => {
|
|
112
115
|
if (item) {
|
|
113
116
|
type = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
|
|
@@ -119,7 +122,7 @@
|
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
});
|
|
122
|
-
|
|
125
|
+
// Determine if the type is date
|
|
123
126
|
type = isDate ? 'date' : type;
|
|
124
127
|
</script>
|
|
125
128
|
|
|
@@ -140,6 +143,7 @@
|
|
|
140
143
|
class="btn variant-filled-primary btn-sm"
|
|
141
144
|
type="button"
|
|
142
145
|
on:click|preventDefault={() => {
|
|
146
|
+
// Set the defaults when cleared
|
|
143
147
|
firstOption = 'isequal';
|
|
144
148
|
firstValue = undefined;
|
|
145
149
|
secondOption = 'isequal';
|
package/src/lib/index.ts
CHANGED
|
@@ -39,6 +39,9 @@ import Notification from './components/page/Notification.svelte';
|
|
|
39
39
|
//table placeholder
|
|
40
40
|
import TablePlaceholder from './components/page/TablePlaceholder.svelte';
|
|
41
41
|
|
|
42
|
+
// theme
|
|
43
|
+
import { bexis2theme } from './themes/theme-bexis2';
|
|
44
|
+
|
|
42
45
|
//Form
|
|
43
46
|
export {
|
|
44
47
|
Checkbox,
|
|
@@ -62,11 +65,6 @@ export { Spinner, Page, Alert, Menu, ErrorMessage };
|
|
|
62
65
|
export { Api } from './services/Api.js';
|
|
63
66
|
export { host, username, password, setApiConfig } from './stores/apiStores.js';
|
|
64
67
|
|
|
65
|
-
// theme
|
|
66
|
-
export { bexis2theme } from './themes/theme-bexis2';
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
68
|
//Type
|
|
71
69
|
export type {
|
|
72
70
|
userType,
|
|
@@ -109,3 +107,6 @@ export { Table, TableFilter, columnFilter, searchFilter };
|
|
|
109
107
|
export { CodeEditor };
|
|
110
108
|
|
|
111
109
|
export type { TableConfig, Columns, Column };
|
|
110
|
+
|
|
111
|
+
// theme
|
|
112
|
+
export { bexis2theme };
|
package/src/lib/models/Models.ts
CHANGED
|
@@ -80,10 +80,10 @@ export interface fileObjType {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export interface ColumnInstructions {
|
|
83
|
-
toStringFn?: (any) => string;
|
|
84
|
-
toSortableValueFn?: (any) => string | number;
|
|
85
|
-
toFilterableValueFn?: (any) => string | number | Date;
|
|
86
|
-
renderComponent?: typeof SvelteComponent;
|
|
83
|
+
toStringFn?: (any) => string; // value by default
|
|
84
|
+
toSortableValueFn?: (any) => string | number; // value by default
|
|
85
|
+
toFilterableValueFn?: (any) => string | number | Date; // value by default
|
|
86
|
+
renderComponent?: typeof SvelteComponent; // null by default
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Table column type
|
|
@@ -111,7 +111,9 @@ export interface TableConfig<T> {
|
|
|
111
111
|
toggle?: boolean; // false by default
|
|
112
112
|
fitToScreen?: boolean; // true by default
|
|
113
113
|
height?: null | number; // null by default
|
|
114
|
+
rowHeight?: number; // auto by default
|
|
114
115
|
columns?: Columns;
|
|
116
|
+
exportable?: boolean; // false by default
|
|
115
117
|
pageSizes?: number[]; // [5, 10, 15, 20] by default
|
|
116
118
|
defaultPageSize?: number; // 10 by default
|
|
117
119
|
optionsComponent?: typeof SvelteComponent;
|
|
@@ -97,5 +97,18 @@ export const bexis2theme: CustomThemeConfig = {
|
|
|
97
97
|
'--color-surface-700': '149 149 149', // #959595
|
|
98
98
|
'--color-surface-800': '119 119 119', // #777777
|
|
99
99
|
'--color-surface-900': '98 98 98' // #626262
|
|
100
|
+
},
|
|
101
|
+
properties_dark: {
|
|
102
|
+
// surface | #2e2e2e
|
|
103
|
+
'--color-surface-50': '224 224 224', // #e0e0e0
|
|
104
|
+
'--color-surface-100': '213 213 213', // #d5d5d5
|
|
105
|
+
'--color-surface-200': '203 203 203', // #cbcbcb
|
|
106
|
+
'--color-surface-300': '171 171 171', // #ababab
|
|
107
|
+
'--color-surface-400': '109 109 109', // #6d6d6d
|
|
108
|
+
'--color-surface-500': '46 46 46', // #2e2e2e
|
|
109
|
+
'--color-surface-600': '41 41 41', // #292929
|
|
110
|
+
'--color-surface-700': '35 35 35', // #232323
|
|
111
|
+
'--color-surface-800': '28 28 28', // #1c1c1c
|
|
112
|
+
'--color-surface-900': '23 23 23' // #171717
|
|
100
113
|
}
|
|
101
114
|
};
|