react-native-gtkx 0.2.0-alpha.1 → 0.2.0-alpha.2
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 +11 -2
- package/dist/adw/widgets.generated.d.ts +4 -4
- package/dist/adw/widgets.generated.js +51 -51
- package/dist/adw/widgets.generated.js.map +1 -1
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/index.d.ts +2 -0
- package/dist/animated/index.js +1 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/value-xy.d.ts +41 -0
- package/dist/animated/value-xy.js +90 -0
- package/dist/animated/value-xy.js.map +1 -0
- package/dist/animated/value.d.ts +9 -0
- package/dist/animated/value.js +30 -2
- package/dist/animated/value.js.map +1 -1
- package/dist/common/icon.d.ts +23 -0
- package/dist/common/icon.js +14 -0
- package/dist/common/icon.js.map +1 -0
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.js +2 -0
- package/dist/common/index.js.map +1 -1
- package/dist/common/list.d.ts +82 -0
- package/dist/common/list.js +166 -0
- package/dist/common/list.js.map +1 -0
- package/dist/common/widget.d.ts +1 -22
- package/dist/common/widget.js +75 -8
- package/dist/common/widget.js.map +1 -1
- package/dist/components/animated.d.ts +4 -2
- package/dist/components/animated.js +3 -1
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +26 -0
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/host-node.d.ts +7 -0
- package/dist/components/host-node.js +26 -1
- package/dist/components/host-node.js.map +1 -1
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +13 -0
- package/dist/components/measure.js +51 -0
- package/dist/components/measure.js.map +1 -0
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/press-event.d.ts +28 -0
- package/dist/components/press-event.js +46 -0
- package/dist/components/press-event.js.map +1 -0
- package/dist/components/pressable.d.ts +2 -6
- package/dist/components/pressable.js +14 -8
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/scroll-view.d.ts +4 -0
- package/dist/components/scroll-view.js +8 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/text-input.js +1 -1
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +25 -1
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +17 -14
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +9 -4
- package/dist/components/view.js +8 -2
- package/dist/components/view.js.map +1 -1
- package/dist/contracts.d.ts +19 -3
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +2 -1
- package/dist/gtk/index.js +8 -1
- package/dist/gtk/index.js.map +1 -1
- package/dist/gtk/widgets.generated.d.ts +14 -14
- package/dist/gtk/widgets.generated.js +101 -102
- package/dist/gtk/widgets.generated.js.map +1 -1
- package/dist/gtk/window-actions.d.ts +77 -0
- package/dist/gtk/window-actions.js +193 -0
- package/dist/gtk/window-actions.js.map +1 -0
- package/dist/gtkx/bridge/geometry.d.ts +15 -0
- package/dist/gtkx/bridge/geometry.js +38 -0
- package/dist/gtkx/bridge/geometry.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +2 -0
- package/dist/gtkx/bridge/index.js +7 -2
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
- package/dist/gtkx/bridge/slot-portal.js +17 -0
- package/dist/gtkx/bridge/slot-portal.js.map +1 -0
- package/dist/gtkx/bridge/svg-node.js +10 -54
- package/dist/gtkx/bridge/svg-node.js.map +1 -1
- package/dist/gtkx/bridge/use-signal.js +2 -2
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +99 -77
- package/dist/mcp/data/generated.js +107 -76
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +31 -1
- package/dist/navigation/sidebar.js +12 -2
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/responder/system.d.ts +27 -0
- package/dist/responder/system.js +184 -0
- package/dist/responder/system.js.map +1 -0
- package/dist/responder/touch-history.d.ts +10 -0
- package/dist/responder/touch-history.js +78 -0
- package/dist/responder/touch-history.js.map +1 -0
- package/dist/responder/types.d.ts +63 -0
- package/dist/responder/types.js +29 -0
- package/dist/responder/types.js.map +1 -0
- package/dist/responder/use-responder.d.ts +12 -0
- package/dist/responder/use-responder.js +98 -0
- package/dist/responder/use-responder.js.map +1 -0
- package/dist/runner/host-dev.js +3 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +5 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/gtkx-config-module.js +1 -1
- package/dist/sea/gtkx-config-module.js.map +1 -1
- package/dist/style/box-shadow.d.ts +7 -0
- package/dist/style/box-shadow.js +197 -0
- package/dist/style/box-shadow.js.map +1 -0
- package/dist/style/index.d.ts +2 -0
- package/dist/style/index.js +2 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.d.ts +5 -2
- package/dist/style/split-style.js +31 -3
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-decoration.d.ts +15 -0
- package/dist/style/text-decoration.js +26 -0
- package/dist/style/text-decoration.js.map +1 -0
- package/dist/style/visual-css.js +37 -1
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/gradient-geometry.js +2 -5
- package/dist/svg/gradient-geometry.js.map +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/vendor/react-native/pan-responder.d.ts +102 -0
- package/dist/vendor/react-native/pan-responder.js +196 -0
- package/dist/vendor/react-native/pan-responder.js.map +1 -0
- package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
- package/dist/vendor/react-native/touch-history-math.js +102 -0
- package/dist/vendor/react-native/touch-history-math.js.map +1 -0
- package/dist/vite/index.js +1 -1
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +1 -1
- package/dist/vitest/index.js.map +1 -1
- package/package.json +9 -9
package/dist/mcp/server.js
CHANGED
|
@@ -98,7 +98,7 @@ const searchDocsTool = () => defineTool({
|
|
|
98
98
|
name: "rn_gtkx_search_docs",
|
|
99
99
|
title: "Search react-native-gtkx docs",
|
|
100
100
|
description: "Free-text search over the docs (getting-started, the platform layer, " +
|
|
101
|
-
"gtkx rc.
|
|
101
|
+
"gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it " +
|
|
102
102
|
"for symptoms and known-issue questions that rn_gtkx_describe_component " +
|
|
103
103
|
"cannot answer by name (workarounds are keyed by mechanism, not by " +
|
|
104
104
|
"component).",
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["// The MCP server itself: three tools over stdio, backed entirely by the\n// generated data in ./data/generated.ts — no @gtkx/* import anywhere in\n// this module or its dependents, so this runs on any OS without GTK\n// installed (an agent reading a react-native-gtkx project on a Mac, for\n// instance). See docs/getting-started.md for how a consumer registers it.\nimport { createRequire } from \"node:module\"\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { listSurface, SURFACE_AREAS } from \"./list.js\"\nimport { formatEntity, resolveComponent } from \"./resolve.js\"\nimport { searchDocs } from \"./search.js\"\nimport {\n defineTool,\n registerTool,\n textContent,\n textError,\n type Tool,\n} from \"./tool.js\"\n\nconst require = createRequire(import.meta.url)\n// Package root is two levels up from dist/mcp/server.js (dist/mcp -> dist\n// -> package root). package.json ships in every npm tarball regardless of\n// the \"files\" allowlist, so this needs no extra packaging step.\nconst { version } = require(\"../../package.json\") as { version: string }\n\nconst SERVER_NAME = \"react-native-gtkx-mcp\"\nconst DEFAULT_SEARCH_LIMIT = 10\nconst MAX_SEARCH_LIMIT = 20\n\nconst listSurfaceShape = {\n area: z\n .enum(SURFACE_AREAS)\n .optional()\n .describe(\n \"Area to list. Omit for an overview with counts across all areas: \" +\n SURFACE_AREAS.join(\", \") +\n \".\",\n ),\n}\n\nconst describeComponentShape = {\n name: z\n .string()\n .describe(\n \"A component/API/widget name: a portable react-native export (e.g. \" +\n '\"FlatList\"), a react-native-gtkx/gtk or /adw widget with or ' +\n 'without its Gtk/Adw prefix (e.g. \"Popover\" resolves to ' +\n '\"GtkPopover\"), or a react-native-gtkx/common export (e.g. ' +\n '\"NavigationStack\"). Case-insensitive.',\n ),\n}\n\nconst searchDocsShape = {\n query: z\n .string()\n .describe(\n \"Free-text query — a symptom, a GTK term, an option name. Use this \" +\n \"when rn_gtkx_describe_component does not have a name to look up, \" +\n 'e.g. \"known issues\", \"chrome content\", \"windowSize\".',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(MAX_SEARCH_LIMIT)\n .optional()\n .describe(\n `Maximum number of results (default ${DEFAULT_SEARCH_LIMIT}, max ${MAX_SEARCH_LIMIT}).`,\n ),\n}\n\nconst listSurfaceTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_list_surface\",\n title: \"List the react-native-gtkx surface\",\n description:\n \"Browse the react-native-gtkx surface without knowing an exact name first: \" +\n \"portable react-native components/APIs, the react-native-gtkx/gtk and \" +\n \"/adw widget lists (wrapped vs. raw), and react-native-gtkx/common. \" +\n \"Without `area`, returns counts and where to look next.\",\n inputSchema: listSurfaceShape,\n handler: ({ area }) => textContent(listSurface(area)),\n })\n\nconst describeComponentTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_describe_component\",\n title: \"Describe a react-native-gtkx component\",\n description:\n \"Look up one component/widget/API by name: does it exist, which \" +\n \"subpath it is exported from, what GTK widget backs it (for \" +\n \"portable components), what differs from React Native, and whether \" +\n \"a gtk/adw widget is wrapped (takes `style`/`onLayout`) or raw. The \" +\n \"single most useful tool here — start with this before \" +\n \"rn_gtkx_search_docs.\",\n inputSchema: describeComponentShape,\n handler: ({ name }) => {\n const result = resolveComponent(name)\n if (result.status === \"resolved\") {\n const body = {\n matchedBy: result.matchedBy,\n ...formatEntity(result.entity),\n }\n return textContent(JSON.stringify(body, null, 2))\n }\n if (result.status === \"ambiguous\") {\n const lines = result.candidates\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `\"${result.query}\" matches ${result.candidates.length} names — call again with one of:\\n${lines}`,\n )\n }\n const lines = result.suggestions\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `No component named \"${result.query}\".` +\n (lines.length > 0\n ? ` Closest known names:\\n${lines}`\n : \" rn_gtkx_list_surface has the full index.\"),\n )\n },\n })\n\nconst searchDocsTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_search_docs\",\n title: \"Search react-native-gtkx docs\",\n description:\n \"Free-text search over the docs (getting-started, the platform layer, \" +\n \"gtkx rc.2 workarounds/quirks, navigation). The fallback tool: use it \" +\n \"for symptoms and known-issue questions that rn_gtkx_describe_component \" +\n \"cannot answer by name (workarounds are keyed by mechanism, not by \" +\n \"component).\",\n inputSchema: searchDocsShape,\n handler: ({ query, limit }) => {\n const results = searchDocs(query, limit ?? DEFAULT_SEARCH_LIMIT)\n if (results.length === 0) {\n return textContent(\n `No matches for \"${query}\". Try rn_gtkx_list_surface or rn_gtkx_describe_component instead.`,\n )\n }\n const body = results\n .map((r) => `## ${r.doc} — ${r.heading}\\n\\n${r.text}`)\n .join(\"\\n\\n---\\n\\n\")\n return textContent(body)\n },\n })\n\nconst buildTools = (): Tool[] => [\n listSurfaceTool(),\n describeComponentTool(),\n searchDocsTool(),\n]\n\nconst createMcpServer = (): McpServer => {\n const server = new McpServer({ name: SERVER_NAME, version })\n for (const tool of buildTools()) {\n registerTool(server, tool)\n }\n return server\n}\n\nconst main = async (): Promise<void> => {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nexport { buildTools, createMcpServer, main, SERVER_NAME }\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["// The MCP server itself: three tools over stdio, backed entirely by the\n// generated data in ./data/generated.ts — no @gtkx/* import anywhere in\n// this module or its dependents, so this runs on any OS without GTK\n// installed (an agent reading a react-native-gtkx project on a Mac, for\n// instance). See docs/getting-started.md for how a consumer registers it.\nimport { createRequire } from \"node:module\"\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { listSurface, SURFACE_AREAS } from \"./list.js\"\nimport { formatEntity, resolveComponent } from \"./resolve.js\"\nimport { searchDocs } from \"./search.js\"\nimport {\n defineTool,\n registerTool,\n textContent,\n textError,\n type Tool,\n} from \"./tool.js\"\n\nconst require = createRequire(import.meta.url)\n// Package root is two levels up from dist/mcp/server.js (dist/mcp -> dist\n// -> package root). package.json ships in every npm tarball regardless of\n// the \"files\" allowlist, so this needs no extra packaging step.\nconst { version } = require(\"../../package.json\") as { version: string }\n\nconst SERVER_NAME = \"react-native-gtkx-mcp\"\nconst DEFAULT_SEARCH_LIMIT = 10\nconst MAX_SEARCH_LIMIT = 20\n\nconst listSurfaceShape = {\n area: z\n .enum(SURFACE_AREAS)\n .optional()\n .describe(\n \"Area to list. Omit for an overview with counts across all areas: \" +\n SURFACE_AREAS.join(\", \") +\n \".\",\n ),\n}\n\nconst describeComponentShape = {\n name: z\n .string()\n .describe(\n \"A component/API/widget name: a portable react-native export (e.g. \" +\n '\"FlatList\"), a react-native-gtkx/gtk or /adw widget with or ' +\n 'without its Gtk/Adw prefix (e.g. \"Popover\" resolves to ' +\n '\"GtkPopover\"), or a react-native-gtkx/common export (e.g. ' +\n '\"NavigationStack\"). Case-insensitive.',\n ),\n}\n\nconst searchDocsShape = {\n query: z\n .string()\n .describe(\n \"Free-text query — a symptom, a GTK term, an option name. Use this \" +\n \"when rn_gtkx_describe_component does not have a name to look up, \" +\n 'e.g. \"known issues\", \"chrome content\", \"windowSize\".',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(MAX_SEARCH_LIMIT)\n .optional()\n .describe(\n `Maximum number of results (default ${DEFAULT_SEARCH_LIMIT}, max ${MAX_SEARCH_LIMIT}).`,\n ),\n}\n\nconst listSurfaceTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_list_surface\",\n title: \"List the react-native-gtkx surface\",\n description:\n \"Browse the react-native-gtkx surface without knowing an exact name first: \" +\n \"portable react-native components/APIs, the react-native-gtkx/gtk and \" +\n \"/adw widget lists (wrapped vs. raw), and react-native-gtkx/common. \" +\n \"Without `area`, returns counts and where to look next.\",\n inputSchema: listSurfaceShape,\n handler: ({ area }) => textContent(listSurface(area)),\n })\n\nconst describeComponentTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_describe_component\",\n title: \"Describe a react-native-gtkx component\",\n description:\n \"Look up one component/widget/API by name: does it exist, which \" +\n \"subpath it is exported from, what GTK widget backs it (for \" +\n \"portable components), what differs from React Native, and whether \" +\n \"a gtk/adw widget is wrapped (takes `style`/`onLayout`) or raw. The \" +\n \"single most useful tool here — start with this before \" +\n \"rn_gtkx_search_docs.\",\n inputSchema: describeComponentShape,\n handler: ({ name }) => {\n const result = resolveComponent(name)\n if (result.status === \"resolved\") {\n const body = {\n matchedBy: result.matchedBy,\n ...formatEntity(result.entity),\n }\n return textContent(JSON.stringify(body, null, 2))\n }\n if (result.status === \"ambiguous\") {\n const lines = result.candidates\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `\"${result.query}\" matches ${result.candidates.length} names — call again with one of:\\n${lines}`,\n )\n }\n const lines = result.suggestions\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `No component named \"${result.query}\".` +\n (lines.length > 0\n ? ` Closest known names:\\n${lines}`\n : \" rn_gtkx_list_surface has the full index.\"),\n )\n },\n })\n\nconst searchDocsTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_search_docs\",\n title: \"Search react-native-gtkx docs\",\n description:\n \"Free-text search over the docs (getting-started, the platform layer, \" +\n \"gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it \" +\n \"for symptoms and known-issue questions that rn_gtkx_describe_component \" +\n \"cannot answer by name (workarounds are keyed by mechanism, not by \" +\n \"component).\",\n inputSchema: searchDocsShape,\n handler: ({ query, limit }) => {\n const results = searchDocs(query, limit ?? DEFAULT_SEARCH_LIMIT)\n if (results.length === 0) {\n return textContent(\n `No matches for \"${query}\". Try rn_gtkx_list_surface or rn_gtkx_describe_component instead.`,\n )\n }\n const body = results\n .map((r) => `## ${r.doc} — ${r.heading}\\n\\n${r.text}`)\n .join(\"\\n\\n---\\n\\n\")\n return textContent(body)\n },\n })\n\nconst buildTools = (): Tool[] => [\n listSurfaceTool(),\n describeComponentTool(),\n searchDocsTool(),\n]\n\nconst createMcpServer = (): McpServer => {\n const server = new McpServer({ name: SERVER_NAME, version })\n for (const tool of buildTools()) {\n registerTool(server, tool)\n }\n return server\n}\n\nconst main = async (): Promise<void> => {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nexport { buildTools, createMcpServer, main, SERVER_NAME }\n"]}
|
|
@@ -124,6 +124,36 @@ type SidebarNavigatorProps = {
|
|
|
124
124
|
screenOptions?: SidebarNavigationOptions;
|
|
125
125
|
/** Title of the sidebar pane's AdwHeaderBar. */
|
|
126
126
|
sidebarTitle?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Content packed at the START of the SIDEBAR pane's AdwHeaderBar — the
|
|
129
|
+
* "new item" action a GNOME sidebar puts next to its title, a search
|
|
130
|
+
* toggle, anything the pane's chrome needs.
|
|
131
|
+
*
|
|
132
|
+
* The content header's `headerLeft`/`headerRight`/`headerTitle` are
|
|
133
|
+
* screen OPTIONS because that header changes with the focused screen.
|
|
134
|
+
* The sidebar's does not: one pane, shared by every screen, so its
|
|
135
|
+
* chrome belongs to the navigator — the same level `sidebarTitle` and
|
|
136
|
+
* `sidebarContent` already sit at. Hence the `sidebar` prefix on
|
|
137
|
+
* otherwise identical names.
|
|
138
|
+
*
|
|
139
|
+
* Mounted through the same `HeaderSlotContent` root the content header
|
|
140
|
+
* uses, so a slot of React Native content lays out as a horizontal,
|
|
141
|
+
* content-hugging cluster flush with natively packed buttons.
|
|
142
|
+
*/
|
|
143
|
+
sidebarHeaderLeft?: () => ReactNode;
|
|
144
|
+
/** Content packed at the END of the sidebar AdwHeaderBar — a primary
|
|
145
|
+
* menu, say. Same contract as {@link sidebarHeaderLeft}. */
|
|
146
|
+
sidebarHeaderRight?: () => ReactNode;
|
|
147
|
+
/**
|
|
148
|
+
* Replaces the sidebar AdwHeaderBar's title WIDGET — a search entry, a
|
|
149
|
+
* switcher, anything the plain `sidebarTitle` string cannot express.
|
|
150
|
+
*
|
|
151
|
+
* `sidebarTitle` sets the pane's `AdwNavigationPage` title, which the
|
|
152
|
+
* HeaderBar renders by default; this overrides what is drawn there,
|
|
153
|
+
* exactly as a screen's `headerTitle` overrides its page `title`. Left
|
|
154
|
+
* unset, `sidebarTitle` shows as before.
|
|
155
|
+
*/
|
|
156
|
+
sidebarHeaderTitle?: () => ReactNode;
|
|
127
157
|
/** Buttons packed at the end of the content AdwHeaderBar. */
|
|
128
158
|
headerButtons?: HeaderButton[];
|
|
129
159
|
/**
|
|
@@ -180,7 +210,7 @@ type SidebarNavigatorProps = {
|
|
|
180
210
|
sidebarContent?: (props: SidebarContentProps) => ReactNode;
|
|
181
211
|
children: ReactNode;
|
|
182
212
|
};
|
|
183
|
-
declare const SidebarNavigator: ({ initialRouteName, screenOptions, sidebarTitle, headerButtons, collapseWidth, minWidth, minHeight, sidebarContent, children, }: SidebarNavigatorProps) => React.JSX.Element;
|
|
213
|
+
declare const SidebarNavigator: ({ initialRouteName, screenOptions, sidebarTitle, sidebarHeaderLeft, sidebarHeaderRight, sidebarHeaderTitle, headerButtons, collapseWidth, minWidth, minHeight, sidebarContent, children, }: SidebarNavigatorProps) => React.JSX.Element;
|
|
184
214
|
export type SidebarNavigationHelpers<ParamList extends ParamListBase = ParamListBase, RouteName extends keyof ParamList = keyof ParamList> = NavigationProp<ParamList, RouteName, TabNavigationState<ParamList>, SidebarNavigationOptions, SidebarNavigationEventMap>;
|
|
185
215
|
export type SidebarScreenProps<ParamList extends ParamListBase = ParamListBase, RouteName extends keyof ParamList = keyof ParamList> = {
|
|
186
216
|
route: RouteProp<ParamList, RouteName>;
|
|
@@ -72,7 +72,7 @@ const listDot = (color) => css `
|
|
|
72
72
|
// minimum at all" (Adwaita's own, which clips) is never the behavior.
|
|
73
73
|
const DEFAULT_MIN_WIDTH = 360;
|
|
74
74
|
const DEFAULT_MIN_HEIGHT = 294;
|
|
75
|
-
const SidebarNavigator = ({ initialRouteName, screenOptions, sidebarTitle = "Sidebar", headerButtons, collapseWidth, minWidth = DEFAULT_MIN_WIDTH, minHeight = DEFAULT_MIN_HEIGHT, sidebarContent, children, }) => {
|
|
75
|
+
const SidebarNavigator = ({ initialRouteName, screenOptions, sidebarTitle = "Sidebar", sidebarHeaderLeft, sidebarHeaderRight, sidebarHeaderTitle, headerButtons, collapseWidth, minWidth = DEFAULT_MIN_WIDTH, minHeight = DEFAULT_MIN_HEIGHT, sidebarContent, children, }) => {
|
|
76
76
|
const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder(TabRouter, {
|
|
77
77
|
initialRouteName,
|
|
78
78
|
screenOptions,
|
|
@@ -225,7 +225,17 @@ const SidebarNavigator = ({ initialRouteName, screenOptions, sidebarTitle = "Sid
|
|
|
225
225
|
// Adwaita's own defaults for a sidebar of rows. Set explicitly
|
|
226
226
|
// because they are what keeps the sidebar's width off the window's
|
|
227
227
|
// minimum now that the scrolled window no longer propagates it.
|
|
228
|
-
minSidebarWidth: 180, maxSidebarWidth: 280, onNotifyShowContent: handleShowContentChanged, sidebar: _jsx(AdwNavigationPage, { title: sidebarTitle, tag: "sidebar", children: _jsx(AdwToolbarView, { topBar:
|
|
228
|
+
minSidebarWidth: 180, maxSidebarWidth: 280, onNotifyShowContent: handleShowContentChanged, sidebar: _jsx(AdwNavigationPage, { title: sidebarTitle, tag: "sidebar", children: _jsx(AdwToolbarView, { topBar:
|
|
229
|
+
// The sidebar pane's own chrome, mounted exactly the way the
|
|
230
|
+
// content HeaderBar below mounts its slots. It used to be a
|
|
231
|
+
// bare `<AdwHeaderBar />` with nothing an app could reach:
|
|
232
|
+
// `examples/tasks-nav` wanted its "New List" button here
|
|
233
|
+
// (where GNOME — and the gtkx tutorial this app family is
|
|
234
|
+
// modelled on — puts a sidebar's add action) and had to move
|
|
235
|
+
// it onto the CONTENT header instead, next to that header's
|
|
236
|
+
// own "New Task" +, leaving the user with two identical plus
|
|
237
|
+
// buttons and no way to tell them apart.
|
|
238
|
+
_jsx(AdwHeaderBar, { titleWidget: sidebarHeaderTitle ? (_jsx(HeaderSlotContent, { children: sidebarHeaderTitle() })) : undefined, start: sidebarHeaderLeft ? (_jsx(HeaderSlotContent, { children: sidebarHeaderLeft() })) : undefined, end: sidebarHeaderRight ? (_jsx(HeaderSlotContent, { children: sidebarHeaderRight() })) : undefined }), children: sidebarContent ? (
|
|
229
239
|
// The whole pane is the app's, mounted the way a page body
|
|
230
240
|
// is: a layout root that FILLS the pane, so React Native
|
|
231
241
|
// content — the common case — behaves as it does anywhere
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.js","sourceRoot":"","sources":["../../src/navigation/sidebar.tsx"],"names":[],"mappings":"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","sourcesContent":["// createSidebarNavigator — the desktop equivalent of a drawer navigator on\n// Adw.NavigationSplitView: a persistent native sidebar (GtkListBox with the\n// Adwaita navigation-sidebar styling) selects between parallel screens.\n// TabRouter fits these semantics (one focused screen out of a set, no\n// stack); the name follows the desktop mental model rather than \"drawer\" —\n// nothing slides over content on this platform.\n//\n// Selection sync mirrors the stack navigator's protocol: the native\n// row-selected signal dispatches jumpTo only when it disagrees with state,\n// and an effect re-selects the row when state changes programmatically —\n// the guard on both sides breaks the echo loop.\n//\n// Collapsed-pane sync is the same two-way protocol, one property lower:\n// `AdwNavigationSplitView.showContent` decides which pane is visible while\n// collapsed, the same role `NavigationStack`'s tag stack plays for the\n// stack navigator.\n// - state → widget: any route becoming active — a row click OR a\n// programmatic navigate()/jumpTo() — reveals content. Re-activating the\n// ALREADY-active row (state does not change) still needs to reveal\n// content too, which is why this rides BOTH the state.index effect and\n// row-activated (GTK's row-selected does not refire without an actual\n// selection change).\n// - widget → state: the split view's own back affordance (back button,\n// Escape, back gesture) flips `showContent` to false with no\n// react-navigation involvement at all — the counterpart to the stack\n// navigator's native pop. Unlike a stack pop, nothing is removed from\n// TabRouter's state (the same route stays focused, only the pane\n// changed), so there is nothing to dispatch; this is purely observed and\n// re-emitted as `SidebarNavigationEventMap`'s `sidebarShown`, for an app\n// that wants to react (see docs/research/navigation-extensibility.md).\n// - the echo guard: the state → widget side only ever WRITES `true`, and\n// the widget → state side only ever REACTS to `false` — two disjoint\n// values, so neither direction can mistake the other's write for a\n// widget-initiated back and re-trigger it. See docs/api.md for the\n// evidence on window-resize behavior (selection and pane both survive an\n// expand/re-collapse round trip unchanged, by the widget's own design,\n// not by any code here).\nimport {\n createNavigatorFactory,\n TabActions,\n TabRouter,\n useNavigationBuilder,\n type NavigationProp,\n type NavigatorTypeBagBase,\n type NavigatorTypeBagFor,\n type ParamListBase,\n type RouteProp,\n type TabNavigationState,\n type TypedNavigator,\n} from \"@react-navigation/native\"\nimport {\n useCallback,\n useEffect,\n useRef,\n type ComponentType,\n type ReactNode,\n} from \"react\"\nimport { getActiveChrome } from \"../components/app-registry\"\n// Widgets come from the public subpaths, the same ones an app would use —\n// the adapter has no privileged access to gtkx.\nimport {\n Adw,\n AdwActionRow,\n AdwBreakpoint,\n AdwBreakpointBin,\n AdwHeaderBar,\n AdwNavigationPage,\n AdwNavigationSplitView,\n AdwToolbarView,\n} from \"../adw\"\nimport {\n HeaderSlotContent,\n IntrinsicContent,\n SlotContent,\n WidgetContent,\n} from \"../common\"\nimport {\n css,\n GObject,\n Gtk,\n GtkBox,\n GtkButton,\n GtkImage,\n GtkLabel,\n GtkListBox,\n GtkListBoxRow,\n GtkScrolledWindow,\n} from \"../gtk\"\nimport { warnIgnoredOptions } from \"./option-warnings\"\n\nconst SIDEBAR_OPTION_KEYS: ReadonlySet<string> = new Set([\n \"title\",\n \"icon\",\n \"color\",\n \"count\",\n \"headerLeft\",\n \"headerRight\",\n \"headerTitle\",\n \"headerButtons\",\n \"contentLayout\",\n \"sidebarRow\",\n])\n\nexport type SidebarNavigationOptions = {\n /** Sidebar row and content AdwHeaderBar title; defaults to the route name. */\n title?: string\n /** Adwaita symbolic icon name for the sidebar row's prefix (e.g.\n * \"view-list-symbolic\"). Ignored when `color` is also set — a row shows\n * a colored dot OR an icon, never both (the same rule tasks-app's own\n * hand-rolled sidebar followed). */\n icon?: string\n /** A CSS color for a colored dot prefix, replacing `icon` on this row —\n * the way a user-created list is told apart from a smart view. */\n color?: string\n /** Badge shown as the row's suffix. Hidden when 0 or undefined — an\n * empty view shows no badge, not a \"0\". */\n count?: number\n /** RN content packed at the start of the CONTENT AdwHeaderBar (an\n * intrinsic-size root, same contract as the stack navigator's\n * `headerLeft`) — a back button for an in-place \"item open\" state, a\n * \"New\" action, anything that changes with THIS screen's selection.\n * Call `navigation.setOptions({ headerLeft: … })` from the screen to\n * change it as its own internal state changes — no stack involved. */\n headerLeft?: () => ReactNode\n /** RN content packed at the end of the content AdwHeaderBar, before\n * `headerButtons`. Same contract as `headerLeft`. */\n headerRight?: () => ReactNode\n /** Replaces the content AdwHeaderBar's title widget for this screen —\n * a filter toggle group, an editable title plus a star toggle,\n * anything a plain string `title` cannot express. Left unset, the\n * HeaderBar shows the page's own title automatically (unchanged\n * default behavior). */\n headerTitle?: () => ReactNode\n /** How this screen's body is mounted into the content page.\n *\n * - `\"react-native\"` (default): the body is a React Native tree, so it\n * gets a Yoga layout root that fills the pane — `<View style={{ flex: 1 }}>`\n * and friends behave exactly as they do anywhere else.\n * - `\"widget\"`: the body IS a GTK widget tree (a `GtkScrolledWindow`\n * around a `.boxed-list` `GtkListBox`, say) and is packed into the page\n * directly, with no layout root in between. GTK's own sizing —\n * `vexpand`, `AdwClamp`, a list's natural height — then works normally.\n * Under the default a widget tree collapses instead: every widget\n * becomes a single Yoga LEAF measured for its own natural size, so a\n * scrolled window reports the ~1px it can shrink to and the pane comes\n * up empty. `examples/tasks-nav` is built this way.\n *\n * Mixing is per SCREEN, not per subtree: a `\"widget\"` screen that wants\n * React Native content somewhere inside it wraps that part in\n * `SlotContent` (or `IntrinsicContent`) itself. */\n contentLayout?: \"react-native\" | \"widget\"\n /** Renders this screen's sidebar row yourself, instead of letting\n * `title`/`icon`/`color`/`count` compose one.\n *\n * Those four are a convenience, not the ceiling. They compose an\n * `AdwActionRow`, which brings Adwaita's own row metrics with it — so\n * an app wanting a different shape, a different density, or simply a\n * row height of its own had nothing to reach for, and every app paid\n * for the richest case whether it used it or not. This is that reach:\n * return anything a `GtkListBoxRow` can hold — React Native content,\n * GTK widgets, a differently-configured Adwaita row.\n *\n * The navigator still owns row BEHAVIOUR — selection, click →\n * `jumpTo`, keeping the list in step with navigation state, the\n * collapsed reveal — so a custom row cannot fall out of sync with the\n * router. Only what is drawn changes. */\n sidebarRow?: () => ReactNode\n /** Overrides the navigator-level `headerButtons` prop for this screen\n * specifically (replaces it entirely when set, same as the stack\n * navigator's per-screen option override). */\n headerButtons?: HeaderButton[]\n}\n\n// A one-off CSS class for a colored dot prefix — the same mechanism\n// tasks-app's own sidebar used (react-native-gtkx/gtk's `css` tag, kept\n// private here since it is a rendering detail of this navigator, not a\n// public primitive).\nconst listDot = (color: string): string => css`\n min-width: 12px;\n min-height: 12px;\n border-radius: 9999px;\n background: ${color};\n`\n\n// A declarative AdwHeaderBar button: the RN-facing API stays GTK-free — the\n// navigator renders the native button itself. `icon` is an Adwaita symbolic\n// icon name (e.g. \"weather-clear-night-symbolic\").\nexport type HeaderButton = {\n id: string\n icon: string\n tooltip?: string\n onPress: () => void\n}\n\n// Matches the shape of StackNavigationEventMap (src/navigation/index.tsx):\n// a real react-navigation event, not a bespoke navigator prop, so it is\n// consumed the standard way (`navigation.addListener` / `options.listeners`)\n// with no second protocol for an app to learn.\nexport type SidebarNavigationEventMap = {\n /**\n * The split view's own back affordance (back button, Escape, the back\n * gesture) hid the content pane while collapsed, returning to the\n * sidebar — `AdwNavigationSplitView`'s `showContent` going false, the\n * counterpart to the stack navigator's native pop. Unlike a stack pop,\n * TabRouter's state does NOT change: nothing was removed, the same route\n * stays focused, only the pane did. Fired on the currently active route,\n * so an app that wants to react — e.g. resetting an in-screen \"item\n * open\" state that only makes sense while content is visible, the way\n * `examples/tasks-nav`'s `ContentScreen` does — can listen without\n * polling the split view itself.\n *\n * Never fired when `collapseWidth` is unset (uncollapsed behavior is\n * unchanged), and never fired for content being REVEALED — that\n * direction already shows up as an ordinary state change (the newly\n * focused route re-rendering), so it needs no event of its own. See\n * docs/research/navigation-extensibility.md for why no event exists for\n * the forward direction.\n */\n sidebarShown: { data: undefined }\n}\n\ntype SidebarDescriptor = {\n options: SidebarNavigationOptions\n render: () => ReactNode\n}\n\n/** What a custom sidebar needs to be able to navigate — the routing\n * surface the composed list uses internally, handed over so a replacement\n * is a real sidebar and not a decoration that cannot select anything. */\nexport type SidebarContentProps = {\n /** Every screen, in declaration order, with its resolved options. */\n routes: {\n key: string\n name: string\n options: SidebarNavigationOptions\n /** Resolved title: `options.title`, falling back to the route name. */\n title: string\n /** Whether this route is the one currently showing in the content pane. */\n focused: boolean\n }[]\n /** Index of the focused route in `routes`. */\n focusedIndex: number\n /** Focus a route by name — the same dispatch a sidebar row click makes,\n * including revealing the content pane when collapsed. */\n jumpTo: (name: string) => void\n}\n\ntype SidebarNavigatorProps = {\n initialRouteName?: string\n screenOptions?: SidebarNavigationOptions\n /** Title of the sidebar pane's AdwHeaderBar. */\n sidebarTitle?: string\n /** Buttons packed at the end of the content AdwHeaderBar. */\n headerButtons?: HeaderButton[]\n /**\n * Width (sp) below which the split view collapses to the sidebar or the\n * content pane alone, driven by a native `Adw.Breakpoint` — NOT a\n * `useWindowDimensions` check (see docs/platform-layer.md, \"Two ways to\n * react to size\"). The property flip happens inside GTK's own allocation\n * pass, with no React render for the resize itself. Unset by default: no\n * `AdwBreakpointBin` is mounted at all, so existing consumers\n * (`examples/gallery`) see no behavior change.\n */\n collapseWidth?: number\n /**\n * The narrowest width (px) this navigator's UI supports, applied to the\n * `AdwBreakpointBin` `collapseWidth` mounts. Ignored when `collapseWidth`\n * is unset, since no bin exists then.\n *\n * Adwaita cannot measure a breakpoint bin: what it contains changes with\n * the breakpoints, so the bin reports a minimum of ZERO and warns\n * (\"AdwBreakpointBin does not have a minimum size, set the\n * 'width-request' and 'height-request' properties to specify it\"). With\n * no minimum the window can be dragged narrower than the pane inside it\n * can render, and Adwaita then over-allocates and CLIPS the pane rather\n * than adapting it — visible as a task list running off the right edge\n * and, in the journal, as \"AdwNavigationSplitView exceeds\n * AdwBreakpointBin width: requested 469 px, 360 px available\".\n *\n * The default is GNOME's own adaptive floor. An app whose content\n * HeaderBar needs more than that (a segmented control as `headerTitle`\n * costs ~110px on its own) must say so — measure the pane, don't guess:\n * the number is the width below which its own chrome stops fitting.\n */\n minWidth?: number\n /** Height counterpart of {@link minWidth}, same reasoning. */\n minHeight?: number\n /**\n * Replaces the ENTIRE sidebar pane's body — for a sidebar that needs\n * sections, a search field, a footer, or anything a flat list of rows\n * cannot express.\n *\n * The rung below this is `sidebarRow` (a screen option), which keeps the\n * navigator's list and replaces one row; reach for that first. This one\n * hands over the whole pane, so it also hands over the routing: use the\n * {@link SidebarContentProps} passed in rather than trying to dispatch\n * yourself, and selection stays consistent with navigation state.\n *\n * The pane's AdwHeaderBar (and `sidebarTitle`) still belong to the\n * navigator — this is the body under it, not the chrome.\n *\n * Mounted as React Native content (a layout root filling the pane). A\n * sidebar built from GTK widgets wraps its own tree in `WidgetContent`,\n * the same escape hatch `contentLayout: \"widget\"` is for a screen body.\n */\n sidebarContent?: (props: SidebarContentProps) => ReactNode\n children: ReactNode\n}\n\n// GNOME's adaptive floor — the size every GNOME app is expected to keep\n// working at, and the default a breakpoint bin gets here so that \"no\n// minimum at all\" (Adwaita's own, which clips) is never the behavior.\nconst DEFAULT_MIN_WIDTH = 360\nconst DEFAULT_MIN_HEIGHT = 294\n\nconst SidebarNavigator = ({\n initialRouteName,\n screenOptions,\n sidebarTitle = \"Sidebar\",\n headerButtons,\n collapseWidth,\n minWidth = DEFAULT_MIN_WIDTH,\n minHeight = DEFAULT_MIN_HEIGHT,\n sidebarContent,\n children,\n}: SidebarNavigatorProps) => {\n const { state, descriptors, navigation, NavigationContent } =\n useNavigationBuilder<\n TabNavigationState<ParamListBase>,\n Record<string, unknown>,\n Record<string, () => void>,\n SidebarNavigationOptions,\n SidebarNavigationEventMap\n >(TabRouter, {\n initialRouteName,\n screenOptions,\n children,\n })\n\n const listRef = useRef<Gtk.ListBox | null>(null)\n const splitViewRef = useRef<Adw.NavigationSplitView | null>(null)\n const breakpointRef = useRef<Adw.Breakpoint | null>(null)\n const collapseRegisteredRef = useRef(false)\n\n // Registers the native setter exactly once: Breakpoint.addSetter wants a\n // real boxed GObject.Value (a bare JS boolean fails a G_IS_VALUE\n // assertion on the native side — see docs/platform-layer.md). Once\n // registered, GTK flips AdwNavigationSplitView's `collapsed` property\n // itself at the threshold, inside its own allocation pass — no React\n // state, no re-render for the resize itself.\n useEffect(() => {\n if (collapseWidth === undefined || collapseRegisteredRef.current) {\n return\n }\n const breakpoint = breakpointRef.current\n const splitView = splitViewRef.current\n if (!breakpoint || !splitView) {\n return\n }\n const collapsed = new GObject.Value()\n collapsed.init(GObject.typeFromName(\"gboolean\"))\n collapsed.setBoolean(true)\n breakpoint.addSetter(splitView, \"collapsed\", collapsed)\n collapseRegisteredRef.current = true\n }, [collapseWidth])\n\n useEffect(() => {\n for (const route of state.routes) {\n const descriptor = descriptors[route.key] as SidebarDescriptor | undefined\n if (descriptor) {\n warnIgnoredOptions(\n \"createSidebarNavigator\",\n descriptor.options,\n SIDEBAR_OPTION_KEYS,\n )\n }\n }\n }, [state, descriptors])\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n getActiveChrome() === \"system\"\n ) {\n console.warn(\n '[react-native-gtkx/navigation] the app runs with the default window chrome — the split view brings its own HeaderBars, so you will see a doubled titlebar. Pass chrome: \"content\" to AppRegistry.runApplication.',\n )\n }\n }, [])\n\n // Selecting a sidebar row while collapsed must reveal the content page —\n // AdwNavigationSplitView already defines the mini push/pop for that\n // (`showContent`), so this is a plain native property write through the\n // ref, not React state. Reads `getCollapsed()` live: when collapseWidth\n // is unset the split view never collapses, so this is always a no-op.\n const showContentIfCollapsed = (): void => {\n const splitView = splitViewRef.current\n if (splitView?.getCollapsed()) {\n splitView.setShowContent(true)\n }\n }\n\n // State → native selection (initial mount and programmatic navigation).\n useEffect(() => {\n const list = listRef.current\n if (!list) {\n return\n }\n const row = list.getRowAtIndex(state.index)\n if (row && list.getSelectedRow() !== row) {\n list.selectRow(row)\n }\n // Every route becoming active reveals content while collapsed, not\n // just a row CLICK — found empirically (see updates/001/progress.md):\n // a plain programmatic navigate()/jumpTo() (no row involved at all)\n // changed state and re-selected the row above, but nothing told the\n // split view to show it, leaving the user stranded on the sidebar\n // exactly like the reported bug, just without a click in the loop.\n // Re-clicking the row already at this index does NOT come through\n // here (state.index does not change), which is exactly why\n // onRowActivated below still needs its own call.\n showContentIfCollapsed()\n }, [state.index])\n\n // Unlike the stack navigator (see src/navigation/index.tsx), `state.routes`\n // here does NOT need slicing for React Navigation 8's preloaded-routes\n // change: TabRouter sits on SwitchRouter, whose `getInitialState` puts\n // every declared screen name into `routes` unconditionally, from the very\n // first render — that's how switch/tab-style routers have always worked,\n // v7 and v8 alike, since they show one of N statically known screens\n // rather than a dynamically growing stack. `preloadedRouteKeys` in v8 is\n // bookkeeping over those already-present routes (which of them have been\n // pre-warmed via `navigation.preload()`); it does not add entries to\n // `state.routes` that weren't already there. Rendering every route as a\n // sidebar row (below) is correct both before and after v8 — confirmed by\n // reading node_modules/@react-navigation/routers' SwitchRouter source,\n // see updates/001/progress.md.\n const active = state.routes[state.index]!\n const activeDescriptor = descriptors[active.key] as\n SidebarDescriptor | undefined\n const optionsOf = (routeKey: string): SidebarNavigationOptions =>\n (descriptors[routeKey] as SidebarDescriptor | undefined)?.options ?? {}\n const titleOf = (routeKey: string, fallback: string): string =>\n optionsOf(routeKey).title ?? fallback\n // The content HeaderBar's own shape — the thing tasks-app's README named\n // as a structural-sounding complaint (\"one content header shared by the\n // whole navigator\") and the PRD allowed finding a real gap. It is not\n // one: descriptor options already merge navigator-level `screenOptions`\n // with the active screen's own `options`, and `navigation.setOptions()`\n // (called from inside the screen, in an effect keyed on the screen's own\n // \"what am I showing\" state) re-resolves them on every call — this is\n // core react-navigation behavior, nothing built here. Reading\n // headerLeft/headerRight/headerTitle off the ACTIVE descriptor on every\n // render is the entire fix.\n const activeOptions = optionsOf(active.key)\n const activeButtons = activeOptions.headerButtons ?? headerButtons\n\n // Dispatch only — revealing the content pane while collapsed is already\n // the job of the state.index effect above, which fires for ANY route\n // becoming active, programmatic ones included. Doing it here too would\n // duplicate that, and reading splitViewRef while building the props\n // object below would be a ref access during render.\n const jumpTo = useCallback(\n (name: string) => {\n navigation.dispatch({ ...TabActions.jumpTo(name), target: state.key })\n },\n [navigation, state.key],\n )\n\n // The routing surface a custom sidebar gets (see SidebarContentProps).\n // jumpTo goes through the SAME dispatch + reveal a row click does, so a\n // replacement pane cannot end up selecting without showing, or showing\n // without selecting.\n const sidebarContentProps: SidebarContentProps = {\n routes: state.routes.map((route, index) => ({\n key: route.key,\n name: route.name,\n options: optionsOf(route.key),\n title: titleOf(route.key, route.name),\n focused: index === state.index,\n })),\n focusedIndex: state.index,\n jumpTo,\n }\n\n // Widget → state: AdwNavigationSplitView's `showContent` notifies on\n // EVERY change, including the ones showContentIfCollapsed above just\n // made (value: true) — filtered out here, since this code only ever\n // WRITES `true` itself, so a `false` can only originate from the split\n // view's own back affordance (back button, Escape, back gesture). No\n // react-navigation state changes as a result (see\n // SidebarNavigationEventMap's doc for why there is nothing TO change),\n // only an event an app may listen for. This asymmetry — forward writes\n // filtered by value on this side, backward reads never touching\n // state.index on the other — is what keeps the two directions from ever\n // triggering each other.\n const handleShowContentChanged = (value: boolean | null): void => {\n if (value !== false || collapseWidth === undefined) {\n return\n }\n navigation.emit({\n type: \"sidebarShown\",\n target: state.routes[state.index]!.key,\n })\n }\n\n const splitView = (\n <AdwNavigationSplitView\n ref={splitViewRef}\n // Adwaita's own defaults for a sidebar of rows. Set explicitly\n // because they are what keeps the sidebar's width off the window's\n // minimum now that the scrolled window no longer propagates it.\n minSidebarWidth={180}\n maxSidebarWidth={280}\n onNotifyShowContent={handleShowContentChanged}\n sidebar={\n <AdwNavigationPage\n title={sidebarTitle}\n tag=\"sidebar\"\n >\n <AdwToolbarView topBar={<AdwHeaderBar />}>\n {sidebarContent ? (\n // The whole pane is the app's, mounted the way a page body\n // is: a layout root that FILLS the pane, so React Native\n // content — the common case — behaves as it does anywhere\n // else. A sidebar built from GTK widgets instead wraps\n // itself in WidgetContent, the same escape hatch\n // contentLayout: \"widget\" is for a screen body.\n <SlotContent>{sidebarContent(sidebarContentProps)}</SlotContent>\n ) : (\n <>\n {/* The list must not dictate the window minimum: the sidebar\n scrolls when the window is shorter than its rows — the\n Adwaita sidebar pattern (and RN semantics: scrolling is\n explicit, never the window's).\n\n Deliberately NOT propagateNaturalWidth, which does the\n opposite on the other axis: it makes the scrolled window\n request the widest ROW's width, so one long title becomes a\n floor the whole window cannot be resized below (seen as\n \"AdwNavigationSplitView exceeds AdwBreakpointBin width:\n requested 469 px, 360 px available\" in the journal, and felt\n as a window that stops shrinking early). The split view sizes\n the sidebar itself — see minSidebarWidth/maxSidebarWidth\n below — which is the Adwaita answer and leaves long titles to\n ellipsize instead of pushing the window around. */}\n <GtkScrolledWindow hscrollbarPolicy={Gtk.PolicyType.NEVER}>\n <GtkListBox\n ref={listRef}\n cssClasses={[\"navigation-sidebar\"]}\n onRowSelected={(row) => {\n if (!row) {\n return\n }\n const route = state.routes[row.getIndex()]\n if (route && route.key !== active.key) {\n navigation.dispatch({\n ...TabActions.jumpTo(route.name),\n target: state.key,\n })\n }\n }}\n // row-selected does not refire for a re-click on the\n // ALREADY-selected row (GTK only emits it on a selection\n // CHANGE) — found while testing the collapsed back-button\n // path: without this, re-clicking the same row after the\n // native back button hid content left the user stranded\n // on the sidebar. row-activated fires on every click\n // regardless, so it carries only the showContent side —\n // the dispatch above already covers an actual selection\n // change, and calling it twice for one click would be a\n // harmless but pointless duplicate dispatch.\n //\n // \"fires on every click\" is only true for rows GTK\n // considers ACTIVATABLE: gtk_list_box_activate() gates\n // the emission on gtk_list_box_row_get_activatable(),\n // so every row rendered below sets it explicitly. See\n // the comments there.\n onRowActivated={showContentIfCollapsed}\n >\n {state.routes.map((route) => {\n const options = optionsOf(route.key)\n if (options.sidebarRow) {\n // A plain GtkListBoxRow, so the list keeps handing the\n // row its selection and activation; everything inside\n // is the app's. IntrinsicContent — the size-to-content\n // layout root — because a row is sized by what it\n // holds, and React Native content (the common case for\n // a custom row) needs a real root to render at all.\n return (\n <GtkListBoxRow\n key={route.key}\n // GtkListBoxRow already defaults this to true;\n // spelled out because the collapsed reveal\n // DEPENDS on it (see onRowActivated above), and\n // a row silently losing row-activated is exactly\n // the bug this navigator shipped with.\n activatable\n >\n <IntrinsicContent>\n {options.sidebarRow()}\n </IntrinsicContent>\n </GtkListBoxRow>\n )\n }\n // Nothing but a title: the compact row this navigator\n // used before rows gained an icon, a dot and a count\n // (GtkListBoxRow + GtkLabel, ~40px). AdwActionRow brings\n // Adwaita's own row metrics — around 2.5x the height —\n // which is right when there IS a prefix and a count to\n // lay out, and pure cost when there is not.\n // examples/gallery passes only titles and had been\n // paying it since.\n if (!options.icon && !options.color && !options.count) {\n return (\n <GtkListBoxRow\n key={route.key}\n activatable\n >\n <GtkLabel\n label={titleOf(route.key, route.name)}\n xalign={0}\n marginTop={8}\n marginBottom={8}\n marginStart={6}\n marginEnd={6}\n />\n </GtkListBoxRow>\n )\n }\n return (\n <AdwActionRow\n key={route.key}\n title={titleOf(route.key, route.name)}\n // AdwActionRow defaults GtkListBoxRow:activatable\n // to FALSE (verified, not assumed: a plain\n // GtkListBoxRow reports true in the same list,\n // this reports false), and GtkListBox only emits\n // row-activated for activatable rows. Without\n // this, clicking the row ALREADY selected did\n // nothing at all while collapsed: the state.index\n // effect cannot fire (state does not change) and\n // row-activated never reached the handler either,\n // so the focused section — the one a cold start\n // at a collapsed width lands on — was the single\n // section that could not be opened. Every row\n // must be openable, the already-selected one\n // included.\n activatable\n prefix={\n options.color ? (\n <GtkBox\n valign={Gtk.Align.CENTER}\n cssClasses={[listDot(options.color)]}\n accessibleRole={Gtk.AccessibleRole.PRESENTATION}\n />\n ) : options.icon ? (\n <GtkImage iconName={options.icon} />\n ) : undefined\n }\n suffix={\n options.count && options.count > 0 ? (\n <GtkLabel\n valign={Gtk.Align.CENTER}\n cssClasses={[\"dimmed\", \"numeric\"]}\n >\n {String(options.count)}\n </GtkLabel>\n ) : undefined\n }\n />\n )\n })}\n </GtkListBox>\n </GtkScrolledWindow>\n </>\n )}\n </AdwToolbarView>\n </AdwNavigationPage>\n }\n >\n <AdwNavigationPage\n title={titleOf(active.key, active.name)}\n tag=\"content\"\n >\n <AdwToolbarView\n topBar={\n <AdwHeaderBar\n titleWidget={\n activeOptions.headerTitle ? (\n <HeaderSlotContent>\n {activeOptions.headerTitle()}\n </HeaderSlotContent>\n ) : undefined\n }\n start={\n activeOptions.headerLeft ? (\n <HeaderSlotContent>\n {activeOptions.headerLeft()}\n </HeaderSlotContent>\n ) : undefined\n }\n end={[\n ...(activeOptions.headerRight\n ? [\n <HeaderSlotContent key=\"header-right\">\n {activeOptions.headerRight()}\n </HeaderSlotContent>,\n ]\n : []),\n ...(activeButtons?.map((button) => (\n <GtkButton\n key={button.id}\n iconName={button.icon}\n tooltipText={button.tooltip}\n onClicked={button.onPress}\n />\n )) ?? []),\n ]}\n />\n }\n >\n {/* Keyed by route: switching sections swaps the whole screen —\n a fresh root per section, the previous one disposes. */}\n {activeOptions.contentLayout === \"widget\" ? (\n <WidgetContent key={active.key}>\n {activeDescriptor?.render()}\n </WidgetContent>\n ) : (\n <SlotContent key={active.key}>\n {activeDescriptor?.render()}\n </SlotContent>\n )}\n </AdwToolbarView>\n </AdwNavigationPage>\n </AdwNavigationSplitView>\n )\n\n return (\n <NavigationContent>\n {collapseWidth === undefined ? (\n splitView\n ) : (\n // A breakpoint's setters may only target widgets INSIDE the bin\n // they're attached to, never the bin itself (Adwaita's own\n // restriction) — so the split view must be the bin's child.\n //\n // widthRequest/heightRequest: Adwaita's own contract for\n // AdwBreakpointBin — adding a breakpoint makes it report NO minimum\n // size, and its docs say these properties \"must always be set\" in\n // that case (otherwise it warns on every use: \"does not have a\n // minimum size, set the 'width-request' and 'height-request'\n // properties to specify it\").\n //\n // The value is load-bearing, not a formality: this bin is NOT laid\n // out by Yoga. Under `chrome: \"content\"` — the chrome this navigator\n // requires — it is the window's own child, so GTK's size negotiation\n // is what decides how narrow the window may go, and the bin's\n // reported minimum IS that floor. Left at zero, the window resizes\n // straight past what the pane inside can draw and Adwaita clips it\n // rather than adapting it: \"AdwNavigationSplitView exceeds\n // AdwBreakpointBin width: requested 469 px, 360 px available\", felt\n // as a task list running off the right edge. See minWidth's doc.\n //\n // Whatever the value, it must never be 0: the current @gtkx property\n // diffing treats 0 as \"unset\" for numeric props (falsy skip) and\n // never issues the native call, so a 0 would silently leave the bin\n // with no minimum at all.\n <AdwBreakpointBin\n widthRequest={minWidth}\n heightRequest={minHeight}\n breakpoints={\n <AdwBreakpoint\n ref={breakpointRef}\n condition={Adw.BreakpointCondition.newLength(\n Adw.BreakpointConditionLengthType.MAX_WIDTH,\n collapseWidth,\n Adw.LengthUnit.SP,\n )}\n />\n }\n >\n {splitView}\n </AdwBreakpointBin>\n )}\n </NavigationContent>\n )\n}\n\n// Mirrors src/navigation/index.tsx's StackNavigationHelpers: a screen that\n// reaches its navigation prop through `useNavigation()` rather than\n// `SidebarScreenProps` (e.g. one `component` shared across several routes,\n// as `examples/tasks-nav`'s `ContentScreen` is) still needs a typed handle\n// on `sidebarShown` — `useNavigation<SidebarNavigationHelpers>()` gives it\n// one without spelling out the full `NavigationProp` generic list.\nexport type SidebarNavigationHelpers<\n ParamList extends ParamListBase = ParamListBase,\n RouteName extends keyof ParamList = keyof ParamList,\n> = NavigationProp<\n ParamList,\n RouteName,\n TabNavigationState<ParamList>,\n SidebarNavigationOptions,\n SidebarNavigationEventMap\n>\n\nexport type SidebarScreenProps<\n ParamList extends ParamListBase = ParamListBase,\n RouteName extends keyof ParamList = keyof ParamList,\n> = {\n route: RouteProp<ParamList, RouteName>\n navigation: SidebarNavigationHelpers<ParamList, RouteName>\n}\n\nexport type SidebarScreenConfig<\n ParamList extends ParamListBase,\n RouteName extends keyof ParamList,\n> = {\n name: RouteName\n component: ComponentType<SidebarScreenProps<ParamList, RouteName>>\n options?: SidebarNavigationOptions\n initialParams?: Partial<ParamList[RouteName]>\n}\n\n// See src/navigation/index.tsx's \"typed factory\" comment for why this\n// replaces a manual cast: createNavigatorFactory is genuinely generic in\n// React Navigation 8, runtime is unchanged.\ninterface SidebarTypeBag extends NavigatorTypeBagBase {\n ParamList: ParamListBase\n State: TabNavigationState<ParamListBase>\n ScreenOptions: SidebarNavigationOptions\n EventMap: SidebarNavigationEventMap\n ActionHelpers: Record<string, () => void>\n Navigator: typeof SidebarNavigator\n}\n\nconst sidebarFactory = createNavigatorFactory<SidebarTypeBag>(SidebarNavigator)\n\n// See src/navigation/index.tsx's createStackNavigator for why the return\n// type is spelled out via TypedNavigator + NavigatorTypeBagFor rather than\n// `ReturnType<typeof sidebarFactory<ParamList>>`.\nexport type TypedSidebarNavigator<ParamList extends ParamListBase> =\n TypedNavigator<NavigatorTypeBagFor<SidebarTypeBag, ParamList>, undefined>\n\nexport const createSidebarNavigator = <\n ParamList extends ParamListBase = ParamListBase,\n>(): TypedSidebarNavigator<ParamList> => sidebarFactory<ParamList>()\n"]}
|
|
1
|
+
{"version":3,"file":"sidebar.js","sourceRoot":"","sources":["../../src/navigation/sidebar.tsx"],"names":[],"mappings":"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","sourcesContent":["// createSidebarNavigator — the desktop equivalent of a drawer navigator on\n// Adw.NavigationSplitView: a persistent native sidebar (GtkListBox with the\n// Adwaita navigation-sidebar styling) selects between parallel screens.\n// TabRouter fits these semantics (one focused screen out of a set, no\n// stack); the name follows the desktop mental model rather than \"drawer\" —\n// nothing slides over content on this platform.\n//\n// Selection sync mirrors the stack navigator's protocol: the native\n// row-selected signal dispatches jumpTo only when it disagrees with state,\n// and an effect re-selects the row when state changes programmatically —\n// the guard on both sides breaks the echo loop.\n//\n// Collapsed-pane sync is the same two-way protocol, one property lower:\n// `AdwNavigationSplitView.showContent` decides which pane is visible while\n// collapsed, the same role `NavigationStack`'s tag stack plays for the\n// stack navigator.\n// - state → widget: any route becoming active — a row click OR a\n// programmatic navigate()/jumpTo() — reveals content. Re-activating the\n// ALREADY-active row (state does not change) still needs to reveal\n// content too, which is why this rides BOTH the state.index effect and\n// row-activated (GTK's row-selected does not refire without an actual\n// selection change).\n// - widget → state: the split view's own back affordance (back button,\n// Escape, back gesture) flips `showContent` to false with no\n// react-navigation involvement at all — the counterpart to the stack\n// navigator's native pop. Unlike a stack pop, nothing is removed from\n// TabRouter's state (the same route stays focused, only the pane\n// changed), so there is nothing to dispatch; this is purely observed and\n// re-emitted as `SidebarNavigationEventMap`'s `sidebarShown`, for an app\n// that wants to react (see docs/research/navigation-extensibility.md).\n// - the echo guard: the state → widget side only ever WRITES `true`, and\n// the widget → state side only ever REACTS to `false` — two disjoint\n// values, so neither direction can mistake the other's write for a\n// widget-initiated back and re-trigger it. See docs/api.md for the\n// evidence on window-resize behavior (selection and pane both survive an\n// expand/re-collapse round trip unchanged, by the widget's own design,\n// not by any code here).\nimport {\n createNavigatorFactory,\n TabActions,\n TabRouter,\n useNavigationBuilder,\n type NavigationProp,\n type NavigatorTypeBagBase,\n type NavigatorTypeBagFor,\n type ParamListBase,\n type RouteProp,\n type TabNavigationState,\n type TypedNavigator,\n} from \"@react-navigation/native\"\nimport {\n useCallback,\n useEffect,\n useRef,\n type ComponentType,\n type ReactNode,\n} from \"react\"\nimport { getActiveChrome } from \"../components/app-registry\"\n// Widgets come from the public subpaths, the same ones an app would use —\n// the adapter has no privileged access to gtkx.\nimport {\n Adw,\n AdwActionRow,\n AdwBreakpoint,\n AdwBreakpointBin,\n AdwHeaderBar,\n AdwNavigationPage,\n AdwNavigationSplitView,\n AdwToolbarView,\n} from \"../adw\"\nimport {\n HeaderSlotContent,\n IntrinsicContent,\n SlotContent,\n WidgetContent,\n} from \"../common\"\nimport {\n css,\n GObject,\n Gtk,\n GtkBox,\n GtkButton,\n GtkImage,\n GtkLabel,\n GtkListBox,\n GtkListBoxRow,\n GtkScrolledWindow,\n} from \"../gtk\"\nimport { warnIgnoredOptions } from \"./option-warnings\"\n\nconst SIDEBAR_OPTION_KEYS: ReadonlySet<string> = new Set([\n \"title\",\n \"icon\",\n \"color\",\n \"count\",\n \"headerLeft\",\n \"headerRight\",\n \"headerTitle\",\n \"headerButtons\",\n \"contentLayout\",\n \"sidebarRow\",\n])\n\nexport type SidebarNavigationOptions = {\n /** Sidebar row and content AdwHeaderBar title; defaults to the route name. */\n title?: string\n /** Adwaita symbolic icon name for the sidebar row's prefix (e.g.\n * \"view-list-symbolic\"). Ignored when `color` is also set — a row shows\n * a colored dot OR an icon, never both (the same rule tasks-app's own\n * hand-rolled sidebar followed). */\n icon?: string\n /** A CSS color for a colored dot prefix, replacing `icon` on this row —\n * the way a user-created list is told apart from a smart view. */\n color?: string\n /** Badge shown as the row's suffix. Hidden when 0 or undefined — an\n * empty view shows no badge, not a \"0\". */\n count?: number\n /** RN content packed at the start of the CONTENT AdwHeaderBar (an\n * intrinsic-size root, same contract as the stack navigator's\n * `headerLeft`) — a back button for an in-place \"item open\" state, a\n * \"New\" action, anything that changes with THIS screen's selection.\n * Call `navigation.setOptions({ headerLeft: … })` from the screen to\n * change it as its own internal state changes — no stack involved. */\n headerLeft?: () => ReactNode\n /** RN content packed at the end of the content AdwHeaderBar, before\n * `headerButtons`. Same contract as `headerLeft`. */\n headerRight?: () => ReactNode\n /** Replaces the content AdwHeaderBar's title widget for this screen —\n * a filter toggle group, an editable title plus a star toggle,\n * anything a plain string `title` cannot express. Left unset, the\n * HeaderBar shows the page's own title automatically (unchanged\n * default behavior). */\n headerTitle?: () => ReactNode\n /** How this screen's body is mounted into the content page.\n *\n * - `\"react-native\"` (default): the body is a React Native tree, so it\n * gets a Yoga layout root that fills the pane — `<View style={{ flex: 1 }}>`\n * and friends behave exactly as they do anywhere else.\n * - `\"widget\"`: the body IS a GTK widget tree (a `GtkScrolledWindow`\n * around a `.boxed-list` `GtkListBox`, say) and is packed into the page\n * directly, with no layout root in between. GTK's own sizing —\n * `vexpand`, `AdwClamp`, a list's natural height — then works normally.\n * Under the default a widget tree collapses instead: every widget\n * becomes a single Yoga LEAF measured for its own natural size, so a\n * scrolled window reports the ~1px it can shrink to and the pane comes\n * up empty. `examples/tasks-nav` is built this way.\n *\n * Mixing is per SCREEN, not per subtree: a `\"widget\"` screen that wants\n * React Native content somewhere inside it wraps that part in\n * `SlotContent` (or `IntrinsicContent`) itself. */\n contentLayout?: \"react-native\" | \"widget\"\n /** Renders this screen's sidebar row yourself, instead of letting\n * `title`/`icon`/`color`/`count` compose one.\n *\n * Those four are a convenience, not the ceiling. They compose an\n * `AdwActionRow`, which brings Adwaita's own row metrics with it — so\n * an app wanting a different shape, a different density, or simply a\n * row height of its own had nothing to reach for, and every app paid\n * for the richest case whether it used it or not. This is that reach:\n * return anything a `GtkListBoxRow` can hold — React Native content,\n * GTK widgets, a differently-configured Adwaita row.\n *\n * The navigator still owns row BEHAVIOUR — selection, click →\n * `jumpTo`, keeping the list in step with navigation state, the\n * collapsed reveal — so a custom row cannot fall out of sync with the\n * router. Only what is drawn changes. */\n sidebarRow?: () => ReactNode\n /** Overrides the navigator-level `headerButtons` prop for this screen\n * specifically (replaces it entirely when set, same as the stack\n * navigator's per-screen option override). */\n headerButtons?: HeaderButton[]\n}\n\n// A one-off CSS class for a colored dot prefix — the same mechanism\n// tasks-app's own sidebar used (react-native-gtkx/gtk's `css` tag, kept\n// private here since it is a rendering detail of this navigator, not a\n// public primitive).\nconst listDot = (color: string): string => css`\n min-width: 12px;\n min-height: 12px;\n border-radius: 9999px;\n background: ${color};\n`\n\n// A declarative AdwHeaderBar button: the RN-facing API stays GTK-free — the\n// navigator renders the native button itself. `icon` is an Adwaita symbolic\n// icon name (e.g. \"weather-clear-night-symbolic\").\nexport type HeaderButton = {\n id: string\n icon: string\n tooltip?: string\n onPress: () => void\n}\n\n// Matches the shape of StackNavigationEventMap (src/navigation/index.tsx):\n// a real react-navigation event, not a bespoke navigator prop, so it is\n// consumed the standard way (`navigation.addListener` / `options.listeners`)\n// with no second protocol for an app to learn.\nexport type SidebarNavigationEventMap = {\n /**\n * The split view's own back affordance (back button, Escape, the back\n * gesture) hid the content pane while collapsed, returning to the\n * sidebar — `AdwNavigationSplitView`'s `showContent` going false, the\n * counterpart to the stack navigator's native pop. Unlike a stack pop,\n * TabRouter's state does NOT change: nothing was removed, the same route\n * stays focused, only the pane did. Fired on the currently active route,\n * so an app that wants to react — e.g. resetting an in-screen \"item\n * open\" state that only makes sense while content is visible, the way\n * `examples/tasks-nav`'s `ContentScreen` does — can listen without\n * polling the split view itself.\n *\n * Never fired when `collapseWidth` is unset (uncollapsed behavior is\n * unchanged), and never fired for content being REVEALED — that\n * direction already shows up as an ordinary state change (the newly\n * focused route re-rendering), so it needs no event of its own. See\n * docs/research/navigation-extensibility.md for why no event exists for\n * the forward direction.\n */\n sidebarShown: { data: undefined }\n}\n\ntype SidebarDescriptor = {\n options: SidebarNavigationOptions\n render: () => ReactNode\n}\n\n/** What a custom sidebar needs to be able to navigate — the routing\n * surface the composed list uses internally, handed over so a replacement\n * is a real sidebar and not a decoration that cannot select anything. */\nexport type SidebarContentProps = {\n /** Every screen, in declaration order, with its resolved options. */\n routes: {\n key: string\n name: string\n options: SidebarNavigationOptions\n /** Resolved title: `options.title`, falling back to the route name. */\n title: string\n /** Whether this route is the one currently showing in the content pane. */\n focused: boolean\n }[]\n /** Index of the focused route in `routes`. */\n focusedIndex: number\n /** Focus a route by name — the same dispatch a sidebar row click makes,\n * including revealing the content pane when collapsed. */\n jumpTo: (name: string) => void\n}\n\ntype SidebarNavigatorProps = {\n initialRouteName?: string\n screenOptions?: SidebarNavigationOptions\n /** Title of the sidebar pane's AdwHeaderBar. */\n sidebarTitle?: string\n /**\n * Content packed at the START of the SIDEBAR pane's AdwHeaderBar — the\n * \"new item\" action a GNOME sidebar puts next to its title, a search\n * toggle, anything the pane's chrome needs.\n *\n * The content header's `headerLeft`/`headerRight`/`headerTitle` are\n * screen OPTIONS because that header changes with the focused screen.\n * The sidebar's does not: one pane, shared by every screen, so its\n * chrome belongs to the navigator — the same level `sidebarTitle` and\n * `sidebarContent` already sit at. Hence the `sidebar` prefix on\n * otherwise identical names.\n *\n * Mounted through the same `HeaderSlotContent` root the content header\n * uses, so a slot of React Native content lays out as a horizontal,\n * content-hugging cluster flush with natively packed buttons.\n */\n sidebarHeaderLeft?: () => ReactNode\n /** Content packed at the END of the sidebar AdwHeaderBar — a primary\n * menu, say. Same contract as {@link sidebarHeaderLeft}. */\n sidebarHeaderRight?: () => ReactNode\n /**\n * Replaces the sidebar AdwHeaderBar's title WIDGET — a search entry, a\n * switcher, anything the plain `sidebarTitle` string cannot express.\n *\n * `sidebarTitle` sets the pane's `AdwNavigationPage` title, which the\n * HeaderBar renders by default; this overrides what is drawn there,\n * exactly as a screen's `headerTitle` overrides its page `title`. Left\n * unset, `sidebarTitle` shows as before.\n */\n sidebarHeaderTitle?: () => ReactNode\n /** Buttons packed at the end of the content AdwHeaderBar. */\n headerButtons?: HeaderButton[]\n /**\n * Width (sp) below which the split view collapses to the sidebar or the\n * content pane alone, driven by a native `Adw.Breakpoint` — NOT a\n * `useWindowDimensions` check (see docs/platform-layer.md, \"Two ways to\n * react to size\"). The property flip happens inside GTK's own allocation\n * pass, with no React render for the resize itself. Unset by default: no\n * `AdwBreakpointBin` is mounted at all, so existing consumers\n * (`examples/gallery`) see no behavior change.\n */\n collapseWidth?: number\n /**\n * The narrowest width (px) this navigator's UI supports, applied to the\n * `AdwBreakpointBin` `collapseWidth` mounts. Ignored when `collapseWidth`\n * is unset, since no bin exists then.\n *\n * Adwaita cannot measure a breakpoint bin: what it contains changes with\n * the breakpoints, so the bin reports a minimum of ZERO and warns\n * (\"AdwBreakpointBin does not have a minimum size, set the\n * 'width-request' and 'height-request' properties to specify it\"). With\n * no minimum the window can be dragged narrower than the pane inside it\n * can render, and Adwaita then over-allocates and CLIPS the pane rather\n * than adapting it — visible as a task list running off the right edge\n * and, in the journal, as \"AdwNavigationSplitView exceeds\n * AdwBreakpointBin width: requested 469 px, 360 px available\".\n *\n * The default is GNOME's own adaptive floor. An app whose content\n * HeaderBar needs more than that (a segmented control as `headerTitle`\n * costs ~110px on its own) must say so — measure the pane, don't guess:\n * the number is the width below which its own chrome stops fitting.\n */\n minWidth?: number\n /** Height counterpart of {@link minWidth}, same reasoning. */\n minHeight?: number\n /**\n * Replaces the ENTIRE sidebar pane's body — for a sidebar that needs\n * sections, a search field, a footer, or anything a flat list of rows\n * cannot express.\n *\n * The rung below this is `sidebarRow` (a screen option), which keeps the\n * navigator's list and replaces one row; reach for that first. This one\n * hands over the whole pane, so it also hands over the routing: use the\n * {@link SidebarContentProps} passed in rather than trying to dispatch\n * yourself, and selection stays consistent with navigation state.\n *\n * The pane's AdwHeaderBar (and `sidebarTitle`) still belong to the\n * navigator — this is the body under it, not the chrome.\n *\n * Mounted as React Native content (a layout root filling the pane). A\n * sidebar built from GTK widgets wraps its own tree in `WidgetContent`,\n * the same escape hatch `contentLayout: \"widget\"` is for a screen body.\n */\n sidebarContent?: (props: SidebarContentProps) => ReactNode\n children: ReactNode\n}\n\n// GNOME's adaptive floor — the size every GNOME app is expected to keep\n// working at, and the default a breakpoint bin gets here so that \"no\n// minimum at all\" (Adwaita's own, which clips) is never the behavior.\nconst DEFAULT_MIN_WIDTH = 360\nconst DEFAULT_MIN_HEIGHT = 294\n\nconst SidebarNavigator = ({\n initialRouteName,\n screenOptions,\n sidebarTitle = \"Sidebar\",\n sidebarHeaderLeft,\n sidebarHeaderRight,\n sidebarHeaderTitle,\n headerButtons,\n collapseWidth,\n minWidth = DEFAULT_MIN_WIDTH,\n minHeight = DEFAULT_MIN_HEIGHT,\n sidebarContent,\n children,\n}: SidebarNavigatorProps) => {\n const { state, descriptors, navigation, NavigationContent } =\n useNavigationBuilder<\n TabNavigationState<ParamListBase>,\n Record<string, unknown>,\n Record<string, () => void>,\n SidebarNavigationOptions,\n SidebarNavigationEventMap\n >(TabRouter, {\n initialRouteName,\n screenOptions,\n children,\n })\n\n const listRef = useRef<Gtk.ListBox | null>(null)\n const splitViewRef = useRef<Adw.NavigationSplitView | null>(null)\n const breakpointRef = useRef<Adw.Breakpoint | null>(null)\n const collapseRegisteredRef = useRef(false)\n\n // Registers the native setter exactly once: Breakpoint.addSetter wants a\n // real boxed GObject.Value (a bare JS boolean fails a G_IS_VALUE\n // assertion on the native side — see docs/platform-layer.md). Once\n // registered, GTK flips AdwNavigationSplitView's `collapsed` property\n // itself at the threshold, inside its own allocation pass — no React\n // state, no re-render for the resize itself.\n useEffect(() => {\n if (collapseWidth === undefined || collapseRegisteredRef.current) {\n return\n }\n const breakpoint = breakpointRef.current\n const splitView = splitViewRef.current\n if (!breakpoint || !splitView) {\n return\n }\n const collapsed = new GObject.Value()\n collapsed.init(GObject.typeFromName(\"gboolean\"))\n collapsed.setBoolean(true)\n breakpoint.addSetter(splitView, \"collapsed\", collapsed)\n collapseRegisteredRef.current = true\n }, [collapseWidth])\n\n useEffect(() => {\n for (const route of state.routes) {\n const descriptor = descriptors[route.key] as SidebarDescriptor | undefined\n if (descriptor) {\n warnIgnoredOptions(\n \"createSidebarNavigator\",\n descriptor.options,\n SIDEBAR_OPTION_KEYS,\n )\n }\n }\n }, [state, descriptors])\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n getActiveChrome() === \"system\"\n ) {\n console.warn(\n '[react-native-gtkx/navigation] the app runs with the default window chrome — the split view brings its own HeaderBars, so you will see a doubled titlebar. Pass chrome: \"content\" to AppRegistry.runApplication.',\n )\n }\n }, [])\n\n // Selecting a sidebar row while collapsed must reveal the content page —\n // AdwNavigationSplitView already defines the mini push/pop for that\n // (`showContent`), so this is a plain native property write through the\n // ref, not React state. Reads `getCollapsed()` live: when collapseWidth\n // is unset the split view never collapses, so this is always a no-op.\n const showContentIfCollapsed = (): void => {\n const splitView = splitViewRef.current\n if (splitView?.getCollapsed()) {\n splitView.setShowContent(true)\n }\n }\n\n // State → native selection (initial mount and programmatic navigation).\n useEffect(() => {\n const list = listRef.current\n if (!list) {\n return\n }\n const row = list.getRowAtIndex(state.index)\n if (row && list.getSelectedRow() !== row) {\n list.selectRow(row)\n }\n // Every route becoming active reveals content while collapsed, not\n // just a row CLICK — found empirically (see updates/001/progress.md):\n // a plain programmatic navigate()/jumpTo() (no row involved at all)\n // changed state and re-selected the row above, but nothing told the\n // split view to show it, leaving the user stranded on the sidebar\n // exactly like the reported bug, just without a click in the loop.\n // Re-clicking the row already at this index does NOT come through\n // here (state.index does not change), which is exactly why\n // onRowActivated below still needs its own call.\n showContentIfCollapsed()\n }, [state.index])\n\n // Unlike the stack navigator (see src/navigation/index.tsx), `state.routes`\n // here does NOT need slicing for React Navigation 8's preloaded-routes\n // change: TabRouter sits on SwitchRouter, whose `getInitialState` puts\n // every declared screen name into `routes` unconditionally, from the very\n // first render — that's how switch/tab-style routers have always worked,\n // v7 and v8 alike, since they show one of N statically known screens\n // rather than a dynamically growing stack. `preloadedRouteKeys` in v8 is\n // bookkeeping over those already-present routes (which of them have been\n // pre-warmed via `navigation.preload()`); it does not add entries to\n // `state.routes` that weren't already there. Rendering every route as a\n // sidebar row (below) is correct both before and after v8 — confirmed by\n // reading node_modules/@react-navigation/routers' SwitchRouter source,\n // see updates/001/progress.md.\n const active = state.routes[state.index]!\n const activeDescriptor = descriptors[active.key] as\n SidebarDescriptor | undefined\n const optionsOf = (routeKey: string): SidebarNavigationOptions =>\n (descriptors[routeKey] as SidebarDescriptor | undefined)?.options ?? {}\n const titleOf = (routeKey: string, fallback: string): string =>\n optionsOf(routeKey).title ?? fallback\n // The content HeaderBar's own shape — the thing tasks-app's README named\n // as a structural-sounding complaint (\"one content header shared by the\n // whole navigator\") and the PRD allowed finding a real gap. It is not\n // one: descriptor options already merge navigator-level `screenOptions`\n // with the active screen's own `options`, and `navigation.setOptions()`\n // (called from inside the screen, in an effect keyed on the screen's own\n // \"what am I showing\" state) re-resolves them on every call — this is\n // core react-navigation behavior, nothing built here. Reading\n // headerLeft/headerRight/headerTitle off the ACTIVE descriptor on every\n // render is the entire fix.\n const activeOptions = optionsOf(active.key)\n const activeButtons = activeOptions.headerButtons ?? headerButtons\n\n // Dispatch only — revealing the content pane while collapsed is already\n // the job of the state.index effect above, which fires for ANY route\n // becoming active, programmatic ones included. Doing it here too would\n // duplicate that, and reading splitViewRef while building the props\n // object below would be a ref access during render.\n const jumpTo = useCallback(\n (name: string) => {\n navigation.dispatch({ ...TabActions.jumpTo(name), target: state.key })\n },\n [navigation, state.key],\n )\n\n // The routing surface a custom sidebar gets (see SidebarContentProps).\n // jumpTo goes through the SAME dispatch + reveal a row click does, so a\n // replacement pane cannot end up selecting without showing, or showing\n // without selecting.\n const sidebarContentProps: SidebarContentProps = {\n routes: state.routes.map((route, index) => ({\n key: route.key,\n name: route.name,\n options: optionsOf(route.key),\n title: titleOf(route.key, route.name),\n focused: index === state.index,\n })),\n focusedIndex: state.index,\n jumpTo,\n }\n\n // Widget → state: AdwNavigationSplitView's `showContent` notifies on\n // EVERY change, including the ones showContentIfCollapsed above just\n // made (value: true) — filtered out here, since this code only ever\n // WRITES `true` itself, so a `false` can only originate from the split\n // view's own back affordance (back button, Escape, back gesture). No\n // react-navigation state changes as a result (see\n // SidebarNavigationEventMap's doc for why there is nothing TO change),\n // only an event an app may listen for. This asymmetry — forward writes\n // filtered by value on this side, backward reads never touching\n // state.index on the other — is what keeps the two directions from ever\n // triggering each other.\n const handleShowContentChanged = (value: boolean | null): void => {\n if (value !== false || collapseWidth === undefined) {\n return\n }\n navigation.emit({\n type: \"sidebarShown\",\n target: state.routes[state.index]!.key,\n })\n }\n\n const splitView = (\n <AdwNavigationSplitView\n ref={splitViewRef}\n // Adwaita's own defaults for a sidebar of rows. Set explicitly\n // because they are what keeps the sidebar's width off the window's\n // minimum now that the scrolled window no longer propagates it.\n minSidebarWidth={180}\n maxSidebarWidth={280}\n onNotifyShowContent={handleShowContentChanged}\n sidebar={\n <AdwNavigationPage\n title={sidebarTitle}\n tag=\"sidebar\"\n >\n <AdwToolbarView\n topBar={\n // The sidebar pane's own chrome, mounted exactly the way the\n // content HeaderBar below mounts its slots. It used to be a\n // bare `<AdwHeaderBar />` with nothing an app could reach:\n // `examples/tasks-nav` wanted its \"New List\" button here\n // (where GNOME — and the gtkx tutorial this app family is\n // modelled on — puts a sidebar's add action) and had to move\n // it onto the CONTENT header instead, next to that header's\n // own \"New Task\" +, leaving the user with two identical plus\n // buttons and no way to tell them apart.\n <AdwHeaderBar\n titleWidget={\n sidebarHeaderTitle ? (\n <HeaderSlotContent>\n {sidebarHeaderTitle()}\n </HeaderSlotContent>\n ) : undefined\n }\n start={\n sidebarHeaderLeft ? (\n <HeaderSlotContent>{sidebarHeaderLeft()}</HeaderSlotContent>\n ) : undefined\n }\n end={\n sidebarHeaderRight ? (\n <HeaderSlotContent>\n {sidebarHeaderRight()}\n </HeaderSlotContent>\n ) : undefined\n }\n />\n }\n >\n {sidebarContent ? (\n // The whole pane is the app's, mounted the way a page body\n // is: a layout root that FILLS the pane, so React Native\n // content — the common case — behaves as it does anywhere\n // else. A sidebar built from GTK widgets instead wraps\n // itself in WidgetContent, the same escape hatch\n // contentLayout: \"widget\" is for a screen body.\n <SlotContent>{sidebarContent(sidebarContentProps)}</SlotContent>\n ) : (\n <>\n {/* The list must not dictate the window minimum: the sidebar\n scrolls when the window is shorter than its rows — the\n Adwaita sidebar pattern (and RN semantics: scrolling is\n explicit, never the window's).\n\n Deliberately NOT propagateNaturalWidth, which does the\n opposite on the other axis: it makes the scrolled window\n request the widest ROW's width, so one long title becomes a\n floor the whole window cannot be resized below (seen as\n \"AdwNavigationSplitView exceeds AdwBreakpointBin width:\n requested 469 px, 360 px available\" in the journal, and felt\n as a window that stops shrinking early). The split view sizes\n the sidebar itself — see minSidebarWidth/maxSidebarWidth\n below — which is the Adwaita answer and leaves long titles to\n ellipsize instead of pushing the window around. */}\n <GtkScrolledWindow hscrollbarPolicy={Gtk.PolicyType.NEVER}>\n <GtkListBox\n ref={listRef}\n cssClasses={[\"navigation-sidebar\"]}\n onRowSelected={(row) => {\n if (!row) {\n return\n }\n const route = state.routes[row.getIndex()]\n if (route && route.key !== active.key) {\n navigation.dispatch({\n ...TabActions.jumpTo(route.name),\n target: state.key,\n })\n }\n }}\n // row-selected does not refire for a re-click on the\n // ALREADY-selected row (GTK only emits it on a selection\n // CHANGE) — found while testing the collapsed back-button\n // path: without this, re-clicking the same row after the\n // native back button hid content left the user stranded\n // on the sidebar. row-activated fires on every click\n // regardless, so it carries only the showContent side —\n // the dispatch above already covers an actual selection\n // change, and calling it twice for one click would be a\n // harmless but pointless duplicate dispatch.\n //\n // \"fires on every click\" is only true for rows GTK\n // considers ACTIVATABLE: gtk_list_box_activate() gates\n // the emission on gtk_list_box_row_get_activatable(),\n // so every row rendered below sets it explicitly. See\n // the comments there.\n onRowActivated={showContentIfCollapsed}\n >\n {state.routes.map((route) => {\n const options = optionsOf(route.key)\n if (options.sidebarRow) {\n // A plain GtkListBoxRow, so the list keeps handing the\n // row its selection and activation; everything inside\n // is the app's. IntrinsicContent — the size-to-content\n // layout root — because a row is sized by what it\n // holds, and React Native content (the common case for\n // a custom row) needs a real root to render at all.\n return (\n <GtkListBoxRow\n key={route.key}\n // GtkListBoxRow already defaults this to true;\n // spelled out because the collapsed reveal\n // DEPENDS on it (see onRowActivated above), and\n // a row silently losing row-activated is exactly\n // the bug this navigator shipped with.\n activatable\n >\n <IntrinsicContent>\n {options.sidebarRow()}\n </IntrinsicContent>\n </GtkListBoxRow>\n )\n }\n // Nothing but a title: the compact row this navigator\n // used before rows gained an icon, a dot and a count\n // (GtkListBoxRow + GtkLabel, ~40px). AdwActionRow brings\n // Adwaita's own row metrics — around 2.5x the height —\n // which is right when there IS a prefix and a count to\n // lay out, and pure cost when there is not.\n // examples/gallery passes only titles and had been\n // paying it since.\n if (!options.icon && !options.color && !options.count) {\n return (\n <GtkListBoxRow\n key={route.key}\n activatable\n >\n <GtkLabel\n label={titleOf(route.key, route.name)}\n xalign={0}\n marginTop={8}\n marginBottom={8}\n marginStart={6}\n marginEnd={6}\n />\n </GtkListBoxRow>\n )\n }\n return (\n <AdwActionRow\n key={route.key}\n title={titleOf(route.key, route.name)}\n // AdwActionRow defaults GtkListBoxRow:activatable\n // to FALSE (verified, not assumed: a plain\n // GtkListBoxRow reports true in the same list,\n // this reports false), and GtkListBox only emits\n // row-activated for activatable rows. Without\n // this, clicking the row ALREADY selected did\n // nothing at all while collapsed: the state.index\n // effect cannot fire (state does not change) and\n // row-activated never reached the handler either,\n // so the focused section — the one a cold start\n // at a collapsed width lands on — was the single\n // section that could not be opened. Every row\n // must be openable, the already-selected one\n // included.\n activatable\n prefix={\n options.color ? (\n <GtkBox\n valign={Gtk.Align.CENTER}\n cssClasses={[listDot(options.color)]}\n accessibleRole={Gtk.AccessibleRole.PRESENTATION}\n />\n ) : options.icon ? (\n <GtkImage iconName={options.icon} />\n ) : undefined\n }\n suffix={\n options.count && options.count > 0 ? (\n <GtkLabel\n valign={Gtk.Align.CENTER}\n cssClasses={[\"dimmed\", \"numeric\"]}\n >\n {String(options.count)}\n </GtkLabel>\n ) : undefined\n }\n />\n )\n })}\n </GtkListBox>\n </GtkScrolledWindow>\n </>\n )}\n </AdwToolbarView>\n </AdwNavigationPage>\n }\n >\n <AdwNavigationPage\n title={titleOf(active.key, active.name)}\n tag=\"content\"\n >\n <AdwToolbarView\n topBar={\n <AdwHeaderBar\n titleWidget={\n activeOptions.headerTitle ? (\n <HeaderSlotContent>\n {activeOptions.headerTitle()}\n </HeaderSlotContent>\n ) : undefined\n }\n start={\n activeOptions.headerLeft ? (\n <HeaderSlotContent>\n {activeOptions.headerLeft()}\n </HeaderSlotContent>\n ) : undefined\n }\n end={[\n ...(activeOptions.headerRight\n ? [\n <HeaderSlotContent key=\"header-right\">\n {activeOptions.headerRight()}\n </HeaderSlotContent>,\n ]\n : []),\n ...(activeButtons?.map((button) => (\n <GtkButton\n key={button.id}\n iconName={button.icon}\n tooltipText={button.tooltip}\n onClicked={button.onPress}\n />\n )) ?? []),\n ]}\n />\n }\n >\n {/* Keyed by route: switching sections swaps the whole screen —\n a fresh root per section, the previous one disposes. */}\n {activeOptions.contentLayout === \"widget\" ? (\n <WidgetContent key={active.key}>\n {activeDescriptor?.render()}\n </WidgetContent>\n ) : (\n <SlotContent key={active.key}>\n {activeDescriptor?.render()}\n </SlotContent>\n )}\n </AdwToolbarView>\n </AdwNavigationPage>\n </AdwNavigationSplitView>\n )\n\n return (\n <NavigationContent>\n {collapseWidth === undefined ? (\n splitView\n ) : (\n // A breakpoint's setters may only target widgets INSIDE the bin\n // they're attached to, never the bin itself (Adwaita's own\n // restriction) — so the split view must be the bin's child.\n //\n // widthRequest/heightRequest: Adwaita's own contract for\n // AdwBreakpointBin — adding a breakpoint makes it report NO minimum\n // size, and its docs say these properties \"must always be set\" in\n // that case (otherwise it warns on every use: \"does not have a\n // minimum size, set the 'width-request' and 'height-request'\n // properties to specify it\").\n //\n // The value is load-bearing, not a formality: this bin is NOT laid\n // out by Yoga. Under `chrome: \"content\"` — the chrome this navigator\n // requires — it is the window's own child, so GTK's size negotiation\n // is what decides how narrow the window may go, and the bin's\n // reported minimum IS that floor. Left at zero, the window resizes\n // straight past what the pane inside can draw and Adwaita clips it\n // rather than adapting it: \"AdwNavigationSplitView exceeds\n // AdwBreakpointBin width: requested 469 px, 360 px available\", felt\n // as a task list running off the right edge. See minWidth's doc.\n //\n // Whatever the value, it must never be 0: the current @gtkx property\n // diffing treats 0 as \"unset\" for numeric props (falsy skip) and\n // never issues the native call, so a 0 would silently leave the bin\n // with no minimum at all.\n <AdwBreakpointBin\n widthRequest={minWidth}\n heightRequest={minHeight}\n breakpoints={\n <AdwBreakpoint\n ref={breakpointRef}\n condition={Adw.BreakpointCondition.newLength(\n Adw.BreakpointConditionLengthType.MAX_WIDTH,\n collapseWidth,\n Adw.LengthUnit.SP,\n )}\n />\n }\n >\n {splitView}\n </AdwBreakpointBin>\n )}\n </NavigationContent>\n )\n}\n\n// Mirrors src/navigation/index.tsx's StackNavigationHelpers: a screen that\n// reaches its navigation prop through `useNavigation()` rather than\n// `SidebarScreenProps` (e.g. one `component` shared across several routes,\n// as `examples/tasks-nav`'s `ContentScreen` is) still needs a typed handle\n// on `sidebarShown` — `useNavigation<SidebarNavigationHelpers>()` gives it\n// one without spelling out the full `NavigationProp` generic list.\nexport type SidebarNavigationHelpers<\n ParamList extends ParamListBase = ParamListBase,\n RouteName extends keyof ParamList = keyof ParamList,\n> = NavigationProp<\n ParamList,\n RouteName,\n TabNavigationState<ParamList>,\n SidebarNavigationOptions,\n SidebarNavigationEventMap\n>\n\nexport type SidebarScreenProps<\n ParamList extends ParamListBase = ParamListBase,\n RouteName extends keyof ParamList = keyof ParamList,\n> = {\n route: RouteProp<ParamList, RouteName>\n navigation: SidebarNavigationHelpers<ParamList, RouteName>\n}\n\nexport type SidebarScreenConfig<\n ParamList extends ParamListBase,\n RouteName extends keyof ParamList,\n> = {\n name: RouteName\n component: ComponentType<SidebarScreenProps<ParamList, RouteName>>\n options?: SidebarNavigationOptions\n initialParams?: Partial<ParamList[RouteName]>\n}\n\n// See src/navigation/index.tsx's \"typed factory\" comment for why this\n// replaces a manual cast: createNavigatorFactory is genuinely generic in\n// React Navigation 8, runtime is unchanged.\ninterface SidebarTypeBag extends NavigatorTypeBagBase {\n ParamList: ParamListBase\n State: TabNavigationState<ParamListBase>\n ScreenOptions: SidebarNavigationOptions\n EventMap: SidebarNavigationEventMap\n ActionHelpers: Record<string, () => void>\n Navigator: typeof SidebarNavigator\n}\n\nconst sidebarFactory = createNavigatorFactory<SidebarTypeBag>(SidebarNavigator)\n\n// See src/navigation/index.tsx's createStackNavigator for why the return\n// type is spelled out via TypedNavigator + NavigatorTypeBagFor rather than\n// `ReturnType<typeof sidebarFactory<ParamList>>`.\nexport type TypedSidebarNavigator<ParamList extends ParamListBase> =\n TypedNavigator<NavigatorTypeBagFor<SidebarTypeBag, ParamList>, undefined>\n\nexport const createSidebarNavigator = <\n ParamList extends ParamListBase = ParamListBase,\n>(): TypedSidebarNavigator<ParamList> => sidebarFactory<ParamList>()\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NativeTouch } from "../components/press-event";
|
|
2
|
+
import type { ResponderProps } from "./types";
|
|
3
|
+
/** An opaque platform handle. A Gtk.Widget in practice; never treated as one. */
|
|
4
|
+
export type ResponderHost = object;
|
|
5
|
+
export type ResponderSystemOptions = {
|
|
6
|
+
/** The platform's parent link. Returning null ends the path. */
|
|
7
|
+
parentOf: (host: ResponderHost) => ResponderHost | null;
|
|
8
|
+
/**
|
|
9
|
+
* Fired after a node is granted the responder — where the platform makes
|
|
10
|
+
* its GTK `CLAIMED` declaration. One-way by construction: GTK has no way
|
|
11
|
+
* to hand a claimed sequence back, so this is only ever called once JS has
|
|
12
|
+
* already decided (docs/research/gestures.md, "no voluntary release").
|
|
13
|
+
*/
|
|
14
|
+
onGrant?: (host: ResponderHost) => void;
|
|
15
|
+
/** Fired when the responder is released or terminated. */
|
|
16
|
+
onRelease?: (host: ResponderHost) => void;
|
|
17
|
+
};
|
|
18
|
+
export type ResponderSystem = {
|
|
19
|
+
register(host: ResponderHost, getProps: () => ResponderProps): () => void;
|
|
20
|
+
touchStart(host: ResponderHost, touch: NativeTouch): void;
|
|
21
|
+
touchMove(host: ResponderHost, touch: NativeTouch): void;
|
|
22
|
+
touchEnd(host: ResponderHost, touch: NativeTouch): void;
|
|
23
|
+
touchCancel(host: ResponderHost, touch: NativeTouch): void;
|
|
24
|
+
/** The node currently holding the responder, or null. */
|
|
25
|
+
getResponder(): ResponderHost | null;
|
|
26
|
+
};
|
|
27
|
+
export declare const createResponderSystem: (options: ResponderSystemOptions) => ResponderSystem;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { createTouchHistory, recordTouchEnd, recordTouchMove, recordTouchStart, refreshActiveTouches, } from "./touch-history";
|
|
2
|
+
export const createResponderSystem = (options) => {
|
|
3
|
+
const registry = new Map();
|
|
4
|
+
const history = createTouchHistory();
|
|
5
|
+
let responder = null;
|
|
6
|
+
let session = null;
|
|
7
|
+
/**
|
|
8
|
+
* The registered nodes from the toplevel down to `host`, root first — RN's
|
|
9
|
+
* capture order, reversed for bubble.
|
|
10
|
+
*
|
|
11
|
+
* The walk stops wherever `parentOf` stops, which on this platform is the
|
|
12
|
+
* layout Root: an RN tree here is an island inside a native GTK widget
|
|
13
|
+
* tree (NestedRoot/IntrinsicRoot mount engines into arbitrary GTK slots),
|
|
14
|
+
* so there is no single app-wide view hierarchy to traverse. Native
|
|
15
|
+
* widgets on the way up are simply not registered and take no part.
|
|
16
|
+
*/
|
|
17
|
+
const pathOf = (host) => {
|
|
18
|
+
const path = [];
|
|
19
|
+
let current = host;
|
|
20
|
+
while (current) {
|
|
21
|
+
if (registry.has(current)) {
|
|
22
|
+
path.unshift(current);
|
|
23
|
+
}
|
|
24
|
+
current = options.parentOf(current);
|
|
25
|
+
}
|
|
26
|
+
return path;
|
|
27
|
+
};
|
|
28
|
+
const propsOf = (host) => registry.get(host)?.getProps() ?? null;
|
|
29
|
+
const createEvent = (touch, stillDown) => ({
|
|
30
|
+
nativeEvent: {
|
|
31
|
+
...touch,
|
|
32
|
+
// Single-pointer: `touches` is the still-active set (empty once the
|
|
33
|
+
// pointer is up), `changedTouches` is always this one.
|
|
34
|
+
touches: stillDown ? [touch] : [],
|
|
35
|
+
changedTouches: [touch],
|
|
36
|
+
},
|
|
37
|
+
touchHistory: history,
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Runs a should-set handler over `path` in order, stopping at the first
|
|
41
|
+
* that returns true — RN's executeDispatchesInOrderStopAtTrue. Handlers
|
|
42
|
+
* before the winner all run, which PanResponder depends on: its capture
|
|
43
|
+
* handlers are where gestureState is initialised and where dx/dy are
|
|
44
|
+
* accumulated on move.
|
|
45
|
+
*/
|
|
46
|
+
const firstToClaim = (path, name, event) => {
|
|
47
|
+
for (const host of path) {
|
|
48
|
+
const handler = propsOf(host)?.[name];
|
|
49
|
+
if (handler?.(event) === true) {
|
|
50
|
+
return host;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
const notify = (host, name, event) => {
|
|
56
|
+
propsOf(host)?.[name]?.(event);
|
|
57
|
+
};
|
|
58
|
+
const dispatchTouch = (path, captureName, bubbleName, event) => {
|
|
59
|
+
for (const host of path) {
|
|
60
|
+
propsOf(host)?.[captureName]?.(event);
|
|
61
|
+
}
|
|
62
|
+
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
63
|
+
propsOf(path[i])?.[bubbleName]?.(event);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const grant = (host, event, fromStart) => {
|
|
67
|
+
responder = host;
|
|
68
|
+
notify(host, "onResponderGrant", event);
|
|
69
|
+
if (fromStart) {
|
|
70
|
+
notify(host, "onResponderStart", event);
|
|
71
|
+
}
|
|
72
|
+
options.onGrant?.(host);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Capture root-to-target, then bubble target-to-root — RN's two-phase
|
|
76
|
+
* negotiation, and the one place GTK's model and RN's agree, since GTK's
|
|
77
|
+
* CAPTURE/TARGET/BUBBLE phases run in exactly this order. We still do the
|
|
78
|
+
* walk ourselves so a single GTK event source can drive the whole path.
|
|
79
|
+
*/
|
|
80
|
+
const negotiate = (path, event, phase) => {
|
|
81
|
+
const capture = phase === "start"
|
|
82
|
+
? "onStartShouldSetResponderCapture"
|
|
83
|
+
: "onMoveShouldSetResponderCapture";
|
|
84
|
+
const bubble = phase === "start"
|
|
85
|
+
? "onStartShouldSetResponder"
|
|
86
|
+
: "onMoveShouldSetResponder";
|
|
87
|
+
const captured = firstToClaim(path, capture, event);
|
|
88
|
+
if (captured) {
|
|
89
|
+
return captured;
|
|
90
|
+
}
|
|
91
|
+
return firstToClaim([...path].reverse(), bubble, event);
|
|
92
|
+
};
|
|
93
|
+
const finish = (touch, terminated, touchProps) => {
|
|
94
|
+
const current = session;
|
|
95
|
+
if (!current) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
recordTouchEnd(history, touch);
|
|
99
|
+
refreshActiveTouches(history);
|
|
100
|
+
const event = createEvent(touch, false);
|
|
101
|
+
dispatchTouch(pathOf(current.host), touchProps[0], touchProps[1], event);
|
|
102
|
+
const holder = responder;
|
|
103
|
+
session = null;
|
|
104
|
+
responder = null;
|
|
105
|
+
if (holder) {
|
|
106
|
+
notify(holder, "onResponderEnd", event);
|
|
107
|
+
notify(holder, terminated ? "onResponderTerminate" : "onResponderRelease", event);
|
|
108
|
+
options.onRelease?.(holder);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
register(host, getProps) {
|
|
113
|
+
registry.set(host, { getProps });
|
|
114
|
+
return () => {
|
|
115
|
+
registry.delete(host);
|
|
116
|
+
// A responder that unmounts mid-gesture cannot be asked anything
|
|
117
|
+
// ever again; drop the lock rather than stranding it.
|
|
118
|
+
if (responder === host) {
|
|
119
|
+
responder = null;
|
|
120
|
+
}
|
|
121
|
+
if (session?.host === host) {
|
|
122
|
+
session = null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
touchStart(host, touch) {
|
|
127
|
+
// Already tracking this press: this is an ancestor's gesture seeing the
|
|
128
|
+
// same event through GTK's bubble chain.
|
|
129
|
+
if (session !== null) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
session = { host, identifier: touch.identifier };
|
|
133
|
+
recordTouchStart(history, touch);
|
|
134
|
+
refreshActiveTouches(history);
|
|
135
|
+
const path = pathOf(host);
|
|
136
|
+
const event = createEvent(touch, true);
|
|
137
|
+
dispatchTouch(path, "onTouchStartCapture", "onTouchStart", event);
|
|
138
|
+
if (responder !== null) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const winner = negotiate(path, event, "start");
|
|
142
|
+
if (winner) {
|
|
143
|
+
grant(winner, event, true);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
touchMove(host, touch) {
|
|
147
|
+
if (session === null || session.host !== host) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
recordTouchMove(history, touch);
|
|
151
|
+
refreshActiveTouches(history);
|
|
152
|
+
const path = pathOf(host);
|
|
153
|
+
const event = createEvent(touch, true);
|
|
154
|
+
dispatchTouch(path, "onTouchMoveCapture", "onTouchMove", event);
|
|
155
|
+
if (responder === null) {
|
|
156
|
+
// The common PanResponder shape: nothing claims on press, and the
|
|
157
|
+
// pan starts once onMoveShouldSetPanResponder sees enough movement.
|
|
158
|
+
const winner = negotiate(path, event, "move");
|
|
159
|
+
if (winner) {
|
|
160
|
+
grant(winner, event, false);
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// v1 deliberately does not renegotiate while a responder holds: GTK
|
|
165
|
+
// cannot revoke a claim, so a transfer we cannot enforce natively would
|
|
166
|
+
// be a lie at the boundary with native widgets. Tracked as task 002b.
|
|
167
|
+
notify(responder, "onResponderMove", event);
|
|
168
|
+
},
|
|
169
|
+
touchEnd(host, touch) {
|
|
170
|
+
if (session === null || session.host !== host) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
finish(touch, false, ["onTouchEndCapture", "onTouchEnd"]);
|
|
174
|
+
},
|
|
175
|
+
touchCancel(host, touch) {
|
|
176
|
+
if (session === null || session.host !== host) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
finish(touch, true, ["onTouchCancelCapture", "onTouchCancel"]);
|
|
180
|
+
},
|
|
181
|
+
getResponder: () => responder,
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=system.js.map
|