@ng-native/fabric 0.2.0 → 0.3.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.
@@ -119,6 +119,7 @@ function visibleShare(target) {
119
119
  const area = (frame.right - frame.left) * (frame.bottom - frame.top);
120
120
  return area > 0 ? (width * height) / area : 1;
121
121
  }
122
+ // Never reset: the globals it patches stay patched for the life of the process.
122
123
  let installed = false;
123
124
  /**
124
125
  * Let an engine node pass Angular's development check that a trigger is an `Element`, and give
@@ -136,7 +137,8 @@ export function installDeferTriggers() {
136
137
  const scope = globalThis;
137
138
  const existing = scope['Element'];
138
139
  if (existing) {
139
- const own = Function.prototype[Symbol.hasInstance];
140
+ // Its own check if it defines one, and the ordinary prototype walk if not.
141
+ const own = existing[Symbol.hasInstance];
140
142
  Object.defineProperty(existing, Symbol.hasInstance, {
141
143
  configurable: true,
142
144
  value(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"defer-triggers.js","sourceRoot":"","sources":["../src/defer-triggers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What Angular's `@defer (on interaction | hover | viewport)` asks of a trigger element, for the\n * engine's nodes.\n *\n * Angular registers those three triggers on the element itself rather than through `Renderer2`:\n * it asserts in development that the element is an `Element`, listens with `addEventListener`,\n * and watches for visibility with a global `IntersectionObserver`. An engine node was none of\n * those, so the placeholder stayed forever and every render logged an assertion. The listeners\n * are on the node (`addEventListener` in `engine.ts`); this is the other two.\n *\n * `installDeferTriggers` runs once, from `mount()`, before anything renders.\n */\nimport { isEngineNode, viewNameOf, type EngineNode, type WindowFrame } from './engine.ts';\n\n/** The views a node scrolls inside of, and whose scrolling can bring it into view. */\nconst SCROLLERS = new Set(['ScrollView', 'AndroidHorizontalScrollView']);\n\ninterface Entry {\n readonly target: EngineNode;\n readonly isIntersecting: boolean;\n readonly intersectionRatio: number;\n}\n\ntype Callback = (entries: Entry[], observer: EngineIntersectionObserver) => void;\n\ninterface Options {\n readonly threshold?: number | readonly number[];\n}\n\ninterface Watched {\n readonly stops: (() => void)[];\n}\n\n/**\n * Visibility for engine nodes, from the layout and scroll events native already sends.\n *\n * A node is checked when it is observed, when it is laid out, and whenever a scroll view it sits\n * in scrolls. The check measures the node in window coordinates, clips it by each scroll view\n * around it and by the window, and reports it intersecting once the visible share reaches the\n * threshold: any of it by default, as the web does, and a node of no size counts once its position\n * is inside. `root` and `rootMargin` are not applied; the root is always the window.\n */\nexport class EngineIntersectionObserver {\n private readonly callback: Callback;\n private readonly threshold: number;\n private readonly watched = new Map<EngineNode, Watched>();\n\n constructor(callback: Callback, options?: Options) {\n this.callback = callback;\n const threshold = options?.threshold;\n this.threshold = Array.isArray(threshold)\n ? Math.min(...(threshold as number[]))\n : ((threshold as number | undefined) ?? 0);\n }\n\n observe(target: EngineNode): void {\n if (this.watched.has(target)) return;\n const check = () => this.check(target);\n const stops = [target.host!.setEventListener(target, 'topLayout', check)];\n for (let at = target.parent; at; at = at.parent) {\n if (!SCROLLERS.has(viewNameOf(at))) continue;\n stops.push(at.host!.setEventListener(at, 'topScroll', check));\n }\n this.watched.set(target, { stops });\n // A node laid out before it was observed sends no layout event of its own. Measured now,\n // after the commit that mounted it, and in a microtask, as an observer never calls back from\n // inside `observe`.\n queueMicrotask(check);\n }\n\n unobserve(target: EngineNode): void {\n const watched = this.watched.get(target);\n if (!watched) return;\n for (const stop of watched.stops) stop();\n this.watched.delete(target);\n }\n\n disconnect(): void {\n for (const target of [...this.watched.keys()]) this.unobserve(target);\n }\n\n takeRecords(): Entry[] {\n return [];\n }\n\n private check(target: EngineNode): void {\n if (!this.watched.has(target)) return;\n const ratio = visibleShare(target);\n if (ratio === null) return;\n const isIntersecting = this.threshold > 0 ? ratio >= this.threshold : ratio >= 0;\n if (isIntersecting) this.callback([{ target, isIntersecting, intersectionRatio: ratio }], this);\n }\n}\n\n/** A frame as its four edges. */\ninterface Box {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\nconst box = (frame: WindowFrame): Box => ({\n left: frame.x,\n top: frame.y,\n right: frame.x + frame.width,\n bottom: frame.y + frame.height,\n});\n\nfunction measured(node: EngineNode): Box | null {\n let found: Box | null = null;\n node.host!.measure(node, (frame) => (found = box(frame)));\n return found;\n}\n\nfunction clip(into: Box, by: Box): void {\n into.left = Math.max(into.left, by.left);\n into.top = Math.max(into.top, by.top);\n into.right = Math.min(into.right, by.right);\n into.bottom = Math.min(into.bottom, by.bottom);\n}\n\n/**\n * How much of the node is on screen, from 0 to 1; below 0 when none of it is, and null when it\n * has not been measured. Measuring is synchronous on the new architecture.\n */\nfunction visibleShare(target: EngineNode): number | null {\n const frame = measured(target);\n if (!frame) return null;\n const visible = { ...frame };\n for (let at = target.parent; at; at = at.parent) {\n if (!SCROLLERS.has(viewNameOf(at))) continue;\n const scroller = measured(at);\n if (scroller) clip(visible, scroller);\n }\n const window = (target.host as { viewport?: { width: number; height: number } }).viewport;\n if (window && window.width > 0 && window.height > 0) {\n clip(visible, { left: 0, top: 0, right: window.width, bottom: window.height });\n }\n const width = visible.right - visible.left;\n const height = visible.bottom - visible.top;\n if (width < 0 || height < 0) return -1;\n const area = (frame.right - frame.left) * (frame.bottom - frame.top);\n return area > 0 ? (width * height) / area : 1;\n}\n\ntype Constructor = abstract new (...args: never[]) => object;\n\n/** React Native's own `IntersectionObserver`, when its feature flag has installed one. */\ninterface NativeObserver {\n observe(target: unknown): void;\n unobserve(target: unknown): void;\n disconnect(): void;\n}\ntype NativeObserverClass = new (callback: unknown, options?: unknown) => NativeObserver;\n\nlet installed = false;\n\n/**\n * Let an engine node pass Angular's development check that a trigger is an `Element`, and give\n * `@defer (on viewport)` an `IntersectionObserver` that can watch one.\n *\n * React Native defines a global `Element` for its own DOM-like nodes, so an engine node is made to\n * answer `instanceof Element` as well, and nothing else about that class changes. Node has none,\n * and gets one that only engine nodes are instances of. An `IntersectionObserver` React Native\n * already has is kept for its own nodes; engine nodes are watched by the one above.\n */\nexport function installDeferTriggers(): void {\n if (installed) return;\n installed = true;\n const scope = globalThis as unknown as Record<string, unknown>;\n\n const existing = scope['Element'] as Constructor | undefined;\n if (existing) {\n const own = Function.prototype[Symbol.hasInstance];\n Object.defineProperty(existing, Symbol.hasInstance, {\n configurable: true,\n value(this: Constructor, value: unknown): boolean {\n return (this === existing && isEngineNode(value)) || own.call(this, value);\n },\n });\n } else {\n scope['Element'] = class Element {\n static [Symbol.hasInstance](value: unknown): boolean {\n return isEngineNode(value);\n }\n };\n }\n\n const native = scope['IntersectionObserver'] as NativeObserverClass | undefined;\n if (!native) {\n scope['IntersectionObserver'] = EngineIntersectionObserver;\n return;\n }\n scope['IntersectionObserver'] = class extends EngineIntersectionObserver {\n private readonly args: [unknown, unknown];\n private other: NativeObserver | null = null;\n\n constructor(callback: Callback, options?: Options) {\n super(callback, options);\n this.args = [callback, options];\n }\n\n override observe(target: EngineNode): void {\n if (isEngineNode(target)) super.observe(target);\n else (this.other ??= new native(...this.args)).observe(target);\n }\n\n override unobserve(target: EngineNode): void {\n if (isEngineNode(target)) super.unobserve(target);\n else this.other?.unobserve(target);\n }\n\n override disconnect(): void {\n super.disconnect();\n this.other?.disconnect();\n }\n };\n}\n"]}
1
+ {"version":3,"file":"defer-triggers.js","sourceRoot":"","sources":["../src/defer-triggers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What Angular's `@defer (on interaction | hover | viewport)` asks of a trigger element, for the\n * engine's nodes.\n *\n * Angular registers those three triggers on the element itself rather than through `Renderer2`:\n * it asserts in development that the element is an `Element`, listens with `addEventListener`,\n * and watches for visibility with a global `IntersectionObserver`. An engine node was none of\n * those, so the placeholder stayed forever and every render logged an assertion. The listeners\n * are on the node (`addEventListener` in `engine.ts`); this is the other two.\n *\n * `installDeferTriggers` runs once, from `mount()`, before anything renders.\n */\nimport { isEngineNode, viewNameOf, type EngineNode, type WindowFrame } from './engine.ts';\n\n/** The views a node scrolls inside of, and whose scrolling can bring it into view. */\nconst SCROLLERS = new Set(['ScrollView', 'AndroidHorizontalScrollView']);\n\ninterface Entry {\n readonly target: EngineNode;\n readonly isIntersecting: boolean;\n readonly intersectionRatio: number;\n}\n\ntype Callback = (entries: Entry[], observer: EngineIntersectionObserver) => void;\n\ninterface Options {\n readonly threshold?: number | readonly number[];\n}\n\ninterface Watched {\n readonly stops: (() => void)[];\n}\n\n/**\n * Visibility for engine nodes, from the layout and scroll events native already sends.\n *\n * A node is checked when it is observed, when it is laid out, and whenever a scroll view it sits\n * in scrolls. The check measures the node in window coordinates, clips it by each scroll view\n * around it and by the window, and reports it intersecting once the visible share reaches the\n * threshold: any of it by default, as the web does, and a node of no size counts once its position\n * is inside. `root` and `rootMargin` are not applied; the root is always the window.\n */\nexport class EngineIntersectionObserver {\n private readonly callback: Callback;\n private readonly threshold: number;\n private readonly watched = new Map<EngineNode, Watched>();\n\n constructor(callback: Callback, options?: Options) {\n this.callback = callback;\n const threshold = options?.threshold;\n this.threshold = Array.isArray(threshold)\n ? Math.min(...(threshold as number[]))\n : ((threshold as number | undefined) ?? 0);\n }\n\n observe(target: EngineNode): void {\n if (this.watched.has(target)) return;\n const check = () => this.check(target);\n const stops = [target.host!.setEventListener(target, 'topLayout', check)];\n for (let at = target.parent; at; at = at.parent) {\n if (!SCROLLERS.has(viewNameOf(at))) continue;\n stops.push(at.host!.setEventListener(at, 'topScroll', check));\n }\n this.watched.set(target, { stops });\n // A node laid out before it was observed sends no layout event of its own. Measured now,\n // after the commit that mounted it, and in a microtask, as an observer never calls back from\n // inside `observe`.\n queueMicrotask(check);\n }\n\n unobserve(target: EngineNode): void {\n const watched = this.watched.get(target);\n if (!watched) return;\n for (const stop of watched.stops) stop();\n this.watched.delete(target);\n }\n\n disconnect(): void {\n for (const target of [...this.watched.keys()]) this.unobserve(target);\n }\n\n takeRecords(): Entry[] {\n return [];\n }\n\n private check(target: EngineNode): void {\n if (!this.watched.has(target)) return;\n const ratio = visibleShare(target);\n if (ratio === null) return;\n const isIntersecting = this.threshold > 0 ? ratio >= this.threshold : ratio >= 0;\n if (isIntersecting) this.callback([{ target, isIntersecting, intersectionRatio: ratio }], this);\n }\n}\n\n/** A frame as its four edges. */\ninterface Box {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\nconst box = (frame: WindowFrame): Box => ({\n left: frame.x,\n top: frame.y,\n right: frame.x + frame.width,\n bottom: frame.y + frame.height,\n});\n\nfunction measured(node: EngineNode): Box | null {\n let found: Box | null = null;\n node.host!.measure(node, (frame) => (found = box(frame)));\n return found;\n}\n\nfunction clip(into: Box, by: Box): void {\n into.left = Math.max(into.left, by.left);\n into.top = Math.max(into.top, by.top);\n into.right = Math.min(into.right, by.right);\n into.bottom = Math.min(into.bottom, by.bottom);\n}\n\n/**\n * How much of the node is on screen, from 0 to 1; below 0 when none of it is, and null when it\n * has not been measured. Measuring is synchronous on the new architecture.\n */\nfunction visibleShare(target: EngineNode): number | null {\n const frame = measured(target);\n if (!frame) return null;\n const visible = { ...frame };\n for (let at = target.parent; at; at = at.parent) {\n if (!SCROLLERS.has(viewNameOf(at))) continue;\n const scroller = measured(at);\n if (scroller) clip(visible, scroller);\n }\n const window = (target.host as { viewport?: { width: number; height: number } }).viewport;\n if (window && window.width > 0 && window.height > 0) {\n clip(visible, { left: 0, top: 0, right: window.width, bottom: window.height });\n }\n const width = visible.right - visible.left;\n const height = visible.bottom - visible.top;\n if (width < 0 || height < 0) return -1;\n const area = (frame.right - frame.left) * (frame.bottom - frame.top);\n return area > 0 ? (width * height) / area : 1;\n}\n\ntype Constructor = abstract new (...args: never[]) => object;\n\n/** React Native's own `IntersectionObserver`, when its feature flag has installed one. */\ninterface NativeObserver {\n observe(target: unknown): void;\n unobserve(target: unknown): void;\n disconnect(): void;\n}\ntype NativeObserverClass = new (callback: unknown, options?: unknown) => NativeObserver;\n\n// Never reset: the globals it patches stay patched for the life of the process.\nlet installed = false;\n\n/**\n * Let an engine node pass Angular's development check that a trigger is an `Element`, and give\n * `@defer (on viewport)` an `IntersectionObserver` that can watch one.\n *\n * React Native defines a global `Element` for its own DOM-like nodes, so an engine node is made to\n * answer `instanceof Element` as well, and nothing else about that class changes. Node has none,\n * and gets one that only engine nodes are instances of. An `IntersectionObserver` React Native\n * already has is kept for its own nodes; engine nodes are watched by the one above.\n */\nexport function installDeferTriggers(): void {\n if (installed) return;\n installed = true;\n const scope = globalThis as unknown as Record<string, unknown>;\n\n const existing = scope['Element'] as Constructor | undefined;\n if (existing) {\n // Its own check if it defines one, and the ordinary prototype walk if not.\n const own = existing[Symbol.hasInstance];\n Object.defineProperty(existing, Symbol.hasInstance, {\n configurable: true,\n value(this: Constructor, value: unknown): boolean {\n return (this === existing && isEngineNode(value)) || own.call(this, value);\n },\n });\n } else {\n scope['Element'] = class Element {\n static [Symbol.hasInstance](value: unknown): boolean {\n return isEngineNode(value);\n }\n };\n }\n\n const native = scope['IntersectionObserver'] as NativeObserverClass | undefined;\n if (!native) {\n scope['IntersectionObserver'] = EngineIntersectionObserver;\n return;\n }\n scope['IntersectionObserver'] = class extends EngineIntersectionObserver {\n private readonly args: [unknown, unknown];\n private other: NativeObserver | null = null;\n\n constructor(callback: Callback, options?: Options) {\n super(callback, options);\n this.args = [callback, options];\n }\n\n override observe(target: EngineNode): void {\n if (isEngineNode(target)) super.observe(target);\n else (this.other ??= new native(...this.args)).observe(target);\n }\n\n override unobserve(target: EngineNode): void {\n if (isEngineNode(target)) super.unobserve(target);\n else this.other?.unobserve(target);\n }\n\n override disconnect(): void {\n super.disconnect();\n this.other?.disconnect();\n }\n };\n}\n"]}
package/dist/engine.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * inherent and is what React's own Fabric renderer does.
9
9
  */
10
10
  import { type Conditions, type StyleCache, type StyleSheet, type TokenValue } from './css.ts';
11
- import { type AnimationSpec, type RunningAnimation, type Transition } from './transition.ts';
11
+ import { type AnimationSpec, type Keyframe, type RunningAnimation, type Transition } from './transition.ts';
12
12
  import { type DrivenProperty, type EventFeed, type NativeAnimated, type ScrollAxis, type ScrollDrive, type ScrollRange, type StaticTransform } from './native-drive.ts';
13
13
  import type { DrivenChannels } from './scroll-animation.ts';
14
14
  export type FabricNode = {
@@ -19,12 +19,16 @@ import type { HostEngine, HostNode, Settling } from './host.ts';
19
19
  /** A node's scroll-driven animation: what it plays, what drives it, and the frame it commits. */
20
20
  interface ScrollAnimation {
21
21
  readonly spec: AnimationSpec;
22
+ /** The `@keyframes` it was laid out from, so a hot swap that edits them lays it out again. */
23
+ readonly frames: readonly Keyframe[];
22
24
  readonly tracks: ReadonlyMap<string, readonly {
23
25
  offset: number;
24
26
  value: unknown;
25
27
  }[]>;
26
28
  readonly resting: Record<string, unknown>;
27
29
  readonly first: Record<string, unknown>;
30
+ /** The colour the node inherits, which the tracks were built with, for frames that read it. */
31
+ readonly inherited: unknown;
28
32
  readonly source: EngineNode | null;
29
33
  readonly drive: {
30
34
  update(channels: DrivenChannels): void;
@@ -65,6 +69,17 @@ export interface FabricUIManager {
65
69
  */
66
70
  measureInWindow?(node: FabricNode, onSuccess: (x: number, y: number, width: number, height: number) => void): void;
67
71
  }
72
+ /**
73
+ * RN's `directEventTypes`: delivered to the target only, never up the tree. Everything else
74
+ * bubbles, which matches RN's `bubblingEventTypes` for the touch and input events that matter.
75
+ * The web host's engine reads this set too, so both hosts deliver an event the same way.
76
+ *
77
+ * ponytail: a hand-kept set rather than real ViewConfigs. It is wrong only for a nested
78
+ * scroll view, where an ancestor listening for the same direct event would not fire (correct)
79
+ * but a third-party component's custom direct event would bubble (harmless in practice).
80
+ * Swap it for per-component ViewConfig metadata if that ever matters.
81
+ */
82
+ export declare const DIRECT_EVENTS: ReadonlySet<string>;
68
83
  /**
69
84
  * What a listener receives. RN's synthetic event carries more, but `nativeEvent` is the part
70
85
  * every RN API is documented against, and `stopPropagation` is the part a nested control needs.
@@ -77,6 +92,11 @@ export interface FabricUIManager {
77
92
  */
78
93
  export interface NativeSyntheticEvent<T = unknown> {
79
94
  readonly nativeEvent: T;
95
+ /**
96
+ * The node the event happened on, which a listener further up can compare with its own: RN's
97
+ * `event.target`. Null where a host has no node to name.
98
+ */
99
+ readonly target?: unknown;
80
100
  stopPropagation(): void;
81
101
  isPropagationStopped(): boolean;
82
102
  }
@@ -87,8 +107,9 @@ export interface NativeSyntheticEvent<T = unknown> {
87
107
  */
88
108
  export declare class SyntheticEvent<T = unknown> implements NativeSyntheticEvent<T> {
89
109
  readonly nativeEvent: T;
110
+ readonly target: unknown;
90
111
  private stopped;
91
- constructor(nativeEvent: T);
112
+ constructor(nativeEvent: T, target?: unknown);
92
113
  stopPropagation(): void;
93
114
  isPropagationStopped(): boolean;
94
115
  }
@@ -214,6 +235,12 @@ export interface EngineNode extends HostNode {
214
235
  * knows. See `markComponentHost`.
215
236
  */
216
237
  componentHost?: true;
238
+ /**
239
+ * Style weaker than every sheet, where no rule and no inline style says otherwise. What lets the
240
+ * root component's host fill the surface by default and still give way to its own `:host`. See
241
+ * `setDefaultStyle`.
242
+ */
243
+ defaultStyle?: Readonly<Record<string, unknown>>;
217
244
  /**
218
245
  * Hoisted nodes (`registerHoist`) inside this subtree while it is out of the tree, held here
219
246
  * rather than by the engine. See `Engine.releaseDetached`.
@@ -274,7 +301,16 @@ export declare function registerPlatformComponents(platform: string): void;
274
301
  * and skipping it is not cosmetic: `RNSScreenStackHeaderConfig` without its `position: absolute`
275
302
  * joins the screen's flex layout and pushes every page down by the height of the header.
276
303
  */
277
- export declare function registerViewName(elementName: string, viewName: string | PlatformViewName, defaultProps?: Record<string, unknown>): void;
304
+ export declare function registerViewName(elementName: string, viewName: string | PlatformViewName, defaultProps?: Record<string, unknown>, options?: ViewNameOptions): void;
305
+ /** What else a registered view needs the engine to know about it. */
306
+ export interface ViewNameOptions {
307
+ /**
308
+ * The prop the view reads its text from, where it takes no children: SwiftUI's `Text` through
309
+ * `@expo/ui` reads `text`. The text written inside the element becomes that prop, as `<text>`
310
+ * takes its own content, and is not committed as children. An explicit prop wins over it.
311
+ */
312
+ readonly textContent?: string;
313
+ }
278
314
  /**
279
315
  * A view whose native name differs by platform. Chosen when the view is looked up rather than when
280
316
  * it is registered, so it does not matter which ran first: an app's imports register the router's
@@ -452,15 +488,19 @@ export declare class Engine implements HostEngine {
452
488
  * for them. Only those in the tree: see `releaseDetached`.
453
489
  */
454
490
  private readonly hoisted;
455
- /** Whether a child list has lost a member since the last commit. See `releaseDetached`. */
456
- private removedSinceCommit;
491
+ /** The nodes taken out of a child list since the last commit. See `releaseDetached`. */
492
+ private readonly removedSinceCommit;
457
493
  /**
458
- * `@keyframes` by name, across every sheet seen so far. One registry rather than one per sheet,
494
+ * `@keyframes` by name, across every sheet registered. One registry rather than one per sheet,
459
495
  * because that is the scope CSS gives them: a name defined in a global stylesheet is usable
460
496
  * from a component's. The cost is that two components defining the same name collide, last one
461
497
  * in wins, exactly as two stylesheets in a document would.
462
498
  */
463
499
  private readonly keyframes;
500
+ /** Every sheet registered, in the order a document would hold them. See `sheetReplaced`. */
501
+ private readonly sheetOrder;
502
+ /** The `@keyframes` each playing animation was started from. See `startPlaying`. */
503
+ private readonly playedFrames;
464
504
  /** Element names already reported as unclaimed, so a list of a thousand rows reports once. */
465
505
  private readonly reported;
466
506
  private readonly processColor;
@@ -499,6 +539,18 @@ export declare class Engine implements HostEngine {
499
539
  * a caller owns the properties it names and nothing else. Each owner writes all of its own at
500
540
  * once - four insets together, never one - so nothing is left over from a previous rotation.
501
541
  */
542
+ /**
543
+ * Match `sheet` against every node from now on, after the global sheet the engine was made
544
+ * with, and restyle everything; false, and nothing done, when it already is. How a platform
545
+ * applies a component whose CSS is not scoped, such as Angular's `ViewEncapsulation.None`.
546
+ * `replacing` is a sheet added before that this one takes the place of, after a hot swap.
547
+ */
548
+ addGlobalSheet(sheet: StyleSheet, replacing?: StyleSheet): boolean;
549
+ /**
550
+ * Stop matching a sheet `addGlobalSheet` added, and restyle everything; false, and nothing
551
+ * done, when it is not one. A hot swap that leaves a None component no rules, or makes it scoped.
552
+ */
553
+ removeGlobalSheet(sheet: StyleSheet): boolean;
502
554
  updateTokens(next: Readonly<Record<string, TokenValue>>): void;
503
555
  /**
504
556
  * The system text size changed: measure every text and text input again, and commit.
@@ -563,16 +615,37 @@ export declare class Engine implements HostEngine {
563
615
  * the wrong height until something else changed.
564
616
  */
565
617
  remeasure(node: EngineNode): void;
618
+ /**
619
+ * A node's component rules are its creating sheet's, so this matches it against `like`'s, or
620
+ * against the one it was created with once `like` is null.
621
+ */
622
+ adoptScope(node: EngineNode, like: EngineNode | null): void;
623
+ /** The sheet each node given another's scope was created with. */
624
+ private readonly ownSheets;
566
625
  /** The window's size, from the conditions media queries use. Zero until the platform says. */
567
626
  get viewport(): {
568
627
  readonly width: number;
569
628
  readonly height: number;
570
629
  };
571
630
  private viewportSize;
631
+ /** The system text size, from the conditions. See `Conditions.fontScale`. */
632
+ private fontScale;
572
633
  updateConditions(next: Conditions): void;
573
634
  createElement(name: string, sheet?: StyleSheet | null): EngineNode;
574
635
  addClass(node: EngineNode, name: string): void;
575
636
  removeClass(node: EngineNode, name: string): void;
637
+ /**
638
+ * The sheet of the component `node` hosts, whose `:host` rules it matches. Set again when a hot
639
+ * swap replaces the component's sheet, which recreates the views inside but not the host, so the
640
+ * host is styled afresh here.
641
+ */
642
+ setHostSheet(node: EngineNode, sheet: StyleSheet | null): void;
643
+ /**
644
+ * Style for `node` that any matched rule or inline style overrides, as a browser's own default
645
+ * for an element is. `mount` gives the root component's host the surface's height this way, as
646
+ * the web's mount point has, unless the app's `:host` sizes it otherwise.
647
+ */
648
+ setDefaultStyle(node: EngineNode, style: Readonly<Record<string, unknown>> | undefined): void;
576
649
  /** `class="a b"` from a template. Replaces the set rather than adding to it. */
577
650
  setClasses(node: EngineNode, value: string): void;
578
651
  /** Angular's `@if`/`@for`/`ViewContainerRef` markers. Ordered, never committed. */
@@ -581,6 +654,11 @@ export declare class Engine implements HostEngine {
581
654
  appendChild(parent: EngineNode, child: EngineNode): void;
582
655
  insertBefore(parent: EngineNode, child: EngineNode, ref: EngineNode | null): void;
583
656
  removeChild(parent: EngineNode | null, child: EngineNode): void;
657
+ /**
658
+ * A child came or went under a view that takes its text as a prop: that prop changed. A span
659
+ * changes it too, by keeping or dropping the space before it.
660
+ */
661
+ private markTextContent;
584
662
  /** A subtree that went out of the tree is back in: its hoisted nodes commit again. */
585
663
  private wakeHoists;
586
664
  /**
@@ -598,11 +676,18 @@ export declare class Engine implements HostEngine {
598
676
  * whole. So a hoisted node moves to the root of the subtree it left with, which is garbage along
599
677
  * with it if the subtree never returns, and is handed back when it does. An animation on a node
600
678
  * out of the tree stops rather than running a frame loop for something nobody can see, and
601
- * starts again if the node returns.
679
+ * starts again if the node returns. And a node out of the tree when the commit runs forgets its
680
+ * committed views, as a withheld modal does: the commit unmounts them, Fabric never re-enables an
681
+ * unmounted view's events (`EventEmitter::setEnabled`), and committed again they would ignore
682
+ * every touch. It comes back created afresh, as React creates an element it mounts again. One
683
+ * put back before the commit never left, and keeps its views.
602
684
  *
603
- * Once per commit, and only after a removal: the walks are the depth of a handful of nodes.
685
+ * Once per commit, and only after a removal: the walks are the depth of a handful of nodes, and
686
+ * forgetting walks each subtree that left once.
604
687
  */
605
688
  private releaseDetached;
689
+ /** Forget the committed views of each node removed since the last commit that is still out. */
690
+ private forgetDetached;
606
691
  /** The root of the tree a node is in: the engine's root while it is attached. */
607
692
  private topOf;
608
693
  /**
@@ -682,9 +767,9 @@ export declare class Engine implements HostEngine {
682
767
  /** Anchors take part in sibling ordering but never reach Fabric. */
683
768
  private visibleChildren;
684
769
  /**
685
- * Everything a node renders with, unprocessed. Precedence, weakest first: native defaults,
686
- * matched CSS, explicit props, inline style, a component's `styleOverride`. Inline wins over CSS for the same reason it does
687
- * on the web.
770
+ * Everything a node renders with, unprocessed. Precedence, weakest first: native defaults, the
771
+ * node's `defaultStyle`, matched CSS, explicit props, inline style, a component's
772
+ * `styleOverride`. Inline wins over CSS for the same reason it does on the web.
688
773
  *
689
774
  * Raw, because this is what the next commit diffs against. Colours and asset ids are converted
690
775
  * on the way out instead (`processed`), and only for the keys that changed: the converters
@@ -724,6 +809,18 @@ export declare class Engine implements HostEngine {
724
809
  * sheets are few.
725
810
  */
726
811
  private registerSheet;
812
+ /**
813
+ * A hot swap edited a component's sheet into `next`, or took it away with null: `sheet`'s
814
+ * `@keyframes` go, and `next`'s take its place among the others, as an edited stylesheet keeps
815
+ * its place in a document. A name another sheet also defines falls back to that one. Every
816
+ * animation re-resolves the name it plays, so one whose keyframes went stops, as in a browser.
817
+ * A `sheet` already gone, such as a global sheet removed, leaves `next` registered last.
818
+ */
819
+ sheetReplaced(sheet: StyleSheet, next: StyleSheet | null): void;
820
+ /** Every sheet's `@keyframes` again, in order, and every animation told to look them up. */
821
+ private reindexKeyframes;
822
+ /** Mark every node under `node` with an animation, so it looks its keyframes up again. */
823
+ private markAnimated;
727
824
  /** Every `@font-face` seen, global as on the web: a face declared in one sheet serves all. */
728
825
  private readonly fontFaces;
729
826
  /** Whether a sheet first used in this commit declared a face. See `rematchFonts`. */
@@ -744,6 +841,17 @@ export declare class Engine implements HostEngine {
744
841
  private animated;
745
842
  /** Start the clock on an animation, unless the node is already playing this one. */
746
843
  private startPlaying;
844
+ /**
845
+ * The colour `node` inherits, for frames that set `color: currentColor`, which is that colour
846
+ * wherever the animation has got to, as in CSS. Undefined for frames that do not.
847
+ */
848
+ private inheritedColour;
849
+ /**
850
+ * The name an animation plays now has other frames: a hot swap edited them, or took away the
851
+ * sheet whose copy won. Or the colour its `color: currentColor` stands for has changed. It
852
+ * carries on along the new tracks, on its own clock, as a browser does, paused or not.
853
+ */
854
+ private reframe;
747
855
  /**
748
856
  * `animation-play-state` changed on an animation already playing: pausing holds the frame it
749
857
  * has reached, and running again carries on from there, the paused time left out of its clock.
@@ -763,6 +871,13 @@ export declare class Engine implements HostEngine {
763
871
  * changed on a later commit would reset what native is animating.
764
872
  */
765
873
  private scrollAnimated;
874
+ /**
875
+ * The first frame of a scroll-driven animation already playing. When the colour its
876
+ * `color: currentColor` stands for has changed, that frame's colour is taken from tracks built
877
+ * again. Native drives opacity and transforms only, so a colour holds its first frame, and
878
+ * nothing native animates is touched.
879
+ */
880
+ private rescrolled;
766
881
  /** Hand a scroll-driven animation's channels to native, fed by `source`'s scroll events. */
767
882
  private driveScrolled;
768
883
  /** The scroll view a scroll-driven animation on `node` follows: the nearest one it is inside. */
@@ -840,6 +955,8 @@ export declare class Engine implements HostEngine {
840
955
  * which, per child, was most of what a one-row update cost.
841
956
  */
842
957
  private reconcileChildren;
958
+ /** Remember an iOS modal host committed as showing, so hiding it waits for `topDismiss`. */
959
+ private notePresented;
843
960
  /**
844
961
  * Whether a child is left out of its parent's commit: a modal host whose `visible` is false.
845
962
  *
@@ -849,9 +966,11 @@ export declare class Engine implements HostEngine {
849
966
  * committed, with `visible` false, until native reports `topDismiss`: Modal.js keeps it that
850
967
  * long so the dismissal animates and `(dismiss)` fires. Android drops it at once, as Modal.js
851
968
  * does there.
969
+ *
970
+ * A withheld host is unmounted, and Fabric drops an unmounted view's event target for good
971
+ * (`EventEmitter::setEnabled`), so its committed handles are forgotten and it is created afresh
972
+ * when it is shown again, as Modal.js creates it afresh. Committed again, it ignores every touch.
852
973
  */
853
- /** Remember an iOS modal host committed as showing, so hiding it waits for `topDismiss`. */
854
- private notePresented;
855
974
  private withheld;
856
975
  /**
857
976
  * `reconcileChildren` for a node other nodes hoist into: its own children, each followed by
@@ -870,7 +989,10 @@ export declare class Engine implements HostEngine {
870
989
  * Its native state goes with it, as it would there.
871
990
  */
872
991
  private reconcileUnder;
873
- /** Let a node and everything under it be created again at the next commit. */
992
+ /**
993
+ * Let a node and everything under it be created again at the next commit. The views it keeps
994
+ * for hoisted names go too: they are children of the native view it is losing.
995
+ */
874
996
  private forgetCommitted;
875
997
  /** A hoisted node, which commits into an ancestor further up than the parent it is in. */
876
998
  private committedElsewhere;
@@ -910,6 +1032,12 @@ export declare class Engine implements HostEngine {
910
1032
  private readonly undefinedTokens;
911
1033
  /** Said once per name: a phone has no inspector to show the declaration struck out. */
912
1034
  private reportUndefinedToken;
1035
+ private readonly unreadDisplays;
1036
+ /**
1037
+ * Said once per token and value: the compiler refuses `display: grid` written out, but a token
1038
+ * is only known here, and without this the view lays out as flex with nothing said.
1039
+ */
1040
+ private reportUnreadDisplay;
913
1041
  private checkProps;
914
1042
  private isDeclared;
915
1043
  /**