@gtkx/components 1.0.0-rc.1 → 1.0.0-rc.2
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/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.d.ts.map +1 -0
- package/dist/adw/index.js +2 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/toast.d.ts +10 -0
- package/dist/adw/toast.d.ts.map +1 -0
- package/dist/adw/toast.js +51 -0
- package/dist/adw/toast.js.map +1 -0
- package/dist/adw/types.d.ts +28 -0
- package/dist/adw/types.d.ts.map +1 -0
- package/dist/adw/types.js +2 -0
- package/dist/adw/types.js.map +1 -0
- package/dist/column-view.d.ts +7 -29
- package/dist/column-view.d.ts.map +1 -1
- package/dist/column-view.js +98 -70
- package/dist/column-view.js.map +1 -1
- package/dist/drop-down.d.ts +8 -27
- package/dist/drop-down.d.ts.map +1 -1
- package/dist/drop-down.js +112 -81
- package/dist/drop-down.js.map +1 -1
- package/dist/grid-view.d.ts +6 -16
- package/dist/grid-view.d.ts.map +1 -1
- package/dist/grid-view.js +25 -33
- package/dist/grid-view.js.map +1 -1
- package/dist/index.d.ts +6 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +50 -0
- package/dist/internal/cells.d.ts.map +1 -0
- package/dist/internal/cells.js +185 -0
- package/dist/internal/cells.js.map +1 -0
- package/dist/internal/collection-index.d.ts +21 -0
- package/dist/internal/collection-index.d.ts.map +1 -0
- package/dist/internal/collection-index.js +106 -0
- package/dist/internal/collection-index.js.map +1 -0
- package/dist/internal/collection-model.d.ts +12 -0
- package/dist/internal/collection-model.d.ts.map +1 -0
- package/dist/internal/collection-model.js +164 -0
- package/dist/internal/collection-model.js.map +1 -0
- package/dist/internal/collection.d.ts +18 -0
- package/dist/internal/collection.d.ts.map +1 -0
- package/dist/internal/collection.js +64 -0
- package/dist/internal/collection.js.map +1 -0
- package/dist/internal/expansion.d.ts +9 -0
- package/dist/internal/expansion.d.ts.map +1 -0
- package/dist/internal/expansion.js +75 -0
- package/dist/internal/expansion.js.map +1 -0
- package/dist/internal/selection.d.ts +12 -0
- package/dist/internal/selection.d.ts.map +1 -0
- package/dist/internal/selection.js +99 -0
- package/dist/internal/selection.js.map +1 -0
- package/dist/internal/use-collection.d.ts +18 -0
- package/dist/internal/use-collection.d.ts.map +1 -0
- package/dist/internal/use-collection.js +42 -0
- package/dist/internal/use-collection.js.map +1 -0
- package/dist/internal/use-widget-ref.d.ts +4 -0
- package/dist/internal/use-widget-ref.d.ts.map +1 -0
- package/dist/internal/use-widget-ref.js +8 -0
- package/dist/internal/use-widget-ref.js.map +1 -0
- package/dist/list-view.d.ts +5 -20
- package/dist/list-view.d.ts.map +1 -1
- package/dist/list-view.js +32 -53
- package/dist/list-view.js.map +1 -1
- package/dist/size-group.d.ts +8 -16
- package/dist/size-group.d.ts.map +1 -1
- package/dist/size-group.js +35 -51
- package/dist/size-group.js.map +1 -1
- package/dist/types.d.ts +97 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +17 -6
- package/src/adw/index.ts +2 -0
- package/src/adw/toast.tsx +74 -0
- package/src/adw/types.ts +32 -0
- package/src/column-view.tsx +176 -160
- package/src/drop-down.tsx +180 -167
- package/src/grid-view.tsx +29 -73
- package/src/index.ts +18 -25
- package/src/internal/cells.tsx +345 -0
- package/src/internal/collection-index.ts +168 -0
- package/src/internal/collection-model.ts +246 -0
- package/src/internal/collection.ts +104 -0
- package/src/internal/expansion.ts +123 -0
- package/src/internal/selection.tsx +159 -0
- package/src/internal/use-collection.tsx +70 -0
- package/src/internal/use-widget-ref.ts +11 -0
- package/src/list-view.tsx +43 -139
- package/src/size-group.tsx +63 -82
- package/src/types.ts +155 -24
- package/dist/cell.d.ts +0 -30
- package/dist/cell.d.ts.map +0 -1
- package/dist/cell.js +0 -45
- package/dist/cell.js.map +0 -1
- package/dist/column-view-column.d.ts +0 -27
- package/dist/column-view-column.d.ts.map +0 -1
- package/dist/column-view-column.js +0 -30
- package/dist/column-view-column.js.map +0 -1
- package/dist/constraint-layout-apply.d.ts +0 -55
- package/dist/constraint-layout-apply.d.ts.map +0 -1
- package/dist/constraint-layout-apply.js +0 -107
- package/dist/constraint-layout-apply.js.map +0 -1
- package/dist/constraint-layout.d.ts +0 -20
- package/dist/constraint-layout.d.ts.map +0 -1
- package/dist/constraint-layout.js +0 -88
- package/dist/constraint-layout.js.map +0 -1
- package/dist/fixed.d.ts +0 -26
- package/dist/fixed.d.ts.map +0 -1
- package/dist/fixed.js +0 -40
- package/dist/fixed.js.map +0 -1
- package/dist/grid.d.ts +0 -27
- package/dist/grid.d.ts.map +0 -1
- package/dist/grid.js +0 -48
- package/dist/grid.js.map +0 -1
- package/dist/hooks/use-cell-containers.d.ts +0 -18
- package/dist/hooks/use-cell-containers.d.ts.map +0 -1
- package/dist/hooks/use-cell-containers.js +0 -67
- package/dist/hooks/use-cell-containers.js.map +0 -1
- package/dist/hooks/use-collection-model.d.ts +0 -26
- package/dist/hooks/use-collection-model.d.ts.map +0 -1
- package/dist/hooks/use-collection-model.js +0 -35
- package/dist/hooks/use-collection-model.js.map +0 -1
- package/dist/hooks/use-collection-widget.d.ts +0 -28
- package/dist/hooks/use-collection-widget.d.ts.map +0 -1
- package/dist/hooks/use-collection-widget.js +0 -21
- package/dist/hooks/use-collection-widget.js.map +0 -1
- package/dist/hooks/use-drop-down-selection.d.ts +0 -16
- package/dist/hooks/use-drop-down-selection.d.ts.map +0 -1
- package/dist/hooks/use-drop-down-selection.js +0 -35
- package/dist/hooks/use-drop-down-selection.js.map +0 -1
- package/dist/hooks/use-expansion-model.d.ts +0 -12
- package/dist/hooks/use-expansion-model.d.ts.map +0 -1
- package/dist/hooks/use-expansion-model.js +0 -71
- package/dist/hooks/use-expansion-model.js.map +0 -1
- package/dist/hooks/use-header-menu.d.ts +0 -4
- package/dist/hooks/use-header-menu.d.ts.map +0 -1
- package/dist/hooks/use-header-menu.js +0 -20
- package/dist/hooks/use-header-menu.js.map +0 -1
- package/dist/hooks/use-installed-model.d.ts +0 -4
- package/dist/hooks/use-installed-model.d.ts.map +0 -1
- package/dist/hooks/use-installed-model.js +0 -15
- package/dist/hooks/use-installed-model.js.map +0 -1
- package/dist/hooks/use-list-model.d.ts +0 -19
- package/dist/hooks/use-list-model.d.ts.map +0 -1
- package/dist/hooks/use-list-model.js +0 -97
- package/dist/hooks/use-list-model.js.map +0 -1
- package/dist/hooks/use-placed-child.d.ts +0 -18
- package/dist/hooks/use-placed-child.d.ts.map +0 -1
- package/dist/hooks/use-placed-child.js +0 -47
- package/dist/hooks/use-placed-child.js.map +0 -1
- package/dist/hooks/use-selection-model.d.ts +0 -13
- package/dist/hooks/use-selection-model.d.ts.map +0 -1
- package/dist/hooks/use-selection-model.js +0 -103
- package/dist/hooks/use-selection-model.js.map +0 -1
- package/dist/hooks/use-sort-handler.d.ts +0 -17
- package/dist/hooks/use-sort-handler.d.ts.map +0 -1
- package/dist/hooks/use-sort-handler.js +0 -87
- package/dist/hooks/use-sort-handler.js.map +0 -1
- package/dist/menu-items.d.ts +0 -5
- package/dist/menu-items.d.ts.map +0 -1
- package/dist/menu-items.js +0 -48
- package/dist/menu-items.js.map +0 -1
- package/dist/menu.d.ts +0 -14
- package/dist/menu.d.ts.map +0 -1
- package/dist/menu.js +0 -24
- package/dist/menu.js.map +0 -1
- package/dist/overlay.d.ts +0 -24
- package/dist/overlay.d.ts.map +0 -1
- package/dist/overlay.js +0 -49
- package/dist/overlay.js.map +0 -1
- package/dist/utils/cell-container-store.d.ts +0 -28
- package/dist/utils/cell-container-store.d.ts.map +0 -1
- package/dist/utils/cell-container-store.js +0 -98
- package/dist/utils/cell-container-store.js.map +0 -1
- package/dist/utils/item-models.d.ts +0 -10
- package/dist/utils/item-models.d.ts.map +0 -1
- package/dist/utils/item-models.js +0 -82
- package/dist/utils/item-models.js.map +0 -1
- package/dist/utils/item-resolver.d.ts +0 -25
- package/dist/utils/item-resolver.d.ts.map +0 -1
- package/dist/utils/item-resolver.js +0 -102
- package/dist/utils/item-resolver.js.map +0 -1
- package/dist/utils/list-item-flatten.d.ts +0 -24
- package/dist/utils/list-item-flatten.d.ts.map +0 -1
- package/dist/utils/list-item-flatten.js +0 -68
- package/dist/utils/list-item-flatten.js.map +0 -1
- package/src/cell.tsx +0 -144
- package/src/column-view-column.tsx +0 -82
- package/src/constraint-layout-apply.ts +0 -173
- package/src/constraint-layout.tsx +0 -135
- package/src/fixed.tsx +0 -86
- package/src/grid.tsx +0 -88
- package/src/hooks/use-cell-containers.ts +0 -93
- package/src/hooks/use-collection-model.ts +0 -60
- package/src/hooks/use-collection-widget.ts +0 -47
- package/src/hooks/use-drop-down-selection.ts +0 -52
- package/src/hooks/use-expansion-model.ts +0 -82
- package/src/hooks/use-header-menu.ts +0 -34
- package/src/hooks/use-installed-model.ts +0 -20
- package/src/hooks/use-list-model.ts +0 -161
- package/src/hooks/use-placed-child.ts +0 -79
- package/src/hooks/use-selection-model.ts +0 -114
- package/src/hooks/use-sort-handler.ts +0 -107
- package/src/menu-items.ts +0 -48
- package/src/menu.tsx +0 -32
- package/src/overlay.tsx +0 -88
- package/src/utils/cell-container-store.ts +0 -114
- package/src/utils/item-models.ts +0 -113
- package/src/utils/item-resolver.ts +0 -138
- package/src/utils/list-item-flatten.ts +0 -90
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
+
import { GtkTreeExpander } from "@gtkx/jsx/gtk";
|
|
4
|
+
import { createPortal, useProperty } from "@gtkx/react";
|
|
5
|
+
import { memo, useLayoutEffect, useState, useSyncExternalStore } from "react";
|
|
6
|
+
import { getId } from "./collection-model.js";
|
|
7
|
+
const ItemCell = memo(ItemCellImpl);
|
|
8
|
+
const HeaderCell = memo(HeaderCellImpl);
|
|
9
|
+
const isListItem = (value) => value instanceof Gtk.ListItem;
|
|
10
|
+
const isListHeader = (value) => value instanceof Gtk.ListHeader;
|
|
11
|
+
const placeholder = (size) => new Gtk.Box({ widthRequest: size.width, heightRequest: size.height });
|
|
12
|
+
function notifyEntry(entry) {
|
|
13
|
+
for (const listener of entry.listeners) {
|
|
14
|
+
listener();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function notifyRoster(state) {
|
|
18
|
+
state.snapshot = null;
|
|
19
|
+
for (const listener of state.listeners) {
|
|
20
|
+
listener();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function createEntry(state, cell) {
|
|
24
|
+
state.serial += 1;
|
|
25
|
+
const entry = {
|
|
26
|
+
key: `gtkx-cell-${String(state.serial)}`,
|
|
27
|
+
cell,
|
|
28
|
+
item: null,
|
|
29
|
+
listeners: new Set(),
|
|
30
|
+
subscribe: (onChange) => {
|
|
31
|
+
entry.listeners.add(onChange);
|
|
32
|
+
return () => {
|
|
33
|
+
entry.listeners.delete(onChange);
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
getItem: () => entry.item,
|
|
37
|
+
};
|
|
38
|
+
return entry;
|
|
39
|
+
}
|
|
40
|
+
function setupCell(state, cell) {
|
|
41
|
+
cell.setChild(placeholder(state.size));
|
|
42
|
+
state.entries.set(cell, createEntry(state, cell));
|
|
43
|
+
notifyRoster(state);
|
|
44
|
+
}
|
|
45
|
+
function teardownCell(state, cell) {
|
|
46
|
+
if (state.entries.delete(cell)) {
|
|
47
|
+
notifyRoster(state);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function writeBinding(state, cell, item) {
|
|
51
|
+
const entry = state.entries.get(cell);
|
|
52
|
+
if (entry === undefined) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
entry.item = item;
|
|
56
|
+
notifyEntry(entry);
|
|
57
|
+
}
|
|
58
|
+
function boundItem(cell) {
|
|
59
|
+
if (cell instanceof Gtk.ListItem) {
|
|
60
|
+
return cell.getItem();
|
|
61
|
+
}
|
|
62
|
+
return cell instanceof Gtk.ListHeader ? cell.getItem() : null;
|
|
63
|
+
}
|
|
64
|
+
function bindCell(state, cell) {
|
|
65
|
+
if (cell.getChild() === null) {
|
|
66
|
+
cell.setChild(placeholder(state.size));
|
|
67
|
+
}
|
|
68
|
+
writeBinding(state, cell, boundItem(cell));
|
|
69
|
+
}
|
|
70
|
+
function subscribeRoster(state, onChange) {
|
|
71
|
+
state.listeners.add(onChange);
|
|
72
|
+
return () => {
|
|
73
|
+
state.listeners.delete(onChange);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function rosterSnapshot(state) {
|
|
77
|
+
state.snapshot ??= state.entries.values().toArray();
|
|
78
|
+
return state.snapshot;
|
|
79
|
+
}
|
|
80
|
+
function guarded(isCell, run) {
|
|
81
|
+
return (cell) => {
|
|
82
|
+
if (isCell(cell)) {
|
|
83
|
+
run(cell);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function createHandlers(state, isCell) {
|
|
88
|
+
return {
|
|
89
|
+
onSetup: guarded(isCell, (cell) => {
|
|
90
|
+
setupCell(state, cell);
|
|
91
|
+
}),
|
|
92
|
+
onBind: guarded(isCell, (cell) => {
|
|
93
|
+
bindCell(state, cell);
|
|
94
|
+
}),
|
|
95
|
+
onUnbind: guarded(isCell, (cell) => {
|
|
96
|
+
writeBinding(state, cell, null);
|
|
97
|
+
}),
|
|
98
|
+
onTeardown: guarded(isCell, (cell) => {
|
|
99
|
+
teardownCell(state, cell);
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function createCellStore(isCell, size) {
|
|
104
|
+
const state = {
|
|
105
|
+
size,
|
|
106
|
+
entries: new Map(),
|
|
107
|
+
snapshot: null,
|
|
108
|
+
listeners: new Set(),
|
|
109
|
+
serial: 0,
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
handlers: createHandlers(state, isCell),
|
|
113
|
+
setSize: (next) => {
|
|
114
|
+
state.size = next;
|
|
115
|
+
},
|
|
116
|
+
subscribe: (onChange) => subscribeRoster(state, onChange),
|
|
117
|
+
getSnapshot: () => rosterSnapshot(state),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function useCellStore(isCell, size) {
|
|
121
|
+
const [store] = useState(() => createCellStore(isCell, size));
|
|
122
|
+
useLayoutEffect(() => {
|
|
123
|
+
store.setSize(size);
|
|
124
|
+
});
|
|
125
|
+
return store;
|
|
126
|
+
}
|
|
127
|
+
function useItemCells(size) {
|
|
128
|
+
return useCellStore(isListItem, size);
|
|
129
|
+
}
|
|
130
|
+
function useHeaderCells(size) {
|
|
131
|
+
return useCellStore(isListHeader, size);
|
|
132
|
+
}
|
|
133
|
+
function isRowWanted(options, item) {
|
|
134
|
+
const { expandedIds } = options;
|
|
135
|
+
return expandedIds == null ? options.isRowExpanded === true : expandedIds.includes(item.id);
|
|
136
|
+
}
|
|
137
|
+
function itemArgs(item, options) {
|
|
138
|
+
const args = { item: item.value, index: options.position ?? 0 };
|
|
139
|
+
const row = options.row;
|
|
140
|
+
if (row === null) {
|
|
141
|
+
return args;
|
|
142
|
+
}
|
|
143
|
+
args.depth = row.getDepth();
|
|
144
|
+
if (row.isExpandable()) {
|
|
145
|
+
args.isExpanded = isRowWanted(options, item);
|
|
146
|
+
}
|
|
147
|
+
return args;
|
|
148
|
+
}
|
|
149
|
+
function wrapExpander(item, row, content) {
|
|
150
|
+
const expander = row === null
|
|
151
|
+
? (content)
|
|
152
|
+
: (_jsx(GtkTreeExpander, { listRow: row, hideExpander: item.hideExpander ?? false, indentForDepth: item.indentForDepth ?? true, indentForIcon: item.indentForIcon ?? true, children: content }));
|
|
153
|
+
return expander;
|
|
154
|
+
}
|
|
155
|
+
function itemBody(options) {
|
|
156
|
+
const id = getId(options.item);
|
|
157
|
+
const item = id === null ? undefined : options.collection.itemFor(id);
|
|
158
|
+
if (item === undefined) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const render = options.render;
|
|
162
|
+
return wrapExpander(item, options.row, render(itemArgs(item, options)));
|
|
163
|
+
}
|
|
164
|
+
function ItemCellImpl({ entry, render, collection, expandedIds }) {
|
|
165
|
+
const item = useSyncExternalStore(entry.subscribe, entry.getItem);
|
|
166
|
+
const position = useProperty(entry.cell, "position");
|
|
167
|
+
const row = item instanceof Gtk.TreeListRow ? item : null;
|
|
168
|
+
const isRowExpanded = useProperty(row, "expanded");
|
|
169
|
+
const body = itemBody({ item, position, row, isRowExpanded, render, collection, expandedIds });
|
|
170
|
+
return createPortal(body, entry.cell, entry.key);
|
|
171
|
+
}
|
|
172
|
+
function HeaderCellImpl({ entry, render, collection }) {
|
|
173
|
+
const item = useSyncExternalStore(entry.subscribe, entry.getItem);
|
|
174
|
+
const id = getId(item);
|
|
175
|
+
const renderHeader = render;
|
|
176
|
+
const body = id === null ? null : renderHeader({ section: collection.sectionFor(id) });
|
|
177
|
+
return createPortal(body, entry.cell, entry.key);
|
|
178
|
+
}
|
|
179
|
+
function usePortalEntries(store) {
|
|
180
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
181
|
+
}
|
|
182
|
+
const ItemPortals = ({ store, render, collection, expandedIds }) => usePortalEntries(store).map((entry) => (_jsx(ItemCell, { entry: entry, render: render, collection: collection, expandedIds: expandedIds }, entry.key)));
|
|
183
|
+
const HeaderPortals = ({ store, render, collection }) => usePortalEntries(store).map((entry) => (_jsx(HeaderCell, { entry: entry, render: render, collection: collection }, entry.key)));
|
|
184
|
+
export { useItemCells, useHeaderCells, ItemPortals, HeaderPortals, };
|
|
185
|
+
//# sourceMappingURL=cells.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cells.js","sourceRoot":"","sources":["../../src/internal/cells.tsx"],"names":[],"mappings":"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","sourcesContent":["import type * as GObject from \"@gtkx/gi/gobject\";\nimport type { ReactNode } from \"react\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { GtkTreeExpander } from \"@gtkx/jsx/gtk\";\nimport { createPortal, useProperty } from \"@gtkx/react\";\nimport { memo, useLayoutEffect, useState, useSyncExternalStore } from \"react\";\nimport type { HeaderRenderer, Item, ItemRenderer, RenderItemArgs } from \"../types.js\";\nimport type { Collection } from \"./collection.js\";\nimport { getId } from \"./collection-model.js\";\n\ntype CellSize = {\n width: number;\n height: number;\n};\n\ntype CellHost = GObject.Object & {\n getChild: () => Gtk.Widget | null;\n setChild: (child: Gtk.Widget | null) => void;\n};\n\ntype CellEntry<C extends CellHost> = {\n key: string;\n cell: C;\n item: GObject.Object | null;\n listeners: Set<() => void>;\n subscribe: (onChange: () => void) => () => void;\n getItem: () => GObject.Object | null;\n};\n\ntype FactoryHandlers = {\n onSetup: (cell: GObject.Object) => void;\n onBind: (cell: GObject.Object) => void;\n onUnbind: (cell: GObject.Object) => void;\n onTeardown: (cell: GObject.Object) => void;\n};\n\ntype CellStore<C extends CellHost> = {\n handlers: FactoryHandlers;\n setSize: (size: CellSize) => void;\n subscribe: (onChange: () => void) => () => void;\n getSnapshot: () => CellEntry<C>[];\n};\n\ntype StoreState<C extends CellHost> = {\n size: CellSize;\n entries: Map<C, CellEntry<C>>;\n snapshot: CellEntry<C>[] | null;\n listeners: Set<() => void>;\n serial: number;\n};\n\ntype CellGuard<C extends CellHost> = (value: GObject.Object) => value is C;\n\ntype ItemBodyOptions = {\n item: GObject.Object | null;\n position: number | undefined;\n row: Gtk.TreeListRow | null;\n isRowExpanded: boolean | undefined;\n render: ItemRenderer<never>;\n collection: Collection;\n expandedIds: string[] | null | undefined;\n};\n\ntype ItemCellProps = {\n entry: CellEntry<Gtk.ListItem>;\n render: ItemRenderer<never>;\n collection: Collection;\n expandedIds: string[] | null | undefined;\n};\n\ntype ItemPortalsProps = {\n store: CellStore<Gtk.ListItem>;\n render: ItemRenderer<never>;\n collection: Collection;\n expandedIds?: string[] | null | undefined;\n};\n\ntype HeaderCellProps = {\n entry: CellEntry<Gtk.ListHeader>;\n render: HeaderRenderer<never>;\n collection: Collection;\n};\n\ntype HeaderPortalsProps = {\n store: CellStore<Gtk.ListHeader>;\n render: HeaderRenderer<never>;\n collection: Collection;\n};\n\nconst ItemCell = memo(ItemCellImpl);\nconst HeaderCell = memo(HeaderCellImpl);\n\nconst isListItem = (value: GObject.Object): value is Gtk.ListItem => value instanceof Gtk.ListItem;\nconst isListHeader = (value: GObject.Object): value is Gtk.ListHeader => value instanceof Gtk.ListHeader;\n\nconst placeholder = (size: CellSize): Gtk.Widget =>\n new Gtk.Box({ widthRequest: size.width, heightRequest: size.height });\n\nfunction notifyEntry<C extends CellHost>(entry: CellEntry<C>): void {\n for (const listener of entry.listeners) {\n listener();\n }\n}\n\nfunction notifyRoster<C extends CellHost>(state: StoreState<C>): void {\n state.snapshot = null;\n\n for (const listener of state.listeners) {\n listener();\n }\n}\n\nfunction createEntry<C extends CellHost>(state: StoreState<C>, cell: C): CellEntry<C> {\n state.serial += 1;\n\n const entry: CellEntry<C> = {\n key: `gtkx-cell-${String(state.serial)}`,\n cell,\n item: null,\n listeners: new Set(),\n subscribe: (onChange) => {\n entry.listeners.add(onChange);\n\n return () => {\n entry.listeners.delete(onChange);\n };\n },\n getItem: () => entry.item,\n };\n\n return entry;\n}\n\nfunction setupCell<C extends CellHost>(state: StoreState<C>, cell: C): void {\n cell.setChild(placeholder(state.size));\n state.entries.set(cell, createEntry(state, cell));\n notifyRoster(state);\n}\n\nfunction teardownCell<C extends CellHost>(state: StoreState<C>, cell: C): void {\n if (state.entries.delete(cell)) {\n notifyRoster(state);\n }\n}\n\nfunction writeBinding<C extends CellHost>(state: StoreState<C>, cell: C, item: GObject.Object | null): void {\n const entry = state.entries.get(cell);\n\n if (entry === undefined) {\n return;\n }\n\n entry.item = item;\n notifyEntry(entry);\n}\n\nfunction boundItem(cell: CellHost): GObject.Object | null {\n if (cell instanceof Gtk.ListItem) {\n return cell.getItem();\n }\n\n return cell instanceof Gtk.ListHeader ? cell.getItem() : null;\n}\n\nfunction bindCell<C extends CellHost>(state: StoreState<C>, cell: C): void {\n if (cell.getChild() === null) {\n cell.setChild(placeholder(state.size));\n }\n\n writeBinding(state, cell, boundItem(cell));\n}\n\nfunction subscribeRoster<C extends CellHost>(state: StoreState<C>, onChange: () => void): () => void {\n state.listeners.add(onChange);\n\n return () => {\n state.listeners.delete(onChange);\n };\n}\n\nfunction rosterSnapshot<C extends CellHost>(state: StoreState<C>): CellEntry<C>[] {\n state.snapshot ??= state.entries.values().toArray();\n\n return state.snapshot;\n}\n\nfunction guarded<C extends CellHost>(isCell: CellGuard<C>, run: (cell: C) => void): (cell: GObject.Object) => void {\n return (cell) => {\n if (isCell(cell)) {\n run(cell);\n }\n };\n}\n\nfunction createHandlers<C extends CellHost>(state: StoreState<C>, isCell: CellGuard<C>): FactoryHandlers {\n return {\n onSetup: guarded(isCell, (cell) => {\n setupCell(state, cell);\n }),\n onBind: guarded(isCell, (cell) => {\n bindCell(state, cell);\n }),\n onUnbind: guarded(isCell, (cell) => {\n writeBinding(state, cell, null);\n }),\n onTeardown: guarded(isCell, (cell) => {\n teardownCell(state, cell);\n }),\n };\n}\n\nfunction createCellStore<C extends CellHost>(isCell: CellGuard<C>, size: CellSize): CellStore<C> {\n const state: StoreState<C> = {\n size,\n entries: new Map(),\n snapshot: null,\n listeners: new Set(),\n serial: 0,\n };\n\n return {\n handlers: createHandlers(state, isCell),\n setSize: (next) => {\n state.size = next;\n },\n subscribe: (onChange) => subscribeRoster(state, onChange),\n getSnapshot: () => rosterSnapshot(state),\n };\n}\n\nfunction useCellStore<C extends CellHost>(isCell: CellGuard<C>, size: CellSize): CellStore<C> {\n const [store] = useState(() => createCellStore(isCell, size));\n\n useLayoutEffect(() => {\n store.setSize(size);\n });\n\n return store;\n}\n\nfunction useItemCells(size: CellSize): CellStore<Gtk.ListItem> {\n return useCellStore(isListItem, size);\n}\n\nfunction useHeaderCells(size: CellSize): CellStore<Gtk.ListHeader> {\n return useCellStore(isListHeader, size);\n}\n\nfunction isRowWanted(options: ItemBodyOptions, item: Item): boolean {\n const { expandedIds } = options;\n\n return expandedIds == null ? options.isRowExpanded === true : expandedIds.includes(item.id);\n}\n\nfunction itemArgs(item: Item, options: ItemBodyOptions): RenderItemArgs<unknown> {\n const args: RenderItemArgs<unknown> = { item: item.value, index: options.position ?? 0 };\n const row = options.row;\n\n if (row === null) {\n return args;\n }\n\n args.depth = row.getDepth();\n\n if (row.isExpandable()) {\n args.isExpanded = isRowWanted(options, item);\n }\n\n return args;\n}\n\nfunction wrapExpander(item: Item, row: Gtk.TreeListRow | null, content: ReactNode): ReactNode {\n const expander: ReactNode =\n row === null\n ? (\n content\n )\n : (\n <GtkTreeExpander\n listRow={row}\n hideExpander={item.hideExpander ?? false}\n indentForDepth={item.indentForDepth ?? true}\n indentForIcon={item.indentForIcon ?? true}\n >\n {content}\n </GtkTreeExpander>\n );\n\n return expander;\n}\n\nfunction itemBody(options: ItemBodyOptions): ReactNode {\n const id = getId(options.item);\n const item = id === null ? undefined : options.collection.itemFor(id);\n\n if (item === undefined) {\n return null;\n }\n\n const render = options.render as ItemRenderer<unknown>;\n\n return wrapExpander(item, options.row, render(itemArgs(item, options)));\n}\n\nfunction ItemCellImpl({ entry, render, collection, expandedIds }: ItemCellProps): ReactNode {\n const item = useSyncExternalStore(entry.subscribe, entry.getItem);\n const position = useProperty(entry.cell, \"position\");\n const row = item instanceof Gtk.TreeListRow ? item : null;\n const isRowExpanded = useProperty(row, \"expanded\");\n const body = itemBody({ item, position, row, isRowExpanded, render, collection, expandedIds });\n\n return createPortal(body, entry.cell, entry.key);\n}\n\nfunction HeaderCellImpl({ entry, render, collection }: HeaderCellProps): ReactNode {\n const item = useSyncExternalStore(entry.subscribe, entry.getItem);\n const id = getId(item);\n const renderHeader = render as HeaderRenderer<unknown>;\n const body = id === null ? null : renderHeader({ section: collection.sectionFor(id) });\n\n return createPortal(body, entry.cell, entry.key);\n}\n\nfunction usePortalEntries<C extends CellHost>(store: CellStore<C>): CellEntry<C>[] {\n return useSyncExternalStore(store.subscribe, store.getSnapshot);\n}\n\nconst ItemPortals = ({ store, render, collection, expandedIds }: ItemPortalsProps): ReactNode =>\n usePortalEntries(store).map((entry) => (\n <ItemCell key={entry.key} entry={entry} render={render} collection={collection} expandedIds={expandedIds} />\n ));\n\nconst HeaderPortals = ({ store, render, collection }: HeaderPortalsProps): ReactNode =>\n usePortalEntries(store).map((entry) => (\n <HeaderCell key={entry.key} entry={entry} render={render} collection={collection} />\n ));\n\nexport {\n useItemCells,\n useHeaderCells,\n ItemPortals,\n HeaderPortals,\n type CellSize,\n type CellStore,\n};\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Item, Section } from "../types.js";
|
|
2
|
+
type Level = {
|
|
3
|
+
key: string;
|
|
4
|
+
ids: string[];
|
|
5
|
+
isExpandable: boolean[];
|
|
6
|
+
};
|
|
7
|
+
type CollectionIndex = {
|
|
8
|
+
isTree: boolean;
|
|
9
|
+
size: number;
|
|
10
|
+
groups: Level[];
|
|
11
|
+
children: Map<string, Level>;
|
|
12
|
+
has: (id: string) => boolean;
|
|
13
|
+
itemFor: (id: string) => Item | undefined;
|
|
14
|
+
sectionFor: (id: string) => unknown;
|
|
15
|
+
positionFor: (id: string) => number;
|
|
16
|
+
};
|
|
17
|
+
declare const ROOT_LEVEL_KEY = "";
|
|
18
|
+
declare const childLevelKey: (id: string) => string;
|
|
19
|
+
declare function createCollectionIndex(items: Item[] | undefined, sections: Section[] | undefined, isFlat: boolean): CollectionIndex;
|
|
20
|
+
export { createCollectionIndex, childLevelKey, ROOT_LEVEL_KEY, type CollectionIndex, type Level };
|
|
21
|
+
//# sourceMappingURL=collection-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-index.d.ts","sourceRoot":"","sources":["../../src/internal/collection-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjD,KAAK,KAAK,GAAG;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,YAAY,EAAE,OAAO,EAAE,CAAC;CAC3B,CAAC;AAEF,KAAK,eAAe,GAAG;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7B,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AAYF,QAAA,MAAM,cAAc,KAAK,CAAC;AAG1B,QAAA,MAAM,aAAa,GAAI,IAAI,MAAM,KAAG,MAAuB,CAAC;AAwG5D,iBAAS,qBAAqB,CAC1B,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,EACzB,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,EAC/B,MAAM,EAAE,OAAO,GAChB,eAAe,CAyBjB;AAED,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const ROOT_LEVEL_KEY = "";
|
|
2
|
+
const hasChildren = (item) => item.children !== undefined && item.children.length > 0;
|
|
3
|
+
const childLevelKey = (id) => `child:${id}`;
|
|
4
|
+
function collectChildren(state, level, item) {
|
|
5
|
+
if (!state.isTree) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const canExpand = hasChildren(item);
|
|
9
|
+
level.isExpandable.push(canExpand);
|
|
10
|
+
if (!canExpand) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const key = childLevelKey(item.id);
|
|
14
|
+
state.children.set(key, collectLevel(state, key, item.children ?? []));
|
|
15
|
+
}
|
|
16
|
+
function collectLevel(state, key, items) {
|
|
17
|
+
const level = { key, ids: [], isExpandable: [] };
|
|
18
|
+
for (const item of items) {
|
|
19
|
+
level.ids.push(item.id);
|
|
20
|
+
state.itemsById.set(item.id, item);
|
|
21
|
+
collectChildren(state, level, item);
|
|
22
|
+
}
|
|
23
|
+
return level;
|
|
24
|
+
}
|
|
25
|
+
function buildPositions(state) {
|
|
26
|
+
const positions = new Map();
|
|
27
|
+
const starts = [];
|
|
28
|
+
let offset = 0;
|
|
29
|
+
for (const level of state.groups) {
|
|
30
|
+
starts.push(offset);
|
|
31
|
+
for (const id of level.ids) {
|
|
32
|
+
positions.set(id, offset);
|
|
33
|
+
offset += 1;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
state.positions = positions;
|
|
37
|
+
state.starts = starts;
|
|
38
|
+
}
|
|
39
|
+
function positionFor(state, id) {
|
|
40
|
+
if (state.isTree) {
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
if (state.positions === null) {
|
|
44
|
+
buildPositions(state);
|
|
45
|
+
}
|
|
46
|
+
return state.positions?.get(id) ?? -1;
|
|
47
|
+
}
|
|
48
|
+
function sectionAt(starts, position) {
|
|
49
|
+
let low = 0;
|
|
50
|
+
let high = starts.length - 1;
|
|
51
|
+
while (low < high) {
|
|
52
|
+
const middle = Math.ceil((low + high) / 2);
|
|
53
|
+
if ((starts[middle] ?? 0) <= position) {
|
|
54
|
+
low = middle;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
high = middle - 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return low;
|
|
61
|
+
}
|
|
62
|
+
function sectionFor(state, id) {
|
|
63
|
+
const sections = state.sections;
|
|
64
|
+
const position = positionFor(state, id);
|
|
65
|
+
if (sections === undefined || position < 0 || state.starts === null) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
return sections[sectionAt(state.starts, position)]?.value;
|
|
69
|
+
}
|
|
70
|
+
function buildGroups(state, source, sections) {
|
|
71
|
+
if (sections === undefined) {
|
|
72
|
+
return [collectLevel(state, ROOT_LEVEL_KEY, source)];
|
|
73
|
+
}
|
|
74
|
+
return sections.map((section) => collectLevel(state, section.id, section.data));
|
|
75
|
+
}
|
|
76
|
+
function isTreeSource(source, sections, isFlat) {
|
|
77
|
+
if (sections !== undefined || isFlat) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return source.some((item) => hasChildren(item));
|
|
81
|
+
}
|
|
82
|
+
function createCollectionIndex(items, sections, isFlat) {
|
|
83
|
+
const source = items ?? [];
|
|
84
|
+
const state = {
|
|
85
|
+
isTree: isTreeSource(source, sections, isFlat),
|
|
86
|
+
groups: [],
|
|
87
|
+
children: new Map(),
|
|
88
|
+
itemsById: new Map(),
|
|
89
|
+
sections,
|
|
90
|
+
positions: null,
|
|
91
|
+
starts: null,
|
|
92
|
+
};
|
|
93
|
+
state.groups = buildGroups(state, source, sections);
|
|
94
|
+
return {
|
|
95
|
+
isTree: state.isTree,
|
|
96
|
+
size: state.itemsById.size,
|
|
97
|
+
groups: state.groups,
|
|
98
|
+
children: state.children,
|
|
99
|
+
has: (id) => state.itemsById.has(id),
|
|
100
|
+
itemFor: (id) => state.itemsById.get(id),
|
|
101
|
+
sectionFor: (id) => sectionFor(state, id),
|
|
102
|
+
positionFor: (id) => positionFor(state, id),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export { createCollectionIndex, childLevelKey, ROOT_LEVEL_KEY };
|
|
106
|
+
//# sourceMappingURL=collection-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-index.js","sourceRoot":"","sources":["../../src/internal/collection-index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Item, Section } from \"../types.js\";\n\ntype Level = {\n key: string;\n ids: string[];\n isExpandable: boolean[];\n};\n\ntype CollectionIndex = {\n isTree: boolean;\n size: number;\n groups: Level[];\n children: Map<string, Level>;\n has: (id: string) => boolean;\n itemFor: (id: string) => Item | undefined;\n sectionFor: (id: string) => unknown;\n positionFor: (id: string) => number;\n};\n\ntype IndexState = {\n isTree: boolean;\n groups: Level[];\n children: Map<string, Level>;\n itemsById: Map<string, Item>;\n sections: Section[] | undefined;\n positions: Map<string, number> | null;\n starts: number[] | null;\n};\n\nconst ROOT_LEVEL_KEY = \"\";\n\nconst hasChildren = (item: Item): boolean => item.children !== undefined && item.children.length > 0;\nconst childLevelKey = (id: string): string => `child:${id}`;\n\nfunction collectChildren(state: IndexState, level: Level, item: Item): void {\n if (!state.isTree) {\n return;\n }\n\n const canExpand = hasChildren(item);\n level.isExpandable.push(canExpand);\n\n if (!canExpand) {\n return;\n }\n\n const key = childLevelKey(item.id);\n state.children.set(key, collectLevel(state, key, item.children ?? []));\n}\n\nfunction collectLevel(state: IndexState, key: string, items: Item[]): Level {\n const level: Level = { key, ids: [], isExpandable: [] };\n\n for (const item of items) {\n level.ids.push(item.id);\n state.itemsById.set(item.id, item);\n collectChildren(state, level, item);\n }\n\n return level;\n}\n\nfunction buildPositions(state: IndexState): void {\n const positions: Map<string, number> = new Map();\n const starts: number[] = [];\n let offset = 0;\n\n for (const level of state.groups) {\n starts.push(offset);\n\n for (const id of level.ids) {\n positions.set(id, offset);\n offset += 1;\n }\n }\n\n state.positions = positions;\n state.starts = starts;\n}\n\nfunction positionFor(state: IndexState, id: string): number {\n if (state.isTree) {\n return -1;\n }\n\n if (state.positions === null) {\n buildPositions(state);\n }\n\n return state.positions?.get(id) ?? -1;\n}\n\nfunction sectionAt(starts: number[], position: number): number {\n let low = 0;\n let high = starts.length - 1;\n\n while (low < high) {\n const middle = Math.ceil((low + high) / 2);\n\n if ((starts[middle] ?? 0) <= position) {\n low = middle;\n } else {\n high = middle - 1;\n }\n }\n\n return low;\n}\n\nfunction sectionFor(state: IndexState, id: string): unknown {\n const sections = state.sections;\n const position = positionFor(state, id);\n\n if (sections === undefined || position < 0 || state.starts === null) {\n return undefined;\n }\n\n return sections[sectionAt(state.starts, position)]?.value;\n}\n\nfunction buildGroups(state: IndexState, source: Item[], sections: Section[] | undefined): Level[] {\n if (sections === undefined) {\n return [collectLevel(state, ROOT_LEVEL_KEY, source)];\n }\n\n return sections.map((section) => collectLevel(state, section.id, section.data));\n}\n\nfunction isTreeSource(source: Item[], sections: Section[] | undefined, isFlat: boolean): boolean {\n if (sections !== undefined || isFlat) {\n return false;\n }\n\n return source.some((item) => hasChildren(item));\n}\n\nfunction createCollectionIndex(\n items: Item[] | undefined,\n sections: Section[] | undefined,\n isFlat: boolean,\n): CollectionIndex {\n const source = items ?? [];\n\n const state: IndexState = {\n isTree: isTreeSource(source, sections, isFlat),\n groups: [],\n children: new Map(),\n itemsById: new Map(),\n sections,\n positions: null,\n starts: null,\n };\n\n state.groups = buildGroups(state, source, sections);\n\n return {\n isTree: state.isTree,\n size: state.itemsById.size,\n groups: state.groups,\n children: state.children,\n has: (id) => state.itemsById.has(id),\n itemFor: (id) => state.itemsById.get(id),\n sectionFor: (id) => sectionFor(state, id),\n positionFor: (id) => positionFor(state, id),\n };\n}\n\nexport { createCollectionIndex, childLevelKey, ROOT_LEVEL_KEY, type CollectionIndex, type Level };\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as GObject from "@gtkx/gi/gobject";
|
|
2
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
+
import type { CollectionIndex } from "./collection-index.js";
|
|
4
|
+
type CollectionModel = {
|
|
5
|
+
model: Gtk.FlattenListModel;
|
|
6
|
+
treeModel: () => Gtk.TreeListModel | null;
|
|
7
|
+
sync: (index: CollectionIndex) => void;
|
|
8
|
+
};
|
|
9
|
+
declare function getId(value: GObject.Object | null): string | null;
|
|
10
|
+
declare function createCollectionModel(): CollectionModel;
|
|
11
|
+
export { createCollectionModel, getId, type CollectionModel };
|
|
12
|
+
//# sourceMappingURL=collection-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-model.d.ts","sourceRoot":"","sources":["../../src/internal/collection-model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAS,MAAM,uBAAuB,CAAC;AAwBpE,KAAK,eAAe,GAAG;IACnB,KAAK,EAAE,GAAG,CAAC,gBAAgB,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC;IAC1C,IAAI,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC1C,CAAC;AAMF,iBAAS,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAI1D;AAuLD,iBAAS,qBAAqB,IAAI,eAAe,CAmBhD;AAED,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,KAAK,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as Gio from "@gtkx/gi/gio";
|
|
2
|
+
import * as GObject from "@gtkx/gi/gobject";
|
|
3
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
4
|
+
import { childLevelKey, ROOT_LEVEL_KEY } from "./collection-index.js";
|
|
5
|
+
const OBJECT_CACHE_MAX = 1 << 20;
|
|
6
|
+
const newStore = () => new Gio.ListStore({ itemType: GObject.TYPE_OBJECT });
|
|
7
|
+
function getId(value) {
|
|
8
|
+
const item = value instanceof Gtk.TreeListRow ? value.getItem() : value;
|
|
9
|
+
return item instanceof Gtk.StringObject ? item.getString() : null;
|
|
10
|
+
}
|
|
11
|
+
function objectFor(state, id) {
|
|
12
|
+
const existing = state.objects.get(id);
|
|
13
|
+
if (existing !== undefined) {
|
|
14
|
+
return existing;
|
|
15
|
+
}
|
|
16
|
+
const created = Gtk.StringObject.new(id);
|
|
17
|
+
state.objects.set(id, created);
|
|
18
|
+
return created;
|
|
19
|
+
}
|
|
20
|
+
function commonPrefix(previous, next, max) {
|
|
21
|
+
let count = 0;
|
|
22
|
+
while (count < max && previous[count] === next[count]) {
|
|
23
|
+
count += 1;
|
|
24
|
+
}
|
|
25
|
+
return count;
|
|
26
|
+
}
|
|
27
|
+
function commonSuffix(previous, next, max) {
|
|
28
|
+
let count = 0;
|
|
29
|
+
while (count < max && previous[previous.length - 1 - count] === next[next.length - 1 - count]) {
|
|
30
|
+
count += 1;
|
|
31
|
+
}
|
|
32
|
+
return count;
|
|
33
|
+
}
|
|
34
|
+
function spliceRange(previous, next) {
|
|
35
|
+
const shared = Math.min(previous.length, next.length);
|
|
36
|
+
const start = commonPrefix(previous, next, shared);
|
|
37
|
+
if (start === previous.length && start === next.length) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const tail = commonSuffix(previous, next, shared - start);
|
|
41
|
+
return { start, removed: previous.length - start - tail, added: next.slice(start, next.length - tail) };
|
|
42
|
+
}
|
|
43
|
+
function levelFor(state, key) {
|
|
44
|
+
const existing = state.levels.get(key);
|
|
45
|
+
if (existing !== undefined) {
|
|
46
|
+
return existing;
|
|
47
|
+
}
|
|
48
|
+
const created = { store: newStore(), ids: [], canExpand: new Map() };
|
|
49
|
+
state.levels.set(key, created);
|
|
50
|
+
return created;
|
|
51
|
+
}
|
|
52
|
+
function refreshExpandable(current, level) {
|
|
53
|
+
const next = new Map();
|
|
54
|
+
for (const [index, id] of level.ids.entries()) {
|
|
55
|
+
const isWanted = level.isExpandable[index] ?? false;
|
|
56
|
+
const previous = current.canExpand.get(id);
|
|
57
|
+
next.set(id, isWanted);
|
|
58
|
+
if (previous !== undefined && previous !== isWanted) {
|
|
59
|
+
current.store.itemsChanged(index, 1, 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
current.canExpand = next;
|
|
63
|
+
}
|
|
64
|
+
function didSpliceLevel(state, level) {
|
|
65
|
+
const current = levelFor(state, level.key);
|
|
66
|
+
const range = spliceRange(current.ids, level.ids);
|
|
67
|
+
if (range === null) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
current.store.splice(range.start, range.removed, range.added.map((id) => objectFor(state, id)));
|
|
71
|
+
current.ids = [...level.ids];
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function dropUnvisited(state, visited) {
|
|
75
|
+
for (const key of state.levels.keys()) {
|
|
76
|
+
if (!visited.has(key)) {
|
|
77
|
+
state.levels.delete(key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Items keep their identity by id, so a collection that shrinks keeps its objects cached: filtering a long
|
|
82
|
+
// list down and back up reuses them instead of rebuilding one GObject per row. Incremental filters shrink and
|
|
83
|
+
// grow the list many times per keystroke, so the cache is only swept once it passes an absolute bound.
|
|
84
|
+
function pruneObjects(state, index) {
|
|
85
|
+
if (state.objects.size <= OBJECT_CACHE_MAX) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
for (const id of state.objects.keys()) {
|
|
89
|
+
if (!index.has(id)) {
|
|
90
|
+
state.objects.delete(id);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function childStoreFor(state, object) {
|
|
95
|
+
const id = getId(object);
|
|
96
|
+
if (id === null) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return state.levels.get(childLevelKey(id))?.store ?? null;
|
|
100
|
+
}
|
|
101
|
+
function ensureTree(state) {
|
|
102
|
+
state.tree ??= Gtk.TreeListModel.new(levelFor(state, ROOT_LEVEL_KEY).store, false, false, (object) => childStoreFor(state, object));
|
|
103
|
+
return state.tree;
|
|
104
|
+
}
|
|
105
|
+
function desiredRootModels(state, index) {
|
|
106
|
+
if (index.isTree) {
|
|
107
|
+
return [ensureTree(state)];
|
|
108
|
+
}
|
|
109
|
+
return index.groups.map((level) => levelFor(state, level.key).store);
|
|
110
|
+
}
|
|
111
|
+
function hasSameModels(previous, next) {
|
|
112
|
+
return previous.length === next.length && next.every((model, index) => previous[index] === model);
|
|
113
|
+
}
|
|
114
|
+
function syncRoot(state, index) {
|
|
115
|
+
const next = desiredRootModels(state, index);
|
|
116
|
+
if (hasSameModels(state.rootModels, next)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
state.root.splice(0, state.rootModels.length, next);
|
|
120
|
+
state.rootModels = next;
|
|
121
|
+
}
|
|
122
|
+
function syncModel(state, index) {
|
|
123
|
+
const levels = [...index.groups, ...index.children.values()];
|
|
124
|
+
const visited = new Set();
|
|
125
|
+
let didChange = false;
|
|
126
|
+
for (const level of levels) {
|
|
127
|
+
didChange = didSpliceLevel(state, level) || didChange;
|
|
128
|
+
visited.add(level.key);
|
|
129
|
+
}
|
|
130
|
+
if (didChange || visited.size !== state.levels.size) {
|
|
131
|
+
dropUnvisited(state, visited);
|
|
132
|
+
pruneObjects(state, index);
|
|
133
|
+
}
|
|
134
|
+
syncRoot(state, index);
|
|
135
|
+
refreshExpandableLevels(state, index, levels);
|
|
136
|
+
}
|
|
137
|
+
function refreshExpandableLevels(state, index, levels) {
|
|
138
|
+
if (!index.isTree) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
for (const level of levels) {
|
|
142
|
+
refreshExpandable(levelFor(state, level.key), level);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function createCollectionModel() {
|
|
146
|
+
const root = newStore();
|
|
147
|
+
const state = {
|
|
148
|
+
root,
|
|
149
|
+
rootModels: [],
|
|
150
|
+
model: Gtk.FlattenListModel.new(root),
|
|
151
|
+
objects: new Map(),
|
|
152
|
+
levels: new Map(),
|
|
153
|
+
tree: null,
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
model: state.model,
|
|
157
|
+
treeModel: () => state.tree,
|
|
158
|
+
sync: (index) => {
|
|
159
|
+
syncModel(state, index);
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export { createCollectionModel, getId };
|
|
164
|
+
//# sourceMappingURL=collection-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-model.js","sourceRoot":"","sources":["../../src/internal/collection-model.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gio from \"@gtkx/gi/gio\";\nimport * as GObject from \"@gtkx/gi/gobject\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport type { CollectionIndex, Level } from \"./collection-index.js\";\nimport { childLevelKey, ROOT_LEVEL_KEY } from \"./collection-index.js\";\n\ntype LevelState = {\n store: Gio.ListStore;\n ids: string[];\n canExpand: Map<string, boolean>;\n};\n\ntype SpliceRange = {\n start: number;\n removed: number;\n added: string[];\n};\n\ntype ModelState = {\n root: Gio.ListStore;\n rootModels: GObject.Object[];\n model: Gtk.FlattenListModel;\n objects: Map<string, Gtk.StringObject>;\n levels: Map<string, LevelState>;\n tree: Gtk.TreeListModel | null;\n};\n\ntype CollectionModel = {\n model: Gtk.FlattenListModel;\n treeModel: () => Gtk.TreeListModel | null;\n sync: (index: CollectionIndex) => void;\n};\n\nconst OBJECT_CACHE_MAX = 1 << 20;\n\nconst newStore = (): Gio.ListStore => new Gio.ListStore({ itemType: GObject.TYPE_OBJECT });\n\nfunction getId(value: GObject.Object | null): string | null {\n const item = value instanceof Gtk.TreeListRow ? value.getItem() : value;\n\n return item instanceof Gtk.StringObject ? item.getString() : null;\n}\n\nfunction objectFor(state: ModelState, id: string): Gtk.StringObject {\n const existing = state.objects.get(id);\n\n if (existing !== undefined) {\n return existing;\n }\n\n const created = Gtk.StringObject.new(id);\n state.objects.set(id, created);\n\n return created;\n}\n\nfunction commonPrefix(previous: string[], next: string[], max: number): number {\n let count = 0;\n\n while (count < max && previous[count] === next[count]) {\n count += 1;\n }\n\n return count;\n}\n\nfunction commonSuffix(previous: string[], next: string[], max: number): number {\n let count = 0;\n\n while (count < max && previous[previous.length - 1 - count] === next[next.length - 1 - count]) {\n count += 1;\n }\n\n return count;\n}\n\nfunction spliceRange(previous: string[], next: string[]): SpliceRange | null {\n const shared = Math.min(previous.length, next.length);\n const start = commonPrefix(previous, next, shared);\n\n if (start === previous.length && start === next.length) {\n return null;\n }\n\n const tail = commonSuffix(previous, next, shared - start);\n\n return { start, removed: previous.length - start - tail, added: next.slice(start, next.length - tail) };\n}\n\nfunction levelFor(state: ModelState, key: string): LevelState {\n const existing = state.levels.get(key);\n\n if (existing !== undefined) {\n return existing;\n }\n\n const created: LevelState = { store: newStore(), ids: [], canExpand: new Map() };\n state.levels.set(key, created);\n\n return created;\n}\n\nfunction refreshExpandable(current: LevelState, level: Level): void {\n const next: Map<string, boolean> = new Map();\n\n for (const [index, id] of level.ids.entries()) {\n const isWanted = level.isExpandable[index] ?? false;\n const previous = current.canExpand.get(id);\n next.set(id, isWanted);\n\n if (previous !== undefined && previous !== isWanted) {\n current.store.itemsChanged(index, 1, 1);\n }\n }\n\n current.canExpand = next;\n}\n\nfunction didSpliceLevel(state: ModelState, level: Level): boolean {\n const current = levelFor(state, level.key);\n const range = spliceRange(current.ids, level.ids);\n\n if (range === null) {\n return false;\n }\n\n current.store.splice(range.start, range.removed, range.added.map((id) => objectFor(state, id)));\n current.ids = [...level.ids];\n\n return true;\n}\n\nfunction dropUnvisited(state: ModelState, visited: Set<string>): void {\n for (const key of state.levels.keys()) {\n if (!visited.has(key)) {\n state.levels.delete(key);\n }\n }\n}\n\n// Items keep their identity by id, so a collection that shrinks keeps its objects cached: filtering a long\n// list down and back up reuses them instead of rebuilding one GObject per row. Incremental filters shrink and\n// grow the list many times per keystroke, so the cache is only swept once it passes an absolute bound.\nfunction pruneObjects(state: ModelState, index: CollectionIndex): void {\n if (state.objects.size <= OBJECT_CACHE_MAX) {\n return;\n }\n\n for (const id of state.objects.keys()) {\n if (!index.has(id)) {\n state.objects.delete(id);\n }\n }\n}\n\nfunction childStoreFor(state: ModelState, object: GObject.Object): Gio.ListStore | null {\n const id = getId(object);\n\n if (id === null) {\n return null;\n }\n\n return state.levels.get(childLevelKey(id))?.store ?? null;\n}\n\nfunction ensureTree(state: ModelState): Gtk.TreeListModel {\n state.tree ??= Gtk.TreeListModel.new(levelFor(state, ROOT_LEVEL_KEY).store, false, false, (object) =>\n childStoreFor(state, object));\n\n return state.tree;\n}\n\nfunction desiredRootModels(state: ModelState, index: CollectionIndex): GObject.Object[] {\n if (index.isTree) {\n return [ensureTree(state)];\n }\n\n return index.groups.map((level) => levelFor(state, level.key).store);\n}\n\nfunction hasSameModels(previous: GObject.Object[], next: GObject.Object[]): boolean {\n return previous.length === next.length && next.every((model, index) => previous[index] === model);\n}\n\nfunction syncRoot(state: ModelState, index: CollectionIndex): void {\n const next = desiredRootModels(state, index);\n\n if (hasSameModels(state.rootModels, next)) {\n return;\n }\n\n state.root.splice(0, state.rootModels.length, next);\n state.rootModels = next;\n}\n\nfunction syncModel(state: ModelState, index: CollectionIndex): void {\n const levels = [...index.groups, ...index.children.values()];\n const visited: Set<string> = new Set();\n let didChange = false;\n\n for (const level of levels) {\n didChange = didSpliceLevel(state, level) || didChange;\n visited.add(level.key);\n }\n\n if (didChange || visited.size !== state.levels.size) {\n dropUnvisited(state, visited);\n pruneObjects(state, index);\n }\n\n syncRoot(state, index);\n refreshExpandableLevels(state, index, levels);\n}\n\nfunction refreshExpandableLevels(state: ModelState, index: CollectionIndex, levels: Level[]): void {\n if (!index.isTree) {\n return;\n }\n\n for (const level of levels) {\n refreshExpandable(levelFor(state, level.key), level);\n }\n}\n\nfunction createCollectionModel(): CollectionModel {\n const root = newStore();\n\n const state: ModelState = {\n root,\n rootModels: [],\n model: Gtk.FlattenListModel.new(root),\n objects: new Map(),\n levels: new Map(),\n tree: null,\n };\n\n return {\n model: state.model,\n treeModel: () => state.tree,\n sync: (index) => {\n syncModel(state, index);\n },\n };\n}\n\nexport { createCollectionModel, getId, type CollectionModel };\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { Item } from "../types.js";
|
|
3
|
+
import type { CollectionIndex } from "./collection-index.js";
|
|
4
|
+
import type { CollectionModel } from "./collection-model.js";
|
|
5
|
+
type Collection = {
|
|
6
|
+
isTree: boolean;
|
|
7
|
+
model: Gtk.FlattenListModel;
|
|
8
|
+
treeModel: () => Gtk.TreeListModel | null;
|
|
9
|
+
itemFor: (id: string) => Item | undefined;
|
|
10
|
+
sectionFor: (id: string) => unknown;
|
|
11
|
+
idAt: (position: number) => string | null;
|
|
12
|
+
positionFor: (id: string) => number;
|
|
13
|
+
positionsFor: (ids: string[]) => number[];
|
|
14
|
+
};
|
|
15
|
+
declare function eachRow(tree: Gtk.TreeListModel, visit: (row: Gtk.TreeListRow, id: string | null) => void): void;
|
|
16
|
+
declare function createCollection(gtk: CollectionModel, index: CollectionIndex): Collection;
|
|
17
|
+
export { createCollection, eachRow, type Collection };
|
|
18
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,KAAK,UAAU,GAAG;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,GAAG,CAAC,gBAAgB,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;CAC7C,CAAC;AAkBF,iBAAS,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAIxG;AAoDD,iBAAS,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAWlF;AAED,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC"}
|