embedded-react 0.2.3 → 0.4.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.
@@ -17,9 +17,9 @@
17
17
  // Animated — the React Native analog, backed by the engine's native-driver value system
18
18
  // (er_anim_value_*). An Animated.Value is a handle to an engine-side float; binding it to a node
19
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';
20
+ import {createElement, useRef, useEffect} from 'react';
21
+ import {NativeUI} from '../native-ui.js';
22
+ import {splitAnimatedStyle} from './split-style.js';
23
23
 
24
24
  /** A standalone animatable value bound to an engine-side float. */
25
25
  export class AnimatedValue {
@@ -74,7 +74,12 @@ export class AnimatedInterpolation {
74
74
  }
75
75
 
76
76
  __bind(node, prop) {
77
- NativeUI.animValueBindInterpolated(this._parent._handle, node, prop, this._config);
77
+ NativeUI.animValueBindInterpolated(
78
+ this._parent._handle,
79
+ node,
80
+ prop,
81
+ this._config,
82
+ );
78
83
  }
79
84
 
80
85
  interpolate(config) {
@@ -86,7 +91,7 @@ export class AnimatedInterpolation {
86
91
  /** Wraps fn so it runs at most once, regardless of how many times it is invoked. */
87
92
  function once(fn) {
88
93
  let called = false;
89
- return (arg) => {
94
+ return arg => {
90
95
  if (called) return;
91
96
  called = true;
92
97
  if (fn) fn(arg);
@@ -107,9 +112,9 @@ function makeAnimation(value, toValue, config) {
107
112
  _value: value,
108
113
  start(onComplete) {
109
114
  // Always pass a wrapper so we can null out the (possibly recycled) handle on completion.
110
- const cb = (finished) => {
115
+ const cb = finished => {
111
116
  handle = 0;
112
- if (onComplete) onComplete({ finished: !!finished });
117
+ if (onComplete) onComplete({finished: !!finished});
113
118
  };
114
119
  handle = NativeUI.animValueAnimate(value._handle, toValue, config, cb);
115
120
  },
@@ -120,19 +125,19 @@ function makeAnimation(value, toValue, config) {
120
125
  }
121
126
 
122
127
  export function timing(value, config = {}) {
123
- const { toValue = 0, useNativeDriver, ...rest } = config;
124
- return makeAnimation(value, toValue, { type: 'timing', ...rest });
128
+ const {toValue = 0, useNativeDriver, ...rest} = config;
129
+ return makeAnimation(value, toValue, {type: 'timing', ...rest});
125
130
  }
126
131
 
127
132
  export function spring(value, config = {}) {
128
- const { toValue = 0, useNativeDriver, ...rest } = config;
129
- return makeAnimation(value, toValue, { type: 'spring', ...rest });
133
+ const {toValue = 0, useNativeDriver, ...rest} = config;
134
+ return makeAnimation(value, toValue, {type: 'spring', ...rest});
130
135
  }
131
136
 
132
137
  export function decay(value, config = {}) {
133
- const { useNativeDriver, ...rest } = config;
138
+ const {useNativeDriver, ...rest} = config;
134
139
  // Decay coasts from its velocity; the engine ignores the target, so pass the current value.
135
- return makeAnimation(value, value.__getValue(), { type: 'decay', ...rest });
140
+ return makeAnimation(value, value.__getValue(), {type: 'decay', ...rest});
136
141
  }
137
142
 
138
143
  // --- Composition -------------------------------------------------------------------------------
@@ -152,22 +157,23 @@ export function sequence(animations) {
152
157
  current = 0;
153
158
  stopped = false;
154
159
  const done = once(onComplete);
155
- const next = (result) => {
160
+ const next = result => {
156
161
  if (stopped || !result || result.finished === false) {
157
- done({ finished: false });
162
+ done({finished: false});
158
163
  return;
159
164
  }
160
165
  if (current >= animations.length) {
161
- done({ finished: true });
166
+ done({finished: true});
162
167
  return;
163
168
  }
164
169
  animations[current++].start(next);
165
170
  };
166
- next({ finished: true }); // kick off the first entry
171
+ next({finished: true}); // kick off the first entry
167
172
  },
168
173
  stop() {
169
174
  stopped = true;
170
- if (current > 0 && current <= animations.length) animations[current - 1].stop();
175
+ if (current > 0 && current <= animations.length)
176
+ animations[current - 1].stop();
171
177
  },
172
178
  };
173
179
  }
@@ -181,12 +187,12 @@ export function parallel(animations, config) {
181
187
  const done = once(onComplete);
182
188
  const total = animations.length;
183
189
  if (total === 0) {
184
- done({ finished: true });
190
+ done({finished: true});
185
191
  return;
186
192
  }
187
193
  let doneCount = 0;
188
194
  let anyUnfinished = false;
189
- const onChild = (result) => {
195
+ const onChild = result => {
190
196
  doneCount++;
191
197
  if (!result || result.finished === false) {
192
198
  anyUnfinished = true;
@@ -195,7 +201,7 @@ export function parallel(animations, config) {
195
201
  for (const a of animations) a.stop();
196
202
  }
197
203
  }
198
- if (doneCount >= total) done({ finished: !anyUnfinished });
204
+ if (doneCount >= total) done({finished: !anyUnfinished});
199
205
  };
200
206
  for (const a of animations) a.start(onChild);
201
207
  },
@@ -215,15 +221,15 @@ export function stagger(delay, animations) {
215
221
  const done = once(onComplete);
216
222
  const total = animations.length;
217
223
  if (total === 0) {
218
- done({ finished: true });
224
+ done({finished: true});
219
225
  return;
220
226
  }
221
227
  let doneCount = 0;
222
228
  let anyUnfinished = false;
223
- const onChild = (result) => {
229
+ const onChild = result => {
224
230
  doneCount++;
225
231
  if (!result || result.finished === false) anyUnfinished = true;
226
- if (doneCount >= total) done({ finished: !anyUnfinished });
232
+ if (doneCount >= total) done({finished: !anyUnfinished});
227
233
  };
228
234
  animations.forEach((a, i) => {
229
235
  if (i === 0) {
@@ -253,7 +259,7 @@ export function delay(time) {
253
259
  const done = once(onComplete);
254
260
  timer = setTimeout(() => {
255
261
  timer = 0;
256
- done({ finished: true });
262
+ done({finished: true});
257
263
  }, time);
258
264
  },
259
265
  stop() {
@@ -271,33 +277,42 @@ export function delay(time) {
271
277
  * (resetBeforeIteration), matching RN — so a timing 0→1 repeats 0→1 rather than ping-ponging.
272
278
  */
273
279
  export function loop(animation, config) {
274
- const iterations = config && Number.isInteger(config.iterations) ? config.iterations : -1;
280
+ const iterations =
281
+ config && Number.isInteger(config.iterations) ? config.iterations : -1;
275
282
  const resetBeforeIteration = !config || config.resetBeforeIteration !== false;
276
283
  let stopped = false;
277
284
  return {
278
285
  _value: animation._value,
279
286
  start(onComplete) {
280
287
  const done = once(onComplete);
281
- const startValue = resetBeforeIteration && animation._value ? animation._value.__getValue() : null;
288
+ const startValue =
289
+ resetBeforeIteration && animation._value
290
+ ? animation._value.__getValue()
291
+ : null;
282
292
  let count = 0;
283
293
  const startIteration = () => {
284
294
  if (stopped) {
285
- done({ finished: false });
295
+ done({finished: false});
286
296
  return;
287
297
  }
288
298
  if (iterations >= 0 && count >= iterations) {
289
- done({ finished: true });
299
+ done({finished: true});
290
300
  return;
291
301
  }
292
302
  count++;
293
- if (resetBeforeIteration && animation._value && startValue != null && count > 1) {
303
+ if (
304
+ resetBeforeIteration &&
305
+ animation._value &&
306
+ startValue != null &&
307
+ count > 1
308
+ ) {
294
309
  animation._value.setValue(startValue);
295
310
  }
296
311
  animation.start(onIterationDone);
297
312
  };
298
- const onIterationDone = (result) => {
313
+ const onIterationDone = result => {
299
314
  if (stopped || !result || result.finished === false) {
300
- done({ finished: false });
315
+ done({finished: false});
301
316
  return;
302
317
  }
303
318
  // Defer the next iteration to a fresh task instead of starting it inline. A child animation can
@@ -343,13 +358,13 @@ export function useAnimatedValue(initial = 0) {
343
358
  */
344
359
  export function createAnimatedComponent(Component) {
345
360
  return function AnimatedComponent(props) {
346
- const { style, ...rest } = props;
347
- const { staticStyle, bindings } = splitAnimatedStyle(style);
348
- const ref = (node) => {
361
+ const {style, ...rest} = props;
362
+ const {staticStyle, bindings} = splitAnimatedStyle(style);
363
+ const ref = node => {
349
364
  if (node == null) return; // unmount
350
365
  for (const b of bindings) b.value.__bind(node, b.prop);
351
366
  };
352
- return createElement(Component, { ...rest, style: staticStyle, ref });
367
+ return createElement(Component, {...rest, style: staticStyle, ref});
353
368
  };
354
369
  }
355
370
 
@@ -21,8 +21,8 @@
21
21
  // "running the bundle IS starting the app", so registerComponent mounts immediately into a root
22
22
  // sized from the host-injected `screen` global. (A host-driven runApplication can be split out
23
23
  // later when the C host owns app lifecycle.)
24
- import { createElement } from 'react';
25
- import { createRoot } from '../renderer.js';
24
+ import {createElement} from 'react';
25
+ import {createRoot} from '../renderer.js';
26
26
 
27
27
  let registered = null;
28
28
 
@@ -32,7 +32,7 @@ export const AppRegistry = {
32
32
  * the component (RN signature), so the component module isn't evaluated until registration.
33
33
  */
34
34
  registerComponent(appKey, componentProvider) {
35
- registered = { appKey, Component: componentProvider() };
35
+ registered = {appKey, Component: componentProvider()};
36
36
  AppRegistry.runApplication(appKey);
37
37
  return appKey;
38
38
  },
@@ -43,7 +43,7 @@ export const AppRegistry = {
43
43
  runApplication(appKey) {
44
44
  if (!registered) return;
45
45
  if (appKey && appKey !== registered.appKey) return;
46
- const root = createRoot({ width: screen.width, height: screen.height });
46
+ const root = createRoot({width: screen.width, height: screen.height});
47
47
  root.render(createElement(registered.Component));
48
48
  },
49
49
  };
@@ -34,6 +34,6 @@ export const Easing = {
34
34
 
35
35
  /** Custom cubic-bezier curve via control points. */
36
36
  bezier(x1, y1, x2, y2) {
37
- return { x1, y1, x2, y2 };
37
+ return {x1, y1, x2, y2};
38
38
  },
39
39
  };
@@ -18,8 +18,14 @@
18
18
  // changes layout makes the engine tween every node whose computed rect moved (instead of snapping)
19
19
  // on the next commit. Backed by the engine's er_layout_anim_configure_next; the tween advances in C
20
20
  // each frame (er_layout_anim_tick), so there's no per-frame JS.
21
- import { NativeUI } from '../native-ui.js';
22
- import { Types, Properties, Presets, toEngineConfig, create } from './layout-anim-config.js';
21
+ import {NativeUI} from '../native-ui.js';
22
+ import {
23
+ Types,
24
+ Properties,
25
+ Presets,
26
+ toEngineConfig,
27
+ create,
28
+ } from './layout-anim-config.js';
23
29
 
24
30
  /**
25
31
  * Arms a layout animation for the next commit. Call right before the setState that changes layout.
@@ -28,7 +34,8 @@ import { Types, Properties, Presets, toEngineConfig, create } from './layout-ani
28
34
  export function configureNext(config, onAnimationDidEnd) {
29
35
  NativeUI.configureNextLayoutAnimation(toEngineConfig(config));
30
36
  if (typeof onAnimationDidEnd === 'function') {
31
- const ms = config && typeof config.duration === 'number' ? config.duration : 300;
37
+ const ms =
38
+ config && typeof config.duration === 'number' ? config.duration : 300;
32
39
  setTimeout(onAnimationDidEnd, ms);
33
40
  }
34
41
  }
@@ -39,7 +46,7 @@ export const LayoutAnimation = {
39
46
  Types,
40
47
  Properties,
41
48
  Presets,
42
- easeInEaseOut: (onDone) => configureNext(Presets.easeInEaseOut, onDone),
43
- linear: (onDone) => configureNext(Presets.linear, onDone),
44
- spring: (onDone) => configureNext(Presets.spring, onDone),
49
+ easeInEaseOut: onDone => configureNext(Presets.easeInEaseOut, onDone),
50
+ linear: onDone => configureNext(Presets.linear, onDone),
51
+ spring: onDone => configureNext(Presets.spring, onDone),
45
52
  };
@@ -32,5 +32,5 @@ export const StyleSheet = {
32
32
  },
33
33
 
34
34
  hairlineWidth: 1,
35
- absoluteFill: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 },
35
+ absoluteFill: {position: 'absolute', top: 0, left: 0, right: 0, bottom: 0},
36
36
  };
@@ -20,8 +20,8 @@
20
20
  // reconcile + flatten dominate. For continuous gestures (e.g., dragging a dial), grab a node handle via
21
21
  // a ref and push updates directly here: it skips React entirely and, for vectors, skips the d-string
22
22
  // parser too. Commit back to React state when the gesture ends so the declarative tree re-syncs.
23
- import { NativeUI } from '../native-ui.js';
24
- import { shapesToVector } from './svg-ops.js';
23
+ import {NativeUI} from '../native-ui.js';
24
+ import {shapesToVector, warnVectorCaps} from './svg-ops.js';
25
25
 
26
26
  /**
27
27
  * Imperatively sets an <Svg> node's geometry from primitive shape descriptors (see shapesToVector).
@@ -33,8 +33,20 @@ import { shapesToVector } from './svg-ops.js';
33
33
  */
34
34
  export function updateVector(handle, shapes, dirtyRect) {
35
35
  if (handle == null) return;
36
- const { ops, paints } = shapesToVector(shapes);
37
- NativeUI.setVectorOps(handle, ops, paints, dirtyRect);
36
+ const {ops, paints} = shapesToVector(shapes);
37
+ warnVectorCaps(
38
+ ops.length,
39
+ paints.length,
40
+ NativeUI.maxVectorOps,
41
+ NativeUI.maxVectorPaints,
42
+ );
43
+ NativeUI.setVectorOps(
44
+ handle,
45
+ ops,
46
+ paints,
47
+ undefined /* gradients: imperative shapes are solid */,
48
+ dirtyRect,
49
+ );
38
50
  }
39
51
 
40
52
  /**
@@ -45,13 +57,13 @@ export function updateVector(handle, shapes, dirtyRect) {
45
57
  */
46
58
  export function updateText(handle, text) {
47
59
  if (handle == null) return;
48
- NativeUI.setTextSpans(handle, [{ text: String(text) }]);
60
+ NativeUI.setTextSpans(handle, [{text: String(text)}]);
49
61
  }
50
62
 
51
63
  /**
52
- * Customises the on-screen software keyboard's appearance and/or layout. Only effective when the engine was
64
+ * Customizes the on-screen software keyboard's appearance and/or layout. Only effective when the engine was
53
65
  * built with the keyboard enabled (ERUI_ONSCREEN_KEYBOARD=1); a no-op otherwise. The config is a plain
54
- * object of colours/sizes plus an optional `layers` array; omit `layers` to keep the built-in QWERTY:
66
+ * object of colors/sizes plus an optional `layers` array; omit `layers` to keep the built-in QWERTY:
55
67
  *
56
68
  * setKeyboardConfig({ keyColor: '#fff', labelColor: '#111', fontSize: 20,
57
69
  * layers: [ [ [ { char: 'a' }, ... ], // a row
@@ -42,11 +42,11 @@ export {
42
42
  G,
43
43
  Arc,
44
44
  } from './components.js';
45
- export { updateVector, updateText, setKeyboardConfig } from './imperative.js';
46
- export { StyleSheet } from './StyleSheet.js';
47
- export { Platform } from './Platform.js';
48
- export { AppRegistry } from './AppRegistry.js';
49
- export { Animated, useAnimatedValue } from './Animated.js';
50
- export { usePersistentState } from './usePersistentState.js';
51
- export { Easing } from './Easing.js';
52
- export { LayoutAnimation } from './LayoutAnimation.js';
45
+ export {updateVector, updateText, setKeyboardConfig} from './imperative.js';
46
+ export {StyleSheet} from './StyleSheet.js';
47
+ export {Platform} from './Platform.js';
48
+ export {AppRegistry} from './AppRegistry.js';
49
+ export {Animated, useAnimatedValue} from './Animated.js';
50
+ export {usePersistentState} from './usePersistentState.js';
51
+ export {Easing} from './Easing.js';
52
+ export {LayoutAnimation} from './LayoutAnimation.js';
@@ -63,18 +63,22 @@ export function toEngineConfig(config) {
63
63
  const update = c.update || {};
64
64
  const type = update.type || c.type || Types.easeInEaseOut;
65
65
  if (type === Types.spring) {
66
- return { type: 'spring', duration };
66
+ return {type: 'spring', duration};
67
67
  }
68
- return { type: 'timing', duration, easing: easingForType(type) };
68
+ return {type: 'timing', duration, easing: easingForType(type)};
69
69
  }
70
70
 
71
71
  /** Builds an RN-shaped config (duration + create/update/delete) from a type + creation property. */
72
- export function create(duration, type = Types.easeInEaseOut, creationProp = Properties.opacity) {
72
+ export function create(
73
+ duration,
74
+ type = Types.easeInEaseOut,
75
+ creationProp = Properties.opacity,
76
+ ) {
73
77
  return {
74
78
  duration,
75
- create: { type, property: creationProp },
76
- update: { type },
77
- delete: { type, property: creationProp },
79
+ create: {type, property: creationProp},
80
+ update: {type},
81
+ delete: {type, property: creationProp},
78
82
  };
79
83
  }
80
84
 
@@ -84,8 +88,8 @@ export const Presets = {
84
88
  linear: create(500, Types.linear, Properties.opacity),
85
89
  spring: {
86
90
  duration: 700,
87
- create: { type: Types.linear, property: Properties.opacity },
88
- update: { type: Types.spring, springDamping: 0.4 },
89
- delete: { type: Types.linear, property: Properties.opacity },
91
+ create: {type: Types.linear, property: Properties.opacity},
92
+ update: {type: Types.spring, springDamping: 0.4},
93
+ delete: {type: Types.linear, property: Properties.opacity},
90
94
  },
91
95
  };
@@ -18,7 +18,7 @@
18
18
  // bindings (Animated.Value / interpolation, bound to the node's prop so the engine drives them).
19
19
  // Pure (no NativeUI), so it's unit-testable. An animated entry is anything with an `__animated`
20
20
  // marker — `transform: [{ translateX: value }]` entries are handled too (scale binds both axes).
21
- import { flattenStyle } from '../props.js';
21
+ import {flattenStyle} from '../props.js';
22
22
 
23
23
  export function splitAnimatedStyle(style) {
24
24
  const staticStyle = {};
@@ -37,10 +37,10 @@ export function splitAnimatedStyle(style) {
37
37
  const v = entry[axis];
38
38
  if (v && v.__animated) {
39
39
  if (axis === 'scale') {
40
- bindings.push({ prop: 'scaleX', value: v });
41
- bindings.push({ prop: 'scaleY', value: v });
40
+ bindings.push({prop: 'scaleX', value: v});
41
+ bindings.push({prop: 'scaleY', value: v});
42
42
  } else {
43
- bindings.push({ prop: axis, value: v });
43
+ bindings.push({prop: axis, value: v});
44
44
  }
45
45
  } else {
46
46
  staticTransform.push(entry);
@@ -48,11 +48,11 @@ export function splitAnimatedStyle(style) {
48
48
  }
49
49
  if (staticTransform.length > 0) staticStyle.transform = staticTransform;
50
50
  } else if (val && val.__animated) {
51
- bindings.push({ prop: key, value: val });
51
+ bindings.push({prop: key, value: val});
52
52
  } else {
53
53
  staticStyle[key] = val;
54
54
  }
55
55
  }
56
56
 
57
- return { staticStyle, bindings };
57
+ return {staticStyle, bindings};
58
58
  }