@lavalogic/scoria 0.37.50 → 0.37.52
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.
|
@@ -711,14 +711,35 @@ export class FocusState {
|
|
|
711
711
|
* the call-site signature clean for cell consumers.
|
|
712
712
|
*/
|
|
713
713
|
getFocusDetailsFor(cell) {
|
|
714
|
-
|
|
715
|
-
if (
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
714
|
+
const cached = this._focusDetailsCache.get(cell);
|
|
715
|
+
if (cached) {
|
|
716
|
+
return cached;
|
|
717
|
+
}
|
|
718
|
+
// The TableContext ref is supplied via a thunk to keep this
|
|
719
|
+
// sub-context decoupled from the parent.
|
|
720
|
+
const parent = this._deps.getTableContextRef();
|
|
721
|
+
// `TableFocusDetails` exposes its whole surface as `$derived` /
|
|
722
|
+
// `$derived.by` fields. This factory is invoked by cell components
|
|
723
|
+
// from inside a `$derived(...)` expression, so constructing the
|
|
724
|
+
// instance inline would bind those deriveds to whichever cell
|
|
725
|
+
// component first missed the cache. That component is destroyed on
|
|
726
|
+
// the next re-render / scroll, leaving the cached instance's
|
|
727
|
+
// deriveds owned by a dead scope - Svelte's `derived_inert` warning,
|
|
728
|
+
// and stale focus-outline values for every later reader. Building it
|
|
729
|
+
// inside an `$effect.root` gives the deriveds a standalone owner that
|
|
730
|
+
// outlives any single cell. The root is reachable only through the
|
|
731
|
+
// `WeakMap`-cached instance, so it is collected together with the
|
|
732
|
+
// cell and needs no explicit teardown.
|
|
733
|
+
let created;
|
|
734
|
+
$effect.root(() => {
|
|
735
|
+
created = new TableFocusDetails(cell, parent);
|
|
736
|
+
});
|
|
737
|
+
if (!created) {
|
|
738
|
+
// `$effect.root` runs its callback synchronously, so this is
|
|
739
|
+
// unreachable; it exists only to narrow the type below.
|
|
740
|
+
throw new Error('getFocusDetailsFor: TableFocusDetails was not constructed');
|
|
741
|
+
}
|
|
742
|
+
this._focusDetailsCache.set(cell, created);
|
|
743
|
+
return created;
|
|
723
744
|
}
|
|
724
745
|
}
|
|
@@ -9,6 +9,18 @@ import type { SortingState as SortingStateArray } from '../DataRepository/Sortin
|
|
|
9
9
|
* pays no extra branching per comparison.
|
|
10
10
|
*/
|
|
11
11
|
export declare function comparableSortingFn<T>(id: keyof T, desc: boolean): (a: T, b: T) => -1 | 0 | 1;
|
|
12
|
+
/**
|
|
13
|
+
* Comparator factory for columns whose sort key comes from a value
|
|
14
|
+
* accessor rather than a row property. `DisplayDef` ids are synthetic, so
|
|
15
|
+
* the row carries no property under the column id — the accessor (the
|
|
16
|
+
* `col.display` `value` thunk) is the only available sort key.
|
|
17
|
+
*
|
|
18
|
+
* A Promise-returning accessor (an async `value` thunk) is not
|
|
19
|
+
* synchronously comparable, so such pairs compare equal — the column is a
|
|
20
|
+
* stable no-op unless the consumer supplies an explicit `getSortingFn`.
|
|
21
|
+
* Nullish results sort last in both directions.
|
|
22
|
+
*/
|
|
23
|
+
export declare function accessorSortingFn<T, V>(accessorFn: ((row: T, index: number) => V) | undefined, desc: boolean): (a: T, b: T) => -1 | 0 | 1;
|
|
12
24
|
/**
|
|
13
25
|
* Comparator factory for `CheckboxDef` columns. Boolean-coercion compares
|
|
14
26
|
* truthy-vs-falsy so undefined / null / 0 collapse to "unchecked".
|
|
@@ -47,6 +47,39 @@ export function comparableSortingFn(id, desc) {
|
|
|
47
47
|
return 0;
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Comparator factory for columns whose sort key comes from a value
|
|
52
|
+
* accessor rather than a row property. `DisplayDef` ids are synthetic, so
|
|
53
|
+
* the row carries no property under the column id — the accessor (the
|
|
54
|
+
* `col.display` `value` thunk) is the only available sort key.
|
|
55
|
+
*
|
|
56
|
+
* A Promise-returning accessor (an async `value` thunk) is not
|
|
57
|
+
* synchronously comparable, so such pairs compare equal — the column is a
|
|
58
|
+
* stable no-op unless the consumer supplies an explicit `getSortingFn`.
|
|
59
|
+
* Nullish results sort last in both directions.
|
|
60
|
+
*/
|
|
61
|
+
export function accessorSortingFn(accessorFn, desc) {
|
|
62
|
+
if (!accessorFn) {
|
|
63
|
+
return () => 0;
|
|
64
|
+
}
|
|
65
|
+
return (a, b) => {
|
|
66
|
+
const av = accessorFn(a, 0);
|
|
67
|
+
const bv = accessorFn(b, 0);
|
|
68
|
+
if (av == null && bv != null) {
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
if (bv == null && av != null) {
|
|
72
|
+
return -1;
|
|
73
|
+
}
|
|
74
|
+
if (av < bv) {
|
|
75
|
+
return desc ? 1 : -1;
|
|
76
|
+
}
|
|
77
|
+
if (av > bv) {
|
|
78
|
+
return desc ? -1 : 1;
|
|
79
|
+
}
|
|
80
|
+
return 0;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
50
83
|
/**
|
|
51
84
|
* Comparator factory for `CheckboxDef` columns. Boolean-coercion compares
|
|
52
85
|
* truthy-vs-falsy so undefined / null / 0 collapse to "unchecked".
|
|
@@ -175,8 +208,12 @@ export class SortingState {
|
|
|
175
208
|
if (def.getSortingFn != undefined) {
|
|
176
209
|
return def.getSortingFn(desc);
|
|
177
210
|
}
|
|
178
|
-
if (def instanceof DisplayDef
|
|
179
|
-
|
|
211
|
+
if (def instanceof DisplayDef) {
|
|
212
|
+
// `DisplayDef` ids are synthetic — the row has no property under
|
|
213
|
+
// `id` — so sort by the column's value accessor, not `row[id]`.
|
|
214
|
+
return accessorSortingFn(def.accessorFn, desc);
|
|
215
|
+
}
|
|
216
|
+
if (def instanceof TextInputDef ||
|
|
180
217
|
def instanceof NumberInputDef ||
|
|
181
218
|
def instanceof DateInputDef) {
|
|
182
219
|
return comparableSortingFn(id, desc);
|