@gtkx/components 1.0.0 → 1.1.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 +1 -1
- package/dist/internal/cells.js +1 -1
- package/dist/internal/cells.js.map +1 -1
- package/dist/internal/collection-index.d.ts +4 -3
- package/dist/internal/collection-index.d.ts.map +1 -1
- package/dist/internal/collection-index.js +50 -33
- package/dist/internal/collection-index.js.map +1 -1
- package/dist/internal/collection-model.d.ts +3 -4
- package/dist/internal/collection-model.d.ts.map +1 -1
- package/dist/internal/collection-model.js +110 -74
- package/dist/internal/collection-model.js.map +1 -1
- package/dist/internal/collection.d.ts +2 -1
- package/dist/internal/collection.d.ts.map +1 -1
- package/dist/internal/collection.js +7 -6
- package/dist/internal/collection.js.map +1 -1
- package/dist/internal/expansion.d.ts.map +1 -1
- package/dist/internal/expansion.js +11 -19
- package/dist/internal/expansion.js.map +1 -1
- package/dist/internal/tree-expansion.d.ts +1 -2
- package/dist/internal/tree-expansion.d.ts.map +1 -1
- package/dist/internal/tree-expansion.js +12 -8
- package/dist/internal/tree-expansion.js.map +1 -1
- package/dist/internal/tree-order.d.ts +3 -4
- package/dist/internal/tree-order.d.ts.map +1 -1
- package/dist/internal/tree-order.js +61 -23
- package/dist/internal/tree-order.js.map +1 -1
- package/dist/types.d.ts +3 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +17 -9
- package/src/internal/cells.tsx +1 -1
- package/src/internal/collection-index.ts +63 -39
- package/src/internal/collection-model.ts +148 -85
- package/src/internal/collection.ts +10 -8
- package/src/internal/expansion.ts +12 -24
- package/src/internal/tree-expansion.ts +14 -9
- package/src/internal/tree-order.ts +98 -28
- package/src/types.ts +3 -2
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type { ListItem } from "../types.js";
|
|
2
2
|
import type { CollectionIndex, Level } from "./collection-index.js";
|
|
3
3
|
import type { SlotMap } from "./slots.js";
|
|
4
|
-
import { encodePart } from "./keys.js";
|
|
5
4
|
|
|
6
5
|
type VisibleRow = {
|
|
7
|
-
level: Level;
|
|
8
|
-
slot: number;
|
|
9
6
|
item: ListItem;
|
|
10
7
|
position: number;
|
|
11
8
|
isOpen: boolean;
|
|
@@ -26,9 +23,12 @@ type WalkOptions = {
|
|
|
26
23
|
visit?: RowVisitor | undefined;
|
|
27
24
|
};
|
|
28
25
|
|
|
29
|
-
type
|
|
26
|
+
type LevelFrame = {
|
|
30
27
|
level: Level;
|
|
31
28
|
cursor: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type WalkFrame = LevelFrame & {
|
|
32
32
|
open: Set<number> | undefined;
|
|
33
33
|
marked: Set<number> | undefined;
|
|
34
34
|
};
|
|
@@ -40,23 +40,55 @@ type WalkState = {
|
|
|
40
40
|
order: VisibleOrder;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
type OpenFrame = LevelFrame & {
|
|
44
|
+
owner: ListItem | null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type OpenWalk = {
|
|
48
|
+
index: CollectionIndex;
|
|
49
|
+
ids: Set<string>;
|
|
50
|
+
paths: Set<string>;
|
|
51
|
+
stack: OpenFrame[];
|
|
52
|
+
ancestors: Set<ListItem>;
|
|
53
|
+
};
|
|
54
|
+
|
|
44
55
|
const NO_ITEM: ListItem = { id: "", value: undefined };
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
function advanceStack<F extends LevelFrame>(
|
|
58
|
+
stack: F[],
|
|
59
|
+
onSlot: (frame: F, slot: number) => void,
|
|
60
|
+
onLeave?: (frame: F) => void,
|
|
61
|
+
): void {
|
|
62
|
+
const frame = stack.at(-1);
|
|
63
|
+
|
|
64
|
+
if (frame === undefined) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (frame.cursor >= frame.level.items.length) {
|
|
69
|
+
stack.pop();
|
|
70
|
+
onLeave?.(frame);
|
|
71
|
+
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const slot = frame.cursor;
|
|
76
|
+
frame.cursor += 1;
|
|
77
|
+
onSlot(frame, slot);
|
|
78
|
+
}
|
|
47
79
|
|
|
48
80
|
function frameFor(options: WalkOptions, level: Level): WalkFrame {
|
|
49
81
|
return { level, cursor: 0, open: options.slots.get(level.path), marked: options.marks?.get(level.path) };
|
|
50
82
|
}
|
|
51
83
|
|
|
52
|
-
function descend(state: WalkState,
|
|
53
|
-
const child = state.options.index.
|
|
84
|
+
function descend(state: WalkState, level: Level, slot: number): void {
|
|
85
|
+
const child = state.options.index.childLevel(level, slot);
|
|
54
86
|
|
|
55
|
-
if (child === undefined
|
|
87
|
+
if (child === undefined) {
|
|
56
88
|
return;
|
|
57
89
|
}
|
|
58
90
|
|
|
59
|
-
state.order.expandedPaths.push(path);
|
|
91
|
+
state.order.expandedPaths.push(child.path);
|
|
60
92
|
state.order.expandedIds.push(state.row.item.id);
|
|
61
93
|
state.stack.push(frameFor(state.options, child));
|
|
62
94
|
}
|
|
@@ -69,8 +101,6 @@ function visitSlot(state: WalkState, frame: WalkFrame, slot: number): void {
|
|
|
69
101
|
}
|
|
70
102
|
|
|
71
103
|
const { row } = state;
|
|
72
|
-
row.level = frame.level;
|
|
73
|
-
row.slot = slot;
|
|
74
104
|
row.item = item;
|
|
75
105
|
row.isOpen = frame.open?.has(slot) ?? false;
|
|
76
106
|
row.isMarked = frame.marked?.has(slot) ?? false;
|
|
@@ -78,35 +108,74 @@ function visitSlot(state: WalkState, frame: WalkFrame, slot: number): void {
|
|
|
78
108
|
row.position += 1;
|
|
79
109
|
|
|
80
110
|
if (row.isOpen) {
|
|
81
|
-
descend(state,
|
|
111
|
+
descend(state, frame.level, slot);
|
|
82
112
|
}
|
|
83
113
|
}
|
|
84
114
|
|
|
85
|
-
function
|
|
86
|
-
const
|
|
115
|
+
function walkVisible(options: WalkOptions): VisibleOrder {
|
|
116
|
+
const order: VisibleOrder = { expandedPaths: [], expandedIds: [] };
|
|
117
|
+
const row: VisibleRow = { item: NO_ITEM, position: 0, isOpen: false, isMarked: false };
|
|
118
|
+
const frames = options.index.groups.map((level) => frameFor(options, level));
|
|
119
|
+
const state: WalkState = { options, stack: frames.toReversed(), row, order };
|
|
87
120
|
|
|
88
|
-
|
|
89
|
-
state
|
|
121
|
+
const onSlot = (frame: WalkFrame, slot: number): void => {
|
|
122
|
+
visitSlot(state, frame, slot);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
while (state.stack.length > 0) {
|
|
126
|
+
advanceStack(state.stack, onSlot);
|
|
127
|
+
}
|
|
90
128
|
|
|
129
|
+
return order;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function openSlot(walk: OpenWalk, frame: OpenFrame, slot: number): void {
|
|
133
|
+
const item = frame.level.items[slot];
|
|
134
|
+
|
|
135
|
+
if (item === undefined || !walk.ids.has(item.id) || walk.ancestors.has(item)) {
|
|
91
136
|
return;
|
|
92
137
|
}
|
|
93
138
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
139
|
+
const child = walk.index.childLevel(frame.level, slot);
|
|
140
|
+
|
|
141
|
+
if (child === undefined) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
walk.paths.add(child.path);
|
|
146
|
+
walk.ancestors.add(item);
|
|
147
|
+
walk.stack.push({ level: child, cursor: 0, owner: item });
|
|
97
148
|
}
|
|
98
149
|
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
150
|
+
function leaveOpenFrame(walk: OpenWalk, frame: OpenFrame): void {
|
|
151
|
+
if (frame.owner !== null) {
|
|
152
|
+
walk.ancestors.delete(frame.owner);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
104
155
|
|
|
105
|
-
|
|
106
|
-
|
|
156
|
+
function expandedPathsFor(index: CollectionIndex, ids: Set<string>): Set<string> {
|
|
157
|
+
const paths: Set<string> = new Set();
|
|
158
|
+
|
|
159
|
+
if (ids.size === 0 || !index.isTree) {
|
|
160
|
+
return paths;
|
|
107
161
|
}
|
|
108
162
|
|
|
109
|
-
|
|
163
|
+
const frames = index.groups.map((level) => ({ level, cursor: 0, owner: null }));
|
|
164
|
+
const walk: OpenWalk = { index, ids, paths, stack: frames.toReversed(), ancestors: new Set() };
|
|
165
|
+
|
|
166
|
+
const onSlot = (frame: OpenFrame, slot: number): void => {
|
|
167
|
+
openSlot(walk, frame, slot);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const onLeave = (frame: OpenFrame): void => {
|
|
171
|
+
leaveOpenFrame(walk, frame);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
while (walk.stack.length > 0) {
|
|
175
|
+
advanceStack(walk.stack, onSlot, onLeave);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return paths;
|
|
110
179
|
}
|
|
111
180
|
|
|
112
181
|
function buildVisibleOrder(index: CollectionIndex, slots: SlotMap): VisibleOrder {
|
|
@@ -131,6 +200,7 @@ function findPositions(index: CollectionIndex, slots: SlotMap, ids: Set<string>)
|
|
|
131
200
|
|
|
132
201
|
export {
|
|
133
202
|
buildVisibleOrder,
|
|
203
|
+
expandedPathsFor,
|
|
134
204
|
findPositions,
|
|
135
205
|
walkVisible,
|
|
136
206
|
type VisibleOrder,
|
package/src/types.ts
CHANGED
|
@@ -17,7 +17,7 @@ type ListItem<T = unknown> = {
|
|
|
17
17
|
id: string;
|
|
18
18
|
/** Payload handed to the cell renderer as `ListItemRenderArgs.item`. */
|
|
19
19
|
value: T;
|
|
20
|
-
/** Child items nested under this one, which turn a plain item list into a tree. */
|
|
20
|
+
/** Child items nested under this one, which turn a plain item list into a tree, read only as rows are drawn. */
|
|
21
21
|
children?: ListItem<T>[] | undefined;
|
|
22
22
|
/** Hides the tree expander arrow even when the item has children, through `hide-expander`. */
|
|
23
23
|
shouldHideExpander?: boolean | undefined;
|
|
@@ -119,7 +119,8 @@ type ExpansionProps = {
|
|
|
119
119
|
/**
|
|
120
120
|
* Ids of the items to keep expanded; omitting it keeps every row collapsed, and `onExpandedChange` is how a
|
|
121
121
|
* user's expansion is adopted into it. An id repeated in several branches names every matching row, so all of
|
|
122
|
-
* them expand together.
|
|
122
|
+
* them expand together. An item whose children lead back to itself expands one level at a time, since a row
|
|
123
|
+
* repeating an item already expanded above it stays collapsed.
|
|
123
124
|
*/
|
|
124
125
|
expandedIds?: string[] | null | undefined;
|
|
125
126
|
/** Called with one id per expanded row, in visible order, whenever expansion changes. */
|