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.
- package/aot/compile.mjs +2407 -697
- package/aot/screenshot-smoke.mjs +34 -17
- package/aot/style-map.mjs +156 -80
- package/assets/bake-font.mjs +45 -21
- package/assets/bake-image.mjs +7 -5
- package/assets/bake-svg.mjs +563 -0
- package/assets/build-builtin-font.mjs +25 -12
- package/assets/emit-c.mjs +52 -20
- package/assets/emit-container.mjs +5 -3
- package/assets/emit-pack.mjs +8 -2
- package/assets/index.mjs +25 -16
- package/assets/rasterize.mjs +45 -11
- package/assets/svg-loader.mjs +81 -0
- package/build.mjs +43 -20
- package/cli.mjs +258 -20
- package/pack-container.mjs +84 -35
- package/package.json +9 -3
- package/persist-transform.mjs +23 -9
- package/qjsc-wasm.mjs +83 -0
- package/sim/embedded-react.cjs +2 -0
- package/sim/embedded-react.js +1 -1
- package/sim/embedded-react.wasm +0 -0
- package/sim-server.mjs +160 -48
- package/src/embedded-react/Animated.js +51 -36
- package/src/embedded-react/AppRegistry.js +4 -4
- package/src/embedded-react/Easing.js +1 -1
- package/src/embedded-react/LayoutAnimation.js +13 -6
- package/src/embedded-react/StyleSheet.js +1 -1
- package/src/embedded-react/imperative.js +19 -7
- package/src/embedded-react/index.js +8 -8
- package/src/embedded-react/layout-anim-config.js +13 -9
- package/src/embedded-react/split-style.js +6 -6
- package/src/embedded-react/svg-ops.js +369 -41
- package/src/embedded-react/usePersistentState.js +3 -3
- package/src/host-config.js +137 -18
- package/src/native-ui.js +3 -1
- package/src/props.js +22 -10
- package/src/renderer.js +3 -3
|
@@ -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 {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
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(
|
|
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
|
|
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 =
|
|
115
|
+
const cb = finished => {
|
|
111
116
|
handle = 0;
|
|
112
|
-
if (onComplete) onComplete({
|
|
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 {
|
|
124
|
-
return makeAnimation(value, toValue, {
|
|
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 {
|
|
129
|
-
return makeAnimation(value, toValue, {
|
|
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 {
|
|
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(), {
|
|
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 =
|
|
160
|
+
const next = result => {
|
|
156
161
|
if (stopped || !result || result.finished === false) {
|
|
157
|
-
done({
|
|
162
|
+
done({finished: false});
|
|
158
163
|
return;
|
|
159
164
|
}
|
|
160
165
|
if (current >= animations.length) {
|
|
161
|
-
done({
|
|
166
|
+
done({finished: true});
|
|
162
167
|
return;
|
|
163
168
|
}
|
|
164
169
|
animations[current++].start(next);
|
|
165
170
|
};
|
|
166
|
-
next({
|
|
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)
|
|
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({
|
|
190
|
+
done({finished: true});
|
|
185
191
|
return;
|
|
186
192
|
}
|
|
187
193
|
let doneCount = 0;
|
|
188
194
|
let anyUnfinished = false;
|
|
189
|
-
const onChild =
|
|
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({
|
|
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({
|
|
224
|
+
done({finished: true});
|
|
219
225
|
return;
|
|
220
226
|
}
|
|
221
227
|
let doneCount = 0;
|
|
222
228
|
let anyUnfinished = false;
|
|
223
|
-
const onChild =
|
|
229
|
+
const onChild = result => {
|
|
224
230
|
doneCount++;
|
|
225
231
|
if (!result || result.finished === false) anyUnfinished = true;
|
|
226
|
-
if (doneCount >= total) done({
|
|
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({
|
|
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 =
|
|
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 =
|
|
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({
|
|
295
|
+
done({finished: false});
|
|
286
296
|
return;
|
|
287
297
|
}
|
|
288
298
|
if (iterations >= 0 && count >= iterations) {
|
|
289
|
-
done({
|
|
299
|
+
done({finished: true});
|
|
290
300
|
return;
|
|
291
301
|
}
|
|
292
302
|
count++;
|
|
293
|
-
if (
|
|
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 =
|
|
313
|
+
const onIterationDone = result => {
|
|
299
314
|
if (stopped || !result || result.finished === false) {
|
|
300
|
-
done({
|
|
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 {
|
|
347
|
-
const {
|
|
348
|
-
const ref =
|
|
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, {
|
|
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 {
|
|
25
|
-
import {
|
|
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 = {
|
|
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({
|
|
46
|
+
const root = createRoot({width: screen.width, height: screen.height});
|
|
47
47
|
root.render(createElement(registered.Component));
|
|
48
48
|
},
|
|
49
49
|
};
|
|
@@ -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 {
|
|
22
|
-
import {
|
|
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 =
|
|
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:
|
|
43
|
-
linear:
|
|
44
|
-
spring:
|
|
49
|
+
easeInEaseOut: onDone => configureNext(Presets.easeInEaseOut, onDone),
|
|
50
|
+
linear: onDone => configureNext(Presets.linear, onDone),
|
|
51
|
+
spring: onDone => configureNext(Presets.spring, onDone),
|
|
45
52
|
};
|
|
@@ -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 {
|
|
24
|
-
import {
|
|
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 {
|
|
37
|
-
|
|
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, [{
|
|
60
|
+
NativeUI.setTextSpans(handle, [{text: String(text)}]);
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
/**
|
|
52
|
-
*
|
|
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
|
|
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 {
|
|
46
|
-
export {
|
|
47
|
-
export {
|
|
48
|
-
export {
|
|
49
|
-
export {
|
|
50
|
-
export {
|
|
51
|
-
export {
|
|
52
|
-
export {
|
|
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 {
|
|
66
|
+
return {type: 'spring', duration};
|
|
67
67
|
}
|
|
68
|
-
return {
|
|
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(
|
|
72
|
+
export function create(
|
|
73
|
+
duration,
|
|
74
|
+
type = Types.easeInEaseOut,
|
|
75
|
+
creationProp = Properties.opacity,
|
|
76
|
+
) {
|
|
73
77
|
return {
|
|
74
78
|
duration,
|
|
75
|
-
create: {
|
|
76
|
-
update: {
|
|
77
|
-
delete: {
|
|
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: {
|
|
88
|
-
update: {
|
|
89
|
-
delete: {
|
|
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 {
|
|
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({
|
|
41
|
-
bindings.push({
|
|
40
|
+
bindings.push({prop: 'scaleX', value: v});
|
|
41
|
+
bindings.push({prop: 'scaleY', value: v});
|
|
42
42
|
} else {
|
|
43
|
-
bindings.push({
|
|
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({
|
|
51
|
+
bindings.push({prop: key, value: val});
|
|
52
52
|
} else {
|
|
53
53
|
staticStyle[key] = val;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
return {
|
|
57
|
+
return {staticStyle, bindings};
|
|
58
58
|
}
|