@symbiote-native/angular 0.0.0-canary-20260724081123

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 (276) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/babel-linker.cjs +7 -0
  4. package/babel-register-composed.cjs +127 -0
  5. package/bin/symbiote-angular-dev.cjs +93 -0
  6. package/build/angular/anchor-host-registry.d.ts +2 -0
  7. package/build/angular/anchor-host-registry.js +84 -0
  8. package/build/angular/anchor-host-registry.js.map +1 -0
  9. package/build/angular/bootstrap.d.ts +7 -0
  10. package/build/angular/bootstrap.js +15 -0
  11. package/build/angular/bootstrap.js.map +1 -0
  12. package/build/angular/components/activity-indicator/index.android.d.ts +12 -0
  13. package/build/angular/components/activity-indicator/index.android.js +21 -0
  14. package/build/angular/components/activity-indicator/index.android.js.map +1 -0
  15. package/build/angular/components/activity-indicator/index.d.ts +1 -0
  16. package/build/angular/components/activity-indicator/index.ios.d.ts +8 -0
  17. package/build/angular/components/activity-indicator/index.ios.js +21 -0
  18. package/build/angular/components/activity-indicator/index.ios.js.map +1 -0
  19. package/build/angular/components/activity-indicator/index.js +4 -0
  20. package/build/angular/components/activity-indicator/index.js.map +1 -0
  21. package/build/angular/components/activity-indicator/shared.d.ts +67 -0
  22. package/build/angular/components/activity-indicator/shared.js +260 -0
  23. package/build/angular/components/activity-indicator/shared.js.map +1 -0
  24. package/build/angular/components/button.d.ts +63 -0
  25. package/build/angular/components/button.js +332 -0
  26. package/build/angular/components/button.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 +688 -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 +406 -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/index.d.ts +60 -0
  46. package/build/angular/components/input-accessory-view/index.js +251 -0
  47. package/build/angular/components/input-accessory-view/index.js.map +1 -0
  48. package/build/angular/components/keyboard-avoiding-view/index.d.ts +82 -0
  49. package/build/angular/components/keyboard-avoiding-view/index.js +358 -0
  50. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -0
  51. package/build/angular/components/modal/index.d.ts +92 -0
  52. package/build/angular/components/modal/index.js +360 -0
  53. package/build/angular/components/modal/index.js.map +1 -0
  54. package/build/angular/components/pressable/index.d.ts +123 -0
  55. package/build/angular/components/pressable/index.js +420 -0
  56. package/build/angular/components/pressable/index.js.map +1 -0
  57. package/build/angular/components/refresh-control/index.d.ts +86 -0
  58. package/build/angular/components/refresh-control/index.js +342 -0
  59. package/build/angular/components/refresh-control/index.js.map +1 -0
  60. package/build/angular/components/safe-area-view/index.d.ts +57 -0
  61. package/build/angular/components/safe-area-view/index.js +248 -0
  62. package/build/angular/components/safe-area-view/index.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 +334 -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 +682 -0
  77. package/build/angular/components/scroll-view/shared.js.map +1 -0
  78. package/build/angular/components/scroll-view/sticky-header.d.ts +40 -0
  79. package/build/angular/components/scroll-view/sticky-header.js +225 -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 +599 -0
  98. package/build/angular/components/text-input.js.map +1 -0
  99. package/build/angular/components/touchable/index.d.ts +245 -0
  100. package/build/angular/components/touchable/index.js +1186 -0
  101. package/build/angular/components/touchable/index.js.map +1 -0
  102. package/build/angular/components/touchable-native-feedback/index.d.ts +83 -0
  103. package/build/angular/components/touchable-native-feedback/index.js +371 -0
  104. package/build/angular/components/touchable-native-feedback/index.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 +153 -0
  107. package/build/angular/components/virtualized-list/directives.js.map +1 -0
  108. package/build/angular/components/virtualized-list/index.d.ts +253 -0
  109. package/build/angular/components/virtualized-list/index.js +1031 -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 +76 -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 +720 -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/index.d.ts +22 -0
  121. package/build/angular/create-portal/index.js +74 -0
  122. package/build/angular/create-portal/index.js.map +1 -0
  123. package/build/angular/create-tunnel/index.d.ts +34 -0
  124. package/build/angular/create-tunnel/index.js +126 -0
  125. package/build/angular/create-tunnel/index.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 +22 -0
  133. package/build/angular/index.js +46 -0
  134. package/build/angular/index.js.map +1 -0
  135. package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +14 -0
  136. package/build/angular/modules/animated/animated-leaf-binder/index.js +91 -0
  137. package/build/angular/modules/animated/animated-leaf-binder/index.js.map +1 -0
  138. package/build/angular/modules/animated/animated-leaf-binder.d.ts +14 -0
  139. package/build/angular/modules/animated/animated-leaf-binder.js +91 -0
  140. package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -0
  141. package/build/angular/modules/animated/create-animated-component.d.ts +59 -0
  142. package/build/angular/modules/animated/create-animated-component.js +364 -0
  143. package/build/angular/modules/animated/create-animated-component.js.map +1 -0
  144. package/build/angular/modules/animated/index.d.ts +51 -0
  145. package/build/angular/modules/animated/index.js +59 -0
  146. package/build/angular/modules/animated/index.js.map +1 -0
  147. package/build/angular/modules/app-registry/index.d.ts +7 -0
  148. package/build/angular/modules/app-registry/index.js +23 -0
  149. package/build/angular/modules/app-registry/index.js.map +1 -0
  150. package/build/angular/modules/status-bar/index.d.ts +20 -0
  151. package/build/angular/modules/status-bar/index.js +59 -0
  152. package/build/angular/modules/status-bar/index.js.map +1 -0
  153. package/build/angular/primitives/index.d.ts +63 -0
  154. package/build/angular/primitives/index.js +189 -0
  155. package/build/angular/primitives/index.js.map +1 -0
  156. package/build/angular/primitives/shared.d.ts +88 -0
  157. package/build/angular/primitives/shared.js +171 -0
  158. package/build/angular/primitives/shared.js.map +1 -0
  159. package/build/angular/render/index.d.ts +11 -0
  160. package/build/angular/render/index.js +171 -0
  161. package/build/angular/render/index.js.map +1 -0
  162. package/build/angular/renderer/index.d.ts +37 -0
  163. package/build/angular/renderer/index.js +301 -0
  164. package/build/angular/renderer/index.js.map +1 -0
  165. package/build/angular/services/color-scheme.service.d.ts +16 -0
  166. package/build/angular/services/color-scheme.service.js +31 -0
  167. package/build/angular/services/color-scheme.service.js.map +1 -0
  168. package/build/angular/services/index.d.ts +2 -0
  169. package/build/angular/services/index.js +3 -0
  170. package/build/angular/services/index.js.map +1 -0
  171. package/build/angular/services/window-dimensions.service.d.ts +16 -0
  172. package/build/angular/services/window-dimensions.service.js +42 -0
  173. package/build/angular/services/window-dimensions.service.js.map +1 -0
  174. package/metro-config.cjs +48 -0
  175. package/metro-css-parser.cjs +13 -0
  176. package/package.json +80 -0
  177. package/src/__tests__/responder-change-detection.test.ts +102 -0
  178. package/src/__tests__/responder-nested-cd.test.ts +113 -0
  179. package/src/anchor-host-registry.ts +88 -0
  180. package/src/angular-gaps.test.ts +142 -0
  181. package/src/angular-imports.test.ts +100 -0
  182. package/src/bootstrap.ts +26 -0
  183. package/src/components/activity-indicator/activity-indicator-class-style.test.ts +44 -0
  184. package/src/components/activity-indicator/activity-indicator.test.ts +131 -0
  185. package/src/components/activity-indicator/index.android.ts +16 -0
  186. package/src/components/activity-indicator/index.ios.ts +17 -0
  187. package/src/components/activity-indicator/index.ts +4 -0
  188. package/src/components/activity-indicator/shared.ts +206 -0
  189. package/src/components/button.ts +193 -0
  190. package/src/components/flat-list/column-wrapper-style-class.test.ts +116 -0
  191. package/src/components/flat-list/flat-list-array-style.test.ts +131 -0
  192. package/src/components/flat-list/flat-list-class-style.test.ts +74 -0
  193. package/src/components/flat-list/flat-list-scroll-containment.test.ts +107 -0
  194. package/src/components/flat-list/flat-list.test.ts +99 -0
  195. package/src/components/flat-list/index.ts +574 -0
  196. package/src/components/image/image-class-style.test.ts +46 -0
  197. package/src/components/image/index.android.ts +48 -0
  198. package/src/components/image/index.ios.ts +48 -0
  199. package/src/components/image/index.ts +3 -0
  200. package/src/components/image/shared.ts +479 -0
  201. package/src/components/image-background-class-style.test.ts +48 -0
  202. package/src/components/image-background-style-class.test.ts +79 -0
  203. package/src/components/image-background.ts +270 -0
  204. package/src/components/input-accessory-view/index.ts +185 -0
  205. package/src/components/input-accessory-view/input-accessory-view.test.ts +43 -0
  206. package/src/components/keyboard-avoiding-view/index.ts +303 -0
  207. package/src/components/keyboard-avoiding-view/keyboard-avoiding-view.test.ts +54 -0
  208. package/src/components/modal/index.ts +299 -0
  209. package/src/components/modal/modal.test.ts +43 -0
  210. package/src/components/pressable/index.ts +373 -0
  211. package/src/components/pressable/pressable.test.ts +85 -0
  212. package/src/components/refresh-control/index.ts +284 -0
  213. package/src/components/refresh-control/refresh-control.test.ts +41 -0
  214. package/src/components/safe-area-view/index.ts +177 -0
  215. package/src/components/safe-area-view/safe-area-view.test.ts +43 -0
  216. package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +123 -0
  217. package/src/components/scroll-view/content-container-style-class.test.ts +75 -0
  218. package/src/components/scroll-view/index.android.ts +190 -0
  219. package/src/components/scroll-view/index.ios.ts +79 -0
  220. package/src/components/scroll-view/index.ts +7 -0
  221. package/src/components/scroll-view/projection.ts +414 -0
  222. package/src/components/scroll-view/scroll-view-class-style.test.ts +134 -0
  223. package/src/components/scroll-view/scroll-view-projection.test.ts +194 -0
  224. package/src/components/scroll-view/shared.ts +844 -0
  225. package/src/components/scroll-view/sticky-header.ts +255 -0
  226. package/src/components/section-list/index.ts +445 -0
  227. package/src/components/switch/android-switch-class.test.ts +42 -0
  228. package/src/components/switch/index.android.ts +61 -0
  229. package/src/components/switch/index.ios.ts +60 -0
  230. package/src/components/switch/index.ts +4 -0
  231. package/src/components/switch/shared.ts +197 -0
  232. package/src/components/switch/switch.test.ts +92 -0
  233. package/src/components/text-input-class-style.test.ts +54 -0
  234. package/src/components/text-input.ts +531 -0
  235. package/src/components/touchable/index.ts +629 -0
  236. package/src/components/touchable/touchable.test.ts +102 -0
  237. package/src/components/touchable-native-feedback/index.ts +254 -0
  238. package/src/components/touchable-native-feedback/touchable-native-feedback.test.ts +49 -0
  239. package/src/components/virtualized-list/directives.ts +168 -0
  240. package/src/components/virtualized-list/index.ts +992 -0
  241. package/src/components/virtualized-list/virtualized-list-class-style.test.ts +76 -0
  242. package/src/components/virtualized-section-list/directives.ts +90 -0
  243. package/src/components/virtualized-section-list/index.ts +601 -0
  244. package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +60 -0
  245. package/src/components.ts +149 -0
  246. package/src/create-portal/create-portal.test.ts +90 -0
  247. package/src/create-portal/index.ts +74 -0
  248. package/src/create-tunnel/create-tunnel.test.ts +120 -0
  249. package/src/create-tunnel/index.ts +136 -0
  250. package/src/descriptor-to-angular/descriptor-outlet.test.ts +94 -0
  251. package/src/descriptor-to-angular/index.ts +190 -0
  252. package/src/host-instance/host-instance.test.ts +49 -0
  253. package/src/host-instance/index.ts +36 -0
  254. package/src/index.ts +261 -0
  255. package/src/modules/animated/animated-leaf-binder/animated-leaf-binder.test.ts +116 -0
  256. package/src/modules/animated/animated-leaf-binder/index.ts +101 -0
  257. package/src/modules/animated/animated-leaf-binder.test.ts +116 -0
  258. package/src/modules/animated/animated-leaf-binder.ts +101 -0
  259. package/src/modules/animated/animated-scroll-view.test.ts +102 -0
  260. package/src/modules/animated/create-animated-component.ts +365 -0
  261. package/src/modules/animated/index.ts +105 -0
  262. package/src/modules/app-registry/app-registry.test.ts +106 -0
  263. package/src/modules/app-registry/index.ts +60 -0
  264. package/src/modules/status-bar/index.ts +57 -0
  265. package/src/modules/status-bar/status-bar.test.ts +50 -0
  266. package/src/primitives/index.ts +119 -0
  267. package/src/primitives/shared.ts +190 -0
  268. package/src/render/index.ts +235 -0
  269. package/src/render/render.test.ts +147 -0
  270. package/src/renderer/index.ts +347 -0
  271. package/src/renderer/renderer.test.ts +203 -0
  272. package/src/services/color-scheme.service.ts +27 -0
  273. package/src/services/index.ts +2 -0
  274. package/src/services/services.test.ts +163 -0
  275. package/src/services/window-dimensions.service.ts +41 -0
  276. package/tsconfig.angular.base.json +30 -0
