@gtkx/react 0.1.49 → 0.1.51

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 CHANGED
@@ -159,7 +159,9 @@ Query types: `ByRole`, `ByText`, `ByLabelText`, `ByTestId`
159
159
  - `userEvent.activate(element)` - Activate element (e.g., press Enter in input)
160
160
  - `userEvent.type(element, text)` - Type text into input
161
161
  - `userEvent.clear(element)` - Clear input text
162
- - `userEvent.setup()` - Create reusable instance
162
+ - `userEvent.tab(element, options?)` - Simulate Tab navigation
163
+ - `userEvent.selectOptions(element, values)` - Select options in ComboBox/ListBox
164
+ - `userEvent.deselectOptions(element, values)` - Deselect options in ListBox
163
165
 
164
166
  **Low-level Events**:
165
167
  - `fireEvent(element, signalName, ...args)` - Emit any GTK signal with optional arguments
@@ -170,14 +172,6 @@ Query types: `ByRole`, `ByText`, `ByLabelText`, `ByTestId`
170
172
 
171
173
  ## Examples
172
174
 
173
- ### Counter
174
-
175
- A minimal counter app demonstrating state management:
176
-
177
- ```bash
178
- turbo start --filter=counter-example
179
- ```
180
-
181
175
  ### GTK4 Demo
182
176
 
183
177
  A comprehensive showcase of GTK4 widgets and features:
@@ -186,12 +180,13 @@ A comprehensive showcase of GTK4 widgets and features:
186
180
  turbo start --filter=gtk4-demo
187
181
  ```
188
182
 
189
- ### List Example
183
+ ### Todo App
190
184
 
191
- Comprehensive showcase of ListView, GridView, and ColumnView with sorting:
185
+ A todo app demonstrating `@gtkx/testing` with realistic component tests:
192
186
 
193
187
  ```bash
