@lavalogic/scoria 0.40.1 → 0.40.2
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 +58 -58
- package/dist/Components/AccordionGroup.svelte +46 -46
- package/dist/Components/AccordionGroupButton.svelte +76 -76
- package/dist/Components/Action.svelte +136 -136
- package/dist/Components/AlertModal.svelte +195 -195
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +729 -729
- package/dist/Components/Base/BaseModal.svelte +125 -125
- package/dist/Components/Base/ContextWrapper.svelte +72 -72
- package/dist/Components/Base/Pagination.svelte +198 -198
- package/dist/Components/Base/Snippets.svelte +36 -36
- package/dist/Components/BigRadioSet.svelte +38 -38
- package/dist/Components/Bubble.svelte +101 -101
- package/dist/Components/Button.svelte +209 -209
- package/dist/Components/Checkbox.svelte +86 -86
- package/dist/Components/CollapsibleCard.svelte +78 -78
- package/dist/Components/Contexts/ContextMenu.svelte +164 -164
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +60 -60
- package/dist/Components/Contexts/Toast.svelte +86 -86
- package/dist/Components/Contexts/ToastContainer.svelte +65 -65
- package/dist/Components/Contexts/Tooltip.svelte +37 -37
- package/dist/Components/DataMatrixIcon.svelte +112 -112
- package/dist/Components/DateInput.svelte +148 -148
- package/dist/Components/DatePicker.svelte +300 -300
- package/dist/Components/DesktopModal.svelte +90 -90
- package/dist/Components/Dial.svelte +204 -204
- package/dist/Components/DimensionInput.svelte +170 -170
- package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -119
- package/dist/Components/DragMenu/DraggableCard.svelte +77 -77
- package/dist/Components/FileCard.svelte +24 -24
- package/dist/Components/FileUpload.svelte +236 -236
- package/dist/Components/GridInput.svelte +196 -196
- package/dist/Components/GridInputSet.svelte +29 -29
- package/dist/Components/HorizontalTabGroup.svelte +82 -82
- package/dist/Components/HorizontalTabGroupButton.svelte +44 -44
- package/dist/Components/Icon.svelte +128 -128
- package/dist/Components/Icons.js +357 -357
- package/dist/Components/InfoAlert.svelte +35 -35
- package/dist/Components/LoadingAnimation.svelte +29 -29
- package/dist/Components/LoadingModal.svelte +45 -45
- package/dist/Components/LoadingOverlay.svelte +25 -25
- package/dist/Components/Modal.svelte +258 -258
- package/dist/Components/MultiSelect.svelte +220 -220
- package/dist/Components/Nav/Nav.svelte +110 -110
- package/dist/Components/Nav/NavServiceMenuList.svelte +43 -43
- package/dist/Components/Nav/NavTab.svelte +62 -62
- package/dist/Components/Nav/ServiceMenuItem.svelte +112 -112
- package/dist/Components/Nav/ServicesNav.svelte +80 -80
- package/dist/Components/NumberInput.svelte +157 -157
- package/dist/Components/PageHeading.svelte +25 -25
- package/dist/Components/PasswordInput.svelte +88 -88
- package/dist/Components/Portal.svelte +36 -36
- package/dist/Components/ProgressBubble.svelte +107 -107
- package/dist/Components/QuerySelect.svelte +366 -366
- package/dist/Components/SingleSelect.svelte +250 -250
- package/dist/Components/StepButton.svelte +70 -70
- package/dist/Components/StepForm.svelte +140 -140
- package/dist/Components/Switch.svelte +69 -69
- package/dist/Components/Table/ARCHITECTURE.md +298 -298
- package/dist/Components/Table/Body/QuickSearchCell.svelte +679 -679
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +51 -51
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +59 -59
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +454 -454
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -79
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +185 -185
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +124 -124
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +131 -120
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +234 -234
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +105 -105
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -87
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +126 -126
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -159
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +246 -246
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +216 -216
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +252 -252
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +179 -179
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +221 -221
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +197 -197
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +190 -190
- package/dist/Components/Table/Body/Rows/TableRow.svelte +290 -290
- package/dist/Components/Table/Body/TableBody.svelte +155 -155
- package/dist/Components/Table/ColumnFactory.svelte.js +1 -0
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -41
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -30
- package/dist/Components/Table/Headers/TableHead.svelte +415 -415
- package/dist/Components/Table/Misc/ColumnPanel.svelte +39 -39
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +39 -39
- package/dist/Components/Table/Misc/FilterInput.svelte +641 -641
- package/dist/Components/Table/Misc/FilterPanel.svelte +190 -190
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +31 -31
- package/dist/Components/Table/Misc/ShareViewModal.svelte +291 -291
- package/dist/Components/Table/Misc/SidePanel.svelte +35 -35
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +644 -644
- package/dist/Components/Table/Misc/TableFooter.svelte +340 -340
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +140 -140
- package/dist/Components/Table/Misc/TableSidebar.svelte +200 -200
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +105 -105
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +100 -100
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -107
- package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +192 -192
- package/dist/Components/Table/NestedTable.svelte +130 -130
- package/dist/Components/Table/Table.svelte +692 -692
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.d.ts +5 -0
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.js +3 -0
- package/dist/Components/Table/Types/Public/ColumnOptions.d.ts +10 -0
- package/dist/Components/Test/TestContextConsumer.svelte +13 -13
- package/dist/Components/TextArea.svelte +130 -130
- package/dist/Components/TextInput.svelte +176 -176
- package/dist/Components/ThreeStateRadio.svelte +131 -131
- package/dist/Components/Touch/BoolCard.svelte +45 -45
- package/dist/Components/Touch/DateModal.svelte +209 -209
- package/dist/Components/Touch/EditCard.svelte +59 -59
- package/dist/Components/Touch/InfoCard.svelte +31 -31
- package/dist/Components/Touch/InfoTextCard.svelte +28 -28
- package/dist/Components/Touch/ModalExplanation.svelte +17 -17
- package/dist/Components/Touch/NumberKeyboard.svelte +225 -225
- package/dist/Components/Touch/NumberModal.svelte +79 -79
- package/dist/Components/Touch/PlusMinusCard.svelte +46 -46
- package/dist/Components/Touch/SelectModal.svelte +127 -127
- package/dist/Components/Touch/SummaryCard.svelte +93 -93
- package/dist/Components/Touch/TextAreaModal.svelte +95 -95
- package/dist/Components/Touch/TextModal.svelte +106 -106
- package/dist/Components/Touch/TouchCard.svelte +33 -33
- package/dist/Components/Touch/TouchModal.svelte +274 -274
- package/dist/Components/Touch/UtilityButton.svelte +109 -109
- package/dist/Components/VerticalTabGroup.svelte +79 -79
- package/dist/Components/VerticalTabGroupButton.svelte +58 -58
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +35 -35
- package/dist/scss/_basics.scss +12 -12
- package/dist/scss/_colours.scss +134 -134
- package/dist/scss/_mixins.scss +3392 -3392
- package/dist/scss/_motion.scss +57 -57
- package/dist/scss/_sizing.scss +85 -85
- package/dist/scss/_transitions.scss +8 -8
- package/package.json +1 -1
|
@@ -1,202 +1,202 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
6
|
-
>
|
|
7
|
-
import Button from '../../Button.svelte';
|
|
8
|
-
import { devCatch } from '../../../Helpers/Helpers.svelte.js';
|
|
9
|
-
import { Side } from '../../../Types/Internal/Side.js';
|
|
10
|
-
import { Size } from '../../../Types/Internal/Size.js';
|
|
11
|
-
import { Variant } from '../../../Types/Internal/Variant.js';
|
|
12
|
-
import { getContext } from 'svelte';
|
|
13
|
-
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
14
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
15
|
-
import { ExpandedPanel } from '../Types/ExpandedPanel.js';
|
|
16
|
-
import { ToolbarPosition } from '../Types/Toolbar/ToolbarPosition.js';
|
|
17
|
-
import ColumnPanel from './ColumnPanel.svelte';
|
|
18
|
-
import FilterPanel from './FilterPanel.svelte';
|
|
19
|
-
import SidePanel from './SidePanel.svelte';
|
|
20
|
-
import TableConfigurationModal from './TableConfigurationModal.svelte';
|
|
21
|
-
import ToolboxPanel from './ToolboxPanel.svelte';
|
|
22
|
-
|
|
23
|
-
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
24
|
-
|
|
25
|
-
// `TableConfigurationModal` is a full `DesktopModal` rather than a
|
|
26
|
-
// side-panel body, so it is registered here but rendered standalone
|
|
27
|
-
// (outside `SidePanel`) below - `panels` only maps the side-panel
|
|
28
|
-
// kinds. Keeping the Configuration key in the map documents the
|
|
29
|
-
// relationship and mirrors `TableHorizontalBar`'s registration.
|
|
30
|
-
const panels = {
|
|
31
|
-
[ExpandedPanel.Column]: ColumnPanel,
|
|
32
|
-
[ExpandedPanel.Filter]: FilterPanel,
|
|
33
|
-
[ExpandedPanel.Toolbox]: ToolboxPanel,
|
|
34
|
-
[ExpandedPanel.Configuration]: TableConfigurationModal,
|
|
35
|
-
} as const;
|
|
36
|
-
|
|
37
|
-
let expandedPanel = $state<ExpandedPanel | null>(null);
|
|
38
|
-
|
|
39
|
-
$effect(() => {
|
|
40
|
-
// The Configuration modal owns its own overlay and must not shrink
|
|
41
|
-
// the table footer the way an inline side panel does.
|
|
42
|
-
tableContext.limitFooter = !!expandedPanel && expandedPanel !== ExpandedPanel.Configuration;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Side-panel body for the inline panel kinds only; the Configuration
|
|
46
|
-
// kind is excluded and handled by the standalone modal block below.
|
|
47
|
-
let ExpandedPanelElement = $derived(
|
|
48
|
-
expandedPanel &&
|
|
49
|
-
expandedPanel !== ExpandedPanel.None &&
|
|
50
|
-
expandedPanel !== ExpandedPanel.Configuration
|
|
51
|
-
? panels[expandedPanel]
|
|
52
|
-
: null
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
function handleClickOutside(e: MouseEvent) {
|
|
56
|
-
const target = e.target as HTMLElement;
|
|
57
|
-
if (!target.isConnected) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
// A click landing inside the side-panel or the toolbar
|
|
61
|
-
// sidebar should keep the panel open.
|
|
62
|
-
if (target.closest('.side-content') != null) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (target.closest('.sidebar') != null) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// A click landing inside a teleported select dropdown (see
|
|
69
|
-
// `Helpers/dropdownPortal.ts`) is a logical extension of the
|
|
70
|
-
// trigger that lives inside the side-panel, so it must not
|
|
71
|
-
// close the panel. The marker attribute is set by the portal
|
|
72
|
-
// helper while the dropdown is teleported to `document.body`.
|
|
73
|
-
if (target.closest('[data-scoria-portal-dropdown]') != null) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
// The Table Configuration modal owns its own backdrop + close
|
|
77
|
-
// affordances; a click inside its `.modal-root` must not be
|
|
78
|
-
// treated as a click-outside that dismisses the panel.
|
|
79
|
-
if (target.closest('.modal-root') != null) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
expandedPanel = null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function handleEscKey(e: KeyboardEvent) {
|
|
86
|
-
if (e.key === 'Escape') {
|
|
87
|
-
expandedPanel = null;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
</script>
|
|
91
|
-
|
|
92
|
-
<svelte:window
|
|
93
|
-
onclick={handleClickOutside}
|
|
94
|
-
onkeydown={handleEscKey}
|
|
95
|
-
/>
|
|
96
|
-
|
|
97
|
-
{#if ExpandedPanelElement != null}
|
|
98
|
-
<SidePanel toolbarPosition={tableContext.toolbarPosition}>
|
|
99
|
-
<ExpandedPanelElement />
|
|
100
|
-
</SidePanel>
|
|
101
|
-
{/if}
|
|
102
|
-
|
|
103
|
-
{#if expandedPanel === ExpandedPanel.Configuration}
|
|
104
|
-
<TableConfigurationModal
|
|
105
|
-
onclose={() => {
|
|
106
|
-
expandedPanel = null;
|
|
107
|
-
}}
|
|
108
|
-
/>
|
|
109
|
-
{/if}
|
|
110
|
-
<div class="sidebar">
|
|
111
|
-
<Button
|
|
112
|
-
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
113
|
-
onclick={() => {
|
|
114
|
-
tableContext.toggleQuickFiltering();
|
|
115
|
-
}}
|
|
116
|
-
toggled={tableContext.showQuickFiltering}
|
|
117
|
-
disabled={!tableContext.allowQuickFiltering}
|
|
118
|
-
variant={Variant.Toolbar}
|
|
119
|
-
iconTop={{
|
|
120
|
-
name: 'assessment',
|
|
121
|
-
size: Size.Medium,
|
|
122
|
-
}}
|
|
123
|
-
label="Quick Filter"
|
|
124
|
-
/>
|
|
125
|
-
<Button
|
|
126
|
-
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
127
|
-
disabled={!tableContext.allowAdvancedFiltering}
|
|
128
|
-
toggled={expandedPanel === ExpandedPanel.Filter}
|
|
129
|
-
onclick={() => {
|
|
130
|
-
expandedPanel = expandedPanel === ExpandedPanel.Filter ? null : ExpandedPanel.Filter;
|
|
131
|
-
}}
|
|
132
|
-
variant={Variant.Toolbar}
|
|
133
|
-
iconTop={{
|
|
134
|
-
name: 'filter-bars',
|
|
135
|
-
size: Size.Medium,
|
|
136
|
-
}}
|
|
137
|
-
label="Advanced Filter"
|
|
138
|
-
/>
|
|
139
|
-
<Button
|
|
140
|
-
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
141
|
-
onclick={() => {
|
|
142
|
-
expandedPanel = expandedPanel === ExpandedPanel.Column ? null : ExpandedPanel.Column;
|
|
143
|
-
}}
|
|
144
|
-
variant={Variant.Toolbar}
|
|
145
|
-
toggled={expandedPanel === ExpandedPanel.Column}
|
|
146
|
-
iconTop={{
|
|
147
|
-
name: 'columns',
|
|
148
|
-
size: Size.Medium,
|
|
149
|
-
}}
|
|
150
|
-
label="Modify Columns"
|
|
151
|
-
/>
|
|
152
|
-
<Button
|
|
153
|
-
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
154
|
-
toggled={expandedPanel === ExpandedPanel.Toolbox}
|
|
155
|
-
onclick={() => {
|
|
156
|
-
expandedPanel = expandedPanel === ExpandedPanel.Toolbox ? null : ExpandedPanel.Toolbox;
|
|
157
|
-
}}
|
|
158
|
-
variant={Variant.Toolbar}
|
|
159
|
-
iconTop={{
|
|
160
|
-
name: 'category',
|
|
161
|
-
size: Size.Medium,
|
|
162
|
-
}}
|
|
163
|
-
label="My Tool Box"
|
|
164
|
-
/>
|
|
165
|
-
<Button
|
|
166
|
-
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
167
|
-
toggled={expandedPanel === ExpandedPanel.Configuration}
|
|
168
|
-
onclick={() => {
|
|
169
|
-
expandedPanel =
|
|
170
|
-
expandedPanel === ExpandedPanel.Configuration ? null : ExpandedPanel.Configuration;
|
|
171
|
-
}}
|
|
172
|
-
variant={Variant.Toolbar}
|
|
173
|
-
iconTop={{
|
|
174
|
-
name: 'setting-cog',
|
|
175
|
-
size: Size.Medium,
|
|
176
|
-
}}
|
|
177
|
-
label="Table Configuration"
|
|
178
|
-
/>
|
|
179
|
-
|
|
180
|
-
{#if tableContext.paginationRepo?.reload}
|
|
181
|
-
<Button
|
|
182
|
-
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
183
|
-
onclick={() => {
|
|
184
|
-
tableContext.paginationRepo?.reload?.().catch(devCatch);
|
|
185
|
-
}}
|
|
186
|
-
disabled={!tableContext.allowQuickFiltering}
|
|
187
|
-
variant={Variant.Toolbar}
|
|
188
|
-
toggled={tableContext.dataRepo.isLoading}
|
|
189
|
-
iconTop={{
|
|
190
|
-
name: 'refresh',
|
|
191
|
-
size: Size.Medium,
|
|
192
|
-
}}
|
|
193
|
-
label="Refresh"
|
|
194
|
-
/>
|
|
195
|
-
{/if}
|
|
196
|
-
|
|
197
|
-
<div class="filler"></div>
|
|
198
|
-
</div>
|
|
199
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
generics="T extends object, IdType extends Primitive"
|
|
6
|
+
>
|
|
7
|
+
import Button from '../../Button.svelte';
|
|
8
|
+
import { devCatch } from '../../../Helpers/Helpers.svelte.js';
|
|
9
|
+
import { Side } from '../../../Types/Internal/Side.js';
|
|
10
|
+
import { Size } from '../../../Types/Internal/Size.js';
|
|
11
|
+
import { Variant } from '../../../Types/Internal/Variant.js';
|
|
12
|
+
import { getContext } from 'svelte';
|
|
13
|
+
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
14
|
+
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
15
|
+
import { ExpandedPanel } from '../Types/ExpandedPanel.js';
|
|
16
|
+
import { ToolbarPosition } from '../Types/Toolbar/ToolbarPosition.js';
|
|
17
|
+
import ColumnPanel from './ColumnPanel.svelte';
|
|
18
|
+
import FilterPanel from './FilterPanel.svelte';
|
|
19
|
+
import SidePanel from './SidePanel.svelte';
|
|
20
|
+
import TableConfigurationModal from './TableConfigurationModal.svelte';
|
|
21
|
+
import ToolboxPanel from './ToolboxPanel.svelte';
|
|
22
|
+
|
|
23
|
+
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
24
|
+
|
|
25
|
+
// `TableConfigurationModal` is a full `DesktopModal` rather than a
|
|
26
|
+
// side-panel body, so it is registered here but rendered standalone
|
|
27
|
+
// (outside `SidePanel`) below - `panels` only maps the side-panel
|
|
28
|
+
// kinds. Keeping the Configuration key in the map documents the
|
|
29
|
+
// relationship and mirrors `TableHorizontalBar`'s registration.
|
|
30
|
+
const panels = {
|
|
31
|
+
[ExpandedPanel.Column]: ColumnPanel,
|
|
32
|
+
[ExpandedPanel.Filter]: FilterPanel,
|
|
33
|
+
[ExpandedPanel.Toolbox]: ToolboxPanel,
|
|
34
|
+
[ExpandedPanel.Configuration]: TableConfigurationModal,
|
|
35
|
+
} as const;
|
|
36
|
+
|
|
37
|
+
let expandedPanel = $state<ExpandedPanel | null>(null);
|
|
38
|
+
|
|
39
|
+
$effect(() => {
|
|
40
|
+
// The Configuration modal owns its own overlay and must not shrink
|
|
41
|
+
// the table footer the way an inline side panel does.
|
|
42
|
+
tableContext.limitFooter = !!expandedPanel && expandedPanel !== ExpandedPanel.Configuration;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Side-panel body for the inline panel kinds only; the Configuration
|
|
46
|
+
// kind is excluded and handled by the standalone modal block below.
|
|
47
|
+
let ExpandedPanelElement = $derived(
|
|
48
|
+
expandedPanel &&
|
|
49
|
+
expandedPanel !== ExpandedPanel.None &&
|
|
50
|
+
expandedPanel !== ExpandedPanel.Configuration
|
|
51
|
+
? panels[expandedPanel]
|
|
52
|
+
: null
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
function handleClickOutside(e: MouseEvent) {
|
|
56
|
+
const target = e.target as HTMLElement;
|
|
57
|
+
if (!target.isConnected) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// A click landing inside the side-panel or the toolbar
|
|
61
|
+
// sidebar should keep the panel open.
|
|
62
|
+
if (target.closest('.side-content') != null) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (target.closest('.sidebar') != null) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// A click landing inside a teleported select dropdown (see
|
|
69
|
+
// `Helpers/dropdownPortal.ts`) is a logical extension of the
|
|
70
|
+
// trigger that lives inside the side-panel, so it must not
|
|
71
|
+
// close the panel. The marker attribute is set by the portal
|
|
72
|
+
// helper while the dropdown is teleported to `document.body`.
|
|
73
|
+
if (target.closest('[data-scoria-portal-dropdown]') != null) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// The Table Configuration modal owns its own backdrop + close
|
|
77
|
+
// affordances; a click inside its `.modal-root` must not be
|
|
78
|
+
// treated as a click-outside that dismisses the panel.
|
|
79
|
+
if (target.closest('.modal-root') != null) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
expandedPanel = null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handleEscKey(e: KeyboardEvent) {
|
|
86
|
+
if (e.key === 'Escape') {
|
|
87
|
+
expandedPanel = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<svelte:window
|
|
93
|
+
onclick={handleClickOutside}
|
|
94
|
+
onkeydown={handleEscKey}
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
{#if ExpandedPanelElement != null}
|
|
98
|
+
<SidePanel toolbarPosition={tableContext.toolbarPosition}>
|
|
99
|
+
<ExpandedPanelElement />
|
|
100
|
+
</SidePanel>
|
|
101
|
+
{/if}
|
|
102
|
+
|
|
103
|
+
{#if expandedPanel === ExpandedPanel.Configuration}
|
|
104
|
+
<TableConfigurationModal
|
|
105
|
+
onclose={() => {
|
|
106
|
+
expandedPanel = null;
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
{/if}
|
|
110
|
+
<div class="sidebar">
|
|
111
|
+
<Button
|
|
112
|
+
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
113
|
+
onclick={() => {
|
|
114
|
+
tableContext.toggleQuickFiltering();
|
|
115
|
+
}}
|
|
116
|
+
toggled={tableContext.showQuickFiltering}
|
|
117
|
+
disabled={!tableContext.allowQuickFiltering}
|
|
118
|
+
variant={Variant.Toolbar}
|
|
119
|
+
iconTop={{
|
|
120
|
+
name: 'assessment',
|
|
121
|
+
size: Size.Medium,
|
|
122
|
+
}}
|
|
123
|
+
label="Quick Filter"
|
|
124
|
+
/>
|
|
125
|
+
<Button
|
|
126
|
+
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
127
|
+
disabled={!tableContext.allowAdvancedFiltering}
|
|
128
|
+
toggled={expandedPanel === ExpandedPanel.Filter}
|
|
129
|
+
onclick={() => {
|
|
130
|
+
expandedPanel = expandedPanel === ExpandedPanel.Filter ? null : ExpandedPanel.Filter;
|
|
131
|
+
}}
|
|
132
|
+
variant={Variant.Toolbar}
|
|
133
|
+
iconTop={{
|
|
134
|
+
name: 'filter-bars',
|
|
135
|
+
size: Size.Medium,
|
|
136
|
+
}}
|
|
137
|
+
label="Advanced Filter"
|
|
138
|
+
/>
|
|
139
|
+
<Button
|
|
140
|
+
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
141
|
+
onclick={() => {
|
|
142
|
+
expandedPanel = expandedPanel === ExpandedPanel.Column ? null : ExpandedPanel.Column;
|
|
143
|
+
}}
|
|
144
|
+
variant={Variant.Toolbar}
|
|
145
|
+
toggled={expandedPanel === ExpandedPanel.Column}
|
|
146
|
+
iconTop={{
|
|
147
|
+
name: 'columns',
|
|
148
|
+
size: Size.Medium,
|
|
149
|
+
}}
|
|
150
|
+
label="Modify Columns"
|
|
151
|
+
/>
|
|
152
|
+
<Button
|
|
153
|
+
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
154
|
+
toggled={expandedPanel === ExpandedPanel.Toolbox}
|
|
155
|
+
onclick={() => {
|
|
156
|
+
expandedPanel = expandedPanel === ExpandedPanel.Toolbox ? null : ExpandedPanel.Toolbox;
|
|
157
|
+
}}
|
|
158
|
+
variant={Variant.Toolbar}
|
|
159
|
+
iconTop={{
|
|
160
|
+
name: 'category',
|
|
161
|
+
size: Size.Medium,
|
|
162
|
+
}}
|
|
163
|
+
label="My Tool Box"
|
|
164
|
+
/>
|
|
165
|
+
<Button
|
|
166
|
+
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
167
|
+
toggled={expandedPanel === ExpandedPanel.Configuration}
|
|
168
|
+
onclick={() => {
|
|
169
|
+
expandedPanel =
|
|
170
|
+
expandedPanel === ExpandedPanel.Configuration ? null : ExpandedPanel.Configuration;
|
|
171
|
+
}}
|
|
172
|
+
variant={Variant.Toolbar}
|
|
173
|
+
iconTop={{
|
|
174
|
+
name: 'setting-cog',
|
|
175
|
+
size: Size.Medium,
|
|
176
|
+
}}
|
|
177
|
+
label="Table Configuration"
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
{#if tableContext.paginationRepo?.reload}
|
|
181
|
+
<Button
|
|
182
|
+
side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
|
|
183
|
+
onclick={() => {
|
|
184
|
+
tableContext.paginationRepo?.reload?.().catch(devCatch);
|
|
185
|
+
}}
|
|
186
|
+
disabled={!tableContext.allowQuickFiltering}
|
|
187
|
+
variant={Variant.Toolbar}
|
|
188
|
+
toggled={tableContext.dataRepo.isLoading}
|
|
189
|
+
iconTop={{
|
|
190
|
+
name: 'refresh',
|
|
191
|
+
size: Size.Medium,
|
|
192
|
+
}}
|
|
193
|
+
label="Refresh"
|
|
194
|
+
/>
|
|
195
|
+
{/if}
|
|
196
|
+
|
|
197
|
+
<div class="filler"></div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
200
|
<style>.sidebar {
|
|
201
201
|
flex-flow: column nowrap;
|
|
202
202
|
text-orientation: sideways;
|
|
@@ -215,4 +215,4 @@
|
|
|
215
215
|
.sidebar .filler {
|
|
216
216
|
flex-grow: 2;
|
|
217
217
|
background-color: #ffffff;
|
|
218
|
-
}</style>
|
|
218
|
+
}</style>
|
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
6
|
-
>
|
|
7
|
-
import SingleSelect from '../../SingleSelect.svelte';
|
|
8
|
-
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
9
|
-
import { getContext } from 'svelte';
|
|
10
|
-
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
11
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
12
|
-
import type { DatatableViewKind } from '../Types/Persistence/DatatableViewKind.js';
|
|
13
|
-
|
|
14
|
-
interface Props {
|
|
15
|
-
/**
|
|
16
|
-
* Which view-state slice this dropdown switches between. `'layout'`
|
|
17
|
-
* lists saved column layouts; `'filter'` lists saved filter sets.
|
|
18
|
-
* Drives every `_viewState` read (label / active view / saved
|
|
19
|
-
* views) and which kind `resetToDefault` is called with.
|
|
20
|
-
*/
|
|
21
|
-
kind: DatatableViewKind;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const { kind }: Props = $props();
|
|
25
|
-
|
|
26
|
-
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
27
|
-
|
|
28
|
-
// Sentinel value for the "Default" option. Must be NON-EMPTY: svelecte
|
|
29
|
-
// treats an empty-string value as "no selection" and shows the
|
|
30
|
-
// placeholder instead of the selected option. The leading space keeps
|
|
31
|
-
// it distinct from every server-assigned saved-view id (numeric
|
|
32
|
-
// strings) and from `UNSAVED_OPTION`.
|
|
33
|
-
const DEFAULT_OPTION = ' default';
|
|
34
|
-
|
|
35
|
-
// The view-state slice for `kind`, read live from the context.
|
|
36
|
-
const activeView = $derived(tableContext._viewState.activeView(kind));
|
|
37
|
-
const savedViews = $derived(tableContext._viewState.savedViews(kind));
|
|
38
|
-
|
|
39
|
-
// Dropdown label: 'Default' / 'Custom (unsaved)' / '<name>' / '<name>
|
|
40
|
-
// (unsaved)'. Used as the displayed value of the select so it reflects
|
|
41
|
-
// the dirty state exactly. When dirty this is a *transient* string
|
|
42
|
-
// ("Custom (unsaved)" / "<name> (unsaved)") that is deliberately NOT
|
|
43
|
-
// one of the selectable options below.
|
|
44
|
-
const dropdownLabel = $derived(tableContext._viewState.dropdownLabel(kind));
|
|
45
|
-
|
|
46
|
-
// The value of the currently-active option: the sentinel for Default,
|
|
47
|
-
// otherwise the saved view's id.
|
|
48
|
-
const activeValue = $derived(activeView.kind === 'saved' ? activeView.id : DEFAULT_OPTION);
|
|
49
|
-
|
|
50
|
-
// The selectable options. ALWAYS "Default" first (with its plain
|
|
51
|
-
// label, so the user can always pick it to revert) followed by every
|
|
52
|
-
// saved view of this kind with its plain name. The transient
|
|
53
|
-
// "…(unsaved)" label is never substituted in here - keeping it out
|
|
54
|
-
// means "Default" and the saved views stay selectable even while the
|
|
55
|
-
// table state is dirty. "Default" is present even when remote saved
|
|
56
|
-
// views are disabled and `savedViews` is empty.
|
|
57
|
-
const options: Array<SelectOption<string>> = $derived([
|
|
58
|
-
{ label: 'Default', value: DEFAULT_OPTION },
|
|
59
|
-
...savedViews.map((view) => ({ label: view.name, value: view.id })),
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
// The value handed to `SingleSelect`. The `value` is always the active
|
|
63
|
-
// option's real value (the Default sentinel or a saved-view id), so it
|
|
64
|
-
// matches an entry in `options` and `SingleSelect` keeps the selection
|
|
65
|
-
// rendered. The `label` is `dropdownLabel`, which already encodes the
|
|
66
|
-
// dirty state ("…(unsaved)"); `SingleSelect` shows it verbatim via its
|
|
67
|
-
// `selection` snippet. An earlier approach fed a transient value that
|
|
68
|
-
// was absent from `options`, which made svelecte drop the selection
|
|
69
|
-
// and fall back to its placeholder.
|
|
70
|
-
const selectedOption: SelectOption<string> = $derived({
|
|
71
|
-
label: dropdownLabel,
|
|
72
|
-
value: activeValue,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
/** Apply the option the user picked from the dropdown. */
|
|
76
|
-
function selectOption(option: SelectOption<string> | null): void {
|
|
77
|
-
if (!option) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
if (option.value === DEFAULT_OPTION) {
|
|
81
|
-
tableContext.resetToDefault(kind);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const view = savedViews.find((v) => v.id === option.value);
|
|
85
|
-
if (view) {
|
|
86
|
-
tableContext.applyDatatableView(view);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
</script>
|
|
90
|
-
|
|
91
|
-
<div class="view-dropdown">
|
|
92
|
-
<SingleSelect
|
|
93
|
-
label={kind === 'filter' ? 'Filter' : 'Layout'}
|
|
94
|
-
name={kind === 'filter' ? 'Filter' : 'Layout'}
|
|
95
|
-
{options}
|
|
96
|
-
labelProp="label"
|
|
97
|
-
valueProp="value"
|
|
98
|
-
value={selectedOption}
|
|
99
|
-
valueAsObject
|
|
100
|
-
searchable={false}
|
|
101
|
-
onchange={selectOption}
|
|
102
|
-
/>
|
|
103
|
-
</div>
|
|
104
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
generics="T extends object, IdType extends Primitive"
|
|
6
|
+
>
|
|
7
|
+
import SingleSelect from '../../SingleSelect.svelte';
|
|
8
|
+
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
9
|
+
import { getContext } from 'svelte';
|
|
10
|
+
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
11
|
+
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
12
|
+
import type { DatatableViewKind } from '../Types/Persistence/DatatableViewKind.js';
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
/**
|
|
16
|
+
* Which view-state slice this dropdown switches between. `'layout'`
|
|
17
|
+
* lists saved column layouts; `'filter'` lists saved filter sets.
|
|
18
|
+
* Drives every `_viewState` read (label / active view / saved
|
|
19
|
+
* views) and which kind `resetToDefault` is called with.
|
|
20
|
+
*/
|
|
21
|
+
kind: DatatableViewKind;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { kind }: Props = $props();
|
|
25
|
+
|
|
26
|
+
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
27
|
+
|
|
28
|
+
// Sentinel value for the "Default" option. Must be NON-EMPTY: svelecte
|
|
29
|
+
// treats an empty-string value as "no selection" and shows the
|
|
30
|
+
// placeholder instead of the selected option. The leading space keeps
|
|
31
|
+
// it distinct from every server-assigned saved-view id (numeric
|
|
32
|
+
// strings) and from `UNSAVED_OPTION`.
|
|
33
|
+
const DEFAULT_OPTION = ' default';
|
|
34
|
+
|
|
35
|
+
// The view-state slice for `kind`, read live from the context.
|
|
36
|
+
const activeView = $derived(tableContext._viewState.activeView(kind));
|
|
37
|
+
const savedViews = $derived(tableContext._viewState.savedViews(kind));
|
|
38
|
+
|
|
39
|
+
// Dropdown label: 'Default' / 'Custom (unsaved)' / '<name>' / '<name>
|
|
40
|
+
// (unsaved)'. Used as the displayed value of the select so it reflects
|
|
41
|
+
// the dirty state exactly. When dirty this is a *transient* string
|
|
42
|
+
// ("Custom (unsaved)" / "<name> (unsaved)") that is deliberately NOT
|
|
43
|
+
// one of the selectable options below.
|
|
44
|
+
const dropdownLabel = $derived(tableContext._viewState.dropdownLabel(kind));
|
|
45
|
+
|
|
46
|
+
// The value of the currently-active option: the sentinel for Default,
|
|
47
|
+
// otherwise the saved view's id.
|
|
48
|
+
const activeValue = $derived(activeView.kind === 'saved' ? activeView.id : DEFAULT_OPTION);
|
|
49
|
+
|
|
50
|
+
// The selectable options. ALWAYS "Default" first (with its plain
|
|
51
|
+
// label, so the user can always pick it to revert) followed by every
|
|
52
|
+
// saved view of this kind with its plain name. The transient
|
|
53
|
+
// "…(unsaved)" label is never substituted in here - keeping it out
|
|
54
|
+
// means "Default" and the saved views stay selectable even while the
|
|
55
|
+
// table state is dirty. "Default" is present even when remote saved
|
|
56
|
+
// views are disabled and `savedViews` is empty.
|
|
57
|
+
const options: Array<SelectOption<string>> = $derived([
|
|
58
|
+
{ label: 'Default', value: DEFAULT_OPTION },
|
|
59
|
+
...savedViews.map((view) => ({ label: view.name, value: view.id })),
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
// The value handed to `SingleSelect`. The `value` is always the active
|
|
63
|
+
// option's real value (the Default sentinel or a saved-view id), so it
|
|
64
|
+
// matches an entry in `options` and `SingleSelect` keeps the selection
|
|
65
|
+
// rendered. The `label` is `dropdownLabel`, which already encodes the
|
|
66
|
+
// dirty state ("…(unsaved)"); `SingleSelect` shows it verbatim via its
|
|
67
|
+
// `selection` snippet. An earlier approach fed a transient value that
|
|
68
|
+
// was absent from `options`, which made svelecte drop the selection
|
|
69
|
+
// and fall back to its placeholder.
|
|
70
|
+
const selectedOption: SelectOption<string> = $derived({
|
|
71
|
+
label: dropdownLabel,
|
|
72
|
+
value: activeValue,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/** Apply the option the user picked from the dropdown. */
|
|
76
|
+
function selectOption(option: SelectOption<string> | null): void {
|
|
77
|
+
if (!option) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (option.value === DEFAULT_OPTION) {
|
|
81
|
+
tableContext.resetToDefault(kind);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const view = savedViews.find((v) => v.id === option.value);
|
|
85
|
+
if (view) {
|
|
86
|
+
tableContext.applyDatatableView(view);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<div class="view-dropdown">
|
|
92
|
+
<SingleSelect
|
|
93
|
+
label={kind === 'filter' ? 'Filter' : 'Layout'}
|
|
94
|
+
name={kind === 'filter' ? 'Filter' : 'Layout'}
|
|
95
|
+
{options}
|
|
96
|
+
labelProp="label"
|
|
97
|
+
valueProp="value"
|
|
98
|
+
value={selectedOption}
|
|
99
|
+
valueAsObject
|
|
100
|
+
searchable={false}
|
|
101
|
+
onchange={selectOption}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
105
|
<style>.view-dropdown {
|
|
106
106
|
display: flex;
|
|
107
107
|
flex-flow: column nowrap;
|
|
108
108
|
width: 100%;
|
|
109
|
-
}</style>
|
|
109
|
+
}</style>
|