@gtkx/cli 0.20.0 → 1.0.0-rc.1
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 +173 -0
- package/bin/gtkx-dev-runner.js +9 -0
- package/dist/builder.d.ts +2 -50
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +22 -48
- package/dist/builder.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -124
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts +11 -0
- package/dist/codegen/freshness.d.ts.map +1 -0
- package/dist/codegen/freshness.js +42 -0
- package/dist/codegen/freshness.js.map +1 -0
- package/dist/codegen/report.d.ts +3 -0
- package/dist/codegen/report.d.ts.map +1 -0
- package/dist/codegen/report.js +16 -0
- package/dist/codegen/report.js.map +1 -0
- package/dist/codegen/run-codegen.d.ts +35 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -0
- package/dist/codegen/run-codegen.js +133 -0
- package/dist/codegen/run-codegen.js.map +1 -0
- package/dist/codegen/store-resolver.d.ts +21 -0
- package/dist/codegen/store-resolver.d.ts.map +1 -0
- package/dist/codegen/store-resolver.js +52 -0
- package/dist/codegen/store-resolver.js.map +1 -0
- package/dist/commands/build.d.ts +9 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +33 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/codegen.d.ts +9 -0
- package/dist/commands/codegen.d.ts.map +1 -0
- package/dist/commands/codegen.js +40 -0
- package/dist/commands/codegen.js.map +1 -0
- package/dist/commands/dev.d.ts +5 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +21 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/docs.d.ts +19 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +59 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/dev/icon-dir.d.ts +2 -0
- package/dist/dev/icon-dir.d.ts.map +1 -0
- package/dist/dev/icon-dir.js +13 -0
- package/dist/dev/icon-dir.js.map +1 -0
- package/dist/dev/refresh-tracker.d.ts +6 -0
- package/dist/dev/refresh-tracker.d.ts.map +1 -0
- package/dist/dev/refresh-tracker.js +18 -0
- package/dist/dev/refresh-tracker.js.map +1 -0
- package/dist/dev/runner-deps.d.ts +3 -0
- package/dist/dev/runner-deps.d.ts.map +1 -0
- package/dist/dev/runner-deps.js +39 -0
- package/dist/dev/runner-deps.js.map +1 -0
- package/dist/dev/runner-main.d.ts +2 -0
- package/dist/dev/runner-main.d.ts.map +1 -0
- package/dist/dev/runner-main.js +28 -0
- package/dist/dev/runner-main.js.map +1 -0
- package/dist/dev/runner.d.ts +23 -0
- package/dist/dev/runner.d.ts.map +1 -0
- package/dist/dev/runner.js +88 -0
- package/dist/dev/runner.js.map +1 -0
- package/dist/dev/schema-dir.d.ts +2 -0
- package/dist/dev/schema-dir.d.ts.map +1 -0
- package/dist/dev/schema-dir.js +10 -0
- package/dist/dev/schema-dir.js.map +1 -0
- package/dist/dev/supervisor.d.ts +17 -0
- package/dist/dev/supervisor.d.ts.map +1 -0
- package/dist/dev/supervisor.js +142 -0
- package/dist/dev/supervisor.js.map +1 -0
- package/dist/dev/vite-dev-server.d.ts +21 -0
- package/dist/dev/vite-dev-server.d.ts.map +1 -0
- package/dist/dev/vite-dev-server.js +12 -0
- package/dist/dev/vite-dev-server.js.map +1 -0
- package/dist/internal/banner.d.ts +3 -0
- package/dist/internal/banner.d.ts.map +1 -0
- package/dist/internal/banner.js +8 -0
- package/dist/internal/banner.js.map +1 -0
- package/dist/internal/data-dir.d.ts +3 -0
- package/dist/internal/data-dir.d.ts.map +1 -0
- package/dist/internal/data-dir.js +37 -0
- package/dist/internal/data-dir.js.map +1 -0
- package/dist/internal/entry-arg.d.ts +19 -0
- package/dist/internal/entry-arg.d.ts.map +1 -0
- package/dist/internal/entry-arg.js +33 -0
- package/dist/internal/entry-arg.js.map +1 -0
- package/dist/internal/errors.d.ts +4 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +27 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/list-files.d.ts +6 -0
- package/dist/internal/list-files.d.ts.map +1 -0
- package/dist/internal/list-files.js +13 -0
- package/dist/internal/list-files.js.map +1 -0
- package/dist/internal/resolve-cli-tool.d.ts +2 -0
- package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
- package/dist/internal/resolve-cli-tool.js +20 -0
- package/dist/internal/resolve-cli-tool.js.map +1 -0
- package/dist/internal/staging-dir.d.ts +3 -0
- package/dist/internal/staging-dir.d.ts.map +1 -0
- package/dist/internal/staging-dir.js +16 -0
- package/dist/internal/staging-dir.js.map +1 -0
- package/dist/internal/xdg-data-dirs.d.ts +2 -0
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
- package/dist/internal/xdg-data-dirs.js +8 -0
- package/dist/internal/xdg-data-dirs.js.map +1 -0
- package/dist/mcp/client.d.ts +23 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +157 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/handlers.d.ts +8 -0
- package/dist/mcp/handlers.d.ts.map +1 -0
- package/dist/mcp/handlers.js +119 -0
- package/dist/mcp/handlers.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/serialize-widget.d.ts +10 -0
- package/dist/mcp/serialize-widget.d.ts.map +1 -0
- package/dist/mcp/serialize-widget.js +20 -0
- package/dist/mcp/serialize-widget.js.map +1 -0
- package/dist/mcp/testing-loader.d.ts +6 -0
- package/dist/mcp/testing-loader.d.ts.map +1 -0
- package/dist/mcp/testing-loader.js +25 -0
- package/dist/mcp/testing-loader.js.map +1 -0
- package/dist/mcp/widget-registry.d.ts +13 -0
- package/dist/mcp/widget-registry.d.ts.map +1 -0
- package/dist/mcp/widget-registry.js +38 -0
- package/dist/mcp/widget-registry.js.map +1 -0
- package/dist/refresh-runtime.d.ts +10 -20
- package/dist/refresh-runtime.d.ts.map +1 -1
- package/dist/refresh-runtime.js +12 -36
- package/dist/refresh-runtime.js.map +1 -1
- package/dist/settings/compile.d.ts +2 -0
- package/dist/settings/compile.d.ts.map +1 -0
- package/dist/settings/compile.js +19 -0
- package/dist/settings/compile.js.map +1 -0
- package/dist/settings/parser.d.ts +23 -0
- package/dist/settings/parser.d.ts.map +1 -0
- package/dist/settings/parser.js +117 -0
- package/dist/settings/parser.js.map +1 -0
- package/dist/settings/render.d.ts +4 -0
- package/dist/settings/render.d.ts.map +1 -0
- package/dist/settings/render.js +148 -0
- package/dist/settings/render.js.map +1 -0
- package/dist/settings/schema.d.ts +13 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +103 -0
- package/dist/settings/schema.js.map +1 -0
- package/dist/vite-plugins/asset-extensions.d.ts +4 -0
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
- package/dist/vite-plugins/asset-extensions.js +27 -0
- package/dist/vite-plugins/asset-extensions.js.map +1 -0
- package/dist/vite-plugins/built-url.d.ts +3 -0
- package/dist/vite-plugins/built-url.d.ts.map +1 -0
- package/dist/{vite-plugin-gtkx-built-url.js → vite-plugins/built-url.js} +4 -17
- package/dist/vite-plugins/built-url.js.map +1 -0
- package/dist/vite-plugins/css.d.ts +3 -0
- package/dist/vite-plugins/css.d.ts.map +1 -0
- package/dist/vite-plugins/css.js +30 -0
- package/dist/vite-plugins/css.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +7 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js +15 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +5 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js +66 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
- package/dist/vite-plugins/icons.d.ts +3 -0
- package/dist/vite-plugins/icons.d.ts.map +1 -0
- package/dist/vite-plugins/icons.js +47 -0
- package/dist/vite-plugins/icons.js.map +1 -0
- package/dist/vite-plugins/index.d.ts +3 -0
- package/dist/vite-plugins/index.d.ts.map +1 -0
- package/dist/vite-plugins/index.js +21 -0
- package/dist/vite-plugins/index.js.map +1 -0
- package/dist/vite-plugins/native.d.ts +3 -0
- package/dist/vite-plugins/native.d.ts.map +1 -0
- package/dist/vite-plugins/native.js +60 -0
- package/dist/vite-plugins/native.js.map +1 -0
- package/dist/vite-plugins/react-compiler.d.ts +4 -0
- package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
- package/dist/vite-plugins/react-compiler.js +56 -0
- package/dist/vite-plugins/react-compiler.js.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts +3 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.js +11 -0
- package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
- package/dist/vite-plugins/resource-init-module.d.ts +5 -0
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
- package/dist/vite-plugins/resource-init-module.js +50 -0
- package/dist/vite-plugins/resource-init-module.js.map +1 -0
- package/dist/vite-plugins/resource-shared.d.ts +6 -0
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
- package/dist/vite-plugins/resource-shared.js +21 -0
- package/dist/vite-plugins/resource-shared.js.map +1 -0
- package/dist/vite-plugins/resources.d.ts +4 -0
- package/dist/vite-plugins/resources.d.ts.map +1 -0
- package/dist/vite-plugins/resources.js +220 -0
- package/dist/vite-plugins/resources.js.map +1 -0
- package/dist/vite-plugins/settings-worker-env.d.ts +3 -0
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
- package/dist/vite-plugins/settings-worker-env.js +21 -0
- package/dist/vite-plugins/settings-worker-env.js.map +1 -0
- package/dist/vite-plugins/settings.d.ts +3 -0
- package/dist/vite-plugins/settings.d.ts.map +1 -0
- package/dist/vite-plugins/settings.js +190 -0
- package/dist/vite-plugins/settings.js.map +1 -0
- package/dist/vite-plugins/undeclared-library.d.ts +3 -0
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
- package/dist/vite-plugins/undeclared-library.js +47 -0
- package/dist/vite-plugins/undeclared-library.js.map +1 -0
- package/dist/vite-plugins/virtual-module.d.ts +22 -0
- package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
- package/dist/vite-plugins/virtual-module.js +18 -0
- package/dist/vite-plugins/virtual-module.js.map +1 -0
- package/dist/vitest-plugin.d.ts +10 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +12 -0
- package/dist/vitest-plugin.js.map +1 -0
- package/env.d.ts +131 -3
- package/package.json +32 -38
- package/src/babel-modules.d.ts +13 -0
- package/src/builder.ts +28 -76
- package/src/cli.ts +22 -0
- package/src/codegen/freshness.ts +52 -0
- package/src/codegen/report.ts +20 -0
- package/src/codegen/run-codegen.ts +185 -0
- package/src/codegen/store-resolver.ts +75 -0
- package/src/commands/build.ts +37 -0
- package/src/commands/codegen.ts +44 -0
- package/src/commands/dev.ts +25 -0
- package/src/commands/docs.ts +66 -0
- package/src/dev/icon-dir.ts +11 -0
- package/src/dev/refresh-tracker.ts +21 -0
- package/src/dev/runner-deps.ts +42 -0
- package/src/dev/runner-main.ts +33 -0
- package/src/dev/runner.ts +126 -0
- package/src/dev/schema-dir.ts +11 -0
- package/src/dev/supervisor.ts +178 -0
- package/src/dev/vite-dev-server.ts +34 -0
- package/src/internal/banner.ts +9 -0
- package/src/internal/data-dir.ts +39 -0
- package/src/internal/entry-arg.ts +38 -0
- package/src/internal/errors.ts +28 -0
- package/src/internal/list-files.ts +17 -0
- package/src/internal/resolve-cli-tool.ts +21 -0
- package/src/internal/staging-dir.ts +16 -0
- package/src/internal/xdg-data-dirs.ts +7 -0
- package/src/mcp/client.ts +177 -0
- package/src/mcp/handlers.ts +156 -0
- package/src/mcp/index.ts +22 -0
- package/src/mcp/serialize-widget.ts +34 -0
- package/src/mcp/testing-loader.ts +32 -0
- package/src/mcp/widget-registry.ts +43 -0
- package/src/refresh-runtime.ts +12 -41
- package/src/settings/compile.ts +19 -0
- package/src/settings/parser.ts +154 -0
- package/src/settings/render.ts +167 -0
- package/src/settings/schema.ts +115 -0
- package/src/vite-plugins/asset-extensions.ts +28 -0
- package/src/{vite-plugin-gtkx-built-url.ts → vite-plugins/built-url.ts} +3 -16
- package/src/vite-plugins/css.ts +38 -0
- package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -0
- package/src/vite-plugins/fast-refresh/swc-refresh.ts +79 -0
- package/src/vite-plugins/icons.ts +58 -0
- package/src/vite-plugins/index.ts +22 -0
- package/src/vite-plugins/native.ts +74 -0
- package/src/vite-plugins/react-compiler.ts +70 -0
- package/src/vite-plugins/react-dom-prebundle.ts +14 -0
- package/src/vite-plugins/resource-init-module.ts +54 -0
- package/src/vite-plugins/resource-shared.ts +27 -0
- package/src/vite-plugins/resources.ts +272 -0
- package/src/vite-plugins/settings-worker-env.ts +25 -0
- package/src/vite-plugins/settings.ts +221 -0
- package/src/vite-plugins/undeclared-library.ts +69 -0
- package/src/vite-plugins/virtual-module.ts +37 -0
- package/src/vitest-plugin.ts +14 -0
- package/dist/create.d.ts +0 -54
- package/dist/create.d.ts.map +0 -1
- package/dist/create.js +0 -221
- package/dist/create.js.map +0 -1
- package/dist/dev-server.d.ts +0 -38
- package/dist/dev-server.d.ts.map +0 -1
- package/dist/dev-server.js +0 -128
- package/dist/dev-server.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -31
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js +0 -430
- package/dist/mcp-client.js.map +0 -1
- package/dist/templates.d.ts +0 -9
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -16
- package/dist/templates.js.map +0 -1
- package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
- package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-assets.js +0 -57
- package/dist/vite-plugin-gtkx-assets.js.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
- package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
- package/dist/vite-plugin-gtkx-native.d.ts +0 -14
- package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-native.js +0 -53
- package/dist/vite-plugin-gtkx-native.js.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
- package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.js +0 -37
- package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
- package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
- package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
- package/src/cli.tsx +0 -154
- package/src/create.ts +0 -310
- package/src/dev-server.tsx +0 -162
- package/src/index.ts +0 -3
- package/src/mcp-client.ts +0 -518
- package/src/templates.ts +0 -26
- package/src/vite-plugin-gtkx-assets.ts +0 -69
- package/src/vite-plugin-gtkx-native.ts +0 -64
- package/src/vite-plugin-gtkx-refresh.ts +0 -54
- package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
- package/templates/claude/EXAMPLES.md.ejs +0 -744
- package/templates/claude/SKILL.md.ejs +0 -93
- package/templates/claude/WIDGETS.md.ejs +0 -820
- package/templates/config/vitest.config.ts.ejs +0 -11
- package/templates/gitignore.ejs +0 -4
- package/templates/package.json.ejs +0 -16
- package/templates/src/app.tsx.ejs +0 -32
- package/templates/src/dev.tsx.ejs +0 -5
- package/templates/src/gtkx-env.d.ts.ejs +0 -1
- package/templates/src/index.tsx.ejs +0 -5
- package/templates/tests/app.test.tsx.ejs +0 -16
- package/templates/tsconfig.json.ejs +0 -14
|
@@ -1,744 +0,0 @@
|
|
|
1
|
-
# GTKX Code Examples
|
|
2
|
-
|
|
3
|
-
## App Structure
|
|
4
|
-
|
|
5
|
-
### Minimal App
|
|
6
|
-
|
|
7
|
-
```tsx
|
|
8
|
-
import { GtkApplicationWindow, GtkLabel, render, quit } from "@gtkx/react";
|
|
9
|
-
|
|
10
|
-
const App = () => (
|
|
11
|
-
<GtkApplicationWindow title="Hello" defaultWidth={400} defaultHeight={300} onClose={quit}>
|
|
12
|
-
<GtkLabel label="Hello, World!" />
|
|
13
|
-
</GtkApplicationWindow>
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
render(<App />, "com.example.hello");
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### Modern Adwaita App
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
23
|
-
import {
|
|
24
|
-
AdwApplicationWindow,
|
|
25
|
-
AdwHeaderBar,
|
|
26
|
-
AdwToolbarView,
|
|
27
|
-
AdwWindowTitle,
|
|
28
|
-
AdwStatusPage,
|
|
29
|
-
GtkButton,
|
|
30
|
-
quit,
|
|
31
|
-
x,
|
|
32
|
-
} from "@gtkx/react";
|
|
33
|
-
|
|
34
|
-
export const App = () => (
|
|
35
|
-
<AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onClose={quit}>
|
|
36
|
-
<AdwToolbarView>
|
|
37
|
-
<x.ContainerSlot for={AdwToolbarView} id="addTopBar">
|
|
38
|
-
<AdwHeaderBar>
|
|
39
|
-
<x.Slot for={AdwHeaderBar} id="titleWidget">
|
|
40
|
-
<AdwWindowTitle title="My App" subtitle="Welcome" />
|
|
41
|
-
</x.Slot>
|
|
42
|
-
</AdwHeaderBar>
|
|
43
|
-
</x.ContainerSlot>
|
|
44
|
-
<AdwStatusPage
|
|
45
|
-
iconName="applications-system-symbolic"
|
|
46
|
-
title="Welcome"
|
|
47
|
-
description="Get started with your GTKX app"
|
|
48
|
-
vexpand
|
|
49
|
-
>
|
|
50
|
-
<GtkButton label="Get Started" cssClasses={["suggested-action", "pill"]} halign={Gtk.Align.CENTER} />
|
|
51
|
-
</AdwStatusPage>
|
|
52
|
-
</AdwToolbarView>
|
|
53
|
-
</AdwApplicationWindow>
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
export const appId = "com.example.myapp";
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## State Management
|
|
62
|
-
|
|
63
|
-
### Controlled Form
|
|
64
|
-
|
|
65
|
-
```tsx
|
|
66
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
67
|
-
import { GtkBox, GtkButton, GtkEntry, GtkGrid, GtkLabel, x } from "@gtkx/react";
|
|
68
|
-
import { useState } from "react";
|
|
69
|
-
|
|
70
|
-
const LoginForm = () => {
|
|
71
|
-
const [email, setEmail] = useState("");
|
|
72
|
-
const [password, setPassword] = useState("");
|
|
73
|
-
|
|
74
|
-
const handleSubmit = () => {
|
|
75
|
-
console.log("Login:", { email, password });
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<GtkGrid rowSpacing={12} columnSpacing={12}>
|
|
80
|
-
<x.GridChild column={0} row={0}>
|
|
81
|
-
<GtkLabel label="Email:" halign={Gtk.Align.END} />
|
|
82
|
-
</x.GridChild>
|
|
83
|
-
<x.GridChild column={1} row={0}>
|
|
84
|
-
<GtkEntry text={email} onChanged={(e) => setEmail(e.getText())} hexpand />
|
|
85
|
-
</x.GridChild>
|
|
86
|
-
<x.GridChild column={0} row={1}>
|
|
87
|
-
<GtkLabel label="Password:" halign={Gtk.Align.END} />
|
|
88
|
-
</x.GridChild>
|
|
89
|
-
<x.GridChild column={1} row={1}>
|
|
90
|
-
<GtkEntry text={password} onChanged={(e) => setPassword(e.getText())} visibility={false} hexpand />
|
|
91
|
-
</x.GridChild>
|
|
92
|
-
<x.GridChild column={0} row={2} columnSpan={2}>
|
|
93
|
-
<GtkButton label="Login" onClicked={handleSubmit} cssClasses={["suggested-action"]} halign={Gtk.Align.END} />
|
|
94
|
-
</x.GridChild>
|
|
95
|
-
</GtkGrid>
|
|
96
|
-
);
|
|
97
|
-
};
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### List with CRUD Operations
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
104
|
-
import { GtkBox, GtkButton, GtkEntry, GtkLabel, GtkScrolledWindow } from "@gtkx/react";
|
|
105
|
-
import { useCallback, useState } from "react";
|
|
106
|
-
|
|
107
|
-
interface Todo {
|
|
108
|
-
id: string;
|
|
109
|
-
text: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let nextId = 1;
|
|
113
|
-
|
|
114
|
-
const TodoList = () => {
|
|
115
|
-
const [todos, setTodos] = useState<Todo[]>([]);
|
|
116
|
-
const [input, setInput] = useState("");
|
|
117
|
-
|
|
118
|
-
const addTodo = useCallback(() => {
|
|
119
|
-
if (!input.trim()) return;
|
|
120
|
-
setTodos((prev) => [...prev, { id: String(nextId++), text: input }]);
|
|
121
|
-
setInput("");
|
|
122
|
-
}, [input]);
|
|
123
|
-
|
|
124
|
-
const deleteTodo = useCallback((id: string) => {
|
|
125
|
-
setTodos((prev) => prev.filter((t) => t.id !== id));
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
|
|
130
|
-
<GtkBox spacing={8}>
|
|
131
|
-
<GtkEntry text={input} onChanged={(e) => setInput(e.getText())} hexpand placeholderText="New todo..." />
|
|
132
|
-
<GtkButton label="Add" onClicked={addTodo} cssClasses={["suggested-action"]} />
|
|
133
|
-
</GtkBox>
|
|
134
|
-
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
135
|
-
<GtkListView
|
|
136
|
-
items={todos.map((todo) => ({ id: todo.id, value: todo }))}
|
|
137
|
-
renderItem={(todo: Todo) => (
|
|
138
|
-
<GtkBox spacing={8} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
|
|
139
|
-
<GtkLabel label={todo.text} hexpand halign={Gtk.Align.START} />
|
|
140
|
-
<GtkButton iconName="edit-delete-symbolic" cssClasses={["flat"]} onClicked={() => deleteTodo(todo.id)} />
|
|
141
|
-
</GtkBox>
|
|
142
|
-
)}
|
|
143
|
-
/>
|
|
144
|
-
</GtkScrolledWindow>
|
|
145
|
-
</GtkBox>
|
|
146
|
-
);
|
|
147
|
-
};
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Navigation Patterns
|
|
153
|
-
|
|
154
|
-
### Stack with Sidebar Navigation
|
|
155
|
-
|
|
156
|
-
```tsx
|
|
157
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
158
|
-
import { GtkBox, GtkLabel, GtkListView, GtkPaned, GtkScrolledWindow, GtkStack, x } from "@gtkx/react";
|
|
159
|
-
import { useState } from "react";
|
|
160
|
-
|
|
161
|
-
interface Page {
|
|
162
|
-
id: string;
|
|
163
|
-
name: string;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const pages: Page[] = [
|
|
167
|
-
{ id: "home", name: "Home" },
|
|
168
|
-
{ id: "settings", name: "Settings" },
|
|
169
|
-
{ id: "about", name: "About" },
|
|
170
|
-
];
|
|
171
|
-
|
|
172
|
-
const SidebarNav = () => {
|
|
173
|
-
const [currentPage, setCurrentPage] = useState("home");
|
|
174
|
-
|
|
175
|
-
return (
|
|
176
|
-
<GtkPaned position={200}>
|
|
177
|
-
<x.Slot for={GtkPaned} id="startChild">
|
|
178
|
-
<GtkScrolledWindow cssClasses={["sidebar"]}>
|
|
179
|
-
<GtkListView
|
|
180
|
-
selected={[currentPage]}
|
|
181
|
-
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
182
|
-
onSelectionChanged={(ids) => setCurrentPage(ids[0])}
|
|
183
|
-
items={pages.map((page) => ({ id: page.id, value: page }))}
|
|
184
|
-
renderItem={(page: Page) => (
|
|
185
|
-
<GtkLabel label={page.name} halign={Gtk.Align.START} marginStart={12} marginTop={8} marginBottom={8} />
|
|
186
|
-
)}
|
|
187
|
-
/>
|
|
188
|
-
</GtkScrolledWindow>
|
|
189
|
-
</x.Slot>
|
|
190
|
-
<x.Slot for={GtkPaned} id="endChild">
|
|
191
|
-
<GtkStack page={currentPage}>
|
|
192
|
-
<x.StackPage id="home"><GtkLabel label="Home Content" vexpand /></x.StackPage>
|
|
193
|
-
<x.StackPage id="settings"><GtkLabel label="Settings Content" vexpand /></x.StackPage>
|
|
194
|
-
<x.StackPage id="about"><GtkLabel label="About Content" vexpand /></x.StackPage>
|
|
195
|
-
</GtkStack>
|
|
196
|
-
</x.Slot>
|
|
197
|
-
</GtkPaned>
|
|
198
|
-
);
|
|
199
|
-
};
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Header Bar with Back Navigation
|
|
203
|
-
|
|
204
|
-
```tsx
|
|
205
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
206
|
-
import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkStack, GtkWindow, quit, x } from "@gtkx/react";
|
|
207
|
-
import { useState } from "react";
|
|
208
|
-
|
|
209
|
-
const AppWithNavigation = () => {
|
|
210
|
-
const [page, setPage] = useState("home");
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
<GtkApplicationWindow title="App" defaultWidth={600} defaultHeight={400} onClose={quit}>
|
|
214
|
-
<x.Slot for={GtkWindow} id="titlebar">
|
|
215
|
-
<GtkHeaderBar>
|
|
216
|
-
<x.ContainerSlot for={GtkHeaderBar} id="packStart">
|
|
217
|
-
{page !== "home" && <GtkButton iconName="go-previous-symbolic" onClicked={() => setPage("home")} />}
|
|
218
|
-
</x.ContainerSlot>
|
|
219
|
-
<x.Slot for={GtkHeaderBar} id="titleWidget">
|
|
220
|
-
<GtkLabel label={page === "home" ? "Home" : "Details"} cssClasses={["title"]} />
|
|
221
|
-
</x.Slot>
|
|
222
|
-
</GtkHeaderBar>
|
|
223
|
-
</x.Slot>
|
|
224
|
-
<GtkStack page={page}>
|
|
225
|
-
<x.StackPage id="home">
|
|
226
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
|
227
|
-
<GtkLabel label="Welcome" />
|
|
228
|
-
<GtkButton label="Go to Details" onClicked={() => setPage("details")} />
|
|
229
|
-
</GtkBox>
|
|
230
|
-
</x.StackPage>
|
|
231
|
-
<x.StackPage id="details">
|
|
232
|
-
<GtkLabel label="Details Page Content" vexpand />
|
|
233
|
-
</x.StackPage>
|
|
234
|
-
</GtkStack>
|
|
235
|
-
</GtkApplicationWindow>
|
|
236
|
-
);
|
|
237
|
-
};
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Settings Page
|
|
243
|
-
|
|
244
|
-
```tsx
|
|
245
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
246
|
-
import {
|
|
247
|
-
AdwPreferencesPage,
|
|
248
|
-
AdwPreferencesGroup,
|
|
249
|
-
AdwActionRow,
|
|
250
|
-
AdwSwitchRow,
|
|
251
|
-
AdwExpanderRow,
|
|
252
|
-
AdwEntryRow,
|
|
253
|
-
GtkImage,
|
|
254
|
-
GtkScrolledWindow,
|
|
255
|
-
x,
|
|
256
|
-
} from "@gtkx/react";
|
|
257
|
-
import { useState } from "react";
|
|
258
|
-
|
|
259
|
-
const SettingsPage = () => {
|
|
260
|
-
const [darkMode, setDarkMode] = useState(false);
|
|
261
|
-
const [notifications, setNotifications] = useState(true);
|
|
262
|
-
const [username, setUsername] = useState("");
|
|
263
|
-
|
|
264
|
-
return (
|
|
265
|
-
<GtkScrolledWindow vexpand>
|
|
266
|
-
<AdwPreferencesPage title="Settings">
|
|
267
|
-
<AdwPreferencesGroup title="Appearance">
|
|
268
|
-
<AdwSwitchRow title="Dark Mode" subtitle="Use dark color scheme" active={darkMode} onActivated={() => setDarkMode(!darkMode)} />
|
|
269
|
-
</AdwPreferencesGroup>
|
|
270
|
-
|
|
271
|
-
<AdwPreferencesGroup title="Account">
|
|
272
|
-
<AdwEntryRow title="Username" text={username} onChanged={(e) => setUsername(e.getText())} />
|
|
273
|
-
<AdwActionRow title="Profile" subtitle="Manage your profile">
|
|
274
|
-
<x.Slot for={AdwActionRow} id="activatableWidget">
|
|
275
|
-
<GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
|
|
276
|
-
</x.Slot>
|
|
277
|
-
</AdwActionRow>
|
|
278
|
-
</AdwPreferencesGroup>
|
|
279
|
-
|
|
280
|
-
<AdwPreferencesGroup title="Notifications">
|
|
281
|
-
<AdwExpanderRow title="Notification Settings" subtitle="Configure alerts">
|
|
282
|
-
<AdwSwitchRow title="Sound" active />
|
|
283
|
-
<AdwSwitchRow title="Badges" active />
|
|
284
|
-
<AdwSwitchRow title="Lock Screen" />
|
|
285
|
-
</AdwExpanderRow>
|
|
286
|
-
</AdwPreferencesGroup>
|
|
287
|
-
</AdwPreferencesPage>
|
|
288
|
-
</GtkScrolledWindow>
|
|
289
|
-
);
|
|
290
|
-
};
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Data Table with Sorting
|
|
296
|
-
|
|
297
|
-
```tsx
|
|
298
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
299
|
-
import { GtkBox, GtkColumnView, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
|
|
300
|
-
import { useMemo, useState } from "react";
|
|
301
|
-
|
|
302
|
-
interface FileItem {
|
|
303
|
-
id: string;
|
|
304
|
-
name: string;
|
|
305
|
-
size: number;
|
|
306
|
-
modified: string;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const files: FileItem[] = [
|
|
310
|
-
{ id: "1", name: "document.pdf", size: 1024, modified: "2024-01-15" },
|
|
311
|
-
{ id: "2", name: "image.png", size: 2048, modified: "2024-01-14" },
|
|
312
|
-
{ id: "3", name: "notes.txt", size: 512, modified: "2024-01-13" },
|
|
313
|
-
];
|
|
314
|
-
|
|
315
|
-
const FileTable = () => {
|
|
316
|
-
const [sortColumn, setSortColumn] = useState("name");
|
|
317
|
-
const [sortOrder, setSortOrder] = useState(Gtk.SortType.ASCENDING);
|
|
318
|
-
|
|
319
|
-
const sortedFiles = useMemo(() => {
|
|
320
|
-
const sorted = [...files].sort((a, b) => {
|
|
321
|
-
const aVal = a[sortColumn as keyof FileItem];
|
|
322
|
-
const bVal = b[sortColumn as keyof FileItem];
|
|
323
|
-
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
324
|
-
return sortOrder === Gtk.SortType.ASCENDING ? cmp : -cmp;
|
|
325
|
-
});
|
|
326
|
-
return sorted;
|
|
327
|
-
}, [sortColumn, sortOrder]);
|
|
328
|
-
|
|
329
|
-
const handleSort = (column: string, order: Gtk.SortType) => {
|
|
330
|
-
setSortColumn(column);
|
|
331
|
-
setSortOrder(order);
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
return (
|
|
335
|
-
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
336
|
-
<GtkColumnView
|
|
337
|
-
estimatedRowHeight={48}
|
|
338
|
-
sortColumn={sortColumn}
|
|
339
|
-
sortOrder={sortOrder}
|
|
340
|
-
onSortChanged={handleSort}
|
|
341
|
-
items={sortedFiles.map((file) => ({ id: file.id, value: file }))}
|
|
342
|
-
>
|
|
343
|
-
<x.ColumnViewColumn title="Name" id="name" expand sortable renderCell={(f: FileItem) => <GtkLabel label={f.name} />} />
|
|
344
|
-
<x.ColumnViewColumn title="Size" id="size" fixedWidth={100} sortable renderCell={(f: FileItem) => <GtkLabel label={`${f.size} KB`} />} />
|
|
345
|
-
<x.ColumnViewColumn title="Modified" id="modified" fixedWidth={120} sortable renderCell={(f: FileItem) => <GtkLabel label={f.modified} />} />
|
|
346
|
-
</GtkColumnView>
|
|
347
|
-
</GtkScrolledWindow>
|
|
348
|
-
);
|
|
349
|
-
};
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
---
|
|
353
|
-
|
|
354
|
-
## Menu with Keyboard Shortcuts
|
|
355
|
-
|
|
356
|
-
```tsx
|
|
357
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
358
|
-
import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu, quit, x } from "@gtkx/react";
|
|
359
|
-
import { useState } from "react";
|
|
360
|
-
|
|
361
|
-
const MenuDemo = () => {
|
|
362
|
-
const [lastAction, setLastAction] = useState<string | null>(null);
|
|
363
|
-
|
|
364
|
-
return (
|
|
365
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
|
|
366
|
-
<GtkMenuButton label="File" halign={Gtk.Align.START}>
|
|
367
|
-
<x.Slot for={GtkMenuButton} id="popover">
|
|
368
|
-
<GtkPopoverMenu>
|
|
369
|
-
<x.MenuSection>
|
|
370
|
-
<x.MenuItem id="new" label="New" onActivate={() => setLastAction("New")} accels="<Control>n" />
|
|
371
|
-
<x.MenuItem id="open" label="Open" onActivate={() => setLastAction("Open")} accels="<Control>o" />
|
|
372
|
-
<x.MenuItem id="save" label="Save" onActivate={() => setLastAction("Save")} accels="<Control>s" />
|
|
373
|
-
</x.MenuSection>
|
|
374
|
-
<x.MenuSection>
|
|
375
|
-
<x.MenuSubmenu label="Export">
|
|
376
|
-
<x.MenuItem id="pdf" label="As PDF" onActivate={() => setLastAction("Export PDF")} />
|
|
377
|
-
<x.MenuItem id="csv" label="As CSV" onActivate={() => setLastAction("Export CSV")} />
|
|
378
|
-
</x.MenuSubmenu>
|
|
379
|
-
</x.MenuSection>
|
|
380
|
-
<x.MenuSection>
|
|
381
|
-
<x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
|
|
382
|
-
</x.MenuSection>
|
|
383
|
-
</GtkPopoverMenu>
|
|
384
|
-
</x.Slot>
|
|
385
|
-
</GtkMenuButton>
|
|
386
|
-
<GtkLabel label={`Last action: ${lastAction ?? "(none)"}`} />
|
|
387
|
-
</GtkBox>
|
|
388
|
-
);
|
|
389
|
-
};
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Async Data Loading
|
|
395
|
-
|
|
396
|
-
```tsx
|
|
397
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
398
|
-
import { AdwSpinner, GtkBox, GtkLabel, GtkListView, GtkScrolledWindow } from "@gtkx/react";
|
|
399
|
-
import { useEffect, useState } from "react";
|
|
400
|
-
|
|
401
|
-
interface User {
|
|
402
|
-
id: string;
|
|
403
|
-
name: string;
|
|
404
|
-
email: string;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const AsyncList = () => {
|
|
408
|
-
const [users, setUsers] = useState<User[]>([]);
|
|
409
|
-
const [loading, setLoading] = useState(true);
|
|
410
|
-
const [error, setError] = useState<string | null>(null);
|
|
411
|
-
|
|
412
|
-
useEffect(() => {
|
|
413
|
-
const fetchUsers = async () => {
|
|
414
|
-
try {
|
|
415
|
-
const response = await fetch("https://api.example.com/users");
|
|
416
|
-
const data = await response.json();
|
|
417
|
-
setUsers(data);
|
|
418
|
-
} catch (error) {
|
|
419
|
-
setError(error instanceof Error ? error.message : "Failed to load");
|
|
420
|
-
} finally {
|
|
421
|
-
setLoading(false);
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
fetchUsers();
|
|
425
|
-
}, []);
|
|
426
|
-
|
|
427
|
-
if (loading) {
|
|
428
|
-
return (
|
|
429
|
-
<GtkBox vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
|
430
|
-
<AdwSpinner widthRequest={32} heightRequest={32} />
|
|
431
|
-
</GtkBox>
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (error) {
|
|
436
|
-
return <GtkLabel label={`Error: ${error}`} cssClasses={["error"]} vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} />;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return (
|
|
440
|
-
<GtkScrolledWindow vexpand>
|
|
441
|
-
<GtkListView
|
|
442
|
-
items={users.map((user) => ({ id: user.id, value: user }))}
|
|
443
|
-
renderItem={(user: User) => (
|
|
444
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} marginStart={12} marginTop={8} marginBottom={8}>
|
|
445
|
-
<GtkLabel label={user.name} halign={Gtk.Align.START} cssClasses={["heading"]} />
|
|
446
|
-
<GtkLabel label={user.email} halign={Gtk.Align.START} cssClasses={["dim-label"]} />
|
|
447
|
-
</GtkBox>
|
|
448
|
-
)}
|
|
449
|
-
/>
|
|
450
|
-
</GtkScrolledWindow>
|
|
451
|
-
);
|
|
452
|
-
};
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
---
|
|
456
|
-
|
|
457
|
-
## Reusable Component Pattern
|
|
458
|
-
|
|
459
|
-
```tsx
|
|
460
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
461
|
-
import { GtkBox, GtkButton, GtkLabel } from "@gtkx/react";
|
|
462
|
-
import type { ReactNode } from "react";
|
|
463
|
-
|
|
464
|
-
interface CardProps {
|
|
465
|
-
title: string;
|
|
466
|
-
children: ReactNode;
|
|
467
|
-
onAction?: () => void;
|
|
468
|
-
actionLabel?: string;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const Card = ({ title, children, onAction, actionLabel }: CardProps) => (
|
|
472
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={8} cssClasses={["card"]} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
|
|
473
|
-
<GtkLabel label={title} cssClasses={["title-4"]} halign={Gtk.Align.START} />
|
|
474
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={4}>
|
|
475
|
-
{children}
|
|
476
|
-
</GtkBox>
|
|
477
|
-
{onAction && actionLabel && (
|
|
478
|
-
<GtkButton label={actionLabel} onClicked={onAction} halign={Gtk.Align.END} cssClasses={["flat"]} />
|
|
479
|
-
)}
|
|
480
|
-
</GtkBox>
|
|
481
|
-
);
|
|
482
|
-
|
|
483
|
-
const CardDemo = () => (
|
|
484
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
|
485
|
-
<Card title="Welcome" actionLabel="Learn More" onAction={() => console.log("clicked")}>
|
|
486
|
-
<GtkLabel label="This is a reusable card component." wrap />
|
|
487
|
-
</Card>
|
|
488
|
-
<Card title="Features">
|
|
489
|
-
<GtkLabel label="Build native GTK apps with React." wrap />
|
|
490
|
-
</Card>
|
|
491
|
-
</GtkBox>
|
|
492
|
-
);
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
## Navigation with AdwNavigationView
|
|
498
|
-
|
|
499
|
-
```tsx
|
|
500
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
501
|
-
import {
|
|
502
|
-
AdwApplicationWindow,
|
|
503
|
-
AdwHeaderBar,
|
|
504
|
-
AdwNavigationView,
|
|
505
|
-
AdwToolbarView,
|
|
506
|
-
GtkBox,
|
|
507
|
-
GtkButton,
|
|
508
|
-
GtkLabel,
|
|
509
|
-
quit,
|
|
510
|
-
x,
|
|
511
|
-
} from "@gtkx/react";
|
|
512
|
-
import { useState } from "react";
|
|
513
|
-
|
|
514
|
-
const NavigationDemo = () => {
|
|
515
|
-
const [history, setHistory] = useState(["home"]);
|
|
516
|
-
|
|
517
|
-
const push = (page: string) => setHistory([...history, page]);
|
|
518
|
-
const pop = () => setHistory(history.slice(0, -1));
|
|
519
|
-
|
|
520
|
-
return (
|
|
521
|
-
<AdwApplicationWindow title="Navigation Demo" defaultWidth={600} defaultHeight={400} onClose={quit}>
|
|
522
|
-
<AdwNavigationView history={history} onHistoryChanged={setHistory}>
|
|
523
|
-
<x.NavigationPage for={AdwNavigationView} id="home" title="Home">
|
|
524
|
-
<AdwToolbarView>
|
|
525
|
-
<x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
|
|
526
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
|
527
|
-
<GtkLabel label="Welcome!" cssClasses={["title-1"]} />
|
|
528
|
-
<GtkButton label="Go to Settings" onClicked={() => push("settings")} cssClasses={["suggested-action"]} />
|
|
529
|
-
</GtkBox>
|
|
530
|
-
</AdwToolbarView>
|
|
531
|
-
</x.NavigationPage>
|
|
532
|
-
<x.NavigationPage for={AdwNavigationView} id="settings" title="Settings" canPop>
|
|
533
|
-
<AdwToolbarView>
|
|
534
|
-
<x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
|
|
535
|
-
<GtkLabel label="Settings page content" vexpand />
|
|
536
|
-
</AdwToolbarView>
|
|
537
|
-
</x.NavigationPage>
|
|
538
|
-
</AdwNavigationView>
|
|
539
|
-
</AdwApplicationWindow>
|
|
540
|
-
);
|
|
541
|
-
};
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
---
|
|
545
|
-
|
|
546
|
-
## Sidebar/Content Split with AdwNavigationSplitView
|
|
547
|
-
|
|
548
|
-
```tsx
|
|
549
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
550
|
-
import {
|
|
551
|
-
AdwActionRow,
|
|
552
|
-
AdwApplicationWindow,
|
|
553
|
-
AdwHeaderBar,
|
|
554
|
-
AdwNavigationSplitView,
|
|
555
|
-
AdwToolbarView,
|
|
556
|
-
GtkBox,
|
|
557
|
-
GtkImage,
|
|
558
|
-
GtkLabel,
|
|
559
|
-
GtkListBox,
|
|
560
|
-
GtkScrolledWindow,
|
|
561
|
-
quit,
|
|
562
|
-
x,
|
|
563
|
-
} from "@gtkx/react";
|
|
564
|
-
import { useState } from "react";
|
|
565
|
-
|
|
566
|
-
interface Item {
|
|
567
|
-
id: string;
|
|
568
|
-
title: string;
|
|
569
|
-
icon: string;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const items: Item[] = [
|
|
573
|
-
{ id: "inbox", title: "Inbox", icon: "mail-unread-symbolic" },
|
|
574
|
-
{ id: "starred", title: "Starred", icon: "starred-symbolic" },
|
|
575
|
-
{ id: "sent", title: "Sent", icon: "mail-send-symbolic" },
|
|
576
|
-
];
|
|
577
|
-
|
|
578
|
-
const SplitViewDemo = () => {
|
|
579
|
-
const [selected, setSelected] = useState(items[0]);
|
|
580
|
-
|
|
581
|
-
return (
|
|
582
|
-
<AdwApplicationWindow title="Split View Demo" defaultWidth={800} defaultHeight={500} onClose={quit}>
|
|
583
|
-
<AdwNavigationSplitView sidebarWidthFraction={0.33} minSidebarWidth={200} maxSidebarWidth={300}>
|
|
584
|
-
<x.NavigationPage for={AdwNavigationSplitView} id="sidebar" title="Mail">
|
|
585
|
-
<AdwToolbarView>
|
|
586
|
-
<x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
|
|
587
|
-
<GtkScrolledWindow vexpand>
|
|
588
|
-
<GtkListBox
|
|
589
|
-
cssClasses={["navigation-sidebar"]}
|
|
590
|
-
onRowSelected={(row) => {
|
|
591
|
-
if (!row) return;
|
|
592
|
-
const item = items[row.getIndex()];
|
|
593
|
-
if (item) setSelected(item);
|
|
594
|
-
}}
|
|
595
|
-
>
|
|
596
|
-
{items.map((item) => (
|
|
597
|
-
<AdwActionRow key={item.id} title={item.title}>
|
|
598
|
-
<x.ContainerSlot for={AdwActionRow} id="addPrefix">
|
|
599
|
-
<GtkImage iconName={item.icon} />
|
|
600
|
-
</x.ContainerSlot>
|
|
601
|
-
</AdwActionRow>
|
|
602
|
-
))}
|
|
603
|
-
</GtkListBox>
|
|
604
|
-
</GtkScrolledWindow>
|
|
605
|
-
</AdwToolbarView>
|
|
606
|
-
</x.NavigationPage>
|
|
607
|
-
|
|
608
|
-
<x.NavigationPage for={AdwNavigationSplitView} id="content" title={selected?.title ?? ""}>
|
|
609
|
-
<AdwToolbarView>
|
|
610
|
-
<x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
|
|
611
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} vexpand>
|
|
612
|
-
<GtkImage iconName={selected?.icon ?? ""} iconSize={Gtk.IconSize.LARGE} />
|
|
613
|
-
<GtkLabel label={selected?.title ?? ""} cssClasses={["title-2"]} />
|
|
614
|
-
</GtkBox>
|
|
615
|
-
</AdwToolbarView>
|
|
616
|
-
</x.NavigationPage>
|
|
617
|
-
</AdwNavigationSplitView>
|
|
618
|
-
</AdwApplicationWindow>
|
|
619
|
-
);
|
|
620
|
-
};
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
---
|
|
624
|
-
|
|
625
|
-
## File Browser with GtkListView (tree)
|
|
626
|
-
|
|
627
|
-
```tsx
|
|
628
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
629
|
-
import { GtkBox, GtkImage, GtkLabel, GtkListView, GtkScrolledWindow } from "@gtkx/react";
|
|
630
|
-
import { useState } from "react";
|
|
631
|
-
|
|
632
|
-
interface FileNode {
|
|
633
|
-
id: string;
|
|
634
|
-
name: string;
|
|
635
|
-
isDirectory: boolean;
|
|
636
|
-
children?: FileNode[];
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
const files: FileNode[] = [
|
|
640
|
-
{
|
|
641
|
-
id: "src",
|
|
642
|
-
name: "src",
|
|
643
|
-
isDirectory: true,
|
|
644
|
-
children: [
|
|
645
|
-
{ id: "src/app.tsx", name: "app.tsx", isDirectory: false },
|
|
646
|
-
{ id: "src/index.tsx", name: "index.tsx", isDirectory: false },
|
|
647
|
-
],
|
|
648
|
-
},
|
|
649
|
-
{ id: "package.json", name: "package.json", isDirectory: false },
|
|
650
|
-
];
|
|
651
|
-
|
|
652
|
-
const FileBrowser = () => {
|
|
653
|
-
const [selected, setSelected] = useState<string | null>(null);
|
|
654
|
-
|
|
655
|
-
return (
|
|
656
|
-
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
657
|
-
<GtkListView
|
|
658
|
-
estimatedItemHeight={36}
|
|
659
|
-
vexpand
|
|
660
|
-
autoexpand={false}
|
|
661
|
-
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
662
|
-
selected={selected ? [selected] : []}
|
|
663
|
-
onSelectionChanged={(ids) => setSelected(ids[0] ?? null)}
|
|
664
|
-
items={files.map((file) => ({
|
|
665
|
-
id: file.id,
|
|
666
|
-
value: file,
|
|
667
|
-
children: file.children?.map((child) => ({ id: child.id, value: child })),
|
|
668
|
-
}))}
|
|
669
|
-
renderItem={(item: FileNode) => (
|
|
670
|
-
<GtkBox spacing={8}>
|
|
671
|
-
<GtkImage iconName={item.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
|
|
672
|
-
<GtkLabel label={item.name} halign={Gtk.Align.START} />
|
|
673
|
-
</GtkBox>
|
|
674
|
-
)}
|
|
675
|
-
/>
|
|
676
|
-
</GtkScrolledWindow>
|
|
677
|
-
);
|
|
678
|
-
};
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
---
|
|
682
|
-
|
|
683
|
-
## Stack with Programmatic Navigation
|
|
684
|
-
|
|
685
|
-
```tsx
|
|
686
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
687
|
-
import { GtkBox, GtkButton, GtkLabel, GtkStack, x } from "@gtkx/react";
|
|
688
|
-
import { useState } from "react";
|
|
689
|
-
|
|
690
|
-
const StackNavigation = () => {
|
|
691
|
-
const [page, setPage] = useState("home");
|
|
692
|
-
|
|
693
|
-
return (
|
|
694
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
|
695
|
-
<GtkBox spacing={6} halign={Gtk.Align.CENTER}>
|
|
696
|
-
<GtkButton label="Home" onClicked={() => setPage("home")} />
|
|
697
|
-
<GtkButton label="Settings" onClicked={() => setPage("settings")} />
|
|
698
|
-
</GtkBox>
|
|
699
|
-
<GtkStack page={page} onPageChanged={setPage} vexpand>
|
|
700
|
-
<x.StackPage id="home">
|
|
701
|
-
<GtkLabel label="Home Content" />
|
|
702
|
-
</x.StackPage>
|
|
703
|
-
<x.StackPage id="settings">
|
|
704
|
-
<GtkLabel label="Settings Content" />
|
|
705
|
-
</x.StackPage>
|
|
706
|
-
</GtkStack>
|
|
707
|
-
</GtkBox>
|
|
708
|
-
);
|
|
709
|
-
};
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
---
|
|
713
|
-
|
|
714
|
-
## Animated Card with Toggle
|
|
715
|
-
|
|
716
|
-
```tsx
|
|
717
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
718
|
-
import { GtkBox, GtkButton, GtkLabel, x } from "@gtkx/react";
|
|
719
|
-
import { useState } from "react";
|
|
720
|
-
|
|
721
|
-
const AnimatedCard = () => {
|
|
722
|
-
const [visible, setVisible] = useState(true);
|
|
723
|
-
|
|
724
|
-
return (
|
|
725
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
|
|
726
|
-
<GtkButton label={visible ? "Hide Card" : "Show Card"} onClicked={() => setVisible(!visible)} halign={Gtk.Align.START} />
|
|
727
|
-
{visible && (
|
|
728
|
-
<x.Animation
|
|
729
|
-
initial={{ opacity: 0, scale: 0.8, translateY: -20 }}
|
|
730
|
-
animate={{ opacity: 1, scale: 1, translateY: 0 }}
|
|
731
|
-
exit={{ opacity: 0, scale: 0.8, translateY: 20 }}
|
|
732
|
-
transition={{ mode: "spring", damping: 0.7, stiffness: 200 }}
|
|
733
|
-
animateOnMount
|
|
734
|
-
>
|
|
735
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={8} cssClasses={["card"]} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
|
|
736
|
-
<GtkLabel label="Animated Card" cssClasses={["title-4"]} halign={Gtk.Align.START} />
|
|
737
|
-
<GtkLabel label="This card animates in with a spring effect and fades out when dismissed." wrap />
|
|
738
|
-
</GtkBox>
|
|
739
|
-
</x.Animation>
|
|
740
|
-
)}
|
|
741
|
-
</GtkBox>
|
|
742
|
-
);
|
|
743
|
-
};
|
|
744
|
-
```
|