@lavalogic/scoria 0.20.0 → 0.21.1

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.
@@ -9,7 +9,7 @@
9
9
  import { TableContext } from '../../../Types/Context/TableContext.svelte.js';
10
10
  import { type Primitive } from '../../../Types/Context/TableInitOptions.js';
11
11
  import { TableFocusDetails } from '../../../Types/Focus/TableFocusDetails.svelte.js';
12
- import { devCatch, getErrorMessage, isValidityTuple } from '../../../../../Helpers/Helpers.svelte.js';
12
+ import { devCatch, isValidityTuple } from '../../../../../Helpers/Helpers.svelte.js';
13
13
  import { Validity } from '../../../../../Types/Internal/Validity.js';
14
14
  import { getContext, tick, untrack } from 'svelte';
15
15
  import type { ChangeEventHandler } from 'svelte/elements';
@@ -31,7 +31,7 @@
31
31
  new TableFocusDetails(cell, tableContext)
32
32
  );
33
33
 
34
- const keyPromise: Promise<RowIdType> = $derived(
34
+ const keyOrPromise: RowIdType | Promise<RowIdType> = $derived(
35
35
  tableContext.selectionExtractor(cell.row.original)
36
36
  );
37
37
  const isSelectable = $derived(
@@ -41,14 +41,26 @@
41
41
  )
42
42
  );
43
43
 
