@lavalogic/scoria 0.40.13 → 0.40.15

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.
Files changed (143) hide show
  1. package/README.md +58 -58
  2. package/dist/Components/AccordionGroup.svelte +35 -46
  3. package/dist/Components/AccordionGroupButton.svelte +68 -76
  4. package/dist/Components/Action.svelte +103 -135
  5. package/dist/Components/AlertModal.svelte +171 -190
  6. package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +614 -697
  7. package/dist/Components/Base/BaseModal.svelte +90 -116
  8. package/dist/Components/Base/ContextWrapper.svelte +67 -72
  9. package/dist/Components/Base/Pagination.svelte +173 -196
  10. package/dist/Components/Base/Snippets.svelte +34 -36
  11. package/dist/Components/BigRadioSet.svelte +36 -38
  12. package/dist/Components/Bubble.svelte +78 -101
  13. package/dist/Components/Button.svelte +175 -208
  14. package/dist/Components/Checkbox.svelte +75 -86
  15. package/dist/Components/CollapsibleCard.svelte +72 -87
  16. package/dist/Components/CollapsibleMenuGroup.svelte +53 -57
  17. package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +39 -64
  18. package/dist/Components/Contexts/ContextMenu.svelte +141 -156
  19. package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
  20. package/dist/Components/Contexts/ContextMenuOption.svelte +53 -60
  21. package/dist/Components/Contexts/Toast.svelte +81 -86
  22. package/dist/Components/Contexts/ToastContainer.svelte +58 -62
  23. package/dist/Components/Contexts/Tooltip.svelte +36 -37
  24. package/dist/Components/DataMatrixIcon.svelte +95 -108
  25. package/dist/Components/DateInput.svelte +118 -146
  26. package/dist/Components/DatePicker.svelte +255 -294
  27. package/dist/Components/DesktopModal.svelte +70 -90
  28. package/dist/Components/Dial.svelte +167 -197
  29. package/dist/Components/DimensionInput.svelte +124 -169
  30. package/dist/Components/DragMenu/DragMenuHolder.svelte +98 -114
  31. package/dist/Components/DragMenu/DraggableCard.svelte +64 -76
  32. package/dist/Components/FileCard.svelte +21 -24
  33. package/dist/Components/FileUpload.svelte +190 -225
  34. package/dist/Components/GridInput.svelte +159 -196
  35. package/dist/Components/GridInputSet.svelte +24 -29
  36. package/dist/Components/HorizontalTabGroup.svelte +73 -89
  37. package/dist/Components/HorizontalTabGroupButton.svelte +40 -44
  38. package/dist/Components/Icon.svelte +99 -128
  39. package/dist/Components/Icons.js +357 -357
  40. package/dist/Components/InfoAlert.svelte +35 -38
  41. package/dist/Components/LoadingAnimation.svelte +691 -755
  42. package/dist/Components/LoadingModal.svelte +36 -44
  43. package/dist/Components/LoadingOverlay.svelte +22 -26
  44. package/dist/Components/Modal.svelte +204 -254
  45. package/dist/Components/MultiSelect.svelte +186 -216
  46. package/dist/Components/Nav/Nav.svelte +94 -110
  47. package/dist/Components/Nav/NavServiceMenuList.svelte +57 -61
  48. package/dist/Components/Nav/NavTab.svelte +58 -62
  49. package/dist/Components/Nav/ServiceMenuItem.svelte +103 -111
  50. package/dist/Components/Nav/ServicesNav.svelte +72 -79
  51. package/dist/Components/NumberInput.svelte +121 -155
  52. package/dist/Components/OptionCards.svelte +62 -73
  53. package/dist/Components/PageHeading.svelte +23 -25
  54. package/dist/Components/PasswordInput.svelte +69 -88
  55. package/dist/Components/PopoverButton.svelte +151 -0
  56. package/dist/Components/PopoverButton.svelte.d.ts +9 -0
  57. package/dist/Components/PopoverButtonProps.d.ts +21 -0
  58. package/dist/Components/PopoverButtonProps.js +1 -0
  59. package/dist/Components/Portal.svelte +29 -35
  60. package/dist/Components/ProgressBubble.svelte +91 -106
  61. package/dist/Components/QuerySelect.svelte +260 -346
  62. package/dist/Components/SidebarPanel.svelte +34 -36
  63. package/dist/Components/SingleSelect.svelte +196 -244
  64. package/dist/Components/StepButton.svelte +65 -70
  65. package/dist/Components/StepForm.svelte +115 -135
  66. package/dist/Components/Switch.svelte +57 -69
  67. package/dist/Components/Table/ARCHITECTURE.md +298 -298
  68. package/dist/Components/Table/Body/QuickSearchCell.svelte +578 -655
  69. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +42 -51
  70. package/dist/Components/Table/Body/Rows/ColumnList.svelte +42 -58
  71. package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +403 -442
  72. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +51 -78
  73. package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +165 -184
  74. package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +110 -123
  75. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +134 -160
  76. package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +190 -231
  77. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +82 -104
  78. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +52 -87
  79. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +122 -150
  80. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +132 -159
  81. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +190 -236
  82. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +173 -211
  83. package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +204 -244
  84. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +140 -177
  85. package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +188 -217
  86. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +173 -193
  87. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +166 -187
  88. package/dist/Components/Table/Body/Rows/TableRow.svelte +245 -288
  89. package/dist/Components/Table/Body/TableBody.svelte +141 -154
  90. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +35 -41
  91. package/dist/Components/Table/Headers/SelectAllHeader.svelte +24 -30
  92. package/dist/Components/Table/Headers/TableHead.svelte +348 -402
  93. package/dist/Components/Table/Misc/ColumnPanel.svelte +37 -39
  94. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +35 -39
  95. package/dist/Components/Table/Misc/FilterInput.svelte +556 -619
  96. package/dist/Components/Table/Misc/FilterPanel.svelte +170 -183
  97. package/dist/Components/Table/Misc/FilterPanelModal.svelte +25 -31
  98. package/dist/Components/Table/Misc/ShareViewModal.svelte +252 -281
  99. package/dist/Components/Table/Misc/SidePanel.svelte +32 -35
  100. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +574 -629
  101. package/dist/Components/Table/Misc/TableFooter.svelte +326 -332
  102. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +134 -140
  103. package/dist/Components/Table/Misc/TableSidebar.svelte +183 -198
  104. package/dist/Components/Table/Misc/TableViewDropdown.svelte +83 -103
  105. package/dist/Components/Table/Misc/ToolboxPanel.svelte +83 -100
  106. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +88 -107
  107. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +163 -190
  108. package/dist/Components/Table/NestedTable.svelte +102 -128
  109. package/dist/Components/Table/Table.svelte +531 -663
  110. package/dist/Components/Test/TestContextConsumer.svelte +11 -13
  111. package/dist/Components/TextArea.svelte +95 -130
  112. package/dist/Components/TextInput.svelte +125 -176
  113. package/dist/Components/ThreeStateRadio.svelte +111 -130
  114. package/dist/Components/Touch/BoolCard.svelte +33 -45
  115. package/dist/Components/Touch/DateModal.svelte +164 -204
  116. package/dist/Components/Touch/EditCard.svelte +48 -59
  117. package/dist/Components/Touch/InfoCard.svelte +28 -31
  118. package/dist/Components/Touch/InfoTextCard.svelte +26 -28
  119. package/dist/Components/Touch/ModalExplanation.svelte +15 -17
  120. package/dist/Components/Touch/NumberKeyboard.svelte +181 -218
  121. package/dist/Components/Touch/NumberModal.svelte +59 -79
  122. package/dist/Components/Touch/PlusMinusCard.svelte +43 -46
  123. package/dist/Components/Touch/SelectModal.svelte +111 -126
  124. package/dist/Components/Touch/SummaryCard.svelte +80 -93
  125. package/dist/Components/Touch/TextAreaModal.svelte +72 -94
  126. package/dist/Components/Touch/TextModal.svelte +77 -105
  127. package/dist/Components/Touch/TouchCard.svelte +31 -33
  128. package/dist/Components/Touch/TouchModal.svelte +214 -270
  129. package/dist/Components/Touch/UtilityButton.svelte +88 -109
  130. package/dist/Components/VerticalTabGroup.svelte +60 -78
  131. package/dist/Components/VerticalTabGroupButton.svelte +54 -58
  132. package/dist/Helpers/Datamatrix.d.ts +19 -19
  133. package/dist/Helpers/Datamatrix.js +291 -291
  134. package/dist/Types/Examples/ExampleModal.svelte +31 -35
  135. package/dist/index.d.ts +2 -0
  136. package/dist/index.js +1 -0
  137. package/dist/scss/_basics.scss +12 -12
  138. package/dist/scss/_colours.scss +134 -134
  139. package/dist/scss/_mixins.scss +3392 -3392
  140. package/dist/scss/_motion.scss +57 -57
  141. package/dist/scss/_sizing.scss +85 -85
  142. package/dist/scss/_transitions.scss +8 -8
  143. package/package.json +1 -1
