@ng-native/fabric 0.0.1 → 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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -1
  3. package/dist/color-mix.d.ts +20 -0
  4. package/dist/color-mix.js +306 -0
  5. package/dist/color-mix.js.map +1 -0
  6. package/dist/css.d.ts +615 -0
  7. package/dist/css.js +1405 -0
  8. package/dist/css.js.map +1 -0
  9. package/dist/defer-triggers.d.ts +53 -0
  10. package/dist/defer-triggers.js +184 -0
  11. package/dist/defer-triggers.js.map +1 -0
  12. package/dist/engine.d.ts +1066 -0
  13. package/dist/engine.js +2764 -0
  14. package/dist/engine.js.map +1 -0
  15. package/dist/fabric.d.ts +20 -0
  16. package/dist/fabric.js +44 -0
  17. package/dist/fabric.js.map +1 -0
  18. package/dist/forms-inputs.d.ts +15 -0
  19. package/dist/forms-inputs.js +54 -0
  20. package/dist/forms-inputs.js.map +1 -0
  21. package/dist/host.d.ts +133 -0
  22. package/dist/host.js +55 -0
  23. package/dist/host.js.map +1 -0
  24. package/dist/index.d.ts +20 -0
  25. package/dist/index.js +21 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/inline-token.d.ts +12 -0
  28. package/dist/inline-token.js +52 -0
  29. package/dist/inline-token.js.map +1 -0
  30. package/dist/inline-transform.d.ts +14 -0
  31. package/dist/inline-transform.js +41 -0
  32. package/dist/inline-transform.js.map +1 -0
  33. package/dist/native-drive.d.ts +100 -0
  34. package/dist/native-drive.js +318 -0
  35. package/dist/native-drive.js.map +1 -0
  36. package/dist/relative-colour.d.ts +17 -0
  37. package/dist/relative-colour.js +71 -0
  38. package/dist/relative-colour.js.map +1 -0
  39. package/dist/scroll-animation.d.ts +41 -0
  40. package/dist/scroll-animation.js +169 -0
  41. package/dist/scroll-animation.js.map +1 -0
  42. package/dist/transition.d.ts +139 -0
  43. package/dist/transition.js +569 -0
  44. package/dist/transition.js.map +1 -0
  45. package/package.json +43 -3
