@gtkx/react 0.1.47 → 0.1.48
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 +8 -0
- package/dist/codegen/jsx-generator.js +11 -4
- package/dist/generated/jsx.d.ts +7 -3
- package/dist/nodes/column-view.d.ts +27 -3
- package/dist/nodes/column-view.js +206 -22
- package/dist/types.d.ts +20 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -186,6 +186,14 @@ A comprehensive showcase of GTK4 widgets and features:
|
|
|
186
186
|
turbo start --filter=gtk4-demo
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
+
### List Example
|
|
190
|
+
|
|
191
|
+
Comprehensive showcase of ListView, GridView, and ColumnView with sorting:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
turbo start --filter=list-example
|
|
195
|
+
```
|
|
196
|
+
|
|
189
197
|
## Packages
|
|
190
198
|
|
|
191
199
|
| Package | Description |
|
|
@@ -136,14 +136,14 @@ export class JsxGenerator {
|
|
|
136
136
|
`import type { ReactNode, Ref } from "react";`,
|
|
137
137
|
...externalImports,
|
|
138
138
|
`import type * as Gtk from "@gtkx/ffi/gtk";`,
|
|
139
|
-
`import type { ColumnViewColumnProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps } from "../types.js";`,
|
|
139
|
+
`import type { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps } from "../types.js";`,
|
|
140
140
|
"",
|
|
141
141
|
].join("\n");
|
|
142
142
|
}
|
|
143
143
|
generateCommonTypes(widgetClass) {
|
|
144
144
|
const widgetPropsContent = this.generateWidgetPropsContent(widgetClass);
|
|
145
145
|
return `
|
|
146
|
-
export { ColumnViewColumnProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps };
|
|
146
|
+
export { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps };
|
|
147
147
|
|
|
148
148
|
${widgetPropsContent}
|
|
149
149
|
`;
|
|
@@ -664,8 +664,15 @@ ${widgetPropsContent}
|
|
|
664
664
|
lines.push(`}`);
|
|
665
665
|
}
|
|
666
666
|
else if (isColumnViewWidget(widgetName)) {
|
|
667
|
-
// Root
|
|
668
|
-
|
|
667
|
+
// Root props type - extends with ColumnViewRootProps for sorting support
|
|
668
|
+
// Uses generics: T for item type, C for column ID union type
|
|
669
|
+
lines.push(`interface ${name}RootPropsExtended<T = unknown, C extends string = string> extends ${name}Props, ColumnViewRootProps<C> {`);
|
|
670
|
+
lines.push(`\t/** Comparison function for sorting items by column. Takes item a, item b, and column id. */`);
|
|
671
|
+
lines.push(`\tsortFn?: (a: T, b: T, columnId: C) => number;`);
|
|
672
|
+
lines.push(`}`);
|
|
673
|
+
lines.push(``);
|
|
674
|
+
// Root wrapper (generic)
|
|
675
|
+
lines.push(`function ${name}Root<T = unknown, C extends string = string>(props: ${name}RootPropsExtended<T, C>): import("react").ReactElement {`);
|
|
669
676
|
lines.push(`\treturn createElement("${name}.Root", props);`);
|
|
670
677
|
lines.push(`}`);
|
|
671
678
|
lines.push(``);
|
package/dist/generated/jsx.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { ReactNode, Ref } from "react";
|
|
|
3
3
|
import type * as Gdk from "@gtkx/ffi/gdk";
|
|
4
4
|
import type * as Gio from "@gtkx/ffi/gio";
|
|
5
5
|
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
6
|
-
import type { ColumnViewColumnProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps } from "../types.js";
|
|
7
|
-
export { ColumnViewColumnProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps, };
|
|
6
|
+
import type { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps } from "../types.js";
|
|
7
|
+
export { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps, };
|
|
8
8
|
/**
|
|
9
9
|
* The base class for all widgets.
|
|
10
10
|
*
|
|
@@ -7075,7 +7075,11 @@ export declare const ColorChooserWidget: "ColorChooserWidget";
|
|
|
7075
7075
|
* it gets the .color style class.
|
|
7076
7076
|
*/
|
|
7077
7077
|
export declare const ColorDialogButton: "ColorDialogButton";
|
|
7078
|
-
|
|
7078
|
+
interface ColumnViewRootPropsExtended<T = unknown, C extends string = string> extends ColumnViewProps, ColumnViewRootProps<C> {
|
|
7079
|
+
/** Comparison function for sorting items by column. Takes item a, item b, and column id. */
|
|
7080
|
+
sortFn?: (a: T, b: T, columnId: C) => number;
|
|
7081
|
+
}
|
|
7082
|
+
declare function ColumnViewRoot<T = unknown, C extends string = string>(props: ColumnViewRootPropsExtended<T, C>): import("react").ReactElement;
|
|
7079
7083
|
interface ColumnViewGenericColumnProps<T> extends Omit<ColumnViewColumnProps, "renderCell"> {
|
|
7080
7084
|
/** Render function for column cells. Called with null during setup. */
|
|
7081
7085
|
renderCell: (item: T | null) => import("react").ReactElement;
|
|
@@ -1,34 +1,58 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
2
|
import type { Props } from "../factory.js";
|
|
3
3
|
import { Node } from "../node.js";
|
|
4
|
+
import type { ColumnSortFn } from "../types.js";
|
|
4
5
|
export declare class ColumnViewNode extends Node<Gtk.ColumnView> {
|
|
5
6
|
static matches(type: string): boolean;
|
|
6
7
|
private stringList;
|
|
7
8
|
private selectionModel;
|
|
9
|
+
private sortListModel;
|
|
8
10
|
private items;
|
|
9
11
|
private columns;
|
|
10
12
|
private committedLength;
|
|
13
|
+
private sortColumn;
|
|
14
|
+
private sortOrder;
|
|
15
|
+
private sortFn;
|
|
16
|
+
private isSorting;
|
|
17
|
+
private onSortChange;
|
|
18
|
+
private sorterChangedHandlerId;
|
|
19
|
+
private lastNotifiedColumn;
|
|
20
|
+
private lastNotifiedOrder;
|
|
11
21
|
constructor(type: string, props: Props, app: Gtk.Application);
|
|
22
|
+
private connectSorterChangedSignal;
|
|
23
|
+
private waitForSortComplete;
|
|
24
|
+
private disconnectSorterChangedSignal;
|
|
25
|
+
private notifySortChange;
|
|
12
26
|
getItems(): unknown[];
|
|
13
|
-
|
|
27
|
+
getSortFn(): ColumnSortFn<unknown, string> | null;
|
|
28
|
+
compareItems(a: unknown, b: unknown, columnId: string): number;
|
|
29
|
+
addColumn(columnNode: ColumnViewColumnNode): void;
|
|
30
|
+
private applySortByColumn;
|
|
31
|
+
findColumnById(id: string): ColumnViewColumnNode | undefined;
|
|
14
32
|
removeColumn(column: ColumnViewColumnNode): void;
|
|
15
33
|
insertColumnBefore(column: ColumnViewColumnNode, before: ColumnViewColumnNode): void;
|
|
16
34
|
private syncStringList;
|
|
17
35
|
addItem(item: unknown): void;
|
|
18
36
|
insertItemBefore(item: unknown, beforeItem: unknown): void;
|
|
19
37
|
removeItem(item: unknown): void;
|
|
38
|
+
protected consumedProps(): Set<string>;
|
|
39
|
+
updateProps(oldProps: Props, newProps: Props): void;
|
|
20
40
|
}
|
|
21
41
|
export declare class ColumnViewColumnNode extends Node {
|
|
22
42
|
static matches(type: string): boolean;
|
|
23
43
|
protected isVirtual(): boolean;
|
|
24
|
-
private
|
|
44
|
+
private column;
|
|
25
45
|
private factory;
|
|
26
46
|
private renderCell;
|
|
27
47
|
private columnView;
|
|
28
48
|
private listItemCache;
|
|
49
|
+
private columnId;
|
|
50
|
+
private sorter;
|
|
29
51
|
constructor(type: string, props: Props, app: Gtk.Application);
|
|
30
|
-
|
|
52
|
+
getColumn(): Gtk.ColumnViewColumn;
|
|
53
|
+
getId(): string | null;
|
|
31
54
|
setColumnView(columnView: ColumnViewNode | null): void;
|
|
55
|
+
updateSorterFromRoot(): void;
|
|
32
56
|
attachToParent(parent: Node): void;
|
|
33
57
|
attachToParentBefore(parent: Node, before: Node): void;
|
|
34
58
|
detachFromParent(parent: Node): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getObject, getObjectId } from "@gtkx/ffi";
|
|
2
|
+
import * as GObject from "@gtkx/ffi/gobject";
|
|
2
3
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
4
|
import { scheduleFlush } from "../batch.js";
|
|
4
5
|
import { createFiberRoot } from "../fiber-root.js";
|
|
@@ -10,29 +11,123 @@ export class ColumnViewNode extends Node {
|
|
|
10
11
|
}
|
|
11
12
|
stringList;
|
|
12
13
|
selectionModel;
|
|
14
|
+
sortListModel;
|
|
13
15
|
items = [];
|
|
14
16
|
columns = [];
|
|
15
17
|
committedLength = 0;
|
|
18
|
+
sortColumn = null;
|
|
19
|
+
sortOrder = Gtk.SortType.ASCENDING;
|
|
20
|
+
sortFn = null;
|
|
21
|
+
isSorting = false;
|
|
22
|
+
onSortChange = null;
|
|
23
|
+
sorterChangedHandlerId = null;
|
|
24
|
+
lastNotifiedColumn = null;
|
|
25
|
+
lastNotifiedOrder = Gtk.SortType.ASCENDING;
|
|
16
26
|
constructor(type, props, app) {
|
|
17
27
|
super(type, props, app);
|
|
18
28
|
this.stringList = new Gtk.StringList([]);
|
|
19
|
-
this.
|
|
29
|
+
this.sortListModel = new Gtk.SortListModel(this.stringList, this.widget.getSorter());
|
|
30
|
+
this.sortListModel.setIncremental(true);
|
|
31
|
+
this.selectionModel = new Gtk.SingleSelection(this.sortListModel);
|
|
20
32
|
this.widget.setModel(this.selectionModel);
|
|
33
|
+
this.sortColumn = props.sortColumn ?? null;
|
|
34
|
+
this.sortOrder = props.sortOrder ?? Gtk.SortType.ASCENDING;
|
|
35
|
+
this.sortFn = props.sortFn ?? null;
|
|
36
|
+
this.onSortChange =
|
|
37
|
+
props.onSortChange ?? null;
|
|
38
|
+
this.connectSorterChangedSignal();
|
|
39
|
+
}
|
|
40
|
+
connectSorterChangedSignal() {
|
|
41
|
+
const sorter = this.widget.getSorter();
|
|
42
|
+
if (!sorter || !this.onSortChange)
|
|
43
|
+
return;
|
|
44
|
+
this.sorterChangedHandlerId = sorter.connect("changed", () => {
|
|
45
|
+
this.waitForSortComplete(() => this.notifySortChange());
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
waitForSortComplete(callback) {
|
|
49
|
+
const pending = this.sortListModel.getPending();
|
|
50
|
+
if (pending === 0) {
|
|
51
|
+
callback();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Sorting still in progress, check again after a short delay
|
|
55
|
+
setTimeout(() => this.waitForSortComplete(callback), 10);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
disconnectSorterChangedSignal() {
|
|
59
|
+
if (this.sorterChangedHandlerId === null)
|
|
60
|
+
return;
|
|
61
|
+
const sorter = this.widget.getSorter();
|
|
62
|
+
if (sorter) {
|
|
63
|
+
GObject.signalHandlerDisconnect(sorter, this.sorterChangedHandlerId);
|
|
64
|
+
}
|
|
65
|
+
this.sorterChangedHandlerId = null;
|
|
66
|
+
}
|
|
67
|
+
notifySortChange() {
|
|
68
|
+
if (!this.onSortChange)
|
|
69
|
+
return;
|
|
70
|
+
const baseSorter = this.widget.getSorter();
|
|
71
|
+
if (!baseSorter)
|
|
72
|
+
return;
|
|
73
|
+
const sorter = getObject(baseSorter.ptr, Gtk.ColumnViewSorter);
|
|
74
|
+
const column = sorter.getPrimarySortColumn();
|
|
75
|
+
const order = sorter.getPrimarySortOrder();
|
|
76
|
+
const columnId = column?.getId() ?? null;
|
|
77
|
+
// Deduplicate: only notify if the sort state actually changed
|
|
78
|
+
if (columnId === this.lastNotifiedColumn && order === this.lastNotifiedOrder) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.lastNotifiedColumn = columnId;
|
|
82
|
+
this.lastNotifiedOrder = order;
|
|
83
|
+
this.onSortChange(columnId, order);
|
|
21
84
|
}
|
|
22
85
|
getItems() {
|
|
23
86
|
return this.items;
|
|
24
87
|
}
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
88
|
+
getSortFn() {
|
|
89
|
+
return this.sortFn;
|
|
90
|
+
}
|
|
91
|
+
compareItems(a, b, columnId) {
|
|
92
|
+
if (this.isSorting || !this.sortFn)
|
|
93
|
+
return 0;
|
|
94
|
+
this.isSorting = true;
|
|
95
|
+
try {
|
|
96
|
+
return this.sortFn(a, b, columnId);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
this.isSorting = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
addColumn(columnNode) {
|
|
103
|
+
this.columns.push(columnNode);
|
|
104
|
+
const column = columnNode.getColumn();
|
|
105
|
+
this.widget.appendColumn(column);
|
|
106
|
+
columnNode.setColumnView(this);
|
|
107
|
+
if (columnNode.getId() === this.sortColumn && this.sortColumn !== null) {
|
|
108
|
+
this.applySortByColumn();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
applySortByColumn() {
|
|
112
|
+
if (this.sortColumn === null) {
|
|
113
|
+
this.widget.sortByColumn(this.sortOrder, null);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!this.columns)
|
|
117
|
+
return;
|
|
118
|
+
const column = this.columns.find((c) => c.getId() === this.sortColumn);
|
|
119
|
+
if (column) {
|
|
120
|
+
this.widget.sortByColumn(this.sortOrder, column.getColumn());
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
findColumnById(id) {
|
|
124
|
+
return this.columns.find((c) => c.getId() === id);
|
|
30
125
|
}
|
|
31
126
|
removeColumn(column) {
|
|
32
127
|
const index = this.columns.indexOf(column);
|
|
33
128
|
if (index !== -1) {
|
|
34
129
|
this.columns.splice(index, 1);
|
|
35
|
-
this.widget.removeColumn(column.
|
|
130
|
+
this.widget.removeColumn(column.getColumn());
|
|
36
131
|
column.setColumnView(null);
|
|
37
132
|
}
|
|
38
133
|
}
|
|
@@ -43,15 +138,16 @@ export class ColumnViewNode extends Node {
|
|
|
43
138
|
return;
|
|
44
139
|
}
|
|
45
140
|
this.columns.splice(beforeIndex, 0, column);
|
|
46
|
-
this.widget.insertColumn(beforeIndex, column.
|
|
141
|
+
this.widget.insertColumn(beforeIndex, column.getColumn());
|
|
47
142
|
column.setColumnView(this);
|
|
48
143
|
}
|
|
49
144
|
syncStringList = () => {
|
|
50
145
|
const newLength = this.items.length;
|
|
51
146
|
if (newLength === this.committedLength)
|
|
52
147
|
return;
|
|
53
|
-
|
|
54
|
-
|
|
148
|
+
// Store indices as strings so we can map back to items in the sorter
|
|
149
|
+
const indices = Array.from({ length: newLength }, (_, i) => String(i));
|
|
150
|
+
this.stringList.splice(0, this.committedLength, indices);
|
|
55
151
|
this.committedLength = newLength;
|
|
56
152
|
};
|
|
57
153
|
addItem(item) {
|
|
@@ -75,6 +171,46 @@ export class ColumnViewNode extends Node {
|
|
|
75
171
|
scheduleFlush(this.syncStringList);
|
|
76
172
|
}
|
|
77
173
|
}
|
|
174
|
+
consumedProps() {
|
|
175
|
+
const consumed = super.consumedProps();
|
|
176
|
+
consumed.add("sortColumn");
|
|
177
|
+
consumed.add("sortOrder");
|
|
178
|
+
consumed.add("onSortChange");
|
|
179
|
+
consumed.add("sortFn");
|
|
180
|
+
return consumed;
|
|
181
|
+
}
|
|
182
|
+
updateProps(oldProps, newProps) {
|
|
183
|
+
super.updateProps(oldProps, newProps);
|
|
184
|
+
const newSortColumn = newProps.sortColumn ?? null;
|
|
185
|
+
const newSortOrder = newProps.sortOrder ?? Gtk.SortType.ASCENDING;
|
|
186
|
+
const newSortFn = newProps.sortFn ?? null;
|
|
187
|
+
const newOnSortChange = newProps.onSortChange ?? null;
|
|
188
|
+
if (oldProps.onSortChange !== newProps.onSortChange) {
|
|
189
|
+
const hadCallback = this.onSortChange !== null;
|
|
190
|
+
this.onSortChange = newOnSortChange;
|
|
191
|
+
const hasCallback = this.onSortChange !== null;
|
|
192
|
+
// Connect or disconnect the signal handler as needed
|
|
193
|
+
if (!hadCallback && hasCallback) {
|
|
194
|
+
this.connectSorterChangedSignal();
|
|
195
|
+
}
|
|
196
|
+
else if (hadCallback && !hasCallback) {
|
|
197
|
+
this.disconnectSorterChangedSignal();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (oldProps.sortFn !== newProps.sortFn) {
|
|
201
|
+
this.sortFn = newSortFn;
|
|
202
|
+
if (this.columns) {
|
|
203
|
+
for (const column of this.columns) {
|
|
204
|
+
column.updateSorterFromRoot();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (oldProps.sortColumn !== newProps.sortColumn || oldProps.sortOrder !== newProps.sortOrder) {
|
|
209
|
+
this.sortColumn = newSortColumn;
|
|
210
|
+
this.sortOrder = newSortOrder;
|
|
211
|
+
this.applySortByColumn();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
78
214
|
}
|
|
79
215
|
export class ColumnViewColumnNode extends Node {
|
|
80
216
|
static matches(type) {
|
|
@@ -83,24 +219,30 @@ export class ColumnViewColumnNode extends Node {
|
|
|
83
219
|
isVirtual() {
|
|
84
220
|
return true;
|
|
85
221
|
}
|
|
86
|
-
|
|
222
|
+
column;
|
|
87
223
|
factory;
|
|
88
224
|
renderCell;
|
|
89
225
|
columnView = null;
|
|
90
226
|
listItemCache = new Map();
|
|
227
|
+
columnId = null;
|
|
228
|
+
sorter = null;
|
|
91
229
|
constructor(type, props, app) {
|
|
92
230
|
super(type, props, app);
|
|
93
231
|
this.factory = new Gtk.SignalListItemFactory();
|
|
94
|
-
this.
|
|
232
|
+
this.column = new Gtk.ColumnViewColumn(props.title, this.factory);
|
|
95
233
|
this.renderCell = props.renderCell;
|
|
234
|
+
this.columnId = props.id ?? null;
|
|
235
|
+
if (this.columnId !== null) {
|
|
236
|
+
this.column.setId(this.columnId);
|
|
237
|
+
}
|
|
96
238
|
if (props.expand !== undefined) {
|
|
97
|
-
this.
|
|
239
|
+
this.column.setExpand(props.expand);
|
|
98
240
|
}
|
|
99
241
|
if (props.resizable !== undefined) {
|
|
100
|
-
this.
|
|
242
|
+
this.column.setResizable(props.resizable);
|
|
101
243
|
}
|
|
102
244
|
if (props.fixedWidth !== undefined) {
|
|
103
|
-
this.
|
|
245
|
+
this.column.setFixedWidth(props.fixedWidth);
|
|
104
246
|
}
|
|
105
247
|
this.factory.connect("setup", (_self, listItemObj) => {
|
|
106
248
|
const listItem = getObject(listItemObj.ptr, Gtk.ListItem);
|
|
@@ -142,11 +284,48 @@ export class ColumnViewColumnNode extends Node {
|
|
|
142
284
|
}
|
|
143
285
|
});
|
|
144
286
|
}
|
|
145
|
-
|
|
146
|
-
return this.
|
|
287
|
+
getColumn() {
|
|
288
|
+
return this.column;
|
|
289
|
+
}
|
|
290
|
+
getId() {
|
|
291
|
+
return this.columnId;
|
|
147
292
|
}
|
|
148
293
|
setColumnView(columnView) {
|
|
149
294
|
this.columnView = columnView;
|
|
295
|
+
this.updateSorterFromRoot();
|
|
296
|
+
}
|
|
297
|
+
updateSorterFromRoot() {
|
|
298
|
+
if (!this.columnView || this.columnId === null) {
|
|
299
|
+
this.column.setSorter(null);
|
|
300
|
+
this.sorter = null;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const rootSortFn = this.columnView.getSortFn();
|
|
304
|
+
if (rootSortFn === null) {
|
|
305
|
+
this.column.setSorter(null);
|
|
306
|
+
this.sorter = null;
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const columnId = this.columnId;
|
|
310
|
+
const columnView = this.columnView;
|
|
311
|
+
const wrappedSortFn = (_a, _b) => {
|
|
312
|
+
const items = columnView.getItems();
|
|
313
|
+
// _a and _b are GtkStringObject pointers - get the string content (indices)
|
|
314
|
+
const stringObjA = getObject(_a, Gtk.StringObject);
|
|
315
|
+
const stringObjB = getObject(_b, Gtk.StringObject);
|
|
316
|
+
const indexA = Number.parseInt(stringObjA.getString(), 10);
|
|
317
|
+
const indexB = Number.parseInt(stringObjB.getString(), 10);
|
|
318
|
+
if (Number.isNaN(indexA) || Number.isNaN(indexB))
|
|
319
|
+
return 0;
|
|
320
|
+
const itemA = items[indexA] ?? null;
|
|
321
|
+
const itemB = items[indexB] ?? null;
|
|
322
|
+
if (itemA === null || itemB === null)
|
|
323
|
+
return 0;
|
|
324
|
+
const result = columnView.compareItems(itemA, itemB, columnId);
|
|
325
|
+
return typeof result === "number" ? result : 0;
|
|
326
|
+
};
|
|
327
|
+
this.sorter = new Gtk.CustomSorter(wrappedSortFn);
|
|
328
|
+
this.column.setSorter(this.sorter);
|
|
150
329
|
}
|
|
151
330
|
attachToParent(parent) {
|
|
152
331
|
if (parent instanceof ColumnViewNode) {
|
|
@@ -173,25 +352,30 @@ export class ColumnViewColumnNode extends Node {
|
|
|
173
352
|
consumed.add("expand");
|
|
174
353
|
consumed.add("resizable");
|
|
175
354
|
consumed.add("fixedWidth");
|
|
355
|
+
consumed.add("id");
|
|
176
356
|
return consumed;
|
|
177
357
|
}
|
|
178
358
|
updateProps(oldProps, newProps) {
|
|
179
359
|
if (oldProps.renderCell !== newProps.renderCell) {
|
|
180
360
|
this.renderCell = newProps.renderCell;
|
|
181
361
|
}
|
|
182
|
-
if (!this.
|
|
362
|
+
if (!this.column)
|
|
183
363
|
return;
|
|
184
364
|
if (oldProps.title !== newProps.title) {
|
|
185
|
-
this.
|
|
365
|
+
this.column.setTitle(newProps.title);
|
|
186
366
|
}
|
|
187
367
|
if (oldProps.expand !== newProps.expand) {
|
|
188
|
-
this.
|
|
368
|
+
this.column.setExpand(newProps.expand);
|
|
189
369
|
}
|
|
190
370
|
if (oldProps.resizable !== newProps.resizable) {
|
|
191
|
-
this.
|
|
371
|
+
this.column.setResizable(newProps.resizable);
|
|
192
372
|
}
|
|
193
373
|
if (oldProps.fixedWidth !== newProps.fixedWidth) {
|
|
194
|
-
this.
|
|
374
|
+
this.column.setFixedWidth(newProps.fixedWidth);
|
|
375
|
+
}
|
|
376
|
+
if (oldProps.id !== newProps.id) {
|
|
377
|
+
this.columnId = newProps.id ?? null;
|
|
378
|
+
this.column.setId(this.columnId);
|
|
195
379
|
}
|
|
196
380
|
}
|
|
197
381
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import type { SortType } from "@gtkx/ffi/gtk";
|
|
1
2
|
import type { ReactElement, ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Props for slot components that accept children.
|
|
5
|
+
* Used by container widgets that render child elements in designated slots.
|
|
6
|
+
*/
|
|
2
7
|
export interface SlotProps {
|
|
3
8
|
children?: ReactNode;
|
|
4
9
|
}
|
|
@@ -19,11 +24,20 @@ export type RenderItemFn<T> = (item: T | null) => ReactElement;
|
|
|
19
24
|
export interface ListViewRenderProps<T = unknown> {
|
|
20
25
|
renderItem: RenderItemFn<T>;
|
|
21
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Comparison function for sorting items by column.
|
|
29
|
+
* Returns negative if a < b, 0 if a === b, positive if a > b.
|
|
30
|
+
* @param a - First item to compare
|
|
31
|
+
* @param b - Second item to compare
|
|
32
|
+
* @param columnId - The ID of the column being sorted
|
|
33
|
+
*/
|
|
34
|
+
export type ColumnSortFn<T, C extends string = string> = (a: T, b: T, columnId: C) => number;
|
|
22
35
|
export interface ColumnViewColumnProps {
|
|
23
36
|
title?: string;
|
|
24
37
|
expand?: boolean;
|
|
25
38
|
resizable?: boolean;
|
|
26
39
|
fixedWidth?: number;
|
|
40
|
+
id?: string;
|
|
27
41
|
/**
|
|
28
42
|
* Render function for column cells.
|
|
29
43
|
* Called with null during setup (for loading state) and with the actual item during bind.
|
|
@@ -31,6 +45,12 @@ export interface ColumnViewColumnProps {
|
|
|
31
45
|
*/
|
|
32
46
|
renderCell: (item: any) => ReactElement;
|
|
33
47
|
}
|
|
48
|
+
export interface ColumnViewRootProps<C extends string = string> {
|
|
49
|
+
sortColumn?: C | null;
|
|
50
|
+
sortOrder?: SortType;
|
|
51
|
+
onSortChange?: (column: C | null, order: SortType) => void;
|
|
52
|
+
sortFn?: ColumnSortFn<any, C>;
|
|
53
|
+
}
|
|
34
54
|
export interface NotebookPageProps extends SlotProps {
|
|
35
55
|
label: string;
|
|
36
56
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "Build GTK4 desktop applications with React and TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtk",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"react-reconciler": "0.33.0",
|
|
39
|
-
"@gtkx/ffi": "0.1.
|
|
39
|
+
"@gtkx/ffi": "0.1.48"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@gtkx/gir": "0.1.
|
|
42
|
+
"@gtkx/gir": "0.1.48"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"react": "^19"
|