@symbiote-native/angular 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/build/angular/components/activity-indicator/index.android.d.ts +12 -0
- package/build/angular/components/activity-indicator/index.android.js +21 -0
- package/build/angular/components/activity-indicator/index.android.js.map +1 -0
- package/build/angular/components/activity-indicator/index.d.ts +1 -0
- package/build/angular/components/activity-indicator/index.ios.d.ts +8 -0
- package/build/angular/components/activity-indicator/index.ios.js +21 -0
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -0
- package/build/angular/components/activity-indicator/index.js +4 -0
- package/build/angular/components/activity-indicator/index.js.map +1 -0
- package/build/angular/components/activity-indicator/shared.d.ts +67 -0
- package/build/angular/components/activity-indicator/shared.js +260 -0
- package/build/angular/components/activity-indicator/shared.js.map +1 -0
- package/build/angular/components/button.d.ts +63 -0
- package/build/angular/components/button.js +332 -0
- package/build/angular/components/button.js.map +1 -0
- package/build/angular/components/drawer-layout-android/index.android.d.ts +34 -0
- package/build/angular/components/drawer-layout-android/index.android.js +291 -0
- package/build/angular/components/drawer-layout-android/index.android.js.map +1 -0
- package/build/angular/components/drawer-layout-android/index.d.ts +11 -0
- package/build/angular/components/drawer-layout-android/index.js +109 -0
- package/build/angular/components/drawer-layout-android/index.js.map +1 -0
- package/build/angular/components/drawer-layout-android/shared.d.ts +74 -0
- package/build/angular/components/drawer-layout-android/shared.js +138 -0
- package/build/angular/components/drawer-layout-android/shared.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +122 -0
- package/build/angular/components/flat-list/index.js +689 -0
- package/build/angular/components/flat-list/index.js.map +1 -0
- package/build/angular/components/image/index.android.d.ts +10 -0
- package/build/angular/components/image/index.android.js +59 -0
- package/build/angular/components/image/index.android.js.map +1 -0
- package/build/angular/components/image/index.d.ts +1 -0
- package/build/angular/components/image/index.ios.d.ts +10 -0
- package/build/angular/components/image/index.ios.js +59 -0
- package/build/angular/components/image/index.ios.js.map +1 -0
- package/build/angular/components/image/index.js +3 -0
- package/build/angular/components/image/index.js.map +1 -0
- package/build/angular/components/image/shared.d.ts +101 -0
- package/build/angular/components/image/shared.js +408 -0
- package/build/angular/components/image/shared.js.map +1 -0
- package/build/angular/components/image-background.d.ts +81 -0
- package/build/angular/components/image-background.js +418 -0
- package/build/angular/components/image-background.js.map +1 -0
- package/build/angular/components/input-accessory-view.d.ts +59 -0
- package/build/angular/components/input-accessory-view.js +266 -0
- package/build/angular/components/input-accessory-view.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view.d.ts +82 -0
- package/build/angular/components/keyboard-avoiding-view.js +363 -0
- package/build/angular/components/keyboard-avoiding-view.js.map +1 -0
- package/build/angular/components/modal.d.ts +92 -0
- package/build/angular/components/modal.js +365 -0
- package/build/angular/components/modal.js.map +1 -0
- package/build/angular/components/pressable/index.d.ts +123 -0
- package/build/angular/components/pressable/index.js +421 -0
- package/build/angular/components/pressable/index.js.map +1 -0
- package/build/angular/components/refresh-control.d.ts +86 -0
- package/build/angular/components/refresh-control.js +347 -0
- package/build/angular/components/refresh-control.js.map +1 -0
- package/build/angular/components/safe-area-view.d.ts +57 -0
- package/build/angular/components/safe-area-view.js +253 -0
- package/build/angular/components/safe-area-view.js.map +1 -0
- package/build/angular/components/scroll-view/index.android.d.ts +21 -0
- package/build/angular/components/scroll-view/index.android.js +235 -0
- package/build/angular/components/scroll-view/index.android.js.map +1 -0
- package/build/angular/components/scroll-view/index.d.ts +3 -0
- package/build/angular/components/scroll-view/index.ios.d.ts +9 -0
- package/build/angular/components/scroll-view/index.ios.js +86 -0
- package/build/angular/components/scroll-view/index.ios.js.map +1 -0
- package/build/angular/components/scroll-view/index.js +6 -0
- package/build/angular/components/scroll-view/index.js.map +1 -0
- package/build/angular/components/scroll-view/projection.d.ts +32 -0
- package/build/angular/components/scroll-view/projection.js +312 -0
- package/build/angular/components/scroll-view/projection.js.map +1 -0
- package/build/angular/components/scroll-view/shared.d.ts +230 -0
- package/build/angular/components/scroll-view/shared.js +679 -0
- package/build/angular/components/scroll-view/shared.js.map +1 -0
- package/build/angular/components/scroll-view/sticky-header.d.ts +41 -0
- package/build/angular/components/scroll-view/sticky-header.js +208 -0
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -0
- package/build/angular/components/section-list/index.d.ts +116 -0
- package/build/angular/components/section-list/index.js +641 -0
- package/build/angular/components/section-list/index.js.map +1 -0
- package/build/angular/components/switch/index.android.d.ts +20 -0
- package/build/angular/components/switch/index.android.js +70 -0
- package/build/angular/components/switch/index.android.js.map +1 -0
- package/build/angular/components/switch/index.d.ts +1 -0
- package/build/angular/components/switch/index.ios.d.ts +19 -0
- package/build/angular/components/switch/index.ios.js +69 -0
- package/build/angular/components/switch/index.ios.js.map +1 -0
- package/build/angular/components/switch/index.js +4 -0
- package/build/angular/components/switch/index.js.map +1 -0
- package/build/angular/components/switch/shared.d.ts +40 -0
- package/build/angular/components/switch/shared.js +127 -0
- package/build/angular/components/switch/shared.js.map +1 -0
- package/build/angular/components/text-input.d.ts +153 -0
- package/build/angular/components/text-input.js +604 -0
- package/build/angular/components/text-input.js.map +1 -0
- package/build/angular/components/touchable-native-feedback.d.ts +83 -0
- package/build/angular/components/touchable-native-feedback.js +376 -0
- package/build/angular/components/touchable-native-feedback.js.map +1 -0
- package/build/angular/components/touchable.d.ts +248 -0
- package/build/angular/components/touchable.js +1206 -0
- package/build/angular/components/touchable.js.map +1 -0
- package/build/angular/components/virtualized-list/directives.d.ts +54 -0
- package/build/angular/components/virtualized-list/directives.js +154 -0
- package/build/angular/components/virtualized-list/directives.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +268 -0
- package/build/angular/components/virtualized-list/index.js +1162 -0
- package/build/angular/components/virtualized-list/index.js.map +1 -0
- package/build/angular/components/virtualized-section-list/directives.d.ts +37 -0
- package/build/angular/components/virtualized-section-list/directives.js +77 -0
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -0
- package/build/angular/components/virtualized-section-list/index.d.ts +178 -0
- package/build/angular/components/virtualized-section-list/index.js +722 -0
- package/build/angular/components/virtualized-section-list/index.js.map +1 -0
- package/build/angular/components.d.ts +39 -0
- package/build/angular/components.js +38 -0
- package/build/angular/components.js.map +1 -0
- package/build/angular/create-portal.d.ts +22 -0
- package/build/angular/create-portal.js +74 -0
- package/build/angular/create-portal.js.map +1 -0
- package/build/angular/create-tunnel.d.ts +34 -0
- package/build/angular/create-tunnel.js +126 -0
- package/build/angular/create-tunnel.js.map +1 -0
- package/build/angular/descriptor-to-angular/index.d.ts +22 -0
- package/build/angular/descriptor-to-angular/index.js +142 -0
- package/build/angular/descriptor-to-angular/index.js.map +1 -0
- package/build/angular/host-instance/index.d.ts +2 -0
- package/build/angular/host-instance/index.js +37 -0
- package/build/angular/host-instance/index.js.map +1 -0
- package/build/angular/index.d.ts +21 -0
- package/build/angular/index.js +42 -0
- package/build/angular/index.js.map +1 -0
- package/build/angular/modules/animated/create-animated-component.d.ts +68 -0
- package/build/angular/modules/animated/create-animated-component.js +429 -0
- package/build/angular/modules/animated/create-animated-component.js.map +1 -0
- package/build/angular/modules/animated/index.d.ts +51 -0
- package/build/angular/modules/animated/index.js +59 -0
- package/build/angular/modules/animated/index.js.map +1 -0
- package/build/angular/modules/app-registry/index.d.ts +7 -0
- package/build/angular/modules/app-registry/index.js +23 -0
- package/build/angular/modules/app-registry/index.js.map +1 -0
- package/build/angular/modules/status-bar.d.ts +20 -0
- package/build/angular/modules/status-bar.js +59 -0
- package/build/angular/modules/status-bar.js.map +1 -0
- package/build/angular/primitives/index.d.ts +63 -0
- package/build/angular/primitives/index.js +189 -0
- package/build/angular/primitives/index.js.map +1 -0
- package/build/angular/primitives/shared.d.ts +89 -0
- package/build/angular/primitives/shared.js +172 -0
- package/build/angular/primitives/shared.js.map +1 -0
- package/build/angular/render.d.ts +11 -0
- package/build/angular/render.js +161 -0
- package/build/angular/render.js.map +1 -0
- package/build/angular/renderer.d.ts +38 -0
- package/build/angular/renderer.js +360 -0
- package/build/angular/renderer.js.map +1 -0
- package/build/angular/services/color-scheme.service.d.ts +16 -0
- package/build/angular/services/color-scheme.service.js +31 -0
- package/build/angular/services/color-scheme.service.js.map +1 -0
- package/build/angular/services/index.d.ts +2 -0
- package/build/angular/services/index.js +3 -0
- package/build/angular/services/index.js.map +1 -0
- package/build/angular/services/window-dimensions.service.d.ts +16 -0
- package/build/angular/services/window-dimensions.service.js +42 -0
- package/build/angular/services/window-dimensions.service.js.map +1 -0
- package/build/components/activity-indicator/index.android.d.ts +9 -0
- package/build/components/activity-indicator/index.android.js +64 -0
- package/build/components/activity-indicator/index.d.ts +1 -0
- package/build/components/activity-indicator/index.ios.d.ts +5 -0
- package/build/components/activity-indicator/index.ios.js +64 -0
- package/build/components/activity-indicator/index.js +3 -0
- package/build/components/activity-indicator/shared.d.ts +64 -0
- package/build/components/activity-indicator/shared.js +424 -0
- package/build/components/button.d.ts +60 -0
- package/build/components/button.js +456 -0
- package/build/components/drawer-layout-android/index.android.d.ts +29 -0
- package/build/components/drawer-layout-android/index.android.js +326 -0
- package/build/components/drawer-layout-android/index.d.ts +8 -0
- package/build/components/drawer-layout-android/index.js +145 -0
- package/build/components/drawer-layout-android/shared.d.ts +71 -0
- package/build/components/drawer-layout-android/shared.js +205 -0
- package/build/components/flat-list/index.d.ts +119 -0
- package/build/components/flat-list/index.js +692 -0
- package/build/components/image/index.android.d.ts +7 -0
- package/build/components/image/index.android.js +88 -0
- package/build/components/image/index.d.ts +1 -0
- package/build/components/image/index.ios.d.ts +7 -0
- package/build/components/image/index.ios.js +88 -0
- package/build/components/image/index.js +2 -0
- package/build/components/image/shared.d.ts +101 -0
- package/build/components/image/shared.js +407 -0
- package/build/components/image-background.d.ts +78 -0
- package/build/components/image-background.js +583 -0
- package/build/components/input-accessory-view.d.ts +56 -0
- package/build/components/input-accessory-view.js +407 -0
- package/build/components/keyboard-avoiding-view.d.ts +79 -0
- package/build/components/keyboard-avoiding-view.js +509 -0
- package/build/components/modal.d.ts +89 -0
- package/build/components/modal.js +534 -0
- package/build/components/pressable/index.d.ts +120 -0
- package/build/components/pressable/index.js +640 -0
- package/build/components/refresh-control.d.ts +83 -0
- package/build/components/refresh-control.js +514 -0
- package/build/components/safe-area-view.d.ts +54 -0
- package/build/components/safe-area-view.js +393 -0
- package/build/components/scroll-view/index.android.d.ts +16 -0
- package/build/components/scroll-view/index.android.js +228 -0
- package/build/components/scroll-view/index.d.ts +3 -0
- package/build/components/scroll-view/index.ios.d.ts +6 -0
- package/build/components/scroll-view/index.ios.js +113 -0
- package/build/components/scroll-view/index.js +5 -0
- package/build/components/scroll-view/projection.d.ts +32 -0
- package/build/components/scroll-view/projection.js +311 -0
- package/build/components/scroll-view/shared.d.ts +225 -0
- package/build/components/scroll-view/shared.js +757 -0
- package/build/components/scroll-view/sticky-header.d.ts +38 -0
- package/build/components/scroll-view/sticky-header.js +257 -0
- package/build/components/section-list/index.d.ts +113 -0
- package/build/components/section-list/index.js +768 -0
- package/build/components/switch/index.android.d.ts +17 -0
- package/build/components/switch/index.android.js +107 -0
- package/build/components/switch/index.d.ts +1 -0
- package/build/components/switch/index.ios.d.ts +16 -0
- package/build/components/switch/index.ios.js +106 -0
- package/build/components/switch/index.js +3 -0
- package/build/components/switch/shared.d.ts +37 -0
- package/build/components/switch/shared.js +176 -0
- package/build/components/text-input.d.ts +150 -0
- package/build/components/text-input.js +826 -0
- package/build/components/touchable-native-feedback.d.ts +80 -0
- package/build/components/touchable-native-feedback.js +550 -0
- package/build/components/touchable.d.ts +241 -0
- package/build/components/touchable.js +1672 -0
- package/build/components/virtualized-list/directives.d.ts +41 -0
- package/build/components/virtualized-list/directives.js +250 -0
- package/build/components/virtualized-list/index.d.ts +265 -0
- package/build/components/virtualized-list/index.js +1344 -0
- package/build/components/virtualized-section-list/directives.d.ts +28 -0
- package/build/components/virtualized-section-list/directives.js +149 -0
- package/build/components/virtualized-section-list/index.d.ts +175 -0
- package/build/components/virtualized-section-list/index.js +854 -0
- package/build/components.d.ts +39 -0
- package/build/components.js +37 -0
- package/build/create-portal.d.ts +17 -0
- package/build/create-portal.js +128 -0
- package/build/create-tunnel.d.ts +29 -0
- package/build/create-tunnel.js +181 -0
- package/build/descriptor-to-angular/index.d.ts +19 -0
- package/build/descriptor-to-angular/index.js +186 -0
- package/build/host-instance/index.d.ts +2 -0
- package/build/host-instance/index.js +36 -0
- package/build/index.d.ts +21 -0
- package/build/index.js +41 -0
- package/build/modules/animated/create-animated-component.d.ts +57 -0
- package/build/modules/animated/create-animated-component.js +490 -0
- package/build/modules/animated/index.d.ts +51 -0
- package/build/modules/animated/index.js +58 -0
- package/build/modules/app-registry/index.d.ts +7 -0
- package/build/modules/app-registry/index.js +22 -0
- package/build/modules/status-bar.d.ts +17 -0
- package/build/modules/status-bar.js +121 -0
- package/build/primitives/index.d.ts +32 -0
- package/build/primitives/index.js +386 -0
- package/build/primitives/shared.d.ts +84 -0
- package/build/primitives/shared.js +231 -0
- package/build/render.d.ts +11 -0
- package/build/render.js +160 -0
- package/build/renderer.d.ts +38 -0
- package/build/renderer.js +359 -0
- package/build/services/color-scheme.service.d.ts +13 -0
- package/build/services/color-scheme.service.js +72 -0
- package/build/services/index.d.ts +2 -0
- package/build/services/index.js +2 -0
- package/build/services/window-dimensions.service.d.ts +13 -0
- package/build/services/window-dimensions.service.js +83 -0
- package/metro-css-parser.cjs +11 -0
- package/package.json +57 -0
- package/src/__tests__/responder-change-detection.test.ts +102 -0
- package/src/__tests__/responder-nested-cd.test.ts +113 -0
- package/src/angular-gaps.test.ts +142 -0
- package/src/angular-imports.test.ts +100 -0
- package/src/components/activity-indicator/activity-indicator-class-style.test.ts +44 -0
- package/src/components/activity-indicator/activity-indicator.test.ts +131 -0
- package/src/components/activity-indicator/index.android.ts +16 -0
- package/src/components/activity-indicator/index.ios.ts +17 -0
- package/src/components/activity-indicator/index.ts +4 -0
- package/src/components/activity-indicator/shared.ts +206 -0
- package/src/components/button.ts +193 -0
- package/src/components/flat-list/column-wrapper-style-class.test.ts +116 -0
- package/src/components/flat-list/flat-list-array-style.test.ts +131 -0
- package/src/components/flat-list/flat-list-class-style.test.ts +74 -0
- package/src/components/flat-list/flat-list-scroll-containment.test.ts +107 -0
- package/src/components/flat-list/flat-list.test.ts +99 -0
- package/src/components/flat-list/index.ts +575 -0
- package/src/components/image/image-class-style.test.ts +46 -0
- package/src/components/image/index.android.ts +48 -0
- package/src/components/image/index.ios.ts +48 -0
- package/src/components/image/index.ts +3 -0
- package/src/components/image/shared.ts +478 -0
- package/src/components/image-background-class-style.test.ts +48 -0
- package/src/components/image-background-style-class.test.ts +79 -0
- package/src/components/image-background.ts +270 -0
- package/src/components/input-accessory-view.test.ts +43 -0
- package/src/components/input-accessory-view.ts +191 -0
- package/src/components/keyboard-avoiding-view.test.ts +54 -0
- package/src/components/keyboard-avoiding-view.ts +308 -0
- package/src/components/modal.test.ts +43 -0
- package/src/components/modal.ts +304 -0
- package/src/components/pressable/index.ts +371 -0
- package/src/components/pressable/pressable.test.ts +85 -0
- package/src/components/refresh-control.test.ts +41 -0
- package/src/components/refresh-control.ts +288 -0
- package/src/components/safe-area-view.test.ts +43 -0
- package/src/components/safe-area-view.ts +178 -0
- package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +123 -0
- package/src/components/scroll-view/content-container-style-class.test.ts +75 -0
- package/src/components/scroll-view/index.android.ts +190 -0
- package/src/components/scroll-view/index.ios.ts +79 -0
- package/src/components/scroll-view/index.ts +7 -0
- package/src/components/scroll-view/projection.ts +384 -0
- package/src/components/scroll-view/scroll-view-class-style.test.ts +134 -0
- package/src/components/scroll-view/scroll-view-projection.test.ts +194 -0
- package/src/components/scroll-view/shared.ts +836 -0
- package/src/components/scroll-view/sticky-header.ts +230 -0
- package/src/components/section-list/index.ts +445 -0
- package/src/components/switch/android-switch-class.test.ts +42 -0
- package/src/components/switch/index.android.ts +61 -0
- package/src/components/switch/index.ios.ts +60 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/shared.ts +197 -0
- package/src/components/switch/switch.test.ts +92 -0
- package/src/components/text-input-class-style.test.ts +54 -0
- package/src/components/text-input.ts +536 -0
- package/src/components/touchable-native-feedback.test.ts +49 -0
- package/src/components/touchable-native-feedback.ts +260 -0
- package/src/components/touchable.test.ts +102 -0
- package/src/components/touchable.ts +643 -0
- package/src/components/virtualized-list/directives.ts +169 -0
- package/src/components/virtualized-list/index.ts +1204 -0
- package/src/components/virtualized-list/virtualized-list-class-style.test.ts +76 -0
- package/src/components/virtualized-section-list/directives.ts +91 -0
- package/src/components/virtualized-section-list/index.ts +598 -0
- package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +60 -0
- package/src/components.ts +149 -0
- package/src/create-portal.test.ts +90 -0
- package/src/create-portal.ts +74 -0
- package/src/create-tunnel.test.ts +120 -0
- package/src/create-tunnel.ts +136 -0
- package/src/descriptor-to-angular/descriptor-outlet.test.ts +94 -0
- package/src/descriptor-to-angular/index.ts +190 -0
- package/src/host-instance/host-instance.test.ts +49 -0
- package/src/host-instance/index.ts +36 -0
- package/src/index.ts +257 -0
- package/src/modules/animated/animated-scroll-view.test.ts +85 -0
- package/src/modules/animated/create-animated-component.ts +435 -0
- package/src/modules/animated/index.ts +105 -0
- package/src/modules/app-registry/app-registry.test.ts +106 -0
- package/src/modules/app-registry/index.ts +60 -0
- package/src/modules/status-bar.test.ts +50 -0
- package/src/modules/status-bar.ts +57 -0
- package/src/primitives/index.ts +119 -0
- package/src/primitives/shared.ts +191 -0
- package/src/render.test.ts +147 -0
- package/src/render.ts +224 -0
- package/src/renderer.test.ts +179 -0
- package/src/renderer.ts +407 -0
- package/src/services/color-scheme.service.ts +27 -0
- package/src/services/index.ts +2 -0
- package/src/services/services.test.ts +163 -0
- package/src/services/window-dimensions.service.ts +41 -0
package/src/render.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Mount an Angular app onto a Fabric surface. The native host hands us a rootTag; we
|
|
2
|
+
// create a surface and bootstrap a standalone Angular component WITHOUT platform-browser
|
|
3
|
+
// (no DOM) — createEnvironmentInjector + createComponent over @angular/core only, with our
|
|
4
|
+
// SymbioteRendererFactory provided so Angular drives the engine, which commits into
|
|
5
|
+
// nativeFabricUIManager; RN's own renderer never in the path. Angular twin of
|
|
6
|
+
// adapters/vue/src/render.ts. See the `angular-adapter` skill (§2 bootstrap).
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
createElement as createEngineElement,
|
|
10
|
+
createSurface,
|
|
11
|
+
disposeRoot,
|
|
12
|
+
dlog,
|
|
13
|
+
toPublicInstance,
|
|
14
|
+
type ISymbioteNode,
|
|
15
|
+
type IRootTag,
|
|
16
|
+
type SymbioteSurface,
|
|
17
|
+
} from '@symbiote-native/engine';
|
|
18
|
+
import { descriptorFor } from '@symbiote-native/components';
|
|
19
|
+
import {
|
|
20
|
+
ApplicationRef,
|
|
21
|
+
createComponent,
|
|
22
|
+
createEnvironmentInjector,
|
|
23
|
+
DOCUMENT,
|
|
24
|
+
RendererFactory2,
|
|
25
|
+
ɵINJECTOR_SCOPE as INJECTOR_SCOPE,
|
|
26
|
+
ɵprovideZonelessChangeDetectionInternal as provideZonelessChangeDetectionInternal,
|
|
27
|
+
type ComponentRef,
|
|
28
|
+
type EnvironmentInjector,
|
|
29
|
+
type Type,
|
|
30
|
+
} from '@angular/core';
|
|
31
|
+
import { SymbioteRendererFactory } from './renderer';
|
|
32
|
+
import { ColorSchemeService, WindowDimensionsService } from './services';
|
|
33
|
+
|
|
34
|
+
// The two FFI-edge casts of this adapter, both confined here to bootstrap. Angular's core
|
|
35
|
+
// types model a browser: the injector parent is a non-null EnvironmentInjector, the host is
|
|
36
|
+
// a DOM Element. Our root has neither shape — a null parent IS a root injector, and the host
|
|
37
|
+
// is a SymbioteSurface (or, for a wrapped AppRegistry root, a plain engine node) the
|
|
38
|
+
// SymbioteRenderer (not the DOM) consumes. These are the sanctioned I/O-edge casts where our
|
|
39
|
+
// objects cross into Angular's web-typed API; revisit if Angular ever exposes a cast-free
|
|
40
|
+
// host. Nothing else in the adapter casts.
|
|
41
|
+
function rootInjectorParent(): EnvironmentInjector {
|
|
42
|
+
return null as unknown as EnvironmentInjector;
|
|
43
|
+
}
|
|
44
|
+
function asAngularHost(hostNode: SymbioteSurface | ISymbioteNode): Element {
|
|
45
|
+
// Angular's locateHostElement reads hostElement.tagName even when a concrete hostElement
|
|
46
|
+
// is supplied. The host container is given Angular the tiny DOM-shaped field it probes
|
|
47
|
+
// without changing the renderer target.
|
|
48
|
+
Object.defineProperty(hostNode, 'tagName', {
|
|
49
|
+
configurable: true,
|
|
50
|
+
value: 'symbiote-root',
|
|
51
|
+
});
|
|
52
|
+
return hostNode as unknown as Element;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// A bare `symbiote-view` node, created directly through the engine (bypassing Angular's own
|
|
56
|
+
// createElement, which only resolves KNOWN symbiote primitives, never an arbitrary component's
|
|
57
|
+
// selector — see SymbioteRenderer.createElement). Used as the AppRegistry root's host when a
|
|
58
|
+
// wrapperComponentProvider is set: Angular has no hostElement-less bootstrap for our renderer
|
|
59
|
+
// (it would try `renderer.createElement(rootComponent.selector)`, which throws for anything
|
|
60
|
+
// that isn't one of our primitives), so the root needs an explicit host too, one we then hand
|
|
61
|
+
// to the wrapper as projectable content.
|
|
62
|
+
function createDetachedViewHost(): ISymbioteNode {
|
|
63
|
+
const descriptor = descriptorFor('View');
|
|
64
|
+
return toPublicInstance(createEngineElement(descriptor.component, descriptor.isText));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface IMountedApp {
|
|
68
|
+
cmpRef: ComponentRef<unknown>;
|
|
69
|
+
rootRef: ComponentRef<unknown> | undefined;
|
|
70
|
+
injector: EnvironmentInjector;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// AppRegistry's `wrapperComponentProvider` support: the root renders detached (Angular gives
|
|
74
|
+
// it its own host node via our renderer, not yet attached anywhere), and the wrapper — the
|
|
75
|
+
// component actually attached to the surface — receives that host node as a projected child.
|
|
76
|
+
// `<ng-content>` in the wrapper's template is the Angular idiom for "render my children", the
|
|
77
|
+
// direct twin of React's `createElement(Wrapper, null, rootElement)`.
|
|
78
|
+
export interface IMountOptions {
|
|
79
|
+
initialProps?: object;
|
|
80
|
+
wrapperComponent?: Type<unknown>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function applyInputs(cmpRef: ComponentRef<unknown>, initialProps: object | undefined): void {
|
|
84
|
+
if (initialProps === undefined) return;
|
|
85
|
+
for (const [key, value] of Object.entries(initialProps)) {
|
|
86
|
+
cmpRef.setInput(key, value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// One Angular app per surface, so a surface can be torn down (unmount) or cleanly
|
|
91
|
+
// re-mounted on the same rootTag: the bridgeless host stops and restarts a surface on Fast
|
|
92
|
+
// Refresh and on lifecycle/focus changes, reusing the rootTag.
|
|
93
|
+
const apps = new Map<IRootTag, IMountedApp>();
|
|
94
|
+
|
|
95
|
+
function teardown(rootTag: IRootTag): void {
|
|
96
|
+
const app = apps.get(rootTag);
|
|
97
|
+
if (app === undefined) return;
|
|
98
|
+
app.rootRef?.destroy();
|
|
99
|
+
app.cmpRef.destroy();
|
|
100
|
+
app.injector.destroy();
|
|
101
|
+
apps.delete(rootTag);
|
|
102
|
+
disposeRoot(rootTag);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function mount(
|
|
106
|
+
rootTag: IRootTag,
|
|
107
|
+
rootComponent: Type<unknown>,
|
|
108
|
+
options?: IMountOptions,
|
|
109
|
+
): SymbioteSurface {
|
|
110
|
+
// A re-mount on a live rootTag starts clean; otherwise the stale app double-drives the surface.
|
|
111
|
+
teardown(rootTag);
|
|
112
|
+
|
|
113
|
+
const surface = createSurface(rootTag);
|
|
114
|
+
const injector = createEnvironmentInjector(
|
|
115
|
+
[
|
|
116
|
+
{
|
|
117
|
+
provide: RendererFactory2,
|
|
118
|
+
useValue: new SymbioteRendererFactory(surface),
|
|
119
|
+
},
|
|
120
|
+
{ provide: DOCUMENT, useValue: { head: surface, body: surface } },
|
|
121
|
+
// createEnvironmentInjector with a null parent gives this injector scope
|
|
122
|
+
// {'environment'} only (see EnvironmentNgModuleRefAdapter), so providedIn:'root'
|
|
123
|
+
// tokens — ApplicationRef included — never resolve on their own (R3Injector.get
|
|
124
|
+
// walks up looking for an injector whose `scopes` contains 'root', and a null
|
|
125
|
+
// parent means that search always dead-ends in NullInjector). platform-browser's
|
|
126
|
+
// BROWSER_MODULE_PROVIDERS solves this the same way for real DOM apps: it hands
|
|
127
|
+
// { provide: INJECTOR_SCOPE, useValue: 'root' } to its OWN app-level providers, and
|
|
128
|
+
// R3Injector's constructor reads that provider and self-tags this.scopes with
|
|
129
|
+
// 'root'. We do the same here — no PlatformRef, no DOM, just this one provider.
|
|
130
|
+
{ provide: INJECTOR_SCOPE, useValue: 'root' },
|
|
131
|
+
// Supplies the real ChangeDetectionSchedulerImpl (microtask-batched, self-scheduling
|
|
132
|
+
// via ApplicationRef.afterTick) + NoopNgZone + ZONELESS_ENABLED: true — the exact
|
|
133
|
+
// bundle internalCreateApplication() uses. With ApplicationRef reachable (see above),
|
|
134
|
+
// there is no more reason for a hand-rolled scheduler: it replaces our old
|
|
135
|
+
// unconditional `rootView.detectChanges(); cmpView.detectChanges()` (which force-ran
|
|
136
|
+
// BOTH root views on every tick regardless of cause) with Angular's own tick(), which
|
|
137
|
+
// only enters an attached view when something actually marked it (RefreshView / Dirty
|
|
138
|
+
// consumer / HasChildViewsToRefresh). NOTE: this does NOT stop the root's own template
|
|
139
|
+
// from re-running on a plain press or `ChangeDetectorRef.markForCheck()` anywhere in
|
|
140
|
+
// the tree — `markViewDirty` (which both native (event) bindings and `markForCheck()`
|
|
141
|
+
// go through, see SymbioteHostPropsDirective) unconditionally sets RefreshView on
|
|
142
|
+
// EVERY ancestor up to the root; that is fundamental, unavoidable Angular zoneless
|
|
143
|
+
// behavior, true in every Angular app, not something this swap changes. What DOES
|
|
144
|
+
// still protect a sibling branch from an unrelated press is a genuine child
|
|
145
|
+
// `@Component` boundary — SignalView-compiled children are skip-eligible regardless of
|
|
146
|
+
// this scheduler (see the `angular-adapter` skill §20 for the full investigation,
|
|
147
|
+
// including the test that disproved the stronger claim before it shipped).
|
|
148
|
+
...provideZonelessChangeDetectionInternal(),
|
|
149
|
+
ColorSchemeService,
|
|
150
|
+
WindowDimensionsService,
|
|
151
|
+
],
|
|
152
|
+
rootInjectorParent(),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// hostElement = the surface: the component's template content commits straight into the
|
|
156
|
+
// surface with no wrapper view, the engine wrapping surface.children in its synthetic flex
|
|
157
|
+
// root — the Angular equivalent of Vue's `app.mount(surface)`.
|
|
158
|
+
let cmpRef: ComponentRef<unknown>;
|
|
159
|
+
let rootRef: ComponentRef<unknown> | undefined;
|
|
160
|
+
if (options?.wrapperComponent === undefined) {
|
|
161
|
+
cmpRef = createComponent<unknown>(rootComponent, {
|
|
162
|
+
environmentInjector: injector,
|
|
163
|
+
hostElement: asAngularHost(surface),
|
|
164
|
+
});
|
|
165
|
+
applyInputs(cmpRef, options?.initialProps);
|
|
166
|
+
} else {
|
|
167
|
+
// The root gets its own host node from the renderer (createComponent without a
|
|
168
|
+
// hostElement) but is not attached to the surface directly; it is handed to the
|
|
169
|
+
// wrapper as projectable content instead, so only the wrapper needs a real host.
|
|
170
|
+
const rootHost = asAngularHost(createDetachedViewHost());
|
|
171
|
+
rootRef = createComponent<unknown>(rootComponent, {
|
|
172
|
+
environmentInjector: injector,
|
|
173
|
+
hostElement: rootHost,
|
|
174
|
+
});
|
|
175
|
+
applyInputs(rootRef, options.initialProps);
|
|
176
|
+
cmpRef = createComponent<unknown>(options.wrapperComponent, {
|
|
177
|
+
environmentInjector: injector,
|
|
178
|
+
hostElement: asAngularHost(surface),
|
|
179
|
+
projectableNodes: [[rootHost]],
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Attach both root views to ApplicationRef so its own tick() (via the real
|
|
184
|
+
// ChangeDetectionSchedulerImpl provided above) drives them from here on — no manual
|
|
185
|
+
// ChangeDetectorRef juggling needed. markForCheck anywhere in the tree now notifies
|
|
186
|
+
// the real scheduler, which batches a microtask and calls appRef.tick() itself.
|
|
187
|
+
const appRef = injector.get(ApplicationRef);
|
|
188
|
+
appRef.attachView(cmpRef.hostView);
|
|
189
|
+
if (rootRef !== undefined) {
|
|
190
|
+
appRef.attachView(rootRef.hostView);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
dlog(`angular mount root=${rootTag}`);
|
|
194
|
+
appRef.tick(); // first paint
|
|
195
|
+
surface.requestCommit();
|
|
196
|
+
|
|
197
|
+
apps.set(rootTag, { cmpRef, rootRef, injector });
|
|
198
|
+
return surface;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Tear down a surface by rootTag: the public pair of `mount`, and the JS half of the
|
|
202
|
+
// bridgeless `RN$stopSurface` contract (see installStopSurfaceGlobal).
|
|
203
|
+
export function unmount(rootTag: IRootTag): void {
|
|
204
|
+
dlog(`angular unmount root=${rootTag}`);
|
|
205
|
+
teardown(rootTag);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// `global.RN$stopSurface` is the JSI hook C++ AppRegistryBinding::stopSurface calls to stop a
|
|
209
|
+
// Fabric surface. RN installs it from its own renderer; symbiote REPLACES that renderer, so
|
|
210
|
+
// without this the binding throws "Global was not installed" on every surface stop (Fast
|
|
211
|
+
// Refresh, focus/lifecycle) and the screen goes blank. Same contract as the React/Vue
|
|
212
|
+
// adapters: an app uses one adapter, so exactly one installer runs.
|
|
213
|
+
declare global {
|
|
214
|
+
var RN$stopSurface: ((surfaceId: number) => void) | undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function installStopSurfaceGlobal(): void {
|
|
218
|
+
globalThis.RN$stopSurface = (surfaceId: number): void => {
|
|
219
|
+
unmount(surfaceId);
|
|
220
|
+
};
|
|
221
|
+
dlog('installed global.RN$stopSurface');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
installStopSurfaceGlobal();
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Co-located headless test for the Angular renderer seam (ADR 0025). Drives SymbioteRenderer
|
|
2
|
+
// directly — no Angular runtime, no compiler — against the shared fake Fabric slot, proving
|
|
3
|
+
// each Renderer2 method maps onto the engine mutation API and commits a correct Fabric tree.
|
|
4
|
+
// The bootstrap (mount → createComponent) is validated separately on a real host / the AOT
|
|
5
|
+
// example; this isolates the seam, which is the deterministic, framework-free half.
|
|
6
|
+
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
|
+
import {
|
|
9
|
+
clearGlobalStyles,
|
|
10
|
+
createSurface,
|
|
11
|
+
disposeRoot,
|
|
12
|
+
isSymbioteNode,
|
|
13
|
+
registerStyles,
|
|
14
|
+
} from '@symbiote-native/engine';
|
|
15
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
16
|
+
import { SymbioteRenderer, SymbioteRendererFactory } from './renderer';
|
|
17
|
+
|
|
18
|
+
const ROOT_TAG = 707;
|
|
19
|
+
const PROBE_ID = 'probe';
|
|
20
|
+
|
|
21
|
+
const fabric = installFabric();
|
|
22
|
+
|
|
23
|
+
// A macrotask boundary drains the engine's coalesced (requestCommit) commit before asserting.
|
|
24
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
25
|
+
|
|
26
|
+
function setup(): { surface: ReturnType<typeof createSurface>; renderer: SymbioteRenderer } {
|
|
27
|
+
const surface = createSurface(ROOT_TAG);
|
|
28
|
+
const renderer = new SymbioteRendererFactory(surface).createRenderer(null, null);
|
|
29
|
+
if (!(renderer instanceof SymbioteRenderer))
|
|
30
|
+
throw new Error('unreachable: factory built the renderer');
|
|
31
|
+
return { surface, renderer };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
beforeEach(() => fabric.reset());
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
disposeRoot(ROOT_TAG);
|
|
37
|
+
clearGlobalStyles();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('Angular SymbioteRenderer drives the engine', () => {
|
|
41
|
+
it('maps createElement / createText / appendChild into a committed Fabric tree', async () => {
|
|
42
|
+
const { surface, renderer } = setup();
|
|
43
|
+
const view = renderer.createElement('symbiote-view');
|
|
44
|
+
const text = renderer.createElement('symbiote-text');
|
|
45
|
+
const raw = renderer.createText('Hello');
|
|
46
|
+
renderer.appendChild(text, raw);
|
|
47
|
+
renderer.appendChild(view, text);
|
|
48
|
+
renderer.appendChild(surface, view);
|
|
49
|
+
await tick();
|
|
50
|
+
|
|
51
|
+
// The engine wraps surface.children in the synthetic box-none AppContainer root.
|
|
52
|
+
const root = fabric.appRoot();
|
|
53
|
+
expect(fabric.serialize(root.children)).toBe('RCTView(RCTText(RCTRawText "Hello"))');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('maps additional symbiote intrinsics through the shared descriptor table', async () => {
|
|
57
|
+
const { surface, renderer } = setup();
|
|
58
|
+
const spinner = renderer.createElement('symbiote-activity-indicator');
|
|
59
|
+
renderer.setProperty(spinner, 'testID', 'spinner');
|
|
60
|
+
renderer.setProperty(spinner, 'animating', true);
|
|
61
|
+
const image = renderer.createElement('symbiote-image');
|
|
62
|
+
renderer.setProperty(image, 'testID', 'image');
|
|
63
|
+
renderer.setProperty(image, 'source', { uri: 'https://example.invalid/image.png' });
|
|
64
|
+
renderer.appendChild(surface, spinner);
|
|
65
|
+
renderer.appendChild(surface, image);
|
|
66
|
+
await tick();
|
|
67
|
+
|
|
68
|
+
const committed = fabric.find(node => node.props.testID === 'spinner');
|
|
69
|
+
expect(committed?.viewName).toBe('ActivityIndicatorView');
|
|
70
|
+
expect(committed?.props.animating).toBe(true);
|
|
71
|
+
const committedImage = fabric.find(node => node.props.testID === 'image');
|
|
72
|
+
expect(committedImage?.viewName).toBe('RCTImageView');
|
|
73
|
+
expect(committedImage?.props.source).toEqual({ uri: 'https://example.invalid/image.png' });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('commits setProperty and merges per-key setStyle into one style prop', async () => {
|
|
77
|
+
const { surface, renderer } = setup();
|
|
78
|
+
const view = renderer.createElement('symbiote-view');
|
|
79
|
+
renderer.setProperty(view, 'nativeID', PROBE_ID);
|
|
80
|
+
renderer.setStyle(view, 'padding', 24);
|
|
81
|
+
renderer.setStyle(view, 'opacity', 0.5);
|
|
82
|
+
renderer.appendChild(surface, view);
|
|
83
|
+
await tick();
|
|
84
|
+
|
|
85
|
+
const committed = fabric.find(node => node.props.nativeID === PROBE_ID);
|
|
86
|
+
expect(committed, 'the probed RCTView committed').toBeDefined();
|
|
87
|
+
// Angular emits a [style] binding as per-key setStyle; the seam folds them into one style
|
|
88
|
+
// object, then the engine HOISTS style keys to top-level Fabric props (RN's flat C++ props
|
|
89
|
+
// contract — style is never a nested key on a committed node).
|
|
90
|
+
expect(committed?.props).toMatchObject({ nativeID: PROBE_ID, padding: 24, opacity: 0.5 });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('resolves addClass tokens through the shared style registry and lets explicit style win', async () => {
|
|
94
|
+
registerStyles({ card: { padding: 10, backgroundColor: 'red' } });
|
|
95
|
+
const { surface, renderer } = setup();
|
|
96
|
+
const view = renderer.createElement('symbiote-view');
|
|
97
|
+
renderer.setProperty(view, 'nativeID', PROBE_ID);
|
|
98
|
+
// Ivy compiles class="card highlight" to one addClass call per token, never a single string.
|
|
99
|
+
renderer.addClass(view, 'card');
|
|
100
|
+
renderer.addClass(view, 'highlight');
|
|
101
|
+
renderer.setStyle(view, 'backgroundColor', 'blue');
|
|
102
|
+
renderer.appendChild(surface, view);
|
|
103
|
+
await tick();
|
|
104
|
+
|
|
105
|
+
const committed = fabric.find(node => node.props.nativeID === PROBE_ID);
|
|
106
|
+
// padding comes from the class; backgroundColor is explicit style, so it wins over the
|
|
107
|
+
// class-derived red — same precedence Vue's class="..."/:style="..." merge guarantees.
|
|
108
|
+
expect(committed?.props).toMatchObject({ padding: 10, backgroundColor: 'blue' });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('removeClass drops a token and recomputes the resolved style', async () => {
|
|
112
|
+
registerStyles({ card: { padding: 10 }, highlight: { opacity: 0.5 } });
|
|
113
|
+
const { surface, renderer } = setup();
|
|
114
|
+
const view = renderer.createElement('symbiote-view');
|
|
115
|
+
renderer.setProperty(view, 'nativeID', PROBE_ID);
|
|
116
|
+
renderer.addClass(view, 'card');
|
|
117
|
+
renderer.addClass(view, 'highlight');
|
|
118
|
+
renderer.removeClass(view, 'highlight');
|
|
119
|
+
renderer.appendChild(surface, view);
|
|
120
|
+
await tick();
|
|
121
|
+
|
|
122
|
+
const committed = fabric.find(node => node.props.nativeID === PROBE_ID);
|
|
123
|
+
expect(committed?.props.padding).toBe(10);
|
|
124
|
+
expect(committed?.props.opacity).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('removeChild detaches a node from the next commit', async () => {
|
|
128
|
+
const { surface, renderer } = setup();
|
|
129
|
+
const keep = renderer.createElement('symbiote-view');
|
|
130
|
+
const drop = renderer.createElement('symbiote-view');
|
|
131
|
+
renderer.setProperty(drop, 'nativeID', PROBE_ID);
|
|
132
|
+
renderer.appendChild(surface, keep);
|
|
133
|
+
renderer.appendChild(surface, drop);
|
|
134
|
+
await tick();
|
|
135
|
+
expect(
|
|
136
|
+
fabric.find(node => node.props.nativeID === PROBE_ID),
|
|
137
|
+
'present before remove',
|
|
138
|
+
).toBeDefined();
|
|
139
|
+
|
|
140
|
+
fabric.reset();
|
|
141
|
+
renderer.removeChild(surface, drop);
|
|
142
|
+
await tick();
|
|
143
|
+
expect(
|
|
144
|
+
fabric.find(node => node.props.nativeID === PROBE_ID),
|
|
145
|
+
'gone after remove',
|
|
146
|
+
).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('createComment yields an anchor the commit walk skips but flattens its children', async () => {
|
|
150
|
+
const { surface, renderer } = setup();
|
|
151
|
+
const anchor = renderer.createComment();
|
|
152
|
+
renderer.appendChild(anchor, renderer.createElement('symbiote-view'));
|
|
153
|
+
renderer.appendChild(surface, anchor);
|
|
154
|
+
renderer.appendChild(surface, renderer.createElement('symbiote-text'));
|
|
155
|
+
await tick();
|
|
156
|
+
|
|
157
|
+
// The anchor never reaches Fabric; its child keeps the same sibling position. Angular
|
|
158
|
+
// composed components use this to make their framework host element disappear.
|
|
159
|
+
const root = fabric.appRoot();
|
|
160
|
+
expect(fabric.serialize(root.children)).toBe('RCTViewRCTText');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('listen attaches an explicit event listener and the unlisten fn removes it', () => {
|
|
164
|
+
const { renderer } = setup();
|
|
165
|
+
const view = renderer.createElement('symbiote-view');
|
|
166
|
+
expect(isSymbioteNode(view)).toBe(true);
|
|
167
|
+
if (!isSymbioteNode(view)) throw new Error('unreachable: createElement returns a node');
|
|
168
|
+
|
|
169
|
+
const unlisten = renderer.listen(view, 'press', () => {});
|
|
170
|
+
expect(view.listeners?.has('press'), 'listener attached under the explicit name').toBe(true);
|
|
171
|
+
unlisten();
|
|
172
|
+
expect(view.listeners?.has('press'), 'unlisten removed it').toBe(false);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('listen on a global target (window/document) is an inert no-op', () => {
|
|
176
|
+
const { renderer } = setup();
|
|
177
|
+
expect(() => renderer.listen('window', 'resize', () => {})()).not.toThrow();
|
|
178
|
+
});
|
|
179
|
+
});
|