@gtkx/react 0.1.48 → 0.1.49

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.
Files changed (42) hide show
  1. package/dist/codegen/jsx-generator.js +4 -21
  2. package/dist/container-interfaces.d.ts +51 -0
  3. package/dist/container-interfaces.js +5 -0
  4. package/dist/factory.d.ts +1 -1
  5. package/dist/factory.js +17 -3
  6. package/dist/generated/jsx.d.ts +5 -11
  7. package/dist/node.d.ts +4 -4
  8. package/dist/node.js +7 -6
  9. package/dist/nodes/about-dialog.d.ts +9 -0
  10. package/dist/nodes/about-dialog.js +14 -0
  11. package/dist/nodes/action-bar.d.ts +9 -0
  12. package/dist/nodes/action-bar.js +15 -0
  13. package/dist/nodes/column-view.d.ts +5 -4
  14. package/dist/nodes/column-view.js +28 -29
  15. package/dist/nodes/dropdown.d.ts +7 -17
  16. package/dist/nodes/dropdown.js +17 -10
  17. package/dist/nodes/flow-box.d.ts +9 -0
  18. package/dist/nodes/flow-box.js +25 -0
  19. package/dist/nodes/grid.d.ts +6 -3
  20. package/dist/nodes/grid.js +28 -26
  21. package/dist/nodes/list-box.d.ts +9 -0
  22. package/dist/nodes/list-box.js +21 -0
  23. package/dist/nodes/list.d.ts +4 -3
  24. package/dist/nodes/list.js +8 -7
  25. package/dist/nodes/notebook.d.ts +7 -3
  26. package/dist/nodes/notebook.js +31 -14
  27. package/dist/nodes/overlay.d.ts +2 -1
  28. package/dist/nodes/root.d.ts +2 -3
  29. package/dist/nodes/root.js +3 -3
  30. package/dist/nodes/slot.d.ts +1 -2
  31. package/dist/nodes/slot.js +2 -2
  32. package/dist/nodes/text-view.d.ts +2 -7
  33. package/dist/nodes/text-view.js +10 -49
  34. package/dist/nodes/widget.d.ts +6 -5
  35. package/dist/nodes/widget.js +9 -149
  36. package/dist/nodes/window.d.ts +11 -0
  37. package/dist/nodes/window.js +37 -0
  38. package/dist/props.d.ts +5 -0
  39. package/dist/props.js +10 -0
  40. package/dist/reconciler.js +4 -5
  41. package/dist/types.d.ts +4 -4
  42. package/package.json +3 -3
@@ -1,62 +1,23 @@
1
- import { getObjectId } from "@gtkx/ffi";
2
- import * as GObject from "@gtkx/ffi/gobject";
3
- import * as Gtk from "@gtkx/ffi/gtk";
1
+ import { isChildContainer } from "../container-interfaces.js";
4
2
  import { Node } from "../node.js";
