@lavalogic/scoria 0.39.2 → 0.40.0

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 (141) hide show
  1. package/README.md +58 -58
  2. package/dist/Components/AccordionGroup.svelte +46 -46
  3. package/dist/Components/AccordionGroupButton.svelte +76 -76
  4. package/dist/Components/Action.svelte +136 -136
  5. package/dist/Components/AlertModal.svelte +195 -195
  6. package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +729 -729
  7. package/dist/Components/Base/BaseModal.svelte +125 -125
  8. package/dist/Components/Base/ContextWrapper.svelte +72 -72
  9. package/dist/Components/Base/Pagination.svelte +198 -198
  10. package/dist/Components/Base/Snippets.svelte +36 -36
  11. package/dist/Components/BigRadioSet.svelte +38 -38
  12. package/dist/Components/Bubble.svelte +101 -101
  13. package/dist/Components/Button.svelte +209 -209
  14. package/dist/Components/Checkbox.svelte +86 -86
  15. package/dist/Components/CollapsibleCard.svelte +78 -78
  16. package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +212 -0
  17. package/dist/Components/ConditionBuilder/ConditionBuilder.svelte.d.ts +25 -0
  18. package/dist/Components/ConditionBuilder/ConditionBuilderProps.d.ts +45 -0
  19. package/dist/Components/ConditionBuilder/ConditionBuilderProps.js +1 -0
  20. package/dist/Components/ConditionBuilder/ConditionTree.d.ts +27 -0
  21. package/dist/Components/ConditionBuilder/ConditionTree.js +9 -0
  22. package/dist/Components/Contexts/ContextMenu.svelte +164 -164
  23. package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
  24. package/dist/Components/Contexts/ContextMenuOption.svelte +60 -60
  25. package/dist/Components/Contexts/Toast.svelte +86 -86
  26. package/dist/Components/Contexts/ToastContainer.svelte +65 -65
  27. package/dist/Components/Contexts/Tooltip.svelte +37 -37
  28. package/dist/Components/DataMatrixIcon.svelte +112 -112
  29. package/dist/Components/DateInput.svelte +148 -148
  30. package/dist/Components/DatePicker.svelte +300 -300
  31. package/dist/Components/DesktopModal.svelte +90 -90
  32. package/dist/Components/Dial.svelte +204 -204
  33. package/dist/Components/DimensionInput.svelte +170 -170
  34. package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -119
  35. package/dist/Components/DragMenu/DraggableCard.svelte +77 -77
  36. package/dist/Components/FileCard.svelte +24 -24
  37. package/dist/Components/FileUpload.svelte +236 -236
  38. package/dist/Components/GridInput.svelte +196 -196
  39. package/dist/Components/GridInputSet.svelte +29 -29
  40. package/dist/Components/HorizontalTabGroup.svelte +82 -82
  41. package/dist/Components/HorizontalTabGroupButton.svelte +44 -44
  42. package/dist/Components/Icon.svelte +128 -128
  43. package/dist/Components/Icons.js +357 -357
  44. package/dist/Components/InfoAlert.svelte +35 -35
  45. package/dist/Components/LoadingAnimation.svelte +29 -29
  46. package/dist/Components/LoadingModal.svelte +45 -45
  47. package/dist/Components/LoadingOverlay.svelte +25 -25
  48. package/dist/Components/Modal.svelte +258 -258
  49. package/dist/Components/MultiSelect.svelte +220 -220
  50. package/dist/Components/Nav/Nav.svelte +110 -110
  51. package/dist/Components/Nav/NavServiceMenuList.svelte +43 -43
  52. package/dist/Components/Nav/NavTab.svelte +62 -62
  53. package/dist/Components/Nav/ServiceMenuItem.svelte +112 -112
  54. package/dist/Components/Nav/ServicesNav.svelte +80 -80
  55. package/dist/Components/NumberInput.svelte +157 -157
  56. package/dist/Components/PageHeading.svelte +25 -25
  57. package/dist/Components/PasswordInput.svelte +88 -88
  58. package/dist/Components/Portal.svelte +36 -36
  59. package/dist/Components/ProgressBubble.svelte +107 -107
  60. package/dist/Components/QuerySelect.svelte +366 -366
  61. package/dist/Components/SingleSelect.svelte +250 -250
  62. package/dist/Components/StepButton.svelte +70 -70
  63. package/dist/Components/StepForm.svelte +140 -140
  64. package/dist/Components/Switch.svelte +69 -69
  65. package/dist/Components/Table/ARCHITECTURE.md +298 -298
  66. package/dist/Components/Table/Body/QuickSearchCell.svelte +679 -679
  67. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +51 -51
  68. package/dist/Components/Table/Body/Rows/ColumnList.svelte +59 -59
  69. package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +454 -454
  70. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -79
  71. package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +185 -185
  72. package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +124 -124
  73. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +120 -120
  74. package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +234 -234
  75. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +105 -105
  76. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -87
  77. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +126 -126
  78. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -159
  79. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +246 -246
  80. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +216 -216
  81. package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +252 -252
  82. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +179 -179
  83. package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +221 -221
  84. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +197 -197
  85. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +190 -190
  86. package/dist/Components/Table/Body/Rows/TableRow.svelte +290 -290
  87. package/dist/Components/Table/Body/TableBody.svelte +155 -155
  88. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -41
  89. package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -30
  90. package/dist/Components/Table/Headers/TableHead.svelte +415 -415
  91. package/dist/Components/Table/Misc/ColumnPanel.svelte +39 -39
  92. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +39 -39
  93. package/dist/Components/Table/Misc/FilterInput.svelte +641 -641
  94. package/dist/Components/Table/Misc/FilterPanel.svelte +190 -190
  95. package/dist/Components/Table/Misc/FilterPanelModal.svelte +31 -31
  96. package/dist/Components/Table/Misc/ShareViewModal.svelte +291 -291
  97. package/dist/Components/Table/Misc/SidePanel.svelte +35 -35
  98. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +644 -644
  99. package/dist/Components/Table/Misc/TableFooter.svelte +340 -340
  100. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +140 -140
  101. package/dist/Components/Table/Misc/TableSidebar.svelte +200 -200
  102. package/dist/Components/Table/Misc/TableViewDropdown.svelte +105 -105
  103. package/dist/Components/Table/Misc/ToolboxPanel.svelte +100 -100
  104. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -107
  105. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +192 -192
  106. package/dist/Components/Table/NestedTable.svelte +130 -130
  107. package/dist/Components/Table/Table.svelte +692 -692
  108. package/dist/Components/Test/TestContextConsumer.svelte +13 -13
  109. package/dist/Components/TextArea.svelte +130 -130
  110. package/dist/Components/TextInput.svelte +176 -176
  111. package/dist/Components/ThreeStateRadio.svelte +131 -131
  112. package/dist/Components/Touch/BoolCard.svelte +45 -45
  113. package/dist/Components/Touch/DateModal.svelte +209 -209
  114. package/dist/Components/Touch/EditCard.svelte +59 -59
  115. package/dist/Components/Touch/InfoCard.svelte +31 -31
  116. package/dist/Components/Touch/InfoTextCard.svelte +28 -28
  117. package/dist/Components/Touch/ModalExplanation.svelte +17 -17
  118. package/dist/Components/Touch/NumberKeyboard.svelte +225 -225
  119. package/dist/Components/Touch/NumberModal.svelte +79 -79
  120. package/dist/Components/Touch/PlusMinusCard.svelte +46 -46
  121. package/dist/Components/Touch/SelectModal.svelte +127 -127
  122. package/dist/Components/Touch/SummaryCard.svelte +93 -93
  123. package/dist/Components/Touch/TextAreaModal.svelte +95 -95
  124. package/dist/Components/Touch/TextModal.svelte +106 -106
  125. package/dist/Components/Touch/TouchCard.svelte +33 -33
  126. package/dist/Components/Touch/TouchModal.svelte +274 -274
  127. package/dist/Components/Touch/UtilityButton.svelte +109 -109
  128. package/dist/Components/VerticalTabGroup.svelte +79 -79
  129. package/dist/Components/VerticalTabGroupButton.svelte +58 -58
  130. package/dist/Helpers/Datamatrix.d.ts +19 -19
  131. package/dist/Helpers/Datamatrix.js +291 -291
  132. package/dist/Types/Examples/ExampleModal.svelte +35 -35
  133. package/dist/index.d.ts +3 -0
  134. package/dist/index.js +2 -0
  135. package/dist/scss/_basics.scss +12 -12
  136. package/dist/scss/_colours.scss +134 -134
  137. package/dist/scss/_mixins.scss +3392 -3392
  138. package/dist/scss/_motion.scss +57 -57
  139. package/dist/scss/_sizing.scss +85 -85
  140. package/dist/scss/_transitions.scss +8 -8
  141. package/package.json +1 -1
