@bexis2/bexis2-core-ui 0.2.15 → 0.2.17

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 CHANGED
@@ -1,7 +1,18 @@
1
1
  # bexis-core-ui
2
+ ## v0.2.17
3
+ - Multiselect
4
+ - fix target bind bug
5
+
6
+ ## v0.2.16
7
+ - Table
8
+ - width - fit to screen
9
+ - Cell - drag to change the height
10
+ - define a height to scroll the content
11
+ - header allways on top whie scrolling
12
+
2
13
  ## v0.2.15
3
14
  - Page - centered -> min width defined
4
- - MultiSelect -> add crealable
15
+ - MultiSelect -> add clearable
5
16
 
6
17
  ## v0.2.14
7
18
  - change fileUploaderModel to FileUploaderType
@@ -8,7 +8,7 @@ import {
8
8
  addTableFilter
9
9
  } from "svelte-headless-table/plugins";
10
10
  import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
11
- import { storePopup } from "@skeletonlabs/skeleton";
11
+ import { SlideToggle, storePopup } from "@skeletonlabs/skeleton";
12
12
  storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
13
13
  import TableFilter from "./TableFilter.svelte";
14
14
  import TablePagination from "./TablePagination.svelte";
@@ -18,10 +18,12 @@ let {
18
18
  id: tableId,
19
19
  data,
20
20
  columns,
21
+ height = null,
21
22
  optionsComponent,
22
23
  defaultPageSize = 10,
23
24
  pageSizes = [5, 10, 15, 20]
24
25
  } = config;
26
+ let fitToScreen = true;
25
27
  const dispatch = createEventDispatcher();
26
28
  const actionDispatcher = (obj) => dispatch("action", obj);
