@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.
Files changed (372) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/build/angular/components/activity-indicator/index.android.d.ts +12 -0
  4. package/build/angular/components/activity-indicator/index.android.js +21 -0
  5. package/build/angular/components/activity-indicator/index.android.js.map +1 -0
  6. package/build/angular/components/activity-indicator/index.d.ts +1 -0
  7. package/build/angular/components/activity-indicator/index.ios.d.ts +8 -0
  8. package/build/angular/components/activity-indicator/index.ios.js +21 -0
  9. package/build/angular/components/activity-indicator/index.ios.js.map +1 -0
  10. package/build/angular/components/activity-indicator/index.js +4 -0
  11. package/build/angular/components/activity-indicator/index.js.map +1 -0
  12. package/build/angular/components/activity-indicator/shared.d.ts +67 -0
  13. package/build/angular/components/activity-indicator/shared.js +260 -0
  14. package/build/angular/components/activity-indicator/shared.js.map +1 -0
  15. package/build/angular/components/button.d.ts +63 -0
  16. package/build/angular/components/button.js +332 -0
  17. package/build/angular/components/button.js.map +1 -0
  18. package/build/angular/components/drawer-layout-android/index.android.d.ts +34 -0
  19. package/build/angular/components/drawer-layout-android/index.android.js +291 -0
  20. package/build/angular/components/drawer-layout-android/index.android.js.map +1 -0
  21. package/build/angular/components/drawer-layout-android/index.d.ts +11 -0
  22. package/build/angular/components/drawer-layout-android/index.js +109 -0
  23. package/build/angular/components/drawer-layout-android/index.js.map +1 -0
  24. package/build/angular/components/drawer-layout-android/shared.d.ts +74 -0
  25. package/build/angular/components/drawer-layout-android/shared.js +138 -0
  26. package/build/angular/components/drawer-layout-android/shared.js.map +1 -0
  27. package/build/angular/components/flat-list/index.d.ts +122 -0
  28. package/build/angular/components/flat-list/index.js +689 -0
  29. package/build/angular/components/flat-list/index.js.map +1 -0
  30. package/build/angular/components/image/index.android.d.ts +10 -0
  31. package/build/angular/components/image/index.android.js +59 -0
  32. package/build/angular/components/image/index.android.js.map +1 -0
  33. package/build/angular/components/image/index.d.ts +1 -0
  34. package/build/angular/components/image/index.ios.d.ts +10 -0
  35. package/build/angular/components/image/index.ios.js +59 -0
  36. package/build/angular/components/image/index.ios.js.map +1 -0
  37. package/build/angular/components/image/index.js +3 -0
  38. package/build/angular/components/image/index.js.map +1 -0
  39. package/build/angular/components/image/shared.d.ts +101 -0
  40. package/build/angular/components/image/shared.js +408 -0
  41. package/build/angular/components/image/shared.js.map +1 -0
  42. package/build/angular/components/image-background.d.ts +81 -0
  43. package/build/angular/components/image-background.js +418 -0
  44. package/build/angular/components/image-background.js.map +1 -0
  45. package/build/angular/components/input-accessory-view.d.ts +59 -0
  46. package/build/angular/components/input-accessory-view.js +266 -0
  47. package/build/angular/components/input-accessory-view.js.map +1 -0
  48. package/build/angular/components/keyboard-avoiding-view.d.ts +82 -0
  49. package/build/angular/components/keyboard-avoiding-view.js +363 -0
  50. package/build/angular/components/keyboard-avoiding-view.js.map +1 -0
  51. package/build/angular/components/modal.d.ts +92 -0
  52. package/build/angular/components/modal.js +365 -0
  53. package/build/angular/components/modal.js.map +1 -0
  54. package/build/angular/components/pressable/index.d.ts +123 -0
  55. package/build/angular/components/pressable/index.js +421 -0
  56. package/build/angular/components/pressable/index.js.map +1 -0
  57. package/build/angular/components/refresh-control.d.ts +86 -0
  58. package/build/angular/components/refresh-control.js +347 -0
  59. package/build/angular/components/refresh-control.js.map +1 -0
  60. package/build/angular/components/safe-area-view.d.ts +57 -0
  61. package/build/angular/components/safe-area-view.js +253 -0
  62. package/build/angular/components/safe-area-view.js.map +1 -0
  63. package/build/angular/components/scroll-view/index.android.d.ts +21 -0
  64. package/build/angular/components/scroll-view/index.android.js +235 -0
  65. package/build/angular/components/scroll-view/index.android.js.map +1 -0
  66. package/build/angular/components/scroll-view/index.d.ts +3 -0
  67. package/build/angular/components/scroll-view/index.ios.d.ts +9 -0
  68. package/build/angular/components/scroll-view/index.ios.js +86 -0
  69. package/build/angular/components/scroll-view/index.ios.js.map +1 -0
  70. package/build/angular/components/scroll-view/index.js +6 -0
  71. package/build/angular/components/scroll-view/index.js.map +1 -0
  72. package/build/angular/components/scroll-view/projection.d.ts +32 -0
  73. package/build/angular/components/scroll-view/projection.js +312 -0
  74. package/build/angular/components/scroll-view/projection.js.map +1 -0
  75. package/build/angular/components/scroll-view/shared.d.ts +230 -0
  76. package/build/angular/components/scroll-view/shared.js +679 -0
  77. package/build/angular/components/scroll-view/shared.js.map +1 -0
  78. package/build/angular/components/scroll-view/sticky-header.d.ts +41 -0
  79. package/build/angular/components/scroll-view/sticky-header.js +208 -0
  80. package/build/angular/components/scroll-view/sticky-header.js.map +1 -0
  81. package/build/angular/components/section-list/index.d.ts +116 -0
  82. package/build/angular/components/section-list/index.js +641 -0
  83. package/build/angular/components/section-list/index.js.map +1 -0
  84. package/build/angular/components/switch/index.android.d.ts +20 -0
  85. package/build/angular/components/switch/index.android.js +70 -0
  86. package/build/angular/components/switch/index.android.js.map +1 -0
  87. package/build/angular/components/switch/index.d.ts +1 -0
  88. package/build/angular/components/switch/index.ios.d.ts +19 -0
  89. package/build/angular/components/switch/index.ios.js +69 -0
  90. package/build/angular/components/switch/index.ios.js.map +1 -0
  91. package/build/angular/components/switch/index.js +4 -0
  92. package/build/angular/components/switch/index.js.map +1 -0
  93. package/build/angular/components/switch/shared.d.ts +40 -0
  94. package/build/angular/components/switch/shared.js +127 -0
  95. package/build/angular/components/switch/shared.js.map +1 -0
  96. package/build/angular/components/text-input.d.ts +153 -0
  97. package/build/angular/components/text-input.js +604 -0
  98. package/build/angular/components/text-input.js.map +1 -0
  99. package/build/angular/components/touchable-native-feedback.d.ts +83 -0
  100. package/build/angular/components/touchable-native-feedback.js +376 -0
  101. package/build/angular/components/touchable-native-feedback.js.map +1 -0
  102. package/build/angular/components/touchable.d.ts +248 -0
  103. package/build/angular/components/touchable.js +1206 -0
  104. package/build/angular/components/touchable.js.map +1 -0
  105. package/build/angular/components/virtualized-list/directives.d.ts +54 -0
  106. package/build/angular/components/virtualized-list/directives.js +154 -0
  107. package/build/angular/components/virtualized-list/directives.js.map +1 -0
  108. package/build/angular/components/virtualized-list/index.d.ts +268 -0
  109. package/build/angular/components/virtualized-list/index.js +1162 -0
  110. package/build/angular/components/virtualized-list/index.js.map +1 -0
  111. package/build/angular/components/virtualized-section-list/directives.d.ts +37 -0
  112. package/build/angular/components/virtualized-section-list/directives.js +77 -0
  113. package/build/angular/components/virtualized-section-list/directives.js.map +1 -0
  114. package/build/angular/components/virtualized-section-list/index.d.ts +178 -0
  115. package/build/angular/components/virtualized-section-list/index.js +722 -0
  116. package/build/angular/components/virtualized-section-list/index.js.map +1 -0
  117. package/build/angular/components.d.ts +39 -0
  118. package/build/angular/components.js +38 -0
  119. package/build/angular/components.js.map +1 -0
  120. package/build/angular/create-portal.d.ts +22 -0
  121. package/build/angular/create-portal.js +74 -0
  122. package/build/angular/create-portal.js.map +1 -0
  123. package/build/angular/create-tunnel.d.ts +34 -0
  124. package/build/angular/create-tunnel.js +126 -0
  125. package/build/angular/create-tunnel.js.map +1 -0
  126. package/build/angular/descriptor-to-angular/index.d.ts +22 -0
  127. package/build/angular/descriptor-to-angular/index.js +142 -0
  128. package/build/angular/descriptor-to-angular/index.js.map +1 -0
  129. package/build/angular/host-instance/index.d.ts +2 -0
  130. package/build/angular/host-instance/index.js +37 -0
  131. package/build/angular/host-instance/index.js.map +1 -0
  132. package/build/angular/index.d.ts +21 -0
  133. package/build/angular/index.js +42 -0
  134. package/build/angular/index.js.map +1 -0
  135. package/build/angular/modules/animated/create-animated-component.d.ts +68 -0
  136. package/build/angular/modules/animated/create-animated-component.js +429 -0
  137. package/build/angular/modules/animated/create-animated-component.js.map +1 -0
  138. package/build/angular/modules/animated/index.d.ts +51 -0
  139. package/build/angular/modules/animated/index.js +59 -0
  140. package/build/angular/modules/animated/index.js.map +1 -0
  141. package/build/angular/modules/app-registry/index.d.ts +7 -0
  142. package/build/angular/modules/app-registry/index.js +23 -0
  143. package/build/angular/modules/app-registry/index.js.map +1 -0
  144. package/build/angular/modules/status-bar.d.ts +20 -0
  145. package/build/angular/modules/status-bar.js +59 -0
  146. package/build/angular/modules/status-bar.js.map +1 -0
  147. package/build/angular/primitives/index.d.ts +63 -0
  148. package/build/angular/primitives/index.js +189 -0
  149. package/build/angular/primitives/index.js.map +1 -0
  150. package/build/angular/primitives/shared.d.ts +89 -0
  151. package/build/angular/primitives/shared.js +172 -0
  152. package/build/angular/primitives/shared.js.map +1 -0
  153. package/build/angular/render.d.ts +11 -0
  154. package/build/angular/render.js +161 -0
  155. package/build/angular/render.js.map +1 -0
  156. package/build/angular/renderer.d.ts +38 -0
  157. package/build/angular/renderer.js +360 -0
  158. package/build/angular/renderer.js.map +1 -0
  159. package/build/angular/services/color-scheme.service.d.ts +16 -0
  160. package/build/angular/services/color-scheme.service.js +31 -0
  161. package/build/angular/services/color-scheme.service.js.map +1 -0
  162. package/build/angular/services/index.d.ts +2 -0
  163. package/build/angular/services/index.js +3 -0
  164. package/build/angular/services/index.js.map +1 -0
  165. package/build/angular/services/window-dimensions.service.d.ts +16 -0
  166. package/build/angular/services/window-dimensions.service.js +42 -0
  167. package/build/angular/services/window-dimensions.service.js.map +1 -0
  168. package/build/components/activity-indicator/index.android.d.ts +9 -0
  169. package/build/components/activity-indicator/index.android.js +64 -0
  170. package/build/components/activity-indicator/index.d.ts +1 -0
  171. package/build/components/activity-indicator/index.ios.d.ts +5 -0
  172. package/build/components/activity-indicator/index.ios.js +64 -0
  173. package/build/components/activity-indicator/index.js +3 -0
  174. package/build/components/activity-indicator/shared.d.ts +64 -0
  175. package/build/components/activity-indicator/shared.js +424 -0
  176. package/build/components/button.d.ts +60 -0
  177. package/build/components/button.js +456 -0
  178. package/build/components/drawer-layout-android/index.android.d.ts +29 -0
  179. package/build/components/drawer-layout-android/index.android.js +326 -0
  180. package/build/components/drawer-layout-android/index.d.ts +8 -0
  181. package/build/components/drawer-layout-android/index.js +145 -0
  182. package/build/components/drawer-layout-android/shared.d.ts +71 -0
  183. package/build/components/drawer-layout-android/shared.js +205 -0
  184. package/build/components/flat-list/index.d.ts +119 -0
  185. package/build/components/flat-list/index.js +692 -0
  186. package/build/components/image/index.android.d.ts +7 -0
  187. package/build/components/image/index.android.js +88 -0
  188. package/build/components/image/index.d.ts +1 -0
  189. package/build/components/image/index.ios.d.ts +7 -0
  190. package/build/components/image/index.ios.js +88 -0
  191. package/build/components/image/index.js +2 -0
  192. package/build/components/image/shared.d.ts +101 -0
  193. package/build/components/image/shared.js +407 -0
  194. package/build/components/image-background.d.ts +78 -0
  195. package/build/components/image-background.js +583 -0
  196. package/build/components/input-accessory-view.d.ts +56 -0
  197. package/build/components/input-accessory-view.js +407 -0
  198. package/build/components/keyboard-avoiding-view.d.ts +79 -0
  199. package/build/components/keyboard-avoiding-view.js +509 -0
  200. package/build/components/modal.d.ts +89 -0
  201. package/build/components/modal.js +534 -0
  202. package/build/components/pressable/index.d.ts +120 -0
  203. package/build/components/pressable/index.js +640 -0
  204. package/build/components/refresh-control.d.ts +83 -0
  205. package/build/components/refresh-control.js +514 -0
  206. package/build/components/safe-area-view.d.ts +54 -0
  207. package/build/components/safe-area-view.js +393 -0
  208. package/build/components/scroll-view/index.android.d.ts +16 -0
  209. package/build/components/scroll-view/index.android.js +228 -0
  210. package/build/components/scroll-view/index.d.ts +3 -0
  211. package/build/components/scroll-view/index.ios.d.ts +6 -0
  212. package/build/components/scroll-view/index.ios.js +113 -0
  213. package/build/components/scroll-view/index.js +5 -0
  214. package/build/components/scroll-view/projection.d.ts +32 -0
  215. package/build/components/scroll-view/projection.js +311 -0
  216. package/build/components/scroll-view/shared.d.ts +225 -0
  217. package/build/components/scroll-view/shared.js +757 -0
  218. package/build/components/scroll-view/sticky-header.d.ts +38 -0
  219. package/build/components/scroll-view/sticky-header.js +257 -0
  220. package/build/components/section-list/index.d.ts +113 -0
  221. package/build/components/section-list/index.js +768 -0
  222. package/build/components/switch/index.android.d.ts +17 -0
  223. package/build/components/switch/index.android.js +107 -0
  224. package/build/components/switch/index.d.ts +1 -0
  225. package/build/components/switch/index.ios.d.ts +16 -0
  226. package/build/components/switch/index.ios.js +106 -0
  227. package/build/components/switch/index.js +3 -0
  228. package/build/components/switch/shared.d.ts +37 -0
  229. package/build/components/switch/shared.js +176 -0
  230. package/build/components/text-input.d.ts +150 -0
  231. package/build/components/text-input.js +826 -0
  232. package/build/components/touchable-native-feedback.d.ts +80 -0
  233. package/build/components/touchable-native-feedback.js +550 -0
  234. package/build/components/touchable.d.ts +241 -0
  235. package/build/components/touchable.js +1672 -0
  236. package/build/components/virtualized-list/directives.d.ts +41 -0
  237. package/build/components/virtualized-list/directives.js +250 -0
  238. package/build/components/virtualized-list/index.d.ts +265 -0
  239. package/build/components/virtualized-list/index.js +1344 -0
  240. package/build/components/virtualized-section-list/directives.d.ts +28 -0
  241. package/build/components/virtualized-section-list/directives.js +149 -0
  242. package/build/components/virtualized-section-list/index.d.ts +175 -0
  243. package/build/components/virtualized-section-list/index.js +854 -0
  244. package/build/components.d.ts +39 -0
  245. package/build/components.js +37 -0
  246. package/build/create-portal.d.ts +17 -0
  247. package/build/create-portal.js +128 -0
  248. package/build/create-tunnel.d.ts +29 -0
  249. package/build/create-tunnel.js +181 -0
  250. package/build/descriptor-to-angular/index.d.ts +19 -0
  251. package/build/descriptor-to-angular/index.js +186 -0
  252. package/build/host-instance/index.d.ts +2 -0
  253. package/build/host-instance/index.js +36 -0
  254. package/build/index.d.ts +21 -0
  255. package/build/index.js +41 -0
  256. package/build/modules/animated/create-animated-component.d.ts +57 -0
  257. package/build/modules/animated/create-animated-component.js +490 -0
  258. package/build/modules/animated/index.d.ts +51 -0
  259. package/build/modules/animated/index.js +58 -0
  260. package/build/modules/app-registry/index.d.ts +7 -0
  261. package/build/modules/app-registry/index.js +22 -0
  262. package/build/modules/status-bar.d.ts +17 -0
  263. package/build/modules/status-bar.js +121 -0
  264. package/build/primitives/index.d.ts +32 -0
  265. package/build/primitives/index.js +386 -0
  266. package/build/primitives/shared.d.ts +84 -0
  267. package/build/primitives/shared.js +231 -0
  268. package/build/render.d.ts +11 -0
  269. package/build/render.js +160 -0
  270. package/build/renderer.d.ts +38 -0
  271. package/build/renderer.js +359 -0
  272. package/build/services/color-scheme.service.d.ts +13 -0
  273. package/build/services/color-scheme.service.js +72 -0
  274. package/build/services/index.d.ts +2 -0
  275. package/build/services/index.js +2 -0
  276. package/build/services/window-dimensions.service.d.ts +13 -0
  277. package/build/services/window-dimensions.service.js +83 -0
  278. package/metro-css-parser.cjs +11 -0
  279. package/package.json +57 -0
  280. package/src/__tests__/responder-change-detection.test.ts +102 -0
  281. package/src/__tests__/responder-nested-cd.test.ts +113 -0
  282. package/src/angular-gaps.test.ts +142 -0
  283. package/src/angular-imports.test.ts +100 -0
  284. package/src/components/activity-indicator/activity-indicator-class-style.test.ts +44 -0
  285. package/src/components/activity-indicator/activity-indicator.test.ts +131 -0
  286. package/src/components/activity-indicator/index.android.ts +16 -0
  287. package/src/components/activity-indicator/index.ios.ts +17 -0
  288. package/src/components/activity-indicator/index.ts +4 -0
  289. package/src/components/activity-indicator/shared.ts +206 -0
  290. package/src/components/button.ts +193 -0
  291. package/src/components/flat-list/column-wrapper-style-class.test.ts +116 -0
  292. package/src/components/flat-list/flat-list-array-style.test.ts +131 -0
  293. package/src/components/flat-list/flat-list-class-style.test.ts +74 -0
  294. package/src/components/flat-list/flat-list-scroll-containment.test.ts +107 -0
  295. package/src/components/flat-list/flat-list.test.ts +99 -0
  296. package/src/components/flat-list/index.ts +575 -0
  297. package/src/components/image/image-class-style.test.ts +46 -0
  298. package/src/components/image/index.android.ts +48 -0
  299. package/src/components/image/index.ios.ts +48 -0
  300. package/src/components/image/index.ts +3 -0
  301. package/src/components/image/shared.ts +478 -0
  302. package/src/components/image-background-class-style.test.ts +48 -0
  303. package/src/components/image-background-style-class.test.ts +79 -0
  304. package/src/components/image-background.ts +270 -0
  305. package/src/components/input-accessory-view.test.ts +43 -0
  306. package/src/components/input-accessory-view.ts +191 -0
  307. package/src/components/keyboard-avoiding-view.test.ts +54 -0
  308. package/src/components/keyboard-avoiding-view.ts +308 -0
  309. package/src/components/modal.test.ts +43 -0
  310. package/src/components/modal.ts +304 -0
  311. package/src/components/pressable/index.ts +371 -0
  312. package/src/components/pressable/pressable.test.ts +85 -0
  313. package/src/components/refresh-control.test.ts +41 -0
  314. package/src/components/refresh-control.ts +288 -0
  315. package/src/components/safe-area-view.test.ts +43 -0
  316. package/src/components/safe-area-view.ts +178 -0
  317. package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +123 -0
  318. package/src/components/scroll-view/content-container-style-class.test.ts +75 -0
  319. package/src/components/scroll-view/index.android.ts +190 -0
  320. package/src/components/scroll-view/index.ios.ts +79 -0
  321. package/src/components/scroll-view/index.ts +7 -0
  322. package/src/components/scroll-view/projection.ts +384 -0
  323. package/src/components/scroll-view/scroll-view-class-style.test.ts +134 -0
  324. package/src/components/scroll-view/scroll-view-projection.test.ts +194 -0
  325. package/src/components/scroll-view/shared.ts +836 -0
  326. package/src/components/scroll-view/sticky-header.ts +230 -0
  327. package/src/components/section-list/index.ts +445 -0
  328. package/src/components/switch/android-switch-class.test.ts +42 -0
  329. package/src/components/switch/index.android.ts +61 -0
  330. package/src/components/switch/index.ios.ts +60 -0
  331. package/src/components/switch/index.ts +4 -0
  332. package/src/components/switch/shared.ts +197 -0
  333. package/src/components/switch/switch.test.ts +92 -0
  334. package/src/components/text-input-class-style.test.ts +54 -0
  335. package/src/components/text-input.ts +536 -0
  336. package/src/components/touchable-native-feedback.test.ts +49 -0
  337. package/src/components/touchable-native-feedback.ts +260 -0
  338. package/src/components/touchable.test.ts +102 -0
  339. package/src/components/touchable.ts +643 -0
  340. package/src/components/virtualized-list/directives.ts +169 -0
  341. package/src/components/virtualized-list/index.ts +1204 -0
  342. package/src/components/virtualized-list/virtualized-list-class-style.test.ts +76 -0
  343. package/src/components/virtualized-section-list/directives.ts +91 -0
  344. package/src/components/virtualized-section-list/index.ts +598 -0
  345. package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +60 -0
  346. package/src/components.ts +149 -0
  347. package/src/create-portal.test.ts +90 -0
  348. package/src/create-portal.ts +74 -0
  349. package/src/create-tunnel.test.ts +120 -0
  350. package/src/create-tunnel.ts +136 -0
  351. package/src/descriptor-to-angular/descriptor-outlet.test.ts +94 -0
  352. package/src/descriptor-to-angular/index.ts +190 -0
  353. package/src/host-instance/host-instance.test.ts +49 -0
  354. package/src/host-instance/index.ts +36 -0
  355. package/src/index.ts +257 -0
  356. package/src/modules/animated/animated-scroll-view.test.ts +85 -0
  357. package/src/modules/animated/create-animated-component.ts +435 -0
  358. package/src/modules/animated/index.ts +105 -0
  359. package/src/modules/app-registry/app-registry.test.ts +106 -0
  360. package/src/modules/app-registry/index.ts +60 -0
  361. package/src/modules/status-bar.test.ts +50 -0
  362. package/src/modules/status-bar.ts +57 -0
  363. package/src/primitives/index.ts +119 -0
  364. package/src/primitives/shared.ts +191 -0
  365. package/src/render.test.ts +147 -0
  366. package/src/render.ts +224 -0
  367. package/src/renderer.test.ts +179 -0
  368. package/src/renderer.ts +407 -0
  369. package/src/services/color-scheme.service.ts +27 -0
  370. package/src/services/index.ts +2 -0
  371. package/src/services/services.test.ts +163 -0
  372. package/src/services/window-dimensions.service.ts +41 -0
