@symbiote-native/angular 3.3.0 → 3.5.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 (187) 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 +15 -7
  114. package/build/angular/index.js +22 -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/render/index.d.ts +2 -2
  132. package/build/angular/render/index.js +10 -29
  133. package/build/angular/render/index.js.map +1 -1
  134. package/build/angular/root-tag.d.ts +4 -0
  135. package/build/angular/root-tag.js +10 -0
  136. package/build/angular/root-tag.js.map +1 -0
  137. package/build/angular/services/color-scheme.service.d.ts +1 -1
  138. package/package.json +6 -6
  139. package/src/accessibility-inputs.ts +3 -0
  140. package/src/callback-host.ts +56 -8
  141. package/src/components/flat-list/flat-list-handle.ts +64 -0
  142. package/src/components/flat-list/flat-list-template.ts +16 -0
  143. package/src/components/flat-list/index.ts +6 -48
  144. package/src/components/image-inputs.ts +94 -0
  145. package/src/components/image-props-resolve.ts +172 -0
  146. package/src/components/image-shared.ts +26 -291
  147. package/src/components/input-accessory-view-props.ts +12 -8
  148. package/src/components/keyboard-avoiding-view/index.ts +39 -79
  149. package/src/components/layout-conformance-props.ts +8 -0
  150. package/src/components/modal/index.ts +42 -20
  151. package/src/components/refresh-control-props.ts +29 -25
  152. package/src/components/scroll-view-props.ts +84 -76
  153. package/src/components/section-list/index.ts +34 -37
  154. package/src/components/touchable-props.ts +6 -2
  155. package/src/components/virtualized-list/cell-renderer-context.ts +36 -0
  156. package/src/components/virtualized-list/directives.ts +84 -8
  157. package/src/components/virtualized-list/index.ts +118 -85
  158. package/src/components/virtualized-list/list-bags.ts +38 -19
  159. package/src/components/virtualized-list/list-effects.ts +20 -32
  160. package/src/components/virtualized-list/list-handle.ts +17 -4
  161. package/src/components/virtualized-list/list-inputs.ts +47 -0
  162. package/src/components/virtualized-list/list-props.ts +57 -50
  163. package/src/components/virtualized-list/list-rows.ts +66 -0
  164. package/src/components/virtualized-list/list-styles.ts +30 -1
  165. package/src/components/virtualized-list/list-template.ts +159 -73
  166. package/src/components/virtualized-list/nested-scope.ts +22 -0
  167. package/src/components/virtualized-section-list/directives.ts +41 -40
  168. package/src/components/virtualized-section-list/index.ts +114 -118
  169. package/src/components/virtualized-section-list/section-cell.ts +109 -0
  170. package/src/components/virtualized-section-list/section-list-props.ts +79 -0
  171. package/src/components/virtualized-section-list/section-list-template.ts +34 -26
  172. package/src/components.ts +8 -0
  173. package/src/element-base.ts +17 -0
  174. package/src/element-props.ts +35 -33
  175. package/src/element-text-input-native.ts +31 -0
  176. package/src/elements-controls.ts +9 -1
  177. package/src/elements-layout.ts +27 -0
  178. package/src/elements-press.ts +10 -2
  179. package/src/elements.ts +3 -0
  180. package/src/index.ts +79 -22
  181. package/src/modules/animated/index.ts +2 -47
  182. package/src/modules/app-registry/index.ts +3 -1
  183. package/src/modules/status-bar/index.ts +24 -11
  184. package/src/primitives/index.ts +7 -0
  185. package/src/render/index.ts +23 -50
  186. package/src/root-tag.ts +14 -0
  187. package/src/services/color-scheme.service.ts +1 -1
@@ -25,12 +25,8 @@ import {
25
25
  type OnInit,
26
26
  } from '@angular/core';
