@gtkx/react 0.1.24 → 0.1.26

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/factory.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import type * as Gtk from "@gtkx/ffi/gtk";
2
2
  import type { Node } from "./node.js";
3
3
  export type Props = Record<string, unknown>;
4
- export declare const createNode: (type: string, props: Props, app: Gtk.Application) => Node;
4
+ export declare const createNode: (type: string, props: Props, app: Gtk.Application, existingWidget?: Gtk.Widget) => Node;
package/dist/factory.js CHANGED
@@ -5,20 +5,20 @@ import { OverlayNode } from "./nodes/overlay.js";
5
5
  import { SlotNode } from "./nodes/slot.js";
6
6
  import { WidgetNode } from "./nodes/widget.js";
7
7
  const NODE_CLASSES = [
8
- SlotNode,
9
8
  ListItemNode,
10
9
  DropDownItemNode,
11
- DropDownNode,
12
10
  GridChildNode,
11
+ SlotNode,
12
+ DropDownNode,
13
13
  GridNode,
14
14
  OverlayNode,
15
15
  ListViewNode,
16
16
  WidgetNode,
17
17
  ];
18
- export const createNode = (type, props, app) => {
18
+ export const createNode = (type, props, app, existingWidget) => {
19
19
  for (const NodeClass of NODE_CLASSES) {
20
20
  if (NodeClass.matches(type)) {
21
- return new NodeClass(type, props, app);
21
+ return new NodeClass(type, props, app, existingWidget);
22
22
  }
23
23
  }
24
24
  throw new Error(`No matching node class for type: ${type}`);
package/dist/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export { createPortal } from "./portal.js";
4
4
  export { reconciler } from "./reconciler.js";
5
5
  export { render } from "./render.js";
6
6
  export declare const quit: () => boolean;
7
+ export declare const getApp: () => import("@gtkx/ffi/gtk").Application;
package/dist/index.js CHANGED
@@ -14,3 +14,4 @@ export const quit = () => {
14
14
  }
15
15
  return true;
16
16
  };
17
+ export const getApp = () => reconciler.getApp();
package/dist/node.d.ts CHANGED
@@ -5,14 +5,15 @@ export declare abstract class Node<T extends Gtk.Widget | undefined = Gtk.Widget
5
5
  protected signalHandlers: Map<string, number>;
6
6
  protected widget: T;
7
7
  protected isVirtual(): boolean;
8
- constructor(type: string, props: Props, app: Gtk.Application);
8
+ constructor(type: string, props: Props, app: Gtk.Application, existingWidget?: Gtk.Widget);
9
9
  protected createWidget(type: string, props: Props, app: Gtk.Application): T;
10
10
  getWidget(): T;
11
11
  appendChild(child: Node): void;
12
12
  removeChild(child: Node): void;
13
- insertBefore(child: Node, _before: Node): void;
13
+ insertBefore(child: Node, before: Node): void;
14
14
  attachToParent(parent: Node): void;
15
15
  detachFromParent(parent: Node): void;
16
+ attachToParentBefore(parent: Node, before: Node): void;
16
17
  protected consumedProps(): Set<string>;
17
18
  updateProps(oldProps: Props, newProps: Props): void;
18
19
  protected setSignalProperty(widget: Gtk.Widget, key: string, handler: unknown): void;
package/dist/node.js CHANGED
@@ -17,7 +17,11 @@ export class Node {
17
17
  isVirtual() {
18
18
  return false;
19
19
  }
20
- constructor(type, props, app) {
20
+ constructor(type, props, app, existingWidget) {
21
+ if (existingWidget) {
22
+ this.widget = existingWidget;
23
+ return;
24
+ }
21
25
  this.widget = (this.isVirtual() ? undefined : this.createWidget(type, props, app));
22
26
  this.updateProps({}, props);
23
27
  }
@@ -27,7 +31,7 @@ export class Node {
27
31
  if (!WidgetClass) {
28
32
  throw new Error(`Unknown GTK widget type: ${normalizedType}`);
29
33
  }
30
- if (normalizedType === "ApplicationWindow") {
34
+ if (WidgetClass === Gtk.ApplicationWindow) {
31
35
  return new WidgetClass(app);
32
36
  }
33
37
  return new WidgetClass(...extractConstructorArgs(normalizedType, props));
@@ -41,8 +45,8 @@ export class Node {
41
45
  removeChild(child) {
42
46
  child.detachFromParent(this);
43
47
  }
44
- insertBefore(child, _before) {
45
- this.appendChild(child);
48
+ insertBefore(child, before) {
49
+ child.attachToParentBefore(this, before);
46
50
  }
47
51
  attachToParent(parent) {
48
52
  const parentWidget = parent.getWidget();
@@ -50,7 +54,7 @@ export class Node {
50
54
  if (!parentWidget || !widget)
51
55
  return;
52
56
  if (isAppendable(parentWidget)) {
53
- parentWidget.append(widget);
57
+ widget.insertBefore(parentWidget, null);
54
58
  }
55
59
  else if (isSingleChild(parentWidget)) {
56
60
  parentWidget.setChild(widget);
@@ -68,6 +72,19 @@ export class Node {
68
72
  parentWidget.setChild(null);
69
73
  }
70
74
  }
75
+ attachToParentBefore(parent, before) {
76
+ const parentWidget = parent.getWidget();
77
+ const widget = this.getWidget();
78
+ const beforeWidget = before.getWidget();
79
+ if (!parentWidget || !widget)
80
+ return;
81
+ if (isAppendable(parentWidget) && beforeWidget) {
82
+ widget.insertBefore(parentWidget, beforeWidget);
83
+ }
84
+ else {
85
+ this.attachToParent(parent);
86
+ }
87
+ }
71
88
  consumedProps() {
72
89
  return new Set(["children"]);
73
90
  }
@@ -11,6 +11,7 @@ export declare class ListViewNode extends Node<Gtk.ListView> {
11
11
  private factorySignalHandlers;
12
12
  constructor(type: string, props: Props, app: Gtk.Application);
13
13
  addItem(item: unknown): void;
14
+ insertItemBefore(item: unknown, beforeItem: unknown): void;
14
15
  removeItem(item: unknown): void;
15
16
  protected consumedProps(): Set<string>;
16
17
  updateProps(oldProps: Props, newProps: Props): void;
@@ -23,5 +24,6 @@ export declare class ListItemNode extends Node {
23
24
  constructor(type: string, props: Props, app: Gtk.Application);
24
25
  getItem(): unknown;
25
26
  attachToParent(parent: Node): void;
27
+ attachToParentBefore(parent: Node, before: Node): void;
26
28
  detachFromParent(parent: Node): void;
27
29
  }
@@ -43,6 +43,15 @@ export class ListViewNode extends Node {
43
43
  this.items.push(item);
44
44
  this.stringList.append("");
45
45
  }
46
+ insertItemBefore(item, beforeItem) {
47
+ const beforeIndex = this.items.indexOf(beforeItem);
48
+ if (beforeIndex === -1) {
49
+ this.addItem(item);
50
+ return;
51
+ }
52
+ this.items.splice(beforeIndex, 0, item);
53
+ this.stringList.splice(beforeIndex, 0, [""]);
54
+ }
46
55
  removeItem(item) {
47
56
  const index = this.items.indexOf(item);
48
57
  if (index !== -1) {
@@ -92,6 +101,14 @@ export class ListItemNode extends Node {
92
101
  parent.addItem(this.item);
93
102
  }
94
103
  }
104
+ attachToParentBefore(parent, before) {
105
+ if (parent instanceof ListViewNode && before instanceof ListItemNode) {
106
+ parent.insertItemBefore(this.item, before.getItem());
107
+ }
108
+ else {
109
+ this.attachToParent(parent);
110
+ }
111
+ }
95
112
  detachFromParent(parent) {
96
113
  if (parent instanceof ListViewNode) {
97
114
  parent.removeItem(this.item);
@@ -5,5 +5,6 @@ export declare class OverlayNode extends Node<Gtk.Overlay> {
5
5
  private mainChild;
6
6
  private overlayChildren;
7
7
  attachChild(childWidget: Gtk.Widget): void;
8
+ insertChildBefore(childWidget: Gtk.Widget, beforeWidget: Gtk.Widget): void;
8
9
  detachChild(childWidget: Gtk.Widget): void;
9
10
  }
@@ -15,6 +15,25 @@ export class OverlayNode extends Node {
15
15
  this.widget.addOverlay(childWidget);
16
16
  }
17
17
  }
18
+ insertChildBefore(childWidget, beforeWidget) {
19
+ if (this.mainChild === null) {
20
+ this.mainChild = childWidget;
21
+ this.widget.setChild(childWidget);
22
+ return;
23
+ }
24
+ if (this.mainChild === beforeWidget) {
25
+ this.overlayChildren.unshift(childWidget);
26
+ this.widget.addOverlay(childWidget);
27
+ return;
28
+ }
29
+ const beforeIndex = this.overlayChildren.indexOf(beforeWidget);
30
+ if (beforeIndex === -1) {
31
+ this.attachChild(childWidget);
32
+ return;
33
+ }
34
+ this.overlayChildren.splice(beforeIndex, 0, childWidget);
35
+ this.widget.addOverlay(childWidget);
36
+ }
18
37
  detachChild(childWidget) {
19
38
  if (this.mainChild === childWidget) {
20
39
  this.widget.setChild(null);
@@ -1,16 +1,13 @@
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
- export declare class WidgetWrapper extends Node<Gtk.Widget> {
5
- constructor(widget: Gtk.Widget, app: Gtk.Application);
6
- protected isVirtual(): boolean;
7
- }
8
4
  export declare class WidgetNode extends Node<Gtk.Widget> {
9
5
  static matches(_type: string): boolean;
10
6
  attachToParent(parent: Node): void;
7
+ attachToParentBefore(parent: Node, before: Node): void;
11
8
  detachFromParent(parent: Node): void;
12
9
  protected consumedProps(): Set<string>;
13
10
  updateProps(oldProps: Props, newProps: Props): void;
14
- mount(app: Gtk.Application): void;
15
- dispose(app: Gtk.Application): void;
11
+ mount(_app: Gtk.Application): void;
12
+ dispose(_app: Gtk.Application): void;
16
13
  }
@@ -13,15 +13,6 @@ const COMBINED_PROPS = [
13
13
  },
14
14
  },
15
15
  ];
16
- export class WidgetWrapper extends Node {
17
- constructor(widget, app) {
18
- super("", {}, app);
19
- this.widget = widget;
20
- }
21
- isVirtual() {
22
- return true;
23
- }
24
- }
25
16
  export class WidgetNode extends Node {
26
17
  static matches(_type) {
27
18
  return true;
@@ -47,6 +38,40 @@ export class WidgetNode extends Node {
47
38
  }
48
39
  super.attachToParent(parent);
49
40
  }
41
+ attachToParentBefore(parent, before) {
42
+ if (this.widget instanceof Gtk.AboutDialog) {
43
+ return;
44
+ }
45
+ if (parent instanceof OverlayNode) {
46
+ const beforeWidget = before.getWidget();
47
+ if (beforeWidget) {
48
+ parent.insertChildBefore(this.widget, beforeWidget);
49
+ }
50
+ else {
51
+ parent.attachChild(this.widget);
52
+ }
53
+ return;
54
+ }
55
+ const parentWidget = parent.getWidget();
56
+ const beforeWidget = before.getWidget();
57
+ if (!parentWidget)
58
+ return;
59
+ if (parentWidget instanceof Gtk.ActionBar) {
60
+ parentWidget.packStart(this.widget);
61
+ return;
62
+ }
63
+ if (parentWidget instanceof Gtk.Notebook && beforeWidget) {
64
+ const beforePageNum = parentWidget.pageNum(beforeWidget);
65
+ if (beforePageNum >= 0) {
66
+ parentWidget.insertPage(this.widget, beforePageNum);
67
+ }
68
+ else {
69
+ parentWidget.appendPage(this.widget);
70
+ }
71
+ return;
72
+ }
73
+ super.attachToParentBefore(parent, before);
74
+ }
50
75
  detachFromParent(parent) {
51
76
  if (this.widget instanceof Gtk.AboutDialog) {
52
77
  return;
@@ -73,7 +98,6 @@ export class WidgetNode extends Node {
73
98
  }
74
99
  consumedProps() {
75
100
  const consumed = super.consumedProps();
76
- consumed.add("application");
77
101
  for (const handler of COMBINED_PROPS) {
78
102
  for (const prop of handler.props) {
79
103
  consumed.add(prop);
@@ -94,21 +118,14 @@ export class WidgetNode extends Node {
94
118
  }
95
119
  super.updateProps(oldProps, newProps);
96
120
  }
97
- mount(app) {
98
- if (this.widget instanceof Gtk.AboutDialog) {
99
- const activeWindow = app.getActiveWindow();
100
- if (activeWindow) {
101
- this.widget.setTransientFor(activeWindow);
102
- }
103
- }
121
+ mount(_app) {
104
122
  if (this.widget instanceof Gtk.Window) {
105
123
  this.widget.present();
106
124
  }
107
125
  }
108
- dispose(app) {
109
- super.dispose(app);
126
+ dispose(_app) {
110
127
  if (this.widget instanceof Gtk.Window) {
111
- this.widget.close();
128
+ this.widget.destroy();
112
129
  }
113
130
  }
114
131
  }
@@ -17,6 +17,7 @@ declare class Reconciler {
17
17
  getInstance(): ReconcilerInstance;
18
18
  private createHostConfig;
19
19
  private createReconcilerContext;
20
+ private createNodeFromContainer;
20
21
  }
21
22
  export declare const reconciler: Reconciler;
22
23
  export {};
@@ -2,7 +2,6 @@ import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import React from "react";
3
3
  import ReactReconciler from "react-reconciler";
4
4
  import { createNode } from "./factory.js";
5
- import { WidgetWrapper } from "./nodes/widget.js";
6
5
  class Reconciler {
7
6
  instance;
8
7
  app = null;
@@ -45,19 +44,16 @@ class Reconciler {
45
44
  removeChild: (parent, child) => parent.removeChild(child),
46
45
  insertBefore: (parent, child, beforeChild) => parent.insertBefore(child, beforeChild),
47
46
  removeChildFromContainer: (container, child) => {
48
- if (container instanceof Gtk.Widget) {
49
- child.detachFromParent(new WidgetWrapper(container, this.getApp()));
50
- }
47
+ const parent = this.createNodeFromContainer(container);
48
+ parent.removeChild(child);
51
49
  },
52
50
  appendChildToContainer: (container, child) => {
53
- if (container instanceof Gtk.Widget) {
54
- child.attachToParent(new WidgetWrapper(container, this.getApp()));
55
- }
51
+ const parent = this.createNodeFromContainer(container);
52
+ parent.appendChild(child);
56
53
  },
57
- insertInContainerBefore: (container, child, _beforeChild) => {
58
- if (container instanceof Gtk.Widget) {
59
- child.attachToParent(new WidgetWrapper(container, this.getApp()));
60
- }
54
+ insertInContainerBefore: (container, child, beforeChild) => {
55
+ const parent = this.createNodeFromContainer(container);
56
+ parent.insertBefore(child, beforeChild);
61
57
  },
62
58
  prepareForCommit: () => null,
63
59
  resetAfterCommit: () => { },
@@ -104,5 +100,11 @@ class Reconciler {
104
100
  const context = React.createContext(value);
105
101
  return context;
106
102
  }
103
+ createNodeFromContainer(container) {
104
+ if (container instanceof Gtk.Widget) {
105
+ return createNode(container.constructor.name, {}, this.getApp(), container);
106
+ }
107
+ return createNode("Application", {}, this.getApp(), container);
108
+ }
107
109
  }
108
110
  export const reconciler = new Reconciler();
package/dist/render.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { ApplicationFlags } from "@gtkx/ffi/gio";
2
- import type { Application } from "@gtkx/ffi/gtk";
3
2
  import type { ReactNode } from "react";
4
3
  export declare let container: unknown;
5
- export declare const render: (element: ReactNode, appId: string, flags?: ApplicationFlags) => Application;
4
+ export declare const render: (element: ReactNode, appId: string, flags?: ApplicationFlags) => void;
package/dist/render.js CHANGED
@@ -9,5 +9,4 @@ export const render = (element, appId, flags) => {
9
9
  console.error("Uncaught error in GTKX application:", error, info);
10
10
  }, (_error, _info) => { }, (_error, _info) => { }, () => { }, null);
11
11
  instance.updateContainer(element, container, null, () => { });
12
- return app;
13
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/react",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Build GTK4 desktop applications with React and TypeScript",
5
5
  "keywords": [
6
6
  "gtk",
@@ -40,10 +40,10 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "react-reconciler": "0.33.0",
43
- "@gtkx/ffi": "0.1.24"
43
+ "@gtkx/ffi": "0.1.26"
44
44
  },
45
45
  "devDependencies": {
46
- "@gtkx/gir": "0.1.24"
46
+ "@gtkx/gir": "0.1.26"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "react": "^19"
@@ -51,6 +51,6 @@
51
51
  "scripts": {
52
52
  "build": "tsc -b && cp ../../README.md .",
53
53
  "codegen": "tsx scripts/codegen.ts",
54
- "test": "GDK_BACKEND=x11 xvfb-run -a vitest run"
54
+ "test": "if [ -n \"$CI\" ]; then vitest run; else GDK_BACKEND=x11 xvfb-run -a vitest run; fi"
55
55
  }
56
56
  }