@bexis2/bexis2-core-ui 0.4.0 → 0.4.1
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 +13 -0
- package/dist/components/Table/TableContent.svelte +39 -13
- package/dist/components/Table/TableFilter.svelte +50 -24
- package/dist/components/Table/TableFilter.svelte.d.ts +2 -0
- package/dist/components/Table/shared.js +8 -8
- package/dist/models/Models.d.ts +1 -0
- package/dist/models/Models.js +2 -0
- package/package.json +2 -1
- package/src/lib/components/Table/TableContent.svelte +41 -14
- package/src/lib/components/Table/TableFilter.svelte +61 -35
- package/src/lib/components/Table/shared.ts +9 -8
- package/src/lib/models/Models.ts +2 -0
package/README.md
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
# bexis-core-ui
|
|
2
|
+
## 0.4.1
|
|
3
|
+
- table
|
|
4
|
+
- Server-side searching
|
|
5
|
+
- Case-insensitive filtering of missing values
|
|
6
|
+
- Date formatting for display patterns
|
|
7
|
+
- Handle missing values regardless of the data type
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
- update dependency libaries
|
|
11
|
+
- svelte
|
|
12
|
+
- sveltekit
|
|
13
|
+
- vite
|
|
14
|
+
|
|
2
15
|
## 0.3.13
|
|
3
16
|
## 0.3.12
|
|
4
17
|
- table
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
|
|
15
15
|
import { SlideToggle, storePopup } from "@skeletonlabs/skeleton";
|
|
16
16
|
storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
|
|
17
|
+
import Spinner from "../page/Spinner.svelte";
|
|
17
18
|
import TableFilter from "./TableFilter.svelte";
|
|
18
19
|
import TableFilterServer from "./TableFilterServer.svelte";
|
|
19
20
|
import TablePagination from "./TablePagination.svelte";
|
|
@@ -68,6 +69,7 @@ let {
|
|
|
68
69
|
// Version ID to send with the request
|
|
69
70
|
} = config;
|
|
70
71
|
let searchValue = "";
|
|
72
|
+
let isFetching = false;
|
|
71
73
|
const filters = writable({});
|
|
72
74
|
const dispatch = createEventDispatcher();
|
|
73
75
|
const actionDispatcher = (obj) => dispatch("action", obj);
|
|
@@ -167,7 +169,9 @@ const tableColumns = [
|
|
|
167
169
|
tableId,
|
|
168
170
|
values,
|
|
169
171
|
toFilterableValueFn,
|
|
170
|
-
filters
|
|
172
|
+
filters,
|
|
173
|
+
toStringFn,
|
|
174
|
+
pageIndex
|
|
171
175
|
});
|
|
172
176
|
}
|
|
173
177
|
} : void 0,
|
|
@@ -205,7 +209,8 @@ const tableColumns = [
|
|
|
205
209
|
id,
|
|
206
210
|
tableId,
|
|
207
211
|
values,
|
|
208
|
-
filters
|
|
212
|
+
filters,
|
|
213
|
+
pageIndex
|
|
209
214
|
});
|
|
210
215
|
}
|
|
211
216
|
}
|
|
@@ -244,14 +249,25 @@ const updateTable = async () => {
|
|
|
244
249
|
sendModel.version = versionId;
|
|
245
250
|
sendModel.id = entityId;
|
|
246
251
|
sendModel.filter = normalizeFilters($filters);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
252
|
+
let fetchData;
|
|
253
|
+
try {
|
|
254
|
+
isFetching = true;
|
|
255
|
+
fetchData = await fetch(URL, {
|
|
256
|
+
headers: {
|
|
257
|
+
"Content-Type": "application/json",
|
|
258
|
+
Authorization: `Bearer ${token}`
|
|
259
|
+
},
|
|
260
|
+
method: "POST",
|
|
261
|
+
body: JSON.stringify(sendModel)
|
|
262
|
+
});
|
|
263
|
+
} catch (error) {
|
|
264
|
+
throw new Error(`Network error: ${error.message}`);
|
|
265
|
+
} finally {
|
|
266
|
+
isFetching = false;
|
|
267
|
+
}
|
|
268
|
+
if (!fetchData.ok) {
|
|
269
|
+
throw new Error("Failed to fetch data");
|
|
270
|
+
}
|
|
255
271
|
const response = await fetchData.json();
|
|
256
272
|
if (response.columns !== void 0) {
|
|
257
273
|
columns = convertServerColumns(response.columns);
|
|
@@ -282,7 +298,13 @@ $:
|
|
|
282
298
|
<!-- Enable the search filter if table is not empty -->
|
|
283
299
|
{#if $data.length > 0}
|
|
284
300
|
{#if !serverSide}
|
|
285
|
-
<
|
|
301
|
+
<form
|
|
302
|
+
class="flex gap-2"
|
|
303
|
+
on:submit|preventDefault={() => {
|
|
304
|
+
sendModel.q = searchValue;
|
|
305
|
+
$filterValue = searchValue;
|
|
306
|
+
}}
|
|
307
|
+
>
|
|
286
308
|
<div class="relative w-full flex items-center">
|
|
287
309
|
<input
|
|
288
310
|
class="input p-2 border border-primary-500"
|
|
@@ -295,18 +317,20 @@ $:
|
|
|
295
317
|
class="absolute right-3 items-center"
|
|
296
318
|
on:click|preventDefault={() => {
|
|
297
319
|
searchValue = '';
|
|
320
|
+
sendModel.q = '';
|
|
298
321
|
$filterValue = '';
|
|
299
322
|
}}><Fa icon={faXmark} /></button
|
|
300
323
|
>
|
|
301
324
|
</div>
|
|
302
325
|
<button
|
|
303
|
-
type="
|
|
326
|
+
type="submit"
|
|
304
327
|
class="btn variant-filled-primary"
|
|
305
328
|
on:click|preventDefault={() => {
|
|
306
329
|
$filterValue = searchValue;
|
|
330
|
+
sendModel.q = searchValue;
|
|
307
331
|
}}>Search</button
|
|
308
332
|
>
|
|
309
|
-
</
|
|
333
|
+
</form>
|
|
310
334
|
{/if}
|
|
311
335
|
<div class="flex justify-between items-center py-2 w-full">
|
|
312
336
|
<div>
|
|
@@ -407,6 +431,8 @@ $:
|
|
|
407
431
|
</tr>
|
|
408
432
|
</Subscribe>
|
|
409
433
|
{/each}
|
|
434
|
+
{:else if isFetching}
|
|
435
|
+
<div class="p-10"><Spinner /></div>
|
|
410
436
|
{:else}
|
|
411
437
|
<!-- Table is empty -->
|
|
412
438
|
<p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
|
|
@@ -6,9 +6,24 @@ export let values;
|
|
|
6
6
|
export let id;
|
|
7
7
|
export let tableId;
|
|
8
8
|
export let toFilterableValueFn = void 0;
|
|
9
|
+
export let toStringFn = void 0;
|
|
9
10
|
export let filterValue;
|
|
10
11
|
export let filters;
|
|
12
|
+
export let pageIndex;
|
|
11
13
|
let active = false;
|
|
14
|
+
let type = "string";
|
|
15
|
+
let isDate = false;
|
|
16
|
+
let dropdowns = [];
|
|
17
|
+
$values.forEach((item) => {
|
|
18
|
+
if (item) {
|
|
19
|
+
type = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
|
|
20
|
+
if (type === "object") {
|
|
21
|
+
if (item instanceof Date) {
|
|
22
|
+
isDate = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
12
27
|
const options = {
|
|
13
28
|
number: [
|
|
14
29
|
{
|
|
@@ -89,28 +104,37 @@ const options = {
|
|
|
89
104
|
}
|
|
90
105
|
]
|
|
91
106
|
};
|
|
92
|
-
let dropdowns = [];
|
|
93
107
|
const popupId = `${tableId}-${id}`;
|
|
94
108
|
const popupFeatured = {
|
|
95
109
|
event: "click",
|
|
96
110
|
target: popupId,
|
|
97
111
|
placement: "bottom-start"
|
|
98
112
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
$values.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
const stringValues = (
|
|
114
|
+
// type === 'number' ?
|
|
115
|
+
$values.map((item) => toStringFn ? toStringFn(item) : item)
|
|
116
|
+
);
|
|
117
|
+
const missingValues = (
|
|
118
|
+
// type === 'number' ?
|
|
119
|
+
stringValues.reduce((acc, item, index) => {
|
|
120
|
+
acc[typeof item === "string" ? item.toLowerCase() : item] = $values[index];
|
|
121
|
+
return acc;
|
|
122
|
+
}, {})
|
|
123
|
+
);
|
|
124
|
+
const getMissingValue = (value) => {
|
|
125
|
+
return Object.keys(missingValues).includes(value.toLowerCase()) ? missingValues[value.toLowerCase()] : value;
|
|
126
|
+
};
|
|
111
127
|
const optionChangeHandler = (e, index) => {
|
|
112
128
|
delete $filters[id][dropdowns[index].option];
|
|
113
|
-
$filters[id] = {
|
|
129
|
+
$filters[id] = {
|
|
130
|
+
...$filters[id],
|
|
131
|
+
[e.target.value]: (
|
|
132
|
+
// type === 'number'
|
|
133
|
+
// ?
|
|
134
|
+
getMissingValue(dropdowns[index].value)
|
|
135
|
+
)
|
|
136
|
+
// : dropdowns[index].value
|
|
137
|
+
};
|
|
114
138
|
$filters = $filters;
|
|
115
139
|
dropdowns[index] = {
|
|
116
140
|
...dropdowns[index],
|
|
@@ -120,11 +144,18 @@ const optionChangeHandler = (e, index) => {
|
|
|
120
144
|
const valueChangeHandler = (e, index) => {
|
|
121
145
|
dropdowns[index] = {
|
|
122
146
|
...dropdowns[index],
|
|
123
|
-
value: type === "
|
|
147
|
+
value: type === "date" ? new Date(e.target.value) : e.target.value
|
|
124
148
|
};
|
|
125
149
|
$filters = {
|
|
126
150
|
...$filters,
|
|
127
|
-
[id]: {
|
|
151
|
+
[id]: {
|
|
152
|
+
...$filters[id],
|
|
153
|
+
[dropdowns[index].option]: (
|
|
154
|
+
// type === 'number' ?
|
|
155
|
+
getMissingValue(e.target.value)
|
|
156
|
+
)
|
|
157
|
+
// : dropdowns[index].value
|
|
158
|
+
}
|
|
128
159
|
};
|
|
129
160
|
};
|
|
130
161
|
const addFilter = (option, value) => {
|
|
@@ -178,6 +209,7 @@ $:
|
|
|
178
209
|
addFilter(options[type][0].value, undefined);
|
|
179
210
|
$filterValue = $filters[id];
|
|
180
211
|
active = false;
|
|
212
|
+
$pageIndex = 0;
|
|
181
213
|
}}>Clear Filters</button
|
|
182
214
|
>
|
|
183
215
|
|
|
@@ -212,14 +244,7 @@ $:
|
|
|
212
244
|
{/if}
|
|
213
245
|
</div>
|
|
214
246
|
|
|
215
|
-
{#if type === 'number'}
|
|
216
|
-
<input
|
|
217
|
-
type="number"
|
|
218
|
-
class="input p-1 border border-primary-500"
|
|
219
|
-
on:input={(e) => valueChangeHandler(e, index)}
|
|
220
|
-
bind:value={dropdown.value}
|
|
221
|
-
/>
|
|
222
|
-
{:else if type === 'string'}
|
|
247
|
+
{#if type === 'number' || type === 'string'}
|
|
223
248
|
<input
|
|
224
249
|
type="text"
|
|
225
250
|
class="input p-1 border border-primary-500"
|
|
@@ -258,6 +283,7 @@ $:
|
|
|
258
283
|
class="btn variant-filled-primary btn-sm"
|
|
259
284
|
type="button"
|
|
260
285
|
on:click|preventDefault={() => {
|
|
286
|
+
$pageIndex = 0;
|
|
261
287
|
$filterValue = $filters[id];
|
|
262
288
|
active = true;
|
|
263
289
|
}}>Apply</button
|
|
@@ -5,8 +5,10 @@ declare const __propDef: {
|
|
|
5
5
|
id: any;
|
|
6
6
|
tableId: any;
|
|
7
7
|
toFilterableValueFn?: ((value: any) => any) | undefined;
|
|
8
|
+
toStringFn?: ((value: any) => string) | undefined;
|
|
8
9
|
filterValue: any;
|
|
9
10
|
filters: any;
|
|
11
|
+
pageIndex: any;
|
|
10
12
|
};
|
|
11
13
|
events: {
|
|
12
14
|
[evt: string]: CustomEvent<any>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dateFormat from 'dateformat';
|
|
1
2
|
// Function to determine minWidth for a column to simplify the logic in the HTML
|
|
2
3
|
export const minWidth = (id, columns) => {
|
|
3
4
|
if (columns && id in columns) {
|
|
@@ -94,14 +95,13 @@ export const convertServerColumns = (columns) => {
|
|
|
94
95
|
const columnsConfig = {};
|
|
95
96
|
columns.forEach((col) => {
|
|
96
97
|
let instructions = {};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// }
|
|
98
|
+
if (col.instructions?.displayPattern) {
|
|
99
|
+
instructions = {
|
|
100
|
+
toStringFn: (date) => dateFormat(date, col.instructions?.displayPattern || ''),
|
|
101
|
+
toSortableValueFn: (date) => date.getTime(),
|
|
102
|
+
toFilterableValueFn: (date) => date
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
105
|
if (col.instructions?.missingValues) {
|
|
106
106
|
instructions = {
|
|
107
107
|
...instructions,
|
package/dist/models/Models.d.ts
CHANGED
package/dist/models/Models.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bexis2/bexis2-core-ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
|
74
74
|
"axios": "^1.6.7",
|
|
75
75
|
"codemirror": "^6.0.1",
|
|
76
|
+
"dateformat": "^5.0.3",
|
|
76
77
|
"delay": "^6.0.0",
|
|
77
78
|
"dotenv": "^16.4.5",
|
|
78
79
|
"eslint4b-prebuilt": "^6.7.2",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
|
|
21
21
|
|
|
22
|
+
import Spinner from '../page/Spinner.svelte';
|
|
22
23
|
import TableFilter from './TableFilter.svelte';
|
|
23
24
|
import TableFilterServer from './TableFilterServer.svelte';
|
|
24
25
|
import TablePagination from './TablePagination.svelte';
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
} = config;
|
|
62
63
|
|
|
63
64
|
let searchValue = '';
|
|
65
|
+
let isFetching = false;
|
|
64
66
|
|
|
65
67
|
const filters = writable<{
|
|
66
68
|
[key: string]: { [key in FilterOptionsEnum]?: number | string | Date };
|
|
@@ -194,7 +196,9 @@
|
|
|
194
196
|
tableId,
|
|
195
197
|
values,
|
|
196
198
|
toFilterableValueFn,
|
|
197
|
-
filters
|
|
199
|
+
filters,
|
|
200
|
+
toStringFn,
|
|
201
|
+
pageIndex
|
|
198
202
|
});
|
|
199
203
|
}
|
|
200
204
|
}
|
|
@@ -237,7 +241,8 @@
|
|
|
237
241
|
id,
|
|
238
242
|
tableId,
|
|
239
243
|
values,
|
|
240
|
-
filters
|
|
244
|
+
filters,
|
|
245
|
+
pageIndex
|
|
241
246
|
});
|
|
242
247
|
}
|
|
243
248
|
}
|
|
@@ -280,7 +285,6 @@
|
|
|
280
285
|
// Page configuration
|
|
281
286
|
const { pageIndex, pageSize } = pluginStates.page;
|
|
282
287
|
|
|
283
|
-
// TODO: Add loading animation for server-side fetch requests
|
|
284
288
|
const updateTable = async () => {
|
|
285
289
|
sendModel.limit = $pageSize;
|
|
286
290
|
sendModel.offset = $pageSize * $pageIndex;
|
|
@@ -288,14 +292,27 @@
|
|
|
288
292
|
sendModel.id = entityId;
|
|
289
293
|
sendModel.filter = normalizeFilters($filters);
|
|
290
294
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
let fetchData;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
isFetching = true;
|
|
299
|
+
fetchData = await fetch(URL, {
|
|
300
|
+
headers: {
|
|
301
|
+
'Content-Type': 'application/json',
|
|
302
|
+
Authorization: `Bearer ${token}`
|
|
303
|
+
},
|
|
304
|
+
method: 'POST',
|
|
305
|
+
body: JSON.stringify(sendModel)
|
|
306
|
+
});
|
|
307
|
+
} catch (error) {
|
|
308
|
+
throw new Error(`Network error: ${(error as Error).message}`);
|
|
309
|
+
} finally {
|
|
310
|
+
isFetching = false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (!fetchData.ok) {
|
|
314
|
+
throw new Error('Failed to fetch data');
|
|
315
|
+
}
|
|
299
316
|
|
|
300
317
|
const response: Receive = await fetchData.json();
|
|
301
318
|
|
|
@@ -335,7 +352,13 @@
|
|
|
335
352
|
<!-- Enable the search filter if table is not empty -->
|
|
336
353
|
{#if $data.length > 0}
|
|
337
354
|
{#if !serverSide}
|
|
338
|
-
<
|
|
355
|
+
<form
|
|
356
|
+
class="flex gap-2"
|
|
357
|
+
on:submit|preventDefault={() => {
|
|
358
|
+
sendModel.q = searchValue;
|
|
359
|
+
$filterValue = searchValue;
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
339
362
|
<div class="relative w-full flex items-center">
|
|
340
363
|
<input
|
|
341
364
|
class="input p-2 border border-primary-500"
|
|
@@ -348,18 +371,20 @@
|
|
|
348
371
|
class="absolute right-3 items-center"
|
|
349
372
|
on:click|preventDefault={() => {
|
|
350
373
|
searchValue = '';
|
|
374
|
+
sendModel.q = '';
|
|
351
375
|
$filterValue = '';
|
|
352
376
|
}}><Fa icon={faXmark} /></button
|
|
353
377
|
>
|
|
354
378
|
</div>
|
|
355
379
|
<button
|
|
356
|
-
type="
|
|
380
|
+
type="submit"
|
|
357
381
|
class="btn variant-filled-primary"
|
|
358
382
|
on:click|preventDefault={() => {
|
|
359
383
|
$filterValue = searchValue;
|
|
384
|
+
sendModel.q = searchValue;
|
|
360
385
|
}}>Search</button
|
|
361
386
|
>
|
|
362
|
-
</
|
|
387
|
+
</form>
|
|
363
388
|
{/if}
|
|
364
389
|
<div class="flex justify-between items-center py-2 w-full">
|
|
365
390
|
<div>
|
|
@@ -460,6 +485,8 @@
|
|
|
460
485
|
</tr>
|
|
461
486
|
</Subscribe>
|
|
462
487
|
{/each}
|
|
488
|
+
{:else if isFetching}
|
|
489
|
+
<div class="p-10"><Spinner /></div>
|
|
463
490
|
{:else}
|
|
464
491
|
<!-- Table is empty -->
|
|
465
492
|
<p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
|
|
@@ -10,13 +10,33 @@
|
|
|
10
10
|
export let id;
|
|
11
11
|
export let tableId;
|
|
12
12
|
export let toFilterableValueFn: undefined | ((value: any) => any) = undefined;
|
|
13
|
+
export let toStringFn: undefined | ((value: any) => string) = undefined;
|
|
13
14
|
export let filterValue;
|
|
14
15
|
export let filters;
|
|
16
|
+
export let pageIndex;
|
|
15
17
|
|
|
16
18
|
// If the filter is applied and the displayed values are filtered
|
|
17
19
|
let active = false;
|
|
20
|
+
let type: string = 'string';
|
|
21
|
+
let isDate = false; // Options for different types of values
|
|
22
|
+
let dropdowns: {
|
|
23
|
+
option: FilterOptionsEnum;
|
|
24
|
+
value: string | number | Date | undefined;
|
|
25
|
+
}[] = [];
|
|
26
|
+
|
|
27
|
+
// Check the type of the column
|
|
28
|
+
$values.forEach((item) => {
|
|
29
|
+
if (item) {
|
|
30
|
+
type = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
|
|
31
|
+
|
|
32
|
+
if (type === 'object') {
|
|
33
|
+
if (item instanceof Date) {
|
|
34
|
+
isDate = true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
18
39
|
|
|
19
|
-
// Options for different types of values
|
|
20
40
|
const options = {
|
|
21
41
|
number: [
|
|
22
42
|
{
|
|
@@ -98,11 +118,6 @@
|
|
|
98
118
|
]
|
|
99
119
|
};
|
|
100
120
|
|
|
101
|
-
let dropdowns: {
|
|
102
|
-
option: FilterOptionsEnum;
|
|
103
|
-
value: string | number | Date | undefined;
|
|
104
|
-
}[] = [];
|
|
105
|
-
|
|
106
121
|
// Unique ID for the column filter popup
|
|
107
122
|
const popupId = `${tableId}-${id}`;
|
|
108
123
|
// Popup config
|
|
@@ -112,24 +127,39 @@
|
|
|
112
127
|
placement: 'bottom-start'
|
|
113
128
|
};
|
|
114
129
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
type = typeof (toFilterableValueFn ? toFilterableValueFn(item) : item);
|
|
130
|
+
// Converted string values and missingValues mapping
|
|
131
|
+
const stringValues =
|
|
132
|
+
// type === 'number' ?
|
|
133
|
+
$values.map((item) => (toStringFn ? toStringFn(item) : item));
|
|
134
|
+
// : [];
|
|
121
135
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
}
|
|
136
|
+
const missingValues =
|
|
137
|
+
// type === 'number' ?
|
|
138
|
+
stringValues.reduce((acc, item, index) => {
|
|
139
|
+
acc[typeof item === 'string' ? item.toLowerCase() : item] = $values[index];
|
|
140
|
+
return acc;
|
|
141
|
+
}, {});
|
|
142
|
+
// : {};
|
|
143
|
+
|
|
144
|
+
const getMissingValue = (value: string) => {
|
|
145
|
+
// if (type === 'number' ||) {
|
|
146
|
+
return Object.keys(missingValues).includes(value.toLowerCase())
|
|
147
|
+
? missingValues[value.toLowerCase()]
|
|
148
|
+
: value;
|
|
149
|
+
// }
|
|
150
|
+
// return value;
|
|
151
|
+
};
|
|
129
152
|
|
|
130
153
|
const optionChangeHandler = (e, index) => {
|
|
131
154
|
delete $filters[id][dropdowns[index].option];
|
|
132
|
-
$filters[id] = {
|
|
155
|
+
$filters[id] = {
|
|
156
|
+
...$filters[id],
|
|
157
|
+
[e.target.value]:
|
|
158
|
+
// type === 'number'
|
|
159
|
+
// ?
|
|
160
|
+
getMissingValue(dropdowns[index].value as string)
|
|
161
|
+
// : dropdowns[index].value
|
|
162
|
+
};
|
|
133
163
|
$filters = $filters;
|
|
134
164
|
|
|
135
165
|
dropdowns[index] = {
|
|
@@ -141,17 +171,18 @@
|
|
|
141
171
|
const valueChangeHandler = (e, index) => {
|
|
142
172
|
dropdowns[index] = {
|
|
143
173
|
...dropdowns[index],
|
|
144
|
-
value:
|
|
145
|
-
type === 'number'
|
|
146
|
-
? +e.target.value
|
|
147
|
-
: type === 'date'
|
|
148
|
-
? new Date(e.target.value)
|
|
149
|
-
: e.target.value
|
|
174
|
+
value: type === 'date' ? new Date(e.target.value) : e.target.value
|
|
150
175
|
};
|
|
151
176
|
|
|
152
177
|
$filters = {
|
|
153
178
|
...$filters,
|
|
154
|
-
[id]: {
|
|
179
|
+
[id]: {
|
|
180
|
+
...$filters[id],
|
|
181
|
+
[dropdowns[index].option]:
|
|
182
|
+
// type === 'number' ?
|
|
183
|
+
getMissingValue(e.target.value)
|
|
184
|
+
// : dropdowns[index].value
|
|
185
|
+
}
|
|
155
186
|
};
|
|
156
187
|
};
|
|
157
188
|
|
|
@@ -212,6 +243,7 @@
|
|
|
212
243
|
addFilter(options[type][0].value, undefined);
|
|
213
244
|
$filterValue = $filters[id];
|
|
214
245
|
active = false;
|
|
246
|
+
$pageIndex = 0;
|
|
215
247
|
}}>Clear Filters</button
|
|
216
248
|
>
|
|
217
249
|
|
|
@@ -246,14 +278,7 @@
|
|
|
246
278
|
{/if}
|
|
247
279
|
</div>
|
|
248
280
|
|
|
249
|
-
{#if type === 'number'}
|
|
250
|
-
<input
|
|
251
|
-
type="number"
|
|
252
|
-
class="input p-1 border border-primary-500"
|
|
253
|
-
on:input={(e) => valueChangeHandler(e, index)}
|
|
254
|
-
bind:value={dropdown.value}
|
|
255
|
-
/>
|
|
256
|
-
{:else if type === 'string'}
|
|
281
|
+
{#if type === 'number' || type === 'string'}
|
|
257
282
|
<input
|
|
258
283
|
type="text"
|
|
259
284
|
class="input p-1 border border-primary-500"
|
|
@@ -292,6 +317,7 @@
|
|
|
292
317
|
class="btn variant-filled-primary btn-sm"
|
|
293
318
|
type="button"
|
|
294
319
|
on:click|preventDefault={() => {
|
|
320
|
+
$pageIndex = 0;
|
|
295
321
|
$filterValue = $filters[id];
|
|
296
322
|
active = true;
|
|
297
323
|
}}>Apply</button
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import dateFormat from 'dateformat';
|
|
2
|
+
|
|
1
3
|
import type { FilterOptionsEnum } from '$models/Enums';
|
|
2
4
|
import type { Columns, Filter, ServerColumn } from '$models/Models';
|
|
3
5
|
|
|
@@ -115,14 +117,13 @@ export const convertServerColumns = (columns: ServerColumn[]) => {
|
|
|
115
117
|
columns.forEach((col) => {
|
|
116
118
|
let instructions = {};
|
|
117
119
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// }
|
|
120
|
+
if (col.instructions?.displayPattern) {
|
|
121
|
+
instructions = {
|
|
122
|
+
toStringFn: (date: Date) => dateFormat(date, col.instructions?.displayPattern || ''),
|
|
123
|
+
toSortableValueFn: (date: Date) => date.getTime(),
|
|
124
|
+
toFilterableValueFn: (date: Date) => date
|
|
125
|
+
};
|
|
126
|
+
}
|
|
126
127
|
|
|
127
128
|
if (col.instructions?.missingValues) {
|
|
128
129
|
instructions = {
|
package/src/lib/models/Models.ts
CHANGED
|
@@ -190,6 +190,7 @@ export class Send {
|
|
|
190
190
|
id: number;
|
|
191
191
|
limit: number;
|
|
192
192
|
offset: number;
|
|
193
|
+
q: string;
|
|
193
194
|
version?: number;
|
|
194
195
|
filter: Filter[];
|
|
195
196
|
order: OrderBy[];
|
|
@@ -199,6 +200,7 @@ export class Send {
|
|
|
199
200
|
this.limit = 10;
|
|
200
201
|
this.offset = 0;
|
|
201
202
|
this.version = 0;
|
|
203
|
+
this.q = '';
|
|
202
204
|
this.filter = [];
|
|
203
205
|
this.order = [];
|
|
204
206
|
}
|