@symbiote-native/angular 3.3.0 → 3.4.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 (179) hide show
  1. package/babel-register-composed.cjs +1 -0
  2. package/build/angular/accessibility-inputs.d.ts +2 -1
  3. package/build/angular/accessibility-inputs.js +4 -1
  4. package/build/angular/accessibility-inputs.js.map +1 -1
  5. package/build/angular/callback-host.d.ts +1 -1
  6. package/build/angular/callback-host.js +57 -9
  7. package/build/angular/callback-host.js.map +1 -1
  8. package/build/angular/components/flat-list/flat-list-handle.d.ts +35 -0
  9. package/build/angular/components/flat-list/flat-list-handle.js +48 -0
  10. package/build/angular/components/flat-list/flat-list-handle.js.map +1 -0
  11. package/build/angular/components/flat-list/flat-list-template.d.ts +1 -1
  12. package/build/angular/components/flat-list/flat-list-template.js +16 -0
  13. package/build/angular/components/flat-list/flat-list-template.js.map +1 -1
  14. package/build/angular/components/flat-list/index.d.ts +6 -30
  15. package/build/angular/components/flat-list/index.js +6 -36
  16. package/build/angular/components/flat-list/index.js.map +1 -1
  17. package/build/angular/components/image-inputs.d.ts +4 -0
  18. package/build/angular/components/image-inputs.js +81 -0
  19. package/build/angular/components/image-inputs.js.map +1 -0
  20. package/build/angular/components/image-props-resolve.d.ts +3 -0
  21. package/build/angular/components/image-props-resolve.js +133 -0
  22. package/build/angular/components/image-props-resolve.js.map +1 -0
  23. package/build/angular/components/image-shared.d.ts +8 -6
  24. package/build/angular/components/image-shared.js +23 -212
  25. package/build/angular/components/image-shared.js.map +1 -1
  26. package/build/angular/components/input-accessory-view-props.d.ts +4 -4
  27. package/build/angular/components/keyboard-avoiding-view/index.d.ts +3 -6
  28. package/build/angular/components/keyboard-avoiding-view/index.js +29 -56
  29. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
  30. package/build/angular/components/layout-conformance-props.d.ts +4 -0
  31. package/build/angular/components/layout-conformance-props.js +2 -0
  32. package/build/angular/components/layout-conformance-props.js.map +1 -0
  33. package/build/angular/components/modal/index.d.ts +6 -5
  34. package/build/angular/components/modal/index.js +17 -2
  35. package/build/angular/components/modal/index.js.map +1 -1
  36. package/build/angular/components/refresh-control-props.d.ts +7 -7
  37. package/build/angular/components/scroll-view-props.d.ts +8 -5
  38. package/build/angular/components/section-list/index.d.ts +17 -8
  39. package/build/angular/components/section-list/index.js +37 -28
  40. package/build/angular/components/section-list/index.js.map +1 -1
  41. package/build/angular/components/touchable-props.d.ts +2 -2
  42. package/build/angular/components/virtualized-list/cell-renderer-context.d.ts +12 -0
  43. package/build/angular/components/virtualized-list/cell-renderer-context.js +16 -0
  44. package/build/angular/components/virtualized-list/cell-renderer-context.js.map +1 -0
  45. package/build/angular/components/virtualized-list/directives.d.ts +26 -6
  46. package/build/angular/components/virtualized-list/directives.js +59 -5
  47. package/build/angular/components/virtualized-list/directives.js.map +1 -1
  48. package/build/angular/components/virtualized-list/index.d.ts +19 -15
  49. package/build/angular/components/virtualized-list/index.js +91 -61
  50. package/build/angular/components/virtualized-list/index.js.map +1 -1
  51. package/build/angular/components/virtualized-list/list-bags.d.ts +5 -1
  52. package/build/angular/components/virtualized-list/list-bags.js +31 -19
  53. package/build/angular/components/virtualized-list/list-bags.js.map +1 -1
  54. package/build/angular/components/virtualized-list/list-effects.d.ts +2 -2
  55. package/build/angular/components/virtualized-list/list-effects.js +17 -27
  56. package/build/angular/components/virtualized-list/list-effects.js.map +1 -1
  57. package/build/angular/components/virtualized-list/list-handle.d.ts +4 -1
  58. package/build/angular/components/virtualized-list/list-handle.js +13 -3
  59. package/build/angular/components/virtualized-list/list-handle.js.map +1 -1
  60. package/build/angular/components/virtualized-list/list-inputs.d.ts +19 -5
  61. package/build/angular/components/virtualized-list/list-inputs.js +47 -4
  62. package/build/angular/components/virtualized-list/list-inputs.js.map +1 -1
  63. package/build/angular/components/virtualized-list/list-props.d.ts +9 -3
  64. package/build/angular/components/virtualized-list/list-rows.d.ts +25 -0
  65. package/build/angular/components/virtualized-list/list-rows.js +25 -0
  66. package/build/angular/components/virtualized-list/list-rows.js.map +1 -0
  67. package/build/angular/components/virtualized-list/list-styles.d.ts +4 -0
  68. package/build/angular/components/virtualized-list/list-styles.js +10 -2
  69. package/build/angular/components/virtualized-list/list-styles.js.map +1 -1
  70. package/build/angular/components/virtualized-list/list-template.d.ts +1 -1
  71. package/build/angular/components/virtualized-list/list-template.js +159 -73
  72. package/build/angular/components/virtualized-list/list-template.js.map +1 -1
  73. package/build/angular/components/virtualized-list/nested-scope.d.ts +6 -0
  74. package/build/angular/components/virtualized-list/nested-scope.js +17 -0
  75. package/build/angular/components/virtualized-list/nested-scope.js.map +1 -0
  76. package/build/angular/components/virtualized-section-list/directives.d.ts +21 -9
  77. package/build/angular/components/virtualized-section-list/directives.js +8 -28
  78. package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
  79. package/build/angular/components/virtualized-section-list/index.d.ts +39 -60
  80. package/build/angular/components/virtualized-section-list/index.js +67 -33
  81. package/build/angular/components/virtualized-section-list/index.js.map +1 -1
  82. package/build/angular/components/virtualized-section-list/section-cell.d.ts +29 -0
  83. package/build/angular/components/virtualized-section-list/section-cell.js +82 -0
  84. package/build/angular/components/virtualized-section-list/section-cell.js.map +1 -0
  85. package/build/angular/components/virtualized-section-list/section-list-props.d.ts +56 -0
  86. package/build/angular/components/virtualized-section-list/section-list-props.js +3 -0
  87. package/build/angular/components/virtualized-section-list/section-list-props.js.map +1 -0
  88. package/build/angular/components/virtualized-section-list/section-list-template.d.ts +1 -1
  89. package/build/angular/components/virtualized-section-list/section-list-template.js +34 -26
  90. package/build/angular/components/virtualized-section-list/section-list-template.js.map +1 -1
  91. package/build/angular/components.d.ts +7 -3
  92. package/build/angular/components.js +2 -1
  93. package/build/angular/components.js.map +1 -1
  94. package/build/angular/element-base.d.ts +17 -1
  95. package/build/angular/element-base.js +33 -1
  96. package/build/angular/element-base.js.map +1 -1
  97. package/build/angular/element-props.d.ts +7 -7
  98. package/build/angular/element-text-input-native.d.ts +27 -0
  99. package/build/angular/element-text-input-native.js +52 -0
  100. package/build/angular/element-text-input-native.js.map +1 -0
  101. package/build/angular/elements-controls.d.ts +8 -2
  102. package/build/angular/elements-controls.js +13 -1
  103. package/build/angular/elements-controls.js.map +1 -1
  104. package/build/angular/elements-layout.d.ts +20 -3
  105. package/build/angular/elements-layout.js +37 -3
  106. package/build/angular/elements-layout.js.map +1 -1
  107. package/build/angular/elements-press.d.ts +10 -3
  108. package/build/angular/elements-press.js +13 -1
  109. package/build/angular/elements-press.js.map +1 -1
  110. package/build/angular/elements.d.ts +3 -3
  111. package/build/angular/elements.js +3 -2
  112. package/build/angular/elements.js.map +1 -1
  113. package/build/angular/index.d.ts +14 -7
  114. package/build/angular/index.js +21 -25
  115. package/build/angular/index.js.map +1 -1
  116. package/build/angular/modules/animated/create-animated-component.d.ts +1 -1
  117. package/build/angular/modules/animated/create-animated-component.js +1 -1
  118. package/build/angular/modules/animated/create-animated-component.js.map +1 -1
  119. package/build/angular/modules/animated/index.d.ts +54 -41
  120. package/build/angular/modules/animated/index.js +2 -26
  121. package/build/angular/modules/animated/index.js.map +1 -1
  122. package/build/angular/modules/app-registry/index.d.ts +3 -3
  123. package/build/angular/modules/app-registry/index.js +2 -2
  124. package/build/angular/modules/app-registry/index.js.map +1 -1
  125. package/build/angular/modules/status-bar/index.d.ts +9 -5
  126. package/build/angular/modules/status-bar/index.js +21 -10
  127. package/build/angular/modules/status-bar/index.js.map +1 -1
  128. package/build/angular/primitives/index.d.ts +4 -0
  129. package/build/angular/primitives/index.js +12 -0
  130. package/build/angular/primitives/index.js.map +1 -1
  131. package/build/angular/services/color-scheme.service.d.ts +1 -1
  132. package/package.json +6 -6
  133. package/src/accessibility-inputs.ts +3 -0
  134. package/src/callback-host.ts +56 -8
  135. package/src/components/flat-list/flat-list-handle.ts +64 -0
  136. package/src/components/flat-list/flat-list-template.ts +16 -0
  137. package/src/components/flat-list/index.ts +6 -48
  138. package/src/components/image-inputs.ts +94 -0
  139. package/src/components/image-props-resolve.ts +172 -0
  140. package/src/components/image-shared.ts +26 -291
  141. package/src/components/input-accessory-view-props.ts +12 -8
  142. package/src/components/keyboard-avoiding-view/index.ts +39 -79
  143. package/src/components/layout-conformance-props.ts +8 -0
  144. package/src/components/modal/index.ts +42 -20
  145. package/src/components/refresh-control-props.ts +29 -25
  146. package/src/components/scroll-view-props.ts +84 -76
  147. package/src/components/section-list/index.ts +34 -37
  148. package/src/components/touchable-props.ts +6 -2
  149. package/src/components/virtualized-list/cell-renderer-context.ts +36 -0
  150. package/src/components/virtualized-list/directives.ts +84 -8
  151. package/src/components/virtualized-list/index.ts +118 -85
  152. package/src/components/virtualized-list/list-bags.ts +38 -19
  153. package/src/components/virtualized-list/list-effects.ts +20 -32
  154. package/src/components/virtualized-list/list-handle.ts +17 -4
  155. package/src/components/virtualized-list/list-inputs.ts +47 -0
  156. package/src/components/virtualized-list/list-props.ts +57 -50
  157. package/src/components/virtualized-list/list-rows.ts +66 -0
  158. package/src/components/virtualized-list/list-styles.ts +30 -1
  159. package/src/components/virtualized-list/list-template.ts +159 -73
  160. package/src/components/virtualized-list/nested-scope.ts +22 -0
  161. package/src/components/virtualized-section-list/directives.ts +41 -40
  162. package/src/components/virtualized-section-list/index.ts +114 -118
  163. package/src/components/virtualized-section-list/section-cell.ts +109 -0
  164. package/src/components/virtualized-section-list/section-list-props.ts +79 -0
  165. package/src/components/virtualized-section-list/section-list-template.ts +34 -26
  166. package/src/components.ts +8 -0
  167. package/src/element-base.ts +17 -0
  168. package/src/element-props.ts +35 -33
  169. package/src/element-text-input-native.ts +31 -0
  170. package/src/elements-controls.ts +9 -1
  171. package/src/elements-layout.ts +27 -0
  172. package/src/elements-press.ts +10 -2
  173. package/src/elements.ts +3 -0
  174. package/src/index.ts +72 -22
  175. package/src/modules/animated/index.ts +2 -47
  176. package/src/modules/app-registry/index.ts +3 -1
  177. package/src/modules/status-bar/index.ts +24 -11
  178. package/src/primitives/index.ts +7 -0
  179. package/src/services/color-scheme.service.ts +1 -1
