@gtkx/react 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/batch.d.ts +4 -1
- package/dist/batch.js +19 -10
- package/dist/codegen/jsx-generator.d.ts +4 -4
- package/dist/codegen/jsx-generator.js +24 -27
- package/dist/container-interfaces.d.ts +19 -6
- package/dist/container-interfaces.js +26 -6
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +38 -0
- package/dist/factory.js +9 -3
- package/dist/generated/jsx.d.ts +38 -26
- package/dist/generated/jsx.js +12 -2
- package/dist/index.js +3 -1
- package/dist/node.d.ts +5 -0
- package/dist/node.js +62 -6
- package/dist/nodes/action-bar.d.ts +2 -6
- package/dist/nodes/action-bar.js +3 -12
- package/dist/nodes/column-view.d.ts +19 -44
- package/dist/nodes/column-view.js +70 -243
- package/dist/nodes/combo-row.d.ts +5 -0
- package/dist/nodes/combo-row.js +6 -0
- package/dist/nodes/drop-down.d.ts +9 -0
- package/dist/nodes/drop-down.js +12 -0
- package/dist/nodes/flow-box.d.ts +4 -6
- package/dist/nodes/flow-box.js +8 -16
- package/dist/nodes/grid.d.ts +15 -15
- package/dist/nodes/grid.js +21 -64
- package/dist/nodes/header-bar.d.ts +34 -11
- package/dist/nodes/header-bar.js +52 -24
- package/dist/nodes/indexed-child-container.d.ts +16 -0
- package/dist/nodes/indexed-child-container.js +22 -0
- package/dist/nodes/list-box.d.ts +3 -6
- package/dist/nodes/list-box.js +6 -14
- package/dist/nodes/list-item-factory.d.ts +19 -0
- package/dist/nodes/list-item-factory.js +58 -0
- package/dist/nodes/list-view.d.ts +24 -0
- package/dist/nodes/list-view.js +46 -0
- package/dist/nodes/menu.d.ts +25 -19
- package/dist/nodes/menu.js +30 -59
- package/dist/nodes/notebook.d.ts +13 -14
- package/dist/nodes/notebook.js +18 -56
- package/dist/nodes/paged-stack.d.ts +39 -0
- package/dist/nodes/paged-stack.js +54 -0
- package/dist/nodes/selectable-list.d.ts +41 -0
- package/dist/nodes/selectable-list.js +228 -0
- package/dist/nodes/stack-page-props.d.ts +11 -0
- package/dist/nodes/stack-page-props.js +23 -0
- package/dist/nodes/stack.d.ts +14 -28
- package/dist/nodes/stack.js +30 -142
- package/dist/nodes/string-list-container.d.ts +41 -0
- package/dist/nodes/string-list-container.js +90 -0
- package/dist/nodes/string-list-item.d.ts +15 -0
- package/dist/nodes/string-list-item.js +48 -0
- package/dist/nodes/string-list-store.d.ts +13 -0
- package/dist/nodes/string-list-store.js +44 -0
- package/dist/nodes/text-view.d.ts +1 -1
- package/dist/nodes/text-view.js +1 -5
- package/dist/nodes/toggle-button.d.ts +1 -1
- package/dist/nodes/toggle-button.js +1 -3
- package/dist/nodes/view-stack.d.ts +9 -0
- package/dist/nodes/view-stack.js +28 -0
- package/dist/nodes/virtual-item.d.ts +20 -0
- package/dist/nodes/virtual-item.js +57 -0
- package/dist/nodes/virtual-slot.d.ts +25 -0
- package/dist/nodes/virtual-slot.js +71 -0
- package/dist/nodes/widget.d.ts +0 -3
- package/dist/nodes/widget.js +0 -28
- package/dist/nodes/window.d.ts +1 -1
- package/dist/nodes/window.js +9 -15
- package/dist/predicates.d.ts +8 -0
- package/dist/predicates.js +8 -0
- package/dist/props.d.ts +7 -5
- package/dist/props.js +11 -9
- package/dist/reconciler/host-config.d.ts +19 -0
- package/dist/reconciler/host-config.js +89 -0
- package/dist/reconciler.d.ts +2 -26
- package/dist/reconciler.js +15 -106
- package/dist/render.d.ts +1 -2
- package/dist/render.js +16 -4
- package/dist/types.d.ts +19 -16
- package/package.json +4 -4
- package/dist/nodes/dropdown.d.ts +0 -39
- package/dist/nodes/dropdown.js +0 -103
- package/dist/nodes/list.d.ts +0 -43
- package/dist/nodes/list.js +0 -153
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Node } from "../node.js";
|
|
2
|
+
export class VirtualSlotNode extends Node {
|
|
3
|
+
isVirtual() {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
childWidget = null;
|
|
7
|
+
parentContainer = null;
|
|
8
|
+
_slotProps;
|
|
9
|
+
get slotProps() {
|
|
10
|
+
if (this._slotProps === undefined) {
|
|
11
|
+
throw new Error("slotProps accessed before initialization");
|
|
12
|
+
}
|
|
13
|
+
return this._slotProps;
|
|
14
|
+
}
|
|
15
|
+
set slotProps(value) {
|
|
16
|
+
this._slotProps = value;
|
|
17
|
+
}
|
|
18
|
+
initialize(props) {
|
|
19
|
+
this.slotProps = this.extractSlotProps(props);
|
|
20
|
+
super.initialize(props);
|
|
21
|
+
}
|
|
22
|
+
getChildWidget() {
|
|
23
|
+
return this.childWidget;
|
|
24
|
+
}
|
|
25
|
+
appendChild(child) {
|
|
26
|
+
const childWidget = child.getWidget();
|
|
27
|
+
if (childWidget) {
|
|
28
|
+
this.childWidget = childWidget;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
attachToParent(parent) {
|
|
32
|
+
if (this.isValidContainer(parent) && this.childWidget) {
|
|
33
|
+
this.parentContainer = parent;
|
|
34
|
+
this.addToContainer(parent, this.childWidget, this.slotProps);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
attachToParentBefore(parent, before) {
|
|
38
|
+
if (this.isValidContainer(parent) && this.childWidget) {
|
|
39
|
+
this.parentContainer = parent;
|
|
40
|
+
const beforeWidget = this.getBeforeWidget(before);
|
|
41
|
+
if (beforeWidget) {
|
|
42
|
+
this.insertBeforeInContainer(parent, this.childWidget, this.slotProps, beforeWidget);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.addToContainer(parent, this.childWidget, this.slotProps);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
getBeforeWidget(before) {
|
|
50
|
+
if (before instanceof VirtualSlotNode) {
|
|
51
|
+
return before.getChildWidget();
|
|
52
|
+
}
|
|
53
|
+
return before.getWidget() ?? null;
|
|
54
|
+
}
|
|
55
|
+
detachFromParent(parent) {
|
|
56
|
+
if (this.isValidContainer(parent) && this.childWidget) {
|
|
57
|
+
this.removeFromContainer(parent, this.childWidget);
|
|
58
|
+
this.parentContainer = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
updateSlotPropsIfChanged(oldProps, newProps, propKeys) {
|
|
62
|
+
const changed = propKeys.some((key) => oldProps[key] !== newProps[key]);
|
|
63
|
+
if (changed) {
|
|
64
|
+
this.slotProps = this.extractSlotProps(newProps);
|
|
65
|
+
if (this.parentContainer && this.childWidget) {
|
|
66
|
+
this.updateInContainer(this.parentContainer, this.childWidget, this.slotProps);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return changed;
|
|
70
|
+
}
|
|
71
|
+
}
|
package/dist/nodes/widget.d.ts
CHANGED
|
@@ -7,7 +7,4 @@ import { Node } from "../node.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class WidgetNode extends Node<Gtk.Widget> {
|
|
9
9
|
static matches(_type: string): boolean;
|
|
10
|
-
attachToParent(parent: Node): void;
|
|
11
|
-
attachToParentBefore(parent: Node, before: Node): void;
|
|
12
|
-
detachFromParent(parent: Node): void;
|
|
13
10
|
}
|
package/dist/nodes/widget.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isChildContainer } from "../container-interfaces.js";
|
|
2
1
|
import { Node } from "../node.js";
|
|
3
2
|
/**
|
|
4
3
|
* Catch-all node for standard GTK widgets that don't need special handling.
|
|
@@ -9,31 +8,4 @@ export class WidgetNode extends Node {
|
|
|
9
8
|
static matches(_type) {
|
|
10
9
|
return true;
|
|
11
10
|
}
|
|
12
|
-
attachToParent(parent) {
|
|
13
|
-
if (isChildContainer(parent)) {
|
|
14
|
-
parent.attachChild(this.widget);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
super.attachToParent(parent);
|
|
18
|
-
}
|
|
19
|
-
attachToParentBefore(parent, before) {
|
|
20
|
-
if (isChildContainer(parent)) {
|
|
21
|
-
const beforeWidget = before.getWidget();
|
|
22
|
-
if (beforeWidget) {
|
|
23
|
-
parent.insertChildBefore(this.widget, beforeWidget);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
parent.attachChild(this.widget);
|
|
27
|
-
}
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
super.attachToParentBefore(parent, before);
|
|
31
|
-
}
|
|
32
|
-
detachFromParent(parent) {
|
|
33
|
-
if (isChildContainer(parent)) {
|
|
34
|
-
parent.detachChild(this.widget);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
super.detachFromParent(parent);
|
|
38
|
-
}
|
|
39
11
|
}
|
package/dist/nodes/window.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import * as Gtk from "@gtkx/ffi/gtk";
|
|
|
2
2
|
import type { Props } from "../factory.js";
|
|
3
3
|
import { Node } from "../node.js";
|
|
4
4
|
export declare class WindowNode extends Node<Gtk.Window> {
|
|
5
|
+
static consumedPropNames: string[];
|
|
5
6
|
static matches(type: string): boolean;
|
|
6
7
|
protected createWidget(type: string, _props: Props): Gtk.Window;
|
|
7
8
|
detachFromParent(_parent: Node): void;
|
|
8
9
|
mount(): void;
|
|
9
|
-
protected consumedProps(): Set<string>;
|
|
10
10
|
updateProps(oldProps: Props, newProps: Props): void;
|
|
11
11
|
}
|
package/dist/nodes/window.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { getCurrentApp } from "@gtkx/ffi";
|
|
2
2
|
import * as Adw from "@gtkx/ffi/adw";
|
|
3
3
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
4
|
-
import { Node } from "../node.js";
|
|
5
|
-
import { getNumberProp } from "../props.js";
|
|
4
|
+
import { Node, normalizeWidgetType } from "../node.js";
|
|
6
5
|
const WINDOW_TYPES = new Set(["Window", "ApplicationWindow", "AdwWindow", "AdwApplicationWindow"]);
|
|
7
6
|
export class WindowNode extends Node {
|
|
7
|
+
static consumedPropNames = ["defaultWidth", "defaultHeight"];
|
|
8
8
|
static matches(type) {
|
|
9
|
-
return WINDOW_TYPES.has(
|
|
9
|
+
return WINDOW_TYPES.has(normalizeWidgetType(type));
|
|
10
10
|
}
|
|
11
11
|
createWidget(type, _props) {
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
12
|
+
const widgetType = normalizeWidgetType(type);
|
|
13
|
+
if (widgetType === "ApplicationWindow") {
|
|
14
14
|
return new Gtk.ApplicationWindow(getCurrentApp());
|
|
15
15
|
}
|
|
16
|
-
if (
|
|
16
|
+
if (widgetType === "AdwApplicationWindow") {
|
|
17
17
|
return new Adw.ApplicationWindow(getCurrentApp());
|
|
18
18
|
}
|
|
19
|
-
if (
|
|
19
|
+
if (widgetType === "AdwWindow") {
|
|
20
20
|
return new Adw.Window();
|
|
21
21
|
}
|
|
22
22
|
return new Gtk.Window();
|
|
@@ -27,18 +27,12 @@ export class WindowNode extends Node {
|
|
|
27
27
|
mount() {
|
|
28
28
|
this.widget.present();
|
|
29
29
|
}
|
|
30
|
-
consumedProps() {
|
|
31
|
-
const consumed = super.consumedProps();
|
|
32
|
-
consumed.add("defaultWidth");
|
|
33
|
-
consumed.add("defaultHeight");
|
|
34
|
-
return consumed;
|
|
35
|
-
}
|
|
36
30
|
updateProps(oldProps, newProps) {
|
|
37
31
|
const widthChanged = oldProps.defaultWidth !== newProps.defaultWidth;
|
|
38
32
|
const heightChanged = oldProps.defaultHeight !== newProps.defaultHeight;
|
|
39
33
|
if (widthChanged || heightChanged) {
|
|
40
|
-
const width =
|
|
41
|
-
const height =
|
|
34
|
+
const width = newProps.defaultWidth ?? -1;
|
|
35
|
+
const height = newProps.defaultHeight ?? -1;
|
|
42
36
|
this.widget.setDefaultSize(width, height);
|
|
43
37
|
}
|
|
44
38
|
super.updateProps(oldProps, newProps);
|
package/dist/predicates.d.ts
CHANGED
|
@@ -27,4 +27,12 @@ export declare const isSingleChild: (widget: Gtk.Widget) => widget is SingleChil
|
|
|
27
27
|
* Type guard that checks if a GTK widget supports removing children via a remove method.
|
|
28
28
|
*/
|
|
29
29
|
export declare const isRemovable: (widget: Gtk.Widget) => widget is Removable;
|
|
30
|
+
/**
|
|
31
|
+
* Type guard that checks if a GTK widget is a FlowBoxChild.
|
|
32
|
+
*/
|
|
33
|
+
export declare const isFlowBoxChild: (widget: Gtk.Widget) => widget is Gtk.FlowBoxChild;
|
|
34
|
+
/**
|
|
35
|
+
* Type guard that checks if a GTK widget is a ListBoxRow.
|
|
36
|
+
*/
|
|
37
|
+
export declare const isListBoxRow: (widget: Gtk.Widget) => widget is Gtk.ListBoxRow;
|
|
30
38
|
export {};
|
package/dist/predicates.js
CHANGED
|
@@ -14,3 +14,11 @@ export const isSingleChild = (widget) => "setChild" in widget && typeof widget.s
|
|
|
14
14
|
* Type guard that checks if a GTK widget supports removing children via a remove method.
|
|
15
15
|
*/
|
|
16
16
|
export const isRemovable = (widget) => "remove" in widget && typeof widget.remove === "function";
|
|
17
|
+
/**
|
|
18
|
+
* Type guard that checks if a GTK widget is a FlowBoxChild.
|
|
19
|
+
*/
|
|
20
|
+
export const isFlowBoxChild = (widget) => "getIndex" in widget && "getChild" in widget && typeof widget.getIndex === "function";
|
|
21
|
+
/**
|
|
22
|
+
* Type guard that checks if a GTK widget is a ListBoxRow.
|
|
23
|
+
*/
|
|
24
|
+
export const isListBoxRow = (widget) => "getIndex" in widget && "isSelected" in widget && typeof widget.getIndex === "function";
|
package/dist/props.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
type CallbackAction = "connect" | "disconnect" | "none";
|
|
2
|
+
type CallbackChange<T> = {
|
|
3
|
+
callback: T | undefined;
|
|
4
|
+
action: CallbackAction;
|
|
5
|
+
};
|
|
6
|
+
export declare const getCallbackChange: <T>(oldCallback: T | undefined, newCallback: T | undefined) => CallbackChange<T>;
|
|
7
|
+
export {};
|
package/dist/props.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
export const getCallbackChange = (oldCallback, newCallback) => {
|
|
2
|
+
if (oldCallback === newCallback) {
|
|
3
|
+
return { callback: newCallback, action: "none" };
|
|
4
|
+
}
|
|
5
|
+
if (oldCallback && !newCallback) {
|
|
6
|
+
return { callback: undefined, action: "disconnect" };
|
|
7
|
+
}
|
|
8
|
+
if (!oldCallback && newCallback) {
|
|
9
|
+
return { callback: newCallback, action: "connect" };
|
|
10
|
+
}
|
|
11
|
+
return { callback: newCallback, action: "none" };
|
|
10
12
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
+
import type ReactReconciler from "react-reconciler";
|
|
3
|
+
import { type Props, type ROOT_NODE_CONTAINER } from "../factory.js";
|
|
4
|
+
import type { Node } from "../node.js";
|
|
5
|
+
type Container = Gtk.Widget | typeof ROOT_NODE_CONTAINER;
|
|
6
|
+
type TextInstance = Node;
|
|
7
|
+
type SuspenseInstance = never;
|
|
8
|
+
type HydratableInstance = never;
|
|
9
|
+
type PublicInstance = Gtk.Widget;
|
|
10
|
+
type HostContext = Record<string, never>;
|
|
11
|
+
type ChildSet = never;
|
|
12
|
+
type TimeoutHandle = number;
|
|
13
|
+
type NoTimeout = -1;
|
|
14
|
+
type TransitionStatus = number;
|
|
15
|
+
type FormInstance = never;
|
|
16
|
+
type HostConfig = ReactReconciler.HostConfig<string, Props, Container, Node, TextInstance, SuspenseInstance, HydratableInstance, FormInstance, PublicInstance, HostContext, ChildSet, TimeoutHandle, NoTimeout, TransitionStatus>;
|
|
17
|
+
export type ReconcilerInstance = ReactReconciler.Reconciler<Container, Node, TextInstance, SuspenseInstance, FormInstance, PublicInstance>;
|
|
18
|
+
export declare function createHostConfig(createNodeFromContainer: (container: Container) => Node): HostConfig;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { beginBatch, endBatch } from "@gtkx/ffi";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { beginCommit, endCommit } from "../batch.js";
|
|
4
|
+
import { createNode } from "../factory.js";
|
|
5
|
+
export function createHostConfig(createNodeFromContainer) {
|
|
6
|
+
return {
|
|
7
|
+
supportsMutation: true,
|
|
8
|
+
supportsPersistence: false,
|
|
9
|
+
supportsHydration: false,
|
|
10
|
+
isPrimaryRenderer: true,
|
|
11
|
+
noTimeout: -1,
|
|
12
|
+
getRootHostContext: () => ({}),
|
|
13
|
+
getChildHostContext: (parentHostContext) => parentHostContext,
|
|
14
|
+
shouldSetTextContent: () => false,
|
|
15
|
+
createInstance: (type, props) => createNode(type, props),
|
|
16
|
+
createTextInstance: (text) => createNode("Label", { label: text }),
|
|
17
|
+
appendInitialChild: (parent, child) => parent.appendChild(child),
|
|
18
|
+
finalizeInitialChildren: () => true,
|
|
19
|
+
commitUpdate: (instance, _type, oldProps, newProps) => {
|
|
20
|
+
instance.updateProps(oldProps, newProps);
|
|
21
|
+
},
|
|
22
|
+
commitMount: (instance) => {
|
|
23
|
+
instance.mount();
|
|
24
|
+
},
|
|
25
|
+
appendChild: (parent, child) => parent.appendChild(child),
|
|
26
|
+
removeChild: (parent, child) => parent.removeChild(child),
|
|
27
|
+
insertBefore: (parent, child, beforeChild) => parent.insertBefore(child, beforeChild),
|
|
28
|
+
removeChildFromContainer: (container, child) => {
|
|
29
|
+
const parent = createNodeFromContainer(container);
|
|
30
|
+
parent.removeChild(child);
|
|
31
|
+
},
|
|
32
|
+
appendChildToContainer: (container, child) => {
|
|
33
|
+
const parent = createNodeFromContainer(container);
|
|
34
|
+
parent.appendChild(child);
|
|
35
|
+
},
|
|
36
|
+
insertInContainerBefore: (container, child, beforeChild) => {
|
|
37
|
+
const parent = createNodeFromContainer(container);
|
|
38
|
+
parent.insertBefore(child, beforeChild);
|
|
39
|
+
},
|
|
40
|
+
prepareForCommit: () => {
|
|
41
|
+
beginBatch();
|
|
42
|
+
beginCommit();
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
resetAfterCommit: () => {
|
|
46
|
+
endCommit();
|
|
47
|
+
endBatch();
|
|
48
|
+
},
|
|
49
|
+
commitTextUpdate: (textInstance, oldText, newText) => {
|
|
50
|
+
textInstance.updateProps({ label: oldText }, { label: newText });
|
|
51
|
+
},
|
|
52
|
+
clearContainer: () => { },
|
|
53
|
+
preparePortalMount: () => { },
|
|
54
|
+
scheduleTimeout: (fn, delay) => {
|
|
55
|
+
const timeoutId = setTimeout(fn, delay ?? 0);
|
|
56
|
+
return typeof timeoutId === "number" ? timeoutId : Number(timeoutId);
|
|
57
|
+
},
|
|
58
|
+
cancelTimeout: (id) => {
|
|
59
|
+
clearTimeout(id);
|
|
60
|
+
},
|
|
61
|
+
getPublicInstance: (instance) => instance.getWidget(),
|
|
62
|
+
getCurrentUpdatePriority: () => 2,
|
|
63
|
+
setCurrentUpdatePriority: () => { },
|
|
64
|
+
resolveUpdatePriority: () => 2,
|
|
65
|
+
NotPendingTransition: null,
|
|
66
|
+
HostTransitionContext: createReconcilerContext(0),
|
|
67
|
+
getInstanceFromNode: () => null,
|
|
68
|
+
beforeActiveInstanceBlur: () => { },
|
|
69
|
+
afterActiveInstanceBlur: () => { },
|
|
70
|
+
prepareScopeUpdate: () => { },
|
|
71
|
+
getInstanceFromScope: () => null,
|
|
72
|
+
detachDeletedInstance: () => { },
|
|
73
|
+
resetFormInstance: () => { },
|
|
74
|
+
requestPostPaintCallback: () => { },
|
|
75
|
+
shouldAttemptEagerTransition: () => false,
|
|
76
|
+
trackSchedulerEvent: () => { },
|
|
77
|
+
resolveEventType: () => null,
|
|
78
|
+
resolveEventTimeStamp: () => Date.now(),
|
|
79
|
+
maySuspendCommit: () => false,
|
|
80
|
+
preloadInstance: () => false,
|
|
81
|
+
startSuspendingCommit: () => { },
|
|
82
|
+
suspendInstance: () => { },
|
|
83
|
+
waitForCommitToBeReady: () => null,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function createReconcilerContext(value) {
|
|
87
|
+
const context = React.createContext(value);
|
|
88
|
+
return context;
|
|
89
|
+
}
|
package/dist/reconciler.d.ts
CHANGED
|
@@ -1,33 +1,9 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import ReactReconciler from "react-reconciler";
|
|
3
|
-
import { type ROOT_NODE_CONTAINER } from "./factory.js";
|
|
4
|
-
import type { Node } from "./node.js";
|
|
5
|
-
type Container = Gtk.Widget | typeof ROOT_NODE_CONTAINER;
|
|
6
|
-
type TextInstance = Node;
|
|
7
|
-
type SuspenseInstance = never;
|
|
8
|
-
type PublicInstance = Gtk.Widget;
|
|
9
|
-
type FormInstance = never;
|
|
10
|
-
type ReconcilerInstance = ReactReconciler.Reconciler<Container, Node, TextInstance, SuspenseInstance, FormInstance, PublicInstance>;
|
|
11
|
-
/**
|
|
12
|
-
* GTKX React reconciler class.
|
|
13
|
-
*/
|
|
1
|
+
import { type ReconcilerInstance } from "./reconciler/host-config.js";
|
|
14
2
|
declare class Reconciler {
|
|
15
3
|
private instance;
|
|
16
|
-
/** Creates a new GTK reconciler instance. */
|
|
17
4
|
constructor();
|
|
18
|
-
/**
|
|
19
|
-
* Gets the underlying React reconciler instance.
|
|
20
|
-
* @returns The react-reconciler instance
|
|
21
|
-
*/
|
|
22
5
|
getInstance(): ReconcilerInstance;
|
|
23
|
-
private
|
|
24
|
-
/**
|
|
25
|
-
* Creates a React context compatible with the reconciler's internal type.
|
|
26
|
-
* Cast is necessary because React.Context and ReactReconciler.ReactContext
|
|
27
|
-
* are structurally identical at runtime but declared as separate types.
|
|
28
|
-
*/
|
|
29
|
-
private createReconcilerContext;
|
|
30
|
-
private createNodeFromContainer;
|
|
6
|
+
private injectDevTools;
|
|
31
7
|
}
|
|
32
8
|
/**
|
|
33
9
|
* The singleton GTKX React reconciler instance.
|
package/dist/reconciler.js
CHANGED
|
@@ -1,118 +1,27 @@
|
|
|
1
|
-
import { beginBatch, endBatch } from "@gtkx/ffi";
|
|
2
|
-
import React from "react";
|
|
3
1
|
import ReactReconciler from "react-reconciler";
|
|
4
|
-
import
|
|
2
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
5
3
|
import { createNode } from "./factory.js";
|
|
6
|
-
|
|
7
|
-
* GTKX React reconciler class.
|
|
8
|
-
*/
|
|
4
|
+
import { createHostConfig } from "./reconciler/host-config.js";
|
|
9
5
|
class Reconciler {
|
|
10
6
|
instance;
|
|
11
|
-
/** Creates a new GTK reconciler instance. */
|
|
12
7
|
constructor() {
|
|
13
|
-
|
|
8
|
+
const createNodeFromContainer = (container) => {
|
|
9
|
+
return createNode(container.constructor.name, {}, container);
|
|
10
|
+
};
|
|
11
|
+
this.instance = ReactReconciler(createHostConfig(createNodeFromContainer));
|
|
12
|
+
this.injectDevTools();
|
|
14
13
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Gets the underlying React reconciler instance.
|
|
17
|
-
* @returns The react-reconciler instance
|
|
18
|
-
*/
|
|
19
14
|
getInstance() {
|
|
20
15
|
return this.instance;
|
|
21
16
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
getChildHostContext: (parentHostContext) => parentHostContext,
|
|
31
|
-
shouldSetTextContent: () => false,
|
|
32
|
-
createInstance: (type, props) => {
|
|
33
|
-
return createNode(type, props);
|
|
34
|
-
},
|
|
35
|
-
createTextInstance: (text) => createNode("Label", { label: text }),
|
|
36
|
-
appendInitialChild: (parent, child) => parent.appendChild(child),
|
|
37
|
-
finalizeInitialChildren: () => true,
|
|
38
|
-
commitUpdate: (instance, _type, oldProps, newProps) => {
|
|
39
|
-
instance.updateProps(oldProps, newProps);
|
|
40
|
-
},
|
|
41
|
-
commitMount: (instance) => {
|
|
42
|
-
instance.mount();
|
|
43
|
-
},
|
|
44
|
-
appendChild: (parent, child) => parent.appendChild(child),
|
|
45
|
-
removeChild: (parent, child) => parent.removeChild(child),
|
|
46
|
-
insertBefore: (parent, child, beforeChild) => parent.insertBefore(child, beforeChild),
|
|
47
|
-
removeChildFromContainer: (container, child) => {
|
|
48
|
-
const parent = this.createNodeFromContainer(container);
|
|
49
|
-
parent.removeChild(child);
|
|
50
|
-
},
|
|
51
|
-
appendChildToContainer: (container, child) => {
|
|
52
|
-
const parent = this.createNodeFromContainer(container);
|
|
53
|
-
parent.appendChild(child);
|
|
54
|
-
},
|
|
55
|
-
insertInContainerBefore: (container, child, beforeChild) => {
|
|
56
|
-
const parent = this.createNodeFromContainer(container);
|
|
57
|
-
parent.insertBefore(child, beforeChild);
|
|
58
|
-
},
|
|
59
|
-
prepareForCommit: () => {
|
|
60
|
-
beginBatch();
|
|
61
|
-
beginCommit();
|
|
62
|
-
return null;
|
|
63
|
-
},
|
|
64
|
-
resetAfterCommit: () => {
|
|
65
|
-
endCommit();
|
|
66
|
-
endBatch();
|
|
67
|
-
},
|
|
68
|
-
commitTextUpdate: (textInstance, oldText, newText) => {
|
|
69
|
-
textInstance.updateProps({ label: oldText }, { label: newText });
|
|
70
|
-
},
|
|
71
|
-
clearContainer: () => { },
|
|
72
|
-
preparePortalMount: () => { },
|
|
73
|
-
scheduleTimeout: (fn, delay) => {
|
|
74
|
-
const timeoutId = setTimeout(fn, delay ?? 0);
|
|
75
|
-
return typeof timeoutId === "number" ? timeoutId : Number(timeoutId);
|
|
76
|
-
},
|
|
77
|
-
cancelTimeout: (id) => {
|
|
78
|
-
clearTimeout(id);
|
|
79
|
-
},
|
|
80
|
-
getPublicInstance: (instance) => instance.getWidget(),
|
|
81
|
-
getCurrentUpdatePriority: () => 2,
|
|
82
|
-
setCurrentUpdatePriority: () => { },
|
|
83
|
-
resolveUpdatePriority: () => 2,
|
|
84
|
-
NotPendingTransition: null,
|
|
85
|
-
HostTransitionContext: this.createReconcilerContext(0),
|
|
86
|
-
getInstanceFromNode: () => null,
|
|
87
|
-
beforeActiveInstanceBlur: () => { },
|
|
88
|
-
afterActiveInstanceBlur: () => { },
|
|
89
|
-
prepareScopeUpdate: () => { },
|
|
90
|
-
getInstanceFromScope: () => null,
|
|
91
|
-
detachDeletedInstance: () => { },
|
|
92
|
-
resetFormInstance: () => { },
|
|
93
|
-
requestPostPaintCallback: () => { },
|
|
94
|
-
shouldAttemptEagerTransition: () => false,
|
|
95
|
-
trackSchedulerEvent: () => { },
|
|
96
|
-
resolveEventType: () => null,
|
|
97
|
-
resolveEventTimeStamp: () => Date.now(),
|
|
98
|
-
maySuspendCommit: () => false,
|
|
99
|
-
preloadInstance: () => false,
|
|
100
|
-
startSuspendingCommit: () => { },
|
|
101
|
-
suspendInstance: () => { },
|
|
102
|
-
waitForCommitToBeReady: () => null,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Creates a React context compatible with the reconciler's internal type.
|
|
107
|
-
* Cast is necessary because React.Context and ReactReconciler.ReactContext
|
|
108
|
-
* are structurally identical at runtime but declared as separate types.
|
|
109
|
-
*/
|
|
110
|
-
createReconcilerContext(value) {
|
|
111
|
-
const context = React.createContext(value);
|
|
112
|
-
return context;
|
|
113
|
-
}
|
|
114
|
-
createNodeFromContainer(container) {
|
|
115
|
-
return createNode(container.constructor.name, {}, container);
|
|
17
|
+
injectDevTools() {
|
|
18
|
+
if (process.env.NODE_ENV === "production")
|
|
19
|
+
return;
|
|
20
|
+
this.instance.injectIntoDevTools({
|
|
21
|
+
bundleType: 1,
|
|
22
|
+
version: packageJson.version,
|
|
23
|
+
rendererPackageName: "@gtkx/react",
|
|
24
|
+
});
|
|
116
25
|
}
|
|
117
26
|
}
|
|
118
27
|
/**
|
package/dist/render.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ApplicationFlags } from "@gtkx/ffi/gio";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
|
|
4
|
-
export declare let container: unknown;
|
|
3
|
+
export declare const getContainer: () => unknown;
|
|
5
4
|
/**
|
|
6
5
|
* Renders a React element tree as a GTK application.
|
|
7
6
|
* This is the main entry point for GTKX applications.
|
package/dist/render.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { start } from "@gtkx/ffi";
|
|
2
|
+
import { formatBoundaryError, formatRenderError } from "./errors.js";
|
|
2
3
|
import { ROOT_NODE_CONTAINER } from "./factory.js";
|
|
3
4
|
import { reconciler } from "./reconciler.js";
|
|
4
|
-
|
|
5
|
-
export
|
|
5
|
+
let container = null;
|
|
6
|
+
export const getContainer = () => container;
|
|
7
|
+
const APP_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/;
|
|
8
|
+
function validateAppId(appId) {
|
|
9
|
+
if (!appId || typeof appId !== "string") {
|
|
10
|
+
throw new Error("appId must be a non-empty string");
|
|
11
|
+
}
|
|
12
|
+
if (!APP_ID_PATTERN.test(appId)) {
|
|
13
|
+
throw new Error(`Invalid appId "${appId}". App ID must be in reverse-DNS format (e.g., "com.example.myapp")`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Renders a React element tree as a GTK application.
|
|
8
18
|
* This is the main entry point for GTKX applications.
|
|
@@ -22,12 +32,14 @@ export let container = null;
|
|
|
22
32
|
* @param flags - Optional GIO application flags
|
|
23
33
|
*/
|
|
24
34
|
export const render = (element, appId, flags) => {
|
|
35
|
+
validateAppId(appId);
|
|
25
36
|
start(appId, flags);
|
|
26
37
|
const instance = reconciler.getInstance();
|
|
27
38
|
container = instance.createContainer(ROOT_NODE_CONTAINER, 0, null, false, null, "", (error) => {
|
|
28
|
-
throw error;
|
|
39
|
+
throw formatRenderError(error);
|
|
29
40
|
}, (error) => {
|
|
30
|
-
|
|
41
|
+
const formattedError = formatBoundaryError(error);
|
|
42
|
+
console.error(formattedError.toString());
|
|
31
43
|
}, () => { }, () => { }, null);
|
|
32
44
|
instance.updateContainer(element, container, null, () => { });
|
|
33
45
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -8,13 +8,25 @@ export type SlotProps = {
|
|
|
8
8
|
children?: ReactNode;
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
-
* Props passed to list item components.
|
|
11
|
+
* Props passed to list item components (ListView, GridView, ColumnView).
|
|
12
12
|
* @typeParam I - The type of the data item
|
|
13
13
|
*/
|
|
14
14
|
export type ListItemProps<I = unknown> = {
|
|
15
|
+
/** Unique identifier for this item. Used for selection. */
|
|
16
|
+
id: string;
|
|
15
17
|
/** The data item to render. */
|
|
16
18
|
item: I;
|
|
17
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Props for string list items (DropDown, ComboRow).
|
|
22
|
+
* Similar to HTML select option elements.
|
|
23
|
+
*/
|
|
24
|
+
export type StringListItemProps = {
|
|
25
|
+
/** Unique identifier for this item. Used for selection. */
|
|
26
|
+
id: string;
|
|
27
|
+
/** Display text shown in the dropdown. */
|
|
28
|
+
label: string;
|
|
29
|
+
};
|
|
18
30
|
export type GridChildProps = SlotProps & {
|
|
19
31
|
column?: number;
|
|
20
32
|
row?: number;
|
|
@@ -34,14 +46,6 @@ export type ListViewRenderProps<T = unknown> = {
|
|
|
34
46
|
/** Render function called for each item in the list. */
|
|
35
47
|
renderItem: RenderItemFn<T>;
|
|
36
48
|
};
|
|
37
|
-
/**
|
|
38
|
-
* Comparison function for sorting items by column.
|
|
39
|
-
* Returns negative if a < b, 0 if a === b, positive if a > b.
|
|
40
|
-
* @param a - First item to compare
|
|
41
|
-
* @param b - Second item to compare
|
|
42
|
-
* @param columnId - The ID of the column being sorted
|
|
43
|
-
*/
|
|
44
|
-
export type ColumnSortFn<T, C extends string = string> = (a: T, b: T, columnId: C) => number;
|
|
45
49
|
/**
|
|
46
50
|
* Props for individual columns in a ColumnView.
|
|
47
51
|
* @typeParam T - The type of the data items displayed in the column
|
|
@@ -66,18 +70,17 @@ export type ColumnViewColumnProps<T = unknown> = {
|
|
|
66
70
|
};
|
|
67
71
|
/**
|
|
68
72
|
* Props for the ColumnView root component.
|
|
69
|
-
*
|
|
73
|
+
* Sorting is handled by the parent component - sort your items before rendering
|
|
74
|
+
* and pass them as ColumnView.Item children in the desired order.
|
|
70
75
|
* @typeParam C - The union type of column IDs
|
|
71
76
|
*/
|
|
72
|
-
export type ColumnViewRootProps<
|
|
73
|
-
/** The ID of the currently sorted column, or null if unsorted. */
|
|
77
|
+
export type ColumnViewRootProps<C extends string = string> = {
|
|
78
|
+
/** The ID of the currently sorted column, or null if unsorted. Controls the sort indicator UI. */
|
|
74
79
|
sortColumn?: C | null;
|
|
75
|
-
/** The current sort direction. */
|
|
80
|
+
/** The current sort direction. Controls the sort indicator UI. */
|
|
76
81
|
sortOrder?: SortType;
|
|
77
|
-
/** Callback fired when the user
|
|
82
|
+
/** Callback fired when the user clicks a column header to change sort. */
|
|
78
83
|
onSortChange?: (column: C | null, order: SortType) => void;
|
|
79
|
-
/** Custom comparison function for sorting items. */
|
|
80
|
-
sortFn?: ColumnSortFn<T, C>;
|
|
81
84
|
};
|
|
82
85
|
export type NotebookPageProps = SlotProps & {
|
|
83
86
|
label: string;
|