@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,445 @@
1
+ // SectionList, the Angular public list-of-sections component. A thin convenience surface over
2
+ // VirtualizedSectionList, mirroring RN's layering (SectionList -> VirtualizedSectionList ->
3
+ // VirtualizedList). All section-flattening / windowing / sticky-header / imperative-scroll logic
4
+ // lives below; this layer re-exposes the same surface under the SectionList name and re-exposes the
5
+ // handle (delegating to the inner VirtualizedSectionList). The Angular twin of
6
+ // adapters/vue/src/components/section-list/index.ts.
7
+ //
8
+ // As in the Vue twin, SectionList is a PURE FORWARDER: its public prop surface IS
9
+ // VirtualizedSectionList's (RN layers them one-for-one), and the SectionList-flavour defaults
10
+ // (stickySectionHeadersEnabled per Platform.OS, the section entry keyExtractor) already live INSIDE
11
+ // VirtualizedSectionList — it applies `stickySectionHeadersEnabled ?? (Platform.OS === 'ios')` in
12
+ // its ngDoCheck and routes keyExtractor through the shared sectionEntryKey. So SectionList forwards
13
+ // each prop unchanged (undefined included) and the default lands one layer down; double-applying
14
+ // here would fight VSL. Vue spreads $attrs to do this in one line — Angular has no attrs-spread, so
15
+ // every prop is forwarded as an explicit @Input binding, except the seven list-lifecycle events
16
+ // (endReached/startReached/refresh/accessibilityAction/accessibilityTap/magicTap/
17
+ // accessibilityEscape), which are real @Output() EventEmitters re-emitted via a listener binding.
18
+ //
19
+ // TEMPLATE FORWARDING — RE-STAMP, the same pattern FlatList's single-column branch was fixed to use
20
+ // (see flat-list/index.ts). A bare `<ng-content></ng-content>` passthrough does NOT let
21
+ // VirtualizedSectionList's own @ContentChild resolve directives across the SECOND projection hop
22
+ // (SectionList's own `<ng-content>` re-projecting content that was actually authored on `<SectionList>`
23
+ // by the app, one level further out) — Angular's content queries resolve against what was projected
24
+ // directly onto the querying component's OWN tag, not transitively through a nested `<ng-content>`
25
+ // relay. So SectionList captures the app's `<ng-template vSectionItem>` / vSectionHeader /
26
+ // vSectionFooter / vSectionSeparator / vListHeader / vListFooter / vListEmpty / vListSeparator with
27
+ // its OWN @ContentChild (a single, direct projection hop — this always resolves) and re-authors
28
+ // equivalent `<ng-template>`s on `<VirtualizedSectionList>`, each forwarding the captured
29
+ // templateRef + context through VListOutletDirective, exactly mirroring how VirtualizedSectionList
30
+ // itself re-stamps its own captured directives onto its inner VirtualizedList.
31
+
32
+ import {
33
+ ChangeDetectionStrategy,
34
+ Component,
35
+ ContentChild,
36
+ ElementRef,
37
+ EventEmitter,
38
+ Input,
39
+ Output,
40
+ ViewChild,
41
+ inject,
42
+ } from '@angular/core';
43
+ import type {
44
+ IAccessibilityProps,
45
+ IAccessibilityStateValue,
46
+ IAriaProps,
47
+ IScrollViewHandle,
48
+ ISection,
49
+ IVirtualizedSectionListHandle,
50
+ } from '@symbiote-native/components';
51
+ import {
52
+ type IStyleProp,
53
+ type ISymbioteEvent,
54
+ type ISymbioteNode,
55
+ type IViewStyle,
56
+ } from '@symbiote-native/engine';
57
+ import {
58
+ VListEmptyDirective,
59
+ VListFooterDirective,
60
+ VListHeaderDirective,
61
+ VListSeparatorDirective,
62
+ type IVListSeparatorContext,
63
+ } from '../virtualized-list';
64
+ import { VListOutletDirective } from '../virtualized-list/directives';
65
+ import { stableAnchorStyle } from '../../primitives';
66
+ import {
67
+ VSectionFooterDirective,
68
+ VSectionHeaderDirective,
69
+ VSectionItemDirective,
70
+ VSectionSeparatorDirective,
71
+ VirtualizedSectionList,
72
+ type IVirtualizedSectionListProps,
73
+ } from '../virtualized-section-list';
74
+
75
+ // Angular cannot preserve VListSeparatorDirective<ItemT>'s type parameter across this re-stamp
76
+ // reuse (the directive is matched structurally in SectionList's own template with no explicit type
77
+ // argument to pin ItemT to, so `let-leadingItem`/`let-trailingItem` arrive typed `unknown`) — even
78
+ // though VirtualizedSectionList's own unwrap always supplies a real ItemT value. The narrowest
79
+ // possible I/O-boundary cast for that gap; not a general-purpose unknown-to-T narrowing (mirrors
80
+ // flat-list/index.ts's identical asItem helper).
81
+ function asItem<ItemT>(value: unknown): ItemT | undefined {
82
+ return value as ItemT | undefined;
83
+ }
84
+
85
+ // Re-export the shared section type + the imperative handle so app code (and any later layer) imports
86
+ // them from SectionList, mirroring how the Vue twin re-exposes ISection + ISectionListHandle.
87
+ export type { ISection } from '@symbiote-native/components';
88
+ export type ISectionListHandle = IVirtualizedSectionListHandle;
89
+
90
+ // Re-export the section authoring directives + the list-level slot directives so app code importing
91
+ // from '@symbiote-native/angular' gets the full `<ng-template vSection*>` / `vList*` authoring surface
92
+ // alongside SectionList (mirrors how virtualized-section-list/index re-exports the section ones and
93
+ // FlatList re-exports the list-level ones).
94
+ export {
95
+ VSectionFooterDirective,
96
+ VSectionHeaderDirective,
97
+ VSectionItemDirective,
98
+ VSectionSeparatorDirective,
99
+ } from '../virtualized-section-list';
100
+ export {
101
+ VListEmptyDirective,
102
+ VListFooterDirective,
103
+ VListHeaderDirective,
104
+ VListSeparatorDirective,
105
+ } from '../virtualized-list';
106
+
107
+ // SectionList's public surface is exactly VirtualizedSectionList's (RN layers them one-for-one), so
108
+ // the prop type is shared verbatim — no SectionList-only field exists.
109
+ export type ISectionListProps<ItemT> = IVirtualizedSectionListProps<ItemT>;
110
+
111
+ // What the SectionList component itself takes as plain @Input()s: the full surface minus the
112
+ // list-lifecycle events, which it exposes as real @Output() EventEmitters instead (see the class
113
+ // below) — mirrors IAngularPressableInputs in pressable/index.ts and VirtualizedSectionList's own
114
+ // IVirtualizedSectionListInputs.
115
+ export type ISectionListInputs<ItemT> = Omit<
116
+ ISectionListProps<ItemT>,
117
+ | 'onEndReached'
118
+ | 'onStartReached'
119
+ | 'onRefresh'
120
+ | 'onAccessibilityAction'
121
+ | 'onAccessibilityTap'
122
+ | 'onMagicTap'
123
+ | 'onAccessibilityEscape'
124
+ >;
125
+
126
+ @Component({
127
+ selector: 'SectionList',
128
+ standalone: true,
129
+ imports: [
130
+ VirtualizedSectionList,
131
+ VSectionItemDirective,
132
+ VSectionHeaderDirective,
133
+ VSectionFooterDirective,
134
+ VSectionSeparatorDirective,
135
+ VListHeaderDirective,
136
+ VListFooterDirective,
137
+ VListEmptyDirective,
138
+ VListSeparatorDirective,
139
+ VListOutletDirective,
140
+ ],
141
+ changeDetection: ChangeDetectionStrategy.OnPush,
142
+ template: `
143
+ <VirtualizedSectionList
144
+ [sections]="sections"
145
+ [keyExtractor]="keyExtractor"
146
+ [stickySectionHeadersEnabled]="stickySectionHeadersEnabled"
147
+ [extraData]="extraData"
148
+ (endReached)="endReached.emit($event)"
149
+ [onEndReachedThreshold]="onEndReachedThreshold"
150
+ (startReached)="startReached.emit($event)"
151
+ [onStartReachedThreshold]="onStartReachedThreshold"
152
+ (refresh)="refresh.emit()"
153
+ [refreshRequested]="refresh.observed"
154
+ [refreshing]="refreshing"
155
+ [progressViewOffset]="progressViewOffset"
156
+ [initialNumToRender]="initialNumToRender"
157
+ [initialScrollIndex]="initialScrollIndex"
158
+ [maxToRenderPerBatch]="maxToRenderPerBatch"
159
+ [updateCellsBatchingPeriod]="updateCellsBatchingPeriod"
160
+ [windowSize]="windowSize"
161
+ [inverted]="inverted"
162
+ [maintainVisibleContentPosition]="maintainVisibleContentPosition"
163
+ [onScroll]="onScroll"
164
+ [onScrollBeginDrag]="onScrollBeginDrag"
165
+ [onScrollEndDrag]="onScrollEndDrag"
166
+ [onMomentumScrollBegin]="onMomentumScrollBegin"
167
+ [onMomentumScrollEnd]="onMomentumScrollEnd"
168
+ [scrollEventThrottle]="scrollEventThrottle"
169
+ [keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
170
+ [keyboardDismissMode]="keyboardDismissMode"
171
+ [style]="resolvedStyle"
172
+ [contentContainerStyle]="contentContainerStyle"
173
+ [testID]="testID"
174
+ [nativeID]="nativeID"
175
+ [accessible]="accessible"
176
+ [accessibilityLabel]="accessibilityLabel"
177
+ [accessibilityHint]="accessibilityHint"
178
+ [accessibilityRole]="accessibilityRole"
179
+ [accessibilityState]="accessibilityState"
180
+ [accessibilityValue]="accessibilityValue"
181
+ [accessibilityActions]="accessibilityActions"
182
+ [accessibilityLabelledBy]="accessibilityLabelledBy"
183
+ [importantForAccessibility]="importantForAccessibility"
184
+ [accessibilityLiveRegion]="accessibilityLiveRegion"
185
+ [screenReaderFocusable]="screenReaderFocusable"
186
+ [accessibilityViewIsModal]="accessibilityViewIsModal"
187
+ [accessibilityElementsHidden]="accessibilityElementsHidden"
188
+ [accessibilityIgnoresInvertColors]="accessibilityIgnoresInvertColors"
189
+ [accessibilityLanguage]="accessibilityLanguage"
190
+ [accessibilityRespondsToUserInteraction]="accessibilityRespondsToUserInteraction"
191
+ [accessibilityShowsLargeContentViewer]="accessibilityShowsLargeContentViewer"
192
+ [accessibilityLargeContentTitle]="accessibilityLargeContentTitle"
193
+ (accessibilityAction)="accessibilityAction.emit($event)"
194
+ (accessibilityTap)="accessibilityTap.emit($event)"
195
+ (magicTap)="magicTap.emit($event)"
196
+ (accessibilityEscape)="accessibilityEscape.emit($event)"
197
+ [role]="role"
198
+ [ariaLabel]="ariaLabel"
199
+ [ariaLabelledBy]="ariaLabelledBy"
200
+ [ariaLive]="ariaLive"
201
+ [ariaHidden]="ariaHidden"
202
+ [ariaBusy]="ariaBusy"
203
+ [ariaChecked]="ariaChecked"
204
+ [ariaDisabled]="ariaDisabled"
205
+ [ariaExpanded]="ariaExpanded"
206
+ [ariaSelected]="ariaSelected"
207
+ [ariaModal]="ariaModal"
208
+ [ariaValueMax]="ariaValueMax"
209
+ [ariaValueMin]="ariaValueMin"
210
+ [ariaValueNow]="ariaValueNow"
211
+ [ariaValueText]="ariaValueText"
212
+ >
213
+ <ng-template
214
+ vSectionItem
215
+ let-item
216
+ let-index="index"
217
+ let-section="section"
218
+ let-separators="separators"
219
+ >
220
+ <ng-container
221
+ [vListOutlet]="sectionItemDir?.templateRef"
222
+ [vListOutletContext]="{ $implicit: item, item, index, section, separators }"
223
+ ></ng-container>
224
+ </ng-template>
225
+ @if (sectionHeaderDir !== undefined) {
226
+ <ng-template vSectionHeader let-section>
227
+ <ng-container
228
+ [vListOutlet]="sectionHeaderDir.templateRef"
229
+ [vListOutletContext]="{ $implicit: section, section }"
230
+ ></ng-container>
231
+ </ng-template>
232
+ }
233
+ @if (sectionFooterDir !== undefined) {
234
+ <ng-template vSectionFooter let-section>
235
+ <ng-container
236
+ [vListOutlet]="sectionFooterDir.templateRef"
237
+ [vListOutletContext]="{ $implicit: section, section }"
238
+ ></ng-container>
239
+ </ng-template>
240
+ }
241
+ @if (sectionSeparatorDir !== undefined) {
242
+ <ng-template vSectionSeparator>
243
+ <ng-container [vListOutlet]="sectionSeparatorDir.templateRef"></ng-container>
244
+ </ng-template>
245
+ }
246
+ @if (listHeaderDir !== undefined) {
247
+ <ng-template vListHeader>
248
+ <ng-container [vListOutlet]="listHeaderDir.templateRef"></ng-container>
249
+ </ng-template>
250
+ }
251
+ @if (listFooterDir !== undefined) {
252
+ <ng-template vListFooter>
253
+ <ng-container [vListOutlet]="listFooterDir.templateRef"></ng-container>
254
+ </ng-template>
255
+ }
256
+ @if (listEmptyDir !== undefined) {
257
+ <ng-template vListEmpty>
258
+ <ng-container [vListOutlet]="listEmptyDir.templateRef"></ng-container>
259
+ </ng-template>
260
+ }
261
+ @if (itemSeparatorDir !== undefined) {
262
+ <ng-template
263
+ vListSeparator
264
+ let-highlighted="highlighted"
265
+ let-leadingItem="leadingItem"
266
+ let-trailingItem="trailingItem"
267
+ >
268
+ <ng-container
269
+ [vListOutlet]="itemSeparatorDir.templateRef"
270
+ [vListOutletContext]="itemSeparatorContext(highlighted, leadingItem, trailingItem)"
271
+ ></ng-container>
272
+ </ng-template>
273
+ }
274
+ </VirtualizedSectionList>
275
+ `,
276
+ })
277
+ export class SectionList<ItemT = unknown>
278
+ implements ISectionListInputs<ItemT>, IVirtualizedSectionListHandle
279
+ {
280
+ @Input({ required: true }) sections!: ReadonlyArray<ISection<ItemT>>;
281
+ @Input() keyExtractor?: (item: ItemT, index: number) => string;
282
+ @Input() stickySectionHeadersEnabled?: boolean;
283
+ @Input() extraData?: unknown;
284
+ @Output() readonly endReached = new EventEmitter<{ distanceFromEnd: number }>();
285
+ @Input() onEndReachedThreshold?: number;
286
+ @Output() readonly startReached = new EventEmitter<{ distanceFromStart: number }>();
287
+ @Input() onStartReachedThreshold?: number;
288
+ @Output() readonly refresh = new EventEmitter<void>();
289
+ @Input() refreshing?: boolean | null;
290
+ @Input() progressViewOffset?: number;
291
+ @Input() initialNumToRender?: number;
292
+ @Input() initialScrollIndex?: number;
293
+ @Input() maxToRenderPerBatch?: number;
294
+ @Input() updateCellsBatchingPeriod?: number;
295
+ @Input() windowSize?: number;
296
+ @Input() inverted?: boolean;
297
+ @Input() maintainVisibleContentPosition?: {
298
+ minIndexForVisible: number;
299
+ autoscrollToTopThreshold?: number;
300
+ };
301
+ @Input() onScroll?: (event: ISymbioteEvent) => void;
302
+ @Input() onScrollBeginDrag?: (event: ISymbioteEvent) => void;
303
+ @Input() onScrollEndDrag?: (event: ISymbioteEvent) => void;
304
+ @Input() onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
305
+ @Input() onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
306
+ @Input() scrollEventThrottle?: number;
307
+ @Input() keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
308
+ @Input() keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
309
+ @Input() style?: IStyleProp<IViewStyle>;
310
+ @Input() contentContainerStyle?: IStyleProp<IViewStyle>;
311
+ @Input() testID?: string;
312
+ @Input() nativeID?: string;
313
+
314
+ // Bound to the template's `[style]="resolvedStyle"`, which Angular compiles to the built-in
315
+ // ɵɵstyleMap instruction — it only understands a flat object, never an array (RN's own
316
+ // `style={[a, b]}` composition idiom crashes deep inside Angular's styling engine), so this
317
+ // flattens `style` via the engine's own flattenStyle before it ever reaches that binding.
318
+ // anchorHostStyle merges in this component's OWN anchor's class-derived style (see its doc
319
+ // comment) — `elementRef` below is SectionList's OWN host, not `list`'s inner VirtualizedSectionList.
320
+ // A plain `flattenStyle([...])` here would allocate a FRESH object on every getter read (every CD
321
+ // check), which — bound onto the nested VirtualizedSectionList/VirtualizedList's own `@Input()
322
+ // style` — defeats VirtualizedList's `ngDoCheck` dedup gate and free-runs change detection
323
+ // forever (the exact bug `stableAnchorStyle` exists to prevent, see its doc comment and the
324
+ // `flat-list-array-style.test.ts` regression it fixes). `cachedResolvedStyle` is the getter's own
325
+ // persisted "previous" value across reads, since a getter has no natural field to compare against.
326
+ private cachedResolvedStyle: Record<string, unknown> | undefined;
327
+ get resolvedStyle(): IViewStyle {
328
+ this.cachedResolvedStyle = stableAnchorStyle(
329
+ this.elementRef,
330
+ this.style,
331
+ this.cachedResolvedStyle,
332
+ );
333
+ return this.cachedResolvedStyle;
334
+ }
335
+ @Input() accessible?: boolean;
336
+ @Input() accessibilityLabel?: string;
337
+ @Input() accessibilityHint?: string;
338
+ @Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
339
+ @Input() accessibilityState?: IAccessibilityStateValue;
340
+ @Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
341
+ @Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
342
+ @Input() accessibilityLabelledBy?: string | string[];
343
+ @Input() importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
344
+ @Input() accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
345
+ @Input() screenReaderFocusable?: boolean;
346
+ @Input() accessibilityViewIsModal?: boolean;
347
+ @Input() accessibilityElementsHidden?: boolean;
348
+ @Input() accessibilityIgnoresInvertColors?: boolean;
349
+ @Input() accessibilityLanguage?: string;
350
+ @Input() accessibilityRespondsToUserInteraction?: boolean;
351
+ @Input() accessibilityShowsLargeContentViewer?: boolean;
352
+ @Input() accessibilityLargeContentTitle?: string;
353
+ @Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
354
+ @Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
355
+ @Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
356
+ @Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
357
+ @Input() role?: IAriaProps['role'];
358
+ @Input() ariaLabel?: string;
359
+ @Input() ariaLabelledBy?: string;
360
+ @Input() ariaLive?: IAriaProps['aria-live'];
361
+ @Input() ariaHidden?: boolean;
362
+ @Input() ariaBusy?: boolean;
363
+ @Input() ariaChecked?: boolean | 'mixed';
364
+ @Input() ariaDisabled?: boolean;
365
+ @Input() ariaExpanded?: boolean;
366
+ @Input() ariaSelected?: boolean;
367
+ @Input() ariaModal?: boolean;
368
+ @Input() ariaValueMax?: number;
369
+ @Input() ariaValueMin?: number;
370
+ @Input() ariaValueNow?: number;
371
+ @Input() ariaValueText?: string;
372
+
373
+ // The app's section/list authoring templates, captured from the app's DIRECT projected content
374
+ // (single hop — always resolves) and re-stamped onto the inner VirtualizedSectionList above.
375
+ @ContentChild(VSectionItemDirective) sectionItemDir?: VSectionItemDirective<ItemT>;
376
+ @ContentChild(VSectionHeaderDirective) sectionHeaderDir?: VSectionHeaderDirective<ItemT>;
377
+ @ContentChild(VSectionFooterDirective) sectionFooterDir?: VSectionFooterDirective<ItemT>;
378
+ @ContentChild(VSectionSeparatorDirective) sectionSeparatorDir?: VSectionSeparatorDirective;
379
+ @ContentChild(VListHeaderDirective) listHeaderDir?: VListHeaderDirective;
380
+ @ContentChild(VListFooterDirective) listFooterDir?: VListFooterDirective;
381
+ @ContentChild(VListEmptyDirective) listEmptyDir?: VListEmptyDirective;
382
+ @ContentChild(VListSeparatorDirective) itemSeparatorDir?: VListSeparatorDirective<ItemT>;
383
+
384
+ // The composed inner VirtualizedSectionList. Its instance IS an IVirtualizedSectionListHandle, so
385
+ // SectionList's handle delegates straight to it. Available from ngAfterViewInit; reads lazily.
386
+ @ViewChild(VirtualizedSectionList) private list?: VirtualizedSectionList<ItemT>;
387
+
388
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
389
+ // onto (see anchorHostStyle's doc comment) — NOT `list` above, which targets the real inner
390
+ // `<VirtualizedSectionList>` one level down (itself its own separate anchor host).
391
+ private readonly elementRef = inject(ElementRef);
392
+
393
+ // The item separator context: leadingItem/trailingItem arrive `unknown` from the template's `let`
394
+ // bindings (see the asItem boundary comment above), already-unwrapped real items (no envelope to
395
+ // narrow through, unlike a section header/footer entry).
396
+ itemSeparatorContext(
397
+ highlighted: unknown,
398
+ leadingItem: unknown,
399
+ trailingItem: unknown,
400
+ ): IVListSeparatorContext<ItemT> {
401
+ const isHighlighted = highlighted === true;
402
+ return {
403
+ $implicit: isHighlighted,
404
+ highlighted: isHighlighted,
405
+ leadingItem: asItem<ItemT>(leadingItem),
406
+ trailingItem: asItem<ItemT>(trailingItem),
407
+ };
408
+ }
409
+
410
+ // ---- imperative handle (the shared IVirtualizedSectionListHandle surface) — delegates down ----
411
+
412
+ scrollToLocation(params: {
413
+ sectionIndex: number;
414
+ itemIndex: number;
415
+ viewOffset?: number;
416
+ viewPosition?: number;
417
+ animated?: boolean;
418
+ }): void {
419
+ this.list?.scrollToLocation(params);
420
+ }
421
+
422
+ flashScrollIndicators(): void {
423
+ this.list?.flashScrollIndicators();
424
+ }
425
+
426
+ getNativeScrollRef(): IScrollViewHandle | null {
427
+ return this.list?.getNativeScrollRef() ?? null;
428
+ }
429
+
430
+ getScrollableNode(): IScrollViewHandle | null {
431
+ return this.list?.getScrollableNode() ?? null;
432
+ }
433
+
434
+ getScrollResponder(): IScrollViewHandle | null {
435
+ return this.list?.getScrollResponder() ?? null;
436
+ }
437
+
438
+ getScrollNode(): ISymbioteNode | null {
439
+ return this.list?.getScrollNode() ?? null;
440
+ }
441
+
442
+ recordInteraction(): void {
443
+ this.list?.recordInteraction();
444
+ }
445
+ }
@@ -0,0 +1,42 @@
1
+ // Android-specific twin of the class= regression covered for iOS in switch.test.ts. Switch's
2
+ // hostProps getter is overridden per-platform component (index.ios.ts / index.android.ts), each
3
+ // injecting its OWN ElementRef, so the fix needs its own coverage against the Android build too.
4
+ import '@angular/compiler';
5
+ import { Component } from '@angular/core';
6
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+ import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
8
+ import { installFabric } from '@symbiote-native/test-utils';
9
+
10
+ import { mount, unmount } from '../../render';
11
+ import { Switch } from './index.android';
12
+
13
+ const ROOT_TAG = 942;
14
+ const fabric = installFabric();
15
+
16
+ beforeEach(() => {
17
+ fabric.reset();
18
+ });
19
+ afterEach(() => {
20
+ unmount(ROOT_TAG);
21
+ clearGlobalStyles();
22
+ });
23
+
24
+ @Component({
25
+ selector: 'symbiote-android-switch-class-host',
26
+ standalone: true,
27
+ imports: [Switch],
28
+ template: `<Switch [testID]="'switch-with-class'" class="card"></Switch>`,
29
+ })
30
+ class AndroidSwitchClassHost {}
31
+
32
+ describe('Switch (android)', () => {
33
+ it('resolves a class= on the Switch use site onto the real committed view, not the anchor', async () => {
34
+ registerStyles({ card: { backgroundColor: 'red' } });
35
+
36
+ mount(ROOT_TAG, AndroidSwitchClassHost);
37
+ await new Promise<void>(resolve => setTimeout(resolve, 0));
38
+
39
+ const node = fabric.find(n => n.props.testID === 'switch-with-class');
40
+ expect(node?.props.backgroundColor).toBe('red');
41
+ });
42
+ });
@@ -0,0 +1,61 @@
1
+ import { CUSTOM_ELEMENTS_SCHEMA, Component, ElementRef, inject } from '@angular/core';
2
+ import { anchorHostStyle, SwitchHost, SymbioteHostPropsDirective } from '../../primitives';
3
+ import { SwitchBase } from './shared';
4
+ export type { ISwitchProps, ISwitchTrackColor } from './shared';
5
+
6
+ @Component({
7
+ selector: 'Switch',
8
+ standalone: true,
9
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
10
+ imports: [SwitchHost, SymbioteHostPropsDirective],
11
+ inputs: [
12
+ 'value',
13
+ 'disabled',
14
+ 'trackColor',
15
+ 'thumbColor',
16
+ 'ios_backgroundColor',
17
+ 'style',
18
+ 'nativeID',
19
+ 'testID',
20
+ 'accessibilityLabel',
21
+ 'accessibilityRole',
22
+ 'accessibilityState',
23
+ 'accessibilityValue',
24
+ 'accessibilityHint',
25
+ 'accessible',
26
+ 'role',
27
+ 'aria-label',
28
+ 'aria-disabled',
29
+ 'aria-checked',
30
+ ],
31
+ outputs: ['valueChange', 'change'],
32
+ template: `
33
+ <symbiote-switch
34
+ #nativeSwitch="symbioteHost"
35
+ [symbioteHostProps]="hostProps"
36
+ (change)="handleChange($event, nativeSwitch)"
37
+ />
38
+ `,
39
+ })
40
+ export class Switch extends SwitchBase {
41
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
42
+ // onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch> leaf one level down.
43
+ private readonly elementRef = inject(ElementRef);
44
+
45
+ protected readonly platform = {
46
+ snapBackCommand: 'setNativeValue',
47
+ trackColorProps: (value: boolean, trackColor?: { false?: string; true?: string }) => ({
48
+ trackColorForFalse: trackColor?.false,
49
+ trackColorForTrue: trackColor?.true,
50
+ trackTintColor: value ? trackColor?.true : trackColor?.false,
51
+ }),
52
+ };
53
+
54
+ // The anchor's class-derived style goes first, then whatever SwitchBase already resolved
55
+ // (its own explicit `style` @Input plus the ios_backgroundColor fold), so an explicit [style]
56
+ // still beats the ambient class.
57
+ override get hostProps(): Record<string, unknown> {
58
+ const props = super.hostProps;
59
+ return { ...props, style: [anchorHostStyle(this.elementRef), props['style']] };
60
+ }
61
+ }
@@ -0,0 +1,60 @@
1
+ import { CUSTOM_ELEMENTS_SCHEMA, Component, ElementRef, inject } from '@angular/core';
2
+ import { anchorHostStyle, SwitchHost, SymbioteHostPropsDirective } from '../../primitives';
3
+ import { SwitchBase } from './shared';
4
+ export type { ISwitchProps, ISwitchTrackColor } from './shared';
5
+
6
+ @Component({
7
+ selector: 'Switch',
8
+ standalone: true,
9
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
10
+ imports: [SwitchHost, SymbioteHostPropsDirective],
11
+ inputs: [
12
+ 'value',
13
+ 'disabled',
14
+ 'trackColor',
15
+ 'thumbColor',
16
+ 'ios_backgroundColor',
17
+ 'style',
18
+ 'nativeID',
19
+ 'testID',
20
+ 'accessibilityLabel',
21
+ 'accessibilityRole',
22
+ 'accessibilityState',
23
+ 'accessibilityValue',
24
+ 'accessibilityHint',
25
+ 'accessible',
26
+ 'role',
27
+ 'aria-label',
28
+ 'aria-disabled',
29
+ 'aria-checked',
30
+ ],
31
+ outputs: ['valueChange', 'change'],
32
+ template: `
33
+ <symbiote-switch
34
+ #nativeSwitch="symbioteHost"
35
+ [symbioteHostProps]="hostProps"
36
+ (change)="handleChange($event, nativeSwitch)"
37
+ />
38
+ `,
39
+ })
40
+ export class Switch extends SwitchBase {
41
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
42
+ // onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch> leaf one level down.
43
+ private readonly elementRef = inject(ElementRef);
44
+
45
+ protected readonly platform = {
46
+ snapBackCommand: 'setValue',
47
+ trackColorProps: (_value: boolean, trackColor?: { false?: string; true?: string }) => ({
48
+ onTintColor: trackColor?.true,
49
+ tintColor: trackColor?.false,
50
+ }),
51
+ };
52
+
53
+ // The anchor's class-derived style goes first, then whatever SwitchBase already resolved
54
+ // (its own explicit `style` @Input plus the ios_backgroundColor fold), so an explicit [style]
55
+ // still beats the ambient class.
56
+ override get hostProps(): Record<string, unknown> {
57
+ const props = super.hostProps;
58
+ return { ...props, style: [anchorHostStyle(this.elementRef), props['style']] };
59
+ }
60
+ }
@@ -0,0 +1,4 @@
1
+ // Base/default Switch. Re-exports the iOS build; Metro picks index.ios.ts / index.android.ts
2
+ // in platform bundles. The Angular lifecycle reuses the shared Switch reducer + render fold.
3
+
4
+ export * from './index.ios';