react-native-gtkx 0.2.0-alpha.2 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +5 -2
  2. package/dist/aliases/index.d.ts +84 -0
  3. package/dist/aliases/index.js +208 -0
  4. package/dist/aliases/index.js.map +1 -0
  5. package/dist/animated/index.d.ts +2 -0
  6. package/dist/animated/index.js +5 -0
  7. package/dist/animated/index.js.map +1 -1
  8. package/dist/animated/types.d.ts +1 -0
  9. package/dist/animated/types.js.map +1 -1
  10. package/dist/animated/value-animation.js +17 -4
  11. package/dist/animated/value-animation.js.map +1 -1
  12. package/dist/apis/host.gtkx.js +9 -12
  13. package/dist/apis/host.gtkx.js.map +1 -1
  14. package/dist/apis/index.d.ts +4 -2
  15. package/dist/apis/index.js +2 -0
  16. package/dist/apis/index.js.map +1 -1
  17. package/dist/apis/keyboard.d.ts +60 -0
  18. package/dist/apis/keyboard.js +63 -0
  19. package/dist/apis/keyboard.js.map +1 -0
  20. package/dist/apis/log-box.d.ts +15 -0
  21. package/dist/apis/log-box.js +46 -0
  22. package/dist/apis/log-box.js.map +1 -0
  23. package/dist/apis/platform.d.ts +18 -1
  24. package/dist/apis/platform.js.map +1 -1
  25. package/dist/common/index.d.ts +0 -1
  26. package/dist/common/index.js +24 -1
  27. package/dist/common/index.js.map +1 -1
  28. package/dist/components/animated.d.ts +70 -6
  29. package/dist/components/animated.js +559 -46
  30. package/dist/components/animated.js.map +1 -1
  31. package/dist/components/driven-size.d.ts +27 -0
  32. package/dist/components/driven-size.js +85 -0
  33. package/dist/components/driven-size.js.map +1 -0
  34. package/dist/components/find-node-handle.d.ts +11 -0
  35. package/dist/components/find-node-handle.js +53 -0
  36. package/dist/components/find-node-handle.js.map +1 -0
  37. package/dist/components/flat-list.d.ts +3 -2
  38. package/dist/components/flat-list.js.map +1 -1
  39. package/dist/components/frame-scheduler.d.ts +16 -0
  40. package/dist/components/frame-scheduler.js +37 -0
  41. package/dist/components/frame-scheduler.js.map +1 -0
  42. package/dist/components/image.d.ts +6 -1
  43. package/dist/components/image.js +15 -4
  44. package/dist/components/image.js.map +1 -1
  45. package/dist/components/index.d.ts +7 -5
  46. package/dist/components/index.js +5 -3
  47. package/dist/components/index.js.map +1 -1
  48. package/dist/components/measure.d.ts +51 -1
  49. package/dist/components/measure.js +101 -2
  50. package/dist/components/measure.js.map +1 -1
  51. package/dist/components/pressable.d.ts +61 -1
  52. package/dist/components/pressable.js +151 -4
  53. package/dist/components/pressable.js.map +1 -1
  54. package/dist/components/rect-store.d.ts +58 -0
  55. package/dist/components/rect-store.js +124 -3
  56. package/dist/components/rect-store.js.map +1 -1
  57. package/dist/components/root.js +6 -1
  58. package/dist/components/root.js.map +1 -1
  59. package/dist/components/scroll-phase.d.ts +35 -0
  60. package/dist/components/scroll-phase.js +26 -0
  61. package/dist/components/scroll-phase.js.map +1 -0
  62. package/dist/components/scroll-view.d.ts +39 -3
  63. package/dist/components/scroll-view.js +378 -43
  64. package/dist/components/scroll-view.js.map +1 -1
  65. package/dist/components/text-input.js +9 -25
  66. package/dist/components/text-input.js.map +1 -1
  67. package/dist/components/text.d.ts +6 -2
  68. package/dist/components/text.js +43 -8
  69. package/dist/components/text.js.map +1 -1
  70. package/dist/components/use-focus.d.ts +27 -0
  71. package/dist/components/use-focus.js +116 -0
  72. package/dist/components/use-focus.js.map +1 -0
  73. package/dist/components/use-layout-child.d.ts +13 -1
  74. package/dist/components/use-layout-child.js +188 -5
  75. package/dist/components/use-layout-child.js.map +1 -1
  76. package/dist/components/view.d.ts +5 -2
  77. package/dist/components/view.js +18 -13
  78. package/dist/components/view.js.map +1 -1
  79. package/dist/components/virtualized-list.d.ts +45 -5
  80. package/dist/components/virtualized-list.js +90 -24
  81. package/dist/components/virtualized-list.js.map +1 -1
  82. package/dist/components/wheel-scroll-session.d.ts +7 -0
  83. package/dist/components/wheel-scroll-session.js +53 -0
  84. package/dist/components/wheel-scroll-session.js.map +1 -0
  85. package/dist/components/widget-retention.d.ts +35 -0
  86. package/dist/components/widget-retention.js +169 -0
  87. package/dist/components/widget-retention.js.map +1 -0
  88. package/dist/contracts.d.ts +4 -0
  89. package/dist/contracts.js.map +1 -1
  90. package/dist/dnd/context.d.ts +69 -0
  91. package/dist/dnd/context.js +149 -0
  92. package/dist/dnd/context.js.map +1 -0
  93. package/dist/dnd/draggable.d.ts +59 -0
  94. package/dist/dnd/draggable.js +149 -0
  95. package/dist/dnd/draggable.js.map +1 -0
  96. package/dist/dnd/droppable.d.ts +24 -0
  97. package/dist/dnd/droppable.js +74 -0
  98. package/dist/dnd/droppable.js.map +1 -0
  99. package/dist/dnd/gtk-controllers.d.ts +39 -0
  100. package/dist/dnd/gtk-controllers.js +110 -0
  101. package/dist/dnd/gtk-controllers.js.map +1 -0
  102. package/dist/dnd/index.d.ts +6 -0
  103. package/dist/dnd/index.js +31 -0
  104. package/dist/dnd/index.js.map +1 -0
  105. package/dist/dnd/order.d.ts +7 -0
  106. package/dist/dnd/order.js +20 -0
  107. package/dist/dnd/order.js.map +1 -0
  108. package/dist/dnd/payload.d.ts +14 -0
  109. package/dist/dnd/payload.js +41 -0
  110. package/dist/dnd/payload.js.map +1 -0
  111. package/dist/dnd/sortable.d.ts +49 -0
  112. package/dist/dnd/sortable.js +307 -0
  113. package/dist/dnd/sortable.js.map +1 -0
  114. package/dist/dnd/types.d.ts +290 -0
  115. package/dist/dnd/types.js +25 -0
  116. package/dist/dnd/types.js.map +1 -0
  117. package/dist/gesture-handler-compat/builder.d.ts +251 -0
  118. package/dist/gesture-handler-compat/builder.js +513 -0
  119. package/dist/gesture-handler-compat/builder.js.map +1 -0
  120. package/dist/gesture-handler-compat/composition.d.ts +23 -0
  121. package/dist/gesture-handler-compat/composition.js +111 -0
  122. package/dist/gesture-handler-compat/composition.js.map +1 -0
  123. package/dist/gesture-handler-compat/deciders.d.ts +3 -0
  124. package/dist/gesture-handler-compat/deciders.js +47 -0
  125. package/dist/gesture-handler-compat/deciders.js.map +1 -0
  126. package/dist/gesture-handler-compat/detector-runtime.d.ts +44 -0
  127. package/dist/gesture-handler-compat/detector-runtime.js +374 -0
  128. package/dist/gesture-handler-compat/detector-runtime.js.map +1 -0
  129. package/dist/gesture-handler-compat/detector.d.ts +22 -0
  130. package/dist/gesture-handler-compat/detector.js +104 -0
  131. package/dist/gesture-handler-compat/detector.js.map +1 -0
  132. package/dist/gesture-handler-compat/fling.d.ts +24 -0
  133. package/dist/gesture-handler-compat/fling.js +123 -0
  134. package/dist/gesture-handler-compat/fling.js.map +1 -0
  135. package/dist/gesture-handler-compat/force-touch.d.ts +13 -0
  136. package/dist/gesture-handler-compat/force-touch.js +40 -0
  137. package/dist/gesture-handler-compat/force-touch.js.map +1 -0
  138. package/dist/gesture-handler-compat/hooks.d.ts +157 -0
  139. package/dist/gesture-handler-compat/hooks.js +268 -0
  140. package/dist/gesture-handler-compat/hooks.js.map +1 -0
  141. package/dist/gesture-handler-compat/hover.d.ts +2 -0
  142. package/dist/gesture-handler-compat/hover.js +22 -0
  143. package/dist/gesture-handler-compat/hover.js.map +1 -0
  144. package/dist/gesture-handler-compat/index.d.ts +98 -0
  145. package/dist/gesture-handler-compat/index.js +255 -0
  146. package/dist/gesture-handler-compat/index.js.map +1 -0
  147. package/dist/gesture-handler-compat/long-press.d.ts +6 -0
  148. package/dist/gesture-handler-compat/long-press.js +21 -0
  149. package/dist/gesture-handler-compat/long-press.js.map +1 -0
  150. package/dist/gesture-handler-compat/manual.d.ts +2 -0
  151. package/dist/gesture-handler-compat/manual.js +18 -0
  152. package/dist/gesture-handler-compat/manual.js.map +1 -0
  153. package/dist/gesture-handler-compat/native.d.ts +7 -0
  154. package/dist/gesture-handler-compat/native.js +34 -0
  155. package/dist/gesture-handler-compat/native.js.map +1 -0
  156. package/dist/gesture-handler-compat/orchestrator.d.ts +77 -0
  157. package/dist/gesture-handler-compat/orchestrator.js +284 -0
  158. package/dist/gesture-handler-compat/orchestrator.js.map +1 -0
  159. package/dist/gesture-handler-compat/pan.d.ts +23 -0
  160. package/dist/gesture-handler-compat/pan.js +116 -0
  161. package/dist/gesture-handler-compat/pan.js.map +1 -0
  162. package/dist/gesture-handler-compat/recognizer.d.ts +268 -0
  163. package/dist/gesture-handler-compat/recognizer.js +1056 -0
  164. package/dist/gesture-handler-compat/recognizer.js.map +1 -0
  165. package/dist/gesture-handler-compat/relations.d.ts +43 -0
  166. package/dist/gesture-handler-compat/relations.js +128 -0
  167. package/dist/gesture-handler-compat/relations.js.map +1 -0
  168. package/dist/gesture-handler-compat/tap.d.ts +6 -0
  169. package/dist/gesture-handler-compat/tap.js +29 -0
  170. package/dist/gesture-handler-compat/tap.js.map +1 -0
  171. package/dist/gesture-handler-compat/touchpad.d.ts +41 -0
  172. package/dist/gesture-handler-compat/touchpad.js +64 -0
  173. package/dist/gesture-handler-compat/touchpad.js.map +1 -0
  174. package/dist/gesture-handler-compat/types.d.ts +441 -0
  175. package/dist/gesture-handler-compat/types.js +167 -0
  176. package/dist/gesture-handler-compat/types.js.map +1 -0
  177. package/dist/gtk/controllers.d.ts +39 -0
  178. package/dist/gtk/controllers.js +92 -0
  179. package/dist/gtk/controllers.js.map +1 -0
  180. package/dist/gtk/index.d.ts +2 -1
  181. package/dist/gtk/index.js +7 -1
  182. package/dist/gtk/index.js.map +1 -1
  183. package/dist/gtkx/bridge/geometry.js +6 -4
  184. package/dist/gtkx/bridge/geometry.js.map +1 -1
  185. package/dist/gtkx/bridge/index.d.ts +5 -3
  186. package/dist/gtkx/bridge/index.js +6 -3
  187. package/dist/gtkx/bridge/index.js.map +1 -1
  188. package/dist/gtkx/bridge/slot-portal.js.map +1 -1
  189. package/dist/gtkx/bridge/use-signal.js +1 -1
  190. package/dist/gtkx/bridge/use-signal.js.map +1 -1
  191. package/dist/gtkx/bridge/view-box.d.ts +56 -0
  192. package/dist/gtkx/bridge/view-box.js +394 -9
  193. package/dist/gtkx/bridge/view-box.js.map +1 -1
  194. package/dist/gtkx/bridge/widget-css.d.ts +17 -0
  195. package/dist/gtkx/bridge/widget-css.js +72 -0
  196. package/dist/gtkx/bridge/widget-css.js.map +1 -0
  197. package/dist/index.d.ts +3 -3
  198. package/dist/index.js +2 -2
  199. package/dist/index.js.map +1 -1
  200. package/dist/layout/driven-size.d.ts +12 -0
  201. package/dist/layout/driven-size.js +70 -0
  202. package/dist/layout/driven-size.js.map +1 -0
  203. package/dist/layout/engine.d.ts +17 -1
  204. package/dist/layout/engine.js +3 -20
  205. package/dist/layout/engine.js.map +1 -1
  206. package/dist/layout/node.d.ts +13 -0
  207. package/dist/layout/node.js +14 -0
  208. package/dist/layout/node.js.map +1 -1
  209. package/dist/layout/yoga.d.ts +3 -2
  210. package/dist/layout/yoga.js +5 -2
  211. package/dist/layout/yoga.js.map +1 -1
  212. package/dist/mcp/data/generated.d.ts +257 -60
  213. package/dist/mcp/data/generated.js +306 -62
  214. package/dist/mcp/data/generated.js.map +1 -1
  215. package/dist/metro/index.d.ts +10 -0
  216. package/dist/metro/index.js +21 -12
  217. package/dist/metro/index.js.map +1 -1
  218. package/dist/navigation/sidebar.d.ts +16 -0
  219. package/dist/navigation/sidebar.js +65 -1
  220. package/dist/navigation/sidebar.js.map +1 -1
  221. package/dist/reanimated-compat/animated-ref.d.ts +32 -0
  222. package/dist/reanimated-compat/animated-ref.js +57 -0
  223. package/dist/reanimated-compat/animated-ref.js.map +1 -0
  224. package/dist/reanimated-compat/animation.d.ts +177 -0
  225. package/dist/reanimated-compat/animation.js +540 -0
  226. package/dist/reanimated-compat/animation.js.map +1 -0
  227. package/dist/reanimated-compat/color.d.ts +28 -0
  228. package/dist/reanimated-compat/color.js +249 -0
  229. package/dist/reanimated-compat/color.js.map +1 -0
  230. package/dist/reanimated-compat/decay.d.ts +39 -0
  231. package/dist/reanimated-compat/decay.js +96 -0
  232. package/dist/reanimated-compat/decay.js.map +1 -0
  233. package/dist/reanimated-compat/easing.d.ts +30 -0
  234. package/dist/reanimated-compat/easing.js +76 -0
  235. package/dist/reanimated-compat/easing.js.map +1 -0
  236. package/dist/reanimated-compat/hooks.d.ts +14 -0
  237. package/dist/reanimated-compat/hooks.js +217 -0
  238. package/dist/reanimated-compat/hooks.js.map +1 -0
  239. package/dist/reanimated-compat/index.d.ts +258 -0
  240. package/dist/reanimated-compat/index.js +464 -0
  241. package/dist/reanimated-compat/index.js.map +1 -0
  242. package/dist/reanimated-compat/interpolation.d.ts +18 -0
  243. package/dist/reanimated-compat/interpolation.js +122 -0
  244. package/dist/reanimated-compat/interpolation.js.map +1 -0
  245. package/dist/reanimated-compat/layout-animation-config.d.ts +43 -0
  246. package/dist/reanimated-compat/layout-animation-config.js +78 -0
  247. package/dist/reanimated-compat/layout-animation-config.js.map +1 -0
  248. package/dist/reanimated-compat/layout-animation-presets.d.ts +117 -0
  249. package/dist/reanimated-compat/layout-animation-presets.js +498 -0
  250. package/dist/reanimated-compat/layout-animation-presets.js.map +1 -0
  251. package/dist/reanimated-compat/layout-animation-runtime.d.ts +21 -0
  252. package/dist/reanimated-compat/layout-animation-runtime.js +311 -0
  253. package/dist/reanimated-compat/layout-animation-runtime.js.map +1 -0
  254. package/dist/reanimated-compat/layout-animation-view.d.ts +27 -0
  255. package/dist/reanimated-compat/layout-animation-view.js +266 -0
  256. package/dist/reanimated-compat/layout-animation-view.js.map +1 -0
  257. package/dist/reanimated-compat/layout-animation.d.ts +171 -0
  258. package/dist/reanimated-compat/layout-animation.js +469 -0
  259. package/dist/reanimated-compat/layout-animation.js.map +1 -0
  260. package/dist/reanimated-compat/layout-transitions.d.ts +83 -0
  261. package/dist/reanimated-compat/layout-transitions.js +350 -0
  262. package/dist/reanimated-compat/layout-transitions.js.map +1 -0
  263. package/dist/reanimated-compat/mutable.d.ts +29 -0
  264. package/dist/reanimated-compat/mutable.js +139 -0
  265. package/dist/reanimated-compat/mutable.js.map +1 -0
  266. package/dist/reanimated-compat/props.d.ts +18 -0
  267. package/dist/reanimated-compat/props.js +111 -0
  268. package/dist/reanimated-compat/props.js.map +1 -0
  269. package/dist/reanimated-compat/scroll-handler.d.ts +119 -0
  270. package/dist/reanimated-compat/scroll-handler.js +274 -0
  271. package/dist/reanimated-compat/scroll-handler.js.map +1 -0
  272. package/dist/reanimated-compat/scroll-offset.d.ts +12 -0
  273. package/dist/reanimated-compat/scroll-offset.js +104 -0
  274. package/dist/reanimated-compat/scroll-offset.js.map +1 -0
  275. package/dist/reanimated-compat/style.d.ts +90 -0
  276. package/dist/reanimated-compat/style.js +452 -0
  277. package/dist/reanimated-compat/style.js.map +1 -0
  278. package/dist/reanimated-compat/test-timers.d.ts +28 -0
  279. package/dist/reanimated-compat/test-timers.js +136 -0
  280. package/dist/reanimated-compat/test-timers.js.map +1 -0
  281. package/dist/reanimated-compat/threads.d.ts +13 -0
  282. package/dist/reanimated-compat/threads.js +41 -0
  283. package/dist/reanimated-compat/threads.js.map +1 -0
  284. package/dist/reanimated-compat/tracking.d.ts +41 -0
  285. package/dist/reanimated-compat/tracking.js +106 -0
  286. package/dist/reanimated-compat/tracking.js.map +1 -0
  287. package/dist/reanimated-compat/updater-animations.d.ts +36 -0
  288. package/dist/reanimated-compat/updater-animations.js +273 -0
  289. package/dist/reanimated-compat/updater-animations.js.map +1 -0
  290. package/dist/responder/system.d.ts +80 -7
  291. package/dist/responder/system.js +143 -23
  292. package/dist/responder/system.js.map +1 -1
  293. package/dist/responder/use-responder.d.ts +11 -0
  294. package/dist/responder/use-responder.js +225 -6
  295. package/dist/responder/use-responder.js.map +1 -1
  296. package/dist/runner/host-dev.js +1 -1
  297. package/dist/runner/host-dev.js.map +1 -1
  298. package/dist/runner/host.js +1 -1
  299. package/dist/runner/host.js.map +1 -1
  300. package/dist/runner/index.js +3 -3
  301. package/dist/runner/index.js.map +1 -1
  302. package/dist/sea/gtkx-config-module.js +1 -1
  303. package/dist/sea/gtkx-config-module.js.map +1 -1
  304. package/dist/style/absolute-insets.d.ts +26 -0
  305. package/dist/style/absolute-insets.js +113 -0
  306. package/dist/style/absolute-insets.js.map +1 -0
  307. package/dist/style/animated-size.d.ts +25 -0
  308. package/dist/style/animated-size.js +225 -0
  309. package/dist/style/animated-size.js.map +1 -0
  310. package/dist/style/colors.d.ts +17 -0
  311. package/dist/style/colors.js +25 -6
  312. package/dist/style/colors.js.map +1 -1
  313. package/dist/style/imperative-css.d.ts +21 -0
  314. package/dist/style/imperative-css.js +63 -0
  315. package/dist/style/imperative-css.js.map +1 -0
  316. package/dist/style/index.d.ts +2 -1
  317. package/dist/style/index.js +2 -1
  318. package/dist/style/index.js.map +1 -1
  319. package/dist/style/split-style.js +5 -0
  320. package/dist/style/split-style.js.map +1 -1
  321. package/dist/testing/index.js.map +1 -1
  322. package/dist/unsupported-export.d.ts +8 -0
  323. package/dist/unsupported-export.js +66 -0
  324. package/dist/unsupported-export.js.map +1 -0
  325. package/dist/vite/index.d.ts +21 -8
  326. package/dist/vite/index.js +127 -82
  327. package/dist/vite/index.js.map +1 -1
  328. package/dist/vitest/index.d.ts +2 -2
  329. package/dist/vitest/index.js.map +1 -1
  330. package/dist/worklets-compat/index.d.ts +4 -0
  331. package/dist/worklets-compat/index.js +28 -0
  332. package/dist/worklets-compat/index.js.map +1 -0
  333. package/dist/worklets-compat/surface.d.ts +110 -0
  334. package/dist/worklets-compat/surface.js +153 -0
  335. package/dist/worklets-compat/surface.js.map +1 -0
  336. package/package.json +25 -9
  337. package/types.d.ts +9 -0
  338. package/dist/common/list.d.ts +0 -82
  339. package/dist/common/list.js +0 -166
  340. package/dist/common/list.js.map +0 -1
