@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/index.js
CHANGED
|
@@ -2,7 +2,15 @@
|
|
|
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,
|
|
5
|
+
export { createElement, createRawText, createAnchor,
|
|
6
|
+
// The component name of a node the commit walk skips and whose children flatten into its parent.
|
|
7
|
+
// Exported so a PRIMITIVE that renders no view of its own can be born with it — RN's
|
|
8
|
+
// TouchableNativeFeedback clones onto its single child and commits nothing
|
|
9
|
+
// (TouchableNativeFeedback.js:339) — rather than being converted after the fact.
|
|
10
|
+
ANCHOR_COMPONENT, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, censusRetainedTree, getExplicitStyle, setNodeHidden, setNodeComponent, setNodePressed, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node.js';
|
|
11
|
+
// For a HOST BEHAVIOR that owns an animated style layer on its own node — TouchableOpacity's press
|
|
12
|
+
// fade, which RN drives from an `Animated.View` the tag replaces.
|
|
13
|
+
export { setAnimatedBehaviorStyle } from './animated/host-binding.js';
|
|
6
14
|
export { isEventFor } from './view-config.js';
|
|
7
15
|
export { registerComponent, setNativeViewConfigSource } from './registry.js';
|
|
8
16
|
// Real cross-package consumer: core/components' KeyboardAvoidingView render narrows
|
|
@@ -24,9 +32,13 @@ export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibility
|
|
|
24
32
|
// across adapters has to hang off this, not off a per-framework lifecycle hook, or it measures a
|
|
25
33
|
// different quantity in each one under the same name.
|
|
26
34
|
export { registerPostCommit, unregisterPostCommit } from './post-commit.js';
|
|
27
|
-
// The
|
|
28
|
-
//
|
|
29
|
-
//
|
|
35
|
+
// The aria/role -> accessibility* fold. Lives here rather than in a component wrapper because a
|
|
36
|
+
// LOWERED element has no wrapper: `fabricProps` runs it on the way to the payload, so every path
|
|
37
|
+
// gets it. `core/components`' typed `resolveAccessibilityProps` delegates to this one.
|
|
38
|
+
export { ARIA_ALIAS_KEYS, foldAriaProps } from './accessibility-props.js';
|
|
39
|
+
// The public instance every host node already is (React's getPublicInstance, the Vue renderer's
|
|
40
|
+
// createElement): the imperative measure/setNativeProps/focus API, on the shared node prototype.
|
|
41
|
+
// toPublicInstance is the identity that names the seam — see ./host-instance.
|
|
30
42
|
export { toPublicInstance } from './host-instance/index.js';
|
|
31
43
|
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue, } from './platform-color/index.js';
|
|
32
44
|
// CSS-style processors (boxShadow/filter): RN parses these in JS before native because
|
|
@@ -87,3 +99,10 @@ export { AccessibilityInfo } from './accessibility-info';
|
|
|
87
99
|
// .ios re-export would otherwise duplicate-export the type symbols).
|
|
88
100
|
export { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight, } from './status-bar';
|
|
89
101
|
export { hideTransition, STATUS_BAR_MANAGER, ANIMATED_HIDE_TRANSITION, STATIC_HIDE_TRANSITION, } from './status-bar/shared.js';
|
|
102
|
+
export { registerHostBehavior, hasHostBehaviors, hostBehaviorFor, clearHostBehaviors, appListenerFor, addDerivedNode, } from './host-behavior.js';
|
|
103
|
+
export { requestCommitFor } from './commit.js';
|
|
104
|
+
// `markPropsDirty` is a behavior's only way to say "the fold reads state I just changed". Every
|
|
105
|
+
// other dirtying route goes through a prop write, and a behavior whose payload is DERIVED — the
|
|
106
|
+
// sticky header's debounced translateY lives in its own runtime, not in `node.props` — has no
|
|
107
|
+
// prop to write. Pair it with `requestCommitFor`: dirtying is not publishing.
|
|
108
|
+
export { setBehaviorListener, markPropsDirty } from './node.js';
|
package/build/node.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { IFabricNode, IFabricProps, IRootTag, IMeasureOnSuccess, IMeasureInWindowOnSuccess, IMeasureLayoutOnSuccess } from './fabric';
|
|
2
|
+
import { type IClassNameValue } from './style-registry';
|
|
3
|
+
import { type IPayloadFold } from './host-behavior';
|
|
1
4
|
declare const BRAND: unique symbol;
|
|
2
5
|
export declare const RAW_TEXT_COMPONENT = "RCTRawText";
|
|
3
6
|
export declare const TEXT_COMPONENT = "RCTText";
|
|
@@ -13,15 +16,68 @@ export type IListener = (event: ISymbioteEvent) => unknown;
|
|
|
13
16
|
export declare function isSymbioteEvent(value: unknown): value is ISymbioteEvent;
|
|
14
17
|
export interface ISymbioteNode {
|
|
15
18
|
readonly [BRAND]: true;
|
|
16
|
-
|
|
19
|
+
component: string;
|
|
17
20
|
readonly isText: boolean;
|
|
18
21
|
props: Record<string, unknown>;
|
|
19
22
|
listeners: Map<string, IListener> | undefined;
|
|
20
23
|
children: ISymbioteNode[];
|
|
21
24
|
parent: ISymbioteNode | undefined;
|
|
22
25
|
dirty: boolean;
|
|
26
|
+
propsDirty: boolean;
|
|
27
|
+
hasAriaAlias: boolean;
|
|
28
|
+
payloadFold: IPayloadFold | undefined;
|
|
29
|
+
structureDirty: boolean;
|
|
30
|
+
committed: IMirror | undefined;
|
|
31
|
+
styleParts: IClassStyleParts | undefined;
|
|
32
|
+
childHost: ISymbioteNode | undefined;
|
|
33
|
+
wrapper: ISymbioteNode | undefined;
|
|
34
|
+
measure(callback: IMeasureOnSuccess): void;
|
|
35
|
+
measureInWindow(callback: IMeasureInWindowOnSuccess): void;
|
|
36
|
+
measureLayout(relativeToNativeNode: ISymbioteNode | number, onSuccess: IMeasureLayoutOnSuccess, onFail?: () => void): void;
|
|
37
|
+
setNativeProps(nativeProps: Record<string, unknown>): void;
|
|
38
|
+
focus(): void;
|
|
39
|
+
blur(): void;
|
|
40
|
+
scrollTo(options?: {
|
|
41
|
+
x?: number;
|
|
42
|
+
y?: number;
|
|
43
|
+
animated?: boolean;
|
|
44
|
+
}): void;
|
|
45
|
+
scrollToEnd(options?: {
|
|
46
|
+
animated?: boolean;
|
|
47
|
+
}): void;
|
|
48
|
+
flashScrollIndicators(): void;
|
|
23
49
|
}
|
|
24
|
-
export
|
|
50
|
+
export interface IMirror {
|
|
51
|
+
handle: IFabricNode;
|
|
52
|
+
tag: number;
|
|
53
|
+
rootTag: IRootTag;
|
|
54
|
+
props: IFabricProps;
|
|
55
|
+
children: readonly ISymbioteNode[];
|
|
56
|
+
viewName: string;
|
|
57
|
+
parent: ISymbioteNode | undefined;
|
|
58
|
+
owner: ISymbioteNode;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The committed record for `node`, or `undefined` if it has never been committed - or if `node` is
|
|
62
|
+
* not the raw retained node at all.
|
|
63
|
+
*
|
|
64
|
+
* That second case is the reason this is a function rather than a bare `node.committed` read. The
|
|
65
|
+
* engine identifies a node BY IDENTITY, and the classic way to break that is to hand the engine a
|
|
66
|
+
* wrapper instead of the node: a Vue `reactive()`/deep-`ref()` Proxy around a host element is the
|
|
67
|
+
* one that actually happens (see the vue-adapter-reactivity skill; `shallowRef` is the fix).
|
|
68
|
+
*
|
|
69
|
+
* The old WeakMap caught this for free - a Proxy is a different object, so `mirror.get(proxy)` missed
|
|
70
|
+
* and every imperative API bailed with a clear "node not committed". A plain property read does NOT:
|
|
71
|
+
* a Proxy forwards `proxy.committed` straight to the target and hands back a real record, whose
|
|
72
|
+
* `handle` Vue would then deep-wrap on the way out. That handle is a JSI host object; a Proxy around
|
|
73
|
+
* it reaches `cloneNodeWithNewProps` and fails somewhere deep in native, far from the cause.
|
|
74
|
+
*
|
|
75
|
+
* So the identity check that was implicit in the WeakMap is explicit here: a record written on the
|
|
76
|
+
* raw node names it, and `record.owner !== node` means whatever we were handed is not that node.
|
|
77
|
+
* One reference comparison, and the wrap now fails LOUDER than it used to rather than quieter.
|
|
78
|
+
*/
|
|
79
|
+
export declare function committedOf(node: ISymbioteNode): IMirror | undefined;
|
|
80
|
+
export declare function createElement(component: string, isText?: boolean, tag?: string): ISymbioteNode;
|
|
25
81
|
export declare function createRawText(text: string): ISymbioteNode;
|
|
26
82
|
export declare function isSymbioteNode(value: unknown): value is ISymbioteNode;
|
|
27
83
|
export declare function debugNodeId(node: ISymbioteNode): number;
|
|
@@ -29,13 +85,53 @@ export declare const ANCHOR_COMPONENT = "#anchor";
|
|
|
29
85
|
export declare function createAnchor(): ISymbioteNode;
|
|
30
86
|
export declare function isAnchor(node: ISymbioteNode): boolean;
|
|
31
87
|
export declare function isEmptyRawText(node: ISymbioteNode): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Change which Fabric view a node commits as, keeping the node's identity.
|
|
90
|
+
*
|
|
91
|
+
* The commit walk already re-creates a node whose `viewName` no longer matches its committed one —
|
|
92
|
+
* that is how a `<Text>` moving in or out of another `<Text>` flips between RCTText and
|
|
93
|
+
* RCTVirtualText (`commit.ts`, reason `view-kind`). This exposes the same door for a prop-driven
|
|
94
|
+
* view choice, so `intrinsicWhen` is honoured on UPDATE and not only at create.
|
|
95
|
+
*
|
|
96
|
+
* The POLICY stays out of the engine: which prop decides, and which view it decides between, lives
|
|
97
|
+
* in `HOST_PRIMITIVES` and is read by `resolveIntrinsicTag` in `@symbiote-native/components`. The
|
|
98
|
+
* engine only knows how to swap the name — the same split every other spec-driven fold has here.
|
|
99
|
+
*
|
|
100
|
+
* A no-op when the name is unchanged, so a renderer may call it on every update without comparing
|
|
101
|
+
* first.
|
|
102
|
+
*/
|
|
103
|
+
export declare function setNodeComponent(node: ISymbioteNode, component: string): void;
|
|
32
104
|
export declare function markDirty(node: ISymbioteNode): void;
|
|
105
|
+
export declare function markPropsDirty(node: ISymbioteNode): void;
|
|
106
|
+
export declare function markStructureDirty(parent: ISymbioteNode): void;
|
|
33
107
|
export declare function takePropStats(): {
|
|
34
108
|
writes: number;
|
|
35
109
|
noops: number;
|
|
36
110
|
};
|
|
37
111
|
export declare function setProp(node: ISymbioteNode, key: string, value: unknown): void;
|
|
112
|
+
/**
|
|
113
|
+
* Install a listener the BEHAVIOR owns, bypassing the ownership check.
|
|
114
|
+
*
|
|
115
|
+
* `setEventListener` diverts an owned name into the stash, which is right for an app listener and
|
|
116
|
+
* circular for the behavior's own dispatcher — it would stash itself and never occupy the slot it
|
|
117
|
+
* exists to hold. This is the one writer allowed past that gate.
|
|
118
|
+
*
|
|
119
|
+
* `undefined` removes it, gate flag included. A behavior whose dispatcher is conditional needs
|
|
120
|
+
* that as much as it needs the install: ScrollView takes the owner's `layout` only while the app
|
|
121
|
+
* or an inverted sticky header wants it, and a one-way installer leaves `onLayout: true` standing
|
|
122
|
+
* in the payload of a ScrollView that no longer reads the event.
|
|
123
|
+
*/
|
|
124
|
+
export declare function setBehaviorListener(node: ISymbioteNode, name: string, listener: IListener | undefined): void;
|
|
38
125
|
export declare function setEventListener(node: ISymbioteNode, name: string, value: unknown): void;
|
|
126
|
+
export interface IClassStyleParts {
|
|
127
|
+
classStyle: unknown;
|
|
128
|
+
explicitStyle: unknown;
|
|
129
|
+
hiddenStyle: unknown;
|
|
130
|
+
className: IClassNameValue | undefined;
|
|
131
|
+
isPressed: boolean;
|
|
132
|
+
activeStyle: unknown;
|
|
133
|
+
activeStyleFromCallback: boolean;
|
|
134
|
+
}
|
|
39
135
|
/**
|
|
40
136
|
* Stop a node painting without unmounting it, or let it paint again.
|
|
41
137
|
*
|
|
@@ -44,12 +140,26 @@ export declare function setEventListener(node: ISymbioteNode, name: string, valu
|
|
|
44
140
|
* author's style byte for byte — belongs to whoever owns the style merge, and that is here.
|
|
45
141
|
*/
|
|
46
142
|
export declare function setNodeHidden(node: ISymbioteNode, hidden: boolean): void;
|
|
143
|
+
/**
|
|
144
|
+
* Put a node into (or out of) its pressed state, so `.x:active` rules apply.
|
|
145
|
+
*
|
|
146
|
+
* The engine-owned half of what `:active` is on the web: the press state resolves BELOW the
|
|
147
|
+
* framework and never crosses into it, which is what lets a pressable be an intrinsic tag rather
|
|
148
|
+
* than a component (`.claude/rules/host-primitive-tier.md`, tier 2). A component is forced only
|
|
149
|
+
* when the TEMPLATE must read the state — `v-slot="{ pressed }"` and the function form of `style`
|
|
150
|
+
* — and this exists so the common case does not have to.
|
|
151
|
+
*
|
|
152
|
+
* Costs nothing when no `:active` rule is registered anywhere: `resolveActiveClassName` hands back
|
|
153
|
+
* the very same object the unpressed path returns, so `isAlreadyPublished` turns the re-push away
|
|
154
|
+
* and the node is never dirtied.
|
|
155
|
+
*/
|
|
156
|
+
export declare function setNodePressed(node: ISymbioteNode, pressed: boolean): void;
|
|
47
157
|
export declare function getExplicitStyle(node: ISymbioteNode): unknown;
|
|
48
158
|
export declare function routeProp(node: ISymbioteNode, key: string, value: unknown): void;
|
|
49
159
|
export declare function setText(node: ISymbioteNode, text: string): void;
|
|
50
|
-
export declare function appendChild(
|
|
51
|
-
export declare function insertBefore(
|
|
52
|
-
export declare function removeChild(
|
|
160
|
+
export declare function appendChild(requestedParent: ISymbioteNode, child: ISymbioteNode): void;
|
|
161
|
+
export declare function insertBefore(requestedParent: ISymbioteNode, child: ISymbioteNode, beforeChild: ISymbioteNode | null): void;
|
|
162
|
+
export declare function removeChild(requestedParent: ISymbioteNode, child: ISymbioteNode): void;
|
|
53
163
|
export interface ITreeCensus {
|
|
54
164
|
nodes: number;
|
|
55
165
|
anchors: number;
|