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,10 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useImperativeHandle, useLayoutEffect, useRef, } from "react";
3
3
  import { StyleSheet } from "../style/index";
4
- import { getViewBoxComponent, setBoxPassthrough, } from "../gtkx/bridge/index";
4
+ import { getViewBoxComponent, setBoxOnly, setBoxPassthrough, setPointerTarget, } from "../gtkx/bridge/index";
5
5
  import { useResponder } from "../responder/use-responder";
6
6
  import { HostNodeContext } from "./host-node";
7
7
  import { createMeasureHandle } from "./measure";
8
+ import { useFocusable, useFocusController } from "./use-focus";
8
9
  import { useLayoutChild, useRnContainer, } from "./use-layout-child";
9
10
  // Every View is a GtkBox subclass (RnGtkxViewBox) driven by RnGtkxLayout:
10
11
  // Yoga computes the children's rects, the manager's allocate() applies
@@ -16,10 +17,12 @@ import { useLayoutChild, useRnContainer, } from "./use-layout-child";
16
17
  // view-box.ts) — the box is never the pick target while children stay
17
18
  // pickable, and toggling never remounts the subtree;
18
19
  // - box-only: direct children (and thus their subtrees) get
19
- // can-target=false; restored when the mode changes. Nesting another
20
- // pointerEvents INSIDE a box-only view is not supported (the restore
21
- // pass cannot know about it).
22
- export const View = ({ style, pointerEvents, onLayout, children, testID, ref, ...responderProps }) => {
20
+ // can-target=false, restored to what their OWN pointerEvents wants when
21
+ // the mode changes. That is why the bridge owns can-target rather than
22
+ // this component writing it: two things want the same boolean, and a
23
+ // blanket restore to `true` was what made nesting a pointerEvents inside
24
+ // a box-only view unsupported.
25
+ export const View = ({ style, pointerEvents, onLayout, focusable = false, onFocus, onBlur, children, testID, ref, ...responderProps }) => {
23
26
  const widgetRef = useRef(null);
24
27
  const { host, node, cssClass } = useLayoutChild(widgetRef, {
25
28
  style,
@@ -27,6 +30,8 @@ export const View = ({ style, pointerEvents, onLayout, children, testID, ref, ..
27
30
  });
28
31
  useRnContainer(widgetRef, node);
29
32
  useImperativeHandle(ref, () => createMeasureHandle(widgetRef, node), [node]);
33
+ useFocusable(widgetRef, focusable);
34
+ useFocusController(widgetRef, onFocus, onBlur);
30
35
  // Spreading PanResponder's panHandlers onto a View is the portable RN
31
36
  // idiom, so every responder prop arrives here as a rest prop.
32
37
  useResponder(widgetRef, responderProps);
@@ -36,11 +41,15 @@ export const View = ({ style, pointerEvents, onLayout, children, testID, ref, ..
36
41
  if (!widget) {
37
42
  return;
38
43
  }
39
- widget.setCanTarget(mode !== "none");
44
+ setPointerTarget(widget, mode !== "none");
40
45
  setBoxPassthrough(widget, mode === "box-none");
41
46
  }, [mode]);
42
- // box-only walks the current children every commit (the set changes with
43
- // renders) and restores once when the mode moves away.
47
+ // box-only re-runs every commit rather than on change, because the child
48
+ // set moves with the renders and a child that appears while the mode is on
49
+ // has to be masked too. Every View commits, and almost none of them are
50
+ // box-only, so the walk is skipped unless this view is masking now or was
51
+ // masking a moment ago — the one commit after the mode goes away is what
52
+ // gives the children their own modes back.
44
53
  const wasBoxOnly = useRef(false);
45
54
  useLayoutEffect(() => {
46
55
  const widget = widgetRef.current;
@@ -49,11 +58,7 @@ export const View = ({ style, pointerEvents, onLayout, children, testID, ref, ..
49
58
  }
50
59
  const boxOnly = mode === "box-only";
51
60
  if (boxOnly || wasBoxOnly.current) {
52
- let child = widget.getFirstChild();
53
- while (child) {
54
- child.setCanTarget(!boxOnly);
55
- child = child.getNextSibling();
56
- }
61
+ setBoxOnly(widget, boxOnly);
57
62
  }
58
63
  wasBoxOnly.current = boxOnly;
59
64
  });
@@ -1 +1 @@
1
- {"version":3,"file":"view.js","sourceRoot":"","sources":["../../src/components/view.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n type ReactNode,\n type Ref,\n} from \"react\"\nimport { StyleSheet } from \"../style/index\"\nimport type { PointerEventsValue, StyleProp } from \"../contracts\"\nimport {\n getViewBoxComponent,\n GtkBox,\n setBoxPassthrough,\n type Gtk,\n} from \"../gtkx/bridge/index\"\nimport type { ResponderProps } from \"../responder/types\"\nimport { useResponder } from \"../responder/use-responder\"\nimport { HostNodeContext } from \"./host-node\"\nimport { createMeasureHandle, type MeasureHandle } from \"./measure\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\n/** RN's imperative geometry methods, on a `View` ref. */\nexport type ViewHandle = MeasureHandle\n\nexport type ViewProps = ResponderProps & {\n style?: StyleProp\n // RN pointerEvents; the prop wins over style.pointerEvents (RN 0.71+).\n pointerEvents?: PointerEventsValue\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n testID?: string\n // React 19 takes `ref` as an ordinary prop, which is what we want here:\n // forwardRef is booby-trapped on this stack (useEffectEvent never\n // refreshes inside it — see gtkx/bridge/use-signal.ts).\n ref?: Ref<ViewHandle>\n}\n\n// Every View is a GtkBox subclass (RnGtkxViewBox) driven by RnGtkxLayout:\n// Yoga computes the children's rects, the manager's allocate() applies\n// them. Visual styles arrive as a GTK CSS class produced by the style\n// system. pointerEvents maps onto GTK picking:\n// - none: can-target=false — GTK skips the widget WITHOUT descending, the\n// whole subtree is transparent (exact RN semantics);\n// - box-none: the subclass' contains() fails for this widget (see\n// view-box.ts) — the box is never the pick target while children stay\n// pickable, and toggling never remounts the subtree;\n// - box-only: direct children (and thus their subtrees) get\n// can-target=false; restored when the mode changes. Nesting another\n// pointerEvents INSIDE a box-only view is not supported (the restore\n// pass cannot know about it).\nexport const View = ({\n style,\n pointerEvents,\n onLayout,\n children,\n testID,\n ref,\n ...responderProps\n}: ViewProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const { host, node, cssClass } = useLayoutChild(widgetRef, {\n style,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n useImperativeHandle(ref, () => createMeasureHandle(widgetRef, node), [node])\n\n // Spreading PanResponder's panHandlers onto a View is the portable RN\n // idiom, so every responder prop arrives here as a rest prop.\n useResponder(widgetRef, responderProps)\n\n const mode: PointerEventsValue =\n pointerEvents ?? StyleSheet.flatten(style)?.pointerEvents ?? \"auto\"\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n widget.setCanTarget(mode !== \"none\")\n setBoxPassthrough(widget, mode === \"box-none\")\n }, [mode])\n\n // box-only walks the current children every commit (the set changes with\n // renders) and restores once when the mode moves away.\n const wasBoxOnly = useRef(false)\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const boxOnly = mode === \"box-only\"\n if (boxOnly || wasBoxOnly.current) {\n let child = widget.getFirstChild()\n while (child) {\n child.setCanTarget(!boxOnly)\n child = child.getNextSibling()\n }\n }\n wasBoxOnly.current = boxOnly\n })\n\n const ViewBox = getViewBoxComponent() as typeof GtkBox\n return (\n <ViewBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {children}\n </HostNodeContext.Provider>\n </ViewBox>\n )\n}\n\n// RN parity aliases (no notches or status bars on the Linux desktop).\nexport const SafeAreaView = View\n\nexport const StatusBar = (): null => null\n"]}
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../src/components/view.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n type ReactNode,\n type Ref,\n} from \"react\"\nimport { StyleSheet } from \"../style/index\"\nimport type { PointerEventsValue, StyleProp } from \"../contracts\"\nimport {\n getViewBoxComponent,\n GtkBox,\n setBoxOnly,\n setBoxPassthrough,\n setPointerTarget,\n type Gtk,\n} from \"../gtkx/bridge/index\"\nimport type { ResponderProps } from \"../responder/types\"\nimport { useResponder } from \"../responder/use-responder\"\nimport { HostNodeContext } from \"./host-node\"\nimport { createMeasureHandle, type MeasureHandle } from \"./measure\"\nimport { useFocusable, useFocusController } from \"./use-focus\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\n/** RN's imperative geometry methods, on a `View` ref. */\nexport type ViewHandle = MeasureHandle\n\nexport type ViewProps = ResponderProps & {\n style?: StyleProp\n // RN pointerEvents; the prop wins over style.pointerEvents (RN 0.71+).\n pointerEvents?: PointerEventsValue\n onLayout?: (event: LayoutEvent) => void\n // RN's own prop (Android, Windows): puts the view into the keyboard focus\n // chain. Off by default, as it is in RN — a desktop's Tab order is the\n // app's to decide, not something a layout box should join by existing.\n focusable?: boolean\n // react-native-web and react-native-windows both put these on View; RN\n // core has them on TextInput and the touchables. No argument, matching\n // this platform's own TextInput.\n onFocus?: () => void\n onBlur?: () => void\n children?: ReactNode\n testID?: string\n // React 19 takes `ref` as an ordinary prop, which is what we want here:\n // forwardRef is booby-trapped on this stack (useEffectEvent never\n // refreshes inside it — see gtkx/bridge/use-signal.ts).\n ref?: Ref<ViewHandle>\n}\n\n// Every View is a GtkBox subclass (RnGtkxViewBox) driven by RnGtkxLayout:\n// Yoga computes the children's rects, the manager's allocate() applies\n// them. Visual styles arrive as a GTK CSS class produced by the style\n// system. pointerEvents maps onto GTK picking:\n// - none: can-target=false — GTK skips the widget WITHOUT descending, the\n// whole subtree is transparent (exact RN semantics);\n// - box-none: the subclass' contains() fails for this widget (see\n// view-box.ts) — the box is never the pick target while children stay\n// pickable, and toggling never remounts the subtree;\n// - box-only: direct children (and thus their subtrees) get\n// can-target=false, restored to what their OWN pointerEvents wants when\n// the mode changes. That is why the bridge owns can-target rather than\n// this component writing it: two things want the same boolean, and a\n// blanket restore to `true` was what made nesting a pointerEvents inside\n// a box-only view unsupported.\nexport const View = ({\n style,\n pointerEvents,\n onLayout,\n focusable = false,\n onFocus,\n onBlur,\n children,\n testID,\n ref,\n ...responderProps\n}: ViewProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const { host, node, cssClass } = useLayoutChild(widgetRef, {\n style,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n useImperativeHandle(ref, () => createMeasureHandle(widgetRef, node), [node])\n\n useFocusable(widgetRef, focusable)\n useFocusController(widgetRef, onFocus, onBlur)\n\n // Spreading PanResponder's panHandlers onto a View is the portable RN\n // idiom, so every responder prop arrives here as a rest prop.\n useResponder(widgetRef, responderProps)\n\n const mode: PointerEventsValue =\n pointerEvents ?? StyleSheet.flatten(style)?.pointerEvents ?? \"auto\"\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n setPointerTarget(widget, mode !== \"none\")\n setBoxPassthrough(widget, mode === \"box-none\")\n }, [mode])\n\n // box-only re-runs every commit rather than on change, because the child\n // set moves with the renders and a child that appears while the mode is on\n // has to be masked too. Every View commits, and almost none of them are\n // box-only, so the walk is skipped unless this view is masking now or was\n // masking a moment ago — the one commit after the mode goes away is what\n // gives the children their own modes back.\n const wasBoxOnly = useRef(false)\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const boxOnly = mode === \"box-only\"\n if (boxOnly || wasBoxOnly.current) {\n setBoxOnly(widget, boxOnly)\n }\n wasBoxOnly.current = boxOnly\n })\n\n const ViewBox = getViewBoxComponent() as typeof GtkBox\n return (\n <ViewBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {children}\n </HostNodeContext.Provider>\n </ViewBox>\n )\n}\n\n// RN parity aliases (no notches or status bars on the Linux desktop).\nexport const SafeAreaView = View\n\nexport const StatusBar = (): null => null\n"]}
@@ -1,6 +1,7 @@
1
- import { type ComponentType, type ReactElement } from "react";
1
+ import { type ComponentType, type ReactElement, type ReactNode } from "react";
2
2
  import type { StyleProp } from "../contracts";
3
- import { type ScrollViewHandle, type ScrollViewProps } from "./scroll-view";
3
+ import { type ScrollHandle, type ScrollViewProps } from "./scroll-view";
4
+ import type { LayoutEvent } from "./use-layout-child";
4
5
  export type ListRenderItemInfo<T> = {
5
6
  item: T;
6
7
  index: number;
@@ -21,7 +22,7 @@ export type ViewabilityConfig = {
21
22
  viewAreaCoveragePercentThreshold?: number;
22
23
  minimumViewTime?: number;
23
24
  };
24
- export type VirtualizedListHandle = ScrollViewHandle & {
25
+ export type VirtualizedListHandle = ScrollHandle & {
25
26
  scrollToIndex(params: {
26
27
  index: number;
27
28
  viewPosition?: number;
@@ -38,11 +39,27 @@ export type VirtualizedListHandle = ScrollViewHandle & {
38
39
  }): void;
39
40
  };
40
41
  export type VirtualizedListProps<T> = Omit<ScrollViewProps, "children"> & {
41
- data: readonly T[];
42
+ data: readonly T[] | unknown;
43
+ /**
44
+ * RN's `VirtualizedList` accessor pair. Supplying them makes `data` opaque:
45
+ * the list asks `getItemCount(data)` for the length and `getItem(data, i)`
46
+ * for a row, and reads nothing else out of it. Left out (FlatList,
47
+ * SectionList) `data` is indexed directly.
48
+ *
49
+ * They are honoured LAZILY — `getItem` is called for the rows the window
50
+ * actually mounts, never for the whole range — because the point of the
51
+ * pair upstream is that the source need not be materialised.
52
+ *
53
+ * `never` for the `data` parameter is what makes an accessor written
54
+ * against a CONCRETE source (`(rows: Row[], i) => rows[i]`) assignable
55
+ * here: parameters are contravariant, and RN's own types say `any`.
56
+ */
57
+ getItem?: (data: never, index: number) => T;
58
+ getItemCount?: (data: never) => number;
42
59
  renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null;
43
60
  keyExtractor?: (item: T, index: number) => string;
44
61
  estimatedItemSize?: number;
45
- getItemLayout?: (data: readonly T[], index: number) => ItemLayout;
62
+ getItemLayout?: (data: never, index: number) => ItemLayout;
46
63
  windowSize?: number;
47
64
  initialNumToRender?: number;
48
65
  maxToRenderPerBatch?: number;
@@ -51,6 +68,29 @@ export type VirtualizedListProps<T> = Omit<ScrollViewProps, "children"> & {
51
68
  inverted?: boolean;
52
69
  refreshing?: boolean;
53
70
  onRefresh?: () => void;
71
+ /**
72
+ * RN's per-cell wrapper. The list still decides where a cell goes — the
73
+ * absolute `style` and the `onLayout` that measures it are handed in and
74
+ * must be applied — and this component owns everything else about the
75
+ * container, which is how a library adds a transform or a context to every
76
+ * row without touching `renderItem`.
77
+ *
78
+ * `react-native-draggable-flatlist` is the reason it exists here: its whole
79
+ * design is a cell renderer that translates the row being dragged and puts
80
+ * the "am I the active cell" context around it.
81
+ *
82
+ * NOT applied to a STICKY cell (`stickyHeaderIndices`): pinning works by
83
+ * reordering the cell's real GTK widget, so the sticky container has to be
84
+ * the cell. See docs/api.md.
85
+ */
86
+ CellRendererComponent?: ComponentType<{
87
+ cellKey: string;
88
+ index: number;
89
+ item: T;
90
+ style?: StyleProp;
91
+ onLayout?: (event: LayoutEvent) => void;
92
+ children?: ReactNode;
93
+ }>;
54
94
  ItemSeparatorComponent?: ComponentType | null;
55
95
  ListHeaderComponent?: ComponentType | ReactElement | null;
56
96
  ListFooterComponent?: ComponentType | ReactElement | null;
@@ -9,6 +9,8 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
9
9
  import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from "react";
10
10
  import { perfAddTime, perfBurst, perfCount, perfEnabled, perfNow, } from "../perf";
11
11
  import { ActivityIndicator } from "./activity-indicator";
12
+ import { registerHandleAlias } from "./measure";
13
+ import { scrollPhaseSink, setScrollPhaseSink } from "./scroll-phase";
12
14
  import { ScrollView, StickySlot, } from "./scroll-view";
13
15
  import { View } from "./view";
14
16
  const renderAux = (component) => {
@@ -23,7 +25,26 @@ const renderAux = (component) => {
23
25
  };
24
26
  // First index whose offset-end is past `target` (binary search over prefix
25
27
  // sums; offsets has count+1 entries).
28
+ //
29
+ // The `target <= offsets[0]` guard is RN's, not an optimisation:
30
+ // `elementsThatOverlapOffsets` treats the FIRST cell's start as INCLUSIVE and
31
+ // every later cell's as exclusive (`mid === 0 && currentOffset <
32
+ // scaledOffsetStart`), and that asymmetry is the whole reason a list of
33
+ // ZERO-LENGTH cells resolves offset 0 to index 0 there. Without it the search
34
+ // walks past every cell whose end is `<= target` — and a zero-length cell at
35
+ // `target` always is — so it lands on the END of the run and the list mounts
36
+ // only its last row.
37
+ //
38
+ // That is not a hypothetical shape. A `FlatList` whose cells each contain one
39
+ // `position: absolute` child has zero flow height in EVERY cell, which is
40
+ // exactly how `react-native-reanimated-dnd`'s `Sortable` renders (rows are
41
+ // absolutely positioned and driven by a per-frame animated `top`), and how
42
+ // any list that positions its own rows does. RN renders all of them; we
43
+ // rendered one. See docs/research/dnd-differential.md.
26
44
  const indexAt = (offsets, target) => {
45
+ if (target <= offsets[0]) {
46
+ return 0;
47
+ }
27
48
  let low = 0;
28
49
  let high = offsets.length - 2;
29
50
  while (low < high) {
@@ -56,8 +77,28 @@ const CellMountProbe = () => {
56
77
  }, []);
57
78
  return null;
58
79
  };
59
- const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estimatedItemSize = 44, getItemLayout, windowSize = DEFAULT_WINDOW_SIZE, initialNumToRender = 10, maxToRenderPerBatch = 10, updateCellsBatchingPeriod = 50, extraData, inverted = false, refreshing = false, onRefresh, ItemSeparatorComponent, ListHeaderComponent, ListFooterComponent, ListEmptyComponent, onEndReached, onEndReachedThreshold = 0.1, onViewableItemsChanged, viewabilityConfig, onScroll, onContentSizeChange, onLayout, horizontal = false, stickyHeaderIndices, ...scrollProps }, ref) => {
60
- const count = data.length;
80
+ const VirtualizedListInner = forwardRef(({ data, getItem, getItemCount, renderItem, keyExtractor, estimatedItemSize = 44, getItemLayout, windowSize = DEFAULT_WINDOW_SIZE, initialNumToRender = 10, maxToRenderPerBatch = 10, updateCellsBatchingPeriod = 50, extraData, inverted = false, refreshing = false, onRefresh, CellRendererComponent, ItemSeparatorComponent, ListHeaderComponent, ListFooterComponent, ListEmptyComponent, onEndReached, onEndReachedThreshold = 0.1, onViewableItemsChanged, viewabilityConfig, onScroll, onContentSizeChange, onLayout, horizontal = false, stickyHeaderIndices, ...scrollProps }, ref) => {
81
+ // The three reads the list makes of its data source, in the one place
82
+ // that knows which shape it was given. Everything below goes through
83
+ // them, so the windowing, the prefix sums, the viewability pass and the
84
+ // scroll handle are identical whether the source is an array or RN's
85
+ // opaque value plus accessors.
86
+ const source = data;
87
+ const count = getItemCount ? getItemCount(data) : source.length;
88
+ const itemAt = (index) => getItem ? getItem(data, index) : source[index];
89
+ const indexOfItem = (item) => {
90
+ if (!getItem) {
91
+ return source.indexOf(item);
92
+ }
93
+ // RN's own VirtualizedList scans for `scrollToItem` too — an opaque
94
+ // source has no index to ask.
95
+ for (let index = 0; index < count; index += 1) {
96
+ if (itemAt(index) === item) {
97
+ return index;
98
+ }
99
+ }
100
+ return -1;
101
+ };
61
102
  const scrollRef = useRef(null);
62
103
  const measured = useRef([]);
63
104
  const [version, setVersion] = useState(0);
@@ -267,7 +308,15 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
267
308
  : { index, viewPosition, animated: params.animated };
268
309
  scrollToAxis(offsetForIndex(index, viewPosition), params.animated);
269
310
  };
270
- useImperativeHandle(ref, () => ({
311
+ // The handle stands for the ScrollView this list renders: `findNodeHandle`
312
+ // on a list resolves to it (RN resolves a FlatList's the same way), and so
313
+ // does `measureLayout(listRef, …)` from another view. It gains no
314
+ // `measure()` of its own — see the note on VirtualizedListHandle.
315
+ const registerAlias = (handle) => {
316
+ registerHandleAlias(handle, () => scrollRef.current);
317
+ return handle;
318
+ };
319
+ useImperativeHandle(ref, () => registerAlias({
271
320
  // Any direct scroll cancels a pending scrollToIndex correction. The
272
321
  // caller always speaks RN-space offsets; inverted translates the main
273
322
  // axis into raw adjustment space.
@@ -298,7 +347,7 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
298
347
  },
299
348
  scrollToIndex,
300
349
  scrollToItem: ({ item, viewPosition, animated }) => {
301
- const index = data.indexOf(item);
350
+ const index = indexOfItem(item);
302
351
  if (index >= 0) {
303
352
  scrollToIndex({ index, viewPosition, animated });
304
353
  }
@@ -404,7 +453,7 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
404
453
  viewable = true;
405
454
  }
406
455
  if (viewable) {
407
- const item = data[index];
456
+ const item = itemAt(index);
408
457
  eligible.set(keyOf(item, index), { item, index });
409
458
  }
410
459
  }
@@ -494,6 +543,24 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
494
543
  }
495
544
  }
496
545
  };
546
+ const toExposedEvent = (event) => {
547
+ if (!inverted) {
548
+ return event;
549
+ }
550
+ const raw = horizontal
551
+ ? event.nativeEvent.contentOffset.x
552
+ : event.nativeEvent.contentOffset.y;
553
+ const exposed = Math.max(0, maxRawScroll() - raw);
554
+ return {
555
+ nativeEvent: {
556
+ ...event.nativeEvent,
557
+ contentOffset: {
558
+ x: horizontal ? exposed : event.nativeEvent.contentOffset.x,
559
+ y: horizontal ? event.nativeEvent.contentOffset.y : exposed,
560
+ },
561
+ },
562
+ };
563
+ };
497
564
  const handleScroll = (event) => {
498
565
  const raw = horizontal
499
566
  ? event.nativeEvent.contentOffset.x
@@ -501,29 +568,25 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
501
568
  scrollY.current = raw;
502
569
  if (inverted) {
503
570
  exposedOffset.current = Math.max(0, maxRawScroll() - raw);
504
- // The caller sees RN-space offsets: contentOffset 0 is the far end
505
- // where the data starts (a chat's latest message).
506
- onScroll?.({
507
- nativeEvent: {
508
- ...event.nativeEvent,
509
- contentOffset: {
510
- x: horizontal
511
- ? exposedOffset.current
512
- : event.nativeEvent.contentOffset.x,
513
- y: horizontal
514
- ? event.nativeEvent.contentOffset.y
515
- : exposedOffset.current,
516
- },
517
- },
518
- });
519
- }
520
- else {
521
- onScroll?.(event);
522
571
  }
572
+ onScroll?.(toExposedEvent(event));
523
573
  updateRange();
524
574
  recomputeViewability();
525
575
  maybeFireEndReached();
526
576
  };
577
+ // Reanimated attaches one phase sink to the handler it hands us. This
578
+ // wrapper used to forward only the function call and silently dropped the
579
+ // sink, so every FlatList/VirtualizedList received onScroll but NONE of
580
+ // onBeginDrag/onEndDrag/momentum — direct ScrollView tests could not catch
581
+ // it. Carry the seam through the composite, translating inverted offsets
582
+ // exactly as the ordinary event path does.
583
+ const upstreamPhaseSink = scrollPhaseSink(onScroll);
584
+ if (upstreamPhaseSink) {
585
+ setScrollPhaseSink(handleScroll, {
586
+ wants: (phase) => upstreamPhaseSink.wants(phase),
587
+ deliver: (phase, event) => upstreamPhaseSink.deliver(phase, toExposedEvent(event)),
588
+ });
589
+ }
527
590
  const handleLayout = (event) => {
528
591
  onLayout?.(event);
529
592
  viewportH.current = horizontal
@@ -590,7 +653,7 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
590
653
  }
591
654
  }
592
655
  const renderCell = (index) => {
593
- const item = data[index];
656
+ const item = itemAt(index);
594
657
  const key = keyOf(item, index);
595
658
  // Vertical cells stretch across the width and sit at their offset;
596
659
  // horizontal cells stretch across the height and take their own
@@ -605,6 +668,9 @@ const VirtualizedListInner = forwardRef(({ data, renderItem, keyExtractor, estim
605
668
  if (stickySorted.includes(index)) {
606
669
  return (_jsx(StickySlot, { stickyKey: `cell-${key}`, top: cellStart(index), style: cellStyle, onLayout: measure, children: body }, key));
607
670
  }
671
+ if (CellRendererComponent) {
672
+ return (_jsx(CellRendererComponent, { cellKey: key, index: index, item: item, style: cellStyle, onLayout: measure, children: body }, key));
673
+ }
608
674
  return (_jsx(View, { style: cellStyle, onLayout: measure, children: body }, key));
609
675
  };
610
676
  const cells = [];