@symbiote-native/engine 1.2.0 → 1.3.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.
- package/android/CMakeLists.txt +31 -1
- package/android/build.gradle +4 -1
- package/build/accessibility-info/index.android.js +21 -17
- package/build/accessibility-info/index.ios.js +2 -0
- package/build/accessibility-info/shared.d.ts +1 -1
- package/build/accessibility-props.d.ts +0 -11
- package/build/accessibility-props.js +30 -68
- package/build/alert/shared.d.ts +1 -1
- package/build/alert/shared.js +3 -7
- package/build/animated/graph.js +1 -1
- package/build/animated/leaf-lifecycle.js +2 -2
- package/build/app-state/index.d.ts +1 -1
- package/build/app-state/index.js +35 -27
- package/build/asset-source-resolver.d.ts +2 -0
- package/build/asset-source-resolver.js +13 -0
- package/build/back-handler/index.d.ts +7 -6
- package/build/back-handler/index.js +19 -16
- package/build/debug.js +8 -22
- package/build/dispatch.js +3 -9
- package/build/events/index.js +12 -3
- package/build/fabric-props.js +75 -180
- package/build/fabric.d.ts +2 -8
- package/build/fabric.js +27 -33
- package/build/host-access.d.ts +1 -128
- package/build/host-access.js +96 -205
- package/build/host-behavior.d.ts +1 -100
- package/build/host-behavior.js +125 -311
- package/build/image-loader.d.ts +4 -2
- package/build/image-loader.js +24 -48
- package/build/image-source-resolver.js +3 -7
- package/build/image-source-write.d.ts +1 -12
- package/build/image-source-write.js +28 -36
- package/build/imperative.d.ts +0 -28
- package/build/imperative.js +42 -92
- package/build/index.d.ts +3 -2
- package/build/index.js +30 -43
- package/build/invariant.d.ts +1 -0
- package/build/invariant.js +10 -0
- package/build/keyboard/index.js +11 -32
- package/build/linking/index.android.js +5 -3
- package/build/linking/shared.d.ts +1 -1
- package/build/linking/shared.js +16 -19
- package/build/mutation-buffer.d.ts +0 -177
- package/build/mutation-buffer.js +137 -308
- package/build/native-engine.d.ts +0 -102
- package/build/native-engine.js +38 -98
- package/build/native-events.d.ts +5 -0
- package/build/native-events.js +30 -18
- package/build/native-tree-host.d.ts +0 -21
- package/build/native-tree-host.js +14 -31
- package/build/node.d.ts +0 -212
- package/build/node.js +354 -774
- package/build/permissions-android/index.android.d.ts +59 -0
- package/build/permissions-android/index.android.js +47 -0
- package/build/permissions-android/index.d.ts +1 -115
- package/build/permissions-android/index.ios.d.ts +59 -0
- package/build/permissions-android/index.ios.js +31 -0
- package/build/permissions-android/index.js +3 -184
- package/build/permissions-android/shared.d.ts +63 -0
- package/build/permissions-android/shared.js +66 -0
- package/build/platform/index.android.js +3 -5
- package/build/platform/index.ios.js +3 -3
- package/build/platform/shared.d.ts +4 -0
- package/build/platform/shared.js +9 -0
- package/build/platform-color/index.android.d.ts +4 -0
- package/build/platform-color/index.android.js +7 -0
- package/build/platform-color/index.d.ts +2 -20
- package/build/platform-color/index.js +1 -45
- package/build/platform-color/shared.d.ts +21 -0
- package/build/platform-color/shared.js +41 -0
- package/build/post-commit.js +3 -8
- package/build/process-aspect-ratio.js +3 -7
- package/build/process-background-longhands.js +10 -19
- package/build/process-filter.js +11 -19
- package/build/process-font-variant.js +3 -7
- package/build/registry.d.ts +0 -33
- package/build/registry.js +22 -57
- package/build/report-error.js +4 -18
- package/build/settings/index.android.d.ts +6 -0
- package/build/settings/index.android.js +21 -0
- package/build/settings/index.d.ts +1 -8
- package/build/settings/index.ios.d.ts +8 -0
- package/build/settings/index.ios.js +122 -0
- package/build/settings/index.js +3 -122
- package/build/share/index.android.js +9 -32
- package/build/share/index.ios.js +14 -15
- package/build/share/shared.d.ts +4 -2
- package/build/share/shared.js +7 -10
- package/build/status-bar/index.android.js +1 -1
- package/build/status-bar/index.ios.js +4 -3
- package/build/structured-style.d.ts +0 -9
- package/build/structured-style.js +16 -31
- package/build/styles.js +3 -6
- package/build/surface.d.ts +0 -26
- package/build/surface.js +29 -76
- package/build/text-input-state.js +4 -8
- package/build/toast-android/index.android.d.ts +10 -0
- package/build/toast-android/index.android.js +108 -0
- package/build/toast-android/index.d.ts +1 -10
- package/build/toast-android/index.ios.d.ts +10 -0
- package/build/toast-android/index.ios.js +19 -0
- package/build/toast-android/index.js +3 -108
- package/build/touch-history.js +5 -11
- package/build/tree-host.d.ts +0 -270
- package/build/tree-host.js +63 -153
- package/build/view-config.js +17 -37
- package/cpp/SymbioteFabricProps.cpp +204 -120
- package/cpp/SymbioteFabricProps.h +5 -0
- package/cpp/SymbioteTree.cpp +20 -6
- package/package.json +2 -2
package/build/image-loader.d.ts
CHANGED
|
@@ -6,8 +6,10 @@ export type IImageSize = {
|
|
|
6
6
|
export type IImageCacheStatus = 'memory' | 'disk' | 'disk/memory';
|
|
7
7
|
type ISizeSuccess = (width: number, height: number) => void;
|
|
8
8
|
type ISizeFailure = (error: unknown) => void;
|
|
9
|
-
declare function getSize(uri: string
|
|
10
|
-
declare function
|
|
9
|
+
declare function getSize(uri: string): Promise<IImageSize>;
|
|
10
|
+
declare function getSize(uri: string, success: ISizeSuccess, failure?: ISizeFailure): undefined;
|
|
11
|
+
declare function getSizeWithHeaders(uri: string, headers: Record<string, string>): Promise<IImageSize>;
|
|
12
|
+
declare function getSizeWithHeaders(uri: string, headers: Record<string, string>, success: ISizeSuccess, failure?: ISizeFailure): undefined;
|
|
11
13
|
declare function prefetch(uri: string, callback?: (requestId: number) => void): Promise<boolean>;
|
|
12
14
|
declare function abortPrefetch(requestId: number): void;
|
|
13
15
|
declare function queryCache(uris: string[]): Promise<Record<string, IImageCacheStatus>>;
|
package/build/image-loader.js
CHANGED
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
// Image static methods (RN's Image.getSize
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// second `requestId` arg), branched on Platform.OS below, not on module name. NOTE the asymmetry
|
|
8
|
-
// in the iOS spec: `getSize` resolves a `[width, height]` ARRAY, while `getSizeWithHeaders`
|
|
9
|
-
// resolves a `{width, height}` OBJECT, both are guarded below before reading. The native result
|
|
10
|
-
// crosses the I/O boundary as `unknown`; we never cast it, we narrow its shape.
|
|
11
|
-
//
|
|
12
|
-
// This is a stateful, native-bridge-touching imperative module (module-level ImageLoader cache +
|
|
13
|
-
// prefetch requestId counter) with no view of its own - it belongs in @symbiote-native/engine
|
|
14
|
-
// alongside Alert/Share, not in a view/render-*.ts file (whose contract is zero state / zero
|
|
15
|
-
// native bridge).
|
|
1
|
+
// Image static methods (RN's Image.getSize/prefetch/queryCache/etc). Both iOS and Android specs
|
|
2
|
+
// register under the same module name ('ImageLoader'), so this stays flat, non-platform-split —
|
|
3
|
+
// only the Android prefetch call signature differs (a second requestId arg), branched below.
|
|
4
|
+
// The native result crosses the I/O boundary as unknown; we never cast it, we narrow its shape.
|
|
5
|
+
// This is a stateful, native-bridge-touching imperative module with no view of its own — it
|
|
6
|
+
// belongs here alongside Alert/Share, not in a view/render-*.ts file.
|
|
16
7
|
import { dlog } from './debug.js';
|
|
17
8
|
import { resolveImageSource, } from './image-source-resolver.js';
|
|
18
9
|
import { getNativeModule } from './native-modules/index.js';
|
|
19
10
|
import { Platform } from './platform';
|
|
20
11
|
import { isNumber } from './type-guards.js';
|
|
21
|
-
// The
|
|
22
|
-
//
|
|
23
|
-
// provable on a real host - a headless fake answers to any name - so this iOS name is
|
|
24
|
-
// device-verify-pending.
|
|
12
|
+
// The native module name RN registers this under. A module name like this is only provable on a
|
|
13
|
+
// real host — a headless fake answers to any name.
|
|
25
14
|
const IMAGE_LOADER_MODULE = 'ImageLoader';
|
|
26
15
|
let imageLoaderModule;
|
|
27
16
|
function getImageLoader() {
|
|
@@ -54,40 +43,29 @@ function requireLoader(method) {
|
|
|
54
43
|
}
|
|
55
44
|
return loader;
|
|
56
45
|
}
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
46
|
+
// Image.ios.js / Image.android.js: the promise when no success callback is given; otherwise the
|
|
47
|
+
// result goes to the callbacks and nothing is returned, a missing `failure` becoming a warning.
|
|
48
|
+
function deliverSize(promise, uri, success, failure) {
|
|
49
|
+
if (typeof success !== 'function')
|
|
50
|
+
return promise;
|
|
51
|
+
promise
|
|
52
|
+
.then(size => success(size.width, size.height))
|
|
53
|
+
.catch(typeof failure === 'function'
|
|
54
|
+
? failure
|
|
55
|
+
: () => console.warn('Failed to get size for image: ' + uri));
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
60
58
|
function getSize(uri, success, failure) {
|
|
61
59
|
const promise = Promise.resolve()
|
|
62
60
|
.then(() => requireLoader('getSize').getSize(uri))
|
|
63
61
|
.then(toImageSize);
|
|
64
|
-
|
|
65
|
-
promise
|
|
66
|
-
.then(size => success(size.width, size.height))
|
|
67
|
-
.catch((error) => {
|
|
68
|
-
if (typeof failure === 'function')
|
|
69
|
-
failure(error);
|
|
70
|
-
else
|
|
71
|
-
dlog(`Image.getSize failed for ${uri}: ${String(error)}`);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return promise;
|
|
62
|
+
return deliverSize(promise, uri, success, failure);
|
|
75
63
|
}
|
|
76
64
|
function getSizeWithHeaders(uri, headers, success, failure) {
|
|
77
65
|
const promise = Promise.resolve()
|
|
78
66
|
.then(() => requireLoader('getSizeWithHeaders').getSizeWithHeaders(uri, headers))
|
|
79
67
|
.then(toImageSize);
|
|
80
|
-
|
|
81
|
-
promise
|
|
82
|
-
.then(size => success(size.width, size.height))
|
|
83
|
-
.catch((error) => {
|
|
84
|
-
if (typeof failure === 'function')
|
|
85
|
-
failure(error);
|
|
86
|
-
else
|
|
87
|
-
dlog(`Image.getSizeWithHeaders failed for ${uri}: ${String(error)}`);
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
return promise;
|
|
68
|
+
return deliverSize(promise, uri, success, failure);
|
|
91
69
|
}
|
|
92
70
|
// Android keys an in-flight prefetch by a monotonic requestId (so abortRequest can cancel it);
|
|
93
71
|
// RN's Image.android.js generates the same way. iOS ignores the arg.
|
|
@@ -159,10 +137,8 @@ async function queryCache(uris) {
|
|
|
159
137
|
throw error;
|
|
160
138
|
});
|
|
161
139
|
}
|
|
162
|
-
//
|
|
163
|
-
// uses via resolveImageSource).
|
|
164
|
-
// {uri, scale, ...}; the app injects the real one with setImageSourceResolver, and this exposes
|
|
165
|
-
// its output to callers directly.
|
|
140
|
+
// Pure JS: run the currently-installed source resolver (the same machinery the Image component
|
|
141
|
+
// uses via resolveImageSource). The app injects the real one with setImageSourceResolver.
|
|
166
142
|
function resolveAssetSource(source) {
|
|
167
143
|
return resolveImageSource(source);
|
|
168
144
|
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// processColor seam - this keeps @symbiote-native/components free of a react-native dependency (and
|
|
5
|
-
// the headless harness working). BOTH the pure renderImage view (@symbiote-native/components) and
|
|
6
|
-
// this package's own image-loader statics (resolveAssetSource) call resolveImageSource; neither
|
|
7
|
-
// reaches into the mutable resolver directly.
|
|
1
|
+
// require('./x.png') asset ids and {uri} sources resolve via RN's own resolveAssetSource, which is
|
|
2
|
+
// platform-specific and injected here rather than imported (mirrors platform-color.ts's
|
|
3
|
+
// processColor seam) so @symbiote-native/components stays free of a react-native dependency.
|
|
8
4
|
let sourceResolver = source => source;
|
|
9
5
|
export function setImageSourceResolver(resolve) {
|
|
10
6
|
sourceResolver = resolve;
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
export declare const IMAGE_SOURCE_PROPS: ReadonlySet<string>;
|
|
2
|
-
|
|
3
|
-
* Resolve a source prop and normalise it to the ARRAY shape native expects.
|
|
4
|
-
*
|
|
5
|
-
* Always an array, including for the single-object and asset-id cases: a bare object reaching
|
|
6
|
-
* Fabric paints nothing and reports nothing, which is worse than an image that is simply absent.
|
|
7
|
-
* The rule downstream then has one shape to reason about instead of three.
|
|
8
|
-
*
|
|
9
|
-
* A value this cannot make sense of comes back UNTOUCHED rather than wrapped. `routeProp` writes
|
|
10
|
-
* whatever it is handed, and a tag with no image behavior must keep its props verbatim — the
|
|
11
|
-
* control case in `image-payload.itest.ts` is what holds that line.
|
|
12
|
-
*/
|
|
13
|
-
export declare function resolveImageSourceProp(value: unknown): unknown;
|
|
2
|
+
export declare function resolveImageSourceProp(value: unknown, dropsSingleSourceHeaders?: boolean): unknown;
|
|
14
3
|
export declare const IMAGE_LOAD_EVENT_NAMES: ReadonlySet<string>;
|
|
15
4
|
/** Whether at least one of the four still has a listener installed on the node. */
|
|
16
5
|
export declare function anyImageLoadEventListenerWired(listeners: ReadonlyMap<string, unknown> | undefined): boolean;
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
// Image sources, resolved on the way IN rather than
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// indexes into, populated at bundle time, in JavaScript. There is no such table in C++ and there
|
|
6
|
-
// should not be: it is the bundler's, not the platform's.
|
|
7
|
-
//
|
|
8
|
-
// This is the same seam and the same argument as `structured-style.ts`, which resolves
|
|
9
|
-
// `boxShadow`/`filter`/`transform` at write time for the identical reason — a value resolved at
|
|
10
|
-
// PAYLOAD-BUILD time is resolved HEADLESS ONLY, because the C++ builder has no JS to call, and the
|
|
11
|
-
// device then commits the raw input and Fabric drops it in silence. Moving the lookup one step
|
|
12
|
-
// earlier costs nothing and leaves the rest of the rule pure, which is what let it move at all.
|
|
13
|
-
//
|
|
14
|
-
// The three names are Image's: `source` is the real one, `defaultSource` the placeholder, and
|
|
1
|
+
// Image sources, resolved on the way IN rather than in the C++ payload builder like the rest of
|
|
2
|
+
// Image's rule: resolveImageSource asks Metro's asset registry, a JS-only, bundle-time table with
|
|
3
|
+
// no C++ side. Same seam as structured-style.ts's write-time boxShadow/filter/transform fold.
|
|
4
|
+
// The three names are Image's: `source` the real one, `defaultSource` the placeholder,
|
|
15
5
|
// `loadingIndicatorSource` Android's spinner.
|
|
16
6
|
import { resolveImageSource } from './image-source-resolver.js';
|
|
17
7
|
export const IMAGE_SOURCE_PROPS = new Set([
|
|
@@ -19,34 +9,36 @@ export const IMAGE_SOURCE_PROPS = new Set([
|
|
|
19
9
|
'defaultSource',
|
|
20
10
|
'loadingIndicatorSource',
|
|
21
11
|
]);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
* A value this cannot make sense of comes back UNTOUCHED rather than wrapped. `routeProp` writes
|
|
30
|
-
* whatever it is handed, and a tag with no image behavior must keep its props verbatim — the
|
|
31
|
-
* control case in `image-payload.itest.ts` is what holds that line.
|
|
32
|
-
*/
|
|
33
|
-
export function resolveImageSourceProp(value) {
|
|
12
|
+
// Resolve a source prop and normalize it to the array shape native expects — always an array,
|
|
13
|
+
// even for the single-object and asset-id cases, since a bare object reaching Fabric paints and
|
|
14
|
+
// reports nothing at all.
|
|
15
|
+
// A value this can't make sense of comes back untouched rather than wrapped: a tag with no image
|
|
16
|
+
// behavior must keep its props verbatim (pinned by image-payload.itest.ts).
|
|
17
|
+
export function resolveImageSourceProp(value, dropsSingleSourceHeaders = false) {
|
|
34
18
|
if (value === undefined || value === null)
|
|
35
19
|
return value;
|
|
36
20
|
if (typeof value !== 'number' && typeof value !== 'object')
|
|
37
21
|
return value;
|
|
38
22
|
const resolved = resolveImageSource(value);
|
|
39
|
-
|
|
23
|
+
if (Array.isArray(resolved))
|
|
24
|
+
return resolved;
|
|
25
|
+
// Android `source` only: `Image.android.js` lifts headers to the native `headers` prop just for an
|
|
26
|
+
// ARRAY source, and ReactImageView ignores per-source headers, so RN sends none for a single
|
|
27
|
+
// object. Wrapping here erases that shape, so its headers go here too.
|
|
28
|
+
if (dropsSingleSourceHeaders &&
|
|
29
|
+
typeof resolved === 'object' &&
|
|
30
|
+
resolved !== null &&
|
|
31
|
+
'headers' in resolved) {
|
|
32
|
+
const { headers: _dropped, ...rest } = resolved;
|
|
33
|
+
return [rest];
|
|
34
|
+
}
|
|
35
|
+
return [resolved];
|
|
40
36
|
}
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// `routeProp` diverts them through `setEventListener`/`node.listeners`, never through `writeProp`
|
|
47
|
-
// — unlike an ordinary function prop, they never reach the `functionProps` stash. Named here in
|
|
48
|
-
// LISTENER form (post `listenerName()`: `onLoad` -> `load`), which is what `node.listeners` keys
|
|
49
|
-
// on. Same shape `GATED_EVENT_PROPS` uses for `onLayout`, applied to a name no host behavior owns.
|
|
37
|
+
// Android's ReactImageView.setShouldNotifyLoadEvents gates on this prop: none of these four ever
|
|
38
|
+
// fire until Image.android.js sets it, because any one of them is authored; iOS has no such gate.
|
|
39
|
+
// Real Fabric events (view-config.ts's COMPONENT_EVENTS.RCTImageView), so routeProp diverts them
|
|
40
|
+
// through setEventListener/node.listeners, never writeProp — named in listener form (onLoad ->
|
|
41
|
+
// load), what node.listeners keys on.
|
|
50
42
|
export const IMAGE_LOAD_EVENT_NAMES = new Set([
|
|
51
43
|
'loadStart',
|
|
52
44
|
'load',
|
package/build/imperative.d.ts
CHANGED
|
@@ -2,47 +2,19 @@ import type { IMeasureInWindowOnSuccess, IMeasureLayoutOnSuccess, IMeasureOnSucc
|
|
|
2
2
|
import { type ISymbioteNode } from './node';
|
|
3
3
|
export declare function registerSurfaceCommit(commit: (rootTag: IRootTag) => void, forget: (rootTag: IRootTag) => void): void;
|
|
4
4
|
export declare function flushNativeProps(): void;
|
|
5
|
-
/**
|
|
6
|
-
* Publish a node whose props changed OUTSIDE any renderer mutation.
|
|
7
|
-
*
|
|
8
|
-
* Recording is not publishing. Every other write reaches Fabric because the framework's own commit
|
|
9
|
-
* follows it; a change driven by a NATIVE EVENT has no such follow-up — the press path's
|
|
10
|
-
* `setNodePressed` is the first caller that is not `setNativeProps`.
|
|
11
|
-
*
|
|
12
|
-
* Queued rather than committed on the spot: several writes in one task publish together at the
|
|
13
|
-
* microtask boundary, one commit per surface.
|
|
14
|
-
*/
|
|
15
5
|
export declare function requestCommitFor(node: ISymbioteNode): void;
|
|
16
6
|
export declare function disposeRoot(rootTag: IRootTag): void;
|
|
17
7
|
/** The committed reactTag, stable across clone-on-write — what the native Animated driver binds. */
|
|
18
8
|
export declare function getNativeTag(node: ISymbioteNode): number | undefined;
|
|
19
|
-
/**
|
|
20
|
-
* The node's current native handle, in kind identical to React's `stateNode.node`.
|
|
21
|
-
*
|
|
22
|
-
* OPAQUE. Under the native host it is a `ShadowNode`, under a headless one whatever that host
|
|
23
|
-
* committed — see `ICommittedRecord.handle`. It used to be typed `IFabricNode`, a brand with no
|
|
24
|
-
* members, so a caller can do exactly as much with it as before.
|
|
25
|
-
*/
|
|
26
9
|
export declare function getNativeNode(node: ISymbioteNode): object | undefined;
|
|
27
10
|
/** Called by the commit path once a batch has published. */
|
|
28
11
|
export declare function notifyCommitted(): void;
|
|
29
|
-
/**
|
|
30
|
-
* Run `action` once `node` has a committed Fabric handle — immediately if it already does, else
|
|
31
|
-
* after the commit that assigns one. Returns a cancel fn (drop the retry, e.g. on unmount).
|
|
32
|
-
*/
|
|
33
12
|
export declare function whenCommitted(node: ISymbioteNode, action: () => void): () => void;
|
|
34
13
|
export declare function dispatchViewCommand(node: ISymbioteNode, commandName: string, args: readonly unknown[]): void;
|
|
35
14
|
export declare function sendAccessibilityEvent(node: ISymbioteNode, eventType: string): void;
|
|
36
15
|
export declare function measure(node: ISymbioteNode, callback: IMeasureOnSuccess): void;
|
|
37
16
|
export declare function measureInWindow(node: ISymbioteNode, callback: IMeasureInWindowOnSuccess): void;
|
|
38
17
|
export declare function measureLayout(node: ISymbioteNode, relativeTo: ISymbioteNode, onSuccess: IMeasureLayoutOnSuccess, onFail?: () => void): void;
|
|
39
|
-
/**
|
|
40
|
-
* Tell native that JS has taken the gesture, or given it up.
|
|
41
|
-
*
|
|
42
|
-
* Through the HOST like the five above it, never through the Fabric slot: under the native tree
|
|
43
|
-
* host the committed handle is our placeholder, and `nativeFabricUIManager.setIsJSResponder` unwraps
|
|
44
|
-
* only a `ShadowNode` reference it minted itself.
|
|
45
|
-
*/
|
|
46
18
|
export declare function setIsJSResponder(node: ISymbioteNode, isResponder: boolean, blockNativeResponder: boolean): void;
|
|
47
19
|
/** The node's CURRENT prop value, as the host holds it. The behaviors' one read. */
|
|
48
20
|
export declare function propOf(node: ISymbioteNode, key: string): unknown;
|
package/build/imperative.js
CHANGED
|
@@ -1,29 +1,18 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// HOST answers now (`tree-host.ts`) — native on device, the TypeScript applier headlessly.
|
|
8
|
-
//
|
|
9
|
-
// `undefined` is the ORDINARY state here, not an error. An adapter that wires an imperative call at
|
|
10
|
-
// lifecycle time runs before `completeRoot` under an async-batched commit (Vue and Svelte schedule
|
|
11
|
-
// it on a microtask), so the node has no handle yet. Every function either defers (`whenCommitted`)
|
|
12
|
-
// or logs and returns — never throws.
|
|
1
|
+
// Everything an app or adapter reaches for outside a render: measure, view commands, accessibility
|
|
2
|
+
// events, the Animated fast path. Each is written against a Fabric handle/tag/root tag answered by
|
|
3
|
+
// the tree host (tree-host.ts): native on device, a TypeScript applier headlessly.
|
|
4
|
+
// undefined is the ordinary state here, not an error. An adapter wiring an imperative call at
|
|
5
|
+
// lifecycle time can run before completeRoot under an async-batched commit, so the node has no
|
|
6
|
+
// handle yet. Every function either defers (whenCommitted) or logs and returns — never throws.
|
|
13
7
|
import { dlog, isDebug } from './debug.js';
|
|
14
8
|
import { clearPublishedStyle, writeProp } from './node.js';
|
|
15
9
|
import { flattenStyle } from './style/index.js';
|
|
16
10
|
import { flushOps, treeHost } from './tree-host.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
* `undefined` is the ORDINARY state, not an error — and it is also what a WRAPPED node gets. The host
|
|
24
|
-
* keys on the handle OBJECT, so a Vue `reactive()` / deep-`ref()` Proxy around a host element misses
|
|
25
|
-
* and every function below degrades to its log. Hold host nodes with `shallowRef`.
|
|
26
|
-
*/
|
|
11
|
+
// What Fabric currently holds for `node`, or undefined before its first commit. Flushes first: an
|
|
12
|
+
// imperative call can land between a mutation and its commit, and the host can't answer about ops
|
|
13
|
+
// it hasn't been handed.
|
|
14
|
+
// undefined is also what a wrapped node gets: the host keys on the handle object, so a Vue
|
|
15
|
+
// reactive()/deep-ref() Proxy around a host element misses. Hold host nodes with shallowRef.
|
|
27
16
|
function committedRecordOf(node) {
|
|
28
17
|
flushOps();
|
|
29
18
|
return treeHost()?.committedRecordOf(node);
|
|
@@ -48,16 +37,11 @@ export function flushNativeProps() {
|
|
|
48
37
|
for (const rootTag of roots)
|
|
49
38
|
commitSurface?.(rootTag);
|
|
50
39
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* `setNodePressed` is the first caller that is not `setNativeProps`.
|
|
57
|
-
*
|
|
58
|
-
* Queued rather than committed on the spot: several writes in one task publish together at the
|
|
59
|
-
* microtask boundary, one commit per surface.
|
|
60
|
-
*/
|
|
40
|
+
// Publish a node whose props changed outside any renderer mutation. Recording is not publishing:
|
|
41
|
+
// every other write reaches Fabric because the framework's own commit follows it, but a change
|
|
42
|
+
// driven by a native event has no such follow-up (setNodePressed is the first such caller).
|
|
43
|
+
// Queued rather than committed on the spot: several writes in one task publish together at the
|
|
44
|
+
// microtask boundary, one commit per surface.
|
|
61
45
|
export function requestCommitFor(node) {
|
|
62
46
|
const record = committedRecordOf(node);
|
|
63
47
|
if (record === undefined) {
|
|
@@ -66,14 +50,9 @@ export function requestCommitFor(node) {
|
|
|
66
50
|
}
|
|
67
51
|
requestCommitForRoot(record.rootTag);
|
|
68
52
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
* `committedRecordOf` crosses the host boundary, and `setNativeProps` fetches the record two lines
|
|
73
|
-
* before it calls this — so asking again was two crossings for one fact, on the path an
|
|
74
|
-
* `AnimatedProps` leaf runs once per frame per animated node. Measured at 2 of the 3 crossings a
|
|
75
|
-
* frame made (`__tests__/animated-frame-cost.test.ts`).
|
|
76
|
-
*/
|
|
53
|
+
// The same request, for a caller that already holds the node's record. committedRecordOf crosses
|
|
54
|
+
// the host boundary, and setNativeProps fetches the record two lines before it calls this — asking
|
|
55
|
+
// again would be a second crossing for one fact, on a path an AnimatedProps leaf runs per frame.
|
|
77
56
|
function requestCommitForRoot(rootTag) {
|
|
78
57
|
pendingRoots.add(rootTag);
|
|
79
58
|
if (!flushScheduled) {
|
|
@@ -92,20 +71,15 @@ export function disposeRoot(rootTag) {
|
|
|
92
71
|
export function getNativeTag(node) {
|
|
93
72
|
return committedRecordOf(node)?.tag;
|
|
94
73
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* OPAQUE. Under the native host it is a `ShadowNode`, under a headless one whatever that host
|
|
99
|
-
* committed — see `ICommittedRecord.handle`. It used to be typed `IFabricNode`, a brand with no
|
|
100
|
-
* members, so a caller can do exactly as much with it as before.
|
|
101
|
-
*/
|
|
74
|
+
// The node's current native handle, in kind identical to React's stateNode.node. Opaque: under the
|
|
75
|
+
// native host it's a ShadowNode, under a headless one whatever that host committed (see
|
|
76
|
+
// ICommittedRecord.handle).
|
|
102
77
|
export function getNativeNode(node) {
|
|
103
78
|
return committedRecordOf(node)?.handle;
|
|
104
79
|
}
|
|
105
|
-
// Actions waiting for their node's first commit
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
// once it runs. React commits synchronously, so its actions run inline and never land here.
|
|
80
|
+
// Actions waiting for their node's first commit — an adapter wiring a call at lifecycle time can
|
|
81
|
+
// run before completeRoot under an async-batched commit, so the node has no handle yet. Each
|
|
82
|
+
// waiter retries after a commit and is dropped once it runs; React runs its actions inline.
|
|
109
83
|
const pendingCommitWaiters = new Set();
|
|
110
84
|
/** Called by the commit path once a batch has published. */
|
|
111
85
|
export function notifyCommitted() {
|
|
@@ -114,10 +88,8 @@ export function notifyCommitted() {
|
|
|
114
88
|
pendingCommitWaiters.delete(waiter);
|
|
115
89
|
}
|
|
116
90
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* after the commit that assigns one. Returns a cancel fn (drop the retry, e.g. on unmount).
|
|
120
|
-
*/
|
|
91
|
+
// Run `action` once `node` has a committed Fabric handle — immediately if it already does, else
|
|
92
|
+
// after the commit that assigns one. Returns a cancel fn to drop the retry (e.g. on unmount).
|
|
121
93
|
export function whenCommitted(node, action) {
|
|
122
94
|
const attempt = () => {
|
|
123
95
|
if (committedRecordOf(node) === undefined)
|
|
@@ -175,13 +147,9 @@ export function measureLayout(node, relativeTo, onSuccess, onFail = () => { }) {
|
|
|
175
147
|
}
|
|
176
148
|
treeHost()?.measureLayout(node, relativeTo, onFail, onSuccess);
|
|
177
149
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* Through the HOST like the five above it, never through the Fabric slot: under the native tree
|
|
182
|
-
* host the committed handle is our placeholder, and `nativeFabricUIManager.setIsJSResponder` unwraps
|
|
183
|
-
* only a `ShadowNode` reference it minted itself.
|
|
184
|
-
*/
|
|
150
|
+
// Tell native that JS has taken the gesture, or given it up. Through the host like the five above
|
|
151
|
+
// it, never through the Fabric slot: under the native tree host the committed handle is our
|
|
152
|
+
// placeholder, and nativeFabricUIManager's own version unwraps only a ShadowNode it minted itself.
|
|
185
153
|
export function setIsJSResponder(node, isResponder, blockNativeResponder) {
|
|
186
154
|
if (committedRecordOf(node) === undefined) {
|
|
187
155
|
dlog('setIsJSResponder skipped: node not committed');
|
|
@@ -194,22 +162,12 @@ export function propOf(node, key) {
|
|
|
194
162
|
flushOps();
|
|
195
163
|
return treeHost()?.propOf(node, key);
|
|
196
164
|
}
|
|
197
|
-
// Per-frame prop write for the JS-driven Animated path
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
// its back or its next diff would be computed against a stale base. The write is an ordinary op now
|
|
204
|
-
// — the commit that carries it updates that record itself. Telling the host EARLY was strictly
|
|
205
|
-
// worse than not telling it: `noteNativePropsBypass` folded the values into `committedProps` before
|
|
206
|
-
// the commit ran, so the diff found nothing to send and the write never reached Fabric at all.
|
|
207
|
-
//
|
|
208
|
-
// One correction survives, because it is about the ENGINE's own state rather than the host's:
|
|
209
|
-
// `parts.published` is cleared, so `pushClassStyle`'s guard cannot turn away the re-push that
|
|
210
|
-
// RESTORES the declarative style this frame just overwrote. The guard is exact, and an app handing
|
|
211
|
-
// over a hoisted style constant (StyleSheet.create, a module-level object) would otherwise never
|
|
212
|
-
// get it back.
|
|
165
|
+
// Per-frame prop write for the JS-driven Animated path — RN flushes an animation frame with an
|
|
166
|
+
// in-place instance.setNativeProps(...); Fabric is persistent, so a frame is one scoped commit
|
|
167
|
+
// instead, an ordinary op the commit that carries it accounts for on its own.
|
|
168
|
+
// One correction survives, about the engine's own state: parts.published is cleared, so
|
|
169
|
+
// pushClassStyle's guard can't turn away the re-push that restores the declarative style this
|
|
170
|
+
// frame just overwrote — otherwise a hoisted style constant would never come back.
|
|
213
171
|
export function setNativeProps(node, partial) {
|
|
214
172
|
const record = committedRecordOf(node);
|
|
215
173
|
if (record === undefined) {
|
|
@@ -229,30 +187,22 @@ export function setNativeProps(node, partial) {
|
|
|
229
187
|
}
|
|
230
188
|
else {
|
|
231
189
|
merged[key] = value;
|
|
232
|
-
// This path used to owe the aria gate that `setProp` carried, since it bypasses it. There is
|
|
233
|
-
// no gate to owe any more: the fold is the device's rule and it recomputes presence from the
|
|
234
|
-
// bag. See `node.ts`'s note at the old choke point.
|
|
235
190
|
}
|
|
236
191
|
}
|
|
237
192
|
for (const [key, value] of Object.entries(merged)) {
|
|
238
|
-
//
|
|
239
|
-
// function reaches the wire from here and nowhere else
|
|
240
|
-
// every key its leaf holds,
|
|
193
|
+
// writeProp, not recordSetProp: this is the path with no routeProp in front of it, so a
|
|
194
|
+
// function reaches the wire from here and nowhere else (AnimatedProps.__getValue() copies
|
|
195
|
+
// every key its leaf holds, panHandlers included).
|
|
241
196
|
writeProp(node, key, value);
|
|
242
197
|
}
|
|
243
198
|
if (merged.style !== undefined)
|
|
244
199
|
clearPublishedStyle(node);
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
// string and the `Object.keys` array it builds are 60 allocations a second per node — times the
|
|
248
|
-
// list, for a list that animates its rows. F-61 dismissed 347 ungated arguments because none of
|
|
249
|
-
// them was on a path that repeats; this one is, and F-61 had it in its own in-loop list and
|
|
250
|
-
// priced it as "runs once".
|
|
200
|
+
// Gated: an AnimatedProps leaf calls this once per frame per animated node, so the template
|
|
201
|
+
// string and Object.keys array below would otherwise allocate 60 times a second per node.
|
|
251
202
|
if (isDebug()) {
|
|
252
203
|
dlog(`setNativeProps root=${record.rootTag} tag=${record.tag} keys=${Object.keys(partial)}`);
|
|
253
204
|
}
|
|
254
205
|
// Queued, not committed: every write made in this task publishes together at the microtask
|
|
255
|
-
// boundary.
|
|
256
|
-
// fact this function fetched four lines up.
|
|
206
|
+
// boundary. The record is already in hand, so this avoids a second crossing for the same fact.
|
|
257
207
|
requestCommitForRoot(record.rootTag);
|
|
258
208
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -65,6 +65,7 @@ export { imageStatics } from './image-loader';
|
|
|
65
65
|
export type { IImageStatics, IImageSize, IImageCacheStatus, } from './image-loader';
|
|
66
66
|
export { setImageSourceResolver, resolveImageSource, } from './image-source-resolver';
|
|
67
67
|
export type { IImageSource, IImageSourceProp } from './image-source-resolver';
|
|
68
|
+
export { setAssetSourceResolver, resolveAssetSource, } from './asset-source-resolver';
|
|
68
69
|
export { ActionSheetIOS } from './action-sheet-ios';
|
|
69
70
|
export type { IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, } from './action-sheet-ios';
|
|
70
71
|
export { Linking } from './linking';
|
|
@@ -90,8 +91,8 @@ export type { IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, } from './ke
|
|
|
90
91
|
export { currentlyFocusedInput, setInputFocused, setInputBlurred, blurTextInput, focusTextInput, } from './text-input-state';
|
|
91
92
|
export { LayoutAnimation } from './layout-animation';
|
|
92
93
|
export type { ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, ILayoutAnimationTypes, ILayoutAnimationProperties, } from './layout-animation';
|
|
93
|
-
export { BackHandler } from './back-handler';
|
|
94
|
-
export type { IBackPressEventName, IBackPressHandler } from './back-handler';
|
|
94
|
+
export { BackHandler, installBackHandler } from './back-handler';
|
|
95
|
+
export type { IBackPressEventName, IBackPressHandler, IHardwareBackPressEvent, } from './back-handler';
|
|
95
96
|
export { PermissionsAndroid, PERMISSIONS, RESULTS, } from './permissions-android';
|
|
96
97
|
export type { IPermission, IPermissionStatus, IRationale, } from './permissions-android';
|
|
97
98
|
export { AccessibilityInfo } from './accessibility-info';
|