@@ -0,0 +1,60 @@
1
+ // AppRegistry: the JS entry point RN apps already use:
2
+ // AppRegistry.registerComponent(appKey, () => App)
3
+ // The registry bookkeeping (sections, host-registrar bridge, headless tasks) is
4
+ // framework-agnostic and lives in @symbiote-native/engine's createAppRegistry; this file supplies
5
+ // only the one Angular-specific seam — building a runnable from a component provider via
6
+ // `mount`'s wrapper/initialProps support (render.ts) — so examples/angular's own
7
+ // registerRunnable + mount call can move onto the same entry point React/Vue already have.
8
+
9
+ import type { Type } from '@angular/core';
10
+ import {
11
+ createAppRegistry,
12
+ dlog,
13
+ type IAppParameters,
14
+ type IRunnable,
15
+ } from '@symbiote-native/engine';
16
+ import { mount } from '../../render';
17
+
18
+ // RN's IComponentProvider: a thunk returning the root component (lazy so the module
19
+ // graph stays cheap until the app actually runs).
20
+ export type IComponentProvider = () => Type<unknown>;
21
+
22
+ // RN's IWrapperComponentProvider: given the surface's parameters, returns a
23
+ // component to wrap the app root in (e.g. a context provider). Optional. Angular has no
24
+ // runtime template synthesis (no JIT under AOT/Metro — see create-animated-component.ts),
25
+ // so the wrapper must be a pre-authored standalone @Component whose template renders
26
+ // `<ng-content>` — the Angular idiom for "render my children" (mount() projects the root's
27
+ // host node into it, the twin of React's createElement(Wrapper, null, rootElement)).
28
+ export type IWrapperComponentProvider = (appParameters: IAppParameters) => Type<unknown>;
29
+
30
+ function runnableFor(
31
+ componentProvider: IComponentProvider,
32
+ getWrapperComponentProvider: () => IWrapperComponentProvider | undefined,
33
+ ): IRunnable {
34
+ return appParameters => {
35
+ dlog(`AppRegistry: mounting on rootTag ${String(appParameters.rootTag)}`);
36
+ // Read live (not at registration time) so setWrapperComponentProvider affects every
37
+ // runnable's next run, not just future ones.
38
+ mount(appParameters.rootTag, componentProvider(), {
39
+ initialProps: appParameters.initialProps,
40
+ wrapperComponent: getWrapperComponentProvider()?.(appParameters),
41
+ });
42
+ };
43
+ }
44
+
45
+ const { AppRegistry, setHostRegistrar } = createAppRegistry<
46
+ IComponentProvider,
47
+ IWrapperComponentProvider
48
+ >(runnableFor);
49
+
50
+ export { AppRegistry, setHostRegistrar };
51
+ export type {
52
+ IAppParameters,
53
+ IRunnable,
54
+ IHostRegistrar,
55
+ IRegistry,
56
+ IHeadlessTask,
57
+ ITaskProvider,
58
+ ITaskCanceller,
59
+ ITaskCancelProvider,
60
+ } from '@symbiote-native/engine';
@@ -0,0 +1,50 @@
1
+ import '@angular/compiler';
2
+ import { Component } from '@angular/core';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import * as engine from '@symbiote-native/engine';
5
+ import { installFabric } from '@symbiote-native/test-utils';
6
+
7
+ import { mount, unmount } from '../render';
8
+ import { StatusBar } from './status-bar';
9
+
10
+ const ROOT_TAG = 901;
11
+ const fabric = installFabric();
12
+
13
+ @Component({
14
+ selector: 'symbiote-status-bar-host',
15
+ standalone: true,
16
+ imports: [StatusBar],
17
+ template: ` <StatusBar [barStyle]="'dark-content'" [hidden]="true" [animated]="false" /> `,
18
+ })
19
+ class StatusBarHost {}
20
+
21
+ beforeEach(() => fabric.reset());
22
+ afterEach(() => unmount(ROOT_TAG));
23
+
24
+ describe('StatusBar', () => {
25
+ it('applies status bar props on mount and renders no Fabric node', async () => {
26
+ const spy = vi.spyOn(engine, 'applyStatusBarProps').mockReturnValue(undefined);
27
+
28
+ mount(ROOT_TAG, StatusBarHost);
29
+ await new Promise<void>(resolve => setTimeout(resolve, 0));
30
+
31
+ expect(spy).toHaveBeenCalledWith(
32
+ expect.objectContaining({
33
+ barStyle: 'dark-content',
34
+ hidden: true,
35
+ animated: false,
36
+ }),
37
+ );
38
+
39
+ const root = fabric.appRoot();
40
+ expect(root.children).toHaveLength(0);
41
+ });
42
+
43
+ it('exposes imperative statics on the component value', () => {
44
+ expect(typeof StatusBar.setHidden).toBe('function');
45
+ expect(typeof StatusBar.setBarStyle).toBe('function');
46
+ expect(typeof StatusBar.setNetworkActivityIndicatorVisible).toBe('function');
47
+ expect(typeof StatusBar.setBackgroundColor).toBe('function');
48
+ expect(typeof StatusBar.setTranslucent).toBe('function');
49
+ });
50
+ });
@@ -0,0 +1,57 @@
1
+ // StatusBar, the Angular lifecycle half. The native StatusBarManager driving, the imperative
2
+ // statics, and the Android bar-height constant all live in @symbiote-native/engine, shared verbatim with
3
+ // React and Vue; Metro selects the engine's platform variant. Angular supplies only the
4
+ // declarative shape: a component that renders nothing and re-applies the props through
5
+ // ngOnChanges on mount + every prop change. Imperative statics are attached to the component
6
+ // object the same way RN does.
7
+
8
+ import { Component, Input, type OnChanges, type SimpleChanges } from '@angular/core';
9
+ import {
10
+ applyStatusBarProps,
11
+ statusBarImperative,
12
+ statusBarCurrentHeight,
13
+ type IColorValue,
14
+ type IStatusBarProps,
15
+ type IStatusBarStyle,
16
+ } from '@symbiote-native/engine';
17
+
18
+ export type { IStatusBarProps, IStatusBarStyle } from '@symbiote-native/engine';
19
+
20
+ @Component({
21
+ selector: 'StatusBar',
22
+ standalone: true,
23
+ template: '',
24
+ })
25
+ class StatusBarComponent implements OnChanges {
26
+ @Input() barStyle?: IStatusBarStyle;
27
+ @Input() hidden?: boolean;
28
+ @Input() animated?: boolean;
29
+ @Input() networkActivityIndicatorVisible?: boolean;
30
+ @Input() backgroundColor?: IColorValue;
31
+ @Input() translucent?: boolean;
32
+
33
+ ngOnChanges(_changes: SimpleChanges): void {
34
+ applyStatusBarProps(this.buildProps());
35
+ }
36
+
37
+ private buildProps(): IStatusBarProps {
38
+ return {
39
+ barStyle: this.barStyle,
40
+ hidden: this.hidden,
41
+ animated: this.animated,
42
+ networkActivityIndicatorVisible: this.networkActivityIndicatorVisible,
43
+ backgroundColor: this.backgroundColor,
44
+ translucent: this.translucent,
45
+ };
46
+ }
47
+ }
48
+
49
+ const StatusBarWithStatics = Object.assign(StatusBarComponent, statusBarImperative);
50
+
51
+ Object.defineProperty(StatusBarWithStatics, 'currentHeight', {
52
+ get: statusBarCurrentHeight,
53
+ enumerable: true,
54
+ });
55
+
56
+ export const StatusBar: typeof StatusBarWithStatics & { readonly currentHeight?: number } =
57
+ StatusBarWithStatics;
@@ -0,0 +1,119 @@
1
+ // Angular primitive host components over Symbiote engine intrinsics. These are the only
2
+ // components that directly own the `symbiote-*` selectors; every composed adapter
3
+ // component imports them and renders them in its template. Declaring `style` as a real
4
+ // Angular input prevents Angular's CSS style engine from decomposing RN `StyleProp` arrays.
5
+
6
+ import { Component } from '@angular/core';
7
+ import { SymbiotePrimitiveHost } from './shared';
8
+
9
+ export {
10
+ anchorHostStyle,
11
+ anchorStyleProp,
12
+ stableAnchorStyle,
13
+ SymbioteHostPropsDirective,
14
+ } from './shared';
15
+
16
+ @Component({
17
+ selector: 'symbiote-view, View',
18
+ standalone: true,
19
+ template: '<ng-content></ng-content>',
20
+ })
21
+ export class ViewHost extends SymbiotePrimitiveHost {}
22
+
23
+ @Component({
24
+ selector: 'symbiote-text, Text',
25
+ standalone: true,
26
+ template: '<ng-content></ng-content>',
27
+ })
28
+ export class TextHost extends SymbiotePrimitiveHost {}
29
+
30
+ @Component({
31
+ selector: 'symbiote-image',
32
+ standalone: true,
33
+ template: '<ng-content></ng-content>',
34
+ })
35
+ export class ImageHost extends SymbiotePrimitiveHost {}
36
+
37
+ @Component({
38
+ selector: 'symbiote-scroll-view',
39
+ standalone: true,
40
+ template: '<ng-content></ng-content>',
41
+ })
42
+ export class ScrollViewHost extends SymbiotePrimitiveHost {}
43
+
44
+ @Component({
45
+ selector: 'symbiote-scroll-content',
46
+ standalone: true,
47
+ template: '<ng-content></ng-content>',
48
+ })
49
+ export class ScrollContentView extends SymbiotePrimitiveHost {}
50
+
51
+ @Component({
52
+ selector: 'symbiote-horizontal-scroll-view',
53
+ standalone: true,
54
+ template: '<ng-content></ng-content>',
55
+ })
56
+ export class HorizontalScrollView extends SymbiotePrimitiveHost {}
57
+
58
+ @Component({
59
+ selector: 'symbiote-horizontal-scroll-content',
60
+ standalone: true,
61
+ template: '<ng-content></ng-content>',
62
+ })
63
+ export class HorizontalScrollContentView extends SymbiotePrimitiveHost {}
64
+
65
+ @Component({
66
+ selector: 'symbiote-text-input',
67
+ standalone: true,
68
+ template: '<ng-content></ng-content>',
69
+ })
70
+ export class TextInputHost extends SymbiotePrimitiveHost {}
71
+
72
+ @Component({
73
+ selector: 'symbiote-text-input-multiline',
74
+ standalone: true,
75
+ template: '<ng-content></ng-content>',
76
+ })
77
+ export class MultilineTextInputHost extends SymbiotePrimitiveHost {}
78
+
79
+ @Component({
80
+ selector: 'symbiote-switch',
81
+ standalone: true,
82
+ template: '<ng-content></ng-content>',
83
+ })
84
+ export class SwitchHost extends SymbiotePrimitiveHost {}
85
+
86
+ @Component({
87
+ selector: 'symbiote-activity-indicator',
88
+ standalone: true,
89
+ template: '<ng-content></ng-content>',
90
+ })
91
+ export class ActivityIndicatorHost extends SymbiotePrimitiveHost {}
92
+
93
+ @Component({
94
+ selector: 'symbiote-safe-area-view',
95
+ standalone: true,
96
+ template: '<ng-content></ng-content>',
97
+ })
98
+ export class SafeAreaViewHost extends SymbiotePrimitiveHost {}
99
+
100
+ @Component({
101
+ selector: 'symbiote-modal',
102
+ standalone: true,
103
+ template: '<ng-content></ng-content>',
104
+ })
105
+ export class ModalHost extends SymbiotePrimitiveHost {}
106
+
107
+ @Component({
108
+ selector: 'symbiote-refresh-control',
109
+ standalone: true,
110
+ template: '<ng-content></ng-content>',
111
+ })
112
+ export class RefreshControlHost extends SymbiotePrimitiveHost {}
113
+
114
+ @Component({
115
+ selector: 'symbiote-input-accessory-view',
116
+ standalone: true,
117
+ template: '<ng-content></ng-content>',
118
+ })
119
+ export class InputAccessoryViewHost extends SymbiotePrimitiveHost {}
@@ -0,0 +1,191 @@
1
+ import {
2
+ ChangeDetectorRef,
3
+ Directive,
4
+ ElementRef,
5
+ inject,
6
+ Input,
7
+ type OnChanges,
8
+ Renderer2,
9
+ type SimpleChanges,
10
+ } from '@angular/core';
11
+ import {
12
+ flattenStyle,
13
+ isSymbioteNode,
14
+ type IHostInstance,
15
+ type IStyleProp,
16
+ } from '@symbiote-native/engine';
17
+
18
+ /**
19
+ * A composed Angular component (Pressable, SafeAreaView, ScrollView, ...) is created as a
20
+ * non-painting ANCHOR host (`ANCHOR_HOST_COMPONENTS` in renderer.ts) so it doesn't paint an
21
+ * extra native view over its own template's real content. `class="..."`/`[class.x]`/`[ngClass]`
22
+ * at the component's use site ALWAYS resolves through the renderer's addClass/removeClass onto
23
+ * that anchor — Angular gives no `@Input()` interception hook for `class` the way it does for
24
+ * `[style]` (which is why `style` alone was declared as an `@Input()` in the first place, see
25
+ * `SymbiotePrimitiveHost` above) — so the anchor ends up holding the fully-resolved class-derived
26
+ * style (`routeProp`'s `commitClassStyle` already wrote it to `anchor.props.style`) while the
27
+ * REAL native node the component's own template creates never sees it.
28
+ *
29
+ * Call this from a composed component's own `hostProps`/style getter and merge the result AHEAD
30
+ * of its own explicit `style` @Input, e.g. `style: [anchorHostStyle(this.elementRef),
31
+ * this.resolvedStyle]` — array order matters: `flattenStyle`'s later-wins collapse is what makes
32
+ * an explicit `[style]` on the component still beat its ambient class, mirroring the
33
+ * class-loses-to-explicit-style cascade `commitClassStyle` already enforces for a direct
34
+ * primitive. `this.elementRef` must be this component's OWN injected `ElementRef` (its anchor
35
+ * host), not a `@ViewChild` into its template's inner primitive.
36
+ *
37
+ * Skipping this call for a new `ANCHOR_HOST_COMPONENTS` entry compiles clean under `tsc` AND a
38
+ * real `ngc` AOT build — no type error, no template error — it just silently drops `class="..."`
39
+ * forever; only a device/simulator render (or a committed-node style assertion) catches it. See
40
+ * the `angular-adapter` skill §21 for the device-confirmed incident and the full checklist.
41
+ */
42
+ export function anchorHostStyle(elementRef: ElementRef<unknown>): unknown {
43
+ const node = elementRef.nativeElement;
44
+ return isSymbioteNode(node) ? node.props.style : undefined;
45
+ }
46
+
47
+ // anchorHostStyle returns `unknown` (it reads whatever already resolved onto the anchor node's
48
+ // props bag); a style prop's fields are all optional so any object structurally satisfies it —
49
+ // this narrows just enough (falsy-per-IStyleProp, or an object/array) to fold it into a
50
+ // strictly-typed IStyleProp<T> array without an `as` cast, for a composed component whose own
51
+ // inner primitive declares a real `@Input() style: IStyleProp<SomeConcreteStyle>` (e.g.
52
+ // TouchableOpacity's Pressable, Button's TouchableOpacity) rather than the loosely-typed
53
+ // `symbioteHostProps` bag most composed components merge anchorHostStyle into directly.
54
+ function isStyleValue<T>(value: unknown): value is IStyleProp<T> {
55
+ return (
56
+ value === undefined ||
57
+ value === null ||
58
+ value === false ||
59
+ value === '' ||
60
+ typeof value === 'object'
61
+ );
62
+ }
63
+
64
+ export function anchorStyleProp<T>(elementRef: ElementRef<unknown>): IStyleProp<T> | undefined {
65
+ const value = anchorHostStyle(elementRef);
66
+ return isStyleValue<T>(value) ? value : undefined;
67
+ }
68
+
69
+ function isRecord(value: unknown): value is Record<string, unknown> {
70
+ return typeof value === 'object' && value !== null;
71
+ }
72
+
73
+ function shallowStyleEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {
74
+ const aKeys = Object.keys(a);
75
+ if (aKeys.length !== Object.keys(b).length) return false;
76
+ return aKeys.every(key => a[key] === b[key]);
77
+ }
78
+
79
+ /**
80
+ * `anchorHostStyle` must be re-read on every check (a bare `class="x"` with no matching
81
+ * `[style]` binding never appears in Angular's `SimpleChanges`/dirty-check inputs, so nothing
82
+ * else signals "the anchor's class-derived style may have changed"). But `flattenStyle([...])`
83
+ * always allocates a FRESH object, even when its content is byte-for-byte identical to last
84
+ * time — and a windowed list component (FlatList/VirtualizedList/SectionList/
85
+ * VirtualizedSectionList) binds its resolved style into a structural-directive-bound `@Input()`
86
+ * chain where a fresh reference on an UNRELATED CD pass is mistaken for a real change, which
87
+ * reschedules ANOTHER CD tick (`VListOutletDirective`'s `context` `@Input()` calling
88
+ * `markForCheck()`) — free-running forever. See the `flat-list-array-style.test.ts` regression
89
+ * this fixes (and the near-identical `lastRecompute` dedup guard in `virtualized-list/index.ts`,
90
+ * which this mirrors at the style-merge call site instead of a whole recompute step). Returns
91
+ * `previous` unchanged when the freshly-flattened merge is shallow-equal to it, so a downstream
92
+ * `@Input() style` binding sees NO change and skips its own update on an unrelated tick.
93
+ */
94
+ export function stableAnchorStyle(
95
+ elementRef: ElementRef<unknown>,
96
+ explicitStyle: unknown,
97
+ previous: unknown,
98
+ ): Record<string, unknown> {
99
+ const next = flattenStyle([anchorHostStyle(elementRef), explicitStyle]);
100
+ return isRecord(previous) && shallowStyleEqual(previous, next) ? previous : next;
101
+ }
102
+
103
+ const ON_PREFIX = /^on[A-Z]/;
104
+
105
+ /**
106
+ * Base for all Angular primitive host components (`symbiote-view`, `symbiote-text`, ...).
107
+ * RN's `StyleProp` can be an object, an array, nested arrays, falsy entries, and Animated
108
+ * values. Angular's raw `[style]` binding on a custom element routes through the CSS style
109
+ * engine (`setStyle` per key), which crashes on numeric array keys. By declaring `style` as
110
+ * an Angular `@Input()` on a known component, we intercept it here and forward a flattened
111
+ * style object through `Renderer2.setProperty`, bypassing Angular's CSS styling path.
112
+ *
113
+ * Other props (source, pointerEvents, ...) are intentionally *not* declared as inputs.
114
+ * Angular then treats them as host property bindings and still dispatches them through the
115
+ * custom renderer's `setProperty`, so the engine's `routeProp` handles them as usual.
116
+ */
117
+ @Directive()
118
+ export abstract class SymbiotePrimitiveHost implements OnChanges {
119
+ private readonly renderer = inject(Renderer2);
120
+ private readonly elementRef = inject(ElementRef);
121
+
122
+ @Input() style?: IStyleProp<unknown>;
123
+
124
+ /**
125
+ * The underlying engine host node (public instance). Exposed so `findNodeHandle` can resolve
126
+ * an Angular template ref (`#ref`) on a primitive host to its native reactTag.
127
+ */
128
+ get nativeElement(): IHostInstance {
129
+ return this.elementRef.nativeElement;
130
+ }
131
+
132
+ ngOnChanges(changes: SimpleChanges): void {
133
+ if (!('style' in changes)) return;
134
+ const value = this.style === undefined ? undefined : flattenStyle(this.style);
135
+ this.renderer.setProperty(this.elementRef.nativeElement, 'style', value);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Applies a whole resolved props record (a shared `renderX(...).props` bag) onto a host
141
+ * element via `Renderer2.setProperty`, one call per key. Angular templates compile bindings
142
+ * ahead of time — there is no `v-bind="obj"` equivalent for a custom element — so a component
143
+ * whose props come from a shared render function would otherwise have to declare every key as
144
+ * its own static `[prop]="hostProps['x']"` binding. This generalizes the pattern
145
+ * `SwitchHostPropsDirective` proved first: `<symbiote-x [symbioteHostProps]="hostProps" #ref="symbioteHost">`,
146
+ * with `ref.node` exposing the native element for any imperative follow-up call.
147
+ */
148
+ @Directive({
149
+ selector: '[symbioteHostProps]',
150
+ standalone: true,
151
+ exportAs: 'symbioteHost',
152
+ inputs: ['symbioteHostProps'],
153
+ })
154
+ export class SymbioteHostPropsDirective {
155
+ private readonly renderer = inject(Renderer2);
156
+ private readonly elementRef = inject<ElementRef<unknown>>(ElementRef);
157
+ // The directive lives in its HOST component's template, so its injected ChangeDetectorRef IS
158
+ // that component's own view detector — the one thing that can refresh it (see wrapCallback).
159
+ private readonly cdr = inject(ChangeDetectorRef);
160
+
161
+ get node(): unknown {
162
+ return this.elementRef.nativeElement;
163
+ }
164
+
165
+ set symbioteHostProps(props: Record<string, unknown>) {
166
+ for (const [key, value] of Object.entries(props)) {
167
+ this.renderer.setProperty(this.elementRef.nativeElement, key, this.wrapCallback(key, value));
168
+ }
169
+ }
170
+
171
+ // A flat-bag `onX` callback (responder negotiation, onLongPress, …) is invoked by the engine's
172
+ // event dispatch — a plain JS call, entirely outside Angular. In Angular 20 a component compiles
173
+ // as a SignalView (not CheckAlways), so a plain (non-signal) state mutation inside such a callback
174
+ // does NOT dirty the view's reactive consumer, and the scheduler's root detectChanges() will NOT
175
+ // descend into it (a Global pass only refreshes CheckAlways / Dirty / RefreshView views) — the
176
+ // "pan does nothing" bug on a nested component. `markForCheck()` flags THIS component's view AND
177
+ // all its ancestors with RefreshView (which survives the descent) and notifies the CD scheduler,
178
+ // so the next root tick repaints the mutation. It targets the directive's injecting view
179
+ // (`_cdRefInjectingView`, i.e. the host component), unlike `detectChanges()` which acts on a
180
+ // resolved `_lView` that is NOT the host component here. The Angular twin of what React/Vue get
181
+ // for free (setState / proxy reactivity). See the `angular-adapter` skill §17.
182
+ private wrapCallback(key: string, value: unknown): unknown {
183
+ if (!ON_PREFIX.test(key) || typeof value !== 'function') return value;
184
+ const handler = value as (...args: unknown[]) => unknown;
185
+ return (...args: unknown[]): unknown => {
186
+ const result = handler(...args);
187
+ this.cdr.markForCheck();
188
+ return result;
189
+ };
190
+ }
191
+ }
@@ -0,0 +1,147 @@
1
+ // Integration coverage: run the real Angular runtime (createComponent + RendererFactory2)
2
+ // over the headless fake Fabric slot. Unlike renderer.test.ts, this proves mount() wires
3
+ // Angular's bootstrap to the Symbiote renderer, so a standalone template paints and updates
4
+ // through the engine.
5
+
6
+ import '@angular/compiler';
7
+ import { Component } from '@angular/core';
8
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
9
+ import { installFabric } from '@symbiote-native/test-utils';
10
+ import { mount, unmount } from './render';
11
+
12
+ const ROOT_TAG = 808;
13
+
14
+ const fabric = installFabric();
15
+ const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
16
+ const drainAngularAndCommit = async (): Promise<void> => {
17
+ await tick();
18
+ await tick();
19
+ };
20
+
21
+ class TestView {}
22
+ Component({
23
+ selector: 'symbiote-view',
24
+ standalone: true,
25
+ template: '<ng-content></ng-content>',
26
+ })(TestView);
27
+
28
+ class TestText {}
29
+ Component({
30
+ selector: 'symbiote-text',
31
+ standalone: true,
32
+ template: '<ng-content></ng-content>',
33
+ })(TestText);
34
+
35
+ class SmokeComponent {
36
+ name = 'Angular';
37
+ count = 0;
38
+ boxStyle = { padding: 12 };
39
+
40
+ increment(): void {
41
+ this.count += 1;
42
+ }
43
+ }
44
+
45
+ Component({
46
+ selector: 'symbiote-angular-smoke',
47
+ standalone: true,
48
+ imports: [TestView, TestText],
49
+ template: `<symbiote-view [style]="boxStyle"><symbiote-text>Hello {{ name }}</symbiote-text><symbiote-view testID="counter" (press)="increment()"><symbiote-text>tapped {{ count }}×</symbiote-text></symbiote-view></symbiote-view>`,
50
+ })(SmokeComponent);
51
+
52
+ beforeEach(() => fabric.reset());
53
+ afterEach(() => unmount(ROOT_TAG));
54
+
55
+ // A plain (non-OnPush) child component compiles as SignalView in Angular 20+, so an UNTOUCHED
56
+ // sibling child is skipped by the per-view CheckAlways/Dirty/RefreshView gate inside
57
+ // detectChangesInView — true with or without ApplicationRef (the gate lives inside
58
+ // detectChangesInView regardless of who calls it). This is NOT a claim that the root's own
59
+ // template is spared: markViewDirty (used by both native (event) bindings and
60
+ // ChangeDetectorRef.markForCheck(), see SymbioteHostPropsDirective) unconditionally sets
61
+ // RefreshView on every ancestor up to the root — a press anywhere always re-runs the root's
62
+ // own template, in every Angular app, regardless of scheduler. What this guards is narrower
63
+ // but still real: a SIBLING component with no dirty descendant of its own must not be dragged
64
+ // along for the ride.
65
+ let unrelatedRenderCount = 0;
66
+ class CounterChild {
67
+ count = 0;
68
+
69
+ increment(): void {
70
+ this.count += 1;
71
+ }
72
+ }
73
+ Component({
74
+ selector: 'counter-child',
75
+ standalone: true,
76
+ imports: [TestView, TestText],
77
+ template:
78
+ '<symbiote-view testID="counter" (press)="increment()"><symbiote-text>tapped {{ count }}×</symbiote-text></symbiote-view>',
79
+ })(CounterChild);
80
+
81
+ class UnrelatedSiblingChild {
82
+ trackRender(): number {
83
+ unrelatedRenderCount++;
84
+ return unrelatedRenderCount;
85
+ }
86
+ }
87
+ Component({
88
+ selector: 'unrelated-sibling-child',
89
+ standalone: true,
90
+ template: '<symbiote-text>unrelated {{ trackRender() }}</symbiote-text>',
91
+ })(UnrelatedSiblingChild);
92
+
93
+ class TargetedComponent {}
94
+ Component({
95
+ selector: 'symbiote-angular-targeted',
96
+ standalone: true,
97
+ imports: [TestView, CounterChild, UnrelatedSiblingChild],
98
+ template: `<symbiote-view><counter-child /><unrelated-sibling-child /></symbiote-view>`,
99
+ })(TargetedComponent);
100
+
101
+ describe('Angular mount', () => {
102
+ it('bootstraps a standalone component into a committed Fabric tree', async () => {
103
+ mount(ROOT_TAG, SmokeComponent);
104
+ await tick();
105
+
106
+ const root = fabric.appRoot();
107
+ expect(fabric.serialize(root.children)).toBe(
108
+ 'RCTView(RCTText(RCTRawText "Hello Angular")RCTView(RCTText(RCTRawText "tapped 0×")))',
109
+ );
110
+ expect(root.children[0]?.props).toMatchObject({ padding: 12 });
111
+ });
112
+
113
+ it('runs Angular change detection after a native press and recommits text', async () => {
114
+ mount(ROOT_TAG, SmokeComponent);
115
+ await tick();
116
+
117
+ const counter = fabric.find(node => node.props.testID === 'counter');
118
+ expect(counter).toBeDefined();
119
+
120
+ fabric.fireEvent(counter?.instanceHandle, 'topTouchStart');
121
+ fabric.fireEvent(counter?.instanceHandle, 'topTouchEnd');
122
+ await drainAngularAndCommit();
123
+
124
+ expect(fabric.serialize(fabric.appRoot().children)).toContain('RCTRawText "tapped 1×"');
125
+ });
126
+
127
+ it('does not re-check a sibling child component on a press inside a different child', async () => {
128
+ unrelatedRenderCount = 0;
129
+ mount(ROOT_TAG, TargetedComponent);
130
+ await tick();
131
+
132
+ const afterFirstPaint = unrelatedRenderCount;
133
+ expect(afterFirstPaint).toBeGreaterThan(0);
134
+
135
+ const counter = fabric.find(node => node.props.testID === 'counter');
136
+ fabric.fireEvent(counter?.instanceHandle, 'topTouchStart');
137
+ fabric.fireEvent(counter?.instanceHandle, 'topTouchEnd');
138
+ await drainAngularAndCommit();
139
+
140
+ expect(fabric.serialize(fabric.appRoot().children)).toContain('RCTRawText "tapped 1×"');
141
+ // UnrelatedSiblingChild has no dirty descendant of its own, so it must not be re-checked
142
+ // just because CounterChild (a completely separate branch) got pressed. See the
143
+ // `angular-adapter` skill §20 for the full investigation, including what this test does
144
+ // NOT prove (the root's own template still re-runs on every press — see render.ts).
145
+ expect(unrelatedRenderCount).toBe(afterFirstPaint);
146
+ });
147
+ });