@symbiote-native/angular 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/symbiote-angular-dev.cjs +20 -19
- package/build/angular/anchor-host-registry.js +2 -2
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/bootstrap.js +1 -1
- package/build/angular/bootstrap.js.map +1 -1
- package/build/angular/components/activity-indicator/index.android.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.android.js +12 -4
- package/build/angular/components/activity-indicator/index.android.js.map +1 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.ios.js +8 -3
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -1
- package/build/angular/components/activity-indicator/shared.d.ts +1 -1
- package/build/angular/components/activity-indicator/shared.js +4 -4
- package/build/angular/components/activity-indicator/shared.js.map +1 -1
- package/build/angular/components/button.d.ts +4 -2
- package/build/angular/components/button.js +41 -22
- package/build/angular/components/button.js.map +1 -1
- package/build/angular/components/flat-list/index.d.ts +6 -4
- package/build/angular/components/flat-list/index.js +80 -50
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image/index.android.d.ts +8 -3
- package/build/angular/components/image/index.android.js +42 -10
- package/build/angular/components/image/index.android.js.map +1 -1
- package/build/angular/components/image/index.ios.d.ts +8 -3
- package/build/angular/components/image/index.ios.js +42 -10
- package/build/angular/components/image/index.ios.js.map +1 -1
- package/build/angular/components/image/shared.d.ts +4 -1
- package/build/angular/components/image/shared.js +51 -8
- package/build/angular/components/image/shared.js.map +1 -1
- package/build/angular/components/image-background.d.ts +2 -1
- package/build/angular/components/image-background.js +26 -10
- package/build/angular/components/image-background.js.map +1 -1
- package/build/angular/components/input-accessory-view/index.d.ts +9 -4
- package/build/angular/components/input-accessory-view/index.js +49 -10
- package/build/angular/components/input-accessory-view/index.js.map +1 -1
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +3 -1
- package/build/angular/components/keyboard-avoiding-view/index.js +44 -12
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +11 -8
- package/build/angular/components/modal/index.js +51 -29
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable/index.d.ts +12 -6
- package/build/angular/components/pressable/index.js +66 -13
- package/build/angular/components/pressable/index.js.map +1 -1
- package/build/angular/components/refresh-control/index.d.ts +8 -4
- package/build/angular/components/refresh-control/index.js +54 -16
- package/build/angular/components/refresh-control/index.js.map +1 -1
- package/build/angular/components/safe-area-view/index.d.ts +9 -4
- package/build/angular/components/safe-area-view/index.js +48 -9
- package/build/angular/components/safe-area-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/index.android.d.ts +2 -3
- package/build/angular/components/scroll-view/index.android.js +88 -61
- package/build/angular/components/scroll-view/index.android.js.map +1 -1
- package/build/angular/components/scroll-view/index.ios.d.ts +2 -3
- package/build/angular/components/scroll-view/index.ios.js +24 -18
- package/build/angular/components/scroll-view/index.ios.js.map +1 -1
- package/build/angular/components/scroll-view/projection.d.ts +35 -0
- package/build/angular/components/scroll-view/projection.js +279 -53
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.d.ts +14 -5
- package/build/angular/components/scroll-view/shared.js +225 -75
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.js +21 -22
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/section-list/index.d.ts +7 -1
- package/build/angular/components/section-list/index.js +91 -54
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/switch/index.android.d.ts +7 -3
- package/build/angular/components/switch/index.android.js +52 -10
- package/build/angular/components/switch/index.android.js.map +1 -1
- package/build/angular/components/switch/index.ios.d.ts +7 -3
- package/build/angular/components/switch/index.ios.js +52 -10
- package/build/angular/components/switch/index.ios.js.map +1 -1
- package/build/angular/components/switch/shared.d.ts +13 -4
- package/build/angular/components/switch/shared.js +71 -7
- package/build/angular/components/switch/shared.js.map +1 -1
- package/build/angular/components/text-input.d.ts +11 -3
- package/build/angular/components/text-input.js +68 -8
- package/build/angular/components/text-input.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +40 -9
- package/build/angular/components/touchable/index.js +303 -67
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +6 -3
- package/build/angular/components/touchable-native-feedback/index.js +63 -27
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
- package/build/angular/components/virtualized-list/directives.js +22 -18
- package/build/angular/components/virtualized-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +15 -4
- package/build/angular/components/virtualized-list/index.js +326 -94
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/directives.js +12 -12
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +19 -1
- package/build/angular/components/virtualized-section-list/index.js +93 -22
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components.d.ts +2 -2
- package/build/angular/components.js +5 -0
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.js +34 -36
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js +19 -22
- package/build/angular/create-tunnel/index.js.map +1 -1
- package/build/angular/descriptor-to-angular/index.js +6 -4
- package/build/angular/descriptor-to-angular/index.js.map +1 -1
- package/build/angular/diagnostics.d.ts +33 -0
- package/build/angular/diagnostics.js +82 -0
- package/build/angular/diagnostics.js.map +1 -0
- package/build/angular/host-instance/index.js +1 -1
- package/build/angular/host-instance/index.js.map +1 -1
- package/build/angular/index.d.ts +9 -4
- package/build/angular/index.js +17 -3
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +1 -7
- package/build/angular/modules/animated/animated-leaf-binder.js +22 -72
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +11 -6
- package/build/angular/modules/animated/create-animated-component.js +106 -55
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/status-bar/index.js +4 -4
- package/build/angular/modules/status-bar/index.js.map +1 -1
- package/build/angular/primitives/index.d.ts +20 -3
- package/build/angular/primitives/index.js +84 -48
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +48 -2
- package/build/angular/primitives/shared.js +158 -11
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/render/index.js +69 -38
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.d.ts +1 -0
- package/build/angular/renderer/index.js +37 -2
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/services/color-scheme.service.js +3 -3
- package/build/angular/services/color-scheme.service.js.map +1 -1
- package/build/angular/services/window-dimensions.service.js +4 -4
- package/build/angular/services/window-dimensions.service.js.map +1 -1
- package/package.json +10 -8
- package/src/anchor-host-registry.ts +2 -2
- package/src/bootstrap.ts +4 -1
- package/src/components/activity-indicator/index.android.ts +8 -1
- package/src/components/activity-indicator/index.ios.ts +4 -0
- package/src/components/activity-indicator/shared.ts +32 -13
- package/src/components/button.ts +46 -19
- package/src/components/flat-list/index.ts +122 -62
- package/src/components/image/index.android.ts +58 -7
- package/src/components/image/index.ios.ts +58 -7
- package/src/components/image/shared.ts +92 -21
- package/src/components/image-background.ts +29 -9
- package/src/components/input-accessory-view/index.ts +67 -12
- package/src/components/keyboard-avoiding-view/index.ts +61 -15
- package/src/components/modal/index.ts +72 -33
- package/src/components/pressable/index.ts +117 -28
- package/src/components/refresh-control/index.ts +74 -18
- package/src/components/safe-area-view/index.ts +64 -10
- package/src/components/scroll-view/index.android.ts +62 -47
- package/src/components/scroll-view/index.ios.ts +19 -17
- package/src/components/scroll-view/projection.ts +363 -64
- package/src/components/scroll-view/shared.ts +312 -90
- package/src/components/scroll-view/sticky-header.ts +36 -24
- package/src/components/section-list/index.ts +86 -53
- package/src/components/switch/index.android.ts +60 -8
- package/src/components/switch/index.ios.ts +60 -8
- package/src/components/switch/shared.ts +93 -6
- package/src/components/text-input.ts +95 -13
- package/src/components/touchable/index.ts +348 -64
- package/src/components/touchable-native-feedback/index.ts +94 -31
- package/src/components/virtualized-list/directives.ts +19 -5
- package/src/components/virtualized-list/index.ts +380 -85
- package/src/components/virtualized-section-list/directives.ts +6 -3
- package/src/components/virtualized-section-list/index.ts +146 -33
- package/src/components.ts +9 -1
- package/src/create-portal/index.ts +31 -31
- package/src/create-tunnel/index.ts +16 -17
- package/src/descriptor-to-angular/index.ts +29 -8
- package/src/diagnostics.ts +138 -0
- package/src/host-instance/index.ts +9 -3
- package/src/index.ts +61 -3
- package/src/modules/animated/animated-leaf-binder.ts +28 -77
- package/src/modules/animated/create-animated-component.ts +115 -47
- package/src/modules/app-registry/index.ts +9 -9
- package/src/modules/status-bar/index.ts +13 -4
- package/src/primitives/index.ts +47 -2
- package/src/primitives/shared.ts +170 -10
- package/src/render/index.ts +77 -39
- package/src/renderer/index.ts +76 -14
- package/src/services/color-scheme.service.ts +3 -1
- package/src/services/window-dimensions.service.ts +11 -4
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +0 -14
- package/build/angular/modules/animated/animated-leaf-binder/index.js +0 -91
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +0 -1
- package/src/modules/animated/animated-leaf-binder/index.ts +0 -101
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Per-window counters for the Angular adapter, the twin of the engine's readCommitProfile().
|
|
2
|
+
// The engine's number answers "what did the commit walk cost"; these answer the question one layer
|
|
3
|
+
// up, which is the one Angular keeps losing: HOW MANY TIMES did anything run at all.
|
|
4
|
+
//
|
|
5
|
+
// Angular is the only adapter whose update model can free-run. A change-detection pass that writes
|
|
6
|
+
// a fresh reference into a downstream @Input marks a view dirty, the zoneless scheduler books
|
|
7
|
+
// another pass, and it never settles - the hazard `stableAnchorStyle` and VirtualizedList's
|
|
8
|
+
// `lastRecompute` gate each guard one instance of. A guard that stops holding is INVISIBLE from the
|
|
9
|
+
// outside: the screen paints correctly, only the frame rate collapses. `cdPasses` at rest is what
|
|
10
|
+
// makes it visible, and every other counter here narrows the same window to a suspect.
|
|
11
|
+
//
|
|
12
|
+
// Deliberately NOT gated behind isDebug(): an integer increment is noise next to the work it
|
|
13
|
+
// counts, and the numbers are only meaningful from a RELEASE build (dev-mode Angular runs its own
|
|
14
|
+
// checks and drowns the signal). The per-name detail map IS gated, since it allocates.
|
|
15
|
+
|
|
16
|
+
export interface IAngularProfile {
|
|
17
|
+
// Change-detection passes: RendererFactory2.end() runs once per ApplicationRef.tick().
|
|
18
|
+
cdPasses: number;
|
|
19
|
+
// Primitive host views (symbiote-view / symbiote-text / ...) Angular actually walked. Divided by
|
|
20
|
+
// cdPasses this is the SIZE OF THE TREE one tick touches - the number that separates "the list is
|
|
21
|
+
// expensive" from "every mounted screen is re-checked because a list scrolled". The primitives are
|
|
22
|
+
// CheckAlways, and a Global tick refreshes every CheckAlways view in the application.
|
|
23
|
+
viewsChecked: number;
|
|
24
|
+
// Prop/style/class/text writes that reached the engine. At rest this must be 0: a nonzero idle
|
|
25
|
+
// value means change detection is rewriting props nothing asked to change.
|
|
26
|
+
rendererWrites: number;
|
|
27
|
+
nodesCreated: number;
|
|
28
|
+
nodesInserted: number;
|
|
29
|
+
nodesRemoved: number;
|
|
30
|
+
// The `class` poll every COMPOSED component runs per check (SymbioteStyleInputDirective: Pressable,
|
|
31
|
+
// ScrollView, Image, the lists...; the raw primitives are counted by `viewsChecked` instead).
|
|
32
|
+
// `styleMarks` is how often the poll actually found a change and dirtied the view - the ratio is
|
|
33
|
+
// how much of the polling is wasted, and a nonzero idle `styleMarks` is a free-run engine.
|
|
34
|
+
styleChecks: number;
|
|
35
|
+
styleMarks: number;
|
|
36
|
+
// VirtualizedList: checks vs the ones that got past the `lastRecompute` dedup gate. Recomputes
|
|
37
|
+
// climbing with checks means the gate is not holding, which is the free-run bug itself.
|
|
38
|
+
listChecks: number;
|
|
39
|
+
listRecomputes: number;
|
|
40
|
+
listMarks: number;
|
|
41
|
+
// Scroll events the list saw. With sticky headers RN pins scrollEventThrottle to 1, so this is
|
|
42
|
+
// the frame rate of the gesture and the denominator for everything measured during a drag.
|
|
43
|
+
scrollTicks: number;
|
|
44
|
+
// Cell views. `outletUpdates` is the cheap path (context patched in place); `outletCreates` past
|
|
45
|
+
// the first window means cells are being rebuilt instead of reused, which is the cost CDK's
|
|
46
|
+
// view-recycling strategy exists to avoid.
|
|
47
|
+
outletCreates: number;
|
|
48
|
+
outletUpdates: number;
|
|
49
|
+
outletDestroys: number;
|
|
50
|
+
// Sticky projection: schedules are cheap (a Set insert), flushes walk every projected record.
|
|
51
|
+
projectionSchedules: number;
|
|
52
|
+
projectionFlushes: number;
|
|
53
|
+
// A sticky wrapper rebuilt loses its measured layout and costs a native round trip; reuse is the
|
|
54
|
+
// fixed path. Creates climbing during a plain scroll is the regression this pair pins.
|
|
55
|
+
stickyWrapperCreates: number;
|
|
56
|
+
stickyWrapperReuses: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type IAngularCounter = keyof IAngularProfile;
|
|
60
|
+
|
|
61
|
+
function createCounters(): IAngularProfile {
|
|
62
|
+
return {
|
|
63
|
+
cdPasses: 0,
|
|
64
|
+
viewsChecked: 0,
|
|
65
|
+
rendererWrites: 0,
|
|
66
|
+
nodesCreated: 0,
|
|
67
|
+
nodesInserted: 0,
|
|
68
|
+
nodesRemoved: 0,
|
|
69
|
+
styleChecks: 0,
|
|
70
|
+
styleMarks: 0,
|
|
71
|
+
listChecks: 0,
|
|
72
|
+
listRecomputes: 0,
|
|
73
|
+
listMarks: 0,
|
|
74
|
+
scrollTicks: 0,
|
|
75
|
+
outletCreates: 0,
|
|
76
|
+
outletUpdates: 0,
|
|
77
|
+
outletDestroys: 0,
|
|
78
|
+
projectionSchedules: 0,
|
|
79
|
+
projectionFlushes: 0,
|
|
80
|
+
stickyWrapperCreates: 0,
|
|
81
|
+
stickyWrapperReuses: 0,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let counters = createCounters();
|
|
86
|
+
|
|
87
|
+
export function countAngular(counter: IAngularCounter): void {
|
|
88
|
+
counters[counter] += 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Reading zeroes the counters, so a sampler on an interval gets disjoint windows rather than a
|
|
92
|
+
// growing total - same contract as readCommitProfile().
|
|
93
|
+
export function readAngularProfile(): IAngularProfile {
|
|
94
|
+
const snapshot = counters;
|
|
95
|
+
counters = createCounters();
|
|
96
|
+
return snapshot;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Which prop names the renderer actually rewrites, and which host tags get created. Off by default
|
|
100
|
+
// because it allocates per write; the probe screen turns it on for one sample. A write nothing
|
|
101
|
+
// asked for is the whole diagnosis, and the prop NAME is what points at the binding responsible.
|
|
102
|
+
let isDetailEnabled = false;
|
|
103
|
+
const writesByProp = new Map<string, number>();
|
|
104
|
+
const createsByTag = new Map<string, number>();
|
|
105
|
+
|
|
106
|
+
export function setAngularProfileDetail(enabled: boolean): void {
|
|
107
|
+
isDetailEnabled = enabled;
|
|
108
|
+
if (!enabled) {
|
|
109
|
+
writesByProp.clear();
|
|
110
|
+
createsByTag.clear();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function noteAngularWrite(propName: string): void {
|
|
115
|
+
if (!isDetailEnabled) return;
|
|
116
|
+
writesByProp.set(propName, (writesByProp.get(propName) ?? 0) + 1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function noteAngularCreate(tagName: string): void {
|
|
120
|
+
if (!isDetailEnabled) return;
|
|
121
|
+
createsByTag.set(tagName, (createsByTag.get(tagName) ?? 0) + 1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface IAngularProfileDetail {
|
|
125
|
+
writesByProp: readonly (readonly [string, number])[];
|
|
126
|
+
createsByTag: readonly (readonly [string, number])[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Sorted hottest-first and read-and-cleared, so a sample names its own top offenders.
|
|
130
|
+
export function readAngularProfileDetail(): IAngularProfileDetail {
|
|
131
|
+
const detail = {
|
|
132
|
+
writesByProp: [...writesByProp.entries()].sort((a, b) => b[1] - a[1]),
|
|
133
|
+
createsByTag: [...createsByTag.entries()].sort((a, b) => b[1] - a[1]),
|
|
134
|
+
};
|
|
135
|
+
writesByProp.clear();
|
|
136
|
+
createsByTag.clear();
|
|
137
|
+
return detail;
|
|
138
|
+
}
|
|
@@ -13,14 +13,19 @@
|
|
|
13
13
|
// An uncommitted or unknown input surfaces as null.
|
|
14
14
|
|
|
15
15
|
import { ElementRef } from '@angular/core';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
getNativeTag,
|
|
18
|
+
isSymbioteNode,
|
|
19
|
+
type ISymbioteNode,
|
|
20
|
+
} from '@symbiote-native/engine';
|
|
17
21
|
|
|
18
22
|
export type { IHostInstance } from '@symbiote-native/engine';
|
|
19
23
|
|
|
20
24
|
function resolveHostNode(candidate: unknown): ISymbioteNode | null {
|
|
21
25
|
if (candidate === null || candidate === undefined) return null;
|
|
22
26
|
if (isSymbioteNode(candidate)) return candidate;
|
|
23
|
-
if (candidate instanceof ElementRef)
|
|
27
|
+
if (candidate instanceof ElementRef)
|
|
28
|
+
return resolveHostNode(candidate.nativeElement);
|
|
24
29
|
const maybeHost = candidate as { nativeElement?: unknown };
|
|
25
30
|
if (typeof maybeHost.nativeElement !== 'undefined') {
|
|
26
31
|
return resolveHostNode(maybeHost.nativeElement);
|
|
@@ -29,7 +34,8 @@ function resolveHostNode(candidate: unknown): ISymbioteNode | null {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export function findNodeHandle(componentOrHandle: unknown): number | null {
|
|
32
|
-
if (componentOrHandle === null || componentOrHandle === undefined)
|
|
37
|
+
if (componentOrHandle === null || componentOrHandle === undefined)
|
|
38
|
+
return null;
|
|
33
39
|
if (typeof componentOrHandle === 'number') return componentOrHandle;
|
|
34
40
|
const node = resolveHostNode(componentOrHandle);
|
|
35
41
|
return node ? (getNativeTag(node) ?? null) : null;
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ export {
|
|
|
27
27
|
stableAnchorStyle,
|
|
28
28
|
Switch,
|
|
29
29
|
SymbioteHostPropsDirective,
|
|
30
|
+
SymbioteStyleInputDirective,
|
|
30
31
|
Text,
|
|
31
32
|
TextInput,
|
|
32
33
|
TouchableHighlight,
|
|
@@ -60,6 +61,16 @@ export {
|
|
|
60
61
|
AnimatedText,
|
|
61
62
|
AnimatedView,
|
|
62
63
|
} from './modules/animated';
|
|
64
|
+
// Exported even though Angular's version maps only the four primitives (View/Text/Image/
|
|
65
|
+
// ScrollView) onto the pre-authored wrappers above and THROWS on anything else — there is no JIT
|
|
66
|
+
// under AOT/Metro, so it cannot synthesize a wrapper at runtime the way the other four adapters
|
|
67
|
+
// do. It ships anyway because portable code (`createAnimatedComponent(View)`) then compiles and
|
|
68
|
+
// runs identically on all five adapters, and the one case Angular cannot serve fails with a
|
|
69
|
+
// message naming the fix (author a standalone @Component over AnimatedComponentBase) instead of
|
|
70
|
+
// failing as a missing export, which names nothing. Found by the barrel audit in
|
|
71
|
+
// `.claude/rules/adapter-parity-audit.md`: it was implemented and tested here all along and simply
|
|
72
|
+
// never reached this file, so `@symbiote-native/angular` consumers could not reach it at all.
|
|
73
|
+
export { createAnimatedComponent } from './modules/animated';
|
|
63
74
|
export type {
|
|
64
75
|
IActivityIndicatorProps,
|
|
65
76
|
IAngularImageBackgroundProps,
|
|
@@ -134,7 +145,12 @@ export { DescriptorOutlet } from './descriptor-to-angular';
|
|
|
134
145
|
// idiom) parameterized by an `@Input()`, rather than a factory returning fresh components per
|
|
135
146
|
// call.
|
|
136
147
|
export { PortalDirective, PortalOutletDirective } from './create-portal';
|
|
137
|
-
export {
|
|
148
|
+
export {
|
|
149
|
+
createTunnel,
|
|
150
|
+
TunnelInDirective,
|
|
151
|
+
TunnelOut,
|
|
152
|
+
type ITunnelStore,
|
|
153
|
+
} from './create-tunnel';
|
|
138
154
|
export { SymbioteRenderer, SymbioteRendererFactory } from './renderer';
|
|
139
155
|
// registerComposedComponent lives in the dependency-free leaf ./anchor-host-registry, NOT in the
|
|
140
156
|
// require-cyclic ./renderer — see the leaf header. The babel-register-composed plugin injects the
|
|
@@ -249,7 +265,6 @@ export type {
|
|
|
249
265
|
} from '@symbiote-native/engine';
|
|
250
266
|
export {
|
|
251
267
|
dlog,
|
|
252
|
-
flattenStyle,
|
|
253
268
|
isDebug,
|
|
254
269
|
Platform,
|
|
255
270
|
processColor,
|
|
@@ -258,4 +273,47 @@ export {
|
|
|
258
273
|
setNativeViewConfigSource,
|
|
259
274
|
StyleSheet,
|
|
260
275
|
} from '@symbiote-native/engine';
|
|
261
|
-
export type {
|
|
276
|
+
export type {
|
|
277
|
+
IRootTag,
|
|
278
|
+
ISymbioteEvent,
|
|
279
|
+
ISymbioteNode,
|
|
280
|
+
} from '@symbiote-native/engine';
|
|
281
|
+
// The agnostic style / platform / view-config types behind the values above. Angular takes its
|
|
282
|
+
// component props as @Input()s and exports no per-component prop type, but an app still needs
|
|
283
|
+
// these to type a style object or a Platform.select spec.
|
|
284
|
+
export type {
|
|
285
|
+
IViewStyle,
|
|
286
|
+
ITextStyle,
|
|
287
|
+
IFlexAlign,
|
|
288
|
+
IFlexJustify,
|
|
289
|
+
IPlatformStatic,
|
|
290
|
+
IPlatformOSType,
|
|
291
|
+
IPlatformConstantsIOS,
|
|
292
|
+
IPlatformConstantsAndroid,
|
|
293
|
+
IPlatformSelectSpec,
|
|
294
|
+
INativeViewConfig,
|
|
295
|
+
INativeViewConfigSource,
|
|
296
|
+
} from '@symbiote-native/engine';
|
|
297
|
+
export type {
|
|
298
|
+
IAccessibilityProps,
|
|
299
|
+
IAriaProps,
|
|
300
|
+
IAccessibilityRole,
|
|
301
|
+
IRole,
|
|
302
|
+
IAccessibilityStateValue,
|
|
303
|
+
IAccessibilityValue,
|
|
304
|
+
IAccessibilityActionInfo,
|
|
305
|
+
IResponderProps,
|
|
306
|
+
IPressState,
|
|
307
|
+
IPressableAndroidRippleConfig,
|
|
308
|
+
IImageStatics,
|
|
309
|
+
} from '@symbiote-native/components';
|
|
310
|
+
|
|
311
|
+
// Diagnostics: per-window Angular-side counters (change-detection passes, renderer writes, list
|
|
312
|
+
// recomputes, cell views). The engine's readCommitProfile() prices the commit; these say how many
|
|
313
|
+
// times anything ran at all - the number that exposes a change-detection free-run.
|
|
314
|
+
export {
|
|
315
|
+
readAngularProfile,
|
|
316
|
+
readAngularProfileDetail,
|
|
317
|
+
setAngularProfileDetail,
|
|
318
|
+
} from './diagnostics';
|
|
319
|
+
export type { IAngularProfile, IAngularProfileDetail } from './diagnostics';
|
|
@@ -1,101 +1,52 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// (
|
|
9
|
-
//
|
|
1
|
+
// Angular's binding of the shared AnimatedProps leaf lifecycle (core/engine's
|
|
2
|
+
// createAnimatedLeafLifecycle). The POLICY - when to rebuild, when to skip, attach-new-before-
|
|
3
|
+
// detach-old, native event rebinding - lives in the engine and is identical for every adapter;
|
|
4
|
+
// this class supplies only the two things that are genuinely Angular's: how the host node is
|
|
5
|
+
// resolved (a @ViewChild directive, not a ref), and the whenCommitted deferral its batched
|
|
6
|
+
// change detection forces.
|
|
7
|
+
//
|
|
8
|
+
// Pure Fabrication (GRASP): kept as its own class rather than folded into AnimatedComponentBase so
|
|
9
|
+
// AnimatedImage - which must extend ImageBase instead - gets the same wiring by composition rather
|
|
10
|
+
// than copy-paste.
|
|
10
11
|
import {
|
|
11
|
-
|
|
12
|
-
attachNativeEventHandler,
|
|
13
|
-
dlog,
|
|
12
|
+
createAnimatedLeafLifecycle,
|
|
14
13
|
whenCommitted,
|
|
14
|
+
type IAnimatedLeafLifecycle,
|
|
15
15
|
type ISymbioteNode,
|
|
16
16
|
} from '@symbiote-native/engine';
|
|
17
17
|
|
|
18
18
|
export class AnimatedLeafBinder {
|
|
19
|
-
|
|
20
|
-
private attached: AnimatedProps | null = null;
|
|
21
|
-
// The whenCommitted waiter for the current leaf's node binding, cancelled before the next.
|
|
22
|
-
private cancelBind: (() => void) | undefined;
|
|
23
|
-
// Detachers for any Animated.event prop bound natively to the committed node (JS path: none).
|
|
24
|
-
private eventDetachers: Array<() => void> = [];
|
|
19
|
+
private readonly lifecycle: IAnimatedLeafLifecycle;
|
|
25
20
|
|
|
26
21
|
// `resolveNode` stays a caller-supplied resolver (not a constructor-captured node) so this
|
|
27
|
-
// class never needs to know HOW the host node is found
|
|
22
|
+
// class never needs to know HOW the host node is found - AnimatedComponentBase and
|
|
28
23
|
// AnimatedImage both resolve it the same way (resolveHostNode + isSymbioteNode over their
|
|
29
24
|
// own @ViewChild), but that's Angular DI state this binder has no business touching.
|
|
30
25
|
// `label` is purely diagnostic (dlog), mirroring the constructor.name the pre-extraction
|
|
31
26
|
// dlog call used.
|
|
32
27
|
constructor(
|
|
33
28
|
private readonly resolveNode: () => ISymbioteNode | null,
|
|
34
|
-
|
|
35
|
-
) {
|
|
36
|
-
|
|
37
|
-
// Build a fresh AnimatedProps leaf from the current props (the React/Vue per-render
|
|
38
|
-
// useMemo(rest)), wire it into the graph, and swap the previous one out. Attach the NEW leaf
|
|
39
|
-
// BEFORE detaching the OLD one: a shared Value self-detaches (dropping its native node) the
|
|
40
|
-
// instant its child count hits zero, so detaching first would kill a running native animation
|
|
41
|
-
// on any unrelated re-render (mirrors RN's AnimatedComponent._attachProps). Then bind the
|
|
42
|
-
// committed node, go native if wanted, and rebind native events.
|
|
43
|
-
reconcile(props: Record<string, unknown>, wantsNative: boolean): void {
|
|
44
|
-
dlog(`${this.label} reconcile`);
|
|
45
|
-
const newLeaf = new AnimatedProps(props);
|
|
46
|
-
newLeaf.__attach();
|
|
47
|
-
if (this.attached !== null && this.attached !== newLeaf) this.attached.__detach();
|
|
48
|
-
this.attached = newLeaf;
|
|
49
|
-
|
|
50
|
-
this.bindNode(newLeaf, props, wantsNative);
|
|
29
|
+
label: string,
|
|
30
|
+
) {
|
|
31
|
+
this.lifecycle = createAnimatedLeafLifecycle(label);
|
|
51
32
|
}
|
|
52
33
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
wantsNative: boolean,
|
|
61
|
-
): void {
|
|
62
|
-
this.cancelBind?.();
|
|
63
|
-
this.cancelBind = undefined;
|
|
34
|
+
// Only the NATIVE half is deferred through whenCommitted (which runs it NOW when the node is
|
|
35
|
+
// already committed - the steady state, and the only path a scroll frame takes). The leaf itself
|
|
36
|
+
// always joins the value graph synchronously: deferring THAT too put the build behind a
|
|
37
|
+
// canceller the next reconcile drops, so a component reconciling faster than it commits never
|
|
38
|
+
// attached anything and a sticky header's rebuilt interpolation never reached the graph.
|
|
39
|
+
// Regression-covered in leaf-attach-before-commit.test.ts.
|
|
40
|
+
reconcile(props: Record<string, unknown>, wantsNative: boolean): void {
|
|
64
41
|
const node = this.resolveNode();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (wantsNative) leaf.__makeNative();
|
|
69
|
-
this.attachEvents(node, props);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Native-attach any Animated.event prop (e.g. onScroll={Animated.event(…,{useNativeDriver:true})})
|
|
74
|
-
// to the committed node. attachNativeEventHandler no-ops (returns undefined) unless the prop is a
|
|
75
|
-
// native event handler with a committed tag, so the JS path stays the fallback. Rebound each
|
|
76
|
-
// reconcile so a new inline event re-attaches; detached first to avoid leaking the prior binding.
|
|
77
|
-
private attachEvents(node: ISymbioteNode, props: Record<string, unknown>): void {
|
|
78
|
-
this.detachEvents();
|
|
79
|
-
for (const key of Object.keys(props)) {
|
|
80
|
-
const attachment = attachNativeEventHandler(node, key, props[key]);
|
|
81
|
-
if (attachment !== undefined) this.eventDetachers.push(attachment.detach);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private detachEvents(): void {
|
|
86
|
-
for (const detach of this.eventDetachers) detach();
|
|
87
|
-
this.eventDetachers = [];
|
|
42
|
+
this.lifecycle.reconcile(props, node, wantsNative, bind =>
|
|
43
|
+
node === null ? undefined : whenCommitted(node, bind),
|
|
44
|
+
);
|
|
88
45
|
}
|
|
89
46
|
|
|
90
47
|
// Tear down everything: cancel a still-pending bind, drop native event listeners, detach the
|
|
91
48
|
// current leaf from the value graph. Safe to call even if reconcile() was never called.
|
|
92
49
|
destroy(): void {
|
|
93
|
-
this.
|
|
94
|
-
this.cancelBind = undefined;
|
|
95
|
-
this.detachEvents();
|
|
96
|
-
if (this.attached !== null) {
|
|
97
|
-
this.attached.__detach();
|
|
98
|
-
this.attached = null;
|
|
99
|
-
}
|
|
50
|
+
this.lifecycle.teardown();
|
|
100
51
|
}
|
|
101
52
|
}
|