194
- turbo start --filter=list-example
188
+ turbo start --filter=todo
189
+ turbo test --filter=todo
195
190
  ```
196
191
 
197
192
  ## Packages
@@ -6,6 +6,15 @@ interface JsxGeneratorOptions {
6
6
  /** Optional Prettier configuration for formatting output. */
7
7
  prettierConfig?: unknown;
8
8
  }
9
+ /**
10
+ * Result of the JSX generation containing both public and internal files.
11
+ */
12
+ interface JsxGeneratorResult {
13
+ /** Public JSX types and components for user consumption. */
14
+ jsx: string;
15
+ /** Internal metadata for reconciler use (not exported to users). */
16
+ internal: string;
17
+ }
9
18
  /**
10
19
  * Generates JSX type definitions for React components from GTK widget classes.
11
20
  * Creates TypeScript interfaces for props and augments React's JSX namespace.
@@ -29,10 +38,11 @@ export declare class JsxGenerator {
29
38
  * Generates JSX type definitions for all widgets in a namespace.
30
39
  * @param namespace - The parsed GIR namespace
31
40
  * @param classMap - Map of class names to class definitions
32
- * @returns Generated TypeScript code as a string
41
+ * @returns Generated TypeScript code as public jsx.ts and internal.ts files
33
42
  */
34
- generate(namespace: GirNamespace, classMap: Map<string, GirClass>): Promise<string>;
43
+ generate(namespace: GirNamespace, classMap: Map<string, GirClass>): Promise<JsxGeneratorResult>;
35
44
  private generateImports;
45
+ private generateInternalImports;
36
46
  private generateCommonTypes;
37
47
  private generateWidgetPropsContent;
38
48
  private buildContainerMetadata;
@@ -5,6 +5,8 @@ const COLUMN_VIEW_WIDGET = "ColumnView";
5
5
  const DROPDOWN_WIDGETS = new Set(["DropDown"]);
6
6
  const GRID_WIDGETS = new Set(["Grid"]);
7
7
  const NOTEBOOK_WIDGET = "Notebook";
8
+ const STACK_WIDGET = "Stack";
9
+ const POPOVER_MENU_WIDGET = "PopoverMenu";
8
10
  const INTERNALLY_PROVIDED_PARAMS = {
9
11
  ApplicationWindow: new Set(["application"]),
10
12
  };
@@ -33,6 +35,8 @@ const isColumnViewWidget = (widgetName) => widgetName === COLUMN_VIEW_WIDGET;
33
35
  const isDropDownWidget = (widgetName) => DROPDOWN_WIDGETS.has(widgetName);
34
36
  const isGridWidget = (widgetName) => GRID_WIDGETS.has(widgetName);
35
37
  const isNotebookWidget = (widgetName) => widgetName === NOTEBOOK_WIDGET;
38
+ const isStackWidget = (widgetName) => widgetName === STACK_WIDGET;
39
+ const isPopoverMenuWidget = (widgetName) => widgetName === POPOVER_MENU_WIDGET;
36
40
  const sanitizeDoc = (doc) => {
37
41
  let result = doc;
38
42
  result = result.replace(/<picture>[\s\S]*?<\/picture>/gi, "");
@@ -98,7 +102,7 @@ export class JsxGenerator {
98
102
  * Generates JSX type definitions for all widgets in a namespace.
99
103
  * @param namespace - The parsed GIR namespace
100
104
  * @param classMap - Map of class names to class definitions
101
- * @returns Generated TypeScript code as a string
105
+ * @returns Generated TypeScript code as public jsx.ts and internal.ts files
102
106
  */
103
107
  async generate(namespace, classMap) {
104
108
  this.classMap = classMap;
@@ -111,18 +115,24 @@ export class JsxGenerator {
111
115
  this.widgetPropertyNames = new Set(widgetClass?.properties.map((p) => toCamelCase(p.name)) ?? []);
112
116
  this.widgetSignalNames = new Set(widgetClass?.signals.map((s) => toCamelCase(s.name)) ?? []);
113
117
  const widgetPropsInterfaces = this.generateWidgetPropsInterfaces(widgets, containerMetadata);
114
- const sections = [
118
+ const jsxSections = [
115
119
  this.generateImports(),
116
120
  this.generateCommonTypes(widgetClass),
117
121
  widgetPropsInterfaces,
118
- this.generateConstructorArgsMetadata(widgets),
119
- this.generatePropSettersMap(widgets),
120
- this.generateSetterGetterMap(widgets),
121
122
  this.generateExports(widgets, containerMetadata),
122
123
  this.generateJsxNamespace(widgets, containerMetadata),
123
124
  "export {};",
124
125
  ];
125
- return this.formatCode(sections.join("\n"));
126
+ const internalSections = [
127
+ this.generateInternalImports(),
128
+ this.generateConstructorArgsMetadata(widgets),
129
+ this.generatePropSettersMap(widgets),
130
+ this.generateSetterGetterMap(widgets),
131
+ ];
132
+ return {
133
+ jsx: await this.formatCode(jsxSections.join("\n")),
134
+ internal: await this.formatCode(internalSections.join("\n")),
135
+ };
126
136
  }
127
137
  generateImports() {
128
138
  const externalImports = [...this.usedExternalNamespaces]
@@ -134,14 +144,17 @@ export class JsxGenerator {
134
144
  `import type { ReactNode, Ref } from "react";`,
135
145
  ...externalImports,
136
146
  `import type * as Gtk from "@gtkx/ffi/gtk";`,
137
- `import type { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps } from "../types.js";`,
147
+ `import type { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, MenuItemProps, MenuRootProps, MenuSectionProps, MenuSubmenuProps, NotebookPageProps, SlotProps, StackPageProps, StackRootProps } from "../types.js";`,
138
148
  "",
139
149
  ].join("\n");
140
150
  }
151
+ generateInternalImports() {
152
+ return "/** Internal metadata for the reconciler. Not part of the public API. */\n";
153
+ }
141
154
  generateCommonTypes(widgetClass) {
142
155
  const widgetPropsContent = this.generateWidgetPropsContent(widgetClass);
143
156
  return `
144
- export { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, NotebookPageProps, SlotProps };
157
+ export { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, MenuItemProps, MenuRootProps, MenuSectionProps, MenuSubmenuProps, NotebookPageProps, SlotProps, StackPageProps, StackRootProps };
145
158
 
146
159
  ${widgetPropsContent}