@@ -31,6 +31,7 @@ import {
31
31
  renderModal,
32
32
  resolveAccessibilityProps,
33
33
  shouldRenderModal,
34
+ warnAboutModalProps,
34
35
  type IAccessibilityProps,
35
36
  type IAriaProps,
36
37
  type IModalAnimationType,
@@ -42,11 +43,16 @@ import {
42
43
  dlog,
43
44
  isSymbioteEvent,
44
45
  Platform,
46
+ type IColorValue,
45
47
  type IStyleProp,
46
48
  type ISymbioteEvent,
47
49
  type IViewStyle,
48
50
  } from '@symbiote-native/engine';
49
51
  import { AccessibilityEventsBase } from '../../accessibility-events';
52
+ import {
53
+ ListScopeProvider,
54
+ NO_LIST_SCOPE,
55
+ } from '../virtualized-list/nested-scope';
50
56
  import {
51
57
  anchorHostStyle,
52
58
  ModalHost,
@@ -63,25 +69,26 @@ export type {
63
69
  } from '@symbiote-native/components';
64
70
 
65
71
  // Mirrors React's IModalProps minus children (Angular takes children via <ng-content>).
66
- export interface IAngularModalProps extends IAccessibilityProps, IAriaProps {
67
- visible?: boolean;
68
- transparent?: boolean;
69
- backdropColor?: string;
70
- animationType?: IModalAnimationType;
71
- presentationStyle?: IModalPresentationStyle;
72
- supportedOrientations?: ReadonlyArray<IModalOrientation>;
73
- hardwareAccelerated?: boolean;
74
- statusBarTranslucent?: boolean;
75
- navigationBarTranslucent?: boolean;
76
- allowSwipeDismissal?: boolean;
77
- onShow?: () => void;
78
- onDismiss?: () => void;
79
- onRequestClose?: () => void;
80
- // The engine hands every listener the ISymbioteEvent wrapper, so the orientation is read at
81
- // event.nativeEvent.orientation (IModalOrientationChangeEvent describes that payload).
82
- onOrientationChange?: (event: ISymbioteEvent) => void;
83
- style?: IStyleProp<IViewStyle>;
84
- }
72
+ export type IAngularModalProps = IAccessibilityProps &
73
+ IAriaProps & {
74
+ visible?: boolean;
75
+ transparent?: boolean;
76
+ backdropColor?: IColorValue;
77
+ animationType?: IModalAnimationType;
78
+ presentationStyle?: IModalPresentationStyle;
79
+ supportedOrientations?: ReadonlyArray<IModalOrientation>;
80
+ hardwareAccelerated?: boolean;
81
+ statusBarTranslucent?: boolean;
82
+ navigationBarTranslucent?: boolean;
83
+ allowSwipeDismissal?: boolean;
84
+ onShow?: () => void;
85
+ onDismiss?: () => void;
86
+ onRequestClose?: () => void;
87
+ // The engine hands every listener the ISymbioteEvent wrapper, so the orientation is read at
88
+ // event.nativeEvent.orientation (IModalOrientationChangeEvent describes that payload).
89
+ onOrientationChange?: (event: ISymbioteEvent) => void;
90
+ style?: IStyleProp<IViewStyle>;
91
+ };
85
92
 
86
93
  // What the Modal component itself takes as plain @Input()s: the full surface minus the
87
94
  // show/dismiss/close/orientation and accessibility events, which it exposes as real @Output()
@@ -107,6 +114,8 @@ export type IAngularModalInputs = Omit<
107
114
  ],
108
115
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
109
116
  imports: [ModalHost, ViewHost, SymbioteHostPropsDirective],
117
+ // The content sits outside the list's scroll, so a list in it is not nested in that list
118
+ providers: [{ provide: ListScopeProvider, useValue: NO_LIST_SCOPE }],
110
119
  changeDetection: ChangeDetectionStrategy.OnPush,
111
120
  template: `
112
121
  @if (shouldRender) {
@@ -133,7 +142,7 @@ export class Modal
133
142
  @Output() readonly requestClose = new EventEmitter<void>();
134
143
  @Input() visible?: boolean;
135
144
  @Input() transparent?: boolean;
136
- @Input() backdropColor?: string;
145
+ @Input() backdropColor?: IColorValue;
137
146
  @Input() animationType?: IModalAnimationType;
138
147
  @Input() presentationStyle?: IModalPresentationStyle;
139
148
  @Input() supportedOrientations?: ReadonlyArray<IModalOrientation>;
@@ -160,6 +169,18 @@ export class Modal
160
169
  this.state = createInitialModalState(isModalVisible(this.visible));
161
170
  }
162
171
 
172
+ // RN checks on mount and on every update, a dev build only
173
+ private warnOnMisuse(): void {
174
+ warnAboutModalProps({
175
+ presentationStyle: this.presentationStyle,
176
+ transparent: this.transparent,
177
+ navigationBarTranslucent: this.navigationBarTranslucent,
178
+ statusBarTranslucent: this.statusBarTranslucent,
179
+ allowSwipeDismissal: this.allowSwipeDismissal,
180
+ onRequestClose: this.requestClose.observed,
181
+ });
182
+ }
183
+
163
184
  // Bridges the non-reactive @Input fields `hostProps` reads into the reactive graph, so it can
164
185
  // memoize. Plain fields read inside a computed() are UNTRACKED - something must signal "a
165
186
  // dependency changed" or the bag goes stale. Signal inputs would do this natively, but `input()`
@@ -174,6 +195,7 @@ export class Modal
174
195
  // Before the keep-alive early-return below: this is the single moment Angular has finished
175
196
  // writing every changed @Input, and `hostProps` depends on far more than `visible`.
176
197
  this.hostPropsRevision.update(revision => revision + 1);
198
+ this.warnOnMisuse();
177
199
  const visibleChange = changes.visible;
178
200
  // First change is reflected by the ngOnInit seed; only later toggles drive the keep-alive.
179
201
  if (visibleChange === undefined || visibleChange.firstChange) return;
@@ -13,29 +13,33 @@ import type {
13
13
  IAccessibilityProps,
14
14
  IAriaProps,
15
15
  } from '@symbiote-native/components';
16
- import type { IStyleProp, IViewStyle } from '@symbiote-native/engine';
16
+ import type {
17
+ IColorValue,
18
+ IStyleProp,
19
+ IViewStyle,
20
+ } from '@symbiote-native/engine';
17
21
 
18
- export interface IAngularRefreshControlProps
19
- extends IAccessibilityProps, IAriaProps {
20
- refreshing: boolean;
21
- // `id` — RN's W3C alias for `nativeID`, folded by the spec entry's ID_ALIAS.
22
- id?: string;
23
- // RN's onRefresh is `() => void | Promise<void>`, the handler may be async; the promise is
24
- // fire-and-forget (native already starts refreshing on the gesture).
25
- onRefresh?: () => void | Promise<void>;
26
- tintColor?: string;
27
- title?: string;
28
- titleColor?: string;
29
- progressViewOffset?: number;
30
- // Android-only spinner styling (RN RefreshControlPropsAndroid): `colors` are the indicator's
31
- // animated stroke colors, `progressBackgroundColor` the disc behind it, `size` the diameter
32
- // preset. AndroidSwipeRefreshLayout reads them; PullToRefreshView on iOS ignores unknown props.
33
- colors?: readonly string[];
34
- progressBackgroundColor?: string;
35
- size?: 'default' | 'large';
36
- // Android-only native prop forwarded to AndroidSwipeRefreshLayout; iOS native never reads it.
37
- enabled?: boolean;
38
- // The Android scroll-view wrap injects the layout half of the style onto this host; iOS leaves it
39
- // unset (the RefreshControl is a childless sibling). Harmless to forward on both.
40
- style?: IStyleProp<IViewStyle>;
41
- }
22
+ export type IAngularRefreshControlProps = IAccessibilityProps &
23
+ IAriaProps & {
24
+ refreshing: boolean;
25
+ // `id` — RN's W3C alias for `nativeID`, folded by the spec entry's ID_ALIAS.
26
+ id?: string;
27
+ // RN's onRefresh is `() => void | Promise<void>`, the handler may be async; the promise is
28
+ // fire-and-forget (native already starts refreshing on the gesture).
29
+ onRefresh?: () => void | Promise<void>;
30
+ tintColor?: IColorValue;
31
+ title?: string;
32
+ titleColor?: IColorValue;
33
+ progressViewOffset?: number;
34
+ // Android-only spinner styling (RN RefreshControlPropsAndroid): `colors` are the indicator's
35
+ // animated stroke colors, `progressBackgroundColor` the disc behind it, `size` the diameter
36
+ // preset. AndroidSwipeRefreshLayout reads them; PullToRefreshView on iOS ignores unknown props.
37
+ colors?: readonly IColorValue[];
38
+ progressBackgroundColor?: IColorValue;
39
+ size?: 'default' | 'large';
40
+ // Android-only native prop forwarded to AndroidSwipeRefreshLayout; iOS native never reads it.
41
+ enabled?: boolean;
42
+ // The Android scroll-view wrap injects the layout half of the style onto this host; iOS leaves it
43
+ // unset (the RefreshControl is a childless sibling). Harmless to forward on both.
44
+ style?: IStyleProp<IViewStyle>;
45
+ };
@@ -15,8 +15,11 @@
15
15
  import type {
16
16
  IAccessibilityProps,
17
17
  IAriaProps,
18
+ IInnerViewRef,
19
+ IScrollViewNativeOnlyProps,
18
20
  } from '@symbiote-native/components';
19
21
  import type {
22
+ IColorValue,
20
23
  IStyleProp,
21
24
  ISymbioteEvent,
22
25
  IViewStyle,
@@ -27,80 +30,85 @@ export type { IScrollViewHandle } from '@symbiote-native/components';
27
30
  type IScrollHandler = (event: ISymbioteEvent) => void;
28
31
  type IContentSizeHandler = (width: number, height: number) => void;
29
32
 
30
- export interface IAngularScrollViewProps
31
- extends IAccessibilityProps, IAriaProps {
32
- style?: IStyleProp<IViewStyle>;
33
- // A bare string resolves through the shared style registry, like `class` on the host node.
34
- contentContainerStyle?: IStyleProp<IViewStyle> | string;
35
- scrollEnabled?: boolean;
36
- showsVerticalScrollIndicator?: boolean;
37
- showsHorizontalScrollIndicator?: boolean;
38
- pagingEnabled?: boolean;
39
- bounces?: boolean;
40
- decelerationRate?: 'normal' | 'fast' | number;
41
- scrollEventThrottle?: number;
42
- contentInset?: {
43
- top?: number;
44
- left?: number;
45
- bottom?: number;
46
- right?: number;
33
+ export type IAngularScrollViewProps = IAccessibilityProps &
34
+ IAriaProps &
35
+ IScrollViewNativeOnlyProps & {
36
+ style?: IStyleProp<IViewStyle>;
37
+ // A bare string resolves through the shared style registry, like `class` on the host node.
38
+ contentContainerStyle?: IStyleProp<IViewStyle> | string;
39
+ scrollEnabled?: boolean;
40
+ showsVerticalScrollIndicator?: boolean;
41
+ showsHorizontalScrollIndicator?: boolean;
42
+ pagingEnabled?: boolean;
43
+ bounces?: boolean;
44
+ decelerationRate?: 'normal' | 'fast' | number;
45
+ scrollEventThrottle?: number;
46
+ contentInset?: {
47
+ top?: number;
48
+ left?: number;
49
+ bottom?: number;
50
+ right?: number;
51
+ };
52
+ contentOffset?: { x: number; y: number };
53
+ removeClippedSubviews?: boolean;
54
+ snapToInterval?: number;
55
+ snapToOffsets?: number[];
56
+ snapToAlignment?: 'start' | 'center' | 'end';
57
+ snapToStart?: boolean;
58
+ snapToEnd?: boolean;
59
+ disableIntervalMomentum?: boolean;
60
+ experimental_endDraggingSensitivityMultiplier?: number;
61
+ // Sticky headers: RN implements stickiness PURELY IN JS — the native scroll view does not honor
62
+ // an index array. The engine's behavior resolves the indices against its own content children.
63
+ stickyHeaderIndices?: number[];
64
+ invertStickyHeaders?: boolean;
65
+ stickyHeaderHiddenOnScroll?: boolean;
66
+ // Gets the content view after the first commit and null when it goes away
67
+ innerViewRef?: IInnerViewRef;
68
+ keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
69
+ keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
70
+ maintainVisibleContentPosition?: {
71
+ minIndexForVisible: number;
72
+ autoscrollToTopThreshold?: number;
73
+ };
74
+ // iOS-only forwarding props (harmless on Android: its manager ignores unknown props).
75
+ alwaysBounceHorizontal?: boolean;
76
+ alwaysBounceVertical?: boolean;
77
+ centerContent?: boolean;
78
+ scrollIndicatorInsets?: {
79
+ top?: number;
80
+ left?: number;
81
+ bottom?: number;
82
+ right?: number;
83
+ };
84
+ indicatorStyle?: 'default' | 'black' | 'white';
85
+ directionalLockEnabled?: boolean;
86
+ automaticallyAdjustKeyboardInsets?: boolean;
87
+ contentInsetAdjustmentBehavior?:
88
+ 'automatic' | 'scrollableAxes' | 'never' | 'always';
89
+ minimumZoomScale?: number;
90
+ maximumZoomScale?: number;
91
+ zoomScale?: number;
92
+ bouncesZoom?: boolean;
93
+ pinchGestureEnabled?: boolean;
94
+ // Android-only forwarding props (harmless on iOS).
95
+ nestedScrollEnabled?: boolean;
96
+ overScrollMode?: 'auto' | 'always' | 'never';
97
+ fadingEdgeLength?: number;
98
+ persistentScrollbar?: boolean;
99
+ endFillColor?: IColorValue;
100
+ onLayout?: IScrollHandler;
101
+ onScroll?: IScrollHandler;
102
+ onScrollBeginDrag?: IScrollHandler;
103
+ onScrollEndDrag?: IScrollHandler;
104
+ onMomentumScrollBegin?: IScrollHandler;
105
+ onMomentumScrollEnd?: IScrollHandler;
106
+ // iOS-only: user tapped the status bar to scroll to top. Inert on Android.
107
+ onScrollToTop?: IScrollHandler;
108
+ // Synthesized in JS from the content view's onLayout (RN _handleContentOnLayout); deduped.
109
+ onContentSizeChange?: IContentSizeHandler;
110
+ onAccessibilityAction?: (event: ISymbioteEvent) => void;
111
+ onAccessibilityTap?: (event: ISymbioteEvent) => void;
112
+ onMagicTap?: (event: ISymbioteEvent) => void;
113
+ onAccessibilityEscape?: (event: ISymbioteEvent) => void;
47
114
  };
48
- contentOffset?: { x: number; y: number };
49
- removeClippedSubviews?: boolean;
50
- snapToInterval?: number;
51
- snapToOffsets?: number[];
52
- snapToAlignment?: 'start' | 'center' | 'end';
53
- snapToStart?: boolean;
54
- snapToEnd?: boolean;
55
- disableIntervalMomentum?: boolean;
56
- // Sticky headers: RN implements stickiness PURELY IN JS — the native scroll view does not honor
57
- // an index array. The engine's behavior resolves the indices against its own content children.
58
- stickyHeaderIndices?: number[];
59
- invertStickyHeaders?: boolean;
60
- keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
61
- keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
62
- maintainVisibleContentPosition?: {
63
- minIndexForVisible: number;
64
- autoscrollToTopThreshold?: number;
65
- };
66
- // iOS-only forwarding props (harmless on Android: its manager ignores unknown props).
67
- alwaysBounceHorizontal?: boolean;
68
- alwaysBounceVertical?: boolean;
69
- centerContent?: boolean;
70
- scrollIndicatorInsets?: {
71
- top?: number;
72
- left?: number;
73
- bottom?: number;
74
- right?: number;
75
- };
76
- indicatorStyle?: 'default' | 'black' | 'white';
77
- directionalLockEnabled?: boolean;
78
- automaticallyAdjustKeyboardInsets?: boolean;
79
- contentInsetAdjustmentBehavior?:
80
- 'automatic' | 'scrollableAxes' | 'never' | 'always';
81
- minimumZoomScale?: number;
82
- maximumZoomScale?: number;
83
- zoomScale?: number;
84
- bouncesZoom?: boolean;
85
- pinchGestureEnabled?: boolean;
86
- // Android-only forwarding props (harmless on iOS).
87
- nestedScrollEnabled?: boolean;
88
- overScrollMode?: 'auto' | 'always' | 'never';
89
- fadingEdgeLength?: number;
90
- persistentScrollbar?: boolean;
91
- endFillColor?: string;
92
- onLayout?: IScrollHandler;
93
- onScroll?: IScrollHandler;
94
- onScrollBeginDrag?: IScrollHandler;
95
- onScrollEndDrag?: IScrollHandler;
96
- onMomentumScrollBegin?: IScrollHandler;
97
- onMomentumScrollEnd?: IScrollHandler;
98
- // iOS-only: user tapped the status bar to scroll to top. Inert on Android.
99
- onScrollToTop?: IScrollHandler;
100
- // Synthesized in JS from the content view's onLayout (RN _handleContentOnLayout); deduped.
101
- onContentSizeChange?: IContentSizeHandler;
102
- onAccessibilityAction?: (event: ISymbioteEvent) => void;
103
- onAccessibilityTap?: (event: ISymbioteEvent) => void;
104
- onMagicTap?: (event: ISymbioteEvent) => void;
105
- onAccessibilityEscape?: (event: ISymbioteEvent) => void;
106
- }
@@ -12,8 +12,8 @@ import {
12
12
  inject,
13
13
  } from '@angular/core';
14
14
  import type {
15
+ IInnerViewRef,
15
16
  IScrollViewHandle,
16
- ISection,
17
17
  IVirtualizedSectionListHandle,
18
18
  } from '@symbiote-native/components';
19
19
  import {
@@ -23,11 +23,11 @@ import {
23
23
  type IViewStyle,
24
24
  } from '@symbiote-native/engine';
25
25
  import {
26
+ VListCellDirective,
26
27
  VListEmptyDirective,
27
28
  VListFooterDirective,
28
29
  VListHeaderDirective,
29
30
  VListSeparatorDirective,
30
- type IVListSeparatorContext,
31
31
  } from '../virtualized-list';
32
32
  import { VListOutletDirective } from '../virtualized-list/directives';
33
33
  import { LIST_ACCESSIBILITY_FORWARD } from '../virtualized-list/list-a11y-forward';
@@ -43,16 +43,11 @@ import {
43
43
  VSectionItemDirective,
44
44
  VSectionSeparatorDirective,
45
45
  VirtualizedSectionList,
46
+ type ISection,
46
47
  type IVirtualizedSectionListProps,
47
48
  } from '../virtualized-section-list';
48
49
 
49
- // The separator context arrives typed `unknown`, the directive is matched structurally in the
50
- // template with no type argument to pin the item type. The narrowest cast for that gap
51
- function asItem<ItemT>(value: unknown): ItemT | undefined {
52
- return value as ItemT | undefined;
53
- }
54
-
55
- export type { ISection } from '@symbiote-native/components';
50
+ export type { ISection } from '../virtualized-section-list';
56
51
  export type ISectionListHandle = IVirtualizedSectionListHandle;
57
52
 
58
53
  // Re-exported so app code gets the whole `vSection*` and `vList*` authoring surface from here
@@ -124,20 +119,28 @@ export type ISectionListInputs<ItemT> = Omit<
124
119
  [maxToRenderPerBatch]="maxToRenderPerBatch"
125
120
  [updateCellsBatchingPeriod]="updateCellsBatchingPeriod"
126
121
  [windowSize]="windowSize"
122
+ [disableVirtualization]="disableVirtualization"
127
123
  [inverted]="inverted"
124
+ [horizontal]="horizontal"
128
125
  [maintainVisibleContentPosition]="maintainVisibleContentPosition"
129
126
  [onScroll]="onScroll"
130
127
  [onScrollBeginDrag]="onScrollBeginDrag"
131
128
  [onScrollEndDrag]="onScrollEndDrag"
132
129
  [onMomentumScrollBegin]="onMomentumScrollBegin"
133
130
  [onMomentumScrollEnd]="onMomentumScrollEnd"
131
+ [onContentSizeChange]="onContentSizeChange"
134
132
  [scrollEventThrottle]="scrollEventThrottle"
135
133
  [keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
136
134
  [keyboardDismissMode]="keyboardDismissMode"
137
135
  [removeClippedSubviews]="removeClippedSubviews"
138
136
  [nestedScrollEnabled]="nestedScrollEnabled"
137
+ [stickyHeaderHiddenOnScroll]="stickyHeaderHiddenOnScroll"
138
+ [innerViewRef]="innerViewRef"
139
139
  [style]="resolvedStyle"
140
140
  [contentContainerStyle]="contentContainerStyle"
141
+ [listHeaderComponentStyle]="listHeaderComponentStyle"
142
+ [listFooterComponentStyle]="listFooterComponentStyle"
143
+ [cellRendererTemplate]="cellDir?.templateRef"
141
144
  [testID]="testID"
142
145
  [nativeID]="nativeID"
143
146
  ${LIST_ACCESSIBILITY_FORWARD}
@@ -177,9 +180,10 @@ export type ISectionListInputs<ItemT> = Omit<
177
180
  </ng-template>
178
181
  }
179
182
  @if (sectionSeparatorDir !== undefined) {
180
- <ng-template vSectionSeparator>
183
+ <ng-template vSectionSeparator let-props="props">
181
184
  <ng-container
182
185
  [vListOutlet]="sectionSeparatorDir.templateRef"
186
+ [vListOutletContext]="props"
183
187
  ></ng-container>
184
188
  </ng-template>
185
189
  }
@@ -203,17 +207,10 @@ export type ISectionListInputs<ItemT> = Omit<
203
207
  </ng-template>
204
208
  }
205
209
  @if (itemSeparatorDir !== undefined) {
206
- <ng-template
207
- vListSeparator
208
- let-highlighted="highlighted"
209
- let-leadingItem="leadingItem"
210
- let-trailingItem="trailingItem"
211
- >
210
+ <ng-template vListSeparator let-props="props">
212
211
  <ng-container
213
212
  [vListOutlet]="itemSeparatorDir.templateRef"
214
- [vListOutletContext]="
215
- itemSeparatorContext(highlighted, leadingItem, trailingItem)
216
- "
213
+ [vListOutletContext]="props"
217
214
  ></ng-container>
218
215
  </ng-template>
219
216
  }
@@ -242,7 +239,9 @@ export class SectionList<ItemT = unknown>
242
239
  @Input() maxToRenderPerBatch?: number;
243
240
  @Input() updateCellsBatchingPeriod?: number;
244
241
  @Input() windowSize?: number;
242
+ @Input() disableVirtualization?: boolean;
245
243
  @Input() inverted?: boolean;
244
+ @Input() horizontal?: boolean;
246
245
  @Input() maintainVisibleContentPosition?: {
247
246
  minIndexForVisible: number;
248
247
  autoscrollToTopThreshold?: number;
@@ -252,13 +251,18 @@ export class SectionList<ItemT = unknown>
252
251
  @Input() onScrollEndDrag?: (event: ISymbioteEvent) => void;
253
252
  @Input() onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
254
253
  @Input() onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
254
+ @Input() onContentSizeChange?: (width: number, height: number) => void;
255
255
  @Input() scrollEventThrottle?: number;
256
256
  @Input() keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
257
257
  @Input() keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
258
258
  @Input() removeClippedSubviews?: boolean;
259
259
  @Input() nestedScrollEnabled?: boolean;
260
+ @Input() stickyHeaderHiddenOnScroll?: boolean;
261
+ @Input() innerViewRef?: IInnerViewRef;
260
262
  @Input() style?: IStyleProp<IViewStyle>;
261
263
  @Input() contentContainerStyle?: IStyleProp<IViewStyle>;
264
+ @Input() listHeaderComponentStyle?: IStyleProp<IViewStyle>;
265
+ @Input() listFooterComponentStyle?: IStyleProp<IViewStyle>;
262
266
  @Input() testID?: string;
263
267
  @Input() nativeID?: string;
264
268
 
@@ -282,12 +286,13 @@ export class SectionList<ItemT = unknown>
282
286
  @ContentChild(VSectionFooterDirective)
283
287
  sectionFooterDir?: VSectionFooterDirective<ItemT>;
284
288
  @ContentChild(VSectionSeparatorDirective)
285
- sectionSeparatorDir?: VSectionSeparatorDirective;
289
+ sectionSeparatorDir?: VSectionSeparatorDirective<ItemT>;
286
290
  @ContentChild(VListHeaderDirective) listHeaderDir?: VListHeaderDirective;
287
291
  @ContentChild(VListFooterDirective) listFooterDir?: VListFooterDirective;
288
292
  @ContentChild(VListEmptyDirective) listEmptyDir?: VListEmptyDirective;
289
293
  @ContentChild(VListSeparatorDirective)
290
294
  itemSeparatorDir?: VListSeparatorDirective<ItemT>;
295
+ @ContentChild(VListCellDirective) cellDir?: VListCellDirective;
291
296
 
292
297
  // The inner list, whose instance is the scroll handle, reads lazily
293
298
  @ViewChild(VirtualizedSectionList)
@@ -296,22 +301,6 @@ export class SectionList<ItemT = unknown>
296
301
  // This component's own host, the anchor `class="..."` resolves onto, not the inner list's
297
302
  private readonly elementRef = inject(ElementRef);
298
303
 
299
- // The leading and trailing items arrive `unknown` from the template's `let` bindings, already
300
- // unwrapped to real items
301
- itemSeparatorContext(
302
- highlighted: unknown,
303
- leadingItem: unknown,
304
- trailingItem: unknown,
305
- ): IVListSeparatorContext<ItemT> {
306
- const isHighlighted = highlighted === true;
307
- return {
308
- $implicit: isHighlighted,
309
- highlighted: isHighlighted,
310
- leadingItem: asItem<ItemT>(leadingItem),
311
- trailingItem: asItem<ItemT>(trailingItem),
312
- };
313
- }
314
-
315
304
  scrollToLocation(params: {
316
305
  sectionIndex: number;
317
306
  itemIndex: number;
@@ -326,7 +315,7 @@ export class SectionList<ItemT = unknown>
326
315
  this.list?.flashScrollIndicators();
327
316
  }
328
317
 
329
- getNativeScrollRef(): IScrollViewHandle | null {
318
+ getNativeScrollRef(): ISymbioteNode | null {
330
319
  return this.list?.getNativeScrollRef() ?? null;
331
320
  }
332
321
 
@@ -342,7 +331,15 @@ export class SectionList<ItemT = unknown>
342
331
  return this.list?.getScrollNode() ?? null;
343
332
  }
344
333
 
334
+ getScrollRef(): ISymbioteNode | null {
335
+ return this.list?.getScrollRef() ?? null;
336
+ }
337
+
345
338
  recordInteraction(): void {
346
339
  this.list?.recordInteraction();
347
340
  }
341
+
342
+ setNativeProps(props: Record<string, unknown>): void {
343
+ this.list?.setNativeProps(props);
344
+ }
348
345
  }
@@ -9,7 +9,11 @@
9
9
  // <prop_types_split_agnostic_vs_per_adapter> describes. Mirrors React/Vue's ITouchableBaseProps.
10
10
 
11
11
  import type { IPressTimingProps } from '@symbiote-native/components';
12
- import type { IStyleProp, IViewStyle } from '@symbiote-native/engine';
12
+ import type {
13
+ IColorValue,
14
+ IStyleProp,
15
+ IViewStyle,
16
+ } from '@symbiote-native/engine';
13
17
 
14
18
  import type { IAngularPressableInputs } from './pressable-props';
15
19
 
@@ -24,7 +28,7 @@ export type IAngularTouchableOpacityProps = IAngularTouchableBaseProps & {
24
28
 
25
29
  export type IAngularTouchableHighlightProps = IAngularTouchableBaseProps & {
26
30
  activeOpacity?: number;
27
- underlayColor?: string;
31
+ underlayColor?: IColorValue;
28
32
  // RN's snapshot affordance (`Pressable.js:151`, `TouchableHighlight.js:61`): render the control in
29
33
  // its pressed state with no gesture, so a test can capture it. Consumed by the engine and stripped
30
34
  // before the payload — no ViewConfig declares it.
@@ -0,0 +1,36 @@
1
+ // The context a `vListCell` template reads for one cell: RN's `CellRendererComponent` props plus
2
+ // the template that stamps the item and its separator inside the wrapper the app draws
3
+
4
+ import type { TemplateRef } from '@angular/core';
5
+ import type { IViewStyle } from '@symbiote-native/engine';
6
+ import type { IWindowCell } from './cell-registry';
7
+ import type { IVListCellContext } from './directives';
8
+
9
+ export type ICellRendererArgs<ItemT> = {
10
+ cell: IWindowCell<ItemT>;
11
+ style: IViewStyle | undefined;
12
+ // The list's own template holding the cell's item and separator outlets
13
+ body: TemplateRef<unknown> | undefined;
14
+ layout: (
15
+ measure: IWindowCell<ItemT>['measure'],
16
+ event: unknown,
17
+ index: number,
18
+ ) => void;
19
+ focus: (index: number) => void;
20
+ };
21
+
22
+ export function buildCellRendererContext<ItemT>(
23
+ args: ICellRendererArgs<ItemT>,
24
+ ): IVListCellContext {
25
+ const { cell, style, body, layout, focus } = args;
26
+ return {
27
+ $implicit: cell.context.$implicit,
28
+ cellKey: cell.key,
29
+ index: cell.index,
30
+ style,
31
+ layout: event => layout(cell.measure, event, cell.index),
32
+ focus: () => focus(cell.index),
33
+ content: body,
34
+ contentContext: { $implicit: cell },
35
+ };
36
+ }