@lavalogic/scoria 0.33.0 → 0.35.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 (130) hide show
  1. package/README.md +58 -58
  2. package/dist/Components/AccordionGroup.svelte +44 -63
  3. package/dist/Components/AccordionGroup.svelte.d.ts +2 -25
  4. package/dist/Components/AccordionGroupButton.svelte +106 -114
  5. package/dist/Components/AccordionGroupButton.svelte.d.ts +2 -2
  6. package/dist/Components/AccordionGroupButtonProps.d.ts +21 -0
  7. package/dist/Components/AccordionGroupButtonProps.js +1 -0
  8. package/dist/Components/Action.svelte +138 -138
  9. package/dist/Components/AlertModal.svelte +125 -125
  10. package/dist/Components/Base/ContextWrapper.svelte +50 -50
  11. package/dist/Components/Base/Pagination.svelte +190 -190
  12. package/dist/Components/Base/Snippets.svelte +29 -29
  13. package/dist/Components/BigRadioSet.svelte +29 -29
  14. package/dist/Components/Bubble.svelte +77 -77
  15. package/dist/Components/Button.svelte +197 -197
  16. package/dist/Components/Checkbox.svelte +68 -68
  17. package/dist/Components/CollapsibleCard.svelte +70 -70
  18. package/dist/Components/Contexts/ContextMenu.svelte +157 -157
  19. package/dist/Components/Contexts/ContextMenuDivider.svelte +11 -11
  20. package/dist/Components/Contexts/ContextMenuOption.svelte +52 -52
  21. package/dist/Components/Contexts/Toast.svelte +61 -61
  22. package/dist/Components/Contexts/ToastContainer.svelte +46 -46
  23. package/dist/Components/Contexts/Tooltip.svelte +26 -26
  24. package/dist/Components/DataMatrixIcon.svelte +41 -41
  25. package/dist/Components/DateInput.svelte +109 -109
  26. package/dist/Components/DatePicker.svelte +312 -312
  27. package/dist/Components/DesktopModal.svelte +106 -106
  28. package/dist/Components/Dial.svelte +151 -151
  29. package/dist/Components/DimensionInput.svelte +184 -184
  30. package/dist/Components/DragMenu/DragMenuHolder.svelte +106 -106
  31. package/dist/Components/DragMenu/DraggableCard.svelte +41 -41
  32. package/dist/Components/FileCard.svelte +20 -20
  33. package/dist/Components/FileUpload.svelte +196 -196
  34. package/dist/Components/GridInput.svelte +190 -190
  35. package/dist/Components/GridInputSet.svelte +22 -22
  36. package/dist/Components/HorizontalTabGroup.svelte +78 -78
  37. package/dist/Components/HorizontalTabGroupButton.svelte +96 -96
  38. package/dist/Components/Icon.svelte +97 -97
  39. package/dist/Components/Icons.js +357 -357
  40. package/dist/Components/LoadingAnimation.svelte +32 -32
  41. package/dist/Components/LoadingModal.svelte +47 -47
  42. package/dist/Components/LoadingOverlay.svelte +27 -27
  43. package/dist/Components/Modal.svelte +251 -251
  44. package/dist/Components/MultiSelect.svelte +197 -197
  45. package/dist/Components/Nav/Nav.svelte +97 -97
  46. package/dist/Components/Nav/NavServiceMenuList.svelte +34 -34
  47. package/dist/Components/Nav/NavTab.svelte +91 -91
  48. package/dist/Components/Nav/ServiceMenuItem.svelte +91 -91
  49. package/dist/Components/Nav/ServicesNav.svelte +53 -53
  50. package/dist/Components/NumberInput.svelte +144 -144
  51. package/dist/Components/PageHeading.svelte +36 -36
  52. package/dist/Components/PasswordInput.svelte +73 -73
  53. package/dist/Components/Portal.svelte +25 -25
  54. package/dist/Components/ProgressBubble.svelte +80 -80
  55. package/dist/Components/QuerySelect.svelte +411 -411
  56. package/dist/Components/SingleSelect.svelte +261 -261
  57. package/dist/Components/StepButton.svelte +120 -120
  58. package/dist/Components/StepForm.svelte +108 -108
  59. package/dist/Components/Switch.svelte +36 -36
  60. package/dist/Components/Table/Body/QuickSearchCell.svelte +632 -632
  61. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +30 -30
  62. package/dist/Components/Table/Body/Rows/ColumnList.svelte +56 -56
  63. package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +179 -179
  64. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +49 -49
  65. package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +180 -180
  66. package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +102 -102
  67. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +119 -119
  68. package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +114 -114
  69. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +96 -96
  70. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +72 -72
  71. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +131 -131
  72. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +144 -144
  73. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +238 -238
  74. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +358 -358
  75. package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +338 -338
  76. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +183 -183
  77. package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +309 -309
  78. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +369 -369
  79. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +170 -170
  80. package/dist/Components/Table/Body/Rows/TableRow.svelte +259 -259
  81. package/dist/Components/Table/Body/TableBody.svelte +67 -67
  82. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +39 -39
  83. package/dist/Components/Table/Headers/SelectAllHeader.svelte +28 -28
  84. package/dist/Components/Table/Headers/TableHead.svelte +260 -260
  85. package/dist/Components/Table/Misc/ColumnPanel.svelte +78 -78
  86. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +99 -99
  87. package/dist/Components/Table/Misc/FilterInput.svelte +556 -556
  88. package/dist/Components/Table/Misc/FilterPanel.svelte +46 -46
  89. package/dist/Components/Table/Misc/FilterPanelModal.svelte +67 -67
  90. package/dist/Components/Table/Misc/SidePanel.svelte +32 -32
  91. package/dist/Components/Table/Misc/TableFooter.svelte +304 -304
  92. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +156 -156
  93. package/dist/Components/Table/Misc/TableSidebar.svelte +176 -176
  94. package/dist/Components/Table/Misc/ToolboxPanel.svelte +77 -77
  95. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +91 -91
  96. package/dist/Components/Table/NestedTable.svelte +84 -84
  97. package/dist/Components/Table/Table.svelte +492 -492
  98. package/dist/Components/Test/TestContextConsumer.svelte +13 -13
  99. package/dist/Components/TextArea.svelte +108 -108
  100. package/dist/Components/TextInput.svelte +147 -147
  101. package/dist/Components/ThreeStateRadio.svelte +124 -124
  102. package/dist/Components/Touch/BoolCard.svelte +52 -52
  103. package/dist/Components/Touch/DateModal.svelte +217 -217
  104. package/dist/Components/Touch/EditCard.svelte +52 -52
  105. package/dist/Components/Touch/InfoCard.svelte +24 -24
  106. package/dist/Components/Touch/InfoTextCard.svelte +21 -21
  107. package/dist/Components/Touch/ModalExplanation.svelte +20 -20
  108. package/dist/Components/Touch/NumberKeyboard.svelte +268 -268
  109. package/dist/Components/Touch/NumberModal.svelte +69 -69
  110. package/dist/Components/Touch/PlusMinusCard.svelte +50 -50
  111. package/dist/Components/Touch/SelectModal.svelte +105 -105
  112. package/dist/Components/Touch/SummaryCard.svelte +65 -65
  113. package/dist/Components/Touch/TextAreaModal.svelte +86 -86
  114. package/dist/Components/Touch/TextModal.svelte +123 -123
  115. package/dist/Components/Touch/TouchCard.svelte +28 -28
  116. package/dist/Components/Touch/TouchModal.svelte +259 -259
  117. package/dist/Components/Touch/UtilityButton.svelte +91 -91
  118. package/dist/Components/VerticalTabGroup.svelte +66 -66
  119. package/dist/Components/VerticalTabGroupButton.svelte +97 -97
  120. package/dist/Helpers/Datamatrix.d.ts +10 -10
  121. package/dist/Helpers/Datamatrix.js +1 -1
  122. package/dist/Helpers/Helpers.svelte.js +6 -4
  123. package/dist/Types/Examples/ExampleModal.svelte +34 -34
  124. package/dist/index.d.ts +1 -0
  125. package/dist/scss/_basics.scss +12 -12
  126. package/dist/scss/_colours.scss +134 -134
  127. package/dist/scss/_mixins.scss +3016 -3016
  128. package/dist/scss/_sizing.scss +69 -69
  129. package/dist/scss/_transitions.scss +8 -8
  130. package/package.json +2 -2
