@symbiote-native/engine 0.1.0

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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/build/accessibility-info/index.android.d.ts +3 -0
  4. package/build/accessibility-info/index.android.js +166 -0
  5. package/build/accessibility-info/index.d.ts +1 -0
  6. package/build/accessibility-info/index.ios.d.ts +3 -0
  7. package/build/accessibility-info/index.ios.js +219 -0
  8. package/build/accessibility-info/index.js +5 -0
  9. package/build/accessibility-info/shared.d.ts +34 -0
  10. package/build/accessibility-info/shared.js +13 -0
  11. package/build/action-sheet-ios/index.d.ts +36 -0
  12. package/build/action-sheet-ios/index.js +74 -0
  13. package/build/alert/index.android.d.ts +5 -0
  14. package/build/alert/index.android.js +117 -0
  15. package/build/alert/index.d.ts +1 -0
  16. package/build/alert/index.ios.d.ts +7 -0
  17. package/build/alert/index.ios.js +83 -0
  18. package/build/alert/index.js +8 -0
  19. package/build/alert/shared.d.ts +19 -0
  20. package/build/alert/shared.js +17 -0
  21. package/build/animated/animated-component-shared.d.ts +5 -0
  22. package/build/animated/animated-component-shared.js +54 -0
  23. package/build/animated/animation.d.ts +9 -0
  24. package/build/animated/animation.js +6 -0
  25. package/build/animated/animations/base.d.ts +27 -0
  26. package/build/animated/animations/base.js +90 -0
  27. package/build/animated/animations/composition.d.ts +38 -0
  28. package/build/animated/animations/composition.js +236 -0
  29. package/build/animated/animations/decay.d.ts +22 -0
  30. package/build/animated/animations/decay.js +65 -0
  31. package/build/animated/animations/raf.d.ts +5 -0
  32. package/build/animated/animations/raf.js +39 -0
  33. package/build/animated/animations/spring-config.d.ts +6 -0
  34. package/build/animated/animations/spring-config.js +55 -0
  35. package/build/animated/animations/spring.d.ts +50 -0
  36. package/build/animated/animations/spring.js +207 -0
  37. package/build/animated/animations/timing.d.ts +27 -0
  38. package/build/animated/animations/timing.js +101 -0
  39. package/build/animated/animations/tracking.d.ts +14 -0
  40. package/build/animated/animations/tracking.js +43 -0
  41. package/build/animated/bezier.d.ts +1 -0
  42. package/build/animated/bezier.js +101 -0
  43. package/build/animated/color.d.ts +37 -0
  44. package/build/animated/color.js +183 -0
  45. package/build/animated/easing.d.ts +20 -0
  46. package/build/animated/easing.js +96 -0
  47. package/build/animated/event.d.ts +36 -0
  48. package/build/animated/event.js +252 -0
  49. package/build/animated/graph.d.ts +38 -0
  50. package/build/animated/graph.js +227 -0
  51. package/build/animated/index.d.ts +20 -0
  52. package/build/animated/index.js +28 -0
  53. package/build/animated/interpolation-node.d.ts +16 -0
  54. package/build/animated/interpolation-node.js +57 -0
  55. package/build/animated/interpolation.d.ts +22 -0
  56. package/build/animated/interpolation.js +199 -0
  57. package/build/animated/mock.d.ts +56 -0
  58. package/build/animated/mock.js +127 -0
  59. package/build/animated/native/native-animated.d.ts +43 -0
  60. package/build/animated/native/native-animated.js +146 -0
  61. package/build/animated/operators.d.ts +80 -0
  62. package/build/animated/operators.js +266 -0
  63. package/build/animated/props.d.ts +20 -0
  64. package/build/animated/props.js +187 -0
  65. package/build/animated/style.d.ts +26 -0
  66. package/build/animated/style.js +187 -0
  67. package/build/animated/value-xy.d.ts +35 -0
  68. package/build/animated/value-xy.js +106 -0
  69. package/build/animated/value.d.ts +36 -0
  70. package/build/animated/value.js +185 -0
  71. package/build/app-registry/index.d.ts +40 -0
  72. package/build/app-registry/index.js +144 -0
  73. package/build/app-state/index.d.ts +16 -0
  74. package/build/app-state/index.js +105 -0
  75. package/build/appearance/index.d.ts +12 -0
  76. package/build/appearance/index.js +84 -0
  77. package/build/back-handler/index.d.ts +14 -0
  78. package/build/back-handler/index.js +106 -0
  79. package/build/commit.d.ts +16 -0
  80. package/build/commit.js +678 -0
  81. package/build/debug.d.ts +5 -0
  82. package/build/debug.js +18 -0
  83. package/build/dimensions/index.d.ts +28 -0
  84. package/build/dimensions/index.js +148 -0
  85. package/build/dispatch.d.ts +2 -0
  86. package/build/dispatch.js +18 -0
  87. package/build/events/index.d.ts +1 -0
  88. package/build/events/index.js +691 -0
  89. package/build/fabric.d.ts +32 -0
  90. package/build/fabric.js +59 -0
  91. package/build/host-instance/index.d.ts +11 -0
  92. package/build/host-instance/index.js +49 -0
  93. package/build/i18n-manager/index.d.ts +13 -0
  94. package/build/i18n-manager/index.js +91 -0
  95. package/build/index.d.ts +80 -0
  96. package/build/index.js +72 -0
  97. package/build/interaction-manager/index.d.ts +45 -0
  98. package/build/interaction-manager/index.js +222 -0
  99. package/build/keyboard/index.d.ts +31 -0
  100. package/build/keyboard/index.js +142 -0
  101. package/build/layout-animation/index.d.ts +66 -0
  102. package/build/layout-animation/index.js +183 -0
  103. package/build/linking/index.android.d.ts +2 -0
  104. package/build/linking/index.android.js +18 -0
  105. package/build/linking/index.d.ts +1 -0
  106. package/build/linking/index.ios.d.ts +2 -0
  107. package/build/linking/index.ios.js +9 -0
  108. package/build/linking/index.js +6 -0
  109. package/build/linking/shared.d.ts +32 -0
  110. package/build/linking/shared.js +98 -0
  111. package/build/native-events.d.ts +24 -0
  112. package/build/native-events.js +129 -0
  113. package/build/native-modules.d.ts +6 -0
  114. package/build/native-modules.js +57 -0
  115. package/build/node.d.ts +36 -0
  116. package/build/node.js +194 -0
  117. package/build/pan-responder/index.d.ts +53 -0
  118. package/build/pan-responder/index.js +353 -0
  119. package/build/permissions-android/index.d.ts +115 -0
  120. package/build/permissions-android/index.js +185 -0
  121. package/build/pixel-ratio/index.d.ts +8 -0
  122. package/build/pixel-ratio/index.js +27 -0
  123. package/build/platform/index.android.d.ts +22 -0
  124. package/build/platform/index.android.js +60 -0
  125. package/build/platform/index.d.ts +1 -0
  126. package/build/platform/index.ios.d.ts +18 -0
  127. package/build/platform/index.ios.js +62 -0
  128. package/build/platform/index.js +5 -0
  129. package/build/platform/shared.d.ts +25 -0
  130. package/build/platform/shared.js +41 -0
  131. package/build/platform-color.d.ts +19 -0
  132. package/build/platform-color.js +25 -0
  133. package/build/post-commit.d.ts +4 -0
  134. package/build/post-commit.js +16 -0
  135. package/build/process-aspect-ratio.d.ts +1 -0
  136. package/build/process-aspect-ratio.js +34 -0
  137. package/build/process-background-image/index.d.ts +28 -0
  138. package/build/process-background-image/index.js +557 -0
  139. package/build/process-box-shadow/index.d.ts +11 -0
  140. package/build/process-box-shadow/index.js +193 -0
  141. package/build/process-filter.d.ts +31 -0
  142. package/build/process-filter.js +304 -0
  143. package/build/process-font-variant.d.ts +1 -0
  144. package/build/process-font-variant.js +17 -0
  145. package/build/process-transform/index.d.ts +5 -0
  146. package/build/process-transform/index.js +120 -0
  147. package/build/process-transform-origin/index.d.ts +3 -0
  148. package/build/process-transform-origin/index.js +108 -0
  149. package/build/registry.d.ts +31 -0
  150. package/build/registry.js +145 -0
  151. package/build/settings/index.d.ts +8 -0
  152. package/build/settings/index.js +126 -0
  153. package/build/share/index.android.d.ts +3 -0
  154. package/build/share/index.android.js +56 -0
  155. package/build/share/index.d.ts +1 -0
  156. package/build/share/index.ios.d.ts +3 -0
  157. package/build/share/index.ios.js +47 -0
  158. package/build/share/index.js +6 -0
  159. package/build/share/shared.d.ts +32 -0
  160. package/build/share/shared.js +32 -0
  161. package/build/status-bar/index.android.d.ts +5 -0
  162. package/build/status-bar/index.android.js +83 -0
  163. package/build/status-bar/index.d.ts +1 -0
  164. package/build/status-bar/index.ios.d.ts +5 -0
  165. package/build/status-bar/index.ios.js +66 -0
  166. package/build/status-bar/index.js +4 -0
  167. package/build/status-bar/shared.d.ts +22 -0
  168. package/build/status-bar/shared.js +22 -0
  169. package/build/style/index.d.ts +1 -0
  170. package/build/style/index.js +30 -0
  171. package/build/style-registry/index.d.ts +11 -0
  172. package/build/style-registry/index.js +165 -0
  173. package/build/style-sheet/index.d.ts +20 -0
  174. package/build/style-sheet/index.js +121 -0
  175. package/build/styles.d.ts +220 -0
  176. package/build/styles.js +7 -0
  177. package/build/surface.d.ts +16 -0
  178. package/build/surface.js +67 -0
  179. package/build/tags.d.ts +1 -0
  180. package/build/tags.js +10 -0
  181. package/build/text-input-state.d.ts +5 -0
  182. package/build/text-input-state.js +29 -0
  183. package/build/toast-android/index.d.ts +10 -0
  184. package/build/toast-android/index.js +108 -0
  185. package/build/vibration/index.android.d.ts +2 -0
  186. package/build/vibration/index.android.js +18 -0
  187. package/build/vibration/index.d.ts +1 -0
  188. package/build/vibration/index.ios.d.ts +2 -0
  189. package/build/vibration/index.ios.js +54 -0
  190. package/build/vibration/index.js +6 -0
  191. package/build/vibration/shared.d.ts +15 -0
  192. package/build/vibration/shared.js +68 -0
  193. package/build/view-config.d.ts +1 -0
  194. package/build/view-config.js +114 -0
  195. package/package.json +41 -0
