@lavalogic/scoria 0.37.56 → 0.38.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/dist/Components/Table/ARCHITECTURE.md +22 -22
- package/dist/Components/Table/Misc/ColumnPanel.svelte +17 -61
- package/dist/Components/Table/Misc/ColumnPanel.svelte.d.ts +2 -24
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +3 -92
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte.d.ts +2 -24
- package/dist/Components/Table/Misc/ColumnPanelModalProps.d.ts +2 -2
- package/dist/Components/Table/Misc/FilterPanel.svelte +25 -113
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +626 -0
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte.d.ts +26 -0
- package/dist/Components/Table/Misc/TableConfigurationModalProps.d.ts +13 -0
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +15 -0
- package/dist/Components/Table/Misc/TableSidebar.svelte +45 -2
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +97 -0
- package/dist/Components/Table/Misc/TableViewDropdown.svelte.d.ts +35 -0
- package/dist/Components/Table/SubApis.svelte.js +0 -41
- package/dist/Components/Table/Types/Columns/JSONTableLayout.d.ts +5 -5
- package/dist/Components/Table/Types/Context/ColumnLayoutState.svelte.d.ts +5 -5
- package/dist/Components/Table/Types/Context/ColumnLayoutState.svelte.js +5 -5
- package/dist/Components/Table/Types/Context/PreferencesState.svelte.d.ts +106 -147
- package/dist/Components/Table/Types/Context/PreferencesState.svelte.js +156 -203
- package/dist/Components/Table/Types/Context/TableContext.svelte.d.ts +323 -44
- package/dist/Components/Table/Types/Context/TableContext.svelte.js +687 -187
- package/dist/Components/Table/Types/Context/TableInitOptions.d.ts +9 -0
- package/dist/Components/Table/Types/Context/ViewState.svelte.d.ts +152 -0
- package/dist/Components/Table/Types/Context/ViewState.svelte.js +223 -0
- package/dist/Components/Table/Types/ExpandedPanel.d.ts +9 -5
- package/dist/Components/Table/Types/ExpandedPanel.js +9 -5
- package/dist/Components/Table/Types/Persistence/DatatableView.d.ts +95 -0
- package/dist/Components/Table/Types/Persistence/DatatableViewEnvelope.d.ts +21 -0
- package/dist/Components/Table/Types/Persistence/DatatableViewEnvelope.js +1 -0
- package/dist/Components/Table/Types/Persistence/DatatableViewKind.d.ts +10 -0
- package/dist/Components/Table/Types/Persistence/DatatableViewKind.js +1 -0
- package/dist/Components/Table/Types/Persistence/JSONActiveView.d.ts +58 -0
- package/dist/Components/Table/Types/Persistence/JSONActiveView.js +41 -0
- package/dist/Components/Table/Types/Persistence/JSONTableFilter.d.ts +53 -0
- package/dist/Components/Table/Types/Persistence/JSONTableFilter.js +42 -0
- package/dist/Components/Table/Types/Persistence/RemoteTableLayoutAdapter.d.ts +47 -0
- package/dist/Components/Table/Types/Persistence/RemoteTableLayoutAdapter.js +1 -0
- package/dist/Components/Table/Types/Public/CreateTableOptions.d.ts +10 -0
- package/dist/Components/Table/Types/Public/TablePersistence.d.ts +1 -4
- package/dist/Components/Table/Types/Public/TableSubApis.d.ts +0 -18
- package/dist/Components/Table/Types/Public/index.d.ts +1 -1
- package/dist/Components/Table/createTable.svelte.js +2 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +9 -0
- package/package.json +1 -1
- package/dist/Components/Table/Types/Columns/Definitions/ColumnDefSet.d.ts +0 -20
- package/dist/Components/Table/Types/Columns/Definitions/JSONColumnDefSet.d.ts +0 -25
- /package/dist/Components/Table/{Types/Columns/Definitions/ColumnDefSet.js → Misc/TableConfigurationModalProps.js} +0 -0
- /package/dist/Components/Table/Types/{Columns/Definitions/JSONColumnDefSet.js → Persistence/DatatableView.js} +0 -0
|
@@ -16,14 +16,14 @@ Everything in the public surface is re-exported from
|
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
createTable,
|
|
20
|
+
localRows,
|
|
21
|
+
remoteRows,
|
|
22
|
+
customRows,
|
|
23
|
+
Table,
|
|
24
|
+
type CreateTableOptions,
|
|
25
|
+
type DisplayModalProps,
|
|
26
|
+
// …per-kind option types: TextColumnOptions, NumberColumnOptions, …
|
|
27
27
|
} from '@lavalogic/scoria';
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -96,16 +96,16 @@ Table/
|
|
|
96
96
|
`TableContext` is the root state object. It owns the data repo and seven
|
|
97
97
|
sub-contexts:
|
|
98
98
|
|
|
99
|
-
| Sub-context
|
|
100
|
-
|
|
|
101
|
-
| `_sorting`
|
|
102
|
-
| `_filtering`
|
|
103
|
-
| `_columnLayout`
|
|
104
|
-
| `_selection`
|
|
105
|
-
| `_preferences`
|
|
106
|
-
| `_focus`
|
|
107
|
-
| `_virtualisation`
|
|
108
|
-
| `_queryValues`
|
|
99
|
+
| Sub-context | Owns |
|
|
100
|
+
| ----------------- | ------------------------------------------------------------------------- |
|
|
101
|
+
| `_sorting` | Sort comparators + the `sortedData` derived chain |
|
|
102
|
+
| `_filtering` | Filter-fn relays + the `filteredData` derived chain + quick-filter toggle |
|
|
103
|
+
| `_columnLayout` | Column widths / pinning / visibility / id-index maps |
|
|
104
|
+
| `_selection` | Selected & highlighted maps, key cache, page-wide select helpers |
|
|
105
|
+
| `_preferences` | Table settings + layout / filter / view localStorage round-trip |
|
|
106
|
+
| `_focus` | Focus-coordinate state + multi-cell selection |
|
|
107
|
+
| `_virtualisation` | Row virtualisation window |
|
|
108
|
+
| `_queryValues` | Typeahead-echo cache for QuerySelect filter inputs |
|
|
109
109
|
|
|
110
110
|
Each sub-context is a class under `Types/Context/<Name>State.svelte.ts`
|
|
111
111
|
that takes a `deps` object of thunk-returning fields the parent reads
|
|
@@ -261,10 +261,10 @@ for the canonical pattern.
|
|
|
261
261
|
|
|
262
262
|
## Testing model
|
|
263
263
|
|
|
264
|
-
| File pattern
|
|
265
|
-
|
|
|
266
|
-
| `*.test.ts` (no `.svelte.`)
|
|
267
|
-
| `*.svelte.test.ts`
|
|
264
|
+
| File pattern | Project | What it covers |
|
|
265
|
+
| --------------------------- | -------- | ------------------------------------------------------------------------------------- |
|
|
266
|
+
| `*.test.ts` (no `.svelte.`) | `server` | Pure-node unit tests (helpers, type contracts). Runs via `pnpm test`. |
|
|
267
|
+
| `*.svelte.test.ts` | `client` | Tests using `$state` / `$effect` from Svelte 5. Browser mode via Vitest + Playwright. |
|
|
268
268
|
|
|
269
269
|
The browser-mode runner has a Windows-specific chromium launch issue;
|
|
270
270
|
set `SCORIA_CHROMIUM_EXECUTABLE` to the headless-shell binary to work
|
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
<svelte:options runes />
|
|
2
2
|
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
6
|
-
>
|
|
7
|
-
import Button from '../../Button.svelte';
|
|
8
|
-
import SingleSelect from '../../SingleSelect.svelte';
|
|
3
|
+
<script lang="ts">
|
|
9
4
|
import ColumnList from '../Body/Rows/ColumnList.svelte';
|
|
10
5
|
import TextInput from '../../TextInput.svelte';
|
|
11
|
-
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
12
6
|
import { Size } from '../../../Types/Internal/Size.js';
|
|
13
|
-
import
|
|
14
|
-
import { getContext } from 'svelte';
|
|
15
|
-
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
16
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
7
|
+
import TableViewDropdown from './TableViewDropdown.svelte';
|
|
17
8
|
|
|
18
|
-
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
19
|
-
|
|
20
|
-
const labelledPresets: Array<SelectOption<string>> = $derived(
|
|
21
|
-
tableContext.columnPresets.map((pr) => ({ label: pr.name, value: pr.name }))
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const selectedValue = $derived(
|
|
25
|
-
labelledPresets.find((it) => it.value === tableContext.selectedPreset?.name) ?? null
|
|
26
|
-
);
|
|
27
9
|
let searchValue = $state('');
|
|
28
|
-
let pendingPresetName = $state('');
|
|
29
10
|
</script>
|
|
30
11
|
|
|
31
12
|
<section class="side-panel-section">
|
|
@@ -34,22 +15,10 @@
|
|
|
34
15
|
</header>
|
|
35
16
|
|
|
36
17
|
<div class="toolbar">
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
labelProp="label"
|
|
42
|
-
valueProp="value"
|
|
43
|
-
value={selectedValue}
|
|
44
|
-
valueAsObject
|
|
45
|
-
onchange={(newSelection: SelectOption<string> | null) => {
|
|
46
|
-
const preset =
|
|
47
|
-
newSelection && tableContext.columnPresets.find((p) => p.name === newSelection.value);
|
|
48
|
-
if (preset) {
|
|
49
|
-
tableContext.setSelectedPreset(preset);
|
|
50
|
-
}
|
|
51
|
-
}}
|
|
52
|
-
/>
|
|
18
|
+
<!-- Quick-switch dropdown for saved column layouts. Sits above the
|
|
19
|
+
search box; saving a layout is done from the Table
|
|
20
|
+
Configuration modal's "Save a new Layout" tab. -->
|
|
21
|
+
<TableViewDropdown kind="layout" />
|
|
53
22
|
<TextInput
|
|
54
23
|
bind:value={searchValue}
|
|
55
24
|
leftIcon={{ name: 'search', size: Size.Medium }}
|
|
@@ -65,30 +34,6 @@
|
|
|
65
34
|
<div class="column-list-wrapper">
|
|
66
35
|
<ColumnList {searchValue} />
|
|
67
36
|
</div>
|
|
68
|
-
|
|
69
|
-
<form
|
|
70
|
-
class="side-panel-footer"
|
|
71
|
-
onsubmit={(e) => {
|
|
72
|
-
e.preventDefault();
|
|
73
|
-
const trimmed = pendingPresetName.trim();
|
|
74
|
-
if (!trimmed) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
tableContext.saveColumnPreset(trimmed);
|
|
78
|
-
pendingPresetName = '';
|
|
79
|
-
}}
|
|
80
|
-
>
|
|
81
|
-
<TextInput
|
|
82
|
-
placeholder="Preset name"
|
|
83
|
-
bind:value={pendingPresetName}
|
|
84
|
-
/>
|
|
85
|
-
<Button
|
|
86
|
-
type="submit"
|
|
87
|
-
variant={Variant.Secondary}
|
|
88
|
-
nowrap
|
|
89
|
-
size={Size.MediumSmall}>Save Layout</Button
|
|
90
|
-
>
|
|
91
|
-
</form>
|
|
92
37
|
</section>
|
|
93
38
|
|
|
94
39
|
<style>.side-panel-section {
|
|
@@ -143,6 +88,10 @@
|
|
|
143
88
|
box-sizing: border-box;
|
|
144
89
|
}
|
|
145
90
|
|
|
91
|
+
/* Top toolbar (saved-layout dropdown + Search input) sits below the
|
|
92
|
+
header but above the scrollable list. It is intentionally not
|
|
93
|
+
inside `.side-panel-body` because the list takes the remaining
|
|
94
|
+
vertical space and scrolls on its own. */
|
|
146
95
|
.toolbar {
|
|
147
96
|
flex-shrink: 0;
|
|
148
97
|
padding: 1rem 1.25rem;
|
|
@@ -160,6 +109,13 @@
|
|
|
160
109
|
display: flex;
|
|
161
110
|
flex-flow: column nowrap;
|
|
162
111
|
background-color: #ffffff;
|
|
112
|
+
/* Horizontal padding aligned with FilterPanel's `.side-panel-body`
|
|
113
|
+
so the column-list cards leave the same gutter before the panel's
|
|
114
|
+
right border as the filter chips do, and the SidePanel's
|
|
115
|
+
`border-right` stays visible between the content and the toolbar
|
|
116
|
+
icons. Vertical padding is 0 because `.column-list` provides
|
|
117
|
+
its own 0.5rem padding (which doubles up to a 1.5rem gap from
|
|
118
|
+
the toolbar above and the footer below). */
|
|
163
119
|
padding: 0 1.25rem;
|
|
164
120
|
box-sizing: border-box;
|
|
165
121
|
}</style>
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
props: Record<string, never>;
|
|
4
|
-
exports: {};
|
|
5
|
-
bindings: "";
|
|
6
|
-
slots: {};
|
|
7
|
-
events: {};
|
|
8
|
-
};
|
|
9
|
-
declare class __sveltets_Render<T extends object, IdType extends Primitive> {
|
|
10
|
-
props(): ReturnType<typeof $$render<T, IdType>>['props'];
|
|
11
|
-
events(): ReturnType<typeof $$render<T, IdType>>['events'];
|
|
12
|
-
slots(): ReturnType<typeof $$render<T, IdType>>['slots'];
|
|
13
|
-
bindings(): "";
|
|
14
|
-
exports(): {};
|
|
15
|
-
}
|
|
16
|
-
interface $$IsomorphicComponent {
|
|
17
|
-
new <T extends object, IdType extends Primitive>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, IdType>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, IdType>['props']>, ReturnType<__sveltets_Render<T, IdType>['events']>, ReturnType<__sveltets_Render<T, IdType>['slots']>> & {
|
|
18
|
-
$$bindings?: ReturnType<__sveltets_Render<T, IdType>['bindings']>;
|
|
19
|
-
} & ReturnType<__sveltets_Render<T, IdType>['exports']>;
|
|
20
|
-
<T extends object, IdType extends Primitive>(internal: unknown, props: {}): ReturnType<__sveltets_Render<T, IdType>['exports']>;
|
|
21
|
-
z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
|
|
22
|
-
}
|
|
23
|
-
declare const ColumnPanel: $$IsomorphicComponent;
|
|
24
|
-
type ColumnPanel<T extends object, IdType extends Primitive> = InstanceType<typeof ColumnPanel<T, IdType>>;
|
|
1
|
+
declare const ColumnPanel: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type ColumnPanel = ReturnType<typeof ColumnPanel>;
|
|
25
3
|
export default ColumnPanel;
|
|
@@ -1,56 +1,15 @@
|
|
|
1
1
|
<svelte:options runes />
|
|
2
2
|
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
6
|
-
>
|
|
7
|
-
import Button from '../../Button.svelte';
|
|
3
|
+
<script lang="ts">
|
|
8
4
|
import DesktopModal from '../../DesktopModal.svelte';
|
|
9
|
-
import SingleSelect from '../../SingleSelect.svelte';
|
|
10
5
|
import ColumnList from '../Body/Rows/ColumnList.svelte';
|
|
11
6
|
import TextInput from '../../TextInput.svelte';
|
|
12
|
-
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
13
7
|
import { Size } from '../../../Types/Internal/Size.js';
|
|
14
|
-
import { Variant } from '../../../Types/Internal/Variant.js';
|
|
15
|
-
import { getContext } from 'svelte';
|
|
16
|
-
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
17
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
18
8
|
import type { ColumnPanelModalProps } from './ColumnPanelModalProps.js';
|
|
19
9
|
|
|
20
|
-
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
21
|
-
|
|
22
10
|
const { onclose }: ColumnPanelModalProps = $props();
|
|
23
|
-
const labelledPresets: Array<SelectOption<string>> = $derived(
|
|
24
|
-
tableContext.columnPresets.map((pr) => ({ label: pr.name, value: pr.name }))
|
|
25
|
-
);
|
|
26
|
-
let searchValue = $state('');
|
|
27
|
-
let pendingPresetName = $state('');
|
|
28
|
-
|
|
29
|
-
const selectedValue = $derived(
|
|
30
|
-
labelledPresets.find((it) => it.value === tableContext.selectedPreset?.name) ?? null
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
function trySavePreset(e: KeyboardEvent) {
|
|
34
|
-
if (e.key === 'Enter') {
|
|
35
|
-
e.preventDefault();
|
|
36
|
-
const trimmed = pendingPresetName.trim();
|
|
37
|
-
if (!trimmed) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
tableContext.saveColumnPreset(trimmed);
|
|
41
|
-
pendingPresetName = '';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
11
|
|
|
45
|
-
|
|
46
|
-
e.preventDefault();
|
|
47
|
-
const trimmed = pendingPresetName.trim();
|
|
48
|
-
if (!trimmed) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
tableContext.saveColumnPreset(trimmed);
|
|
52
|
-
pendingPresetName = '';
|
|
53
|
-
}
|
|
12
|
+
let searchValue = $state('');
|
|
54
13
|
</script>
|
|
55
14
|
|
|
56
15
|
<DesktopModal
|
|
@@ -59,25 +18,7 @@
|
|
|
59
18
|
buttons={[{ label: 'Close', callback: () => onclose?.() }]}
|
|
60
19
|
>
|
|
61
20
|
<div class="flex">
|
|
62
|
-
<div class="column-panel top
|
|
63
|
-
<div class="select">
|
|
64
|
-
<SingleSelect
|
|
65
|
-
label="Selected Preset"
|
|
66
|
-
name="Preset"
|
|
67
|
-
options={labelledPresets}
|
|
68
|
-
labelProp="label"
|
|
69
|
-
valueProp="value"
|
|
70
|
-
value={selectedValue}
|
|
71
|
-
valueAsObject
|
|
72
|
-
onchange={(newSelection: SelectOption<string> | null) => {
|
|
73
|
-
const preset =
|
|
74
|
-
newSelection && tableContext.columnPresets.find((p) => p.name === newSelection.value);
|
|
75
|
-
if (preset) {
|
|
76
|
-
tableContext.setSelectedPreset(preset);
|
|
77
|
-
}
|
|
78
|
-
}}
|
|
79
|
-
/>
|
|
80
|
-
</div>
|
|
21
|
+
<div class="column-panel top">
|
|
81
22
|
<TextInput
|
|
82
23
|
bind:value={searchValue}
|
|
83
24
|
leftIcon={{ name: 'search', size: Size.Medium }}
|
|
@@ -92,20 +33,6 @@
|
|
|
92
33
|
<div class="scrollable">
|
|
93
34
|
<ColumnList {searchValue} />
|
|
94
35
|
</div>
|
|
95
|
-
<div class="column-panel bottom save-preset-wrapper">
|
|
96
|
-
<TextInput
|
|
97
|
-
placeholder="Preset name"
|
|
98
|
-
bind:value={pendingPresetName}
|
|
99
|
-
onkeydown={trySavePreset}
|
|
100
|
-
/>
|
|
101
|
-
<Button
|
|
102
|
-
type="submit"
|
|
103
|
-
variant={Variant.Secondary}
|
|
104
|
-
nowrap
|
|
105
|
-
size={Size.MediumSmall}
|
|
106
|
-
onclick={savePreset}>Save Layout</Button
|
|
107
|
-
>
|
|
108
|
-
</div>
|
|
109
36
|
</div>
|
|
110
37
|
</DesktopModal>
|
|
111
38
|
|
|
@@ -128,25 +55,9 @@
|
|
|
128
55
|
}
|
|
129
56
|
.column-panel.top {
|
|
130
57
|
border-bottom: solid 2px #cbd4da;
|
|
131
|
-
}
|
|
132
|
-
.column-panel.bottom {
|
|
133
|
-
border-top: solid 2px #cbd4da;
|
|
134
|
-
}
|
|
135
|
-
.column-panel.presets-and-filter {
|
|
136
58
|
display: flex;
|
|
137
59
|
flex-flow: column;
|
|
138
60
|
row-gap: 0.5rem;
|
|
139
61
|
padding: 0.5rem;
|
|
140
62
|
flex-basis: min-content;
|
|
141
|
-
}
|
|
142
|
-
.column-panel.save-preset-wrapper {
|
|
143
|
-
display: flex;
|
|
144
|
-
flex-flow: row nowrap;
|
|
145
|
-
justify-content: space-between;
|
|
146
|
-
gap: 0.5rem;
|
|
147
|
-
padding: 0.5rem;
|
|
148
|
-
flex-basis: min-content;
|
|
149
|
-
}
|
|
150
|
-
.column-panel .select {
|
|
151
|
-
width: 100%;
|
|
152
63
|
}</style>
|
|
@@ -1,26 +1,4 @@
|
|
|
1
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
2
1
|
import type { ColumnPanelModalProps } from './ColumnPanelModalProps.js';
|
|
3
|
-
declare
|
|
4
|
-
|
|
5
|
-
exports: {};
|
|
6
|
-
bindings: "";
|
|
7
|
-
slots: {};
|
|
8
|
-
events: {};
|
|
9
|
-
};
|
|
10
|
-
declare class __sveltets_Render<T extends object, IdType extends Primitive> {
|
|
11
|
-
props(): ReturnType<typeof $$render<T, IdType>>['props'];
|
|
12
|
-
events(): ReturnType<typeof $$render<T, IdType>>['events'];
|
|
13
|
-
slots(): ReturnType<typeof $$render<T, IdType>>['slots'];
|
|
14
|
-
bindings(): "";
|
|
15
|
-
exports(): {};
|
|
16
|
-
}
|
|
17
|
-
interface $$IsomorphicComponent {
|
|
18
|
-
new <T extends object, IdType extends Primitive>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, IdType>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, IdType>['props']>, ReturnType<__sveltets_Render<T, IdType>['events']>, ReturnType<__sveltets_Render<T, IdType>['slots']>> & {
|
|
19
|
-
$$bindings?: ReturnType<__sveltets_Render<T, IdType>['bindings']>;
|
|
20
|
-
} & ReturnType<__sveltets_Render<T, IdType>['exports']>;
|
|
21
|
-
<T extends object, IdType extends Primitive>(internal: unknown, props: ReturnType<__sveltets_Render<T, IdType>['props']> & {}): ReturnType<__sveltets_Render<T, IdType>['exports']>;
|
|
22
|
-
z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
|
|
23
|
-
}
|
|
24
|
-
declare const ColumnPanelModal: $$IsomorphicComponent;
|
|
25
|
-
type ColumnPanelModal<T extends object, IdType extends Primitive> = InstanceType<typeof ColumnPanelModal<T, IdType>>;
|
|
2
|
+
declare const ColumnPanelModal: import("svelte").Component<ColumnPanelModalProps, {}, "">;
|
|
3
|
+
type ColumnPanelModal = ReturnType<typeof ColumnPanelModal>;
|
|
26
4
|
export default ColumnPanelModal;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Props for `ColumnPanelModal` (the touch variant of the column-
|
|
3
|
-
*
|
|
2
|
+
* Props for `ColumnPanelModal` (the touch variant of the column-list
|
|
3
|
+
* visibility / reorder management panel).
|
|
4
4
|
*/
|
|
5
5
|
export interface ColumnPanelModalProps {
|
|
6
6
|
/** Called when the modal requests close. */
|
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
generics="T extends object, IdType extends Primitive"
|
|
6
6
|
>
|
|
7
7
|
import Action from '../../Action.svelte';
|
|
8
|
-
import Button from '../../Button.svelte';
|
|
9
8
|
import TextInput from '../../TextInput.svelte';
|
|
10
9
|
import { Colour, ColourSet } from '../../../scss/colours.js';
|
|
11
10
|
import { Size } from '../../../Types/Internal/Size.js';
|
|
12
|
-
import { Variant } from '../../../Types/Internal/Variant.js';
|
|
13
11
|
import { SvelteSet } from 'svelte/reactivity';
|
|
14
12
|
import { getContext } from 'svelte';
|
|
15
13
|
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
16
14
|
import type { ColumnDef } from '../Types/Columns/Definitions/ColumnDef.svelte.js';
|
|
17
15
|
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
18
16
|
import FilterInput from './FilterInput.svelte';
|
|
17
|
+
import TableViewDropdown from './TableViewDropdown.svelte';
|
|
19
18
|
|
|
20
19
|
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
21
20
|
|
|
@@ -31,11 +30,6 @@
|
|
|
31
30
|
* string is the "no filter" state. */
|
|
32
31
|
let quickFindValue: string = $state('');
|
|
33
32
|
|
|
34
|
-
/** Whether the Save Filter overflow menu is open. The chevron
|
|
35
|
-
* toggles it; clicking anywhere else (via the window-level
|
|
36
|
-
* listener below) closes it. */
|
|
37
|
-
let saveMenuOpen: boolean = $state(false);
|
|
38
|
-
|
|
39
33
|
/** Names of currently-collapsed groups. A name absent from this
|
|
40
34
|
* set is rendered expanded. Reactive via `SvelteSet` so the panel
|
|
41
35
|
* re-renders on toggle without manually re-assigning. */
|
|
@@ -106,39 +100,6 @@
|
|
|
106
100
|
}
|
|
107
101
|
}
|
|
108
102
|
|
|
109
|
-
function resetFilters(): void {
|
|
110
|
-
tableContext.paginationRepo?.resetFilters();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Placeholder for the future Save-Filter / preset workflow. For
|
|
115
|
-
* now this just closes the overflow menu; consumers wire the real
|
|
116
|
-
* persistence layer in a follow-up workorder. The chevron menu is
|
|
117
|
-
* still useful today as a home for the Reset action and as the
|
|
118
|
-
* insertion point for "Load Filter" / "Manage Filters" entries
|
|
119
|
-
* once those land.
|
|
120
|
-
*/
|
|
121
|
-
function saveFilter(): void {
|
|
122
|
-
saveMenuOpen = false;
|
|
123
|
-
if (typeof window !== 'undefined') {
|
|
124
|
-
window.dispatchEvent(
|
|
125
|
-
new CustomEvent('scoria:save-filter', {
|
|
126
|
-
detail: { tableName: tableContext.tableName },
|
|
127
|
-
})
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function handleWindowClick(e: MouseEvent): void {
|
|
133
|
-
if (!saveMenuOpen) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const target = e.target as HTMLElement | null;
|
|
137
|
-
if (target?.closest('.save-filter-cluster') == null) {
|
|
138
|
-
saveMenuOpen = false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
103
|
/**
|
|
143
104
|
* No-op keydown handler used to override `TextInput`'s default
|
|
144
105
|
* `passthrough` keydown (defined in `Helpers.svelte.ts`), which
|
|
@@ -155,14 +116,16 @@
|
|
|
155
116
|
}
|
|
156
117
|
</script>
|
|
157
118
|
|
|
158
|
-
<svelte:window onclick={handleWindowClick} />
|
|
159
|
-
|
|
160
119
|
<section class="side-panel-section">
|
|
161
120
|
<header class="side-panel-header">
|
|
162
121
|
<h2>Advanced Filter</h2>
|
|
163
122
|
</header>
|
|
164
123
|
|
|
165
124
|
<div class="toolbar">
|
|
125
|
+
<!-- Quick-switch dropdown for saved filter sets. Sits above the
|
|
126
|
+
search box; saving a filter is done from the Table
|
|
127
|
+
Configuration modal's "Save a new Filter" tab. -->
|
|
128
|
+
<TableViewDropdown kind="filter" />
|
|
166
129
|
<div class="quick-find">
|
|
167
130
|
<TextInput
|
|
168
131
|
labelTop="Quick Find a Filter"
|
|
@@ -172,36 +135,6 @@
|
|
|
172
135
|
onkeydown={noopKeydown}
|
|
173
136
|
/>
|
|
174
137
|
</div>
|
|
175
|
-
|
|
176
|
-
<div class="save-filter-cluster">
|
|
177
|
-
<Button
|
|
178
|
-
variant={Variant.Secondary}
|
|
179
|
-
nowrap
|
|
180
|
-
disabled
|
|
181
|
-
size={Size.MediumSmall}
|
|
182
|
-
onclick={saveFilter}>Save Filter</Button
|
|
183
|
-
>
|
|
184
|
-
<Action
|
|
185
|
-
label="More save options"
|
|
186
|
-
colourSet={ColourSet.Auxiliary}
|
|
187
|
-
icon={{ name: 'chevron-down', size: Size.Medium }}
|
|
188
|
-
onclick={() => {
|
|
189
|
-
saveMenuOpen = !saveMenuOpen;
|
|
190
|
-
}}
|
|
191
|
-
/>
|
|
192
|
-
{#if saveMenuOpen}
|
|
193
|
-
<div class="save-menu">
|
|
194
|
-
<button
|
|
195
|
-
type="button"
|
|
196
|
-
class="save-menu-item"
|
|
197
|
-
onclick={() => {
|
|
198
|
-
saveMenuOpen = false;
|
|
199
|
-
resetFilters();
|
|
200
|
-
}}>Reset all filters</button
|
|
201
|
-
>
|
|
202
|
-
</div>
|
|
203
|
-
{/if}
|
|
204
|
-
</div>
|
|
205
138
|
</div>
|
|
206
139
|
|
|
207
140
|
<div class="side-panel-body">
|
|
@@ -306,59 +239,24 @@
|
|
|
306
239
|
box-sizing: border-box;
|
|
307
240
|
}
|
|
308
241
|
|
|
242
|
+
/* Top toolbar (saved-filter dropdown + Quick Find) sits below the
|
|
243
|
+
header but above the scrollable body. Kept outside
|
|
244
|
+
`.side-panel-body` so it stays pinned while the groups list
|
|
245
|
+
scrolls underneath. The two rows stack vertically: the
|
|
246
|
+
quick-switch dropdown on top, the search field below it. */
|
|
309
247
|
.toolbar {
|
|
310
248
|
flex-shrink: 0;
|
|
311
249
|
display: flex;
|
|
312
|
-
flex-flow:
|
|
313
|
-
align-items: center;
|
|
250
|
+
flex-flow: column nowrap;
|
|
314
251
|
gap: 0.75rem;
|
|
315
252
|
padding: 1rem 1.25rem;
|
|
316
253
|
background-color: #ffffff;
|
|
317
254
|
border-bottom: solid 1px #cbd4da;
|
|
318
255
|
}
|
|
319
256
|
.toolbar .quick-find {
|
|
320
|
-
flex: 1;
|
|
321
257
|
min-width: 0;
|
|
322
258
|
--input-height: 3rem;
|
|
323
259
|
}
|
|
324
|
-
.toolbar .save-filter-cluster {
|
|
325
|
-
position: relative;
|
|
326
|
-
display: flex;
|
|
327
|
-
flex-flow: row nowrap;
|
|
328
|
-
align-items: center;
|
|
329
|
-
gap: 0.25rem;
|
|
330
|
-
}
|
|
331
|
-
.toolbar .save-filter-cluster .save-menu {
|
|
332
|
-
position: absolute;
|
|
333
|
-
top: 100%;
|
|
334
|
-
right: 0;
|
|
335
|
-
margin-top: 0.25rem;
|
|
336
|
-
background-color: #ffffff;
|
|
337
|
-
border: solid 1px #cbd4da;
|
|
338
|
-
border-radius: 4px;
|
|
339
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
340
|
-
min-width: 12rem;
|
|
341
|
-
z-index: 5;
|
|
342
|
-
display: flex;
|
|
343
|
-
flex-flow: column nowrap;
|
|
344
|
-
padding: 0.25rem 0;
|
|
345
|
-
}
|
|
346
|
-
.toolbar .save-filter-cluster .save-menu-item {
|
|
347
|
-
background: transparent;
|
|
348
|
-
border: none;
|
|
349
|
-
text-align: left;
|
|
350
|
-
padding: 0.5rem 0.75rem;
|
|
351
|
-
cursor: pointer;
|
|
352
|
-
font: inherit;
|
|
353
|
-
color: #3a4952;
|
|
354
|
-
}
|
|
355
|
-
.toolbar .save-filter-cluster .save-menu-item:hover {
|
|
356
|
-
background-color: #f4f7f9;
|
|
357
|
-
}
|
|
358
|
-
.toolbar .save-filter-cluster .save-menu-item:focus-visible {
|
|
359
|
-
outline: 2px solid #259fc7;
|
|
360
|
-
outline-offset: -2px;
|
|
361
|
-
}
|
|
362
260
|
|
|
363
261
|
p.empty {
|
|
364
262
|
color: #647d8e;
|
|
@@ -372,6 +270,11 @@ p.empty {
|
|
|
372
270
|
gap: 1rem;
|
|
373
271
|
}
|
|
374
272
|
|
|
273
|
+
/* `overflow: visible` (no longer `overflow: hidden`) so a filter
|
|
274
|
+
row's select dropdown can extend below the group's bottom edge
|
|
275
|
+
when there are not enough rows in the group to push the dropdown
|
|
276
|
+
past it. The header / body each round their own outer corners
|
|
277
|
+
instead so the rounded-card look survives without clipping. */
|
|
375
278
|
.group {
|
|
376
279
|
border: solid 1px #cbd4da;
|
|
377
280
|
border-radius: 4px;
|
|
@@ -387,6 +290,11 @@ p.empty {
|
|
|
387
290
|
padding: 0.5rem 0.75rem;
|
|
388
291
|
background-color: #f4f7f9;
|
|
389
292
|
border-bottom: solid 1px #cbd4da;
|
|
293
|
+
/* Round the header's top corners directly so the tinted strip
|
|
294
|
+
follows the parent's rounded outline. Without this rule
|
|
295
|
+
(combined with the `.group` `overflow: visible` change) the
|
|
296
|
+
header's blue band would leak past the parent's rounded
|
|
297
|
+
corners. */
|
|
390
298
|
border-top-left-radius: 4px;
|
|
391
299
|
border-top-right-radius: 4px;
|
|
392
300
|
}
|
|
@@ -399,10 +307,14 @@ p.empty {
|
|
|
399
307
|
|
|
400
308
|
.group.collapsed > .group-header {
|
|
401
309
|
border-bottom: none;
|
|
310
|
+
/* When collapsed the header is the only visible row, so it
|
|
311
|
+
has to round all four corners to follow the parent outline. */
|
|
402
312
|
border-bottom-left-radius: 4px;
|
|
403
313
|
border-bottom-right-radius: 4px;
|
|
404
314
|
}
|
|
405
315
|
|
|
316
|
+
/* Round the bottom of the body so its visual edge follows the
|
|
317
|
+
parent's rounded outline once `overflow: hidden` is gone. */
|
|
406
318
|
.group:not(.collapsed) > .group-body {
|
|
407
319
|
border-bottom-left-radius: 4px;
|
|
408
320
|
border-bottom-right-radius: 4px;
|