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,35 +1,43 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useLayoutEffect, useRef } from "react";
2
+ import { useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
3
+ import { INSET_PROPERTIES, insetRefusalReason, insetTranslation, } from "../style/absolute-insets";
4
+ import { drivenSizeRefusal, SIZE_PROPERTIES, } from "../style/animated-size";
5
+ import { DRIVEABLE_COLOR_PROPERTIES, driveableColorsToCss, } from "../style/imperative-css";
6
+ import { StyleSheet } from "../style/index";
3
7
  import { createAnimated } from "../animated/index";
4
- import { GLib, GtkBox, queueAllocate } from "../gtkx/bridge/index";
8
+ import { createWidgetCss, GtkBox, queueAllocate, setBoxPassthrough, setPointerTarget, setZIndex, } from "../gtkx/bridge/index";
5
9
  import { useResponder } from "../responder/use-responder";
6
- import { HostNodeContext } from "./host-node";
10
+ import { applyDrivenSize, releaseDrivenSize } from "./driven-size";
11
+ import { glibScheduler } from "./frame-scheduler";
12
+ import { HostNodeContext, useHostNode } from "./host-node";
13
+ import { Image } from "./image";
14
+ import { createMeasureHandle, widgetForHandle, } from "./measure";
7
15
  import { setStoredTransform } from "./rect-store";
8
- import { useLayoutChild, useRnContainer, } from "./use-layout-child";
9
- // ~60fps one-shot ticks off the GLib main loop. A frame-clock driver (per
10
- // window) is a later optimization; timeouts keep the driver widget-free.
11
- const glibScheduler = {
12
- schedule(callback) {
13
- let cancelled = false;
14
- const id = GLib.timeoutAdd(GLib.PRIORITY_DEFAULT, 16, () => {
15
- if (!cancelled) {
16
- callback(Number(GLib.getMonotonicTime()) / 1000);
17
- }
18
- return false;
19
- });
20
- return () => {
21
- if (!cancelled) {
22
- cancelled = true;
23
- GLib.Source.remove(id);
24
- }
25
- };
26
- },
27
- };
16
+ import { ScrollView } from "./scroll-view";
17
+ import { Text } from "./text";
18
+ import { nodeForWidget, useLayoutChild, useRnContainer, } from "./use-layout-child";
28
19
  const api = createAnimated(glibScheduler);
29
20
  const isAnimatedNode = (value) => typeof value === "object" &&
30
21
  value !== null &&
31
22
  typeof value.addListener === "function" &&
32
23
  typeof value.__getValue === "function";
24
+ const toNumber = (value) => typeof value === "number" ? value : Number.parseFloat(value);
25
+ // What a slot actually writes into the transform array. An ordinary slot
26
+ // writes its value; a derived inset slot writes its offset from the committed
27
+ // base, which is what makes an absolute `top` a translation.
28
+ const slotValue = (slot, value) => {
29
+ if (!slot.inset) {
30
+ return value;
31
+ }
32
+ const numeric = toNumber(value);
33
+ // A non-numeric inset (a percentage produced mid-animation) has no offset
34
+ // from a point base. Holding the last position beats jumping to NaN, and
35
+ // the leaf never becomes driveable in the first place unless it starts
36
+ // numeric — see style/absolute-insets.ts and reanimated-compat/style.ts.
37
+ return Number.isFinite(numeric)
38
+ ? slot.inset.sign * (numeric - slot.inset.base)
39
+ : 0;
40
+ };
33
41
  // Stable per-node identity for the effect's dependency key: Animated nodes
34
42
  // are objects, and the bindings must be rebuilt when the node behind a slot
35
43
  // changes — not on every render that rebuilds an equal-looking array.
@@ -43,6 +51,62 @@ const nodeId = (value) => {
43
51
  }
44
52
  return id;
45
53
  };