@@ -0,0 +1,199 @@
1
+ // Interpolation, ported from React Native's AnimatedInterpolation.js (the pure
2
+ // `interpolate` math plus the string/color output-range branch). Maps an input
3
+ // range to an output range through an easing function with configurable
4
+ // extrapolation. A numeric output range stays number -> number; a string output
5
+ // range (values-with-units like '0deg' or colors like '#000') is interpolated
6
+ // per numeric token and reassembled. See createStringInterpolation below.
7
+ import { normalizeColor } from './color';
8
+ import { Easing } from './easing';
9
+ function interpolateSegment(input, inputMin, inputMax, outputMin, outputMax, easing, extrapolateLeft, extrapolateRight) {
10
+ let result = input;
11
+ // Extrapolate below the range.
12
+ if (result < inputMin) {
13
+ if (extrapolateLeft === 'identity')
14
+ return result;
15
+ if (extrapolateLeft === 'clamp')
16
+ result = inputMin;
17
+ // 'extend' falls through (no-op).
18
+ }
19
+ // Extrapolate above the range.
20
+ if (result > inputMax) {
21
+ if (extrapolateRight === 'identity')
22
+ return result;
23
+ if (extrapolateRight === 'clamp')
24
+ result = inputMax;
25
+ // 'extend' falls through (no-op).
26
+ }
27
+ if (outputMin === outputMax)
28
+ return outputMin;
29
+ if (inputMin === inputMax) {
30
+ return input <= inputMin ? outputMin : outputMax;
31
+ }
32
+ // Normalize into the input segment.
33
+ if (inputMin === -Infinity) {
34
+ result = -result;
35
+ }
36
+ else if (inputMax === Infinity) {
37
+ result = result - inputMin;
38
+ }
39
+ else {
40
+ result = (result - inputMin) / (inputMax - inputMin);
41
+ }
42
+ result = easing(result);
43
+ // Project onto the output segment.
44
+ if (outputMin === -Infinity) {
45
+ result = -result;
46
+ }
47
+ else if (outputMax === Infinity) {
48
+ result = result + outputMin;
49
+ }
50
+ else {
51
+ result = result * (outputMax - outputMin) + outputMin;
52
+ }
53
+ return result;
54
+ }
55
+ function findRange(input, inputRange) {
56
+ let i = 1;
57
+ for (; i < inputRange.length - 1; ++i) {
58
+ if (inputRange[i] >= input)
59
+ break;
60
+ }
61
+ return i - 1;
62
+ }
63
+ function checkValidInputRange(arr) {
64
+ if (arr.length < 2)
65
+ throw new Error('inputRange must have at least 2 elements');
66
+ for (let i = 1; i < arr.length; ++i) {
67
+ if (!(arr[i] >= arr[i - 1])) {
68
+ throw new Error(`inputRange must be monotonically non-decreasing ${String(arr)}`);
69
+ }
70
+ }
71
+ }
72
+ function checkInfiniteRange(name, arr) {
73
+ if (arr.length < 2)
74
+ throw new Error(`${name} must have at least 2 elements`);
75
+ if (arr.length === 2 && arr[0] === -Infinity && arr[1] === Infinity) {
76
+ throw new Error(`${name} cannot be ]-infinity;+infinity[ ${String(arr)}`);
77
+ }
78
+ }
79
+ export function checkValidRanges(inputRange, outputRange) {
80
+ checkInfiniteRange('outputRange', outputRange);
81
+ checkInfiniteRange('inputRange', inputRange);
82
+ checkValidInputRange(inputRange);
83
+ if (inputRange.length !== outputRange.length) {
84
+ throw new Error(`inputRange (${inputRange.length}) and outputRange (${outputRange.length}) must have the same length`);
85
+ }
86
+ }
87
+ export function createNumericInterpolation(config) {
88
+ const { inputRange, outputRange } = config;
89
+ const easing = config.easing ?? Easing.linear;
90
+ const extrapolateLeft = config.extrapolateLeft ?? config.extrapolate ?? 'extend';
91
+ const extrapolateRight = config.extrapolateRight ?? config.extrapolate ?? 'extend';
92
+ return input => {
93
+ const range = findRange(input, inputRange);
94
+ return interpolateSegment(input, inputRange[range], inputRange[range + 1], outputRange[range], outputRange[range + 1], easing, extrapolateLeft, extrapolateRight);
95
+ };
96
+ }
97
+ // One numeric token: a signed int/float with optional fraction and exponent.
98
+ // Sweeps a string for these so the surrounding non-numeric text (units, commas,
99
+ // 'rgba(' ... ')') survives as the template. Ported from RN
100
+ // AnimatedInterpolation.js:183.
101
+ const numericComponentRegex = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
102
+ // Split one output string into numeric components plus surrounding template.
103
+ // A parseable color collapses to four channels (r,g,b 0-255, a 0-1) via the
104
+ // shared color.ts RGBA decoder; any other string keeps its non-numeric text so
105
+ // it can be rebuilt around the interpolated numbers. Ported from RN
106
+ // AnimatedInterpolation.js:186 (mapStringToNumericComponents), with RN's packed-
107
+ // int decode replaced by color.ts's RgbaValue (DRY: one RGBA parser).
108
+ function mapStringToNumericComponents(input) {
109
+ const color = normalizeColor(input);
110
+ if (color !== undefined) {
111
+ return { isColor: true, components: [color.r, color.g, color.b, color.a] };
112
+ }
113
+ const components = [];
114
+ let lastMatchEnd = 0;
115
+ let match;
116
+ numericComponentRegex.lastIndex = 0;
117
+ while ((match = numericComponentRegex.exec(input)) !== null) {
118
+ if (match.index > lastMatchEnd) {
119
+ components.push(input.substring(lastMatchEnd, match.index));
120
+ }
121
+ components.push(Number.parseFloat(match[0]));
122
+ lastMatchEnd = match.index + match[0].length;
123
+ }
124
+ if (components.length === 0) {
125
+ throw new Error('outputRange must contain color or value with numeric component');
126
+ }
127
+ if (lastMatchEnd < input.length) {
128
+ components.push(input.substring(lastMatchEnd));
129
+ }
130
+ return { isColor: false, components };
131
+ }
132
+ // Interpolate a string output range. Each output string is decomposed into its
133
+ // numeric tokens (and, for non-colors, the literal template between them); one
134
+ // numeric interpolation is built per token position; at evaluation time the
135
+ // tokens are interpolated and recombined into a string of the original shape:
136
+ //
137
+ // '0deg' -> '360deg' value-with-units -> '180deg' at 0.5
138
+ // '#000000' -> '#ffffff' color -> 'rgba(128, 128, 128, 1)'
139
+ //
140
+ // Ported from RN AnimatedInterpolation.js:234 (createStringInterpolation).
141
+ function createStringInterpolation(config, outputRange) {
142
+ if (outputRange.length < 2)
143
+ throw new Error('Bad output range');
144
+ const decomposed = outputRange.map(mapStringToNumericComponents);
145
+ const isColor = decomposed[0].isColor;
146
+ if (!decomposed.every(output => output.isColor === isColor)) {
147
+ throw new Error('All elements of output range should either be a color or a string with numeric components');
148
+ }
149
+ const firstLength = decomposed[0].components.length;
150
+ if (!decomposed.every(output => output.components.length === firstLength)) {
151
+ throw new Error('All elements of output range should have the same number of components');
152
+ }
153
+ // Per token position, the numeric values across all output strings, a number
154
+ // output range the scalar path can interpolate. Colors are already all-numeric;
155
+ // templates keep only the numeric tokens (the literal text is rejoined later).
156
+ const numericComponents = decomposed.map(output => output.isColor
157
+ ? output.components
158
+ : output.components.filter((c) => typeof c === 'number'));
159
+ const interpolations = numericComponents[0].map((_, tokenIndex) => createNumericInterpolation({
160
+ inputRange: config.inputRange,
161
+ outputRange: numericComponents.map(components => components[tokenIndex]),
162
+ easing: config.easing,
163
+ extrapolate: config.extrapolate,
164
+ extrapolateLeft: config.extrapolateLeft,
165
+ extrapolateRight: config.extrapolateRight,
166
+ }));
167
+ if (!isColor) {
168
+ // Walk the first output's template, dropping each interpolated number into
169
+ // the slot its numeric token occupied and copying the literal text through.
170
+ const template = decomposed[0].components;
171
+ return input => {
172
+ const values = interpolations.map(interpolation => interpolation(input));
173
+ let i = 0;
174
+ return template.map(c => (typeof c === 'number' ? values[i++] : c)).join('');
175
+ };
176
+ }
177
+ // Colors: r,g,b must be integers, so round them; alpha stays continuous but is
178
+ // rounded to 3 decimals to match RN's output. Matches RN
179
+ // AnimatedInterpolation.js:288-296.
180
+ return input => {
181
+ const channels = interpolations.map((interpolation, i) => {
182
+ const value = interpolation(input);
183
+ return i < 3 ? Math.round(value) : Math.round(value * 1000) / 1000;
184
+ });
185
+ return `rgba(${channels[0]}, ${channels[1]}, ${channels[2]}, ${channels[3]})`;
186
+ };
187
+ }
188
+ function isStringOutputRange(outputRange) {
189
+ return typeof outputRange[0] === 'string';
190
+ }
191
+ // Dispatch on the output range's element type: a string range (units or colors)
192
+ // goes through createStringInterpolation, a numeric range through the scalar
193
+ // path. Mirrors RN AnimatedInterpolation.js:373 (_getInterpolation).
194
+ export function createInterpolation(config) {
195
+ if (isStringOutputRange(config.outputRange)) {
196
+ return createStringInterpolation(config, config.outputRange);
197
+ }
198
+ return createNumericInterpolation({ ...config, outputRange: config.outputRange });
199
+ }
@@ -0,0 +1,56 @@
1
+ import { AnimatedValue } from './value';
2
+ import { AnimatedValueXY } from './value-xy';
3
+ import { AnimatedColor } from './color';
4
+ import { add, subtract, multiply, divide, modulo, diffClamp } from './operators';
5
+ import { event, forkEvent, unforkEvent } from './event';
6
+ import type { ICompositeAnimation, ITimingConfig, ISpringConfig, IDecayConfig, IParallelConfig } from './animations/composition';
7
+ declare function spring(value: AnimatedValue, config: ISpringConfig): ICompositeAnimation;
8
+ declare function timing(value: AnimatedValue, config: ITimingConfig): ICompositeAnimation;
9
+ declare function decay(_value: AnimatedValue, _config: IDecayConfig): ICompositeAnimation;
10
+ declare function sequence(animations: ICompositeAnimation[]): ICompositeAnimation;
11
+ declare function parallel(animations: ICompositeAnimation[], _config?: IParallelConfig): ICompositeAnimation;
12
+ declare function delay(_time: number): ICompositeAnimation;
13
+ declare function stagger(_time: number, animations: ICompositeAnimation[]): ICompositeAnimation;
14
+ declare function loop(_animation: ICompositeAnimation): ICompositeAnimation;
15
+ export declare const AnimatedMock: {
16
+ Value: typeof AnimatedValue;
17
+ ValueXY: typeof AnimatedValueXY;
18
+ Color: typeof AnimatedColor;
19
+ Easing: {
20
+ step0(n: number): number;
21
+ step1(n: number): number;
22
+ linear(t: number): number;
23
+ ease(t: number): number;
24
+ quad(t: number): number;
25
+ cubic(t: number): number;
26
+ poly(n: number): import("./easing").IEasingFunction;
27
+ sin(t: number): number;
28
+ circle(t: number): number;
29
+ exp(t: number): number;
30
+ elastic(bounciness?: number): import("./easing").IEasingFunction;
31
+ back(s?: number): import("./easing").IEasingFunction;
32
+ bounce(t: number): number;
33
+ bezier(x1: number, y1: number, x2: number, y2: number): import("./easing").IEasingFunction;
34
+ in(easing: import("./easing").IEasingFunction): import("./easing").IEasingFunction;
35
+ out(easing: import("./easing").IEasingFunction): import("./easing").IEasingFunction;
36
+ inOut(easing: import("./easing").IEasingFunction): import("./easing").IEasingFunction;
37
+ };
38
+ timing: typeof timing;
39
+ spring: typeof spring;
40
+ decay: typeof decay;
41
+ parallel: typeof parallel;
42
+ sequence: typeof sequence;
43
+ stagger: typeof stagger;
44
+ loop: typeof loop;
45
+ delay: typeof delay;
46
+ add: typeof add;
47
+ subtract: typeof subtract;
48
+ multiply: typeof multiply;
49
+ divide: typeof divide;
50
+ modulo: typeof modulo;
51
+ diffClamp: typeof diffClamp;
52
+ event: typeof event;
53
+ forkEvent: typeof forkEvent;
54
+ unforkEvent: typeof unforkEvent;
55
+ };
56
+ export {};
@@ -0,0 +1,127 @@
1
+ // AnimatedMock: ported from RN's AnimatedMock.js. When the host reports
2
+ // Platform.isDisableAnimations (reduced-motion accessibility setting, or a test
3
+ // environment), RN swaps the whole Animated namespace for this mock: the surface
4
+ // is identical but every animation jumps straight to its final value and fires the
5
+ // end callback synchronously, with no frames. Snapshot tests and reduced-motion
6
+ // users get the resting state without flake. The value graph, operators, easing,
7
+ // events and createAnimatedComponent are reused verbatim from the live engine.
8
+ // Only the driver factories (timing/spring/decay) and compositions are mocked.
9
+ import { AnimatedValue } from './value';
10
+ import { AnimatedValueXY } from './value-xy';
11
+ import { AnimatedColor } from './color';
12
+ import { AnimatedNode } from './graph';
13
+ import { Easing } from './easing';
14
+ import { add, subtract, multiply, divide, modulo, diffClamp } from './operators';
15
+ import { event, forkEvent, unforkEvent } from './event';
16
+ import { dlog } from '../debug';
17
+ // Prevent a callback invocation from recursively triggering another callback,
18
+ // which may trigger another animation (RN's AnimatedMock.js:36-60).
19
+ let inAnimationCallback = false;
20
+ function mockAnimationStart(start) {
21
+ return callback => {
22
+ const guardedCallback = callback === undefined
23
+ ? callback
24
+ : (result) => {
25
+ if (inAnimationCallback) {
26
+ dlog('Ignoring recursive animation callback when running mock animations');
27
+ return;
28
+ }
29
+ inAnimationCallback = true;
30
+ try {
31
+ callback(result);
32
+ }
33
+ finally {
34
+ inAnimationCallback = false;
35
+ }
36
+ };
37
+ start(guardedCallback);
38
+ };
39
+ }
40
+ const emptyAnimation = {
41
+ start: () => { },
42
+ stop: () => { },
43
+ reset: () => { },
44
+ };
45
+ function mockCompositeAnimation(animations) {
46
+ return {
47
+ ...emptyAnimation,
48
+ start: mockAnimationStart(callback => {
49
+ animations.forEach(animation => animation.start());
50
+ callback?.({ finished: true });
51
+ }),
52
+ };
53
+ }
54
+ // `toValue` is widened to `number | AnimatedNode` at the composition layer; the
55
+ // mock needs a concrete number to land on, so resolve a node target to its current
56
+ // value (RN reaches the same number through `anyValue`).
57
+ function resolveToValue(toValue) {
58
+ if (toValue instanceof AnimatedNode) {
59
+ const current = toValue.__getValue();
60
+ return typeof current === 'number' ? current : 0;
61
+ }
62
+ return toValue;
63
+ }
64
+ function spring(value, config) {
65
+ return {
66
+ ...emptyAnimation,
67
+ start: mockAnimationStart(callback => {
68
+ value.setValue(resolveToValue(config.toValue));
69
+ callback?.({ finished: true });
70
+ }),
71
+ };
72
+ }
73
+ function timing(value, config) {
74
+ return {
75
+ ...emptyAnimation,
76
+ start: mockAnimationStart(callback => {
77
+ value.setValue(resolveToValue(config.toValue));
78
+ callback?.({ finished: true });
79
+ }),
80
+ };
81
+ }
82
+ // Decay has no toValue to land on, so RN returns the empty animation (AnimatedMock.js:121).
83
+ function decay(_value, _config) {
84
+ return emptyAnimation;
85
+ }
86
+ function sequence(animations) {
87
+ return mockCompositeAnimation(animations);
88
+ }
89
+ function parallel(animations, _config) {
90
+ return mockCompositeAnimation(animations);
91
+ }
92
+ function delay(_time) {
93
+ return emptyAnimation;
94
+ }
95
+ function stagger(_time, animations) {
96
+ return mockCompositeAnimation(animations);
97
+ }
98
+ function loop(_animation) {
99
+ return emptyAnimation;
100
+ }
101
+ // The mocked namespace surface. The animation factories above resolve immediately;
102
+ // everything else (value nodes, operators, easing, events) is the real engine. The
103
+ // animated components are spread in by the caller (animated/index.ts) so this file
104
+ // stays free of the createAnimatedComponent / TDZ-sensitive container wrapping.
105
+ export const AnimatedMock = {
106
+ Value: AnimatedValue,
107
+ ValueXY: AnimatedValueXY,
108
+ Color: AnimatedColor,
109
+ Easing,
110
+ timing,
111
+ spring,
112
+ decay,
113
+ parallel,
114
+ sequence,
115
+ stagger,
116
+ loop,
117
+ delay,
118
+ add,
119
+ subtract,
120
+ multiply,
121
+ divide,
122
+ modulo,
123
+ diffClamp,
124
+ event,
125
+ forkEvent,
126
+ unforkEvent,
127
+ };
@@ -0,0 +1,43 @@
1
+ export type IPlatformConfig = Record<string, unknown>;
2
+ export interface INativeNodeConfig {
3
+ readonly type: string;
4
+ readonly [key: string]: unknown;
5
+ }
6
+ export interface INativeAnimationConfig {
7
+ readonly type: string;
8
+ readonly [key: string]: unknown;
9
+ }
10
+ export interface INativeEventMapping {
11
+ readonly nativeEventPath: readonly string[];
12
+ readonly animatedValueTag: number;
13
+ }
14
+ export interface INativeEndResult {
15
+ finished: boolean;
16
+ value?: number;
17
+ offset?: number;
18
+ }
19
+ export type INativeEndCallback = (result: INativeEndResult) => void;
20
+ export declare function isNativeAnimatedAvailable(): boolean;
21
+ export declare function generateNativeNodeTag(): number;
22
+ export declare function generateNativeAnimationId(): number;
23
+ export declare const nativeAnimated: {
24
+ createAnimatedNode(tag: number, config: INativeNodeConfig): void;
25
+ connectAnimatedNodes(parentTag: number, childTag: number): void;
26
+ disconnectAnimatedNodes(parentTag: number, childTag: number): void;
27
+ connectAnimatedNodeToView(nodeTag: number, viewTag: number): void;
28
+ disconnectAnimatedNodeFromView(nodeTag: number, viewTag: number): void;
29
+ restoreDefaultValues(nodeTag: number): void;
30
+ dropAnimatedNode(tag: number): void;
31
+ startAnimatingNode(animationId: number, nodeTag: number, config: INativeAnimationConfig, endCallback: INativeEndCallback): void;
32
+ stopAnimation(animationId: number): void;
33
+ setAnimatedNodeValue(nodeTag: number, value: number): void;
34
+ setAnimatedNodeOffset(nodeTag: number, offset: number): void;
35
+ flattenAnimatedNodeOffset(nodeTag: number): void;
36
+ extractAnimatedNodeOffset(nodeTag: number): void;
37
+ startListeningToAnimatedNodeValue(tag: number): void;
38
+ stopListeningToAnimatedNodeValue(tag: number): void;
39
+ startListeningToValue(tag: number, callback: (value: number) => void): void;
40
+ stopListeningToValue(tag: number): void;
41
+ addAnimatedEventToView(viewTag: number, eventName: string, mapping: INativeEventMapping): void;
42
+ removeAnimatedEventFromView(viewTag: number, eventName: string, animatedNodeTag: number): void;
43
+ };
@@ -0,0 +1,146 @@
1
+ // The native-driver bridge (ADR 0017). When an animation runs with
2
+ // useNativeDriver:true, the whole value graph is mirrored into native "animated
3
+ // nodes" and the curve is handed to the stock NativeAnimated TurboModule, which
4
+ // then mutates the bound shadow node every frame with ZERO JS per frame.
5
+ //
6
+ // We consume the module that ships in stock react-native, no native fork. On
7
+ // iOS bridgeless it registers as `NativeAnimatedTurboModule`; we fall back to the
8
+ // legacy `NativeAnimatedModule` name. Resolution goes through the same JSI seam as
9
+ // every other native module (getNativeModule), consistent with ADR 0012. The
10
+ // module is resolved lazily on first use, so importing this file headless (no
11
+ // native host) is inert until a native-driven animation actually starts.
12
+ import { dlog } from '../../debug';
13
+ import { getNativeModule } from '../../native-modules';
14
+ import { NativeEventEmitter } from '../../native-events';
15
+ // iOS bridgeless registers the Turbo variant; the legacy name is the fallback.
16
+ const TURBO_MODULE_NAME = 'NativeAnimatedTurboModule';
17
+ const LEGACY_MODULE_NAME = 'NativeAnimatedModule';
18
+ let resolved = null;
19
+ function module() {
20
+ if (resolved !== null)
21
+ return resolved;
22
+ // Don't cache a miss: the native host may not be installed yet at first call
23
+ // (or a headless smoke installs a fake afterwards).
24
+ const found = getNativeModule(TURBO_MODULE_NAME) ??
25
+ getNativeModule(LEGACY_MODULE_NAME);
26
+ if (found !== null)
27
+ resolved = found;
28
+ return found;
29
+ }
30
+ // True when the stock native module is present in the binary: the gate the
31
+ // drivers consult before honouring useNativeDriver:true (else they fall back to
32
+ // the JS-driven path of ADR 0016).
33
+ export function isNativeAnimatedAvailable() {
34
+ return module() !== null;
35
+ }
36
+ let nextNodeTag = 1;
37
+ let nextAnimationId = 1;
38
+ export function generateNativeNodeTag() {
39
+ return nextNodeTag++;
40
+ }
41
+ export function generateNativeAnimationId() {
42
+ return nextAnimationId++;
43
+ }
44
+ // JS observation of a native-driven value. While native owns the frames, JS sees
45
+ // no per-frame change, so a JS listener on a native value asks native to stream
46
+ // updates back, which it emits as `onAnimatedValueUpdate` ({tag, value}) on the
47
+ // device event bus. One subscription fans those out to per-tag callbacks. The event
48
+ // NAME is the load-bearing contract with the stock native module (a wrong name is
49
+ // silent headless and dead on device).
50
+ const VALUE_UPDATE_EVENT = 'onAnimatedValueUpdate';
51
+ const valueListeners = new Map();
52
+ let valueUpdateSubscription;
53
+ function isRecord(value) {
54
+ return typeof value === 'object' && value !== null;
55
+ }
56
+ function ensureValueUpdateSubscription() {
57
+ if (valueUpdateSubscription !== undefined)
58
+ return;
59
+ valueUpdateSubscription = new NativeEventEmitter().addListener(VALUE_UPDATE_EVENT, payload => {
60
+ if (!isRecord(payload))
61
+ return;
62
+ const tag = Reflect.get(payload, 'tag');
63
+ const value = Reflect.get(payload, 'value');
64
+ if (typeof tag === 'number' && typeof value === 'number') {
65
+ valueListeners.get(tag)?.(value);
66
+ }
67
+ });
68
+ }
69
+ // Thin pass-throughs. Calls are issued synchronously in dependency order
70
+ // (createAnimatedNode before connect before start), so no operation queue is
71
+ // needed on iOS. RN's own non-single-op path calls straight through when its
72
+ // queue is empty. Each guards on availability so a missing module degrades to a
73
+ // logged no-op rather than a throw inside a commit.
74
+ export const nativeAnimated = {
75
+ createAnimatedNode(tag, config) {
76
+ module()?.createAnimatedNode(tag, config);
77
+ },
78
+ connectAnimatedNodes(parentTag, childTag) {
79
+ module()?.connectAnimatedNodes(parentTag, childTag);
80
+ },
81
+ disconnectAnimatedNodes(parentTag, childTag) {
82
+ module()?.disconnectAnimatedNodes(parentTag, childTag);
83
+ },
84
+ connectAnimatedNodeToView(nodeTag, viewTag) {
85
+ dlog(`native: connect node=${nodeTag} -> view=${viewTag}`);
86
+ module()?.connectAnimatedNodeToView(nodeTag, viewTag);
87
+ },
88
+ disconnectAnimatedNodeFromView(nodeTag, viewTag) {
89
+ module()?.disconnectAnimatedNodeFromView(nodeTag, viewTag);
90
+ },
91
+ restoreDefaultValues(nodeTag) {
92
+ dlog(`native: restoreDefaultValues node=${nodeTag}`);
93
+ module()?.restoreDefaultValues(nodeTag);
94
+ },
95
+ dropAnimatedNode(tag) {
96
+ module()?.dropAnimatedNode(tag);
97
+ },
98
+ startAnimatingNode(animationId, nodeTag, config, endCallback) {
99
+ dlog(`native: startAnimatingNode id=${animationId} node=${nodeTag} type=${config.type}`);
100
+ module()?.startAnimatingNode(animationId, nodeTag, config, endCallback);
101
+ },
102
+ stopAnimation(animationId) {
103
+ module()?.stopAnimation(animationId);
104
+ },
105
+ setAnimatedNodeValue(nodeTag, value) {
106
+ module()?.setAnimatedNodeValue(nodeTag, value);
107
+ },
108
+ setAnimatedNodeOffset(nodeTag, offset) {
109
+ module()?.setAnimatedNodeOffset(nodeTag, offset);
110
+ },
111
+ flattenAnimatedNodeOffset(nodeTag) {
112
+ module()?.flattenAnimatedNodeOffset(nodeTag);
113
+ },
114
+ extractAnimatedNodeOffset(nodeTag) {
115
+ module()?.extractAnimatedNodeOffset(nodeTag);
116
+ },
117
+ startListeningToAnimatedNodeValue(tag) {
118
+ module()?.startListeningToAnimatedNodeValue(tag);
119
+ },
120
+ stopListeningToAnimatedNodeValue(tag) {
121
+ module()?.stopListeningToAnimatedNodeValue(tag);
122
+ },
123
+ // High-level value observation: register a per-tag callback and ask native to
124
+ // stream this node's updates. The last unsubscribe tears the shared device
125
+ // subscription down too.
126
+ startListeningToValue(tag, callback) {
127
+ ensureValueUpdateSubscription();
128
+ valueListeners.set(tag, callback);
129
+ dlog(`native: startListeningToValue node=${tag}`);
130
+ module()?.startListeningToAnimatedNodeValue(tag);
131
+ },
132
+ stopListeningToValue(tag) {
133
+ valueListeners.delete(tag);
134
+ module()?.stopListeningToAnimatedNodeValue(tag);
135
+ if (valueListeners.size === 0 && valueUpdateSubscription !== undefined) {
136
+ valueUpdateSubscription.remove();
137
+ valueUpdateSubscription = undefined;
138
+ }
139
+ },
140
+ addAnimatedEventToView(viewTag, eventName, mapping) {
141
+ module()?.addAnimatedEventToView(viewTag, eventName, mapping);
142
+ },
143
+ removeAnimatedEventFromView(viewTag, eventName, animatedNodeTag) {
144
+ module()?.removeAnimatedEventFromView(viewTag, eventName, animatedNodeTag);
145
+ },
146
+ };
@@ -0,0 +1,80 @@
1
+ import { AnimatedNode, AnimatedWithChildren } from './graph';
2
+ import { AnimatedInterpolation } from './interpolation-node';
3
+ import type { IInterpolationConfig } from './interpolation';
4
+ import type { INativeNodeConfig, IPlatformConfig } from './native/native-animated';
5
+ export declare class AnimatedAddition extends AnimatedWithChildren {
6
+ private readonly a;
7
+ private readonly b;
8
+ constructor(a: AnimatedNode | number, b: AnimatedNode | number);
9
+ __getValue(): number;
10
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
11
+ __attach(): void;
12
+ __detach(): void;
13
+ __makeNative(platformConfig?: IPlatformConfig): void;
14
+ __getNativeConfig(): INativeNodeConfig;
15
+ }
16
+ export declare class AnimatedSubtraction extends AnimatedWithChildren {
17
+ private readonly a;
18
+ private readonly b;
19
+ constructor(a: AnimatedNode | number, b: AnimatedNode | number);
20
+ __getValue(): number;
21
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
22
+ __attach(): void;
23
+ __detach(): void;
24
+ __makeNative(platformConfig?: IPlatformConfig): void;
25
+ __getNativeConfig(): INativeNodeConfig;
26
+ }
27
+ export declare class AnimatedMultiplication extends AnimatedWithChildren {
28
+ private readonly a;
29
+ private readonly b;
30
+ constructor(a: AnimatedNode | number, b: AnimatedNode | number);
31
+ __getValue(): number;
32
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
33
+ __attach(): void;
34
+ __detach(): void;
35
+ __makeNative(platformConfig?: IPlatformConfig): void;
36
+ __getNativeConfig(): INativeNodeConfig;
37
+ }
38
+ export declare class AnimatedDivision extends AnimatedWithChildren {
39
+ private readonly a;
40
+ private readonly b;
41
+ private warnedAboutDivideByZero;
42
+ constructor(a: AnimatedNode | number, b: AnimatedNode | number);
43
+ __getValue(): number;
44
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
45
+ __attach(): void;
46
+ __detach(): void;
47
+ __makeNative(platformConfig?: IPlatformConfig): void;
48
+ __getNativeConfig(): INativeNodeConfig;
49
+ }
50
+ export declare class AnimatedModulo extends AnimatedWithChildren {
51
+ private readonly a;
52
+ private readonly modulus;
53
+ constructor(a: AnimatedNode, modulus: number);
54
+ __getValue(): number;
55
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
56
+ __attach(): void;
57
+ __detach(): void;
58
+ __makeNative(platformConfig?: IPlatformConfig): void;
59
+ __getNativeConfig(): INativeNodeConfig;
60
+ }
61
+ export declare class AnimatedDiffClamp extends AnimatedWithChildren {
62
+ private readonly a;
63
+ private readonly min;
64
+ private readonly max;
65
+ private value;
66
+ private lastValue;
67
+ constructor(a: AnimatedNode, min: number, max: number);
68
+ __getValue(): number;
69
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
70
+ __attach(): void;
71
+ __detach(): void;
72
+ __makeNative(platformConfig?: IPlatformConfig): void;
73
+ __getNativeConfig(): INativeNodeConfig;
74
+ }
75
+ export declare function add(a: AnimatedNode | number, b: AnimatedNode | number): AnimatedAddition;
76
+ export declare function subtract(a: AnimatedNode | number, b: AnimatedNode | number): AnimatedSubtraction;
77
+ export declare function multiply(a: AnimatedNode | number, b: AnimatedNode | number): AnimatedMultiplication;
78
+ export declare function divide(a: AnimatedNode | number, b: AnimatedNode | number): AnimatedDivision;
79
+ export declare function modulo(a: AnimatedNode, modulus: number): AnimatedModulo;
80
+ export declare function diffClamp(a: AnimatedNode, min: number, max: number): AnimatedDiffClamp;