27
27
  import {
28
- computeInset,
29
- configureKeyboardAvoidingAnimation,
28
+ createKeyboardAvoidingModel,
30
29
  keyboardAvoidingEventNamesFor,
31
- readKeyboardAnimationTiming,
32
- readKeyboardFrame,
33
- readLayoutFrame,
34
30
  readPrefersCrossFadeTransitions,
35
31
  resolveAccessibilityProps,
36
32
  resolveKeyboardAvoidingLayout,
@@ -39,12 +35,10 @@ import {
39
35
  type IAriaProps,
40
36
  type IKeyboardAvoidingBehavior,
41
37
  type IKeyboardAvoidingLayout,
42
- type IMeasuredFrame,
43
38
  } from '@symbiote-native/components';
44
39
  import {
45
40
  Keyboard,
46
41
  Platform,
47
- dlog,
48
42
  isSymbioteEvent,
49
43
  type IEventSubscription,
50
44
  type IStyleProp,
@@ -64,15 +58,15 @@ export type { IKeyboardAvoidingBehavior } from '@symbiote-native/components';
64
58
  // Mirrors React's IKeyboardAvoidingViewProps minus children (Angular takes children via
65
59
  // <ng-content>), declared per-adapter over the shared accessibility base since a framework-specific
66
60
  // children field keeps it from being fully shared across adapters.
67
- export interface IAngularKeyboardAvoidingViewProps
68
- extends IAccessibilityProps, IAriaProps {
69
- behavior?: IKeyboardAvoidingBehavior;
70
- enabled?: boolean;
71
- keyboardVerticalOffset?: number;
72
- contentContainerStyle?: IStyleProp<IViewStyle>;
73
- style?: IStyleProp<IViewStyle>;
74
- onLayout?: (event: ISymbioteEvent) => void;
75
- }
61
+ export type IAngularKeyboardAvoidingViewProps = IAccessibilityProps &
62
+ IAriaProps & {
63
+ behavior?: IKeyboardAvoidingBehavior;
64
+ enabled?: boolean;
65
+ keyboardVerticalOffset?: number;
66
+ contentContainerStyle?: IStyleProp<IViewStyle>;
67
+ style?: IStyleProp<IViewStyle>;
68
+ onLayout?: (event: ISymbioteEvent) => void;
69
+ };
76
70
 
77
71
  // What the component itself takes as plain @Input()s: the full surface minus onLayout and the
78
72
  // accessibility callbacks, which it exposes as real @Output() EventEmitters instead (see the
@@ -133,36 +127,41 @@ export class KeyboardAvoidingView
133
127
  @Input() testID?: string;
134
128
  @Input() nativeID?: string;
135
129
 
136
- // How far the view must move so it no longer overlaps the keyboard. A plain field, not reactive:
137
- // OnPush + zoneless means a keyboard event mutates it and pulls the view via markForCheck.
130
+ // Plain field, the model sets it and `markForCheck` pulls the OnPush view
138
131
  private inset = 0;
139
- // Mutable, not state: changing the measured frame alone shouldn't re-render; it feeds the next
140
- // keyboard event's inset math (React's frameRef / initialHeightRef, Vue's frame / initialHeight).
141
- private frame?: IMeasuredFrame;
142
- private initialHeight?: number;
143
- // The iOS "Prefer Cross-Fade Transitions" setting, read once per mount. Deliberately a plain
144
- // field and NOT markForCheck'd when the promise lands: it is a device setting that cannot change
145
- // mid-session, and it feeds only the NEXT keyboard event's math — never the current render — so
146
- // under zoneless CD there is nothing to repaint at resolve time. The keyboard event that does use
147
- // it already calls markForCheck itself.
132
+ // A device setting that cannot change mid-session, learning it must not repaint
148
133
  private prefersCrossFadeTransitions = false;
149
134
  private subscriptions: IEventSubscription[] = [];
150
135
 
151
136
  private readonly changeDetector = inject(ChangeDetectorRef);
152
- // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
153
- // onto (see anchorHostStyle's doc comment) — NOT a ViewChild into the inner view (this
154
- // component has none; the wrapper IS the outer template node hostProps binds onto).
137
+ // This component's own host, the anchor that `class` at the use site resolves onto
155
138
  private readonly elementRef = inject(ElementRef);
156
139
 
140
+ // Options are read at event time, т.к. an `@Input` can change under a live subscription
141
+ private readonly model = createKeyboardAvoidingModel({
142
+ options: () => ({
143
+ behavior: this.behavior,
144
+ enabled: this.enabled !== false,
145
+ keyboardVerticalOffset:
146
+ this.keyboardVerticalOffset ?? DEFAULT_VERTICAL_OFFSET,
147
+ }),
148
+ setInset: value => {
149
+ this.inset = value;
150
+ this.changeDetector.markForCheck();
151
+ },
152
+ prefersCrossFade: () => this.prefersCrossFadeTransitions,
153
+ });
154
+
157
155
  ngOnInit(): void {
158
- // TWO events, picked per host, never three: iOS takes the will* pair so the view rides up with
159
- // the keyboard animation, Android the did* pair. The change-frame notification is deliberately
160
- // absent — with an undocked/split/floating iOS keyboard it fires BEFORE the hide, so listening
161
- // to it applies a frame captured mid-dismissal (RN KeyboardAvoidingView.js's own comment).
156
+ // iOS takes the `will*` pair so the view rides up with the keyboard, Android the `did*` pair
162
157
  const events = keyboardAvoidingEventNamesFor(Platform.OS);
163
158
  this.subscriptions = [
164
- Keyboard.addListener(events.show, payload => this.onShow(payload)),
165
- Keyboard.addListener(events.hide, () => this.onHide()),
159
+ Keyboard.addListener(events.show, payload => {
160
+ this.model.keyboardShown(payload);
161
+ // An unchanged inset skips `setInset`, the event still has to carry a new `@Input` down
162
+ this.changeDetector.markForCheck();
163
+ }),
164
+ Keyboard.addListener(events.hide, this.model.keyboardHidden),
166
165
  ];
167
166
  // The core wrapper, not AccessibilityInfo directly: the engine's iOS getter REJECTS on a
168
167
  // native error (RN parity), and nobody awaits this read, so an unwrapped call would surface
@@ -177,47 +176,10 @@ export class KeyboardAvoidingView
177
176
  this.subscriptions = [];
178
177
  }
179
178
 
180
- private onShow(payload: unknown): void {
181
- const keyboard = readKeyboardFrame(payload);
182
- const offset = this.keyboardVerticalOffset ?? DEFAULT_VERTICAL_OFFSET;
183
- const next = computeInset(this.frame, keyboard, offset, {
184
- // Read off the field at event time, not captured when the subscription was built: `behavior`
185
- // is an @Input that can change under a long-lived subscription, and a stale one keeps
186
- // applying the old mode's math (it gates the previous-inset correction below).
187
- behavior: this.behavior,
188
- // The inset CURRENTLY applied (RN's this.state.bottom), read off the field at event time so
189
- // 'height' mode's fixpoint correction sees the live value: that mode SHRINKS the wrapper by
190
- // the inset, so the next onLayout reports a frame shorter by exactly that much.
191
- previousInset: this.inset,
192
- prefersCrossFadeTransitions: this.prefersCrossFadeTransitions,
193
- });
194
- // RN's `_updateBottomIfNecessary` skips the animation when the inset did not change.
195
- if (next !== this.inset)
196
- configureKeyboardAvoidingAnimation(
197
- readKeyboardAnimationTiming(payload),
198
- this.enabled !== false,
199
- );
200
- dlog(`KeyboardAvoidingView show -> inset ${next}`);
201
- this.inset = next;
202
- this.changeDetector.markForCheck();
203
- }
204
-
205
- private onHide(): void {
206
- dlog('KeyboardAvoidingView hide -> inset 0');
207
- this.inset = 0;
208
- this.changeDetector.markForCheck();
209
- }
210
-
211
- // The wrapper's onLayout measures the frame BEFORE forwarding to the caller's onLayout, so the
212
- // next keyboard event's inset math has the view's real position (React/Vue's handleLayout).
179
+ // Measured before the caller's `layout` output fires
213
180
  handleLayout(event: unknown): void {
214
181
  if (!isSymbioteEvent(event)) return;
215
- const measured = readLayoutFrame(event.nativeEvent.layout);
216
- if (measured !== undefined) {
217
- this.frame = measured;
218
- if (this.initialHeight === undefined)
219
- this.initialHeight = measured.height;
220
- }
182
+ this.model.laidOut(event.nativeEvent.layout);
221
183
  this.layout.emit(event);
222
184
  }
223
185
 
@@ -227,14 +189,12 @@ export class KeyboardAvoidingView
227
189
  return this.enabled === false ? 0 : this.inset;
228
190
  }
229
191
 
230
- // The behavior + effective inset → wrapper/inner styles and the nesting decision (the shared core
231
- // of RN's render). Recomputed per CD pass, the Angular twin of React/Vue's per-render call.
232
- // Named `resolvedLayout`, not `layout` — that name is now the onLayout @Output() EventEmitter.
192
+ // Not `layout`, that name is the `onLayout` output
233
193
  private get resolvedLayout(): IKeyboardAvoidingLayout {
234
194
  return resolveKeyboardAvoidingLayout({
235
195
  behavior: this.behavior,
236
196
  effectiveInset: this.effectiveInset,
237
- initialHeight: this.initialHeight,
197
+ initialHeight: this.model.initialHeight(),
238
198
  style: this.style,
239
199
  contentContainerStyle: this.contentContainerStyle,
240
200
  });
@@ -0,0 +1,8 @@
1
+ // The prop surface of `<layout-conformance>`, RN's `experimental_LayoutConformance`, for Angular
2
+ // Children come through `<ng-content>`, so there is no `children` here
3
+ import type { ILayoutConformanceMode } from '@symbiote-native/components';
4
+
5
+ export type IAngularLayoutConformanceProps = {
6
+ // `strict` lays out by the W3C spec even where it breaks compatibility
7
+ mode: ILayoutConformanceMode;
8
+ };
@@ -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
- }