@@ -1,494 +1,494 @@
1
- <svelte:options runes />
2
-
3
- <script
4
- lang="ts"
5
- module
6
- >
7
- import {
8
- ContextMenuContext,
9
- type IContextMenuOption,
10
- } from '../../Contexts/ContextMenuContext.svelte';
11
- import { devCatch, refWrapper } from '../../Helpers/Helpers.svelte';
12
- import type { RefWrapper } from '../../Types/Internal/RefWrapper.js';
13
- import { getContext, onMount, setContext, tick, untrack } from 'svelte';
14
- import LoadingOverlay from '../LoadingOverlay.svelte';
15
- import type { HeightData } from './Body/HeightData.js';
16
- import { shownPopup } from './Body/Rows/Columns/ActionsCell.svelte';
17
- import TableBody from './Body/TableBody.svelte';
18
- import TableHead from './Headers/TableHead.svelte';
19
- import TableFooter from './Misc/TableFooter.svelte';
20
- import TableHorizontalBar from './Misc/TableHorizontalBar.svelte';
21
- import TableSidebar from './Misc/TableSidebar.svelte';
22
- import { TableContext } from './Types/Context/TableContext.svelte';
23
- import type { Primitive } from './Types/Context/TableInitOptions.js';
24
- import { PaginationType } from './Types/Pagination/PaginationType.js';
25
- import { ToolbarPosition } from './Types/Toolbar/ToolbarPosition.js';
26
-
27
- declare global {
28
- interface HTMLElementEventMap {
29
- edit: CustomEvent<{
30
- selectAll: boolean;
31
- }>;
32
- }
33
- }
34
-
35
- export interface TableProps<T extends object, IdType extends Primitive> {
36
- readonly tableContext: TableContext<T, IdType>;
37
- nested?: boolean;
38
- }
39
-
40
- export const quickFilterContextIdentifier: symbol = Symbol();
41
-
42
- const openedQuickFilterSelect = refWrapper(null);
43
- </script>
44
-
45
- <script
46
- lang="ts"
47
- generics="T extends object,IdType extends Primitive"
48
- >
49
- const { nested = false, tableContext }: TableProps<T, IdType> = $props();
50
-
51
- // NOTE: known limitation. This is passed as a wrapper, and they should never require reassignment. It is deeply reactive
52
- // svelte-ignore state_referenced_locally
53
- setContext(TableContext.identifier, tableContext);
54
-
55
- const { openContextMenu: _openContextMenu } = getContext<ContextMenuContext>(
56
- ContextMenuContext.identifier
57
- );
58
-
59
- let ref: HTMLDivElement | undefined = $state();
60
- let unfocusRef: HTMLDivElement | undefined = $state();
61
-
62
- setContext(quickFilterContextIdentifier, openedQuickFilterSelect);
63
-
64
- $effect(() => {
65
- void tableContext.focusStart;
66
- void ref;
67
- untrack(() => {
68
- if (tableContext.focusStart && ref) {
69
- const { row, column: index } = tableContext.focusStart;
70
- let element = ref.querySelector(
71
- `#${tableContext.tableName}-cell-${row}-${tableContext.columnDefs[index].id}`
72
- ) as HTMLElement | undefined;
73
-
74
- if (element?.tabIndex == -1) {
75
- const td = element;
76
- element = td.querySelector('button') as HTMLElement | undefined;
77
- if (!element) {
78
- element = td.querySelector('input') as HTMLElement | undefined;
79
- }
80
- }
81
-
82
- element?.focus({ preventScroll: true });
83
- element?.scrollIntoView({
84
- behavior: 'instant',
85
- block: 'nearest',
86
- inline: 'nearest',
87
- });
88
- }
89
- });
90
- });
91
-
92
- $effect(() => {
93
- if (tableContext.focusEnd && ref) {
94
- const { row, column: index } = tableContext.focusEnd;
95
- const cell = ref.querySelector(
96
- `#${tableContext.tableName}-cell-${row}-${tableContext.columnDefs[index].id}`
97
- ) as HTMLElement | undefined;
98
-
99
- cell?.scrollIntoView({
100
- behavior: 'instant',
101
- block: 'nearest',
102
- inline: 'nearest',
103
- });
104
- }
105
- });
106
-
107
- function openContextMenu(e: MouseEvent) {
108
- e.stopPropagation();
109
-
110
- if (
111
- (e.target as HTMLElement).tagName === 'INPUT' ||
112
- (e.target as HTMLElement).closest('.select-wrapper') != null
113
- ) {
114
- return;
115
- }
116
-
117
- const clickedElement = document.activeElement?.id.split('-cell-')[1]?.split('-');
118
-
119
- if (clickedElement) {
120
- const [rowString, columnId] = clickedElement;
121
- const row = parseInt(rowString);
122
-
123
- if (
124
- !tableContext.focusedColumnIds.has(columnId) ||
125
- !tableContext.focusedRowIndices.has(row)
126
- ) {
127
- const columnIndex = tableContext.columnDefs.find((it) => it.id === columnId)?.index;
128
-
129
- if (columnIndex) {
130
- tableContext.moveFocus({
131
- column: columnIndex,
132
- row,
133
- id: columnId,
134
- });
135
- }
136
- }
137
- }
138
-
139
- // this may not exist in http contexts
140
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
141
- if (tableContext.allowCopy && window.Clipboard) {
142
- e.preventDefault();
143
-
144
- const options: Array<IContextMenuOption> = [
145
- {
146
- callback: () => {
147
- tableContext.copyDataToClipboard().catch(devCatch);
148
- },
149
- text: 'Copy Selection',
150
- },
151
- ];
152
-
153
- if (tableContext.hasHighlightedItems) {
154
- options.push({
155
- callback: () => {
156
- tableContext.copyHighlightedRowsToClipboard().catch(devCatch);
157
- },
158
- text: 'Copy Highlighted Rows',
159
- });
160
- }
161
-
162
- _openContextMenu(e, options).catch(devCatch);
163
- }
164
- }
165
-
166
- function handleClickOutside(e: MouseEvent) {
167
- const target = e.target as HTMLElement;
168
- if (target.isConnected) {
169
- if (target.closest('div.floating') == null && target.closest('div.stick-right') == null) {
170
- openedQuickFilterSelect.value = null;
171
- }
172
-
173
- if (target.closest('div.slot-wrapper') == null) {
174
- // svelte type gen/eslint miscommunication
175
- (shownPopup as RefWrapper<symbol | undefined>).value = undefined;
176
- }
177
- }
178
- }
179
-
180
- // nested table resizing
181
- let isResizing = $state(false);
182
- let remPxRatio = $state(0);
183
- const minNestedHeight = $derived(20 * remPxRatio);
184
-
185
- // HACK please figure this one out
186
- let nestedHeight = $derived(minNestedHeight);
187
-
188
- let tableRef: HTMLDivElement | undefined = undefined;
189
- let mousedDownEmptySpace = $state(false);
190
-
191
- onMount(() => {
192
- remPxRatio = parseFloat(getComputedStyle(document.documentElement).fontSize);
193
- });
194
-
195
- function addOverrideResizeHandler() {
196
- isResizing = true;
197
- window.addEventListener('mousemove', onResize);
198
- }
199
-
200
- function onResize(e: MouseEvent) {
201
- const sliderHeight = tableRef ? e.clientY - tableRef.getBoundingClientRect().top : 0;
202
- nestedHeight = Math.max(sliderHeight, minNestedHeight);
203
- }
204
-
205
- function removeOverrideResizeHandler() {
206
- isResizing = false;
207
- window.removeEventListener('mousemove', onResize);
208
- }
209
-
210
- const usesLoading = $derived(
211
- tableContext.paginationRepo &&
212
- tableContext.paginationRepo.paginationType === PaginationType.Remote
213
- );
214
-
215
- const shouldUnfocusRemote = $derived(
216
- usesLoading &&
217
- tableContext.paginationRepo?.isLoading === false &&
218
- tableContext.paginationRepo.paginationState.pageIndex !==
219
- tableContext.paginationRepo.previouslyViewedPage
220
- );
221
-
222
- const shouldUnfocusLocal = $derived(
223
- !usesLoading && tableContext.paginationRepo?.paginationState.pageIndex
224
- );
225
-
226
- $effect(() => {
227
- if ((shouldUnfocusRemote || shouldUnfocusLocal) && unfocusRef) {
228
- untrack(() => {
229
- tableContext.unfocus();
230
- });
231
- unfocusRef.scrollTop = 0;
232
- } else if (usesLoading && tableContext.paginationRepo?.isLoading === false) {
233
- // refocus same element after a reload
234
- // this does not break mutifocus
235
- untrack(() => {
236
- const fs = tableContext.focusStart;
237
- if (!fs) {
238
- return;
239
- }
240
- const idToFocusTo = `#${tableContext.tableName}-cell-${fs.row}-${fs.id}`;
241
-
242
- tick()
243
- .then(() => {
244
- if (idToFocusTo) {
245
- (document.querySelector(idToFocusTo) as HTMLTableCellElement | undefined)?.focus();
246
- }
247
- })
248
- .catch(devCatch);
249
- });
250
- }
251
- });
252
-
253
- const leftTotalSize = $derived(tableContext.table.getLeftTotalSize());
254
- const centreTotalSize = $derived(tableContext.table.getCenterTotalSize());
255
- const rightTotalSize = $derived(tableContext.table.getRightTotalSize());
256
-
257
- const leftColumns = $derived(tableContext.table.getLeftVisibleLeafColumns());
258
- const centreColumns = $derived(tableContext.table.getCenterVisibleLeafColumns());
259
- const rightColumns = $derived(tableContext.table.getRightVisibleLeafColumns());
260
-
261
- const appropriateSide = $derived.by(() => {
262
- if (leftColumns.length > 0) {
263
- return 'left';
264
- } else if (rightColumns.length != tableContext.columnDefs.length) {
265
- return 'centre';
266
- } else {
267
- return 'right';
268
- }
269
- });
270
-
271
- type SvelteTableElement = {
272
- rowHeights: RefWrapper<Array<number>>;
273
- };
274
-
275
- let leftTable: SvelteTableElement | undefined = $state();
276
- let centreTable: SvelteTableElement | undefined = $state();
277
- let rightTable: SvelteTableElement | undefined = $state();
278
-
279
- const totalRowSizes: Array<HeightData<T>> = $derived.by(() => {
280
- if (!leftTable || !centreTable || !rightTable) {
281
- return [];
282
- }
283
-
284
- // console.debug(`got refs for all tables`);
285
- const centre = centreTable.rowHeights.value;
286
- const right = rightTable.rowHeights.value;
287
-
288
- return leftTable.rowHeights.value.map((_leftHeight, index) => {
289
- const leftHeight = _leftHeight || 0;
290
- const centreHeight = centre[index] || 0;
291
- const rightHeight = right[index] || 0;
292
-
293
- const maxHeight = Math.max(leftHeight, centreHeight, rightHeight);
294
- const applyTo: HeightData<T>['applyTo'] = [];
295
-
296
- if (leftHeight < maxHeight) {
297
- // console.debug(`left (${leftHeight}) was lower than max (${maxHeight})`);
298
- applyTo.push('left');
299
- }
300
- if (centreHeight < maxHeight) {
301
- // console.debug(`centre (${centreHeight}) was lower than max (${maxHeight})`);
302
- applyTo.push('centre');
303
- }
304
- if (rightHeight < maxHeight) {
305
- // console.debug(`right (${rightHeight}) was lower than max (${maxHeight})`);
306
- applyTo.push('right');
307
- }
308
-
309
- return {
310
- index,
311
- applyTo,
312
- maxHeight,
313
- };
314
- });
315
- });
316
- </script>
317
-
318
- <svelte:window
319
- onmouseup={(e: MouseEvent) => {
320
- if (
321
- // if it's not a click on an item in the context menu
322
- !(e.target as HTMLElement | undefined)?.classList.contains('context-menu-item') &&
323
- // if it's outside the table or inside the empty space of the table
324
- (e.target === unfocusRef ||
325
- ((e.target as HTMLElement | undefined)?.closest('.table-wrapper') == null &&
326
- mousedDownEmptySpace))
327
- ) {
328
- tableContext.unfocus();
329
- }
330
- mousedDownEmptySpace = false;
331
-
332
- if (tableContext.isMousingDown) {
333
- tableContext.isMousingDown = false;
334
- }
335
-
336
- removeOverrideResizeHandler();
337
- }}
338
- onclick={(e: MouseEvent) => {
339
- handleClickOutside(e);
340
- }}
341
- onmousedown={(e: MouseEvent) => {
342
- mousedDownEmptySpace =
343
- e.target === unfocusRef || (e.target as HTMLElement).closest('.table-wrapper') == null;
344
- }}
345
- />
346
-
347
- <div
348
- class="grid"
349
- class:nested
350
- >
351
- {#if tableContext.dataRepo.isLoading}
352
- <LoadingOverlay
353
- absolute
354
- borderRadius="4px"
355
- message={tableContext.dataRepo.loadingMessage}
356
- />
357
- {/if}
358
-
359
- <div
360
- class="horizontal-bar"
361
- class:border={tableContext.toolbarPosition === ToolbarPosition.Top}
362
- >
363
- {#if tableContext.toolbarPosition === ToolbarPosition.Top}
364
- <TableHorizontalBar />
365
- {/if}
366
- </div>
367
-
368
- <div
369
- class="sidebar-wrapper sidebar-wrapper-left"
370
- class:nested
371
- style="--nested-height: {nestedHeight}px;"
372
- >
373
- {#if tableContext.toolbarPosition === ToolbarPosition.Left}
374
- <div class="sidebar toolbar-left">
375
- <TableSidebar />
376
- </div>
377
- {/if}
378
- </div>
379
- <div
380
- class="sidebar-wrapper sidebar-wrapper-right"
381
- class:nested
382
- style="--nested-height: {nestedHeight}px;"
383
- >
384
- {#if tableContext.toolbarPosition === ToolbarPosition.Right}
385
- <div class="sidebar toolbar-right">
386
- <TableSidebar />
387
- </div>
388
- {/if}
389
- </div>
390
-
391
- <div class="footer">
392
- {#if tableContext.displayFooter}
393
- <TableFooter />
394
- {/if}
395
- </div>
396
-
397
- <!-- svelte-ignore a11y_no_static_element_interactions -->
398
- <div
399
- bind:this={tableRef}
400
- class="datatable"
401
- class:nested
402
- class:compact={tableContext.isCompact}
403
- class:quick-filtering={tableContext.showQuickFiltering}
404
- onkeydown={tableContext.handleTdKeydown}
405
- >
406
- <div
407
- class="flex_table"
408
- bind:this={ref}
409
- class:nested
410
- >
411
- <div
412
- bind:this={unfocusRef}
413
- class="table-wrapper"
414
- oncontextmenu={openContextMenu}
415
- >
416
- <!-- {#if leftTotalSize > 0} -->
417
- <table
418
- class="datatable__table left"
419
- style="min-width: {leftTotalSize}px"
420
- >
421
- <TableHead
422
- showHighlight={leftTotalSize > 0}
423
- side="left"
424
- />
425
- <TableBody
426
- includeHighlight={appropriateSide == 'left'}
427
- columns={leftColumns}
428
- side="left"
429
- heights={totalRowSizes}
430
- bind:this={leftTable}
431
- />
432
- </table>
433
- <!-- {/if} -->
434
-
435
- <!-- {#if centreTotalSize > 0} -->
436
-
437
- <table
438
- class="datatable__table centre"
439
- style="min-width: {centreTotalSize}px"
440
- >
441
- <TableHead
442
- showHighlight={leftTotalSize == 0}
443
- side="centre"
444
- />
445
- <TableBody
446
- includeHighlight={appropriateSide == 'centre'}
447
- columns={centreColumns}
448
- side="centre"
449
- heights={totalRowSizes}
450
- bind:this={centreTable}
451
- />
452
- </table>
453
- <!-- {/if} -->
454
-
455
- <!-- {#if rightTotalSize > 0} -->
456
- <table
457
- class="datatable__table right"
458
- style="min-width: {rightTotalSize}px"
459
- >
460
- <TableHead
461
- showHighlight={false}
462
- side="right"
463
- />
464
- <TableBody
465
- includeHighlight={appropriateSide == 'right'}
466
- columns={rightColumns}
467
- side="right"
468
- heights={totalRowSizes}
469
- bind:this={rightTable}
470
- />
471
- </table>
472
- <!-- {/if} -->
473
- </div>
474
-
475
- <!-- TODO add filter callbacks -->
476
- </div>
477
-
478
- {#if nested}
479
- <!-- svelte-ignore a11y_no_static_element_interactions -->
480
- <div
481
- class:isResizing
482
- class="resizer"
483
- onmousedown={(e) => {
484
- e.preventDefault();
485
- addOverrideResizeHandler();
486
- }}
487
- ></div>
488
- {/if}
489
- </div>
490
- </div>
491
-
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ module
6
+ >
7
+ import {
8
+ ContextMenuContext,
9
+ type IContextMenuOption,
10
+ } from '../../Contexts/ContextMenuContext.svelte';
11
+ import { devCatch, refWrapper } from '../../Helpers/Helpers.svelte';
12
+ import type { RefWrapper } from '../../Types/Internal/RefWrapper.js';
13
+ import { getContext, onMount, setContext, tick, untrack } from 'svelte';
14
+ import LoadingOverlay from '../LoadingOverlay.svelte';
15
+ import type { HeightData } from './Body/HeightData.js';
16
+ import { shownPopup } from './Body/Rows/Columns/ActionsCell.svelte';
17
+ import TableBody from './Body/TableBody.svelte';
18
+ import TableHead from './Headers/TableHead.svelte';
19
+ import TableFooter from './Misc/TableFooter.svelte';
20
+ import TableHorizontalBar from './Misc/TableHorizontalBar.svelte';
21
+ import TableSidebar from './Misc/TableSidebar.svelte';
22
+ import { TableContext } from './Types/Context/TableContext.svelte';
23
+ import type { Primitive } from './Types/Context/TableInitOptions.js';
24
+ import { PaginationType } from './Types/Pagination/PaginationType.js';
25
+ import { ToolbarPosition } from './Types/Toolbar/ToolbarPosition.js';
26
+
27
+ declare global {
28
+ interface HTMLElementEventMap {
29
+ edit: CustomEvent<{
30
+ selectAll: boolean;
31
+ }>;
32
+ }
33
+ }
34
+
35
+ export interface TableProps<T extends object, IdType extends Primitive> {
36
+ readonly tableContext: TableContext<T, IdType>;
37
+ nested?: boolean;
38
+ }
39
+
40
+ export const quickFilterContextIdentifier: symbol = Symbol();
41
+
42
+ const openedQuickFilterSelect = refWrapper(null);
43
+ </script>
44
+
45
+ <script
46
+ lang="ts"
47
+ generics="T extends object,IdType extends Primitive"
48
+ >
49
+ const { nested = false, tableContext }: TableProps<T, IdType> = $props();
50
+
51
+ // NOTE: known limitation. This is passed as a wrapper, and they should never require reassignment. It is deeply reactive
52
+ // svelte-ignore state_referenced_locally
53
+ setContext(TableContext.identifier, tableContext);
54
+
55
+ const { openContextMenu: _openContextMenu } = getContext<ContextMenuContext>(
56
+ ContextMenuContext.identifier
57
+ );
58
+
59
+ let ref: HTMLDivElement | undefined = $state();
60
+ let unfocusRef: HTMLDivElement | undefined = $state();
61
+
62
+ setContext(quickFilterContextIdentifier, openedQuickFilterSelect);
63
+
64
+ $effect(() => {
65
+ void tableContext.focusStart;
66
+ void ref;
67
+ untrack(() => {
68
+ if (tableContext.focusStart && ref) {
69
+ const { row, column: index } = tableContext.focusStart;
70
+ let element = ref.querySelector(
71
+ `#${tableContext.tableName}-cell-${row}-${tableContext.columnDefs[index].id}`
72
+ ) as HTMLElement | undefined;
73
+
74
+ if (element?.tabIndex == -1) {
75
+ const td = element;
76
+ element = td.querySelector('button') as HTMLElement | undefined;
77
+ if (!element) {
78
+ element = td.querySelector('input') as HTMLElement | undefined;
79
+ }
80
+ }
81
+
82
+ element?.focus({ preventScroll: true });
83
+ element?.scrollIntoView({
84
+ behavior: 'instant',
85
+ block: 'nearest',
86
+ inline: 'nearest',
87
+ });
88
+ }
89
+ });
90
+ });
91
+
92
+ $effect(() => {
93
+ if (tableContext.focusEnd && ref) {
94
+ const { row, column: index } = tableContext.focusEnd;
95
+ const cell = ref.querySelector(
96
+ `#${tableContext.tableName}-cell-${row}-${tableContext.columnDefs[index].id}`
97
+ ) as HTMLElement | undefined;
98
+
99
+ cell?.scrollIntoView({
100
+ behavior: 'instant',
101
+ block: 'nearest',
102
+ inline: 'nearest',
103
+ });
104
+ }
105
+ });
106
+
107
+ function openContextMenu(e: MouseEvent) {
108
+ e.stopPropagation();
109
+
110
+ if (
111
+ (e.target as HTMLElement).tagName === 'INPUT' ||
112
+ (e.target as HTMLElement).closest('.select-wrapper') != null
113
+ ) {
114
+ return;
115
+ }
116
+
117
+ const clickedElement = document.activeElement?.id.split('-cell-')[1]?.split('-');
118
+
119
+ if (clickedElement) {
120
+ const [rowString, columnId] = clickedElement;
121
+ const row = parseInt(rowString);
122
+
123
+ if (
124
+ !tableContext.focusedColumnIds.has(columnId) ||
125
+ !tableContext.focusedRowIndices.has(row)
126
+ ) {
127
+ const columnIndex = tableContext.columnDefs.find((it) => it.id === columnId)?.index;
128
+
129
+ if (columnIndex) {
130
+ tableContext.moveFocus({
131
+ column: columnIndex,
132
+ row,
133
+ id: columnId,
134
+ });
135
+ }
136
+ }
137
+ }
138
+
139
+ // this may not exist in http contexts
140
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
141
+ if (tableContext.allowCopy && window.Clipboard) {
142
+ e.preventDefault();
143
+
144
+ const options: Array<IContextMenuOption> = [
145
+ {
146
+ callback: () => {
147
+ tableContext.copyDataToClipboard().catch(devCatch);
148
+ },
149
+ text: 'Copy Selection',
150
+ },
151
+ ];
152
+
153
+ if (tableContext.hasHighlightedItems) {
154
+ options.push({
155
+ callback: () => {
156
+ tableContext.copyHighlightedRowsToClipboard().catch(devCatch);
157
+ },
158
+ text: 'Copy Highlighted Rows',
159
+ });
160
+ }
161
+
162
+ _openContextMenu(e, options).catch(devCatch);
163
+ }
164
+ }
165
+
166
+ function handleClickOutside(e: MouseEvent) {
167
+ const target = e.target as HTMLElement;
168
+ if (target.isConnected) {
169
+ if (target.closest('div.floating') == null && target.closest('div.stick-right') == null) {
170
+ openedQuickFilterSelect.value = null;
171
+ }
172
+
173
+ if (target.closest('div.slot-wrapper') == null) {
174
+ // svelte type gen/eslint miscommunication
175
+ (shownPopup as RefWrapper<symbol | undefined>).value = undefined;
176
+ }
177
+ }
178
+ }
179
+
180
+ // nested table resizing
181
+ let isResizing = $state(false);
182
+ let remPxRatio = $state(0);
183
+ const minNestedHeight = $derived(20 * remPxRatio);
184
+
185
+ // HACK please figure this one out
186
+ let nestedHeight = $derived(minNestedHeight);
187
+
188
+ let tableRef: HTMLDivElement | undefined = undefined;
189
+ let mousedDownEmptySpace = $state(false);
190
+
191
+ onMount(() => {
192
+ remPxRatio = parseFloat(getComputedStyle(document.documentElement).fontSize);
193
+ });
194
+
195
+ function addOverrideResizeHandler() {
196
+ isResizing = true;
197
+ window.addEventListener('mousemove', onResize);
198
+ }
199
+
200
+ function onResize(e: MouseEvent) {
201
+ const sliderHeight = tableRef ? e.clientY - tableRef.getBoundingClientRect().top : 0;
202
+ nestedHeight = Math.max(sliderHeight, minNestedHeight);
203
+ }
204
+
205
+ function removeOverrideResizeHandler() {
206
+ isResizing = false;
207
+ window.removeEventListener('mousemove', onResize);
208
+ }
209
+
210
+ const usesLoading = $derived(
211
+ tableContext.paginationRepo &&
212
+ tableContext.paginationRepo.paginationType === PaginationType.Remote
213
+ );
214
+
215
+ const shouldUnfocusRemote = $derived(
216
+ usesLoading &&
217
+ tableContext.paginationRepo?.isLoading === false &&
218
+ tableContext.paginationRepo.paginationState.pageIndex !==
219
+ tableContext.paginationRepo.previouslyViewedPage
220
+ );
221
+
222
+ const shouldUnfocusLocal = $derived(
223
+ !usesLoading && tableContext.paginationRepo?.paginationState.pageIndex
224
+ );
225
+
226
+ $effect(() => {
227
+ if ((shouldUnfocusRemote || shouldUnfocusLocal) && unfocusRef) {
228
+ untrack(() => {
229
+ tableContext.unfocus();
230
+ });
231
+ unfocusRef.scrollTop = 0;
232
+ } else if (usesLoading && tableContext.paginationRepo?.isLoading === false) {
233
+ // refocus same element after a reload
234
+ // this does not break mutifocus
235
+ untrack(() => {
236
+ const fs = tableContext.focusStart;
237
+ if (!fs) {
238
+ return;
239
+ }
240
+ const idToFocusTo = `#${tableContext.tableName}-cell-${fs.row}-${fs.id}`;
241
+
242
+ tick()
243
+ .then(() => {
244
+ if (idToFocusTo) {
245
+ (document.querySelector(idToFocusTo) as HTMLTableCellElement | undefined)?.focus();
246
+ }
247
+ })
248
+ .catch(devCatch);
249
+ });
250
+ }
251
+ });
252
+
253
+ const leftTotalSize = $derived(tableContext.table.getLeftTotalSize());
254
+ const centreTotalSize = $derived(tableContext.table.getCenterTotalSize());
255
+ const rightTotalSize = $derived(tableContext.table.getRightTotalSize());
256
+
257
+ const leftColumns = $derived(tableContext.table.getLeftVisibleLeafColumns());
258
+ const centreColumns = $derived(tableContext.table.getCenterVisibleLeafColumns());
259
+ const rightColumns = $derived(tableContext.table.getRightVisibleLeafColumns());
260
+
261
+ const appropriateSide = $derived.by(() => {
262
+ if (leftColumns.length > 0) {
263
+ return 'left';
264
+ } else if (rightColumns.length != tableContext.columnDefs.length) {
265
+ return 'centre';
266
+ } else {
267
+ return 'right';
268
+ }
269
+ });
270
+
271
+ type SvelteTableElement = {
272
+ rowHeights: RefWrapper<Array<number>>;
273
+ };
274
+
275
+ let leftTable: SvelteTableElement | undefined = $state();
276
+ let centreTable: SvelteTableElement | undefined = $state();
277
+ let rightTable: SvelteTableElement | undefined = $state();
278
+
279
+ const totalRowSizes: Array<HeightData<T>> = $derived.by(() => {
280
+ if (!leftTable || !centreTable || !rightTable) {
281
+ return [];
282
+ }
283
+
284
+ // console.debug(`got refs for all tables`);
285
+ const centre = centreTable.rowHeights.value;
286
+ const right = rightTable.rowHeights.value;
287
+
288
+ return leftTable.rowHeights.value.map((_leftHeight, index) => {
289
+ const leftHeight = _leftHeight || 0;
290
+ const centreHeight = centre[index] || 0;
291
+ const rightHeight = right[index] || 0;
292
+
293
+ const maxHeight = Math.max(leftHeight, centreHeight, rightHeight);
294
+ const applyTo: HeightData<T>['applyTo'] = [];
295
+
296
+ if (leftHeight < maxHeight) {
297
+ // console.debug(`left (${leftHeight}) was lower than max (${maxHeight})`);
298
+ applyTo.push('left');
299
+ }
300
+ if (centreHeight < maxHeight) {
301
+ // console.debug(`centre (${centreHeight}) was lower than max (${maxHeight})`);
302
+ applyTo.push('centre');
303
+ }
304
+ if (rightHeight < maxHeight) {
305
+ // console.debug(`right (${rightHeight}) was lower than max (${maxHeight})`);
306
+ applyTo.push('right');
307
+ }
308
+
309
+ return {
310
+ index,
311
+ applyTo,
312
+ maxHeight,
313
+ };
314
+ });
315
+ });
316
+ </script>
317
+
318
+ <svelte:window
319
+ onmouseup={(e: MouseEvent) => {
320
+ if (
321
+ // if it's not a click on an item in the context menu
322
+ !(e.target as HTMLElement | undefined)?.classList.contains('context-menu-item') &&
323
+ // if it's outside the table or inside the empty space of the table
324
+ (e.target === unfocusRef ||
325
+ ((e.target as HTMLElement | undefined)?.closest('.table-wrapper') == null &&
326
+ mousedDownEmptySpace))
327
+ ) {
328
+ tableContext.unfocus();
329
+ }
330
+ mousedDownEmptySpace = false;
331
+
332
+ if (tableContext.isMousingDown) {
333
+ tableContext.isMousingDown = false;
334
+ }
335
+
336
+ removeOverrideResizeHandler();
337
+ }}
338
+ onclick={(e: MouseEvent) => {
339
+ handleClickOutside(e);
340
+ }}
341
+ onmousedown={(e: MouseEvent) => {
342
+ mousedDownEmptySpace =
343
+ e.target === unfocusRef || (e.target as HTMLElement).closest('.table-wrapper') == null;
344
+ }}
345
+ />
346
+
347
+ <div
348
+ class="grid"
349
+ class:nested
350
+ >
351
+ {#if tableContext.dataRepo.isLoading}
352
+ <LoadingOverlay
353
+ absolute
354
+ borderRadius="4px"
355
+ message={tableContext.dataRepo.loadingMessage}
356
+ />
357
+ {/if}
358
+
359
+ <div
360
+ class="horizontal-bar"
361
+ class:border={tableContext.toolbarPosition === ToolbarPosition.Top}
362
+ >
363
+ {#if tableContext.toolbarPosition === ToolbarPosition.Top}
364
+ <TableHorizontalBar />
365
+ {/if}
366
+ </div>
367
+
368
+ <div
369
+ class="sidebar-wrapper sidebar-wrapper-left"
370
+ class:nested
371
+ style="--nested-height: {nestedHeight}px;"
372
+ >
373
+ {#if tableContext.toolbarPosition === ToolbarPosition.Left}
374
+ <div class="sidebar toolbar-left">
375
+ <TableSidebar />
376
+ </div>
377
+ {/if}
378
+ </div>
379
+ <div
380
+ class="sidebar-wrapper sidebar-wrapper-right"
381
+ class:nested
382
+ style="--nested-height: {nestedHeight}px;"
383
+ >
384
+ {#if tableContext.toolbarPosition === ToolbarPosition.Right}
385
+ <div class="sidebar toolbar-right">
386
+ <TableSidebar />
387
+ </div>
388
+ {/if}
389
+ </div>
390
+
391
+ <div class="footer">
392
+ {#if tableContext.displayFooter}
393
+ <TableFooter />
394
+ {/if}
395
+ </div>
396
+
397
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
398
+ <div
399
+ bind:this={tableRef}
400
+ class="datatable"
401
+ class:nested
402
+ class:compact={tableContext.isCompact}
403
+ class:quick-filtering={tableContext.showQuickFiltering}
404
+ onkeydown={tableContext.handleTdKeydown}
405
+ >
406
+ <div
407
+ class="flex_table"
408
+ bind:this={ref}
409
+ class:nested
410
+ >
411
+ <div
412
+ bind:this={unfocusRef}
413
+ class="table-wrapper"
414
+ oncontextmenu={openContextMenu}
415
+ >
416
+ <!-- {#if leftTotalSize > 0} -->
417
+ <table
418
+ class="datatable__table left"
419
+ style="min-width: {leftTotalSize}px"
420
+ >
421
+ <TableHead
422
+ showHighlight={leftTotalSize > 0}
423
+ side="left"
424
+ />
425
+ <TableBody
426
+ includeHighlight={appropriateSide == 'left'}
427
+ columns={leftColumns}
428
+ side="left"
429
+ heights={totalRowSizes}
430
+ bind:this={leftTable}
431
+ />
432
+ </table>
433
+ <!-- {/if} -->
434
+
435
+ <!-- {#if centreTotalSize > 0} -->
436
+
437
+ <table
438
+ class="datatable__table centre"
439
+ style="min-width: {centreTotalSize}px"
440
+ >
441
+ <TableHead
442
+ showHighlight={leftTotalSize == 0}
443
+ side="centre"
444
+ />
445
+ <TableBody
446
+ includeHighlight={appropriateSide == 'centre'}
447
+ columns={centreColumns}
448
+ side="centre"
449
+ heights={totalRowSizes}
450
+ bind:this={centreTable}
451
+ />
452
+ </table>
453
+ <!-- {/if} -->
454
+
455
+ <!-- {#if rightTotalSize > 0} -->
456
+ <table
457
+ class="datatable__table right"
458
+ style="min-width: {rightTotalSize}px"
459
+ >
460
+ <TableHead
461
+ showHighlight={false}
462
+ side="right"
463
+ />
464
+ <TableBody
465
+ includeHighlight={appropriateSide == 'right'}
466
+ columns={rightColumns}
467
+ side="right"
468
+ heights={totalRowSizes}
469
+ bind:this={rightTable}
470
+ />
471
+ </table>
472
+ <!-- {/if} -->
473
+ </div>
474
+
475
+ <!-- TODO add filter callbacks -->
476
+ </div>
477
+
478
+ {#if nested}
479
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
480
+ <div
481
+ class:isResizing
482
+ class="resizer"
483
+ onmousedown={(e) => {
484
+ e.preventDefault();
485
+ addOverrideResizeHandler();
486
+ }}
487
+ ></div>
488
+ {/if}
489
+ </div>
490
+ </div>
491
+
492
492
  <style>div.grid {
493
493
  border-radius: 4px;
494
494
  display: grid;
@@ -657,4 +657,4 @@ div.sidebar-wrapper .sidebar.toolbar-right {
657
657
  position: sticky;
658
658
  z-index: 2;
659
659
  right: 0;
660
- }</style>
660
+ }</style>