@rafaelaugusto/jolt 0.1.0-beta.9 → 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (576) hide show
  1. package/README.md +167 -21
  2. package/dist/animate.d.ts +25 -4
  3. package/dist/animate.d.ts.map +1 -1
  4. package/dist/animate.js +67 -26
  5. package/dist/animate.js.map +1 -1
  6. package/dist/blur/index.d.ts +6 -0
  7. package/dist/blur/index.d.ts.map +1 -0
  8. package/dist/blur/index.js +196 -0
  9. package/dist/blur/index.js.map +1 -0
  10. package/dist/blur/native.d.ts +17 -0
  11. package/dist/blur/native.d.ts.map +1 -0
  12. package/dist/blur/native.js +44 -0
  13. package/dist/blur/native.js.map +1 -0
  14. package/dist/blur/types.d.ts +32 -0
  15. package/dist/blur/types.d.ts.map +1 -0
  16. package/dist/blur/types.js +2 -0
  17. package/dist/blur/types.js.map +1 -0
  18. package/dist/bottom-sheet/geometry.d.ts +18 -0
  19. package/dist/bottom-sheet/geometry.d.ts.map +1 -0
  20. package/dist/bottom-sheet/geometry.js +51 -0
  21. package/dist/bottom-sheet/geometry.js.map +1 -0
  22. package/dist/bottom-sheet/index.d.ts +56 -0
  23. package/dist/bottom-sheet/index.d.ts.map +1 -0
  24. package/dist/bottom-sheet/index.js +462 -0
  25. package/dist/bottom-sheet/index.js.map +1 -0
  26. package/dist/bottom-sheet/native.d.ts +11 -0
  27. package/dist/bottom-sheet/native.d.ts.map +1 -0
  28. package/dist/bottom-sheet/native.js +173 -0
  29. package/dist/bottom-sheet/native.js.map +1 -0
  30. package/dist/config.d.ts +9 -0
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js +40 -1
  33. package/dist/config.js.map +1 -1
  34. package/dist/decay.d.ts +1 -0
  35. package/dist/decay.d.ts.map +1 -1
  36. package/dist/decay.js +7 -0
  37. package/dist/decay.js.map +1 -1
  38. package/dist/drag-physics.d.ts +6 -1
  39. package/dist/drag-physics.d.ts.map +1 -1
  40. package/dist/drag.d.ts +17 -3
  41. package/dist/drag.d.ts.map +1 -1
  42. package/dist/drag.js +34 -11
  43. package/dist/drag.js.map +1 -1
  44. package/dist/driver/android-gradient.d.ts +5 -0
  45. package/dist/driver/android-gradient.d.ts.map +1 -0
  46. package/dist/driver/android-gradient.js +99 -0
  47. package/dist/driver/android-gradient.js.map +1 -0
  48. package/dist/driver/android-particles.d.ts +6 -0
  49. package/dist/driver/android-particles.d.ts.map +1 -0
  50. package/dist/driver/android-particles.js +93 -0
  51. package/dist/driver/android-particles.js.map +1 -0
  52. package/dist/driver/android.d.ts.map +1 -1
  53. package/dist/driver/android.js +27 -1
  54. package/dist/driver/android.js.map +1 -1
  55. package/dist/driver/frame.d.ts +3 -0
  56. package/dist/driver/frame.d.ts.map +1 -0
  57. package/dist/driver/frame.js +7 -0
  58. package/dist/driver/frame.js.map +1 -0
  59. package/dist/driver/index.d.ts +4 -0
  60. package/dist/driver/index.d.ts.map +1 -1
  61. package/dist/driver/index.js.map +1 -1
  62. package/dist/driver/ios-gradient.d.ts +5 -0
  63. package/dist/driver/ios-gradient.d.ts.map +1 -0
  64. package/dist/driver/ios-gradient.js +131 -0
  65. package/dist/driver/ios-gradient.js.map +1 -0
  66. package/dist/driver/ios-scroll-delegate.d.ts +4 -0
  67. package/dist/driver/ios-scroll-delegate.d.ts.map +1 -0
  68. package/dist/driver/ios-scroll-delegate.js +26 -0
  69. package/dist/driver/ios-scroll-delegate.js.map +1 -0
  70. package/dist/driver/ios.d.ts.map +1 -1
  71. package/dist/driver/ios.js +9 -0
  72. package/dist/driver/ios.js.map +1 -1
  73. package/dist/easing.d.ts +3 -1
  74. package/dist/easing.d.ts.map +1 -1
  75. package/dist/easing.js +18 -1
  76. package/dist/easing.js.map +1 -1
  77. package/dist/engine.d.ts +31 -2
  78. package/dist/engine.d.ts.map +1 -1
  79. package/dist/engine.js +194 -42
  80. package/dist/engine.js.map +1 -1
  81. package/dist/gesture-native.d.ts +28 -0
  82. package/dist/gesture-native.d.ts.map +1 -0
  83. package/dist/gesture-native.js +61 -0
  84. package/dist/gesture-native.js.map +1 -0
  85. package/dist/gestures/index.d.ts +69 -0
  86. package/dist/gestures/index.d.ts.map +1 -0
  87. package/dist/gestures/index.js +262 -0
  88. package/dist/gestures/index.js.map +1 -0
  89. package/dist/glass/index.d.ts +55 -0
  90. package/dist/glass/index.d.ts.map +1 -0
  91. package/dist/glass/index.js +205 -0
  92. package/dist/glass/index.js.map +1 -0
  93. package/dist/gradient-binding.d.ts +38 -0
  94. package/dist/gradient-binding.d.ts.map +1 -0
  95. package/dist/gradient-binding.js +128 -0
  96. package/dist/gradient-binding.js.map +1 -0
  97. package/dist/gradient.d.ts +105 -0
  98. package/dist/gradient.d.ts.map +1 -0
  99. package/dist/gradient.js +514 -0
  100. package/dist/gradient.js.map +1 -0
  101. package/dist/gravity.d.ts +13 -0
  102. package/dist/gravity.d.ts.map +1 -0
  103. package/dist/gravity.js +84 -0
  104. package/dist/gravity.js.map +1 -0
  105. package/dist/haptics.d.ts.map +1 -1
  106. package/dist/haptics.js +101 -28
  107. package/dist/haptics.js.map +1 -1
  108. package/dist/image-effect.d.ts +1 -0
  109. package/dist/image-effect.d.ts.map +1 -1
  110. package/dist/image-effect.js +10 -2
  111. package/dist/image-effect.js.map +1 -1
  112. package/dist/index.d.ts +19 -7
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +10 -4
  115. package/dist/index.js.map +1 -1
  116. package/dist/inspector/index.d.ts +86 -0
  117. package/dist/inspector/index.d.ts.map +1 -0
  118. package/dist/inspector/index.js +419 -0
  119. package/dist/inspector/index.js.map +1 -0
  120. package/dist/inspector/plot.d.ts +4 -0
  121. package/dist/inspector/plot.d.ts.map +1 -0
  122. package/dist/inspector/plot.js +23 -0
  123. package/dist/inspector/plot.js.map +1 -0
  124. package/dist/inspector/probe.d.ts +19 -0
  125. package/dist/inspector/probe.d.ts.map +1 -0
  126. package/dist/inspector/probe.js +37 -0
  127. package/dist/inspector/probe.js.map +1 -0
  128. package/dist/inspector/tuning.d.ts +19 -0
  129. package/dist/inspector/tuning.d.ts.map +1 -0
  130. package/dist/inspector/tuning.js +37 -0
  131. package/dist/inspector/tuning.js.map +1 -0
  132. package/dist/interactive-navigation/index.d.ts +44 -0
  133. package/dist/interactive-navigation/index.d.ts.map +1 -0
  134. package/dist/interactive-navigation/index.js +350 -0
  135. package/dist/interactive-navigation/index.js.map +1 -0
  136. package/dist/keyboard.d.ts +24 -0
  137. package/dist/keyboard.d.ts.map +1 -1
  138. package/dist/keyboard.js +199 -6
  139. package/dist/keyboard.js.map +1 -1
  140. package/dist/live-spring-driver.d.ts +52 -0
  141. package/dist/live-spring-driver.d.ts.map +1 -0
  142. package/dist/live-spring-driver.js +104 -0
  143. package/dist/live-spring-driver.js.map +1 -0
  144. package/dist/mixer.d.ts +55 -0
  145. package/dist/mixer.d.ts.map +1 -0
  146. package/dist/mixer.js +286 -0
  147. package/dist/mixer.js.map +1 -0
  148. package/dist/motion-scope.d.ts +8 -2
  149. package/dist/motion-scope.d.ts.map +1 -1
  150. package/dist/motion-scope.js +24 -3
  151. package/dist/motion-scope.js.map +1 -1
  152. package/dist/motion-states.d.ts +19 -7
  153. package/dist/motion-states.d.ts.map +1 -1
  154. package/dist/motion-states.js +116 -26
  155. package/dist/motion-states.js.map +1 -1
  156. package/dist/motion-value.d.ts +227 -0
  157. package/dist/motion-value.d.ts.map +1 -0
  158. package/dist/motion-value.js +1074 -0
  159. package/dist/motion-value.js.map +1 -0
  160. package/dist/native/index.d.ts +10 -0
  161. package/dist/native/index.d.ts.map +1 -0
  162. package/dist/native/index.js +6 -0
  163. package/dist/native/index.js.map +1 -0
  164. package/dist/native-properties.d.ts +28 -0
  165. package/dist/native-properties.d.ts.map +1 -0
  166. package/dist/native-properties.js +154 -0
  167. package/dist/native-properties.js.map +1 -0
  168. package/dist/native-property-driver.d.ts +19 -0
  169. package/dist/native-property-driver.d.ts.map +1 -0
  170. package/dist/native-property-driver.js +69 -0
  171. package/dist/native-property-driver.js.map +1 -0
  172. package/dist/native-property.d.ts +37 -0
  173. package/dist/native-property.d.ts.map +1 -0
  174. package/dist/native-property.js +285 -0
  175. package/dist/native-property.js.map +1 -0
  176. package/dist/particles.d.ts +5 -2
  177. package/dist/particles.d.ts.map +1 -1
  178. package/dist/particles.js +16 -10
  179. package/dist/particles.js.map +1 -1
  180. package/dist/path.d.ts.map +1 -1
  181. package/dist/path.js +2 -2
  182. package/dist/path.js.map +1 -1
  183. package/dist/physics/index.d.ts +139 -0
  184. package/dist/physics/index.d.ts.map +1 -0
  185. package/dist/physics/index.js +316 -0
  186. package/dist/physics/index.js.map +1 -0
  187. package/dist/physics/native.d.ts +54 -0
  188. package/dist/physics/native.d.ts.map +1 -0
  189. package/dist/physics/native.js +64 -0
  190. package/dist/physics/native.js.map +1 -0
  191. package/dist/playback-group.d.ts.map +1 -1
  192. package/dist/playback-group.js +17 -1
  193. package/dist/playback-group.js.map +1 -1
  194. package/dist/playback.d.ts +15 -0
  195. package/dist/playback.d.ts.map +1 -1
  196. package/dist/playback.js +23 -0
  197. package/dist/playback.js.map +1 -1
  198. package/dist/predictive-back/index.d.ts +45 -0
  199. package/dist/predictive-back/index.d.ts.map +1 -0
  200. package/dist/predictive-back/index.js +220 -0
  201. package/dist/predictive-back/index.js.map +1 -0
  202. package/dist/predictive-back/presentation.d.ts +4 -0
  203. package/dist/predictive-back/presentation.d.ts.map +1 -0
  204. package/dist/predictive-back/presentation.js +3 -0
  205. package/dist/predictive-back/presentation.js.map +1 -0
  206. package/dist/press.d.ts.map +1 -1
  207. package/dist/press.js +11 -2
  208. package/dist/press.js.map +1 -1
  209. package/dist/reorder/handle.d.ts +6 -0
  210. package/dist/reorder/handle.d.ts.map +1 -0
  211. package/dist/reorder/handle.js +10 -0
  212. package/dist/reorder/handle.js.map +1 -0
  213. package/dist/reorder/index.d.ts +10 -0
  214. package/dist/reorder/index.d.ts.map +1 -0
  215. package/dist/reorder/index.js +233 -0
  216. package/dist/reorder/index.js.map +1 -0
  217. package/dist/reorder/list-view.d.ts +5 -0
  218. package/dist/reorder/list-view.d.ts.map +1 -0
  219. package/dist/reorder/list-view.js +238 -0
  220. package/dist/reorder/list-view.js.map +1 -0
  221. package/dist/reorder/model.d.ts +6 -0
  222. package/dist/reorder/model.d.ts.map +1 -0
  223. package/dist/reorder/model.js +30 -0
  224. package/dist/reorder/model.js.map +1 -0
  225. package/dist/reorder/surface.d.ts +6 -0
  226. package/dist/reorder/surface.d.ts.map +1 -0
  227. package/dist/reorder/surface.js +10 -0
  228. package/dist/reorder/surface.js.map +1 -0
  229. package/dist/reorder/types.d.ts +92 -0
  230. package/dist/reorder/types.d.ts.map +1 -0
  231. package/dist/reorder/types.js +2 -0
  232. package/dist/reorder/types.js.map +1 -0
  233. package/dist/sampling.d.ts +35 -0
  234. package/dist/sampling.d.ts.map +1 -0
  235. package/dist/sampling.js +123 -0
  236. package/dist/sampling.js.map +1 -0
  237. package/dist/scene-appearance.d.ts +14 -0
  238. package/dist/scene-appearance.d.ts.map +1 -0
  239. package/dist/scene-appearance.js +68 -0
  240. package/dist/scene-appearance.js.map +1 -0
  241. package/dist/scene-camera.d.ts +5 -0
  242. package/dist/scene-camera.d.ts.map +1 -0
  243. package/dist/scene-camera.js +23 -0
  244. package/dist/scene-camera.js.map +1 -0
  245. package/dist/scene-shader.d.ts +18 -0
  246. package/dist/scene-shader.d.ts.map +1 -0
  247. package/dist/scene-shader.js +76 -0
  248. package/dist/scene-shader.js.map +1 -0
  249. package/dist/scroll-header/geometry.d.ts +18 -0
  250. package/dist/scroll-header/geometry.d.ts.map +1 -0
  251. package/dist/scroll-header/geometry.js +46 -0
  252. package/dist/scroll-header/geometry.js.map +1 -0
  253. package/dist/scroll-header/index.d.ts +48 -0
  254. package/dist/scroll-header/index.d.ts.map +1 -0
  255. package/dist/scroll-header/index.js +399 -0
  256. package/dist/scroll-header/index.js.map +1 -0
  257. package/dist/scroll-header/native.d.ts +28 -0
  258. package/dist/scroll-header/native.d.ts.map +1 -0
  259. package/dist/scroll-header/native.js +43 -0
  260. package/dist/scroll-header/native.js.map +1 -0
  261. package/dist/sensors/index.d.ts +69 -0
  262. package/dist/sensors/index.d.ts.map +1 -0
  263. package/dist/sensors/index.js +149 -0
  264. package/dist/sensors/index.js.map +1 -0
  265. package/dist/sensors/native.d.ts +20 -0
  266. package/dist/sensors/native.d.ts.map +1 -0
  267. package/dist/sensors/native.js +48 -0
  268. package/dist/sensors/native.js.map +1 -0
  269. package/dist/sequence.d.ts +24 -0
  270. package/dist/sequence.d.ts.map +1 -1
  271. package/dist/sequence.js +181 -19
  272. package/dist/sequence.js.map +1 -1
  273. package/dist/shapes/android.d.ts +3 -0
  274. package/dist/shapes/android.d.ts.map +1 -0
  275. package/dist/shapes/android.js +51 -0
  276. package/dist/shapes/android.js.map +1 -0
  277. package/dist/shapes/index.d.ts +6 -0
  278. package/dist/shapes/index.d.ts.map +1 -0
  279. package/dist/shapes/index.js +238 -0
  280. package/dist/shapes/index.js.map +1 -0
  281. package/dist/shapes/ios.d.ts +3 -0
  282. package/dist/shapes/ios.d.ts.map +1 -0
  283. package/dist/shapes/ios.js +164 -0
  284. package/dist/shapes/ios.js.map +1 -0
  285. package/dist/shapes/native.d.ts +18 -0
  286. package/dist/shapes/native.d.ts.map +1 -0
  287. package/dist/shapes/native.js +10 -0
  288. package/dist/shapes/native.js.map +1 -0
  289. package/dist/shapes/path.d.ts +10 -0
  290. package/dist/shapes/path.d.ts.map +1 -0
  291. package/dist/shapes/path.js +106 -0
  292. package/dist/shapes/path.js.map +1 -0
  293. package/dist/shapes/types.d.ts +55 -0
  294. package/dist/shapes/types.d.ts.map +1 -0
  295. package/dist/shapes/types.js +11 -0
  296. package/dist/shapes/types.js.map +1 -0
  297. package/dist/shared-layout/index.d.ts +40 -0
  298. package/dist/shared-layout/index.d.ts.map +1 -0
  299. package/dist/shared-layout/index.js +267 -0
  300. package/dist/shared-layout/index.js.map +1 -0
  301. package/dist/shared-layout/native.d.ts +15 -0
  302. package/dist/shared-layout/native.d.ts.map +1 -0
  303. package/dist/shared-layout/native.js +79 -0
  304. package/dist/shared-layout/native.js.map +1 -0
  305. package/dist/shared-layout/timing.d.ts +9 -0
  306. package/dist/shared-layout/timing.d.ts.map +1 -0
  307. package/dist/shared-layout/timing.js +68 -0
  308. package/dist/shared-layout/timing.js.map +1 -0
  309. package/dist/shared-navigation/entry.d.ts +51 -0
  310. package/dist/shared-navigation/entry.d.ts.map +1 -0
  311. package/dist/shared-navigation/entry.js +619 -0
  312. package/dist/shared-navigation/entry.js.map +1 -0
  313. package/dist/shared-navigation/index.d.ts +58 -0
  314. package/dist/shared-navigation/index.d.ts.map +1 -0
  315. package/dist/shared-navigation/index.js +318 -0
  316. package/dist/shared-navigation/index.js.map +1 -0
  317. package/dist/shared-navigation/return-signal.d.ts +16 -0
  318. package/dist/shared-navigation/return-signal.d.ts.map +1 -0
  319. package/dist/shared-navigation/return-signal.js +35 -0
  320. package/dist/shared-navigation/return-signal.js.map +1 -0
  321. package/dist/shared-navigation/return-target.d.ts +32 -0
  322. package/dist/shared-navigation/return-target.d.ts.map +1 -0
  323. package/dist/shared-navigation/return-target.js +168 -0
  324. package/dist/shared-navigation/return-target.js.map +1 -0
  325. package/dist/shared-navigation/scope.d.ts +4 -0
  326. package/dist/shared-navigation/scope.d.ts.map +1 -0
  327. package/dist/shared-navigation/scope.js +2 -0
  328. package/dist/shared-navigation/scope.js.map +1 -0
  329. package/dist/shared-navigation/targets.d.ts +6 -0
  330. package/dist/shared-navigation/targets.d.ts.map +1 -0
  331. package/dist/shared-navigation/targets.js +36 -0
  332. package/dist/shared-navigation/targets.js.map +1 -0
  333. package/dist/size-layout.d.ts +19 -0
  334. package/dist/size-layout.d.ts.map +1 -0
  335. package/dist/size-layout.js +216 -0
  336. package/dist/size-layout.js.map +1 -0
  337. package/dist/size.d.ts +19 -0
  338. package/dist/size.d.ts.map +1 -0
  339. package/dist/size.js +251 -0
  340. package/dist/size.js.map +1 -0
  341. package/dist/spring.d.ts +2 -0
  342. package/dist/spring.d.ts.map +1 -1
  343. package/dist/spring.js +11 -0
  344. package/dist/spring.js.map +1 -1
  345. package/dist/svg-path.d.ts +18 -0
  346. package/dist/svg-path.d.ts.map +1 -1
  347. package/dist/svg-path.js +3 -2
  348. package/dist/svg-path.js.map +1 -1
  349. package/dist/testing/clock.d.ts +40 -0
  350. package/dist/testing/clock.d.ts.map +1 -0
  351. package/dist/testing/clock.js +116 -0
  352. package/dist/testing/clock.js.map +1 -0
  353. package/dist/testing/handoff-recorder.d.ts +8 -0
  354. package/dist/testing/handoff-recorder.d.ts.map +1 -0
  355. package/dist/testing/handoff-recorder.js +77 -0
  356. package/dist/testing/handoff-recorder.js.map +1 -0
  357. package/dist/testing/index.d.ts +5 -0
  358. package/dist/testing/index.d.ts.map +1 -0
  359. package/dist/testing/index.js +5 -0
  360. package/dist/testing/index.js.map +1 -0
  361. package/dist/testing/native.d.ts +46 -0
  362. package/dist/testing/native.d.ts.map +1 -0
  363. package/dist/testing/native.js +313 -0
  364. package/dist/testing/native.js.map +1 -0
  365. package/dist/testing/presentation.d.ts +11 -0
  366. package/dist/testing/presentation.d.ts.map +1 -0
  367. package/dist/testing/presentation.js +45 -0
  368. package/dist/testing/presentation.js.map +1 -0
  369. package/dist/testing/replay.d.ts +39 -0
  370. package/dist/testing/replay.d.ts.map +1 -0
  371. package/dist/testing/replay.js +99 -0
  372. package/dist/testing/replay.js.map +1 -0
  373. package/dist/testing/scroll-handoff-native.d.ts +10 -0
  374. package/dist/testing/scroll-handoff-native.d.ts.map +1 -0
  375. package/dist/testing/scroll-handoff-native.js +151 -0
  376. package/dist/testing/scroll-handoff-native.js.map +1 -0
  377. package/dist/testing/scroll-handoff.d.ts +25 -0
  378. package/dist/testing/scroll-handoff.d.ts.map +1 -0
  379. package/dist/testing/scroll-handoff.js +127 -0
  380. package/dist/testing/scroll-handoff.js.map +1 -0
  381. package/dist/testing/sensors.d.ts +13 -0
  382. package/dist/testing/sensors.d.ts.map +1 -0
  383. package/dist/testing/sensors.js +26 -0
  384. package/dist/testing/sensors.js.map +1 -0
  385. package/dist/testing/trace.d.ts +101 -0
  386. package/dist/testing/trace.d.ts.map +1 -0
  387. package/dist/testing/trace.js +263 -0
  388. package/dist/testing/trace.js.map +1 -0
  389. package/dist/timed-animation.d.ts +38 -0
  390. package/dist/timed-animation.d.ts.map +1 -0
  391. package/dist/timed-animation.js +177 -0
  392. package/dist/timed-animation.js.map +1 -0
  393. package/dist/track.d.ts +3 -0
  394. package/dist/track.d.ts.map +1 -1
  395. package/dist/track.js +281 -40
  396. package/dist/track.js.map +1 -1
  397. package/dist/transition.d.ts +54 -2
  398. package/dist/transition.d.ts.map +1 -1
  399. package/dist/transition.js +128 -8
  400. package/dist/transition.js.map +1 -1
  401. package/dist/types.d.ts +10 -1
  402. package/dist/types.d.ts.map +1 -1
  403. package/dist/value-observers.d.ts +22 -0
  404. package/dist/value-observers.d.ts.map +1 -0
  405. package/dist/value-observers.js +122 -0
  406. package/dist/value-observers.js.map +1 -0
  407. package/dist/value-track.d.ts +13 -0
  408. package/dist/value-track.d.ts.map +1 -0
  409. package/dist/value-track.js +59 -0
  410. package/dist/value-track.js.map +1 -0
  411. package/dist/vue/index.d.ts +33 -3
  412. package/dist/vue/index.d.ts.map +1 -1
  413. package/dist/vue/index.js +118 -3
  414. package/dist/vue/index.js.map +1 -1
  415. package/dist/zoomable.d.ts +1 -1
  416. package/dist/zoomable.d.ts.map +1 -1
  417. package/package.json +68 -3
  418. package/platforms/android/include.gradle +10 -0
  419. package/platforms/android/java/org/rafaelaugusto/jolt/BlurEffect.java +89 -0
  420. package/platforms/android/java/org/rafaelaugusto/jolt/CircleMath.java +158 -0
  421. package/platforms/android/java/org/rafaelaugusto/jolt/CircleWorld.java +221 -0
  422. package/platforms/android/java/org/rafaelaugusto/jolt/GestureSession.java +227 -0
  423. package/platforms/android/java/org/rafaelaugusto/jolt/GestureSurface.java +54 -0
  424. package/platforms/android/java/org/rafaelaugusto/jolt/GradientLayer.java +224 -0
  425. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrame.java +53 -0
  426. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrameFollower.java +57 -0
  427. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardObserver.java +97 -0
  428. package/platforms/android/java/org/rafaelaugusto/jolt/LiveSpring.java +277 -0
  429. package/platforms/android/java/org/rafaelaugusto/jolt/MotionProbe.java +70 -0
  430. package/platforms/android/java/org/rafaelaugusto/jolt/ParticleEmitter.java +192 -0
  431. package/platforms/android/java/org/rafaelaugusto/jolt/ParticleSimulation.java +114 -0
  432. package/platforms/android/java/org/rafaelaugusto/jolt/PredictiveBackSession.java +212 -0
  433. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderHandle.java +25 -0
  434. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSession.java +213 -0
  435. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSurface.java +27 -0
  436. package/platforms/android/java/org/rafaelaugusto/jolt/ScrollHeader.java +84 -0
  437. package/platforms/android/java/org/rafaelaugusto/jolt/SensorMath.java +28 -0
  438. package/platforms/android/java/org/rafaelaugusto/jolt/SensorSource.java +217 -0
  439. package/platforms/android/java/org/rafaelaugusto/jolt/ShapeLayer.java +120 -0
  440. package/platforms/android/java/org/rafaelaugusto/jolt/SharedLayoutFlight.java +287 -0
  441. package/platforms/android/java/org/rafaelaugusto/jolt/SharedNavigationScene.java +115 -0
  442. package/platforms/android/java/org/rafaelaugusto/jolt/SpringState.java +39 -0
  443. package/platforms/android/proguard-rules.pro +20 -0
  444. package/platforms/ios/build.xcconfig +1 -1
  445. package/platforms/ios/src/JoltBlur.h +14 -0
  446. package/platforms/ios/src/JoltBlur.m +86 -0
  447. package/platforms/ios/src/JoltCircleMath.h +191 -0
  448. package/platforms/ios/src/JoltCircleWorld.h +18 -0
  449. package/platforms/ios/src/JoltCircleWorld.m +218 -0
  450. package/platforms/ios/src/JoltGestureSession.h +23 -0
  451. package/platforms/ios/src/JoltGestureSession.m +251 -0
  452. package/platforms/ios/src/JoltInteractiveNavigation.h +23 -0
  453. package/platforms/ios/src/JoltInteractiveNavigation.m +255 -0
  454. package/platforms/ios/src/JoltLiveSpring.h +29 -0
  455. package/platforms/ios/src/JoltLiveSpring.m +243 -0
  456. package/platforms/ios/src/JoltMotionProbe.h +9 -0
  457. package/platforms/ios/src/JoltMotionProbe.m +72 -0
  458. package/platforms/ios/src/JoltReorderSession.h +15 -0
  459. package/platforms/ios/src/JoltReorderSession.m +205 -0
  460. package/platforms/ios/src/JoltScrollHeader.h +13 -0
  461. package/platforms/ios/src/JoltScrollHeader.m +101 -0
  462. package/platforms/ios/src/JoltSensorMath.h +39 -0
  463. package/platforms/ios/src/JoltSensorSource.h +13 -0
  464. package/platforms/ios/src/JoltSensorSource.m +201 -0
  465. package/platforms/ios/src/JoltSharedLayoutFlight.h +19 -0
  466. package/platforms/ios/src/JoltSharedLayoutFlight.m +250 -0
  467. package/platforms/ios/src/JoltSpringState.h +35 -0
  468. package/platforms/ios/src/module.modulemap +10 -0
  469. package/src/android-plugin.d.ts +464 -0
  470. package/src/animate.ts +129 -15
  471. package/src/blur/index.ts +195 -0
  472. package/src/blur/native.ts +70 -0
  473. package/src/blur/types.ts +32 -0
  474. package/src/bottom-sheet/geometry.ts +84 -0
  475. package/src/bottom-sheet/index.ts +536 -0
  476. package/src/bottom-sheet/native.ts +214 -0
  477. package/src/config.ts +39 -1
  478. package/src/decay.ts +9 -0
  479. package/src/drag.ts +67 -21
  480. package/src/driver/android-gradient.ts +116 -0
  481. package/src/driver/android-particles.ts +118 -0
  482. package/src/driver/android.ts +31 -1
  483. package/src/driver/frame.ts +10 -0
  484. package/src/driver/index.ts +4 -0
  485. package/src/driver/ios-gradient.ts +136 -0
  486. package/src/driver/ios-scroll-delegate.ts +23 -0
  487. package/src/driver/ios.ts +10 -0
  488. package/src/easing.ts +21 -1
  489. package/src/engine.ts +209 -60
  490. package/src/gesture-native.ts +139 -0
  491. package/src/gestures/index.ts +365 -0
  492. package/src/glass/index.ts +218 -0
  493. package/src/gradient-binding.ts +158 -0
  494. package/src/gradient.ts +663 -0
  495. package/src/gravity.ts +107 -0
  496. package/src/haptics.ts +106 -28
  497. package/src/image-effect.ts +10 -2
  498. package/src/index.ts +56 -7
  499. package/src/inspector/index.ts +523 -0
  500. package/src/inspector/plot.ts +27 -0
  501. package/src/inspector/probe.ts +50 -0
  502. package/src/inspector/tuning.ts +51 -0
  503. package/src/interactive-navigation/index.ts +427 -0
  504. package/src/ios-plugin.d.ts +280 -0
  505. package/src/keyboard.ts +231 -7
  506. package/src/live-spring-driver.ts +181 -0
  507. package/src/mixer.ts +312 -0
  508. package/src/motion-scope.ts +39 -6
  509. package/src/motion-states.ts +149 -42
  510. package/src/motion-value.ts +1289 -0
  511. package/src/native/index.ts +30 -0
  512. package/src/native-properties.ts +188 -0
  513. package/src/native-property-driver.ts +123 -0
  514. package/src/native-property.ts +366 -0
  515. package/src/particles.ts +18 -17
  516. package/src/path.ts +2 -1
  517. package/src/physics/index.ts +476 -0
  518. package/src/physics/native.ts +121 -0
  519. package/src/playback-group.ts +17 -1
  520. package/src/playback.ts +41 -0
  521. package/src/predictive-back/index.ts +280 -0
  522. package/src/predictive-back/presentation.ts +6 -0
  523. package/src/press.ts +10 -2
  524. package/src/reorder/handle.ts +9 -0
  525. package/src/reorder/index.ts +243 -0
  526. package/src/reorder/list-view.ts +262 -0
  527. package/src/reorder/model.ts +47 -0
  528. package/src/reorder/surface.ts +10 -0
  529. package/src/reorder/types.ts +87 -0
  530. package/src/sampling.ts +173 -0
  531. package/src/scene-appearance.ts +92 -0
  532. package/src/scene-camera.ts +30 -0
  533. package/src/scene-shader.ts +139 -0
  534. package/src/scroll-header/geometry.ts +88 -0
  535. package/src/scroll-header/index.ts +530 -0
  536. package/src/scroll-header/native.ts +89 -0
  537. package/src/sensors/index.ts +246 -0
  538. package/src/sensors/native.ts +72 -0
  539. package/src/sequence.ts +209 -20
  540. package/src/shapes/android.ts +63 -0
  541. package/src/shapes/index.ts +267 -0
  542. package/src/shapes/ios.ts +194 -0
  543. package/src/shapes/native.ts +27 -0
  544. package/src/shapes/path.ts +127 -0
  545. package/src/shapes/types.ts +63 -0
  546. package/src/shared-layout/index.ts +343 -0
  547. package/src/shared-layout/native.ts +138 -0
  548. package/src/shared-layout/timing.ts +87 -0
  549. package/src/shared-navigation/entry.ts +695 -0
  550. package/src/shared-navigation/index.ts +414 -0
  551. package/src/shared-navigation/return-signal.ts +41 -0
  552. package/src/shared-navigation/return-target.ts +197 -0
  553. package/src/shared-navigation/scope.ts +3 -0
  554. package/src/shared-navigation/targets.ts +38 -0
  555. package/src/size-layout.ts +237 -0
  556. package/src/size.ts +302 -0
  557. package/src/spring.ts +18 -0
  558. package/src/svg-path.ts +8 -3
  559. package/src/testing/clock.ts +128 -0
  560. package/src/testing/handoff-recorder.ts +98 -0
  561. package/src/testing/index.ts +4 -0
  562. package/src/testing/native.ts +386 -0
  563. package/src/testing/presentation.ts +46 -0
  564. package/src/testing/replay.ts +149 -0
  565. package/src/testing/scroll-handoff-native.ts +201 -0
  566. package/src/testing/scroll-handoff.ts +140 -0
  567. package/src/testing/sensors.ts +41 -0
  568. package/src/testing/trace.ts +340 -0
  569. package/src/timed-animation.ts +192 -0
  570. package/src/track.ts +334 -38
  571. package/src/transition.ts +197 -9
  572. package/src/types.ts +11 -1
  573. package/src/value-observers.ts +147 -0
  574. package/src/value-track.ts +71 -0
  575. package/src/vue/index.ts +147 -1
  576. package/src/zoomable.ts +1 -1