@@ -0,0 +1,75 @@
1
+ // Regression guard: contentContainerStyle previously accepted ONLY a JS style object/array. It
2
+ // now also resolves a class-name string through the shared style registry, same as `class`/
3
+ // `style` (see shared.ts's resolvedContentContainerStyle getter). Mirrors the Vue twin
4
+ // (content-container-style-class.test.ts) and scroll-view-class-style.test.ts's style-for-this-
5
+ // exact-scenario shape.
6
+ import '@angular/compiler';
7
+ import { Component } from '@angular/core';
8
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
9
+ import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
10
+ import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
11
+
12
+ import { mount, unmount } from '../../render';
13
+ import { ScrollView } from './index.ios';
14
+
15
+ const ROOT_TAG = 952;
16
+ const fabric = installFabric();
17
+ const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
18
+
19
+ function committedContentView(): IFakeNode {
20
+ const node = fabric.find(n => n.viewName === 'RCTScrollContentView');
21
+ expect(node, 'the scroll content view was committed').toBeDefined();
22
+ if (node === undefined) throw new Error('unreachable: content view missing');
23
+ return node;
24
+ }
25
+
26
+ @Component({
27
+ selector: 'symbiote-scroll-content-style-class-host',
28
+ standalone: true,
29
+ imports: [ScrollView],
30
+ template: `
31
+ <ScrollView [contentContainerStyle]="'padded'">
32
+ <symbiote-text>content</symbiote-text>
33
+ </ScrollView>
34
+ `,
35
+ })
36
+ class ScrollViewContentStyleClassHost {}
37
+
38
+ @Component({
39
+ selector: 'symbiote-scroll-content-style-object-host',
40
+ standalone: true,
41
+ imports: [ScrollView],
42
+ template: `
43
+ <ScrollView [contentContainerStyle]="{ padding: 12 }">
44
+ <symbiote-text>content</symbiote-text>
45
+ </ScrollView>
46
+ `,
47
+ })
48
+ class ScrollViewContentStyleObjectHost {}
49
+
50
+ beforeEach(() => {
51
+ fabric.reset();
52
+ registerStyles({ padded: { padding: 20 } });
53
+ });
54
+ afterEach(() => {
55
+ unmount(ROOT_TAG);
56
+ clearGlobalStyles();
57
+ });
58
+
59
+ describe('ScrollView contentContainerStyle class-name support', () => {
60
+ it('resolves a class-name string onto the content view, not the outer scroll view', async () => {
61
+ mount(ROOT_TAG, ScrollViewContentStyleClassHost);
62
+ await tick();
63
+
64
+ expect(committedContentView().props.padding).toBe(20);
65
+ const scrollHost = fabric.find(n => n.viewName === 'RCTScrollView');
66
+ expect(scrollHost?.props.padding).toBeUndefined();
67
+ });
68
+
69
+ it('still accepts an ordinary style object unchanged', async () => {
70
+ mount(ROOT_TAG, ScrollViewContentStyleObjectHost);
71
+ await tick();
72
+
73
+ expect(committedContentView().props.padding).toBe(12);
74
+ });
75
+ });
@@ -0,0 +1,190 @@
1
+ // ScrollView on Android. An Android ScrollView accepts only ONE child, so a
2
+ // RefreshControl can't be a sibling of the content the way iOS allows ("addViewAt: failed to insert
3
+ // view ... at index 1"). RN solves this by having the RefreshControl (AndroidSwipeRefreshLayout)
4
+ // WRAP the scroll view (cloneElement(refreshControl, {style}, scrollView)). Angular mirrors that
5
+ // shape by querying a projected <RefreshControl>, re-rendering its native prop surface as the outer
6
+ // wrapper, and projecting every non-RefreshControl child into the inner scroll content.
7
+ //
8
+ // Android genuinely needs a different Fabric view per
9
+ // axis for BOTH the outer scroll container (RCTScrollView is vertical-only; AndroidHorizontalScrollView
10
+ // is a dedicated ViewManager) AND the inner content view (vertical content is a plain Android View,
11
+ // horizontal content is AndroidHorizontalScrollContentView, which carries its own ShadowNode::
12
+ // layout() override). So the content tag differs by axis — but Angular only reliably projects into a
13
+ // component's LAST-declared <ng-content> when 2+ DISTINCT declarations exist (angular/angular#22972),
14
+ // which an earlier 4-call-site template (one <ng-content> per axis x refresh-control branch) tripped.
15
+ // Fix: exactly ONE `<ng-content>` textually, inside a single top-level `<ng-template>`, re-instantiated
16
+ // into whichever branch is active via a local template-outlet directive (SymbioteTemplateOutletDirective
17
+ // below, `@angular/common`-free per this adapter's dependency policy — see package.json). Only one
18
+ // branch is ever live at a time, and the `<ng-content>` DECLARATION COUNT stays at one, so the "last
19
+ // one wins" limitation never triggers. Covered by `android-scroll-view-axis-projection.test.ts` (all
20
+ // four axis x refresh combos, plus a runtime axis switch) and `flat-list-scroll-containment.test.ts`.
21
+ //
22
+ // Metro picks this on an Android host; no Platform.OS read.
23
+
24
+ import {
25
+ CUSTOM_ELEMENTS_SCHEMA,
26
+ ChangeDetectionStrategy,
27
+ Component,
28
+ Directive,
29
+ ElementRef,
30
+ Input,
31
+ TemplateRef,
32
+ ViewContainerRef,
33
+ inject,
34
+ type EmbeddedViewRef,
35
+ type OnChanges,
36
+ type OnDestroy,
37
+ } from '@angular/core';
38
+ import type { IStyleProp, IViewStyle } from '@symbiote-native/engine';
39
+ import {
40
+ anchorHostStyle,
41
+ HorizontalScrollView,
42
+ HorizontalScrollContentView,
43
+ RefreshControlHost,
44
+ ScrollContentView,
45
+ ScrollViewHost,
46
+ SymbioteHostPropsDirective,
47
+ } from '../../primitives';
48
+ import { ScrollViewBase, ScrollViewProjectionDirective, SCROLL_VIEW_INPUTS } from './shared';
49
+ export type { IAngularScrollViewProps, IScrollViewHandle } from './shared';
50
+
51
+ // Minimal local twin of `@angular/common`'s NgTemplateOutlet (this adapter deliberately has no
52
+ // @angular/common dependency — see package.json): re-instantiates the SAME TemplateRef into
53
+ // whichever structural branch below is currently active, so the component's compiled template
54
+ // keeps exactly ONE <ng-content> declaration regardless of how many places reference it.
55
+ @Directive({
56
+ selector: '[symbioteTemplateOutlet]',
57
+ standalone: true,
58
+ })
59
+ export class SymbioteTemplateOutletDirective implements OnChanges, OnDestroy {
60
+ @Input() symbioteTemplateOutlet: TemplateRef<unknown> | null = null;
61
+
62
+ private readonly viewContainerRef = inject(ViewContainerRef);
63
+ private viewRef: EmbeddedViewRef<unknown> | undefined;
64
+
65
+ ngOnChanges(): void {
66
+ if (this.viewRef !== undefined) {
67
+ this.viewRef.destroy();
68
+ this.viewRef = undefined;
69
+ }
70
+ if (this.symbioteTemplateOutlet !== null) {
71
+ this.viewRef = this.viewContainerRef.createEmbeddedView(this.symbioteTemplateOutlet);
72
+ }
73
+ }
74
+
75
+ ngOnDestroy(): void {
76
+ this.viewRef?.destroy();
77
+ }
78
+ }
79
+
80
+ // anchorHostStyle returns `unknown` (the anchor's already-resolved style, any shape); narrowed
81
+ // here (mirrors the identical `asStyle` in image/shared.ts) rather than `as`-cast to satisfy
82
+ // `IStyleProp<IViewStyle>`'s stricter array-element union.
83
+ function asStyle(value: unknown): IStyleProp<IViewStyle> | undefined {
84
+ return typeof value === 'object' && value !== null ? value : undefined;
85
+ }
86
+
87
+ // The symbiote-* host elements are imported as standalone components; the props directive is
88
+ // applied as a template directive and the projection directive manages sticky-header wrapping.
89
+ @Component({
90
+ selector: 'ScrollView',
91
+ standalone: true,
92
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
93
+ imports: [
94
+ ScrollViewHost,
95
+ ScrollContentView,
96
+ HorizontalScrollView,
97
+ HorizontalScrollContentView,
98
+ RefreshControlHost,
99
+ SymbioteHostPropsDirective,
100
+ ScrollViewProjectionDirective,
101
+ SymbioteTemplateOutletDirective,
102
+ ],
103
+ changeDetection: ChangeDetectionStrategy.OnPush,
104
+ inputs: SCROLL_VIEW_INPUTS,
105
+ template: `
106
+ <ng-template #sharedContent>
107
+ <ng-content></ng-content>
108
+ </ng-template>
109
+ @if (isHorizontal) {
110
+ @if (hasProjectedRefreshControl) {
111
+ <symbiote-refresh-control
112
+ #refreshHost="symbioteHost"
113
+ [symbioteHostProps]="androidRefreshControlProps"
114
+ (refresh)="handleProjectedRefresh(refreshHost.node)"
115
+ >
116
+ <symbiote-horizontal-scroll-view
117
+ #host="symbioteHost"
118
+ [symbioteHostProps]="androidWrappedScrollProps"
119
+ >
120
+ <symbiote-horizontal-scroll-content
121
+ [symbioteHostProps]="contentProps"
122
+ [symbioteScrollViewProjection]="projectionController"
123
+ >
124
+ <ng-container [symbioteTemplateOutlet]="sharedContent"></ng-container>
125
+ </symbiote-horizontal-scroll-content>
126
+ </symbiote-horizontal-scroll-view>
127
+ </symbiote-refresh-control>
128
+ } @else {
129
+ <symbiote-horizontal-scroll-view #host="symbioteHost" [symbioteHostProps]="scrollProps">
130
+ <symbiote-horizontal-scroll-content
131
+ [symbioteHostProps]="contentProps"
132
+ [symbioteScrollViewProjection]="projectionController"
133
+ >
134
+ <ng-container [symbioteTemplateOutlet]="sharedContent"></ng-container>
135
+ </symbiote-horizontal-scroll-content>
136
+ </symbiote-horizontal-scroll-view>
137
+ }
138
+ } @else {
139
+ @if (hasProjectedRefreshControl) {
140
+ <symbiote-refresh-control
141
+ #refreshHost="symbioteHost"
142
+ [symbioteHostProps]="androidRefreshControlProps"
143
+ (refresh)="handleProjectedRefresh(refreshHost.node)"
144
+ >
145
+ <symbiote-scroll-view
146
+ #host="symbioteHost"
147
+ [symbioteHostProps]="androidWrappedScrollProps"
148
+ >
149
+ <symbiote-scroll-content
150
+ [symbioteHostProps]="contentProps"
151
+ [symbioteScrollViewProjection]="projectionController"
152
+ >
153
+ <ng-container [symbioteTemplateOutlet]="sharedContent"></ng-container>
154
+ </symbiote-scroll-content>
155
+ </symbiote-scroll-view>
156
+ </symbiote-refresh-control>
157
+ } @else {
158
+ <symbiote-scroll-view #host="symbioteHost" [symbioteHostProps]="scrollProps">
159
+ <symbiote-scroll-content
160
+ [symbioteHostProps]="contentProps"
161
+ [symbioteScrollViewProjection]="projectionController"
162
+ >
163
+ <ng-container [symbioteTemplateOutlet]="sharedContent"></ng-container>
164
+ </symbiote-scroll-content>
165
+ </symbiote-scroll-view>
166
+ }
167
+ }
168
+ `,
169
+ })
170
+ export class ScrollView extends ScrollViewBase {
171
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
172
+ // onto (see anchorHostStyle's doc comment) — NOT `#host` in the template above, which targets
173
+ // the real inner `symbiote-scroll-view`/`symbiote-horizontal-scroll-view` one level down.
174
+ private readonly elementRef = inject(ElementRef);
175
+
176
+ override get scrollProps(): Record<string, unknown> {
177
+ const props = super.scrollProps;
178
+ return { ...props, style: [anchorHostStyle(this.elementRef), props.style] };
179
+ }
180
+
181
+ // Feeds the anchor's class-derived style INTO the base class's own `splitLayoutProps` call
182
+ // (shared.ts's `androidWrappedScrollProps`/`androidRefreshControlProps`), not tacked on after —
183
+ // a class-only layout style (flex/height/gap/…) must be visible to the split itself, or the
184
+ // Android outer refresh-control wrapper never receives its share and collapses to zero size
185
+ // (a real device bug: the whole ScrollView renders nothing). See layoutSplitStyle's doc comment
186
+ // in shared.ts and the identical fix already shipped in the Vue adapter's scroll-view.
187
+ protected override get layoutSplitStyle(): IStyleProp<IViewStyle> {
188
+ return [asStyle(anchorHostStyle(this.elementRef)), this.style];
189
+ }
190
+ }
@@ -0,0 +1,79 @@
1
+ // ScrollView on iOS. The RefreshControl is a CHILD of the scroll view, rendered
2
+ // as a SIBLING BEFORE the content container (RN ScrollView.js: {refreshControl}{contentContainer}).
3
+ // The developer composes it as a projected child of <ScrollView>; this template selects it into the
4
+ // sibling slot before the scroll-content. Also the base (index.ts re-exports it) for headless /
5
+ // web. Metro picks this on an iOS host; no Platform.OS read. Mirrors the React/Vue iOS binding.
6
+ //
7
+ // UNLIKE Android, iOS has only ONE host intrinsic pair regardless of axis (both horizontal and
8
+ // vertical map to RCTScrollView / RCTScrollContentView) — `shared.ts`'s `scrollProps` already
9
+ // forwards `horizontal` as a plain prop ("iOS needs `horizontal` to flip RCTScrollView's axis").
10
+ // So, unlike `index.android.ts`, this file does NOT branch the host tag on `isHorizontal` — doing
11
+ // so anyway (mirroring Android's shape for symmetry) used to declare `<ng-content>` once per
12
+ // `@if`/`@else` branch, which trips a documented Angular limitation: content projected into the
13
+ // FIRST (`@if`) branch never receives Angular's native catch-up placement, only the SECOND
14
+ // (`@else`) branch does (angular/angular#53310, #54840 — Angular's own docs: "You should not
15
+ // conditionally include <ng-content> with @if, @for, or @switch"). Symptom here was axis-specific:
16
+ // a horizontal FlatList's cells landed outside the ScrollView entirely. Fix: single unconditional
17
+ // `<ng-content>` call site.
18
+
19
+ import {
20
+ CUSTOM_ELEMENTS_SCHEMA,
21
+ ChangeDetectionStrategy,
22
+ Component,
23
+ ElementRef,
24
+ inject,
25
+ } from '@angular/core';
26
+ import {
27
+ anchorHostStyle,
28
+ RefreshControlHost,
29
+ ScrollContentView,
30
+ ScrollViewHost,
31
+ SymbioteHostPropsDirective,
32
+ } from '../../primitives';
33
+ import { ScrollViewBase, ScrollViewProjectionDirective, SCROLL_VIEW_INPUTS } from './shared';
34
+ export type { IAngularScrollViewProps, IScrollViewHandle } from './shared';
35
+
36
+ // The symbiote-* host elements are imported as standalone components; the props directive is
37
+ // applied as a template directive and the projection directive manages sticky-header wrapping.
38
+ @Component({
39
+ selector: 'ScrollView',
40
+ standalone: true,
41
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
42
+ imports: [
43
+ ScrollViewHost,
44
+ ScrollContentView,
45
+ RefreshControlHost,
46
+ SymbioteHostPropsDirective,
47
+ ScrollViewProjectionDirective,
48
+ ],
49
+ changeDetection: ChangeDetectionStrategy.OnPush,
50
+ inputs: SCROLL_VIEW_INPUTS,
51
+ template: `
52
+ <symbiote-scroll-view #host="symbioteHost" [symbioteHostProps]="scrollProps">
53
+ @if (hasProjectedRefreshControl) {
54
+ <symbiote-refresh-control
55
+ #refreshHost="symbioteHost"
56
+ [symbioteHostProps]="iosRefreshControlProps"
57
+ (refresh)="handleProjectedRefresh(refreshHost.node)"
58
+ ></symbiote-refresh-control>
59
+ }
60
+ <symbiote-scroll-content
61
+ [symbioteHostProps]="contentProps"
62
+ [symbioteScrollViewProjection]="projectionController"
63
+ >
64
+ <ng-content></ng-content>
65
+ </symbiote-scroll-content>
66
+ </symbiote-scroll-view>
67
+ `,
68
+ })
69
+ export class ScrollView extends ScrollViewBase {
70
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
71
+ // onto (see anchorHostStyle's doc comment) — NOT `#host` in the template above, which targets
72
+ // the real inner `symbiote-scroll-view` one level down.
73
+ private readonly elementRef = inject(ElementRef);
74
+
75
+ override get scrollProps(): Record<string, unknown> {
76
+ const props = super.scrollProps;
77
+ return { ...props, style: [anchorHostStyle(this.elementRef), props.style] };
78
+ }
79
+ }
@@ -0,0 +1,7 @@
1
+ // Base / default ScrollView. Re-exports the iOS build. Metro overrides this with index.ios.ts /
2
+ // index.android.ts on a real host; under tsx / tsc / web the host resolves here. Filename is the
3
+ // selector, no Platform.OS read.
4
+
5
+ export * from './index.ios';
6
+ export { ScrollViewStickyHeader } from './sticky-header';
7
+ export type { IStickyHeaderComponentType } from './sticky-header';