@symbiote-native/angular 0.3.3 → 0.5.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/babel-register-composed.cjs +119 -0
- package/bin/symbiote-angular-dev.cjs +43 -1
- package/build/angular/components/image/shared.d.ts +4 -4
- package/build/angular/components/image/shared.js +1 -3
- package/build/angular/components/image/shared.js.map +1 -1
- package/build/angular/components/{input-accessory-view.d.ts → input-accessory-view/index.d.ts} +1 -1
- package/build/angular/components/{input-accessory-view.js → input-accessory-view/index.js} +3 -7
- package/build/angular/components/input-accessory-view/index.js.map +1 -0
- package/build/angular/components/{keyboard-avoiding-view.js → keyboard-avoiding-view/index.js} +3 -8
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -0
- package/build/angular/components/{modal.js → modal/index.js} +3 -8
- package/build/angular/components/modal/index.js.map +1 -0
- package/build/angular/components/pressable/index.js +13 -14
- package/build/angular/components/pressable/index.js.map +1 -1
- package/build/angular/components/{refresh-control.js → refresh-control/index.js} +3 -8
- package/build/angular/components/refresh-control/index.js.map +1 -0
- package/build/angular/components/{safe-area-view.d.ts → safe-area-view/index.d.ts} +1 -1
- package/build/angular/components/{safe-area-view.js → safe-area-view/index.js} +3 -7
- package/build/angular/components/safe-area-view/index.js.map +1 -0
- package/build/angular/components/text-input.js +1 -6
- package/build/angular/components/text-input.js.map +1 -1
- package/build/angular/components/{touchable.d.ts → touchable/index.d.ts} +1 -1
- package/build/angular/components/{touchable.js → touchable/index.js} +4 -4
- package/build/angular/components/touchable/index.js.map +1 -0
- package/build/angular/components/{touchable-native-feedback.d.ts → touchable-native-feedback/index.d.ts} +1 -1
- package/build/angular/components/{touchable-native-feedback.js → touchable-native-feedback/index.js} +4 -9
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -0
- package/build/angular/{create-portal.js → create-portal/index.js} +1 -1
- package/build/angular/create-portal/index.js.map +1 -0
- package/build/angular/{create-tunnel.js → create-tunnel/index.js} +1 -1
- package/build/angular/create-tunnel/index.js.map +1 -0
- package/build/angular/index.d.ts +1 -1
- package/build/angular/index.js +1 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +14 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.js +91 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +1 -0
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +14 -0
- package/build/angular/modules/animated/animated-leaf-binder.js +91 -0
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -0
- package/build/angular/modules/animated/create-animated-component.d.ts +3 -12
- package/build/angular/modules/animated/create-animated-component.js +48 -113
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/{status-bar.js → status-bar/index.js} +1 -1
- package/build/angular/modules/status-bar/index.js.map +1 -0
- package/build/angular/{render.js → render/index.js} +15 -4
- package/build/angular/render/index.js.map +1 -0
- package/build/{renderer.js → angular/renderer/index.js} +17 -18
- package/build/angular/renderer/index.js.map +1 -0
- package/build/components/image/shared.d.ts +4 -4
- package/build/components/image/shared.js +1 -3
- package/build/components/{input-accessory-view.d.ts → input-accessory-view/index.d.ts} +1 -1
- package/build/components/{input-accessory-view.js → input-accessory-view/index.js} +2 -6
- package/build/components/{keyboard-avoiding-view.js → keyboard-avoiding-view/index.js} +2 -7
- package/build/components/{modal.js → modal/index.js} +2 -7
- package/build/components/pressable/index.js +13 -14
- package/build/components/{refresh-control.js → refresh-control/index.js} +2 -7
- package/build/components/{safe-area-view.d.ts → safe-area-view/index.d.ts} +1 -1
- package/build/components/{safe-area-view.js → safe-area-view/index.js} +2 -6
- package/build/components/text-input.js +1 -6
- package/build/components/{touchable.d.ts → touchable/index.d.ts} +1 -1
- package/build/components/{touchable.js → touchable/index.js} +3 -3
- package/build/components/{touchable-native-feedback.d.ts → touchable-native-feedback/index.d.ts} +1 -1
- package/build/components/{touchable-native-feedback.js → touchable-native-feedback/index.js} +3 -8
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/modules/animated/animated-leaf-binder/index.d.ts +14 -0
- package/build/modules/animated/animated-leaf-binder/index.js +90 -0
- package/build/modules/animated/animated-leaf-binder.d.ts +14 -0
- package/build/modules/animated/animated-leaf-binder.js +90 -0
- package/build/modules/animated/create-animated-component.d.ts +3 -12
- package/build/modules/animated/create-animated-component.js +47 -112
- package/build/{render.js → render/index.js} +14 -3
- package/build/{angular/renderer.js → renderer/index.js} +16 -19
- package/package.json +10 -5
- package/src/angular-gaps.test.ts +1 -1
- package/src/components/image/shared.ts +6 -5
- package/src/components/{input-accessory-view.ts → input-accessory-view/index.ts} +11 -8
- package/src/components/{input-accessory-view.test.ts → input-accessory-view/input-accessory-view.test.ts} +2 -2
- package/src/components/{keyboard-avoiding-view.ts → keyboard-avoiding-view/index.ts} +2 -7
- package/src/components/{keyboard-avoiding-view.test.ts → keyboard-avoiding-view/keyboard-avoiding-view.test.ts} +2 -2
- package/src/components/{modal.ts → modal/index.ts} +2 -7
- package/src/components/{modal.test.ts → modal/modal.test.ts} +2 -2
- package/src/components/pressable/index.ts +17 -15
- package/src/components/{refresh-control.ts → refresh-control/index.ts} +2 -7
- package/src/components/{refresh-control.test.ts → refresh-control/refresh-control.test.ts} +2 -2
- package/src/components/{safe-area-view.ts → safe-area-view/index.ts} +7 -8
- package/src/components/{safe-area-view.test.ts → safe-area-view/safe-area-view.test.ts} +2 -2
- package/src/components/text-input.ts +1 -6
- package/src/components/{touchable.ts → touchable/index.ts} +3 -3
- package/src/components/{touchable.test.ts → touchable/touchable.test.ts} +2 -2
- package/src/components/{touchable-native-feedback.ts → touchable-native-feedback/index.ts} +3 -9
- package/src/components/{touchable-native-feedback.test.ts → touchable-native-feedback/touchable-native-feedback.test.ts} +2 -2
- package/src/{create-portal.test.ts → create-portal/create-portal.test.ts} +3 -3
- package/src/{create-tunnel.test.ts → create-tunnel/create-tunnel.test.ts} +3 -3
- package/src/index.ts +1 -1
- package/src/modules/animated/animated-leaf-binder/animated-leaf-binder.test.ts +116 -0
- package/src/modules/animated/animated-leaf-binder/index.ts +101 -0
- package/src/modules/animated/animated-leaf-binder.test.ts +116 -0
- package/src/modules/animated/animated-leaf-binder.ts +101 -0
- package/src/modules/animated/animated-scroll-view.test.ts +17 -0
- package/src/modules/animated/create-animated-component.ts +53 -123
- package/src/modules/{status-bar.test.ts → status-bar/status-bar.test.ts} +2 -2
- package/src/{render.ts → render/index.ts} +14 -2
- package/src/{render.test.ts → render/render.test.ts} +1 -1
- package/src/{renderer.ts → renderer/index.ts} +58 -58
- package/src/{renderer.test.ts → renderer/renderer.test.ts} +24 -1
- package/build/angular/components/input-accessory-view.js.map +0 -1
- package/build/angular/components/keyboard-avoiding-view.js.map +0 -1
- package/build/angular/components/modal.js.map +0 -1
- package/build/angular/components/refresh-control.js.map +0 -1
- package/build/angular/components/safe-area-view.js.map +0 -1
- package/build/angular/components/touchable-native-feedback.js.map +0 -1
- package/build/angular/components/touchable.js.map +0 -1
- package/build/angular/create-portal.js.map +0 -1
- package/build/angular/create-tunnel.js.map +0 -1
- package/build/angular/modules/status-bar.js.map +0 -1
- package/build/angular/render.js.map +0 -1
- package/build/angular/renderer.js.map +0 -1
- /package/build/angular/components/{keyboard-avoiding-view.d.ts → keyboard-avoiding-view/index.d.ts} +0 -0
- /package/build/angular/components/{modal.d.ts → modal/index.d.ts} +0 -0
- /package/build/angular/components/{refresh-control.d.ts → refresh-control/index.d.ts} +0 -0
- /package/build/angular/{create-portal.d.ts → create-portal/index.d.ts} +0 -0
- /package/build/angular/{create-tunnel.d.ts → create-tunnel/index.d.ts} +0 -0
- /package/build/angular/modules/{status-bar.d.ts → status-bar/index.d.ts} +0 -0
- /package/build/angular/{render.d.ts → render/index.d.ts} +0 -0
- /package/build/angular/{renderer.d.ts → renderer/index.d.ts} +0 -0
- /package/build/components/{keyboard-avoiding-view.d.ts → keyboard-avoiding-view/index.d.ts} +0 -0
- /package/build/components/{modal.d.ts → modal/index.d.ts} +0 -0
- /package/build/components/{refresh-control.d.ts → refresh-control/index.d.ts} +0 -0
- /package/build/{create-portal.d.ts → create-portal/index.d.ts} +0 -0
- /package/build/{create-portal.js → create-portal/index.js} +0 -0
- /package/build/{create-tunnel.d.ts → create-tunnel/index.d.ts} +0 -0
- /package/build/{create-tunnel.js → create-tunnel/index.js} +0 -0
- /package/build/modules/{status-bar.d.ts → status-bar/index.d.ts} +0 -0
- /package/build/modules/{status-bar.js → status-bar/index.js} +0 -0
- /package/build/{render.d.ts → render/index.d.ts} +0 -0
- /package/build/{renderer.d.ts → renderer/index.d.ts} +0 -0
- /package/src/{create-portal.ts → create-portal/index.ts} +0 -0
- /package/src/{create-tunnel.ts → create-tunnel/index.ts} +0 -0
- /package/src/modules/{status-bar.ts → status-bar/index.ts} +0 -0
|
@@ -52,12 +52,14 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
52
52
|
return useValue ? value : void 0;
|
|
53
53
|
};
|
|
54
54
|
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, Directive, ElementRef, inject, ViewChild, } from '@angular/core';
|
|
55
|
-
import {
|
|
55
|
+
import { isNativeAnimatedAvailable, isSymbioteNode, reduceProps, readPassthroughStyle, resolveHostNode, } from '@symbiote-native/engine';
|
|
56
|
+
import { selectScrollIntrinsics } from '@symbiote-native/components';
|
|
56
57
|
import { Image, ScrollView, Text, View } from '../../components';
|
|
57
58
|
import { anchorHostStyle, ViewHost, TextHost, ImageHost, ScrollViewHost, SymbioteHostPropsDirective, } from '../../primitives';
|
|
58
59
|
import { FlatList } from '../../components/flat-list';
|
|
59
60
|
import { IMAGE_INPUTS, IMAGE_OUTPUTS, ImageBase, resolveImageProps, } from '../../components/image/shared';
|
|
60
61
|
import { SectionList } from '../../components/section-list';
|
|
62
|
+
import { AnimatedLeafBinder } from './animated-leaf-binder';
|
|
61
63
|
// RN's prop carrying explicit (already-rasterized) values that override the animated prop in
|
|
62
64
|
// the COMMITTED props (sticky-header passthrough). Named once so the directive input and the
|
|
63
65
|
// reconcile agree.
|
|
@@ -105,12 +107,11 @@ let AnimatedComponentBase = (() => {
|
|
|
105
107
|
// The directive sitting on the inner host primitive; its `node` is the committed SymbioteNode
|
|
106
108
|
// the leaf binds to. Inherited by the decorated subclass (Angular collects base-class queries).
|
|
107
109
|
hostDirective = __runInitializers(this, _hostDirective_initializers, void 0);
|
|
108
|
-
// The leaf
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
eventDetachers = [];
|
|
110
|
+
// The leaf-lifecycle orchestration (build/attach/swap/bind/detach an AnimatedProps leaf) is a
|
|
111
|
+
// Pure Fabrication shared with AnimatedImage, which cannot extend this class (it must extend
|
|
112
|
+
// ImageBase — see the file header). `resolveNode` stays a resolver, not a captured node: the
|
|
113
|
+
// committed node only exists after `hostDirective` is populated post-view-init.
|
|
114
|
+
leafBinder = (__runInitializers(this, _hostDirective_extraInitializers), new AnimatedLeafBinder(() => this.hostNode(), this.constructor.name));
|
|
114
115
|
// Set once the view (and the inner host directive) exists; ngOnChanges before then is a no-op.
|
|
115
116
|
viewReady = false;
|
|
116
117
|
// The concrete props for the host element each change-detection pass: animated entries reduced
|
|
@@ -134,73 +135,21 @@ let AnimatedComponentBase = (() => {
|
|
|
134
135
|
this.reconcile();
|
|
135
136
|
}
|
|
136
137
|
ngOnDestroy() {
|
|
137
|
-
this.
|
|
138
|
-
this.cancelBind = undefined;
|
|
139
|
-
this.detachEvents();
|
|
140
|
-
if (this.attached !== null) {
|
|
141
|
-
this.attached.__detach();
|
|
142
|
-
this.attached = null;
|
|
143
|
-
}
|
|
138
|
+
this.leafBinder.destroy();
|
|
144
139
|
}
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
// committed node, go native if wanted, and rebind native events.
|
|
140
|
+
// A non-stable [animatedProps]/[style] input (a fresh object literal in the template) pushes
|
|
141
|
+
// a new reference on every change-detection pass of the HOST view, re-triggering this on every
|
|
142
|
+
// unrelated interaction elsewhere in the app — DEBUG=1 turns this into a visible tick count in
|
|
143
|
+
// AnimatedLeafBinder.reconcile, so an unexpectedly high rate for a component nobody is
|
|
144
|
+
// animating is the tell (see AnimatedScrollView's stable-reference fix in examples/angular/App.ts).
|
|
151
145
|
reconcile() {
|
|
152
146
|
if (!this.viewReady)
|
|
153
147
|
return;
|
|
154
|
-
// A non-stable [animatedProps]/[style] input (a fresh object literal in the template)
|
|
155
|
-
// pushes a new reference on every change-detection pass of the HOST view, re-triggering
|
|
156
|
-
// this on every unrelated interaction elsewhere in the app — DEBUG=1 turns this into a
|
|
157
|
-
// visible tick count, so an unexpectedly high rate for a component nobody is animating
|
|
158
|
-
// is the tell (see AnimatedScrollView's stable-reference fix in examples/angular/App.ts).
|
|
159
|
-
dlog(`AnimatedComponentBase reconcile (${this.constructor.name})`);
|
|
160
148
|
const props = this.mergedProps();
|
|
161
149
|
const hasPassthroughAnimatedValues = this.passthroughAnimatedPropExplicitValues !== null &&
|
|
162
150
|
this.passthroughAnimatedPropExplicitValues !== undefined;
|
|
163
151
|
const wantsNative = hasPassthroughAnimatedValues && isNativeAnimatedAvailable();
|
|
164
|
-
|
|
165
|
-
newLeaf.__attach();
|
|
166
|
-
if (this.attached !== null && this.attached !== newLeaf)
|
|
167
|
-
this.attached.__detach();
|
|
168
|
-
this.attached = newLeaf;
|
|
169
|
-
this.bindNode(newLeaf, props, wantsNative);
|
|
170
|
-
}
|
|
171
|
-
// Bind the leaf to the host's Fabric node THROUGH whenCommitted: under Angular's zoneless
|
|
172
|
-
// batched change detection the inner host's tag does not exist yet at ngAfterViewInit time
|
|
173
|
-
// (the same async-commit gotcha Vue documents), so binding eagerly would no-op. whenCommitted
|
|
174
|
-
// runs the action now if already committed, else after the commit that assigns the tag.
|
|
175
|
-
bindNode(leaf, props, wantsNative) {
|
|
176
|
-
this.cancelBind?.();
|
|
177
|
-
this.cancelBind = undefined;
|
|
178
|
-
const node = this.hostNode();
|
|
179
|
-
if (node === null)
|
|
180
|
-
return;
|
|
181
|
-
this.cancelBind = whenCommitted(node, () => {
|
|
182
|
-
leaf.setNativeView(node);
|
|
183
|
-
if (wantsNative)
|
|
184
|
-
leaf.__makeNative();
|
|
185
|
-
this.attachEvents(node, props);
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
// Native-attach any Animated.event prop (e.g. onScroll={Animated.event(…,{useNativeDriver:true})})
|
|
189
|
-
// to the committed node. attachNativeEventHandler no-ops (returns undefined) unless the prop is a
|
|
190
|
-
// native event handler with a committed tag, so the JS path stays the fallback. Rebound each
|
|
191
|
-
// reconcile so a new inline event re-attaches; detached first to avoid leaking the prior binding.
|
|
192
|
-
attachEvents(node, props) {
|
|
193
|
-
this.detachEvents();
|
|
194
|
-
for (const key of Object.keys(props)) {
|
|
195
|
-
const attachment = attachNativeEventHandler(node, key, props[key]);
|
|
196
|
-
if (attachment !== undefined)
|
|
197
|
-
this.eventDetachers.push(attachment.detach);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
detachEvents() {
|
|
201
|
-
for (const detach of this.eventDetachers)
|
|
202
|
-
detach();
|
|
203
|
-
this.eventDetachers = [];
|
|
152
|
+
this.leafBinder.reconcile(props, wantsNative);
|
|
204
153
|
}
|
|
205
154
|
// Merge the dedicated `style` input over the generic `animatedProps` bag into one props map —
|
|
206
155
|
// the equivalent of React/Vue `rest`. `style` wins so `[style]` and `[animatedProps].style`
|
|
@@ -333,9 +282,10 @@ let AnimatedImage = (() => {
|
|
|
333
282
|
// comment on AnimatedComponentBase.elementRef.
|
|
334
283
|
elementRef = inject(ElementRef);
|
|
335
284
|
hostDirective = __runInitializers(this, _hostDirective_initializers, void 0);
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
285
|
+
// Same Pure Fabrication AnimatedComponentBase holds — see its matching comment. AnimatedImage
|
|
286
|
+
// can't extend AnimatedComponentBase (it must extend ImageBase, see the file header), so it
|
|
287
|
+
// gets the leaf-lifecycle via composition instead of duplicating it.
|
|
288
|
+
leafBinder = (__runInitializers(this, _hostDirective_extraInitializers), new AnimatedLeafBinder(() => this.hostNode(), this.constructor.name));
|
|
339
289
|
viewReady = false;
|
|
340
290
|
get animatedImageProps() {
|
|
341
291
|
const reduced = reduceProps(this.mergedProps());
|
|
@@ -356,13 +306,7 @@ let AnimatedImage = (() => {
|
|
|
356
306
|
this.reconcile();
|
|
357
307
|
}
|
|
358
308
|
ngOnDestroy() {
|
|
359
|
-
this.
|
|
360
|
-
this.cancelBind = undefined;
|
|
361
|
-
this.detachEvents();
|
|
362
|
-
if (this.attached !== null) {
|
|
363
|
-
this.attached.__detach();
|
|
364
|
-
this.attached = null;
|
|
365
|
-
}
|
|
309
|
+
this.leafBinder.destroy();
|
|
366
310
|
}
|
|
367
311
|
reconcile() {
|
|
368
312
|
if (!this.viewReady)
|
|
@@ -371,38 +315,7 @@ let AnimatedImage = (() => {
|
|
|
371
315
|
const hasPassthroughAnimatedValues = this.passthroughAnimatedPropExplicitValues !== null &&
|
|
372
316
|
this.passthroughAnimatedPropExplicitValues !== undefined;
|
|
373
317
|
const wantsNative = hasPassthroughAnimatedValues && isNativeAnimatedAvailable();
|
|
374
|
-
|
|
375
|
-
newLeaf.__attach();
|
|
376
|
-
if (this.attached !== null && this.attached !== newLeaf)
|
|
377
|
-
this.attached.__detach();
|
|
378
|
-
this.attached = newLeaf;
|
|
379
|
-
this.bindNode(newLeaf, props, wantsNative);
|
|
380
|
-
}
|
|
381
|
-
bindNode(leaf, props, wantsNative) {
|
|
382
|
-
this.cancelBind?.();
|
|
383
|
-
this.cancelBind = undefined;
|
|
384
|
-
const node = this.hostNode();
|
|
385
|
-
if (node === null)
|
|
386
|
-
return;
|
|
387
|
-
this.cancelBind = whenCommitted(node, () => {
|
|
388
|
-
leaf.setNativeView(node);
|
|
389
|
-
if (wantsNative)
|
|
390
|
-
leaf.__makeNative();
|
|
391
|
-
this.attachEvents(node, props);
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
attachEvents(node, props) {
|
|
395
|
-
this.detachEvents();
|
|
396
|
-
for (const key of Object.keys(props)) {
|
|
397
|
-
const attachment = attachNativeEventHandler(node, key, props[key]);
|
|
398
|
-
if (attachment !== undefined)
|
|
399
|
-
this.eventDetachers.push(attachment.detach);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
detachEvents() {
|
|
403
|
-
for (const detach of this.eventDetachers)
|
|
404
|
-
detach();
|
|
405
|
-
this.eventDetachers = [];
|
|
318
|
+
this.leafBinder.reconcile(props, wantsNative);
|
|
406
319
|
}
|
|
407
320
|
mergedProps() {
|
|
408
321
|
const props = { ...(this.animatedProps ?? {}) };
|
|
@@ -429,7 +342,7 @@ let AnimatedScrollView = (() => {
|
|
|
429
342
|
inputs: ANIMATED_INPUTS,
|
|
430
343
|
template: `
|
|
431
344
|
<symbiote-scroll-view [symbioteHostProps]="reducedProps">
|
|
432
|
-
<symbiote-scroll-content [
|
|
345
|
+
<symbiote-scroll-content [symbioteHostProps]="contentProps">
|
|
433
346
|
<ng-content></ng-content>
|
|
434
347
|
</symbiote-scroll-content>
|
|
435
348
|
</symbiote-scroll-view>
|
|
@@ -454,11 +367,33 @@ let AnimatedScrollView = (() => {
|
|
|
454
367
|
// receives touch (swallowed by the outer scroll view; iOS doesn't gate on this). The real
|
|
455
368
|
// ScrollView component already defaults this (scroll-view/shared.ts), but AnimatedScrollView
|
|
456
369
|
// talks to the raw primitive directly and has no such defaulting, so it must apply it too.
|
|
370
|
+
//
|
|
371
|
+
// scrollViewBaseStyle (overflow: 'scroll' + the per-axis flexDirection) is the OTHER thing
|
|
372
|
+
// the real ScrollView applies that this bespoke template used to skip entirely — without it,
|
|
373
|
+
// iOS Fabric never clips the scroll view's content to its own frame (Android's native
|
|
374
|
+
// ViewGroup clips regardless of the style prop, which is why this was invisible there). This
|
|
375
|
+
// wrapper has no dedicated `horizontal` input (only the generic `animatedProps`/`style`
|
|
376
|
+
// surface), and its template is hardcoded to the vertical intrinsics
|
|
377
|
+
// (symbiote-scroll-view/symbiote-scroll-content, not the horizontal variants), so
|
|
378
|
+
// selectScrollIntrinsics is always called with isHorizontal=false here — matching what the
|
|
379
|
+
// template can actually render.
|
|
457
380
|
get reducedProps() {
|
|
458
381
|
const reduced = super.reducedProps;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
382
|
+
const { scrollViewBaseStyle } = selectScrollIntrinsics(false, undefined);
|
|
383
|
+
const withScrollBase = {
|
|
384
|
+
...reduced,
|
|
385
|
+
style: [scrollViewBaseStyle, reduced.style],
|
|
386
|
+
};
|
|
387
|
+
return withScrollBase.nestedScrollEnabled === undefined
|
|
388
|
+
? { ...withScrollBase, nestedScrollEnabled: true }
|
|
389
|
+
: withScrollBase;
|
|
390
|
+
}
|
|
391
|
+
// The content (inner) view's props: contentStyle from the same intrinsics selection, plus
|
|
392
|
+
// `collapsable: false` (the Android multi-child fix this file's tests already cover) — mirrors
|
|
393
|
+
// the real ScrollView's own `contentProps` getter (scroll-view/shared.ts).
|
|
394
|
+
get contentProps() {
|
|
395
|
+
const { contentStyle } = selectScrollIntrinsics(false, undefined);
|
|
396
|
+
return { style: contentStyle, collapsable: false };
|
|
462
397
|
}
|
|
463
398
|
};
|
|
464
399
|
return AnimatedScrollView = _classThis;
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// adapters/vue/src/render.ts.
|
|
7
7
|
import { createElement as createEngineElement, createSurface, disposeRoot, dlog, toPublicInstance, } from '@symbiote-native/engine';
|
|
8
8
|
import { descriptorFor } from '@symbiote-native/components';
|
|
9
|
-
import { ApplicationRef, createComponent, createEnvironmentInjector, DOCUMENT, RendererFactory2, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵprovideZonelessChangeDetectionInternal as provideZonelessChangeDetectionInternal, } from '@angular/core';
|
|
10
|
-
import { SymbioteRendererFactory } from '
|
|
11
|
-
import { ColorSchemeService, WindowDimensionsService } from '
|
|
9
|
+
import { ApplicationRef, createComponent, createEnvironmentInjector, DOCUMENT, ErrorHandler, RendererFactory2, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵprovideZonelessChangeDetectionInternal as provideZonelessChangeDetectionInternal, } from '@angular/core';
|
|
10
|
+
import { SymbioteRendererFactory } from '../renderer';
|
|
11
|
+
import { ColorSchemeService, WindowDimensionsService } from '../services';
|
|
12
12
|
// The two FFI-edge casts of this adapter, both confined here to bootstrap. Angular's core
|
|
13
13
|
// types model a browser: the injector parent is a non-null EnvironmentInjector, the host is
|
|
14
14
|
// a DOM Element. Our root has neither shape — a null parent IS a root injector, and the host
|
|
@@ -98,6 +98,17 @@ export function mount(rootTag, rootComponent, options) {
|
|
|
98
98
|
// `@Component` boundary — SignalView-compiled children are skip-eligible regardless of
|
|
99
99
|
// this scheduler.
|
|
100
100
|
...provideZonelessChangeDetectionInternal(),
|
|
101
|
+
// Angular's own INTERNAL_APPLICATION_ERROR_HANDLER (core.mjs) reports a tick()
|
|
102
|
+
// exception by calling `injector.get(ErrorHandler)` — a normal `bootstrapApplication`
|
|
103
|
+
// registers this token by default (platform-browser's BROWSER_MODULE_PROVIDERS), but
|
|
104
|
+
// our from-scratch environment injector never did, so that lookup itself threw
|
|
105
|
+
// NG0201 and REPLACED whatever the real error was with an unrelated "No provider
|
|
106
|
+
// found for ErrorHandler" — the real exception never got logged, and the NG0201 itself
|
|
107
|
+
// propagated out of a bare Timeout callback uncaught (nothing above it in the stack to
|
|
108
|
+
// catch it), i.e. any async tick() exception, anywhere in the app, crashed hard instead
|
|
109
|
+
// of being reported. Providing the default `ErrorHandler` (same one bootstrapApplication
|
|
110
|
+
// ships) restores the intended behavior: `console.error('ERROR', e)` and keep running.
|
|
111
|
+
{ provide: ErrorHandler, useClass: ErrorHandler },
|
|
101
112
|
ColorSchemeService,
|
|
102
113
|
WindowDimensionsService,
|
|
103
114
|
], rootInjectorParent());
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// that the same engine mutation API drives both frameworks.
|
|
7
7
|
import { appendChild, createAnchor, createElement, createRawText, dlog, getExplicitStyle, insertBefore, isDebug, isSymbioteNode, removeChild, routeProp, setEventListener, setText, toPublicInstance, RAW_TEXT_COMPONENT, SymbioteSurface, } from '@symbiote-native/engine';
|
|
8
8
|
import { descriptorFor } from '@symbiote-native/components';
|
|
9
|
-
import { getScrollViewProjection, removeScrollViewProjectedChild, } from '
|
|
9
|
+
import { getScrollViewProjection, removeScrollViewProjectedChild, } from '../components/scroll-view/projection';
|
|
10
10
|
function isSurface(parent) {
|
|
11
11
|
return parent instanceof SymbioteSurface;
|
|
12
12
|
}
|
|
@@ -47,29 +47,27 @@ const PRIMITIVE_SELECTOR_ALIAS = {
|
|
|
47
47
|
// anchor host (see the set comment below). Tests register their own child-component selectors
|
|
48
48
|
// through this at module load; app-authored composed components can too.
|
|
49
49
|
export function registerComposedComponent(selector) {
|
|
50
|
-
ANCHOR_HOST_COMPONENTS.add(selector);
|
|
50
|
+
ANCHOR_HOST_COMPONENTS.add(selector.toLowerCase());
|
|
51
51
|
}
|
|
52
|
+
// Lowercased at construction — createElement's lookup and registerComposedComponent's insert
|
|
53
|
+
// both normalize to lowercase (Angular lowercases a dynamically-mounted component's selector at
|
|
54
|
+
// runtime, see §11a), so the literal entries below must match that or every capitalized one
|
|
55
|
+
// (all but the handful already written lowercase) silently never matches .has() and falls
|
|
56
|
+
// through to a real Fabric createNode — the exact "Unimplemented component" / extra-wrapper-node
|
|
57
|
+
// bug this Set exists to prevent.
|
|
52
58
|
const ANCHOR_HOST_COMPONENTS = new Set([
|
|
53
59
|
// Composed Angular components render their real Fabric descriptor tree from the template;
|
|
54
60
|
// their Angular host element is only a framework bookkeeping node and must not paint. This
|
|
55
61
|
// is NOT limited to adapter-authored components — ANY custom composed @Component used as a
|
|
56
|
-
// plain <Tag> inside another template needs its selector listed here too,
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
+
// plain <Tag> inside another template needs its selector listed here too, or Angular's
|
|
63
|
+
// automatic host-element creation for it falls through to a raw Fabric createNode call with
|
|
64
|
+
// an unrecognized view name, which paints RN's own "Unimplemented component: <Tag>" fallback
|
|
65
|
+
// view instead (a real device-visible bug, not a silent no-op). This Set holds only
|
|
66
|
+
// adapter/engine-owned selectors; app code and third-party packages self-register their own
|
|
67
|
+
// composed components through registerComposedComponent instead of being hardcoded here.
|
|
62
68
|
'ActivityIndicator',
|
|
63
|
-
'AnimatedDemo',
|
|
64
|
-
'AnimatedParityDemo',
|
|
65
69
|
'Button',
|
|
66
70
|
'FlatList',
|
|
67
|
-
'NativeModulesDemo',
|
|
68
|
-
'ParityDemo',
|
|
69
|
-
'PlatformColorDemo',
|
|
70
|
-
'RefApiDemo',
|
|
71
|
-
'ResponderDemo',
|
|
72
|
-
'Slider',
|
|
73
71
|
'AnimatedView',
|
|
74
72
|
'symbiote-animated-view',
|
|
75
73
|
'AnimatedText',
|
|
@@ -103,7 +101,7 @@ const ANCHOR_HOST_COMPONENTS = new Set([
|
|
|
103
101
|
'VirtualizedList',
|
|
104
102
|
'VirtualizedSectionList',
|
|
105
103
|
'symbiote-pressable',
|
|
106
|
-
]);
|
|
104
|
+
].map(selector => selector.toLowerCase()));
|
|
107
105
|
// Inserting a bare raw-text node anywhere but inside a <Text> is invalid in Fabric (a
|
|
108
106
|
// stray RCTRawText would paint). Angular's ɵɵtext only ever lands text inside a <Text>,
|
|
109
107
|
// but guard anyway for parity with the Vue adapter and to fail loudly on a bad template.
|
|
@@ -134,7 +132,7 @@ export class SymbioteRenderer {
|
|
|
134
132
|
// ViewConfig). toPublicInstance grafts the imperative API (measure / setNativeProps /
|
|
135
133
|
// focus) onto the raw node in place, returning the SAME identity the commit mirror keys on.
|
|
136
134
|
const engineName = PRIMITIVE_SELECTOR_ALIAS[name] ?? name;
|
|
137
|
-
if (ANCHOR_HOST_COMPONENTS.has(engineName)) {
|
|
135
|
+
if (ANCHOR_HOST_COMPONENTS.has(engineName.toLowerCase())) {
|
|
138
136
|
const anchor = tagAnchorForDebug(createAnchor());
|
|
139
137
|
dlog(`angular createElement ${name} -> anchor host ${describeHost(anchor)}`);
|
|
140
138
|
return anchor;
|
|
@@ -355,4 +353,3 @@ export class SymbioteRendererFactory {
|
|
|
355
353
|
return (this.renderer ??= new SymbioteRenderer(this.surface));
|
|
356
354
|
}
|
|
357
355
|
}
|
|
358
|
-
//# sourceMappingURL=renderer.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "SymbioteNative's Angular adapter — a Renderer2/RendererFactory2 with DOM-less bootstrap driving real native iOS/Android views through the same engine as the other adapters.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"./metro-css-parser": "./metro-css-parser.cjs",
|
|
31
31
|
"./metro-config": "./metro-config.cjs",
|
|
32
32
|
"./babel-linker": "./babel-linker.cjs",
|
|
33
|
+
"./babel-register-composed": "./babel-register-composed.cjs",
|
|
33
34
|
"./tsconfig.angular.base.json": "./tsconfig.angular.base.json"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"metro-css-parser.cjs",
|
|
40
41
|
"metro-config.cjs",
|
|
41
42
|
"babel-linker.cjs",
|
|
43
|
+
"babel-register-composed.cjs",
|
|
42
44
|
"tsconfig.angular.base.json"
|
|
43
45
|
],
|
|
44
46
|
"bin": {
|
|
@@ -50,20 +52,23 @@
|
|
|
50
52
|
"dependencies": {
|
|
51
53
|
"@angular/compiler-cli": "^22",
|
|
52
54
|
"@react-native/typescript-config": "0.86.0",
|
|
53
|
-
"@symbiote-native/components": "0.2.
|
|
54
|
-
"@symbiote-native/
|
|
55
|
-
"@symbiote-native/css-parser": "0.2.1"
|
|
55
|
+
"@symbiote-native/components": "0.2.5",
|
|
56
|
+
"@symbiote-native/css-parser": "0.2.2"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"@angular/core": ">=20",
|
|
60
|
+
"@symbiote-native/engine": ">=0.1.6",
|
|
59
61
|
"react-native": ">=0.86"
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"@angular/compiler": "^22",
|
|
63
65
|
"@angular/core": "^22",
|
|
66
|
+
"@babel/core": "^7.25.2",
|
|
67
|
+
"@types/babel__core": "^7.20.5",
|
|
64
68
|
"@types/node": "^26.0.0",
|
|
65
69
|
"typescript": "~6.0.0",
|
|
66
|
-
"@symbiote-native/test-utils": "0.1.5"
|
|
70
|
+
"@symbiote-native/test-utils": "0.1.5",
|
|
71
|
+
"@symbiote-native/engine": "0.1.6"
|
|
67
72
|
},
|
|
68
73
|
"scripts": {
|
|
69
74
|
"ng:build": "ngc -p tsconfig.angular.json",
|
package/src/angular-gaps.test.ts
CHANGED
|
@@ -62,7 +62,7 @@ describe('Angular adapter gap regressions', () => {
|
|
|
62
62
|
|
|
63
63
|
it('Button forwards its full accessibility and TV-focus surface through TouchableOpacity', () => {
|
|
64
64
|
const buttonSource = readSource('adapters/angular/src/components/button.ts');
|
|
65
|
-
const touchableSource = readSource('adapters/angular/src/components/touchable.ts');
|
|
65
|
+
const touchableSource = readSource('adapters/angular/src/components/touchable/index.ts');
|
|
66
66
|
const pressableSource = readSource('adapters/angular/src/components/pressable/index.ts');
|
|
67
67
|
|
|
68
68
|
for (const binding of [
|
|
@@ -9,7 +9,12 @@ import {
|
|
|
9
9
|
type IImageSourceProp,
|
|
10
10
|
type IResizeMode,
|
|
11
11
|
} from '@symbiote-native/components';
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
isSymbioteEvent,
|
|
14
|
+
type IStyleProp,
|
|
15
|
+
type ISymbioteEvent,
|
|
16
|
+
type IViewStyle,
|
|
17
|
+
} from '@symbiote-native/engine';
|
|
13
18
|
|
|
14
19
|
export { setImageSourceResolver } from '@symbiote-native/components';
|
|
15
20
|
export type {
|
|
@@ -153,10 +158,6 @@ function asStyle(value: unknown): IStyleProp<IViewStyle> | undefined {
|
|
|
153
158
|
return typeof value === 'object' && value !== null ? value : undefined;
|
|
154
159
|
}
|
|
155
160
|
|
|
156
|
-
function isSymbioteEvent(value: unknown): value is ISymbioteEvent {
|
|
157
|
-
return typeof value === 'object' && value !== null && Reflect.has(value, 'nativeEvent');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
161
|
export function resolveImageProps(input: Record<string, unknown>): Record<string, unknown> {
|
|
161
162
|
const passthrough: IImagePassthroughProps = {
|
|
162
163
|
resizeMethod: input['resizeMethod'] as IImageProps['resizeMethod'],
|
|
@@ -21,14 +21,17 @@ import {
|
|
|
21
21
|
type IAccessibilityStateValue,
|
|
22
22
|
type IAriaProps,
|
|
23
23
|
} from '@symbiote-native/components';
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
import {
|
|
25
|
+
isSymbioteEvent,
|
|
26
|
+
type IStyleProp,
|
|
27
|
+
type ISymbioteEvent,
|
|
28
|
+
type IViewStyle,
|
|
29
|
+
} from '@symbiote-native/engine';
|
|
30
|
+
import {
|
|
31
|
+
anchorHostStyle,
|
|
32
|
+
InputAccessoryViewHost,
|
|
33
|
+
SymbioteHostPropsDirective,
|
|
34
|
+
} from '../../primitives';
|
|
32
35
|
|
|
33
36
|
// Mirrors React's IInputAccessoryViewProps minus children (Angular takes children via
|
|
34
37
|
// <ng-content>), declared per-adapter over the shared a11y base.
|
|
@@ -4,8 +4,8 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
4
4
|
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
5
5
|
import { installFabric } from '@symbiote-native/test-utils';
|
|
6
6
|
|
|
7
|
-
import { mount, unmount } from '
|
|
8
|
-
import { InputAccessoryView } from './
|
|
7
|
+
import { mount, unmount } from '../../render';
|
|
8
|
+
import { InputAccessoryView } from './index';
|
|
9
9
|
|
|
10
10
|
const ROOT_TAG = 913;
|
|
11
11
|
const fabric = installFabric();
|
|
@@ -42,21 +42,16 @@ import {
|
|
|
42
42
|
Keyboard,
|
|
43
43
|
KEYBOARD_EVENT,
|
|
44
44
|
dlog,
|
|
45
|
+
isSymbioteEvent,
|
|
45
46
|
type IEventSubscription,
|
|
46
47
|
type IStyleProp,
|
|
47
48
|
type ISymbioteEvent,
|
|
48
49
|
type IViewStyle,
|
|
49
50
|
} from '@symbiote-native/engine';
|
|
50
|
-
import { anchorHostStyle, SymbioteHostPropsDirective, ViewHost } from '
|
|
51
|
+
import { anchorHostStyle, SymbioteHostPropsDirective, ViewHost } from '../../primitives';
|
|
51
52
|
|
|
52
53
|
export type { IKeyboardAvoidingBehavior } from '@symbiote-native/components';
|
|
53
54
|
|
|
54
|
-
// Angular infers an `(event)` binding's $event as the DOM Event for a custom-schema element,
|
|
55
|
-
// so each handler enters as `unknown` and is narrowed here before reaching a typed callback.
|
|
56
|
-
function isSymbioteEvent(value: unknown): value is ISymbioteEvent {
|
|
57
|
-
return typeof value === 'object' && value !== null && 'nativeEvent' in value;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
55
|
// Mirrors React's IKeyboardAvoidingViewProps minus children (Angular takes children via
|
|
61
56
|
// <ng-content>), declared per-adapter over the shared accessibility base since a framework-specific
|
|
62
57
|
// children field keeps it from being fully shared across adapters.
|
|
@@ -4,8 +4,8 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
4
4
|
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
5
5
|
import { installFabric } from '@symbiote-native/test-utils';
|
|
6
6
|
|
|
7
|
-
import { mount, unmount } from '
|
|
8
|
-
import { KeyboardAvoidingView } from './
|
|
7
|
+
import { mount, unmount } from '../../render';
|
|
8
|
+
import { KeyboardAvoidingView } from './index';
|
|
9
9
|
|
|
10
10
|
const ROOT_TAG = 911;
|
|
11
11
|
const fabric = installFabric();
|
|
@@ -39,11 +39,12 @@ import {
|
|
|
39
39
|
} from '@symbiote-native/components';
|
|
40
40
|
import {
|
|
41
41
|
dlog,
|
|
42
|
+
isSymbioteEvent,
|
|
42
43
|
type IStyleProp,
|
|
43
44
|
type ISymbioteEvent,
|
|
44
45
|
type IViewStyle,
|
|
45
46
|
} from '@symbiote-native/engine';
|
|
46
|
-
import { anchorHostStyle, ModalHost, SymbioteHostPropsDirective, ViewHost } from '
|
|
47
|
+
import { anchorHostStyle, ModalHost, SymbioteHostPropsDirective, ViewHost } from '../../primitives';
|
|
47
48
|
|
|
48
49
|
export type {
|
|
49
50
|
IModalAnimationType,
|
|
@@ -52,12 +53,6 @@ export type {
|
|
|
52
53
|
IModalOrientationChangeEvent,
|
|
53
54
|
} from '@symbiote-native/components';
|
|
54
55
|
|
|
55
|
-
// Angular infers an `(event)` binding's $event as the DOM Event for a custom-schema element,
|
|
56
|
-
// so each handler enters as `unknown` and is narrowed here before reaching a typed callback.
|
|
57
|
-
function isSymbioteEvent(value: unknown): value is ISymbioteEvent {
|
|
58
|
-
return typeof value === 'object' && value !== null && 'nativeEvent' in value;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
56
|
const ORIENTATIONS: ReadonlyArray<IModalOrientation> = ['portrait', 'landscape'];
|
|
62
57
|
|
|
63
58
|
function readOrientation(event: ISymbioteEvent): IModalOrientationChangeEvent | undefined {
|
|
@@ -4,8 +4,8 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
4
4
|
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
5
5
|
import { installFabric } from '@symbiote-native/test-utils';
|
|
6
6
|
|
|
7
|
-
import { mount, unmount } from '
|
|
8
|
-
import { Modal } from './
|
|
7
|
+
import { mount, unmount } from '../../render';
|
|
8
|
+
import { Modal } from './index';
|
|
9
9
|
|
|
10
10
|
const ROOT_TAG = 912;
|
|
11
11
|
const fabric = installFabric();
|