147
160
  `;
@@ -572,11 +585,17 @@ ${widgetPropsContent}
572
585
  isColumnViewWidget(widget.name) ||
573
586
  isDropDownWidget(widget.name) ||
574
587
  isGridWidget(widget.name) ||
575
- isNotebookWidget(widget.name);
588
+ isNotebookWidget(widget.name) ||
589
+ isStackWidget(widget.name) ||
590
+ isPopoverMenuWidget(widget.name);
576
591
  const docComment = widget.doc ? formatDoc(widget.doc).trimEnd() : "";
577
592
  if (hasMeaningfulSlots) {
578
593
  // For list widgets, generate wrapper components with proper generics
579
- if (isListWidget(widget.name) || isColumnViewWidget(widget.name) || isDropDownWidget(widget.name)) {
594
+ if (isListWidget(widget.name) ||
595
+ isColumnViewWidget(widget.name) ||
596
+ isDropDownWidget(widget.name) ||
597
+ isStackWidget(widget.name) ||
598
+ isPopoverMenuWidget(widget.name)) {
580
599
  const wrapperComponents = this.generateGenericWrapperComponents(widget.name, metadata);
581
600
  const exportMembers = this.getWrapperExportMembers(widget.name, metadata);
582
601
  if (docComment) {
@@ -610,6 +629,38 @@ ${widgetPropsContent}
610
629
  }
611
630
  }
612
631
  }
632
+ // Add ApplicationMenu and Menu namespace components
633
+ lines.push(`/**
634
+ * Sets the application-wide menu bar.
635
+ * The menu will appear in the window's title bar on supported platforms.
636
+ * Use Menu.Item, Menu.Section, and Menu.Submenu as children.
637
+ */
638
+ export function ApplicationMenu(props: MenuRootProps): import("react").ReactElement {
639
+ \treturn createElement("ApplicationMenu", props);
640
+ }
641
+
642
+ function MenuItem(props: MenuItemProps): import("react").ReactElement {
643
+ \treturn createElement("Menu.Item", props);
644
+ }
645
+
646
+ function MenuSection(props: MenuSectionProps): import("react").ReactElement {
647
+ \treturn createElement("Menu.Section", props);
648
+ }
649
+
650
+ function MenuSubmenu(props: MenuSubmenuProps): import("react").ReactElement {
651
+ \treturn createElement("Menu.Submenu", props);
652
+ }
653
+
654
+ /**
655
+ * Declarative menu builder for use with PopoverMenu and ApplicationMenu.
656
+ * Use Menu.Item for action items, Menu.Section for groups, Menu.Submenu for nested menus.
657
+ */
658
+ export const Menu = {
659
+ \tItem: MenuItem,
660
+ \tSection: MenuSection,
661
+ \tSubmenu: MenuSubmenu,
662
+ };
663
+ `);
613
664
  return `${lines.join("\n")}\n`;
614
665
  }
615
666
  getWrapperExportMembers(widgetName, metadata) {
@@ -625,6 +676,14 @@ ${widgetPropsContent}
625
676
  else if (isDropDownWidget(widgetName)) {
626
677
  members.push(`Item: ${name}Item`);
627
678
  }
679
+ else if (isStackWidget(widgetName)) {
680
+ members.push(`Page: ${name}Page`);
681
+ }
682
+ else if (isPopoverMenuWidget(widgetName)) {
683
+ members.push(`Item: ${name}Item`);
684
+ members.push(`Section: ${name}Section`);
685
+ members.push(`Submenu: ${name}Submenu`);
686
+ }
628
687
  // Add named child slots
629
688
  for (const slot of metadata.namedChildSlots) {
630
689
  members.push(`${slot.slotName}: ${name}${slot.slotName}`);
@@ -635,66 +694,98 @@ ${widgetPropsContent}
635
694
  const name = toPascalCase(widgetName);
636
695
  const lines = [];
637
696
  if (isListWidget(widgetName)) {
638
- // Props type for the generic Root component
697
+ lines.push(`/**`);
698
+ lines.push(` * Props for the ${name}.Root component with type-safe item rendering.`);
699
+ lines.push(` * @typeParam T - The type of items in the list.`);
700
+ lines.push(` */`);
639
701
  lines.push(`interface ${name}RootProps<T> extends Omit<${name}Props, "renderItem"> {`);
640
- lines.push(`\t/** Render function for list items. Called with null during setup. */`);
702
+ lines.push(`\t/** Render function for list items. Called with null during setup (for loading state). */`);
641
703
  lines.push(`\trenderItem: (item: T | null) => import("react").ReactElement;`);
642
704
  lines.push(`}`);
643
705
  lines.push(``);
644
- // Root wrapper component
645
706
  lines.push(`function ${name}Root<T>(props: ${name}RootProps<T>): import("react").ReactElement {`);
646
707
  lines.push(`\treturn createElement("${name}.Root", props);`);
647
708
  lines.push(`}`);
648
709
  lines.push(``);
649
- // Item wrapper component
650
710
  lines.push(`function ${name}Item<T>(props: ListItemProps<T>): import("react").ReactElement {`);
651
711
  lines.push(`\treturn createElement("${name}.Item", props);`);
652
712
  lines.push(`}`);
653
713
  }
654
714
  else if (isColumnViewWidget(widgetName)) {
715
+ lines.push(`/**`);
716
+ lines.push(` * Props for the ${name}.Root component with type-safe item and column rendering.`);
717
+ lines.push(` * @typeParam T - The type of items in the column view.`);
718
+ lines.push(` * @typeParam C - The union type of column IDs for type-safe sorting.`);
719
+ lines.push(` */`);
655
720
  lines.push(`interface ${name}RootPropsExtended<T = unknown, C extends string = string> extends ${name}Props, ColumnViewRootProps<T, C> {}`);
656
721
  lines.push(``);
657
- // Root wrapper (generic)
658
722
  lines.push(`function ${name}Root<T = unknown, C extends string = string>(props: ${name}RootPropsExtended<T, C>): import("react").ReactElement {`);
659
723
  lines.push(`\treturn createElement("${name}.Root", props);`);
660
724
  lines.push(`}`);
661
725
  lines.push(``);
662
- // Column props type - use GenericColumnProps to avoid conflict with imported ColumnViewColumnProps
726
+ lines.push(`/**`);
727
+ lines.push(` * Props for ${name}.Column with type-safe cell rendering.`);
728
+ lines.push(` * @typeParam T - The type of items passed to the renderCell function.`);
729
+ lines.push(` */`);
663
730
  lines.push(`interface ${name}GenericColumnProps<T> extends Omit<ColumnViewColumnProps, "renderCell"> {`);
664
- lines.push(`\t/** Render function for column cells. Called with null during setup. */`);
731
+ lines.push(`\t/** Render function for column cells. Called with null during setup (for loading state). */`);
665
732
  lines.push(`\trenderCell: (item: T | null) => import("react").ReactElement;`);
666
733
  lines.push(`}`);
667
734
  lines.push(``);
668
- // Column wrapper component
669
735
  lines.push(`function ${name}Column<T>(props: ${name}GenericColumnProps<T>): import("react").ReactElement {`);
670
736
  lines.push(`\treturn createElement("${name}.Column", props);`);
671
737
  lines.push(`}`);
672
738
  lines.push(``);
673
- // Item wrapper component
674
739
  lines.push(`function ${name}Item<T>(props: ListItemProps<T>): import("react").ReactElement {`);
675
740
  lines.push(`\treturn createElement("${name}.Item", props);`);
676
741
  lines.push(`}`);
677
742
  }
678
743
  else if (isDropDownWidget(widgetName)) {
679
- // Props type for the generic Root component
744
+ lines.push(`/**`);
745
+ lines.push(` * Props for the ${name}.Root component with type-safe item handling.`);
746
+ lines.push(` * @typeParam T - The type of items in the dropdown.`);
747
+ lines.push(` */`);
680
748
  lines.push(`interface ${name}RootProps<T> extends Omit<${name}Props, "itemLabel" | "onSelectionChanged"> {`);
681
- lines.push(`\t/** Function to convert item to display label */`);
749
+ lines.push(`\t/** Function to convert an item to its display label. */`);
682
750
  lines.push(`\titemLabel?: (item: T) => string;`);
683
- lines.push(`\t/** Called when selection changes */`);
751
+ lines.push(`\t/** Called when the selected item changes. */`);
684
752
  lines.push(`\tonSelectionChanged?: (item: T, index: number) => void;`);
685
753
  lines.push(`}`);
686
754
  lines.push(``);
687
- // Root wrapper component
688
755
  lines.push(`function ${name}Root<T>(props: ${name}RootProps<T>): import("react").ReactElement {`);
689
756
  lines.push(`\treturn createElement("${name}.Root", props);`);
690
757
  lines.push(`}`);
691
758
  lines.push(``);
692
- // Item wrapper component
693
759
  lines.push(`function ${name}Item<T>(props: ListItemProps<T>): import("react").ReactElement {`);
694
760
  lines.push(`\treturn createElement("${name}.Item", props);`);
695
761
  lines.push(`}`);
696
762
  }
697
- // Generate slot wrapper components
763
+ else if (isStackWidget(widgetName)) {
764
+ lines.push(`function ${name}Root(props: StackRootProps & ${name}Props): import("react").ReactElement {`);
765
+ lines.push(`\treturn createElement("${name}.Root", props);`);
766
+ lines.push(`}`);
767
+ lines.push(``);
768
+ lines.push(`function ${name}Page(props: StackPageProps): import("react").ReactElement {`);
769
+ lines.push(`\treturn createElement("${name}.Page", props);`);
770
+ lines.push(`}`);
771
+ }
772
+ else if (isPopoverMenuWidget(widgetName)) {
773
+ lines.push(`function ${name}Root(props: MenuRootProps & ${name}Props): import("react").ReactElement {`);
774
+ lines.push(`\treturn createElement("${name}.Root", props);`);
775
+ lines.push(`}`);
776
+ lines.push(``);
777
+ lines.push(`function ${name}Item(props: MenuItemProps): import("react").ReactElement {`);
778
+ lines.push(`\treturn createElement("Menu.Item", props);`);
779
+ lines.push(`}`);
780
+ lines.push(``);
781
+ lines.push(`function ${name}Section(props: MenuSectionProps): import("react").ReactElement {`);
782
+ lines.push(`\treturn createElement("Menu.Section", props);`);
783
+ lines.push(`}`);
784
+ lines.push(``);
785
+ lines.push(`function ${name}Submenu(props: MenuSubmenuProps): import("react").ReactElement {`);
786
+ lines.push(`\treturn createElement("Menu.Submenu", props);`);
787
+ lines.push(`}`);
788
+ }
698
789
  for (const slot of metadata.namedChildSlots) {
699
790
  lines.push(``);
700
791
  lines.push(`function ${name}${slot.slotName}(props: SlotProps): import("react").ReactElement {`);
@@ -719,7 +810,9 @@ ${widgetPropsContent}
719
810
  isColumnViewWidget(widget.name) ||
720
811
  isDropDownWidget(widget.name) ||
721
812
  isGridWidget(widget.name) ||
722
- isNotebookWidget(widget.name);
813
+ isNotebookWidget(widget.name) ||
814
+ isStackWidget(widget.name) ||
815
+ isPopoverMenuWidget(widget.name);
723
816
  if (hasMeaningfulSlots) {
724
817
  elements.push(`"${widgetName}.Root": ${propsName};`);
725
818
  }
@@ -745,7 +838,16 @@ ${widgetPropsContent}
745
838
  if (isNotebookWidget(widget.name)) {
746
839
  elements.push(`"${widgetName}.Page": NotebookPageProps;`);
747
840
  }
841
+ if (isStackWidget(widget.name)) {
842
+ elements.push(`"${widgetName}.Page": StackPageProps;`);
843
+ }
748
844
  }
845
+ // Add shared Menu elements (used by PopoverMenu, PopoverMenuBar, and ApplicationMenu)
846
+ elements.push(`"Menu.Item": MenuItemProps;`);
847
+ elements.push(`"Menu.Section": MenuSectionProps;`);
848
+ elements.push(`"Menu.Submenu": MenuSubmenuProps;`);
849
+ // Add ApplicationMenu element
850
+ elements.push(`ApplicationMenu: MenuRootProps;`);
749
851
  return `
750
852
  declare global {
751
853
  \tnamespace React {
@@ -18,6 +18,26 @@ export interface PageContainer {
18
18
  removePage(child: Gtk.Widget): void;
19
19
  updatePageLabel(child: Gtk.Widget, label: string): void;
20
20
  }
21
+ /**
22
+ * Props for Stack pages.
23
+ */
24
+ export interface StackPageProps {
25
+ name?: string;
26
+ title?: string;
27
+ iconName?: string;
28
+ needsAttention?: boolean;
29
+ visible?: boolean;
30
+ useUnderline?: boolean;
31
+ }
32
+ /**
33
+ * Interface for Stack containers.
34
+ */
35
+ export interface StackPageContainer {
36
+ addStackPage(child: Gtk.Widget, props: StackPageProps): void;
37
+ insertStackPageBefore(child: Gtk.Widget, props: StackPageProps, beforeChild: Gtk.Widget): void;
38
+ removeStackPage(child: Gtk.Widget): void;
39
+ updateStackPageProps(child: Gtk.Widget, props: StackPageProps): void;
40
+ }
21
41
  /**
22
42
  * Interface for grid-based containers.
23
43
  */
@@ -46,6 +66,7 @@ export interface ColumnContainer {
46
66
  }
47
67
  export declare const isChildContainer: (node: Node) => node is Node & ChildContainer;
48
68
  export declare const isPageContainer: (node: Node) => node is Node & PageContainer;
69
+ export declare const isStackPageContainer: (node: Node) => node is Node & StackPageContainer;
49
70
  export declare const isGridContainer: (node: Node) => node is Node & GridContainer;
50
71
  export declare const isItemContainer: <T>(node: Node) => node is Node & ItemContainer<T>;
51
72
  export declare const isColumnContainer: (node: Node) => node is Node & ColumnContainer;
@@ -1,5 +1,6 @@
1
1
  export const isChildContainer = (node) => "attachChild" in node && "detachChild" in node && "insertChildBefore" in node;
2
2
  export const isPageContainer = (node) => "addPage" in node && "removePage" in node && "insertPageBefore" in node && "updatePageLabel" in node;
3
+ export const isStackPageContainer = (node) => "addStackPage" in node && "removeStackPage" in node && "updateStackPageProps" in node;
3
4
  export const isGridContainer = (node) => "attachToGrid" in node && "removeFromGrid" in node;
4
5
  export const isItemContainer = (node) => "addItem" in node && "insertItemBefore" in node && "removeItem" in node;
5
6
  export const isColumnContainer = (node) => "addColumn" in node && "removeColumn" in node && "getSortFn" in node;
package/dist/factory.js CHANGED
@@ -6,10 +6,12 @@ import { FlowBoxNode } from "./nodes/flow-box.js";
6
6
  import { GridChildNode, GridNode } from "./nodes/grid.js";
7
7
  import { ListItemNode, ListViewNode } from "./nodes/list.js";
8
8
  import { ListBoxNode } from "./nodes/list-box.js";
9
+ import { ApplicationMenuNode, MenuItemNode, MenuSectionNode, MenuSubmenuNode, PopoverMenuBarNode, PopoverMenuRootNode, } from "./nodes/menu.js";
9
10
  import { NotebookNode, NotebookPageNode } from "./nodes/notebook.js";
10
11
  import { OverlayNode } from "./nodes/overlay.js";
11
12
  import { RootNode } from "./nodes/root.js";
12
13
  import { SlotNode } from "./nodes/slot.js";
14
+ import { StackNode, StackPageNode } from "./nodes/stack.js";
13
15
  import { TextViewNode } from "./nodes/text-view.js";
14
16
  import { WidgetNode } from "./nodes/widget.js";
15
17
  import { WindowNode } from "./nodes/window.js";
@@ -23,11 +25,18 @@ const NODE_CLASSES = [
23
25
  DropDownItemNode,
24
26
  GridChildNode,
25
27
  NotebookPageNode,
28
+ StackPageNode,
29
+ MenuItemNode,
30
+ MenuSectionNode,
31
+ MenuSubmenuNode,
26
32
  SlotNode,
27
33
  // Specialized widget nodes
28
34
  WindowNode,
29
35
  AboutDialogNode,
30
36
  TextViewNode,
37
+ ApplicationMenuNode,
38
+ PopoverMenuRootNode,
39
+ PopoverMenuBarNode,
31
40
  // Container nodes
32
41
  ActionBarNode,
33
42
  FlowBoxNode,
@@ -38,13 +47,16 @@ const NODE_CLASSES = [
38
47
  ColumnViewNode,
39
48
  ListViewNode,
40
49
  NotebookNode,
50
+ StackNode,
41
51
  // Catch-all (must be last)
42
52
  WidgetNode,
43
53
  ];
44
54
  export const createNode = (type, props, existingWidget) => {
45
55
  for (const NodeClass of NODE_CLASSES) {
46
56
  if (NodeClass.matches(type, existingWidget)) {
47
- return new NodeClass(type, props, existingWidget);
57
+ const node = new NodeClass(type, existingWidget);
58
+ node.initialize(props);
59
+ return node;
48
60
  }
49
61
  }
50
62
  throw new Error(`No matching node class for type: ${type}`);
@@ -0,0 +1,7 @@
1
+ /** Internal metadata for the reconciler. Not part of the public API. */
2
+ export declare const CONSTRUCTOR_PARAMS: Record<string, {
3
+ name: string;
4
+ hasDefault: boolean;
5
+ }[]>;
6
+ export declare const PROP_SETTERS: Record<string, Record<string, string>>;
7
+ export declare const SETTER_GETTERS: Record<string, Record<string, string>>;