embedded-react 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,352 +1,357 @@
1
- /*
2
- * Copyright 2026 Cory Lamming
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- // Animated — the React Native analog, backed by the engine's native-driver value system
18
- // (er_anim_value_*). An Animated.Value is a handle to an engine-side float; binding it to a node
19
- // prop (via Animated.View) lets the engine advance the animation each frame with NO per-frame JS.
20
- import { createElement, useRef, useEffect } from 'react';
21
- import { NativeUI } from '../native-ui.js';
22
- import { splitAnimatedStyle } from './split-style.js';
23
-
24
- /** A standalone animatable value bound to an engine-side float. */
25
- export class AnimatedValue {
26
- constructor(initial = 0) {
27
- this.__animated = true;
28
- this._handle = NativeUI.animValueCreate(initial);
29
- this._value = initial;
30
- this._destroyed = false;
31
- }
32
-
33
- setValue(v) {
34
- this._value = v;
35
- NativeUI.animValueSet(this._handle, v);
36
- }
37
-
38
- /**
39
- * Releases the engine-side value slot. The engine's value pool is fixed-size (unlike RN, where the
40
- * JS value is simply garbage-collected), so a value tied to a mounting/unmounting component must be
41
- * freed explicitly — see useAnimatedValue. Idempotent; the value must not be used after destroy().
42
- */
43
- destroy() {
44
- if (this._destroyed) return;
45
- this._destroyed = true;
46
- NativeUI.animValueDestroy(this._handle);
47
- }
48
-
49
- __getValue() {
50
- return NativeUI.animValueGet(this._handle);
51
- }
52
-
53
- __bind(node, prop) {
54
- // The engine applies the current value on bind and ignores duplicate (node, prop) pairs, so
55
- // this is safe to call on every render.
56
- NativeUI.animValueBind(this._handle, node, prop);
57
- }
58
-
59
- interpolate(config) {
60
- return new AnimatedInterpolation(this, config);
61
- }
62
- }
63
-
64
- /**
65
- * A value derived from a parent Animated.Value through a piecewise-linear interpolation.
66
- * config = { inputRange, outputRange, extrapolate?, extrapolateLeft?, extrapolateRight? }; the
67
- * extrapolate tokens ('extend' | 'clamp' | 'identity') control behavior outside the input range.
68
- */
69
- export class AnimatedInterpolation {
70
- constructor(parent, config) {
71
- this.__animated = true;
72
- this._parent = parent;
73
- this._config = config;
74
- }
75
-
76
- __bind(node, prop) {
77
- NativeUI.animValueBindInterpolated(this._parent._handle, node, prop, this._config);
78
- }
79
-
80
- interpolate(config) {
81
- // Chained interpolation isn't supported by the engine; re-map against the same parent.
82
- return new AnimatedInterpolation(this._parent, config);
83
- }
84
- }
85
-
86
- /** Wraps fn so it runs at most once, regardless of how many times it is invoked. */
87
- function once(fn) {
88
- let called = false;
89
- return (arg) => {
90
- if (called) return;
91
- called = true;
92
- if (fn) fn(arg);
93
- };
94
- }
95
-
96
- /**
97
- * Wraps a NativeUI animation call in an object with start()/stop() (RN's animation handle shape).
98
- *
99
- * start(onComplete) wires the engine's completion callback through the bridge: onComplete is called
100
- * with `{ finished }` when the animation ends naturally (finished: true) or is interrupted by a new
101
- * animation / stop() (finished: false). `_value` is exposed so Animated.loop can reset it between
102
- * iterations.
103
- */
104
- function makeAnimation(value, toValue, config) {
105
- let handle = 0;
106
- return {
107
- _value: value,
108
- start(onComplete) {
109
- // Always pass a wrapper so we can null out the (possibly recycled) handle on completion.
110
- const cb = (finished) => {
111
- handle = 0;
112
- if (onComplete) onComplete({ finished: !!finished });
113
- };
114
- handle = NativeUI.animValueAnimate(value._handle, toValue, config, cb);
115
- },
116
- stop() {
117
- if (handle) NativeUI.animStop(handle);
118
- },
119
- };
120
- }
121
-
122
- export function timing(value, config = {}) {
123
- const { toValue = 0, useNativeDriver, ...rest } = config;
124
- return makeAnimation(value, toValue, { type: 'timing', ...rest });
125
- }
126
-
127
- export function spring(value, config = {}) {
128
- const { toValue = 0, useNativeDriver, ...rest } = config;
129
- return makeAnimation(value, toValue, { type: 'spring', ...rest });
130
- }
131
-
132
- export function decay(value, config = {}) {
133
- const { useNativeDriver, ...rest } = config;
134
- // Decay coasts from its velocity; the engine ignores the target, so pass the current value.
135
- return makeAnimation(value, value.__getValue(), { type: 'decay', ...rest });
136
- }
137
-
138
- // --- Composition -------------------------------------------------------------------------------
139
- // These are pure JS over each child animation's start()/stop() + the §2 timer globals. The engine
140
- // deactivates a finished animation before firing its completion callback, so chaining the next
141
- // animation from inside a completion handler is safe (no per-frame JS — each child still runs in C).
142
-
143
- /** Runs animations one after another; each starts when the previous finishes. */
144
- export function sequence(animations) {
145
- let current = 0;
146
- let stopped = false;
147
- return {
148
- start(onComplete) {
149
- const done = once(onComplete);
150
- const next = (result) => {
151
- if (stopped || !result || result.finished === false) {
152
- done({ finished: false });
153
- return;
154
- }
155
- if (current >= animations.length) {
156
- done({ finished: true });
157
- return;
158
- }
159
- animations[current++].start(next);
160
- };
161
- next({ finished: true }); // kick off the first entry
162
- },
163
- stop() {
164
- stopped = true;
165
- if (current > 0 && current <= animations.length) animations[current - 1].stop();
166
- },
167
- };
168
- }
169
-
170
- /** Runs animations simultaneously; completes when all finish. stopTogether (default true). */
171
- export function parallel(animations, config) {
172
- const stopTogether = !config || config.stopTogether !== false;
173
- let stopped = false;
174
- return {
175
- start(onComplete) {
176
- const done = once(onComplete);
177
- const total = animations.length;
178
- if (total === 0) {
179
- done({ finished: true });
180
- return;
181
- }
182
- let doneCount = 0;
183
- let anyUnfinished = false;
184
- const onChild = (result) => {
185
- doneCount++;
186
- if (!result || result.finished === false) {
187
- anyUnfinished = true;
188
- if (stopTogether && !stopped) {
189
- stopped = true;
190
- for (const a of animations) a.stop();
191
- }
192
- }
193
- if (doneCount >= total) done({ finished: !anyUnfinished });
194
- };
195
- for (const a of animations) a.start(onChild);
196
- },
197
- stop() {
198
- stopped = true;
199
- for (const a of animations) a.stop();
200
- },
201
- };
202
- }
203
-
204
- /** Like parallel, but each animation starts `delay` ms after the previous one (uses setTimeout). */
205
- export function stagger(delay, animations) {
206
- let stopped = false;
207
- const timers = [];
208
- return {
209
- start(onComplete) {
210
- const done = once(onComplete);
211
- const total = animations.length;
212
- if (total === 0) {
213
- done({ finished: true });
214
- return;
215
- }
216
- let doneCount = 0;
217
- let anyUnfinished = false;
218
- const onChild = (result) => {
219
- doneCount++;
220
- if (!result || result.finished === false) anyUnfinished = true;
221
- if (doneCount >= total) done({ finished: !anyUnfinished });
222
- };
223
- animations.forEach((a, i) => {
224
- if (i === 0) {
225
- a.start(onChild);
226
- } else {
227
- timers.push(
228
- setTimeout(() => {
229
- if (!stopped) a.start(onChild);
230
- }, delay * i),
231
- );
232
- }
233
- });
234
- },
235
- stop() {
236
- stopped = true;
237
- for (const t of timers) clearTimeout(t);
238
- for (const a of animations) a.stop();
239
- },
240
- };
241
- }
242
-
243
- /** An animation that simply waits `time` ms — useful as a spacer inside sequence (uses setTimeout). */
244
- export function delay(time) {
245
- let timer = 0;
246
- return {
247
- start(onComplete) {
248
- const done = once(onComplete);
249
- timer = setTimeout(() => {
250
- timer = 0;
251
- done({ finished: true });
252
- }, time);
253
- },
254
- stop() {
255
- if (timer) {
256
- clearTimeout(timer);
257
- timer = 0;
258
- }
259
- },
260
- };
261
- }
262
-
263
- /**
264
- * Repeats an animation. iterations: -1 (default) loops forever; otherwise that many times.
265
- * By default the underlying value is reset to its loop-start position before each iteration
266
- * (resetBeforeIteration), matching RN — so a timing 0→1 repeats 0→1 rather than ping-ponging.
267
- */
268
- export function loop(animation, config) {
269
- const iterations = config && Number.isInteger(config.iterations) ? config.iterations : -1;
270
- const resetBeforeIteration = !config || config.resetBeforeIteration !== false;
271
- let stopped = false;
272
- return {
273
- _value: animation._value,
274
- start(onComplete) {
275
- const done = once(onComplete);
276
- const startValue = resetBeforeIteration && animation._value ? animation._value.__getValue() : null;
277
- let count = 0;
278
- const run = (result) => {
279
- if (stopped || !result || result.finished === false) {
280
- done({ finished: false });
281
- return;
282
- }
283
- if (iterations >= 0 && count >= iterations) {
284
- done({ finished: true });
285
- return;
286
- }
287
- count++;
288
- if (resetBeforeIteration && animation._value && startValue != null && count > 1) {
289
- animation._value.setValue(startValue);
290
- }
291
- animation.start(run);
292
- };
293
- run({ finished: true });
294
- },
295
- stop() {
296
- stopped = true;
297
- animation.stop();
298
- },
299
- };
300
- }
301
-
302
- /**
303
- * Returns a stable Animated.Value that persists across renders (the analog of
304
- * `useRef(new Animated.Value(initial)).current`), and frees the engine-side value slot when the
305
- * component unmounts. The unmount cleanup matters here in a way it doesn't in React Native: the
306
- * engine's value pool is fixed-size, so a value owned by a mounting/unmounting component (e.g., a list
307
- * row or a tab screen) would otherwise leak a slot on every unmount.
308
- */
309
- export function useAnimatedValue(initial = 0) {
310
- const ref = useRef(null);
311
- if (ref.current == null) {
312
- ref.current = new AnimatedValue(initial);
313
- }
314
- // Empty deps → the cleanup runs only on unmount. `initial` is read once on the first render (matching
315
- // useRef semantics); later changes don't recreate the value, as in React Native.
316
- useEffect(() => {
317
- const value = ref.current;
318
- return () => value.destroy();
319
- }, []);
320
- return ref.current;
321
- }
322
-
323
- /**
324
- * Wraps a host component so animated values in its `style` are bound to the node (native driver).
325
- */
326
- export function createAnimatedComponent(Component) {
327
- return function AnimatedComponent(props) {
328
- const { style, ...rest } = props;
329
- const { staticStyle, bindings } = splitAnimatedStyle(style);
330
- const ref = (node) => {
331
- if (node == null) return; // unmount
332
- for (const b of bindings) b.value.__bind(node, b.prop);
333
- };
334
- return createElement(Component, { ...rest, style: staticStyle, ref });
335
- };
336
- }
337
-
338
- export const Animated = {
339
- Value: AnimatedValue,
340
- View: createAnimatedComponent('View'),
341
- Text: createAnimatedComponent('Text'),
342
- Image: createAnimatedComponent('Image'),
343
- timing,
344
- spring,
345
- decay,
346
- sequence,
347
- parallel,
348
- stagger,
349
- delay,
350
- loop,
351
- createAnimatedComponent,
352
- };
1
+ /*
2
+ * Copyright 2026 Cory Lamming
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ // Animated — the React Native analog, backed by the engine's native-driver value system
18
+ // (er_anim_value_*). An Animated.Value is a handle to an engine-side float; binding it to a node
19
+ // prop (via Animated.View) lets the engine advance the animation each frame with NO per-frame JS.
20
+ import { createElement, useRef, useEffect } from 'react';
21
+ import { NativeUI } from '../native-ui.js';
22
+ import { splitAnimatedStyle } from './split-style.js';
23
+
24
+ /** A standalone animatable value bound to an engine-side float. */
25
+ export class AnimatedValue {
26
+ constructor(initial = 0) {
27
+ this.__animated = true;
28
+ this._handle = NativeUI.animValueCreate(initial);
29
+ this._value = initial;
30
+ this._destroyed = false;
31
+ }
32
+
33
+ setValue(v) {
34
+ this._value = v;
35
+ NativeUI.animValueSet(this._handle, v);
36
+ }
37
+
38
+ /**
39
+ * Releases the engine-side value slot. The engine's value pool is fixed-size (unlike RN, where the
40
+ * JS value is simply garbage-collected), so a value tied to a mounting/unmounting component must be
41
+ * freed explicitly — see useAnimatedValue. Idempotent; the value must not be used after destroy().
42
+ */
43
+ destroy() {
44
+ if (this._destroyed) return;
45
+ this._destroyed = true;
46
+ NativeUI.animValueDestroy(this._handle);
47
+ }
48
+
49
+ __getValue() {
50
+ return NativeUI.animValueGet(this._handle);
51
+ }
52
+
53
+ __bind(node, prop) {
54
+ // The engine applies the current value on bind and ignores duplicate (node, prop) pairs, so
55
+ // this is safe to call on every render.
56
+ NativeUI.animValueBind(this._handle, node, prop);
57
+ }
58
+
59
+ interpolate(config) {
60
+ return new AnimatedInterpolation(this, config);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * A value derived from a parent Animated.Value through a piecewise-linear interpolation.
66
+ * config = { inputRange, outputRange, extrapolate?, extrapolateLeft?, extrapolateRight? }; the
67
+ * extrapolate tokens ('extend' | 'clamp' | 'identity') control behavior outside the input range.
68
+ */
69
+ export class AnimatedInterpolation {
70
+ constructor(parent, config) {
71
+ this.__animated = true;
72
+ this._parent = parent;
73
+ this._config = config;
74
+ }
75
+
76
+ __bind(node, prop) {
77
+ NativeUI.animValueBindInterpolated(this._parent._handle, node, prop, this._config);
78
+ }
79
+
80
+ interpolate(config) {
81
+ // Chained interpolation isn't supported by the engine; re-map against the same parent.
82
+ return new AnimatedInterpolation(this._parent, config);
83
+ }
84
+ }
85
+
86
+ /** Wraps fn so it runs at most once, regardless of how many times it is invoked. */
87
+ function once(fn) {
88
+ let called = false;
89
+ return (arg) => {
90
+ if (called) return;
91
+ called = true;
92
+ if (fn) fn(arg);
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Wraps a NativeUI animation call in an object with start()/stop() (RN's animation handle shape).
98
+ *
99
+ * start(onComplete) wires the engine's completion callback through the bridge: onComplete is called
100
+ * with `{ finished }` when the animation ends naturally (finished: true) or is interrupted by a new
101
+ * animation / stop() (finished: false). `_value` is exposed so Animated.loop can reset it between
102
+ * iterations.
103
+ */
104
+ function makeAnimation(value, toValue, config) {
105
+ let handle = 0;
106
+ return {
107
+ _value: value,
108
+ start(onComplete) {
109
+ // Always pass a wrapper so we can null out the (possibly recycled) handle on completion.
110
+ const cb = (finished) => {
111
+ handle = 0;
112
+ if (onComplete) onComplete({ finished: !!finished });
113
+ };
114
+ handle = NativeUI.animValueAnimate(value._handle, toValue, config, cb);
115
+ },
116
+ stop() {
117
+ if (handle) NativeUI.animStop(handle);
118
+ },
119
+ };
120
+ }
121
+
122
+ export function timing(value, config = {}) {
123
+ const { toValue = 0, useNativeDriver, ...rest } = config;
124
+ return makeAnimation(value, toValue, { type: 'timing', ...rest });
125
+ }
126
+
127
+ export function spring(value, config = {}) {
128
+ const { toValue = 0, useNativeDriver, ...rest } = config;
129
+ return makeAnimation(value, toValue, { type: 'spring', ...rest });
130
+ }
131
+
132
+ export function decay(value, config = {}) {
133
+ const { useNativeDriver, ...rest } = config;
134
+ // Decay coasts from its velocity; the engine ignores the target, so pass the current value.
135
+ return makeAnimation(value, value.__getValue(), { type: 'decay', ...rest });
136
+ }
137
+
138
+ // --- Composition -------------------------------------------------------------------------------
139
+ // These are pure JS over each child animation's start()/stop() + the §2 timer globals. The engine
140
+ // deactivates a finished animation before firing its completion callback, so chaining the next
141
+ // animation from inside a completion handler is safe (no per-frame JS — each child still runs in C).
142
+
143
+ /** Runs animations one after another; each starts when the previous finishes. */
144
+ export function sequence(animations) {
145
+ let current = 0;
146
+ let stopped = false;
147
+ return {
148
+ start(onComplete) {
149
+ // Reset the per-run state so the sequence is restartable — e.g., each iteration of Animated.loop calls
150
+ // start() again. Without this, `current` stays at animations.length from the previous run, so the next
151
+ // start() completes instantly and the loop re-enters synchronously → stack overflow.
152
+ current = 0;
153
+ stopped = false;
154
+ const done = once(onComplete);
155
+ const next = (result) => {
156
+ if (stopped || !result || result.finished === false) {
157
+ done({ finished: false });
158
+ return;
159
+ }
160
+ if (current >= animations.length) {
161
+ done({ finished: true });
162
+ return;
163
+ }
164
+ animations[current++].start(next);
165
+ };
166
+ next({ finished: true }); // kick off the first entry
167
+ },
168
+ stop() {
169
+ stopped = true;
170
+ if (current > 0 && current <= animations.length) animations[current - 1].stop();
171
+ },
172
+ };
173
+ }
174
+
175
+ /** Runs animations simultaneously; completes when all finish. stopTogether (default true). */
176
+ export function parallel(animations, config) {
177
+ const stopTogether = !config || config.stopTogether !== false;
178
+ let stopped = false;
179
+ return {
180
+ start(onComplete) {
181
+ const done = once(onComplete);
182
+ const total = animations.length;
183
+ if (total === 0) {
184
+ done({ finished: true });
185
+ return;
186
+ }
187
+ let doneCount = 0;
188
+ let anyUnfinished = false;
189
+ const onChild = (result) => {
190
+ doneCount++;
191
+ if (!result || result.finished === false) {
192
+ anyUnfinished = true;
193
+ if (stopTogether && !stopped) {
194
+ stopped = true;
195
+ for (const a of animations) a.stop();
196
+ }
197
+ }
198
+ if (doneCount >= total) done({ finished: !anyUnfinished });
199
+ };
200
+ for (const a of animations) a.start(onChild);
201
+ },
202
+ stop() {
203
+ stopped = true;
204
+ for (const a of animations) a.stop();
205
+ },
206
+ };
207
+ }
208
+
209
+ /** Like parallel, but each animation starts `delay` ms after the previous one (uses setTimeout). */
210
+ export function stagger(delay, animations) {
211
+ let stopped = false;
212
+ const timers = [];
213
+ return {
214
+ start(onComplete) {
215
+ const done = once(onComplete);
216
+ const total = animations.length;
217
+ if (total === 0) {
218
+ done({ finished: true });
219
+ return;
220
+ }
221
+ let doneCount = 0;
222
+ let anyUnfinished = false;
223
+ const onChild = (result) => {
224
+ doneCount++;
225
+ if (!result || result.finished === false) anyUnfinished = true;
226
+ if (doneCount >= total) done({ finished: !anyUnfinished });
227
+ };
228
+ animations.forEach((a, i) => {
229
+ if (i === 0) {
230
+ a.start(onChild);
231
+ } else {
232
+ timers.push(
233
+ setTimeout(() => {
234
+ if (!stopped) a.start(onChild);
235
+ }, delay * i),
236
+ );
237
+ }
238
+ });
239
+ },
240
+ stop() {
241
+ stopped = true;
242
+ for (const t of timers) clearTimeout(t);
243
+ for (const a of animations) a.stop();
244
+ },
245
+ };
246
+ }
247
+
248
+ /** An animation that simply waits `time` ms — useful as a spacer inside sequence (uses setTimeout). */
249
+ export function delay(time) {
250
+ let timer = 0;
251
+ return {
252
+ start(onComplete) {
253
+ const done = once(onComplete);
254
+ timer = setTimeout(() => {
255
+ timer = 0;
256
+ done({ finished: true });
257
+ }, time);
258
+ },
259
+ stop() {
260
+ if (timer) {
261
+ clearTimeout(timer);
262
+ timer = 0;
263
+ }
264
+ },
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Repeats an animation. iterations: -1 (default) loops forever; otherwise that many times.
270
+ * By default the underlying value is reset to its loop-start position before each iteration
271
+ * (resetBeforeIteration), matching RN — so a timing 0→1 repeats 0→1 rather than ping-ponging.
272
+ */
273
+ export function loop(animation, config) {
274
+ const iterations = config && Number.isInteger(config.iterations) ? config.iterations : -1;
275
+ const resetBeforeIteration = !config || config.resetBeforeIteration !== false;
276
+ let stopped = false;
277
+ return {
278
+ _value: animation._value,
279
+ start(onComplete) {
280
+ const done = once(onComplete);
281
+ const startValue = resetBeforeIteration && animation._value ? animation._value.__getValue() : null;
282
+ let count = 0;
283
+ const run = (result) => {
284
+ if (stopped || !result || result.finished === false) {
285
+ done({ finished: false });
286
+ return;
287
+ }
288
+ if (iterations >= 0 && count >= iterations) {
289
+ done({ finished: true });
290
+ return;
291
+ }
292
+ count++;
293
+ if (resetBeforeIteration && animation._value && startValue != null && count > 1) {
294
+ animation._value.setValue(startValue);
295
+ }
296
+ animation.start(run);
297
+ };
298
+ run({ finished: true });
299
+ },
300
+ stop() {
301
+ stopped = true;
302
+ animation.stop();
303
+ },
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Returns a stable Animated.Value that persists across renders (the analog of
309
+ * `useRef(new Animated.Value(initial)).current`), and frees the engine-side value slot when the
310
+ * component unmounts. The unmount cleanup matters here in a way it doesn't in React Native: the
311
+ * engine's value pool is fixed-size, so a value owned by a mounting/unmounting component (e.g., a list
312
+ * row or a tab screen) would otherwise leak a slot on every unmount.
313
+ */
314
+ export function useAnimatedValue(initial = 0) {
315
+ const ref = useRef(null);
316
+ if (ref.current == null) {
317
+ ref.current = new AnimatedValue(initial);
318
+ }
319
+ // Empty deps → the cleanup runs only on unmount. `initial` is read once on the first render (matching
320
+ // useRef semantics); later changes don't recreate the value, as in React Native.
321
+ useEffect(() => {
322
+ const value = ref.current;
323
+ return () => value.destroy();
324
+ }, []);
325
+ return ref.current;
326
+ }
327
+
328
+ /**
329
+ * Wraps a host component so animated values in its `style` are bound to the node (native driver).
330
+ */
331
+ export function createAnimatedComponent(Component) {
332
+ return function AnimatedComponent(props) {
333
+ const { style, ...rest } = props;
334
+ const { staticStyle, bindings } = splitAnimatedStyle(style);
335
+ const ref = (node) => {
336
+ if (node == null) return; // unmount
337
+ for (const b of bindings) b.value.__bind(node, b.prop);
338
+ };
339
+ return createElement(Component, { ...rest, style: staticStyle, ref });
340
+ };
341
+ }
342
+
343
+ export const Animated = {
344
+ Value: AnimatedValue,
345
+ View: createAnimatedComponent('View'),
346
+ Text: createAnimatedComponent('Text'),
347
+ Image: createAnimatedComponent('Image'),
348
+ timing,
349
+ spring,
350
+ decay,
351
+ sequence,
352
+ parallel,
353
+ stagger,
354
+ delay,
355
+ loop,
356
+ createAnimatedComponent,
357
+ };