27
29
  const table = createTable(data, {
@@ -128,88 +130,101 @@ if (optionsComponent !== void 0) {
128
130
  const createdTableColumns = table.createColumns(tableColumns);
129
131
  const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(createdTableColumns);
130
132
  const { filterValue } = pluginStates.tableFilter;
131
- </script>
132
-
133
- <div class="grid gap-2">
134
- <div class="table-container">
135
- {#if $data.length > 0}
136
- <input
137
- class="input p-2 mb-2 border border-primary-500"
138
- type="text"
139
- bind:value={$filterValue}
140
- placeholder="Search rows..."
141
- />
142
- {/if}
143
- <table {...$tableAttrs} class="table table-compact bg-tertiary-200">
144
- <thead>
145
- {#each $headerRows as headerRow (headerRow.id)}
146
- <Subscribe
147
- rowAttrs={headerRow.attrs()}
148
- let:rowAttrs
149
- rowProps={headerRow.props()}
150
- let:rowProps
151
- >
152
- <tr {...rowAttrs} class="bg-primary-300">
153
- {#each headerRow.cells as cell (cell.id)}
154
- <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
155
- <th scope="col" class="!p-2 w-min" {...attrs}>
156
- <div class="flex w-full justify-between items-center">
157
- <div class="flex gap-1">
158
- <span
159
- class:underline={props.sort.order}
160
- class:normal-case={cell.id !== cell.label}
161
- class:cursor-pointer={!props.sort.disabled}
162
- on:click={props.sort.toggle}
163
- on:keydown={props.sort.toggle}
164
- >
165
- {cell.render()}
166
- </span>
167
- <div class="w-2">
168
- {#if props.sort.order === 'asc'}
169
-
170
- {:else if props.sort.order === 'desc'}
171
-
172
- {/if}
173
- </div>
174
- </div>
175
- {#if cell.isData()}
176
- {#if props.colFilter?.render}
177
- <div>
178
- <Render of={props.colFilter.render} />
179
- </div>
180
- {/if}
181
- {/if}
182
- </div>
183
- </th>
184
- </Subscribe>
185
- {/each}
186
- </tr>
187
- </Subscribe>
188
- {:else}
189
- <p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
190
- {/each}
191
- </thead>
192
-
193
- <tbody class="" {...$tableBodyAttrs}>
194
- {#each $pageRows as row (row.id)}
195
- <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
196
- <tr {...rowAttrs}>
197
- {#each row.cells as cell (cell?.id)}
198
- <Subscribe attrs={cell.attrs()} let:attrs>
199
- <td {...attrs} class="!p-2 w-min">
200
- <div class="flex items-center w-full h-full table-cell-fit">
201
- <Render of={cell.render()} />
202
- </div>
203
- </td>
204
- </Subscribe>
205
- {/each}
206
- </tr>
207
- </Subscribe>
208
- {/each}
209
- </tbody>
210
- </table>
211
- </div>
212
- {#if $data.length > 0}
213
- <TablePagination pageConfig={pluginStates.page} {pageSizes} />
214
- {/if}
215
- </div>
133
+ </script>
134
+
135
+ <div class="grid gap-2 overflow-auto" class:w-max={!fitToScreen} class:w-full={fitToScreen}>
136
+ <div class="table-container">
137
+ {#if $data.length > 0}
138
+ <input
139
+ class="input p-2 mb-2 border border-primary-500"
140
+ type="text"
141
+ bind:value={$filterValue}
142
+ placeholder="Search rows..."
143
+ />
144
+ {/if}
145
+ <SlideToggle
146
+ name="slider-label"
147
+ active="bg-primary-500"
148
+ size="sm"
149
+ checked={fitToScreen}
150
+ on:change={() => (fitToScreen = !fitToScreen)}>Fit to screen</SlideToggle
151
+ >
152
+
153
+ <div class="overflow-auto" style="height: {height}px">
154
+ <table {...$tableAttrs} class="table table-compact bg-tertiary-200 overflow-clip">
155
+ <thead class={height != null ? `sticky top-0` : ''}>
156
+ {#each $headerRows as headerRow (headerRow.id)}
157
+ <Subscribe
158
+ rowAttrs={headerRow.attrs()}
159
+ let:rowAttrs
160
+ rowProps={headerRow.props()}
161
+ let:rowProps
162
+ >
163
+ <tr {...rowAttrs} class="bg-primary-300">
164
+ {#each headerRow.cells as cell (cell.id)}
165
+ <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
166
+ <th scope="col" class="!p-2 w-min" {...attrs}>
167
+ <div class="flex w-full justify-between items-center">
168
+ <div class="flex gap-1 whitespace-pre-wrap">
169
+ <span
170
+ class:underline={props.sort.order}
171
+ class:normal-case={cell.id !== cell.label}
172
+ class:cursor-pointer={!props.sort.disabled}
173
+ on:click={props.sort.toggle}
174
+ on:keydown={props.sort.toggle}
175
+ >
176
+ {cell.render()}
177
+ </span>
178
+ <div class="w-2">
179
+ {#if props.sort.order === 'asc'}
180
+
181
+ {:else if props.sort.order === 'desc'}
182
+
183
+ {/if}
184
+ </div>
185
+ </div>
186
+ {#if cell.isData()}
187
+ {#if props.colFilter?.render}
188
+ <div class="">
189
+ <Render of={props.colFilter.render} />
190
+ </div>
191
+ {/if}
192
+ {/if}
193
+ </div>
194
+ </th>
195
+ </Subscribe>
196
+ {/each}
197
+ </tr>
198
+ </Subscribe>
199
+ {:else}
200
+ <p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
201
+ {/each}
202
+ </thead>
203
+
204
+ <tbody class="overflow-auto" {...$tableBodyAttrs}>
205
+ {#each $pageRows as row (row.id)}
206
+ <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
207
+ <tr {...rowAttrs}>
208
+ {#each row.cells as cell (cell?.id)}
209
+ <Subscribe attrs={cell.attrs()} let:attrs>
210
+ <td {...attrs} class="!p-2 w-max focus:resize">
211
+ <div
212
+ class="flex items-center h-max overflow-x-auto resize-none hover:resize"
213
+ class:max-w-md={!fitToScreen}
214
+ >
215
+ <Render of={cell.render()} />
216
+ </div>
217
+ </td>
218
+ </Subscribe>
219
+ {/each}
220
+ </tr>
221
+ </Subscribe>
222
+ {/each}
223
+ </tbody>
224
+ </table>
225
+ </div>
226
+ </div>
227
+ {#if $data.length > 0}
228
+ <TablePagination pageConfig={pluginStates.page} {pageSizes} />
229
+ {/if}
230
+ </div>
@@ -115,8 +115,8 @@ if (type === "object") {
115
115
  <Fa icon={faFilter} size="12" />
116
116
  </button>
117
117
 
118
- <div data-popup={`${popupId}`}>
119
- <div class="card p-3 absolute grid gap-2 shadow-lg z-10 w-min bg-base-100">
118
+ <div data-popup={`${popupId}`} class="z-50">
119
+ <div class="card p-3 grid gap-2 shadow-lg w-min bg-base-100">
120
120
  <button
121
121
  class="btn variant-filled-primary btn-sm"
122
122
  type="button"
@@ -26,6 +26,7 @@
26
26
 
27
27
  $: value = null;
28
28
  $: updateTarget(value);
29
+ $:target, setValue(target);
29
30
 
30
31
  let groupBy;
31
32
  $: groupBy;
@@ -73,70 +74,72 @@
73
74
  }
74
75
 
75
76
  onMount(async () => {
76
- console.log('on mount multiselect');
77
- ////console.log(source);
78
-
79
- //a) source is complex model is simple
80
- if (complexSource && !complexTarget && isMulti) {
81
- let items = [];
82
- // event.detail will be null unless isMulti is true and user has removed a single item
83
- for (let i in target) {
84
- let t = target[i];
85
- items.push(source.find((item) => item[itemId] === t));
86
- }
87
-
88
- isLoaded = true;
89
- if (items.length > 0) {
90
- value = items;
91
- }
92
- ////console.log(value);
93
- groupBy = (item) => item[itemGroup];
94
- }
95
-
96
- if (complexSource && complexTarget && isMulti) {
97
- value = target;
98
- isLoaded = true;
99
- groupBy = (item) => item[itemGroup];
100
- }
101
-
102
- //b) simple liust and simple model
103
- if (!complexSource && !complexTarget && isMulti) {
104
- ////console.log("source", source);
105
- ////console.log("target",target);
106
- isLoaded = true;
107
- //set target only if its nit empty
108
- if (target != null && target !== undefined && target != '') {
109
- value = target;
110
- }
111
- }
77
+ setValue(target);
78
+ });
112
79
 
113
- if (!isMulti) {
114
- //console.log("onmount",complexSource,complexTarget,value,target)
115
- if (!complexSource && !complexTarget) {
116
- value = {
117
- value: target,
118
- label: target
119
- };
80
+ function setValue(t)
81
+ {
82
+ //a) source is complex model is simple
83
+ if (complexSource && !complexTarget && isMulti) {
84
+ let items = [];
85
+ // event.detail will be null unless isMulti is true and user has removed a single item
86
+ for (let i in t) {
87
+ let t = target[i];
88
+ items.push(source.find((item) => item[itemId] === t));
89
+ }
90
+
91
+ isLoaded = true;
92
+ if (items.length > 0) {
93
+ value = items;
94
+ }
95
+ ////console.log(value);
96
+ groupBy = (item) => item[itemGroup];
120
97
  }
121
98
 
122
- if (complexSource && complexTarget) {
123
- value = target;
99
+ if (complexSource && complexTarget && isMulti) {
100
+ value = t;
101
+ isLoaded = true;
124
102
  groupBy = (item) => item[itemGroup];
125
103
  }
126
104
 
127
- if (complexSource && !complexTarget) {
128
- //value = target
129
- console.log(
130
- 'this case is currently not supported (complexSource,complexTarget,isMulti)',
131
- complexSource,
132
- complexTarget,
133
- isMulti
134
- );
105
+ //b) simple liust and simple model
106
+ if (!complexSource && !complexTarget && isMulti) {
107
+ ////console.log("source", source);
108
+ ////console.log("target",target);
109
+ isLoaded = true;
110
+ //set target only if its nit empty
111
+ if (t != null && t !== undefined && t != '') {
112
+ value = t;
113
+ }
135
114
  }
136
115
 
137
- isLoaded = true;
138
- }
139
- });
116
+ if (!isMulti) {
117
+ //console.log("onmount",complexSource,complexTarget,value,target)
118
+ if (!complexSource && !complexTarget) {
119
+ value = {
120
+ value: t,
121
+ label: t
122
+ };
123
+ }
124
+
125
+ if (complexSource && complexTarget) {
126
+ value = t;
127
+ groupBy = (item) => item[itemGroup];
128
+ }
129
+
130
+ if (complexSource && !complexTarget) {
131
+ //value = target
132
+ console.log(
133
+ 'this case is currently not supported (complexSource,complexTarget,isMulti)',
134
+ complexSource,
135
+ complexTarget,
136
+ isMulti
137
+ );
138
+ }
139
+
140
+ isLoaded = true;
141
+ }
142
+ }
140
143
  </script>
141
144
 
142
145
  <InputContainer {id} label={title} {feedback} {required} {help}>
@@ -81,6 +81,7 @@ export interface Columns {
81
81
  export interface TableConfig<T> {
82
82
  id: string;
83
83
  data: Writable<T[]>;
84
+ height?: null | number;
84
85
  columns?: Columns;
85
86
  pageSizes?: number[];
86
87
  defaultPageSize?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bexis2/bexis2-core-ui",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -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"
@@ -26,6 +26,7 @@
26
26
 
27
27
  $: value = null;
28
28
  $: updateTarget(value);
29
+ $:target, setValue(target);
29
30
 
30
31
  let groupBy;
31
32
  $: groupBy;
@@ -73,70 +74,72 @@
73
74
  }
74
75
 
75
76
  onMount(async () => {
76
- console.log('on mount multiselect');
77
- ////console.log(source);
78
-
79
- //a) source is complex model is simple
80
- if (complexSource && !complexTarget && isMulti) {
81
- let items = [];
82
- // event.detail will be null unless isMulti is true and user has removed a single item
83
- for (let i in target) {
84
- let t = target[i];
85
- items.push(source.find((item) => item[itemId] === t));
86
- }
87
-
88
- isLoaded = true;
89
- if (items.length > 0) {
90
- value = items;
91
- }
92
- ////console.log(value);
93
- groupBy = (item) => item[itemGroup];
94
- }
95
-
96
- if (complexSource && complexTarget && isMulti) {
97
- value = target;
98
- isLoaded = true;
99
- groupBy = (item) => item[itemGroup];
100
- }
101
-
102
- //b) simple liust and simple model
103
- if (!complexSource && !complexTarget && isMulti) {
104
- ////console.log("source", source);
105
- ////console.log("target",target);
106
- isLoaded = true;
107
- //set target only if its nit empty
108
- if (target != null && target !== undefined && target != '') {
109
- value = target;
110
- }
111
- }
77
+ setValue(target);
78
+ });
112
79
 
113
- if (!isMulti) {
114
- //console.log("onmount",complexSource,complexTarget,value,target)
115
- if (!complexSource && !complexTarget) {
116
- value = {
117
- value: target,
118
- label: target
119
- };
80
+ function setValue(t)
81
+ {
82
+ //a) source is complex model is simple
83
+ if (complexSource && !complexTarget && isMulti) {
84
+ let items = [];
85
+ // event.detail will be null unless isMulti is true and user has removed a single item
86
+ for (let i in t) {
87
+ let t = target[i];
88
+ items.push(source.find((item) => item[itemId] === t));
89
+ }
90
+
91
+ isLoaded = true;
92
+ if (items.length > 0) {
93
+ value = items;
94
+ }
95
+ ////console.log(value);
96
+ groupBy = (item) => item[itemGroup];
120
97
  }
121
98
 
122
- if (complexSource && complexTarget) {
123
- value = target;
99
+ if (complexSource && complexTarget && isMulti) {
100
+ value = t;
101
+ isLoaded = true;
124
102
  groupBy = (item) => item[itemGroup];
125
103
  }
126
104
 
127
- if (complexSource && !complexTarget) {
128
- //value = target
129
- console.log(
130
- 'this case is currently not supported (complexSource,complexTarget,isMulti)',
131
- complexSource,
132
- complexTarget,
133
- isMulti
134
- );
105
+ //b) simple liust and simple model
106
+ if (!complexSource && !complexTarget && isMulti) {
107
+ ////console.log("source", source);
108
+ ////console.log("target",target);
109
+ isLoaded = true;
110
+ //set target only if its nit empty
111
+ if (t != null && t !== undefined && t != '') {
112
+ value = t;
113
+ }
135
114
  }
136
115
 
137
- isLoaded = true;
138
- }
139
- });
116
+ if (!isMulti) {
117
+ //console.log("onmount",complexSource,complexTarget,value,target)
118
+ if (!complexSource && !complexTarget) {
119
+ value = {
120
+ value: t,
121
+ label: t
122
+ };
123
+ }
124
+
125
+ if (complexSource && complexTarget) {
126
+ value = t;
127
+ groupBy = (item) => item[itemGroup];
128
+ }
129
+
130
+ if (complexSource && !complexTarget) {
131
+ //value = target
132
+ console.log(
133
+ 'this case is currently not supported (complexSource,complexTarget,isMulti)',
134
+ complexSource,
135
+ complexTarget,
136
+ isMulti
137
+ );
138
+ }
139
+
140
+ isLoaded = true;
141
+ }
142
+ }
140
143
  </script>
141
144
 
142
145
  <InputContainer {id} label={title} {feedback} {required} {help}>
@@ -1,133 +1,134 @@
1
- import type { SvelteComponent } from 'svelte';
2
- import type { ColumnFilterFn } from 'svelte-headless-table/lib/plugins';
3
- import type { Writable } from 'svelte/store';
4
-
5
- import {decimalCharacterType, orientationType,textMarkerType,textSeperatorType} from './Enums'
6
-
7
- // page
8
- export interface linkType {
9
- label: string;
10
- url: string;
11
- }
12
-
13
- // Form
14
- export interface inputType {
15
- id: string;
16
- label: string;
17
- feedback: string[];
18
- invalid: boolean;
19
- valid: boolean;
20
- required: boolean;
21
- placeholder: string;
22
- }
23
-
24
- export interface fileInfoType {
25
- name: string;
26
- type: string;
27
- length: number;
28
- description: string;
29
- validationHash: string;
30
- }
31
-
32
- export interface fileUploaderType {
33
- accept: string[];
34
- existingFiles: fileInfoType[];
35
- descriptionType: number;
36
- multiple: boolean;
37
- maxSize: number;
38
- }
39
-
40
- export interface asciiFileReaderInfoType extends fileReaderInfoType {
41
- cells: boolean[];
42
- seperator: textSeperatorType;
43
- textMarker: textMarkerType;
44
- }
45
-
46
-
47
- export interface fileReaderInfoType {
48
- decimal: decimalCharacterType;
49
- orientation: orientationType;
50
- offset: number;
51
- variables: number;
52
- data: number;
53
- unit: number;
54
- description: number;
55
- dateformat: string;
56
- }
57
-
58
-
59
- export interface filesType {
60
- accepted: Blob[];
61
- rejected: Blob[];
62
- }
63
-
64
- export type userType = {
65
- name: string;
66
- };
67
-
68
- export interface fileObjType {
69
- path: string;
70
- lastModified: number;
71
- lastModifiedDate: Date;
72
- name: string;
73
- size: number;
74
- type: string;
75
- webkitRelativePath: string;
76
- }
77
-
78
- export interface ColumnInstructions {
79
- toStringFn?: (value: any) => string;
80
- toSortableValueFn?: (value: any) => string | number;
81
- toFilterableValueFn?: (value: any) => string | number | Date;
82
- renderComponent?: typeof SvelteComponent;
83
- }
84
-
85
- // Table column type
86
- export interface Column {
87
- header?: string;
88
- exclude?: boolean; // false by default
89
- instructions?: ColumnInstructions;
90
- disableFiltering?: boolean; // false by default
91
- disableSorting?: boolean; // false by default
92
- colFilterFn?: ColumnFilterFn;
93
- colFilterComponent?: typeof SvelteComponent;
94
- }
95
-
96
- export interface Columns {
97
- [key: string]: Column;
98
- }
99
-
100
- // Table config type
101
- export interface TableConfig<T> {
102
- id: string;
103
- data: Writable<T[]>;
104
- columns?: Columns;
105
- pageSizes?: number[];
106
- defaultPageSize?: number;
107
- optionsComponent?: typeof SvelteComponent;
108
- }
109
-
110
- // lists
111
- export interface keyValuePairType {
112
- id: number;
113
- text: string;
114
- }
115
-
116
- export interface listItemType {
117
- id: number;
118
- text: string;
119
- group: string;
120
- }
121
-
122
- //help
123
- export interface helpItemType {
124
- id?: string;
125
- name: string;
126
- description: string;
127
- link?: string;
128
- }
129
-
130
- export interface helpStoreType {
131
- itemId?: string;
132
- helpItems: helpItemType[];
133
- }
1
+ import type { SvelteComponent } from 'svelte';
2
+ import type { ColumnFilterFn } from 'svelte-headless-table/lib/plugins';
3
+ import type { Writable } from 'svelte/store';
4
+
5
+ import {decimalCharacterType, orientationType,textMarkerType,textSeperatorType} from './Enums'
6
+
7
+ // page
8
+ export interface linkType {
9
+ label: string;
10
+ url: string;
11
+ }
12
+
13
+ // Form
14
+ export interface inputType {
15
+ id: string;
16
+ label: string;
17
+ feedback: string[];
18
+ invalid: boolean;
19
+ valid: boolean;
20
+ required: boolean;
21
+ placeholder: string;
22
+ }
23
+
24
+ export interface fileInfoType {
25
+ name: string;
26
+ type: string;
27
+ length: number;
28
+ description: string;
29
+ validationHash: string;
30
+ }
31
+
32
+ export interface fileUploaderType {
33
+ accept: string[];
34
+ existingFiles: fileInfoType[];
35
+ descriptionType: number;
36
+ multiple: boolean;
37
+ maxSize: number;
38
+ }
39
+
40
+ export interface asciiFileReaderInfoType extends fileReaderInfoType {
41
+ cells: boolean[];
42
+ seperator: textSeperatorType;
43
+ textMarker: textMarkerType;
44
+ }
45
+
46
+
47
+ export interface fileReaderInfoType {
48
+ decimal: decimalCharacterType;
49
+ orientation: orientationType;
50
+ offset: number;
51
+ variables: number;
52
+ data: number;
53
+ unit: number;
54
+ description: number;
55
+ dateformat: string;
56
+ }
57
+
58
+
59
+ export interface filesType {
60
+ accepted: Blob[];
61
+ rejected: Blob[];
62
+ }
63
+
64
+ export type userType = {
65
+ name: string;
66
+ };
67
+
68
+ export interface fileObjType {
69
+ path: string;
70
+ lastModified: number;
71
+ lastModifiedDate: Date;
72
+ name: string;
73
+ size: number;
74
+ type: string;
75
+ webkitRelativePath: string;
76
+ }
77
+
78
+ export interface ColumnInstructions {
79
+ toStringFn?: (value: any) => string;
80
+ toSortableValueFn?: (value: any) => string | number;
81
+ toFilterableValueFn?: (value: any) => string | number | Date;
82
+ renderComponent?: typeof SvelteComponent;
83
+ }
84
+
85
+ // Table column type
86
+ export interface Column {
87
+ header?: string;
88
+ exclude?: boolean; // false by default
89
+ instructions?: ColumnInstructions;
90
+ disableFiltering?: boolean; // false by default
91
+ disableSorting?: boolean; // false by default
92
+ colFilterFn?: ColumnFilterFn;
93
+ colFilterComponent?: typeof SvelteComponent;
94
+ }
95
+
96
+ export interface Columns {
97
+ [key: string]: Column;
98
+ }
99
+
100
+ // Table config type
101
+ export interface TableConfig<T> {
102
+ id: string;
103
+ data: Writable<T[]>;
104
+ height?: null | number;
105
+ columns?: Columns;
106
+ pageSizes?: number[];
107
+ defaultPageSize?: number;
108
+ optionsComponent?: typeof SvelteComponent;
109
+ }
110
+
111
+ // lists
112
+ export interface keyValuePairType {
113
+ id: number;
114
+ text: string;
115
+ }
116
+
117
+ export interface listItemType {
118
+ id: number;
119
+ text: string;
120
+ group: string;
121
+ }
122
+
123
+ //help
124
+ export interface helpItemType {
125
+ id?: string;
126
+ name: string;
127
+ description: string;
128
+ link?: string;
129
+ }
130
+
131
+ export interface helpStoreType {
132
+ itemId?: string;
133
+ helpItems: helpItemType[];
134
+ }