@@ -1,292 +1,292 @@
1
- <svelte:options runes />
2
-
3
- <script
4
- lang="ts"
5
- generics="T extends object, RowIdType extends Primitive"
6
- >
7
- import type { ScoriaStorageKey } from '../../../../Types/Internal/Misc.js';
8
- import { dev } from '$app/environment';
9
- import { devCatch, generateWeakId } from '../../../../Helpers/Helpers.svelte.js';
10
- import { getContext, untrack } from 'svelte';
11
- import NestedTable from '../../NestedTable.svelte';
12
- import type { ExpandDef } from '../../Types/Columns/Definitions/Expand/ExpandDef.svelte.js';
13
- import type { DefsWrapper } from '../../Types/Columns/DefsWrapper.js';
14
- import type { TableCell } from '../../Types/Columns/TableCell.js';
15
- import { TableContext } from '../../Types/Context/TableContext.svelte.js';
16
- import { type Primitive } from '../../Types/Context/TableInitOptions.js';
17
- import ColumnHighlight from './ColumnHighlight.svelte';
18
- import TableColumn from './Columns/TableColumn.svelte';
19
- import type { TableRowProps } from './TableRowProps.js';
20
-
21
- const tableContext = getContext<TableContext<T, RowIdType>>(TableContext.identifier);
22
-
23
- const { row, odd = false, includeHighlight }: TableRowProps<T> = $props();
24
-
25
- const hoverable = $derived(tableContext.changeColourOnHover);
26
-
27
- // Visible cells in display order: left-pinned -> centre -> right-pinned.
28
- // The browser now handles row-height alignment natively because all
29
- // cells live in the same grid row, so the previous per-side fan-out
30
- // and height-reconciliation pool is gone.
31
- const leftCells: Array<TableCell<T>> = $derived(row.getLeftVisibleCells());
32
- const centreCells: Array<TableCell<T>> = $derived(row.getCenterVisibleCells());
33
- const rightCells: Array<TableCell<T>> = $derived(row.getRightVisibleCells());
34
-
35
- // Cumulative pinning offsets for the sticky-positioned cells. Reads
36
- // rendered widths from `_columnRenderedWidths` (populated by the
37
- // ResizeObserver in TableHead) so the sticky positions stay
38
- // accurate even though each pinned column track uses
39
- // `minmax(W, 1fr)` and the actual width can exceed `columnDef.width`.
40
- function renderedWidth(cell: TableCell<T>): number {
41
- return tableContext._columnRenderedWidths.get(cell.column.id) ?? cell.column.columnDef.width;
42
- }
43
-
44
- const leftOffsets: Array<number> = $derived.by(() => {
45
- void tableContext._columnRenderedWidths.size;
46
- const offsets: Array<number> = [];
47
- let cumulative = includeHighlight ? 20 : 0;
48
- for (const cell of leftCells) {
49
- offsets.push(cumulative);
50
- cumulative += renderedWidth(cell);
51
- }
52
- return offsets;
53
- });
54
-
55
- const rightOffsets: Array<number> = $derived.by(() => {
56
- void tableContext._columnRenderedWidths.size;
57
- const offsets = new Array<number>(rightCells.length).fill(0);
58
- let cumulative = 0;
59
- for (let i = rightCells.length - 1; i >= 0; i--) {
60
- offsets[i] = cumulative;
61
- cumulative += renderedWidth(rightCells[i]);
62
- }
63
- return offsets;
64
- });
65
-
66
- let expandedColumnDef: ExpandDef<T, object> | undefined = $state();
67
-
68
- $effect(() => {
69
- if (leftCells.length + centreCells.length + rightCells.length > 0) {
70
- void tableContext.table.data;
71
- void tableContext.paginationRepo?.paginationState.pageIndex;
72
- void tableContext.paginationRepo?.paginationState.pageSize;
73
- untrack(() => {
74
- expandedColumnDef = undefined;
75
- });
76
- }
77
- });
78
-
79
- let innerDataPromise: Array<object> | Promise<Array<object>> | undefined = $state();
80
- let innerColumnDefs: DefsWrapper<object> = $state({ defs: [] });
81
- let innerTableName: ScoriaStorageKey | undefined = $state();
82
-
83
- let prevExpandedColumnDef: ExpandDef<T, object> | undefined = $state();
84
-
85
- const firstCell = $derived(leftCells.at(0) ?? centreCells.at(0) ?? rightCells.at(0));
86
- const rowIndex = $derived(firstCell?.row.visibleIndex);
87
- let id = $state<RowIdType | undefined>();
88
- const highlighted = $derived(id != undefined && tableContext.highlightedItems.has(id));
89
-
90
- $effect(() => {
91
- void row.original;
92
- void tableContext.selectionExtractor;
93
-
94
- if (rowIndex != undefined) {
95
- const maybePromise = tableContext.selectionExtractor(row.original);
96
- if (maybePromise instanceof Promise) {
97
- maybePromise
98
- .then((it) => {
99
- id = it;
100
- })
101
- .catch(devCatch);
102
- } else {
103
- id = maybePromise;
104
- }
105
- } else {
106
- id = undefined;
107
- }
108
- });
109
-
110
- const showHighlightButton = $derived(rowIndex != null && includeHighlight);
111
-
112
- $effect(() => {
113
- void expandedColumnDef;
114
-
115
- untrack(() => {
116
- // Guard: if all columns have been hidden the cells arrays are
117
- // empty; `firstCell` would otherwise be undefined.
118
- if (!firstCell) {
119
- return;
120
- }
121
- if (expandedColumnDef && expandedColumnDef.id != prevExpandedColumnDef?.id) {
122
- prevExpandedColumnDef = expandedColumnDef;
123
- innerColumnDefs = expandedColumnDef.getInnerColumnDefs(
124
- firstCell.row.original,
125
- firstCell.row.originalIndex
126
- );
127
-
128
- innerDataPromise = expandedColumnDef.accessorFn(
129
- firstCell.row.original,
130
- firstCell.row.originalIndex
131
- ) as Promise<Array<object>>;
132
- innerTableName = expandedColumnDef.innerTableName;
133
- } else if (
134
- !expandedColumnDef ||
135
- tableContext.columnVisibility[expandedColumnDef.id] === false
136
- ) {
137
- innerTableName = undefined;
138
- expandedColumnDef = prevExpandedColumnDef = innerDataPromise = undefined;
139
- innerColumnDefs = { defs: [] };
140
- }
141
- });
142
- });
143
-
144
- function onExpandChange(columnDef: ExpandDef<T, object>) {
145
- if (expandedColumnDef?.id === columnDef.id) {
146
- expandedColumnDef = undefined;
147
- } else {
148
- expandedColumnDef = columnDef;
149
- }
150
- }
151
-
152
- // Stable fallback name used when an ExpandDef does not supply its own
153
- // `innerTableName`. Generating this once per row prevents localStorage
154
- // preset keys from accumulating UUID-keyed garbage every expand toggle.
155
- const fallbackInnerTableName = generateWeakId<ScoriaStorageKey>();
156
-
157
- /**
158
- * Inline style appended to a cell's `style="..."` when its column opts
159
- * into `wrapValue`. The hooks switch the leaf cell's `white-space`,
160
- * `overflow-x`, and `height` to wrap-friendly values. Implemented as
161
- * CSS-variable overrides so the table-cell-basic mixin in
162
- * `_mixins.scss` consumes them via `var(--cell-* , fallback)`.
163
- */
164
- function cellWrapStyle(cell: TableCell<T>): string {
165
- return cell.column.columnDef.wrapValue
166
- ? '--cell-white-space: normal; --cell-overflow-x: visible; --cell-height: auto; --cell-min-height: var(--height);'
167
- : '';
168
- }
169
- </script>
170
-
171
- <div
172
- role="row"
173
- class="datatable-row"
174
- class:hoverable
175
- class:alt={tableContext.alternateRowColours && odd}
176
- class:highlighted
177
- >
178
- {#if showHighlightButton}
179
- <div
180
- role="cell"
181
- class="cell highlight-cell pinned-left"
182
- style="left: 0px"
183
- >
184
- <ColumnHighlight
185
- rowIndex={rowIndex ??
186
- (() => {
187
- if (dev) {
188
- console.error(`column had invalid row index: ${rowIndex}`);
189
- }
190
- return -1;
191
- })()}
192
- {highlighted}
193
- toggleHighlightedRow={() => {
194
- tableContext.toggleHighlightedRow(row.original)?.catch(devCatch);
195
- }}
196
- />
197
- </div>
198
- {/if}
199
- {#each leftCells as cell, index (cell.column.id)}
200
- <div
201
- role="cell"
202
- class="cell pinned-left"
203
- style="left: {leftOffsets[index]}px; {cellWrapStyle(cell)}"
204
- >
205
- <TableColumn
206
- {cell}
207
- {expandedColumnDef}
208
- {onExpandChange}
209
- />
210
- </div>
211
- {/each}
212
- {#each centreCells as cell (cell.column.id)}
213
- <div
214
- role="cell"
215
- class="cell"
216
- style={cellWrapStyle(cell)}
217
- >
218
- <TableColumn
219
- {cell}
220
- {expandedColumnDef}
221
- {onExpandChange}
222
- />
223
- </div>
224
- {/each}
225
- {#each rightCells as cell, index (cell.column.id)}
226
- <div
227
- role="cell"
228
- class="cell pinned-right"
229
- style="right: {rightOffsets[index]}px; {cellWrapStyle(cell)}"
230
- >
231
- <TableColumn
232
- {cell}
233
- {expandedColumnDef}
234
- {onExpandChange}
235
- />
236
- </div>
237
- {/each}
238
- </div>
239
-
240
- {#if expandedColumnDef}
241
- <div
242
- role="row"
243
- class="datatable-row expand-row"
244
- >
245
- <div
246
- role="cell"
247
- class="cell expand-cell"
248
- >
249
- {#await innerDataPromise}
250
- loading&hellip;
251
- {:then data}
252
- {#if data && data.length}
253
- {@const options = expandedColumnDef.innerTableOptions}
254
- {#if innerTableName}
255
- {#key innerTableName}
256
- <NestedTable
257
- tableName={innerTableName}
258
- {data}
259
- columnDefWrapper={innerColumnDefs}
260
- {options}
261
- getUserId={tableContext.getUserId}
262
- />
263
- {/key}
264
- {:else}
265
- <NestedTable
266
- tableName={fallbackInnerTableName}
267
- {data}
268
- columnDefWrapper={innerColumnDefs}
269
- {options}
270
- getUserId={tableContext.getUserId}
271
- />
272
- {/if}
273
- {:else if !data}
274
- Error Fetching data
275
- {:else}
276
- No Data
277
- {/if}
278
- {:catch e}
279
- {#if dev}
280
- error: {e}
281
- <!-- eslint-disable-next-line svelte/no-at-debug-tags -->
282
- {@debug e}
283
- {/if}
284
- Error Fetching data
285
- {/await}
286
- </div>
287
- </div>
288
- {/if}
289
-
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ generics="T extends object, RowIdType extends Primitive"
6
+ >
7
+ import type { ScoriaStorageKey } from '../../../../Types/Internal/Misc.js';
8
+ import { dev } from '$app/environment';
9
+ import { devCatch, generateWeakId } from '../../../../Helpers/Helpers.svelte.js';
10
+ import { getContext, untrack } from 'svelte';
11
+ import NestedTable from '../../NestedTable.svelte';
12
+ import type { ExpandDef } from '../../Types/Columns/Definitions/Expand/ExpandDef.svelte.js';
13
+ import type { DefsWrapper } from '../../Types/Columns/DefsWrapper.js';
14
+ import type { TableCell } from '../../Types/Columns/TableCell.js';
15
+ import { TableContext } from '../../Types/Context/TableContext.svelte.js';
16
+ import { type Primitive } from '../../Types/Context/TableInitOptions.js';
17
+ import ColumnHighlight from './ColumnHighlight.svelte';
18
+ import TableColumn from './Columns/TableColumn.svelte';
19
+ import type { TableRowProps } from './TableRowProps.js';
20
+
21
+ const tableContext = getContext<TableContext<T, RowIdType>>(TableContext.identifier);
22
+
23
+ const { row, odd = false, includeHighlight }: TableRowProps<T> = $props();
24
+
25
+ const hoverable = $derived(tableContext.changeColourOnHover);
26
+
27
+ // Visible cells in display order: left-pinned -> centre -> right-pinned.
28
+ // The browser now handles row-height alignment natively because all
29
+ // cells live in the same grid row, so the previous per-side fan-out
30
+ // and height-reconciliation pool is gone.
31
+ const leftCells: Array<TableCell<T>> = $derived(row.getLeftVisibleCells());
32
+ const centreCells: Array<TableCell<T>> = $derived(row.getCenterVisibleCells());
33
+ const rightCells: Array<TableCell<T>> = $derived(row.getRightVisibleCells());
34
+
35
+ // Cumulative pinning offsets for the sticky-positioned cells. Reads
36
+ // rendered widths from `_columnRenderedWidths` (populated by the
37
+ // ResizeObserver in TableHead) so the sticky positions stay
38
+ // accurate even though each pinned column track uses
39
+ // `minmax(W, 1fr)` and the actual width can exceed `columnDef.width`.
40
+ function renderedWidth(cell: TableCell<T>): number {
41
+ return tableContext._columnRenderedWidths.get(cell.column.id) ?? cell.column.columnDef.width;
42
+ }
43
+
44
+ const leftOffsets: Array<number> = $derived.by(() => {
45
+ void tableContext._columnRenderedWidths.size;
46
+ const offsets: Array<number> = [];
47
+ let cumulative = includeHighlight ? 20 : 0;
48
+ for (const cell of leftCells) {
49
+ offsets.push(cumulative);
50
+ cumulative += renderedWidth(cell);
51
+ }
52
+ return offsets;
53
+ });
54
+
55
+ const rightOffsets: Array<number> = $derived.by(() => {
56
+ void tableContext._columnRenderedWidths.size;
57
+ const offsets = new Array<number>(rightCells.length).fill(0);
58
+ let cumulative = 0;
59
+ for (let i = rightCells.length - 1; i >= 0; i--) {
60
+ offsets[i] = cumulative;
61
+ cumulative += renderedWidth(rightCells[i]);
62
+ }
63
+ return offsets;
64
+ });
65
+
66
+ let expandedColumnDef: ExpandDef<T, object> | undefined = $state();
67
+
68
+ $effect(() => {
69
+ if (leftCells.length + centreCells.length + rightCells.length > 0) {
70
+ void tableContext.table.data;
71
+ void tableContext.paginationRepo?.paginationState.pageIndex;
72
+ void tableContext.paginationRepo?.paginationState.pageSize;
73
+ untrack(() => {
74
+ expandedColumnDef = undefined;
75
+ });
76
+ }
77
+ });
78
+
79
+ let innerDataPromise: Array<object> | Promise<Array<object>> | undefined = $state();
80
+ let innerColumnDefs: DefsWrapper<object> = $state({ defs: [] });
81
+ let innerTableName: ScoriaStorageKey | undefined = $state();
82
+
83
+ let prevExpandedColumnDef: ExpandDef<T, object> | undefined = $state();
84
+
85
+ const firstCell = $derived(leftCells.at(0) ?? centreCells.at(0) ?? rightCells.at(0));
86
+ const rowIndex = $derived(firstCell?.row.visibleIndex);
87
+ let id = $state<RowIdType | undefined>();
88
+ const highlighted = $derived(id != undefined && tableContext.highlightedItems.has(id));
89
+
90
+ $effect(() => {
91
+ void row.original;
92
+ void tableContext.selectionExtractor;
93
+
94
+ if (rowIndex != undefined) {
95
+ const maybePromise = tableContext.selectionExtractor(row.original);
96
+ if (maybePromise instanceof Promise) {
97
+ maybePromise
98
+ .then((it) => {
99
+ id = it;
100
+ })
101
+ .catch(devCatch);
102
+ } else {
103
+ id = maybePromise;
104
+ }
105
+ } else {
106
+ id = undefined;
107
+ }
108
+ });
109
+
110
+ const showHighlightButton = $derived(rowIndex != null && includeHighlight);
111
+
112
+ $effect(() => {
113
+ void expandedColumnDef;
114
+
115
+ untrack(() => {
116
+ // Guard: if all columns have been hidden the cells arrays are
117
+ // empty; `firstCell` would otherwise be undefined.
118
+ if (!firstCell) {
119
+ return;
120
+ }
121
+ if (expandedColumnDef && expandedColumnDef.id != prevExpandedColumnDef?.id) {
122
+ prevExpandedColumnDef = expandedColumnDef;
123
+ innerColumnDefs = expandedColumnDef.getInnerColumnDefs(
124
+ firstCell.row.original,
125
+ firstCell.row.originalIndex
126
+ );
127
+
128
+ innerDataPromise = expandedColumnDef.accessorFn(
129
+ firstCell.row.original,
130
+ firstCell.row.originalIndex
131
+ ) as Promise<Array<object>>;
132
+ innerTableName = expandedColumnDef.innerTableName;
133
+ } else if (
134
+ !expandedColumnDef ||
135
+ tableContext.columnVisibility[expandedColumnDef.id] === false
136
+ ) {
137
+ innerTableName = undefined;
138
+ expandedColumnDef = prevExpandedColumnDef = innerDataPromise = undefined;
139
+ innerColumnDefs = { defs: [] };
140
+ }
141
+ });
142
+ });
143
+
144
+ function onExpandChange(columnDef: ExpandDef<T, object>) {
145
+ if (expandedColumnDef?.id === columnDef.id) {
146
+ expandedColumnDef = undefined;
147
+ } else {
148
+ expandedColumnDef = columnDef;
149
+ }
150
+ }
151
+
152
+ // Stable fallback name used when an ExpandDef does not supply its own
153
+ // `innerTableName`. Generating this once per row prevents localStorage
154
+ // preset keys from accumulating UUID-keyed garbage every expand toggle.
155
+ const fallbackInnerTableName = generateWeakId<ScoriaStorageKey>();
156
+
157
+ /**
158
+ * Inline style appended to a cell's `style="..."` when its column opts
159
+ * into `wrapValue`. The hooks switch the leaf cell's `white-space`,
160
+ * `overflow-x`, and `height` to wrap-friendly values. Implemented as
161
+ * CSS-variable overrides so the table-cell-basic mixin in
162
+ * `_mixins.scss` consumes them via `var(--cell-* , fallback)`.
163
+ */
164
+ function cellWrapStyle(cell: TableCell<T>): string {
165
+ return cell.column.columnDef.wrapValue
166
+ ? '--cell-white-space: normal; --cell-overflow-x: visible; --cell-height: auto; --cell-min-height: var(--height);'
167
+ : '';
168
+ }
169
+ </script>
170
+
171
+ <div
172
+ role="row"
173
+ class="datatable-row"
174
+ class:hoverable
175
+ class:alt={tableContext.alternateRowColours && odd}
176
+ class:highlighted
177
+ >
178
+ {#if showHighlightButton}
179
+ <div
180
+ role="cell"
181
+ class="cell highlight-cell pinned-left"
182
+ style="left: 0px"
183
+ >
184
+ <ColumnHighlight
185
+ rowIndex={rowIndex ??
186
+ (() => {
187
+ if (dev) {
188
+ console.error(`column had invalid row index: ${rowIndex}`);
189
+ }
190
+ return -1;
191
+ })()}
192
+ {highlighted}
193
+ toggleHighlightedRow={() => {
194
+ tableContext.toggleHighlightedRow(row.original)?.catch(devCatch);
195
+ }}
196
+ />
197
+ </div>
198
+ {/if}
199
+ {#each leftCells as cell, index (cell.column.id)}
200
+ <div
201
+ role="cell"
202
+ class="cell pinned-left"
203
+ style="left: {leftOffsets[index]}px; {cellWrapStyle(cell)}"
204
+ >
205
+ <TableColumn
206
+ {cell}
207
+ {expandedColumnDef}
208
+ {onExpandChange}
209
+ />
210
+ </div>
211
+ {/each}
212
+ {#each centreCells as cell (cell.column.id)}
213
+ <div
214
+ role="cell"
215
+ class="cell"
216
+ style={cellWrapStyle(cell)}
217
+ >
218
+ <TableColumn
219
+ {cell}
220
+ {expandedColumnDef}
221
+ {onExpandChange}
222
+ />
223
+ </div>
224
+ {/each}
225
+ {#each rightCells as cell, index (cell.column.id)}
226
+ <div
227
+ role="cell"
228
+ class="cell pinned-right"
229
+ style="right: {rightOffsets[index]}px; {cellWrapStyle(cell)}"
230
+ >
231
+ <TableColumn
232
+ {cell}
233
+ {expandedColumnDef}
234
+ {onExpandChange}
235
+ />
236
+ </div>
237
+ {/each}
238
+ </div>
239
+
240
+ {#if expandedColumnDef}
241
+ <div
242
+ role="row"
243
+ class="datatable-row expand-row"
244
+ >
245
+ <div
246
+ role="cell"
247
+ class="cell expand-cell"
248
+ >
249
+ {#await innerDataPromise}
250
+ loading&hellip;
251
+ {:then data}
252
+ {#if data && data.length}
253
+ {@const options = expandedColumnDef.innerTableOptions}
254
+ {#if innerTableName}
255
+ {#key innerTableName}
256
+ <NestedTable
257
+ tableName={innerTableName}
258
+ {data}
259
+ columnDefWrapper={innerColumnDefs}
260
+ {options}
261
+ getUserId={tableContext.getUserId}
262
+ />
263
+ {/key}
264
+ {:else}
265
+ <NestedTable
266
+ tableName={fallbackInnerTableName}
267
+ {data}
268
+ columnDefWrapper={innerColumnDefs}
269
+ {options}
270
+ getUserId={tableContext.getUserId}
271
+ />
272
+ {/if}
273
+ {:else if !data}
274
+ Error Fetching data
275
+ {:else}
276
+ No Data
277
+ {/if}
278
+ {:catch e}
279
+ {#if dev}
280
+ error: {e}
281
+ <!-- eslint-disable-next-line svelte/no-at-debug-tags -->
282
+ {@debug e}
283
+ {/if}
284
+ Error Fetching data
285
+ {/await}
286
+ </div>
287
+ </div>
288
+ {/if}
289
+
290
290
  <style>.datatable-row {
291
291
  display: grid;
292
292
  grid-template-columns: var(--cols);
@@ -351,4 +351,4 @@
351
351
  padding: 0;
352
352
  display: block;
353
353
  width: 100%;
354
- }</style>
354
+ }</style>