5
- import { OverlayNode } from "./overlay.js";
6
- const isFlowBox = (widget) => widget instanceof Gtk.FlowBox;
7
- const isFlowBoxChild = (widget) => GObject.typeNameFromInstance(getObjectId(widget.ptr)) === "GtkFlowBoxChild";
8
- const isListBox = (widget) => widget instanceof Gtk.ListBox;
9
- const isListBoxRow = (widget) => GObject.typeNameFromInstance(getObjectId(widget.ptr)) === "GtkListBoxRow";
10
- const COMBINED_PROPS = [
11
- {
12
- props: ["defaultWidth", "defaultHeight"],
13
- apply: (widget) => (values) => {
14
- if (widget instanceof Gtk.Window) {
15
- const width = values.defaultWidth ?? -1;
16
- const height = values.defaultHeight ?? -1;
17
- widget.setDefaultSize(width, height);
18
- }
19
- },
20
- },
21
- ];
3
+ /**
4
+ * Catch-all node for standard GTK widgets that don't need special handling.
5
+ * Specialized widgets (Window, AboutDialog, ActionBar, FlowBox, ListBox, etc.)
6
+ * are handled by their own dedicated Node classes.
7
+ */
22
8
  export class WidgetNode extends Node {
23
9
  static matches(_type) {
24
10
  return true;
25
11
  }
26
12
  attachToParent(parent) {
27
- if (this.widget instanceof Gtk.AboutDialog) {
28
- return;
29
- }
30
- if (parent instanceof OverlayNode) {
13
+ if (isChildContainer(parent)) {
31
14
  parent.attachChild(this.widget);
32
15
  return;
33
16
  }
34
- const parentWidget = parent.getWidget();
35
- if (!parentWidget)
36
- return;
37
- if (parentWidget instanceof Gtk.ActionBar) {
38
- parentWidget.packStart(this.widget);
39
- return;
40
- }
41
- if (parentWidget instanceof Gtk.Notebook) {
42
- parentWidget.appendPage(this.widget);
43
- return;
44
- }
45
- if (isFlowBox(parentWidget)) {
46
- parentWidget.append(this.widget);
47
- return;
48
- }
49
- if (isListBox(parentWidget)) {
50
- parentWidget.append(this.widget);
51
- return;
52
- }
53
17
  super.attachToParent(parent);
54
18
  }
55
19
  attachToParentBefore(parent, before) {
56
- if (this.widget instanceof Gtk.AboutDialog) {
57
- return;
58
- }
59
- if (parent instanceof OverlayNode) {
20
+ if (isChildContainer(parent)) {
60
21
  const beforeWidget = before.getWidget();
61
22
  if (beforeWidget) {
62
23
  parent.insertChildBefore(this.widget, beforeWidget);
@@ -66,114 +27,13 @@ export class WidgetNode extends Node {
66
27
  }
67
28
  return;
68
29
  }
69
- const parentWidget = parent.getWidget();
70
- const beforeWidget = before.getWidget();
71
- if (!parentWidget)
72
- return;
73
- if (parentWidget instanceof Gtk.ActionBar) {
74
- parentWidget.packStart(this.widget);
75
- return;
76
- }
77
- if (parentWidget instanceof Gtk.Notebook && beforeWidget) {
78
- const beforePageNum = parentWidget.pageNum(beforeWidget);
79
- if (beforePageNum >= 0) {
80
- parentWidget.insertPage(this.widget, beforePageNum);
81
- }
82
- else {
83
- parentWidget.appendPage(this.widget);
84
- }
85
- return;
86
- }
87
- if (isFlowBox(parentWidget)) {
88
- if (beforeWidget) {
89
- const beforeChild = beforeWidget.getParent();
90
- if (beforeChild && isFlowBoxChild(beforeChild)) {
91
- parentWidget.insert(this.widget, beforeChild.getIndex());
92
- }
93
- else {
94
- parentWidget.append(this.widget);
95
- }
96
- }
97
- else {
98
- parentWidget.append(this.widget);
99
- }
100
- return;
101
- }
102
- if (isListBox(parentWidget)) {
103
- if (beforeWidget && isListBoxRow(beforeWidget)) {
104
- parentWidget.insert(this.widget, beforeWidget.getIndex());
105
- }
106
- else {
107
- parentWidget.append(this.widget);
108
- }
109
- return;
110
- }
111
30
  super.attachToParentBefore(parent, before);
112
31
  }
113
32
  detachFromParent(parent) {
114
- if (this.widget instanceof Gtk.Window) {
115
- this.widget.destroy();
116
- return;
117
- }
118
- if (this.widget instanceof Gtk.AboutDialog) {
119
- return;
120
- }
121
- if (parent instanceof OverlayNode) {
33
+ if (isChildContainer(parent)) {
122
34
  parent.detachChild(this.widget);
123
35
  return;
124
36
  }
125
- const parentWidget = parent.getWidget();
126
- if (!parentWidget)
127
- return;
128
- if (parentWidget instanceof Gtk.ActionBar) {
129
- parentWidget.remove(this.widget);
130
- return;
131
- }
132
- if (parentWidget instanceof Gtk.Notebook) {
133
- const pageNum = parentWidget.pageNum(this.widget);
134
- if (pageNum >= 0) {
135
- parentWidget.removePage(pageNum);
136
- }
137
- return;
138
- }
139
- if (isFlowBox(parentWidget)) {
140
- const flowBoxChild = this.widget.getParent();
141
- if (flowBoxChild) {
142
- parentWidget.remove(flowBoxChild);
143
- }
144
- return;
145
- }
146
- if (isListBox(parentWidget)) {
147
- parentWidget.remove(this.widget);
148
- return;
149
- }
150
37
  super.detachFromParent(parent);
151
38
  }
152
- consumedProps() {
153
- const consumed = super.consumedProps();
154
- for (const handler of COMBINED_PROPS) {
155
- for (const prop of handler.props) {
156
- consumed.add(prop);
157
- }
158
- }
159
- return consumed;
160
- }
161
- updateProps(oldProps, newProps) {
162
- for (const handler of COMBINED_PROPS) {
163
- const hasAnyChanged = handler.props.some((prop) => oldProps[prop] !== newProps[prop]);
164
- if (hasAnyChanged) {
165
- const values = {};
166
- for (const prop of handler.props) {
167
- values[prop] = newProps[prop];
168
- }
169
- handler.apply(this.widget)(values);
170
- }
171
- }
172
- super.updateProps(oldProps, newProps);
173
- }
174
- mount(_app) {
175
- if (this.widget instanceof Gtk.Window) {
176
- this.widget.present();
177
- }
178
- }
179
39
  }
@@ -0,0 +1,11 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { Props } from "../factory.js";
3
+ import { Node } from "../node.js";
4
+ export declare class WindowNode extends Node<Gtk.Window> {
5
+ static matches(type: string): boolean;
6
+ protected createWidget(type: string, _props: Props): Gtk.Window;
7
+ detachFromParent(_parent: Node): void;
8
+ mount(): void;
9
+ protected consumedProps(): Set<string>;
10
+ updateProps(oldProps: Props, newProps: Props): void;
11
+ }
@@ -0,0 +1,37 @@
1
+ import { getCurrentApp } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
3
+ import { Node } from "../node.js";
4
+ import { getNumberProp } from "../props.js";
5
+ export class WindowNode extends Node {
6
+ static matches(type) {
7
+ return type === "Window" || type === "ApplicationWindow";
8
+ }
9
+ createWidget(type, _props) {
10
+ if (type === "ApplicationWindow") {
11
+ return new Gtk.ApplicationWindow(getCurrentApp());
12
+ }
13
+ return new Gtk.Window();
14
+ }
15
+ detachFromParent(_parent) {
16
+ this.widget.destroy();
17
+ }
18
+ mount() {
19
+ this.widget.present();
20
+ }
21
+ consumedProps() {
22
+ const consumed = super.consumedProps();
23
+ consumed.add("defaultWidth");
24
+ consumed.add("defaultHeight");
25
+ return consumed;
26
+ }
27
+ updateProps(oldProps, newProps) {
28
+ const widthChanged = oldProps.defaultWidth !== newProps.defaultWidth;
29
+ const heightChanged = oldProps.defaultHeight !== newProps.defaultHeight;
30
+ if (widthChanged || heightChanged) {
31
+ const width = getNumberProp(newProps, "defaultWidth", -1);
32
+ const height = getNumberProp(newProps, "defaultHeight", -1);
33
+ this.widget.setDefaultSize(width, height);
34
+ }
35
+ super.updateProps(oldProps, newProps);
36
+ }
37
+ }
@@ -0,0 +1,5 @@
1
+ import type { Props } from "./factory.js";
2
+ /** Extracts a number property from props with a default value. */
3
+ export declare const getNumberProp: (props: Props, key: string, defaultValue: number) => number;
4
+ /** Extracts a string property from props with a default value. */
5
+ export declare const getStringProp: (props: Props, key: string, defaultValue: string) => string;
package/dist/props.js ADDED
@@ -0,0 +1,10 @@
1
+ /** Extracts a number property from props with a default value. */
2
+ export const getNumberProp = (props, key, defaultValue) => {
3
+ const value = props[key];
4
+ return typeof value === "number" ? value : defaultValue;
5
+ };
6
+ /** Extracts a string property from props with a default value. */
7
+ export const getStringProp = (props, key, defaultValue) => {
8
+ const value = props[key];
9
+ return typeof value === "string" ? value : defaultValue;
10
+ };
@@ -1,4 +1,3 @@
1
- import { getCurrentApp } from "@gtkx/ffi";
2
1
  import React from "react";
3
2
  import ReactReconciler from "react-reconciler";
4
3
  import { beginCommit, endCommit } from "./batch.js";
@@ -30,16 +29,16 @@ class Reconciler {
30
29
  getChildHostContext: (parentHostContext) => parentHostContext,
31
30
  shouldSetTextContent: () => false,
32
31
  createInstance: (type, props) => {
33
- return createNode(type, props, getCurrentApp());
32
+ return createNode(type, props);
34
33
  },
35
- createTextInstance: (text) => createNode("Label.Root", { label: text }, getCurrentApp()),
34
+ createTextInstance: (text) => createNode("Label.Root", { label: text }),
36
35
  appendInitialChild: (parent, child) => parent.appendChild(child),
37
36
  finalizeInitialChildren: () => true,
38
37
  commitUpdate: (instance, _type, oldProps, newProps) => {
39
38
  instance.updateProps(oldProps, newProps);
40
39
  },
41
40
  commitMount: (instance) => {
42
- instance.mount(getCurrentApp());
41
+ instance.mount();
43
42
  },
44
43
  appendChild: (parent, child) => parent.appendChild(child),
45
44
  removeChild: (parent, child) => parent.removeChild(child),
@@ -105,7 +104,7 @@ class Reconciler {
105
104
  return context;
106
105
  }
107
106
  createNodeFromContainer(container) {
108
- return createNode(container.constructor.name, {}, getCurrentApp(), container);
107
+ return createNode(container.constructor.name, {}, container);
109
108
  }
110
109
  }
111
110
  /** The singleton GTKX React reconciler instance. */
package/dist/types.d.ts CHANGED
@@ -32,7 +32,7 @@ export interface ListViewRenderProps<T = unknown> {
32
32
  * @param columnId - The ID of the column being sorted
33
33
  */
34
34
  export type ColumnSortFn<T, C extends string = string> = (a: T, b: T, columnId: C) => number;
35
- export interface ColumnViewColumnProps {
35
+ export interface ColumnViewColumnProps<T = unknown> {
36
36
  title?: string;
37
37
  expand?: boolean;
38
38
  resizable?: boolean;
@@ -43,13 +43,13 @@ export interface ColumnViewColumnProps {
43
43
  * Called with null during setup (for loading state) and with the actual item during bind.
44
44
  * Always annotate your callback parameter type to include null, e.g.: `(item: MyItem | null) => ...`
45
45
  */
46
- renderCell: (item: any) => ReactElement;
46
+ renderCell: (item: T | null) => ReactElement;
47
47
  }
48
- export interface ColumnViewRootProps<C extends string = string> {
48
+ export interface ColumnViewRootProps<T = unknown, C extends string = string> {
49
49
  sortColumn?: C | null;
50
50
  sortOrder?: SortType;
51
51
  onSortChange?: (column: C | null, order: SortType) => void;
52
- sortFn?: ColumnSortFn<any, C>;
52
+ sortFn?: ColumnSortFn<T, C>;
53
53
  }
54
54
  export interface NotebookPageProps extends SlotProps {
55
55
  label: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/react",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
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.48"
39
+ "@gtkx/ffi": "0.1.49"
40
40
  },
41
41
  "devDependencies": {
42
- "@gtkx/gir": "0.1.48"
42
+ "@gtkx/gir": "0.1.49"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "react": "^19"