@@ -0,0 +1,1066 @@
1
+ /**
2
+ * Framework-agnostic retained tree + incremental Fabric commit.
3
+ * Nothing Angular-specific may enter this file; lint bans the import (see ARCHITECTURE.md).
4
+ *
5
+ * Angular mutates; Fabric is persistent. A committed node is never mutated, it is cloned with
6
+ * new props and/or a new child set. Cloning a leaf therefore forces every ancestor to re-clone,
7
+ * because a persistent parent holds references to specific child handles. That bubble is
8
+ * inherent and is what React's own Fabric renderer does.
9
+ */
10
+ import { type Conditions, type StyleCache, type StyleSheet, type TokenValue } from './css.ts';
11
+ import { type AnimationSpec, type RunningAnimation, type Transition } from './transition.ts';
12
+ import { type DrivenProperty, type EventFeed, type NativeAnimated, type ScrollAxis, type ScrollDrive, type ScrollRange, type StaticTransform } from './native-drive.ts';
13
+ import type { DrivenChannels } from './scroll-animation.ts';
14
+ export type FabricNode = {
15
+ readonly __fabricNode: unique symbol;
16
+ } | object;
17
+ /** Where a node is, in the window's coordinates. What `Engine.measure` reports. */
18
+ import type { HostEngine, HostNode, Settling } from './host.ts';
19
+ /** A node's scroll-driven animation: what it plays, what drives it, and the frame it commits. */
20
+ interface ScrollAnimation {
21
+ readonly spec: AnimationSpec;
22
+ readonly tracks: ReadonlyMap<string, readonly {
23
+ offset: number;
24
+ value: unknown;
25
+ }[]>;
26
+ readonly resting: Record<string, unknown>;
27
+ readonly first: Record<string, unknown>;
28
+ readonly source: EngineNode | null;
29
+ readonly drive: {
30
+ update(channels: DrivenChannels): void;
31
+ stop(): void;
32
+ } | null;
33
+ }
34
+ export interface WindowFrame {
35
+ readonly x: number;
36
+ readonly y: number;
37
+ readonly width: number;
38
+ readonly height: number;
39
+ }
40
+ export type FabricNodeSet = unknown;
41
+ /** The slice of `global.nativeFabricUIManager` we actually use. */
42
+ export interface FabricUIManager {
43
+ createNode(reactTag: number, viewName: string, rootTag: number, props: object, instanceHandle: unknown): FabricNode;
44
+ /** Clone with the *given* children (omitting the argument means none) and the same props. */
45
+ cloneNodeWithNewChildren(node: FabricNode): FabricNode;
46
+ /** Clone with the given props and the *same* children. */
47
+ cloneNodeWithNewProps(node: FabricNode, newProps: object): FabricNode;
48
+ /** Clone with the given props and the given children (omitting them means none). */
49
+ cloneNodeWithNewChildrenAndProps(node: FabricNode, newProps: object): FabricNode;
50
+ appendChild(parent: FabricNode, child: FabricNode): FabricNode;
51
+ createChildSet(rootTag: number): FabricNodeSet;
52
+ appendChildToSet(set: FabricNodeSet, child: FabricNode): void;
53
+ completeRoot(rootTag: number, set: FabricNodeSet): void;
54
+ registerEventHandler?(handler: (instanceHandle: unknown, topLevelType: string, nativeEvent: unknown) => void): void;
55
+ /** Tells the native side that JS owns the gesture, and whether to stop its own. */
56
+ setIsJSResponder?(node: FabricNode, isResponder: boolean, blockNativeResponder: boolean): void;
57
+ /** A view command: `focus` on a text input, `scrollTo` on a scroll view. */
58
+ dispatchCommand?(node: FabricNode, name: string, args: readonly unknown[]): void;
59
+ /**
60
+ * A node's frame in window coordinates, which is the only frame two nodes can be compared in.
61
+ *
62
+ * Optional because an older host may not have it, and because the callback is the platform's
63
+ * own shape: on the new architecture it fires before this returns, which is what lets an
64
+ * overlay place itself in the same frame it was opened in.
65
+ */
66
+ measureInWindow?(node: FabricNode, onSuccess: (x: number, y: number, width: number, height: number) => void): void;
67
+ }
68
+ /**
69
+ * What a listener receives. RN's synthetic event carries more, but `nativeEvent` is the part
70
+ * every RN API is documented against, and `stopPropagation` is the part a nested control needs.
71
+ *
72
+ * `stopPropagation` ends the walk to the root after the current node, as it does in the DOM and
73
+ * on RN's `SyntheticEvent`: the other listeners on the node that called it still run. It does not
74
+ * touch the responder negotiation, which has already finished by the time a listener sees the
75
+ * event, so it cannot take a press away from a pressable; the negotiation already keeps an inner
76
+ * pressable's press from reaching the one around it.
77
+ */
78
+ export interface NativeSyntheticEvent<T = unknown> {
79
+ readonly nativeEvent: T;
80
+ stopPropagation(): void;
81
+ isPropagationStopped(): boolean;
82
+ }
83
+ /**
84
+ * The event object a dispatch hands every listener along the path. Exported so another host can
85
+ * hand its listeners the same shape rather than a bare `{ nativeEvent }` that lacks the methods
86
+ * the type promises.
87
+ */
88
+ export declare class SyntheticEvent<T = unknown> implements NativeSyntheticEvent<T> {
89
+ readonly nativeEvent: T;
90
+ private stopped;
91
+ constructor(nativeEvent: T);
92
+ stopPropagation(): void;
93
+ isPropagationStopped(): boolean;
94
+ }
95
+ /**
96
+ * The JS responder system: RN's second touch layer, and the renderer's job rather than the app's.
97
+ *
98
+ * Fabric delivers raw touches to whichever node was hit. On top of that, React runs a negotiation
99
+ * deciding *who owns the gesture*: a capture pass from the root down, then a bubble pass from the
100
+ * target up, electing a single responder that alone receives grant/move/release/terminate. When
101
+ * ownership changes the renderer tells native through `setIsJSResponder`, which is how a JS
102
+ * control stops a native scroll view from also scrolling.
103
+ *
104
+ * All of RN's `Pressability` sits on this, never on raw touch events. Without it there is no way
105
+ * for a button and an enclosing scroll view to agree on who owns a drag.
106
+ */
107
+ /**
108
+ * What a touch event carries. The responder system is touch-only, so its handlers are typed
109
+ * against this rather than `unknown`: every consumer wants the coordinates and would otherwise
110
+ * cast to get at them.
111
+ */
112
+ export interface TouchPayload {
113
+ readonly pageX?: number;
114
+ readonly pageY?: number;
115
+ readonly locationX?: number;
116
+ readonly locationY?: number;
117
+ readonly identifier?: number;
118
+ readonly target?: number;
119
+ readonly timestamp?: number;
120
+ }
121
+ export type ResponderEvent = NativeSyntheticEvent<TouchPayload>;
122
+ export interface ResponderHandlers {
123
+ /** The `target` is the node the touch landed on, which a capturing ancestor may inspect. */
124
+ onStartShouldSetResponderCapture?(event: ResponderEvent, target: EngineNode): boolean;
125
+ onStartShouldSetResponder?(event: ResponderEvent, target: EngineNode): boolean;
126
+ onMoveShouldSetResponderCapture?(event: ResponderEvent, target: EngineNode): boolean;
127
+ onMoveShouldSetResponder?(event: ResponderEvent, target: EngineNode): boolean;
128
+ onResponderGrant?(event: ResponderEvent): void;
129
+ onResponderMove?(event: ResponderEvent): void;
130
+ onResponderRelease?(event: ResponderEvent): void;
131
+ onResponderTerminate?(event: ResponderEvent): void;
132
+ /** Return false to keep the gesture when something else asks for it. Defaults to allowing. */
133
+ onResponderTerminationRequest?(event: ResponderEvent): boolean;
134
+ /** Ask native to stop its own gesture while we hold the responder (a slider inside a list). */
135
+ blockNativeResponder?: boolean;
136
+ }
137
+ export type NodeKind = 'element' | 'text' | 'anchor';
138
+ /** What Fabric currently holds for a node. `null` until the node has been committed once. */
139
+ interface Committed {
140
+ handle: FabricNode;
141
+ /**
142
+ * The react tag this node was created with. Kept because the native animation driver addresses
143
+ * a view by tag and has no other way to find one of ours: `findNodeHandle` returns a number it
144
+ * is given unchanged, so a tag is all an animated graph needs to point here.
145
+ */
146
+ tag: number;
147
+ props: Record<string, unknown>;
148
+ /** The child handles this node was committed with, in order. */
149
+ childHandles: FabricNode[];
150
+ }
151
+ export interface EngineNode extends HostNode {
152
+ readonly kind: NodeKind;
153
+ /** Lowercase template spelling, e.g. `view`. Never the native view name. */
154
+ readonly name: string;
155
+ /** `name` again, for Angular 22.0, which reads it on a component's host element. */
156
+ readonly tagName: string;
157
+ props: Record<string, unknown>;
158
+ text: string;
159
+ children: EngineNode[];
160
+ parent: EngineNode | null;
161
+ listeners: Map<string, Set<(event: unknown) => void>> | null;
162
+ /** CSS classes on this node, for selector matching. */
163
+ classes: Set<string> | null;
164
+ /** The compiled stylesheet of the component that created this node, if any. */
165
+ sheet: StyleSheet | null;
166
+ /** The sheet of the component this node hosts, if it hosts one. What `:host` matches against. */
167
+ hostSheet: StyleSheet | null;
168
+ /** Custom properties set on the node itself; see `StyleTarget.customProperties`. */
169
+ customProperties: Record<string, TokenValue> | null;
170
+ /** Resolved style, memoised across commits. Owned by the CSS resolver. */
171
+ styleCache: StyleCache | null;
172
+ /** Set when something that could change what this node matches has changed. */
173
+ styleDirty: boolean;
174
+ /** `:focus`, from the native focus and blur events. */
175
+ focused?: boolean;
176
+ /** `:active`, set on the responder and every ancestor of it. */
177
+ active?: boolean;
178
+ /**
179
+ * The style object the platform adapter made for this node, and may therefore write into.
180
+ *
181
+ * Angular sets style properties one at a time, so building a fresh object per property means a
182
+ * style of ten properties costs ten copies of a growing object, for every element that has one.
183
+ * An object the adapter created is its to mutate; one that arrived any other way is copied,
184
+ * once. A field rather than a `WeakSet` of such objects, which Hermes makes slow to consult.
185
+ */
186
+ ownStyle?: object;
187
+ /** The resolved style this node was last committed with, for the skip check in `reconcile`. */
188
+ styleCommitted: StyleCache | null;
189
+ /**
190
+ * Per-property transition state, for every property this node has ever seen a `transition` for.
191
+ * A finished entry stays, because its target is what the next change is measured against.
192
+ */
193
+ transitions?: Map<string, Transition>;
194
+ /** The `@keyframes` animation this node is playing, if any. One at a time. */
195
+ playing?: RunningAnimation;
196
+ /** An animation played by a scroll view's offset rather than the clock. See `scrollAnimated`. */
197
+ scrolled?: ScrollAnimation;
198
+ /**
199
+ * The last `transform` built from the individual transform properties, and what it was built
200
+ * from. Reused while the parts are the same objects, so an unrelated change does not re-send it.
201
+ */
202
+ composedTransform?: {
203
+ parts: readonly unknown[];
204
+ value: unknown[];
205
+ };
206
+ /**
207
+ * Set by the component that owns this element. A host primitive that reaches a commit without it
208
+ * was written in a template that never imported the component, which is reported in dev.
209
+ */
210
+ claimed?: true;
211
+ /**
212
+ * Set by the platform adapter when Angular mounts a component on this element. What makes an
213
+ * `<x-card>` host legitimate where a `<veiw>` typo is not, since both are names no view table
214
+ * knows. See `markComponentHost`.
215
+ */
216
+ componentHost?: true;
217
+ /**
218
+ * Hoisted nodes (`registerHoist`) inside this subtree while it is out of the tree, held here
219
+ * rather than by the engine. See `Engine.releaseDetached`.
220
+ */
221
+ dormantHoists?: EngineNode[] | null;
222
+ committed: Committed | null;
223
+ /** The node this one was committed as a child of, which native holds it to for its life. */
224
+ committedUnder: EngineNode | null;
225
+ /** The hoisted views this node keeps committed for, by element name. See `HoistOptions`. */
226
+ kept?: Map<string, KeptHoist>;
227
+ /** Commit this node again with its children so Fabric measures it again. `remeasureText`. */
228
+ remeasure?: true;
229
+ /**
230
+ * A modal host committed as visible on iOS whose dismissal native has not yet reported. It
231
+ * stays committed after `visible` goes false until `topDismiss`, as React Native's Modal.js
232
+ * keeps it for the dismissal animation. See `Engine.withheld`.
233
+ */
234
+ presented?: true;
235
+ /** This node's own props changed. */
236
+ propsDirty: boolean;
237
+ /** This node's child list changed. */
238
+ structureDirty: boolean;
239
+ /** Something below this node is dirty, so the walk must descend. */
240
+ subtreeDirty: boolean;
241
+ }
242
+ /**
243
+ * Declare props the native view behind an element accepts. `@ng-native/components` calls this
244
+ * once per host primitive with the component's inputs; an app driving a primitive's native view
245
+ * with a prop the component has no input for can declare that prop too.
246
+ */
247
+ export declare function declareNativeProps(elementName: string, props: Iterable<string>): void;
248
+ /**
249
+ * Mark a host element as owned by its component. The components package calls this from each
250
+ * host primitive's constructor; see `EngineNode.claimed`.
251
+ */
252
+ export declare function claimHost(node: HostNode): void;
253
+ /**
254
+ * Mark an element as the host of a component. The platform adapter calls this from
255
+ * `RendererFactory2.createRenderer`, which Angular calls once for every component it mounts,
256
+ * with the host element in hand, whatever the component's selector.
257
+ */
258
+ export declare function markComponentHost(node: HostNode): void;
259
+ /** Names Android registers differently. */
260
+ export declare const ANDROID_VIEW_NAMES: Record<string, string>;
261
+ export declare function nativePlatform(): string;
262
+ /**
263
+ * Call once at startup with `Platform.OS`. The view names above are the iOS/canonical ones, so
264
+ * only the mapping half is a no-op elsewhere.
265
+ */
266
+ export declare function registerPlatformComponents(platform: string): void;
267
+ /**
268
+ * Register a codegen'd third-party Fabric component, e.g.
269
+ * `registerViewName('rns-screen', 'RNSScreen')`, which is how the router reaches
270
+ * react-native-screens.
271
+ *
272
+ * `defaultProps` is for a component whose React wrapper supplies a base style in JS, the way
273
+ * `ScrollView` and `Modal` do below. Driving the native component directly skips that wrapper,
274
+ * and skipping it is not cosmetic: `RNSScreenStackHeaderConfig` without its `position: absolute`
275
+ * joins the screen's flex layout and pushes every page down by the height of the header.
276
+ */
277
+ export declare function registerViewName(elementName: string, viewName: string | PlatformViewName, defaultProps?: Record<string, unknown>): void;
278
+ /**
279
+ * A view whose native name differs by platform. Chosen when the view is looked up rather than when
280
+ * it is registered, so it does not matter which ran first: an app's imports register the router's
281
+ * views before its `main.ts` gets to report the platform.
282
+ */
283
+ export interface PlatformViewName {
284
+ readonly ios: string;
285
+ readonly android: string;
286
+ }
287
+ /**
288
+ * Commit every `elementName` as a direct child of its nearest `ancestorName`, wherever the
289
+ * template put it, e.g. `registerHoist('native-header', 'screen')`.
290
+ *
291
+ * For a native component that reads a configuration node from its own direct children and
292
+ * nowhere else. `RNSScreen.mm` finds its header that way, so a header written inside a
293
+ * `safe-area-view`, or inside any component a page wraps itself in, was never found: no title,
294
+ * no back button, and nothing said why. The retained tree keeps the node where Angular put it,
295
+ * so styles, `@if` and removal all behave as written; only what reaches Fabric moves. It lands
296
+ * just after the ancestor's child it was written in, or after all of them when it was written in
297
+ * the ancestor itself, which keeps a page's scroll view ahead of its header as UIKit's large
298
+ * title wants. A node with no such ancestor commits where it is.
299
+ */
300
+ export declare function registerHoist(elementName: string, ancestorName: string, options?: HoistOptions): void;
301
+ export interface HoistOptions {
302
+ /**
303
+ * Props for a native view the ancestor keeps once the element has committed into it.
304
+ *
305
+ * For a configuration node the native side reads when it arrives but never forgets when it
306
+ * leaves. `RNSScreen` hides its navigation bar when a header config it holds says `hidden`, and
307
+ * does nothing at all when the config is taken away, so the last bar it was told about stays on
308
+ * screen. With a stand-in, removing the element commits these props onto the same native view,
309
+ * children dropped, and the next element of that name adopts the view rather than creating
310
+ * another, so what the native side sees is one config that is updated, never one that goes.
311
+ */
312
+ standIn?: Record<string, unknown>;
313
+ }
314
+ /** A native view an ancestor keeps for a hoisted name with a stand-in, and who it is for now. */
315
+ interface KeptHoist {
316
+ /** The element committed as this view, or null while the stand-in props are. */
317
+ node: EngineNode | null;
318
+ committed: Committed;
319
+ }
320
+ /** The part of a node `viewNameOf` reads: an engine node, or any host's mirror of one. */
321
+ export interface ViewNameNode {
322
+ readonly kind: string;
323
+ readonly name: string;
324
+ readonly parent: {
325
+ readonly name: string;
326
+ } | null;
327
+ }
328
+ /**
329
+ * The native view a node commits as: `Paragraph` for a `<text>`, `VirtualText` for one nested in
330
+ * another, `RawText` for a run of text, and `View` for anything unregistered, which is what an
331
+ * Angular component's host element is.
332
+ *
333
+ * Exported for tools that show a template beside what it becomes, such as the X-ray in the
334
+ * documentation's course, which labels `@ng-native/web`'s DOM with these names. It reads the same
335
+ * table the commit does, so after `registerPlatformComponents('android')` it answers for Android.
336
+ */
337
+ export declare function viewNameOf(node: ViewNameNode): string;
338
+ /** Whether a value is one of the engine's own nodes. */
339
+ export declare function isEngineNode(value: unknown): value is EngineNode;
340
+ /**
341
+ * RN-specific helpers, injected rather than imported so the engine stays runnable under Node.
342
+ */
343
+ export interface EngineOptions {
344
+ /**
345
+ * React Native's `NativeAnimatedHelper`, which lets native move a view by a scroll with no
346
+ * JavaScript in between: how sticky headers stay pinned on a fast fling. The platform supplies it
347
+ * on a device; without it, the components move those views from JavaScript as before.
348
+ */
349
+ nativeAnimated?: NativeAnimated | null;
350
+ /**
351
+ * RN's `processColor`. Narrower than `unknown` on purpose: it is only ever handed the value of
352
+ * a `*color` prop, and typing it that way is what lets an app pass RN's function straight in
353
+ * rather than casting at every call site.
354
+ */
355
+ processColor?: (value: string | number) => unknown;
356
+ /**
357
+ * RN's `resolveAssetSource`. `require('./x.png')` compiles to an asset *id*, and only this
358
+ * turns it into `{uri, width, height, scale}`. Without it local images cannot work at all,
359
+ * while remote `{uri}` objects pass through and appear to prove the prop is fine.
360
+ */
361
+ resolveAssetSource?: (value: unknown) => unknown;
362
+ /**
363
+ * The application-level sheet, compiled at build time. The one set of rules allowed to match a
364
+ * node whatever component created it, which is what resets, utility classes and `:root` tokens
365
+ * all need.
366
+ */
367
+ globalStyles?: StyleSheet | null;
368
+ /**
369
+ * What media queries are evaluated against. Pushed in rather than read, because the engine
370
+ * imports nothing from React Native and a host may have its own idea of the viewport.
371
+ */
372
+ conditions?: Conditions;
373
+ /**
374
+ * Custom properties the device knows and the stylesheet cannot: the hairline width, and
375
+ * whatever else is settled before the first frame. Seeded on the root, below `:root`, so an
376
+ * app's own sheet still overrides them. Values that only arrive later - the safe-area insets,
377
+ * which nothing knows until a view has been laid out - come through `updateTokens` instead.
378
+ */
379
+ tokens?: Readonly<Record<string, TokenValue>>;
380
+ /**
381
+ * Report host primitives used without their component (see `claimHost`). Defaults to RN's
382
+ * `__DEV__`, so a release build never looks.
383
+ */
384
+ dev?: boolean;
385
+ /**
386
+ * The clock CSS transitions run on. Pushed in for the same reason everything else is: the
387
+ * engine imports nothing, and a test drives time rather than waiting for it.
388
+ */
389
+ now?: () => number;
390
+ /**
391
+ * Called when the tree goes from clean to dirty. The host uses it to schedule a commit for a
392
+ * change that arrives outside change detection, which is where Angular's animation queue puts
393
+ * the classes for `animate.enter` and `animate.leave`.
394
+ */
395
+ onDirty?: () => void;
396
+ /**
397
+ * Where an error thrown while a native event is being dispatched goes, with the event's name.
398
+ * The platform adapter points it at the app's `ErrorHandler`. Without one it is logged, and
399
+ * either way it is never rethrown: see `dispatchEvent` for why.
400
+ */
401
+ onError?: (error: unknown, topLevelType: string) => void;
402
+ }
403
+ /** Dev-time commit accounting, so a slow frame can be attributed rather than guessed at. */
404
+ export interface EngineStats {
405
+ commits: number;
406
+ lastCommitMs: number;
407
+ /** The initial mount, which is a different animal from a steady-state update. */
408
+ firstCommitMs: number;
409
+ /** Worst commit *after* the mount. This is the number that matters for frame rate. */
410
+ worstCommitMs: number;
411
+ /** Commits that blew a 60fps frame budget, excluding the mount. */
412
+ slowCommits: number;
413
+ /**
414
+ * Worst `begin()`..`end()` span, excluding the mount. This brackets Angular's whole render
415
+ * pass - template execution, bindings, the commit - so comparing it against `worstCommitMs`
416
+ * says whether time is going into the renderer or into change detection around it.
417
+ */
418
+ worstRenderMs: number;
419
+ createdNodes: number;
420
+ clonedNodes: number;
421
+ }
422
+ export declare class Engine implements HostEngine {
423
+ /** Scopes the CSS resolver's memo. Bumped once per commit. */
424
+ private styleEpoch;
425
+ /** The node `:focus` currently applies to. */
426
+ private focusedNode;
427
+ readonly stats: EngineStats;
428
+ private eventsRegistered;
429
+ readonly root: EngineNode;
430
+ private readonly fabric;
431
+ private readonly rootTag;
432
+ private readonly styles;
433
+ /** Whether to run the development checks, which a release build skips. */
434
+ readonly dev: boolean;
435
+ private readonly now;
436
+ private onDirty;
437
+ private onError;
438
+ /** Nodes with at least one transition in flight, so a frame walks those and nothing else. */
439
+ private readonly running;
440
+ /** Nodes playing a `@keyframes` animation. */
441
+ private readonly playing;
442
+ /**
443
+ * Elements of a hoisted name (`registerHoist`) that are in the tree, so a commit need not search
444
+ * for them. Only those in the tree: see `releaseDetached`.
445
+ */
446
+ private readonly hoisted;
447
+ /** Whether a child list has lost a member since the last commit. See `releaseDetached`. */
448
+ private removedSinceCommit;
449
+ /**
450
+ * `@keyframes` by name, across every sheet seen so far. One registry rather than one per sheet,
451
+ * because that is the scope CSS gives them: a name defined in a global stylesheet is usable
452
+ * from a component's. The cost is that two components defining the same name collide, last one
453
+ * in wins, exactly as two stylesheets in a document would.
454
+ */
455
+ private readonly keyframes;
456
+ /** Element names already reported as unclaimed, so a list of a thousand rows reports once. */
457
+ private readonly reported;
458
+ private readonly processColor;
459
+ /**
460
+ * Whether any sheet seen so far asks about a node's position among its siblings. Until one
461
+ * does, a child list can move without anything else needing to be looked at again.
462
+ */
463
+ private structuralSheets;
464
+ private readonly resolveAssetSource;
465
+ constructor(fabric: FabricUIManager, rootTag: number, options?: EngineOptions);
466
+ /**
467
+ * A rotation, a window resize, or the system switching to dark mode.
468
+ *
469
+ * None of those change a single node, so nothing would be dirty and nothing would re-render.
470
+ * Marking the root is what gets the tree walked again; every cached style is already invalid,
471
+ * because each carries the conditions version it was resolved under.
472
+ */
473
+ /**
474
+ * Move `:focus`, invalidating only the two nodes involved.
475
+ *
476
+ * Precise rather than by generation: unlike a rotation, the engine knows exactly which nodes
477
+ * changed, and a keystroke's worth of focus movement should not invalidate the tree.
478
+ */
479
+ private setFocused;
480
+ /**
481
+ * Set or clear `:active` along a responder chain, marking each node so its style re-resolves.
482
+ *
483
+ * Returns whether anything moved, because the caller has to commit: a gesture changes no
484
+ * binding, so nothing marks an Angular view dirty and no change-detection pass would follow.
485
+ */
486
+ private setActiveChain;
487
+ /**
488
+ * Custom properties the device knows and the stylesheet cannot, once they change.
489
+ *
490
+ * Merged rather than replaced: the insets come from a view and the hairline from the device, so
491
+ * a caller owns the properties it names and nothing else. Each owner writes all of its own at
492
+ * once - four insets together, never one - so nothing is left over from a previous rotation.
493
+ */
494
+ updateTokens(next: Readonly<Record<string, TokenValue>>): void;
495
+ /**
496
+ * The system text size changed: measure every text and text input again, and commit.
497
+ *
498
+ * React Native's surface re-measures its text when the content size category changes, on a
499
+ * commit of its own. The next commit from here is built from the handles the engine holds,
500
+ * which are the nodes from before that, laid out at the old size, and Yoga keeps a clean node's
501
+ * layout: every text whose content had not changed kept its old box, with its glyphs drawn at
502
+ * the new size inside it, clipped. So each one is committed again, cloned with its children,
503
+ * which is what makes Fabric dirty a measured node and measure it at the size now in effect.
504
+ */
505
+ remeasureText(): void;
506
+ /**
507
+ * Put the cursor in a text input; bring anything else on screen, in the scroll view it is in.
508
+ * A form sends the user to the first field that needs fixing this way, and a toggle or a picker
509
+ * has no cursor to take, so it is scrolled to instead.
510
+ */
511
+ focus(node: EngineNode): void;
512
+ /**
513
+ * Scroll the nearest scroll view the node is in by as little as brings the node fully on
514
+ * screen, with `margin` to spare - UIKit's `scrollRectToVisible`. `visibleBottom` is where in
515
+ * the window the visible area ends when something covers the bottom of the scroll view: the
516
+ * keyboard's top edge, for a field the keyboard would otherwise hide.
517
+ *
518
+ * Measured in the window, all of it, so the scroll view's offset needs no tracking: it is how
519
+ * far its content's top edge is above its own.
520
+ */
521
+ reveal(node: EngineNode, options?: {
522
+ visibleBottom?: number;
523
+ margin?: number;
524
+ }): void;
525
+ /**
526
+ * Commit one measured node again, cloned with its children, so Fabric measures it once more.
527
+ *
528
+ * A text input is measured from its native state, and a text set from JavaScript reaches that
529
+ * state only in the layout after the measurement, so the commit carrying the text sizes the
530
+ * field for the text it had before. This is the commit that sizes it for the new one: one of
531
+ * the exceptions to a commit per pass, because waiting for the next pass would leave the field
532
+ * the wrong height until something else changed.
533
+ */
534
+ remeasure(node: EngineNode): void;
535
+ /** The window's size, from the conditions media queries use. Zero until the platform says. */
536
+ get viewport(): {
537
+ readonly width: number;
538
+ readonly height: number;
539
+ };
540
+ private viewportSize;
541
+ updateConditions(next: Conditions): void;
542
+ createElement(name: string, sheet?: StyleSheet | null): EngineNode;
543
+ addClass(node: EngineNode, name: string): void;
544
+ removeClass(node: EngineNode, name: string): void;
545
+ /** `class="a b"` from a template. Replaces the set rather than adding to it. */
546
+ setClasses(node: EngineNode, value: string): void;
547
+ /** Angular's `@if`/`@for`/`ViewContainerRef` markers. Ordered, never committed. */
548
+ createAnchor(): EngineNode;
549
+ createText(value: string): EngineNode;
550
+ appendChild(parent: EngineNode, child: EngineNode): void;
551
+ insertBefore(parent: EngineNode, child: EngineNode, ref: EngineNode | null): void;
552
+ removeChild(parent: EngineNode | null, child: EngineNode): void;
553
+ /** A subtree that went out of the tree is back in: its hoisted nodes commit again. */
554
+ private wakeHoists;
555
+ /**
556
+ * Let go of everything the engine holds for nodes that have left the tree.
557
+ *
558
+ * Removal is the only signal a destroyed subtree reliably sends. Angular calls `destroyNode` for
559
+ * the top-level nodes of the view it destroys and not for anything inside a child component's
560
+ * view, so a screen's own template is never told it is gone. A reference the engine keeps to any
561
+ * one node of such a subtree keeps all of it, because a node reaches every other node of its
562
+ * subtree through its parent and children, and each of those holds its committed Fabric handle,
563
+ * which keeps its native shadow node alive. A screen with a header was retained in full that way,
564
+ * every native node of it, for as long as the app ran.
565
+ *
566
+ * Removal is not destruction, though: a detached screen can be put back, and must come back
567
+ * whole. So a hoisted node moves to the root of the subtree it left with, which is garbage along
568
+ * with it if the subtree never returns, and is handed back when it does. An animation on a node
569
+ * out of the tree stops rather than running a frame loop for something nobody can see, and
570
+ * starts again if the node returns.
571
+ *
572
+ * Once per commit, and only after a removal: the walks are the depth of a handful of nodes.
573
+ */
574
+ private releaseDetached;
575
+ /** The root of the tree a node is in: the engine's root while it is attached. */
576
+ private topOf;
577
+ /**
578
+ * Angular calls this per node on view destroy, but only when `Renderer2.destroyNode` is
579
+ * non-null. Without it the listener sets on discarded nodes stay reachable from the
580
+ * closures Angular hands us, which is a slow leak on any screen that churns views.
581
+ */
582
+ destroyNode(node: EngineNode): void;
583
+ parentNode(node: EngineNode): EngineNode | null;
584
+ nextSibling(node: EngineNode): EngineNode | null;
585
+ setProp(node: EngineNode, key: string, value: unknown): void;
586
+ /** Whether a node's inline style sets `direction`, remembering that one has. */
587
+ private noteInlineDirection;
588
+ /**
589
+ * The style object a node already holds has been changed in place.
590
+ *
591
+ * Angular's styling instructions set one property at a time, so a style of ten properties would
592
+ * otherwise mean ten copies of a growing object per element. The adapter owns the object it put
593
+ * there and may write into it; this is how it says so, since nothing about the object's identity
594
+ * has changed for the props diff to notice.
595
+ */
596
+ styleChanged(node: EngineNode): void;
597
+ /**
598
+ * Set a custom property on a node, `--tint` from `[style.--tint]`, or remove it with `null`.
599
+ *
600
+ * Not a prop: Fabric has no such thing, and would be sent one. It is a token for the cascade,
601
+ * converted from the value here because nothing built it ahead of time (`inline-token.ts`), and
602
+ * changing it re-resolves the node and so everything under it.
603
+ */
604
+ setCustomProperty(node: EngineNode, name: string, value: unknown): void;
605
+ setText(node: EngineNode, value: string): void;
606
+ setEventListener(node: EngineNode, topLevelType: string, fn: (event: unknown) => void): () => void;
607
+ private markProps;
608
+ private markLaterSiblings;
609
+ private markStructure;
610
+ /** Walk up until we hit a node already known to have a dirty subtree. */
611
+ private markPath;
612
+ private isClean;
613
+ /**
614
+ * Incremental: clone only what changed and reuse untouched subtrees by reference. A full
615
+ * rebuild would mint fresh shadow nodes for every node on every tick, which drops scroll
616
+ * offset, text cursor and keyboard focus. That is a correctness requirement, not an
617
+ * optimisation.
618
+ */
619
+ commit(): boolean;
620
+ private readonly scrollDriver;
621
+ /** Whether native can move a view by a scroll with no JavaScript in between. */
622
+ get drivesScroll(): boolean;
623
+ /** See `HostEngine.driveByScroll`. The connection waits for a commit of both views. */
624
+ driveByScroll(view: EngineNode, scroll: EngineNode, axis: ScrollAxis, range: ScrollRange, statics?: readonly StaticTransform[]): ScrollDrive | null;
625
+ /** See `HostEngine.driveByEvent`. */
626
+ driveByEvent(view: EngineNode, source: EngineNode, feed: EventFeed, property: DrivenProperty, range: ScrollRange, statics?: readonly StaticTransform[], shift?: number | null): ScrollDrive | null;
627
+ /**
628
+ * `transitionstart` and `transitionend`, for anything listening.
629
+ *
630
+ * Angular's `animate.enter` and `animate.leave` are built on these: they add a class, wait for
631
+ * the transition it starts, and clean up when it ends. Nothing native emits them - there is no
632
+ * CSS animation in React Native to emit them - so the engine that runs the transition is what
633
+ * says when one began and ended.
634
+ */
635
+ /** Created since the last commit and waiting to settle. See `HostEngine.settle`. */
636
+ private unsettled;
637
+ settle(target: Settling): void;
638
+ /**
639
+ * Let every element created since the last commit write what its inputs say. One that was
640
+ * checked already has, and answers at once; one whose view is detached writes its props here,
641
+ * so the commit that first mounts it carries them.
642
+ */
643
+ private settleCreated;
644
+ private readonly transitionEvents;
645
+ private emitTransition;
646
+ /**
647
+ * After the commit, never during it: a listener is free to add a class or drop an element, and
648
+ * a tree that changes halfway through being walked is the one thing a commit cannot survive.
649
+ */
650
+ private flushTransitionEvents;
651
+ /** Anchors take part in sibling ordering but never reach Fabric. */
652
+ private visibleChildren;
653
+ /**
654
+ * Everything a node renders with, unprocessed. Precedence, weakest first: native defaults,
655
+ * matched CSS, explicit props, inline style, a component's `styleOverride`. Inline wins over CSS for the same reason it does
656
+ * on the web.
657
+ *
658
+ * Raw, because this is what the next commit diffs against. Colours and asset ids are converted
659
+ * on the way out instead (`processed`), and only for the keys that changed: the converters
660
+ * return fresh objects, so diffing their output would re-send every image and shadow on every
661
+ * unrelated change.
662
+ */
663
+ private mergeProps;
664
+ /** Set once any node has had a `direction` in its inline style. See `directionOf`. */
665
+ private inlineDirection;
666
+ /**
667
+ * The direction a paragraph is laid out in, where the app wrote one: its own, or the nearest
668
+ * ancestor's. Undefined when nothing above it says, which leaves the platform's own.
669
+ *
670
+ * A stylesheet's `direction` is inherited through the cascade, so it is already in `props`. An
671
+ * inline one is not, and is found by walking up, which only happens once some node has had one.
672
+ */
673
+ private directionOf;
674
+ /**
675
+ * Hold back what a transition is meant to ease into.
676
+ *
677
+ * Called with everything the node would otherwise commit with, so a property is compared
678
+ * against the last value aimed at rather than the last one painted: a transition redirected
679
+ * halfway carries on from where it had got to instead of snapping.
680
+ *
681
+ * A property seen for the first time is recorded and left alone. That is CSS's own rule, and
682
+ * without it every element would fade in from whatever the previous value happened to be.
683
+ */
684
+ /** Take a sheet's `@keyframes` into the registry. Cheap and idempotent; sheets are few. */
685
+ private registerKeyframes;
686
+ private readonly knownSheets;
687
+ /**
688
+ * Start, advance or stop the `@keyframes` animation a node's style asks for.
689
+ *
690
+ * Unlike a transition this does not need anything to have changed: an animation plays from its
691
+ * own frames, which is what lets an element animate in without differing from anything.
692
+ */
693
+ private animated;
694
+ /** Start the clock on an animation, unless the node is already playing this one. */
695
+ private startPlaying;
696
+ /**
697
+ * `animation-play-state` changed on an animation already playing: pausing holds the frame it
698
+ * has reached, and running again carries on from there, the paused time left out of its clock.
699
+ * Neither starts it over, as a change of play state does not in CSS.
700
+ */
701
+ private playOrPause;
702
+ /** Every node playing a scroll-driven animation, by the scroll view that plays it. */
703
+ private readonly scrollTimelines;
704
+ /** How far each such scroll view scrolls, along each axis, once one of its events has said. */
705
+ private readonly scrollExtents;
706
+ /**
707
+ * An animation played by the nearest scroll view's offset: `animation-timeline: scroll()`.
708
+ *
709
+ * Its keyframes are laid along the offset and handed to native, which plays them as the view
710
+ * scrolls, with no JavaScript between (`scroll-animation.ts`, `NativeScrollDriver.animate`).
711
+ * The frame at the offset the view starts at is committed once and then left alone: a prop that
712
+ * changed on a later commit would reset what native is animating.
713
+ */
714
+ private scrollAnimated;
715
+ /** Hand a scroll-driven animation's channels to native, fed by `source`'s scroll events. */
716
+ private driveScrolled;
717
+ /** The scroll view a scroll-driven animation on `node` follows: the nearest one it is inside. */
718
+ private scrollSourceOf;
719
+ private stopScrolled;
720
+ /**
721
+ * A scroll view has said how far it scrolls. Every animation on it with a range that depends on
722
+ * that - none written, or a percentage - is laid out again along the new extent.
723
+ */
724
+ private measuredScroll;
725
+ private reportUnscrolled;
726
+ private reportHeld;
727
+ private reportMissingKeyframes;
728
+ private transitioned;
729
+ /** One `@keyframes` player, one frame on. */
730
+ private advancePlayer;
731
+ /**
732
+ * Late-bound because the host that wants the callback is usually built from this engine, so
733
+ * neither can be constructed with the other in hand.
734
+ */
735
+ setOnDirty(fn: () => void): void;
736
+ /** Late-bound for the same reason: the app's `ErrorHandler` lives in an injector built later. */
737
+ setOnError(fn: (error: unknown, topLevelType: string) => void): void;
738
+ /** Whether any transition or animation still has frames left to run. */
739
+ get animating(): boolean;
740
+ /**
741
+ * Whether anything has changed since the last commit.
742
+ *
743
+ * The frame loop reads this because a `transitionend` listener runs after its commit has
744
+ * finished, and what it does is often to remove the element that just finished leaving. That
745
+ * change needs a commit of its own or the view stays on screen, fully transparent, forever.
746
+ */
747
+ get pending(): boolean;
748
+ /**
749
+ * Advance every transition to `now` and mark what changed. The caller commits afterwards; on a
750
+ * device the platform pumps this from `requestAnimationFrame` for as long as it returns true.
751
+ */
752
+ advanceAnimations(now?: number): boolean;
753
+ /** The host's conversions, applied to a copy. A removed prop travels as null and stays null. */
754
+ private processed;
755
+ /** Copy a list of shadow-like objects, running each `color` through the host's converter. */
756
+ private processNestedColors;
757
+ /**
758
+ * Copy a filter list, running a drop shadow's colour through the host's converter. Android reads
759
+ * that colour as a number and throws on a string, taking the whole surface down with it.
760
+ */
761
+ private processFilters;
762
+ /**
763
+ * Copy a list of gradients, running every stop's colour through the host's converter.
764
+ *
765
+ * A stop's colour is a colour like any other, but it is two levels down and nothing else looks
766
+ * there: without this the device is handed the string `rgb(255, 0, 0)` where it wants a number,
767
+ * and paints nothing.
768
+ */
769
+ private processGradients;
770
+ private clearFlags;
771
+ private reconcile;
772
+ /**
773
+ * Whether a node has to be committed with its children again.
774
+ *
775
+ * A child that re-cloned has a new handle, so the parent must re-clone too. This is the bubble
776
+ * that persistent trees force, and it is why dirty marking propagates upwards. A node to be
777
+ * measured again is cloned with its children even when they are the same ones: new children
778
+ * is what makes Fabric dirty a measured node (`remeasureText`).
779
+ */
780
+ private childrenMoved;
781
+ /**
782
+ * The handles of a node's children, reconciling only the ones that can have changed.
783
+ *
784
+ * One changed row in a list of a thousand makes the list re-clone, and the other 999 are asked
785
+ * for their handle. A clean child whose style still holds under this node's is its committed
786
+ * handle as it stands, so it is answered here without the call, the resolve and the walk -
787
+ * which, per child, was most of what a one-row update cost.
788
+ */
789
+ private reconcileChildren;
790
+ /**
791
+ * Whether a child is left out of its parent's commit: a modal host whose `visible` is false.
792
+ *
793
+ * The native host covers the screen and takes every touch whether or not it presents anything,
794
+ * which is why React Native's Modal.js renders null while hidden. This is the same thing, so a
795
+ * hidden `<modal>` leaves the screen under it usable. On iOS a modal that was showing stays
796
+ * committed, with `visible` false, until native reports `topDismiss`: Modal.js keeps it that
797
+ * long so the dismissal animates and `(dismiss)` fires. Android drops it at once, as Modal.js
798
+ * does there.
799
+ */
800
+ /** Remember an iOS modal host committed as showing, so hiding it waits for `topDismiss`. */
801
+ private notePresented;
802
+ private withheld;
803
+ /**
804
+ * `reconcileChildren` for a node other nodes hoist into: its own children, each followed by
805
+ * the hoisted nodes written inside it, then those written in it directly, then the views it
806
+ * keeps for hoisted nodes that are gone.
807
+ */
808
+ private reconcileHoistTarget;
809
+ /**
810
+ * A child's handle as a child of `parent`, created again when it was committed under another.
811
+ *
812
+ * Native gives a view one parent for as long as it lives: `ShadowNodeFamily::setParent`
813
+ * asserts it in a debug build, and a release build keeps the first parent and measures and lays
814
+ * the view out against the wrong ancestry. A node Angular moves - content projected into a
815
+ * container that was mounted again, a scroll view Android wraps in its swipe layout - is
816
+ * therefore created afresh, with everything under it, as React creates an element that moves.
817
+ * Its native state goes with it, as it would there.
818
+ */
819
+ private reconcileUnder;
820
+ /** Let a node and everything under it be created again at the next commit. */
821
+ private forgetCommitted;
822
+ /** A hoisted node, which commits into an ancestor further up than the parent it is in. */
823
+ private committedElsewhere;
824
+ /**
825
+ * Reconcile a hoisted element committing into `target`, into the native view `target` already
826
+ * keeps for its name when it has one (`HoistOptions.standIn`), and keep the view it commits as.
827
+ */
828
+ private land;
829
+ /**
830
+ * Commit the stand-in props onto every view `target` keeps whose element is gone, on the view
831
+ * itself so the native side sees an update rather than a removal.
832
+ */
833
+ private standIn;
834
+ /** A clean child's committed handle, when its style still holds under its parent's. */
835
+ private stillCommitted;
836
+ /**
837
+ * The hoisted nodes that commit as direct children of `node`, keyed by the child of `node` each
838
+ * was written inside. Null when there are none, which is nearly always.
839
+ */
840
+ private hoistedInto;
841
+ /** Whether a hoisted node commits further up than the parent it was written in. */
842
+ private hoistsAbove;
843
+ private create;
844
+ /**
845
+ * Angular's own unknown-element check does not run in this pipeline, so a `<text>` in a
846
+ * template that never imported `Text` compiles, renders as a plain view, and looks like a
847
+ * layout bug. On the first commit of any known element with no component behind it, say so.
848
+ */
849
+ private checkClaimed;
850
+ /**
851
+ * The misspelt-prop check. Angular's own "Can't bind to" check needs a DOM to run, so a
852
+ * `[numberofLines]` on a `<text>`, or a static `iosBackgroundColor` on a `<switch>`, went to
853
+ * native as a prop no view reads, and nothing said so. A prop on a host primitive that its
854
+ * component did not declare is reported once per element name and prop, with the declared name
855
+ * it most likely meant when there is one.
856
+ */
857
+ private readonly undefinedTokens;
858
+ /** Said once per name: a phone has no inspector to show the declaration struck out. */
859
+ private reportUndefinedToken;
860
+ private checkProps;
861
+ private isDeclared;
862
+ /**
863
+ * Element names some template's directives select by tag: every component with an element
864
+ * selector, and directives such as Angular's `router-outlet` that own an element without
865
+ * hosting a component. Declared by the platform adapter, which sees each template's directive
866
+ * registry as the component is created.
867
+ */
868
+ private readonly declaredElements;
869
+ /** An element name a directive or component selects, and so is not a typo. */
870
+ declareElement(name: string): void;
871
+ /**
872
+ * Attribute names that belong to Angular rather than to a native view: what a directive selects
873
+ * on or takes as an input, and what a component's `<ng-content select>` projects by. Angular
874
+ * writes a static attribute to the node whatever consumes it, so without these a
875
+ * `nativeRouterLink` or a `listHeader` marker would read as a misspelt prop. See `checkProps`.
876
+ */
877
+ private readonly declaredAttributes;
878
+ declareAttribute(name: string): void;
879
+ /**
880
+ * The typo check. The set a name is looked up in is app-wide rather than per template, so a
881
+ * directive imported in one template also quiets its name in another that forgot it. That is
882
+ * the trade for not holding a registry per component, and the typo is the case that matters.
883
+ */
884
+ private checkKnown;
885
+ private readonly responders;
886
+ private currentResponder;
887
+ /** Register a node as a candidate responder. Returns an unsubscribe. */
888
+ setResponder(node: EngineNode, handlers: ResponderHandlers): () => void;
889
+ /**
890
+ * Keep a native view for this node, or stop keeping one.
891
+ *
892
+ * Fabric flattens a view whose props say nothing visual and nothing interactive: no native view
893
+ * is created and its layout is folded into the parent. Responders live here, in JavaScript, and
894
+ * Fabric cannot see them - so a transparent touch-catcher looks exactly like a view worth
895
+ * flattening, and gets flattened, and never receives the touch it exists to catch.
896
+ *
897
+ * That is a popover that will not close. The full-screen catcher over the app has no background
898
+ * and nothing else on it; the dialog's backdrop survived the same treatment only because
899
+ * `bg-black/50` happened to make it visual.
900
+ *
901
+ * Only plain views: flattening is `ViewShadowNode`'s, so a paragraph, a scroll view or a text
902
+ * input is already a native view and marking it would be a prop on every text node in the tree
903
+ * that buys nothing.
904
+ *
905
+ * A floor rather than an override: `collapsable="true"` written on an element is left alone,
906
+ * because someone who writes it means it.
907
+ */
908
+ private keepNative;
909
+ /**
910
+ * A view command, by name: `focus` and `blur` on a text input, `scrollTo` on a scroll view.
911
+ * Fabric addresses the committed shadow node, so a node that has not reached a commit yet has
912
+ * nothing to receive it and the call is dropped.
913
+ */
914
+ dispatchCommand(node: EngineNode, name: string, args?: readonly unknown[]): void;
915
+ /**
916
+ * Where a node is, in window coordinates.
917
+ *
918
+ * `(layout)` reports a frame in the parent's coordinates, which sizes a view and says nothing
919
+ * about where it is relative to anything else. This is what an anchored overlay needs: a
920
+ * popover and its trigger live in different parts of the tree and can only be compared in the
921
+ * window's frame.
922
+ *
923
+ * The callback is the platform's own shape rather than a promise, and on the new architecture
924
+ * it fires before this returns - so a caller that needs the value immediately can capture it,
925
+ * and one that does not is unaffected if that ever changes.
926
+ *
927
+ * Silent when the node has never been committed, or when the host has no `measureInWindow`.
928
+ * Reporting zeroes would be worse than reporting nothing: a zero frame is a position, and an
929
+ * overlay would believe it and place itself in the corner.
930
+ */
931
+ measure(node: EngineNode, into: (frame: WindowFrame) => void): void;
932
+ /**
933
+ * The react tag a node was committed with, or null if it has never reached a commit.
934
+ *
935
+ * This is what the native animation driver writes to: `connectAnimatedNodeToView` takes a tag,
936
+ * and `findNodeHandle` hands a number straight back, so an animated graph can point at one of
937
+ * our nodes without a React instance existing anywhere.
938
+ */
939
+ tagOf(node: EngineNode): number | null;
940
+ /**
941
+ * The shadow node Fabric currently holds for a node, or null before its first commit.
942
+ *
943
+ * A clone-on-write tree hands back a different object every time a node's props change, so
944
+ * this is not an identity. What is stable is the family behind it, which is what native keys a
945
+ * node by, and is why a UI-thread animation given one of these keeps writing to the right view
946
+ * however many times the node is cloned afterwards.
947
+ */
948
+ shadowNodeOf(node: EngineNode): FabricNode | null;
949
+ /** The node currently owning the gesture, if any. */
950
+ get responder(): EngineNode | null;
951
+ /**
952
+ * The node native last reported focused, if any: the text input the keyboard belongs to.
953
+ *
954
+ * Checked against the tree rather than trusted, because a focused node that outlives its view
955
+ * is not a stale style - it is every tap in the application being swallowed. Every scroll view
956
+ * reads this on every touch to decide whether the touch means "dismiss the keyboard", and one
957
+ * that decides yes *captures* the touch before any child sees it. So a field left focused after
958
+ * its view is gone makes the whole app untappable until it is restarted.
959
+ *
960
+ * Neither teardown hook can be relied on to clear it. `removeChild` unlinks the subtree's root
961
+ * and leaves everything under it pointing at its own parent, so a focused descendant still looks
962
+ * attached; and Angular calls `destroyNode` for the nodes it tracks, not for every node in a
963
+ * removed subtree. An overlay closing with a field inside it misses both.
964
+ *
965
+ * So it is answered by looking: a node still in the tree reaches the root by walking up. That
966
+ * costs the depth of one node, on a touch, and only while something is focused.
967
+ */
968
+ get focused(): EngineNode | null;
969
+ /** Whether a node is still part of the tree, rather than in a subtree that was removed. */
970
+ private isAttached;
971
+ /** The host's `resolveAssetSource`, for a component that needs an asset's size up front. */
972
+ resolveAsset(value: unknown): unknown;
973
+ /**
974
+ * The host's `processColor`, and the one place a platform colour becomes one.
975
+ *
976
+ * Props are processed by name - anything ending in `color` - which covers every colour that is
977
+ * a prop. A colour *nested* inside a prop native parses itself is invisible to that rule: a tab
978
+ * bar's appearance carries eight of them, and unprocessed they arrive as strings native reads
979
+ * as nothing at all. So this is public, and the commit walk uses it too.
980
+ *
981
+ * `platform-color(label)` cannot be resolved at build time - the same declaration is
982
+ * `{semantic}` on iOS and `{resource_paths}` on Android - so the names travel and the shape is
983
+ * put on here. `processColor` understands both, exactly as it does for RN's own `PlatformColor`.
984
+ */
985
+ color(value: unknown): unknown;
986
+ /** Root-first, so a capture pass reads forwards and a bubble pass reads backwards. */
987
+ private pathTo;
988
+ private negotiate;
989
+ private grant;
990
+ private release;
991
+ private tellNative;
992
+ /** Runs before listener propagation, as React's responder plugin does. */
993
+ private runResponder;
994
+ private registerEventHandler;
995
+ /**
996
+ * Fabric's `(instanceHandle, topLevelType, nativeEvent)` callback.
997
+ *
998
+ * **Events propagate up the retained tree.** Fabric hands JS only the hit target; React
999
+ * implements two-phase propagation itself by walking the fiber tree, and so must we. Without
1000
+ * it a tap on a `<text>` label inside a `<pressable>` reaches the text node and stops, so the
1001
+ * button only responds on its padding. That reads as "taps sometimes do not register".
1002
+ *
1003
+ * ponytail: event priority (`unstable_getCurrentEventPriority` and the
1004
+ * `unstable_*EventPriority` constants) is read into the facade but not acted on. Angular's
1005
+ * zoneless scheduler already flushes on a microtask, which is soon enough for taps. It stops
1006
+ * being soon enough for a controlled TextInput, where the echo has to beat the next
1007
+ * keystroke. `mostRecentEventCount` covers that today by letting native reject a stale write;
1008
+ * a synchronous flush for discrete priority is the fix if it ever stops being enough.
1009
+ *
1010
+ * **Nothing thrown here may escape.** Fabric calls this synchronously from C++
1011
+ * (`UIManagerBinding::dispatchEventToJS`), often from inside a mounting transaction, and a throw
1012
+ * unwinds through that transaction: for a screen push the next commit then reads props freed on
1013
+ * the way out and the process dies with no JS stack at all. Angular
1014
+ * already catches what its own template listeners throw; everything else on this path - the
1015
+ * responder handlers, listeners registered on the engine directly, a commit started by a focus
1016
+ * change - is caught here and handed to `onError`, which the platform points at the app's
1017
+ * `ErrorHandler`. Each listener is guarded on its own, so one that throws does not stop the
1018
+ * bubble for the rest, as in the DOM.
1019
+ */
1020
+ dispatchEvent(instanceHandle: unknown, topLevelType: string, nativeEvent: unknown): void;
1021
+ /** A presented modal host has finished leaving, so a hidden one can come out of the tree. */
1022
+ private dismissed;
1023
+ private reportEventError;
1024
+ /**
1025
+ * A list that scrolls under a finger takes the touch away from whatever it started on.
1026
+ *
1027
+ * Distance alone cannot tell a scroll from a tap. The press rect is the control's own bounds
1028
+ * plus a retention offset, which is right - a 15pt threshold cancels an ordinary thumb tap on a
1029
+ * full-width button - but on a 72pt list row it means a 72pt drag still counts as a tap, and a
1030
+ * 72pt drag is unmistakably a scroll. So the list is asked instead of the arithmetic: it knows
1031
+ * it is scrolling, and every platform cancels the touch at that moment.
1032
+ *
1033
+ * Only a responder *inside* the thing that scrolled, so a scroll view moving does not cancel a
1034
+ * press somewhere else on screen. And only while a touch is in progress: momentum after the
1035
+ * finger has lifted, and programmatic scrolling, have no press to cancel.
1036
+ */
1037
+ private cancelPressForScroll;
1038
+ /**
1039
+ * Tell "the native side never sent it" from "it arrived and nothing was listening", which are
1040
+ * otherwise identical symptoms. One line per event, so it is off unless asked for.
1041
+ */
1042
+ private trace;
1043
+ /**
1044
+ * `:focus` follows the native events rather than being inferred from a tap, so it is right for a
1045
+ * field focused programmatically or by the keyboard's next-field button.
1046
+ */
1047
+ private trackFocus;
1048
+ /** Walk target to root. Fabric hands JS only the hit target; the propagation is ours to do. */
1049
+ private propagate;
1050
+ /**
1051
+ * Clone a committed node with whatever changed. Fabric's persistent tree has no in-place edit:
1052
+ * a new revision is a new node, which is why a changed child forces its parent to clone too.
1053
+ *
1054
+ * `children` is null when the child list is unchanged, in which case Fabric keeps the previous
1055
+ * revision's children and there is nothing to append.
1056
+ *
1057
+ * Otherwise this clones the way React does: the no-children form, then `appendChild` per child.
1058
+ * Omitting the child list gives the clone *empty* children, not the old ones, and whether a
1059
+ * binding takes that argument cannot be feature-detected - `UIManagerBinding.cpp` registers
1060
+ * both clone methods with a fixed `paramCount` and argument validation is commented out - so
1061
+ * `fn.length` says nothing. One code path, no detection.
1062
+ */
1063
+ private clone;
1064
+ private detach;
1065
+ }
1066
+ export {};