@bexis2/bexis2-core-ui 0.2.14 → 0.2.16

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,246 +1,262 @@
1
- <script lang="ts">
2
- import { createEventDispatcher } from 'svelte';
3
- import { createTable, Subscribe, Render, createRender } from 'svelte-headless-table';
4
- import {
5
- addSortBy,
6
- addPagination,
7
- addExpandedRows,
8
- addColumnFilters,
9
- addTableFilter
10
- } from 'svelte-headless-table/plugins';
11
- import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
12
- import { storePopup } from '@skeletonlabs/skeleton';
13
-
14
- storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
15
-
16
- import TableFilter from './TableFilter.svelte';
17
- import TablePagination from './TablePagination.svelte';
18
- import { columnFilter, searchFilter } from './filter';
19
- import type { TableConfig } from '$lib/models/Models';
20
-
21
- export let config: TableConfig<any>;
22
- let {
23
- id: tableId,
24
- data,
25
- columns,
26
- optionsComponent,
27
- defaultPageSize = 10,
28
- pageSizes = [5, 10, 15, 20]
29
- } = config;
30
-
31
- type AccessorType = keyof (typeof $data)[number];
32
-
33
- const dispatch = createEventDispatcher();
34
- const actionDispatcher = (obj) => dispatch('action', obj);
35
-
36
- const table = createTable(data, {
37
- colFilter: addColumnFilters(),
38
- tableFilter: addTableFilter({
39
- fn: searchFilter
40
- }),
41
- sort: addSortBy({ disableMultiSort: true }),
42
- page: addPagination({ initialPageSize: defaultPageSize }),
43
- expand: addExpandedRows()
44
- });
45
-
46
- const accessors: AccessorType[] =
47
- $data.length > 0 ? (Object.keys($data[0]) as AccessorType[]) : [];
48
-
49
- const tableColumns = [
50
- ...accessors
51
- .filter((accessor) => {
52
- const key = accessor as string;
53
- if (columns !== undefined && key in columns && columns[key].exclude === true) {
54
- return false;
55
- }
56
- return true;
57
- })
58
- .map((accessor) => {
59
- const key = accessor as string;
60
- if (columns !== undefined && key in columns) {
61
- const {
62
- header,
63
- colFilterFn,
64
- colFilterComponent,
65
- instructions,
66
- disableFiltering = false,
67
- disableSorting = false
68
- } = columns[key];
69
-
70
- const { toSortableValueFn, toFilterableValueFn, toStringFn, renderComponent } =
71
- instructions ?? {};
72
-
73
- return table.column({
74
- header: header ?? key,
75
- accessor: accessor,
76
- cell: ({ value, row }) => {
77
- return renderComponent
78
- ? createRender(renderComponent, { value, row })
79
- : toStringFn
80
- ? toStringFn(value)
81
- : value;
82
- },
83
- plugins: {
84
- sort: {
85
- disable: disableSorting,
86
- invert: true,
87
- getSortValue: (row) => {
88
- return toSortableValueFn ? toSortableValueFn(row) : row;
89
- }
90
- },
91
- colFilter: !disableFiltering
92
- ? {
93
- fn: ({ filterValue, value }) => {
94
- const val = toFilterableValueFn ? toFilterableValueFn(value) : value;
95
-
96
- return colFilterFn
97
- ? colFilterFn({ filterValue, value: val })
98
- : columnFilter({ filterValue, value: val });
99
- },
100
- render: ({ filterValue, values, id }) => {
101
- return createRender(colFilterComponent ?? TableFilter, {
102
- filterValue,
103
- id,
104
- tableId,
105
- values,
106
- toFilterableValueFn
107
- });
108
- }
109
- }
110
- : undefined,
111
- tableFilter: {
112
- getFilterValue: (row) => {
113
- return toStringFn ? toStringFn(row) : row;
114
- }
115
- }
116
- }
117
- });
118
- } else {
119
- return table.column({
120
- header: key,
121
- accessor: accessor,
122
- plugins: {
123
- sort: {
124
- invert: true
125
- },
126
- colFilter: {
127
- fn: columnFilter,
128
- render: ({ filterValue, values, id }) =>
129
- createRender(TableFilter, {
130
- filterValue,
131
- id,
132
- tableId,
133
- values
134
- })
135
- }
136
- }
137
- });
138
- }
139
- })
140
- ];
141
-
142
- if (optionsComponent !== undefined) {
143
- tableColumns.push(
144
- table.display({
145
- id: 'options',
146
- header: '',
147
- cell: ({ row }, _) => {
148
- return createRender(optionsComponent!, {
149
- row: row.isData() ? row.original : null,
150
- dispatchFn: actionDispatcher
151
- });
152
- }
153
- }) as any
154
- );
155
- }
156
-
157
- const createdTableColumns = table.createColumns(tableColumns);
158
-
159
- const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } =
160
- table.createViewModel(createdTableColumns);
161
- const { filterValue } = pluginStates.tableFilter;
162
- </script>
163
-
164
- <div class="grid gap-2">
165
- <div class="table-container">
166
- {#if $data.length > 0}
167
- <input
168
- class="input p-2 mb-2 border border-primary-500"
169
- type="text"
170
- bind:value={$filterValue}
171
- placeholder="Search rows..."
172
- />
173
- {/if}
174
- <table {...$tableAttrs} class="table table-compact bg-tertiary-200">
175
- <thead>
176
- {#each $headerRows as headerRow (headerRow.id)}
177
- <Subscribe
178
- rowAttrs={headerRow.attrs()}
179
- let:rowAttrs
180
- rowProps={headerRow.props()}
181
- let:rowProps
182
- >
183
- <tr {...rowAttrs} class="bg-primary-300">
184
- {#each headerRow.cells as cell (cell.id)}
185
- <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
186
- <th scope="col" class="!p-2 w-min" {...attrs}>
187
- <div class="flex w-full justify-between items-center">
188
- <div class="flex gap-1">
189
- <span
190
- class:underline={props.sort.order}
191
- class:normal-case={cell.id !== cell.label}
192
- class:cursor-pointer={!props.sort.disabled}
193
- on:click={props.sort.toggle}
194
- on:keydown={props.sort.toggle}
195
- >
196
- {cell.render()}
197
- </span>
198
- <div class="w-2">
199
- {#if props.sort.order === 'asc'}
200
-
201
- {:else if props.sort.order === 'desc'}
202
-
203
- {/if}
204
- </div>
205
- </div>
206
- {#if cell.isData()}
207
- {#if props.colFilter?.render}
208
- <div>
209
- <Render of={props.colFilter.render} />
210
- </div>
211
- {/if}
212
- {/if}
213
- </div>
214
- </th>
215
- </Subscribe>
216
- {/each}
217
- </tr>
218
- </Subscribe>
219
- {:else}
220
- <p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
221
- {/each}
222
- </thead>
223
-
224
- <tbody class="" {...$tableBodyAttrs}>
225
- {#each $pageRows as row (row.id)}
226
- <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
227
- <tr {...rowAttrs}>
228
- {#each row.cells as cell (cell?.id)}
229
- <Subscribe attrs={cell.attrs()} let:attrs>
230
- <td {...attrs} class="!p-2 w-min">
231
- <div class="flex items-center w-full h-full table-cell-fit">
232
- <Render of={cell.render()} />
233
- </div>
234
- </td>
235
- </Subscribe>
236
- {/each}
237
- </tr>
238
- </Subscribe>
239
- {/each}
240
- </tbody>
241
- </table>
242
- </div>
243
- {#if $data.length > 0}
244
- <TablePagination pageConfig={pluginStates.page} {pageSizes} />
245
- {/if}
246
- </div>
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from 'svelte';
3
+ import { createTable, Subscribe, Render, createRender } from 'svelte-headless-table';
4
+ import {
5
+ addSortBy,
6
+ addPagination,
7
+ addExpandedRows,
8
+ addColumnFilters,
9
+ addTableFilter
10
+ } from 'svelte-headless-table/plugins';
11
+ import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
12
+ import { SlideToggle, storePopup } from '@skeletonlabs/skeleton';
13
+
14
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
15
+
16
+ import TableFilter from './TableFilter.svelte';
17
+ import TablePagination from './TablePagination.svelte';
18
+ import { columnFilter, searchFilter } from './filter';
19
+ import type { TableConfig } from '$lib/models/Models';
20
+
21
+ export let config: TableConfig<any>;
22
+
23
+ let {
24
+ id: tableId,
25
+ data,
26
+ columns,
27
+ height = null,
28
+ optionsComponent,
29
+ defaultPageSize = 10,
30
+ pageSizes = [5, 10, 15, 20]
31
+ } = config;
32
+ let fitToScreen = true;
33
+
34
+ type AccessorType = keyof (typeof $data)[number];
35
+
36
+ const dispatch = createEventDispatcher();
37
+ const actionDispatcher = (obj) => dispatch('action', obj);
38
+
39
+ const table = createTable(data, {
40
+ colFilter: addColumnFilters(),
41
+ tableFilter: addTableFilter({
42
+ fn: searchFilter
43
+ }),
44
+ sort: addSortBy({ disableMultiSort: true }),
45
+ page: addPagination({ initialPageSize: defaultPageSize }),
46
+ expand: addExpandedRows()
47
+ });
48
+
49
+ const accessors: AccessorType[] =
50
+ $data.length > 0 ? (Object.keys($data[0]) as AccessorType[]) : [];
51
+
52
+ const tableColumns = [
53
+ ...accessors
54
+ .filter((accessor) => {
55
+ const key = accessor as string;
56
+ if (columns !== undefined && key in columns && columns[key].exclude === true) {
57
+ return false;
58
+ }
59
+ return true;
60
+ })
61
+ .map((accessor) => {
62
+ const key = accessor as string;
63
+ if (columns !== undefined && key in columns) {
64
+ const {
65
+ header,
66
+ colFilterFn,
67
+ colFilterComponent,
68
+ instructions,
69
+ disableFiltering = false,
70
+ disableSorting = false
71
+ } = columns[key];
72
+
73
+ const { toSortableValueFn, toFilterableValueFn, toStringFn, renderComponent } =
74
+ instructions ?? {};
75
+
76
+ return table.column({
77
+ header: header ?? key,
78
+ accessor: accessor,
79
+ cell: ({ value, row }) => {
80
+ return renderComponent
81
+ ? createRender(renderComponent, { value, row })
82
+ : toStringFn
83
+ ? toStringFn(value)
84
+ : value;
85
+ },
86
+ plugins: {
87
+ sort: {
88
+ disable: disableSorting,
89
+ invert: true,
90
+ getSortValue: (row) => {
91
+ return toSortableValueFn ? toSortableValueFn(row) : row;
92
+ }
93
+ },
94
+ colFilter: !disableFiltering
95
+ ? {
96
+ fn: ({ filterValue, value }) => {
97
+ const val = toFilterableValueFn ? toFilterableValueFn(value) : value;
98
+
99
+ return colFilterFn
100
+ ? colFilterFn({ filterValue, value: val })
101
+ : columnFilter({ filterValue, value: val });
102
+ },
103
+ render: ({ filterValue, values, id }) => {
104
+ return createRender(colFilterComponent ?? TableFilter, {
105
+ filterValue,
106
+ id,
107
+ tableId,
108
+ values,
109
+ toFilterableValueFn
110
+ });
111
+ }
112
+ }
113
+ : undefined,
114
+ tableFilter: {
115
+ getFilterValue: (row) => {
116
+ return toStringFn ? toStringFn(row) : row;
117
+ }
118
+ }
119
+ }
120
+ });
121
+ } else {
122
+ return table.column({
123
+ header: key,
124
+ accessor: accessor,
125
+ plugins: {
126
+ sort: {
127
+ invert: true
128
+ },
129
+ colFilter: {
130
+ fn: columnFilter,
131
+ render: ({ filterValue, values, id }) =>
132
+ createRender(TableFilter, {
133
+ filterValue,
134
+ id,
135
+ tableId,
136
+ values
137
+ })
138
+ }
139
+ }
140
+ });
141
+ }
142
+ })
143
+ ];
144
+
145
+ if (optionsComponent !== undefined) {
146
+ tableColumns.push(
147
+ table.display({
148
+ id: 'options',
149
+ header: '',
150
+ cell: ({ row }, _) => {
151
+ return createRender(optionsComponent!, {
152
+ row: row.isData() ? row.original : null,
153
+ dispatchFn: actionDispatcher
154
+ });
155
+ }
156
+ }) as any
157
+ );
158
+ }
159
+
160
+ const createdTableColumns = table.createColumns(tableColumns);
161
+
162
+ const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } =
163
+ table.createViewModel(createdTableColumns);
164
+ const { filterValue } = pluginStates.tableFilter;
165
+ </script>
166
+
167
+ <div class="grid gap-2 overflow-auto" class:w-max={!fitToScreen} class:w-full={fitToScreen}>
168
+ <div class="table-container">
169
+ {#if $data.length > 0}
170
+ <input
171
+ class="input p-2 mb-2 border border-primary-500"
172
+ type="text"
173
+ bind:value={$filterValue}
174
+ placeholder="Search rows..."
175
+ />
176
+ {/if}
177
+ <SlideToggle
178
+ name="slider-label"
179
+ active="bg-primary-500"
180
+ size="sm"
181
+ checked={fitToScreen}
182
+ on:change={() => (fitToScreen = !fitToScreen)}>Fit to screen</SlideToggle
183
+ >
184
+
185
+ <div class="overflow-auto" style="height: {height}px">
186
+ <table {...$tableAttrs} class="table table-compact bg-tertiary-200 overflow-clip">
187
+ <thead class={height != null ? `sticky top-0` : ''}>
188
+ {#each $headerRows as headerRow (headerRow.id)}
189
+ <Subscribe
190
+ rowAttrs={headerRow.attrs()}
191
+ let:rowAttrs
192
+ rowProps={headerRow.props()}
193
+ let:rowProps
194
+ >
195
+ <tr {...rowAttrs} class="bg-primary-300">
196
+ {#each headerRow.cells as cell (cell.id)}
197
+ <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
198
+ <th scope="col" class="!p-2 w-min" {...attrs}>
199
+ <div class="flex w-full justify-between items-center">
200
+ <div class="flex gap-1 whitespace-pre-wrap">
201
+ <span
202
+ class:underline={props.sort.order}
203
+ class:normal-case={cell.id !== cell.label}
204
+ class:cursor-pointer={!props.sort.disabled}
205
+ on:click={props.sort.toggle}
206
+ on:keydown={props.sort.toggle}
207
+ >
208
+ {cell.render()}
209
+ </span>
210
+ <div class="w-2">
211
+ {#if props.sort.order === 'asc'}
212
+
213
+ {:else if props.sort.order === 'desc'}
214
+
215
+ {/if}
216
+ </div>
217
+ </div>
218
+ {#if cell.isData()}
219
+ {#if props.colFilter?.render}
220
+ <div class="">
221
+ <Render of={props.colFilter.render} />
222
+ </div>
223
+ {/if}
224
+ {/if}
225
+ </div>
226
+ </th>
227
+ </Subscribe>
228
+ {/each}
229
+ </tr>
230
+ </Subscribe>
231
+ {:else}
232
+ <p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
233
+ {/each}
234
+ </thead>
235
+
236
+ <tbody class="overflow-auto" {...$tableBodyAttrs}>
237
+ {#each $pageRows as row (row.id)}
238
+ <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
239
+ <tr {...rowAttrs}>
240
+ {#each row.cells as cell (cell?.id)}
241
+ <Subscribe attrs={cell.attrs()} let:attrs>
242
+ <td {...attrs} class="!p-2 w-max focus:resize">
243
+ <div
244
+ class="flex items-center h-max overflow-x-auto resize-none hover:resize"
245
+ class:max-w-md={!fitToScreen}
246
+ >
247
+ <Render of={cell.render()} />
248
+ </div>
249
+ </td>
250
+ </Subscribe>
251
+ {/each}
252
+ </tr>
253
+ </Subscribe>
254
+ {/each}
255
+ </tbody>
256
+ </table>
257
+ </div>
258
+ </div>
259
+ {#if $data.length > 0}
260
+ <TablePagination pageConfig={pluginStates.page} {pageSizes} />
261
+ {/if}
262
+ </div>
@@ -123,8 +123,8 @@
123
123
  <Fa icon={faFilter} size="12" />
124
124
  </button>
125
125
 
126
- <div data-popup={`${popupId}`}>
127
- <div class="card p-3 absolute grid gap-2 shadow-lg z-10 w-min bg-base-100">
126
+ <div data-popup={`${popupId}`} class="z-50">
127
+ <div class="card p-3 grid gap-2 shadow-lg w-min bg-base-100">
128
128
  <button
129
129
  class="btn variant-filled-primary btn-sm"
130
130
  type="button"
@@ -20,6 +20,7 @@
20
20
  export let invalid = false;
21
21
  export let loading = false;
22
22
  export let help = false;
23
+ export let clearable = true;
23
24
 
24
25
  let isLoaded = false;
25
26
 
@@ -150,6 +151,7 @@
150
151
  {placeholder}
151
152
  hasError={invalid}
152
153
  {loading}
154
+ {clearable}
153
155
  on:change
154
156
  on:input
155
157
  on:focus