@symbiote-native/engine 0.3.0 → 0.5.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 +7 -1
- package/build/accessibility-props.d.ts +20 -0
- package/build/accessibility-props.js +213 -0
- package/build/animated/graph.d.ts +2 -0
- package/build/animated/graph.js +14 -0
- package/build/animated/host-binding.d.ts +39 -0
- package/build/animated/host-binding.js +263 -0
- package/build/animated/leaf-lifecycle.js +10 -22
- package/build/app-registry/index.d.ts +1 -0
- package/build/app-registry/index.js +15 -5
- package/build/commit.d.ts +22 -0
- package/build/commit.js +509 -78
- package/build/debug.js +10 -4
- package/build/events/index.js +172 -87
- package/build/fabric-props.js +179 -10
- package/build/fabric.d.ts +11 -3
- package/build/fabric.js +19 -2
- package/build/host-behavior.d.ts +84 -0
- package/build/host-behavior.js +374 -0
- package/build/host-instance/index.d.ts +2 -10
- package/build/host-instance/index.js +13 -46
- package/build/index.d.ts +7 -1
- package/build/index.js +23 -4
- package/build/node.d.ts +115 -5
- package/build/node.js +737 -70
- package/build/pan-responder/index.d.ts +2 -2
- package/build/pan-responder/index.js +10 -4
- package/build/style-registry/index.d.ts +2 -0
- package/build/style-registry/index.js +79 -0
- package/build/styles.d.ts +5 -1
- package/build/surface.js +21 -4
- package/build/view-config.js +6 -0
- package/package.json +12 -2
package/build/fabric.d.ts
CHANGED
|
@@ -13,8 +13,9 @@ export type IMeasureLayoutOnSuccess = (left: number, top: number, width: number,
|
|
|
13
13
|
export interface IFabricSlot {
|
|
14
14
|
createNode(reactTag: number, viewName: string, rootTag: IRootTag, props: IFabricProps, instanceHandle: unknown): IFabricNode;
|
|
15
15
|
cloneNodeWithNewProps(node: IFabricNode, newProps: IFabricProps): IFabricNode;
|
|
16
|
-
cloneNodeWithNewChildren(node: IFabricNode): IFabricNode;
|
|
17
|
-
cloneNodeWithNewChildrenAndProps(node: IFabricNode, newProps: IFabricProps): IFabricNode;
|
|
16
|
+
cloneNodeWithNewChildren(node: IFabricNode, children?: readonly IFabricNode[]): IFabricNode;
|
|
17
|
+
cloneNodeWithNewChildrenAndProps(node: IFabricNode, newProps: IFabricProps, children?: readonly IFabricNode[]): IFabricNode;
|
|
18
|
+
supportsCloneWithChildren: boolean;
|
|
18
19
|
createChildSet(rootTag: IRootTag): IFabricChildSet;
|
|
19
20
|
appendChild(parent: IFabricNode, child: IFabricNode): IFabricNode;
|
|
20
21
|
appendChildToSet(childSet: IFabricChildSet, child: IFabricNode): void;
|
|
@@ -22,11 +23,18 @@ export interface IFabricSlot {
|
|
|
22
23
|
registerEventHandler(handler: IFabricEventHandler): void;
|
|
23
24
|
dispatchCommand(node: IFabricNode, commandName: string, args: readonly unknown[]): void;
|
|
24
25
|
sendAccessibilityEvent(node: IFabricNode, eventType: string): void;
|
|
26
|
+
setIsJSResponder(node: IFabricNode, isResponder: boolean, blockNativeResponder: boolean): void;
|
|
25
27
|
measure(node: IFabricNode, callback: IMeasureOnSuccess): void;
|
|
26
28
|
measureInWindow(node: IFabricNode, callback: IMeasureInWindowOnSuccess): void;
|
|
27
29
|
measureLayout(node: IFabricNode, relativeToNode: IFabricNode, onFail: () => void, onSuccess: IMeasureLayoutOnSuccess): void;
|
|
28
30
|
}
|
|
31
|
+
interface IFabricHost extends Omit<IFabricSlot, 'cloneNodeWithNewChildren' | 'cloneNodeWithNewChildrenAndProps' | 'supportsCloneWithChildren'> {
|
|
32
|
+
cloneNodeWithNewChildren(node: IFabricNode, children?: readonly IFabricNode[]): IFabricNode;
|
|
33
|
+
cloneNodeWithNewChildrenAndProps(node: IFabricNode, newProps: IFabricProps): IFabricNode;
|
|
34
|
+
cloneNodeWithNewChildrenAndProps(node: IFabricNode, children: readonly IFabricNode[], newProps: IFabricProps): IFabricNode;
|
|
35
|
+
}
|
|
29
36
|
declare global {
|
|
30
|
-
var nativeFabricUIManager:
|
|
37
|
+
var nativeFabricUIManager: IFabricHost | undefined;
|
|
31
38
|
}
|
|
32
39
|
export declare function getSlot(): IFabricSlot;
|
|
40
|
+
export {};
|
package/build/fabric.js
CHANGED
|
@@ -29,14 +29,24 @@ export function getSlot() {
|
|
|
29
29
|
// Optional on some hosts: read it off the live binding and feature-detect below so an
|
|
30
30
|
// older slot without it degrades to a logged no-op instead of throwing.
|
|
31
31
|
const { sendAccessibilityEvent } = host;
|
|
32
|
+
const { setIsJSResponder } = host;
|
|
32
33
|
const { measure } = host;
|
|
33
34
|
const { measureInWindow } = host;
|
|
34
35
|
const { measureLayout } = host;
|
|
35
36
|
cached = {
|
|
36
37
|
createNode: (reactTag, viewName, rootTag, props, instanceHandle) => createNode(reactTag, viewName, rootTag, props, instanceHandle),
|
|
37
38
|
cloneNodeWithNewProps: (node, newProps) => cloneNodeWithNewProps(node, newProps),
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// The host's 3-arg form is (node, children, props); ours keeps props second.
|
|
40
|
+
cloneNodeWithNewChildren: (node, children) => children === undefined
|
|
41
|
+
? cloneNodeWithNewChildren(node)
|
|
42
|
+
: cloneNodeWithNewChildren(node, children),
|
|
43
|
+
cloneNodeWithNewChildrenAndProps: (node, newProps, children) => children === undefined
|
|
44
|
+
? cloneNodeWithNewChildrenAndProps(node, newProps)
|
|
45
|
+
: cloneNodeWithNewChildrenAndProps(node, children, newProps),
|
|
46
|
+
supportsCloneWithChildren: typeof cloneNodeWithNewChildren === 'function' &&
|
|
47
|
+
cloneNodeWithNewChildren.length >= 2 &&
|
|
48
|
+
typeof cloneNodeWithNewChildrenAndProps === 'function' &&
|
|
49
|
+
cloneNodeWithNewChildrenAndProps.length >= 3,
|
|
40
50
|
createChildSet: rootTag => createChildSet(rootTag),
|
|
41
51
|
appendChild: (parent, child) => appendChild(parent, child),
|
|
42
52
|
appendChildToSet: (childSet, child) => appendChildToSet(childSet, child),
|
|
@@ -50,6 +60,13 @@ export function getSlot() {
|
|
|
50
60
|
}
|
|
51
61
|
sendAccessibilityEvent(node, eventType);
|
|
52
62
|
},
|
|
63
|
+
setIsJSResponder: (node, isResponder, blockNativeResponder) => {
|
|
64
|
+
if (typeof setIsJSResponder !== 'function') {
|
|
65
|
+
dlog('setIsJSResponder -> host lacks the method (no-op)');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setIsJSResponder(node, isResponder, blockNativeResponder);
|
|
69
|
+
},
|
|
53
70
|
measure: (node, callback) => measure(node, callback),
|
|
54
71
|
measureInWindow: (node, callback) => measureInWindow(node, callback),
|
|
55
72
|
measureLayout: (node, relativeToNode, onFail, onSuccess) => measureLayout(node, relativeToNode, onFail, onSuccess),
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ISymbioteNode } from './node';
|
|
2
|
+
/**
|
|
3
|
+
* A pure props -> props mapping a behavior applies on the way to the Fabric payload.
|
|
4
|
+
*
|
|
5
|
+
* It exists because a lowered element has no component body, and a wrapper's body is where the
|
|
6
|
+
* per-primitive prop FOLDS live — TextInput's W3C aliases (`inputMode` -> `keyboardType`,
|
|
7
|
+
* `readOnly` -> `editable`), Pressable's `disabled` -> `accessibilityState`. Every one of those was
|
|
8
|
+
* silently dropped the moment the primitive lowered: the raw alias reached Fabric as a key no
|
|
9
|
+
* ViewConfig declares, so nothing threw and nothing rendered differently in a headless test —
|
|
10
|
+
* only the device showed a numeric keyboard that never appeared.
|
|
11
|
+
*
|
|
12
|
+
* NOT a hook on `setProp`, for the same reason `afterCommit` is not: `setProp` is the hottest path
|
|
13
|
+
* in the engine. This runs once per node per payload build, and only for a node whose behavior
|
|
14
|
+
* supplied one.
|
|
15
|
+
*
|
|
16
|
+
* MUST be pure and MUST NOT mutate its input — `node.props` is the live bag, and the folds beside
|
|
17
|
+
* this one return their input by identity when there is nothing to do.
|
|
18
|
+
*/
|
|
19
|
+
export type IPayloadFold = (props: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
20
|
+
export type IClaimMode = 'beside' | 'wrap';
|
|
21
|
+
export interface IHostBehavior {
|
|
22
|
+
readonly ownedListeners?: readonly string[];
|
|
23
|
+
readonly slotProps?: Readonly<Record<string, string>>;
|
|
24
|
+
readonly slotPropsExcept?: readonly string[];
|
|
25
|
+
readonly slotTakesNoChildren?: boolean;
|
|
26
|
+
readonly slotDerived?: readonly string[];
|
|
27
|
+
readonly claimedChildren?: Readonly<Record<string, IClaimMode>>;
|
|
28
|
+
onChildInserted?(node: ISymbioteNode, child: ISymbioteNode): void;
|
|
29
|
+
buildStructure?(node: ISymbioteNode): ISymbioteNode | undefined;
|
|
30
|
+
attach(node: ISymbioteNode): void;
|
|
31
|
+
attachAfterCommit?(node: ISymbioteNode): void;
|
|
32
|
+
onWrapChange?(owner: ISymbioteNode, wrapper: ISymbioteNode | undefined): void;
|
|
33
|
+
onOwnedListenerChange?(node: ISymbioteNode, name: string, wired: boolean): void;
|
|
34
|
+
afterCommit?(node: ISymbioteNode): void;
|
|
35
|
+
detach(node: ISymbioteNode): void;
|
|
36
|
+
readonly foldPayload?: IPayloadFold;
|
|
37
|
+
}
|
|
38
|
+
export declare function registerHostBehavior(component: string, behavior: IHostBehavior): void;
|
|
39
|
+
export declare function hostBehaviorFor(tag: string): IHostBehavior | undefined;
|
|
40
|
+
export declare function hasHostBehaviors(): boolean;
|
|
41
|
+
export declare function slotPropNameFor(node: ISymbioteNode, key: string): string | undefined;
|
|
42
|
+
export declare function slotTakesChildren(node: ISymbioteNode): boolean;
|
|
43
|
+
export declare function addDerivedNode(owner: ISymbioteNode, node: ISymbioteNode): void;
|
|
44
|
+
export declare function derivedNodesOf(owner: ISymbioteNode): readonly ISymbioteNode[] | undefined;
|
|
45
|
+
export declare function notifyOwnedListenerChange(node: ISymbioteNode, name: string, wired: boolean): void;
|
|
46
|
+
export declare function notifyChildInserted(node: ISymbioteNode, child: ISymbioteNode): void;
|
|
47
|
+
export declare function notifyWrapChange(owner: ISymbioteNode, wrapper: ISymbioteNode | undefined): void;
|
|
48
|
+
export declare function claimModeFor(node: ISymbioteNode, component: string): IClaimMode | undefined;
|
|
49
|
+
export declare function slotDerivesFrom(node: ISymbioteNode, key: string): boolean;
|
|
50
|
+
export declare function ownsListener(node: ISymbioteNode, name: string): boolean;
|
|
51
|
+
export declare function stashAppListener(node: ISymbioteNode, name: string, listener: unknown): void;
|
|
52
|
+
export declare function appListenerFor(node: ISymbioteNode, name: string): unknown;
|
|
53
|
+
export declare function attachHostBehavior(node: ISymbioteNode, tag: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Run the deferred half of every behavior whose node has now been committed. Called from the commit
|
|
56
|
+
* path immediately after `completeRoot`, where fresh Fabric tags have just been assigned.
|
|
57
|
+
*
|
|
58
|
+
* `isCommitted` is passed in for the same no-cycle reason — `committedOf` lives in `commit.ts`. A
|
|
59
|
+
* still-uncommitted node stays in the set: a create superseded before it ever reached Fabric waits
|
|
60
|
+
* for the commit that lands it, and `detachSubtree` drops it if that commit never comes.
|
|
61
|
+
*/
|
|
62
|
+
export declare function runDeferredAttaches(isCommitted: (node: ISymbioteNode) => boolean): void;
|
|
63
|
+
/**
|
|
64
|
+
* The recurring beat. Split from `runDeferredAttaches` because the two answer different questions:
|
|
65
|
+
* `attachAfterCommit` needs a FRESH FABRIC TAG, so it belongs below `completeRoot` and must not run
|
|
66
|
+
* on a commit that made no native call; `afterCommit` needs only "props were published", which a
|
|
67
|
+
* no-op commit satisfies just as well.
|
|
68
|
+
*
|
|
69
|
+
* Keeping them together made `afterCommit` unreachable for exactly the props a behavior owns: a fold
|
|
70
|
+
* that STRIPS a prop makes its own commit byte-identical, `commitContainer` returns above the drain,
|
|
71
|
+
* and the hook never sees the flip. TouchableOpacity's re-settle on `disabled` is the case
|
|
72
|
+
* (`disabled` is a MACHINE_ONLY key), Button's `title`/`color` the other.
|
|
73
|
+
*
|
|
74
|
+
* SETUP STILL RUNS BEFORE THE BEAT on the first commit, and the order is load-bearing: a node
|
|
75
|
+
* carrying both hooks has `attachAfterCommit` seed the mirrors `afterCommit` compares against. The
|
|
76
|
+
* caller preserves it by calling this AFTER `runDeferredAttaches` on the changed path — the no-op
|
|
77
|
+
* path has no setup to run, since a node with no Fabric tag has not committed at all.
|
|
78
|
+
*/
|
|
79
|
+
export declare function runCommittedHooks(isCommitted: (node: ISymbioteNode) => boolean): void;
|
|
80
|
+
export declare function markDetachCandidate(node: ISymbioteNode): void;
|
|
81
|
+
export declare function sweepDetachedBehaviors(topLevel: readonly ISymbioteNode[], onDetached: (node: ISymbioteNode) => void): void;
|
|
82
|
+
export declare function teardownSubtree(node: ISymbioteNode, onDetached: (node: ISymbioteNode) => void): void;
|
|
83
|
+
export declare function reattachHostBehaviors(node: ISymbioteNode): void;
|
|
84
|
+
export declare function clearHostBehaviors(): void;
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
// Per-TAG behavior on an engine node — the seam that lets a primitive's state machine live BELOW
|
|
2
|
+
// the framework instead of inside a framework component.
|
|
3
|
+
//
|
|
4
|
+
// WHY IT EXISTS. Vue, Svelte, Solid and Angular all optimize element subtrees and stop at a
|
|
5
|
+
// component boundary, so a primitive shipped as a component is charged a per-instance price in
|
|
6
|
+
// each framework's own currency (an instance, a props Proxy, anchor nodes, an LView). A primitive
|
|
7
|
+
// whose state the TEMPLATE never reads does not need to be a component at all — its machine only
|
|
8
|
+
// needs a per-node home, and the engine node is one. `.claude/rules/host-primitive-tier.md` has
|
|
9
|
+
// the tier model; this module is the tier-2 half of it.
|
|
10
|
+
//
|
|
11
|
+
// WHY A REGISTRY RATHER THAN A DIRECT IMPORT. `@symbiote-native/components` depends on
|
|
12
|
+
// `@symbiote-native/engine`, never the reverse, so the engine cannot import `createPressHandlers`
|
|
13
|
+
// and friends. CLAUDE.md's preferred answer to a registration problem — delete the indirection —
|
|
14
|
+
// is therefore unavailable here; the inversion is forced, not chosen.
|
|
15
|
+
//
|
|
16
|
+
// WHICH MEANS THE REGISTRATION ITSELF IS THE HAZARD, and it is the one CLAUDE.md spells out:
|
|
17
|
+
// Metro turns on `inlineRequires` for production only, moving a `require` down to the first place
|
|
18
|
+
// its binding is used as a VALUE, and a barrel's `export { X } from './x'` compiles to a lazy
|
|
19
|
+
// getter. A module whose only job is to call `registerHostBehavior` is never named as a value, so
|
|
20
|
+
// re-exporting it from a barrel means it NEVER EVALUATES in a release build — dev is perfect,
|
|
21
|
+
// release silently has no behavior. The one shape that works is a bare side-effect import that is
|
|
22
|
+
// never re-exported from that barrel, the pattern in
|
|
23
|
+
// `packages/slider/src/{react,vue,svelte,angular}/index.ts`:
|
|
24
|
+
//
|
|
25
|
+
// import '../register'; // in the adapter entry — NOT `export * from '../register'`
|
|
26
|
+
//
|
|
27
|
+
// `registerHostBehavior` emits a `dlog` precisely so `DEBUG=1` answers "did my registration run at
|
|
28
|
+
// all" before anyone starts debugging the behavior itself.
|
|
29
|
+
import { dlog } from './debug.js';
|
|
30
|
+
const behaviors = new Map();
|
|
31
|
+
// Nodes that `removeChild` unlinked and that may or may not be coming back. See
|
|
32
|
+
// `sweepDetachedBehaviors` for why the answer is not known until commit.
|
|
33
|
+
const detachCandidates = new Set();
|
|
34
|
+
// Nodes the sweep has torn down. A torn-down node can still be re-inserted — see
|
|
35
|
+
// `reattachHostBehaviors` — and this is what tells an insert whether it must walk at all, so the
|
|
36
|
+
// common case (building a fresh tree) never walks anything.
|
|
37
|
+
const tornDown = new WeakSet();
|
|
38
|
+
// The behavior a node actually got, remembered from its one and only registry lookup.
|
|
39
|
+
//
|
|
40
|
+
// THE REGISTRY IS KEYED BY INTRINSIC TAG AND THE NODE IS NOT. `node.component` is the FABRIC view
|
|
41
|
+
// name: every adapter resolves the tag through `descriptorFor` before calling `createElement`, so
|
|
42
|
+
// `view` arrives as `RCTView`. Keying the registry by Fabric name instead is not an
|
|
43
|
+
// option — a pressable resolves to `RCTView` like any other view, so the press machine would
|
|
44
|
+
// attach to every plain `View` in the app. So the tag alphabet is used EXACTLY ONCE, at
|
|
45
|
+
// `attachHostBehavior`, where the caller still holds it; every later lookup reads this map.
|
|
46
|
+
//
|
|
47
|
+
// Found by a peer session probing the installed shape, not by a unit test: the tests built their
|
|
48
|
+
// subject with `createElement(PRESSABLE_TAG)`, which passes the tag AS the Fabric name and makes
|
|
49
|
+
// the key match by accident. No adapter constructs a node that way, so the registration could
|
|
50
|
+
// never have fired in an app while all six break-tests kept failing correctly on their own axes.
|
|
51
|
+
const attached = new WeakMap();
|
|
52
|
+
// The gate. `createElement` and `removeChild` are the two hottest paths in the engine (9 002 and
|
|
53
|
+
// ~1 000 calls on one benchmark row set), so neither may pay a Set insert for a feature no app
|
|
54
|
+
// uses yet. While this is false both paths cost one boolean read, the same discipline as `isDebug`.
|
|
55
|
+
let hasBehaviors = false;
|
|
56
|
+
export function registerHostBehavior(component, behavior) {
|
|
57
|
+
dlog(`registerHostBehavior: ${component}`);
|
|
58
|
+
behaviors.set(component, behavior);
|
|
59
|
+
hasBehaviors = true;
|
|
60
|
+
}
|
|
61
|
+
// Read access to the registry, so an audit can DERIVE what a behavior owns instead of restating it.
|
|
62
|
+
// The one that matters: a name in `ownedListeners` is only reachable if `routeProp` also treats it
|
|
63
|
+
// as a registered event — otherwise the app's callback lands in `node.props` and the machine, which
|
|
64
|
+
// reads the stash, never sees it. That set difference is a test
|
|
65
|
+
// (`core/components/src/behaviors/owned-listeners-are-routable.test.ts`) and it needs this to stay
|
|
66
|
+
// derived rather than becoming another hand-written list.
|
|
67
|
+
export function hostBehaviorFor(tag) {
|
|
68
|
+
return behaviors.get(tag);
|
|
69
|
+
}
|
|
70
|
+
export function hasHostBehaviors() {
|
|
71
|
+
return hasBehaviors;
|
|
72
|
+
}
|
|
73
|
+
// What an owner prop is called on the slot, or undefined when it belongs to the owner after all.
|
|
74
|
+
//
|
|
75
|
+
// Called from `routeProp` only for a node that HAS a slot (`node.childHost !== undefined`), which
|
|
76
|
+
// is what keeps a WeakMap probe off the hot path: every other node is turned away by one field
|
|
77
|
+
// read, the same gate `payloadFold` uses one layer down.
|
|
78
|
+
export function slotPropNameFor(node, key) {
|
|
79
|
+
const behavior = attached.get(node);
|
|
80
|
+
if (behavior === undefined)
|
|
81
|
+
return undefined;
|
|
82
|
+
const named = behavior.slotProps?.[key];
|
|
83
|
+
if (named !== undefined)
|
|
84
|
+
return named;
|
|
85
|
+
const except = behavior.slotPropsExcept;
|
|
86
|
+
if (except !== undefined && !except.includes(key))
|
|
87
|
+
return key;
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
// Does this owner's slot host the app's children, or is it a built sibling they land beside? See
|
|
91
|
+
// `slotTakesNoChildren`. Same `node.childHost` gate as every other probe here: the two callers ask
|
|
92
|
+
// only after the field said there is a slot at all.
|
|
93
|
+
export function slotTakesChildren(node) {
|
|
94
|
+
return attached.get(node)?.slotTakesNoChildren !== true;
|
|
95
|
+
}
|
|
96
|
+
// Nodes a behavior built that are NOT the slot, and whose payloads derive from the owner's props.
|
|
97
|
+
//
|
|
98
|
+
// `slotDerived` marks `node.childHost` and nothing else, which is one hop — enough for ScrollView,
|
|
99
|
+
// whose only derived node IS the slot, and not enough for a primitive whose `buildStructure` builds
|
|
100
|
+
// a chain. Button builds view > text > raw text and folds two of them from the same three owner
|
|
101
|
+
// props; without this the deeper nodes freeze at their mount values, and the workaround is to write
|
|
102
|
+
// them from inside a fold whose contract says it MUST be pure.
|
|
103
|
+
//
|
|
104
|
+
// A WeakMap rather than a field, for the reason `stashed` is one: this exists only for the handful
|
|
105
|
+
// of nodes a composed behavior built, and a field costs a shape transition on every node in every
|
|
106
|
+
// app. It is read only inside the `slotDerived` branch, which has already paid a WeakMap probe.
|
|
107
|
+
const derived = new WeakMap();
|
|
108
|
+
// Called from `buildStructure` for each node past the slot. Not idempotent-checked: structure is
|
|
109
|
+
// built exactly once (`attachHostBehavior`, never `reattachSubtree`), so a second call would be a
|
|
110
|
+
// bug worth seeing rather than one worth absorbing.
|
|
111
|
+
export function addDerivedNode(owner, node) {
|
|
112
|
+
const existing = derived.get(owner);
|
|
113
|
+
if (existing === undefined)
|
|
114
|
+
derived.set(owner, [node]);
|
|
115
|
+
else
|
|
116
|
+
existing.push(node);
|
|
117
|
+
}
|
|
118
|
+
export function derivedNodesOf(owner) {
|
|
119
|
+
return derived.get(owner);
|
|
120
|
+
}
|
|
121
|
+
// Called from `setEventListener` on a PRESENCE flip of an owned name, and only there — the caller
|
|
122
|
+
// has already established that this node owns the name, so the WeakMap probe is one it just paid.
|
|
123
|
+
export function notifyOwnedListenerChange(node, name, wired) {
|
|
124
|
+
attached.get(node)?.onOwnedListenerChange?.(node, name, wired);
|
|
125
|
+
}
|
|
126
|
+
// Called from the two inserts once the child is in place. See `onChildInserted`.
|
|
127
|
+
export function notifyChildInserted(node, child) {
|
|
128
|
+
attached.get(node)?.onChildInserted?.(node, child);
|
|
129
|
+
}
|
|
130
|
+
// Called from the two structural entry points when a wrap claim lands or leaves. See
|
|
131
|
+
// `onWrapChange`.
|
|
132
|
+
export function notifyWrapChange(owner, wrapper) {
|
|
133
|
+
attached.get(owner)?.onWrapChange?.(owner, wrapper);
|
|
134
|
+
}
|
|
135
|
+
// What this owner does with a child of that Fabric component, or undefined when it does not claim
|
|
136
|
+
// it at all. See `claimedChildren`.
|
|
137
|
+
export function claimModeFor(node, component) {
|
|
138
|
+
return attached.get(node)?.claimedChildren?.[component];
|
|
139
|
+
}
|
|
140
|
+
// Does this owner key feed the slot's payload? See `slotDerived`. Same `node.childHost` gate as
|
|
141
|
+
// above keeps the WeakMap probe off every node that has no slot.
|
|
142
|
+
export function slotDerivesFrom(node, key) {
|
|
143
|
+
return attached.get(node)?.slotDerived?.includes(key) === true;
|
|
144
|
+
}
|
|
145
|
+
// The app's listeners for names a behavior owns, per node. Not on the node: this exists only for
|
|
146
|
+
// nodes carrying a behavior, and adding a field for it would pay a shape transition on every node
|
|
147
|
+
// in every app for a feature almost none of them use.
|
|
148
|
+
const stashed = new WeakMap();
|
|
149
|
+
// Takes the NODE, not a component string: the caller (`setEventListener`) has only the Fabric name
|
|
150
|
+
// by then, which is not the registry's alphabet. Reads the same map `attachHostBehavior` wrote.
|
|
151
|
+
export function ownsListener(node, name) {
|
|
152
|
+
return attached.get(node)?.ownedListeners?.includes(name) === true;
|
|
153
|
+
}
|
|
154
|
+
export function stashAppListener(node, name, listener) {
|
|
155
|
+
let bag = stashed.get(node);
|
|
156
|
+
if (bag === undefined) {
|
|
157
|
+
bag = new Map();
|
|
158
|
+
stashed.set(node, bag);
|
|
159
|
+
}
|
|
160
|
+
if (listener === undefined)
|
|
161
|
+
bag.delete(name);
|
|
162
|
+
else
|
|
163
|
+
bag.set(name, listener);
|
|
164
|
+
}
|
|
165
|
+
// What the app wrote for an owned event name — the behavior's OUTPUT target. Undefined when the
|
|
166
|
+
// app wired nothing, which is an ordinary case, not an error.
|
|
167
|
+
export function appListenerFor(node, name) {
|
|
168
|
+
return stashed.get(node)?.get(name);
|
|
169
|
+
}
|
|
170
|
+
// `tag` is the INTRINSIC tag the adapter started from, not the resolved Fabric name it put on the
|
|
171
|
+
// node. Defaulted to `node.component` so an adapter that has not been taught to pass it keeps
|
|
172
|
+
// working for a behavior registered under a Fabric name — no adapter registers one, so in practice
|
|
173
|
+
// the default simply never matches and costs one failed lookup.
|
|
174
|
+
export function attachHostBehavior(node, tag) {
|
|
175
|
+
const behavior = behaviors.get(tag);
|
|
176
|
+
if (behavior === undefined)
|
|
177
|
+
return;
|
|
178
|
+
attached.set(node, behavior);
|
|
179
|
+
// A field rather than a lookup at payload-build time: `fabricProps` runs per node per commit and
|
|
180
|
+
// must not pay a Map probe to discover that almost nothing has a fold.
|
|
181
|
+
node.payloadFold = behavior.foldPayload;
|
|
182
|
+
// Shape before runtime: `attach` may want to read `node.childHost`, and nothing in `attach`'s
|
|
183
|
+
// contract depends on the node being childless. Deliberately NOT repeated in `reattachSubtree` —
|
|
184
|
+
// see `buildStructure`.
|
|
185
|
+
if (behavior.buildStructure !== undefined) {
|
|
186
|
+
node.childHost = behavior.buildStructure(node);
|
|
187
|
+
}
|
|
188
|
+
behavior.attach(node);
|
|
189
|
+
if (behavior.attachAfterCommit !== undefined)
|
|
190
|
+
awaitingCommit.add(node);
|
|
191
|
+
if (behavior.afterCommit !== undefined)
|
|
192
|
+
committedEachTime.add(node);
|
|
193
|
+
}
|
|
194
|
+
// Nodes whose behavior declared `afterCommit`. Separate from `awaitingCommit` because the two have
|
|
195
|
+
// opposite lifetimes: one empties as its nodes commit, this one holds until teardown.
|
|
196
|
+
const committedEachTime = new Set();
|
|
197
|
+
// Nodes whose behavior declared `attachAfterCommit` and whose first commit has not happened yet.
|
|
198
|
+
//
|
|
199
|
+
// A plain Set rather than a call into `whenCommitted`: `commit.ts` already imports this module, so
|
|
200
|
+
// reaching back for it would close an import cycle. Metro's `inlineRequires` has made module
|
|
201
|
+
// evaluation order a real hazard here rather than a theoretical one (see this file's own
|
|
202
|
+
// registration comment), so the dependency stays one-directional and commit DRAINS this instead.
|
|
203
|
+
const awaitingCommit = new Set();
|
|
204
|
+
/**
|
|
205
|
+
* Run the deferred half of every behavior whose node has now been committed. Called from the commit
|
|
206
|
+
* path immediately after `completeRoot`, where fresh Fabric tags have just been assigned.
|
|
207
|
+
*
|
|
208
|
+
* `isCommitted` is passed in for the same no-cycle reason — `committedOf` lives in `commit.ts`. A
|
|
209
|
+
* still-uncommitted node stays in the set: a create superseded before it ever reached Fabric waits
|
|
210
|
+
* for the commit that lands it, and `detachSubtree` drops it if that commit never comes.
|
|
211
|
+
*/
|
|
212
|
+
export function runDeferredAttaches(isCommitted) {
|
|
213
|
+
// The gate: an app registering no behavior pays one Set-size read per commit, matching the
|
|
214
|
+
// discipline `hasBehaviors` sets for `createElement`.
|
|
215
|
+
if (awaitingCommit.size === 0)
|
|
216
|
+
return;
|
|
217
|
+
for (const node of awaitingCommit) {
|
|
218
|
+
if (!isCommitted(node))
|
|
219
|
+
continue;
|
|
220
|
+
awaitingCommit.delete(node);
|
|
221
|
+
attached.get(node)?.attachAfterCommit?.(node);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The recurring beat. Split from `runDeferredAttaches` because the two answer different questions:
|
|
226
|
+
* `attachAfterCommit` needs a FRESH FABRIC TAG, so it belongs below `completeRoot` and must not run
|
|
227
|
+
* on a commit that made no native call; `afterCommit` needs only "props were published", which a
|
|
228
|
+
* no-op commit satisfies just as well.
|
|
229
|
+
*
|
|
230
|
+
* Keeping them together made `afterCommit` unreachable for exactly the props a behavior owns: a fold
|
|
231
|
+
* that STRIPS a prop makes its own commit byte-identical, `commitContainer` returns above the drain,
|
|
232
|
+
* and the hook never sees the flip. TouchableOpacity's re-settle on `disabled` is the case
|
|
233
|
+
* (`disabled` is a MACHINE_ONLY key), Button's `title`/`color` the other.
|
|
234
|
+
*
|
|
235
|
+
* SETUP STILL RUNS BEFORE THE BEAT on the first commit, and the order is load-bearing: a node
|
|
236
|
+
* carrying both hooks has `attachAfterCommit` seed the mirrors `afterCommit` compares against. The
|
|
237
|
+
* caller preserves it by calling this AFTER `runDeferredAttaches` on the changed path — the no-op
|
|
238
|
+
* path has no setup to run, since a node with no Fabric tag has not committed at all.
|
|
239
|
+
*/
|
|
240
|
+
export function runCommittedHooks(isCommitted) {
|
|
241
|
+
if (committedEachTime.size === 0)
|
|
242
|
+
return;
|
|
243
|
+
for (const node of committedEachTime) {
|
|
244
|
+
if (!isCommitted(node))
|
|
245
|
+
continue;
|
|
246
|
+
attached.get(node)?.afterCommit?.(node);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// `removeChild` is NOT the destroy signal, and reading it as one is the bug this indirection
|
|
250
|
+
// exists to avoid. Engine-side it looks like one — a reorder goes through `detach` inside
|
|
251
|
+
// appendChild/insertBefore and never lands in removeChild — but a FRAMEWORK can spell a move as
|
|
252
|
+
// remove-then-reinsert. Solid does, in `solid-js/universal`: `replaceNode` (universal.cjs:186) is
|
|
253
|
+
// `insertNode` + `removeNode`, and `reconcileArrays` calls it at :157 for a node that IS in the
|
|
254
|
+
// new array and is needed at a later index. Its sibling call at :130 is guarded by
|
|
255
|
+
// `if (!map || !map.has(a[aStart]))` and removes only genuinely absent nodes — one guarded call
|
|
256
|
+
// and one not, which is why a quick read of that file says "removeChild means gone".
|
|
257
|
+
//
|
|
258
|
+
// Tearing down there would kill the machine of a node that returns alive a few operations later,
|
|
259
|
+
// in the same batch: long-press silently stops working after certain list reorders, on device
|
|
260
|
+
// only, with nothing red. So removal only nominates.
|
|
261
|
+
export function markDetachCandidate(node) {
|
|
262
|
+
detachCandidates.add(node);
|
|
263
|
+
}
|
|
264
|
+
// Commit is where a removal is CHEAPEST to distinguish from a move — a node unlinked and
|
|
265
|
+
// reinserted before the commit is back in the tree by now, which covers Solid's replaceNode. It is
|
|
266
|
+
// NOT a proof of death, and the earlier version of this comment claimed it was. Svelte parks LIVE
|
|
267
|
+
// nodes offscreen across commits and sometimes across seconds: `detachFromParent`
|
|
268
|
+
// (adapters/svelte/src/dom-shim/shim-node.ts) moves a node into a DocumentFragment that has no
|
|
269
|
+
// engine node, calls engineRemoveChild AND requestCommit, and Svelte fully intends to bring it
|
|
270
|
+
// back — from a parked `{#if}` branch, from `each.js`'s destroy_effects, and worst, from
|
|
271
|
+
// boundary.js's move_effect while a pending snippet shows, which returns when async work resolves.
|
|
272
|
+
// So a sweep can and does tear down a node that comes back, which is why `attach` is re-runnable
|
|
273
|
+
// (reattachHostBehaviors) rather than why the sweep tries to be cleverer. The machine RESTARTS on
|
|
274
|
+
// re-insert instead of surviving an arbitrary absence; a parked subtree is offscreen, so nobody is
|
|
275
|
+
// mid-gesture in it, and teardown staying unconditional means there is no leak mode.
|
|
276
|
+
//
|
|
277
|
+
// The subtree walk lives here rather than at removal, and is cheaper for it: only the nodes that
|
|
278
|
+
// actually left are walked.
|
|
279
|
+
//
|
|
280
|
+
// `onDetached` runs for EVERY node of a genuinely-removed subtree, whether or not it carries a
|
|
281
|
+
// behavior — it is how the engine's other per-node lifetime state (an Animated subscription, see
|
|
282
|
+
// `animated/host-binding.ts`) gets the same "did it really leave" answer this sweep exists to
|
|
283
|
+
// compute. Passed in for the no-cycle reason `runDeferredAttaches`' predicate is: this module must
|
|
284
|
+
// keep pointing one way, and Metro's `inlineRequires` makes that a live hazard rather than taste.
|
|
285
|
+
export function sweepDetachedBehaviors(topLevel, onDetached) {
|
|
286
|
+
if (detachCandidates.size === 0)
|
|
287
|
+
return;
|
|
288
|
+
// A surface's top-level nodes carry `parent === undefined` by design (surface.ts), and
|
|
289
|
+
// `commitChildren` re-lists them without going through appendChild — so for those the parent
|
|
290
|
+
// check alone would report a live node as gone.
|
|
291
|
+
const seen = new Set();
|
|
292
|
+
for (const node of detachCandidates) {
|
|
293
|
+
if (node.parent !== undefined || topLevel.includes(node))
|
|
294
|
+
continue;
|
|
295
|
+
detachSubtree(node, seen, onDetached);
|
|
296
|
+
}
|
|
297
|
+
detachCandidates.clear();
|
|
298
|
+
}
|
|
299
|
+
// Tear a subtree down unconditionally — the SURFACE teardown path, where there is nothing to
|
|
300
|
+
// decide: `disposeRoot` drops the root container, so every node under it has left for good whatever
|
|
301
|
+
// any framework intended.
|
|
302
|
+
//
|
|
303
|
+
// It exists because the sweep above cannot answer this. The sweep only sees nodes a `removeChild`
|
|
304
|
+
// NOMINATED, and an unmount removes nothing — the adapter drops the whole surface. So before this,
|
|
305
|
+
// `disposeRoot` touched no node at all: `committedOf` reads `node.committed`, a field on the node,
|
|
306
|
+
// so every node of a dead surface still answered `isCommitted` and stayed in `committedEachTime`,
|
|
307
|
+
// drained on every later commit anywhere in the process, with its timers still armed.
|
|
308
|
+
export function teardownSubtree(node, onDetached) {
|
|
309
|
+
detachSubtree(node, new Set(), onDetached);
|
|
310
|
+
}
|
|
311
|
+
// `seen` guards the one overlap the candidate set can contain: a removed parent and a removed
|
|
312
|
+
// descendant of it are both nominated, and without it the descendant is detached twice. `tornDown`
|
|
313
|
+
// guards the same overlap ACROSS calls — a node the sweep already released and that `disposeRoot`
|
|
314
|
+
// then walks again, which is the ordinary shape of an unmount after the framework emptied the tree.
|
|
315
|
+
function detachSubtree(node, seen, onDetached) {
|
|
316
|
+
if (seen.has(node) || tornDown.has(node))
|
|
317
|
+
return;
|
|
318
|
+
seen.add(node);
|
|
319
|
+
onDetached(node);
|
|
320
|
+
// Marked whether or not THIS node carries a behavior: the mark is what tells a later insert to
|
|
321
|
+
// walk, and the node re-inserted is usually a plain container whose DESCENDANT holds the
|
|
322
|
+
// machine. Gating the mark on `behaviors.has` made the row wrapper unmarked and the whole walk
|
|
323
|
+
// skip — the first version of the parked-node test caught exactly that.
|
|
324
|
+
tornDown.add(node);
|
|
325
|
+
// Drop a deferral the node never got to run. NO TEST CAN SEE THIS, and it is kept anyway —
|
|
326
|
+
// stated rather than left as apparent coverage. The `isCommitted` predicate in the drain already
|
|
327
|
+
// stops such a node from firing, so removing this line changes no observable behaviour; what it
|
|
328
|
+
// changes is that a node created and torn down inside one tick stays in the Set forever, holding
|
|
329
|
+
// a strong reference to a dead subtree. A leak, not a wrong result, and this file's break-test
|
|
330
|
+
// discipline correctly reports it as unfalsifiable.
|
|
331
|
+
awaitingCommit.delete(node);
|
|
332
|
+
// The recurring hook stops with the node, and unlike the deferral above this one has a visible
|
|
333
|
+
// consequence if forgotten: a torn-down node would keep being asked to reconcile props against a
|
|
334
|
+
// subtree that has left the tree, on every commit, forever.
|
|
335
|
+
committedEachTime.delete(node);
|
|
336
|
+
// The map, not the registry: by here only the Fabric name is left on the node.
|
|
337
|
+
attached.get(node)?.detach(node);
|
|
338
|
+
for (const child of node.children)
|
|
339
|
+
detachSubtree(child, seen, onDetached);
|
|
340
|
+
}
|
|
341
|
+
// Re-arms a node the sweep tore down but that the framework put back. Called from appendChild and
|
|
342
|
+
// insertBefore, and it is a WeakSet miss — no walk at all — for every node in a freshly built
|
|
343
|
+
// tree, which is the path that runs ~9 000 times per benchmark create.
|
|
344
|
+
export function reattachHostBehaviors(node) {
|
|
345
|
+
if (!tornDown.has(node))
|
|
346
|
+
return;
|
|
347
|
+
reattachSubtree(node);
|
|
348
|
+
}
|
|
349
|
+
function reattachSubtree(node) {
|
|
350
|
+
if (tornDown.has(node)) {
|
|
351
|
+
tornDown.delete(node);
|
|
352
|
+
const behavior = attached.get(node);
|
|
353
|
+
behavior?.attach(node);
|
|
354
|
+
// Re-arm the deferred half too. A parked node usually returns with its tag intact, so this
|
|
355
|
+
// fires on the next drain — but re-arming is what keeps `attach` and `attachAfterCommit` a
|
|
356
|
+
// PAIR. Restore only one and a behavior that splits its setup across the two comes back
|
|
357
|
+
// half-initialised, which is the failure this seam exists to prevent.
|
|
358
|
+
if (behavior?.attachAfterCommit !== undefined)
|
|
359
|
+
awaitingCommit.add(node);
|
|
360
|
+
if (behavior?.afterCommit !== undefined)
|
|
361
|
+
committedEachTime.add(node);
|
|
362
|
+
}
|
|
363
|
+
for (const child of node.children)
|
|
364
|
+
reattachSubtree(child);
|
|
365
|
+
}
|
|
366
|
+
// Test-only. A registry is module state, so a suite that registers a behavior leaks it into every
|
|
367
|
+
// later test in the same file unless it is cleared.
|
|
368
|
+
export function clearHostBehaviors() {
|
|
369
|
+
behaviors.clear();
|
|
370
|
+
detachCandidates.clear();
|
|
371
|
+
awaitingCommit.clear();
|
|
372
|
+
committedEachTime.clear();
|
|
373
|
+
hasBehaviors = false;
|
|
374
|
+
}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export interface IHostInstance extends ISymbioteNode {
|
|
4
|
-
measure(callback: IMeasureOnSuccess): void;
|
|
5
|
-
measureInWindow(callback: IMeasureInWindowOnSuccess): void;
|
|
6
|
-
measureLayout(relativeToNativeNode: IHostInstance | number, onSuccess: IMeasureLayoutOnSuccess, onFail?: () => void): void;
|
|
7
|
-
setNativeProps(nativeProps: Record<string, unknown>): void;
|
|
8
|
-
focus(): void;
|
|
9
|
-
blur(): void;
|
|
10
|
-
}
|
|
1
|
+
import type { ISymbioteNode } from '../node';
|
|
2
|
+
export type IHostInstance = ISymbioteNode;
|
|
11
3
|
export declare function toPublicInstance(node: ISymbioteNode): IHostInstance;
|
|
@@ -1,49 +1,16 @@
|
|
|
1
|
-
// The public instance a host ref hands back, RN's ReactFabricHostComponent
|
|
2
|
-
//
|
|
3
|
-
// gesture-handler, react-navigation)
|
|
4
|
-
// setNativeProps / focus / blur. The methods are attached onto the node object itself (not its
|
|
5
|
-
// props, so they never reach Fabric); each resolves the node's CURRENT committed handle at call
|
|
6
|
-
// time through the engine, so a clone-on-write commit between calls is transparent.
|
|
1
|
+
// The public instance a host ref hands back, RN's ReactFabricHostComponent: measure /
|
|
2
|
+
// measureInWindow / measureLayout / setNativeProps / focus / blur, the imperative API libraries
|
|
3
|
+
// reach through (reanimated, gesture-handler, react-navigation).
|
|
7
4
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return typeof Reflect.get(node, 'measure') === 'function';
|
|
18
|
-
}
|
|
19
|
-
// Augment the retained node with the public-instance methods, once. The same node
|
|
20
|
-
// instance persists across commits, so attaching once is enough, every method reads
|
|
21
|
-
// the live handle through the engine on each call.
|
|
5
|
+
// EVERY retained node already carries it — the six methods live on the shared node prototype
|
|
6
|
+
// (`SymbioteNode` in ../node), so there is nothing left to graft and `toPublicInstance` is the
|
|
7
|
+
// identity. It used to Object.assign six fresh closures onto each node; that cost 54 000 closures
|
|
8
|
+
// per 1 000-row create and made GC the largest bucket in the profile. The rationale, and why
|
|
9
|
+
// React was the one adapter not paying it, is recorded on ISymbioteNode.
|
|
10
|
+
//
|
|
11
|
+
// The function and the type both stay: they are the seam every adapter names (React's
|
|
12
|
+
// getPublicInstance, Vue's nodeOps createElement, Angular's Renderer2, Svelte's dom-shim), and a
|
|
13
|
+
// call site saying "hand me the public instance" still reads correctly at a no-op.
|
|
22
14
|
export function toPublicInstance(node) {
|
|
23
|
-
|
|
24
|
-
return node;
|
|
25
|
-
return Object.assign(node, {
|
|
26
|
-
measure(callback) {
|
|
27
|
-
engineMeasure(node, callback);
|
|
28
|
-
},
|
|
29
|
-
measureInWindow(callback) {
|
|
30
|
-
engineMeasureInWindow(node, callback);
|
|
31
|
-
},
|
|
32
|
-
measureLayout(relativeToNativeNode, onSuccess, onFail) {
|
|
33
|
-
if (!isSymbioteNode(relativeToNativeNode)) {
|
|
34
|
-
dlog('measureLayout: relative target must be a host ref');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
engineMeasureLayout(node, relativeToNativeNode, onSuccess, onFail);
|
|
38
|
-
},
|
|
39
|
-
setNativeProps(nativeProps) {
|
|
40
|
-
engineSetNativeProps(node, nativeProps);
|
|
41
|
-
},
|
|
42
|
-
focus() {
|
|
43
|
-
dispatchViewCommand(node, FOCUS_COMMAND, []);
|
|
44
|
-
},
|
|
45
|
-
blur() {
|
|
46
|
-
dispatchViewCommand(node, BLUR_COMMAND, []);
|
|
47
|
-
},
|
|
48
|
-
});
|
|
15
|
+
return node;
|
|
49
16
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, censusRetainedTree, getExplicitStyle, setNodeHidden, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node';
|
|
1
|
+
export { createElement, createRawText, createAnchor, ANCHOR_COMPONENT, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, censusRetainedTree, getExplicitStyle, setNodeHidden, setNodeComponent, setNodePressed, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node';
|
|
2
|
+
export { setAnimatedBehaviorStyle } from './animated/host-binding';
|
|
2
3
|
export { isEventFor } from './view-config';
|
|
3
4
|
export { registerComponent, setNativeViewConfigSource } from './registry';
|
|
4
5
|
export { isRecord } from './type-guards';
|
|
@@ -13,6 +14,7 @@ export { setEventDispatcher } from './dispatch';
|
|
|
13
14
|
export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibilityEvent, setNativeProps, getNativeTag, getNativeNode, whenCommitted, measure, measureInWindow, measureLayout, disposeRoot, readCommitProfile, } from './commit';
|
|
14
15
|
export type { ICommitProfile } from './commit';
|
|
15
16
|
export { registerPostCommit, unregisterPostCommit } from './post-commit';
|
|
17
|
+
export { ARIA_ALIAS_KEYS, foldAriaProps } from './accessibility-props';
|
|
16
18
|
export { toPublicInstance } from './host-instance';
|
|
17
19
|
export type { IHostInstance } from './host-instance';
|
|
18
20
|
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue, } from './platform-color';
|
|
@@ -89,3 +91,7 @@ export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessi
|
|
|
89
91
|
export { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight, } from './status-bar';
|
|
90
92
|
export { hideTransition, STATUS_BAR_MANAGER, ANIMATED_HIDE_TRANSITION, STATIC_HIDE_TRANSITION, } from './status-bar/shared';
|
|
91
93
|
export type { IStatusBarProps, IStatusBarStyle, IStatusBarAnimation, IStatusBarImperative, } from './status-bar/shared';
|
|
94
|
+
export { registerHostBehavior, hasHostBehaviors, hostBehaviorFor, clearHostBehaviors, appListenerFor, addDerivedNode, } from './host-behavior';
|
|
95
|
+
export type { IClaimMode, IHostBehavior, IPayloadFold } from './host-behavior';
|
|
96
|
+
export { requestCommitFor } from './commit';
|
|
97
|
+
export { setBehaviorListener, markPropsDirty } from './node';
|