@symbiote-native/angular 0.7.0 → 0.8.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 (158) hide show
  1. package/README.md +3 -3
  2. package/build/angular/bootstrap.js +1 -1
  3. package/build/angular/bootstrap.js.map +1 -1
  4. package/build/angular/components/activity-indicator/index.android.js +7 -2
  5. package/build/angular/components/activity-indicator/index.android.js.map +1 -1
  6. package/build/angular/components/activity-indicator/index.ios.js +3 -1
  7. package/build/angular/components/activity-indicator/index.ios.js.map +1 -1
  8. package/build/angular/components/activity-indicator/shared.d.ts +1 -1
  9. package/build/angular/components/activity-indicator/shared.js +1 -1
  10. package/build/angular/components/activity-indicator/shared.js.map +1 -1
  11. package/build/angular/components/button.js +13 -5
  12. package/build/angular/components/button.js.map +1 -1
  13. package/build/angular/components/flat-list/index.d.ts +1 -1
  14. package/build/angular/components/flat-list/index.js +47 -14
  15. package/build/angular/components/flat-list/index.js.map +1 -1
  16. package/build/angular/components/image/index.android.js +4 -2
  17. package/build/angular/components/image/index.android.js.map +1 -1
  18. package/build/angular/components/image/index.ios.js +4 -2
  19. package/build/angular/components/image/index.ios.js.map +1 -1
  20. package/build/angular/components/image/shared.js +18 -6
  21. package/build/angular/components/image/shared.js.map +1 -1
  22. package/build/angular/components/image-background.js +22 -8
  23. package/build/angular/components/image-background.js.map +1 -1
  24. package/build/angular/components/input-accessory-view/index.js +7 -2
  25. package/build/angular/components/input-accessory-view/index.js.map +1 -1
  26. package/build/angular/components/keyboard-avoiding-view/index.d.ts +1 -0
  27. package/build/angular/components/keyboard-avoiding-view/index.js +39 -9
  28. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
  29. package/build/angular/components/modal/index.js +7 -2
  30. package/build/angular/components/modal/index.js.map +1 -1
  31. package/build/angular/components/pressable/index.d.ts +1 -1
  32. package/build/angular/components/pressable/index.js +6 -2
  33. package/build/angular/components/pressable/index.js.map +1 -1
  34. package/build/angular/components/refresh-control/index.js +3 -1
  35. package/build/angular/components/refresh-control/index.js.map +1 -1
  36. package/build/angular/components/safe-area-view/index.js +3 -1
  37. package/build/angular/components/safe-area-view/index.js.map +1 -1
  38. package/build/angular/components/scroll-view/index.android.js +44 -14
  39. package/build/angular/components/scroll-view/index.android.js.map +1 -1
  40. package/build/angular/components/scroll-view/index.ios.js +13 -5
  41. package/build/angular/components/scroll-view/index.ios.js.map +1 -1
  42. package/build/angular/components/scroll-view/projection.d.ts +32 -0
  43. package/build/angular/components/scroll-view/projection.js +212 -47
  44. package/build/angular/components/scroll-view/projection.js.map +1 -1
  45. package/build/angular/components/scroll-view/shared.js +28 -7
  46. package/build/angular/components/scroll-view/shared.js.map +1 -1
  47. package/build/angular/components/scroll-view/sticky-header.js +7 -3
  48. package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
  49. package/build/angular/components/section-list/index.d.ts +6 -1
  50. package/build/angular/components/section-list/index.js +63 -18
  51. package/build/angular/components/section-list/index.js.map +1 -1
  52. package/build/angular/components/switch/index.android.js +17 -3
  53. package/build/angular/components/switch/index.android.js.map +1 -1
  54. package/build/angular/components/switch/index.ios.js +17 -3
  55. package/build/angular/components/switch/index.ios.js.map +1 -1
  56. package/build/angular/components/switch/shared.d.ts +1 -1
  57. package/build/angular/components/switch/shared.js +5 -2
  58. package/build/angular/components/switch/shared.js.map +1 -1
  59. package/build/angular/components/text-input.d.ts +1 -1
  60. package/build/angular/components/text-input.js +25 -5
  61. package/build/angular/components/text-input.js.map +1 -1
  62. package/build/angular/components/touchable/index.d.ts +29 -7
  63. package/build/angular/components/touchable/index.js +246 -69
  64. package/build/angular/components/touchable/index.js.map +1 -1
  65. package/build/angular/components/touchable-native-feedback/index.js +2 -2
  66. package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
  67. package/build/angular/components/virtualized-list/directives.js +4 -0
  68. package/build/angular/components/virtualized-list/directives.js.map +1 -1
  69. package/build/angular/components/virtualized-list/index.d.ts +5 -2
  70. package/build/angular/components/virtualized-list/index.js +197 -60
  71. package/build/angular/components/virtualized-list/index.js.map +1 -1
  72. package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
  73. package/build/angular/components/virtualized-section-list/index.d.ts +18 -1
  74. package/build/angular/components/virtualized-section-list/index.js +90 -21
  75. package/build/angular/components/virtualized-section-list/index.js.map +1 -1
  76. package/build/angular/components.d.ts +1 -1
  77. package/build/angular/components.js.map +1 -1
  78. package/build/angular/create-portal/index.js +5 -1
  79. package/build/angular/create-portal/index.js.map +1 -1
  80. package/build/angular/create-tunnel/index.js.map +1 -1
  81. package/build/angular/descriptor-to-angular/index.js +3 -1
  82. package/build/angular/descriptor-to-angular/index.js.map +1 -1
  83. package/build/angular/diagnostics.d.ts +33 -0
  84. package/build/angular/diagnostics.js +82 -0
  85. package/build/angular/diagnostics.js.map +1 -0
  86. package/build/angular/host-instance/index.js +1 -1
  87. package/build/angular/host-instance/index.js.map +1 -1
  88. package/build/angular/index.d.ts +5 -2
  89. package/build/angular/index.js +15 -1
  90. package/build/angular/index.js.map +1 -1
  91. package/build/angular/modules/animated/create-animated-component.js +27 -9
  92. package/build/angular/modules/animated/create-animated-component.js.map +1 -1
  93. package/build/angular/modules/app-registry/index.js.map +1 -1
  94. package/build/angular/modules/status-bar/index.js +1 -1
  95. package/build/angular/modules/status-bar/index.js.map +1 -1
  96. package/build/angular/primitives/index.d.ts +19 -2
  97. package/build/angular/primitives/index.js +39 -3
  98. package/build/angular/primitives/index.js.map +1 -1
  99. package/build/angular/primitives/shared.d.ts +9 -1
  100. package/build/angular/primitives/shared.js +49 -3
  101. package/build/angular/primitives/shared.js.map +1 -1
  102. package/build/angular/render/index.js +48 -5
  103. package/build/angular/render/index.js.map +1 -1
  104. package/build/angular/renderer/index.d.ts +1 -0
  105. package/build/angular/renderer/index.js +37 -2
  106. package/build/angular/renderer/index.js.map +1 -1
  107. package/build/angular/services/color-scheme.service.js.map +1 -1
  108. package/build/angular/services/window-dimensions.service.js +1 -1
  109. package/build/angular/services/window-dimensions.service.js.map +1 -1
  110. package/package.json +5 -5
  111. package/src/bootstrap.ts +4 -1
  112. package/src/components/activity-indicator/index.android.ts +7 -2
  113. package/src/components/activity-indicator/index.ios.ts +3 -1
  114. package/src/components/activity-indicator/shared.ts +32 -13
  115. package/src/components/button.ts +19 -6
  116. package/src/components/flat-list/index.ts +91 -29
  117. package/src/components/image/index.android.ts +9 -2
  118. package/src/components/image/index.ios.ts +9 -2
  119. package/src/components/image/shared.ts +58 -19
  120. package/src/components/image-background.ts +29 -10
  121. package/src/components/input-accessory-view/index.ts +27 -10
  122. package/src/components/keyboard-avoiding-view/index.ts +55 -15
  123. package/src/components/modal/index.ts +22 -8
  124. package/src/components/pressable/index.ts +51 -21
  125. package/src/components/refresh-control/index.ts +22 -8
  126. package/src/components/safe-area-view/index.ts +16 -7
  127. package/src/components/scroll-view/index.android.ts +31 -9
  128. package/src/components/scroll-view/index.ios.ts +17 -4
  129. package/src/components/scroll-view/projection.ts +290 -63
  130. package/src/components/scroll-view/shared.ts +93 -26
  131. package/src/components/scroll-view/sticky-header.ts +25 -8
  132. package/src/components/section-list/index.ts +61 -19
  133. package/src/components/switch/index.android.ts +14 -3
  134. package/src/components/switch/index.ios.ts +14 -3
  135. package/src/components/switch/shared.ts +15 -4
  136. package/src/components/text-input.ts +45 -13
  137. package/src/components/touchable/index.ts +296 -72
  138. package/src/components/touchable-native-feedback/index.ts +32 -10
  139. package/src/components/virtualized-list/directives.ts +19 -5
  140. package/src/components/virtualized-list/index.ts +274 -72
  141. package/src/components/virtualized-section-list/directives.ts +6 -3
  142. package/src/components/virtualized-section-list/index.ts +146 -34
  143. package/src/components.ts +4 -1
  144. package/src/create-portal/index.ts +8 -2
  145. package/src/create-tunnel/index.ts +3 -1
  146. package/src/descriptor-to-angular/index.ts +29 -8
  147. package/src/diagnostics.ts +138 -0
  148. package/src/host-instance/index.ts +9 -3
  149. package/src/index.ts +31 -2
  150. package/src/modules/animated/create-animated-component.ts +44 -15
  151. package/src/modules/app-registry/index.ts +3 -1
  152. package/src/modules/status-bar/index.ts +13 -4
  153. package/src/primitives/index.ts +46 -2
  154. package/src/primitives/shared.ts +70 -9
  155. package/src/render/index.ts +56 -6
  156. package/src/renderer/index.ts +76 -14
  157. package/src/services/color-scheme.service.ts +3 -1
  158. package/src/services/window-dimensions.service.ts +11 -4
