@lavalogic/scoria 0.9.2 → 0.9.4
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/dist/Components/Table/Body/QuickSearchCell.svelte +104 -24
- package/dist/Components/Table/Body/TableBody.svelte +2 -0
- package/dist/Components/Table/Misc/FilterInput.svelte +4 -4
- package/dist/Components/Table/Types/Context/TableContext.svelte.js +24 -3
- package/dist/Components/Table/Types/DataRepository/LocalTableDataRepository.svelte.d.ts +5 -1
- package/dist/Components/Table/Types/DataRepository/LocalTableDataRepository.svelte.js +52 -15
- package/dist/Components/Table/Types/Filtering/CustomFilter.d.ts +1 -1
- package/dist/Components/ThreeStateRadio.svelte +32 -11
- package/dist/Components/ThreeStateRadio.svelte.d.ts +10 -4
- package/package.json +14 -14
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
import { SelectDef } from '../Types/Columns/Definitions/Accessors/SelectDef.svelte.js';
|
|
29
29
|
import { NumberInputDef } from '../Types/Columns/Definitions/Accessors/NumberInputDef.svelte.js';
|
|
30
30
|
import { dev } from '$app/environment';
|
|
31
|
-
import { QuerySelect } from '../../../index.js';
|
|
31
|
+
import { CheckboxDef, QuerySelect, Ternary } from '../../../index.js';
|
|
32
|
+
import ThreeStateRadio from '../../ThreeStateRadio.svelte';
|
|
32
33
|
|
|
33
34
|
export interface QuickSearchCellProps<T extends object> {
|
|
34
35
|
column: Column<T>;
|
|
@@ -79,8 +80,6 @@
|
|
|
79
80
|
const isRemoteSelect = $derived(customFilter?.type === CustomFilterType.Select);
|
|
80
81
|
const hasQuery = $derived(isRemoteSelect && customFilter?.query);
|
|
81
82
|
|
|
82
|
-
let inputRef: HTMLInputElement | undefined = $state();
|
|
83
|
-
|
|
84
83
|
const selectSymbol = Symbol();
|
|
85
84
|
|
|
86
85
|
const openedSelect = getContext<{ value: symbol | null } | null | undefined>(
|
|
@@ -347,7 +346,7 @@
|
|
|
347
346
|
{/if}
|
|
348
347
|
{:else if isSelect}
|
|
349
348
|
{#if hasQuery}
|
|
350
|
-
{#if customFilter!.valueAsObject
|
|
349
|
+
{#if customFilter!.valueAsObject !== true}
|
|
351
350
|
{@const label =
|
|
352
351
|
customFilter?.id ??
|
|
353
352
|
customFilter?.label ??
|
|
@@ -408,6 +407,48 @@
|
|
|
408
407
|
/>
|
|
409
408
|
{/await}
|
|
410
409
|
{/if}
|
|
410
|
+
{:else if def instanceof CheckboxDef}
|
|
411
|
+
{@const value = (() => {
|
|
412
|
+
switch (tableContext.paginationRepo?.filtering.find((it) => it.id === def.id)?.value) {
|
|
413
|
+
case true: {
|
|
414
|
+
return Ternary.True;
|
|
415
|
+
}
|
|
416
|
+
case false: {
|
|
417
|
+
return Ternary.False;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
case null:
|
|
421
|
+
case undefined: {
|
|
422
|
+
return Ternary.Neutral;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
})()}
|
|
426
|
+
<div class="centered">
|
|
427
|
+
<ThreeStateRadio
|
|
428
|
+
{value}
|
|
429
|
+
yesLabel="Selected"
|
|
430
|
+
noLabel="Not Selected"
|
|
431
|
+
neutralLabel="Any"
|
|
432
|
+
neutralIcon={{ name: 'circle-minus' }}
|
|
433
|
+
onchange={(v) => {
|
|
434
|
+
switch (v) {
|
|
435
|
+
case Ternary.False: {
|
|
436
|
+
tableContext.paginationRepo?.filterBy(def.id, false);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
case Ternary.True: {
|
|
440
|
+
tableContext.paginationRepo?.filterBy(def.id, true);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
case Ternary.Neutral:
|
|
444
|
+
default: {
|
|
445
|
+
tableContext.paginationRepo?.filterBy(def.id, null);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}}
|
|
450
|
+
/>
|
|
451
|
+
</div>
|
|
411
452
|
{:else if isNumeric}
|
|
412
453
|
{#if selectedNumberFilterType.value === NumberDateFilterMode.Between}
|
|
413
454
|
<div class="input-wrapper left">
|
|
@@ -438,39 +479,66 @@
|
|
|
438
479
|
</div>
|
|
439
480
|
{:else if value == null || typeof value == 'string'}
|
|
440
481
|
<div class="input-wrapper right">
|
|
482
|
+
{#if selectedNumberFilterType?.value == NumberDateFilterMode.null}
|
|
483
|
+
<TextInput
|
|
484
|
+
data-type="quicksearch"
|
|
485
|
+
id={inputId}
|
|
486
|
+
value="is Null"
|
|
487
|
+
disabled
|
|
488
|
+
variant={InputVariant.Table}
|
|
489
|
+
/>
|
|
490
|
+
{:else}
|
|
491
|
+
<TextInput
|
|
492
|
+
data-type="quicksearch"
|
|
493
|
+
id={inputId}
|
|
494
|
+
bind:value
|
|
495
|
+
placeholder="Filter…"
|
|
496
|
+
variant={InputVariant.Table}
|
|
497
|
+
oninput={() => {
|
|
498
|
+
tableContext.paginationRepo?.filterBy(def.id, [value]);
|
|
499
|
+
}}
|
|
500
|
+
onkeydown={closeOnCtrlF}
|
|
501
|
+
/>
|
|
502
|
+
{/if}
|
|
503
|
+
</div>
|
|
504
|
+
{/if}
|
|
505
|
+
{:else if value == null || typeof value == 'string'}
|
|
506
|
+
<div class="input-wrapper right">
|
|
507
|
+
{#if selectedStringFilterType?.value == StringFilterMode.null}
|
|
441
508
|
<TextInput
|
|
442
509
|
data-type="quicksearch"
|
|
443
510
|
id={inputId}
|
|
444
|
-
|
|
445
|
-
|
|
511
|
+
value="is Null"
|
|
512
|
+
disabled
|
|
446
513
|
variant={InputVariant.Table}
|
|
514
|
+
/>
|
|
515
|
+
{:else if selectedStringFilterType?.value == StringFilterMode.Empty}
|
|
516
|
+
<TextInput
|
|
517
|
+
data-type="quicksearch"
|
|
518
|
+
id={inputId}
|
|
519
|
+
value="is Empty"
|
|
520
|
+
disabled
|
|
521
|
+
variant={InputVariant.Table}
|
|
522
|
+
/>
|
|
523
|
+
{:else}
|
|
524
|
+
<TextInput
|
|
525
|
+
data-type="quicksearch"
|
|
526
|
+
id={inputId}
|
|
447
527
|
oninput={() => {
|
|
448
|
-
tableContext.paginationRepo?.filterBy(def.id,
|
|
528
|
+
tableContext.paginationRepo?.filterBy(def.id, value);
|
|
449
529
|
}}
|
|
450
|
-
|
|
530
|
+
variant={InputVariant.Table}
|
|
531
|
+
bind:value
|
|
532
|
+
placeholder="Filter…"
|
|
451
533
|
/>
|
|
452
|
-
|
|
453
|
-
{/if}
|
|
454
|
-
{:else if value == null || typeof value == 'string'}
|
|
455
|
-
<div class="input-wrapper right">
|
|
456
|
-
<TextInput
|
|
457
|
-
data-type="quicksearch"
|
|
458
|
-
id={inputId}
|
|
459
|
-
oninput={() => {
|
|
460
|
-
tableContext.paginationRepo?.filterBy(def.id, value);
|
|
461
|
-
}}
|
|
462
|
-
variant={InputVariant.Table}
|
|
463
|
-
bind:value
|
|
464
|
-
bind:inputRef
|
|
465
|
-
placeholder="Filter…"
|
|
466
|
-
/>
|
|
534
|
+
{/if}
|
|
467
535
|
</div>
|
|
468
536
|
{/if}
|
|
469
537
|
|
|
470
538
|
<!-- TODO button for date, checkbox or select for checkbox -->
|
|
471
539
|
</div>
|
|
472
540
|
|
|
473
|
-
{#if !isSelect && !isDate}
|
|
541
|
+
{#if !isSelect && !isDate && !(def instanceof CheckboxDef)}
|
|
474
542
|
<div class="stick-right">
|
|
475
543
|
<Action
|
|
476
544
|
data-type="quicksearch"
|
|
@@ -666,4 +734,16 @@ td {
|
|
|
666
734
|
}
|
|
667
735
|
td .input-wrapper-parent {
|
|
668
736
|
width: 100%;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.centered {
|
|
740
|
+
display: flex;
|
|
741
|
+
width: fit-content;
|
|
742
|
+
flex-flow: column nowrap;
|
|
743
|
+
align-items: center;
|
|
744
|
+
justify-content: center;
|
|
745
|
+
margin: auto;
|
|
746
|
+
height: 60px;
|
|
747
|
+
--input-height: 60px;
|
|
748
|
+
background-color: #ffffff;
|
|
669
749
|
}</style>
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
typeof filteringStateValue == 'number'
|
|
243
243
|
) {
|
|
244
244
|
value = filteringStateValue;
|
|
245
|
-
} else {
|
|
245
|
+
} else if (typeof filteringStateValue !== 'boolean') {
|
|
246
246
|
console.warn('unknown value type:', $state.snapshot(filteringStateValue));
|
|
247
247
|
value = filteringStateValue as string; // HACK attempt to keep production running
|
|
248
248
|
}
|
|
@@ -266,8 +266,8 @@
|
|
|
266
266
|
const gotValue = filterOption.value;
|
|
267
267
|
if (gotValue == null || typeof gotValue == 'string' || typeof gotValue == 'number') {
|
|
268
268
|
value = gotValue;
|
|
269
|
-
} else {
|
|
270
|
-
console.warn('unknown value type:', gotValue);
|
|
269
|
+
} else if (typeof filteringStateValue !== 'boolean') {
|
|
270
|
+
console.warn('unknown value type:', $state.snapshot(gotValue));
|
|
271
271
|
value = gotValue as string; // HACK attempt to keep production running
|
|
272
272
|
}
|
|
273
273
|
}
|
|
@@ -400,7 +400,7 @@
|
|
|
400
400
|
<div class="contents">
|
|
401
401
|
{#await options then options}
|
|
402
402
|
{#if hasQuery}
|
|
403
|
-
{#if customFilter!.valueAsObject
|
|
403
|
+
{#if customFilter!.valueAsObject !== true}
|
|
404
404
|
{@const label =
|
|
405
405
|
customFilter?.id ??
|
|
406
406
|
customFilter?.label ??
|
|
@@ -4,7 +4,7 @@ import { ToolbarPosition } from '../Toolbar/ToolbarPosition.js';
|
|
|
4
4
|
import { RowSelectionDef } from '../Columns/Definitions/RowSelection/RowSelectionDef.svelte.js';
|
|
5
5
|
import { tick } from 'svelte';
|
|
6
6
|
import { PaginationType } from '../Pagination/PaginationType.js';
|
|
7
|
-
import { asyncAll,
|
|
7
|
+
import { asyncAll, asyncFilterIndices, asyncSleep, getCanvasContext, getErrorMessage } from '../../../../Helpers/Helpers.svelte.js';
|
|
8
8
|
import { AccessorDef } from '../Columns/Definitions/Accessors/AccessorDef.svelte.js';
|
|
9
9
|
import { NumberInputDef } from '../Columns/Definitions/Accessors/NumberInputDef.svelte.js';
|
|
10
10
|
import { SelectDef } from '../Columns/Definitions/Accessors/SelectDef.svelte.js';
|
|
@@ -14,8 +14,8 @@ import { DateInputDef } from '../Columns/Definitions/Accessors/DateInputDef.svel
|
|
|
14
14
|
import { ProgressBarDef } from '../Columns/Definitions/Display/ProgressBarDef.svelte.js';
|
|
15
15
|
import { DisplayDef } from '../Columns/Definitions/Display/DisplayDef.svelte.js';
|
|
16
16
|
import { browser, dev } from '$app/environment';
|
|
17
|
-
import { ActionsDef } from '../Columns/Definitions/Actions/ActionsDef.svelte.js';
|
|
18
17
|
import { BubbleDef } from '../Columns/Definitions/Display/BubbleDef.svelte.js';
|
|
18
|
+
import { allowedEmptyFilterModes } from '../DataRepository/LocalTableDataRepository.svelte.js';
|
|
19
19
|
const SORTING_ICON_WIDTH = 24;
|
|
20
20
|
const InternalSymbol = Symbol();
|
|
21
21
|
const uncopyableColumns = new Set([
|
|
@@ -153,8 +153,29 @@ export class TableContext {
|
|
|
153
153
|
const abortController = new AbortController();
|
|
154
154
|
try {
|
|
155
155
|
if (this.paginationRepo?.paginationType == PaginationType.Local) {
|
|
156
|
-
const
|
|
156
|
+
const checkedIds = new Set();
|
|
157
|
+
const filterState = this.dataRepo?.filtering?.filter((filter) => {
|
|
158
|
+
const snapshot = $state.snapshot(filter.value);
|
|
159
|
+
checkedIds.add(filter.id);
|
|
160
|
+
const x = this.dataRepo.filterModes.get(filter.id);
|
|
161
|
+
return (allowedEmptyFilterModes.has(x) ||
|
|
162
|
+
(snapshot !== '' && (!Array.isArray(snapshot) || snapshot[0] !== '')));
|
|
163
|
+
}) ?? [];
|
|
157
164
|
const data = this.dataRepo?.data ?? [];
|
|
165
|
+
// HACK add filter state for filters that don't require a value to function
|
|
166
|
+
// this.dataRepo?.filterModes?.entries().forEach(([key, value]) => {
|
|
167
|
+
// if (!checkedIds.has(key)) {
|
|
168
|
+
// checkedIds.add(key);
|
|
169
|
+
// if (allowedEmptyFilterModes.has(value)) {
|
|
170
|
+
// filterState.push({
|
|
171
|
+
// id: key,
|
|
172
|
+
// value: ''
|
|
173
|
+
// });
|
|
174
|
+
// }
|
|
175
|
+
// } else {
|
|
176
|
+
// console.warn(`key ${key} was already checked, new value:`, value);
|
|
177
|
+
// }
|
|
178
|
+
// });
|
|
158
179
|
if (!filterState.length) {
|
|
159
180
|
this.filteredIndices = data.map((_, index) => index) ?? [];
|
|
160
181
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { RefWrapper } from '../../../../Types/Internal/RefWrapper.js';
|
|
2
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
2
3
|
import type { Row } from '../Context/Row.js';
|
|
3
4
|
import type { ColumnFiltersState } from '../Filtering/ColumnFiltersState.js';
|
|
4
5
|
import type { BoolFilterValueType, NumberFilterValueType, SelectFilterValueType, StringFilterValueType } from '../Filtering/FilterFn.js';
|
|
5
6
|
import type { FilterMode } from '../Filtering/FilterMode.js';
|
|
7
|
+
import { NumberDateFilterMode } from '../Filtering/NumberDateFilterMode.js';
|
|
8
|
+
import { StringFilterMode } from '../Filtering/StringFilterMode.js';
|
|
6
9
|
import type { OnChangeFn } from '../OnChangeFn.js';
|
|
7
10
|
import type { ILocalDataRepository } from './ILocalDataRepository.js';
|
|
8
11
|
import type { SortingState } from './SortingState.js';
|
|
9
12
|
import { TableDataRepository } from './TableDataRepository.svelte.js';
|
|
13
|
+
export declare const allowedEmptyFilterModes: SvelteSet<StringFilterMode | NumberDateFilterMode>;
|
|
10
14
|
export declare class LocalTableDataRepository<T extends object> extends TableDataRepository<T> implements ILocalDataRepository<T> {
|
|
11
15
|
addEmptyRow?: (() => void) | undefined;
|
|
12
16
|
readonly filterType: "Real";
|
|
@@ -48,5 +52,5 @@ export declare class LocalTableDataRepository<T extends object> extends TableDat
|
|
|
48
52
|
readonly boolFilter: (row: Row<T>, columnId: string, // & keyof T,
|
|
49
53
|
filterValue: BoolFilterValueType) => Promise<boolean>;
|
|
50
54
|
readonly stringFilter: (row: Row<T>, columnId: string, // & keyof T,
|
|
51
|
-
filterValue: StringFilterValueType) => Promise<
|
|
55
|
+
filterValue: StringFilterValueType) => Promise<boolean>;
|
|
52
56
|
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { Delay } from '../../../../Delay/Delay.js';
|
|
2
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
2
3
|
import { FilterType } from '../Filtering/FilterType.js';
|
|
3
4
|
import { NumberDateFilterMode } from '../Filtering/NumberDateFilterMode.js';
|
|
4
5
|
import { StringFilterMode } from '../Filtering/StringFilterMode.js';
|
|
5
6
|
import { PaginationType } from '../Pagination/PaginationType.js';
|
|
6
7
|
import { TableDataRepository } from './TableDataRepository.svelte.js';
|
|
8
|
+
export const allowedEmptyFilterModes = new SvelteSet([
|
|
9
|
+
StringFilterMode.Empty,
|
|
10
|
+
StringFilterMode.null,
|
|
11
|
+
NumberDateFilterMode.null
|
|
12
|
+
]);
|
|
7
13
|
export class LocalTableDataRepository extends TableDataRepository {
|
|
8
14
|
addEmptyRow;
|
|
9
15
|
filterType = FilterType.Real;
|
|
@@ -60,6 +66,10 @@ export class LocalTableDataRepository extends TableDataRepository {
|
|
|
60
66
|
setFilterMode = (id, mode) => {
|
|
61
67
|
if (mode != undefined) {
|
|
62
68
|
this.filterModes.set(id, mode);
|
|
69
|
+
//add stub filtering in empty cases
|
|
70
|
+
if (allowedEmptyFilterModes.has(mode) && !this.filtering.some((it) => it.id === id)) {
|
|
71
|
+
this.filterBy(id, '');
|
|
72
|
+
}
|
|
63
73
|
}
|
|
64
74
|
else {
|
|
65
75
|
this.filterModes.delete(id);
|
|
@@ -69,16 +79,18 @@ export class LocalTableDataRepository extends TableDataRepository {
|
|
|
69
79
|
if (typeof value === 'string') {
|
|
70
80
|
value = value.trim();
|
|
71
81
|
}
|
|
82
|
+
// add stub filter value here if filter mode is empty, not empty, null, or not null
|
|
72
83
|
const oldIndex = this.filtering.findIndex((it) => it.id === id);
|
|
73
84
|
const newFiltering = this.filtering.concat();
|
|
74
85
|
if (oldIndex === -1 || newFiltering[oldIndex].value != value) {
|
|
75
86
|
if (oldIndex > -1) {
|
|
76
87
|
newFiltering.splice(oldIndex, 1);
|
|
77
88
|
}
|
|
78
|
-
const
|
|
79
|
-
(
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
const hasValue = allowedEmptyFilterModes.has(this.filterModes.get(id)) ||
|
|
90
|
+
!(value == null ||
|
|
91
|
+
(typeof value === 'object' && Array.isArray(value) && value.length === 0) ||
|
|
92
|
+
(typeof value === 'string' && value.length === 0));
|
|
93
|
+
if (hasValue) {
|
|
82
94
|
newFiltering.push({ id, value });
|
|
83
95
|
}
|
|
84
96
|
}
|
|
@@ -162,10 +174,13 @@ export class LocalTableDataRepository extends TableDataRepository {
|
|
|
162
174
|
numberFilter = async (row, columnId, // & keyof T,
|
|
163
175
|
values) => {
|
|
164
176
|
const columnValue = await row.getValue(columnId);
|
|
177
|
+
const filterType = this.filterModes.get(columnId) ?? NumberDateFilterMode.In;
|
|
178
|
+
if (filterType == NumberDateFilterMode.null) {
|
|
179
|
+
return columnValue == null || isNaN(columnValue);
|
|
180
|
+
}
|
|
165
181
|
if (!columnValue) {
|
|
166
182
|
return false;
|
|
167
183
|
}
|
|
168
|
-
const filterType = this.filterModes.get(columnId) ?? NumberDateFilterMode.In;
|
|
169
184
|
switch (filterType) {
|
|
170
185
|
case NumberDateFilterMode.Equals: {
|
|
171
186
|
const [min] = TableDataRepository.getMinMax(values);
|
|
@@ -224,10 +239,6 @@ export class LocalTableDataRepository extends TableDataRepository {
|
|
|
224
239
|
}
|
|
225
240
|
return !valueSet.has(columnValue);
|
|
226
241
|
}
|
|
227
|
-
case NumberDateFilterMode.null: {
|
|
228
|
-
// TODO yes or no setting here
|
|
229
|
-
return columnValue == null || isNaN(columnValue);
|
|
230
|
-
}
|
|
231
242
|
case StringFilterMode.ImplicitEquals:
|
|
232
243
|
case StringFilterMode.Equals:
|
|
233
244
|
case StringFilterMode.NotEquals:
|
|
@@ -271,15 +282,27 @@ export class LocalTableDataRepository extends TableDataRepository {
|
|
|
271
282
|
};
|
|
272
283
|
stringFilter = async (row, columnId, // & keyof T,
|
|
273
284
|
filterValue) => {
|
|
285
|
+
const filterType = this.filterModes.get(columnId) ?? StringFilterMode.Contains;
|
|
286
|
+
if (filterType == StringFilterMode.Empty) {
|
|
287
|
+
const columnValue = (await row.getValue(columnId))?.toString();
|
|
288
|
+
return !columnValue;
|
|
289
|
+
}
|
|
290
|
+
if (filterType == StringFilterMode.null) {
|
|
291
|
+
const columnValue = await row.getValue(columnId);
|
|
292
|
+
return columnValue == null;
|
|
293
|
+
}
|
|
274
294
|
if (!filterValue) {
|
|
295
|
+
console.log('empty search value');
|
|
275
296
|
return true;
|
|
276
297
|
}
|
|
277
298
|
filterValue = filterValue.trim().toLocaleLowerCase();
|
|
278
|
-
const columnValue = (await row.getValue(columnId))
|
|
299
|
+
const columnValue = (await row.getValue(columnId))
|
|
300
|
+
?.toString()
|
|
301
|
+
.trim()
|
|
302
|
+
.toLocaleLowerCase();
|
|
279
303
|
if (!columnValue) {
|
|
280
304
|
return true;
|
|
281
305
|
}
|
|
282
|
-
const filterType = this.filterModes.get(columnId) ?? StringFilterMode.Contains;
|
|
283
306
|
if (filterType === StringFilterMode.EndsWith) {
|
|
284
307
|
return columnValue.endsWith(filterValue);
|
|
285
308
|
}
|
|
@@ -292,10 +315,24 @@ export class LocalTableDataRepository extends TableDataRepository {
|
|
|
292
315
|
if (filterType === StringFilterMode.NotEquals) {
|
|
293
316
|
return columnValue !== filterValue;
|
|
294
317
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
318
|
+
if (filterType == StringFilterMode.NotEndsWith) {
|
|
319
|
+
return !columnValue.endsWith(filterValue);
|
|
320
|
+
}
|
|
321
|
+
if (filterType == StringFilterMode.Contains) {
|
|
322
|
+
return columnValue.includes(filterValue);
|
|
323
|
+
}
|
|
324
|
+
if (filterType == StringFilterMode.NotContains) {
|
|
325
|
+
return !columnValue.includes(filterValue);
|
|
298
326
|
}
|
|
299
|
-
|
|
327
|
+
if (filterType == StringFilterMode.In) {
|
|
328
|
+
const filterValues = filterValue.split(',');
|
|
329
|
+
return filterValues.some((v) => columnValue === v);
|
|
330
|
+
}
|
|
331
|
+
if (filterType == StringFilterMode.NotIn) {
|
|
332
|
+
const filterValues = filterValue.split(',');
|
|
333
|
+
return !filterValues.some((v) => columnValue === v);
|
|
334
|
+
}
|
|
335
|
+
console.warn(`unhandled filter type value ${filterType}`);
|
|
336
|
+
return true;
|
|
300
337
|
};
|
|
301
338
|
}
|
|
@@ -7,22 +7,29 @@
|
|
|
7
7
|
import { generateShortUUID } from '../Helpers/Helpers.svelte.js';
|
|
8
8
|
import { Colour, ColourSet } from '../scss/colours.js';
|
|
9
9
|
import { Ternary } from '../Types/Internal/Ternary.js';
|
|
10
|
+
import type { PartialIconProps } from './Icon.svelte';
|
|
10
11
|
export interface ThreeStateRadioProps {
|
|
11
|
-
label
|
|
12
|
-
value
|
|
13
|
-
onchange?:
|
|
12
|
+
label?: string;
|
|
13
|
+
value?: Ternary;
|
|
14
|
+
onchange?: (value: Ternary) => void;
|
|
14
15
|
id?: string;
|
|
15
16
|
disabled?: boolean;
|
|
16
17
|
isValid?: boolean;
|
|
17
18
|
noDisabled?: boolean;
|
|
18
19
|
yesDisabled?: boolean;
|
|
19
20
|
neutralDisabled?: boolean;
|
|
21
|
+
noLabel?: string;
|
|
22
|
+
yesLabel?: string;
|
|
23
|
+
neutralLabel?: string;
|
|
24
|
+
noIcon?: PartialIconProps;
|
|
25
|
+
yesIcon?: PartialIconProps;
|
|
26
|
+
neutralIcon?: PartialIconProps;
|
|
20
27
|
}
|
|
21
28
|
</script>
|
|
22
29
|
|
|
23
30
|
<script lang="ts">
|
|
24
31
|
let {
|
|
25
|
-
value = $bindable(),
|
|
32
|
+
value = $bindable(Ternary.Neutral),
|
|
26
33
|
id = generateShortUUID(),
|
|
27
34
|
label,
|
|
28
35
|
disabled,
|
|
@@ -30,11 +37,15 @@
|
|
|
30
37
|
onchange,
|
|
31
38
|
noDisabled = false,
|
|
32
39
|
yesDisabled = false,
|
|
33
|
-
neutralDisabled = false
|
|
40
|
+
neutralDisabled = false,
|
|
41
|
+
noLabel,
|
|
42
|
+
yesLabel,
|
|
43
|
+
neutralLabel,
|
|
44
|
+
noIcon,
|
|
45
|
+
yesIcon,
|
|
46
|
+
neutralIcon
|
|
34
47
|
}: ThreeStateRadioProps = $props();
|
|
35
48
|
|
|
36
|
-
let ref: HTMLInputElement | undefined = $state();
|
|
37
|
-
|
|
38
49
|
let textValue = $state('');
|
|
39
50
|
|
|
40
51
|
$effect(() => {
|
|
@@ -55,17 +66,22 @@
|
|
|
55
66
|
untrack(() => {
|
|
56
67
|
if (textValue === '0' && value !== Ternary.False) {
|
|
57
68
|
value = Ternary.False;
|
|
69
|
+
onchange?.(Ternary.False);
|
|
58
70
|
} else if (textValue === '-1' && value !== Ternary.Neutral) {
|
|
59
71
|
value = Ternary.Neutral;
|
|
72
|
+
onchange?.(Ternary.Neutral);
|
|
60
73
|
} else if (textValue === '1' && value !== Ternary.True) {
|
|
61
74
|
value = Ternary.True;
|
|
75
|
+
onchange?.(Ternary.True);
|
|
62
76
|
}
|
|
63
77
|
});
|
|
64
78
|
});
|
|
65
79
|
</script>
|
|
66
80
|
|
|
67
81
|
<div class="text-label" class:invalid={isValid === false}>
|
|
68
|
-
|
|
82
|
+
{#if label != undefined}
|
|
83
|
+
<span>{label}</span>
|
|
84
|
+
{/if}
|
|
69
85
|
|
|
70
86
|
<div class="radios">
|
|
71
87
|
<label>
|
|
@@ -74,9 +90,10 @@
|
|
|
74
90
|
colourSet={value === Ternary.False ? ColourSet.Primary : ColourSet.Auxiliary}
|
|
75
91
|
icon={{
|
|
76
92
|
name: 'circle-cross',
|
|
93
|
+
...noIcon,
|
|
77
94
|
colour: value === Ternary.False ? undefined : Colour['$ui-blue-5']
|
|
78
95
|
}}
|
|
79
|
-
label=
|
|
96
|
+
label={noLabel ?? 'No'}
|
|
80
97
|
onclick={() => {
|
|
81
98
|
textValue = '0';
|
|
82
99
|
}}
|
|
@@ -89,9 +106,10 @@
|
|
|
89
106
|
colourSet={value === Ternary.Neutral ? ColourSet.Primary : ColourSet.Auxiliary}
|
|
90
107
|
icon={{
|
|
91
108
|
name: 'database',
|
|
109
|
+
...neutralIcon,
|
|
92
110
|
colour: value === Ternary.Neutral ? undefined : Colour['$ui-blue-5']
|
|
93
111
|
}}
|
|
94
|
-
label=
|
|
112
|
+
label={neutralLabel ?? 'Allow system to decide'}
|
|
95
113
|
onclick={() => {
|
|
96
114
|
textValue = '-1';
|
|
97
115
|
}}
|
|
@@ -109,9 +127,10 @@
|
|
|
109
127
|
colourSet={value === Ternary.True ? ColourSet.Primary : ColourSet.Auxiliary}
|
|
110
128
|
icon={{
|
|
111
129
|
name: 'circle-tick',
|
|
130
|
+
...yesIcon,
|
|
112
131
|
colour: value === Ternary.True ? undefined : Colour['$ui-blue-5']
|
|
113
132
|
}}
|
|
114
|
-
label=
|
|
133
|
+
label={yesLabel ?? 'Yes'}
|
|
115
134
|
onclick={() => {
|
|
116
135
|
textValue = '1';
|
|
117
136
|
}}
|
|
@@ -122,6 +141,7 @@
|
|
|
122
141
|
</div>
|
|
123
142
|
|
|
124
143
|
<style>.text-label {
|
|
144
|
+
box-sizing: border-box;
|
|
125
145
|
height: max(var(--input-height, 3rem), 2rem);
|
|
126
146
|
width: var(--input-width, 100%);
|
|
127
147
|
color: #3a4952;
|
|
@@ -139,6 +159,7 @@
|
|
|
139
159
|
transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
|
|
140
160
|
}
|
|
141
161
|
.text-label .radios {
|
|
162
|
+
overflow: hidden;
|
|
142
163
|
display: flex;
|
|
143
164
|
flex-flow: row nowrap;
|
|
144
165
|
height: 100%;
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import type { ChangeEventHandler } from 'svelte/elements';
|
|
2
1
|
import { Ternary } from '../Types/Internal/Ternary.js';
|
|
2
|
+
import type { PartialIconProps } from './Icon.svelte';
|
|
3
3
|
export interface ThreeStateRadioProps {
|
|
4
|
-
label
|
|
5
|
-
value
|
|
6
|
-
onchange?:
|
|
4
|
+
label?: string;
|
|
5
|
+
value?: Ternary;
|
|
6
|
+
onchange?: (value: Ternary) => void;
|
|
7
7
|
id?: string;
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
isValid?: boolean;
|
|
10
10
|
noDisabled?: boolean;
|
|
11
11
|
yesDisabled?: boolean;
|
|
12
12
|
neutralDisabled?: boolean;
|
|
13
|
+
noLabel?: string;
|
|
14
|
+
yesLabel?: string;
|
|
15
|
+
neutralLabel?: string;
|
|
16
|
+
noIcon?: PartialIconProps;
|
|
17
|
+
yesIcon?: PartialIconProps;
|
|
18
|
+
neutralIcon?: PartialIconProps;
|
|
13
19
|
}
|
|
14
20
|
declare const ThreeStateRadio: import("svelte").Component<ThreeStateRadioProps, {}, "value">;
|
|
15
21
|
type ThreeStateRadio = ReturnType<typeof ThreeStateRadio>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lavalogic/scoria",
|
|
3
3
|
"description": "Svelte components used for the FloWMS Web Frontend",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "restricted"
|
|
7
7
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@chromatic-com/storybook": "^4.1.3",
|
|
33
|
-
"@eslint/compat": "^2.0.
|
|
33
|
+
"@eslint/compat": "^2.0.1",
|
|
34
34
|
"@eslint/js": "^9.39.2",
|
|
35
35
|
"@playwright/test": "^1.57.0",
|
|
36
36
|
"@storybook/addon-a11y": "^10.1.11",
|
|
@@ -38,20 +38,20 @@
|
|
|
38
38
|
"@storybook/addon-svelte-csf": "^5.0.10",
|
|
39
39
|
"@storybook/addon-vitest": "^10.1.11",
|
|
40
40
|
"@storybook/sveltekit": "^10.1.11",
|
|
41
|
-
"@sveltejs/adapter-node": "^5.
|
|
42
|
-
"@sveltejs/kit": "^2.49.
|
|
41
|
+
"@sveltejs/adapter-node": "^5.5.0",
|
|
42
|
+
"@sveltejs/kit": "^2.49.4",
|
|
43
43
|
"@sveltejs/package": "^2.5.7",
|
|
44
|
-
"@sveltejs/vite-plugin-svelte": "^6.2.
|
|
44
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
45
45
|
"@types/eslint": "^9.6.1",
|
|
46
|
-
"@types/node": "^
|
|
47
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
48
|
-
"@typescript-eslint/parser": "^8.
|
|
49
|
-
"@vitest/browser": "^4.0.
|
|
46
|
+
"@types/node": "^25.0.7",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
48
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
49
|
+
"@vitest/browser": "^4.0.17",
|
|
50
50
|
"eslint": "^9.39.2",
|
|
51
51
|
"eslint-config-prettier": "^10.1.8",
|
|
52
|
-
"eslint-plugin-storybook": "10.1.
|
|
52
|
+
"eslint-plugin-storybook": "^10.1.11",
|
|
53
53
|
"eslint-plugin-svelte": "^3.14.0",
|
|
54
|
-
"globals": "^
|
|
54
|
+
"globals": "^17.0.0",
|
|
55
55
|
"mdsvex": "^0.12.6",
|
|
56
56
|
"playwright": "^1.57.0",
|
|
57
57
|
"prettier": "^3.7.4",
|
|
@@ -59,14 +59,14 @@
|
|
|
59
59
|
"publint": "^0.3.16",
|
|
60
60
|
"sass-embedded": "^1.97.2",
|
|
61
61
|
"storybook": "^10.1.11",
|
|
62
|
-
"svelte": "^5.46.
|
|
62
|
+
"svelte": "^5.46.3",
|
|
63
63
|
"svelte-check": "^4.3.5",
|
|
64
64
|
"svelte-render-scan": "^1.1.0",
|
|
65
65
|
"typescript": "^5.9.3",
|
|
66
|
-
"typescript-eslint": "^8.
|
|
66
|
+
"typescript-eslint": "^8.53.0",
|
|
67
67
|
"vite": "^7.3.1",
|
|
68
68
|
"vite-plugin-devtools-json": "^1.0.0",
|
|
69
|
-
"vitest": "^4.0.
|
|
69
|
+
"vitest": "^4.0.17",
|
|
70
70
|
"vitest-browser-svelte": "^2.0.1"
|
|
71
71
|
},
|
|
72
72
|
"keywords": [
|