54
+ // One warning per inset property per session, like every other channel here.
55
+ const warnedInsets = new Set();
56
+ const insetTranslationOf = (property) => property === "top" || property === "bottom" ? "translateY" : "translateX";
57
+ const warnInsetNotTranslatable = (property, reason) => {
58
+ if (warnedInsets.has(property)) {
59
+ return;
60
+ }
61
+ warnedInsets.add(property);
62
+ const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
63
+ if (isProduction) {
64
+ return;
65
+ }
66
+ const spec = insetTranslationOf(property);
67
+ console.warn(`react-native-gtkx: an animated \`${property}\` cannot be driven here. ` +
68
+ (reason
69
+ ? `The node IS absolutely positioned, but ${reason}, so there is no translation that reproduces it. `
70
+ : "`top`/`left`/`right`/`bottom` are driven at frame rate only on a node whose own `position` is " +
71
+ '"absolute", where moving it is exactly a translation and touches no sibling. Anything else needs a ' +
72
+ "Yoga pass over the container plus its commit walk, which costs what the CONTAINER costs (71 µs at " +
73
+ "five children, 509 µs at three hundred) against a transform's 0.12 µs. ") +
74
+ `Animate \`transform: [{ ${spec}: … }]\` instead. ` +
75
+ "The value is still applied on the next React render. See docs/api.md.");
76
+ };
77
+ /** @internal Test seam: the warning is once per property per session by design. */
78
+ export const resetAnimatedInsetWarnings = () => {
79
+ warnedInsets.clear();
80
+ };
81
+ // Same policy for sizes: once per property per session, and the reason is the
82
+ // actionable part — the rule refuses for one of eight named reasons and each
83
+ // of them has a different fix.
84
+ const warnedSizes = new Set();
85
+ const warnSizeNotDriveable = (property, reason) => {
86
+ if (warnedSizes.has(property)) {
87
+ return;
88
+ }
89
+ warnedSizes.add(property);
90
+ const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
91
+ if (isProduction) {
92
+ return;
93
+ }
94
+ const scale = property === "width" ? "scaleX" : "scaleY";
95
+ console.warn(`react-native-gtkx: an animated \`${property}\` cannot be driven here, because ${reason}. ` +
96
+ `\`width\`/\`height\` run at frame rate only where the change stops at the node that owns it — the ` +
97
+ "node's own subtree is then re-laid-out pinned to the driven value (7.1 µs for a leaf, 21.7 µs with " +
98
+ "wrapped text, the same at five children and at three hundred) and one allocation puts it on screen. " +
99
+ "Anything else needs a Yoga pass over the container plus its commit walk, which costs what the " +
100
+ "CONTAINER costs: 71 µs at five children, 509 µs at three hundred, against a transform's 0.6 µs. " +
101
+ `The closest transform is \`transform: [{ ${scale}: … }]\`, but it is NOT the same thing — a scale ` +
102
+ "grows about the view's centre, so the box moves as it grows, and it scales the content with the box " +
103
+ "instead of re-laying it out, so text stretches rather than re-wrapping. " +
104
+ "The new value is applied on the next React render either way. See docs/api.md.");
105
+ };
106
+ /** @internal Test seam: the warning is once per property per session by design. */
107
+ export const resetAnimatedSizeWarnings = () => {
108
+ warnedSizes.clear();
109
+ };
46
110
  const splitAnimated = (style) => {
47
111
  const flat = {};
48
112
  const collect = (entry) => {
@@ -57,11 +121,31 @@ const splitAnimated = (style) => {
57
121
  };
58
122
  collect(style);
59
123
  let opacity = null;
124
+ let zIndex = null;
60
125
  const slots = [];
126
+ const colors = [];
127
+ const sizes = [];
61
128
  if (isAnimatedNode(flat.opacity)) {
62
129
  opacity = flat.opacity;
63
130
  delete flat.opacity;
64
131
  }
132
+ // `zIndex` is in `useSortable`'s style object on every frame and changes
133
+ // value about twice per drag. It takes the same door opacity does — one
134
+ // widget-level write, no Yoga, no CSS — so there is nothing to refuse.
135
+ if (isAnimatedNode(flat.zIndex)) {
136
+ zIndex = flat.zIndex;
137
+ delete flat.zIndex;
138
+ }
139
+ for (const property of DRIVEABLE_COLOR_PROPERTIES) {
140
+ const value = flat[property];
141
+ if (isAnimatedNode(value)) {
142
+ colors.push({ property, node: value });
143
+ // Removed from the static style so the shared class registry never sees
144
+ // a per-frame value: its memoisation stays keyed on styles that change
145
+ // when React renders, which is the only thing it is good at.
146
+ delete flat[property];
147
+ }
148
+ }
65
149
  if (Array.isArray(flat.transform)) {
66
150
  for (const part of flat.transform) {
67
151
  const key = Object.keys(part)[0];
@@ -79,39 +163,166 @@ const splitAnimated = (style) => {
79
163
  // each other every render.
80
164
  delete flat.transform;
81
165
  }
82
- return { staticStyle: flat, opacity, slots };
166
+ // Slice 2b: an animated inset on an absolutely positioned node becomes a
167
+ // derived translate. It is the one layout property with an exact transform
168
+ // equivalent, because an out-of-flow node's position affects nothing but
169
+ // where it is drawn — see src/style/absolute-insets.ts for the measured
170
+ // rule and for the configurations where the equivalence does NOT hold.
171
+ //
172
+ // Three things happen here at once, and all three are load-bearing:
173
+ //
174
+ // - THE BASE. The inset's current value is written back into the static
175
+ // style as a plain number, so Yoga is given a real position and the
176
+ // committed rect the parent's allocate reads is the base the offset is
177
+ // measured from. Nothing about the shadow tree changes.
178
+ // - THE REBASE. This runs on every render, so when React commits a layout
179
+ // with a different inset, the base moves and the offset is recomputed
180
+ // against it in the SAME commit — the Yoga write and the offset write
181
+ // are both layout effects, and the engine's flush is a microtask, so GTK
182
+ // never gets a frame in between and there is no jump.
183
+ // - THE ORDER. Derived slots go FIRST. The array's leftmost entry is the
184
+ // outermost matrix (style/transform.ts), so a derived translate is
185
+ // applied to the point LAST — it moves the already-rotated, already-
186
+ // scaled box in the parent's coordinates, exactly as a layout position
187
+ // does. Appending instead would put the user's scale on the OUTSIDE and
188
+ // multiply the offset by it.
189
+ const insetSlots = [];
190
+ for (const property of INSET_PROPERTIES) {
191
+ const value = flat[property];
192
+ if (!isAnimatedNode(value)) {
193
+ continue;
194
+ }
195
+ const base = toNumber(value.__getValue());
196
+ const translation = Number.isFinite(base)
197
+ ? insetTranslation(flat, property)
198
+ : null;
199
+ if (!translation) {
200
+ // Not an equivalence. Resolve to the current value so the static style
201
+ // is at least correct on this render, and keep slice 2's refusal loud.
202
+ flat[property] = Number.isFinite(base) ? base : undefined;
203
+ warnInsetNotTranslatable(property, insetRefusalReason(flat, property));
204
+ continue;
205
+ }
206
+ flat[property] = base;
207
+ insetSlots.push({
208
+ key: translation.transform,
209
+ node: value,
210
+ value: 0,
211
+ inset: { base, sign: translation.sign },
212
+ });
213
+ }
214
+ // Slice 3: an animated `width`/`height` where the change is confined to the
215
+ // node that owns it. Unlike an inset this is NOT re-expressed as a
216
+ // transform — there is no transform that reproduces a size change, which
217
+ // docs/research/animated-size.md §6 measured rather than assumed — so the
218
+ // slot carries the property itself and the view layer drives it through a
219
+ // pinned pass over the node's own subtree.
220
+ //
221
+ // THE REBASE, exactly as above and for the same reason: the driven value is
222
+ // written back into the static style as a plain number, so Yoga is given the
223
+ // size the animation is currently showing and the committed rect the
224
+ // override sits on catches up in the SAME commit. Both writes are layout
225
+ // effects and the engine's flush is a microtask, so GTK never gets a frame
226
+ // in between.
227
+ //
228
+ // WHETHER it can be driven is not decided here. The answer depends on the
229
+ // CONTAINER's `flexDirection`, its `alignItems` and where its own size comes
230
+ // from, none of which are in this style object or in this component's props
231
+ // — it is asked of the layout tree in useAnimatedBinding, which is the only
232
+ // place the tree exists.
233
+ for (const property of SIZE_PROPERTIES) {
234
+ const value = flat[property];
235
+ if (!isAnimatedNode(value)) {
236
+ continue;
237
+ }
238
+ const base = toNumber(value.__getValue());
239
+ if (!Number.isFinite(base)) {
240
+ // A percentage produced mid-animation has no point base to lay out at.
241
+ // Dropped rather than driven, and the leaf never becomes driveable in
242
+ // the first place unless it starts numeric — reanimated-compat/style.ts.
243
+ flat[property] = undefined;
244
+ continue;
245
+ }
246
+ flat[property] = base;
247
+ sizes.push({ property, node: value, base });
248
+ }
249
+ return {
250
+ staticStyle: flat,
251
+ opacity,
252
+ zIndex,
253
+ slots: insetSlots.length > 0 ? [...insetSlots, ...slots] : slots,
254
+ colors,
255
+ sizes,
256
+ };
83
257
  };
84
- // Animated values bypass React entirely: listeners write straight to the
85
- // widget (opacity) and to the rect store (the transform applied by the
86
- // parent's allocate), on top of the engine-committed base rect — the fast
87
- // path measured in the spike. Animation frames never touch Yoga.
88
- const AnimatedView = ({ style, children, onLayout, testID, ...responderProps }) => {
89
- const widgetRef = useRef(null);
90
- const { staticStyle, opacity, slots } = splitAnimated(style);
91
- const { host, node, cssClass } = useLayoutChild(widgetRef, {
92
- style: staticStyle,
93
- onLayout,
94
- });
95
- useRnContainer(widgetRef, node);
96
- useResponder(widgetRef, responderProps);
258
+ /**
259
+ * The imperative write path, and the whole of it.
260
+ *
261
+ * Animated values bypass React entirely: listeners write straight to the
262
+ * widget (opacity), to the rect store (the transform applied by the parent's
263
+ * allocate) on top of the engine-committed base rect, and to a CSS provider
264
+ * private to the widget (colours). Animation frames never touch Yoga, and
265
+ * never touch the shared stylesheet.
266
+ *
267
+ * Nothing here is View-specific, which is why it is a hook rather than a
268
+ * paragraph inside `AnimatedView`: it needs the CHILD's widget (for
269
+ * `setOpacity` and the rect store) and its PARENT's (for `queueAllocate`),
270
+ * and any component that can produce those two can be animated. The child is
271
+ * reached through a getter rather than a ref because a wrapper does not own
272
+ * the widget — it reads it back out of whatever handle the wrapped component
273
+ * exposed, which may only exist after that component's own layout effects.
274
+ */
275
+ const useAnimatedBinding = (getWidget, host, opacity, zIndex, slots, colors, sizes,
276
+ // Anything else that must force a rebind. `AnimatedView` passes its layout
277
+ // node; the generic wrapper has nothing to add.
278
+ rebindKey) => {
97
279
  // The effect reads the slots of the render that (re)armed it; the values
98
280
  // inside are then owned by the listeners.
99
281
  const slotsRef = useRef(slots);
100
282
  slotsRef.current = slots;
283
+ const colorsRef = useRef(colors);
284
+ colorsRef.current = colors;
285
+ const sizesRef = useRef(sizes);
286
+ sizesRef.current = sizes;
287
+ const getWidgetRef = useRef(getWidget);
288
+ getWidgetRef.current = getWidget;
289
+ const hostRef = useRef(host);
290
+ hostRef.current = host;
291
+ const parentWidgetRef = host.widgetRef;
101
292
  // Rebind on a change of shape (which transforms, in which order), of the
102
293
  // node behind an animated entry, or of a static entry's value — not on
103
294
  // every render that rebuilds an equal-looking array.
295
+ //
296
+ // A derived inset slot adds its BASE to the key, and that is what makes the
297
+ // rebase happen: React committing a new `top` moves the base, which re-arms
298
+ // this effect in the same commit that gives Yoga the new position.
104
299
  const bindingKey = (opacity ? `opacity#${nodeId(opacity)}` : "") +
300
+ (zIndex ? `zIndex#${nodeId(zIndex)}` : "") +
105
301
  slots
106
- .map((slot) => slot.node
107
- ? `|${slot.key}#${nodeId(slot.node)}`
108
- : `|${slot.key}=${String(slot.value)}`)
302
+ .map((slot) => {
303
+ const inset = slot.inset ? `~${slot.inset.sign}:${slot.inset.base}` : "";
304
+ return slot.node
305
+ ? `|${slot.key}#${nodeId(slot.node)}${inset}`
306
+ : `|${slot.key}=${String(slot.value)}`;
307
+ })
308
+ .join("") +
309
+ colors.map((slot) => `|${slot.property}#${nodeId(slot.node)}`).join("") +
310
+ // The base is in the key for the same reason a derived inset's is: React
311
+ // committing a new size moves it, and re-arming here is what hands the
312
+ // override back to the engine in the same commit that gives Yoga the new
313
+ // value.
314
+ sizes
315
+ .map((slot) => `|${slot.property}#${nodeId(slot.node)}~${slot.base}`)
109
316
  .join("");
110
317
  useLayoutEffect(() => {
111
- const widget = widgetRef.current;
318
+ const widget = getWidgetRef.current();
112
319
  if (!widget) {
113
320
  return;
114
321
  }
322
+ // Captured for the CLEANUP only: by then the ref may already have been
323
+ // detached, and the container to re-allocate is the one this binding was
324
+ // made against. Every live write below re-reads the ref instead.
325
+ const boundParentWidget = parentWidgetRef.current;
115
326
  // The live transform array, mutated in place: an Animated write must
116
327
  // stay one numeric store plus one queued allocation — no React render,
117
328
  // no Yoga pass, no allocation per frame beyond the composed matrix.
@@ -120,10 +331,18 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
120
331
  // rect store unclamped — the parent's measure ignores children, so a
121
332
  // transformed child draws past the boundary over its neighbors without
122
333
  // moving a single ancestor.
123
- const parts = slotsRef.current.map((slot) => ({ [slot.key]: slot.value }));
334
+ const parts = slotsRef.current.map((slot) => ({
335
+ [slot.key]: slot.node
336
+ ? slotValue(slot, slot.node.__getValue())
337
+ : slot.value,
338
+ }));
124
339
  const flush = () => {
340
+ const widget = getWidgetRef.current();
341
+ if (!widget) {
342
+ return;
343
+ }
125
344
  setStoredTransform(widget, parts);
126
- const parentWidget = host.widgetRef.current;
345
+ const parentWidget = parentWidgetRef.current;
127
346
  if (parentWidget) {
128
347
  queueAllocate(parentWidget);
129
348
  }
@@ -137,7 +356,7 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
137
356
  subscriptions.push({
138
357
  node: slot.node,
139
358
  id: slot.node.addListener(({ value }) => {
140
- part[slot.key] = value;
359
+ part[slot.key] = slotValue(slot, value);
141
360
  flush();
142
361
  }),
143
362
  });
@@ -145,7 +364,7 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
145
364
  if (opacity) {
146
365
  const applyOpacity = (value) => {
147
366
  const numeric = typeof value === "number" ? value : parseFloat(value);
148
- widgetRef.current?.setOpacity(Math.min(1, Math.max(0, numeric)));
367
+ getWidgetRef.current()?.setOpacity(Math.min(1, Math.max(0, numeric)));
149
368
  };
150
369
  subscriptions.push({
151
370
  node: opacity,
@@ -155,19 +374,313 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
155
374
  });
156
375
  applyOpacity(opacity.__getValue());
157
376
  }
377
+ // The container's paint and pick order (gtkx/bridge/view-box.ts). The
378
+ // write drops the container's cached order and queues a redraw; nothing
379
+ // is re-measured and nothing is re-allocated, because zIndex changes
380
+ // neither.
381
+ if (zIndex) {
382
+ const applyZIndex = (value) => {
383
+ const widget = getWidgetRef.current();
384
+ if (!widget) {
385
+ return;
386
+ }
387
+ const numeric = typeof value === "number" ? value : parseFloat(value);
388
+ setZIndex(widget, Number.isFinite(numeric) ? Math.round(numeric) : 0);
389
+ };
390
+ subscriptions.push({
391
+ node: zIndex,
392
+ id: zIndex.addListener(({ value }) => {
393
+ applyZIndex(value);
394
+ }),
395
+ });
396
+ applyZIndex(zIndex.__getValue());
397
+ }
398
+ // Colours take the other imperative door: a CSS provider private to this
399
+ // widget, replaced in place. Deliberately NOT the shared class registry —
400
+ // that one memoises by CSS text, so a driven colour would mint a class per
401
+ // frame into a document GTK re-parses whole and that is never pruned
402
+ // (docs/research/animated-colors.md). All driven colours share one
403
+ // provider and one declaration block: two properties animating together
404
+ // are one write, not two.
405
+ const colorSlots = colorsRef.current;
406
+ let widgetCss = null;
407
+ if (colorSlots.length > 0) {
408
+ const css = createWidgetCss(widget);
409
+ widgetCss = css;
410
+ const values = {};
411
+ const writeColors = () => {
412
+ css.set(driveableColorsToCss(values));
413
+ };
414
+ for (const slot of colorSlots) {
415
+ values[slot.property] = slot.node.__getValue();
416
+ subscriptions.push({
417
+ node: slot.node,
418
+ id: slot.node.addListener(({ value }) => {
419
+ values[slot.property] = value;
420
+ writeColors();
421
+ }),
422
+ });
423
+ }
424
+ writeColors();
425
+ }
426
+ // Sizes take the third door, and it is the only one that runs Yoga.
427
+ //
428
+ // Nothing is written at BIND time on purpose: the render that armed this
429
+ // effect put the current value into the static style, so the engine is
430
+ // already committing exactly this size and an override would be a copy of
431
+ // it. The first write happens when a frame asks for something else.
432
+ const sizeSlots = sizesRef.current;
433
+ const drivenWidgets = [];
434
+ if (sizeSlots.length > 0) {
435
+ const layoutNode = nodeForWidget(widget);
436
+ const driven = {};
437
+ const driveable = new Set();
438
+ let decided = false;
439
+ // Deferred to the first frame that actually asks for a new size, and
440
+ // that is not laziness: the rule is asked of the CONTAINER's Yoga node,
441
+ // and on a first mount a container's own layout effects run AFTER its
442
+ // children's — so at bind time the container may still be carrying the
443
+ // defaults rather than its style. By the first frame the tree has been
444
+ // laid out at least once and the answer is the real one.
445
+ const decide = () => {
446
+ decided = true;
447
+ if (!layoutNode) {
448
+ return;
449
+ }
450
+ const context = {
451
+ node: layoutNode,
452
+ rootIsContentSized: hostRef.current.engine.contentSized,
453
+ };
454
+ for (const slot of sizeSlots) {
455
+ const reason = drivenSizeRefusal(context, slot.property);
456
+ if (reason === null) {
457
+ driveable.add(slot.property);
458
+ }
459
+ else {
460
+ warnSizeNotDriveable(slot.property, reason);
461
+ }
462
+ }
463
+ };
464
+ for (const slot of sizeSlots) {
465
+ subscriptions.push({
466
+ node: slot.node,
467
+ id: slot.node.addListener(({ value }) => {
468
+ const numeric = toNumber(value);
469
+ if (!Number.isFinite(numeric)) {
470
+ return;
471
+ }
472
+ if (!decided) {
473
+ decide();
474
+ }
475
+ if (!driveable.has(slot.property) || !layoutNode) {
476
+ return;
477
+ }
478
+ driven[slot.property] = numeric;
479
+ const target = getWidgetRef.current();
480
+ if (target) {
481
+ applyDrivenSize(layoutNode, target, parentWidgetRef.current, driven, drivenWidgets);
482
+ }
483
+ }),
484
+ });
485
+ }
486
+ }
158
487
  flush();
159
488
  return () => {
160
489
  for (const { node: animated, id } of subscriptions) {
161
490
  animated.removeListener(id);
162
491
  }
492
+ widgetCss?.dispose();
493
+ // Every widget this binding was driving goes back to the rect the
494
+ // engine committed. On a REBASE that is the whole point: the render
495
+ // that re-armed this effect handed Yoga the size the animation is
496
+ // showing, so the committed rect is about to become the driven one and
497
+ // holding an override on top of it would be holding a stale copy.
498
+ if (drivenWidgets.length > 0) {
499
+ releaseDrivenSize(drivenWidgets);
500
+ if (boundParentWidget) {
501
+ queueAllocate(boundParentWidget);
502
+ }
503
+ }
163
504
  };
164
505
  // eslint-disable-next-line react-hooks/exhaustive-deps
165
- }, [bindingKey, node]);
506
+ }, [bindingKey, rebindKey]);
507
+ };
508
+ const AnimatedView = ({ style, children, onLayout, testID, ref, pointerEvents, animatedProps, ...responderProps }) => {
509
+ const widgetRef = useRef(null);
510
+ const { staticStyle, opacity, zIndex, slots, colors, sizes } = splitAnimated(style);
511
+ const { host, node, cssClass } = useLayoutChild(widgetRef, {
512
+ style: staticStyle,
513
+ onLayout,
514
+ });
515
+ useRnContainer(widgetRef, node);
516
+ useResponder(widgetRef, responderProps);
517
+ // `animatedProps` LAST, matching `createAnimatedComponent`'s
518
+ // `{ ...rest, ...animatedProps }` — a value produced by the updater is the
519
+ // live one and wins over the static prop. Only the picking mode is read
520
+ // here: a numeric animated prop has no meaning on a View, and the SVG
521
+ // shapes that do take them go through `createAnimatedComponent` already.
522
+ const mode = animatedProps?.pointerEvents ??
523
+ pointerEvents ??
524
+ StyleSheet.flatten(staticStyle)?.pointerEvents ??
525
+ "auto";
526
+ useLayoutEffect(() => {
527
+ const widget = widgetRef.current;
528
+ if (!widget) {
529
+ return;
530
+ }
531
+ setPointerTarget(widget, mode !== "none");
532
+ setBoxPassthrough(widget, mode === "box-none");
533
+ }, [mode]);
534
+ useImperativeHandle(ref, () => createMeasureHandle(widgetRef, node), [node]);
535
+ useAnimatedBinding(() => widgetRef.current, host, opacity, zIndex, slots, colors, sizes, node);
166
536
  return (_jsx(GtkBox, { ref: widgetRef, name: testID, cssClasses: cssClass ? [cssClass] : [], children: _jsx(HostNodeContext.Provider, { value: { engine: host.engine, node, widgetRef }, children: children }) }));
167
537
  };
538
+ const displayNameOf = (component) => {
539
+ if (typeof component === "string") {
540
+ return component;
541
+ }
542
+ const named = component;
543
+ return named?.displayName ?? named?.name ?? "Component";
544
+ };
545
+ // One warning per wrapped component per session — a mapper runs at frame
546
+ // rate, and the name is the actionable part.
547
+ const warnedWithoutWidget = new Set();
548
+ const warnNoWidget = (name) => {
549
+ if (warnedWithoutWidget.has(name)) {
550
+ return;
551
+ }
552
+ warnedWithoutWidget.add(name);
553
+ const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
554
+ if (!isProduction) {
555
+ console.warn(`react-native-gtkx: createAnimatedComponent(${name}) was given an animated \`opacity\`, ` +
556
+ "`transform`, colour or size, but the component exposed no ref carrying a widget, so there is nothing to write " +
557
+ "to. Give it a `ref?: Ref<MeasureHandle>` built with the platform's own measure handle, or wrap " +
558
+ "it in an `Animated.View`. See docs/api.md.");
559
+ }
560
+ };
561
+ /** @internal Test seam: the warning is once per component per session by design. */
562
+ export const resetAnimatedComponentWarnings = () => {
563
+ warnedWithoutWidget.clear();
564
+ };
565
+ /**
566
+ * Wraps a component so its `style`'s `opacity`/`transform` and its
567
+ * `animatedProps` can be driven imperatively — RN's own
568
+ * `Animated.createAnimatedComponent`.
569
+ *
570
+ * IT ADDS NO WIDGET. Wrapping in an `Animated.View` would have been three
571
+ * lines and would have been wrong: an extra box changes flex layout, changes
572
+ * what `measureLayout` is relative to, and changes which widget a parent's
573
+ * allocate walks. That is not a shim, it is a different tree. Instead the
574
+ * wrapper renders the component itself and reaches the widget through the
575
+ * handle the component already exposes (see measure.ts's `widgetOf`), so the
576
+ * rendered output is byte-for-byte what the unwrapped component produces.
577
+ *
578
+ * `animatedProps` are passed straight through as animated NODES rather than
579
+ * resolved to numbers, because the components that take them already accept
580
+ * one: the SVG shapes duck-type an animated node on every numeric geometry
581
+ * and paint prop and subscribe to it themselves (svg/animated-support.ts),
582
+ * redrawing through `queueDraw`. Nothing is resolved here that the receiver
583
+ * can resolve better.
584
+ */
585
+ export const createAnimatedComponent = (component) => {
586
+ const name = displayNameOf(component);
587
+ const AnimatedWrapper = ({ style, animatedProps, ref, ...rest }) => {
588
+ // The PARENT's widget, for `queueAllocate` — the wrapper is not a host
589
+ // node itself, so this is the container the wrapped component is laid out
590
+ // by, exactly as it would be without the wrapper.
591
+ const host = useHostNode();
592
+ const handleRef = useRef(null);
593
+ const { staticStyle, opacity, zIndex, slots, colors, sizes } = splitAnimated(style);
594
+ const driven = opacity !== null ||
595
+ zIndex !== null ||
596
+ slots.length > 0 ||
597
+ colors.length > 0 ||
598
+ sizes.length > 0;
599
+ // Stable identity: a fresh callback ref every render would detach and
600
+ // reattach the wrapped component's handle on every render.
601
+ const forwardedRef = useRef(ref);
602
+ forwardedRef.current = ref;
603
+ const [assignHandle] = useState(() => (instance) => {
604
+ handleRef.current = instance;
605
+ const target = forwardedRef.current;
606
+ if (typeof target === "function") {
607
+ target(instance);
608
+ }
609
+ else if (target) {
610
+ ;
611
+ target.current = instance;
612
+ }
613
+ // No return value: React 19 reads one as a callback-ref cleanup.
614
+ });
615
+ useAnimatedBinding(() => widgetForHandle(handleRef.current), host, opacity, zIndex, slots, colors, sizes);
616
+ // A silent no-op is the failure this repo refuses. Checked after the
617
+ // wrapped component's own layout effects have run, which is where its
618
+ // `useImperativeHandle` publishes the handle.
619
+ useLayoutEffect(() => {
620
+ if (driven && widgetForHandle(handleRef.current) === null) {
621
+ warnNoWidget(name);
622
+ }
623
+ });
624
+ const targetProps = { ...rest, ...animatedProps };
625
+ // Only when there is something to attach: a component that takes no ref
626
+ // (the SVG shapes) would otherwise get one in its rest props.
627
+ if (driven || ref) {
628
+ targetProps.ref = assignHandle;
629
+ }
630
+ // Likewise `style`: passing `{}` to a component that has no style prop
631
+ // would push an empty object into whatever collects its rest props.
632
+ if (style !== undefined) {
633
+ targetProps.style = staticStyle;
634
+ }
635
+ const Target = component;
636
+ return _jsx(Target, { ...targetProps });
637
+ };
638
+ AnimatedWrapper.displayName = `Animated(${name})`;
639
+ return AnimatedWrapper;
640
+ };
641
+ /**
642
+ * `Animated.FlatList` is deliberately absent, and this is the refusal.
643
+ *
644
+ * FlatList is a COMPOSITE, not a host component: it renders the windowed
645
+ * VirtualizedList core, which renders a ScrollView, which is the only thing
646
+ * in that chain owning a widget. Its ref is a scroll API by contract
647
+ * (`scrollToIndex`/`scrollToItem`/`scrollToOffset`), so there is no handle to
648
+ * read a widget back out of — and giving it one would mean publishing the
649
+ * scrolled window through two layers that exist to hide it.
650
+ *
651
+ * Upstream's `Animated.FlatList` mostly exists so `onScroll` can be an
652
+ * `Animated.event`/`useAnimatedScrollHandler`, and neither of those is
653
+ * implemented here either. So this throws where it is used, naming itself and
654
+ * naming the two things that do work.
655
+ */
656
+ const AnimatedFlatList = () => {
657
+ throw new Error("react-native-gtkx: `Animated.FlatList` is not implemented. FlatList is a composite — the windowed " +
658
+ "core over a ScrollView — and its ref is a scroll API, so there is no host widget for an animated " +
659
+ "style to write to. Put the animated style on an `Animated.View` wrapping the list, or use " +
660
+ "`Animated.ScrollView` if the list does not need virtualization. See docs/api.md.");
661
+ };
662
+ AnimatedFlatList.displayName = "Animated.FlatList";
663
+ /**
664
+ * `Text`, `Image` and `ScrollView` are the generic wrapper over the
665
+ * platform's own components — no subclass, no special case, and no widget
666
+ * added to the tree. They are animatable at all because those three now
667
+ * expose a ref carrying their widget, which is RN parity in its own right.
668
+ */
669
+ const AnimatedText = createAnimatedComponent(Text);
670
+ const AnimatedImage = createAnimatedComponent(Image);
671
+ const AnimatedScrollView = createAnimatedComponent(ScrollView);
168
672
  export const Animated = {
169
673
  ...api,
170
674
  View: AnimatedView,
675
+ Text: AnimatedText,
676
+ Image: AnimatedImage,
677
+ ScrollView: AnimatedScrollView,
678
+ // Typed as taking the props it refuses, so the failure is at RUNTIME with
679
+ // the message above rather than at compile time with "no overload matches
680
+ // this call" — an app porting from mobile deserves the sentence, not a
681
+ // type puzzle.
682
+ FlatList: AnimatedFlatList,
683
+ createAnimatedComponent,
171
684
  };
172
685
  export { Easing } from "../animated/index";
173
686
  //# sourceMappingURL=animated.js.map