@gtkx/react 0.9.4 → 0.10.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.
Files changed (182) hide show
  1. package/README.md +55 -67
  2. package/dist/errors.d.ts +3 -3
  3. package/dist/errors.js +8 -8
  4. package/dist/factory.d.ts +3 -5
  5. package/dist/factory.js +18 -71
  6. package/dist/fiber-root.d.ts +1 -1
  7. package/dist/fiber-root.js +1 -2
  8. package/dist/generated/internal.d.ts +3 -6
  9. package/dist/generated/internal.js +10386 -13577
  10. package/dist/generated/jsx.d.ts +914 -808
  11. package/dist/generated/jsx.js +123 -358
  12. package/dist/generated/registry.d.ts +4 -0
  13. package/dist/generated/registry.js +13 -0
  14. package/dist/host-config.d.ts +7 -4
  15. package/dist/host-config.js +53 -18
  16. package/dist/index.d.ts +2 -22
  17. package/dist/index.js +2 -40
  18. package/dist/jsx.d.ts +719 -0
  19. package/dist/jsx.js +392 -0
  20. package/dist/node.d.ts +15 -32
  21. package/dist/node.js +20 -240
  22. package/dist/nodes/action-row-child.d.ts +21 -0
  23. package/dist/nodes/action-row-child.js +69 -0
  24. package/dist/nodes/action-row.js +33 -0
  25. package/dist/nodes/application.d.ts +1 -0
  26. package/dist/nodes/application.js +38 -0
  27. package/dist/nodes/autowrapped.d.ts +1 -0
  28. package/dist/nodes/autowrapped.js +109 -0
  29. package/dist/nodes/column-view-column.d.ts +16 -0
  30. package/dist/nodes/column-view-column.js +54 -0
  31. package/dist/nodes/column-view.d.ts +0 -59
  32. package/dist/nodes/column-view.js +107 -226
  33. package/dist/nodes/fixed-child.d.ts +1 -0
  34. package/dist/nodes/fixed-child.js +45 -0
  35. package/dist/nodes/grid-child.d.ts +1 -0
  36. package/dist/nodes/grid-child.js +54 -0
  37. package/dist/nodes/index.d.ts +34 -0
  38. package/dist/nodes/index.js +34 -0
  39. package/dist/nodes/internal/list-item-renderer.d.ts +18 -0
  40. package/dist/nodes/internal/list-item-renderer.js +67 -0
  41. package/dist/nodes/internal/list-store.d.ts +16 -0
  42. package/dist/nodes/internal/list-store.js +69 -0
  43. package/dist/nodes/internal/predicates.d.ts +26 -0
  44. package/dist/nodes/internal/predicates.js +36 -0
  45. package/dist/nodes/internal/signal-store.d.ts +9 -0
  46. package/dist/nodes/internal/signal-store.js +54 -0
  47. package/dist/nodes/internal/simple-list-store.d.ts +14 -0
  48. package/dist/nodes/internal/simple-list-store.js +60 -0
  49. package/dist/nodes/internal/tree-list-item-renderer.d.ts +18 -0
  50. package/dist/nodes/internal/tree-list-item-renderer.js +90 -0
  51. package/dist/nodes/internal/tree-store.d.ts +28 -0
  52. package/dist/nodes/internal/tree-store.js +153 -0
  53. package/dist/nodes/internal/utils.d.ts +3 -0
  54. package/dist/nodes/internal/utils.js +20 -0
  55. package/dist/nodes/list-item.d.ts +12 -0
  56. package/dist/nodes/list-item.js +24 -0
  57. package/dist/nodes/list-view.d.ts +0 -22
  58. package/dist/nodes/list-view.js +45 -38
  59. package/dist/nodes/menu.d.ts +6 -106
  60. package/dist/nodes/menu.js +16 -268
  61. package/dist/nodes/models/list.d.ts +24 -0
  62. package/dist/nodes/models/list.js +102 -0
  63. package/dist/nodes/models/menu.d.ts +45 -0
  64. package/dist/nodes/models/menu.js +265 -0
  65. package/dist/nodes/models/tree-list.d.ts +28 -0
  66. package/dist/nodes/models/tree-list.js +141 -0
  67. package/dist/nodes/navigation-page.d.ts +21 -0
  68. package/dist/nodes/navigation-page.js +95 -0
  69. package/dist/nodes/navigation-view.d.ts +1 -0
  70. package/dist/nodes/navigation-view.js +29 -0
  71. package/dist/nodes/notebook-page-tab.d.ts +15 -0
  72. package/dist/nodes/notebook-page-tab.js +42 -0
  73. package/dist/nodes/notebook-page.d.ts +23 -0
  74. package/dist/nodes/notebook-page.js +106 -0
  75. package/dist/nodes/notebook.d.ts +0 -32
  76. package/dist/nodes/notebook.js +20 -113
  77. package/dist/nodes/overlay-child.d.ts +1 -0
  78. package/dist/nodes/overlay-child.js +30 -0
  79. package/dist/nodes/pack-child.d.ts +21 -0
  80. package/dist/nodes/pack-child.js +68 -0
  81. package/dist/nodes/pack.d.ts +1 -0
  82. package/dist/nodes/pack.js +33 -0
  83. package/dist/nodes/popover-menu.d.ts +1 -0
  84. package/dist/nodes/popover-menu.js +58 -0
  85. package/dist/nodes/simple-list-item.d.ts +9 -0
  86. package/dist/nodes/simple-list-item.js +9 -0
  87. package/dist/nodes/simple-list-view.d.ts +1 -0
  88. package/dist/nodes/simple-list-view.js +75 -0
  89. package/dist/nodes/slot.d.ts +18 -10
  90. package/dist/nodes/slot.js +83 -51
  91. package/dist/nodes/stack-page.d.ts +1 -0
  92. package/dist/nodes/stack-page.js +80 -0
  93. package/dist/nodes/stack.d.ts +1 -22
  94. package/dist/nodes/stack.js +21 -60
  95. package/dist/nodes/toast-overlay.d.ts +1 -0
  96. package/dist/nodes/toast-overlay.js +35 -0
  97. package/dist/nodes/toast.d.ts +17 -0
  98. package/dist/nodes/toast.js +77 -0
  99. package/dist/nodes/toolbar-child.d.ts +9 -0
  100. package/dist/nodes/toolbar-child.js +33 -0
  101. package/dist/nodes/toolbar.d.ts +1 -0
  102. package/dist/nodes/toolbar.js +42 -0
  103. package/dist/nodes/tree-list-item.d.ts +20 -0
  104. package/dist/nodes/tree-list-item.js +102 -0
  105. package/dist/nodes/tree-list-view.d.ts +1 -0
  106. package/dist/nodes/tree-list-view.js +57 -0
  107. package/dist/nodes/virtual.d.ts +13 -0
  108. package/dist/nodes/virtual.js +21 -0
  109. package/dist/nodes/widget.d.ts +17 -3
  110. package/dist/nodes/widget.js +258 -2
  111. package/dist/nodes/window.d.ts +1 -12
  112. package/dist/nodes/window.js +66 -27
  113. package/dist/portal.d.ts +18 -13
  114. package/dist/portal.js +17 -14
  115. package/dist/reconciler.d.ts +0 -4
  116. package/dist/reconciler.js +1 -9
  117. package/dist/registry.d.ts +8 -0
  118. package/dist/registry.js +5 -0
  119. package/dist/render.d.ts +108 -12
  120. package/dist/render.js +140 -16
  121. package/dist/scheduler.d.ts +4 -0
  122. package/dist/scheduler.js +10 -0
  123. package/dist/types.d.ts +3 -136
  124. package/package.json +6 -6
  125. package/dist/batch.d.ts +0 -5
  126. package/dist/batch.js +0 -31
  127. package/dist/codegen/jsx-generator.d.ts +0 -56
  128. package/dist/codegen/jsx-generator.js +0 -959
  129. package/dist/containers.d.ts +0 -58
  130. package/dist/nodes/about-dialog.d.ts +0 -8
  131. package/dist/nodes/about-dialog.js +0 -16
  132. package/dist/nodes/action-bar.d.ts +0 -5
  133. package/dist/nodes/action-bar.js +0 -6
  134. package/dist/nodes/combo-row.d.ts +0 -5
  135. package/dist/nodes/combo-row.js +0 -6
  136. package/dist/nodes/drop-down.d.ts +0 -9
  137. package/dist/nodes/drop-down.js +0 -12
  138. package/dist/nodes/flow-box.d.ts +0 -10
  139. package/dist/nodes/flow-box.js +0 -41
  140. package/dist/nodes/grid.d.ts +0 -30
  141. package/dist/nodes/grid.js +0 -84
  142. package/dist/nodes/header-bar.d.ts +0 -43
  143. package/dist/nodes/header-bar.js +0 -116
  144. package/dist/nodes/indexed-child-container.d.ts +0 -16
  145. package/dist/nodes/indexed-child-container.js +0 -22
  146. package/dist/nodes/list-box.d.ts +0 -10
  147. package/dist/nodes/list-box.js +0 -48
  148. package/dist/nodes/list-item-factory.d.ts +0 -19
  149. package/dist/nodes/list-item-factory.js +0 -58
  150. package/dist/nodes/overlay.d.ts +0 -11
  151. package/dist/nodes/overlay.js +0 -50
  152. package/dist/nodes/paged-stack.d.ts +0 -31
  153. package/dist/nodes/paged-stack.js +0 -95
  154. package/dist/nodes/root.d.ts +0 -8
  155. package/dist/nodes/root.js +0 -13
  156. package/dist/nodes/selectable-list.d.ts +0 -45
  157. package/dist/nodes/selectable-list.js +0 -260
  158. package/dist/nodes/stack-page-props.d.ts +0 -11
  159. package/dist/nodes/stack-page-props.js +0 -23
  160. package/dist/nodes/string-list-container.d.ts +0 -34
  161. package/dist/nodes/string-list-container.js +0 -118
  162. package/dist/nodes/string-list-item.d.ts +0 -19
  163. package/dist/nodes/string-list-item.js +0 -50
  164. package/dist/nodes/string-list-store.d.ts +0 -13
  165. package/dist/nodes/string-list-store.js +0 -44
  166. package/dist/nodes/text-view.d.ts +0 -8
  167. package/dist/nodes/text-view.js +0 -16
  168. package/dist/nodes/toggle-button.d.ts +0 -14
  169. package/dist/nodes/toggle-button.js +0 -39
  170. package/dist/nodes/toolbar-view.d.ts +0 -14
  171. package/dist/nodes/toolbar-view.js +0 -78
  172. package/dist/nodes/view-stack.d.ts +0 -9
  173. package/dist/nodes/view-stack.js +0 -28
  174. package/dist/nodes/virtual-item.d.ts +0 -19
  175. package/dist/nodes/virtual-item.js +0 -48
  176. package/dist/nodes/virtual-slot.d.ts +0 -25
  177. package/dist/nodes/virtual-slot.js +0 -57
  178. package/dist/predicates.d.ts +0 -29
  179. package/dist/predicates.js +0 -37
  180. package/dist/props.d.ts +0 -7
  181. package/dist/props.js +0 -12
  182. /package/dist/{containers.js → nodes/action-row.d.ts} +0 -0
