@symbiote-native/engine 0.2.0 → 0.3.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/README.md +17 -10
- package/build/action-sheet-ios/index.js +4 -1
- package/build/animated/animations/composition.js +3 -1
- package/build/animated/animations/spring-config.js +4 -1
- package/build/animated/animations/spring.js +23 -9
- package/build/animated/animations/timing.js +3 -2
- package/build/animated/bezier.js +2 -1
- package/build/animated/color.js +4 -2
- package/build/animated/event.js +7 -3
- package/build/animated/graph.js +8 -3
- package/build/animated/index.d.ts +2 -2
- package/build/animated/index.js +2 -2
- package/build/animated/interpolation.js +7 -2
- package/build/animated/mock.js +1 -1
- package/build/animated/native/native-animated.js +1 -1
- package/build/animated/operators.js +27 -6
- package/build/animated/props.js +1 -1
- package/build/animated/rgba.js +18 -3
- package/build/animated/style.js +13 -3
- package/build/animated/value-xy.d.ts +8 -5
- package/build/animated/value-xy.js +4 -1
- package/build/appearance/index.js +4 -1
- package/build/commit.d.ts +13 -0
- package/build/commit.js +145 -9
- package/build/events/index.js +19 -8
- package/build/fabric-props.js +8 -2
- package/build/image-loader.js +3 -2
- package/build/index.d.ts +19 -17
- package/build/index.js +17 -10
- package/build/interaction-manager/index.js +2 -1
- package/build/layout-animation/index.js +12 -4
- package/build/native-events.js +2 -1
- package/build/native-modules/index.js +3 -1
- package/build/node.d.ts +25 -0
- package/build/node.js +168 -2
- package/build/pan-responder/index.js +20 -9
- package/build/permissions-android/index.js +3 -1
- package/build/platform/index.android.d.ts +1 -1
- package/build/platform/index.android.js +1 -1
- package/build/platform/index.ios.d.ts +1 -1
- package/build/platform-color/index.js +3 -1
- package/build/post-commit.d.ts +1 -0
- package/build/post-commit.js +6 -0
- package/build/process-background-image/index.js +30 -9
- package/build/process-filter.js +11 -4
- package/build/registry.js +10 -2
- package/build/report-error.js +4 -1
- package/build/share/index.android.js +1 -1
- package/build/share/index.ios.js +1 -1
- package/build/status-bar/index.android.js +3 -4
- package/build/status-bar/index.ios.js +1 -1
- package/build/style-registry/index.d.ts +9 -2
- package/build/style-registry/index.js +145 -170
- package/build/style-registry/scope.d.ts +15 -1
- package/build/style-registry/scope.js +29 -27
- package/build/styles.d.ts +11 -1
- package/build/surface.d.ts +1 -1
- package/build/surface.js +5 -0
- package/build/tags.d.ts +1 -0
- package/build/tags.js +31 -1
- package/build/touch-history.js +9 -2
- package/build/vibration/index.ios.js +1 -1
- package/build/view-config.js +14 -2
- package/package.json +2 -2
package/build/commit.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// references to specific child handles. That bubble is inherent to a persistent
|
|
14
14
|
// tree and is exactly what React's own Fabric renderer does.
|
|
15
15
|
import { getSlot, } from './fabric.js';
|
|
16
|
-
import { createElement, debugNodeId, isAnchor, VIRTUAL_TEXT_COMPONENT, } from './node.js';
|
|
16
|
+
import { createElement, debugNodeId, isAnchor, isEmptyRawText, markDirty, takePropStats, VIRTUAL_TEXT_COMPONENT, } from './node.js';
|
|
17
17
|
import { dlog, isDebug } from './debug.js';
|
|
18
18
|
import { flattenStyle } from './style/index.js';
|
|
19
19
|
import { nextTag } from './tags.js';
|
|
@@ -25,6 +25,40 @@ export { processColor, setColorProcessor } from './platform-color/index.js';
|
|
|
25
25
|
// Per-commit work counters, surfaced via dlog so a device run can prove the
|
|
26
26
|
// engine is incremental (created=0 with clones after the first mount).
|
|
27
27
|
const stats = { created: 0, cloneProps: 0, cloneChildren: 0, reused: 0 };
|
|
28
|
+
// Cumulative cost of the reconcile walk. Unlike `stats` (per-commit, zeroed at the top of every
|
|
29
|
+
// commit), this ACCUMULATES: one scroll frame produces a burst of commits and the frame's real cost
|
|
30
|
+
// is only visible as their sum. Read-and-zeroed via readCommitProfile().
|
|
31
|
+
//
|
|
32
|
+
// Deliberately NOT gated behind isDebug(): two performance.now() calls per commit are noise next to
|
|
33
|
+
// the walk they measure, and the number is only meaningful from a RELEASE build - dev-mode JS
|
|
34
|
+
// drowns the signal. The dlog below stays gated as usual.
|
|
35
|
+
const profile = { commits: 0, walkMs: 0, nodesVisited: 0 };
|
|
36
|
+
// What `renderableChildren` costs, accumulated over the same window as `profile`. Anchors are the
|
|
37
|
+
// only reason that function is not free, and how many a tree carries is a property of the ADAPTER,
|
|
38
|
+
// not of the app: a Vue/React/Svelte/Solid component is a function returning children and allocates
|
|
39
|
+
// no node, while an Angular component is bound to a host ELEMENT and therefore always has one
|
|
40
|
+
// (anchor-host-registry.ts keeps it from painting). So the same screen is a no-anchor tree under
|
|
41
|
+
// four adapters and an anchor-per-composed-component tree under the fifth, and only a counter tells
|
|
42
|
+
// them apart at runtime - a source-level grep for "anchor" measures instrumentation density, not
|
|
43
|
+
// trees.
|
|
44
|
+
//
|
|
45
|
+
// `scans` counts invocations; `probed` counts children actually examined by the fast-path probe,
|
|
46
|
+
// which is why that probe is a counted loop rather than `.some()` (a `.some` short-circuits at the
|
|
47
|
+
// first anchor, so children.length would overstate a defeated scan by however much it skipped).
|
|
48
|
+
// `flattens` is the probe defeated: a fresh array, a second pass, and a recursion into every
|
|
49
|
+
// anchor. `widest` is the widest single flatten in the window, because a defeated scan over 3
|
|
50
|
+
// children is noise and one over 1000 is not - a count alone cannot separate them.
|
|
51
|
+
//
|
|
52
|
+
// Not gated behind isDebug(), for the same reason the rest of the profile is not: integer
|
|
53
|
+
// increments are noise next to the array work they price, and the number is only meaningful from a
|
|
54
|
+
// release build.
|
|
55
|
+
const childScan = {
|
|
56
|
+
scans: 0,
|
|
57
|
+
probed: 0,
|
|
58
|
+
flattens: 0,
|
|
59
|
+
flattenProbed: 0,
|
|
60
|
+
widest: 0,
|
|
61
|
+
};
|
|
28
62
|
// Diagnostic (gated): Fabric serializes props to folly::dynamic, which rejects a JS
|
|
29
63
|
// Symbol or function with "JS Symbols are not convertible to dynamic". A hard native
|
|
30
64
|
// throw deep in cloneNode*. Walk a props payload and return the dotted path of the
|
|
@@ -79,7 +113,9 @@ function guardSerializable(propsDiff, viewName, tag) {
|
|
|
79
113
|
function viewNameFor(node, hasTextAncestor) {
|
|
80
114
|
// The only position-dependent name: a <Text> inside another <Text> becomes a
|
|
81
115
|
// virtual span. Everything else is the component string the adapter chose.
|
|
82
|
-
return node.isText && hasTextAncestor
|
|
116
|
+
return node.isText && hasTextAncestor
|
|
117
|
+
? VIRTUAL_TEXT_COMPONENT
|
|
118
|
+
: node.component;
|
|
83
119
|
}
|
|
84
120
|
// Fabric's clone*WithNewProps MERGES the raw payload onto the node's existing props,
|
|
85
121
|
// so the payload must be a MINIMAL diff: only the keys that actually changed, plus any
|
|
@@ -129,18 +165,46 @@ function jsonEqual(a, b) {
|
|
|
129
165
|
return keys.every(key => key in b && jsonEqual(a[key], b[key]));
|
|
130
166
|
}
|
|
131
167
|
const mirror = new WeakMap();
|
|
168
|
+
function isSkippedAtCommit(node) {
|
|
169
|
+
return isAnchor(node) || isEmptyRawText(node);
|
|
170
|
+
}
|
|
132
171
|
function renderableChildren(node) {
|
|
133
172
|
// Anchor nodes (Vue fragment/v-if/v-for placeholders, Angular component hosts that should
|
|
134
173
|
// not paint) live in the retained tree for sibling ordering but never become Fabric views.
|
|
135
174
|
// When an anchor owns children, flatten them into the parent's renderable list: this lets a
|
|
136
175
|
// DOM-less framework use an anchor as a fragment/component host without adding a native
|
|
137
176
|
// wrapper node. Fast path: no anchors reuses the array, so the common case allocates nothing.
|
|
138
|
-
|
|
177
|
+
// An empty raw text is skipped for a different reason and with no flattening: it has no
|
|
178
|
+
// children, and committing it aborts the app inside Fabric's text walk (isEmptyRawText).
|
|
179
|
+
//
|
|
180
|
+
// The probe is a counted loop rather than `.some()` only so childScan can price it honestly;
|
|
181
|
+
// see the childScan declaration for what the five counters mean and what they were built to
|
|
182
|
+
// settle.
|
|
183
|
+
childScan.scans += 1;
|
|
184
|
+
const total = node.children.length;
|
|
185
|
+
let index = 0;
|
|
186
|
+
while (index < total && !isSkippedAtCommit(node.children[index]))
|
|
187
|
+
index += 1;
|
|
188
|
+
// The defeating child was examined too, so it counts.
|
|
189
|
+
childScan.probed += index === total ? total : index + 1;
|
|
190
|
+
if (index === total)
|
|
139
191
|
return node.children;
|
|
192
|
+
childScan.flattens += 1;
|
|
193
|
+
childScan.flattenProbed += total;
|
|
194
|
+
if (total > childScan.widest)
|
|
195
|
+
childScan.widest = total;
|
|
140
196
|
const children = [];
|
|
141
197
|
for (const child of node.children) {
|
|
142
|
-
if (
|
|
143
|
-
|
|
198
|
+
if (isSkippedAtCommit(child)) {
|
|
199
|
+
// A skipped child is flattened away here and never reaches reconcile, so this is the only
|
|
200
|
+
// place that can clear its dirty flag. Leaving it set would be a silent stale-UI bug:
|
|
201
|
+
// markDirty stops at the first dirty ancestor, so a permanently-dirty skipped node swallows
|
|
202
|
+
// every later mark - from an anchor's subtree, or from the setText that turns an empty raw
|
|
203
|
+
// text back into real content - and the real parent never learns anything changed.
|
|
204
|
+
child.dirty = false;
|
|
205
|
+
if (isAnchor(child))
|
|
206
|
+
children.push(...renderableChildren(child));
|
|
207
|
+
}
|
|
144
208
|
else
|
|
145
209
|
children.push(child);
|
|
146
210
|
}
|
|
@@ -166,11 +230,41 @@ function logScrollChildren(node, viewName, selfTag) {
|
|
|
166
230
|
const flag = kids.length === 1 ? 'OK' : 'MULTI!!';
|
|
167
231
|
dlog(`SCROLL-${flag} ${viewName} tag=${selfTag} children(${kids.length})=[${kids.join(',')}]`);
|
|
168
232
|
}
|
|
233
|
+
// The failure mode dirty-marking introduces: a mutation path that forgets to markDirty leaves a
|
|
234
|
+
// node whose desired props have drifted from what Fabric holds, and the screen silently keeps
|
|
235
|
+
// showing the old value - no error, no crash, nothing to grep for. So under DEBUG pay back the full
|
|
236
|
+
// price just saved and verify the skip was honest, naming the node loudly enough to find the
|
|
237
|
+
// missing mark.
|
|
238
|
+
function warnIfStale(node, committed) {
|
|
239
|
+
if (!isDebug())
|
|
240
|
+
return;
|
|
241
|
+
const fresh = fabricProps(node);
|
|
242
|
+
if (propsEqual(committed.props, fresh))
|
|
243
|
+
return;
|
|
244
|
+
dlog(`DIRTY-MISS ${committed.viewName}#${committed.tag} node=${debugNodeId(node)} skipped as clean ` +
|
|
245
|
+
`but props differ: committed=${JSON.stringify(committed.props)} desired=${JSON.stringify(fresh)}`);
|
|
246
|
+
}
|
|
169
247
|
function reconcile(slot, node, rootTag, hasTextAncestor, renderableParent, forceFreshFamily) {
|
|
248
|
+
profile.nodesVisited += 1;
|
|
170
249
|
const viewName = viewNameFor(node, hasTextAncestor);
|
|
250
|
+
const committed = mirror.get(node);
|
|
251
|
+
// Nothing under here changed: hand back the committed handle without rebuilding this node's
|
|
252
|
+
// Fabric props or descending into it at all. The walk below costs ~13 us/node on device, and an
|
|
253
|
+
// untouched sibling subtree would pay it on every commit. `committed.parent` is re-checked rather
|
|
254
|
+
// than trusted to the flag because a MOVED node can still be legitimately clean (see node.ts's
|
|
255
|
+
// structural ops), and a reparent must fall through to the fresh-family path below.
|
|
256
|
+
if (!forceFreshFamily &&
|
|
257
|
+
!node.dirty &&
|
|
258
|
+
committed !== undefined &&
|
|
259
|
+
committed.parent === renderableParent &&
|
|
260
|
+
committed.viewName === viewName) {
|
|
261
|
+
stats.reused += 1;
|
|
262
|
+
warnIfStale(node, committed);
|
|
263
|
+
return { handle: committed.handle, changed: false };
|
|
264
|
+
}
|
|
265
|
+
node.dirty = false;
|
|
171
266
|
const props = fabricProps(node);
|
|
172
267
|
const childInText = node.isText || hasTextAncestor;
|
|
173
|
-
const committed = mirror.get(node);
|
|
174
268
|
const kids = renderableChildren(node);
|
|
175
269
|
// First mount, or the view kind flipped (RCTText <-> RCTVirtualText when a
|
|
176
270
|
// <Text> moves in or out of another <Text>): a different native component
|
|
@@ -195,7 +289,8 @@ function reconcile(slot, node, rootTag, hasTextAncestor, renderableParent, force
|
|
|
195
289
|
`bg=${JSON.stringify(props.backgroundColor)} color=${JSON.stringify(props.color)} ` +
|
|
196
290
|
`opacity=${JSON.stringify(props.opacity)}`);
|
|
197
291
|
}
|
|
198
|
-
if (viewName === 'AndroidSwipeRefreshLayout' ||
|
|
292
|
+
if (viewName === 'AndroidSwipeRefreshLayout' ||
|
|
293
|
+
viewName === 'RCTScrollView') {
|
|
199
294
|
dlog(`commit root=${rootTag} layoutProbe tag=${tag} view=${viewName} ` +
|
|
200
295
|
`flex=${JSON.stringify(props.flex)} height=${JSON.stringify(props.height)} ` +
|
|
201
296
|
`width=${JSON.stringify(props.width)} minHeight=${JSON.stringify(props.minHeight)} ` +
|
|
@@ -329,6 +424,13 @@ export function commitChildren(rootTag, children) {
|
|
|
329
424
|
function commitContainer(rootTag) {
|
|
330
425
|
const slot = getSlot();
|
|
331
426
|
const container = rootContainerFor(rootTag);
|
|
427
|
+
// The synthetic container is dirtied here, at the one entry point, because markDirty can never
|
|
428
|
+
// bubble up to it: a surface's top-level nodes carry `parent === undefined` (surface.ts sets it
|
|
429
|
+
// deliberately), so a mark stops at the top-level node and the container above it stays clean -
|
|
430
|
+
// it would then early-exit and swallow the whole commit. Marking unconditionally costs one node's
|
|
431
|
+
// props rebuild per commit and closes the hole for both callers, mutation commit and
|
|
432
|
+
// setNativeProps alike.
|
|
433
|
+
markDirty(container);
|
|
332
434
|
stats.created = 0;
|
|
333
435
|
stats.cloneProps = 0;
|
|
334
436
|
stats.cloneChildren = 0;
|
|
@@ -338,7 +440,11 @@ function commitContainer(rootTag) {
|
|
|
338
440
|
// tree walk); if `start` itself never prints, the stall is upstream: React's commit
|
|
339
441
|
// phase or the mutation ops before we are even called.
|
|
340
442
|
dlog(`commit root=${rootTag} start children=${container.children.length}`);
|
|
443
|
+
const walkStart = performance.now();
|
|
341
444
|
const result = reconcile(slot, container, rootTag, false, undefined, false);
|
|
445
|
+
const walkMs = performance.now() - walkStart;
|
|
446
|
+
profile.walkMs += walkMs;
|
|
447
|
+
profile.commits += 1;
|
|
342
448
|
// Boundary seam: prints once reconcile returns. If a commit hangs and this line
|
|
343
449
|
// never appears, the stall is inside reconcile (JS); if it appears but the
|
|
344
450
|
// post-completeRoot line below never does, the stall is inside the native commit.
|
|
@@ -361,9 +467,34 @@ function commitContainer(rootTag) {
|
|
|
361
467
|
const mode = stats.created > 0 && stats.reused === 0 ? 'full' : 'incremental';
|
|
362
468
|
dlog(`commit root=${rootTag} ${mode} ` +
|
|
363
469
|
`created=${stats.created} cloneProps=${stats.cloneProps} ` +
|
|
364
|
-
`cloneChildren=${stats.cloneChildren} reused=${stats.reused}`
|
|
470
|
+
`cloneChildren=${stats.cloneChildren} reused=${stats.reused} ` +
|
|
471
|
+
`walk=${walkMs.toFixed(3)}ms`);
|
|
365
472
|
}
|
|
366
473
|
}
|
|
474
|
+
export function readCommitProfile() {
|
|
475
|
+
const props = takePropStats();
|
|
476
|
+
const snapshot = {
|
|
477
|
+
commits: profile.commits,
|
|
478
|
+
walkMs: profile.walkMs,
|
|
479
|
+
nodesVisited: profile.nodesVisited,
|
|
480
|
+
propWrites: props.writes,
|
|
481
|
+
propNoops: props.noops,
|
|
482
|
+
childScans: childScan.scans,
|
|
483
|
+
childScanProbed: childScan.probed,
|
|
484
|
+
childFlattens: childScan.flattens,
|
|
485
|
+
childFlattenProbed: childScan.flattenProbed,
|
|
486
|
+
childFlattenWidest: childScan.widest,
|
|
487
|
+
};
|
|
488
|
+
profile.commits = 0;
|
|
489
|
+
profile.walkMs = 0;
|
|
490
|
+
profile.nodesVisited = 0;
|
|
491
|
+
childScan.scans = 0;
|
|
492
|
+
childScan.probed = 0;
|
|
493
|
+
childScan.flattens = 0;
|
|
494
|
+
childScan.flattenProbed = 0;
|
|
495
|
+
childScan.widest = 0;
|
|
496
|
+
return snapshot;
|
|
497
|
+
}
|
|
367
498
|
// Targeted per-frame prop write for the JS-driven Animated path. RN flushes an
|
|
368
499
|
// animation frame with an in-place `instance.setNativeProps(...)`; we have no
|
|
369
500
|
// in-place mutation (Fabric is persistent), so a frame is one scoped commit: mutate
|
|
@@ -382,12 +513,17 @@ export function setNativeProps(node, partial) {
|
|
|
382
513
|
// A partial style override MERGES onto the declarative style (RN semantics):
|
|
383
514
|
// setNativeProps({style:{backgroundColor}}) recolors without dropping height
|
|
384
515
|
// or radius. Transient: the next React commit re-applies the full style.
|
|
385
|
-
node.props.style = {
|
|
516
|
+
node.props.style = {
|
|
517
|
+
...flattenStyle(node.props.style),
|
|
518
|
+
...flattenStyle(value),
|
|
519
|
+
};
|
|
386
520
|
}
|
|
387
521
|
else {
|
|
388
522
|
node.props[key] = value;
|
|
389
523
|
}
|
|
390
524
|
}
|
|
525
|
+
// Writes node.props directly rather than through setProp, so it owes its own mark.
|
|
526
|
+
markDirty(node);
|
|
391
527
|
dlog(`setNativeProps root=${record.rootTag} tag=${record.tag} keys=${Object.keys(partial)}`);
|
|
392
528
|
commitContainer(record.rootTag);
|
|
393
529
|
}
|
package/build/events/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { dlog } from '../debug.js';
|
|
9
9
|
import { runWrapped } from '../dispatch.js';
|
|
10
10
|
import { getSlot } from '../fabric.js';
|
|
11
|
-
import { isAnchor, isSymbioteNode } from '../node.js';
|
|
11
|
+
import { isAnchor, isSymbioteNode, } from '../node.js';
|
|
12
12
|
import { registeredNativeEvent } from '../registry.js';
|
|
13
13
|
import { attachTouchHistory, recordTouchTrack, resetTouchHistory, touchHistory, } from '../touch-history.js';
|
|
14
14
|
import { isRecord } from '../type-guards.js';
|
|
@@ -228,12 +228,14 @@ function lowestCommonAncestor(a, b) {
|
|
|
228
228
|
// handler, so asking the responder again would zero its move).
|
|
229
229
|
function findWantsResponder(path, captureName, bubbleName, nativeEvent, skip) {
|
|
230
230
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
231
|
-
if (path[i] !== skip &&
|
|
231
|
+
if (path[i] !== skip &&
|
|
232
|
+
callOwnListener(path[i], captureName, nativeEvent) === true) {
|
|
232
233
|
return path[i];
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
for (const node of path) {
|
|
236
|
-
if (node !== skip &&
|
|
237
|
+
if (node !== skip &&
|
|
238
|
+
callOwnListener(node, bubbleName, nativeEvent) === true)
|
|
237
239
|
return node;
|
|
238
240
|
}
|
|
239
241
|
return undefined;
|
|
@@ -248,7 +250,9 @@ function negotiateResponder(target, phase, nativeEvent) {
|
|
|
248
250
|
// the responder) and skips the deepest node when it IS the responder (Responder-
|
|
249
251
|
// EventPlugin.setResponderAndExtractTransfer). At touch start currentResponder is
|
|
250
252
|
// cleared, so this collapses to the plain target->root start walk.
|
|
251
|
-
const from = currentResponder === undefined
|
|
253
|
+
const from = currentResponder === undefined
|
|
254
|
+
? target
|
|
255
|
+
: lowestCommonAncestor(currentResponder, target);
|
|
252
256
|
if (!from)
|
|
253
257
|
return;
|
|
254
258
|
const path = pathToRoot(from);
|
|
@@ -268,7 +272,9 @@ function negotiateResponder(target, phase, nativeEvent) {
|
|
|
268
272
|
// A missing termination-request listener means implicit consent (RN default true);
|
|
269
273
|
// only an explicit non-true answer keeps the incumbent and rejects the taker.
|
|
270
274
|
const guarded = incumbent.listeners?.has(RESPONDER_TERMINATION_REQUEST) === true;
|
|
271
|
-
const allowed = !guarded ||
|
|
275
|
+
const allowed = !guarded ||
|
|
276
|
+
callOwnListener(incumbent, RESPONDER_TERMINATION_REQUEST, nativeEvent) ===
|
|
277
|
+
true;
|
|
272
278
|
if (allowed) {
|
|
273
279
|
// RN's transfer order (setResponderAndExtractTransfer): grant the TAKER first, then
|
|
274
280
|
// terminate the incumbent. RN dispatches grant ahead of the terminationRequest too,
|
|
@@ -362,7 +368,8 @@ export function installEventHandler() {
|
|
|
362
368
|
// started inside it; lifting ONE finger in a multi-touch gesture must NOT release.
|
|
363
369
|
// onResponderEnd still fires on every finger-up. (ResponderEventPlugin: responderEnd
|
|
364
370
|
// is unconditional, responderRelease is gated on noResponderTouches.)
|
|
365
|
-
const releases = responder !== undefined &&
|
|
371
|
+
const releases = responder !== undefined &&
|
|
372
|
+
!hasRemainingResponderTouch(responder, nativeEvent);
|
|
366
373
|
if (releases)
|
|
367
374
|
currentResponder = undefined;
|
|
368
375
|
// A completed long press eats the tap (RN), but pressOut still fires below.
|
|
@@ -495,7 +502,9 @@ function bubble(target, listenerName, nativeEvent) {
|
|
|
495
502
|
// machinery (already delivered directly, e.g. Angular's EventEmitter.subscribe) also
|
|
496
503
|
// registered it through Renderer2.listen. Bubbling into it would refire that same
|
|
497
504
|
// callback a second time, so anchors are transparent to listener lookup, not just paint.
|
|
498
|
-
const listener = isAnchor(node)
|
|
505
|
+
const listener = isAnchor(node)
|
|
506
|
+
? undefined
|
|
507
|
+
: node.listeners?.get(captureName);
|
|
499
508
|
if (listener) {
|
|
500
509
|
dlog(`event ${listenerName} capture on ${node.component}`);
|
|
501
510
|
listener({
|
|
@@ -513,7 +522,9 @@ function bubble(target, listenerName, nativeEvent) {
|
|
|
513
522
|
// transparent here too, same reason (see the capture-phase comment above).
|
|
514
523
|
let node = target;
|
|
515
524
|
while (node) {
|
|
516
|
-
const listener = isAnchor(node)
|
|
525
|
+
const listener = isAnchor(node)
|
|
526
|
+
? undefined
|
|
527
|
+
: node.listeners?.get(listenerName);
|
|
517
528
|
if (listener) {
|
|
518
529
|
const event = {
|
|
519
530
|
type: listenerName,
|
package/build/fabric-props.js
CHANGED
|
@@ -70,11 +70,17 @@ const COLOR_PROPS = new Set([
|
|
|
70
70
|
const STYLE_PROCESSORS = new Map([
|
|
71
71
|
['boxShadow', value => processBoxShadow(asBoxShadowInput(value))],
|
|
72
72
|
['filter', value => processFilter(asFilterInput(value))],
|
|
73
|
-
[
|
|
73
|
+
[
|
|
74
|
+
'transformOrigin',
|
|
75
|
+
value => processTransformOrigin(asTransformOriginInput(value)),
|
|
76
|
+
],
|
|
74
77
|
['transform', processTransformValue],
|
|
75
78
|
['aspectRatio', value => processAspectRatio(asAspectRatioInput(value))],
|
|
76
79
|
['fontVariant', value => processFontVariant(asFontVariantInput(value))],
|
|
77
|
-
[
|
|
80
|
+
[
|
|
81
|
+
'experimental_backgroundImage',
|
|
82
|
+
value => processBackgroundImage(asBackgroundImageInput(value)),
|
|
83
|
+
],
|
|
78
84
|
]);
|
|
79
85
|
// boxShadow accepts a CSS string or an array of shadow objects; anything else is
|
|
80
86
|
// undefined to processBoxShadow (which returns []). Narrowing avoids an `as` cast.
|
package/build/image-loader.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// alongside Alert/Share, not in a view/render-*.ts file (whose contract is zero state / zero
|
|
15
15
|
// native bridge).
|
|
16
16
|
import { dlog } from './debug.js';
|
|
17
|
-
import { resolveImageSource } from './image-source-resolver.js';
|
|
17
|
+
import { resolveImageSource, } from './image-source-resolver.js';
|
|
18
18
|
import { getNativeModule } from './native-modules/index.js';
|
|
19
19
|
import { Platform } from './platform';
|
|
20
20
|
import { isNumber } from './type-guards.js';
|
|
@@ -26,7 +26,8 @@ const IMAGE_LOADER_MODULE = 'ImageLoader';
|
|
|
26
26
|
let imageLoaderModule;
|
|
27
27
|
function getImageLoader() {
|
|
28
28
|
if (imageLoaderModule === undefined) {
|
|
29
|
-
imageLoaderModule =
|
|
29
|
+
imageLoaderModule =
|
|
30
|
+
getNativeModule(IMAGE_LOADER_MODULE);
|
|
30
31
|
dlog(`Image: ImageLoader module ${imageLoaderModule ? 'resolved' : 'NOT resolved (null)'}`);
|
|
31
32
|
}
|
|
32
33
|
return imageLoaderModule;
|
package/build/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, getExplicitStyle, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node';
|
|
1
|
+
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, censusRetainedTree, getExplicitStyle, setNodeHidden, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node';
|
|
2
2
|
export { isEventFor } from './view-config';
|
|
3
3
|
export { registerComponent, setNativeViewConfigSource } from './registry';
|
|
4
4
|
export { isRecord } from './type-guards';
|
|
5
|
-
export { InteractionManager, Events as InteractionManagerEvents } from './interaction-manager';
|
|
5
|
+
export { InteractionManager, Events as InteractionManagerEvents, } from './interaction-manager';
|
|
6
6
|
export type { IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, ICancellable, } from './interaction-manager';
|
|
7
7
|
export { default as PanResponder } from './pan-responder';
|
|
8
8
|
export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from './pan-responder';
|
|
@@ -10,11 +10,13 @@ export type { IComponentRegistration, INativeEventBinding, INativeViewConfig, IN
|
|
|
10
10
|
export type { ISymbioteNode, ISymbioteEvent, IListener } from './node';
|
|
11
11
|
export { SymbioteSurface, createSurface } from './surface';
|
|
12
12
|
export { setEventDispatcher } from './dispatch';
|
|
13
|
-
export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibilityEvent, setNativeProps, getNativeTag, getNativeNode, whenCommitted, measure, measureInWindow, measureLayout, disposeRoot, } from './commit';
|
|
13
|
+
export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibilityEvent, setNativeProps, getNativeTag, getNativeNode, whenCommitted, measure, measureInWindow, measureLayout, disposeRoot, readCommitProfile, } from './commit';
|
|
14
|
+
export type { ICommitProfile } from './commit';
|
|
15
|
+
export { registerPostCommit, unregisterPostCommit } from './post-commit';
|
|
14
16
|
export { toPublicInstance } from './host-instance';
|
|
15
17
|
export type { IHostInstance } from './host-instance';
|
|
16
|
-
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue } from './platform-color';
|
|
17
|
-
export type { IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple } from './platform-color';
|
|
18
|
+
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue, } from './platform-color';
|
|
19
|
+
export type { IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, } from './platform-color';
|
|
18
20
|
export { processBoxShadow } from './process-box-shadow';
|
|
19
21
|
export type { IParsedBoxShadow } from './process-box-shadow';
|
|
20
22
|
export { processFilter } from './process-filter';
|
|
@@ -28,19 +30,19 @@ export type { IParsedBackgroundImage } from './process-background-image';
|
|
|
28
30
|
export { flattenStyle } from './style';
|
|
29
31
|
export type { IViewStyle, ITextStyle, IStyleProp, IDimensionValue, IFlexAlign, IFlexJustify, ITransformProp, IBoxShadowValue, IDropShadowValue, IFilterFunction, IColorStopValue, ILinearGradientValue, IRadialGradientValue, IRadialGradientPosition, IRadialGradientShape, IRadialGradientSize, IBackgroundImageValue, IBlendMode, } from './styles';
|
|
30
32
|
export { StyleSheet, computeHairlineWidth } from './style-sheet';
|
|
31
|
-
export {
|
|
32
|
-
export type { IClassNameValue } from './style-registry';
|
|
33
|
-
export {
|
|
34
|
-
export type { IClassToggleMap, IScopableClassValue } from './style-registry/scope';
|
|
33
|
+
export { registerRules, resolveClassName, clearGlobalStyles, isClassNameValue, } from './style-registry';
|
|
34
|
+
export type { IClassNameValue, IStyleRule } from './style-registry';
|
|
35
|
+
export { renameClassTokens } from './style-registry/scope';
|
|
36
|
+
export type { IClassNameRenames, IClassToggleMap, IScopableClassValue, } from './style-registry/scope';
|
|
35
37
|
export { Platform } from './platform';
|
|
36
|
-
export type { IPlatformStatic, IPlatformOSType, IPlatformSelectSpec } from './platform';
|
|
38
|
+
export type { IPlatformStatic, IPlatformOSType, IPlatformSelectSpec, } from './platform';
|
|
37
39
|
export type { IPlatformConstantsIOS } from './platform/index.ios';
|
|
38
40
|
export type { IPlatformConstantsAndroid } from './platform/index.android';
|
|
39
41
|
export { dlog, isDebug } from './debug';
|
|
40
42
|
export { reportUncaughtError } from './report-error';
|
|
41
43
|
export type { IUncaughtErrorInfo } from './report-error';
|
|
42
44
|
export { getNativeModule, getEnforcingNativeModule } from './native-modules';
|
|
43
|
-
export { installDeviceEventHub, NativeEventEmitter, setDeviceEventSource } from './native-events';
|
|
45
|
+
export { installDeviceEventHub, NativeEventEmitter, setDeviceEventSource, } from './native-events';
|
|
44
46
|
export type { IEventSubscription, IEventEmitterModule, INativeEventListener, IDeviceEventSource, } from './native-events';
|
|
45
47
|
export { AnimatedNode, AnimatedWithChildren, AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedInterpolation, AnimatedAddition, AnimatedSubtraction, AnimatedMultiplication, AnimatedDivision, AnimatedModulo, AnimatedDiffClamp, AnimatedEvent, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, attachNativeEvent, attachNativeEventHandler, flushValue, Easing, createNumericInterpolation, checkValidRanges, TimingAnimation, SpringAnimation, DecayAnimation, AnimatedTracking, timing, spring, decay, parallel, sequence, stagger, loop, delay, nativeAnimated, isNativeAnimatedAvailable, AnimatedProps, createAnimatedLeafLifecycle, AnimatedStyle, AnimatedTransform, AnimatedMock, reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode, } from './animated';
|
|
46
48
|
export type { IAnimatedLeafLifecycle, IValueXY, IRgbaValue, IColorInput, IEventConfig, IEventListener, IAnimatedEventHandler, INativeEventAttachment, IValueListener, IEasingFunction, IInterpolationConfig, IExtrapolateType, IAnimation, IEndCallback, IEndResult, ICompositeAnimation, ITimingConfig, ISpringConfig, IDecayConfig, IParallelConfig, ILoopAnimationConfig, INativeNodeConfig, INativeAnimationConfig, INativeEventMapping, IPlatformConfig, } from './animated';
|
|
@@ -51,8 +53,8 @@ export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlert
|
|
|
51
53
|
export { Share } from './share';
|
|
52
54
|
export type { IShareContent, IShareOptions, IShareAction } from './share';
|
|
53
55
|
export { imageStatics } from './image-loader';
|
|
54
|
-
export type { IImageStatics, IImageSize, IImageCacheStatus } from './image-loader';
|
|
55
|
-
export { setImageSourceResolver, resolveImageSource } from './image-source-resolver';
|
|
56
|
+
export type { IImageStatics, IImageSize, IImageCacheStatus, } from './image-loader';
|
|
57
|
+
export { setImageSourceResolver, resolveImageSource, } from './image-source-resolver';
|
|
56
58
|
export type { IImageSource, IImageSourceProp } from './image-source-resolver';
|
|
57
59
|
export { ActionSheetIOS } from './action-sheet-ios';
|
|
58
60
|
export type { IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, } from './action-sheet-ios';
|
|
@@ -74,16 +76,16 @@ export type { IAppStateStatus, IAppStateEvent } from './app-state';
|
|
|
74
76
|
export { createAppRegistry } from './app-registry';
|
|
75
77
|
export type { IAppRegistry, ICreateAppRegistryResult, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './app-registry';
|
|
76
78
|
export { Keyboard, KEYBOARD_EVENT } from './keyboard';
|
|
77
|
-
export type { IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics } from './keyboard';
|
|
79
|
+
export type { IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, } from './keyboard';
|
|
78
80
|
export { currentlyFocusedInput, setInputFocused, setInputBlurred, blurTextInput, } from './text-input-state';
|
|
79
81
|
export { LayoutAnimation } from './layout-animation';
|
|
80
82
|
export type { ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, ILayoutAnimationTypes, ILayoutAnimationProperties, } from './layout-animation';
|
|
81
83
|
export { BackHandler } from './back-handler';
|
|
82
84
|
export type { IBackPressEventName, IBackPressHandler } from './back-handler';
|
|
83
|
-
export { PermissionsAndroid, PERMISSIONS, RESULTS } from './permissions-android';
|
|
84
|
-
export type { IPermission, IPermissionStatus, IRationale } from './permissions-android';
|
|
85
|
+
export { PermissionsAndroid, PERMISSIONS, RESULTS, } from './permissions-android';
|
|
86
|
+
export type { IPermission, IPermissionStatus, IRationale, } from './permissions-android';
|
|
85
87
|
export { AccessibilityInfo } from './accessibility-info';
|
|
86
88
|
export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityInfoStatic, IAccessibilityEventType, IAccessibilityHandle, } from './accessibility-info/shared';
|
|
87
|
-
export { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight } from './status-bar';
|
|
89
|
+
export { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight, } from './status-bar';
|
|
88
90
|
export { hideTransition, STATUS_BAR_MANAGER, ANIMATED_HIDE_TRANSITION, STATIC_HIDE_TRANSITION, } from './status-bar/shared';
|
|
89
91
|
export type { IStatusBarProps, IStatusBarStyle, IStatusBarAnimation, IStatusBarImperative, } from './status-bar/shared';
|
package/build/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Every framework adapter drives this tiny mutation API; all Fabric-specific
|
|
3
3
|
// logic (tag allocation, view-name resolution, clone-on-write, event
|
|
4
4
|
// normalization) lives behind it, in one place.
|
|
5
|
-
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, getExplicitStyle, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node.js';
|
|
5
|
+
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, censusRetainedTree, getExplicitStyle, setNodeHidden, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node.js';
|
|
6
6
|
export { isEventFor } from './view-config.js';
|
|
7
7
|
export { registerComponent, setNativeViewConfigSource } from './registry.js';
|
|
8
8
|
// Real cross-package consumer: core/components' KeyboardAvoidingView render narrows
|
|
@@ -10,18 +10,25 @@ export { registerComponent, setNativeViewConfigSource } from './registry.js';
|
|
|
10
10
|
export { isRecord } from './type-guards.js';
|
|
11
11
|
// InteractionManager: pure JS (timers + emitter), framework-agnostic, so it lives
|
|
12
12
|
// here; every adapter re-exports it.
|
|
13
|
-
export { InteractionManager, Events as InteractionManagerEvents } from './interaction-manager/index.js';
|
|
13
|
+
export { InteractionManager, Events as InteractionManagerEvents, } from './interaction-manager/index.js';
|
|
14
14
|
// PanResponder: pure JS gesture recognition over the View responder props,
|
|
15
15
|
// framework-agnostic, so it lives here; every adapter re-exports it.
|
|
16
16
|
export { default as PanResponder } from './pan-responder/index.js';
|
|
17
17
|
export { SymbioteSurface, createSurface } from './surface.js';
|
|
18
18
|
export { setEventDispatcher } from './dispatch.js';
|
|
19
|
-
export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibilityEvent, setNativeProps, getNativeTag, getNativeNode, whenCommitted, measure, measureInWindow, measureLayout, disposeRoot, } from './commit.js';
|
|
19
|
+
export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibilityEvent, setNativeProps, getNativeTag, getNativeNode, whenCommitted, measure, measureInWindow, measureLayout, disposeRoot, readCommitProfile, } from './commit.js';
|
|
20
|
+
// "A commit just reached completeRoot." The one seam that means the same thing under every
|
|
21
|
+
// adapter: React commits synchronously inside its own commit phase, while Vue / Svelte / Angular
|
|
22
|
+
// schedule completeRoot on a microtask, so each framework's own after-render hook fires at a
|
|
23
|
+
// DIFFERENT point relative to the native commit. Anything timing or comparing the commit path
|
|
24
|
+
// across adapters has to hang off this, not off a per-framework lifecycle hook, or it measures a
|
|
25
|
+
// different quantity in each one under the same name.
|
|
26
|
+
export { registerPostCommit, unregisterPostCommit } from './post-commit.js';
|
|
20
27
|
// The public instance grafted onto a host node by every adapter (React's getPublicInstance,
|
|
21
28
|
// the Vue renderer's createElement): the imperative measure/setNativeProps/focus API. Lives
|
|
22
29
|
// here because it depends only on engine internals, so all adapters inherit it identically.
|
|
23
30
|
export { toPublicInstance } from './host-instance/index.js';
|
|
24
|
-
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue } from './platform-color/index.js';
|
|
31
|
+
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue, } from './platform-color/index.js';
|
|
25
32
|
// CSS-style processors (boxShadow/filter): RN parses these in JS before native because
|
|
26
33
|
// enableNativeCSSParsing() defaults to false. Exported so an adapter / test can reuse them.
|
|
27
34
|
export { processBoxShadow } from './process-box-shadow/index.js';
|
|
@@ -33,13 +40,13 @@ export { processFontVariant } from './process-font-variant.js';
|
|
|
33
40
|
export { processBackgroundImage } from './process-background-image/index.js';
|
|
34
41
|
export { flattenStyle } from './style/index.js';
|
|
35
42
|
export { StyleSheet, computeHairlineWidth } from './style-sheet/index.js';
|
|
36
|
-
export {
|
|
37
|
-
export {
|
|
43
|
+
export { registerRules, resolveClassName, clearGlobalStyles, isClassNameValue, } from './style-registry/index.js';
|
|
44
|
+
export { renameClassTokens } from './style-registry/scope.js';
|
|
38
45
|
export { Platform } from './platform';
|
|
39
46
|
export { dlog, isDebug } from './debug.js';
|
|
40
47
|
export { reportUncaughtError } from './report-error.js';
|
|
41
48
|
export { getNativeModule, getEnforcingNativeModule } from './native-modules/index.js';
|
|
42
|
-
export { installDeviceEventHub, NativeEventEmitter, setDeviceEventSource } from './native-events.js';
|
|
49
|
+
export { installDeviceEventHub, NativeEventEmitter, setDeviceEventSource, } from './native-events.js';
|
|
43
50
|
export { AnimatedNode, AnimatedWithChildren, AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedInterpolation, AnimatedAddition, AnimatedSubtraction, AnimatedMultiplication, AnimatedDivision, AnimatedModulo, AnimatedDiffClamp, AnimatedEvent, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, attachNativeEvent, attachNativeEventHandler, flushValue, Easing, createNumericInterpolation, checkValidRanges, TimingAnimation, SpringAnimation, DecayAnimation, AnimatedTracking, timing, spring, decay, parallel, sequence, stagger, loop, delay, nativeAnimated, isNativeAnimatedAvailable, AnimatedProps, createAnimatedLeafLifecycle, AnimatedStyle, AnimatedTransform, AnimatedMock, reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode, } from './animated/index.js';
|
|
44
51
|
export { getSlot } from './fabric.js';
|
|
45
52
|
// Imperative runtime modules: framework-agnostic native-bridge consumers (no visual, no
|
|
@@ -52,7 +59,7 @@ export { Share } from './share';
|
|
|
52
59
|
// no view of its own, same shape as Alert/Share. The source-resolution seam it shares with
|
|
53
60
|
// @symbiote-native/components' renderImage lives alongside it in image-source-resolver.
|
|
54
61
|
export { imageStatics } from './image-loader.js';
|
|
55
|
-
export { setImageSourceResolver, resolveImageSource } from './image-source-resolver.js';
|
|
62
|
+
export { setImageSourceResolver, resolveImageSource, } from './image-source-resolver.js';
|
|
56
63
|
export { ActionSheetIOS } from './action-sheet-ios/index.js';
|
|
57
64
|
export { Linking } from './linking';
|
|
58
65
|
export { Vibration } from './vibration';
|
|
@@ -74,9 +81,9 @@ export { Keyboard, KEYBOARD_EVENT } from './keyboard/index.js';
|
|
|
74
81
|
export { currentlyFocusedInput, setInputFocused, setInputBlurred, blurTextInput, } from './text-input-state.js';
|
|
75
82
|
export { LayoutAnimation } from './layout-animation/index.js';
|
|
76
83
|
export { BackHandler } from './back-handler/index.js';
|
|
77
|
-
export { PermissionsAndroid, PERMISSIONS, RESULTS } from './permissions-android/index.js';
|
|
84
|
+
export { PermissionsAndroid, PERMISSIONS, RESULTS, } from './permissions-android/index.js';
|
|
78
85
|
export { AccessibilityInfo } from './accessibility-info';
|
|
79
86
|
// StatusBar: values from the platform-selected './status-bar', types from '-shared' (the
|
|
80
87
|
// .ios re-export would otherwise duplicate-export the type symbols).
|
|
81
|
-
export { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight } from './status-bar';
|
|
88
|
+
export { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight, } from './status-bar';
|
|
82
89
|
export { hideTransition, STATUS_BAR_MANAGER, ANIMATED_HIDE_TRANSITION, STATIC_HIDE_TRANSITION, } from './status-bar/shared.js';
|
|
@@ -202,7 +202,8 @@ class InteractionManagerImpl {
|
|
|
202
202
|
break;
|
|
203
203
|
if (typeof next === 'function')
|
|
204
204
|
next();
|
|
205
|
-
if (this.deadline > DEFAULT_DEADLINE &&
|
|
205
|
+
if (this.deadline > DEFAULT_DEADLINE &&
|
|
206
|
+
Date.now() - startTime >= this.deadline) {
|
|
206
207
|
if (this.taskQueue.length > 0) {
|
|
207
208
|
scheduleTimeout(() => {
|
|
208
209
|
this.flushQueue();
|
|
@@ -42,7 +42,7 @@ const ANIMATION_PROPERTY = {
|
|
|
42
42
|
// (`FabricUIManager?.configureNextLayoutAnimation` in LayoutAnimation.js) before
|
|
43
43
|
// calling it, no `as` cast needed.
|
|
44
44
|
function hasConfigureNextLayoutAnimation(value) {
|
|
45
|
-
return isRecord(value) && typeof value.configureNextLayoutAnimation === 'function';
|
|
45
|
+
return (isRecord(value) && typeof value.configureNextLayoutAnimation === 'function');
|
|
46
46
|
}
|
|
47
47
|
// Resolved FRESH on every configureNext, deliberately NOT memoized. Either mechanism can
|
|
48
48
|
// come and go at runtime (the Fabric global installs during bootstrap; a TurboModule can be
|
|
@@ -93,9 +93,15 @@ const Presets = {
|
|
|
93
93
|
linear: createLayoutAnimation(PRESET_DURATION.linear, ANIMATION_TYPE.linear, ANIMATION_PROPERTY.opacity),
|
|
94
94
|
spring: {
|
|
95
95
|
duration: PRESET_DURATION.spring,
|
|
96
|
-
create: {
|
|
96
|
+
create: {
|
|
97
|
+
type: ANIMATION_TYPE.linear,
|
|
98
|
+
property: ANIMATION_PROPERTY.opacity,
|
|
99
|
+
},
|
|
97
100
|
update: { type: ANIMATION_TYPE.spring, springDamping: SPRING_DAMPING },
|
|
98
|
-
delete: {
|
|
101
|
+
delete: {
|
|
102
|
+
type: ANIMATION_TYPE.linear,
|
|
103
|
+
property: ANIMATION_PROPERTY.opacity,
|
|
104
|
+
},
|
|
99
105
|
},
|
|
100
106
|
};
|
|
101
107
|
// ---- enabled gate --------------------------------------------------------
|
|
@@ -154,7 +160,9 @@ function configureNext(config, onAnimationDidEnd, onAnimationDidFail) {
|
|
|
154
160
|
class LayoutAnimationImpl {
|
|
155
161
|
// Frozen so callers can't mutate the shared type/property tables.
|
|
156
162
|
Types = Object.freeze({ ...ANIMATION_TYPE });
|
|
157
|
-
Properties = Object.freeze({
|
|
163
|
+
Properties = Object.freeze({
|
|
164
|
+
...ANIMATION_PROPERTY,
|
|
165
|
+
});
|
|
158
166
|
Presets = Presets;
|
|
159
167
|
// Methods (not class fields) so they stay overridable under
|
|
160
168
|
// useDefineForClassFields.
|
package/build/native-events.js
CHANGED
|
@@ -70,7 +70,8 @@ export function setDeviceEventSource(source) {
|
|
|
70
70
|
// would throw "undefined is not a function" — the `?.` guards the module, not a
|
|
71
71
|
// missing method. Mirrors RN's NativeEventEmitter constructor probe.
|
|
72
72
|
function hasObserveCounters(module) {
|
|
73
|
-
return typeof module.addListener === 'function' &&
|
|
73
|
+
return (typeof module.addListener === 'function' &&
|
|
74
|
+
typeof module.removeListeners === 'function');
|
|
74
75
|
}
|
|
75
76
|
// Subscribe to events for one native module. Mirrors RN's NativeEventEmitter: each
|
|
76
77
|
// `addListener` also pings the module's `addListener` counter, and removal pings
|
|
@@ -110,7 +110,9 @@ export function createDeviceEventModule(config) {
|
|
|
110
110
|
installDeviceEventHub();
|
|
111
111
|
const resolved = getModule();
|
|
112
112
|
const bindModule = config.bindModuleToEmitter ?? true;
|
|
113
|
-
const boundModule = bindModule && resolved !== null && hasEventEmitterShape(resolved)
|
|
113
|
+
const boundModule = bindModule && resolved !== null && hasEventEmitterShape(resolved)
|
|
114
|
+
? resolved
|
|
115
|
+
: undefined;
|
|
114
116
|
emitter = new NativeEventEmitter(boundModule);
|
|
115
117
|
config.onEmitterCreated?.(emitter, resolved);
|
|
116
118
|
}
|