44
- const isSelectedPromise = $derived.by(async () => {
45
- void keyPromise;
46
- void isSelectable;
47
- void tableContext.selectedItems.size;
48
-
49
- const key = await keyPromise;
44
+ // Sync path (common): uses selectedItems.has(key) for per-key reactive tracking —
45
+ // only this row's checkbox re-runs when its specific key changes.
46
+ // Async path (rare): returns null and defers to _isSelectedAsync updated by the $effect below.
47
+ const isSelected: boolean | null = $derived.by(() => {
48
+ const k = keyOrPromise;
49
+ if (k instanceof Promise) {
50
+ void tableContext.selectedItems.size; // async fallback: re-run when any selection changes
51
+ return null;
52
+ }
53
+ return tableContext.selectedItems.has(k);
54
+ });
50
55
 
51
- return tableContext.selectedItems.has(key);
56
+ let _isSelectedAsync: boolean = $state(false);
57
+ $effect(() => {
58
+ const k = keyOrPromise;
59
+ if (!(k instanceof Promise)) { return; }
60
+ void tableContext.selectedItems.size; // re-run when selection changes
61
+ k.then((key) => {
62
+ _isSelectedAsync = tableContext.selectedItems.has(key);
63
+ }).catch(devCatch);
52
64
  });
53
65
 
54
66
  let previousValidity: Validity = $state(Validity.Valid);
@@ -74,7 +86,7 @@
74
86
  untrack(() => {
75
87
  tick()
76
88
  .then(() => {
77
- const x: Promise<void> =
89
+ const x: void | Promise<void> =
78
90
  validity === Validity.Invalid
79
91
  ? tableContext.addInvalidValue(cell.row.original)
80
92
  : tableContext.removeInvalidValue(cell.row.original);
@@ -88,7 +100,7 @@
88
100
  });
89
101
 
90
102
  const onchange: ChangeEventHandler<HTMLInputElement> = () => {
91
- tableContext.toggleSelected(cell.row.original).catch(devCatch);
103
+ tableContext.toggleSelected(cell.row.original)?.catch(devCatch);
92
104
  };
93
105
 
94
106
  let inputRef: HTMLInputElement | undefined = $state();
@@ -121,7 +133,7 @@
121
133
  if (e.key === 'Enter') {
122
134
  e.stopPropagation();
123
135
 
124
- tableContext.toggleSelected(cell.row.original).catch(devCatch);
136
+ tableContext.toggleSelected(cell.row.original)?.catch(devCatch);
125
137
 
126
138
  inputRef?.dispatchEvent(new InputEvent('change'));
127
139
  // tableContext.onEditCell(cell.row.original, focusDetails.coordinates, !value);
@@ -141,25 +153,14 @@
141
153
  class:multifocus-start={focusDetails.isMultiFocusStart}
142
154
  style={focusDetails.outline}
143
155
  >
144
- {#await isSelectedPromise}
145
- <input
146
- id={`${tableContext.tableName}-cell-${cell.row.visibleIndex}-${cell.column.id}`}
147
- type="checkbox"
148
- checked={false}
149
- disabled
150
- />
151
- {:then isSelected}
152
- <input
153
- id={`${tableContext.tableName}-cell-${cell.row.visibleIndex}-${cell.column.id}`}
154
- bind:this={inputRef}
155
- {onchange}
156
- type="checkbox"
157
- checked={isSelected}
158
- disabled={!isSelectable || tableContext.showSelectedOnly}
159
- />
160
- {:catch e}
161
- Error: {getErrorMessage(e)}
162
- {/await}
156
+ <input
157
+ id={`${tableContext.tableName}-cell-${cell.row.visibleIndex}-${cell.column.id}`}
158
+ bind:this={inputRef}
159
+ {onchange}
160
+ type="checkbox"
161
+ checked={isSelected ?? _isSelectedAsync}
162
+ disabled={!isSelectable || tableContext.showSelectedOnly}
163
+ />
163
164
  </label>
164
165
  </td>
165
166
 
@@ -165,7 +165,7 @@ onMouseLeave={onMouseLeave} -->
165
165
  })()}
166
166
  {highlighted}
167
167
  toggleHighlightedRow={() => {
168
- tableContext.toggleHighlightedRow(row.original).catch(devCatch);
168
+ tableContext.toggleHighlightedRow(row.original)?.catch(devCatch);
169
169
  }}
170
170
  />
171
171
  {/if}
@@ -32,6 +32,10 @@
32
32
  // return _rowHeights;
33
33
  // }
34
34
  // };
35
+
36
+ // $inspect(tableContext.paginationRowModel).with((...v) => {
37
+ // console.trace(...v);
38
+ // });
35
39
  </script>
36
40
 
37
41
  <tbody>
@@ -49,22 +53,20 @@
49
53
  </tr>
50
54
  {/if}
51
55
 
52
- {#await tableContext.paginationRowModel then prm}
53
- {#each prm.rows as row, index (row)}
54
- <!-- {@const customHeight = heights[index]?.applyTo.includes(side)
56
+ {#each tableContext.paginationRowModel.rows as row, index (row.visibleIndex)}
57
+ <!-- {@const customHeight = heights[index]?.applyTo.includes(side)
55
58
  ? heights[index].maxHeight
56
59
  : undefined} -->
57
- <!-- {customHeight} -->
58
- <TableRow
59
- customHeight={undefined}
60
- {includeHighlight}
61
- {row}
62
- {side}
63
- odd={index % 2 == 1}
64
- />
65
- <!-- bind:this={rows[index]} -->
66
- {/each}
67
- {/await}
60
+ <!-- {customHeight} -->
61
+ <TableRow
62
+ customHeight={undefined}
63
+ {includeHighlight}
64
+ {row}
65
+ {side}
66
+ odd={index % 2 == 1}
67
+ />
68
+ <!-- bind:this={rows[index]} -->
69
+ {/each}
68
70
  </tbody>
69
71
 
70
72
  <style>td {
@@ -13,18 +13,8 @@
13
13
 
14
14
  const tableContext = getContext<TableContext<T, never>>(TableContext.identifier);
15
15
 
16
- let allHighlighted = $state(false);
17
-
18
- $effect(() => {
19
- tableContext.allHighlighted
20
- .then((flag) => {
21
- allHighlighted = flag;
22
- })
23
- .catch(devCatch);
24
- });
25
-
26
- const backgroundColour = $derived.by(async () => {
27
- return (await tableContext.sortedData).length > 0 && allHighlighted
16
+ const backgroundColour = $derived.by(() => {
17
+ return tableContext.sortedData.length > 0 && tableContext.allHighlighted
28
18
  ? Colour['$brand-primary']
29
19
  : Colour['$ui-white'];
30
20
  });
@@ -32,30 +22,17 @@
32
22
 
33
23
  <div class="checkbox-cell">
34
24
  <div class="wrapper">
35
- {#await backgroundColour}
36
- <Action
37
- hoverBackgroundColour={Colour['$brand-dark']}
38
- size={Size.FillWidth}
39
- icon={{
40
- name: 'nothing',
41
- }}
42
- onclick={() => {
43
- tableContext.onHighlightAll();
44
- }}
45
- />
46
- {:then backgroundColour}
47
- <Action
48
- {backgroundColour}
49
- hoverBackgroundColour={Colour['$brand-dark']}
50
- size={Size.FillWidth}
51
- icon={{
52
- name: 'nothing',
53
- }}
54
- onclick={() => {
55
- tableContext.onHighlightAll();
56
- }}
57
- />
58
- {/await}
25
+ <Action
26
+ {backgroundColour}
27
+ hoverBackgroundColour={Colour['$brand-dark']}
28
+ size={Size.FillWidth}
29
+ icon={{
30
+ name: 'nothing',
31
+ }}
32
+ onclick={() => {
33
+ tableContext.onHighlightAll()?.catch(devCatch);
34
+ }}
35
+ />
59
36
  </div>
60
37
  </div>
61
38
 
@@ -9,29 +9,19 @@
9
9
 
10
10
  const tableContext = getContext<TableContext<T, never>>(TableContext.identifier);
11
11
 
12
- const allSelected: Promise<boolean> = $derived(tableContext.allSelectedInCurrentPage);
13
- const checked: Promise<boolean> = $derived.by(() => {
14
- return tableContext.dataRepo.data.length > 0 ? allSelected : Promise.resolve(false);
15
- });
12
+ const checked: boolean = $derived(
13
+ tableContext.dataRepo.data.length > 0 ? tableContext.allSelectedInCurrentPage : false
14
+ );
16
15
  </script>
17
16
 
18
17
  <div class="checkbox-cell">
19
18
  <label>
20
- {#await checked}
21
- <input
22
- onchange={tableContext.selectAllInCurrentPage}
23
- type="checkbox"
24
- checked={false}
25
- disabled={tableContext.showSelectedOnly}
26
- />
27
- {:then checked}
28
- <input
29
- onchange={tableContext.selectAllInCurrentPage}
30
- type="checkbox"
31
- {checked}
32
- disabled={tableContext.showSelectedOnly}
33
- />
34
- {/await}
19
+ <input
20
+ onchange={tableContext.selectAllInCurrentPage}
21
+ type="checkbox"
22
+ {checked}
23
+ disabled={tableContext.showSelectedOnly}
24
+ />
35
25
  </label>
36
26
  </div>
37
27
 
@@ -102,16 +102,6 @@
102
102
  }
103
103
  });
104
104
 
105
- $effect(() => {
106
- void tableContext.dataRepo.data.length;
107
- void tableContext.selectedItems;
108
- void tableContext.paginationRepo?.pageStartIndex;
109
-
110
- untrack(() => {
111
- tableContext.recalculateAllSelectedInCurrentPage();
112
- });
113
- });
114
-
115
105
  function openContextMenu(e: MouseEvent) {
116
106
  e.stopPropagation();
117
107
 
@@ -127,21 +117,21 @@
127
117
  if (clickedElement) {
128
118
  const [rowString, columnId] = clickedElement;
129
119
  const row = parseInt(rowString);
130
- Promise.all([tableContext.focusedColumnIds, tableContext.focusedRowIndices])
131
- .then(([focusedColumnIds, focusedRowIndices]) => {
132
- if (!focusedColumnIds.has(columnId) || !focusedRowIndices.has(row)) {
133
- const columnIndex = tableContext.columnDefs.find((it) => it.id === columnId)?.index;
134
-
135
- if (columnIndex) {
136
- tableContext.moveFocus({
137
- column: columnIndex,
138
- row,
139
- id: columnId,
140
- });
141
- }
142
- }
143
- })
144
- .catch(devCatch);
120
+
121
+ if (
122
+ !tableContext.focusedColumnIds.has(columnId) ||
123
+ !tableContext.focusedRowIndices.has(row)
124
+ ) {
125
+ const columnIndex = tableContext.columnDefs.find((it) => it.id === columnId)?.index;
126
+
127
+ if (columnIndex) {
128
+ tableContext.moveFocus({
129
+ column: columnIndex,
130
+ row,
131
+ id: columnId,
132
+ });
133
+ }
134
+ }
145
135
  }
146
136
 
147
137
  // this may not exist in http contexts
@@ -1,4 +1,4 @@
1
1
  export interface ColumnPinningState {
2
- left?: Map<string, number>;
3
- right?: Map<string, number>;
2
+ left?: ReadonlyMap<string, number>;
3
+ right?: ReadonlyMap<string, number>;
4
4
  }
@@ -9,7 +9,7 @@ import type { HeaderGroup } from '../HeaderGroup.js';
9
9
  import type { OnChangeFn } from '../OnChangeFn.js';
10
10
  import type { PaginationState } from '../Pagination/PaginationState.js';
11
11
  export interface ITable<T extends object> {
12
- data: Promise<Array<T>>;
12
+ data: Array<T>;
13
13
  columns: Array<ColumnDef<T>>;
14
14
  columnSizing: ColumnSizingState;
15
15
  columnVisibility: VisibilityState;
@@ -1,5 +1,5 @@
1
1
  import type { Row } from './Row.js';
2
2
  export interface RowModel<T extends object> {
3
3
  rows: Array<Row<T>>;
4
- rowsById: Map<string, Row<T>>;
4
+ rowsById: ReadonlyMap<string, Row<T>>;
5
5
  }
@@ -1,4 +1,4 @@
1
- import { SvelteMap, SvelteSet } from 'svelte/reactivity';
1
+ import { SvelteMap } from 'svelte/reactivity';
2
2
  import type { Updater } from 'svelte/store';
3
3
  import type { ColumnPinningState } from '../Columns/ColumnPinningState.js';
4
4
  import type { ColumnSizingState } from '../Columns/ColumnSizingState.js';
@@ -51,6 +51,7 @@ export declare class TableContext<T extends object, RowIdType extends Primitive>
51
51
  get remoteFilters(): CustomRemoteFilters<unknown> | undefined;
52
52
  private set remoteFilters(value);
53
53
  private _showSelectedOnly;
54
+ private _pageBeforeSelectedOnly;
54
55
  get showSelectedOnly(): boolean;
55
56
  set showSelectedOnly(v: boolean);
56
57
  readonly dateFilter: FilterFn<T, DateFilterValueType> | undefined;
@@ -73,11 +74,12 @@ export declare class TableContext<T extends object, RowIdType extends Primitive>
73
74
  private _selectedPreset;
74
75
  get selectedPreset(): ColumnDefSet<T> | undefined;
75
76
  set selectedPreset(v: ColumnDefSet<T> | undefined);
76
- private readonly _pageColData;
77
- readonly focusedRowIndices: Promise<SvelteSet<number>>;
77
+ private _focusedRowIndices;
78
+ get focusedRowIndices(): ReadonlySet<number>;
78
79
  private readonly _highlightedItems;
79
80
  get highlightedItems(): SvelteMap<Awaited<unknown>, T>;
80
- readonly focusedColumnIds: Promise<SvelteSet<string>>;
81
+ _focusedColumnIds: ReadonlySet<string>;
82
+ get focusedColumnIds(): ReadonlySet<string>;
81
83
  private _isMousingDown;
82
84
  get isMousingDown(): boolean;
83
85
  set isMousingDown(v: boolean);
@@ -120,12 +122,13 @@ export declare class TableContext<T extends object, RowIdType extends Primitive>
120
122
  private _openModalCoordinates;
121
123
  get openModalCoordinates(): CellCoordinatesWithoutColumn | null;
122
124
  set openModalCoordinates(v: CellCoordinatesWithoutColumn | null);
125
+ private _allSelectedAsync;
123
126
  private _allSelectedInCurrentPage;
124
- get allSelectedInCurrentPage(): Promise<boolean>;
127
+ get allSelectedInCurrentPage(): boolean;
125
128
  private allSelectedBySelectableMethod;
126
129
  private _areAllHighlighted;
127
130
  private _allHighlighted;
128
- get allHighlighted(): Promise<boolean>;
131
+ get allHighlighted(): boolean;
129
132
  private _hasHighlightedItems;
130
133
  get hasHighlightedItems(): boolean;
131
134
  private _selectedItems;
@@ -140,31 +143,37 @@ export declare class TableContext<T extends object, RowIdType extends Primitive>
140
143
  private readonly getSortingFn;
141
144
  private _filteredIndices;
142
145
  get filteredIndices(): ReadonlyArray<number>;
143
- readonly filteredData: Promise<T[]>;
146
+ readonly filteredData: T[];
144
147
  private sortedDataAbortController;
145
- readonly sortedData: Promise<T[]>;
146
- readonly sortedRowModel: Promise<RowModel<T>>;
148
+ readonly sortedData: T[];
149
+ private _sortedRowModel;
150
+ get sortedRowModel(): RowModel<T>;
147
151
  private CurrentPageDataAbortController;
148
- readonly currentPageData: Promise<T[]>;
149
- readonly originalRowIndices: Promise<ReadonlyMap<unknown, number>>;
150
- private rowsPromiseAbortController;
152
+ readonly currentPageData: T[];
153
+ private _originalRowIndices;
154
+ get originalRowIndices(): ReadonlyMap<unknown, number>;
151
155
  private rowsPromise;
152
- readonly paginationRowModel: Promise<RowModel<T>>;
156
+ private _paginationRowModel;
157
+ get paginationRowModel(): RowModel<T>;
158
+ private readonly _keyCache;
159
+ private _cachedKey;
160
+ private readonly _pendingSelectionChanges;
161
+ private _flushScheduled;
162
+ private _flushPendingSelectionChanges;
163
+ private _schedulePendingFlush;
153
164
  readonly selectionContains: (item: T) => Promise<boolean>;
154
- readonly addInvalidValue: (item: T) => Promise<void>;
155
- readonly removeInvalidValue: (item: T) => Promise<void>;
165
+ readonly addInvalidValue: (item: T) => void | Promise<void>;
166
+ readonly removeInvalidValue: (item: T) => void | Promise<void>;
156
167
  readonly setOpenModalCoordinates: (coordinates: CellCoordinatesWithoutColumn | null) => void;
157
- readonly toggleHighlightedRow: (item: T) => Promise<void>;
168
+ readonly toggleHighlightedRow: (item: T) => void | Promise<void>;
158
169
  readonly resetHighlightedRows: () => void;
159
- readonly toggleSelected: (item: T) => Promise<void>;
160
- readonly setSelected: (item: T, selected: boolean) => Promise<void>;
170
+ readonly toggleSelected: (item: T) => void | Promise<void>;
171
+ readonly setSelected: (item: T, selected: boolean) => void | Promise<void>;
161
172
  setSelectionMap: (set: SvelteMap<RowIdType, T>) => void;
162
173
  readonly updateSelectedItems: () => Promise<void>;
163
174
  readonly resetSelectedItems: () => void;
164
- private lastIncrement;
165
- readonly recalculateAllSelectedInCurrentPage: () => void;
166
175
  readonly selectAllInCurrentPage: () => Promise<void>;
167
- readonly onHighlightAll: () => void;
176
+ readonly onHighlightAll: () => void | Promise<void>;
168
177
  readonly onEditCell: (item: T, coords: CellCoordinates, value: unknown) => Promise<void>;
169
178
  readonly handleTdKeydown: (e: KeyboardEvent) => void;
170
179
  private handleKeystroke;
@@ -183,20 +192,20 @@ export declare class TableContext<T extends object, RowIdType extends Primitive>
183
192
  readonly startFocus: (coords: CellCoordinates) => void;
184
193
  readonly moveFocus: (coords: CellCoordinates, startEditing?: boolean) => void;
185
194
  readonly unfocus: () => void;
186
- readonly focusToTop: () => Promise<void>;
187
- readonly focusToBottom: () => Promise<void> | undefined;
188
- readonly moveToBottom: () => Promise<void>;
189
- readonly moveToTop: () => Promise<void>;
190
- readonly moveFocusUp: () => Promise<void>;
191
- readonly setFocusUp: () => Promise<void>;
192
- readonly tabForward: () => Promise<void>;
195
+ readonly focusToTop: () => void;
196
+ readonly focusToBottom: () => void;
197
+ readonly moveToBottom: () => void;
198
+ readonly moveToTop: () => void;
199
+ readonly moveFocusUp: () => void;
200
+ readonly setFocusUp: () => void;
201
+ readonly tabForward: () => void;
193
202
  readonly tabBackward: () => void;
194
203
  readonly focusToStart: () => void;
195
204
  readonly moveToStart: () => void;
196
205
  readonly focusToEnd: () => void;
197
206
  readonly moveToEnd: () => void;
198
- readonly moveFocusDown: () => Promise<void>;
199
- readonly setFocusDown: () => Promise<void>;
207
+ readonly moveFocusDown: () => void;
208
+ readonly setFocusDown: () => void;
200
209
  readonly moveFocusLeft: () => void;
201
210
  readonly setFocusLeft: () => void;
202
211
  readonly moveFocusRight: () => void;