@@ -164,7 +164,8 @@ export const SCROLL_VIEW_INPUTS = [
164
164
  // (ReactNode children, ReactElement refreshControl) — Angular takes children via <ng-content> and
165
165
  // composes RefreshControl through projection instead. Declared per-adapter over the shared
166
166
  // accessibility base since a framework element/ref field can't live in a shared agnostic type.
167
- export interface IAngularScrollViewProps extends IAccessibilityProps, IAriaProps {
167
+ export interface IAngularScrollViewProps
168
+ extends IAccessibilityProps, IAriaProps {
168
169
  style?: IStyleProp<IViewStyle>;
169
170
  // A bare string resolves through the shared style registry, like `class` on the host node.
170
171
  contentContainerStyle?: IStyleProp<IViewStyle> | string;
@@ -176,7 +177,12 @@ export interface IAngularScrollViewProps extends IAccessibilityProps, IAriaProps
176
177
  bounces?: boolean;
177
178
  decelerationRate?: 'normal' | 'fast' | number;
178
179
  scrollEventThrottle?: number;
179
- contentInset?: { top?: number; left?: number; bottom?: number; right?: number };
180
+ contentInset?: {
181
+ top?: number;
182
+ left?: number;
183
+ bottom?: number;
184
+ right?: number;
185
+ };
180
186
  contentOffset?: { x: number; y: number };
181
187
  removeClippedSubviews?: boolean;
182
188
  snapToInterval?: number;
@@ -204,11 +210,17 @@ export interface IAngularScrollViewProps extends IAccessibilityProps, IAriaProps
204
210
  alwaysBounceHorizontal?: boolean;
205
211
  alwaysBounceVertical?: boolean;
206
212
  centerContent?: boolean;
207
- scrollIndicatorInsets?: { top?: number; left?: number; bottom?: number; right?: number };
213
+ scrollIndicatorInsets?: {
214
+ top?: number;
215
+ left?: number;
216
+ bottom?: number;
217
+ right?: number;
218
+ };
208
219
  indicatorStyle?: 'default' | 'black' | 'white';
209
220
  directionalLockEnabled?: boolean;
210
221
  automaticallyAdjustKeyboardInsets?: boolean;
211
- contentInsetAdjustmentBehavior?: 'automatic' | 'scrollableAxes' | 'never' | 'always';
222
+ contentInsetAdjustmentBehavior?:
223
+ 'automatic' | 'scrollableAxes' | 'never' | 'always';
212
224
  minimumZoomScale?: number;
213
225
  maximumZoomScale?: number;
214
226
  zoomScale?: number;
@@ -399,7 +411,8 @@ export abstract class ScrollViewBase
399
411
  @ViewChild('host', { read: SymbioteHostPropsDirective })
400
412
  private hostDirective?: SymbioteHostPropsDirective;
401
413
 
402
- @ContentChild(RefreshControl) protected projectedRefreshControl?: RefreshControl;
414
+ @ContentChild(RefreshControl)
415
+ protected projectedRefreshControl?: RefreshControl;
403
416
 
404
417
  // This component's OWN anchor host — where a `class="..."` at the use site resolves (see
405
418
  // anchorHostStyle's doc comment) — NOT `#host` in the platform templates, which targets the real
@@ -459,7 +472,9 @@ export abstract class ScrollViewBase
459
472
  // (commit.ts) falls back to Object.is on a function leaf, so even a structurally-identical bag
460
473
  // reads as "props changed" and forces a real Fabric re-clone that cascades up every ancestor
461
474
  // (see AnimatedComponentBase.reconcile()'s identical warning).
462
- private readonly handleInvertedStickyLayout = (event: ISymbioteEvent): void => {
475
+ private readonly handleInvertedStickyLayout = (
476
+ event: ISymbioteEvent,
477
+ ): void => {
463
478
  const height = readLayoutDimension(event, 'height');
464
479
  // The projection controller is pushed from here rather than picked up on the next prop-bag
465
480
  // read: viewportHeight is internal mutable state, so once `scrollProps` became a memoized
@@ -502,7 +517,8 @@ export abstract class ScrollViewBase
502
517
  // new listener wrapper (and a new AnimatedEvent walking `collectMappedValues`) on every unrelated
503
518
  // change-detection pass while sticky headers are active.
504
519
  private cachedStickyOnScrollSource: IScrollHandler | undefined;
505
- private cachedStickyOnScrollHandler: ((...args: readonly unknown[]) => void) | undefined;
520
+ private cachedStickyOnScrollHandler:
521
+ ((...args: readonly unknown[]) => void) | undefined;
506
522
 
507
523
  get isHorizontal(): boolean {
508
524
  const value = this.horizontal === true;
@@ -516,20 +532,26 @@ export abstract class ScrollViewBase
516
532
  }
517
533
 
518
534
  private get hasStickyHeaders(): boolean {
519
- return this.stickyHeaderIndices !== undefined && this.stickyHeaderIndices.length > 0;
535
+ return (
536
+ this.stickyHeaderIndices !== undefined &&
537
+ this.stickyHeaderIndices.length > 0
538
+ );
520
539
  }
521
540
 
522
541
  // A class-name string resolves through the shared registry before it reaches
523
542
  // selectScrollIntrinsics, which only understands style objects/arrays.
524
- private get resolvedContentContainerStyle(): IStyleProp<IViewStyle> | undefined {
543
+ private get resolvedContentContainerStyle():
544
+ IStyleProp<IViewStyle> | undefined {
525
545
  return typeof this.contentContainerStyle === 'string'
526
546
  ? resolveClassName(this.contentContainerStyle)
527
547
  : this.contentContainerStyle;
528
548
  }
529
549
 
530
550
  private get scrollViewBaseStyle(): IViewStyle {
531
- return selectScrollIntrinsics(this.isHorizontal, this.resolvedContentContainerStyle)
532
- .scrollViewBaseStyle;
551
+ return selectScrollIntrinsics(
552
+ this.isHorizontal,
553
+ this.resolvedContentContainerStyle,
554
+ ).scrollViewBaseStyle;
533
555
  }
534
556
 
535
557
  // Bridges non-reactive @Input fields into the reactive graph, so a computed() can memoize a
@@ -623,7 +645,8 @@ export abstract class ScrollViewBase
623
645
  // value each frame (JS jitter). The DECISIONS (which path, throttle defaults, whether to
624
646
  // capture viewport height) are folded out to the shared resolveScrollForwarding; Angular only
625
647
  // EXECUTES them, keeping stable-reference handlers so a fresh closure never forces a re-clone.
626
- const nativeStickyAvailable = this.hasStickyHeaders && isNativeAnimatedAvailable();
648
+ const nativeStickyAvailable =
649
+ this.hasStickyHeaders && isNativeAnimatedAvailable();
627
650
  const forwarding = resolveScrollForwarding({
628
651
  hasStickyHeaders: this.hasStickyHeaders,
629
652
  nativeStickyAvailable,
@@ -725,7 +748,9 @@ export abstract class ScrollViewBase
725
748
  this.projectedRefreshControl?.handleRefresh(nativeNode);
726
749
  }
727
750
 
728
- private refreshControlProps(style?: IStyleProp<IViewStyle>): Record<string, unknown> {
751
+ private refreshControlProps(
752
+ style?: IStyleProp<IViewStyle>,
753
+ ): Record<string, unknown> {
729
754
  const refresh = this.projectedRefreshControl;
730
755
  if (refresh === undefined) return {};
731
756
  return compact({
@@ -765,8 +790,14 @@ export abstract class ScrollViewBase
765
790
  this.isHorizontal,
766
791
  this.resolvedContentContainerStyle,
767
792
  );
768
- const bag: Record<string, unknown> = { style: contentStyle, collapsable: false };
769
- if (this.maintainVisibleContentPosition !== undefined || this.snapToAlignment !== undefined) {
793
+ const bag: Record<string, unknown> = {
794
+ style: contentStyle,
795
+ collapsable: false,
796
+ };
797
+ if (
798
+ this.maintainVisibleContentPosition !== undefined ||
799
+ this.snapToAlignment !== undefined
800
+ ) {
770
801
  bag.collapsableChildren = false;
771
802
  }
772
803
  bag.onLayout = this.handleContentLayout;
@@ -783,7 +814,9 @@ export abstract class ScrollViewBase
783
814
  // subscribes a template's (event) listeners in its CREATION pass, before any input is written and
784
815
  // before this component's own view is first refreshed - so `observed` is already settled at the
785
816
  // first evaluation and cannot flip afterwards without destroying the whole component.
786
- private emitterCallback(emitter: EventEmitter<ISymbioteEvent>): IScrollHandler | undefined {
817
+ private emitterCallback(
818
+ emitter: EventEmitter<ISymbioteEvent>,
819
+ ): IScrollHandler | undefined {
787
820
  if (!emitter.observed) return undefined;
788
821
  let cached = this.emitterCallbackCache.get(emitter);
789
822
  if (cached === undefined) {
@@ -807,7 +840,10 @@ export abstract class ScrollViewBase
807
840
  [{ nativeEvent: { contentOffset: { y: this.scrollAnimatedValue } } }],
808
841
  userOnScroll === undefined
809
842
  ? undefined
810
- : { listener: (...args: readonly unknown[]) => forwardScrollEvent(userOnScroll, args) },
843
+ : {
844
+ listener: (...args: readonly unknown[]) =>
845
+ forwardScrollEvent(userOnScroll, args),
846
+ },
811
847
  );
812
848
  }
813
849
  return this.cachedStickyOnScrollHandler;
@@ -823,7 +859,9 @@ export abstract class ScrollViewBase
823
859
  // The imperative API a parent reaches via @ViewChild(ScrollView). buildScrollViewHandle is the
824
860
  // shared, proven handle (React/Vue use it verbatim); it reads the node through the LAZY getter on
825
861
  // every call, so a command before commit no-ops rather than freezing a null node.
826
- private readonly handle: IScrollViewHandle = buildScrollViewHandle(() => this.hostNode);
862
+ private readonly handle: IScrollViewHandle = buildScrollViewHandle(
863
+ () => this.hostNode,
864
+ );
827
865
 
828
866
  scrollTo(options?: { x?: number; y?: number; animated?: boolean }): void {
829
867
  this.handle.scrollTo(options);
@@ -861,6 +899,20 @@ export abstract class ScrollViewBase
861
899
  // actually moved, so `signal.set`'s own Object.is check makes an unchanged poll dirty nothing.
862
900
  ngDoCheck(): void {
863
901
  this.anchorStyle.set(anchorHostStyle(this.elementRef));
902
+ // The attach can want a node that does not exist yet, and nothing else would ask again.
903
+ // ngOnChanges covers "indices arrived late"; it does NOT cover "indices arrived late AND the host
904
+ // node had not committed yet", which is exactly what a VirtualizedList does - it derives
905
+ // stickyHeaderIndices from the measured window, so they land after this component's
906
+ // ngAfterViewInit and before its host node resolves. Device-diagnosed 2026-08-18 on
907
+ // examples/angular BenchmarkScreen STICKY PATH B, whose log ends on `attachSticky NOT attaching
908
+ // (wantsAttach=true hasNode=false)` - sticky never turned on, while PATH A (literal indices in
909
+ // the template) worked because ngAfterViewInit already saw a node. Retried ONLY from that stuck
910
+ // state, so a settled ScrollView pays nothing per check. Device-verified the same day.
911
+ // Not covered headless: sticky-native-attach.test.ts's late-indices case has its node resolved by
912
+ // the time the indices land, and the deferred-creation ordering under `@if` never rendered the
913
+ // ScrollView at all.
914
+ if (this.stickyAttachedEnabled && this.stickyAttachedNode === null)
915
+ this.attachSticky();
864
916
  }
865
917
 
866
918
  // The projected RefreshControl is a @ContentChild, not an @Input, so ngOnChanges never fires when
@@ -869,7 +921,8 @@ export abstract class ScrollViewBase
869
921
  // hook. Guarded on the flag itself so a check that changed nothing skips the reconcile walk,
870
922
  // which the old getter-driven update could not do.
871
923
  ngAfterContentChecked(): void {
872
- if (this.hasProjectedRefreshControl === this.lastHasProjectedRefreshControl) return;
924
+ if (this.hasProjectedRefreshControl === this.lastHasProjectedRefreshControl)
925
+ return;
873
926
  this.lastHasProjectedRefreshControl = this.hasProjectedRefreshControl;
874
927
  this.updateProjectionController();
875
928
  }
@@ -910,8 +963,13 @@ export abstract class ScrollViewBase
910
963
  // Nothing that decides the attach has changed - return rather than detach and rebind the
911
964
  // native scroll event, which every unrelated @Input change would otherwise churn now that
912
965
  // ngOnChanges drives this too.
913
- if (wantsAttach === this.stickyAttachedEnabled && node === this.stickyAttachedNode) {
914
- dlog('STICKY[sv] attachSticky skipped (nothing about the attach changed)');
966
+ if (
967
+ wantsAttach === this.stickyAttachedEnabled &&
968
+ node === this.stickyAttachedNode
969
+ ) {
970
+ dlog(
971
+ 'STICKY[sv] attachSticky skipped (nothing about the attach changed)',
972
+ );
915
973
  return;
916
974
  }
917
975
 
@@ -931,10 +989,15 @@ export abstract class ScrollViewBase
931
989
  );
932
990
  return;
933
991
  }
934
- dlog('STICKY[sv] attachSticky waiting for commit to bind the native scroll event');
992
+ dlog(
993
+ 'STICKY[sv] attachSticky waiting for commit to bind the native scroll event',
994
+ );
935
995
  this.cancelStickyBind = whenCommitted(node, () => {
936
996
  dlog('STICKY[sv] attachSticky node committed -> attachStickyScroll');
937
- this.detachStickyScroll = attachStickyScroll(node, this.scrollAnimatedValue);
997
+ this.detachStickyScroll = attachStickyScroll(
998
+ node,
999
+ this.scrollAnimatedValue,
1000
+ );
938
1001
  });
939
1002
  }
940
1003
 
@@ -957,7 +1020,9 @@ export abstract class ScrollViewBase
957
1020
 
958
1021
  // Typed as the a11y intersection WITH the string index so resolveAccessibilityProps's result
959
1022
  // stays assignable into the forwarded bag (a genuine narrowing, built at that type — no cast).
960
- private accessibilityInputs(): IAccessibilityProps & IAriaProps & Record<string, unknown> {
1023
+ private accessibilityInputs(): IAccessibilityProps &
1024
+ IAriaProps &
1025
+ Record<string, unknown> {
961
1026
  return {
962
1027
  testID: this.testID,
963
1028
  nativeID: this.nativeID,
@@ -976,8 +1041,10 @@ export abstract class ScrollViewBase
976
1041
  accessibilityElementsHidden: this.accessibilityElementsHidden,
977
1042
  accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
978
1043
  accessibilityLanguage: this.accessibilityLanguage,
979
- accessibilityRespondsToUserInteraction: this.accessibilityRespondsToUserInteraction,
980
- accessibilityShowsLargeContentViewer: this.accessibilityShowsLargeContentViewer,
1044
+ accessibilityRespondsToUserInteraction:
1045
+ this.accessibilityRespondsToUserInteraction,
1046
+ accessibilityShowsLargeContentViewer:
1047
+ this.accessibilityShowsLargeContentViewer,
981
1048
  accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
982
1049
  role: this.role,
983
1050
  'aria-label': this['aria-label'],
@@ -96,7 +96,9 @@ export class ScrollViewStickyHeader
96
96
  // The animated node driving the transform (RN's animatedTranslateY), replaced by the
97
97
  // rebuild-interpolation effect. When the scroll value is native, this interpolation runs on the UI
98
98
  // thread: the smooth pin.
99
- private animatedTranslateY: AnimatedInterpolation = new AnimatedValue(0).interpolate({
99
+ private animatedTranslateY: AnimatedInterpolation = new AnimatedValue(
100
+ 0,
101
+ ).interpolate({
100
102
  inputRange: [-1, 0],
101
103
  outputRange: [0, 0],
102
104
  });
@@ -135,13 +137,17 @@ export class ScrollViewStickyHeader
135
137
  // anchor style first so the fixed transform/zIndex below still wins on conflict.
136
138
  return [
137
139
  anchorHostStyle(this.elementRef),
138
- { transform: [{ translateY: this.animatedTranslateY }], zIndex: STICKY_HEADER_Z_INDEX },
140
+ {
141
+ transform: [{ translateY: this.animatedTranslateY }],
142
+ zIndex: STICKY_HEADER_Z_INDEX,
143
+ },
139
144
  ];
140
145
  }
141
146
 
142
147
  // Stable reference (bound once) so AnimatedView's directive does not re-wire the layout listener
143
148
  // every change-detection pass.
144
- private readonly onHostLayout = (event: ISymbioteEvent): void => this.handleLayout(event);
149
+ private readonly onHostLayout = (event: ISymbioteEvent): void =>
150
+ this.handleLayout(event);
145
151
 
146
152
  // Also a stable reference, not a getter: jsonEqual (commit.ts) can't structurally compare the
147
153
  // embedded onLayout function, so a fresh object here would read as "props changed" on every
@@ -156,7 +162,9 @@ export class ScrollViewStickyHeader
156
162
 
157
163
  // The EXPLICIT debounced translateY overrides the committed transform for hit-testing while
158
164
  // animatedTranslateY does the smooth (native-driven) pin (RN ScrollViewStickyHeader.js).
159
- get passthrough(): { style: { transform: Array<{ translateY: number }> } } | null {
165
+ get passthrough(): {
166
+ style: { transform: Array<{ translateY: number }> };
167
+ } | null {
160
168
  return this.state.translateY !== null
161
169
  ? { style: { transform: [{ translateY: this.state.translateY }] } }
162
170
  : null;
@@ -181,7 +189,10 @@ export class ScrollViewStickyHeader
181
189
  case 'rebuild-interpolation': {
182
190
  // Detach the old listener, build a fresh interpolation onto the shared scroll value, and
183
191
  // wire the settled-value listener (symbiote is always Fabric; RN attaches it only there).
184
- if (this.interpolation !== undefined && this.listenerId !== undefined) {
192
+ if (
193
+ this.interpolation !== undefined &&
194
+ this.listenerId !== undefined
195
+ ) {
185
196
  this.interpolation.removeListener(this.listenerId);
186
197
  this.listenerId = undefined;
187
198
  }
@@ -200,7 +211,8 @@ export class ScrollViewStickyHeader
200
211
  case 'schedule-debounce':
201
212
  // The animated value updates several times per frame; debounce the settled value into the
202
213
  // committed transform so hit detection stays current (a Fabric issue, worse on Android).
203
- if (this.debounceTimer !== undefined) clearTimeout(this.debounceTimer);
214
+ if (this.debounceTimer !== undefined)
215
+ clearTimeout(this.debounceTimer);
204
216
  this.debounceTimer = setTimeout(() => {
205
217
  this.debounceTimer = undefined;
206
218
  this.dispatch({ kind: 'debounce-fired', value: effect.value });
@@ -233,8 +245,13 @@ export class ScrollViewStickyHeader
233
245
  this.changeDetector.markForCheck();
234
246
  }
235
247
 
236
- private readonly animatedValueListener = ({ value }: { value: number | string }): void => {
237
- if (typeof value === 'number') this.dispatch({ kind: 'animated-tick', value });
248
+ private readonly animatedValueListener = ({
249
+ value,
250
+ }: {
251
+ value: number | string;
252
+ }): void => {
253
+ if (typeof value === 'number')
254
+ this.dispatch({ kind: 'animated-tick', value });
238
255
  };
239
256
 
240
257
  private teardown(): void {
@@ -56,7 +56,10 @@ import {
56
56
  type IVListSeparatorContext,
57
57
  } from '../virtualized-list';
58
58
  import { VListOutletDirective } from '../virtualized-list/directives';
59
- import { stableAnchorStyle, SymbioteStyleInputDirective } from '../../primitives';
59
+ import {
60
+ stableAnchorStyle,
61
+ SymbioteStyleInputDirective,
62
+ } from '../../primitives';
60
63
  import {
61
64
  VSectionFooterDirective,
62
65
  VSectionHeaderDirective,
@@ -120,7 +123,9 @@ export type ISectionListInputs<ItemT> = Omit<
120
123
  @Component({
121
124
  selector: 'SectionList',
122
125
  standalone: true,
123
- hostDirectives: [{ directive: SymbioteStyleInputDirective, inputs: ['style'] }],
126
+ hostDirectives: [
127
+ { directive: SymbioteStyleInputDirective, inputs: ['style'] },
128
+ ],
124
129
  imports: [
125
130
  VirtualizedSectionList,
126
131
  VSectionItemDirective,
@@ -138,6 +143,7 @@ export type ISectionListInputs<ItemT> = Omit<
138
143
  <VirtualizedSectionList
139
144
  [sections]="sections"
140
145
  [keyExtractor]="keyExtractor"
146
+ [getItemLayout]="getItemLayout"
141
147
  [stickySectionHeadersEnabled]="stickySectionHeadersEnabled"
142
148
  [extraData]="extraData"
143
149
  (endReached)="endReached.emit($event)"
@@ -182,8 +188,12 @@ export type ISectionListInputs<ItemT> = Omit<
182
188
  [accessibilityElementsHidden]="accessibilityElementsHidden"
183
189
  [accessibilityIgnoresInvertColors]="accessibilityIgnoresInvertColors"
184
190
  [accessibilityLanguage]="accessibilityLanguage"
185
- [accessibilityRespondsToUserInteraction]="accessibilityRespondsToUserInteraction"
186
- [accessibilityShowsLargeContentViewer]="accessibilityShowsLargeContentViewer"
191
+ [accessibilityRespondsToUserInteraction]="
192
+ accessibilityRespondsToUserInteraction
193
+ "
194
+ [accessibilityShowsLargeContentViewer]="
195
+ accessibilityShowsLargeContentViewer
196
+ "
187
197
  [accessibilityLargeContentTitle]="accessibilityLargeContentTitle"
188
198
  (accessibilityAction)="accessibilityAction.emit($event)"
189
199
  (accessibilityTap)="accessibilityTap.emit($event)"
@@ -214,7 +224,13 @@ export type ISectionListInputs<ItemT> = Omit<
214
224
  >
215
225
  <ng-container
216
226
  [vListOutlet]="sectionItemDir?.templateRef"
217
- [vListOutletContext]="{ $implicit: item, item, index, section, separators }"
227
+ [vListOutletContext]="{
228
+ $implicit: item,
229
+ item,
230
+ index,
231
+ section,
232
+ separators,
233
+ }"
218
234
  ></ng-container>
219
235
  </ng-template>
220
236
  @if (sectionHeaderDir !== undefined) {
@@ -235,17 +251,23 @@ export type ISectionListInputs<ItemT> = Omit<
235
251
  }
236
252
  @if (sectionSeparatorDir !== undefined) {
237
253
  <ng-template vSectionSeparator>
238
- <ng-container [vListOutlet]="sectionSeparatorDir.templateRef"></ng-container>
254
+ <ng-container
255
+ [vListOutlet]="sectionSeparatorDir.templateRef"
256
+ ></ng-container>
239
257
  </ng-template>
240
258
  }
241
259
  @if (listHeaderDir !== undefined) {
242
260
  <ng-template vListHeader>
243
- <ng-container [vListOutlet]="listHeaderDir.templateRef"></ng-container>
261
+ <ng-container
262
+ [vListOutlet]="listHeaderDir.templateRef"
263
+ ></ng-container>
244
264
  </ng-template>
245
265
  }
246
266
  @if (listFooterDir !== undefined) {
247
267
  <ng-template vListFooter>
248
- <ng-container [vListOutlet]="listFooterDir.templateRef"></ng-container>
268
+ <ng-container
269
+ [vListOutlet]="listFooterDir.templateRef"
270
+ ></ng-container>
249
271
  </ng-template>
250
272
  }
251
273
  @if (listEmptyDir !== undefined) {
@@ -262,7 +284,9 @@ export type ISectionListInputs<ItemT> = Omit<
262
284
  >
263
285
  <ng-container
264
286
  [vListOutlet]="itemSeparatorDir.templateRef"
265
- [vListOutletContext]="itemSeparatorContext(highlighted, leadingItem, trailingItem)"
287
+ [vListOutletContext]="
288
+ itemSeparatorContext(highlighted, leadingItem, trailingItem)
289
+ "
266
290
  ></ng-container>
267
291
  </ng-template>
268
292
  }
@@ -274,11 +298,21 @@ export class SectionList<ItemT = unknown>
274
298
  {
275
299
  @Input({ required: true }) sections!: ReadonlyArray<ISection<ItemT>>;
276
300
  @Input() keyExtractor?: (item: ItemT, index: number) => string;
301
+ // Passed through FLAT (sections array + flat entry index); VirtualizedSectionList owns the
302
+ // sections-vs-entries wrapper - see its entryItemLayout getter.
303
+ @Input() getItemLayout?: (
304
+ data: ReadonlyArray<ISection<ItemT>> | null,
305
+ index: number,
306
+ ) => { length: number; offset: number; index: number };
277
307
  @Input() stickySectionHeadersEnabled?: boolean;
278
308
  @Input() extraData?: unknown;
279
- @Output() readonly endReached = new EventEmitter<{ distanceFromEnd: number }>();
309
+ @Output() readonly endReached = new EventEmitter<{
310
+ distanceFromEnd: number;
311
+ }>();
280
312
  @Input() onEndReachedThreshold?: number;
281
- @Output() readonly startReached = new EventEmitter<{ distanceFromStart: number }>();
313
+ @Output() readonly startReached = new EventEmitter<{
314
+ distanceFromStart: number;
315
+ }>();
282
316
  @Input() onStartReachedThreshold?: number;
283
317
  @Output() readonly refresh = new EventEmitter<void>();
284
318
  @Input() refreshing?: boolean | null;
@@ -331,8 +365,10 @@ export class SectionList<ItemT = unknown>
331
365
  @Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
332
366
  @Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
333
367
  @Input() accessibilityLabelledBy?: string | string[];
334
- @Input() importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
335
- @Input() accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
368
+ @Input()
369
+ importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
370
+ @Input()
371
+ accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
336
372
  @Input() screenReaderFocusable?: boolean;
337
373
  @Input() accessibilityViewIsModal?: boolean;
338
374
  @Input() accessibilityElementsHidden?: boolean;
@@ -363,18 +399,24 @@ export class SectionList<ItemT = unknown>
363
399
 
364
400
  // The app's section/list authoring templates, captured from the app's DIRECT projected content
365
401
  // (single hop — always resolves) and re-stamped onto the inner VirtualizedSectionList above.
366
- @ContentChild(VSectionItemDirective) sectionItemDir?: VSectionItemDirective<ItemT>;
367
- @ContentChild(VSectionHeaderDirective) sectionHeaderDir?: VSectionHeaderDirective<ItemT>;
368
- @ContentChild(VSectionFooterDirective) sectionFooterDir?: VSectionFooterDirective<ItemT>;
369
- @ContentChild(VSectionSeparatorDirective) sectionSeparatorDir?: VSectionSeparatorDirective;
402
+ @ContentChild(VSectionItemDirective)
403
+ sectionItemDir?: VSectionItemDirective<ItemT>;
404
+ @ContentChild(VSectionHeaderDirective)
405
+ sectionHeaderDir?: VSectionHeaderDirective<ItemT>;
406
+ @ContentChild(VSectionFooterDirective)
407
+ sectionFooterDir?: VSectionFooterDirective<ItemT>;
408
+ @ContentChild(VSectionSeparatorDirective)
409
+ sectionSeparatorDir?: VSectionSeparatorDirective;
370
410
  @ContentChild(VListHeaderDirective) listHeaderDir?: VListHeaderDirective;
371
411
  @ContentChild(VListFooterDirective) listFooterDir?: VListFooterDirective;
372
412
  @ContentChild(VListEmptyDirective) listEmptyDir?: VListEmptyDirective;
373
- @ContentChild(VListSeparatorDirective) itemSeparatorDir?: VListSeparatorDirective<ItemT>;
413
+ @ContentChild(VListSeparatorDirective)
414
+ itemSeparatorDir?: VListSeparatorDirective<ItemT>;
374
415
 
375
416
  // The composed inner VirtualizedSectionList. Its instance IS an IVirtualizedSectionListHandle, so
376
417
  // SectionList's handle delegates straight to it. Available from ngAfterViewInit; reads lazily.
377
- @ViewChild(VirtualizedSectionList) private list?: VirtualizedSectionList<ItemT>;
418
+ @ViewChild(VirtualizedSectionList)
419
+ private list?: VirtualizedSectionList<ItemT>;
378
420
 
379
421
  // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
380
422
  // onto (see anchorHostStyle's doc comment) — NOT `list` above, which targets the real inner
@@ -20,10 +20,18 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
20
20
  @Component({
21
21
  selector: 'Switch',
22
22
  standalone: true,
23
- hostDirectives: [{ directive: SymbioteStyleInputDirective, inputs: ['style'] }],
23
+ hostDirectives: [
24
+ { directive: SymbioteStyleInputDirective, inputs: ['style'] },
25
+ ],
24
26
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
25
27
  imports: [SwitchHost, SymbioteHostPropsDirective],
26
- providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Switch), multi: true }],
28
+ providers: [
29
+ {
30
+ provide: NG_VALUE_ACCESSOR,
31
+ useExisting: forwardRef(() => Switch),
32
+ multi: true,
33
+ },
34
+ ],
27
35
  inputs: [
28
36
  'value',
29
37
  'disabled',
@@ -60,7 +68,10 @@ export class Switch extends SwitchBase implements DoCheck {
60
68
 
61
69
  protected readonly platform = {
62
70
  snapBackCommand: 'setNativeValue',
63
- trackColorProps: (value: boolean, trackColor?: { false?: string; true?: string }) => ({
71
+ trackColorProps: (
72
+ value: boolean,
73
+ trackColor?: { false?: string; true?: string },
74
+ ) => ({
64
75
  trackColorForFalse: trackColor?.false,
65
76
  trackColorForTrue: trackColor?.true,
66
77
  trackTintColor: value ? trackColor?.true : trackColor?.false,
@@ -20,10 +20,18 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
20
20
  @Component({
21
21
  selector: 'Switch',
22
22
  standalone: true,
23
- hostDirectives: [{ directive: SymbioteStyleInputDirective, inputs: ['style'] }],
23
+ hostDirectives: [
24
+ { directive: SymbioteStyleInputDirective, inputs: ['style'] },
25
+ ],
24
26
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
25
27
  imports: [SwitchHost, SymbioteHostPropsDirective],
26
- providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Switch), multi: true }],
28
+ providers: [
29
+ {
30
+ provide: NG_VALUE_ACCESSOR,
31
+ useExisting: forwardRef(() => Switch),
32
+ multi: true,
33
+ },
34
+ ],
27
35
  inputs: [
28
36
  'value',
29
37
  'disabled',
@@ -60,7 +68,10 @@ export class Switch extends SwitchBase implements DoCheck {
60
68
 
61
69
  protected readonly platform = {
62
70
  snapBackCommand: 'setValue',
63
- trackColorProps: (_value: boolean, trackColor?: { false?: string; true?: string }) => ({
71
+ trackColorProps: (
72
+ _value: boolean,
73
+ trackColor?: { false?: string; true?: string },
74
+ ) => ({
64
75
  onTintColor: trackColor?.true,
65
76
  tintColor: trackColor?.false,
66
77
  }),
@@ -9,7 +9,10 @@ import {
9
9
  type OnChanges,
10
10
  } from '@angular/core';
11
11
  import type { ControlValueAccessor } from '@angular/forms';
12
- import { renderSwitch, resolveAccessibilityProps } from '@symbiote-native/components';
12
+ import {
13
+ renderSwitch,
14
+ resolveAccessibilityProps,
15
+ } from '@symbiote-native/components';
13
16
  import type {
14
17
  ISwitchPlatform,
15
18
  ISwitchProps,
@@ -33,7 +36,10 @@ import {
33
36
  type IViewStyle,
34
37
  } from '@symbiote-native/engine';
35
38
 
36
- export type { ISwitchProps, ISwitchTrackColor } from '@symbiote-native/components';
39
+ export type {
40
+ ISwitchProps,
41
+ ISwitchTrackColor,
42
+ } from '@symbiote-native/components';
37
43
 
38
44
  export type ISwitchInputs = Pick<
39
45
  ISwitchProps,
@@ -88,7 +94,9 @@ function isSwitchEvent(event: unknown): event is ISymbioteEvent {
88
94
  // concrete subclasses are real @Components — ngc's AOT build (NG2007) requires it the moment the
89
95
  // base itself carries decorated members (@Output() here), mirroring ScrollViewBase.
90
96
  @Directive()
91
- export abstract class SwitchBase implements ISwitchInputs, ControlValueAccessor, OnChanges {
97
+ export abstract class SwitchBase
98
+ implements ISwitchInputs, ControlValueAccessor, OnChanges
99
+ {
92
100
  // The value/change lifecycle as real Angular events: `(valueChange)="onToggle($event)"`, not
93
101
  // `[onValueChange]="onToggle"` — mirrors Pressable's press/hover @Output() conversion. Unlike
94
102
  // Pressable's createPressHandlers, handleChange() below calls `.emit()` unconditionally: it needs
@@ -191,7 +199,10 @@ export abstract class SwitchBase implements ISwitchInputs, ControlValueAccessor,
191
199
  if (next === undefined) return;
192
200
 
193
201
  this.valueChange.emit(next);
194
- this.switchState = switchReducer(this.switchState, { type: 'native-reported', value: next });
202
+ this.switchState = switchReducer(this.switchState, {
203
+ type: 'native-reported',
204
+ value: next,
205
+ });
195
206
 
196
207
  const hostNode = readSwitchNode(node);
197
208
  if (hostNode === null) return;