@@ -1,293 +1,264 @@
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 DesktopModal from '../../DesktopModal.svelte';
9
- import Icon from '../../Icon.svelte';
10
- import InfoAlert from '../../InfoAlert.svelte';
11
- import MultiSelect from '../../MultiSelect.svelte';
12
- import { cssLength } from '../../../Helpers/Helpers.svelte.js';
13
- import { ColourSet } from '../../../scss/colours.js';
14
- import { Size } from '../../../Types/Internal/Size.js';
15
- import { Variant } from '../../../Types/Internal/Variant.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 { DatatableViewUser } from '../Types/Persistence/RemoteTableLayoutAdapter.js';
20
- import type { ShareViewModalProps } from './ShareViewModalProps.js';
21
-
22
- const { view, onclose, onsaved }: ShareViewModalProps = $props();
23
-
24
- const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
25
-
26
- // All users the current user is allowed to share with. `null` until the
27
- // boot-time `listUsers` fetch resolves; an array (possibly empty) once
28
- // the call lands. Bound to the `MultiSelect` options below.
29
- let allUsers = $state<ReadonlyArray<DatatableViewUser> | null>(null);
30
-
31
- // User-visible error banner. Adapter rejections are routed here so the
32
- // modal never throws into the host UI.
33
- let errorMessage = $state<string | null>(null);
34
-
35
- // In-flight flags for the two adapter call sites. `loadingUsers` blanks
36
- // the picker while `listUsers` is in flight; `saving` disables the
37
- // footer buttons while the subscribe / unsubscribe deltas land.
38
- let loadingUsers = $state(false);
39
- let saving = $state(false);
40
-
41
- /**
42
- * Current picker selection. Each entry is a `DatatableViewUser`. The
43
- * initial value is seeded from `view.subscribers` (the existing
44
- * roster) inside `onMount` so opening the modal shows "who is already
45
- * shared with", and Save can diff against the originals.
46
- *
47
- * `displayName` falls back to the opaque `userId` when the subscriber
48
- * record carries no label, matching the rest of the Configuration
49
- * modal's fallback behaviour.
50
- */
51
- let selected = $state<Array<DatatableViewUser>>([]);
52
-
53
- /**
54
- * Snapshot of the subscriber ids the view started with. The Save diff
55
- * reads it to compute which subscribers were added (POST subscribe)
56
- * vs removed (POST unsubscribe). Seeded in `onMount` from
57
- * `view.subscribers` so the snapshot stays a true *initial* roster
58
- * regardless of any concurrent edits to `view`.
59
- *
60
- * Typed as a plain `Set` (not `ReadonlySet`) only because it is
61
- * reassigned once in `onMount`; reads downstream still treat it as
62
- * read-only. Not reactive - the modal owns this snapshot for its
63
- * lifetime and never re-derives it.
64
- */
65
- let initialIds: Set<string> = new Set();
66
-
67
- // Whether anything in the picker differs from the initial roster.
68
- // Drives the Save button's disabled state so a no-op Save never hits
69
- // the network.
70
- const dirty = $derived.by(() => {
71
- const currentIds = new Set(selected.map((s) => s.id));
72
- if (currentIds.size !== initialIds.size) {
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ generics="T extends object, IdType extends Primitive"
6
+ >import Button from "../../Button.svelte";
7
+ import DesktopModal from "../../DesktopModal.svelte";
8
+ import Icon from "../../Icon.svelte";
9
+ import InfoAlert from "../../InfoAlert.svelte";
10
+ import MultiSelect from "../../MultiSelect.svelte";
11
+ import { cssLength } from "../../../Helpers/Helpers.svelte.js";
12
+ import { ColourSet } from "../../../scss/colours.js";
13
+ import { Size } from "../../../Types/Internal/Size.js";
14
+ import { Variant } from "../../../Types/Internal/Variant.js";
15
+ import { getContext, onMount } from "svelte";
16
+ import { TableContext } from "../Types/Context/TableContext.svelte.js";
17
+ const { view, onclose, onsaved } = $props();
18
+ const tableContext = getContext(TableContext.identifier);
19
+ // All users the current user is allowed to share with. `null` until the
20
+ // boot-time `listUsers` fetch resolves; an array (possibly empty) once
21
+ // the call lands. Bound to the `MultiSelect` options below.
22
+ let allUsers = $state(null);
23
+ // User-visible error banner. Adapter rejections are routed here so the
24
+ // modal never throws into the host UI.
25
+ let errorMessage = $state(null);
26
+ // In-flight flags for the two adapter call sites. `loadingUsers` blanks
27
+ // the picker while `listUsers` is in flight; `saving` disables the
28
+ // footer buttons while the subscribe / unsubscribe deltas land.
29
+ let loadingUsers = $state(false);
30
+ let saving = $state(false);
31
+ /**
32
+ * Current picker selection. Each entry is a `DatatableViewUser`. The
33
+ * initial value is seeded from `view.subscribers` (the existing
34
+ * roster) inside `onMount` so opening the modal shows "who is already
35
+ * shared with", and Save can diff against the originals.
36
+ *
37
+ * `displayName` falls back to the opaque `userId` when the subscriber
38
+ * record carries no label, matching the rest of the Configuration
39
+ * modal's fallback behaviour.
40
+ */
41
+ let selected = $state([]);
42
+ /**
43
+ * Snapshot of the subscriber ids the view started with. The Save diff
44
+ * reads it to compute which subscribers were added (POST subscribe)
45
+ * vs removed (POST unsubscribe). Seeded in `onMount` from
46
+ * `view.subscribers` so the snapshot stays a true *initial* roster
47
+ * regardless of any concurrent edits to `view`.
48
+ *
49
+ * Typed as a plain `Set` (not `ReadonlySet`) only because it is
50
+ * reassigned once in `onMount`; reads downstream still treat it as
51
+ * read-only. Not reactive - the modal owns this snapshot for its
52
+ * lifetime and never re-derives it.
53
+ */
54
+ let initialIds = new Set();
55
+ // Whether anything in the picker differs from the initial roster.
56
+ // Drives the Save button's disabled state so a no-op Save never hits
57
+ // the network.
58
+ const dirty = $derived.by(() => {
59
+ const currentIds = new Set(selected.map((s) => s.id));
60
+ if (currentIds.size !== initialIds.size) {
61
+ return true;
62
+ }
63
+ for (const id of currentIds) {
64
+ if (!initialIds.has(id)) {
73
65
  return true;
74
66
  }
75
- for (const id of currentIds) {
76
- if (!initialIds.has(id)) {
77
- return true;
78
- }
79
- }
80
- return false;
81
- });
82
-
83
- onMount(() => {
84
- // Snapshot the initial subscriber roster once on mount so the
85
- // Save diff has a stable starting point. Done inside `onMount`
86
- // (rather than at `$state` init) so Svelte's reactivity lint does
87
- // not flag the read as "captures the initial value of `view`" -
88
- // the modal owns this snapshot for its lifetime intentionally.
89
- const initial = (view.subscribers ?? []).map((s) => ({
90
- id: s.userId,
91
- displayName: s.displayName?.trim() ? s.displayName : s.userId,
92
- }));
93
- selected = initial;
94
- initialIds = new Set(initial.map((u) => u.id));
95
-
96
- const adapter = tableContext.remoteLayouts;
97
- if (!adapter) {
98
- errorMessage = 'Sharing is unavailable: no remote layouts adapter is configured.';
99
- return;
100
- }
101
- loadingUsers = true;
102
- adapter
103
- .listUsers()
104
- .then((users) => {
105
- // Exclude the view's owner from the picker - sharing a view
106
- // with its own owner is nonsensical and the backend would
107
- // reject the subscribe anyway.
108
- allUsers = users.filter((u) => u.id !== view.ownerUserId);
109
- })
110
- .catch((e: unknown) => {
111
- console.error('[ShareViewModal] listUsers failed:', e);
112
- errorMessage =
113
- e instanceof Error
114
- ? `Could not load the user list. (${e.message})`
115
- : 'Could not load the user list.';
116
- })
117
- .finally(() => {
118
- loadingUsers = false;
119
- });
67
+ }
68
+ return false;
69
+ });
70
+ onMount(() => {
71
+ // Snapshot the initial subscriber roster once on mount so the
72
+ // Save diff has a stable starting point. Done inside `onMount`
73
+ // (rather than at `$state` init) so Svelte's reactivity lint does
74
+ // not flag the read as "captures the initial value of `view`" -
75
+ // the modal owns this snapshot for its lifetime intentionally.
76
+ const initial = (view.subscribers ?? []).map((s) => ({
77
+ id: s.userId,
78
+ displayName: s.displayName?.trim() ? s.displayName : s.userId
79
+ }));
80
+ selected = initial;
81
+ initialIds = new Set(initial.map((u) => u.id));
82
+ const adapter = tableContext.remoteLayouts;
83
+ if (!adapter) {
84
+ errorMessage = "Sharing is unavailable: no remote layouts adapter is configured.";
85
+ return;
86
+ }
87
+ loadingUsers = true;
88
+ adapter.listUsers().then((users) => {
89
+ // Exclude the view's owner from the picker - sharing a view
90
+ // with its own owner is nonsensical and the backend would
91
+ // reject the subscribe anyway.
92
+ allUsers = users.filter((u) => u.id !== view.ownerUserId);
93
+ }).catch((e) => {
94
+ console.error("[ShareViewModal] listUsers failed:", e);
95
+ errorMessage = e instanceof Error ? `Could not load the user list. (${e.message})` : "Could not load the user list.";
96
+ }).finally(() => {
97
+ loadingUsers = false;
120
98
  });
121
-
122
- /** Remove a user from the current selection. */
123
- function removeFromSelection(userId: string): void {
124
- selected = selected.filter((s) => s.id !== userId);
99
+ });
100
+ /** Remove a user from the current selection. */
101
+ function removeFromSelection(userId) {
102
+ selected = selected.filter((s) => s.id !== userId);
103
+ }
104
+ /** Apply the picker selection by issuing the required deltas. */
105
+ async function save() {
106
+ const adapter = tableContext.remoteLayouts;
107
+ if (!adapter) {
108
+ errorMessage = "Sharing is unavailable: no remote layouts adapter is configured.";
109
+ return;
125
110
  }
126
-
127
- /** Apply the picker selection by issuing the required deltas. */
128
- async function save(): Promise<void> {
129
- const adapter = tableContext.remoteLayouts;
130
- if (!adapter) {
131
- errorMessage = 'Sharing is unavailable: no remote layouts adapter is configured.';
132
- return;
111
+ if (!dirty) {
112
+ onclose();
113
+ return;
114
+ }
115
+ const currentIds = new Set(selected.map((s) => s.id));
116
+ const toAdd = [];
117
+ const toRemove = [];
118
+ // Added: in the new selection but not the original roster.
119
+ for (const id of currentIds) {
120
+ if (!initialIds.has(id)) {
121
+ toAdd.push(id);
133
122
  }
134
- if (!dirty) {
135
- onclose();
136
- return;
123
+ }
124
+ // Removed: in the original roster but not the new selection.
125
+ for (const id of initialIds) {
126
+ if (!currentIds.has(id)) {
127
+ toRemove.push(id);
137
128
  }
138
-
139
- const currentIds = new Set(selected.map((s) => s.id));
140
- const toAdd: Array<string> = [];
141
- const toRemove: Array<string> = [];
142
-
143
- // Added: in the new selection but not the original roster.
144
- for (const id of currentIds) {
145
- if (!initialIds.has(id)) {
146
- toAdd.push(id);
147
- }
129
+ }
130
+ errorMessage = null;
131
+ saving = true;
132
+ try {
133
+ // Run sequentially so a partial failure surfaces a useful error
134
+ // and leaves the already-applied deltas committed (an aborted
135
+ // `Promise.all` is much harder to reason about).
136
+ for (const userId of toAdd) {
137
+ await adapter.addSubscriber(view.id, userId);
148
138
  }
149
- // Removed: in the original roster but not the new selection.
150
- for (const id of initialIds) {
151
- if (!currentIds.has(id)) {
152
- toRemove.push(id);
153
- }
139
+ for (const userId of toRemove) {
140
+ await adapter.removeSubscriber(view.id, userId);
154
141
  }
155
-
156
- errorMessage = null;
157
- saving = true;
158
- try {
159
- // Run sequentially so a partial failure surfaces a useful error
160
- // and leaves the already-applied deltas committed (an aborted
161
- // `Promise.all` is much harder to reason about).
162
- for (const userId of toAdd) {
163
- await adapter.addSubscriber(view.id, userId);
164
- }
165
- for (const userId of toRemove) {
166
- await adapter.removeSubscriber(view.id, userId);
167
- }
168
- if (onsaved) {
169
- await onsaved();
170
- }
171
- onclose();
172
- } catch (e: unknown) {
173
- console.error('[ShareViewModal] save failed:', e);
174
- errorMessage =
175
- e instanceof Error
176
- ? `Could not update sharing. Some changes may have been applied. (${e.message})`
177
- : 'Could not update sharing. Some changes may have been applied.';
178
- } finally {
179
- saving = false;
142
+ if (onsaved) {
143
+ await onsaved();
180
144
  }
145
+ onclose();
146
+ } catch (e) {
147
+ console.error("[ShareViewModal] save failed:", e);
148
+ errorMessage = e instanceof Error ? `Could not update sharing. Some changes may have been applied. (${e.message})` : "Could not update sharing. Some changes may have been applied.";
149
+ } finally {
150
+ saving = false;
181
151
  }
182
- </script>
183
-
184
- <DesktopModal
185
- headerLabel={`Share "${view.name}"`}
186
- headerSubtitle={`Choose who can access this ${view.kind === 'filter' ? 'filter' : 'layout'}.`}
187
- onclose={() => {
188
- if (!saving) {
189
- onclose();
190
- }
191
- }}
192
- width={cssLength('40vw')}
193
- minWidth={cssLength('32rem')}
194
- buttons={[
195
- {
196
- label: 'Cancel',
197
- callback: () => {
198
- if (!saving) {
199
- onclose();
200
- }
201
- },
202
- disabled: saving,
203
- },
204
- {
205
- label: saving ? 'Saving…' : 'Save',
206
- callback: () => {
207
- void save();
208
- },
209
- colourSet: ColourSet.Primary,
210
- disabled: saving || !dirty || allUsers === null,
211
- },
212
- ]}
213
- >
214
- <div class="share-body">
215
- {#if errorMessage}
216
- <div
217
- class="error-banner"
218
- role="alert"
219
- >
220
- <Icon
221
- name="circle-exclamation"
222
- size={Size.Small}
223
- />
224
- <span>{errorMessage}</span>
225
- </div>
226
- {/if}
227
-
228
- <div class="picker">
229
- {#if loadingUsers && allUsers === null}
230
- <InfoAlert>Loading users…</InfoAlert>
231
- {:else if allUsers !== null}
232
- <MultiSelect
233
- label="Users"
234
- name="User"
235
- options={allUsers as Array<DatatableViewUser>}
236
- labelProp="displayName"
237
- valueProp="id"
238
- valueAsObject={true}
239
- selectedValues={selected}
240
- onchange={(values) => {
241
- selected = values;
242
- }}
243
- />
244
- {/if}
245
- </div>
246
-
247
- {#if selected.length === 0}
248
- <InfoAlert>
249
- No users selected. Use the picker above to share this {view.kind === 'filter'
250
- ? 'filter'
251
- : 'layout'} with one or more users.
252
- </InfoAlert>
253
- {:else}
254
- <ul class="user-card-list">
255
- {#each selected as user (user.id)}
256
- <li class="user-card">
257
- <div class="user-info">
258
- <Icon
259
- name="user-3"
260
- size={Size.Medium}
261
- />
262
- <span class="user-name">{user.displayName}</span>
263
- </div>
264
- <div class="user-meta">
265
- <!--
266
- v1 sharing exposes a single role - subscriber - so
267
- the "permission" surface is informational only. The
268
- backend's `editor` / `systemOwned` flags are
269
- reserved no-ops in v1 (see `DatatableView` DTO
270
- JSDoc); scoria never sets them.
271
- -->
272
- <span class="permission-pill">Subscriber</span>
273
- <Button
274
- variant={Variant.Secondary}
275
- size={Size.MediumSmall}
276
- nowrap
277
- iconLeft={{ name: 'circle-cross', size: Size.Small }}
278
- disabled={saving}
279
- onclick={() => {
280
- removeFromSelection(user.id);
281
- }}>Remove</Button
282
- >
283
- </div>
284
- </li>
285
- {/each}
286
- </ul>
287
- {/if}
288
- </div>
289
- </DesktopModal>
290
-
152
+ }
153
+ </script>
154
+
155
+ <DesktopModal
156
+ headerLabel={`Share "${view.name}"`}
157
+ headerSubtitle={`Choose who can access this ${view.kind === 'filter' ? 'filter' : 'layout'}.`}
158
+ onclose={() => {
159
+ if (!saving) {
160
+ onclose();
161
+ }
162
+ }}
163
+ width={cssLength('40vw')}
164
+ minWidth={cssLength('32rem')}
165
+ buttons={[
166
+ {
167
+ label: 'Cancel',
168
+ callback: () => {
169
+ if (!saving) {
170
+ onclose();
171
+ }
172
+ },
173
+ disabled: saving,
174
+ },
175
+ {
176
+ label: saving ? 'Saving…' : 'Save',
177
+ callback: () => {
178
+ void save();
179
+ },
180
+ colourSet: ColourSet.Primary,
181
+ disabled: saving || !dirty || allUsers === null,
182
+ },
183
+ ]}
184
+ >
185
+ <div class="share-body">
186
+ {#if errorMessage}
187
+ <div
188
+ class="error-banner"
189
+ role="alert"
190
+ >
191
+ <Icon
192
+ name="circle-exclamation"
193
+ size={Size.Small}
194
+ />
195
+ <span>{errorMessage}</span>
196
+ </div>
197
+ {/if}
198
+
199
+ <div class="picker">
200
+ {#if loadingUsers && allUsers === null}
201
+ <InfoAlert>Loading users…</InfoAlert>
202
+ {:else if allUsers !== null}
203
+ <MultiSelect
204
+ label="Users"
205
+ name="User"
206
+ options={allUsers as Array<DatatableViewUser>}
207
+ labelProp="displayName"
208
+ valueProp="id"
209
+ valueAsObject={true}
210
+ selectedValues={selected}
211
+ onchange={(values) => {
212
+ selected = values;
213
+ }}
214
+ />
215
+ {/if}
216
+ </div>
217
+
218
+ {#if selected.length === 0}
219
+ <InfoAlert>
220
+ No users selected. Use the picker above to share this {view.kind === 'filter'
221
+ ? 'filter'
222
+ : 'layout'} with one or more users.
223
+ </InfoAlert>
224
+ {:else}
225
+ <ul class="user-card-list">
226
+ {#each selected as user (user.id)}
227
+ <li class="user-card">
228
+ <div class="user-info">
229
+ <Icon
230
+ name="user-3"
231
+ size={Size.Medium}
232
+ />
233
+ <span class="user-name">{user.displayName}</span>
234
+ </div>
235
+ <div class="user-meta">
236
+ <!--
237
+ v1 sharing exposes a single role - subscriber - so
238
+ the "permission" surface is informational only. The
239
+ backend's `editor` / `systemOwned` flags are
240
+ reserved no-ops in v1 (see `DatatableView` DTO
241
+ JSDoc); scoria never sets them.
242
+ -->
243
+ <span class="permission-pill">Subscriber</span>
244
+ <Button
245
+ variant={Variant.Secondary}
246
+ size={Size.MediumSmall}
247
+ nowrap
248
+ iconLeft={{ name: 'circle-cross', size: Size.Small }}
249
+ disabled={saving}
250
+ onclick={() => {
251
+ removeFromSelection(user.id);
252
+ }}>Remove</Button
253
+ >
254
+ </div>
255
+ </li>
256
+ {/each}
257
+ </ul>
258
+ {/if}
259
+ </div>
260
+ </DesktopModal>
261
+
291
262
  <style>.share-body {
292
263
  display: flex;
293
264
  flex-flow: column nowrap;
@@ -370,4 +341,4 @@
370
341
  border-radius: 999px;
371
342
  background-color: #e9ecf1;
372
343
  color: #3a4952;
373
- }</style>
344
+ }</style>
@@ -1,37 +1,34 @@
1
- <svelte:options runes />
2
-
3
- <script
4
- lang="ts"
5
- module
6
- >
7
- import { quintOut } from 'svelte/easing';
8
- import { slide } from 'svelte/transition';
9
- import { Delay } from '../../../Delay/Delay.js';
10
- import { usePrefersReducedMotion } from '../../../Helpers/prefersReducedMotion.svelte.js';
11
- import { ToolbarPosition } from '../Types/Toolbar/ToolbarPosition.js';
12
- import type { SidePanelProps } from './SidePanelProps.js';
13
- </script>
14
-
15
- <script lang="ts">
16
- const { toolbarPosition, onkeydown, ariaLabel, children }: SidePanelProps = $props();
17
- const reducedMotion = usePrefersReducedMotion();
18
- const slideDuration = $derived(reducedMotion.matches ? 0 : Delay.Default);
19
- </script>
20
-
21
- <!-- svelte-ignore a11y_no_static_element_interactions -->
22
- <div
23
- class="side-panel"
24
- class:left={toolbarPosition === ToolbarPosition.Left}
25
- class:right={toolbarPosition === ToolbarPosition.Right}
26
- aria-label={ariaLabel}
27
- {onkeydown}
28
- transition:slide={{ duration: slideDuration, easing: quintOut, axis: 'x' }}
29
- >
30
- <div class="side-content">
31
- {#if children}{@render children()}{/if}
32
- </div>
33
- </div>
34
-
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ module
6
+ >import { quintOut } from "svelte/easing";
7
+ import { slide } from "svelte/transition";
8
+ import { Delay } from "../../../Delay/Delay.js";
9
+ import { usePrefersReducedMotion } from "../../../Helpers/prefersReducedMotion.svelte.js";
10
+ import { ToolbarPosition } from "../Types/Toolbar/ToolbarPosition.js";
11
+ </script>
12
+
13
+ <script lang="ts">const { toolbarPosition, onkeydown, ariaLabel, children } = $props();
14
+ const reducedMotion = usePrefersReducedMotion();
15
+ const slideDuration = $derived(reducedMotion.matches ? 0 : Delay.Default);
16
+ </script>
17
+
18
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
19
+ <div
20
+ class="side-panel"
21
+ class:left={toolbarPosition === ToolbarPosition.Left}
22
+ class:right={toolbarPosition === ToolbarPosition.Right}
23
+ aria-label={ariaLabel}
24
+ {onkeydown}
25
+ transition:slide={{ duration: slideDuration, easing: quintOut, axis: 'x' }}
26
+ >
27
+ <div class="side-content">
28
+ {#if children}{@render children()}{/if}
29
+ </div>
30
+ </div>
31
+
35
32
  <style>.side-panel {
36
33
  display: flex;
37
34
  flex-shrink: 0;
@@ -51,4 +48,4 @@
51
48
  flex-flow: column nowrap;
52
49
  flex: 1;
53
50
  width: 400px;
54
- }</style>
51
+ }</style>