@lavalogic/scoria 0.40.0 → 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,194 +1,194 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
6
|
-
>
|
|
7
|
-
import DesktopModal from '../../DesktopModal.svelte';
|
|
8
|
-
import Icon from '../../Icon.svelte';
|
|
9
|
-
import InfoAlert from '../../InfoAlert.svelte';
|
|
10
|
-
import SingleSelect from '../../SingleSelect.svelte';
|
|
11
|
-
import TextInput from '../../TextInput.svelte';
|
|
12
|
-
import { cssLength } from '../../../Helpers/Helpers.svelte.js';
|
|
13
|
-
import { ColourSet } from '../../../scss/colours.js';
|
|
14
|
-
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
15
|
-
import { Size } from '../../../Types/Internal/Size.js';
|
|
16
|
-
import { getContext, onMount } from 'svelte';
|
|
17
|
-
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
18
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
19
|
-
import type { DatatableViewVisibility } from '../Types/Persistence/DatatableView.js';
|
|
20
|
-
import type { UpdateViewOptionsModalProps } from './UpdateViewOptionsModalProps.js';
|
|
21
|
-
|
|
22
|
-
const { view, onclose, onsaved }: UpdateViewOptionsModalProps = $props();
|
|
23
|
-
|
|
24
|
-
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
25
|
-
|
|
26
|
-
// Local form state. Seeded from the view's current values inside
|
|
27
|
-
// `onMount` so the modal opens populated with what's already saved
|
|
28
|
-
// without triggering Svelte's `state_referenced_locally` lint
|
|
29
|
-
// against the prop read; Save diffs against the view's current
|
|
30
|
-
// values to skip a no-op PATCH.
|
|
31
|
-
let name: string = $state('');
|
|
32
|
-
let visibility: DatatableViewVisibility = $state('private');
|
|
33
|
-
|
|
34
|
-
onMount(() => {
|
|
35
|
-
name = view.name;
|
|
36
|
-
visibility = view.visibility ?? 'private';
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// User-visible error banner. Adapter rejections are routed here so
|
|
40
|
-
// the modal never throws into the host UI.
|
|
41
|
-
let errorMessage = $state<string | null>(null);
|
|
42
|
-
|
|
43
|
-
// `true` while the PATCH is in flight, disables the footer buttons.
|
|
44
|
-
let saving = $state(false);
|
|
45
|
-
|
|
46
|
-
const visibilityOptions: Array<SelectOption<DatatableViewVisibility>> = [
|
|
47
|
-
{
|
|
48
|
-
label: 'Private — only owner and explicit subscribers',
|
|
49
|
-
value: 'private',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
label: 'Public — anyone with access to this table can subscribe',
|
|
53
|
-
value: 'public',
|
|
54
|
-
},
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
const selectedVisibility: SelectOption<DatatableViewVisibility> = $derived(
|
|
58
|
-
visibilityOptions.find((o) => o.value === visibility) ?? visibilityOptions[0]
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// Whether anything in the form differs from the initial values.
|
|
62
|
-
// Drives the Save button's disabled state so a no-op Save never
|
|
63
|
-
// hits the network (also enforced inside `updateViewOptions`).
|
|
64
|
-
const dirty = $derived(
|
|
65
|
-
name.trim() !== view.name || visibility !== (view.visibility ?? 'private')
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
/** Whether the name is valid for submission (non-empty, trimmed). */
|
|
69
|
-
const isValidName = $derived(name.trim().length > 0);
|
|
70
|
-
|
|
71
|
-
async function save(): Promise<void> {
|
|
72
|
-
if (!dirty) {
|
|
73
|
-
onclose();
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (!isValidName) {
|
|
77
|
-
errorMessage = `Name cannot be empty.`;
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
errorMessage = null;
|
|
82
|
-
saving = true;
|
|
83
|
-
try {
|
|
84
|
-
await tableContext.updateViewOptions(view, {
|
|
85
|
-
name: name.trim(),
|
|
86
|
-
visibility,
|
|
87
|
-
});
|
|
88
|
-
if (onsaved) {
|
|
89
|
-
await onsaved();
|
|
90
|
-
}
|
|
91
|
-
onclose();
|
|
92
|
-
} catch (e: unknown) {
|
|
93
|
-
console.error('[UpdateViewOptionsModal] save failed:', e);
|
|
94
|
-
errorMessage =
|
|
95
|
-
e instanceof Error
|
|
96
|
-
? `Could not update options. (${e.message})`
|
|
97
|
-
: 'Could not update options.';
|
|
98
|
-
} finally {
|
|
99
|
-
saving = false;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
</script>
|
|
103
|
-
|
|
104
|
-
<DesktopModal
|
|
105
|
-
headerLabel={`Update Options - "${view.name}"`}
|
|
106
|
-
headerSubtitle={`Rename and change sharing for this ${view.kind === 'filter' ? 'filter' : 'layout'}.`}
|
|
107
|
-
onclose={() => {
|
|
108
|
-
if (!saving) {
|
|
109
|
-
onclose();
|
|
110
|
-
}
|
|
111
|
-
}}
|
|
112
|
-
width={cssLength('40vw')}
|
|
113
|
-
minWidth={cssLength('32rem')}
|
|
114
|
-
buttons={[
|
|
115
|
-
{
|
|
116
|
-
label: 'Cancel',
|
|
117
|
-
callback: () => {
|
|
118
|
-
if (!saving) {
|
|
119
|
-
onclose();
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
disabled: saving,
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
label: saving ? 'Saving…' : 'Save',
|
|
126
|
-
callback: () => {
|
|
127
|
-
void save();
|
|
128
|
-
},
|
|
129
|
-
colourSet: ColourSet.Primary,
|
|
130
|
-
disabled: saving || !dirty || !isValidName,
|
|
131
|
-
},
|
|
132
|
-
]}
|
|
133
|
-
>
|
|
134
|
-
<div class="options-body">
|
|
135
|
-
{#if errorMessage}
|
|
136
|
-
<div
|
|
137
|
-
class="error-banner"
|
|
138
|
-
role="alert"
|
|
139
|
-
>
|
|
140
|
-
<Icon
|
|
141
|
-
name="circle-exclamation"
|
|
142
|
-
size={Size.Small}
|
|
143
|
-
/>
|
|
144
|
-
<span>{errorMessage}</span>
|
|
145
|
-
</div>
|
|
146
|
-
{/if}
|
|
147
|
-
|
|
148
|
-
<InfoAlert>
|
|
149
|
-
Changes apply to every user who has access to this {view.kind === 'filter'
|
|
150
|
-
? 'filter'
|
|
151
|
-
: 'layout'}. Making it public allows any user with access to the table to subscribe.
|
|
152
|
-
</InfoAlert>
|
|
153
|
-
|
|
154
|
-
<div class="field">
|
|
155
|
-
<label
|
|
156
|
-
class="field-label"
|
|
157
|
-
for="update-view-name">Name</label
|
|
158
|
-
>
|
|
159
|
-
<TextInput
|
|
160
|
-
id="update-view-name"
|
|
161
|
-
bind:value={name}
|
|
162
|
-
placeholder={`${view.kind === 'filter' ? 'Filter' : 'Layout'} name`}
|
|
163
|
-
required
|
|
164
|
-
invalid={!isValidName}
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
|
|
168
|
-
<div class="field">
|
|
169
|
-
<label
|
|
170
|
-
class="field-label"
|
|
171
|
-
for="update-view-visibility">Visibility</label
|
|
172
|
-
>
|
|
173
|
-
<SingleSelect
|
|
174
|
-
inputId="update-view-visibility"
|
|
175
|
-
name="Visibility"
|
|
176
|
-
options={visibilityOptions}
|
|
177
|
-
labelProp="label"
|
|
178
|
-
valueProp="value"
|
|
179
|
-
value={selectedVisibility}
|
|
180
|
-
valueAsObject
|
|
181
|
-
searchable={false}
|
|
182
|
-
onchange={(option: SelectOption<DatatableViewVisibility> | null) => {
|
|
183
|
-
if (option) {
|
|
184
|
-
visibility = option.value;
|
|
185
|
-
}
|
|
186
|
-
}}
|
|
187
|
-
/>
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
</DesktopModal>
|
|
191
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
generics="T extends object, IdType extends Primitive"
|
|
6
|
+
>
|
|
7
|
+
import DesktopModal from '../../DesktopModal.svelte';
|
|
8
|
+
import Icon from '../../Icon.svelte';
|
|
9
|
+
import InfoAlert from '../../InfoAlert.svelte';
|
|
10
|
+
import SingleSelect from '../../SingleSelect.svelte';
|
|
11
|
+
import TextInput from '../../TextInput.svelte';
|
|
12
|
+
import { cssLength } from '../../../Helpers/Helpers.svelte.js';
|
|
13
|
+
import { ColourSet } from '../../../scss/colours.js';
|
|
14
|
+
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
15
|
+
import { Size } from '../../../Types/Internal/Size.js';
|
|
16
|
+
import { getContext, onMount } from 'svelte';
|
|
17
|
+
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
18
|
+
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
19
|
+
import type { DatatableViewVisibility } from '../Types/Persistence/DatatableView.js';
|
|
20
|
+
import type { UpdateViewOptionsModalProps } from './UpdateViewOptionsModalProps.js';
|
|
21
|
+
|
|
22
|
+
const { view, onclose, onsaved }: UpdateViewOptionsModalProps = $props();
|
|
23
|
+
|
|
24
|
+
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
25
|
+
|
|
26
|
+
// Local form state. Seeded from the view's current values inside
|
|
27
|
+
// `onMount` so the modal opens populated with what's already saved
|
|
28
|
+
// without triggering Svelte's `state_referenced_locally` lint
|
|
29
|
+
// against the prop read; Save diffs against the view's current
|
|
30
|
+
// values to skip a no-op PATCH.
|
|
31
|
+
let name: string = $state('');
|
|
32
|
+
let visibility: DatatableViewVisibility = $state('private');
|
|
33
|
+
|
|
34
|
+
onMount(() => {
|
|
35
|
+
name = view.name;
|
|
36
|
+
visibility = view.visibility ?? 'private';
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// User-visible error banner. Adapter rejections are routed here so
|
|
40
|
+
// the modal never throws into the host UI.
|
|
41
|
+
let errorMessage = $state<string | null>(null);
|
|
42
|
+
|
|
43
|
+
// `true` while the PATCH is in flight, disables the footer buttons.
|
|
44
|
+
let saving = $state(false);
|
|
45
|
+
|
|
46
|
+
const visibilityOptions: Array<SelectOption<DatatableViewVisibility>> = [
|
|
47
|
+
{
|
|
48
|
+
label: 'Private — only owner and explicit subscribers',
|
|
49
|
+
value: 'private',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Public — anyone with access to this table can subscribe',
|
|
53
|
+
value: 'public',
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const selectedVisibility: SelectOption<DatatableViewVisibility> = $derived(
|
|
58
|
+
visibilityOptions.find((o) => o.value === visibility) ?? visibilityOptions[0]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Whether anything in the form differs from the initial values.
|
|
62
|
+
// Drives the Save button's disabled state so a no-op Save never
|
|
63
|
+
// hits the network (also enforced inside `updateViewOptions`).
|
|
64
|
+
const dirty = $derived(
|
|
65
|
+
name.trim() !== view.name || visibility !== (view.visibility ?? 'private')
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
/** Whether the name is valid for submission (non-empty, trimmed). */
|
|
69
|
+
const isValidName = $derived(name.trim().length > 0);
|
|
70
|
+
|
|
71
|
+
async function save(): Promise<void> {
|
|
72
|
+
if (!dirty) {
|
|
73
|
+
onclose();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!isValidName) {
|
|
77
|
+
errorMessage = `Name cannot be empty.`;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
errorMessage = null;
|
|
82
|
+
saving = true;
|
|
83
|
+
try {
|
|
84
|
+
await tableContext.updateViewOptions(view, {
|
|
85
|
+
name: name.trim(),
|
|
86
|
+
visibility,
|
|
87
|
+
});
|
|
88
|
+
if (onsaved) {
|
|
89
|
+
await onsaved();
|
|
90
|
+
}
|
|
91
|
+
onclose();
|
|
92
|
+
} catch (e: unknown) {
|
|
93
|
+
console.error('[UpdateViewOptionsModal] save failed:', e);
|
|
94
|
+
errorMessage =
|
|
95
|
+
e instanceof Error
|
|
96
|
+
? `Could not update options. (${e.message})`
|
|
97
|
+
: 'Could not update options.';
|
|
98
|
+
} finally {
|
|
99
|
+
saving = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<DesktopModal
|
|
105
|
+
headerLabel={`Update Options - "${view.name}"`}
|
|
106
|
+
headerSubtitle={`Rename and change sharing for this ${view.kind === 'filter' ? 'filter' : 'layout'}.`}
|
|
107
|
+
onclose={() => {
|
|
108
|
+
if (!saving) {
|
|
109
|
+
onclose();
|
|
110
|
+
}
|
|
111
|
+
}}
|
|
112
|
+
width={cssLength('40vw')}
|
|
113
|
+
minWidth={cssLength('32rem')}
|
|
114
|
+
buttons={[
|
|
115
|
+
{
|
|
116
|
+
label: 'Cancel',
|
|
117
|
+
callback: () => {
|
|
118
|
+
if (!saving) {
|
|
119
|
+
onclose();
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
disabled: saving,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
label: saving ? 'Saving…' : 'Save',
|
|
126
|
+
callback: () => {
|
|
127
|
+
void save();
|
|
128
|
+
},
|
|
129
|
+
colourSet: ColourSet.Primary,
|
|
130
|
+
disabled: saving || !dirty || !isValidName,
|
|
131
|
+
},
|
|
132
|
+
]}
|
|
133
|
+
>
|
|
134
|
+
<div class="options-body">
|
|
135
|
+
{#if errorMessage}
|
|
136
|
+
<div
|
|
137
|
+
class="error-banner"
|
|
138
|
+
role="alert"
|
|
139
|
+
>
|
|
140
|
+
<Icon
|
|
141
|
+
name="circle-exclamation"
|
|
142
|
+
size={Size.Small}
|
|
143
|
+
/>
|
|
144
|
+
<span>{errorMessage}</span>
|
|
145
|
+
</div>
|
|
146
|
+
{/if}
|
|
147
|
+
|
|
148
|
+
<InfoAlert>
|
|
149
|
+
Changes apply to every user who has access to this {view.kind === 'filter'
|
|
150
|
+
? 'filter'
|
|
151
|
+
: 'layout'}. Making it public allows any user with access to the table to subscribe.
|
|
152
|
+
</InfoAlert>
|
|
153
|
+
|
|
154
|
+
<div class="field">
|
|
155
|
+
<label
|
|
156
|
+
class="field-label"
|
|
157
|
+
for="update-view-name">Name</label
|
|
158
|
+
>
|
|
159
|
+
<TextInput
|
|
160
|
+
id="update-view-name"
|
|
161
|
+
bind:value={name}
|
|
162
|
+
placeholder={`${view.kind === 'filter' ? 'Filter' : 'Layout'} name`}
|
|
163
|
+
required
|
|
164
|
+
invalid={!isValidName}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div class="field">
|
|
169
|
+
<label
|
|
170
|
+
class="field-label"
|
|
171
|
+
for="update-view-visibility">Visibility</label
|
|
172
|
+
>
|
|
173
|
+
<SingleSelect
|
|
174
|
+
inputId="update-view-visibility"
|
|
175
|
+
name="Visibility"
|
|
176
|
+
options={visibilityOptions}
|
|
177
|
+
labelProp="label"
|
|
178
|
+
valueProp="value"
|
|
179
|
+
value={selectedVisibility}
|
|
180
|
+
valueAsObject
|
|
181
|
+
searchable={false}
|
|
182
|
+
onchange={(option: SelectOption<DatatableViewVisibility> | null) => {
|
|
183
|
+
if (option) {
|
|
184
|
+
visibility = option.value;
|
|
185
|
+
}
|
|
186
|
+
}}
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</DesktopModal>
|
|
191
|
+
|
|
192
192
|
<style>.options-body {
|
|
193
193
|
display: flex;
|
|
194
194
|
flex-flow: column nowrap;
|
|
@@ -222,4 +222,4 @@
|
|
|
222
222
|
font-size: 1.125rem;
|
|
223
223
|
font-weight: 600;
|
|
224
224
|
color: #3a4952;
|
|
225
|
-
}</style>
|
|
225
|
+
}</style>
|
|
@@ -1,132 +1,132 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
module
|
|
6
|
-
>
|
|
7
|
-
import Table from './Table.svelte';
|
|
8
|
-
import type { NestedTableProps } from './NestedTableProps.js';
|
|
9
|
-
import type { Primitive, TableInitOptions } from './Types/Context/TableInitOptions.js';
|
|
10
|
-
import { TableContext } from './Types/Context/TableContext.svelte.js';
|
|
11
|
-
import { LocalTableDataRepository } from './Types/DataRepository/LocalTableDataRepository.svelte.js';
|
|
12
|
-
import { ToolbarPosition } from './Types/Toolbar/ToolbarPosition.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Default `TableInitOptions` overrides applied to every nested table.
|
|
16
|
-
* Tuned for read-only inline display: copy, resize and column
|
|
17
|
-
* reordering are turned off, quick / advanced filtering remain on so
|
|
18
|
-
* the consumer can still narrow rows, the toolbar is hidden, and the
|
|
19
|
-
* footer is suppressed because pagination is rarely useful for inline
|
|
20
|
-
* nested data.
|
|
21
|
-
*
|
|
22
|
-
* Frozen so consumers cannot accidentally mutate the shared default
|
|
23
|
-
* object.
|
|
24
|
-
*/
|
|
25
|
-
const DEFAULT_NESTED_TABLE_OPTIONS: Omit<
|
|
26
|
-
TableInitOptions<object, never>,
|
|
27
|
-
'selectionExtractor' | 'getUserId'
|
|
28
|
-
> = Object.freeze({
|
|
29
|
-
allowCopy: false,
|
|
30
|
-
enableResize: false,
|
|
31
|
-
// Nested-table columns can be reordered and pinned by default like
|
|
32
|
-
// the outer table. Callers opt out per inner table via the
|
|
33
|
-
// `inner.allowReorder` option on `col.expand` (or the matching
|
|
34
|
-
// `allowReorder` on top-level `createTable`).
|
|
35
|
-
allowColumnReordering: true,
|
|
36
|
-
allowQuickFiltering: true,
|
|
37
|
-
allowAdvancedFiltering: true,
|
|
38
|
-
toolbar: ToolbarPosition.None,
|
|
39
|
-
displayFooter: false,
|
|
40
|
-
});
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<script
|
|
44
|
-
lang="ts"
|
|
45
|
-
generics="T extends object,IdType extends Primitive"
|
|
46
|
-
>
|
|
47
|
-
const {
|
|
48
|
-
tableName,
|
|
49
|
-
data,
|
|
50
|
-
columnDefWrapper,
|
|
51
|
-
options,
|
|
52
|
-
oncontextmenu,
|
|
53
|
-
getUserId,
|
|
54
|
-
}: NestedTableProps<T, IdType> = $props();
|
|
55
|
-
|
|
56
|
-
// Reactive wrapper exposing the `data` prop as `.value` so the
|
|
57
|
-
// underlying LocalTableDataRepository can read it through its
|
|
58
|
-
// RefWrapper contract without us materialising a copy.
|
|
59
|
-
const wrapper = {
|
|
60
|
-
get value() {
|
|
61
|
-
return data;
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
let root: HTMLDivElement | undefined = $state();
|
|
66
|
-
|
|
67
|
-
// Track the parent `.datatable`'s visible width (clientWidth) via
|
|
68
|
-
// ResizeObserver. `.datatable` is the horizontal scroll container, so
|
|
69
|
-
// its `clientWidth` is the visible viewport regardless of how wide the
|
|
70
|
-
// inner `.datatable-grid` has grown via `min-width: max-content`. Do
|
|
71
|
-
// NOT measure `.flex_table` instead - flex `min-width: auto`
|
|
72
|
-
// propagates the scroll-content width and sizes the nested table to
|
|
73
|
-
// the full grid, breaking the sticky-fit behaviour.
|
|
74
|
-
//
|
|
75
|
-
// Reading layout inside a derivation would force a synchronous reflow
|
|
76
|
-
// on every reactive re-run; the ResizeObserver path keeps it out of
|
|
77
|
-
// the reactive graph.
|
|
78
|
-
let maxWidth: number | undefined = $state();
|
|
79
|
-
$effect(() => {
|
|
80
|
-
if (!root) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const scrollHost = root.closest('.datatable');
|
|
84
|
-
if (!(scrollHost instanceof HTMLElement)) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
maxWidth = scrollHost.clientWidth;
|
|
88
|
-
const ro = new ResizeObserver(() => {
|
|
89
|
-
maxWidth = scrollHost.clientWidth;
|
|
90
|
-
});
|
|
91
|
-
ro.observe(scrollHost);
|
|
92
|
-
return () => {
|
|
93
|
-
ro.disconnect();
|
|
94
|
-
};
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// These class instances have `$state`-backed internal fields, so they
|
|
98
|
-
// must NOT be wrapped in `$state(...)` (that would add a redundant
|
|
99
|
-
// reactive layer). The `dataRepo` and `tableContext` references must
|
|
100
|
-
// stay stable across updates: a fresh `dataRepo` would clear
|
|
101
|
-
// filter/sort state, and a fresh `tableContext` would tear down focus.
|
|
102
|
-
//
|
|
103
|
-
// svelte-ignore state_referenced_locally
|
|
104
|
-
const dataRepo = new LocalTableDataRepository<T>(tableName, wrapper, () => getUserId());
|
|
105
|
-
|
|
106
|
-
// svelte-ignore state_referenced_locally
|
|
107
|
-
const tableContext = TableContext.init<T, IdType>(columnDefWrapper, tableName, dataRepo, {
|
|
108
|
-
...DEFAULT_NESTED_TABLE_OPTIONS,
|
|
109
|
-
...options,
|
|
110
|
-
getUserId: () => getUserId(),
|
|
111
|
-
});
|
|
112
|
-
</script>
|
|
113
|
-
|
|
114
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
115
|
-
<div
|
|
116
|
-
class="limit-height"
|
|
117
|
-
bind:this={root}
|
|
118
|
-
style:width={maxWidth != null ? `${maxWidth}px` : null}
|
|
119
|
-
oncontextmenu={(e) => {
|
|
120
|
-
e.stopPropagation();
|
|
121
|
-
oncontextmenu?.(e);
|
|
122
|
-
}}
|
|
123
|
-
>
|
|
124
|
-
<Table
|
|
125
|
-
nested
|
|
126
|
-
{tableContext}
|
|
127
|
-
/>
|
|
128
|
-
</div>
|
|
129
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
module
|
|
6
|
+
>
|
|
7
|
+
import Table from './Table.svelte';
|
|
8
|
+
import type { NestedTableProps } from './NestedTableProps.js';
|
|
9
|
+
import type { Primitive, TableInitOptions } from './Types/Context/TableInitOptions.js';
|
|
10
|
+
import { TableContext } from './Types/Context/TableContext.svelte.js';
|
|
11
|
+
import { LocalTableDataRepository } from './Types/DataRepository/LocalTableDataRepository.svelte.js';
|
|
12
|
+
import { ToolbarPosition } from './Types/Toolbar/ToolbarPosition.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Default `TableInitOptions` overrides applied to every nested table.
|
|
16
|
+
* Tuned for read-only inline display: copy, resize and column
|
|
17
|
+
* reordering are turned off, quick / advanced filtering remain on so
|
|
18
|
+
* the consumer can still narrow rows, the toolbar is hidden, and the
|
|
19
|
+
* footer is suppressed because pagination is rarely useful for inline
|
|
20
|
+
* nested data.
|
|
21
|
+
*
|
|
22
|
+
* Frozen so consumers cannot accidentally mutate the shared default
|
|
23
|
+
* object.
|
|
24
|
+
*/
|
|
25
|
+
const DEFAULT_NESTED_TABLE_OPTIONS: Omit<
|
|
26
|
+
TableInitOptions<object, never>,
|
|
27
|
+
'selectionExtractor' | 'getUserId'
|
|
28
|
+
> = Object.freeze({
|
|
29
|
+
allowCopy: false,
|
|
30
|
+
enableResize: false,
|
|
31
|
+
// Nested-table columns can be reordered and pinned by default like
|
|
32
|
+
// the outer table. Callers opt out per inner table via the
|
|
33
|
+
// `inner.allowReorder` option on `col.expand` (or the matching
|
|
34
|
+
// `allowReorder` on top-level `createTable`).
|
|
35
|
+
allowColumnReordering: true,
|
|
36
|
+
allowQuickFiltering: true,
|
|
37
|
+
allowAdvancedFiltering: true,
|
|
38
|
+
toolbar: ToolbarPosition.None,
|
|
39
|
+
displayFooter: false,
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<script
|
|
44
|
+
lang="ts"
|
|
45
|
+
generics="T extends object,IdType extends Primitive"
|
|
46
|
+
>
|
|
47
|
+
const {
|
|
48
|
+
tableName,
|
|
49
|
+
data,
|
|
50
|
+
columnDefWrapper,
|
|
51
|
+
options,
|
|
52
|
+
oncontextmenu,
|
|
53
|
+
getUserId,
|
|
54
|
+
}: NestedTableProps<T, IdType> = $props();
|
|
55
|
+
|
|
56
|
+
// Reactive wrapper exposing the `data` prop as `.value` so the
|
|
57
|
+
// underlying LocalTableDataRepository can read it through its
|
|
58
|
+
// RefWrapper contract without us materialising a copy.
|
|
59
|
+
const wrapper = {
|
|
60
|
+
get value() {
|
|
61
|
+
return data;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
let root: HTMLDivElement | undefined = $state();
|
|
66
|
+
|
|
67
|
+
// Track the parent `.datatable`'s visible width (clientWidth) via
|
|
68
|
+
// ResizeObserver. `.datatable` is the horizontal scroll container, so
|
|
69
|
+
// its `clientWidth` is the visible viewport regardless of how wide the
|
|
70
|
+
// inner `.datatable-grid` has grown via `min-width: max-content`. Do
|
|
71
|
+
// NOT measure `.flex_table` instead - flex `min-width: auto`
|
|
72
|
+
// propagates the scroll-content width and sizes the nested table to
|
|
73
|
+
// the full grid, breaking the sticky-fit behaviour.
|
|
74
|
+
//
|
|
75
|
+
// Reading layout inside a derivation would force a synchronous reflow
|
|
76
|
+
// on every reactive re-run; the ResizeObserver path keeps it out of
|
|
77
|
+
// the reactive graph.
|
|
78
|
+
let maxWidth: number | undefined = $state();
|
|
79
|
+
$effect(() => {
|
|
80
|
+
if (!root) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const scrollHost = root.closest('.datatable');
|
|
84
|
+
if (!(scrollHost instanceof HTMLElement)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
maxWidth = scrollHost.clientWidth;
|
|
88
|
+
const ro = new ResizeObserver(() => {
|
|
89
|
+
maxWidth = scrollHost.clientWidth;
|
|
90
|
+
});
|
|
91
|
+
ro.observe(scrollHost);
|
|
92
|
+
return () => {
|
|
93
|
+
ro.disconnect();
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// These class instances have `$state`-backed internal fields, so they
|
|
98
|
+
// must NOT be wrapped in `$state(...)` (that would add a redundant
|
|
99
|
+
// reactive layer). The `dataRepo` and `tableContext` references must
|
|
100
|
+
// stay stable across updates: a fresh `dataRepo` would clear
|
|
101
|
+
// filter/sort state, and a fresh `tableContext` would tear down focus.
|
|
102
|
+
//
|
|
103
|
+
// svelte-ignore state_referenced_locally
|
|
104
|
+
const dataRepo = new LocalTableDataRepository<T>(tableName, wrapper, () => getUserId());
|
|
105
|
+
|
|
106
|
+
// svelte-ignore state_referenced_locally
|
|
107
|
+
const tableContext = TableContext.init<T, IdType>(columnDefWrapper, tableName, dataRepo, {
|
|
108
|
+
...DEFAULT_NESTED_TABLE_OPTIONS,
|
|
109
|
+
...options,
|
|
110
|
+
getUserId: () => getUserId(),
|
|
111
|
+
});
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
115
|
+
<div
|
|
116
|
+
class="limit-height"
|
|
117
|
+
bind:this={root}
|
|
118
|
+
style:width={maxWidth != null ? `${maxWidth}px` : null}
|
|
119
|
+
oncontextmenu={(e) => {
|
|
120
|
+
e.stopPropagation();
|
|
121
|
+
oncontextmenu?.(e);
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<Table
|
|
125
|
+
nested
|
|
126
|
+
{tableContext}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
130
|
<style>.limit-height {
|
|
131
131
|
position: sticky;
|
|
132
132
|
left: 0;
|
|
@@ -135,4 +135,4 @@
|
|
|
135
135
|
box-sizing: border-box;
|
|
136
136
|
background-color: #f4f7f9;
|
|
137
137
|
box-shadow: inset 0px 7px 5px -6px #cbd4da, inset 0px -7px 5px -6px #cbd4da;
|
|
138
|
-
}</style>
|
|
138
|
+
}</style>
|