@@ -1,3 +1,6 @@
1
+ import { type AliasOverrides } from "../aliases/index.js";
2
+ export { CONFIGURABLE_ALIASES, DEFAULT_ALIASES, PLATFORM_ALIAS, } from "../aliases/index.js";
3
+ export type { AliasOverride, AliasOverrides, AliasPattern, AliasTable, CompiledAlias, } from "../aliases/index.js";
1
4
  /** Modules the run-linux host provides to the bundle at runtime. */
2
5
  export declare const HOST_MODULE_EXTERNALS: string[];
3
6
  export declare const LINUX_PLATFORM = "linux";
@@ -24,6 +27,13 @@ export type LinuxPlatformOptions = {
24
27
  externals?: readonly string[];
25
28
  /** Proxy directory override (default: node_modules/.react-native-gtkx). */
26
29
  proxyDir?: string;
30
+ /**
31
+ * Deltas over the preset's package aliases, keyed by package name — a
32
+ * string target, a `{ pattern, replace }` rule, or `false` to drop one of
33
+ * ours so the real package loads. See ../aliases/index.ts and docs/api.md.
34
+ * Invalid entries throw here, while the config is being read.
35
+ */
36
+ aliases?: AliasOverrides;
27
37
  };
28
38
  export type LinuxPlatformConfig<T extends MetroLikeConfig> = T & {
29
39
  resolver: {
@@ -8,7 +8,9 @@
8
8
  // Platform.OS come along with it).
9
9
  // 2. Redirects `react-native` (and subpaths) to `react-native-gtkx` — the
10
10
  // out-of-tree npmPackageName declaration alone does not alias imports
11
- // for `bundle`, the resolver has to.
11
+ // for `bundle`, the resolver has to — plus five package substitutions
12
+ // for libraries whose real implementation cannot run here. The rules
13
+ // are data, shared with the vite preset: see ../aliases/index.ts.
12
14
  // 3. EXTERNALIZES host-side singletons. Unlike react-native-windows, whose
13
15
  // native side lives outside the JS bundle by construction, our "native"
14
16
  // is Node modules with NAPI bindings (@gtkx/*) plus yoga-layout's WASM —
@@ -24,6 +26,11 @@
24
26
  import { mkdirSync, writeFileSync } from "node:fs";
25
27
  import { builtinModules, isBuiltin } from "node:module";
26
28
  import { join } from "node:path";
29
+ // Explicit .js: bare Node loads metro.config.ts and does not guess extensions.
30
+ import { applyAliases, compileAliases, } from "../aliases/index.js";
31
+ // Re-exported so an app configuring `aliases` never has to reach past the
32
+ // preset subpath it already imports.
33
+ export { CONFIGURABLE_ALIASES, DEFAULT_ALIASES, PLATFORM_ALIAS, } from "../aliases/index.js";
27
34
  /** Modules the run-linux host provides to the bundle at runtime. */
28
35
  export const HOST_MODULE_EXTERNALS = [
29
36
  "@gtkx/css",
@@ -80,6 +87,9 @@ export const withLinuxPlatform = (config, options = {}) => {
80
87
  const proxyDir = options.proxyDir ??
81
88
  join(config.projectRoot ?? process.cwd(), "node_modules", ".react-native-gtkx", "metro-externals");
82
89
  generateProxies(proxyDir, externals);
90
+ // Throws on an unknown key, an overlapping pattern or an attempt to remove
91
+ // the platform alias — at config load, naming what is valid.
92
+ const aliases = compileAliases(options.aliases);
83
93
  const previousResolve = config.resolver?.resolveRequest ?? null;
84
94
  const fallback = (context, moduleName, platform) => previousResolve
85
95
  ? previousResolve(context, moduleName, platform)
@@ -94,17 +104,16 @@ export const withLinuxPlatform = (config, options = {}) => {
94
104
  filePath: join(proxyDir, sanitize(moduleName)),
95
105
  };
96
106
  }
97
- if (moduleName === "react-native" ||
98
- moduleName.startsWith("react-native/")) {
99
- return fallback(context, moduleName.replace(/^react-native/, "react-native-gtkx"), platform);
100
- }
101
- // Same alias, same guard shape, for the SVG compat subpath (see
102
- // src/svg-compat/index.ts and the vite preset's rewriteReactNativeImport
103
- // kept in sync deliberately rather than sharing code, Metro and vite
104
- // resolvers have never shared an implementation in this package).
105
- if (moduleName === "react-native-svg" ||
106
- moduleName.startsWith("react-native-svg/")) {
107
- return fallback(context, moduleName.replace(/^react-native-svg/, "react-native-gtkx/svg"), platform);
107
+ // The five package substitutions plus the platform alias, from the table
108
+ // both presets share (../aliases/index.ts). Each rule is anchored to its
109
+ // package name and matches the exact name or a slash-prefixed subpath,
110
+ // with the tail transplanted onto the target — the guard, not just the
111
+ // order, is what keeps `react-native-reanimated-dnd` out of the
112
+ // `react-native-reanimated` rule and the real, unrelated
113
+ // `react-native-worklets-core` out of the `react-native-worklets` one.
114
+ const aliased = applyAliases(aliases, moduleName);
115
+ if (aliased !== null) {
116
+ return fallback(context, aliased, platform);
108
117
  }
109
118
  return fallback(context, moduleName, platform);
110
119
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metro/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Metro preset for the linux out-of-tree platform: wrap the app's Metro\n// config with `withLinuxPlatform` and the standard RN toolchain bundles for\n// `--platform linux`.\n//\n// What it does on top of RN defaults (validated by spike/rn-platform):\n//\n// 1. Adds \"linux\" to resolver.platforms (`.linux.tsx` extensions and\n// Platform.OS come along with it).\n// 2. Redirects `react-native` (and subpaths) to `react-native-gtkx` — the\n// out-of-tree npmPackageName declaration alone does not alias imports\n// for `bundle`, the resolver has to.\n// 3. EXTERNALIZES host-side singletons. Unlike react-native-windows, whose\n// native side lives outside the JS bundle by construction, our \"native\"\n// is Node modules with NAPI bindings (@gtkx/*) plus yoga-layout's WASM —\n// Metro cannot bundle those. react (and its jsx runtimes) must also stay\n// host-side: the reconciler inside @gtkx/react and the app have to share\n// one React instance. Every external resolves to a generated proxy that\n// reads the real module from global.__hostModules — the run-linux host\n// injects them before executing the bundle.\n// 4. Proxies Node builtins through the host's require: apps on this\n// platform may use the whole Node API, it is a platform feature.\n// 5. Drops InitializeCore (RN's mobile environment polyfills): the runtime\n// environment IS Node, the host provides everything.\nimport { mkdirSync, writeFileSync } from \"node:fs\"\nimport { builtinModules, isBuiltin } from \"node:module\"\nimport { join } from \"node:path\"\n\n/** Modules the run-linux host provides to the bundle at runtime. */\nexport const HOST_MODULE_EXTERNALS = [\n \"@gtkx/css\",\n \"@gtkx/gi/adw\",\n \"@gtkx/gi/gdk\",\n \"@gtkx/gi/gio\",\n \"@gtkx/gi/glib\",\n \"@gtkx/gi/gobject\",\n \"@gtkx/gi/graphene\",\n \"@gtkx/gi/gsk\",\n \"@gtkx/gi/gtk\",\n \"@gtkx/gi/pango\",\n \"@gtkx/jsx/adw\",\n \"@gtkx/jsx/gio\",\n \"@gtkx/jsx/gtk\",\n \"@gtkx/react\",\n \"@gtkx/react/internal\",\n \"@gtkx/runtime\",\n \"react\",\n \"react/jsx-runtime\",\n \"react/jsx-dev-runtime\",\n \"yoga-layout\",\n]\n\nexport const LINUX_PLATFORM = \"linux\"\n\n// Structural slices of Metro's config/resolution types: Metro is the app's\n// dependency, not ours — depending on its types here would pin versions.\nexport type MetroResolutionContext = {\n resolveRequest: MetroResolver\n}\nexport type MetroResolution = { type: string; filePath?: string }\nexport type MetroResolver = (\n context: MetroResolutionContext,\n moduleName: string,\n platform: string | null,\n) => MetroResolution\n\nexport type MetroLikeConfig = {\n projectRoot?: string\n resolver?: {\n platforms?: readonly string[]\n resolveRequest?: MetroResolver | null\n }\n serializer?: {\n getModulesRunBeforeMainModule?: (entryFilePath: string) => string[]\n }\n}\n\nexport type LinuxPlatformOptions = {\n /** Extra host-provided modules to keep out of the bundle. */\n externals?: readonly string[]\n /** Proxy directory override (default: node_modules/.react-native-gtkx). */\n proxyDir?: string\n}\n\nconst sanitize = (name: string): string => name.replace(/[@/:]/g, \"_\") + \".js\"\n\nconst hostModuleProxy = (name: string): string =>\n // The run-linux host guarantees __hostModules before executing the bundle.\n `module.exports = global.__hostModules[${JSON.stringify(name)}]\\n`\n\nconst builtinProxy = (name: string): string =>\n `module.exports = global.__hostRequire(${JSON.stringify(name)})\\n`\n\n// Every proxy is generated eagerly when the config loads: Metro crawls the\n// file map before transforming, and files that appear mid-build lose the\n// race (\"Failed to get the SHA-1\"). The builtin list is finite, so the\n// whole proxy set can exist up front.\nconst generateProxies = (dir: string, externals: Set<string>): void => {\n mkdirSync(dir, { recursive: true })\n for (const name of externals) {\n writeFileSync(join(dir, sanitize(name)), hostModuleProxy(name))\n }\n for (const bare of builtinModules) {\n writeFileSync(join(dir, sanitize(bare)), builtinProxy(bare))\n writeFileSync(\n join(dir, sanitize(`node:${bare}`)),\n builtinProxy(`node:${bare}`),\n )\n }\n}\n\nexport type LinuxPlatformConfig<T extends MetroLikeConfig> = T & {\n resolver: {\n platforms: string[]\n resolveRequest: MetroResolver\n }\n serializer: {\n getModulesRunBeforeMainModule: (entryFilePath: string) => string[]\n }\n}\n\n/**\n * Wraps a Metro config (RN's getDefaultConfig output) with everything the\n * linux platform needs. Composes with an existing custom resolveRequest —\n * non-linux platforms fall through to it untouched.\n */\nexport const withLinuxPlatform = <T extends MetroLikeConfig>(\n config: T,\n options: LinuxPlatformOptions = {},\n): LinuxPlatformConfig<T> => {\n const externals = new Set([\n ...HOST_MODULE_EXTERNALS,\n ...(options.externals ?? []),\n ])\n const proxyDir =\n options.proxyDir ??\n join(\n config.projectRoot ?? process.cwd(),\n \"node_modules\",\n \".react-native-gtkx\",\n \"metro-externals\",\n )\n generateProxies(proxyDir, externals)\n const previousResolve = config.resolver?.resolveRequest ?? null\n\n const fallback: MetroResolver = (context, moduleName, platform) =>\n previousResolve\n ? previousResolve(context, moduleName, platform)\n : context.resolveRequest(context, moduleName, platform)\n\n const resolveRequest: MetroResolver = (context, moduleName, platform) => {\n if (platform !== LINUX_PLATFORM) {\n return fallback(context, moduleName, platform)\n }\n if (externals.has(moduleName) || isBuiltin(moduleName)) {\n return {\n type: \"sourceFile\",\n filePath: join(proxyDir, sanitize(moduleName)),\n }\n }\n if (\n moduleName === \"react-native\" ||\n moduleName.startsWith(\"react-native/\")\n ) {\n return fallback(\n context,\n moduleName.replace(/^react-native/, \"react-native-gtkx\"),\n platform,\n )\n }\n // Same alias, same guard shape, for the SVG compat subpath (see\n // src/svg-compat/index.ts and the vite preset's rewriteReactNativeImport\n // — kept in sync deliberately rather than sharing code, Metro and vite\n // resolvers have never shared an implementation in this package).\n if (\n moduleName === \"react-native-svg\" ||\n moduleName.startsWith(\"react-native-svg/\")\n ) {\n return fallback(\n context,\n moduleName.replace(/^react-native-svg/, \"react-native-gtkx/svg\"),\n platform,\n )\n }\n return fallback(context, moduleName, platform)\n }\n\n // The cast: T may carry narrower resolver/serializer types than the ones\n // rebuilt here — the runtime shape is a strict superset of both.\n return {\n ...config,\n resolver: {\n ...config.resolver,\n platforms: [\n ...new Set([...(config.resolver?.platforms ?? []), LINUX_PLATFORM]),\n ],\n resolveRequest,\n },\n serializer: {\n ...config.serializer,\n // RN prepends InitializeCore (Hermes Promise, nativeLoggingHook\n // console...). Our runtime environment IS Node — nothing to\n // initialize inside the bundle.\n getModulesRunBeforeMainModule: () => [],\n },\n } as LinuxPlatformConfig<T>\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metro/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Metro preset for the linux out-of-tree platform: wrap the app's Metro\n// config with `withLinuxPlatform` and the standard RN toolchain bundles for\n// `--platform linux`.\n//\n// What it does on top of RN defaults (validated by spike/rn-platform):\n//\n// 1. Adds \"linux\" to resolver.platforms (`.linux.tsx` extensions and\n// Platform.OS come along with it).\n// 2. Redirects `react-native` (and subpaths) to `react-native-gtkx` — the\n// out-of-tree npmPackageName declaration alone does not alias imports\n// for `bundle`, the resolver has to — plus five package substitutions\n// for libraries whose real implementation cannot run here. The rules\n// are data, shared with the vite preset: see ../aliases/index.ts.\n// 3. EXTERNALIZES host-side singletons. Unlike react-native-windows, whose\n// native side lives outside the JS bundle by construction, our \"native\"\n// is Node modules with NAPI bindings (@gtkx/*) plus yoga-layout's WASM —\n// Metro cannot bundle those. react (and its jsx runtimes) must also stay\n// host-side: the reconciler inside @gtkx/react and the app have to share\n// one React instance. Every external resolves to a generated proxy that\n// reads the real module from global.__hostModules — the run-linux host\n// injects them before executing the bundle.\n// 4. Proxies Node builtins through the host's require: apps on this\n// platform may use the whole Node API, it is a platform feature.\n// 5. Drops InitializeCore (RN's mobile environment polyfills): the runtime\n// environment IS Node, the host provides everything.\nimport { mkdirSync, writeFileSync } from \"node:fs\"\nimport { builtinModules, isBuiltin } from \"node:module\"\nimport { join } from \"node:path\"\n// Explicit .js: bare Node loads metro.config.ts and does not guess extensions.\nimport {\n applyAliases,\n compileAliases,\n type AliasOverrides,\n} from \"../aliases/index.js\"\n\n// Re-exported so an app configuring `aliases` never has to reach past the\n// preset subpath it already imports.\nexport {\n CONFIGURABLE_ALIASES,\n DEFAULT_ALIASES,\n PLATFORM_ALIAS,\n} from \"../aliases/index.js\"\nexport type {\n AliasOverride,\n AliasOverrides,\n AliasPattern,\n AliasTable,\n CompiledAlias,\n} from \"../aliases/index.js\"\n\n/** Modules the run-linux host provides to the bundle at runtime. */\nexport const HOST_MODULE_EXTERNALS = [\n \"@gtkx/css\",\n \"@gtkx/gi/adw\",\n \"@gtkx/gi/gdk\",\n \"@gtkx/gi/gio\",\n \"@gtkx/gi/glib\",\n \"@gtkx/gi/gobject\",\n \"@gtkx/gi/graphene\",\n \"@gtkx/gi/gsk\",\n \"@gtkx/gi/gtk\",\n \"@gtkx/gi/pango\",\n \"@gtkx/jsx/adw\",\n \"@gtkx/jsx/gio\",\n \"@gtkx/jsx/gtk\",\n \"@gtkx/react\",\n \"@gtkx/react/internal\",\n \"@gtkx/runtime\",\n \"react\",\n \"react/jsx-runtime\",\n \"react/jsx-dev-runtime\",\n \"yoga-layout\",\n]\n\nexport const LINUX_PLATFORM = \"linux\"\n\n// Structural slices of Metro's config/resolution types: Metro is the app's\n// dependency, not ours — depending on its types here would pin versions.\nexport type MetroResolutionContext = {\n resolveRequest: MetroResolver\n}\nexport type MetroResolution = { type: string; filePath?: string }\nexport type MetroResolver = (\n context: MetroResolutionContext,\n moduleName: string,\n platform: string | null,\n) => MetroResolution\n\nexport type MetroLikeConfig = {\n projectRoot?: string\n resolver?: {\n platforms?: readonly string[]\n resolveRequest?: MetroResolver | null\n }\n serializer?: {\n getModulesRunBeforeMainModule?: (entryFilePath: string) => string[]\n }\n}\n\nexport type LinuxPlatformOptions = {\n /** Extra host-provided modules to keep out of the bundle. */\n externals?: readonly string[]\n /** Proxy directory override (default: node_modules/.react-native-gtkx). */\n proxyDir?: string\n /**\n * Deltas over the preset's package aliases, keyed by package name — a\n * string target, a `{ pattern, replace }` rule, or `false` to drop one of\n * ours so the real package loads. See ../aliases/index.ts and docs/api.md.\n * Invalid entries throw here, while the config is being read.\n */\n aliases?: AliasOverrides\n}\n\nconst sanitize = (name: string): string => name.replace(/[@/:]/g, \"_\") + \".js\"\n\nconst hostModuleProxy = (name: string): string =>\n // The run-linux host guarantees __hostModules before executing the bundle.\n `module.exports = global.__hostModules[${JSON.stringify(name)}]\\n`\n\nconst builtinProxy = (name: string): string =>\n `module.exports = global.__hostRequire(${JSON.stringify(name)})\\n`\n\n// Every proxy is generated eagerly when the config loads: Metro crawls the\n// file map before transforming, and files that appear mid-build lose the\n// race (\"Failed to get the SHA-1\"). The builtin list is finite, so the\n// whole proxy set can exist up front.\nconst generateProxies = (dir: string, externals: Set<string>): void => {\n mkdirSync(dir, { recursive: true })\n for (const name of externals) {\n writeFileSync(join(dir, sanitize(name)), hostModuleProxy(name))\n }\n for (const bare of builtinModules) {\n writeFileSync(join(dir, sanitize(bare)), builtinProxy(bare))\n writeFileSync(\n join(dir, sanitize(`node:${bare}`)),\n builtinProxy(`node:${bare}`),\n )\n }\n}\n\nexport type LinuxPlatformConfig<T extends MetroLikeConfig> = T & {\n resolver: {\n platforms: string[]\n resolveRequest: MetroResolver\n }\n serializer: {\n getModulesRunBeforeMainModule: (entryFilePath: string) => string[]\n }\n}\n\n/**\n * Wraps a Metro config (RN's getDefaultConfig output) with everything the\n * linux platform needs. Composes with an existing custom resolveRequest —\n * non-linux platforms fall through to it untouched.\n */\nexport const withLinuxPlatform = <T extends MetroLikeConfig>(\n config: T,\n options: LinuxPlatformOptions = {},\n): LinuxPlatformConfig<T> => {\n const externals = new Set([\n ...HOST_MODULE_EXTERNALS,\n ...(options.externals ?? []),\n ])\n const proxyDir =\n options.proxyDir ??\n join(\n config.projectRoot ?? process.cwd(),\n \"node_modules\",\n \".react-native-gtkx\",\n \"metro-externals\",\n )\n generateProxies(proxyDir, externals)\n // Throws on an unknown key, an overlapping pattern or an attempt to remove\n // the platform alias — at config load, naming what is valid.\n const aliases = compileAliases(options.aliases)\n const previousResolve = config.resolver?.resolveRequest ?? null\n\n const fallback: MetroResolver = (context, moduleName, platform) =>\n previousResolve\n ? previousResolve(context, moduleName, platform)\n : context.resolveRequest(context, moduleName, platform)\n\n const resolveRequest: MetroResolver = (context, moduleName, platform) => {\n if (platform !== LINUX_PLATFORM) {\n return fallback(context, moduleName, platform)\n }\n if (externals.has(moduleName) || isBuiltin(moduleName)) {\n return {\n type: \"sourceFile\",\n filePath: join(proxyDir, sanitize(moduleName)),\n }\n }\n // The five package substitutions plus the platform alias, from the table\n // both presets share (../aliases/index.ts). Each rule is anchored to its\n // package name and matches the exact name or a slash-prefixed subpath,\n // with the tail transplanted onto the target — the guard, not just the\n // order, is what keeps `react-native-reanimated-dnd` out of the\n // `react-native-reanimated` rule and the real, unrelated\n // `react-native-worklets-core` out of the `react-native-worklets` one.\n const aliased = applyAliases(aliases, moduleName)\n if (aliased !== null) {\n return fallback(context, aliased, platform)\n }\n return fallback(context, moduleName, platform)\n }\n\n // The cast: T may carry narrower resolver/serializer types than the ones\n // rebuilt here — the runtime shape is a strict superset of both.\n return {\n ...config,\n resolver: {\n ...config.resolver,\n platforms: [\n ...new Set([...(config.resolver?.platforms ?? []), LINUX_PLATFORM]),\n ],\n resolveRequest,\n },\n serializer: {\n ...config.serializer,\n // RN prepends InitializeCore (Hermes Promise, nativeLoggingHook\n // console...). Our runtime environment IS Node — nothing to\n // initialize inside the bundle.\n getModulesRunBeforeMainModule: () => [],\n },\n } as LinuxPlatformConfig<T>\n}\n"]}
@@ -68,6 +68,22 @@ export type SidebarNavigationOptions = {
68
68
  * specifically (replaces it entirely when set, same as the stack
69
69
  * navigator's per-screen option override). */
70
70
  headerButtons?: HeaderButton[];
71
+ /** Section this row belongs to. Consecutive screens sharing a `group`
72
+ * get one Adwaita section header above the first of them.
73
+ *
74
+ * The header is attached with `GtkListBox.set_header_func`, which is
75
+ * what GNOME's own sidebars use: a header is a DECORATION owned by the
76
+ * row below it, not a row of its own. That distinction is the whole
77
+ * reason to do it this way — a header faked as a non-selectable
78
+ * `GtkListBoxRow` still sits in the list's focus chain, so Tab and the
79
+ * arrow keys stop on it and assistive technology announces a row that
80
+ * cannot be activated. A real header is skipped by both, for free.
81
+ *
82
+ * Grouping follows ROW ORDER, so screens in one group are declared
83
+ * together; a group name reappearing after a gap starts a second
84
+ * header rather than reordering anything. Leave it unset on every
85
+ * screen (the default) and the list is flat, exactly as before. */
86
+ group?: string;
71
87
  };
72
88
  export type HeaderButton = {
73
89
  id: string;
@@ -56,6 +56,7 @@ const SIDEBAR_OPTION_KEYS = new Set([
56
56
  "headerButtons",
57
57
  "contentLayout",
58
58
  "sidebarRow",
59
+ "group",
59
60
  ]);
60
61
  // A one-off CSS class for a colored dot prefix — the same mechanism
61
62
  // tasks-app's own sidebar used (react-native-gtkx/gtk's `css` tag, kept
@@ -166,6 +167,69 @@ const SidebarNavigator = ({ initialRouteName, screenOptions, sidebarTitle = "Sid
166
167
  const activeDescriptor = descriptors[active.key];
167
168
  const optionsOf = (routeKey) => descriptors[routeKey]?.options ?? {};
168
169
  const titleOf = (routeKey, fallback) => optionsOf(routeKey).title ?? fallback;
170
+ // Group headers, through GtkListBox's own `set_header_func` — the
171
+ // mechanism GNOME's sidebars use, and the reason this is not a row: a
172
+ // header attached to a row is a decoration, outside the list's selection
173
+ // model and outside its focus chain, so arrow keys and Tab walk straight
174
+ // past it and assistive technology never announces an unactivatable row.
175
+ //
176
+ // The effect re-runs only when the group LIST changes (joined below with a
177
+ // separator that cannot occur in a group name), not on every render: the
178
+ // callback closes over `groups`, so a stale one would label rows with the
179
+ // previous grouping.
180
+ const groups = state.routes.map((route) => optionsOf(route.key).group);
181
+ const groupsKey = groups.map((group) => group ?? "").join("");
182
+ useEffect(() => {
183
+ const list = listRef.current;
184
+ if (!list) {
185
+ return;
186
+ }
187
+ if (groups.every((group) => group === undefined)) {
188
+ // Ungrouped is the default and must stay byte-identical to what this
189
+ // navigator did before groups existed — including after a navigator
190
+ // that HAD groups re-rendered without them.
191
+ list.setHeaderFunc(null);
192
+ list.invalidateHeaders();
193
+ return;
194
+ }
195
+ list.setHeaderFunc((row, before) => {
196
+ const group = groups[row.getIndex()];
197
+ // `before` is null for the first row, which is where a header is most
198
+ // wanted; a row whose predecessor carries the same group gets none.
199
+ if (group === undefined ||
200
+ (before !== null && groups[before.getIndex()] === group)) {
201
+ row.setHeader(null);
202
+ return;
203
+ }
204
+ // The structure Adwaita's own stylesheet styles, rather than margins
205
+ // picked by eye. From libadwaita's compiled CSS:
206
+ //
207
+ // .navigation-sidebar > .header > .heading { margin: 12px }
208
+ // .navigation-sidebar > .header.first > .heading { margin-top: 0 }
209
+ // .navigation-sidebar > row { min-height: 36px; padding: 0 8px;
210
+ // margin: 0 6px 2px }
211
+ //
212
+ // so a `.header` box holding a `.heading` label lands its text 12px
213
+ // from the list edge against a row label's 6 + 8 = 14px, and gets
214
+ // 12px of air above and below — which is the alignment and the rhythm
215
+ // GNOME ships, arrived at by matching the selectors rather than the
216
+ // pixels. `.first` is libadwaita's own class for the header that needs
217
+ // no top margin; its CSS carries the comment "No top margin on the
218
+ // first header".
219
+ const header = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0);
220
+ header.addCssClass("header");
221
+ if (before === null) {
222
+ header.addCssClass("first");
223
+ }
224
+ const label = Gtk.Label.new(group);
225
+ label.setXalign(0);
226
+ label.addCssClass("heading");
227
+ header.append(label);
228
+ row.setHeader(header);
229
+ });
230
+ list.invalidateHeaders();
231
+ // eslint-disable-next-line react-hooks/exhaustive-deps
232
+ }, [groupsKey]);
169
233
  // The content HeaderBar's own shape — the thing tasks-app's README named
170
234
  // as a structural-sounding complaint ("one content header shared by the
171
235
  // whole navigator") and the PRD allowed finding a real gap. It is not
@@ -296,7 +360,7 @@ const SidebarNavigator = ({ initialRouteName, screenOptions, sidebarTitle = "Sid
296
360
  // examples/gallery passes only titles and had been
297
361
  // paying it since.
298
362
  if (!options.icon && !options.color && !options.count) {
299
- return (_jsx(GtkListBoxRow, { activatable: true, children: _jsx(GtkLabel, { label: titleOf(route.key, route.name), xalign: 0, marginTop: 8, marginBottom: 8, marginStart: 6, marginEnd: 6 }) }, route.key));
363
+ return (_jsx(GtkListBoxRow, { activatable: true, children: _jsx(GtkLabel, { label: titleOf(route.key, route.name), xalign: 0, marginTop: 8, marginBottom: 8 }) }, route.key));
300
364
  }
301
365
  return (_jsx(AdwActionRow, { title: titleOf(route.key, route.name),
302
366
  // AdwActionRow defaults GtkListBoxRow:activatable
@@ -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 /**\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"]}
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 \"group\",\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 /** Section this row belongs to. Consecutive screens sharing a `group`\n * get one Adwaita section header above the first of them.\n *\n * The header is attached with `GtkListBox.set_header_func`, which is\n * what GNOME's own sidebars use: a header is a DECORATION owned by the\n * row below it, not a row of its own. That distinction is the whole\n * reason to do it this way — a header faked as a non-selectable\n * `GtkListBoxRow` still sits in the list's focus chain, so Tab and the\n * arrow keys stop on it and assistive technology announces a row that\n * cannot be activated. A real header is skipped by both, for free.\n *\n * Grouping follows ROW ORDER, so screens in one group are declared\n * together; a group name reappearing after a gap starts a second\n * header rather than reordering anything. Leave it unset on every\n * screen (the default) and the list is flat, exactly as before. */\n group?: string\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\n // Group headers, through GtkListBox's own `set_header_func` — the\n // mechanism GNOME's sidebars use, and the reason this is not a row: a\n // header attached to a row is a decoration, outside the list's selection\n // model and outside its focus chain, so arrow keys and Tab walk straight\n // past it and assistive technology never announces an unactivatable row.\n //\n // The effect re-runs only when the group LIST changes (joined below with a\n // separator that cannot occur in a group name), not on every render: the\n // callback closes over `groups`, so a stale one would label rows with the\n // previous grouping.\n const groups = state.routes.map((route) => optionsOf(route.key).group)\n const groupsKey = groups.map((group) => group ?? \"\u0001\").join(\"\u0000\")\n\n useEffect(() => {\n const list = listRef.current\n if (!list) {\n return\n }\n if (groups.every((group) => group === undefined)) {\n // Ungrouped is the default and must stay byte-identical to what this\n // navigator did before groups existed — including after a navigator\n // that HAD groups re-rendered without them.\n list.setHeaderFunc(null)\n list.invalidateHeaders()\n return\n }\n list.setHeaderFunc((row, before) => {\n const group = groups[row.getIndex()]\n // `before` is null for the first row, which is where a header is most\n // wanted; a row whose predecessor carries the same group gets none.\n if (\n group === undefined ||\n (before !== null && groups[before.getIndex()] === group)\n ) {\n row.setHeader(null)\n return\n }\n // The structure Adwaita's own stylesheet styles, rather than margins\n // picked by eye. From libadwaita's compiled CSS:\n //\n // .navigation-sidebar > .header > .heading { margin: 12px }\n // .navigation-sidebar > .header.first > .heading { margin-top: 0 }\n // .navigation-sidebar > row { min-height: 36px; padding: 0 8px;\n // margin: 0 6px 2px }\n //\n // so a `.header` box holding a `.heading` label lands its text 12px\n // from the list edge against a row label's 6 + 8 = 14px, and gets\n // 12px of air above and below — which is the alignment and the rhythm\n // GNOME ships, arrived at by matching the selectors rather than the\n // pixels. `.first` is libadwaita's own class for the header that needs\n // no top margin; its CSS carries the comment \"No top margin on the\n // first header\".\n const header = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)\n header.addCssClass(\"header\")\n if (before === null) {\n header.addCssClass(\"first\")\n }\n const label = Gtk.Label.new(group)\n label.setXalign(0)\n label.addCssClass(\"heading\")\n header.append(label)\n row.setHeader(header)\n })\n list.invalidateHeaders()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [groupsKey])\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 {/* No horizontal margin: Adwaita's own\n `.navigation-sidebar > row` already carries\n `padding: 0 8px` on top of the row's `margin:\n 0 6px 2px`, which puts this label at the 14px\n inset the theme intends — and which a\n `.header > .heading`'s 12px margin is designed\n to sit against. An extra margin here misaligns\n the two by exactly itself. */}\n <GtkLabel\n label={titleOf(route.key, route.name)}\n xalign={0}\n marginTop={8}\n marginBottom={8}\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,32 @@
1
+ import type { MeasureHandle } from "../components/measure";
2
+ /**
3
+ * A ref that is also callable, as upstream's is: React sets it through the
4
+ * call, and `measure()` reads it back through a call with no argument.
5
+ *
6
+ * The no-argument read is this platform's own shape rather than upstream's.
7
+ * Upstream's callback ref RETURNS the instance, which React 19 reads as a
8
+ * callback-ref cleanup value and complains about; discriminating on the
9
+ * argument keeps both callers working and keeps React quiet.
10
+ */
11
+ export type AnimatedRef<T> = {
12
+ (): T | null;
13
+ (instance: T | null): void;
14
+ current: T | null;
15
+ };
16
+ export type MeasuredDimensions = {
17
+ x: number;
18
+ y: number;
19
+ width: number;
20
+ height: number;
21
+ pageX: number;
22
+ pageY: number;
23
+ };
24
+ export declare const useAnimatedRef: <T = MeasureHandle>() => AnimatedRef<T>;
25
+ /**
26
+ * Reads a view's geometry: its own size, its position in its parent and its
27
+ * position in the window. Returns null when the view is not mounted or its
28
+ * layout has not been committed yet.
29
+ */
30
+ export declare const measure: <T>(animatedRef: AnimatedRef<T> | {
31
+ current: T | null;
32
+ } | null | undefined) => MeasuredDimensions | null;