@gtkx/components 1.2.2 → 1.4.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.
- package/README.md +6 -4
- package/dist/internal/collection.d.ts +3 -1
- package/dist/internal/collection.d.ts.map +1 -1
- package/dist/internal/collection.js +16 -7
- package/dist/internal/collection.js.map +1 -1
- package/dist/internal/selection.d.ts.map +1 -1
- package/dist/internal/selection.js +75 -34
- package/dist/internal/selection.js.map +1 -1
- package/dist/internal/tree-order.d.ts +7 -2
- package/dist/internal/tree-order.d.ts.map +1 -1
- package/dist/internal/tree-order.js +21 -4
- package/dist/internal/tree-order.js.map +1 -1
- package/package.json +9 -9
- package/src/internal/collection.ts +22 -8
- package/src/internal/selection.tsx +107 -40
- package/src/internal/tree-order.ts +34 -5
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<h1 align="center">GTKX</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
The React framework for Linux.<br />
|
|
9
|
-
Write native
|
|
10
|
-
|
|
8
|
+
The React framework for Linux, built on GTK.<br />
|
|
9
|
+
Write native GTK4 desktop applications with React and TypeScript.
|
|
10
|
+
Real GTK widgets, the GNOME stack, and standard web tooling.
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
@@ -98,7 +98,7 @@ GTK4 is mature, and GtkBuilder XML can lay out a static interface, but nothing r
|
|
|
98
98
|
- a React reconciler that exposes every GObject as a JSX element,
|
|
99
99
|
- a CLI for scaffolding, development, and production builds,
|
|
100
100
|
- a dev server with Fast Refresh that patches your running UI in place,
|
|
101
|
-
- CSS-in-JS styling and high-level list, grid, and dialog components,
|
|
101
|
+
- CSS-in-JS styling, React Spring animations, React Navigation stack, tab, drawer, and split view navigators, and high-level list, grid, and dialog components,
|
|
102
102
|
- a Testing Library-style API for querying and driving your widgets in tests,
|
|
103
103
|
- and a Model Context Protocol (MCP) server that exposes your live app to AI agents.
|
|
104
104
|
|
|
@@ -157,6 +157,8 @@ Explore the [example apps](https://github.com/gtkx-org/gtkx/tree/main/examples):
|
|
|
157
157
|
- [`hello-world`](https://github.com/gtkx-org/gtkx/tree/main/examples/hello-world): the counter above.
|
|
158
158
|
- [`gtk-demo`](https://github.com/gtkx-org/gtkx/tree/main/examples/gtk-demo): a React port of the official GTK4 widget showcase, covering lists, dialogs, gestures, CSS, and OpenGL.
|
|
159
159
|
- [`browser`](https://github.com/gtkx-org/gtkx/tree/main/examples/browser): a WebKitWebView-based web browser.
|
|
160
|
+
- [`animations`](https://github.com/gtkx-org/gtkx/tree/main/examples/animations): a tour of `@gtkx/animated`, React Spring animations driven by the GTK frame clock.
|
|
161
|
+
- [`navigation`](https://github.com/gtkx-org/gtkx/tree/main/examples/navigation): a tour of `@gtkx/navigation`, React Navigation's stack, tab, and drawer navigators rendered with libadwaita.
|
|
160
162
|
- [`tutorial`](https://github.com/gtkx-org/gtkx/tree/main/examples/tutorial): the Tasks app the documentation builds.
|
|
161
163
|
|
|
162
164
|
## Status
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { ListItem } from "../types.js";
|
|
2
2
|
import type { CollectionIndex } from "./collection-index.js";
|
|
3
3
|
import type { CollectionModel, SlotRef } from "./collection-model.js";
|
|
4
|
+
import type { MatchedRows } from "./tree-order.js";
|
|
4
5
|
type Collection = Pick<CollectionModel, "model" | "expansion" | "rowAt"> & {
|
|
5
6
|
isTree: boolean;
|
|
6
7
|
itemAt: (ref: SlotRef) => ListItem | undefined;
|
|
7
8
|
sectionFor: (levelPath: string) => unknown;
|
|
8
9
|
idAt: (position: number) => string | null;
|
|
10
|
+
idsAt: (positions: number[]) => string[];
|
|
9
11
|
pathAt: (position: number) => string | null;
|
|
10
12
|
positionFor: (id: string) => number;
|
|
11
|
-
|
|
13
|
+
rowsFor: (ids: string[]) => MatchedRows;
|
|
12
14
|
};
|
|
13
15
|
declare function isCollectionIdle(collection: Collection): boolean;
|
|
14
16
|
declare function createCollection(collectionModel: CollectionModel, index: CollectionIndex): Collection;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAInD,KAAK,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC,GAAG;IACvE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;IAC/C,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC1C,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;IACzC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,WAAW,CAAC;CAC3C,CAAC;AA2DF,iBAAS,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAIzD;AAED,iBAAS,gBAAgB,CAAC,eAAe,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAc9F;AAED,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,CAAC"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { slotPathAt, slotRefFor } from "./collection-model.js";
|
|
2
|
-
import {
|
|
3
|
-
const
|
|
4
|
-
|
|
2
|
+
import { findIds, findRows } from "./tree-order.js";
|
|
3
|
+
const NO_ROWS = { positions: [], ids: [] };
|
|
4
|
+
const NO_IDS = [];
|
|
5
|
+
function rowsFor(collectionModel, ids) {
|
|
5
6
|
if (ids.length === 0) {
|
|
6
|
-
return
|
|
7
|
+
return NO_ROWS;
|
|
7
8
|
}
|
|
8
9
|
const { expansion } = collectionModel;
|
|
9
|
-
return
|
|
10
|
+
return findRows(expansion.index, expansion.slots, new Set(ids));
|
|
10
11
|
}
|
|
11
12
|
function positionFor(collectionModel, id) {
|
|
12
|
-
const [first = -1] =
|
|
13
|
+
const [first = -1] = rowsFor(collectionModel, [id]).positions;
|
|
13
14
|
return first;
|
|
14
15
|
}
|
|
15
16
|
function refAt(collectionModel, position) {
|
|
@@ -32,6 +33,13 @@ function pathAt(collectionModel, position) {
|
|
|
32
33
|
}
|
|
33
34
|
return slotPathAt(ref.store, ref.slot);
|
|
34
35
|
}
|
|
36
|
+
function idsAt(collectionModel, positions) {
|
|
37
|
+
if (positions.length === 0) {
|
|
38
|
+
return NO_IDS;
|
|
39
|
+
}
|
|
40
|
+
const { expansion } = collectionModel;
|
|
41
|
+
return findIds(expansion.index, expansion.slots, new Set(positions));
|
|
42
|
+
}
|
|
35
43
|
function isCollectionIdle(collection) {
|
|
36
44
|
const { expansion } = collection;
|
|
37
45
|
return !expansion.isApplying && !expansion.isSyncing;
|
|
@@ -45,9 +53,10 @@ function createCollection(collectionModel, index) {
|
|
|
45
53
|
itemAt: (ref) => itemAt(index, ref),
|
|
46
54
|
sectionFor: index.sectionFor,
|
|
47
55
|
idAt: (position) => idAt(collectionModel, index, position),
|
|
56
|
+
idsAt: (positions) => idsAt(collectionModel, positions),
|
|
48
57
|
pathAt: (position) => pathAt(collectionModel, position),
|
|
49
58
|
positionFor: (id) => positionFor(collectionModel, id),
|
|
50
|
-
|
|
59
|
+
rowsFor: (ids) => rowsFor(collectionModel, ids),
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
62
|
export { createCollection, isCollectionIdle };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ListItem } from \"../types.js\";\nimport type { CollectionIndex } from \"./collection-index.js\";\nimport type { CollectionModel, SlotRef } from \"./collection-model.js\";\nimport type { MatchedRows } from \"./tree-order.js\";\nimport { slotPathAt, slotRefFor } from \"./collection-model.js\";\nimport { findIds, findRows } from \"./tree-order.js\";\n\ntype Collection = Pick<CollectionModel, \"model\" | \"expansion\" | \"rowAt\"> & {\n isTree: boolean;\n itemAt: (ref: SlotRef) => ListItem | undefined;\n sectionFor: (levelPath: string) => unknown;\n idAt: (position: number) => string | null;\n idsAt: (positions: number[]) => string[];\n pathAt: (position: number) => string | null;\n positionFor: (id: string) => number;\n rowsFor: (ids: string[]) => MatchedRows;\n};\n\nconst NO_ROWS: MatchedRows = { positions: [], ids: [] };\nconst NO_IDS: string[] = [];\n\nfunction rowsFor(collectionModel: CollectionModel, ids: string[]): MatchedRows {\n if (ids.length === 0) {\n return NO_ROWS;\n }\n\n const { expansion } = collectionModel;\n\n return findRows(expansion.index, expansion.slots, new Set(ids));\n}\n\nfunction positionFor(collectionModel: CollectionModel, id: string): number {\n const [first = -1] = rowsFor(collectionModel, [id]).positions;\n\n return first;\n}\n\nfunction refAt(collectionModel: CollectionModel, position: number): SlotRef | null {\n if (position < 0) {\n return null;\n }\n\n return slotRefFor(collectionModel.model.getItem(position));\n}\n\nfunction itemAt(index: CollectionIndex, ref: SlotRef): ListItem | undefined {\n return index.itemAt(ref.store.level.path, ref.slot);\n}\n\nfunction idAt(collectionModel: CollectionModel, index: CollectionIndex, position: number): string | null {\n const ref = refAt(collectionModel, position);\n\n return ref === null ? null : (itemAt(index, ref)?.id ?? null);\n}\n\nfunction pathAt(collectionModel: CollectionModel, position: number): string | null {\n const ref = refAt(collectionModel, position);\n\n if (ref === null) {\n return null;\n }\n\n return slotPathAt(ref.store, ref.slot);\n}\n\nfunction idsAt(collectionModel: CollectionModel, positions: number[]): string[] {\n if (positions.length === 0) {\n return NO_IDS;\n }\n\n const { expansion } = collectionModel;\n\n return findIds(expansion.index, expansion.slots, new Set(positions));\n}\n\nfunction isCollectionIdle(collection: Collection): boolean {\n const { expansion } = collection;\n\n return !expansion.isApplying && !expansion.isSyncing;\n}\n\nfunction createCollection(collectionModel: CollectionModel, index: CollectionIndex): Collection {\n return {\n model: collectionModel.model,\n expansion: collectionModel.expansion,\n isTree: index.isTree,\n rowAt: collectionModel.rowAt,\n itemAt: (ref) => itemAt(index, ref),\n sectionFor: index.sectionFor,\n idAt: (position) => idAt(collectionModel, index, position),\n idsAt: (positions) => idsAt(collectionModel, positions),\n pathAt: (position) => pathAt(collectionModel, position),\n positionFor: (id) => positionFor(collectionModel, id),\n rowsFor: (ids) => rowsFor(collectionModel, ids),\n };\n}\n\nexport { createCollection, isCollectionIdle, type Collection };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAMzD,KAAK,gBAAgB,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAClE,aAAa,CAAC,EAAE,GAAG,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC;IACrD,cAAc,EAAE,kBAAkB,CAAC;CACtC,CAAC;AAyOF,iBAAS,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAwB7D;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -5,35 +5,42 @@ import { useState } from "react";
|
|
|
5
5
|
import { isCollectionIdle } from "./collection.js";
|
|
6
6
|
import { useControlledSync } from "./controlled-sync.js";
|
|
7
7
|
import { joinParts } from "./keys.js";
|
|
8
|
+
function contiguousPositions(bitset, size) {
|
|
9
|
+
const first = bitset.getMinimum();
|
|
10
|
+
if (bitset.getMaximum() - first + 1 !== size) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return Array.from({ length: size }, (_, offset) => first + offset);
|
|
14
|
+
}
|
|
8
15
|
function selectedPositions(selection) {
|
|
9
16
|
const bitset = selection.getSelection();
|
|
10
17
|
const size = Number(bitset.getSize());
|
|
18
|
+
if (size === 0) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
const contiguous = contiguousPositions(bitset, size);
|
|
22
|
+
if (contiguous !== null) {
|
|
23
|
+
return contiguous;
|
|
24
|
+
}
|
|
11
25
|
const positions = [];
|
|
12
26
|
for (let index = 0; index < size; index++) {
|
|
13
27
|
positions.push(bitset.getNth(index));
|
|
14
28
|
}
|
|
15
29
|
return positions;
|
|
16
30
|
}
|
|
17
|
-
function
|
|
18
|
-
const ids = [];
|
|
19
|
-
for (const position of positions) {
|
|
20
|
-
const id = collection.idAt(position);
|
|
21
|
-
if (id !== null) {
|
|
22
|
-
ids.push(id);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return ids;
|
|
26
|
-
}
|
|
27
|
-
function plannedPositions(context, ids) {
|
|
31
|
+
function plannedSelection(context, ids) {
|
|
28
32
|
const { selection, collection } = context;
|
|
29
33
|
if (selection === null || selection instanceof Gtk.NoSelection) {
|
|
30
34
|
return null;
|
|
31
35
|
}
|
|
32
|
-
const
|
|
33
|
-
if (ids.length > 0 && positions.length === 0) {
|
|
36
|
+
const rows = collection.rowsFor(ids);
|
|
37
|
+
if (ids.length > 0 && rows.positions.length === 0) {
|
|
34
38
|
return null;
|
|
35
39
|
}
|
|
36
|
-
|
|
40
|
+
if (selection instanceof Gtk.SingleSelection) {
|
|
41
|
+
return { positions: rows.positions.slice(0, 1), ids: rows.ids.slice(0, 1) };
|
|
42
|
+
}
|
|
43
|
+
return rows;
|
|
37
44
|
}
|
|
38
45
|
function applySingleSelection(selection, positions) {
|
|
39
46
|
const [first] = positions;
|
|
@@ -43,19 +50,35 @@ function applySingleSelection(selection, positions) {
|
|
|
43
50
|
}
|
|
44
51
|
selection.selectItem(first, true);
|
|
45
52
|
}
|
|
46
|
-
function
|
|
47
|
-
const
|
|
53
|
+
function positionRuns(positions) {
|
|
54
|
+
const runs = [];
|
|
48
55
|
for (const position of positions) {
|
|
49
|
-
|
|
56
|
+
const last = runs.at(-1);
|
|
57
|
+
if (last !== undefined && last.start + last.length === position) {
|
|
58
|
+
last.length += 1;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
runs.push({ start: position, length: 1 });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return runs;
|
|
65
|
+
}
|
|
66
|
+
function positionBitset(positions) {
|
|
67
|
+
const selected = Gtk.Bitset.newEmpty();
|
|
68
|
+
for (const run of positionRuns(positions)) {
|
|
69
|
+
selected.addRange(run.start, run.length);
|
|
50
70
|
}
|
|
51
|
-
|
|
71
|
+
return selected;
|
|
52
72
|
}
|
|
53
73
|
function applySelection(selection, positions) {
|
|
74
|
+
const selected = positionBitset(positions);
|
|
54
75
|
if (selection instanceof Gtk.SingleSelection) {
|
|
55
76
|
applySingleSelection(selection, positions);
|
|
56
|
-
return;
|
|
57
77
|
}
|
|
58
|
-
|
|
78
|
+
else {
|
|
79
|
+
selection.setSelection(selected, Gtk.Bitset.newRange(0, selection.getNItems()));
|
|
80
|
+
}
|
|
81
|
+
return selected;
|
|
59
82
|
}
|
|
60
83
|
function reportSelection(context, ids) {
|
|
61
84
|
const key = joinParts(ids);
|
|
@@ -78,40 +101,58 @@ function selectionElement(mode, props) {
|
|
|
78
101
|
}
|
|
79
102
|
return _jsx(GtkSingleSelection, { ...props, autoselect: false, canUnselect: true });
|
|
80
103
|
}
|
|
104
|
+
function didApplyPlanned(context, selection, positions) {
|
|
105
|
+
const { state } = context;
|
|
106
|
+
state.isApplying = true;
|
|
107
|
+
try {
|
|
108
|
+
const selected = applySelection(selection, positions);
|
|
109
|
+
return selection.getSelection().equals(selected);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
state.isApplying = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
81
115
|
function applyControlledSelection(context, selectedIds) {
|
|
82
116
|
const { selection, collection } = context;
|
|
83
117
|
if (selection === null) {
|
|
84
118
|
return;
|
|
85
119
|
}
|
|
86
|
-
const planned =
|
|
87
|
-
if (planned !== null) {
|
|
88
|
-
|
|
120
|
+
const planned = plannedSelection(context, selectedIds ?? []);
|
|
121
|
+
if (planned !== null && didApplyPlanned(context, selection, planned.positions)) {
|
|
122
|
+
reportSelection(context, planned.ids);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
reportSelection(context, collection.idsAt(selectedPositions(selection)));
|
|
126
|
+
}
|
|
127
|
+
function reportDrift(context, selectedIds, positions, onDrift) {
|
|
128
|
+
const planned = plannedSelection(context, selectedIds ?? []);
|
|
129
|
+
if (planned !== null && hasSelectionDrifted(positions, planned.positions)) {
|
|
130
|
+
onDrift();
|
|
89
131
|
}
|
|
90
|
-
reportSelection(context, idsAt(collection, selectedPositions(selection)));
|
|
91
132
|
}
|
|
92
133
|
function observeSelection(context, selectedIds, onDrift) {
|
|
93
|
-
const { selection, collection } = context;
|
|
94
|
-
if (selection === null) {
|
|
134
|
+
const { selection, collection, state } = context;
|
|
135
|
+
if (selection === null || state.isApplying) {
|
|
95
136
|
return;
|
|
96
137
|
}
|
|
97
138
|
const positions = selectedPositions(selection);
|
|
98
|
-
reportSelection(context, idsAt(
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
const planned = plannedPositions(context, selectedIds ?? []);
|
|
103
|
-
if (planned !== null && hasSelectionDrifted(positions, planned)) {
|
|
104
|
-
onDrift();
|
|
139
|
+
reportSelection(context, collection.idsAt(positions));
|
|
140
|
+
if (isCollectionIdle(collection)) {
|
|
141
|
+
reportDrift(context, selectedIds, positions, onDrift);
|
|
105
142
|
}
|
|
106
143
|
}
|
|
107
144
|
function newLastSelection() {
|
|
108
145
|
return { selection: null, key: null };
|
|
109
146
|
}
|
|
147
|
+
function newApplyState() {
|
|
148
|
+
return { isApplying: false };
|
|
149
|
+
}
|
|
110
150
|
function useSelection(options) {
|
|
111
151
|
const { collection, selectedIds, onSelectionChanged, selectionMode } = options;
|
|
112
152
|
const [selection, setSelection] = useState(null);
|
|
113
153
|
const [last] = useState(newLastSelection);
|
|
114
|
-
const
|
|
154
|
+
const [state] = useState(newApplyState);
|
|
155
|
+
const context = { selection, collection, last, state, onSelectionChanged };
|
|
115
156
|
const markDrift = useControlledSync({
|
|
116
157
|
ids: selectedIds,
|
|
117
158
|
collection,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"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","sourcesContent":["import type * as Gio from \"@gtkx/gi/gio\";\nimport type { ReactElement } from \"react\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { GtkMultiSelection, GtkNoSelection, GtkSingleSelection } from \"@gtkx/jsx/gtk\";\nimport { useState } from \"react\";\nimport type { Collection } from \"./collection.js\";\nimport type { ItemsChangeHandler } from \"./expansion.js\";\nimport { isCollectionIdle } from \"./collection.js\";\nimport { useControlledSync } from \"./controlled-sync.js\";\nimport { joinParts } from \"./keys.js\";\n\ntype SelectionOptions = {\n collection: Collection;\n selectedIds?: string[] | null | undefined;\n onSelectionChanged?: ((ids: string[]) => void) | null | undefined;\n selectionMode?: Gtk.SelectionMode | null | undefined;\n onItemsChanged: ItemsChangeHandler;\n};\n\ntype LastSelection = {\n selection: Gtk.SelectionModel | null;\n key: string | null;\n};\n\ntype SelectionContext = {\n selection: Gtk.SelectionModel | null;\n collection: Collection;\n last: LastSelection;\n onSelectionChanged?: ((ids: string[]) => void) | null | undefined;\n};\n\ntype SelectionElementProps = {\n ref: (value: Gtk.SelectionModel | null) => void;\n model: Gio.ListModel;\n onSelectionChanged: () => void;\n onItemsChanged: ItemsChangeHandler;\n};\n\nfunction selectedPositions(selection: Gtk.SelectionModel): number[] {\n const bitset = selection.getSelection();\n const size = Number(bitset.getSize());\n const positions: number[] = [];\n\n for (let index = 0; index < size; index++) {\n positions.push(bitset.getNth(index));\n }\n\n return positions;\n}\n\nfunction idsAt(collection: Collection, positions: number[]): string[] {\n const ids: string[] = [];\n\n for (const position of positions) {\n const id = collection.idAt(position);\n\n if (id !== null) {\n ids.push(id);\n }\n }\n\n return ids;\n}\n\nfunction plannedPositions(context: SelectionContext, ids: string[]): number[] | null {\n const { selection, collection } = context;\n\n if (selection === null || selection instanceof Gtk.NoSelection) {\n return null;\n }\n\n const positions = collection.positionsFor(ids);\n\n if (ids.length > 0 && positions.length === 0) {\n return null;\n }\n\n return selection instanceof Gtk.SingleSelection ? positions.slice(0, 1) : positions;\n}\n\nfunction applySingleSelection(selection: Gtk.SingleSelection, positions: number[]): void {\n const [first] = positions;\n\n if (first === undefined) {\n selection.unselectAll();\n\n return;\n }\n\n selection.selectItem(first, true);\n}\n\nfunction applyMultiSelection(selection: Gtk.SelectionModel, positions: number[]): void {\n const selected = Gtk.Bitset.newEmpty();\n\n for (const position of positions) {\n selected.add(position);\n }\n\n selection.setSelection(selected, Gtk.Bitset.newRange(0, selection.getNItems()));\n}\n\nfunction applySelection(selection: Gtk.SelectionModel, positions: number[]): void {\n if (selection instanceof Gtk.SingleSelection) {\n applySingleSelection(selection, positions);\n\n return;\n }\n\n applyMultiSelection(selection, positions);\n}\n\nfunction reportSelection(context: SelectionContext, ids: string[]): void {\n const key = joinParts(ids);\n\n if (context.last.selection === context.selection && context.last.key === key) {\n return;\n }\n\n context.last.selection = context.selection;\n context.last.key = key;\n context.onSelectionChanged?.(ids);\n}\n\nfunction hasSelectionDrifted(actual: number[], planned: number[]): boolean {\n return actual.length !== planned.length || planned.some((position, index) => actual[index] !== position);\n}\n\nfunction selectionElement(mode: Gtk.SelectionMode | null | undefined, props: SelectionElementProps): ReactElement {\n if (mode === Gtk.SelectionMode.MULTIPLE) {\n return <GtkMultiSelection {...props} />;\n }\n\n if (mode === Gtk.SelectionMode.NONE) {\n return <GtkNoSelection {...props} />;\n }\n\n return <GtkSingleSelection {...props} autoselect={false} canUnselect />;\n}\n\nfunction applyControlledSelection(context: SelectionContext, selectedIds: string[] | null | undefined): void {\n const { selection, collection } = context;\n\n if (selection === null) {\n return;\n }\n\n const planned = plannedPositions(context, selectedIds ?? []);\n\n if (planned !== null) {\n applySelection(selection, planned);\n }\n\n reportSelection(context, idsAt(collection, selectedPositions(selection)));\n}\n\nfunction observeSelection(\n context: SelectionContext,\n selectedIds: string[] | null | undefined,\n onDrift: () => void,\n): void {\n const { selection, collection } = context;\n\n if (selection === null) {\n return;\n }\n\n const positions = selectedPositions(selection);\n reportSelection(context, idsAt(collection, positions));\n\n if (!isCollectionIdle(collection)) {\n return;\n }\n\n const planned = plannedPositions(context, selectedIds ?? []);\n\n if (planned !== null && hasSelectionDrifted(positions, planned)) {\n onDrift();\n }\n}\n\nfunction newLastSelection(): LastSelection {\n return { selection: null, key: null };\n}\n\nfunction useSelection(options: SelectionOptions): ReactElement {\n const { collection, selectedIds, onSelectionChanged, selectionMode } = options;\n const [selection, setSelection] = useState<Gtk.SelectionModel | null>(null);\n const [last] = useState<LastSelection>(newLastSelection);\n const context: SelectionContext = { selection, collection, last, onSelectionChanged };\n\n const markDrift = useControlledSync({\n ids: selectedIds,\n collection,\n widget: selection,\n apply: (ids) => {\n applyControlledSelection(context, ids);\n },\n });\n\n return selectionElement(selectionMode, {\n ref: setSelection,\n model: collection.model,\n onSelectionChanged: () => {\n observeSelection(context, selectedIds, markDrift);\n },\n onItemsChanged: options.onItemsChanged,\n });\n}\n\nexport { useSelection };\n"]}
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":";AAEA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAIjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAuCtC,SAAS,mBAAmB,CAAC,MAAkB,EAAE,IAAY;IACzD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAElC,IAAI,MAAM,CAAC,UAAU,EAAE,GAAG,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,iBAAiB,CAAC,SAA6B;IACpD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAEtC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAErD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,UAAU,CAAC;IACtB,CAAC;IAED,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACxC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAyB,EAAE,GAAa;IAC9D,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE1C,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,YAAY,GAAG,CAAC,WAAW,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,SAAS,YAAY,GAAG,CAAC,eAAe,EAAE,CAAC;QAC3C,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAChF,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,oBAAoB,CAAC,SAA8B,EAAE,SAAmB;IAC7E,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAE1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACtB,SAAS,CAAC,WAAW,EAAE,CAAC;QAExB,OAAO;IACX,CAAC;IAED,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,YAAY,CAAC,SAAmB;IACrC,MAAM,IAAI,GAAkB,EAAE,CAAC;IAE/B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEzB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC9D,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,SAAmB;IACvC,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAEvC,KAAK,MAAM,GAAG,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,SAA6B,EAAE,SAAmB;IACtE,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAE3C,IAAI,SAAS,YAAY,GAAG,CAAC,eAAe,EAAE,CAAC;QAC3C,oBAAoB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACJ,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,SAAS,eAAe,CAAC,OAAyB,EAAE,GAAa;IAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE3B,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;QAC3E,OAAO;IACX,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACvB,OAAO,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAgB,EAAE,OAAiB;IAC5D,OAAO,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,CAAC;AAC7G,CAAC;AAED,SAAS,gBAAgB,CAAC,IAA0C,EAAE,KAA4B;IAC9F,IAAI,IAAI,KAAK,GAAG,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;QACtC,OAAO,KAAC,iBAAiB,OAAK,KAAK,GAAI,CAAC;IAC5C,CAAC;IAED,IAAI,IAAI,KAAK,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;QAClC,OAAO,KAAC,cAAc,OAAK,KAAK,GAAI,CAAC;IACzC,CAAC;IAED,OAAO,KAAC,kBAAkB,OAAK,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,SAAG,CAAC;AAC5E,CAAC;AAED,SAAS,eAAe,CAAC,OAAyB,EAAE,SAA6B,EAAE,SAAmB;IAClG,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;IAExB,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAEtD,OAAO,SAAS,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;YAAS,CAAC;QACP,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC;IAC7B,CAAC;AACL,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAyB,EAAE,WAAwC;IACjG,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE1C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO;IACX,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;IAE7D,IAAI,OAAO,KAAK,IAAI,IAAI,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7E,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAEtC,OAAO;IACX,CAAC;IAED,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,WAAW,CAChB,OAAyB,EACzB,WAAwC,EACxC,SAAmB,EACnB,OAAmB;IAEnB,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;IAE7D,IAAI,OAAO,KAAK,IAAI,IAAI,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CACrB,OAAyB,EACzB,WAAwC,EACxC,OAAmB;IAEnB,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAEjD,IAAI,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACzC,OAAO;IACX,CAAC;IAED,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/C,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAEtD,IAAI,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1D,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB;IACrB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,aAAa;IAClB,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,YAAY,CAAC,OAAyB;IAC3C,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAC/E,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAA4B,IAAI,CAAC,CAAC;IAC5E,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAgB,gBAAgB,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAa,aAAa,CAAC,CAAC;IACpD,MAAM,OAAO,GAAqB,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAE7F,MAAM,SAAS,GAAG,iBAAiB,CAAC;QAChC,GAAG,EAAE,WAAW;QAChB,UAAU;QACV,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;YACX,wBAAwB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC3C,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,gBAAgB,CAAC,aAAa,EAAE;QACnC,GAAG,EAAE,YAAY;QACjB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,kBAAkB,EAAE,GAAG,EAAE;YACrB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,cAAc,EAAE,OAAO,CAAC,cAAc;KACzC,CAAC,CAAC;AACP,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import type * as Gio from \"@gtkx/gi/gio\";\nimport type { ReactElement } from \"react\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { GtkMultiSelection, GtkNoSelection, GtkSingleSelection } from \"@gtkx/jsx/gtk\";\nimport { useState } from \"react\";\nimport type { Collection } from \"./collection.js\";\nimport type { ItemsChangeHandler } from \"./expansion.js\";\nimport type { MatchedRows } from \"./tree-order.js\";\nimport { isCollectionIdle } from \"./collection.js\";\nimport { useControlledSync } from \"./controlled-sync.js\";\nimport { joinParts } from \"./keys.js\";\n\ntype SelectionOptions = {\n collection: Collection;\n selectedIds?: string[] | null | undefined;\n onSelectionChanged?: ((ids: string[]) => void) | null | undefined;\n selectionMode?: Gtk.SelectionMode | null | undefined;\n onItemsChanged: ItemsChangeHandler;\n};\n\ntype LastSelection = {\n selection: Gtk.SelectionModel | null;\n key: string | null;\n};\n\ntype ApplyState = {\n isApplying: boolean;\n};\n\ntype SelectionContext = {\n selection: Gtk.SelectionModel | null;\n collection: Collection;\n last: LastSelection;\n state: ApplyState;\n onSelectionChanged?: ((ids: string[]) => void) | null | undefined;\n};\n\ntype PositionRun = {\n start: number;\n length: number;\n};\n\ntype SelectionElementProps = {\n ref: (value: Gtk.SelectionModel | null) => void;\n model: Gio.ListModel;\n onSelectionChanged: () => void;\n onItemsChanged: ItemsChangeHandler;\n};\n\nfunction contiguousPositions(bitset: Gtk.Bitset, size: number): number[] | null {\n const first = bitset.getMinimum();\n\n if (bitset.getMaximum() - first + 1 !== size) {\n return null;\n }\n\n return Array.from({ length: size }, (_, offset) => first + offset);\n}\n\nfunction selectedPositions(selection: Gtk.SelectionModel): number[] {\n const bitset = selection.getSelection();\n const size = Number(bitset.getSize());\n\n if (size === 0) {\n return [];\n }\n\n const contiguous = contiguousPositions(bitset, size);\n\n if (contiguous !== null) {\n return contiguous;\n }\n\n const positions: number[] = [];\n\n for (let index = 0; index < size; index++) {\n positions.push(bitset.getNth(index));\n }\n\n return positions;\n}\n\nfunction plannedSelection(context: SelectionContext, ids: string[]): MatchedRows | null {\n const { selection, collection } = context;\n\n if (selection === null || selection instanceof Gtk.NoSelection) {\n return null;\n }\n\n const rows = collection.rowsFor(ids);\n\n if (ids.length > 0 && rows.positions.length === 0) {\n return null;\n }\n\n if (selection instanceof Gtk.SingleSelection) {\n return { positions: rows.positions.slice(0, 1), ids: rows.ids.slice(0, 1) };\n }\n\n return rows;\n}\n\nfunction applySingleSelection(selection: Gtk.SingleSelection, positions: number[]): void {\n const [first] = positions;\n\n if (first === undefined) {\n selection.unselectAll();\n\n return;\n }\n\n selection.selectItem(first, true);\n}\n\nfunction positionRuns(positions: number[]): PositionRun[] {\n const runs: PositionRun[] = [];\n\n for (const position of positions) {\n const last = runs.at(-1);\n\n if (last !== undefined && last.start + last.length === position) {\n last.length += 1;\n } else {\n runs.push({ start: position, length: 1 });\n }\n }\n\n return runs;\n}\n\nfunction positionBitset(positions: number[]): Gtk.Bitset {\n const selected = Gtk.Bitset.newEmpty();\n\n for (const run of positionRuns(positions)) {\n selected.addRange(run.start, run.length);\n }\n\n return selected;\n}\n\nfunction applySelection(selection: Gtk.SelectionModel, positions: number[]): Gtk.Bitset {\n const selected = positionBitset(positions);\n\n if (selection instanceof Gtk.SingleSelection) {\n applySingleSelection(selection, positions);\n } else {\n selection.setSelection(selected, Gtk.Bitset.newRange(0, selection.getNItems()));\n }\n\n return selected;\n}\n\nfunction reportSelection(context: SelectionContext, ids: string[]): void {\n const key = joinParts(ids);\n\n if (context.last.selection === context.selection && context.last.key === key) {\n return;\n }\n\n context.last.selection = context.selection;\n context.last.key = key;\n context.onSelectionChanged?.(ids);\n}\n\nfunction hasSelectionDrifted(actual: number[], planned: number[]): boolean {\n return actual.length !== planned.length || planned.some((position, index) => actual[index] !== position);\n}\n\nfunction selectionElement(mode: Gtk.SelectionMode | null | undefined, props: SelectionElementProps): ReactElement {\n if (mode === Gtk.SelectionMode.MULTIPLE) {\n return <GtkMultiSelection {...props} />;\n }\n\n if (mode === Gtk.SelectionMode.NONE) {\n return <GtkNoSelection {...props} />;\n }\n\n return <GtkSingleSelection {...props} autoselect={false} canUnselect />;\n}\n\nfunction didApplyPlanned(context: SelectionContext, selection: Gtk.SelectionModel, positions: number[]): boolean {\n const { state } = context;\n state.isApplying = true;\n\n try {\n const selected = applySelection(selection, positions);\n\n return selection.getSelection().equals(selected);\n } finally {\n state.isApplying = false;\n }\n}\n\nfunction applyControlledSelection(context: SelectionContext, selectedIds: string[] | null | undefined): void {\n const { selection, collection } = context;\n\n if (selection === null) {\n return;\n }\n\n const planned = plannedSelection(context, selectedIds ?? []);\n\n if (planned !== null && didApplyPlanned(context, selection, planned.positions)) {\n reportSelection(context, planned.ids);\n\n return;\n }\n\n reportSelection(context, collection.idsAt(selectedPositions(selection)));\n}\n\nfunction reportDrift(\n context: SelectionContext,\n selectedIds: string[] | null | undefined,\n positions: number[],\n onDrift: () => void,\n): void {\n const planned = plannedSelection(context, selectedIds ?? []);\n\n if (planned !== null && hasSelectionDrifted(positions, planned.positions)) {\n onDrift();\n }\n}\n\nfunction observeSelection(\n context: SelectionContext,\n selectedIds: string[] | null | undefined,\n onDrift: () => void,\n): void {\n const { selection, collection, state } = context;\n\n if (selection === null || state.isApplying) {\n return;\n }\n\n const positions = selectedPositions(selection);\n reportSelection(context, collection.idsAt(positions));\n\n if (isCollectionIdle(collection)) {\n reportDrift(context, selectedIds, positions, onDrift);\n }\n}\n\nfunction newLastSelection(): LastSelection {\n return { selection: null, key: null };\n}\n\nfunction newApplyState(): ApplyState {\n return { isApplying: false };\n}\n\nfunction useSelection(options: SelectionOptions): ReactElement {\n const { collection, selectedIds, onSelectionChanged, selectionMode } = options;\n const [selection, setSelection] = useState<Gtk.SelectionModel | null>(null);\n const [last] = useState<LastSelection>(newLastSelection);\n const [state] = useState<ApplyState>(newApplyState);\n const context: SelectionContext = { selection, collection, last, state, onSelectionChanged };\n\n const markDrift = useControlledSync({\n ids: selectedIds,\n collection,\n widget: selection,\n apply: (ids) => {\n applyControlledSelection(context, ids);\n },\n });\n\n return selectionElement(selectionMode, {\n ref: setSelection,\n model: collection.model,\n onSelectionChanged: () => {\n observeSelection(context, selectedIds, markDrift);\n },\n onItemsChanged: options.onItemsChanged,\n });\n}\n\nexport { useSelection };\n"]}
|
|
@@ -11,6 +11,10 @@ type VisibleOrder = {
|
|
|
11
11
|
expandedPaths: string[];
|
|
12
12
|
expandedIds: string[];
|
|
13
13
|
};
|
|
14
|
+
type MatchedRows = {
|
|
15
|
+
positions: number[];
|
|
16
|
+
ids: string[];
|
|
17
|
+
};
|
|
14
18
|
type RowVisitor = (row: VisibleRow) => void;
|
|
15
19
|
type WalkOptions = {
|
|
16
20
|
index: CollectionIndex;
|
|
@@ -21,6 +25,7 @@ type WalkOptions = {
|
|
|
21
25
|
declare function walkVisible(options: WalkOptions): VisibleOrder;
|
|
22
26
|
declare function expandedPathsFor(index: CollectionIndex, ids: Set<string>): Set<string>;
|
|
23
27
|
declare function buildVisibleOrder(index: CollectionIndex, slots: SlotMap): VisibleOrder;
|
|
24
|
-
declare function
|
|
25
|
-
|
|
28
|
+
declare function findRows(index: CollectionIndex, slots: SlotMap, ids: Set<string>): MatchedRows;
|
|
29
|
+
declare function findIds(index: CollectionIndex, slots: SlotMap, positions: Set<number>): string[];
|
|
30
|
+
export { buildVisibleOrder, expandedPathsFor, findIds, findRows, walkVisible, type MatchedRows, type VisibleOrder, type WalkOptions, };
|
|
26
31
|
//# sourceMappingURL=tree-order.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-order.d.ts","sourceRoot":"","sources":["../../src/internal/tree-order.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAS,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,KAAK,UAAU,GAAG;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,KAAK,YAAY,GAAG;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAC;AAE5C,KAAK,WAAW,GAAG;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAClC,CAAC;AA2FF,iBAAS,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAevD;AA0BD,iBAAS,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAuB/E;AAED,iBAAS,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAE/E;AAED,iBAAS,
|
|
1
|
+
{"version":3,"file":"tree-order.d.ts","sourceRoot":"","sources":["../../src/internal/tree-order.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAS,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,KAAK,UAAU,GAAG;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,KAAK,YAAY,GAAG;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,KAAK,WAAW,GAAG;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAC;AAE5C,KAAK,WAAW,GAAG;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAClC,CAAC;AA2FF,iBAAS,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAevD;AA0BD,iBAAS,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAuB/E;AAED,iBAAS,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAE/E;AAED,iBAAS,QAAQ,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,WAAW,CAkBvF;AAED,iBAAS,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAgBzF;AAED,OAAO,EACH,iBAAiB,EACjB,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,GACnB,CAAC"}
|
|
@@ -92,18 +92,35 @@ function expandedPathsFor(index, ids) {
|
|
|
92
92
|
function buildVisibleOrder(index, slots) {
|
|
93
93
|
return walkVisible({ index, slots });
|
|
94
94
|
}
|
|
95
|
-
function
|
|
95
|
+
function findRows(index, slots, ids) {
|
|
96
|
+
const positions = [];
|
|
96
97
|
const found = [];
|
|
97
98
|
walkVisible({
|
|
98
99
|
index,
|
|
99
100
|
slots,
|
|
100
101
|
visit: (row) => {
|
|
101
|
-
if (ids.has(row.item.id)) {
|
|
102
|
-
|
|
102
|
+
if (!ids.has(row.item.id)) {
|
|
103
|
+
return;
|
|
103
104
|
}
|
|
105
|
+
positions.push(row.position);
|
|
106
|
+
found.push(row.item.id);
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
return { positions, ids: found };
|
|
110
|
+
}
|
|
111
|
+
function findIds(index, slots, positions) {
|
|
112
|
+
const found = [];
|
|
113
|
+
walkVisible({
|
|
114
|
+
index,
|
|
115
|
+
slots,
|
|
116
|
+
visit: (row) => {
|
|
117
|
+
if (!positions.has(row.position)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
found.push(row.item.id);
|
|
104
121
|
},
|
|
105
122
|
});
|
|
106
123
|
return found;
|
|
107
124
|
}
|
|
108
|
-
export { buildVisibleOrder, expandedPathsFor,
|
|
125
|
+
export { buildVisibleOrder, expandedPathsFor, findIds, findRows, walkVisible, };
|
|
109
126
|
//# sourceMappingURL=tree-order.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-order.js","sourceRoot":"","sources":["../../src/internal/tree-order.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ListItem } from \"../types.js\";\nimport type { CollectionIndex, Level } from \"./collection-index.js\";\nimport type { SlotMap } from \"./slots.js\";\n\ntype VisibleRow = {\n item: ListItem;\n position: number;\n isOpen: boolean;\n isMarked: boolean;\n};\n\ntype VisibleOrder = {\n expandedPaths: string[];\n expandedIds: string[];\n};\n\ntype RowVisitor = (row: VisibleRow) => void;\n\ntype WalkOptions = {\n index: CollectionIndex;\n slots: SlotMap;\n marks?: SlotMap | undefined;\n visit?: RowVisitor | undefined;\n};\n\ntype LevelFrame = {\n level: Level;\n cursor: number;\n};\n\ntype WalkFrame = LevelFrame & {\n open: Set<number> | undefined;\n marked: Set<number> | undefined;\n};\n\ntype WalkState = {\n options: WalkOptions;\n stack: WalkFrame[];\n row: VisibleRow;\n order: VisibleOrder;\n};\n\ntype OpenFrame = LevelFrame & {\n owner: ListItem | null;\n};\n\ntype OpenWalk = {\n index: CollectionIndex;\n ids: Set<string>;\n paths: Set<string>;\n stack: OpenFrame[];\n ancestors: Set<ListItem>;\n};\n\nconst NO_ITEM: ListItem = { id: \"\", value: undefined };\n\nfunction advanceStack<F extends LevelFrame>(\n stack: F[],\n onSlot: (frame: F, slot: number) => void,\n onLeave?: (frame: F) => void,\n): void {\n const frame = stack.at(-1);\n\n if (frame === undefined) {\n return;\n }\n\n if (frame.cursor >= frame.level.items.length) {\n stack.pop();\n onLeave?.(frame);\n\n return;\n }\n\n const slot = frame.cursor;\n frame.cursor += 1;\n onSlot(frame, slot);\n}\n\nfunction frameFor(options: WalkOptions, level: Level): WalkFrame {\n return { level, cursor: 0, open: options.slots.get(level.path), marked: options.marks?.get(level.path) };\n}\n\nfunction descend(state: WalkState, level: Level, slot: number): void {\n const child = state.options.index.childLevel(level, slot);\n\n if (child === undefined) {\n return;\n }\n\n state.order.expandedPaths.push(child.path);\n state.order.expandedIds.push(state.row.item.id);\n state.stack.push(frameFor(state.options, child));\n}\n\nfunction visitSlot(state: WalkState, frame: WalkFrame, slot: number): void {\n const item = frame.level.items[slot];\n\n if (item === undefined) {\n return;\n }\n\n const { row } = state;\n row.item = item;\n row.isOpen = frame.open?.has(slot) ?? false;\n row.isMarked = frame.marked?.has(slot) ?? false;\n state.options.visit?.(row);\n row.position += 1;\n\n if (row.isOpen) {\n descend(state, frame.level, slot);\n }\n}\n\nfunction walkVisible(options: WalkOptions): VisibleOrder {\n const order: VisibleOrder = { expandedPaths: [], expandedIds: [] };\n const row: VisibleRow = { item: NO_ITEM, position: 0, isOpen: false, isMarked: false };\n const frames = options.index.groups.map((level) => frameFor(options, level));\n const state: WalkState = { options, stack: frames.toReversed(), row, order };\n\n const onSlot = (frame: WalkFrame, slot: number): void => {\n visitSlot(state, frame, slot);\n };\n\n while (state.stack.length > 0) {\n advanceStack(state.stack, onSlot);\n }\n\n return order;\n}\n\nfunction openSlot(walk: OpenWalk, frame: OpenFrame, slot: number): void {\n const item = frame.level.items[slot];\n\n if (item === undefined || !walk.ids.has(item.id) || walk.ancestors.has(item)) {\n return;\n }\n\n const child = walk.index.childLevel(frame.level, slot);\n\n if (child === undefined) {\n return;\n }\n\n walk.paths.add(child.path);\n walk.ancestors.add(item);\n walk.stack.push({ level: child, cursor: 0, owner: item });\n}\n\nfunction leaveOpenFrame(walk: OpenWalk, frame: OpenFrame): void {\n if (frame.owner !== null) {\n walk.ancestors.delete(frame.owner);\n }\n}\n\nfunction expandedPathsFor(index: CollectionIndex, ids: Set<string>): Set<string> {\n const paths: Set<string> = new Set();\n\n if (ids.size === 0 || !index.isTree) {\n return paths;\n }\n\n const frames = index.groups.map((level) => ({ level, cursor: 0, owner: null }));\n const walk: OpenWalk = { index, ids, paths, stack: frames.toReversed(), ancestors: new Set() };\n\n const onSlot = (frame: OpenFrame, slot: number): void => {\n openSlot(walk, frame, slot);\n };\n\n const onLeave = (frame: OpenFrame): void => {\n leaveOpenFrame(walk, frame);\n };\n\n while (walk.stack.length > 0) {\n advanceStack(walk.stack, onSlot, onLeave);\n }\n\n return paths;\n}\n\nfunction buildVisibleOrder(index: CollectionIndex, slots: SlotMap): VisibleOrder {\n return walkVisible({ index, slots });\n}\n\nfunction findPositions(index: CollectionIndex, slots: SlotMap, ids: Set<string>): number[] {\n const found: number[] = [];\n\n walkVisible({\n index,\n slots,\n visit: (row) => {\n if (ids.has(row.item.id)) {\n found.push(row.position);\n }\n },\n });\n\n return found;\n}\n\nexport {\n buildVisibleOrder,\n expandedPathsFor,\n findPositions,\n walkVisible,\n type VisibleOrder,\n type WalkOptions,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"tree-order.js","sourceRoot":"","sources":["../../src/internal/tree-order.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ListItem } from \"../types.js\";\nimport type { CollectionIndex, Level } from \"./collection-index.js\";\nimport type { SlotMap } from \"./slots.js\";\n\ntype VisibleRow = {\n item: ListItem;\n position: number;\n isOpen: boolean;\n isMarked: boolean;\n};\n\ntype VisibleOrder = {\n expandedPaths: string[];\n expandedIds: string[];\n};\n\ntype MatchedRows = {\n positions: number[];\n ids: string[];\n};\n\ntype RowVisitor = (row: VisibleRow) => void;\n\ntype WalkOptions = {\n index: CollectionIndex;\n slots: SlotMap;\n marks?: SlotMap | undefined;\n visit?: RowVisitor | undefined;\n};\n\ntype LevelFrame = {\n level: Level;\n cursor: number;\n};\n\ntype WalkFrame = LevelFrame & {\n open: Set<number> | undefined;\n marked: Set<number> | undefined;\n};\n\ntype WalkState = {\n options: WalkOptions;\n stack: WalkFrame[];\n row: VisibleRow;\n order: VisibleOrder;\n};\n\ntype OpenFrame = LevelFrame & {\n owner: ListItem | null;\n};\n\ntype OpenWalk = {\n index: CollectionIndex;\n ids: Set<string>;\n paths: Set<string>;\n stack: OpenFrame[];\n ancestors: Set<ListItem>;\n};\n\nconst NO_ITEM: ListItem = { id: \"\", value: undefined };\n\nfunction advanceStack<F extends LevelFrame>(\n stack: F[],\n onSlot: (frame: F, slot: number) => void,\n onLeave?: (frame: F) => void,\n): void {\n const frame = stack.at(-1);\n\n if (frame === undefined) {\n return;\n }\n\n if (frame.cursor >= frame.level.items.length) {\n stack.pop();\n onLeave?.(frame);\n\n return;\n }\n\n const slot = frame.cursor;\n frame.cursor += 1;\n onSlot(frame, slot);\n}\n\nfunction frameFor(options: WalkOptions, level: Level): WalkFrame {\n return { level, cursor: 0, open: options.slots.get(level.path), marked: options.marks?.get(level.path) };\n}\n\nfunction descend(state: WalkState, level: Level, slot: number): void {\n const child = state.options.index.childLevel(level, slot);\n\n if (child === undefined) {\n return;\n }\n\n state.order.expandedPaths.push(child.path);\n state.order.expandedIds.push(state.row.item.id);\n state.stack.push(frameFor(state.options, child));\n}\n\nfunction visitSlot(state: WalkState, frame: WalkFrame, slot: number): void {\n const item = frame.level.items[slot];\n\n if (item === undefined) {\n return;\n }\n\n const { row } = state;\n row.item = item;\n row.isOpen = frame.open?.has(slot) ?? false;\n row.isMarked = frame.marked?.has(slot) ?? false;\n state.options.visit?.(row);\n row.position += 1;\n\n if (row.isOpen) {\n descend(state, frame.level, slot);\n }\n}\n\nfunction walkVisible(options: WalkOptions): VisibleOrder {\n const order: VisibleOrder = { expandedPaths: [], expandedIds: [] };\n const row: VisibleRow = { item: NO_ITEM, position: 0, isOpen: false, isMarked: false };\n const frames = options.index.groups.map((level) => frameFor(options, level));\n const state: WalkState = { options, stack: frames.toReversed(), row, order };\n\n const onSlot = (frame: WalkFrame, slot: number): void => {\n visitSlot(state, frame, slot);\n };\n\n while (state.stack.length > 0) {\n advanceStack(state.stack, onSlot);\n }\n\n return order;\n}\n\nfunction openSlot(walk: OpenWalk, frame: OpenFrame, slot: number): void {\n const item = frame.level.items[slot];\n\n if (item === undefined || !walk.ids.has(item.id) || walk.ancestors.has(item)) {\n return;\n }\n\n const child = walk.index.childLevel(frame.level, slot);\n\n if (child === undefined) {\n return;\n }\n\n walk.paths.add(child.path);\n walk.ancestors.add(item);\n walk.stack.push({ level: child, cursor: 0, owner: item });\n}\n\nfunction leaveOpenFrame(walk: OpenWalk, frame: OpenFrame): void {\n if (frame.owner !== null) {\n walk.ancestors.delete(frame.owner);\n }\n}\n\nfunction expandedPathsFor(index: CollectionIndex, ids: Set<string>): Set<string> {\n const paths: Set<string> = new Set();\n\n if (ids.size === 0 || !index.isTree) {\n return paths;\n }\n\n const frames = index.groups.map((level) => ({ level, cursor: 0, owner: null }));\n const walk: OpenWalk = { index, ids, paths, stack: frames.toReversed(), ancestors: new Set() };\n\n const onSlot = (frame: OpenFrame, slot: number): void => {\n openSlot(walk, frame, slot);\n };\n\n const onLeave = (frame: OpenFrame): void => {\n leaveOpenFrame(walk, frame);\n };\n\n while (walk.stack.length > 0) {\n advanceStack(walk.stack, onSlot, onLeave);\n }\n\n return paths;\n}\n\nfunction buildVisibleOrder(index: CollectionIndex, slots: SlotMap): VisibleOrder {\n return walkVisible({ index, slots });\n}\n\nfunction findRows(index: CollectionIndex, slots: SlotMap, ids: Set<string>): MatchedRows {\n const positions: number[] = [];\n const found: string[] = [];\n\n walkVisible({\n index,\n slots,\n visit: (row) => {\n if (!ids.has(row.item.id)) {\n return;\n }\n\n positions.push(row.position);\n found.push(row.item.id);\n },\n });\n\n return { positions, ids: found };\n}\n\nfunction findIds(index: CollectionIndex, slots: SlotMap, positions: Set<number>): string[] {\n const found: string[] = [];\n\n walkVisible({\n index,\n slots,\n visit: (row) => {\n if (!positions.has(row.position)) {\n return;\n }\n\n found.push(row.item.id);\n },\n });\n\n return found;\n}\n\nexport {\n buildVisibleOrder,\n expandedPathsFor,\n findIds,\n findRows,\n walkVisible,\n type MatchedRows,\n type VisibleOrder,\n type WalkOptions,\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/components",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "High-level
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "High-level GTK components: collection views, menus, containers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
7
7
|
"gtk",
|
|
@@ -54,18 +54,18 @@
|
|
|
54
54
|
"node": ">=24"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@gtkx/react": "1.
|
|
58
|
-
"@gtkx/
|
|
59
|
-
"@gtkx/
|
|
57
|
+
"@gtkx/react": "1.4.0",
|
|
58
|
+
"@gtkx/utils": "1.4.0",
|
|
59
|
+
"@gtkx/runtime": "1.4.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"react": "^19.2"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"vitest": "^4.1.
|
|
66
|
-
"@gtkx/config": "1.
|
|
67
|
-
"@gtkx/testing": "1.
|
|
68
|
-
"@gtkx/vitest": "1.
|
|
65
|
+
"vitest": "^4.1.11",
|
|
66
|
+
"@gtkx/config": "1.4.0",
|
|
67
|
+
"@gtkx/testing": "1.4.0",
|
|
68
|
+
"@gtkx/vitest": "1.4.0"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"release": "tsx ../../scripts/release-package.ts"
|
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
import type { ListItem } from "../types.js";
|
|
2
2
|
import type { CollectionIndex } from "./collection-index.js";
|
|
3
3
|
import type { CollectionModel, SlotRef } from "./collection-model.js";
|
|
4
|
+
import type { MatchedRows } from "./tree-order.js";
|
|
4
5
|
import { slotPathAt, slotRefFor } from "./collection-model.js";
|
|
5
|
-
import {
|
|
6
|
+
import { findIds, findRows } from "./tree-order.js";
|
|
6
7
|
|
|
7
8
|
type Collection = Pick<CollectionModel, "model" | "expansion" | "rowAt"> & {
|
|
8
9
|
isTree: boolean;
|
|
9
10
|
itemAt: (ref: SlotRef) => ListItem | undefined;
|
|
10
11
|
sectionFor: (levelPath: string) => unknown;
|
|
11
12
|
idAt: (position: number) => string | null;
|
|
13
|
+
idsAt: (positions: number[]) => string[];
|
|
12
14
|
pathAt: (position: number) => string | null;
|
|
13
15
|
positionFor: (id: string) => number;
|
|
14
|
-
|
|
16
|
+
rowsFor: (ids: string[]) => MatchedRows;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
const
|
|
19
|
+
const NO_ROWS: MatchedRows = { positions: [], ids: [] };
|
|
20
|
+
const NO_IDS: string[] = [];
|
|
18
21
|
|
|
19
|
-
function
|
|
22
|
+
function rowsFor(collectionModel: CollectionModel, ids: string[]): MatchedRows {
|
|
20
23
|
if (ids.length === 0) {
|
|
21
|
-
return
|
|
24
|
+
return NO_ROWS;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
const { expansion } = collectionModel;
|
|
25
28
|
|
|
26
|
-
return
|
|
29
|
+
return findRows(expansion.index, expansion.slots, new Set(ids));
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
function positionFor(collectionModel: CollectionModel, id: string): number {
|
|
30
|
-
const [first = -1] =
|
|
33
|
+
const [first = -1] = rowsFor(collectionModel, [id]).positions;
|
|
31
34
|
|
|
32
35
|
return first;
|
|
33
36
|
}
|
|
@@ -60,6 +63,16 @@ function pathAt(collectionModel: CollectionModel, position: number): string | nu
|
|
|
60
63
|
return slotPathAt(ref.store, ref.slot);
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
function idsAt(collectionModel: CollectionModel, positions: number[]): string[] {
|
|
67
|
+
if (positions.length === 0) {
|
|
68
|
+
return NO_IDS;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { expansion } = collectionModel;
|
|
72
|
+
|
|
73
|
+
return findIds(expansion.index, expansion.slots, new Set(positions));
|
|
74
|
+
}
|
|
75
|
+
|
|
63
76
|
function isCollectionIdle(collection: Collection): boolean {
|
|
64
77
|
const { expansion } = collection;
|
|
65
78
|
|
|
@@ -75,9 +88,10 @@ function createCollection(collectionModel: CollectionModel, index: CollectionInd
|
|
|
75
88
|
itemAt: (ref) => itemAt(index, ref),
|
|
76
89
|
sectionFor: index.sectionFor,
|
|
77
90
|
idAt: (position) => idAt(collectionModel, index, position),
|
|
91
|
+
idsAt: (positions) => idsAt(collectionModel, positions),
|
|
78
92
|
pathAt: (position) => pathAt(collectionModel, position),
|
|
79
93
|
positionFor: (id) => positionFor(collectionModel, id),
|
|
80
|
-
|
|
94
|
+
rowsFor: (ids) => rowsFor(collectionModel, ids),
|
|
81
95
|
};
|
|
82
96
|
}
|
|
83
97
|
|
|
@@ -5,6 +5,7 @@ import { GtkMultiSelection, GtkNoSelection, GtkSingleSelection } from "@gtkx/jsx
|
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import type { Collection } from "./collection.js";
|
|
7
7
|
import type { ItemsChangeHandler } from "./expansion.js";
|
|
8
|
+
import type { MatchedRows } from "./tree-order.js";
|
|
8
9
|
import { isCollectionIdle } from "./collection.js";
|
|
9
10
|
import { useControlledSync } from "./controlled-sync.js";
|
|
10
11
|
import { joinParts } from "./keys.js";
|
|
@@ -22,13 +23,23 @@ type LastSelection = {
|
|
|
22
23
|
key: string | null;
|
|
23
24
|
};
|
|
24
25
|
|
|
26
|
+
type ApplyState = {
|
|
27
|
+
isApplying: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
type SelectionContext = {
|
|
26
31
|
selection: Gtk.SelectionModel | null;
|
|
27
32
|
collection: Collection;
|
|
28
33
|
last: LastSelection;
|
|
34
|
+
state: ApplyState;
|
|
29
35
|
onSelectionChanged?: ((ids: string[]) => void) | null | undefined;
|
|
30
36
|
};
|
|
31
37
|
|
|
38
|
+
type PositionRun = {
|
|
39
|
+
start: number;
|
|
40
|
+
length: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
32
43
|
type SelectionElementProps = {
|
|
33
44
|
ref: (value: Gtk.SelectionModel | null) => void;
|
|
34
45
|
model: Gio.ListModel;
|
|
@@ -36,46 +47,57 @@ type SelectionElementProps = {
|
|
|
36
47
|
onItemsChanged: ItemsChangeHandler;
|
|
37
48
|
};
|
|
38
49
|
|
|
50
|
+
function contiguousPositions(bitset: Gtk.Bitset, size: number): number[] | null {
|
|
51
|
+
const first = bitset.getMinimum();
|
|
52
|
+
|
|
53
|
+
if (bitset.getMaximum() - first + 1 !== size) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return Array.from({ length: size }, (_, offset) => first + offset);
|
|
58
|
+
}
|
|
59
|
+
|
|
39
60
|
function selectedPositions(selection: Gtk.SelectionModel): number[] {
|
|
40
61
|
const bitset = selection.getSelection();
|
|
41
62
|
const size = Number(bitset.getSize());
|
|
42
|
-
const positions: number[] = [];
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
if (size === 0) {
|
|
65
|
+
return [];
|
|
46
66
|
}
|
|
47
67
|
|
|
48
|
-
|
|
49
|
-
}
|
|
68
|
+
const contiguous = contiguousPositions(bitset, size);
|
|
50
69
|
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
if (contiguous !== null) {
|
|
71
|
+
return contiguous;
|
|
72
|
+
}
|
|
53
73
|
|
|
54
|
-
|
|
55
|
-
const id = collection.idAt(position);
|
|
74
|
+
const positions: number[] = [];
|
|
56
75
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
76
|
+
for (let index = 0; index < size; index++) {
|
|
77
|
+
positions.push(bitset.getNth(index));
|
|
60
78
|
}
|
|
61
79
|
|
|
62
|
-
return
|
|
80
|
+
return positions;
|
|
63
81
|
}
|
|
64
82
|
|
|
65
|
-
function
|
|
83
|
+
function plannedSelection(context: SelectionContext, ids: string[]): MatchedRows | null {
|
|
66
84
|
const { selection, collection } = context;
|
|
67
85
|
|
|
68
86
|
if (selection === null || selection instanceof Gtk.NoSelection) {
|
|
69
87
|
return null;
|
|
70
88
|
}
|
|
71
89
|
|
|
72
|
-
const
|
|
90
|
+
const rows = collection.rowsFor(ids);
|
|
73
91
|
|
|
74
|
-
if (ids.length > 0 && positions.length === 0) {
|
|
92
|
+
if (ids.length > 0 && rows.positions.length === 0) {
|
|
75
93
|
return null;
|
|
76
94
|
}
|
|
77
95
|
|
|
78
|
-
|
|
96
|
+
if (selection instanceof Gtk.SingleSelection) {
|
|
97
|
+
return { positions: rows.positions.slice(0, 1), ids: rows.ids.slice(0, 1) };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return rows;
|
|
79
101
|
}
|
|
80
102
|
|
|
81
103
|
function applySingleSelection(selection: Gtk.SingleSelection, positions: number[]): void {
|
|
@@ -90,24 +112,42 @@ function applySingleSelection(selection: Gtk.SingleSelection, positions: number[
|
|
|
90
112
|
selection.selectItem(first, true);
|
|
91
113
|
}
|
|
92
114
|
|
|
93
|
-
function
|
|
94
|
-
const
|
|
115
|
+
function positionRuns(positions: number[]): PositionRun[] {
|
|
116
|
+
const runs: PositionRun[] = [];
|
|
95
117
|
|
|
96
118
|
for (const position of positions) {
|
|
97
|
-
|
|
119
|
+
const last = runs.at(-1);
|
|
120
|
+
|
|
121
|
+
if (last !== undefined && last.start + last.length === position) {
|
|
122
|
+
last.length += 1;
|
|
123
|
+
} else {
|
|
124
|
+
runs.push({ start: position, length: 1 });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return runs;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function positionBitset(positions: number[]): Gtk.Bitset {
|
|
132
|
+
const selected = Gtk.Bitset.newEmpty();
|
|
133
|
+
|
|
134
|
+
for (const run of positionRuns(positions)) {
|
|
135
|
+
selected.addRange(run.start, run.length);
|
|
98
136
|
}
|
|
99
137
|
|
|
100
|
-
|
|
138
|
+
return selected;
|
|
101
139
|
}
|
|
102
140
|
|
|
103
|
-
function applySelection(selection: Gtk.SelectionModel, positions: number[]):
|
|
141
|
+
function applySelection(selection: Gtk.SelectionModel, positions: number[]): Gtk.Bitset {
|
|
142
|
+
const selected = positionBitset(positions);
|
|
143
|
+
|
|
104
144
|
if (selection instanceof Gtk.SingleSelection) {
|
|
105
145
|
applySingleSelection(selection, positions);
|
|
106
|
-
|
|
107
|
-
|
|
146
|
+
} else {
|
|
147
|
+
selection.setSelection(selected, Gtk.Bitset.newRange(0, selection.getNItems()));
|
|
108
148
|
}
|
|
109
149
|
|
|
110
|
-
|
|
150
|
+
return selected;
|
|
111
151
|
}
|
|
112
152
|
|
|
113
153
|
function reportSelection(context: SelectionContext, ids: string[]): void {
|
|
@@ -138,6 +178,19 @@ function selectionElement(mode: Gtk.SelectionMode | null | undefined, props: Sel
|
|
|
138
178
|
return <GtkSingleSelection {...props} autoselect={false} canUnselect />;
|
|
139
179
|
}
|
|
140
180
|
|
|
181
|
+
function didApplyPlanned(context: SelectionContext, selection: Gtk.SelectionModel, positions: number[]): boolean {
|
|
182
|
+
const { state } = context;
|
|
183
|
+
state.isApplying = true;
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const selected = applySelection(selection, positions);
|
|
187
|
+
|
|
188
|
+
return selection.getSelection().equals(selected);
|
|
189
|
+
} finally {
|
|
190
|
+
state.isApplying = false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
141
194
|
function applyControlledSelection(context: SelectionContext, selectedIds: string[] | null | undefined): void {
|
|
142
195
|
const { selection, collection } = context;
|
|
143
196
|
|
|
@@ -145,37 +198,46 @@ function applyControlledSelection(context: SelectionContext, selectedIds: string
|
|
|
145
198
|
return;
|
|
146
199
|
}
|
|
147
200
|
|
|
148
|
-
const planned =
|
|
201
|
+
const planned = plannedSelection(context, selectedIds ?? []);
|
|
149
202
|
|
|
150
|
-
if (planned !== null) {
|
|
151
|
-
|
|
203
|
+
if (planned !== null && didApplyPlanned(context, selection, planned.positions)) {
|
|
204
|
+
reportSelection(context, planned.ids);
|
|
205
|
+
|
|
206
|
+
return;
|
|
152
207
|
}
|
|
153
208
|
|
|
154
|
-
reportSelection(context, idsAt(
|
|
209
|
+
reportSelection(context, collection.idsAt(selectedPositions(selection)));
|
|
155
210
|
}
|
|
156
211
|
|
|
157
|
-
function
|
|
212
|
+
function reportDrift(
|
|
158
213
|
context: SelectionContext,
|
|
159
214
|
selectedIds: string[] | null | undefined,
|
|
215
|
+
positions: number[],
|
|
160
216
|
onDrift: () => void,
|
|
161
217
|
): void {
|
|
162
|
-
const
|
|
218
|
+
const planned = plannedSelection(context, selectedIds ?? []);
|
|
163
219
|
|
|
164
|
-
if (
|
|
165
|
-
|
|
220
|
+
if (planned !== null && hasSelectionDrifted(positions, planned.positions)) {
|
|
221
|
+
onDrift();
|
|
166
222
|
}
|
|
223
|
+
}
|
|
167
224
|
|
|
168
|
-
|
|
169
|
-
|
|
225
|
+
function observeSelection(
|
|
226
|
+
context: SelectionContext,
|
|
227
|
+
selectedIds: string[] | null | undefined,
|
|
228
|
+
onDrift: () => void,
|
|
229
|
+
): void {
|
|
230
|
+
const { selection, collection, state } = context;
|
|
170
231
|
|
|
171
|
-
if (
|
|
232
|
+
if (selection === null || state.isApplying) {
|
|
172
233
|
return;
|
|
173
234
|
}
|
|
174
235
|
|
|
175
|
-
const
|
|
236
|
+
const positions = selectedPositions(selection);
|
|
237
|
+
reportSelection(context, collection.idsAt(positions));
|
|
176
238
|
|
|
177
|
-
if (
|
|
178
|
-
onDrift
|
|
239
|
+
if (isCollectionIdle(collection)) {
|
|
240
|
+
reportDrift(context, selectedIds, positions, onDrift);
|
|
179
241
|
}
|
|
180
242
|
}
|
|
181
243
|
|
|
@@ -183,11 +245,16 @@ function newLastSelection(): LastSelection {
|
|
|
183
245
|
return { selection: null, key: null };
|
|
184
246
|
}
|
|
185
247
|
|
|
248
|
+
function newApplyState(): ApplyState {
|
|
249
|
+
return { isApplying: false };
|
|
250
|
+
}
|
|
251
|
+
|
|
186
252
|
function useSelection(options: SelectionOptions): ReactElement {
|
|
187
253
|
const { collection, selectedIds, onSelectionChanged, selectionMode } = options;
|
|
188
254
|
const [selection, setSelection] = useState<Gtk.SelectionModel | null>(null);
|
|
189
255
|
const [last] = useState<LastSelection>(newLastSelection);
|
|
190
|
-
const
|
|
256
|
+
const [state] = useState<ApplyState>(newApplyState);
|
|
257
|
+
const context: SelectionContext = { selection, collection, last, state, onSelectionChanged };
|
|
191
258
|
|
|
192
259
|
const markDrift = useControlledSync({
|
|
193
260
|
ids: selectedIds,
|
|
@@ -14,6 +14,11 @@ type VisibleOrder = {
|
|
|
14
14
|
expandedIds: string[];
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
type MatchedRows = {
|
|
18
|
+
positions: number[];
|
|
19
|
+
ids: string[];
|
|
20
|
+
};
|
|
21
|
+
|
|
17
22
|
type RowVisitor = (row: VisibleRow) => void;
|
|
18
23
|
|
|
19
24
|
type WalkOptions = {
|
|
@@ -182,16 +187,38 @@ function buildVisibleOrder(index: CollectionIndex, slots: SlotMap): VisibleOrder
|
|
|
182
187
|
return walkVisible({ index, slots });
|
|
183
188
|
}
|
|
184
189
|
|
|
185
|
-
function
|
|
186
|
-
const
|
|
190
|
+
function findRows(index: CollectionIndex, slots: SlotMap, ids: Set<string>): MatchedRows {
|
|
191
|
+
const positions: number[] = [];
|
|
192
|
+
const found: string[] = [];
|
|
187
193
|
|
|
188
194
|
walkVisible({
|
|
189
195
|
index,
|
|
190
196
|
slots,
|
|
191
197
|
visit: (row) => {
|
|
192
|
-
if (ids.has(row.item.id)) {
|
|
193
|
-
|
|
198
|
+
if (!ids.has(row.item.id)) {
|
|
199
|
+
return;
|
|
194
200
|
}
|
|
201
|
+
|
|
202
|
+
positions.push(row.position);
|
|
203
|
+
found.push(row.item.id);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return { positions, ids: found };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function findIds(index: CollectionIndex, slots: SlotMap, positions: Set<number>): string[] {
|
|
211
|
+
const found: string[] = [];
|
|
212
|
+
|
|
213
|
+
walkVisible({
|
|
214
|
+
index,
|
|
215
|
+
slots,
|
|
216
|
+
visit: (row) => {
|
|
217
|
+
if (!positions.has(row.position)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
found.push(row.item.id);
|
|
195
222
|
},
|
|
196
223
|
});
|
|
197
224
|
|
|
@@ -201,8 +228,10 @@ function findPositions(index: CollectionIndex, slots: SlotMap, ids: Set<string>)
|
|
|
201
228
|
export {
|
|
202
229
|
buildVisibleOrder,
|
|
203
230
|
expandedPathsFor,
|
|
204
|
-
|
|
231
|
+
findIds,
|
|
232
|
+
findRows,
|
|
205
233
|
walkVisible,
|
|
234
|
+
type MatchedRows,
|
|
206
235
|
type VisibleOrder,
|
|
207
236
|
type WalkOptions,
|
|
208
237
|
};
|