@@ -0,0 +1,69 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { scheduleAfterCommit } from "../../scheduler.js";
3
+ export class ListStore {
4
+ items = new Map();
5
+ model = new Gtk.StringList();
6
+ newSortedIds = [];
7
+ sortedIds = [];
8
+ shouldSync = false;
9
+ addItem(id, item) {
10
+ this.items.set(id, item);
11
+ const existingIndex = this.newSortedIds.indexOf(id);
12
+ if (existingIndex !== -1) {
13
+ this.newSortedIds.splice(existingIndex, 1);
14
+ }
15
+ this.newSortedIds.push(id);
16
+ this.scheduleSync();
17
+ }
18
+ removeItem(id) {
19
+ const index = this.newSortedIds.indexOf(id);
20
+ if (index !== -1) {
21
+ this.newSortedIds.splice(index, 1);
22
+ this.items.delete(id);
23
+ this.scheduleSync();
24
+ }
25
+ }
26
+ insertItemBefore(id, beforeId, item) {
27
+ this.items.set(id, item);
28
+ const existingIndex = this.newSortedIds.indexOf(id);
29
+ if (existingIndex !== -1) {
30
+ this.newSortedIds.splice(existingIndex, 1);
31
+ }
32
+ const beforeIndex = this.newSortedIds.indexOf(beforeId);
33
+ if (beforeIndex === -1) {
34
+ this.newSortedIds.push(id);
35
+ }
36
+ else {
37
+ this.newSortedIds.splice(beforeIndex, 0, id);
38
+ }
39
+ this.scheduleSync();
40
+ }
41
+ updateItem(id, item) {
42
+ if (this.items.has(id)) {
43
+ this.items.set(id, item);
44
+ }
45
+ else {
46
+ this.addItem(id, item);
47
+ }
48
+ }
49
+ getItem(id) {
50
+ return this.items.get(id);
51
+ }
52
+ getModel() {
53
+ return this.model;
54
+ }
55
+ scheduleSync() {
56
+ if (this.shouldSync) {
57
+ return;
58
+ }
59
+ this.shouldSync = true;
60
+ scheduleAfterCommit(() => this.sync());
61
+ }
62
+ sync() {
63
+ this.shouldSync = false;
64
+ const newOrder = this.newSortedIds;
65
+ const oldLength = this.sortedIds.length;
66
+ this.model.splice(0, oldLength, newOrder.length > 0 ? newOrder : undefined);
67
+ this.sortedIds = [...newOrder];
68
+ }
69
+ }
@@ -0,0 +1,26 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ export declare const isAppendable: (obj: unknown) => obj is Gtk.Widget & {
3
+ append: (child: Gtk.Widget) => void;
4
+ };
5
+ export declare const isAddable: (obj: unknown) => obj is Gtk.Widget & {
6
+ add: (child: Gtk.Widget) => void;
7
+ };
8
+ export declare const isSingleChild: (obj: unknown) => obj is Gtk.Widget & {
9
+ setChild: (child: Gtk.Widget | null) => void;
10
+ };
11
+ export declare const isRemovable: (obj: unknown) => obj is Gtk.Widget & {
12
+ remove: (child: Gtk.Widget) => void;
13
+ };
14
+ export declare const isReorderable: (obj: unknown) => obj is Gtk.Widget & {
15
+ reorderChildAfter: (child: Gtk.Widget, sibling?: Gtk.Widget) => void;
16
+ insertChildAfter: (child: Gtk.Widget, sibling?: Gtk.Widget) => void;
17
+ };
18
+ export declare const isInsertable: (obj: unknown) => obj is Gtk.Widget & {
19
+ insert: (child: Gtk.Widget, position: number) => void;
20
+ getFirstChild: () => Gtk.Widget | null;
21
+ };
22
+ export declare const isEditable: (obj: unknown) => obj is Gtk.Widget & {
23
+ getPosition: () => number;
24
+ setPosition: (position: number) => void;
25
+ getText: () => string;
26
+ };
@@ -0,0 +1,36 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ export const isAppendable = (obj) => {
3
+ return obj instanceof Gtk.Widget && "append" in obj && typeof obj.append === "function";
4
+ };
5
+ export const isAddable = (obj) => {
6
+ return obj instanceof Gtk.Widget && "add" in obj && typeof obj.add === "function";
7
+ };
8
+ export const isSingleChild = (obj) => {
9
+ return obj instanceof Gtk.Widget && "setChild" in obj && typeof obj.setChild === "function";
10
+ };
11
+ export const isRemovable = (obj) => {
12
+ return obj instanceof Gtk.Widget && "remove" in obj && typeof obj.remove === "function";
13
+ };
14
+ export const isReorderable = (obj) => {
15
+ return (obj instanceof Gtk.Widget &&
16
+ "reorderChildAfter" in obj &&
17
+ typeof obj.reorderChildAfter === "function" &&
18
+ "insertChildAfter" in obj &&
19
+ typeof obj.insertChildAfter === "function");
20
+ };
21
+ export const isInsertable = (obj) => {
22
+ return (obj instanceof Gtk.Widget &&
23
+ "insert" in obj &&
24
+ typeof obj.insert === "function" &&
25
+ "getFirstChild" in obj &&
26
+ typeof obj.getFirstChild === "function");
27
+ };
28
+ export const isEditable = (obj) => {
29
+ return (obj instanceof Gtk.Widget &&
30
+ "getPosition" in obj &&
31
+ typeof obj.getPosition === "function" &&
32
+ "setPosition" in obj &&
33
+ typeof obj.setPosition === "function" &&
34
+ "getText" in obj &&
35
+ typeof obj.getText === "function");
36
+ };
@@ -0,0 +1,9 @@
1
+ import * as GObject from "@gtkx/ffi/gobject";
2
+ export type SignalHandler = (...args: any[]) => any;
3
+ export declare class SignalStore {
4
+ private signalHandlers;
5
+ private disconnect;
6
+ private connect;
7
+ set(obj: GObject.GObject, signal: string, handler?: SignalHandler): void;
8
+ clear(): void;
9
+ }
@@ -0,0 +1,54 @@
1
+ import { getObjectId } from "@gtkx/ffi";
2
+ import * as GObject from "@gtkx/ffi/gobject";
3
+ import { isCommitting } from "../../host-config.js";
4
+ const LIFECYCLE_SIGNALS = new Set([
5
+ "realize",
6
+ "unrealize",
7
+ "map",
8
+ "unmap",
9
+ "show",
10
+ "hide",
11
+ "destroy",
12
+ "resize",
13
+ "render",
14
+ "setup",
15
+ "bind",
16
+ "unbind",
17
+ "teardown",
18
+ ]);
19
+ export class SignalStore {
20
+ signalHandlers = new Map();
21
+ disconnect(obj, signal) {
22
+ const objectId = getObjectId(obj.id);
23
+ const key = `${objectId}:${signal}`;
24
+ const existing = this.signalHandlers.get(key);
25
+ if (existing) {
26
+ GObject.signalHandlerDisconnect(existing.obj, existing.handlerId);
27
+ this.signalHandlers.delete(key);
28
+ }
29
+ }
30
+ connect(obj, signal, handler) {
31
+ const objectId = getObjectId(obj.id);
32
+ const key = `${objectId}:${signal}`;
33
+ const wrappedHandler = (...args) => {
34
+ if (isCommitting() && !LIFECYCLE_SIGNALS.has(signal)) {
35
+ return;
36
+ }
37
+ return handler(...args);
38
+ };
39
+ const handlerId = obj.connect(signal, wrappedHandler);
40
+ this.signalHandlers.set(key, { obj, handlerId });
41
+ }
42
+ set(obj, signal, handler) {
43
+ this.disconnect(obj, signal);
44
+ if (handler) {
45
+ this.connect(obj, signal, handler);
46
+ }
47
+ }
48
+ clear() {
49
+ for (const [_, { obj, handlerId }] of this.signalHandlers) {
50
+ GObject.signalHandlerDisconnect(obj, handlerId);
51
+ }
52
+ this.signalHandlers.clear();
53
+ }
54
+ }
@@ -0,0 +1,14 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ export declare class SimpleListStore {
3
+ private ids;
4
+ private model;
5
+ addItem(id: string, label: string): void;
6
+ appendItem(id: string, label: string): void;
7
+ removeItem(id: string): void;
8
+ insertItemBefore(id: string, beforeId: string, label: string): void;
9
+ updateItem(id: string, label: string): void;
10
+ getItem(id: string): string | null;
11
+ getIdAtIndex(index: number): string | undefined;
12
+ getIndexById(id: string): number | undefined;
13
+ getModel(): Gtk.StringList;
14
+ }
@@ -0,0 +1,60 @@
1
+ import { batch } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
3
+ export class SimpleListStore {
4
+ ids = [];
5
+ model = new Gtk.StringList();
6
+ addItem(id, label) {
7
+ this.ids.push(id);
8
+ this.model.append(label);
9
+ }
10
+ appendItem(id, label) {
11
+ const existingIndex = this.ids.indexOf(id);
12
+ batch(() => {
13
+ if (existingIndex >= 0) {
14
+ this.model.remove(existingIndex);
15
+ this.ids.splice(existingIndex, 1);
16
+ }
17
+ this.ids.push(id);
18
+ this.model.append(label);
19
+ });
20
+ }
21
+ removeItem(id) {
22
+ const index = this.ids.indexOf(id);
23
+ if (index < 0) {
24
+ return;
25
+ }
26
+ this.model.remove(index);
27
+ this.ids.splice(index, 1);
28
+ }
29
+ insertItemBefore(id, beforeId, label) {
30
+ const beforeIndex = this.ids.indexOf(beforeId);
31
+ if (beforeIndex < 0) {
32
+ this.addItem(id, label);
33
+ }
34
+ else {
35
+ this.ids.splice(beforeIndex, 0, id);
36
+ this.model.splice(beforeIndex, 0, [label]);
37
+ }
38
+ }
39
+ updateItem(id, label) {
40
+ const index = this.ids.indexOf(id);
41
+ if (index < 0) {
42
+ this.addItem(id, label);
43
+ return;
44
+ }
45
+ this.model.splice(index, 1, [label]);
46
+ }
47
+ getItem(id) {
48
+ return this.model.getString(this.ids.indexOf(id));
49
+ }
50
+ getIdAtIndex(index) {
51
+ return this.ids[index];
52
+ }
53
+ getIndexById(id) {
54
+ const index = this.ids.indexOf(id);
55
+ return index >= 0 ? index : undefined;
56
+ }
57
+ getModel() {
58
+ return this.model;
59
+ }
60
+ }
@@ -0,0 +1,18 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { ReactNode } from "react";
3
+ import type { SignalStore } from "./signal-store.js";
4
+ import type { TreeStore } from "./tree-store.js";
5
+ export type TreeRenderItemFn<T> = (item: T | null, row: Gtk.TreeListRow | null) => ReactNode;
6
+ export declare class TreeListItemRenderer {
7
+ private factory;
8
+ private store?;
9
+ private fiberRoots;
10
+ private renderFn?;
11
+ private signalStore;
12
+ constructor(signalStore: SignalStore);
13
+ getFactory(): Gtk.SignalListItemFactory;
14
+ setRenderFn(renderFn?: TreeRenderItemFn<unknown>): void;
15
+ setStore(store?: TreeStore): void;
16
+ private getStore;
17
+ private initialize;
18
+ }
@@ -0,0 +1,90 @@
1
+ import { getObjectId } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
3
+ import { createFiberRoot } from "../../fiber-root.js";
4
+ import { reconciler } from "../../reconciler.js";
5
+ export class TreeListItemRenderer {
6
+ factory;
7
+ store;
8
+ fiberRoots = new Map();
9
+ renderFn = () => null;
10
+ signalStore;
11
+ constructor(signalStore) {
12
+ this.signalStore = signalStore;
13
+ this.factory = new Gtk.SignalListItemFactory();
14
+ this.initialize();
15
+ }
16
+ getFactory() {
17
+ return this.factory;
18
+ }
19
+ setRenderFn(renderFn) {
20
+ this.renderFn = renderFn;
21
+ }
22
+ setStore(store) {
23
+ this.store = store;
24
+ }
25
+ getStore() {
26
+ if (!this.store) {
27
+ throw new Error("Expected tree store to be set on TreeListItemRenderer");
28
+ }
29
+ return this.store;
30
+ }
31
+ initialize() {
32
+ this.signalStore.set(this.factory, "setup", (_self, listItem) => {
33
+ const ptr = getObjectId(listItem.id);
34
+ const expander = new Gtk.TreeExpander();
35
+ const box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
36
+ expander.setChild(box);
37
+ listItem.setChild(expander);
38
+ const fiberRoot = createFiberRoot(box);
39
+ this.fiberRoots.set(ptr, fiberRoot);
40
+ const element = this.renderFn?.(null, null);
41
+ reconciler.getInstance().updateContainer(element, fiberRoot, null, () => { });
42
+ });
43
+ this.signalStore.set(this.factory, "bind", (_self, listItem) => {
44
+ const ptr = getObjectId(listItem.id);
45
+ const fiberRoot = this.fiberRoots.get(ptr);
46
+ if (!fiberRoot)
47
+ return;
48
+ const treeListRow = listItem.getItem();
49
+ if (!treeListRow)
50
+ return;
51
+ const expander = listItem.getChild();
52
+ expander.setListRow(treeListRow);
53
+ const stringObject = treeListRow.getItem();
54
+ if (!stringObject)
55
+ return;
56
+ const id = stringObject.getString();
57
+ const itemData = this.getStore().getItem(id);
58
+ if (itemData) {
59
+ if (itemData.indentForDepth !== undefined) {
60
+ expander.setIndentForDepth(itemData.indentForDepth);
61
+ }
62
+ if (itemData.indentForIcon !== undefined) {
63
+ expander.setIndentForIcon(itemData.indentForIcon);
64
+ }
65
+ if (itemData.hideExpander !== undefined) {
66
+ expander.setHideExpander(itemData.hideExpander);
67
+ }
68
+ }
69
+ const element = this.renderFn?.(itemData?.value ?? null, treeListRow);
70
+ reconciler.getInstance().updateContainer(element, fiberRoot, null, () => { });
71
+ });
72
+ this.signalStore.set(this.factory, "unbind", (_self, listItem) => {
73
+ const ptr = getObjectId(listItem.id);
74
+ const fiberRoot = this.fiberRoots.get(ptr);
75
+ if (!fiberRoot)
76
+ return;
77
+ const expander = listItem.getChild();
78
+ expander.setListRow(undefined);
79
+ reconciler.getInstance().updateContainer(null, fiberRoot, null, () => { });
80
+ });
81
+ this.signalStore.set(this.factory, "teardown", (_self, listItem) => {
82
+ const ptr = getObjectId(listItem.id);
83
+ const fiberRoot = this.fiberRoots.get(ptr);
84
+ if (fiberRoot) {
85
+ reconciler.getInstance().updateContainer(null, fiberRoot, null, () => { });
86
+ queueMicrotask(() => this.fiberRoots.delete(ptr));
87
+ }
88
+ });
89
+ }
90
+ }
@@ -0,0 +1,28 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ export interface TreeItemData<T = unknown> {
3
+ value: T;
4
+ indentForDepth?: boolean;
5
+ indentForIcon?: boolean;
6
+ hideExpander?: boolean;
7
+ }
8
+ export declare class TreeStore {
9
+ private items;
10
+ private rootIds;
11
+ private newRootIds;
12
+ private children;
13
+ private newChildren;
14
+ private rootModel;
15
+ private childModels;
16
+ private shouldSync;
17
+ constructor();
18
+ addItem(id: string, data: TreeItemData, parentId?: string): void;
19
+ removeItem(id: string, parentId?: string): void;
20
+ insertItemBefore(id: string, beforeId: string, data: TreeItemData, parentId?: string): void;
21
+ updateItem(id: string, data: TreeItemData): void;
22
+ getItem(id: string): TreeItemData | undefined;
23
+ getRootModel(): Gtk.StringList;
24
+ getChildrenModel(parentId: string): Gtk.StringList | null;
25
+ hasChildren(parentId: string): boolean;
26
+ private scheduleSync;
27
+ private sync;
28
+ }
@@ -0,0 +1,153 @@
1
+ import { batch } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
3
+ import { scheduleAfterCommit } from "../../scheduler.js";
4
+ export class TreeStore {
5
+ items = new Map();
6
+ rootIds = [];
7
+ newRootIds = [];
8
+ children = new Map();
9
+ newChildren = new Map();
10
+ rootModel;
11
+ childModels = new Map();
12
+ shouldSync = false;
13
+ constructor() {
14
+ this.rootModel = new Gtk.StringList();
15
+ }
16
+ addItem(id, data, parentId) {
17
+ this.items.set(id, data);
18
+ if (parentId === undefined) {
19
+ const existingIndex = this.newRootIds.indexOf(id);
20
+ if (existingIndex !== -1) {
21
+ this.newRootIds.splice(existingIndex, 1);
22
+ }
23
+ this.newRootIds.push(id);
24
+ }
25
+ else {
26
+ let siblings = this.newChildren.get(parentId);
27
+ if (!siblings) {
28
+ siblings = [];
29
+ this.newChildren.set(parentId, siblings);
30
+ }
31
+ const existingIndex = siblings.indexOf(id);
32
+ if (existingIndex !== -1) {
33
+ siblings.splice(existingIndex, 1);
34
+ }
35
+ siblings.push(id);
36
+ }
37
+ this.scheduleSync();
38
+ }
39
+ removeItem(id, parentId) {
40
+ this.items.delete(id);
41
+ this.newChildren.delete(id);
42
+ if (parentId === undefined) {
43
+ const index = this.newRootIds.indexOf(id);
44
+ if (index !== -1) {
45
+ this.newRootIds.splice(index, 1);
46
+ }
47
+ }
48
+ else {
49
+ const siblings = this.newChildren.get(parentId);
50
+ if (siblings) {
51
+ const index = siblings.indexOf(id);
52
+ if (index !== -1) {
53
+ siblings.splice(index, 1);
54
+ }
55
+ if (siblings.length === 0) {
56
+ this.newChildren.delete(parentId);
57
+ }
58
+ }
59
+ }
60
+ this.scheduleSync();
61
+ }
62
+ insertItemBefore(id, beforeId, data, parentId) {
63
+ this.items.set(id, data);
64
+ if (parentId === undefined) {
65
+ const existingIndex = this.newRootIds.indexOf(id);
66
+ if (existingIndex !== -1) {
67
+ this.newRootIds.splice(existingIndex, 1);
68
+ }
69
+ const beforeIndex = this.newRootIds.indexOf(beforeId);
70
+ if (beforeIndex === -1) {
71
+ this.newRootIds.push(id);
72
+ }
73
+ else {
74
+ this.newRootIds.splice(beforeIndex, 0, id);
75
+ }
76
+ }
77
+ else {
78
+ let siblings = this.newChildren.get(parentId);
79
+ if (!siblings) {
80
+ siblings = [];
81
+ this.newChildren.set(parentId, siblings);
82
+ }
83
+ const existingIndex = siblings.indexOf(id);
84
+ if (existingIndex !== -1) {
85
+ siblings.splice(existingIndex, 1);
86
+ }
87
+ const beforeIndex = siblings.indexOf(beforeId);
88
+ if (beforeIndex === -1) {
89
+ siblings.push(id);
90
+ }
91
+ else {
92
+ siblings.splice(beforeIndex, 0, id);
93
+ }
94
+ }
95
+ this.scheduleSync();
96
+ }
97
+ updateItem(id, data) {
98
+ if (this.items.has(id)) {
99
+ this.items.set(id, data);
100
+ }
101
+ }
102
+ getItem(id) {
103
+ return this.items.get(id);
104
+ }
105
+ getRootModel() {
106
+ return this.rootModel;
107
+ }
108
+ getChildrenModel(parentId) {
109
+ const childIds = this.children.get(parentId) ?? this.newChildren.get(parentId);
110
+ if (!childIds || childIds.length === 0) {
111
+ return null;
112
+ }
113
+ let model = this.childModels.get(parentId);
114
+ if (!model) {
115
+ model = new Gtk.StringList(childIds);
116
+ this.childModels.set(parentId, model);
117
+ }
118
+ return model;
119
+ }
120
+ hasChildren(parentId) {
121
+ const childIds = this.children.get(parentId);
122
+ return childIds !== undefined && childIds.length > 0;
123
+ }
124
+ scheduleSync() {
125
+ if (this.shouldSync) {
126
+ return;
127
+ }
128
+ this.shouldSync = true;
129
+ scheduleAfterCommit(() => this.sync());
130
+ }
131
+ sync() {
132
+ this.shouldSync = false;
133
+ batch(() => {
134
+ const oldRootLength = this.rootIds.length;
135
+ this.rootModel.splice(0, oldRootLength, this.newRootIds.length > 0 ? this.newRootIds : undefined);
136
+ this.rootIds = [...this.newRootIds];
137
+ for (const [parentId, newChildIds] of this.newChildren) {
138
+ const oldChildIds = this.children.get(parentId) ?? [];
139
+ const model = this.childModels.get(parentId);
140
+ if (model) {
141
+ const oldLength = oldChildIds.length;
142
+ model.splice(0, oldLength, newChildIds.length > 0 ? newChildIds : undefined);
143
+ }
144
+ }
145
+ for (const [parentId] of this.children) {
146
+ if (!this.newChildren.has(parentId)) {
147
+ this.childModels.delete(parentId);
148
+ }
149
+ }
150
+ this.children = new Map(this.newChildren);
151
+ });
152
+ }
153
+ }
@@ -0,0 +1,3 @@
1
+ import type { Container, ContainerClass, Props } from "../../types.js";
2
+ export declare const isContainerType: (cls: new (...args: any[]) => Container, containerOrClass?: Container | ContainerClass) => boolean;
3
+ export declare const filterProps: (props: Props, excludeKeys: string[]) => Props;
@@ -0,0 +1,20 @@
1
+ export const isContainerType = (
2
+ // biome-ignore lint/suspicious/noExplicitAny: Required for contravariant behavior
3
+ cls, containerOrClass) => {
4
+ if (!containerOrClass) {
5
+ return false;
6
+ }
7
+ if (containerOrClass instanceof cls) {
8
+ return true;
9
+ }
10
+ return containerOrClass === cls || Object.prototype.isPrototypeOf.call(cls, containerOrClass);
11
+ };
12
+ export const filterProps = (props, excludeKeys) => {
13
+ const result = {};
14
+ for (const key of Object.keys(props)) {
15
+ if (!excludeKeys.includes(key)) {
16
+ result[key] = props[key];
17
+ }
18
+ }
19
+ return result;
20
+ };
@@ -0,0 +1,12 @@
1
+ import type { ListItemProps } from "../jsx.js";
2
+ import type { ListStore } from "./internal/list-store.js";
3
+ import { VirtualNode } from "./virtual.js";
4
+ type Props = Partial<ListItemProps>;
5
+ export declare class ListItemNode<T extends Omit<ListStore, "items" | "model"> = ListStore, P extends Props = Props> extends VirtualNode<P> {
6
+ static priority: number;
7
+ private store?;
8
+ static matches(type: string): boolean;
9
+ setStore(store?: T): void;
10
+ updateProps(oldProps: P | null, newProps: P): void;
11
+ }
12
+ export {};
@@ -0,0 +1,24 @@
1
+ import { registerNodeClass } from "../registry.js";
2
+ import { VirtualNode } from "./virtual.js";
3
+ export class ListItemNode extends VirtualNode {
4
+ static priority = 1;
5
+ store;
6
+ static matches(type) {
7
+ return type === "ListItem";
8
+ }
9
+ setStore(store) {
10
+ this.store = store;
11
+ }
12
+ updateProps(oldProps, newProps) {
13
+ super.updateProps(oldProps, newProps);
14
+ if (!this.store) {
15
+ return;
16
+ }
17
+ if (!oldProps || oldProps.id !== newProps.id || oldProps.value !== newProps.value) {
18
+ if (newProps.id !== undefined) {
19
+ this.store.updateItem(newProps.id, newProps.value);
20
+ }
21
+ }
22
+ }
23
+ }
24
+ registerNodeClass(ListItemNode);
@@ -1,23 +1 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
2
- import type { Props } from "../factory.js";
3
- import type { RenderItemFn } from "../types.js";
4
- import { type ListItemFactoryHandlers, type ListItemInfo } from "./list-item-factory.js";
5
- import { SelectableListNode, type SelectableListState } from "./selectable-list.js";
6
- import { VirtualItemNode } from "./virtual-item.js";
7
- type ListViewState = SelectableListState & {
8
- factory: Gtk.SignalListItemFactory;
9
- factoryHandlers: ListItemFactoryHandlers | null;
10
- renderItem: RenderItemFn<unknown>;
11
- listItemCache: Map<number, ListItemInfo>;
12
- };
13
- export declare class ListViewNode extends SelectableListNode<Gtk.ListView | Gtk.GridView, ListViewState> {
14
- static consumedPropNames: string[];
15
- static matches(type: string): boolean;
16
- initialize(props: Props): void;
17
- unmount(): void;
18
- updateProps(oldProps: Props, newProps: Props): void;
19
- }
20
- export declare class ListItemNode extends VirtualItemNode {
21
- static matches(type: string): boolean;
22
- }
23
1
  export {};