@symbiote-native/engine 0.1.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.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/build/accessibility-info/index.android.d.ts +3 -0
  4. package/build/accessibility-info/index.android.js +166 -0
  5. package/build/accessibility-info/index.d.ts +1 -0
  6. package/build/accessibility-info/index.ios.d.ts +3 -0
  7. package/build/accessibility-info/index.ios.js +219 -0
  8. package/build/accessibility-info/index.js +5 -0
  9. package/build/accessibility-info/shared.d.ts +34 -0
  10. package/build/accessibility-info/shared.js +13 -0
  11. package/build/action-sheet-ios/index.d.ts +36 -0
  12. package/build/action-sheet-ios/index.js +74 -0
  13. package/build/alert/index.android.d.ts +5 -0
  14. package/build/alert/index.android.js +117 -0
  15. package/build/alert/index.d.ts +1 -0
  16. package/build/alert/index.ios.d.ts +7 -0
  17. package/build/alert/index.ios.js +83 -0
  18. package/build/alert/index.js +8 -0
  19. package/build/alert/shared.d.ts +19 -0
  20. package/build/alert/shared.js +17 -0
  21. package/build/animated/animated-component-shared.d.ts +5 -0
  22. package/build/animated/animated-component-shared.js +54 -0
  23. package/build/animated/animation.d.ts +9 -0
  24. package/build/animated/animation.js +6 -0
  25. package/build/animated/animations/base.d.ts +27 -0
  26. package/build/animated/animations/base.js +90 -0
  27. package/build/animated/animations/composition.d.ts +38 -0
  28. package/build/animated/animations/composition.js +236 -0
  29. package/build/animated/animations/decay.d.ts +22 -0
  30. package/build/animated/animations/decay.js +65 -0
  31. package/build/animated/animations/raf.d.ts +5 -0
  32. package/build/animated/animations/raf.js +39 -0
  33. package/build/animated/animations/spring-config.d.ts +6 -0
  34. package/build/animated/animations/spring-config.js +55 -0
  35. package/build/animated/animations/spring.d.ts +50 -0
  36. package/build/animated/animations/spring.js +207 -0
  37. package/build/animated/animations/timing.d.ts +27 -0
  38. package/build/animated/animations/timing.js +101 -0
  39. package/build/animated/animations/tracking.d.ts +14 -0
  40. package/build/animated/animations/tracking.js +43 -0
  41. package/build/animated/bezier.d.ts +1 -0
  42. package/build/animated/bezier.js +101 -0
  43. package/build/animated/color.d.ts +37 -0
  44. package/build/animated/color.js +183 -0
  45. package/build/animated/easing.d.ts +20 -0
  46. package/build/animated/easing.js +96 -0
  47. package/build/animated/event.d.ts +36 -0
  48. package/build/animated/event.js +252 -0
  49. package/build/animated/graph.d.ts +38 -0
  50. package/build/animated/graph.js +227 -0
  51. package/build/animated/index.d.ts +20 -0
  52. package/build/animated/index.js +28 -0
  53. package/build/animated/interpolation-node.d.ts +16 -0
  54. package/build/animated/interpolation-node.js +57 -0
  55. package/build/animated/interpolation.d.ts +22 -0
  56. package/build/animated/interpolation.js +199 -0
  57. package/build/animated/mock.d.ts +56 -0
  58. package/build/animated/mock.js +127 -0
  59. package/build/animated/native/native-animated.d.ts +43 -0
  60. package/build/animated/native/native-animated.js +146 -0
  61. package/build/animated/operators.d.ts +80 -0
  62. package/build/animated/operators.js +266 -0
  63. package/build/animated/props.d.ts +20 -0
  64. package/build/animated/props.js +187 -0
  65. package/build/animated/style.d.ts +26 -0
  66. package/build/animated/style.js +187 -0
  67. package/build/animated/value-xy.d.ts +35 -0
  68. package/build/animated/value-xy.js +106 -0
  69. package/build/animated/value.d.ts +36 -0
  70. package/build/animated/value.js +185 -0
  71. package/build/app-registry/index.d.ts +40 -0
  72. package/build/app-registry/index.js +144 -0
  73. package/build/app-state/index.d.ts +16 -0
  74. package/build/app-state/index.js +105 -0
  75. package/build/appearance/index.d.ts +12 -0
  76. package/build/appearance/index.js +84 -0
  77. package/build/back-handler/index.d.ts +14 -0
  78. package/build/back-handler/index.js +106 -0
  79. package/build/commit.d.ts +16 -0
  80. package/build/commit.js +678 -0
  81. package/build/debug.d.ts +5 -0
  82. package/build/debug.js +18 -0
  83. package/build/dimensions/index.d.ts +28 -0
  84. package/build/dimensions/index.js +148 -0
  85. package/build/dispatch.d.ts +2 -0
  86. package/build/dispatch.js +18 -0
  87. package/build/events/index.d.ts +1 -0
  88. package/build/events/index.js +691 -0
  89. package/build/fabric.d.ts +32 -0
  90. package/build/fabric.js +59 -0
  91. package/build/host-instance/index.d.ts +11 -0
  92. package/build/host-instance/index.js +49 -0
  93. package/build/i18n-manager/index.d.ts +13 -0
  94. package/build/i18n-manager/index.js +91 -0
  95. package/build/index.d.ts +80 -0
  96. package/build/index.js +72 -0
  97. package/build/interaction-manager/index.d.ts +45 -0
  98. package/build/interaction-manager/index.js +222 -0
  99. package/build/keyboard/index.d.ts +31 -0
  100. package/build/keyboard/index.js +142 -0
  101. package/build/layout-animation/index.d.ts +66 -0
  102. package/build/layout-animation/index.js +183 -0
  103. package/build/linking/index.android.d.ts +2 -0
  104. package/build/linking/index.android.js +18 -0
  105. package/build/linking/index.d.ts +1 -0
  106. package/build/linking/index.ios.d.ts +2 -0
  107. package/build/linking/index.ios.js +9 -0
  108. package/build/linking/index.js +6 -0
  109. package/build/linking/shared.d.ts +32 -0
  110. package/build/linking/shared.js +98 -0
  111. package/build/native-events.d.ts +24 -0
  112. package/build/native-events.js +129 -0
  113. package/build/native-modules.d.ts +6 -0
  114. package/build/native-modules.js +57 -0
  115. package/build/node.d.ts +36 -0
  116. package/build/node.js +194 -0
  117. package/build/pan-responder/index.d.ts +53 -0
  118. package/build/pan-responder/index.js +353 -0
  119. package/build/permissions-android/index.d.ts +115 -0
  120. package/build/permissions-android/index.js +185 -0
  121. package/build/pixel-ratio/index.d.ts +8 -0
  122. package/build/pixel-ratio/index.js +27 -0
  123. package/build/platform/index.android.d.ts +22 -0
  124. package/build/platform/index.android.js +60 -0
  125. package/build/platform/index.d.ts +1 -0
  126. package/build/platform/index.ios.d.ts +18 -0
  127. package/build/platform/index.ios.js +62 -0
  128. package/build/platform/index.js +5 -0
  129. package/build/platform/shared.d.ts +25 -0
  130. package/build/platform/shared.js +41 -0
  131. package/build/platform-color.d.ts +19 -0
  132. package/build/platform-color.js +25 -0
  133. package/build/post-commit.d.ts +4 -0
  134. package/build/post-commit.js +16 -0
  135. package/build/process-aspect-ratio.d.ts +1 -0
  136. package/build/process-aspect-ratio.js +34 -0
  137. package/build/process-background-image/index.d.ts +28 -0
  138. package/build/process-background-image/index.js +557 -0
  139. package/build/process-box-shadow/index.d.ts +11 -0
  140. package/build/process-box-shadow/index.js +193 -0
  141. package/build/process-filter.d.ts +31 -0
  142. package/build/process-filter.js +304 -0
  143. package/build/process-font-variant.d.ts +1 -0
  144. package/build/process-font-variant.js +17 -0
  145. package/build/process-transform/index.d.ts +5 -0
  146. package/build/process-transform/index.js +120 -0
  147. package/build/process-transform-origin/index.d.ts +3 -0
  148. package/build/process-transform-origin/index.js +108 -0
  149. package/build/registry.d.ts +31 -0
  150. package/build/registry.js +145 -0
  151. package/build/settings/index.d.ts +8 -0
  152. package/build/settings/index.js +126 -0
  153. package/build/share/index.android.d.ts +3 -0
  154. package/build/share/index.android.js +56 -0
  155. package/build/share/index.d.ts +1 -0
  156. package/build/share/index.ios.d.ts +3 -0
  157. package/build/share/index.ios.js +47 -0
  158. package/build/share/index.js +6 -0
  159. package/build/share/shared.d.ts +32 -0
  160. package/build/share/shared.js +32 -0
  161. package/build/status-bar/index.android.d.ts +5 -0
  162. package/build/status-bar/index.android.js +83 -0
  163. package/build/status-bar/index.d.ts +1 -0
  164. package/build/status-bar/index.ios.d.ts +5 -0
  165. package/build/status-bar/index.ios.js +66 -0
  166. package/build/status-bar/index.js +4 -0
  167. package/build/status-bar/shared.d.ts +22 -0
  168. package/build/status-bar/shared.js +22 -0
  169. package/build/style/index.d.ts +1 -0
  170. package/build/style/index.js +30 -0
  171. package/build/style-registry/index.d.ts +11 -0
  172. package/build/style-registry/index.js +165 -0
  173. package/build/style-sheet/index.d.ts +20 -0
  174. package/build/style-sheet/index.js +121 -0
  175. package/build/styles.d.ts +220 -0
  176. package/build/styles.js +7 -0
  177. package/build/surface.d.ts +16 -0
  178. package/build/surface.js +67 -0
  179. package/build/tags.d.ts +1 -0
  180. package/build/tags.js +10 -0
  181. package/build/text-input-state.d.ts +5 -0
  182. package/build/text-input-state.js +29 -0
  183. package/build/toast-android/index.d.ts +10 -0
  184. package/build/toast-android/index.js +108 -0
  185. package/build/vibration/index.android.d.ts +2 -0
  186. package/build/vibration/index.android.js +18 -0
  187. package/build/vibration/index.d.ts +1 -0
  188. package/build/vibration/index.ios.d.ts +2 -0
  189. package/build/vibration/index.ios.js +54 -0
  190. package/build/vibration/index.js +6 -0
  191. package/build/vibration/shared.d.ts +15 -0
  192. package/build/vibration/shared.js +68 -0
  193. package/build/view-config.d.ts +1 -0
  194. package/build/view-config.js +114 -0
  195. package/package.json +41 -0
