@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
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
// createAnimatedComponent for @symbiote-native/angular
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// values for the first paint, bind the leaf to the committed host node, swap leaves on
|
|
11
|
-
// re-render, tear down on destroy) lives once in AnimatedComponentBase; only the per-host
|
|
12
|
-
// template differs.
|
|
1
|
+
// createAnimatedComponent for @symbiote-native/angular. React/Vue have a runtime HOC
|
|
2
|
+
// (`createAnimatedComponent(Component)` builds a fresh wrapped component on the fly); Angular
|
|
3
|
+
// can't — the AOT-under-Metro build compiles every @Component ahead of time and ships no JIT
|
|
4
|
+
// compiler to Hermes, so a runtime `Component({...})(class)` would throw. The AOT-safe
|
|
5
|
+
// equivalent is an EXPLICIT set of standalone components — AnimatedView / AnimatedText /
|
|
6
|
+
// AnimatedImage / AnimatedScrollView — each a thin @Component over the matching host primitive,
|
|
7
|
+
// sharing one abstract lifecycle base. The mechanism (build an AnimatedProps leaf, reduce
|
|
8
|
+
// animated props to current values for the first paint, bind the leaf to the committed host
|
|
9
|
+
// node, swap leaves on re-render, tear down on destroy) lives once in AnimatedComponentBase.
|
|
13
10
|
//
|
|
14
11
|
// The framework-agnostic half (reduceProps / readPassthroughStyle / resolveHostNode /
|
|
15
12
|
// AnimatedProps / attachNativeEventHandler / isNativeAnimatedAvailable) comes from
|
|
16
|
-
// @symbiote-native/engine, shared
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// setNativeProps(node), exactly as on the other adapters.
|
|
13
|
+
// @symbiote-native/engine, shared with React and Vue. The per-frame path NEVER goes through
|
|
14
|
+
// Angular change detection: value.setValue / animation -> flushValue -> AnimatedProps.update()
|
|
15
|
+
// -> setNativeProps(node), same as the other adapters.
|
|
20
16
|
|
|
21
17
|
import {
|
|
22
18
|
ChangeDetectionStrategy,
|
|
@@ -25,8 +21,10 @@ import {
|
|
|
25
21
|
Directive,
|
|
26
22
|
ElementRef,
|
|
27
23
|
inject,
|
|
24
|
+
signal,
|
|
28
25
|
ViewChild,
|
|
29
26
|
type AfterViewInit,
|
|
27
|
+
type DoCheck,
|
|
30
28
|
type OnChanges,
|
|
31
29
|
type OnDestroy,
|
|
32
30
|
type Type,
|
|
@@ -43,11 +41,12 @@ import { selectScrollIntrinsics } from '@symbiote-native/components';
|
|
|
43
41
|
import { Image, ScrollView, Text, View } from '../../components';
|
|
44
42
|
import {
|
|
45
43
|
anchorHostStyle,
|
|
46
|
-
ViewHost,
|
|
47
|
-
TextHost,
|
|
48
44
|
ImageHost,
|
|
49
45
|
ScrollViewHost,
|
|
50
46
|
SymbioteHostPropsDirective,
|
|
47
|
+
SymbioteStyleInputDirective,
|
|
48
|
+
TextHost,
|
|
49
|
+
ViewHost,
|
|
51
50
|
} from '../../primitives';
|
|
52
51
|
import { FlatList } from '../../components/flat-list';
|
|
53
52
|
import {
|
|
@@ -67,11 +66,14 @@ const PASSTHROUGH_PROP = 'passthroughAnimatedPropExplicitValues';
|
|
|
67
66
|
// The inputs every animated wrapper accepts. `style` is the (possibly animated) style — the
|
|
68
67
|
// common path (TouchableOpacity passes `[style, { opacity }]`). `animatedProps` is the generic
|
|
69
68
|
// escape hatch for any OTHER prop that may hold an AnimatedNode or an Animated.event handler,
|
|
70
|
-
// plus static host props to forward (the React/Vue `rest`).
|
|
71
|
-
//
|
|
72
|
-
// Switch/Image), so they are inherited consistently.
|
|
69
|
+
// plus static host props to forward (the React/Vue `rest`). Each concrete @Component re-lists
|
|
70
|
+
// these (Angular's inputs-on-base convention, mirroring Switch/Image).
|
|
73
71
|
export const ANIMATED_INPUTS = ['style', 'animatedProps', PASSTHROUGH_PROP];
|
|
74
|
-
export const ANIMATED_IMAGE_INPUTS = [
|
|
72
|
+
export const ANIMATED_IMAGE_INPUTS = [
|
|
73
|
+
...IMAGE_INPUTS,
|
|
74
|
+
'animatedProps',
|
|
75
|
+
PASSTHROUGH_PROP,
|
|
76
|
+
];
|
|
75
77
|
|
|
76
78
|
// The shared lifecycle for every animated wrapper. Concrete subclasses add ONLY a @Component
|
|
77
79
|
// decorator (selector + the host-primitive template) — no behavior. Plain fields (no @Input
|
|
@@ -79,7 +81,9 @@ export const ANIMATED_IMAGE_INPUTS = [...IMAGE_INPUTS, 'animatedProps', PASSTHRO
|
|
|
79
81
|
// @Directive() (no selector) is the Angular-sanctioned decorator for an abstract base that
|
|
80
82
|
// declares lifecycle hooks — without it ngtsc rejects the inherited hooks (NG2007).
|
|
81
83
|
@Directive()
|
|
82
|
-
export abstract class AnimatedComponentBase
|
|
84
|
+
export abstract class AnimatedComponentBase
|
|
85
|
+
implements AfterViewInit, DoCheck, OnChanges, OnDestroy
|
|
86
|
+
{
|
|
83
87
|
style: unknown;
|
|
84
88
|
animatedProps: Record<string, unknown> | undefined;
|
|
85
89
|
passthroughAnimatedPropExplicitValues: unknown;
|
|
@@ -92,7 +96,8 @@ export abstract class AnimatedComponentBase implements AfterViewInit, OnChanges,
|
|
|
92
96
|
|
|
93
97
|
// The directive sitting on the inner host primitive; its `node` is the committed SymbioteNode
|
|
94
98
|
// the leaf binds to. Inherited by the decorated subclass (Angular collects base-class queries).
|
|
95
|
-
@ViewChild(SymbioteHostPropsDirective)
|
|
99
|
+
@ViewChild(SymbioteHostPropsDirective)
|
|
100
|
+
private hostDirective?: SymbioteHostPropsDirective;
|
|
96
101
|
|
|
97
102
|
// The leaf-lifecycle orchestration (build/attach/swap/bind/detach an AnimatedProps leaf) is a
|
|
98
103
|
// Pure Fabrication shared with AnimatedImage, which cannot extend this class (it must extend
|
|
@@ -110,15 +115,38 @@ export abstract class AnimatedComponentBase implements AfterViewInit, OnChanges,
|
|
|
110
115
|
// array, which the commit layer flattens) so the ShadowTree carries the current transform.
|
|
111
116
|
get reducedProps(): Record<string, unknown> {
|
|
112
117
|
const reduced = reduceProps(this.mergedProps());
|
|
113
|
-
const passthroughStyle = readPassthroughStyle(
|
|
118
|
+
const passthroughStyle = readPassthroughStyle(
|
|
119
|
+
this.passthroughAnimatedPropExplicitValues,
|
|
120
|
+
);
|
|
114
121
|
if (passthroughStyle !== undefined) {
|
|
115
122
|
reduced['style'] =
|
|
116
|
-
reduced['style'] === undefined
|
|
123
|
+
reduced['style'] === undefined
|
|
124
|
+
? passthroughStyle
|
|
125
|
+
: [reduced['style'], passthroughStyle];
|
|
117
126
|
}
|
|
118
|
-
reduced['style'] = [
|
|
127
|
+
reduced['style'] = [this.anchorStyle(), reduced['style']];
|
|
119
128
|
return reduced;
|
|
120
129
|
}
|
|
121
130
|
|
|
131
|
+
// The anchor's class-derived style, polled rather than read live inside `reducedProps`.
|
|
132
|
+
//
|
|
133
|
+
// Two different writers land style on this component's anchor and NEITHER goes through
|
|
134
|
+
// ngOnChanges: `class=` / `[class.x]` / `[ngClass]` at the use site (via the renderer's
|
|
135
|
+
// addClass/removeClass -> commitClassStyle), and a `[style]` binding from a composing parent
|
|
136
|
+
// that Angular writes as a host property because `style` is not declared as an @Input here.
|
|
137
|
+
// Reading them live in the getter worked at CREATION only - nothing dirties THIS view when the
|
|
138
|
+
// anchor changes, so the getter was never re-read and the update was lost. Device-visible as a
|
|
139
|
+
// TouchableOpacity whose `class` toggled after mount never repainted.
|
|
140
|
+
//
|
|
141
|
+
// ngDoCheck runs during the PARENT's refresh even when this view is skipped, and the signal
|
|
142
|
+
// write is what then marks this view for refresh. `signal.set`'s own Object.is makes an
|
|
143
|
+
// unchanged poll a no-op, so a steady screen costs one property read per pass and nothing else.
|
|
144
|
+
private readonly anchorStyle = signal<unknown>(undefined);
|
|
145
|
+
|
|
146
|
+
ngDoCheck(): void {
|
|
147
|
+
this.anchorStyle.set(anchorHostStyle(this.elementRef));
|
|
148
|
+
}
|
|
149
|
+
|
|
122
150
|
ngAfterViewInit(): void {
|
|
123
151
|
this.viewReady = true;
|
|
124
152
|
this.reconcile();
|
|
@@ -143,7 +171,8 @@ export abstract class AnimatedComponentBase implements AfterViewInit, OnChanges,
|
|
|
143
171
|
const hasPassthroughAnimatedValues =
|
|
144
172
|
this.passthroughAnimatedPropExplicitValues !== null &&
|
|
145
173
|
this.passthroughAnimatedPropExplicitValues !== undefined;
|
|
146
|
-
const wantsNative =
|
|
174
|
+
const wantsNative =
|
|
175
|
+
hasPassthroughAnimatedValues && isNativeAnimatedAvailable();
|
|
147
176
|
this.leafBinder.reconcile(props, wantsNative);
|
|
148
177
|
}
|
|
149
178
|
|
|
@@ -152,7 +181,9 @@ export abstract class AnimatedComponentBase implements AfterViewInit, OnChanges,
|
|
|
152
181
|
// never disagree.
|
|
153
182
|
private mergedProps(): Record<string, unknown> {
|
|
154
183
|
const base = this.animatedProps ?? {};
|
|
155
|
-
return this.style === undefined
|
|
184
|
+
return this.style === undefined
|
|
185
|
+
? { ...base }
|
|
186
|
+
: { ...base, style: this.style };
|
|
156
187
|
}
|
|
157
188
|
|
|
158
189
|
// The committed host node held by IDENTITY. resolveHostNode unwraps an imperative scroll
|
|
@@ -167,6 +198,9 @@ export abstract class AnimatedComponentBase implements AfterViewInit, OnChanges,
|
|
|
167
198
|
@Component({
|
|
168
199
|
selector: 'AnimatedView, symbiote-animated-view',
|
|
169
200
|
standalone: true,
|
|
201
|
+
hostDirectives: [
|
|
202
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
203
|
+
],
|
|
170
204
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
171
205
|
imports: [SymbioteHostPropsDirective, ViewHost],
|
|
172
206
|
inputs: ANIMATED_INPUTS,
|
|
@@ -182,6 +216,9 @@ export class AnimatedView extends AnimatedComponentBase {}
|
|
|
182
216
|
@Component({
|
|
183
217
|
selector: 'AnimatedText, symbiote-animated-text',
|
|
184
218
|
standalone: true,
|
|
219
|
+
hostDirectives: [
|
|
220
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
221
|
+
],
|
|
185
222
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
186
223
|
imports: [SymbioteHostPropsDirective, TextHost],
|
|
187
224
|
inputs: ANIMATED_INPUTS,
|
|
@@ -197,6 +234,9 @@ export class AnimatedText extends AnimatedComponentBase {}
|
|
|
197
234
|
@Component({
|
|
198
235
|
selector: 'AnimatedImage, symbiote-animated-image',
|
|
199
236
|
standalone: true,
|
|
237
|
+
hostDirectives: [
|
|
238
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
239
|
+
],
|
|
200
240
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
201
241
|
imports: [SymbioteHostPropsDirective, ImageHost],
|
|
202
242
|
inputs: ANIMATED_IMAGE_INPUTS,
|
|
@@ -220,7 +260,10 @@ export class AnimatedText extends AnimatedComponentBase {}
|
|
|
220
260
|
`,
|
|
221
261
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
222
262
|
})
|
|
223
|
-
export class AnimatedImage
|
|
263
|
+
export class AnimatedImage
|
|
264
|
+
extends ImageBase
|
|
265
|
+
implements AfterViewInit, OnChanges, OnDestroy
|
|
266
|
+
{
|
|
224
267
|
animatedProps: Record<string, unknown> | undefined;
|
|
225
268
|
passthroughAnimatedPropExplicitValues: unknown;
|
|
226
269
|
|
|
@@ -228,7 +271,8 @@ export class AnimatedImage extends ImageBase implements AfterViewInit, OnChanges
|
|
|
228
271
|
// comment on AnimatedComponentBase.elementRef.
|
|
229
272
|
private readonly elementRef = inject(ElementRef);
|
|
230
273
|
|
|
231
|
-
@ViewChild(SymbioteHostPropsDirective)
|
|
274
|
+
@ViewChild(SymbioteHostPropsDirective)
|
|
275
|
+
private hostDirective?: SymbioteHostPropsDirective;
|
|
232
276
|
|
|
233
277
|
// Same Pure Fabrication AnimatedComponentBase holds — see its matching comment. AnimatedImage
|
|
234
278
|
// can't extend AnimatedComponentBase (it must extend ImageBase, see the file header), so it
|
|
@@ -241,16 +285,39 @@ export class AnimatedImage extends ImageBase implements AfterViewInit, OnChanges
|
|
|
241
285
|
|
|
242
286
|
get animatedImageProps(): Record<string, unknown> {
|
|
243
287
|
const reduced = reduceProps(this.mergedProps());
|
|
244
|
-
const passthroughStyle = readPassthroughStyle(
|
|
288
|
+
const passthroughStyle = readPassthroughStyle(
|
|
289
|
+
this.passthroughAnimatedPropExplicitValues,
|
|
290
|
+
);
|
|
245
291
|
if (passthroughStyle !== undefined) {
|
|
246
292
|
reduced['style'] =
|
|
247
|
-
reduced['style'] === undefined
|
|
293
|
+
reduced['style'] === undefined
|
|
294
|
+
? passthroughStyle
|
|
295
|
+
: [reduced['style'], passthroughStyle];
|
|
248
296
|
}
|
|
249
297
|
const resolved = resolveImageProps(reduced);
|
|
250
298
|
resolved['style'] = [anchorHostStyle(this.elementRef), resolved['style']];
|
|
251
299
|
return resolved;
|
|
252
300
|
}
|
|
253
301
|
|
|
302
|
+
// The anchor's class-derived style, polled rather than read live inside `reducedProps`.
|
|
303
|
+
//
|
|
304
|
+
// Two different writers land style on this component's anchor and NEITHER goes through
|
|
305
|
+
// ngOnChanges: `class=` / `[class.x]` / `[ngClass]` at the use site (via the renderer's
|
|
306
|
+
// addClass/removeClass -> commitClassStyle), and a `[style]` binding from a composing parent
|
|
307
|
+
// that Angular writes as a host property because `style` is not declared as an @Input here.
|
|
308
|
+
// Reading them live in the getter worked at CREATION only - nothing dirties THIS view when the
|
|
309
|
+
// anchor changes, so the getter was never re-read and the update was lost. Device-visible as a
|
|
310
|
+
// TouchableOpacity whose `class` toggled after mount never repainted.
|
|
311
|
+
//
|
|
312
|
+
// ngDoCheck runs during the PARENT's refresh even when this view is skipped, and the signal
|
|
313
|
+
// write is what then marks this view for refresh. `signal.set`'s own Object.is makes an
|
|
314
|
+
// unchanged poll a no-op, so a steady screen costs one property read per pass and nothing else.
|
|
315
|
+
private readonly anchorStyle = signal<unknown>(undefined);
|
|
316
|
+
|
|
317
|
+
ngDoCheck(): void {
|
|
318
|
+
this.anchorStyle.set(anchorHostStyle(this.elementRef));
|
|
319
|
+
}
|
|
320
|
+
|
|
254
321
|
ngAfterViewInit(): void {
|
|
255
322
|
this.viewReady = true;
|
|
256
323
|
this.reconcile();
|
|
@@ -270,7 +337,8 @@ export class AnimatedImage extends ImageBase implements AfterViewInit, OnChanges
|
|
|
270
337
|
const hasPassthroughAnimatedValues =
|
|
271
338
|
this.passthroughAnimatedPropExplicitValues !== null &&
|
|
272
339
|
this.passthroughAnimatedPropExplicitValues !== undefined;
|
|
273
|
-
const wantsNative =
|
|
340
|
+
const wantsNative =
|
|
341
|
+
hasPassthroughAnimatedValues && isNativeAnimatedAvailable();
|
|
274
342
|
this.leafBinder.reconcile(props, wantsNative);
|
|
275
343
|
}
|
|
276
344
|
|
|
@@ -291,6 +359,9 @@ export class AnimatedImage extends ImageBase implements AfterViewInit, OnChanges
|
|
|
291
359
|
@Component({
|
|
292
360
|
selector: 'AnimatedScrollView, symbiote-animated-scroll-view',
|
|
293
361
|
standalone: true,
|
|
362
|
+
hostDirectives: [
|
|
363
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
364
|
+
],
|
|
294
365
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
295
366
|
imports: [SymbioteHostPropsDirective, ScrollViewHost],
|
|
296
367
|
inputs: ANIMATED_INPUTS,
|
|
@@ -305,20 +376,17 @@ export class AnimatedImage extends ImageBase implements AfterViewInit, OnChanges
|
|
|
305
376
|
})
|
|
306
377
|
export class AnimatedScrollView extends AnimatedComponentBase {
|
|
307
378
|
// RN defaults nested scrolling ON (ScrollView.js `nestedScrollEnabled ?? true`) — Android
|
|
308
|
-
// needs it explicit or a ScrollView
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
379
|
+
// needs it explicit or a nested ScrollView never receives touch (swallowed by the outer one;
|
|
380
|
+
// iOS doesn't gate on this). The real ScrollView component already defaults this
|
|
381
|
+
// (scroll-view/shared.ts), but AnimatedScrollView talks to the raw primitive directly and
|
|
382
|
+
// must apply it too.
|
|
312
383
|
//
|
|
313
|
-
// scrollViewBaseStyle (overflow: 'scroll' + the per-axis flexDirection) is the
|
|
314
|
-
//
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
// (symbiote-scroll-view/symbiote-scroll-content, not the horizontal variants), so
|
|
320
|
-
// selectScrollIntrinsics is always called with isHorizontal=false here — matching what the
|
|
321
|
-
// template can actually render.
|
|
384
|
+
// scrollViewBaseStyle (overflow: 'scroll' + the per-axis flexDirection) is the other thing the
|
|
385
|
+
// real ScrollView applies that this bespoke template used to skip — without it, iOS Fabric
|
|
386
|
+
// never clips content to the scroll view's frame (invisible on Android, whose native ViewGroup
|
|
387
|
+
// clips regardless of style). This wrapper has no `horizontal` input and its template is
|
|
388
|
+
// hardcoded to the vertical intrinsics, so selectScrollIntrinsics is always called with
|
|
389
|
+
// isHorizontal=false here, matching what the template can actually render.
|
|
322
390
|
override get reducedProps(): Record<string, unknown> {
|
|
323
391
|
const reduced = super.reducedProps;
|
|
324
392
|
const { scrollViewBaseStyle } = selectScrollIntrinsics(false, undefined);
|
|
@@ -15,17 +15,17 @@ import {
|
|
|
15
15
|
} from '@symbiote-native/engine';
|
|
16
16
|
import { mount } from '../../render';
|
|
17
17
|
|
|
18
|
-
// RN's IComponentProvider
|
|
19
|
-
// graph stays cheap until the app actually runs).
|
|
18
|
+
// RN's IComponentProvider. Lazy so the module graph stays cheap until the app actually runs.
|
|
20
19
|
export type IComponentProvider = () => Type<unknown>;
|
|
21
20
|
|
|
22
|
-
// RN's IWrapperComponentProvider
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
// RN's IWrapperComponentProvider (e.g. a context provider). Angular has no runtime template
|
|
22
|
+
// synthesis (no JIT under AOT/Metro — see create-animated-component.ts), so the wrapper must be
|
|
23
|
+
// a pre-authored standalone @Component whose template renders `<ng-content>` — the Angular idiom
|
|
24
|
+
// for "render my children" (mount() projects the root's host node into it, the twin of React's
|
|
25
|
+
// createElement(Wrapper, null, rootElement)).
|
|
26
|
+
export type IWrapperComponentProvider = (
|
|
27
|
+
appParameters: IAppParameters,
|
|
28
|
+
) => Type<unknown>;
|
|
29
29
|
|
|
30
30
|
function runnableFor(
|
|
31
31
|
componentProvider: IComponentProvider,
|
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
// ngOnChanges on mount + every prop change. Imperative statics are attached to the component
|
|
6
6
|
// object the same way RN does.
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Component,
|
|
10
|
+
Input,
|
|
11
|
+
type OnChanges,
|
|
12
|
+
type SimpleChanges,
|
|
13
|
+
} from '@angular/core';
|
|
9
14
|
import {
|
|
10
15
|
applyStatusBarProps,
|
|
11
16
|
statusBarImperative,
|
|
@@ -46,12 +51,16 @@ class StatusBarComponent implements OnChanges {
|
|
|
46
51
|
}
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
const StatusBarWithStatics = Object.assign(
|
|
54
|
+
const StatusBarWithStatics = Object.assign(
|
|
55
|
+
StatusBarComponent,
|
|
56
|
+
statusBarImperative,
|
|
57
|
+
);
|
|
50
58
|
|
|
51
59
|
Object.defineProperty(StatusBarWithStatics, 'currentHeight', {
|
|
52
60
|
get: statusBarCurrentHeight,
|
|
53
61
|
enumerable: true,
|
|
54
62
|
});
|
|
55
63
|
|
|
56
|
-
export const StatusBar: typeof StatusBarWithStatics & {
|
|
57
|
-
|
|
64
|
+
export const StatusBar: typeof StatusBarWithStatics & {
|
|
65
|
+
readonly currentHeight?: number;
|
|
66
|
+
} = StatusBarWithStatics;
|
package/src/primitives/index.ts
CHANGED
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
// component imports them and renders them in its template. Declaring `style` as a real
|
|
4
4
|
// Angular input prevents Angular's CSS style engine from decomposing RN `StyleProp` arrays.
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Component,
|
|
8
|
+
Input,
|
|
9
|
+
type OnInit,
|
|
10
|
+
type SimpleChanges,
|
|
11
|
+
} from '@angular/core';
|
|
12
|
+
import {
|
|
13
|
+
resolveTextProps,
|
|
14
|
+
type IEllipsizeMode,
|
|
15
|
+
} from '@symbiote-native/components';
|
|
7
16
|
import { SymbiotePrimitiveHost } from './shared';
|
|
8
17
|
|
|
9
18
|
export {
|
|
@@ -11,6 +20,7 @@ export {
|
|
|
11
20
|
anchorStyleProp,
|
|
12
21
|
stableAnchorStyle,
|
|
13
22
|
SymbioteHostPropsDirective,
|
|
23
|
+
SymbioteStyleInputDirective,
|
|
14
24
|
} from './shared';
|
|
15
25
|
|
|
16
26
|
@Component({
|
|
@@ -20,12 +30,47 @@ export {
|
|
|
20
30
|
})
|
|
21
31
|
export class ViewHost extends SymbiotePrimitiveHost {}
|
|
22
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Text carries the two defaults RN's Text.js applies unconditionally (`ellipsizeMode ?? 'tail'`,
|
|
35
|
+
* `allowFontScaling !== false`, Text.js:288-291). Without them native falls back to `clip` and a
|
|
36
|
+
* clamped Text cuts mid-word with no ellipsis — device-observed on the other adapters 2026-08-19.
|
|
37
|
+
*
|
|
38
|
+
* They are declared as real `@Input()`s, unlike the pass-through props the base's comment
|
|
39
|
+
* describes, for one reason: the default can only be applied by code that can SEE whether the
|
|
40
|
+
* caller supplied a value. A host-binding pass-through is invisible here, so blindly writing
|
|
41
|
+
* 'tail' would silently overwrite an explicit `ellipsizeMode="clip"`.
|
|
42
|
+
*/
|
|
23
43
|
@Component({
|
|
24
44
|
selector: 'symbiote-text, Text',
|
|
25
45
|
standalone: true,
|
|
26
46
|
template: '<ng-content></ng-content>',
|
|
27
47
|
})
|
|
28
|
-
export class TextHost extends SymbiotePrimitiveHost {
|
|
48
|
+
export class TextHost extends SymbiotePrimitiveHost implements OnInit {
|
|
49
|
+
@Input() ellipsizeMode?: IEllipsizeMode;
|
|
50
|
+
@Input() allowFontScaling?: boolean;
|
|
51
|
+
|
|
52
|
+
// ngOnChanges fires before ngOnInit when a binding exists, and not at all when none does —
|
|
53
|
+
// so both hooks are needed to guarantee the defaults land exactly once per settled value.
|
|
54
|
+
ngOnInit(): void {
|
|
55
|
+
this.applyTextDefaults();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override ngOnChanges(changes: SimpleChanges): void {
|
|
59
|
+
super.ngOnChanges(changes);
|
|
60
|
+
if ('ellipsizeMode' in changes || 'allowFontScaling' in changes) {
|
|
61
|
+
this.applyTextDefaults();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private applyTextDefaults(): void {
|
|
66
|
+
const resolved = resolveTextProps({
|
|
67
|
+
ellipsizeMode: this.ellipsizeMode,
|
|
68
|
+
allowFontScaling: this.allowFontScaling,
|
|
69
|
+
});
|
|
70
|
+
this.setHostProp('ellipsizeMode', resolved.ellipsizeMode);
|
|
71
|
+
this.setHostProp('allowFontScaling', resolved.allowFontScaling);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
29
74
|
|
|
30
75
|
@Component({
|
|
31
76
|
selector: 'symbiote-image',
|