react-native-gtkx 0.1.0-alpha.2 → 0.1.0-alpha.4
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 +41 -8
- package/dist/animated/create-animated.js +1 -1
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/index.js +3 -3
- package/dist/animated/index.js.map +1 -1
- package/dist/apis/back-handler.d.ts +9 -0
- package/dist/apis/back-handler.js +30 -0
- package/dist/apis/back-handler.js.map +1 -0
- package/dist/apis/host.d.ts +7 -1
- package/dist/apis/host.gtkx.js +12 -1
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/apis/host.js.map +1 -1
- package/dist/apis/i18n-manager.d.ts +14 -0
- package/dist/apis/i18n-manager.js +20 -0
- package/dist/apis/i18n-manager.js.map +1 -0
- package/dist/apis/index.d.ts +17 -0
- package/dist/apis/index.js +5 -0
- package/dist/apis/index.js.map +1 -1
- package/dist/apis/interaction-manager.d.ts +16 -0
- package/dist/apis/interaction-manager.js +96 -0
- package/dist/apis/interaction-manager.js.map +1 -0
- package/dist/apis/linking.d.ts +4 -0
- package/dist/apis/linking.js +9 -0
- package/dist/apis/linking.js.map +1 -1
- package/dist/components/app-registry.d.ts +2 -0
- package/dist/components/app-registry.js +19 -2
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/flat-list.d.ts +2 -0
- package/dist/components/flat-list.js.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/pressable.js +4 -4
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/root.d.ts +10 -1
- package/dist/components/root.js +39 -1
- package/dist/components/root.js.map +1 -1
- package/dist/components/scroll-view.js +58 -5
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/text-input.d.ts +1 -0
- package/dist/components/text-input.js +34 -5
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/use-layout-child.d.ts +1 -1
- package/dist/components/use-layout-child.js +22 -2
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/virtualized-list.d.ts +2 -0
- package/dist/components/virtualized-list.js +132 -10
- package/dist/components/virtualized-list.js.map +1 -1
- package/dist/contracts.js +2 -3
- package/dist/contracts.js.map +1 -1
- package/dist/gtkx/bridge/index.d.ts +4 -2
- package/dist/gtkx/bridge/index.js +7 -3
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/measure.js +5 -0
- package/dist/gtkx/bridge/measure.js.map +1 -1
- package/dist/gtkx/bridge/theme.js +1 -1
- package/dist/gtkx/bridge/theme.js.map +1 -1
- package/dist/gtkx/bridge/use-signal.d.ts +2 -0
- package/dist/gtkx/bridge/use-signal.js +31 -0
- package/dist/gtkx/bridge/use-signal.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/layout/engine.d.ts +4 -0
- package/dist/layout/engine.js +97 -5
- package/dist/layout/engine.js.map +1 -1
- package/dist/layout/node.d.ts +1 -1
- package/dist/layout/node.js +8 -5
- package/dist/layout/node.js.map +1 -1
- package/dist/metro/index.js +1 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/index.d.ts +44 -0
- package/dist/navigation/index.js +216 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/navigation/option-warnings.d.ts +2 -0
- package/dist/navigation/option-warnings.js +61 -0
- package/dist/navigation/option-warnings.js.map +1 -0
- package/dist/navigation/sidebar.d.ts +37 -0
- package/dist/navigation/sidebar.js +71 -0
- package/dist/navigation/sidebar.js.map +1 -0
- package/dist/perf.d.ts +9 -0
- package/dist/perf.js +156 -0
- package/dist/perf.js.map +1 -0
- package/dist/runner/host-dev.js +15 -6
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +24 -9
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.js +7 -3
- package/dist/runner/index.js.map +1 -1
- package/dist/style/colors.js +3 -1
- package/dist/style/colors.js.map +1 -1
- package/dist/style/index.js +2 -2
- package/dist/style/index.js.map +1 -1
- package/dist/style/registry.gtkx.js +1 -1
- package/dist/style/registry.gtkx.js.map +1 -1
- package/dist/style/split-style.js +1 -1
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-align.js +1 -1
- package/dist/style/text-align.js.map +1 -1
- package/dist/vite/index.d.ts +5 -4
- package/dist/vite/index.js +42 -7
- package/dist/vite/index.js.map +1 -1
- package/package.json +18 -10
- package/types.d.ts +13 -0
package/dist/apis/linking.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linking.js","sourceRoot":"","sources":["../../src/apis/linking.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"linking.js","sourceRoot":"","sources":["../../src/apis/linking.ts"],"names":[],"mappings":"AAGA,8EAA8E;AAC9E,wEAAwE;AACxE,8CAA8C;AAC9C,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAA;AAEtE,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAiB,EAAE;IAC9C,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAChD,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;IACzB,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;AAC3D,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAiB,EAAE,EAAE,CAAC,CAAC;IACnD,0EAA0E;IAC1E,OAAO,EAAE,CAAC,GAAW,EAAiB,EAAE;QACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAC7D,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IACD,UAAU,EAAE,CAAC,GAAW,EAAoB,EAAE;QAC5C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CAAC,0CAA0C,CAAC,CACtD,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1E,CAAC;IACD,iEAAiE;IACjE,aAAa,EAAE,GAA2B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IAElE,gEAAgE;IAChE,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,gBAAgB,EAAE,CAChB,IAAW,EACX,OAAyC,EACrB,EAAE;QACtB,KAAK,IAAI,CAAA;QACT,KAAK,OAAO,CAAA;QACZ,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAA;IAC7B,CAAC;CACF,CAAC,CAAA","sourcesContent":["import type { SubscriptionHandle } from \"../contracts\"\nimport type { LinkingHost } from \"./host\"\n\n// Schemes every Linux desktop resolves through the portal / default handlers.\n// There is no per-handler introspection API comparable to canOpenURL on\n// mobile, so the answer is static per scheme.\nconst SUPPORTED_SCHEMES = new Set([\"http\", \"https\", \"mailto\", \"file\"])\n\nconst schemeOf = (url: string): string | null => {\n const match = /^([a-z][a-z0-9+.-]*):/i.exec(url)\n const scheme = match?.[1]\n return scheme === undefined ? null : scheme.toLowerCase()\n}\n\nexport const createLinking = (host: LinkingHost) => ({\n // Opens the URL in the user's default handler (Gtk.UriLauncher → portal).\n openURL: (url: string): Promise<void> => {\n if (typeof url !== \"string\" || url.length === 0) {\n return Promise.reject(\n new Error(\"Linking.openURL: url must be a non-empty string\"),\n )\n }\n return host.launchUri(url)\n },\n canOpenURL: (url: string): Promise<boolean> => {\n if (typeof url !== \"string\") {\n return Promise.reject(\n new Error(\"Linking.canOpenURL: url must be a string\"),\n )\n }\n const scheme = schemeOf(url)\n return Promise.resolve(scheme !== null && SUPPORTED_SCHEMES.has(scheme))\n },\n // Desktop apps are not launched through deep links: always null.\n getInitialURL: (): Promise<string | null> => Promise.resolve(null),\n\n // RN parity for consumers that subscribe to incoming deep links\n // (react-navigation's useLinking does on mount). Nothing delivers \"url\"\n // events on desktop today — a future Gio.Application::open wiring would;\n // the subscription contract is honored so such consumers mount cleanly.\n addEventListener: (\n type: \"url\",\n handler: (event: { url: string }) => void,\n ): SubscriptionHandle => {\n void type\n void handler\n return { remove: () => {} }\n },\n})\n\nexport type LinkingModule = ReturnType<typeof createLinking>\n"]}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type ComponentType } from "react";
|
|
2
2
|
type ComponentProvider = () => ComponentType<Record<string, unknown>>;
|
|
3
|
+
export declare const getActiveChrome: () => "system" | "content" | null;
|
|
3
4
|
export type RunApplicationParams = {
|
|
4
5
|
initialProps?: Record<string, unknown>;
|
|
5
6
|
title?: string;
|
|
6
7
|
width?: number;
|
|
7
8
|
height?: number;
|
|
9
|
+
chrome?: "system" | "content";
|
|
8
10
|
};
|
|
9
11
|
export declare const AppRegistry: {
|
|
10
12
|
registerComponent(appKey: string, provider: ComponentProvider): string;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useLayoutEffect } from "react";
|
|
3
|
-
import { createRoot, Gtk, GtkApplication, GtkApplicationWindow, quit, } from "../gtkx/bridge/index";
|
|
3
|
+
import { AdwApplicationWindow, createRoot, Gtk, GtkApplication, GtkApplicationWindow, quit, } from "../gtkx/bridge/index";
|
|
4
4
|
import { Root } from "./root";
|
|
5
5
|
const registry = new Map();
|
|
6
|
+
// The chrome the running app was started with — read by the navigation
|
|
7
|
+
// dev-mode hint (a HeaderBar page under "system" chrome renders a doubled
|
|
8
|
+
// titlebar). null until runApplication ran (test harnesses never set it).
|
|
9
|
+
let activeChrome = null;
|
|
10
|
+
export const getActiveChrome = () => activeChrome;
|
|
6
11
|
// RN parity: system "reduce animations" hints never auto-stop RN animations
|
|
7
12
|
// (ActivityIndicator keeps spinning; honoring reduce-motion is an explicit
|
|
8
13
|
// opt-in via AccessibilityInfo). Our Animated runs on GLib timers and already
|
|
@@ -24,6 +29,15 @@ const forceEnableAnimations = () => {
|
|
|
24
29
|
// later portal update cannot flip this back.
|
|
25
30
|
settings.gtkEnableAnimations = true;
|
|
26
31
|
};
|
|
32
|
+
// chrome "content": the app component IS the window content (a navigation
|
|
33
|
+
// container whose pages host their own NestedRoots) — no window-level Yoga
|
|
34
|
+
// root in between: the layout root would not allocate foreign GTK children.
|
|
35
|
+
const ContentChrome = ({ App, initialProps, }) => {
|
|
36
|
+
useLayoutEffect(() => {
|
|
37
|
+
forceEnableAnimations();
|
|
38
|
+
}, []);
|
|
39
|
+
return _jsx(App, { ...initialProps });
|
|
40
|
+
};
|
|
27
41
|
const WindowContent = ({ App, initialProps, initialWidth, initialHeight, }) => {
|
|
28
42
|
// Settings need an initialized display, and writing them mid-render is
|
|
29
43
|
// unsafe — apply after the window mounts.
|
|
@@ -54,7 +68,10 @@ export const AppRegistry = {
|
|
|
54
68
|
const App = provider();
|
|
55
69
|
const width = params.width ?? 800;
|
|
56
70
|
const height = params.height ?? 600;
|
|
57
|
-
const
|
|
71
|
+
const contentChrome = params.chrome === "content";
|
|
72
|
+
activeChrome = contentChrome ? "content" : "system";
|
|
73
|
+
const Window = contentChrome ? AdwApplicationWindow : GtkApplicationWindow;
|
|
74
|
+
const AppWindow = () => (_jsx(Window, { title: params.title ?? appKey, defaultWidth: width, defaultHeight: height, onCloseRequest: quit, children: contentChrome ? (_jsx(ContentChrome, { App: App, initialProps: params.initialProps ?? {} })) : (_jsx(WindowContent, { App: App, initialProps: params.initialProps ?? {}, initialWidth: width, initialHeight: height })) }));
|
|
58
75
|
createRoot().render(_jsx(GtkApplication, { children: _jsx(AppWindow, {}) }));
|
|
59
76
|
},
|
|
60
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAsB,MAAM,OAAO,CAAA;AAC3D,OAAO,EACL,UAAU,EACV,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAI7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, type ComponentType } from \"react\"\nimport {\n AdwApplicationWindow,\n createRoot,\n Gtk,\n GtkApplication,\n GtkApplicationWindow,\n quit,\n} from \"../gtkx/bridge/index\"\nimport { Root } from \"./root\"\n\ntype ComponentProvider = () => ComponentType<Record<string, unknown>>\n\nconst registry = new Map<string, ComponentProvider>()\n\n// The chrome the running app was started with — read by the navigation\n// dev-mode hint (a HeaderBar page under \"system\" chrome renders a doubled\n// titlebar). null until runApplication ran (test harnesses never set it).\nlet activeChrome: \"system\" | \"content\" | null = null\nexport const getActiveChrome = (): \"system\" | \"content\" | null => activeChrome\n\nexport type RunApplicationParams = {\n initialProps?: Record<string, unknown>\n title?: string\n width?: number\n height?: number\n // Window chrome. \"system\" (default): a GtkApplicationWindow with its own\n // titlebar. \"content\": an AdwApplicationWindow with NO window titlebar —\n // the app's content provides HeaderBars (navigation apps: the page\n // HeaderBar becomes the titlebar, with the window controls in it).\n chrome?: \"system\" | \"content\"\n}\n\n// RN parity: system \"reduce animations\" hints never auto-stop RN animations\n// (ActivityIndicator keeps spinning; honoring reduce-motion is an explicit\n// opt-in via AccessibilityInfo). Our Animated runs on GLib timers and already\n// ignores the hint, so GTK-internal animations (GtkSpinner, switch slides)\n// must behave the same — otherwise e.g. GNOME under software rendering\n// reports enable-animations=false through the settings portal and spinners\n// freeze while Animated keeps moving.\nlet animationsForced = false\nconst forceEnableAnimations = (): void => {\n if (animationsForced) {\n return\n }\n const settings = Gtk.Settings.getDefault()\n if (!settings) {\n return\n }\n animationsForced = true\n // Application-set GtkSettings values outrank the desktop backend, so a\n // later portal update cannot flip this back.\n settings.gtkEnableAnimations = true\n}\n\n// chrome \"content\": the app component IS the window content (a navigation\n// container whose pages host their own NestedRoots) — no window-level Yoga\n// root in between: the layout root would not allocate foreign GTK children.\nconst ContentChrome = ({\n App,\n initialProps,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n}) => {\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n return <App {...initialProps} />\n}\n\nconst WindowContent = ({\n App,\n initialProps,\n initialWidth,\n initialHeight,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n initialWidth: number\n initialHeight: number\n}) => {\n // Settings need an initialized display, and writing them mid-render is\n // unsafe — apply after the window mounts.\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n\n // The Root is the window's direct child: RnGtkxLayout reports a zero\n // minimum (the window shrinks freely — no ratchet) and adopts the actual\n // content-area allocation as the layout viewport. No scrollable wrapper\n // means a window can never scroll its own root — RN semantics, scrolling\n // stays opt-in via <ScrollView>.\n return (\n <Root\n width={initialWidth}\n height={initialHeight}\n followAllocation\n >\n <App {...initialProps} />\n </Root>\n )\n}\n\nexport const AppRegistry = {\n registerComponent(appKey: string, provider: ComponentProvider): string {\n registry.set(appKey, provider)\n return appKey\n },\n\n getAppKeys(): string[] {\n return [...registry.keys()]\n },\n\n runApplication(appKey: string, params: RunApplicationParams = {}): void {\n const provider = registry.get(appKey)\n if (!provider) {\n throw new Error(\n `AppRegistry: no component registered for \"${appKey}\". ` +\n `Registered: ${[...registry.keys()].join(\", \") || \"(none)\"}`,\n )\n }\n const App = provider()\n const width = params.width ?? 800\n const height = params.height ?? 600\n\n const contentChrome = params.chrome === \"content\"\n activeChrome = contentChrome ? \"content\" : \"system\"\n const Window = contentChrome ? AdwApplicationWindow : GtkApplicationWindow\n const AppWindow = () => (\n <Window\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n >\n {contentChrome ? (\n <ContentChrome\n App={App}\n initialProps={params.initialProps ?? {}}\n />\n ) : (\n <WindowContent\n App={App}\n initialProps={params.initialProps ?? {}}\n initialWidth={width}\n initialHeight={height}\n />\n )}\n </Window>\n )\n\n createRoot().render(\n <GtkApplication>\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
|
|
@@ -23,6 +23,8 @@ export type FlatListProps<T> = Omit<ScrollViewProps, "children"> & {
|
|
|
23
23
|
getItemLayout?: (data: readonly T[], index: number) => ItemLayout;
|
|
24
24
|
windowSize?: number;
|
|
25
25
|
initialNumToRender?: number;
|
|
26
|
+
maxToRenderPerBatch?: number;
|
|
27
|
+
updateCellsBatchingPeriod?: number;
|
|
26
28
|
extraData?: unknown;
|
|
27
29
|
inverted?: boolean;
|
|
28
30
|
refreshing?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flat-list.js","sourceRoot":"","sources":["../../src/components/flat-list.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAyC,MAAM,OAAO,CAAA;AAGzE,OAAO,EACL,eAAe,GAMhB,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"flat-list.js","sourceRoot":"","sources":["../../src/components/flat-list.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAyC,MAAM,OAAO,CAAA;AAGzE,OAAO,EACL,eAAe,GAMhB,MAAM,oBAAoB,CAAA;AAuC3B,6EAA6E;AAC7E,mEAAmE;AACnE,MAAM,aAAa,GAAG,UAAU,CAC9B,CAAK,KAAuB,EAAE,GAAqC,EAAE,EAAE,CAAC,CACtE,KAAC,eAAe,OACV,KAAK,EACT,GAAG,EAAE,GAAG,GACR,CACH,CACF,CAAA;AACD,aAAa,CAAC,WAAW,GAAG,UAAU,CAAA;AAEtC,MAAM,CAAC,MAAM,QAAQ,GAAG,aAEP,CAAA;AA+BjB,MAAM,CAAC,MAAM,WAAW,GAAG,CAAK,EAC9B,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,2BAA2B,GAAG,IAAI,EAClC,GAAG,EACH,GAAG,IAAI,EACa,EAAgB,EAAE;IACtC,MAAM,IAAI,GAAoB,EAAE,CAAA;IAChC,MAAM,aAAa,GAAa,EAAE,CAAA;IAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACnC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,CACL,KAAC,QAAQ,OACH,IAAI,EACR,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,mBAAmB,EACjB,2BAA2B,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAEzD,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAC5C,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAC5B,GAAG,CAAC,IAAI,KAAK,QAAQ;YACnB,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC;YAC3D,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,GAEtD,CACH,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { forwardRef, type ComponentType, type ReactElement } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport type { ScrollViewProps } from \"./scroll-view\"\nimport {\n VirtualizedList,\n type ItemLayout,\n type ListRenderItemInfo,\n type ViewabilityConfig,\n type ViewToken,\n type VirtualizedListHandle,\n} from \"./virtualized-list\"\n\nexport type {\n ListRenderItemInfo,\n ViewabilityConfig,\n ViewToken,\n} from \"./virtualized-list\"\n// The FlatList ref exposes the full scroll surface of the windowed core\n// (scrollTo, scrollToEnd, scrollToIndex, scrollToItem, scrollToOffset).\nexport type { VirtualizedListHandle as FlatListHandle } from \"./virtualized-list\"\n\nexport type FlatListProps<T> = Omit<ScrollViewProps, \"children\"> & {\n data: readonly T[]\n renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null\n keyExtractor?: (item: T, index: number) => string\n ItemSeparatorComponent?: ComponentType | null\n ListHeaderComponent?: ComponentType | ReactElement | null\n ListFooterComponent?: ComponentType | ReactElement | null\n ListEmptyComponent?: ComponentType | ReactElement | null\n onEndReached?: () => void\n onEndReachedThreshold?: number\n onViewableItemsChanged?: (info: {\n viewableItems: ViewToken<T>[]\n changed: ViewToken<T>[]\n }) => void\n viewabilityConfig?: ViewabilityConfig\n estimatedItemSize?: number\n getItemLayout?: (data: readonly T[], index: number) => ItemLayout\n windowSize?: number\n initialNumToRender?: number\n maxToRenderPerBatch?: number\n updateCellsBatchingPeriod?: number\n extraData?: unknown\n inverted?: boolean\n refreshing?: boolean\n onRefresh?: () => void\n style?: StyleProp\n}\n\n// FlatList is a thin veneer over the windowed VirtualizedList core: only the\n// rows around the viewport are mounted (see virtualized-list.tsx).\nconst FlatListInner = forwardRef(\n <T,>(props: FlatListProps<T>, ref: React.Ref<VirtualizedListHandle>) => (\n <VirtualizedList\n {...props}\n ref={ref}\n />\n ),\n)\nFlatListInner.displayName = \"FlatList\"\n\nexport const FlatList = FlatListInner as <T>(\n props: FlatListProps<T> & { ref?: React.Ref<VirtualizedListHandle> },\n) => ReactElement\n\nexport type SectionListData<T> = { title: string; data: readonly T[] }\n\n// Viewability props are excluded: SectionList flattens sections into private\n// row records, so ViewTokens would leak that internal row type instead of T —\n// section-aware tokens are not implemented yet.\nexport type SectionListProps<T> = Omit<\n FlatListProps<T>,\n | \"data\"\n | \"renderItem\"\n | \"getItemLayout\"\n | \"onViewableItemsChanged\"\n | \"viewabilityConfig\"\n> & {\n sections: readonly SectionListData<T>[]\n renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null\n renderSectionHeader?: (info: {\n section: SectionListData<T>\n }) => ReactElement | null\n // RN default on iOS; ours defaults to true too — section headers pin to\n // the top and get pushed out by the next section's header.\n stickySectionHeadersEnabled?: boolean\n // React 19 ref-as-prop: the same scroll surface FlatList exposes.\n ref?: React.Ref<VirtualizedListHandle>\n}\n\ntype SectionRow<T> =\n | { kind: \"header\"; section: SectionListData<T> }\n | { kind: \"item\"; item: T; index: number }\n\nexport const SectionList = <T,>({\n sections,\n renderItem,\n renderSectionHeader,\n stickySectionHeadersEnabled = true,\n ref,\n ...rest\n}: SectionListProps<T>): ReactElement => {\n const rows: SectionRow<T>[] = []\n const headerIndices: number[] = []\n for (const section of sections) {\n headerIndices.push(rows.length)\n rows.push({ kind: \"header\", section })\n section.data.forEach((item, index) => {\n rows.push({ kind: \"item\", item, index })\n })\n }\n return (\n <FlatList\n {...rest}\n ref={ref}\n data={rows}\n stickyHeaderIndices={\n stickySectionHeadersEnabled ? headerIndices : undefined\n }\n keyExtractor={(_row, index) => String(index)}\n renderItem={({ item: row }) =>\n row.kind === \"header\"\n ? (renderSectionHeader?.({ section: row.section }) ?? null)\n : renderItem({ item: row.item, index: row.index })\n }\n />\n )\n}\n"]}
|
|
@@ -5,7 +5,7 @@ export { FlatList, SectionList, type FlatListProps, type ListRenderItemInfo, typ
|
|
|
5
5
|
export { Image, type ImageProps, type ImageSource } from "./image";
|
|
6
6
|
export { Modal, type ModalProps } from "./modal";
|
|
7
7
|
export { Pressable, TouchableOpacity, type PressableProps, type PressableStateCallbackType, type PressEvent, type TouchableOpacityProps, } from "./pressable";
|
|
8
|
-
export { Root, type RootProps } from "./root";
|
|
8
|
+
export { IntrinsicRoot, NestedRoot, Root, type IntrinsicRootProps, type NestedRootProps, type RootProps, } from "./root";
|
|
9
9
|
export { ScrollView, type ScrollEvent, type ScrollViewHandle, type ScrollViewProps, } from "./scroll-view";
|
|
10
10
|
export { Switch, type SwitchProps } from "./switch";
|
|
11
11
|
export { Text, type TextProps } from "./text";
|
package/dist/components/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { FlatList, SectionList, } from "./flat-list";
|
|
|
5
5
|
export { Image } from "./image";
|
|
6
6
|
export { Modal } from "./modal";
|
|
7
7
|
export { Pressable, TouchableOpacity, } from "./pressable";
|
|
8
|
-
export { Root } from "./root";
|
|
8
|
+
export { IntrinsicRoot, NestedRoot, Root, } from "./root";
|
|
9
9
|
export { ScrollView, } from "./scroll-view";
|
|
10
10
|
export { Switch } from "./switch";
|
|
11
11
|
export { Text } from "./text";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,QAAQ,EACR,MAAM,GAGP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAA6B,MAAM,gBAAgB,CAAA;AACvE,OAAO,EACL,QAAQ,EACR,WAAW,GAOZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAqC,MAAM,SAAS,CAAA;AAClE,OAAO,EAAE,KAAK,EAAmB,MAAM,SAAS,CAAA;AAChD,OAAO,EACL,SAAS,EACT,gBAAgB,GAKjB,MAAM,aAAa,CAAA;AACpB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,QAAQ,EACR,MAAM,GAGP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAA6B,MAAM,gBAAgB,CAAA;AACvE,OAAO,EACL,QAAQ,EACR,WAAW,GAOZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAqC,MAAM,SAAS,CAAA;AAClE,OAAO,EAAE,KAAK,EAAmB,MAAM,SAAS,CAAA;AAChD,OAAO,EACL,SAAS,EACT,gBAAgB,GAKjB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,aAAa,EACb,UAAU,EACV,IAAI,GAIL,MAAM,QAAQ,CAAA;AACf,OAAO,EACL,UAAU,GAIX,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAuB,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA","sourcesContent":["export {\n ActivityIndicator,\n type ActivityIndicatorProps,\n} from \"./activity-indicator\"\nexport {\n Animated,\n Easing,\n type AnimatedViewProps,\n type AnimatedViewStyle,\n} from \"./animated\"\nexport { AppRegistry, type RunApplicationParams } from \"./app-registry\"\nexport {\n FlatList,\n SectionList,\n type FlatListProps,\n type ListRenderItemInfo,\n type SectionListData,\n type SectionListProps,\n type ViewabilityConfig,\n type ViewToken,\n} from \"./flat-list\"\nexport { Image, type ImageProps, type ImageSource } from \"./image\"\nexport { Modal, type ModalProps } from \"./modal\"\nexport {\n Pressable,\n TouchableOpacity,\n type PressableProps,\n type PressableStateCallbackType,\n type PressEvent,\n type TouchableOpacityProps,\n} from \"./pressable\"\nexport {\n IntrinsicRoot,\n NestedRoot,\n Root,\n type IntrinsicRootProps,\n type NestedRootProps,\n type RootProps,\n} from \"./root\"\nexport {\n ScrollView,\n type ScrollEvent,\n type ScrollViewHandle,\n type ScrollViewProps,\n} from \"./scroll-view\"\nexport { Switch, type SwitchProps } from \"./switch\"\nexport { Text, type TextProps } from \"./text\"\nexport { TextInput, type TextInputProps } from \"./text-input\"\nexport { SafeAreaView, StatusBar, View, type ViewProps } from \"./view\"\nexport { type LayoutEvent } from \"./use-layout-child\"\n"]}
|
|
@@ -9,4 +9,4 @@ export type ModalProps = {
|
|
|
9
9
|
height?: number;
|
|
10
10
|
children?: ReactNode;
|
|
11
11
|
};
|
|
12
|
-
export declare const Modal: ({ visible, onRequestClose, title, width, height, children, }: ModalProps) =>
|
|
12
|
+
export declare const Modal: ({ visible, onRequestClose, title, width, height, children, }: ModalProps) => ReactNode;
|
|
@@ -15,10 +15,10 @@ export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onL
|
|
|
15
15
|
const longPressTimer = useRef(null);
|
|
16
16
|
const longPressFired = useRef(false);
|
|
17
17
|
const state = { pressed, hovered };
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
18
|
+
// Gestures are attached imperatively even though rc.2 has a declarative
|
|
19
|
+
// `controllers` slot: the controllers are wired exactly once per widget and
|
|
20
|
+
// the handlers read the latest props through a ref, so a re-render never
|
|
21
|
+
// detaches and re-adds a gesture mid-press.
|
|
22
22
|
const handlersRef = useRef({
|
|
23
23
|
handlePressed: (n, x, y) => {
|
|
24
24
|
void n;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../../src/components/pressable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState, type ReactNode } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport { Gtk, GtkBox } from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type PressableStateCallbackType = {\n pressed: boolean\n hovered: boolean\n}\n\nexport type PressEvent = { nativeEvent: { x: number; y: number } }\n\nexport type PressableProps = {\n style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp)\n children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode)\n onPress?: (event: PressEvent) => void\n onPressIn?: (event: PressEvent) => void\n onPressOut?: (event: PressEvent) => void\n onLongPress?: (event: PressEvent) => void\n onHoverIn?: () => void\n onHoverOut?: () => void\n disabled?: boolean\n delayLongPress?: number\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst pressEvent = (x: number, y: number): PressEvent => ({\n nativeEvent: { x, y },\n})\n\n// A View with a click gesture and hover tracking. State-dependent style and\n// children follow the RN Pressable function-prop contract.\nexport const Pressable = ({\n style,\n children,\n onPress,\n onPressIn,\n onPressOut,\n onLongPress,\n onHoverIn,\n onHoverOut,\n disabled = false,\n delayLongPress = 500,\n onLayout,\n testID,\n}: PressableProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [pressed, setPressed] = useState(false)\n const [hovered, setHovered] = useState(false)\n const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const longPressFired = useRef(false)\n\n const state: PressableStateCallbackType = { pressed, hovered }\n\n // RC1-WORKAROUND(controllers-as-children): see docs/gtkx-rc1-vs-main.md\n // rc.1 has no controller-as-JSX-children support (main-only feature):\n // gestures are attached imperatively; handlers read the latest props via a\n // ref so the controllers are wired exactly once.\n const handlersRef = useRef({\n handlePressed: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleReleased: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleCancel: (): void => {},\n handleEnter: (): void => {},\n handleLeave: (): void => {},\n })\n const resolvedStyle = typeof style === \"function\" ? style(state) : style\n const resolvedChildren =\n typeof children === \"function\" ? children(state) : children\n\n const { host, node, cssClass } = useLayoutChild(widgetRef, {\n style: resolvedStyle,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n const clearLongPress = (): void => {\n if (longPressTimer.current !== null) {\n clearTimeout(longPressTimer.current)\n longPressTimer.current = null\n }\n }\n\n const handlePressed = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n longPressFired.current = false\n setPressed(true)\n onPressIn?.(pressEvent(x, y))\n if (onLongPress) {\n clearLongPress()\n longPressTimer.current = setTimeout(() => {\n longPressFired.current = true\n onLongPress(pressEvent(x, y))\n }, delayLongPress)\n }\n }\n\n const handleReleased = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n clearLongPress()\n setPressed(false)\n onPressOut?.(pressEvent(x, y))\n if (!longPressFired.current) {\n onPress?.(pressEvent(x, y))\n }\n }\n\n const handleCancel = (): void => {\n clearLongPress()\n setPressed(false)\n }\n\n handlersRef.current = {\n handlePressed,\n handleReleased,\n handleCancel,\n handleEnter: () => {\n setHovered(true)\n onHoverIn?.()\n },\n handleLeave: () => {\n setHovered(false)\n onHoverOut?.()\n },\n }\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const click = new Gtk.GestureClick()\n click.on(\"pressed\", (n: number, x: number, y: number) =>\n handlersRef.current.handlePressed(n, x, y),\n )\n click.on(\"released\", (n: number, x: number, y: number) =>\n handlersRef.current.handleReleased(n, x, y),\n )\n click.on(\"cancel\", () => handlersRef.current.handleCancel())\n widget.addController(click)\n\n const motion = new Gtk.EventControllerMotion()\n motion.on(\"enter\", () => handlersRef.current.handleEnter())\n motion.on(\"leave\", () => handlersRef.current.handleLeave())\n widget.addController(motion)\n\n return () => {\n widget.removeController(click)\n widget.removeController(motion)\n }\n }, [])\n\n return (\n <GtkBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {resolvedChildren}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type TouchableOpacityProps = Omit<PressableProps, \"style\"> & {\n style?: StyleProp\n activeOpacity?: number\n}\n\n// Classic RN touchable: dims itself while pressed via widget opacity.\nexport const TouchableOpacity = ({\n style,\n activeOpacity = 0.5,\n ...rest\n}: TouchableOpacityProps) => (\n <Pressable\n {...rest}\n style={({ pressed }) => [style, pressed && { opacity: activeOpacity }]}\n />\n)\n"]}
|
|
1
|
+
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../../src/components/pressable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState, type ReactNode } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport { Gtk, GtkBox } from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type PressableStateCallbackType = {\n pressed: boolean\n hovered: boolean\n}\n\nexport type PressEvent = { nativeEvent: { x: number; y: number } }\n\nexport type PressableProps = {\n style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp)\n children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode)\n onPress?: (event: PressEvent) => void\n onPressIn?: (event: PressEvent) => void\n onPressOut?: (event: PressEvent) => void\n onLongPress?: (event: PressEvent) => void\n onHoverIn?: () => void\n onHoverOut?: () => void\n disabled?: boolean\n delayLongPress?: number\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst pressEvent = (x: number, y: number): PressEvent => ({\n nativeEvent: { x, y },\n})\n\n// A View with a click gesture and hover tracking. State-dependent style and\n// children follow the RN Pressable function-prop contract.\nexport const Pressable = ({\n style,\n children,\n onPress,\n onPressIn,\n onPressOut,\n onLongPress,\n onHoverIn,\n onHoverOut,\n disabled = false,\n delayLongPress = 500,\n onLayout,\n testID,\n}: PressableProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [pressed, setPressed] = useState(false)\n const [hovered, setHovered] = useState(false)\n const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const longPressFired = useRef(false)\n\n const state: PressableStateCallbackType = { pressed, hovered }\n\n // Gestures are attached imperatively even though rc.2 has a declarative\n // `controllers` slot: the controllers are wired exactly once per widget and\n // the handlers read the latest props through a ref, so a re-render never\n // detaches and re-adds a gesture mid-press.\n const handlersRef = useRef({\n handlePressed: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleReleased: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleCancel: (): void => {},\n handleEnter: (): void => {},\n handleLeave: (): void => {},\n })\n const resolvedStyle = typeof style === \"function\" ? style(state) : style\n const resolvedChildren =\n typeof children === \"function\" ? children(state) : children\n\n const { host, node, cssClass } = useLayoutChild(widgetRef, {\n style: resolvedStyle,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n const clearLongPress = (): void => {\n if (longPressTimer.current !== null) {\n clearTimeout(longPressTimer.current)\n longPressTimer.current = null\n }\n }\n\n const handlePressed = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n longPressFired.current = false\n setPressed(true)\n onPressIn?.(pressEvent(x, y))\n if (onLongPress) {\n clearLongPress()\n longPressTimer.current = setTimeout(() => {\n longPressFired.current = true\n onLongPress(pressEvent(x, y))\n }, delayLongPress)\n }\n }\n\n const handleReleased = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n clearLongPress()\n setPressed(false)\n onPressOut?.(pressEvent(x, y))\n if (!longPressFired.current) {\n onPress?.(pressEvent(x, y))\n }\n }\n\n const handleCancel = (): void => {\n clearLongPress()\n setPressed(false)\n }\n\n handlersRef.current = {\n handlePressed,\n handleReleased,\n handleCancel,\n handleEnter: () => {\n setHovered(true)\n onHoverIn?.()\n },\n handleLeave: () => {\n setHovered(false)\n onHoverOut?.()\n },\n }\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const click = new Gtk.GestureClick()\n click.on(\"pressed\", (n: number, x: number, y: number) =>\n handlersRef.current.handlePressed(n, x, y),\n )\n click.on(\"released\", (n: number, x: number, y: number) =>\n handlersRef.current.handleReleased(n, x, y),\n )\n click.on(\"cancel\", () => handlersRef.current.handleCancel())\n widget.addController(click)\n\n const motion = new Gtk.EventControllerMotion()\n motion.on(\"enter\", () => handlersRef.current.handleEnter())\n motion.on(\"leave\", () => handlersRef.current.handleLeave())\n widget.addController(motion)\n\n return () => {\n widget.removeController(click)\n widget.removeController(motion)\n }\n }, [])\n\n return (\n <GtkBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {resolvedChildren}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type TouchableOpacityProps = Omit<PressableProps, \"style\"> & {\n style?: StyleProp\n activeOpacity?: number\n}\n\n// Classic RN touchable: dims itself while pressed via widget opacity.\nexport const TouchableOpacity = ({\n style,\n activeOpacity = 0.5,\n ...rest\n}: TouchableOpacityProps) => (\n <Pressable\n {...rest}\n style={({ pressed }) => [style, pressed && { opacity: activeOpacity }]}\n />\n)\n"]}
|
|
@@ -3,6 +3,15 @@ export type RootProps = {
|
|
|
3
3
|
width: number;
|
|
4
4
|
height: number;
|
|
5
5
|
followAllocation?: boolean;
|
|
6
|
+
expand?: boolean;
|
|
6
7
|
children?: ReactNode;
|
|
7
8
|
};
|
|
8
|
-
export declare const Root: ({ width, height, followAllocation, children, }: RootProps) => React.JSX.Element;
|
|
9
|
+
export declare const Root: ({ width, height, followAllocation, expand, children, }: RootProps) => React.JSX.Element;
|
|
10
|
+
export type NestedRootProps = {
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
export declare const NestedRoot: ({ children }: NestedRootProps) => React.JSX.Element;
|
|
14
|
+
export type IntrinsicRootProps = {
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
};
|
|
17
|
+
export declare const IntrinsicRoot: ({ children }: IntrinsicRootProps) => React.JSX.Element;
|
package/dist/components/root.js
CHANGED
|
@@ -9,7 +9,7 @@ import { useRnContainer } from "./use-layout-child";
|
|
|
9
9
|
// reports the engine viewport as its measure (via RnGtkxLayout) and is sized
|
|
10
10
|
// by its parent (window/harness); the owner keeps the engine viewport in sync
|
|
11
11
|
// (AppRegistry does it from window-size changes).
|
|
12
|
-
export const Root = ({ width, height, followAllocation = false, children, }) => {
|
|
12
|
+
export const Root = ({ width, height, followAllocation = false, expand = false, children, }) => {
|
|
13
13
|
const widgetRef = useRef(null);
|
|
14
14
|
// useState lazy init — see the note in use-layout-child.ts (React Compiler).
|
|
15
15
|
const [engine] = useState(() => new LayoutEngine({ width, height }));
|
|
@@ -54,6 +54,44 @@ export const Root = ({ width, height, followAllocation = false, children, }) =>
|
|
|
54
54
|
},
|
|
55
55
|
}
|
|
56
56
|
: undefined);
|
|
57
|
+
return (_jsx(GtkBox, { ref: widgetRef, hexpand: expand, vexpand: expand, children: _jsx(HostNodeContext.Provider, { value: host, children: children }) }));
|
|
58
|
+
};
|
|
59
|
+
// Nested layout root: a full Yoga engine mounted inside ANY GTK container
|
|
60
|
+
// slot — an Adw.NavigationPage, a toolbar view content area, a future
|
|
61
|
+
// gtk-components container. The slot's allocation is the layout viewport
|
|
62
|
+
// (viewport-following; the initial 0×0 viewport is replaced synchronously
|
|
63
|
+
// inside the first allocation pass, so nothing paints at zero). Attach is
|
|
64
|
+
// the JSX mount; detach is the unmount — the engine is disposed with it.
|
|
65
|
+
// Allocate passes of sibling/nested roots may overlap: the rect-store
|
|
66
|
+
// tracks pass depth, so deferred GTK queue jobs run only after the
|
|
67
|
+
// outermost pass ends.
|
|
68
|
+
export const NestedRoot = ({ children }) => (_jsx(Root, { width: 0, height: 0, followAllocation: true, expand: true, children: children }));
|
|
69
|
+
// Content-sized layout root: unlike NestedRoot (which fills a slot), the
|
|
70
|
+
// intrinsic root REPORTS its Yoga-computed content size to GTK — this is
|
|
71
|
+
// what lets RN content live in size-to-content chrome slots (HeaderBar
|
|
72
|
+
// start/end, sidebar rows). Measure runs a speculative uncommitted Yoga
|
|
73
|
+
// pass (height honors the width constraint); the allocation pass then
|
|
74
|
+
// recomputes at the real size and commits.
|
|
75
|
+
export const IntrinsicRoot = ({ children }) => {
|
|
76
|
+
const widgetRef = useRef(null);
|
|
77
|
+
const [engine] = useState(() => new LayoutEngine({ width: 0, height: 0 }));
|
|
78
|
+
useEffect(() => () => {
|
|
79
|
+
engine.dispose();
|
|
80
|
+
}, [engine]);
|
|
81
|
+
const host = useMemo(() => ({ engine, node: engine.root, widgetRef }), [engine]);
|
|
82
|
+
useRnContainer(widgetRef, engine.root, {
|
|
83
|
+
measure: (orientation, forSize) => engine.measureContent(orientation, forSize),
|
|
84
|
+
beforeAllocate: (allocatedWidth, allocatedHeight) => {
|
|
85
|
+
beginAllocatePass();
|
|
86
|
+
try {
|
|
87
|
+
engine.setViewport({ width: allocatedWidth, height: allocatedHeight });
|
|
88
|
+
engine.flushSync();
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
endAllocatePass();
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
57
95
|
return (_jsx(GtkBox, { ref: widgetRef, children: _jsx(HostNodeContext.Provider, { value: host, children: children }) }));
|
|
58
96
|
};
|
|
59
97
|
//# sourceMappingURL=root.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/components/root.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,SAAS,EACT,eAAe,EACf,OAAO,EACP,MAAM,EACN,QAAQ,GAET,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAY,MAAM,sBAAsB,CAAA;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/components/root.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { LayoutEngine } from \"../layout/index\"\nimport { GtkBox, queueResize, type Gtk } from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport { beginAllocatePass, endAllocatePass } from \"./rect-store\"\nimport { useRnContainer } from \"./use-layout-child\"\n\nexport type RootProps = {\n width: number\n height: number\n // Window mode: report a zero minimum (the window may shrink freely) and\n // adopt whatever the window actually allocates as the engine viewport —\n // the allocation IS the layout viewport, headerbar excluded by GTK itself.\n followAllocation?: boolean\n // Claim the parent slot: a zero-minimum box inside a plain GTK container\n // gets no space unless it expands (window children fill implicitly, other\n // containers honor the expand flags).\n expand?: boolean\n children?: ReactNode\n}\n\n// Layout root: one engine per window (or per test harness). The root GtkBox\n// reports the engine viewport as its measure (via RnGtkxLayout) and is sized\n// by its parent (window/harness); the owner keeps the engine viewport in sync\n// (AppRegistry does it from window-size changes).\nexport const Root = ({\n width,\n height,\n followAllocation = false,\n expand = false,\n children,\n}: RootProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [engine] = useState<LayoutEngine>(\n () => new LayoutEngine({ width, height }),\n )\n\n useLayoutEffect(() => {\n if (followAllocation) {\n return\n }\n engine.setViewport({ width, height })\n // The viewport is this widget's measure — invalidate the cached one.\n const widget = widgetRef.current\n if (widget) {\n queueResize(widget)\n }\n }, [engine, followAllocation, width, height])\n\n useEffect(\n () => () => {\n engine.dispose()\n },\n [engine],\n )\n\n const host = useMemo(\n () => ({ engine, node: engine.root, widgetRef }),\n [engine],\n )\n\n useRnContainer(\n widgetRef,\n engine.root,\n followAllocation\n ? {\n measure: () => 0,\n beforeAllocate: (allocatedWidth, allocatedHeight) => {\n const current = engine.root.getRect()\n if (\n current &&\n current.width === allocatedWidth &&\n current.height === allocatedHeight\n ) {\n return\n }\n // Synchronous reflow inside the allocation pass: commits fill the\n // rect store now; their GTK queue calls are deferred past the pass.\n beginAllocatePass()\n try {\n engine.setViewport({\n width: allocatedWidth,\n height: allocatedHeight,\n })\n engine.flushSync()\n } finally {\n endAllocatePass()\n }\n },\n }\n : undefined,\n )\n\n return (\n <GtkBox\n ref={widgetRef}\n hexpand={expand}\n vexpand={expand}\n >\n <HostNodeContext.Provider value={host}>\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type NestedRootProps = {\n children?: ReactNode\n}\n\n// Nested layout root: a full Yoga engine mounted inside ANY GTK container\n// slot — an Adw.NavigationPage, a toolbar view content area, a future\n// gtk-components container. The slot's allocation is the layout viewport\n// (viewport-following; the initial 0×0 viewport is replaced synchronously\n// inside the first allocation pass, so nothing paints at zero). Attach is\n// the JSX mount; detach is the unmount — the engine is disposed with it.\n// Allocate passes of sibling/nested roots may overlap: the rect-store\n// tracks pass depth, so deferred GTK queue jobs run only after the\n// outermost pass ends.\nexport const NestedRoot = ({ children }: NestedRootProps) => (\n <Root\n width={0}\n height={0}\n followAllocation\n expand\n >\n {children}\n </Root>\n)\n\nexport type IntrinsicRootProps = {\n children?: ReactNode\n}\n\n// Content-sized layout root: unlike NestedRoot (which fills a slot), the\n// intrinsic root REPORTS its Yoga-computed content size to GTK — this is\n// what lets RN content live in size-to-content chrome slots (HeaderBar\n// start/end, sidebar rows). Measure runs a speculative uncommitted Yoga\n// pass (height honors the width constraint); the allocation pass then\n// recomputes at the real size and commits.\nexport const IntrinsicRoot = ({ children }: IntrinsicRootProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [engine] = useState<LayoutEngine>(\n () => new LayoutEngine({ width: 0, height: 0 }),\n )\n useEffect(\n () => () => {\n engine.dispose()\n },\n [engine],\n )\n const host = useMemo(\n () => ({ engine, node: engine.root, widgetRef }),\n [engine],\n )\n useRnContainer(widgetRef, engine.root, {\n measure: (orientation, forSize) =>\n engine.measureContent(orientation, forSize),\n beforeAllocate: (allocatedWidth, allocatedHeight) => {\n beginAllocatePass()\n try {\n engine.setViewport({ width: allocatedWidth, height: allocatedHeight })\n engine.flushSync()\n } finally {\n endAllocatePass()\n }\n },\n })\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider value={host}>\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { Children, createContext, forwardRef, useContext, useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
|
|
3
3
|
import { splitStyle, StyleSheet } from "../style/index";
|
|
4
4
|
import { Gtk, GtkBox, GtkScrolledWindow, queueAllocate, queueResize, useSignal, } from "../gtkx/bridge/index";
|
|
5
|
+
import { ensurePerfReporter, perfAddTime, perfCount, perfEnabled, perfFrameTick, perfNow, } from "../perf";
|
|
5
6
|
import { HostNodeContext } from "./host-node";
|
|
6
7
|
import { deferDuringAllocate, setStoredOffset } from "./rect-store";
|
|
7
8
|
import { useLayoutChild, useRnContainer, } from "./use-layout-child";
|
|
@@ -254,7 +255,17 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
254
255
|
? (scrolled?.getHadjustment() ?? null)
|
|
255
256
|
: (scrolled?.getVadjustment() ?? null);
|
|
256
257
|
const onAdjustment = () => {
|
|
258
|
+
if (!perfEnabled) {
|
|
259
|
+
emitScroll();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// Perf: full wall time of one scroll tick's JS work — the FFI reads in
|
|
263
|
+
// emitScroll plus the app/VirtualizedList onScroll handler (windowing,
|
|
264
|
+
// viewability, endReached) — everything that blocks the GTK frame.
|
|
265
|
+
perfCount("scroll.events");
|
|
266
|
+
const start = perfNow();
|
|
257
267
|
emitScroll();
|
|
268
|
+
perfAddTime("scroll.js", perfNow() - start);
|
|
258
269
|
};
|
|
259
270
|
// Content-size reports dedupe on the engine rect: "changed" also fires
|
|
260
271
|
// for pure viewport (page-size) changes, which RN does not report.
|
|
@@ -288,12 +299,32 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
288
299
|
if (!widget) {
|
|
289
300
|
return;
|
|
290
301
|
}
|
|
302
|
+
const vadjustment = widget.getVadjustment();
|
|
303
|
+
let lastValue = vadjustment?.getValue() ?? 0;
|
|
304
|
+
// Frames to keep requeueing after the last observed move. The scrolled
|
|
305
|
+
// window's own kinetic tick may run AFTER ours, so this tick's read can
|
|
306
|
+
// be one frame stale: without the grace window the first frame of a
|
|
307
|
+
// deceleration (its value change lands after our read) would miss its
|
|
308
|
+
// correction. Kinetic deceleration changes the value every frame, so
|
|
309
|
+
// three frames of slack cover the handoff at both ends of the motion.
|
|
310
|
+
const graceFrames = 3;
|
|
311
|
+
let remaining = graceFrames;
|
|
291
312
|
const id = widget.addTickCallback(() => {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
313
|
+
perfCount("sticky.ticks");
|
|
314
|
+
const value = vadjustment?.getValue() ?? 0;
|
|
315
|
+
if (value !== lastValue) {
|
|
316
|
+
lastValue = value;
|
|
317
|
+
remaining = graceFrames;
|
|
318
|
+
}
|
|
319
|
+
else if (remaining > 0) {
|
|
320
|
+
remaining -= 1;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// At rest: the pin is already correct and nothing translated it,
|
|
324
|
+
// so an allocation pass per frame would be pure idle cost.
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
perfCount("sticky.queues");
|
|
297
328
|
const content = contentRef.current;
|
|
298
329
|
if (content) {
|
|
299
330
|
queueAllocate(content);
|
|
@@ -304,6 +335,28 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
304
335
|
widget.removeTickCallback(id);
|
|
305
336
|
};
|
|
306
337
|
}, [scrolled, hasSticky, horizontal]);
|
|
338
|
+
// Perf: observe the GTK frame clock through a tick callback — the delta
|
|
339
|
+
// between consecutive ticks is the real frame interval (late frames show
|
|
340
|
+
// up directly). Note: registering a tick callback keeps the frame clock
|
|
341
|
+
// running, so idle seconds show ~60 ticks too; the reporter separates
|
|
342
|
+
// load by the accompanying counters.
|
|
343
|
+
useLayoutEffect(() => {
|
|
344
|
+
if (!perfEnabled) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
ensurePerfReporter();
|
|
348
|
+
const widget = scrolled;
|
|
349
|
+
if (!widget) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const id = widget.addTickCallback(() => {
|
|
353
|
+
perfFrameTick(perfNow());
|
|
354
|
+
return true;
|
|
355
|
+
});
|
|
356
|
+
return () => {
|
|
357
|
+
widget.removeTickCallback(id);
|
|
358
|
+
};
|
|
359
|
+
}, [scrolled]);
|
|
307
360
|
useSignal(adjustment, "value-changed", onAdjustment);
|
|
308
361
|
// Both axes report into the same handler — RN's callback carries both
|
|
309
362
|
// dimensions no matter which one moved.
|