@@ -0,0 +1,252 @@
1
+ // Animated.event: bridge a real native event (a scroll's contentOffset, a pan's
2
+ // translation) into the value graph. An argMapping like
3
+ // `[{nativeEvent: {contentOffset: {y: scrollY}}}]` names, at its leaf positions,
4
+ // the AnimatedValues to drive; the path to each leaf is the key path inside the
5
+ // event object. Ported from RN's AnimatedEvent.js (JS + native paths), with the
6
+ // native-driver branches kept and AnimatedValueXY deferred.
7
+ //
8
+ // Two ways the mapping is consumed:
9
+ // - JS path: __getHandler() returns a callback the adapter wires as the view's
10
+ // event prop. Each fired event is walked against the mapping; every leaf
11
+ // AnimatedValue is set from the matching event field, then flushed so its
12
+ // bound props re-paint. config.listener is invoked passthrough.
13
+ // - Native path: __attach(viewTag, eventName) mirrors each leaf value into native
14
+ // and registers the key path with the stock native module, so the event drives
15
+ // the view with zero JS per event.
16
+ import { dlog } from '../debug';
17
+ import { getNativeTag, whenCommitted } from '../commit';
18
+ import { isSymbioteNode } from '../node';
19
+ import { AnimatedNode, flushValue } from './graph';
20
+ import { isNativeAnimatedAvailable, nativeAnimated } from './native/native-animated';
21
+ function isRecord(value) {
22
+ return typeof value === 'object' && value !== null;
23
+ }
24
+ // A value node settable from an event field. AnimatedValue carries setValue; we
25
+ // duck-type it (rather than importing AnimatedValue) to keep this leaf-agnostic
26
+ // and avoid a value<->event import cycle.
27
+ function settableValue(node) {
28
+ const candidate = Reflect.get(node, 'setValue');
29
+ return typeof candidate === 'function' ? value => candidate.call(node, value) : undefined;
30
+ }
31
+ // Walk the mapping to every leaf AnimatedNode, recording its key path. Shared by
32
+ // the JS handler (to set values) and the native attach (to register paths).
33
+ function collectMappedValues(mapping, path, out) {
34
+ if (mapping instanceof AnimatedNode) {
35
+ out.push({ path, node: mapping });
36
+ return;
37
+ }
38
+ for (const key of Object.keys(mapping)) {
39
+ const child = Reflect.get(mapping, key);
40
+ if (child instanceof AnimatedNode) {
41
+ out.push({ path: [...path, key], node: child });
42
+ }
43
+ else if (isRecord(child)) {
44
+ collectMappedValues(child, [...path, key], out);
45
+ }
46
+ }
47
+ }
48
+ // Pull the numeric field at `path` out of one event argument. Returns undefined
49
+ // when the path is absent or the leaf is not a number, so a malformed event is a
50
+ // no-op rather than a throw inside an event dispatch.
51
+ function extractAtPath(event, path) {
52
+ let current = event;
53
+ for (const key of path) {
54
+ if (!isRecord(current))
55
+ return undefined;
56
+ current = Reflect.get(current, key);
57
+ }
58
+ return typeof current === 'number' ? current : undefined;
59
+ }
60
+ export class AnimatedEvent {
61
+ // Listeners fired (in registration order) after the values are driven. Seeded
62
+ // with config.listener; forkEvent appends more via __addListener (RN
63
+ // AnimatedEvent.js seeds `__addListener(config.listener)` in its constructor).
64
+ listeners = [];
65
+ // The leaves under argMapping[0].nativeEvent, the only place native-driven
66
+ // events accept animated values (RN invariant). Resolved once at construction.
67
+ mappedValues;
68
+ // RN parity: the event remembers whether useNativeDriver was requested. Honored only
69
+ // when a native module is present (__isNative); otherwise the JS path drives values.
70
+ nativeDriverRequested;
71
+ // Flipped once a host view native-attaches this event. The JS handler then stops
72
+ // setting values (native owns them on the UI thread) and only forwards listeners.
73
+ // no double-drive, no redundant per-tick commit.
74
+ attachedNatively = false;
75
+ constructor(argMapping, config) {
76
+ if (config?.listener !== undefined)
77
+ this.listeners.push(config.listener);
78
+ this.nativeDriverRequested = config?.useNativeDriver === true;
79
+ const mapped = [];
80
+ const first = argMapping[0];
81
+ if (isRecord(first)) {
82
+ const nativeEvent = Reflect.get(first, 'nativeEvent');
83
+ if (isRecord(nativeEvent)) {
84
+ collectMappedValues(nativeEvent, [], mapped);
85
+ }
86
+ }
87
+ this.mappedValues = mapped;
88
+ }
89
+ // Append / drop a listener (RN AnimatedEvent.js __addListener / __removeListener).
90
+ // forkEvent/unforkEvent use these to combine extra handlers onto one AnimatedEvent.
91
+ __addListener(callback) {
92
+ this.listeners.push(callback);
93
+ }
94
+ __removeListener(callback) {
95
+ const index = this.listeners.indexOf(callback);
96
+ if (index !== -1)
97
+ this.listeners.splice(index, 1);
98
+ }
99
+ // Native path: mirror each leaf value into native and register its key path with
100
+ // the stock module, so the event drives the view with zero JS per event.
101
+ // Whether this event should drive its values natively: useNativeDriver requested AND
102
+ // a native module present. The adapter consults it before native-attaching to a view.
103
+ __isNative() {
104
+ return this.nativeDriverRequested && isNativeAnimatedAvailable();
105
+ }
106
+ __attach(viewTag, eventName) {
107
+ for (const mapped of this.mappedValues) {
108
+ mapped.node.__makeNative();
109
+ dlog(`event: attach ${eventName} path=${mapped.path.join('.')} -> view=${viewTag}`);
110
+ nativeAnimated.addAnimatedEventToView(viewTag, eventName, {
111
+ nativeEventPath: mapped.path,
112
+ animatedValueTag: mapped.node.__getNativeTag(),
113
+ });
114
+ }
115
+ this.attachedNatively = true;
116
+ }
117
+ __detach(viewTag, eventName) {
118
+ for (const mapped of this.mappedValues) {
119
+ dlog(`event: detach ${eventName} -> view=${viewTag}`);
120
+ nativeAnimated.removeAnimatedEventFromView(viewTag, eventName, mapped.node.__getNativeTag());
121
+ }
122
+ this.attachedNatively = false;
123
+ }
124
+ // JS path: walk each leaf, set its value from the matching event field, flush so
125
+ // its bound props re-paint, then forward the raw args to the user's listener.
126
+ __getHandler() {
127
+ const handler = Object.assign((...args) => {
128
+ // Once natively attached, the UI thread owns the values, so skip the JS set/flush
129
+ // (it would double-drive and commit per tick); only forward listeners below.
130
+ if (!this.attachedNatively) {
131
+ // Paths are stored relative to `nativeEvent` (the native module excludes
132
+ // that prefix); the JS event arg still carries it, so re-add it here.
133
+ const nativeEvent = isRecord(args[0]) ? Reflect.get(args[0], 'nativeEvent') : undefined;
134
+ for (const mapped of this.mappedValues) {
135
+ const extracted = extractAtPath(nativeEvent, mapped.path);
136
+ if (extracted === undefined)
137
+ continue;
138
+ const setValue = settableValue(mapped.node);
139
+ if (setValue === undefined)
140
+ continue;
141
+ setValue(extracted);
142
+ flushValue(mapped.node);
143
+ }
144
+ }
145
+ for (const listener of this.listeners)
146
+ listener(...args);
147
+ }, { __getEvent: () => this });
148
+ return handler;
149
+ }
150
+ }
151
+ // The Animated.event factory: returns the handler an adapter wires as the event
152
+ // prop. The handler also exposes the AnimatedEvent (__getEvent) for adapters that
153
+ // need the native attach path.
154
+ export function event(argMapping, config) {
155
+ return new AnimatedEvent(argMapping, config).__getHandler();
156
+ }
157
+ // Imperatively bind a real native event on a host node to the mapping's animated values, so the
158
+ // event drives them on the UI thread with zero JS per event. RN's
159
+ // AnimatedImplementation.attachNativeEvent (ScrollView.js:1095 uses it for sticky headers). The
160
+ // ScrollView is NOT an animated component, so the event can't ride a prop; it is attached to the
161
+ // node's native tag directly. Returns a detach handle. Callers MUST gate on
162
+ // isNativeAnimatedAvailable(): with no native module __attach no-ops and the values never move,
163
+ // so a JS Animated.event path must remain the fallback.
164
+ export function attachNativeEvent(node, eventName, argMapping) {
165
+ const animatedEvent = new AnimatedEvent(argMapping);
166
+ let attachedTag;
167
+ // Bind once the node is committed: now if it already has a Fabric tag, else after the commit that
168
+ // assigns it. Vue/Svelte batch commits on a microtask, so the adapter can wire this — e.g. the
169
+ // sticky-header scroll — before the tag exists; whenCommitted defers instead of silently binding
170
+ // nothing. React commits synchronously and binds on the first try.
171
+ const cancel = whenCommitted(node, () => {
172
+ const viewTag = getNativeTag(node);
173
+ if (viewTag === undefined)
174
+ return;
175
+ dlog(`attachNativeEvent: ${eventName} -> view=${viewTag}`);
176
+ animatedEvent.__attach(viewTag, eventName);
177
+ attachedTag = viewTag;
178
+ });
179
+ return {
180
+ detach() {
181
+ cancel();
182
+ if (attachedTag !== undefined)
183
+ animatedEvent.__detach(attachedTag, eventName);
184
+ },
185
+ };
186
+ }
187
+ // Native-attach the AnimatedEvent already behind a handler from `event(...)`, binding it
188
+ // to a committed host node. Unlike attachNativeEvent (which builds a fresh event from a
189
+ // raw mapping for ScrollView's internal sticky value), this REUSES the caller's handler,
190
+ // so createAnimatedComponent can offload `onScroll={Animated.event(…, {useNativeDriver})}`
191
+ // to the UI thread, and the __makeNative cascade carries the bound interpolations/props
192
+ // native with it. Returns undefined (caller keeps the JS path) when the prop is not a native
193
+ // event handler; if it IS but the node has no tag yet, the bind defers to the commit (same race).
194
+ export function attachNativeEventHandler(node, eventName, handler) {
195
+ if (!isSymbioteNode(node) || typeof handler !== 'function')
196
+ return undefined;
197
+ const accessor = Reflect.get(handler, '__getEvent');
198
+ if (typeof accessor !== 'function')
199
+ return undefined;
200
+ const animatedEvent = accessor.call(handler);
201
+ if (!(animatedEvent instanceof AnimatedEvent) || !animatedEvent.__isNative())
202
+ return undefined;
203
+ const event = animatedEvent;
204
+ let attachedTag;
205
+ const cancel = whenCommitted(node, () => {
206
+ const viewTag = getNativeTag(node);
207
+ if (viewTag === undefined)
208
+ return;
209
+ event.__attach(viewTag, eventName);
210
+ attachedTag = viewTag;
211
+ });
212
+ return {
213
+ detach: () => {
214
+ cancel();
215
+ if (attachedTag !== undefined)
216
+ event.__detach(attachedTag, eventName);
217
+ },
218
+ };
219
+ }
220
+ // Combine an existing event handler with an extra listener (RN
221
+ // AnimatedImplementation.js forkEventImpl ~519). Three cases, by the existing event:
222
+ // - absent -> the listener becomes the handler
223
+ // - an AnimatedEvent -> the listener is appended to it; the same event is returned
224
+ // - a plain function -> a new function calling both
225
+ // The AnimatedEvent is recognised through its handler's __getEvent (the only public
226
+ // seam), so a handler built by `event(...)` forks into the underlying AnimatedEvent.
227
+ export function forkEvent(existing, listener) {
228
+ if (existing === undefined)
229
+ return listener;
230
+ const animatedEvent = getAnimatedEvent(existing);
231
+ if (animatedEvent !== undefined) {
232
+ animatedEvent.__addListener(listener);
233
+ return existing;
234
+ }
235
+ return (...args) => {
236
+ existing(...args);
237
+ listener(...args);
238
+ };
239
+ }
240
+ // Undo a forkEvent that targeted an AnimatedEvent (RN unforkEventImpl ~531). A
241
+ // plain-function fork has no removable seam, so this is a no-op for that case,
242
+ // exactly as RN, which only removes from an AnimatedEvent.
243
+ export function unforkEvent(existing, listener) {
244
+ const animatedEvent = existing === undefined ? undefined : getAnimatedEvent(existing);
245
+ animatedEvent?.__removeListener(listener);
246
+ }
247
+ // Reach the AnimatedEvent behind a handler. event(...) returns an AnimatedEventHandler
248
+ // carrying __getEvent; a bare listener does not, so this narrows the fork cases.
249
+ function getAnimatedEvent(candidate) {
250
+ const accessor = Reflect.get(candidate, '__getEvent');
251
+ return typeof accessor === 'function' ? accessor.call(candidate) : undefined;
252
+ }
@@ -0,0 +1,38 @@
1
+ import { type INativeNodeConfig, type IPlatformConfig } from './native/native-animated';
2
+ export type IValueListener = (state: {
3
+ value: number | string;
4
+ }) => void;
5
+ export declare class AnimatedNode {
6
+ private readonly listeners;
7
+ private suspendCallbacks;
8
+ protected isNative: boolean;
9
+ private nativeTag;
10
+ private platformConfig;
11
+ __attach(): void;
12
+ __detach(): void;
13
+ __isNative(): boolean;
14
+ __makeNative(platformConfig?: IPlatformConfig): void;
15
+ protected __connectNativeChildren(): void;
16
+ __getNativeTag(): number;
17
+ __getNativeConfig(): INativeNodeConfig;
18
+ __getValue(): unknown;
19
+ __getAnimatedValue(): unknown;
20
+ __addChild(_child: AnimatedNode): void;
21
+ __removeChild(_child: AnimatedNode): void;
22
+ __getChildren(): readonly AnimatedNode[];
23
+ addListener(callback: IValueListener): string;
24
+ removeListener(id: string): void;
25
+ removeAllListeners(): void;
26
+ hasListeners(): boolean;
27
+ __callListeners(value: number | string): void;
28
+ protected withSuspendedCallbacks(fn: () => void): void;
29
+ }
30
+ export declare class AnimatedWithChildren extends AnimatedNode {
31
+ protected children: AnimatedNode[];
32
+ protected __connectNativeChildren(): void;
33
+ __addChild(child: AnimatedNode): void;
34
+ __removeChild(child: AnimatedNode): void;
35
+ __getChildren(): readonly AnimatedNode[];
36
+ __callListeners(value: number | string): void;
37
+ }
38
+ export declare function flushValue(rootNode: AnimatedNode): void;
@@ -0,0 +1,227 @@
1
+ // The Animated dependency graph: a directed acyclic graph of value nodes that
2
+ // sits ABOVE symbiote's shadow tree. Ported from React Native's AnimatedNode.js
3
+ // + AnimatedWithChildren.js, with every native-driver path
4
+ // (NativeAnimatedHelper / __isNative / __makeNative / __getNativeConfig) removed:
5
+ // this is the JS-driven engine (ADR 0016). The native driver re-introduces those
6
+ // hooks separately (ADR 0017).
7
+ //
8
+ // Two phases drive an update:
9
+ // A) top-down: when a Value changes, walk children to the leaf nodes (the
10
+ // ones with an `update()` method) and flag them.
11
+ // B) bottom-up: each flagged leaf re-pulls its whole subtree via __getValue()
12
+ // to rebuild composite props (e.g. transform from many parents).
13
+ import { generateNativeNodeTag, nativeAnimated, } from './native/native-animated';
14
+ let nextListenerId = 1;
15
+ // Depth of the currently-active withSuspendedCallbacks blocks. While > 0, a
16
+ // composite setter (AnimatedColor.setValue) is driving several channels in a row;
17
+ // each channel's own flushValue is suppressed so the bound leaf commits ONCE (at
18
+ // the single flushValue the composite issues after the block) instead of once per
19
+ // channel. RN tolerates the per-channel flushes and relies on a downstream commit-
20
+ // coalescing layer; symbiote has none here, so it coalesces at the source.
21
+ let flushSuspendDepth = 0;
22
+ export class AnimatedNode {
23
+ listeners = new Map();
24
+ // While > 0, this node's own __callListeners is a no-op. A composite setter
25
+ // (AnimatedColor.setValue) that drives several channels in a row would otherwise
26
+ // fire this node's listeners once per channel, each with an intermediate value
27
+ // that never logically existed. The setter wraps the channel writes in
28
+ // _withSuspendedCallbacks, then fires once with the final value. Ported from RN's
29
+ // AnimatedColor._suspendCallbacks.
30
+ suspendCallbacks = 0;
31
+ // Native-driver state (ADR 0017). Off until a useNativeDriver animation marks
32
+ // the graph native; `nativeTag` is the node's identity in the native module,
33
+ // allocated lazily on first reference (which also creates the native node).
34
+ isNative = false;
35
+ nativeTag;
36
+ // RN's AnimatedNode._platformConfig (AnimatedNode.js:34): the platform tuning bag
37
+ // a useNativeDriver animation hands down via __makeNative, merged into this node's
38
+ // native config at creation (__getNativeTag). Optional: undefined when no caller
39
+ // supplied one, matching today's behavior.
40
+ platformConfig;
41
+ __attach() { }
42
+ __detach() {
43
+ this.removeAllListeners();
44
+ if (this.isNative && this.nativeTag !== undefined) {
45
+ nativeAnimated.dropAnimatedNode(this.nativeTag);
46
+ this.nativeTag = undefined;
47
+ }
48
+ }
49
+ __isNative() {
50
+ return this.isNative;
51
+ }
52
+ // Mirror this node (and its subtree) into the native module. Two strict phases,
53
+ // because the native module asserts a node exists before connectAnimatedNodes
54
+ // references it (a connect-before-create crashes RCTNativeAnimatedNodesManager):
55
+ // 1. CREATE every node: __getNativeTag enqueues createAnimatedNode. A config
56
+ // only READS child tags (also creating them), never connects, so creation is
57
+ // side-effect-free w.r.t. edges and createAnimatedNode may precede its refs.
58
+ // 2. CONNECT every edge: only once both endpoints are guaranteed created.
59
+ __makeNative(platformConfig) {
60
+ if (this.isNative)
61
+ return;
62
+ this.isNative = true;
63
+ // RN stores the platform bag before minting the tag (AnimatedNode.js:80) so it
64
+ // is already present when __getNativeTag folds it into the create config.
65
+ if (platformConfig !== undefined)
66
+ this.platformConfig = platformConfig;
67
+ this.__getNativeTag(); // phase 1: create self (config may create referenced children)
68
+ this.__connectNativeChildren(); // phase 2: wire edges
69
+ }
70
+ // Phase 2 hook. A leaf has no graph children; AnimatedWithChildren wires its own.
71
+ __connectNativeChildren() { }
72
+ // The node's native tag, minting it (and creating the native node) on first use.
73
+ // Stable for the node's life: the native side keys animations off it. CREATION
74
+ // ONLY: the config reads child tags but issues no connectAnimatedNodes, so a node
75
+ // is safe to create before the nodes its config references exist.
76
+ __getNativeTag() {
77
+ if (this.nativeTag === undefined) {
78
+ this.nativeTag = generateNativeNodeTag();
79
+ // RN merges _platformConfig into the config after __getNativeConfig returns
80
+ // (AnimatedNode.js:146-148) rather than in each node's config method.
81
+ const config = this.platformConfig === undefined
82
+ ? this.__getNativeConfig()
83
+ : { ...this.__getNativeConfig(), platformConfig: this.platformConfig };
84
+ nativeAnimated.createAnimatedNode(this.nativeTag, config);
85
+ }
86
+ return this.nativeTag;
87
+ }
88
+ // Each concrete node type (value / interpolation / style / transform / props)
89
+ // overrides this with its native shape; a plain node cannot be offloaded.
90
+ __getNativeConfig() {
91
+ throw new Error('This animated node type cannot be used as a native animated node');
92
+ }
93
+ // The current rasterized value. Heterogeneous across the graph: scalar nodes
94
+ // return a number, a props leaf returns a flat payload, so the base is
95
+ // `unknown` and numeric subclasses narrow it.
96
+ __getValue() {
97
+ return undefined;
98
+ }
99
+ __getAnimatedValue() {
100
+ return this.__getValue();
101
+ }
102
+ __addChild(_child) { }
103
+ __removeChild(_child) { }
104
+ __getChildren() {
105
+ return [];
106
+ }
107
+ // Asynchronous observation of value updates. There is no synchronous read of a
108
+ // value once it is driven by an animation, so consumers subscribe instead.
109
+ addListener(callback) {
110
+ const id = String(nextListenerId++);
111
+ this.listeners.set(id, callback);
112
+ return id;
113
+ }
114
+ removeListener(id) {
115
+ this.listeners.delete(id);
116
+ }
117
+ removeAllListeners() {
118
+ this.listeners.clear();
119
+ }
120
+ hasListeners() {
121
+ return this.listeners.size > 0;
122
+ }
123
+ __callListeners(value) {
124
+ if (this.suspendCallbacks > 0)
125
+ return;
126
+ const event = { value };
127
+ this.listeners.forEach(listener => {
128
+ listener(event);
129
+ });
130
+ }
131
+ // Run `fn` with this node's listener fires AND every flushValue suspended,
132
+ // restoring the prior depth even if `fn` throws. The composite setter pairs this
133
+ // with one explicit flush + one listener fire after the block.
134
+ withSuspendedCallbacks(fn) {
135
+ this.suspendCallbacks++;
136
+ flushSuspendDepth++;
137
+ try {
138
+ fn();
139
+ }
140
+ finally {
141
+ this.suspendCallbacks--;
142
+ flushSuspendDepth--;
143
+ }
144
+ }
145
+ }
146
+ export class AnimatedWithChildren extends AnimatedNode {
147
+ children = [];
148
+ // Phase 2: every child's subtree is fully made native (created + its own edges
149
+ // wired) before we connect this node to it, so both endpoints exist at connect.
150
+ __connectNativeChildren() {
151
+ for (const child of this.children) {
152
+ child.__makeNative();
153
+ }
154
+ for (const child of this.children) {
155
+ nativeAnimated.connectAnimatedNodes(this.__getNativeTag(), child.__getNativeTag());
156
+ }
157
+ }
158
+ __addChild(child) {
159
+ if (this.children.length === 0) {
160
+ this.__attach();
161
+ }
162
+ this.children.push(child);
163
+ // A child joining an already-native parent must itself be made native and wired.
164
+ if (this.isNative) {
165
+ child.__makeNative();
166
+ nativeAnimated.connectAnimatedNodes(this.__getNativeTag(), child.__getNativeTag());
167
+ }
168
+ }
169
+ __removeChild(child) {
170
+ const index = this.children.indexOf(child);
171
+ if (index === -1) {
172
+ return;
173
+ }
174
+ if (this.isNative && child.__isNative()) {
175
+ nativeAnimated.disconnectAnimatedNodes(this.__getNativeTag(), child.__getNativeTag());
176
+ }
177
+ this.children.splice(index, 1);
178
+ if (this.children.length === 0) {
179
+ this.__detach();
180
+ }
181
+ }
182
+ __getChildren() {
183
+ return this.children;
184
+ }
185
+ __callListeners(value) {
186
+ super.__callListeners(value);
187
+ // A native-driven node's children are updated natively; don't also walk them
188
+ // here (their values aren't tracked in JS while native owns the animation).
189
+ if (this.isNative)
190
+ return;
191
+ for (const child of this.children) {
192
+ child.__callListeners(numericValueOf(child));
193
+ }
194
+ }
195
+ }
196
+ function numericValueOf(node) {
197
+ const value = node.__getValue();
198
+ return typeof value === 'number' ? value : 0;
199
+ }
200
+ // A leaf carries an `update()` that re-pulls its subtree and commits: the seam
201
+ // where the value graph meets symbiote's engine. It is NOT declared on the base:
202
+ // a class field would be initialised to undefined and shadow a subclass method
203
+ // under useDefineForClassFields. So leaves are detected structurally instead.
204
+ function leafUpdate(node) {
205
+ const candidate = Reflect.get(node, 'update');
206
+ return typeof candidate === 'function' ? () => candidate.call(node) : undefined;
207
+ }
208
+ // Top-down walk to the leaves, then re-pull each leaf (deduped by node identity,
209
+ // so a diamond in the graph still updates a leaf once). Suppressed inside a
210
+ // withSuspendedCallbacks block: the composite setter issues the one flush that
211
+ // actually rebuilds the leaves after all its channel writes land.
212
+ export function flushValue(rootNode) {
213
+ if (flushSuspendDepth > 0)
214
+ return;
215
+ const leaves = new Map();
216
+ function collect(node) {
217
+ const update = leafUpdate(node);
218
+ if (update !== undefined) {
219
+ leaves.set(node, update);
220
+ }
221
+ else {
222
+ node.__getChildren().forEach(collect);
223
+ }
224
+ }
225
+ collect(rootNode);
226
+ leaves.forEach(update => update());
227
+ }
@@ -0,0 +1,20 @@
1
+ export { AnimatedNode, AnimatedWithChildren, flushValue, type IValueListener } from './graph';
2
+ export { AnimatedValue } from './value';
3
+ export { AnimatedValueXY, type IValueXY } from './value-xy';
4
+ export { AnimatedInterpolation } from './interpolation-node';
5
+ export { AnimatedAddition, AnimatedSubtraction, AnimatedMultiplication, AnimatedDivision, AnimatedModulo, AnimatedDiffClamp, add, subtract, multiply, divide, modulo, diffClamp, } from './operators';
6
+ export { AnimatedEvent, event, forkEvent, unforkEvent, attachNativeEvent, attachNativeEventHandler, type IEventConfig, type IEventListener, type IAnimatedEventHandler, type INativeEventAttachment, } from './event';
7
+ export { AnimatedColor, type IRgbaValue, type IColorInput } from './color';
8
+ export { Easing, type IEasingFunction } from './easing';
9
+ export { createNumericInterpolation, createInterpolation, checkValidRanges, type IInterpolationConfig, type IExtrapolateType, } from './interpolation';
10
+ export type { IAnimation, IEndCallback, IEndResult } from './animation';
11
+ export { TimingAnimation } from './animations/timing';
12
+ export { SpringAnimation } from './animations/spring';
13
+ export { DecayAnimation } from './animations/decay';
14
+ export { AnimatedTracking } from './animations/tracking';
15
+ export { timing, spring, decay, parallel, sequence, stagger, loop, delay, type ICompositeAnimation, type ITimingConfig, type ISpringConfig, type IDecayConfig, type IParallelConfig, type ILoopAnimationConfig, } from './animations/composition';
16
+ export { nativeAnimated, isNativeAnimatedAvailable, type INativeNodeConfig, type INativeAnimationConfig, type INativeEventMapping, type IPlatformConfig, } from './native/native-animated';
17
+ export { AnimatedProps } from './props';
18
+ export { AnimatedStyle, AnimatedTransform } from './style';
19
+ export { AnimatedMock } from './mock';
20
+ export { reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode, } from './animated-component-shared';
@@ -0,0 +1,28 @@
1
+ // @symbiote-native/engine/animated: the framework-agnostic, JS-driven Animated engine
2
+ // (ADR 0016). The value graph, easing, interpolation and (Phase 2) drivers are
3
+ // pure JS with no React and no native dependency; every adapter re-exports them.
4
+ export { AnimatedNode, AnimatedWithChildren, flushValue } from './graph';
5
+ export { AnimatedValue } from './value';
6
+ export { AnimatedValueXY } from './value-xy';
7
+ export { AnimatedInterpolation } from './interpolation-node';
8
+ export { AnimatedAddition, AnimatedSubtraction, AnimatedMultiplication, AnimatedDivision, AnimatedModulo, AnimatedDiffClamp, add, subtract, multiply, divide, modulo, diffClamp, } from './operators';
9
+ export { AnimatedEvent, event, forkEvent, unforkEvent, attachNativeEvent, attachNativeEventHandler, } from './event';
10
+ export { AnimatedColor } from './color';
11
+ export { Easing } from './easing';
12
+ export { createNumericInterpolation, createInterpolation, checkValidRanges, } from './interpolation';
13
+ export { TimingAnimation } from './animations/timing';
14
+ export { SpringAnimation } from './animations/spring';
15
+ export { DecayAnimation } from './animations/decay';
16
+ export { AnimatedTracking } from './animations/tracking';
17
+ export { timing, spring, decay, parallel, sequence, stagger, loop, delay, } from './animations/composition';
18
+ // The native-driver bridge (ADR 0017). Adapters need it to connect a props leaf to
19
+ // a host view tag and to restore default values on disconnect.
20
+ export { nativeAnimated, isNativeAnimatedAvailable, } from './native/native-animated';
21
+ // The pure graph leaves and the mock, framework-agnostic (extend AnimatedWithChildren,
22
+ // no React/Vue). They live here with the rest of the graph; every adapter's
23
+ // createAnimatedComponent + Animated namespace re-exports them (ADR 0016/0017).
24
+ export { AnimatedProps } from './props';
25
+ export { AnimatedStyle, AnimatedTransform } from './style';
26
+ export { AnimatedMock } from './mock';
27
+ // Framework-agnostic createAnimatedComponent helpers. Both adapters import them.
28
+ export { reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode, } from './animated-component-shared';
@@ -0,0 +1,16 @@
1
+ import { AnimatedNode, AnimatedWithChildren } from './graph';
2
+ import { type IInterpolationConfig } from './interpolation';
3
+ import type { INativeNodeConfig, IPlatformConfig } from './native/native-animated';
4
+ export declare class AnimatedInterpolation extends AnimatedWithChildren {
5
+ private readonly parent;
6
+ private readonly config;
7
+ private interpolation;
8
+ constructor(parent: AnimatedNode, config: IInterpolationConfig);
9
+ private getInterpolation;
10
+ __getValue(): number | string;
11
+ interpolate(config: IInterpolationConfig): AnimatedInterpolation;
12
+ __attach(): void;
13
+ __detach(): void;
14
+ __makeNative(platformConfig?: IPlatformConfig): void;
15
+ __getNativeConfig(): INativeNodeConfig;
16
+ }
@@ -0,0 +1,57 @@
1
+ // AnimatedInterpolation: a graph node that maps its parent's numeric value
2
+ // through an interpolation. Ported from RN's AnimatedInterpolation.js: numeric,
3
+ // string-with-units, and color output ranges (the value graph; native config
4
+ // removed). Platform (Native) colors stay out of scope, color.ts defers them.
5
+ import { AnimatedNode, AnimatedWithChildren } from './graph';
6
+ import { checkValidRanges, createInterpolation } from './interpolation';
7
+ export class AnimatedInterpolation extends AnimatedWithChildren {
8
+ parent;
9
+ config;
10
+ interpolation;
11
+ constructor(parent, config) {
12
+ super();
13
+ this.parent = parent;
14
+ this.config = config;
15
+ // Validate eagerly so a bad range fails at construction, not first frame.
16
+ checkValidRanges(config.inputRange, config.outputRange);
17
+ }
18
+ getInterpolation() {
19
+ if (this.interpolation === undefined) {
20
+ this.interpolation = createInterpolation(this.config);
21
+ }
22
+ return this.interpolation;
23
+ }
24
+ __getValue() {
25
+ const parentValue = this.parent.__getValue();
26
+ if (typeof parentValue !== 'number') {
27
+ throw new Error('Cannot interpolate an input which is not a number');
28
+ }
29
+ return this.getInterpolation()(parentValue);
30
+ }
31
+ interpolate(config) {
32
+ return new AnimatedInterpolation(this, config);
33
+ }
34
+ __attach() {
35
+ this.parent.__addChild(this);
36
+ super.__attach();
37
+ }
38
+ __detach() {
39
+ this.parent.__removeChild(this);
40
+ super.__detach();
41
+ }
42
+ // Make the upstream value native first, so the parent->interpolation edge can be
43
+ // wired when this node is reached from a leaf rather than from the value.
44
+ __makeNative(platformConfig) {
45
+ this.parent.__makeNative(platformConfig);
46
+ super.__makeNative(platformConfig);
47
+ }
48
+ __getNativeConfig() {
49
+ return {
50
+ type: 'interpolation',
51
+ inputRange: this.config.inputRange,
52
+ outputRange: this.config.outputRange,
53
+ extrapolateLeft: this.config.extrapolateLeft ?? this.config.extrapolate ?? 'extend',
54
+ extrapolateRight: this.config.extrapolateRight ?? this.config.extrapolate ?? 'extend',
55
+ };
56
+ }
57
+ }
@@ -0,0 +1,22 @@
1
+ import { type IEasingFunction } from './easing';
2
+ export type IExtrapolateType = 'extend' | 'identity' | 'clamp';
3
+ export interface IInterpolationConfig {
4
+ inputRange: readonly number[];
5
+ outputRange: readonly number[] | readonly string[];
6
+ easing?: IEasingFunction;
7
+ extrapolate?: IExtrapolateType;
8
+ extrapolateLeft?: IExtrapolateType;
9
+ extrapolateRight?: IExtrapolateType;
10
+ }
11
+ interface INumericInterpolationConfig {
12
+ inputRange: readonly number[];
13
+ outputRange: readonly number[];
14
+ easing?: IEasingFunction;
15
+ extrapolate?: IExtrapolateType;
16
+ extrapolateLeft?: IExtrapolateType;
17
+ extrapolateRight?: IExtrapolateType;
18
+ }
19
+ export declare function checkValidRanges(inputRange: readonly number[], outputRange: readonly number[] | readonly string[]): void;
20
+ export declare function createNumericInterpolation(config: INumericInterpolationConfig): (input: number) => number;
21
+ export declare function createInterpolation(config: IInterpolationConfig): (input: number) => number | string;
22
+ export {};