@@ -0,0 +1,1289 @@
1
+ import {
2
+ Application,
3
+ Color,
4
+ ScrollView,
5
+ type EventData,
6
+ type ScrollEventData,
7
+ type View,
8
+ } from '@nativescript/core'
9
+ import { toViews } from './animate.js'
10
+ import {
11
+ colorMixer,
12
+ isColorProperty,
13
+ parseColor,
14
+ validateColorTarget,
15
+ type ColorValue,
16
+ } from './color.js'
17
+ import { onReducedMotion, reducedMotion, timeScale } from './config.js'
18
+ import { springPhysics, type SpringParams } from './spring.js'
19
+ import { mixFor } from './mixer.js'
20
+ import {
21
+ liveSpringDriver,
22
+ syncLiveSprings,
23
+ type LiveNode,
24
+ type LiveMapping,
25
+ type LiveBinding,
26
+ } from './live-spring-driver.js'
27
+ import {
28
+ clampDecoration,
29
+ isDecorationProperty,
30
+ validateDecorationTarget,
31
+ } from './decoration.js'
32
+ import { getDriver } from './driver/index.js'
33
+ import { setBase, stopBase, ViewAnimation, type Animation, type Plan } from './engine.js'
34
+ import { linear } from './easing.js'
35
+ import { isProperty, type NumericProperty, type Property } from './properties.js'
36
+ import { createTrack, instantTrack, type Track } from './track.js'
37
+ import {
38
+ resolveTransition,
39
+ springs,
40
+ type ResolvedTransition,
41
+ type SpringName,
42
+ type Transition,
43
+ } from './transition.js'
44
+ import type { KeyframeProperty, Target } from './types.js'
45
+ import { playbackSource, type PlaybackAnimation, type PlaybackClock } from './playback.js'
46
+ import { TimedAnimation } from './timed-animation.js'
47
+ import { valueTrack } from './value-track.js'
48
+ import {
49
+ ValueObservers,
50
+ type SubscribeOptions,
51
+ type ValueListener,
52
+ } from './value-observers.js'
53
+
54
+ export interface MapOptions {
55
+ /** Hold the first and last outputs outside the input range. Defaults to `true`. */
56
+ clamp?: boolean
57
+ }
58
+
59
+ /** Named motion value layers add scalar offsets before derived mappings. */
60
+ export type ValueAnimationOptions = Transition & { layer?: string }
61
+
62
+ declare const motionValueBrand: unique symbol
63
+
64
+ /** A number derived from a motion value, ready to bind to a view. */
65
+ export interface DerivedValue {
66
+ /** The value now, while it animates too. */
67
+ get(): number
68
+ /** Observe changes in JavaScript on the native frame clock. Unsubscribe when finished. */
69
+ subscribe(listener: ValueListener<number>, options?: SubscribeOptions): () => void
70
+ /** Maps through ascending or descending input ranges, like React Native's `interpolate`. */
71
+ to(
72
+ input: readonly number[],
73
+ output: readonly number[],
74
+ options?: MapOptions,
75
+ ): DerivedValue
76
+ to(input: readonly number[], output: readonly ColorValue[]): DerivedColor
77
+ }
78
+
79
+ /** A color derived from a motion value, for `backgroundColor`, `borderColor` or `color`. */
80
+ export interface DerivedColor {
81
+ get(): Color
82
+ /** Observe mapped color changes in JavaScript on the native frame clock. */
83
+ subscribe(listener: ValueListener<Color>, options?: SubscribeOptions): () => void
84
+ }
85
+
86
+ /** A number that animates natively and drives every view property bound to it. */
87
+ export interface MotionValue extends DerivedValue {
88
+ readonly [motionValueBrand]: true
89
+ }
90
+
91
+ type ColorKey = 'backgroundColor' | 'borderColor' | 'color'
92
+ export type Bindings = {
93
+ [K in Exclude<KeyframeProperty, ColorKey>]?: DerivedValue
94
+ } & { [K in ColorKey]?: DerivedColor }
95
+
96
+ interface Binding {
97
+ readonly view: View
98
+ readonly property: Property
99
+ readonly root: Root
100
+ readonly source?: Derived
101
+ readonly read: (value: number) => number
102
+ animation?: ViewAnimation
103
+ cleanup?: () => void
104
+ sync(): void
105
+ }
106
+
107
+ interface Run {
108
+ readonly track: Track
109
+ readonly base: number
110
+ readonly span: number
111
+ readonly animation: TimedAnimation
112
+ readonly startedAt: number
113
+ readonly clock?: Pick<PlaybackClock, 'time'>
114
+ }
115
+
116
+ type Presentation = Pick<Run, 'track' | 'base' | 'span' | 'startedAt' | 'clock'>
117
+
118
+ const bound = new WeakMap<View, Map<Property, Binding>>()
119
+
120
+ class Derived {
121
+ constructor(
122
+ private readonly source: Root | undefined,
123
+ readonly read: (value: number) => number,
124
+ readonly color: boolean,
125
+ readonly mappings: readonly LiveMapping[] = [],
126
+ ) {}
127
+
128
+ get root(): Root {
129
+ return this.source ?? (this as unknown as Root)
130
+ }
131
+
132
+ get(): number | Color {
133
+ const value = this.read(this.root.raw())
134
+ return this.color ? new Color(value) : value
135
+ }
136
+
137
+ subscribe(listener: ValueListener<number | Color>, options?: SubscribeOptions) {
138
+ const release = this.root.retainConsumer()
139
+ try {
140
+ const stop = this.root.observers.subscribe(this.read, this.color, listener, options)
141
+ return () => {
142
+ stop()
143
+ release()
144
+ }
145
+ } catch (error) {
146
+ release()
147
+ throw error
148
+ }
149
+ }
150
+
151
+ to(
152
+ input: readonly number[],
153
+ output: readonly (number | ColorValue)[],
154
+ options?: MapOptions,
155
+ ): Derived {
156
+ if (this.color) throw new TypeError('A color cannot be mapped again.')
157
+ const map = mapping(input, output, options)
158
+ if (this.root instanceof SpringRoot && map.color)
159
+ throw new TypeError(
160
+ 'Live spring bindings currently support numeric transforms and opacity.',
161
+ )
162
+ const read = this.read
163
+ return new Derived(
164
+ this.root,
165
+ (value) => map.read(read(value)),
166
+ map.color,
167
+ map.color
168
+ ? []
169
+ : [
170
+ ...this.mappings,
171
+ {
172
+ input: [...map.input],
173
+ output: [...map.numbers],
174
+ clamp: options?.clamp !== false,
175
+ },
176
+ ],
177
+ )
178
+ }
179
+ }
180
+
181
+ class Root extends Derived {
182
+ private consumers = 0
183
+ readonly demandListeners = new Set<(active: boolean) => void>()
184
+ get demanded() {
185
+ return this.consumers > 0
186
+ }
187
+ retainConsumer(): () => void {
188
+ this.consumers++
189
+ try {
190
+ this.demandChanged()
191
+ } catch (error) {
192
+ this.consumers--
193
+ this.demandChanged()
194
+ throw error
195
+ }
196
+ let ended = false
197
+ return () => {
198
+ if (ended) return
199
+ ended = true
200
+ this.consumers--
201
+ this.demandChanged()
202
+ }
203
+ }
204
+ protected demandChanged() {
205
+ for (const listener of [...this.demandListeners]) listener(this.demanded)
206
+ }
207
+ readonly bindings = new Set<Binding>()
208
+ readonly listeners = new Set<() => void>()
209
+ readonly observers = new ValueObservers(
210
+ (time) => this.raw(time),
211
+ () => this.running,
212
+ )
213
+ private readonly rests = new Map<string, number>()
214
+ private readonly runs = new Map<string, Run>()
215
+ private presentation: Presentation | undefined
216
+ private cancelPresentation: (() => void) | undefined
217
+
218
+ constructor(initial: number) {
219
+ super(undefined, (value) => value, false)
220
+ if (!Number.isFinite(initial))
221
+ throw new RangeError(`A motion value starts from a finite number, got ${initial}.`)
222
+ this.rests.set('', initial)
223
+ }
224
+
225
+ raw(time = getDriver().now()): number {
226
+ let result = 0
227
+ for (const key of new Set([...this.rests.keys(), ...this.runs.keys()]))
228
+ result += this.part(key, time)
229
+ return result
230
+ }
231
+
232
+ /** Drag input owns only the base; bounds and snap points stay in base units. */
233
+ base(): number {
234
+ return this.part('')
235
+ }
236
+
237
+ private part(key: string, time = getDriver().now()): number {
238
+ const run = this.runs.get(key)
239
+ return run
240
+ ? run.base + run.span * run.track.valueAt(this.runTime(run, time))
241
+ : (this.rests.get(key) ?? 0)
242
+ }
243
+
244
+ private runTime(run: Presentation, time = getDriver().now()) {
245
+ return run.clock?.time() ?? time - run.startedAt
246
+ }
247
+
248
+ /** Internal development snapshot; reading it installs no observers. */
249
+ inspect() {
250
+ return {
251
+ velocity: [...this.runs.keys()].reduce((sum, key) => sum + this.velocity(key), 0),
252
+ resources:
253
+ this.bindings.size + this.listeners.size + this.observers.size + this.runs.size,
254
+ }
255
+ }
256
+
257
+ private velocity(key: string): number {
258
+ const run = this.runs.get(key)
259
+ if (!run || run.animation.paused) return 0
260
+ return run.span * run.track.velocityAt(this.runTime(run))
261
+ }
262
+
263
+ animate(
264
+ target: number | readonly (number | null)[],
265
+ transition: ValueAnimationOptions | SpringName = {},
266
+ ): PlaybackAnimation {
267
+ const options: ValueAnimationOptions =
268
+ typeof transition === 'string' ? springOf(transition) : transition
269
+ const key = options.layer === undefined ? '' : layerName(options.layer)
270
+ const from = this.part(key)
271
+ const frames = (typeof target === 'number' ? [null, target] : [...target]).map(
272
+ (frame) => (frame === null ? from : frame),
273
+ )
274
+ if (frames.length < 2) frames.unshift(from)
275
+ if (frames.some((frame) => typeof frame !== 'number' || !Number.isFinite(frame)))
276
+ throw new RangeError(`Invalid motion value keyframes: ${JSON.stringify(target)}.`)
277
+ const resolved = resolveTransition(options, 'x', frames.length)
278
+ const base = Math.min(...frames)
279
+ const span = Math.max(...frames) - base
280
+ const velocity = this.velocity(key)
281
+ if (resolved.curve.type === 'gravity') {
282
+ // Acceleration and velocity belong to the value's units, not normalized progress.
283
+ const track = createTrack('x', frames, resolved, velocity)
284
+ this.halt(key)
285
+ return track.end === 0 ? this.jump(track.to, key) : this.start(track, 0, 1, key)
286
+ }
287
+ if (span === 0) {
288
+ this.halt(key)
289
+ return this.jump(frames.at(-1)!, key)
290
+ }
291
+
292
+ // Sampled in the value's own range so springs settle relative to the distance.
293
+ const track = createTrack(
294
+ 'opacity',
295
+ frames.map((frame) => (frame - base) / span),
296
+ resolved,
297
+ velocity / span,
298
+ )
299
+ this.halt(key)
300
+ return track.end === 0
301
+ ? this.jump(base + span * track.to, key)
302
+ : this.start(track, base, span, key)
303
+ }
304
+
305
+ /** Plays a track sampled in the value's own units, such as a drag release. */
306
+ playTrack(track: Track): PlaybackAnimation {
307
+ this.halt('')
308
+ return track.end === 0 ? this.jump(track.to, '') : this.start(track, 0, 1, '')
309
+ }
310
+
311
+ private jump(value: number, key: string): PlaybackAnimation {
312
+ this.rests.set(key, value)
313
+ this.sync()
314
+ const animation = new TimedAnimation(
315
+ 0,
316
+ 0,
317
+ () => {},
318
+ () => {},
319
+ )
320
+ animation.start()
321
+ return animation
322
+ }
323
+
324
+ private start(
325
+ track: Track,
326
+ base: number,
327
+ span: number,
328
+ key: string,
329
+ ): PlaybackAnimation {
330
+ const driver = getDriver()
331
+ const animation = new TimedAnimation(
332
+ track.end,
333
+ track.arrival,
334
+ () => this.sync(),
335
+ (completed) => {
336
+ if (this.runs.get(key)?.animation !== animation) return
337
+ const held = base + span * track.valueAt(animation.time)
338
+ this.runs.delete(key)
339
+ if (!completed && key !== '') this.rests.delete(key)
340
+ else this.rests.set(key, completed ? base + span * track.to : held)
341
+ this.sync()
342
+ },
343
+ )
344
+ const run: Run = {
345
+ track,
346
+ base,
347
+ span,
348
+ animation,
349
+ startedAt: driver.now(),
350
+ clock: { time: () => animation.time },
351
+ }
352
+ this.runs.set(key, run)
353
+ this.sync()
354
+ animation.start()
355
+ return animation
356
+ }
357
+
358
+ set(value: number) {
359
+ if (!Number.isFinite(value))
360
+ throw new RangeError(`A motion value takes a finite number, got ${value}.`)
361
+ for (const key of [...this.runs.keys()]) this.halt(key)
362
+ this.rests.clear()
363
+ this.rests.set('', value)
364
+ this.sync()
365
+ }
366
+
367
+ setBase(value: number) {
368
+ if (!Number.isFinite(value))
369
+ throw new RangeError('A motion value takes a finite number.')
370
+ this.halt('')
371
+ this.rests.set('', value)
372
+ this.sync()
373
+ }
374
+
375
+ stopBase() {
376
+ this.stopChannel('')
377
+ }
378
+
379
+ stop(layer?: string) {
380
+ if (layer !== undefined) return this.stopChannel(layerName(layer))
381
+ if (!this.runs.size && this.rests.size === 1) return
382
+ const value = this.raw()
383
+ for (const key of [...this.runs.keys()]) this.halt(key)
384
+ this.rests.clear()
385
+ this.rests.set('', value)
386
+ this.sync()
387
+ }
388
+
389
+ private stopChannel(key: string) {
390
+ if (!this.runs.has(key) && (key === '' || !this.rests.has(key))) return
391
+ const held = this.part(key)
392
+ this.halt(key)
393
+ if (key === '') this.rests.set(key, held)
394
+ else this.rests.delete(key)
395
+ this.sync()
396
+ }
397
+
398
+ private sync() {
399
+ this.cancelPresentation?.()
400
+ this.cancelPresentation = undefined
401
+ const now = getDriver().now()
402
+ let rest = 0
403
+ for (const [key, value] of this.rests) if (!this.runs.has(key)) rest += value
404
+ const runs = [...this.runs.values()].filter((run) => !run.animation.paused)
405
+ for (const [key, run] of this.runs)
406
+ if (run.animation.paused) rest += this.part(key, now)
407
+ if (!runs.length) this.presentation = undefined
408
+ else if (runs.length === 1) {
409
+ const run = runs[0]!
410
+ this.presentation = { ...run, base: run.base + rest }
411
+ } else {
412
+ const { track, windowed } = valueTrack(
413
+ runs.map((run) => ({
414
+ track: run.track,
415
+ base: run.base,
416
+ span: run.span,
417
+ elapsed: this.runTime(run, now),
418
+ })),
419
+ rest,
420
+ )
421
+ const driver = getDriver()
422
+ const clock = driver.clock?.(track.end)
423
+ this.presentation = { track, base: 0, span: 1, startedAt: now, clock }
424
+ const cancelWindow = windowed
425
+ ? driver.after(track.end, () => this.sync())
426
+ : undefined
427
+ this.cancelPresentation = () => {
428
+ cancelWindow?.()
429
+ clock?.cancel()
430
+ }
431
+ }
432
+ this.changed()
433
+ }
434
+
435
+ protected changed() {
436
+ for (const binding of this.bindings) binding.sync()
437
+ for (const listener of [...this.listeners]) listener()
438
+ this.observers.changed(this.running)
439
+ }
440
+
441
+ /** The plan that plays this value's run, mapped, on one bound property. */
442
+ plan(binding: Binding): Plan {
443
+ const run = this.presentation
444
+ const { property, read } = binding
445
+ const write = (value: number) =>
446
+ isDecorationProperty(property)
447
+ ? clampDecoration(property, read(value))
448
+ : read(value)
449
+ if (!run) {
450
+ const value = write(this.raw())
451
+ return {
452
+ property,
453
+ keyframes: [],
454
+ transition: still,
455
+ track: () => instantTrack(property, value),
456
+ }
457
+ }
458
+ const { track, base, span } = run
459
+ const at = (progress: number) => write(base + span * progress)
460
+ const transition: ResolvedTransition = {
461
+ curve: {
462
+ type: 'tween',
463
+ duration: track.duration,
464
+ ease: [linear],
465
+ times: undefined,
466
+ },
467
+ delay: track.delay,
468
+ repeat: track.repeat,
469
+ reverse: track.reverse,
470
+ }
471
+ const mapped = (): Track => {
472
+ if (reducedMotion() && property !== 'opacity' && !isColorProperty(property))
473
+ return instantTrack(property, at(track.to))
474
+ return {
475
+ property,
476
+ delay: track.delay,
477
+ duration: track.duration,
478
+ repeat: track.repeat,
479
+ reverse: track.reverse,
480
+ times: track.times,
481
+ values: track.values.map(at),
482
+ to: at(track.to),
483
+ end: track.end,
484
+ arrival: track.arrival,
485
+ valueAt: (elapsed) => at(track.valueAt(elapsed)),
486
+ velocityAt: isColorProperty(property)
487
+ ? () => 0
488
+ : (elapsed) =>
489
+ (at(track.valueAt(elapsed + 0.5)) - at(track.valueAt(elapsed - 0.5))) *
490
+ 1000,
491
+ }
492
+ }
493
+ return { property, keyframes: [], transition, track: mapped }
494
+ }
495
+
496
+ /** Capture the current native presentation clock, even if a later command replaces it. */
497
+ captureTime(): () => number {
498
+ const run = this.presentation
499
+ return run ? () => this.runTime(run) : () => 0
500
+ }
501
+
502
+ elapsed(): number {
503
+ return this.presentation ? this.runTime(this.presentation) : 0
504
+ }
505
+
506
+ get paused(): boolean {
507
+ return this.runs.size > 0 && !this.running
508
+ }
509
+
510
+ get running(): boolean {
511
+ return [...this.runs.values()].some((run) => !run.animation.paused)
512
+ }
513
+
514
+ private halt(key: string) {
515
+ const run = this.runs.get(key)
516
+ if (!run) return
517
+ this.runs.delete(key)
518
+ run.animation.stop()
519
+ }
520
+ }
521
+
522
+ export interface SpringValueOptions extends SpringParams {
523
+ /** Initial follower position. Defaults to the source's current value. */
524
+ initial?: number
525
+ /** Absolute source units. Defaults to 0.001. */
526
+ restDelta?: number
527
+ /** Source units per second. Defaults to 0.01. */
528
+ restSpeed?: number
529
+ }
530
+
531
+ /** A retained native follower. Settling does not dispose its source subscription. */
532
+ export interface SpringValue extends DerivedValue {
533
+ readonly disposed: boolean
534
+ readonly moving: boolean
535
+ readonly paused: boolean
536
+ /** Hold position and velocity while retaining the current source target. */
537
+ pause(): void
538
+ /** Resume from the held state toward the current source. */
539
+ play(): void
540
+ /** Current velocity in source units per second. */
541
+ getVelocity(): number
542
+ dispose(): void
543
+ }
544
+
545
+ class SpringRoot extends Root {
546
+ private releaseDemand: (() => void) | undefined
547
+ externalActive = false
548
+ protected override demandChanged() {
549
+ const active = this.demanded && !this.ended && !this.held && !this.background
550
+ if (active && !this.releaseDemand) {
551
+ // A source status callback may synchronously suspend or dispose this follower.
552
+ this.releaseDemand = () => {}
553
+ let release: () => void
554
+ try {
555
+ release = this.input.root.retainConsumer()
556
+ } catch (error) {
557
+ this.releaseDemand = undefined
558
+ throw error
559
+ }
560
+ this.releaseDemand = release
561
+ if (!this.demanded || this.ended || this.held || this.background) {
562
+ this.releaseDemand = undefined
563
+ release()
564
+ }
565
+ } else if (!active && this.releaseDemand) {
566
+ const release = this.releaseDemand
567
+ this.releaseDemand = undefined
568
+ release()
569
+ }
570
+ super.demandChanged()
571
+ }
572
+ readonly node: LiveNode
573
+ private releaseSource: () => void = () => {}
574
+ private releaseReduced: () => void = () => {}
575
+ private ended = false
576
+ private held = false
577
+ private background = false
578
+ private suspend = () => {
579
+ this.background = true
580
+ this.updatePause()
581
+ }
582
+ private resume = () => {
583
+ this.background = false
584
+ this.updatePause()
585
+ }
586
+
587
+ constructor(
588
+ readonly input: Derived,
589
+ options: SpringValueOptions,
590
+ ) {
591
+ const source = input
592
+ const initial = options.initial ?? (source.get() as number)
593
+ const instant = options.duration === 0
594
+ const params = springPhysics(instant ? { ...options, duration: undefined } : options)
595
+ const speed = timeScale()
596
+ const physics = {
597
+ stiffness: params.stiffness * speed * speed,
598
+ damping: params.damping * speed,
599
+ mass: params.mass,
600
+ restDelta: options.restDelta ?? 0.001,
601
+ restSpeed: options.restSpeed ?? 0.01,
602
+ }
603
+ for (const [key, value] of Object.entries(physics))
604
+ if (!Number.isFinite(value) || (key === 'damping' ? value < 0 : value <= 0))
605
+ throw new RangeError(`Invalid springValue ${key}: ${value}.`)
606
+ if (
607
+ options.duration !== undefined &&
608
+ (!Number.isFinite(options.duration) || options.duration < 0)
609
+ )
610
+ throw new RangeError('springValue duration must be finite and nonnegative.')
611
+ if (options.bounce !== undefined && !Number.isFinite(options.bounce))
612
+ throw new RangeError('springValue bounce must be finite.')
613
+ super(initial)
614
+ this.node = liveSpringDriver().create(initial, physics, instant || reducedMotion())
615
+ const root = source.root
616
+ try {
617
+ let update: () => void
618
+ if (root instanceof SpringRoot) {
619
+ if (root.disposed) throw new Error('Cannot follow a disposed spring value.')
620
+ this.node.follow(root.node, source.mappings)
621
+ update = () => this.notify()
622
+ } else {
623
+ const playback = valuePlayback(source as unknown as DerivedValue)
624
+ update = () => {
625
+ if (!root.running) {
626
+ this.node.update({
627
+ value: source.get() as number,
628
+ running: false,
629
+ elapsed: 0,
630
+ duration: 0,
631
+ delay: 0,
632
+ repeat: 0,
633
+ reverse: false,
634
+ to: 0,
635
+ times: [],
636
+ values: [],
637
+ })
638
+ this.notify()
639
+ return
640
+ }
641
+ const { track, elapsed, running, value } = playback.snapshot()
642
+ this.node.update({
643
+ value,
644
+ elapsed,
645
+ running,
646
+ duration: track.duration,
647
+ delay: track.delay,
648
+ repeat: Number.isFinite(track.repeat) ? track.repeat : -1,
649
+ reverse: track.reverse,
650
+ to: track.to,
651
+ times: track.times,
652
+ values: track.values,
653
+ })
654
+ this.notify()
655
+ }
656
+ }
657
+ root.listeners.add(update)
658
+ this.releaseSource = () => {
659
+ root.listeners.delete(update)
660
+ }
661
+ this.releaseReduced = onReducedMotion((reduced) => {
662
+ this.node.reduced(instant || reduced)
663
+ this.notify()
664
+ })
665
+ Application.on(Application.suspendEvent, this.suspend)
666
+ Application.on(Application.resumeEvent, this.resume)
667
+ update()
668
+ } catch (error) {
669
+ this.dispose()
670
+ throw error
671
+ }
672
+ }
673
+ override raw(): number {
674
+ return this.node?.position() ?? super.raw()
675
+ }
676
+ override get running(): boolean {
677
+ return (
678
+ !this.ended &&
679
+ !this.held &&
680
+ !this.background &&
681
+ (this.externalActive || (this.node?.moving() ?? false))
682
+ )
683
+ }
684
+ get moving(): boolean {
685
+ return this.running
686
+ }
687
+ get disposed(): boolean {
688
+ return this.ended
689
+ }
690
+ override get paused(): boolean {
691
+ return this.held
692
+ }
693
+ pause() {
694
+ if (!this.ended) {
695
+ this.held = true
696
+ this.updatePause()
697
+ }
698
+ }
699
+ play() {
700
+ if (!this.ended) {
701
+ this.held = false
702
+ this.updatePause()
703
+ }
704
+ }
705
+ private updatePause() {
706
+ this.node.paused(this.held || this.background)
707
+ this.demandChanged()
708
+ this.notify()
709
+ }
710
+ getVelocity(): number {
711
+ return this.ended ? 0 : this.node.velocity()
712
+ }
713
+ override plan(): Plan {
714
+ throw new TypeError(
715
+ 'A live spring cannot be sampled as a fixed track. Bind it to transforms or opacity.',
716
+ )
717
+ }
718
+ notify() {
719
+ this.observers.changed(this.running)
720
+ for (const listener of [...this.listeners]) listener()
721
+ }
722
+ dispose() {
723
+ if (this.ended) return
724
+ this.ended = true
725
+ this.demandChanged()
726
+ this.releaseSource()
727
+ this.releaseReduced()
728
+ Application.off(Application.suspendEvent, this.suspend)
729
+ Application.off(Application.resumeEvent, this.resume)
730
+ for (const binding of [...this.bindings]) release(binding)
731
+ this.node.dispose()
732
+ this.observers.clear()
733
+ for (const listener of [...this.listeners]) listener()
734
+ this.listeners.clear()
735
+ }
736
+ }
737
+
738
+ /** Follow changing targets without rebuilding sampled animation tracks. Dispose with the owning scope. */
739
+ export function springValue(
740
+ source: DerivedValue,
741
+ options: SpringValueOptions = {},
742
+ ): SpringValue {
743
+ if (!(source instanceof Derived) || source.color)
744
+ throw new TypeError('springValue() needs a numeric Jolt motion value.')
745
+ if (!options || typeof options !== 'object')
746
+ throw new TypeError('springValue() options must be an object.')
747
+ return new SpringRoot(source, options) as unknown as SpringValue
748
+ }
749
+
750
+ /** Internal boundary notification for values driven directly by native input. */
751
+ export function liveValueInput(value: DerivedValue) {
752
+ if (!(value instanceof Derived) || !(value.root instanceof SpringRoot))
753
+ throw new TypeError('Native input requires a live value.')
754
+ const root = value.root
755
+ return {
756
+ node: root.node,
757
+ changed: () => root.notify(),
758
+ active(value: boolean) {
759
+ root.externalActive = value
760
+ root.notify()
761
+ },
762
+ demand(listener: (active: boolean) => void) {
763
+ root.demandListeners.add(listener)
764
+ listener(root.demanded)
765
+ return () => {
766
+ root.demandListeners.delete(listener)
767
+ }
768
+ },
769
+ }
770
+ }
771
+
772
+ function layerName(name: string): string {
773
+ if (typeof name !== 'string' || !name.length)
774
+ throw new TypeError('A layer is named by a nonempty string.')
775
+ return name
776
+ }
777
+
778
+ const still: ResolvedTransition = {
779
+ curve: { type: 'tween', duration: 0, ease: [linear], times: undefined },
780
+ delay: 0,
781
+ repeat: 0,
782
+ reverse: false,
783
+ }
784
+
785
+ /**
786
+ * A number that animates natively and drives the view properties bound to it.
787
+ *
788
+ * ```ts
789
+ * const progress = motionValue(0)
790
+ * bind(sheet, { y: progress.to([0, 1], [600, 0]) })
791
+ * animate(progress, 1, 'snappy')
792
+ * ```
793
+ */
794
+ export function motionValue(initial = 0): MotionValue {
795
+ return new Root(initial) as unknown as MotionValue
796
+ }
797
+
798
+ export function isMotionValue(target: unknown): target is MotionValue {
799
+ return target instanceof Root && !(target instanceof SpringRoot)
800
+ }
801
+
802
+ export function asRoot(value: MotionValue): Root {
803
+ return value as unknown as Root
804
+ }
805
+
806
+ /**
807
+ * Lets motion values drive a view's properties. The view takes their current values
808
+ * at once and follows every change on the same native clock. Returns a cleanup function.
809
+ *
810
+ * ```ts
811
+ * const release = bind(backdrop, { opacity: progress.to([0, 1], [0, 0.4]) })
812
+ * ```
813
+ */
814
+ export function bind(target: Target | readonly Target[], bindings: Bindings): () => void {
815
+ if (!bindings || typeof bindings !== 'object' || Array.isArray(bindings))
816
+ throw new TypeError('bind() takes an object of motion values.')
817
+ const views = toViews(target)
818
+ const entries: [Property, Derived][] = []
819
+ for (const [key, source] of Object.entries(bindings)) {
820
+ if (source === undefined) continue
821
+ if (!(source instanceof Derived))
822
+ throw new TypeError(`"${key}" needs a motion value, got ${String(source)}.`)
823
+ const properties: Property[] =
824
+ key === 'scale' ? ['scaleX', 'scaleY'] : isProperty(key) ? [key] : []
825
+ if (!properties.length) throw new Error(`Can't bind "${key}".`)
826
+ for (const property of properties) {
827
+ if (source.root instanceof SpringRoot) {
828
+ if (source.root.disposed) throw new Error('Cannot bind a disposed spring value.')
829
+ if (isColorProperty(property) || isDecorationProperty(property))
830
+ throw new TypeError(
831
+ 'Live spring bindings currently support numeric transforms and opacity.',
832
+ )
833
+ }
834
+ if (isColorProperty(property) !== source.color)
835
+ throw new TypeError(
836
+ source.color
837
+ ? `"${key}" takes numbers; map the value to numbers.`
838
+ : `"${key}" is a color; map the value to colors with to(input, colors).`,
839
+ )
840
+ entries.push([property, source])
841
+ }
842
+ }
843
+ for (const view of views)
844
+ for (const [property] of entries)
845
+ if (isColorProperty(property)) validateColorTarget(view, property)
846
+ else if (isDecorationProperty(property)) validateDecorationTarget(view, property)
847
+
848
+ const made: (() => void)[] = []
849
+ try {
850
+ for (const view of views)
851
+ for (const [property, source] of entries) made.push(attach(view, property, source))
852
+ } catch (error) {
853
+ for (const cleanup of made.reverse()) cleanup()
854
+ throw error
855
+ }
856
+ return () => made.forEach((release) => release())
857
+ }
858
+
859
+ function attach(view: View, property: Property, source: Derived) {
860
+ const existing = bound.get(view)?.get(property)
861
+ if (existing) release(existing)
862
+ const root = source.root
863
+ if (root instanceof SpringRoot)
864
+ return attachSpring(view, property as NumericProperty, source, root)
865
+ const binding: Binding = {
866
+ view,
867
+ property,
868
+ root,
869
+ source,
870
+ read: source.read,
871
+ sync() {
872
+ if (!view.isLoaded || !view.nativeViewProtected) return
873
+ if (!root.running) {
874
+ // A value at rest writes straight through, so scroll offsets cost one write per event.
875
+ binding.animation?.stop()
876
+ binding.animation = undefined
877
+ stopBase(view, [property])
878
+ const value = source.read(root.raw())
879
+ setBase(
880
+ view,
881
+ property,
882
+ isDecorationProperty(property) ? clampDecoration(property, value) : value,
883
+ )
884
+ if (isColorProperty(property) || isDecorationProperty(property))
885
+ getDriver().sync?.(view)
886
+ return
887
+ }
888
+ const animation = new ViewAnimation(view, [root.plan(binding)])
889
+ // Native keyframe setup can take time, especially for a composite track on Android.
890
+ // Catch up after construction so each binding joins the source's current phase.
891
+ const elapsed = root.elapsed()
892
+ if (root.paused) animation.pause()
893
+ if ((elapsed > 1 || root.paused) && animation.duration > 0)
894
+ animation.seekTime(elapsed)
895
+ binding.animation = animation
896
+ },
897
+ }
898
+ let properties = bound.get(view)
899
+ if (!properties) bound.set(view, (properties = new Map()))
900
+ properties.set(property, binding)
901
+ root.bindings.add(binding)
902
+ view.on('loaded', binding.sync)
903
+ try {
904
+ binding.sync()
905
+ } catch (error) {
906
+ release(binding)
907
+ throw error
908
+ }
909
+ return () => {
910
+ if (root.bindings.has(binding)) {
911
+ release(binding)
912
+ binding.animation?.stop()
913
+ }
914
+ }
915
+ }
916
+
917
+ function attachSpring(
918
+ view: View,
919
+ property: NumericProperty,
920
+ source: Derived,
921
+ root: SpringRoot,
922
+ ) {
923
+ let native: LiveBinding | undefined
924
+ let releaseMix: (() => void) | undefined
925
+ let releaseDemand: (() => void) | undefined
926
+ const detach = () => {
927
+ if (!native) return
928
+ native.cancel()
929
+ releaseMix?.()
930
+ releaseMix = undefined
931
+ native = undefined
932
+ releaseDemand?.()
933
+ releaseDemand = undefined
934
+ syncLiveSprings(view)
935
+ }
936
+ const binding: Binding = {
937
+ view,
938
+ property,
939
+ root,
940
+ source,
941
+ read: source.read,
942
+ sync() {
943
+ if (!view.isLoaded || !view.nativeViewProtected || root.disposed) return
944
+ detach()
945
+ stopBase(view, [property])
946
+ native = root.node.bind(view, property, source.mappings)
947
+ const handle = native
948
+ const retained = root.retainConsumer()
949
+ if (native !== handle || root.disposed) {
950
+ retained()
951
+ return
952
+ }
953
+ releaseDemand = retained
954
+ releaseMix = mixFor(view, property).attachLive({
955
+ read: () => handle.value(),
956
+ layers: (layers) => handle.layers(layers),
957
+ })
958
+ syncLiveSprings(view)
959
+ },
960
+ cleanup() {
961
+ view.off('unloaded', detach)
962
+ detach()
963
+ },
964
+ }
965
+ let properties = bound.get(view)
966
+ if (!properties) bound.set(view, (properties = new Map()))
967
+ properties.set(property, binding)
968
+ root.bindings.add(binding)
969
+ view.on('loaded', binding.sync)
970
+ view.on('unloaded', detach)
971
+ try {
972
+ binding.sync()
973
+ } catch (error) {
974
+ release(binding)
975
+ throw error
976
+ }
977
+ return () => {
978
+ if (root.bindings.has(binding)) release(binding)
979
+ }
980
+ }
981
+
982
+ /** A read-only value on an existing finite animation's clock. No second animation or timer. */
983
+ export function playbackProgress(animation: Animation): DerivedValue {
984
+ const source = playbackSource(animation)
985
+ if (!Number.isFinite(source.read().duration))
986
+ throw new RangeError('Progress requires a finite animation.')
987
+ const root = new PlaybackValue(source)
988
+ return new Derived(root, (value) => value, false) as unknown as DerivedValue
989
+ }
990
+
991
+ class PlaybackValue extends Root {
992
+ constructor(private readonly playback: ReturnType<typeof playbackSource>) {
993
+ super(0)
994
+ const source = playback
995
+ const off = source.subscribe(() => {
996
+ this.changed()
997
+ if (source.read().settled) off()
998
+ })
999
+ if (source.read().settled) off()
1000
+ this.observers.changed(this.running)
1001
+ }
1002
+
1003
+ override raw() {
1004
+ const state = this.playback.read()
1005
+ return (
1006
+ state.position ??
1007
+ (state.completed
1008
+ ? 1
1009
+ : state.duration > 0
1010
+ ? Math.max(0, Math.min(1, state.time / state.duration))
1011
+ : 0)
1012
+ )
1013
+ }
1014
+
1015
+ override get running() {
1016
+ const state = this.playback.read()
1017
+ return state.ready && !state.settled && !state.paused && state.duration > 0
1018
+ }
1019
+
1020
+ override get paused() {
1021
+ return this.playback.read().paused
1022
+ }
1023
+ override captureTime() {
1024
+ return () => this.playback.read().time
1025
+ }
1026
+ override elapsed() {
1027
+ return this.playback.read().time
1028
+ }
1029
+
1030
+ override plan(binding: Binding): Plan {
1031
+ const state = this.playback.read()
1032
+ if (!state.ready || state.settled || state.duration === 0)
1033
+ return {
1034
+ property: binding.property,
1035
+ keyframes: [],
1036
+ transition: still,
1037
+ track: () => instantTrack(binding.property, binding.read(this.raw())),
1038
+ }
1039
+ // Bypass timeScale: the source duration is already in platform playback milliseconds.
1040
+ const duration = state.duration
1041
+ const at = (ms: number) => binding.read(Math.max(0, Math.min(1, ms / duration)))
1042
+ const track: Track = {
1043
+ property: binding.property,
1044
+ delay: 0,
1045
+ duration,
1046
+ repeat: 0,
1047
+ reverse: false,
1048
+ times: [0, 1],
1049
+ values: [at(0), at(duration)],
1050
+ to: at(duration),
1051
+ end: duration,
1052
+ arrival: duration,
1053
+ valueAt: at,
1054
+ velocityAt: (ms) => (at(ms + 0.5) - at(ms - 0.5)) * 1000,
1055
+ }
1056
+ // Piecewise/color derivations need intermediate samples, not only the two endpoints.
1057
+ const steps = Math.max(1, Math.min(6000, Math.ceil((duration * 60) / 1000)))
1058
+ const times = Array.from({ length: steps + 1 }, (_, i) => i / steps)
1059
+ return {
1060
+ property: binding.property,
1061
+ keyframes: [],
1062
+ transition: still,
1063
+ track: () =>
1064
+ reducedMotion() &&
1065
+ binding.property !== 'opacity' &&
1066
+ !isColorProperty(binding.property)
1067
+ ? instantTrack(binding.property, at(duration))
1068
+ : { ...track, times, values: times.map((time) => at(time * duration)) },
1069
+ }
1070
+ }
1071
+ }
1072
+
1073
+ /** Internal compound-binding bridge. One raw snapshot per root keeps sibling mappings coherent. */
1074
+ export function valueSource(source: DerivedValue | DerivedColor, color: boolean) {
1075
+ if (!(source instanceof Derived) || source.color !== color)
1076
+ throw new TypeError(
1077
+ `Gradient component needs a ${color ? 'color' : 'numeric'} Jolt value.`,
1078
+ )
1079
+ const { root, read } = source
1080
+ return {
1081
+ identity: root,
1082
+ read,
1083
+ snapshot() {
1084
+ return {
1085
+ track: root.plan({ property: 'opacity', read: (value) => value } as Binding)
1086
+ .track!(),
1087
+ elapsed: root.elapsed(),
1088
+ time: root.captureTime(),
1089
+ running: root.running,
1090
+ value: root.raw(),
1091
+ }
1092
+ },
1093
+ subscribe(listener: () => void) {
1094
+ root.listeners.add(listener)
1095
+ return () => {
1096
+ root.listeners.delete(listener)
1097
+ }
1098
+ },
1099
+ }
1100
+ }
1101
+
1102
+ /** Treat values as opaque identities in declarative option comparisons. */
1103
+ export function isDerived(value: unknown): value is DerivedValue | DerivedColor {
1104
+ return value instanceof Derived
1105
+ }
1106
+
1107
+ /** Internal native-adapter bridge. Mappings are sampled at boundaries, never per frame. */
1108
+ export function valuePlayback(source: DerivedValue) {
1109
+ if (!(source instanceof Derived) || source.color)
1110
+ throw new TypeError('A native binding needs a numeric Jolt value.')
1111
+ const { root, read } = source
1112
+ return {
1113
+ snapshot() {
1114
+ return {
1115
+ track: root.plan({ property: 'opacity', read } as Binding).track!(),
1116
+ elapsed: root.elapsed(),
1117
+ paused: root.paused,
1118
+ running: root.running,
1119
+ value: read(root.raw()),
1120
+ }
1121
+ },
1122
+ subscribe(listener: () => void) {
1123
+ root.listeners.add(listener)
1124
+ return () => {
1125
+ root.listeners.delete(listener)
1126
+ }
1127
+ },
1128
+ }
1129
+ }
1130
+
1131
+ function release(binding: Binding) {
1132
+ binding.root.bindings.delete(binding)
1133
+ binding.view.off('loaded', binding.sync)
1134
+ const properties = bound.get(binding.view)
1135
+ if (properties?.get(binding.property) === binding) properties.delete(binding.property)
1136
+ binding.cleanup?.()
1137
+ }
1138
+
1139
+ /** A regular animation on a bound property takes it over; the value keeps its other bindings. */
1140
+ export function detachBindings(view: View, properties: readonly Property[]) {
1141
+ const map = bound.get(view)
1142
+ if (!map) return
1143
+ for (const property of properties) {
1144
+ const binding = map.get(property)
1145
+ if (binding) release(binding)
1146
+ }
1147
+ }
1148
+
1149
+ interface InspectedSource {
1150
+ identity: object
1151
+ kind: string
1152
+ value: number
1153
+ mappings: readonly LiveMapping[]
1154
+ input?: InspectedSource
1155
+ }
1156
+
1157
+ /** Read existing binding topology without subscribing or constructing new tracks. */
1158
+ export function inspectViewBindings(view: View) {
1159
+ const describe = (source: Derived, depth = 0): InspectedSource => ({
1160
+ identity: source.root,
1161
+ kind: source.root instanceof SpringRoot ? 'live spring' : 'motion value',
1162
+ value: source.root.raw(),
1163
+ mappings: source.mappings.map((mapping) => ({
1164
+ ...mapping,
1165
+ input: [...mapping.input],
1166
+ output: [...mapping.output],
1167
+ })),
1168
+ input:
1169
+ source.root instanceof SpringRoot && depth < 8
1170
+ ? describe(source.root.input, depth + 1)
1171
+ : undefined,
1172
+ })
1173
+ return [...(bound.get(view)?.values() ?? [])].map((binding) => ({
1174
+ property: binding.property,
1175
+ source: describe(binding.source ?? binding.root),
1176
+ value: binding.read(binding.root.raw()),
1177
+ }))
1178
+ }
1179
+
1180
+ function mapping(
1181
+ input: readonly number[],
1182
+ output: readonly (number | ColorValue)[],
1183
+ options: MapOptions = {},
1184
+ ) {
1185
+ if (!Array.isArray(input) || !Array.isArray(output) || input.length < 2)
1186
+ throw new RangeError('to() needs at least two input and output values.')
1187
+ if (input.length !== output.length)
1188
+ throw new RangeError(
1189
+ `to() has ${input.length} inputs but ${output.length} outputs; give one output per input.`,
1190
+ )
1191
+ if (input.some((value) => !Number.isFinite(value)))
1192
+ throw new RangeError('to() inputs must all be finite numbers.')
1193
+ const descending = input[input.length - 1]! < input[0]!
1194
+ if (
1195
+ input.some(
1196
+ (value, i) => i > 0 && (descending ? value > input[i - 1]! : value < input[i - 1]!),
1197
+ )
1198
+ )
1199
+ throw new RangeError(
1200
+ 'to() inputs must be monotonic (ascending or descending); ranges cannot change direction.',
1201
+ )
1202
+ // Normalize pairs together, without changing caller-owned arrays or duplicate-stop rules.
1203
+ if (descending) {
1204
+ input = [...input].reverse()
1205
+ output = [...output].reverse()
1206
+ }
1207
+ const color = typeof output[0] !== 'number'
1208
+ const numbers = color ? output.map((value) => parseColor(value)) : (output as number[])
1209
+ if (
1210
+ !color &&
1211
+ numbers.some((value) => typeof value !== 'number' || !Number.isFinite(value))
1212
+ )
1213
+ throw new RangeError('to() outputs must all be finite numbers, or all colors.')
1214
+ const mixers = color
1215
+ ? numbers.slice(1).map((value, i) => colorMixer(numbers[i]!, value))
1216
+ : undefined
1217
+ const clamp = color || options.clamp !== false
1218
+ const last = input.length - 1
1219
+ const read = (value: number) => {
1220
+ if (clamp && value <= input[0]!) return numbers[0]!
1221
+ if (clamp && value >= input[last]!) return numbers[last]!
1222
+ let i = 0
1223
+ while (i < last - 1 && value > input[i + 1]!) i++
1224
+ const span = input[i + 1]! - input[i]!
1225
+ const t = span > 0 ? (value - input[i]!) / span : value < input[i]! ? 0 : 1
1226
+ if (mixers) return mixers[i]!(Math.min(1, Math.max(0, t)))
1227
+ return numbers[i]! + (numbers[i + 1]! - numbers[i]!) * t
1228
+ }
1229
+ return { read, color, input, numbers }
1230
+ }
1231
+
1232
+ function springOf(name: SpringName): Transition {
1233
+ if (!Object.hasOwn(springs, name)) throw new Error(`Unknown spring "${name}".`)
1234
+ return springs[name]
1235
+ }
1236
+
1237
+ /** A number that follows a scroll view's offset. Bind it like a motion value. */
1238
+ export interface ScrollValue extends DerivedValue {
1239
+ /** Follows this scroll view instead, or none with `null`. Stable, so it works as a template ref. */
1240
+ readonly ref: (scrollView: Target | null) => void
1241
+ /** Stops following; bound views stay where they are. */
1242
+ dispose(): void
1243
+ }
1244
+
1245
+ /**
1246
+ * The scroll offset of a ScrollView, in DIPs, as a value that bindings follow.
1247
+ *
1248
+ * ```ts
1249
+ * const offset = scrollValue(scroll)
1250
+ * bind(header, { y: offset.to([0, 120], [0, -60]), opacity: offset.to([0, 80], [1, 0]) })
1251
+ * ```
1252
+ */
1253
+ export function scrollValue(
1254
+ scrollView?: Target | null,
1255
+ options: { axis?: 'x' | 'y' } = {},
1256
+ ): ScrollValue {
1257
+ const axis = options.axis ?? 'y'
1258
+ if (axis !== 'x' && axis !== 'y') throw new TypeError('Scroll axis must be "x" or "y".')
1259
+ const root = new Root(0)
1260
+ let source: ScrollView | undefined
1261
+ const read = () => {
1262
+ if (!source) return
1263
+ const offset = axis === 'x' ? source.horizontalOffset : source.verticalOffset
1264
+ if (Number.isFinite(offset) && offset !== root.raw()) root.set(offset)
1265
+ }
1266
+ const onScroll = (event: EventData) => {
1267
+ const { scrollX, scrollY } = event as ScrollEventData
1268
+ const offset = axis === 'x' ? scrollX : scrollY
1269
+ if (Number.isFinite(offset) && offset !== root.raw()) root.set(offset)
1270
+ }
1271
+ const detach = () => {
1272
+ source?.off(ScrollView.scrollEvent, onScroll)
1273
+ source?.off('loaded', read)
1274
+ source = undefined
1275
+ }
1276
+ const ref = (target: Target | null) => {
1277
+ const next = target === null ? undefined : toViews(target)[0]
1278
+ if (next === source) return
1279
+ if (next !== undefined && !(next instanceof ScrollView))
1280
+ throw new TypeError('scrollValue() follows a NativeScript ScrollView.')
1281
+ detach()
1282
+ source = next
1283
+ source?.on(ScrollView.scrollEvent, onScroll)
1284
+ source?.on('loaded', read)
1285
+ read()
1286
+ }
1287
+ if (scrollView) ref(scrollView)
1288
+ return Object.assign(root as unknown as DerivedValue, { ref, dispose: detach })
1289
+ }