@gtkx/react 0.1.49 → 0.1.50
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 +7 -12
- package/dist/codegen/jsx-generator.d.ts +12 -2
- package/dist/codegen/jsx-generator.js +36 -21
- package/dist/generated/internal.d.ts +7 -0
- package/dist/generated/internal.js +7818 -0
- package/dist/generated/jsx.d.ts +26 -11
- package/dist/generated/jsx.js +0 -7817
- package/dist/node.js +1 -1
- package/dist/reconciler.d.ts +4 -1
- package/dist/reconciler.js +4 -1
- package/package.json +3 -3
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.
|
|
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
|
-
###
|
|
183
|
+
### Todo App
|
|
190
184
|
|
|
191
|
-
|
|
185
|
+
A todo app demonstrating `@gtkx/testing` with realistic component tests:
|
|
192
186
|
|
|
193
187
|
```bash
|
|
194
|
-
turbo start --filter=
|
|
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
|
|
41
|
+
* @returns Generated TypeScript code as public jsx.ts and internal.ts files
|
|
33
42
|
*/
|
|
34
|
-
generate(namespace: GirNamespace, classMap: Map<string, GirClass>): Promise<
|
|
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;
|
|
@@ -98,7 +98,7 @@ export class JsxGenerator {
|
|
|
98
98
|
* Generates JSX type definitions for all widgets in a namespace.
|
|
99
99
|
* @param namespace - The parsed GIR namespace
|
|
100
100
|
* @param classMap - Map of class names to class definitions
|
|
101
|
-
* @returns Generated TypeScript code as
|
|
101
|
+
* @returns Generated TypeScript code as public jsx.ts and internal.ts files
|
|
102
102
|
*/
|
|
103
103
|
async generate(namespace, classMap) {
|
|
104
104
|
this.classMap = classMap;
|
|
@@ -111,18 +111,24 @@ export class JsxGenerator {
|
|
|
111
111
|
this.widgetPropertyNames = new Set(widgetClass?.properties.map((p) => toCamelCase(p.name)) ?? []);
|
|
112
112
|
this.widgetSignalNames = new Set(widgetClass?.signals.map((s) => toCamelCase(s.name)) ?? []);
|
|
113
113
|
const widgetPropsInterfaces = this.generateWidgetPropsInterfaces(widgets, containerMetadata);
|
|
114
|
-
const
|
|
114
|
+
const jsxSections = [
|
|
115
115
|
this.generateImports(),
|
|
116
116
|
this.generateCommonTypes(widgetClass),
|
|
117
117
|
widgetPropsInterfaces,
|
|
118
|
-
this.generateConstructorArgsMetadata(widgets),
|
|
119
|
-
this.generatePropSettersMap(widgets),
|
|
120
|
-
this.generateSetterGetterMap(widgets),
|
|
121
118
|
this.generateExports(widgets, containerMetadata),
|
|
122
119
|
this.generateJsxNamespace(widgets, containerMetadata),
|
|
123
120
|
"export {};",
|
|
124
121
|
];
|
|
125
|
-
|
|
122
|
+
const internalSections = [
|
|
123
|
+
this.generateInternalImports(),
|
|
124
|
+
this.generateConstructorArgsMetadata(widgets),
|
|
125
|
+
this.generatePropSettersMap(widgets),
|
|
126
|
+
this.generateSetterGetterMap(widgets),
|
|
127
|
+
];
|
|
128
|
+
return {
|
|
129
|
+
jsx: await this.formatCode(jsxSections.join("\n")),
|
|
130
|
+
internal: await this.formatCode(internalSections.join("\n")),
|
|
131
|
+
};
|
|
126
132
|
}
|
|
127
133
|
generateImports() {
|
|
128
134
|
const externalImports = [...this.usedExternalNamespaces]
|
|
@@ -138,6 +144,9 @@ export class JsxGenerator {
|
|
|
138
144
|
"",
|
|
139
145
|
].join("\n");
|
|
140
146
|
}
|
|
147
|
+
generateInternalImports() {
|
|
148
|
+
return "/** Internal metadata for the reconciler. Not part of the public API. */\n";
|
|
149
|
+
}
|
|
141
150
|
generateCommonTypes(widgetClass) {
|
|
142
151
|
const widgetPropsContent = this.generateWidgetPropsContent(widgetClass);
|
|
143
152
|
return `
|
|
@@ -635,66 +644,72 @@ ${widgetPropsContent}
|
|
|
635
644
|
const name = toPascalCase(widgetName);
|
|
636
645
|
const lines = [];
|
|
637
646
|
if (isListWidget(widgetName)) {
|
|
638
|
-
|
|
647
|
+
lines.push(`/**`);
|
|
648
|
+
lines.push(` * Props for the ${name}.Root component with type-safe item rendering.`);
|
|
649
|
+
lines.push(` * @typeParam T - The type of items in the list.`);
|
|
650
|
+
lines.push(` */`);
|
|
639
651
|
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. */`);
|
|
652
|
+
lines.push(`\t/** Render function for list items. Called with null during setup (for loading state). */`);
|
|
641
653
|
lines.push(`\trenderItem: (item: T | null) => import("react").ReactElement;`);
|
|
642
654
|
lines.push(`}`);
|
|
643
655
|
lines.push(``);
|
|
644
|
-
// Root wrapper component
|
|
645
656
|
lines.push(`function ${name}Root<T>(props: ${name}RootProps<T>): import("react").ReactElement {`);
|
|
646
657
|
lines.push(`\treturn createElement("${name}.Root", props);`);
|
|
647
658
|
lines.push(`}`);
|
|
648
659
|
lines.push(``);
|
|
649
|
-
// Item wrapper component
|
|
650
660
|
lines.push(`function ${name}Item<T>(props: ListItemProps<T>): import("react").ReactElement {`);
|
|
651
661
|
lines.push(`\treturn createElement("${name}.Item", props);`);
|
|
652
662
|
lines.push(`}`);
|
|
653
663
|
}
|
|
654
664
|
else if (isColumnViewWidget(widgetName)) {
|
|
665
|
+
lines.push(`/**`);
|
|
666
|
+
lines.push(` * Props for the ${name}.Root component with type-safe item and column rendering.`);
|
|
667
|
+
lines.push(` * @typeParam T - The type of items in the column view.`);
|
|
668
|
+
lines.push(` * @typeParam C - The union type of column IDs for type-safe sorting.`);
|
|
669
|
+
lines.push(` */`);
|
|
655
670
|
lines.push(`interface ${name}RootPropsExtended<T = unknown, C extends string = string> extends ${name}Props, ColumnViewRootProps<T, C> {}`);
|
|
656
671
|
lines.push(``);
|
|
657
|
-
// Root wrapper (generic)
|
|
658
672
|
lines.push(`function ${name}Root<T = unknown, C extends string = string>(props: ${name}RootPropsExtended<T, C>): import("react").ReactElement {`);
|
|
659
673
|
lines.push(`\treturn createElement("${name}.Root", props);`);
|
|
660
674
|
lines.push(`}`);
|
|
661
675
|
lines.push(``);
|
|
662
|
-
|
|
676
|
+
lines.push(`/**`);
|
|
677
|
+
lines.push(` * Props for ${name}.Column with type-safe cell rendering.`);
|
|
678
|
+
lines.push(` * @typeParam T - The type of items passed to the renderCell function.`);
|
|
679
|
+
lines.push(` */`);
|
|
663
680
|
lines.push(`interface ${name}GenericColumnProps<T> extends Omit<ColumnViewColumnProps, "renderCell"> {`);
|
|
664
|
-
lines.push(`\t/** Render function for column cells. Called with null during setup. */`);
|
|
681
|
+
lines.push(`\t/** Render function for column cells. Called with null during setup (for loading state). */`);
|
|
665
682
|
lines.push(`\trenderCell: (item: T | null) => import("react").ReactElement;`);
|
|
666
683
|
lines.push(`}`);
|
|
667
684
|
lines.push(``);
|
|
668
|
-
// Column wrapper component
|
|
669
685
|
lines.push(`function ${name}Column<T>(props: ${name}GenericColumnProps<T>): import("react").ReactElement {`);
|
|
670
686
|
lines.push(`\treturn createElement("${name}.Column", props);`);
|
|
671
687
|
lines.push(`}`);
|
|
672
688
|
lines.push(``);
|
|
673
|
-
// Item wrapper component
|
|
674
689
|
lines.push(`function ${name}Item<T>(props: ListItemProps<T>): import("react").ReactElement {`);
|
|
675
690
|
lines.push(`\treturn createElement("${name}.Item", props);`);
|
|
676
691
|
lines.push(`}`);
|
|
677
692
|
}
|
|
678
693
|
else if (isDropDownWidget(widgetName)) {
|
|
679
|
-
|
|
694
|
+
lines.push(`/**`);
|
|
695
|
+
lines.push(` * Props for the ${name}.Root component with type-safe item handling.`);
|
|
696
|
+
lines.push(` * @typeParam T - The type of items in the dropdown.`);
|
|
697
|
+
lines.push(` */`);
|
|
680
698
|
lines.push(`interface ${name}RootProps<T> extends Omit<${name}Props, "itemLabel" | "onSelectionChanged"> {`);
|
|
681
|
-
lines.push(`\t/** Function to convert item to display label */`);
|
|
699
|
+
lines.push(`\t/** Function to convert an item to its display label. */`);
|
|
682
700
|
lines.push(`\titemLabel?: (item: T) => string;`);
|
|
683
|
-
lines.push(`\t/** Called when
|
|
701
|
+
lines.push(`\t/** Called when the selected item changes. */`);
|
|
684
702
|
lines.push(`\tonSelectionChanged?: (item: T, index: number) => void;`);
|
|
685
703
|
lines.push(`}`);
|
|
686
704
|
lines.push(``);
|
|
687
|
-
// Root wrapper component
|
|
688
705
|
lines.push(`function ${name}Root<T>(props: ${name}RootProps<T>): import("react").ReactElement {`);
|
|
689
706
|
lines.push(`\treturn createElement("${name}.Root", props);`);
|
|
690
707
|
lines.push(`}`);
|
|
691
708
|
lines.push(``);
|
|
692
|
-
// Item wrapper component
|
|
693
709
|
lines.push(`function ${name}Item<T>(props: ListItemProps<T>): import("react").ReactElement {`);
|
|
694
710
|
lines.push(`\treturn createElement("${name}.Item", props);`);
|
|
695
711
|
lines.push(`}`);
|
|
696
712
|
}
|
|
697
|
-
// Generate slot wrapper components
|
|
698
713
|
for (const slot of metadata.namedChildSlots) {
|
|
699
714
|
lines.push(``);
|
|
700
715
|
lines.push(`function ${name}${slot.slotName}(props: SlotProps): import("react").ReactElement {`);
|
|
@@ -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>>;
|