@symbiote-native/angular 3.1.3 → 3.3.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 (165) hide show
  1. package/babel-register-composed.cjs +1 -0
  2. package/build/angular/accessibility-events.d.ts +12 -0
  3. package/build/angular/accessibility-events.js +28 -0
  4. package/build/angular/accessibility-events.js.map +1 -0
  5. package/build/angular/accessibility-inputs.d.ts +40 -0
  6. package/build/angular/accessibility-inputs.js +130 -0
  7. package/build/angular/accessibility-inputs.js.map +1 -0
  8. package/build/angular/bootstrap.d.ts +3 -1
  9. package/build/angular/bootstrap.js +4 -1
  10. package/build/angular/bootstrap.js.map +1 -1
  11. package/build/angular/components/checkbox-props.d.ts +2 -0
  12. package/build/angular/components/checkbox-props.js +2 -0
  13. package/build/angular/components/checkbox-props.js.map +1 -0
  14. package/build/angular/components/flat-list/flat-list-template.d.ts +1 -0
  15. package/build/angular/components/flat-list/flat-list-template.js +158 -0
  16. package/build/angular/components/flat-list/flat-list-template.js.map +1 -0
  17. package/build/angular/components/flat-list/index.d.ts +6 -61
  18. package/build/angular/components/flat-list/index.js +26 -463
  19. package/build/angular/components/flat-list/index.js.map +1 -1
  20. package/build/angular/components/keyboard-avoiding-view/index.d.ts +4 -40
  21. package/build/angular/components/keyboard-avoiding-view/index.js +7 -131
  22. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
  23. package/build/angular/components/modal/index.d.ts +5 -41
  24. package/build/angular/components/modal/index.js +8 -133
  25. package/build/angular/components/modal/index.js.map +1 -1
  26. package/build/angular/components/section-list/index.d.ts +4 -51
  27. package/build/angular/components/section-list/index.js +18 -335
  28. package/build/angular/components/section-list/index.js.map +1 -1
  29. package/build/angular/components/virtualized-list/cell-registry.d.ts +35 -0
  30. package/build/angular/components/virtualized-list/cell-registry.js +133 -0
  31. package/build/angular/components/virtualized-list/cell-registry.js.map +1 -0
  32. package/build/angular/components/virtualized-list/index.d.ts +18 -253
  33. package/build/angular/components/virtualized-list/index.js +112 -1178
  34. package/build/angular/components/virtualized-list/index.js.map +1 -1
  35. package/build/angular/components/virtualized-list/list-a11y-forward.d.ts +1 -0
  36. package/build/angular/components/virtualized-list/list-a11y-forward.js +42 -0
  37. package/build/angular/components/virtualized-list/list-a11y-forward.js.map +1 -0
  38. package/build/angular/components/virtualized-list/list-bags.d.ts +32 -0
  39. package/build/angular/components/virtualized-list/list-bags.js +120 -0
  40. package/build/angular/components/virtualized-list/list-bags.js.map +1 -0
  41. package/build/angular/components/virtualized-list/list-effects.d.ts +31 -0
  42. package/build/angular/components/virtualized-list/list-effects.js +83 -0
  43. package/build/angular/components/virtualized-list/list-effects.js.map +1 -0
  44. package/build/angular/components/virtualized-list/list-events.d.ts +17 -0
  45. package/build/angular/components/virtualized-list/list-events.js +34 -0
  46. package/build/angular/components/virtualized-list/list-events.js.map +1 -0
  47. package/build/angular/components/virtualized-list/list-handle.d.ts +35 -0
  48. package/build/angular/components/virtualized-list/list-handle.js +61 -0
  49. package/build/angular/components/virtualized-list/list-handle.js.map +1 -0
  50. package/build/angular/components/virtualized-list/list-inputs.d.ts +78 -0
  51. package/build/angular/components/virtualized-list/list-inputs.js +150 -0
  52. package/build/angular/components/virtualized-list/list-inputs.js.map +1 -0
  53. package/build/angular/components/virtualized-list/list-props.d.ts +58 -0
  54. package/build/angular/components/virtualized-list/list-props.js +3 -0
  55. package/build/angular/components/virtualized-list/list-props.js.map +1 -0
  56. package/build/angular/components/virtualized-list/list-styles.d.ts +23 -0
  57. package/build/angular/components/virtualized-list/list-styles.js +26 -0
  58. package/build/angular/components/virtualized-list/list-styles.js.map +1 -0
  59. package/build/angular/components/virtualized-list/list-template.d.ts +1 -0
  60. package/build/angular/components/virtualized-list/list-template.js +176 -0
  61. package/build/angular/components/virtualized-list/list-template.js.map +1 -0
  62. package/build/angular/components/virtualized-section-list/index.d.ts +13 -70
  63. package/build/angular/components/virtualized-section-list/index.js +22 -455
  64. package/build/angular/components/virtualized-section-list/index.js.map +1 -1
  65. package/build/angular/components/virtualized-section-list/section-list-template.d.ts +1 -0
  66. package/build/angular/components/virtualized-section-list/section-list-template.js +107 -0
  67. package/build/angular/components/virtualized-section-list/section-list-template.js.map +1 -0
  68. package/build/angular/components.d.ts +1 -0
  69. package/build/angular/components.js.map +1 -1
  70. package/build/angular/descriptor-to-angular/descriptor-host.d.ts +8 -0
  71. package/build/angular/descriptor-to-angular/descriptor-host.js +38 -0
  72. package/build/angular/descriptor-to-angular/descriptor-host.js.map +1 -0
  73. package/build/angular/descriptor-to-angular/index.d.ts +1 -0
  74. package/build/angular/descriptor-to-angular/index.js +4 -0
  75. package/build/angular/descriptor-to-angular/index.js.map +1 -1
  76. package/build/angular/element-base.d.ts +153 -0
  77. package/build/angular/element-base.js +272 -0
  78. package/build/angular/element-base.js.map +1 -0
  79. package/build/angular/elements-controls.d.ts +165 -0
  80. package/build/angular/elements-controls.js +382 -0
  81. package/build/angular/elements-controls.js.map +1 -0
  82. package/build/angular/elements-layout.d.ts +122 -0
  83. package/build/angular/elements-layout.js +242 -0
  84. package/build/angular/elements-layout.js.map +1 -0
  85. package/build/angular/elements-press.d.ts +96 -0
  86. package/build/angular/elements-press.js +227 -0
  87. package/build/angular/elements-press.js.map +1 -0
  88. package/build/angular/elements.d.ts +6 -516
  89. package/build/angular/elements.js +8 -1091
  90. package/build/angular/elements.js.map +1 -1
  91. package/build/angular/finalization-registry.d.ts +4 -0
  92. package/build/angular/finalization-registry.js +17 -0
  93. package/build/angular/finalization-registry.js.map +1 -0
  94. package/build/angular/host-instance/index.d.ts +2 -0
  95. package/build/angular/host-instance/index.js +4 -4
  96. package/build/angular/host-instance/index.js.map +1 -1
  97. package/build/angular/index.d.ts +9 -4
  98. package/build/angular/index.js +8 -3
  99. package/build/angular/index.js.map +1 -1
  100. package/build/angular/native-view-base.d.ts +27 -0
  101. package/build/angular/native-view-base.js +69 -0
  102. package/build/angular/native-view-base.js.map +1 -0
  103. package/build/angular/render/index.d.ts +2 -1
  104. package/build/angular/render/index.js +46 -73
  105. package/build/angular/render/index.js.map +1 -1
  106. package/build/angular/services/connect-watched-signal.d.ts +2 -0
  107. package/build/angular/services/connect-watched-signal.js +9 -0
  108. package/build/angular/services/connect-watched-signal.js.map +1 -0
  109. package/build/angular/services/create-event-value-hook.d.ts +3 -0
  110. package/build/angular/services/create-event-value-hook.js +17 -0
  111. package/build/angular/services/create-event-value-hook.js.map +1 -0
  112. package/build/angular/services/create-resource-hook.d.ts +7 -0
  113. package/build/angular/services/create-resource-hook.js +20 -0
  114. package/build/angular/services/create-resource-hook.js.map +1 -0
  115. package/build/angular/services/index.d.ts +5 -0
  116. package/build/angular/services/index.js +5 -0
  117. package/build/angular/services/index.js.map +1 -1
  118. package/build/angular/services/inject-event.d.ts +6 -0
  119. package/build/angular/services/inject-event.js +24 -0
  120. package/build/angular/services/inject-event.js.map +1 -0
  121. package/build/angular/services/permissions-service-base.d.ts +14 -0
  122. package/build/angular/services/permissions-service-base.js +38 -0
  123. package/build/angular/services/permissions-service-base.js.map +1 -0
  124. package/package.json +6 -6
  125. package/src/accessibility-events.ts +13 -0
  126. package/src/accessibility-inputs.ts +89 -0
  127. package/src/bootstrap.ts +6 -2
  128. package/src/components/checkbox-props.ts +12 -0
  129. package/src/components/flat-list/flat-list-template.ts +159 -0
  130. package/src/components/flat-list/index.ts +30 -307
  131. package/src/components/keyboard-avoiding-view/index.ts +4 -78
  132. package/src/components/modal/index.ts +7 -80
  133. package/src/components/section-list/index.ts +21 -172
  134. package/src/components/virtualized-list/cell-registry.ts +199 -0
  135. package/src/components/virtualized-list/index.ts +145 -1181
  136. package/src/components/virtualized-list/list-a11y-forward.ts +42 -0
  137. package/src/components/virtualized-list/list-bags.ts +142 -0
  138. package/src/components/virtualized-list/list-effects.ts +116 -0
  139. package/src/components/virtualized-list/list-events.ts +31 -0
  140. package/src/components/virtualized-list/list-handle.ts +92 -0
  141. package/src/components/virtualized-list/list-inputs.ts +149 -0
  142. package/src/components/virtualized-list/list-props.ts +81 -0
  143. package/src/components/virtualized-list/list-styles.ts +62 -0
  144. package/src/components/virtualized-list/list-template.ts +175 -0
  145. package/src/components/virtualized-section-list/index.ts +40 -351
  146. package/src/components/virtualized-section-list/section-list-template.ts +108 -0
  147. package/src/components.ts +1 -0
  148. package/src/descriptor-to-angular/descriptor-host.ts +27 -0
  149. package/src/descriptor-to-angular/index.ts +5 -0
  150. package/src/element-base.ts +272 -0
  151. package/src/elements-controls.ts +282 -0
  152. package/src/elements-layout.ts +141 -0
  153. package/src/elements-press.ts +145 -0
  154. package/src/elements.ts +73 -788
  155. package/src/finalization-registry.ts +18 -0
  156. package/src/host-instance/index.ts +4 -4
  157. package/src/index.ts +18 -2
  158. package/src/native-view-base.ts +86 -0
  159. package/src/render/index.ts +76 -77
  160. package/src/services/connect-watched-signal.ts +15 -0
  161. package/src/services/create-event-value-hook.ts +24 -0
  162. package/src/services/create-resource-hook.ts +32 -0
  163. package/src/services/index.ts +5 -0
  164. package/src/services/inject-event.ts +52 -0
  165. package/src/services/permissions-service-base.ts +46 -0
@@ -0,0 +1,108 @@
1
+ // One synthesized `vListItem` template switches on the flattened entry kind and stamps the matching
2
+ // app template, since the inner list renders every cell through a single one
3
+
4
+ import { LIST_ACCESSIBILITY_FORWARD } from '../virtualized-list/list-a11y-forward';
5
+
6
+ export const VIRTUALIZED_SECTION_LIST_TEMPLATE = `
7
+ <VirtualizedList
8
+ [data]="flatEntries"
9
+ [getItem]="getEntry"
10
+ [getItemCount]="getEntryCount"
11
+ [keyExtractor]="entryKeyExtractor"
12
+ [getItemLayout]="entryItemLayout"
13
+ [stickyHeaderIndices]="stickyHeaderIndices"
14
+ [extraData]="extraData"
15
+ [inverted]="inverted"
16
+ [refreshing]="refreshing"
17
+ [progressViewOffset]="progressViewOffset"
18
+ [refreshRequested]="refreshRequested ?? refresh.observed"
19
+ (refresh)="resolvedOnRefresh?.()"
20
+ (endReached)="resolvedOnEndReached?.($event)"
21
+ [onEndReachedThreshold]="onEndReachedThreshold"
22
+ (startReached)="resolvedOnStartReached?.($event)"
23
+ [onStartReachedThreshold]="onStartReachedThreshold"
24
+ [initialNumToRender]="initialNumToRender"
25
+ [initialScrollIndex]="initialScrollIndex"
26
+ [maxToRenderPerBatch]="maxToRenderPerBatch"
27
+ [updateCellsBatchingPeriod]="updateCellsBatchingPeriod"
28
+ [windowSize]="windowSize"
29
+ [maintainVisibleContentPosition]="maintainVisibleContentPosition"
30
+ [onScroll]="onScroll"
31
+ [onScrollBeginDrag]="onScrollBeginDrag"
32
+ [onScrollEndDrag]="onScrollEndDrag"
33
+ [onMomentumScrollBegin]="onMomentumScrollBegin"
34
+ [onMomentumScrollEnd]="onMomentumScrollEnd"
35
+ [scrollEventThrottle]="scrollEventThrottle"
36
+ [keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
37
+ [keyboardDismissMode]="keyboardDismissMode"
38
+ [removeClippedSubviews]="removeClippedSubviews"
39
+ [nestedScrollEnabled]="nestedScrollEnabled"
40
+ [style]="resolvedStyle"
41
+ [contentContainerStyle]="contentContainerStyle"
42
+ [testID]="testID"
43
+ [nativeID]="nativeID"
44
+ ${LIST_ACCESSIBILITY_FORWARD}
45
+ >
46
+ <!-- The single synthesized cell template: dispatch per flattened entry tag -->
47
+ <ng-template vListItem let-entry let-separators="separators">
48
+ @switch (entryKind(entry)) {
49
+ @case ('header') {
50
+ <ng-container
51
+ [vListOutlet]="sectionHeaderDir?.templateRef"
52
+ [vListOutletContext]="sectionContextOf(entry)"
53
+ ></ng-container>
54
+ }
55
+ @case ('footer') {
56
+ <ng-container
57
+ [vListOutlet]="sectionFooterDir?.templateRef"
58
+ [vListOutletContext]="sectionContextOf(entry)"
59
+ ></ng-container>
60
+ }
61
+ @case ('section-separator') {
62
+ <ng-container
63
+ [vListOutlet]="sectionSeparatorDir?.templateRef"
64
+ ></ng-container>
65
+ }
66
+ @case ('item') {
67
+ <ng-container
68
+ [vListOutlet]="sectionItemDir?.templateRef"
69
+ [vListOutletContext]="itemContextOf(entry, separators)"
70
+ ></ng-container>
71
+ }
72
+ }
73
+ </ng-template>
74
+
75
+ <!-- List-level slots: forward the app's directives to the inner list -->
76
+ @if (listHeaderDir !== undefined) {
77
+ <ng-template vListHeader>
78
+ <ng-container [vListOutlet]="listHeaderDir.templateRef"></ng-container>
79
+ </ng-template>
80
+ }
81
+ @if (listFooterDir !== undefined) {
82
+ <ng-template vListFooter>
83
+ <ng-container [vListOutlet]="listFooterDir.templateRef"></ng-container>
84
+ </ng-template>
85
+ }
86
+ @if (listEmptyDir !== undefined) {
87
+ <ng-template vListEmpty>
88
+ <ng-container [vListOutlet]="listEmptyDir.templateRef"></ng-container>
89
+ </ng-template>
90
+ }
91
+ <!-- Item separator: unwrap each flattened entry back to its item -->
92
+ @if (itemSeparatorDir !== undefined) {
93
+ <ng-template
94
+ vListSeparator
95
+ let-highlighted
96
+ let-leadingItem="leadingItem"
97
+ let-trailingItem="trailingItem"
98
+ >
99
+ <ng-container
100
+ [vListOutlet]="itemSeparatorDir.templateRef"
101
+ [vListOutletContext]="
102
+ itemSeparatorContextOf(highlighted, leadingItem, trailingItem)
103
+ "
104
+ ></ng-container>
105
+ </ng-template>
106
+ }
107
+ </VirtualizedList>
108
+ `;
package/src/components.ts CHANGED
@@ -123,6 +123,7 @@ export type {
123
123
  // its place. RN's Button has no statics, so the name exports nothing at all now; the prop type
124
124
  // stays, for a component forwarding a bag.
125
125
  export type { IButtonProps } from './components/button-props';
126
+ export type { ICheckboxProps } from './components/checkbox-props';
126
127
  export {
127
128
  VirtualizedList,
128
129
  VListEmptyDirective,
@@ -0,0 +1,27 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ import { SymbioteHostPropsDirective, ViewHost } from '../primitives';
4
+ import { DescriptorOutlet } from './index';
5
+
6
+ // Angular cannot project content into a node a Renderer2 loop built, so the root is a template
7
+ // `<view>` that takes the descriptor's props, and only the native leaf goes through the outlet
8
+ @Component({
9
+ selector: 'symbiote-descriptor-host',
10
+ standalone: true,
11
+ imports: [DescriptorOutlet, SymbioteHostPropsDirective, ViewHost],
12
+ changeDetection: ChangeDetectionStrategy.OnPush,
13
+ template: `<view [symbioteHostProps]="node.props">
14
+ @if (leafOf(node); as leaf) {
15
+ <symbiote-descriptor-outlet [node]="leaf" />
16
+ }
17
+ <ng-content />
18
+ </view>`,
19
+ })
20
+ export class DescriptorHost {
21
+ @Input({ required: true }) node!: IDescriptor;
22
+
23
+ // A native wrapper descriptor carries one own child at most, its absolute-fill native leaf
24
+ leafOf(descriptor: IDescriptor): IDescriptor | undefined {
25
+ return descriptor.children.find(child => typeof child !== 'string');
26
+ }
27
+ }
@@ -50,6 +50,11 @@ export class DescriptorOutlet implements OnChanges, OnDestroy {
50
50
  inject<ElementRef<unknown>>(ElementRef).nativeElement;
51
51
  private rendered: IRenderedElement | undefined;
52
52
 
53
+ // The host node of the root element, for a component that calls a function of its native view
54
+ get rootNode(): unknown {
55
+ return this.rendered?.node ?? null;
56
+ }
57
+
53
58
  ngOnChanges(changes: SimpleChanges): void {
54
59
  if (!('node' in changes) || this.node === undefined) return;
55
60
  this.rendered = this.patchRoot(this.rendered, this.node);
@@ -0,0 +1,272 @@
1
+ import {
2
+ ChangeDetectorRef,
3
+ Directive,
4
+ ElementRef,
5
+ ErrorHandler,
6
+ Input,
7
+ Renderer2,
8
+ inject,
9
+ } from '@angular/core';
10
+ import type {
11
+ OnChanges,
12
+ OnDestroy,
13
+ OnInit,
14
+ SimpleChanges,
15
+ } from '@angular/core';
16
+ import { VALUE_CHANGE_EVENT } from './renderer/value-change';
17
+ import {
18
+ registerViewFlush,
19
+ unregisterViewFlush,
20
+ } from './change-detection-flush';
21
+ import type { IElementProps } from './element-props';
22
+
23
+ /**
24
+ * The shared half of every element directive: the prop surface all tags accept, and the ONE
25
+ * generic forward that puts a claimed binding back on the engine node.
26
+ *
27
+ * The forward is the deciding fact of this whole route. A binding claimed by a directive input
28
+ * never reaches `Renderer2.setProperty` on its own — Angular writes it to the directive instance
29
+ * — so without this loop a bare tag commits nothing at all. It stays generic on purpose: per-prop
30
+ * forwarding code is a component wrapper by another name, which is what this migration removes.
31
+ * `renderer.setProperty` lands in the adapter's own renderer, which routes the value through the
32
+ * engine's `routeProp` and applies the `id` -> `nativeID` alias.
33
+ */
34
+ @Directive()
35
+ export abstract class SymbioteElement implements OnChanges {
36
+ private readonly renderer = inject(Renderer2);
37
+ protected readonly host = inject(ElementRef);
38
+
39
+ // NO `ChangeDetectorRef` HERE, and its absence is the point. This class is instantiated once per
40
+ // ELEMENT, so injecting one cost ~1.0-2.4 us on every tag of a screen to serve the few that carry
41
+ // an `on*` prop — ten thousand `ViewRef`s on a thousand-row create, read by none of them
42
+ // (`core/engine/cpp/tests/js/angular-directive-cost.itest.ts`). `SymbioteCallbackHost` owns one and
43
+ // MATCHES on the callback attributes instead, registering it against the node; `markViewFor` is how
44
+ // the wrapper reaches it. The inputs did not move, so nothing about this file's public surface did.
45
+
46
+ // No `on*` wrapper here: `SymbioteRenderer.setProperty` handles it. Most directives are withheld
47
+ // from runtime matching (`./runtime-matching`), so a binding reaches the renderer through
48
+ // `ɵɵproperty` without passing through any directive — the renderer is reached by every path.
49
+
50
+ @Input() testID?: IElementProps['testID'];
51
+ @Input() nativeID?: IElementProps['nativeID'];
52
+ @Input() id?: IElementProps['id'];
53
+ @Input() accessible?: IElementProps['accessible'];
54
+ @Input() accessibilityLabel?: IElementProps['accessibilityLabel'];
55
+ @Input() accessibilityHint?: IElementProps['accessibilityHint'];
56
+ @Input() accessibilityRole?: IElementProps['accessibilityRole'];
57
+ @Input() accessibilityState?: IElementProps['accessibilityState'];
58
+ @Input() accessibilityValue?: IElementProps['accessibilityValue'];
59
+ @Input() accessibilityActions?: IElementProps['accessibilityActions'];
60
+ @Input() accessibilityLabelledBy?: IElementProps['accessibilityLabelledBy'];
61
+ @Input()
62
+ importantForAccessibility?: IElementProps['importantForAccessibility'];
63
+ @Input() accessibilityLiveRegion?: IElementProps['accessibilityLiveRegion'];
64
+ @Input() screenReaderFocusable?: IElementProps['screenReaderFocusable'];
65
+ @Input() accessibilityViewIsModal?: IElementProps['accessibilityViewIsModal'];
66
+ @Input()
67
+ accessibilityElementsHidden?: IElementProps['accessibilityElementsHidden'];
68
+ @Input()
69
+ accessibilityIgnoresInvertColors?: IElementProps['accessibilityIgnoresInvertColors'];
70
+ @Input() accessibilityLanguage?: IElementProps['accessibilityLanguage'];
71
+ @Input()
72
+ accessibilityRespondsToUserInteraction?: IElementProps['accessibilityRespondsToUserInteraction'];
73
+ @Input()
74
+ accessibilityShowsLargeContentViewer?: IElementProps['accessibilityShowsLargeContentViewer'];
75
+ @Input()
76
+ accessibilityLargeContentTitle?: IElementProps['accessibilityLargeContentTitle'];
77
+ @Input() onAccessibilityAction?: IElementProps['onAccessibilityAction'];
78
+ @Input() onAccessibilityTap?: IElementProps['onAccessibilityTap'];
79
+ @Input() onMagicTap?: IElementProps['onMagicTap'];
80
+ @Input() onAccessibilityEscape?: IElementProps['onAccessibilityEscape'];
81
+
82
+ // The web aliases. Native reads only `accessibility*`; the engine folds these on the way in.
83
+ @Input() role?: IElementProps['role'];
84
+ @Input() 'aria-label'?: IElementProps['aria-label'];
85
+ @Input() 'aria-labelledby'?: IElementProps['aria-labelledby'];
86
+ @Input() 'aria-live'?: IElementProps['aria-live'];
87
+ @Input() 'aria-hidden'?: IElementProps['aria-hidden'];
88
+ @Input() 'aria-busy'?: IElementProps['aria-busy'];
89
+ @Input() 'aria-checked'?: IElementProps['aria-checked'];
90
+ @Input() 'aria-disabled'?: IElementProps['aria-disabled'];
91
+ @Input() 'aria-expanded'?: IElementProps['aria-expanded'];
92
+ @Input() 'aria-selected'?: IElementProps['aria-selected'];
93
+ @Input() 'aria-modal'?: IElementProps['aria-modal'];
94
+ @Input() 'aria-valuemax'?: IElementProps['aria-valuemax'];
95
+ @Input() 'aria-valuemin'?: IElementProps['aria-valuemin'];
96
+ @Input() 'aria-valuenow'?: IElementProps['aria-valuenow'];
97
+ @Input() 'aria-valuetext'?: IElementProps['aria-valuetext'];
98
+
99
+ // The responder gates return a boolean, which an Angular `(event)` binding cannot carry back to
100
+ // the caller — so the whole family is inputs, never outputs.
101
+ @Input()
102
+ onStartShouldSetResponder?: IElementProps['onStartShouldSetResponder'];
103
+ @Input()
104
+ onStartShouldSetResponderCapture?: IElementProps['onStartShouldSetResponderCapture'];
105
+ @Input() onMoveShouldSetResponder?: IElementProps['onMoveShouldSetResponder'];
106
+ @Input()
107
+ onMoveShouldSetResponderCapture?: IElementProps['onMoveShouldSetResponderCapture'];
108
+ @Input() onResponderGrant?: IElementProps['onResponderGrant'];
109
+ @Input() onResponderReject?: IElementProps['onResponderReject'];
110
+ @Input() onResponderStart?: IElementProps['onResponderStart'];
111
+ @Input() onResponderMove?: IElementProps['onResponderMove'];
112
+ @Input() onResponderEnd?: IElementProps['onResponderEnd'];
113
+ @Input() onResponderRelease?: IElementProps['onResponderRelease'];
114
+ @Input() onResponderTerminate?: IElementProps['onResponderTerminate'];
115
+ @Input()
116
+ onResponderTerminationRequest?: IElementProps['onResponderTerminationRequest'];
117
+
118
+ @Input() pointerEvents?: IElementProps['pointerEvents'];
119
+ @Input() hitSlop?: IElementProps['hitSlop'];
120
+ @Input() focusable?: IElementProps['focusable'];
121
+ @Input() collapsable?: IElementProps['collapsable'];
122
+ @Input() removeClippedSubviews?: IElementProps['removeClippedSubviews'];
123
+ @Input()
124
+ renderToHardwareTextureAndroid?: IElementProps['renderToHardwareTextureAndroid'];
125
+ @Input() shouldRasterizeIOS?: IElementProps['shouldRasterizeIOS'];
126
+ @Input()
127
+ needsOffscreenAlphaCompositing?: IElementProps['needsOffscreenAlphaCompositing'];
128
+ // `[style]` and `[class]` are declared for the TYPE CHECK only. Withheld from run-time matching, no
129
+ // input claims them, so Angular's own styling engine decomposes them into `setStyle`/`addClass`
130
+ // exactly as on a DOM element - which is why `style` is typed as the object/string that engine can
131
+ // hold. An RN style ARRAY or press-state FUNCTION throws in there ("indexOf is not a function",
132
+ // "Unsupported styling type: function"), so it is `[styleProp]`: an ordinary property binding the
133
+ // renderer routes to `style` whole. A claim would cost a directive instance per element.
134
+ //
135
+ // The press-state callback rides the BASE type rather than `PressableElement` alone: a subclass
136
+ // cannot widen an inherited property, and only the pressables have a `pressed` to read — so on
137
+ // any other tag the engine resolves it at `pressed: false`.
138
+ @Input() style?: IElementProps['style'];
139
+ @Input() styleProp?: IElementProps['styleProp'];
140
+
141
+ // `ɵɵclassMap` hands over a string after joining any static `class=` prefix; `[class.foo]` and
142
+ // `[ngClass]` arrive as `addClass`. The renderer unions both sources (`classStringFor`).
143
+ @Input() class?: string;
144
+
145
+ // The flat-bag spelling of the events below. `(press)` and `[onPress]` are both supported and
146
+ // land in the same place; an app that already holds a handler bag binds the props.
147
+ @Input() onPress?: IElementProps['onPress'];
148
+ @Input() onPressIn?: IElementProps['onPressIn'];
149
+ @Input() onPressOut?: IElementProps['onPressOut'];
150
+ @Input() onPressMove?: IElementProps['onPressMove'];
151
+ @Input() onLongPress?: IElementProps['onLongPress'];
152
+ @Input() onLayout?: IElementProps['onLayout'];
153
+ @Input() onFocus?: IElementProps['onFocus'];
154
+ @Input() onBlur?: IElementProps['onBlur'];
155
+
156
+ ngOnChanges(changes: SimpleChanges): void {
157
+ for (const name of Object.keys(changes)) {
158
+ this.renderer.setProperty(
159
+ this.host.nativeElement,
160
+ name,
161
+ // Unwrapped on purpose: `SymbioteRenderer.setProperty` is where an `on*` value is wrapped
162
+ // now, and it is the call this line makes.
163
+ changes[name]?.currentValue,
164
+ );
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * The base for a tag whose app callback an engine behavior READS BACK inside the same microtask
171
+ * turn — `<text-input>`, `<switch>`, `<refresh-control>`. Zoneless change detection is a macrotask,
172
+ * so without this the behavior sees the PRE-event value and undoes the user; the full mechanism,
173
+ * and why this is `detectChanges()` on one view rather than `ApplicationRef.tick()`, is in
174
+ * `./change-detection-flush`.
175
+ *
176
+ * The directive is the only thing in the adapter that owns a `ChangeDetectorRef` for the view
177
+ * holding the binding, so it hands one to the renderer keyed on its own node.
178
+ */
179
+ @Directive()
180
+ export abstract class ReadBackElement
181
+ extends SymbioteElement
182
+ implements OnDestroy
183
+ {
184
+ // ITS OWN, now that the base has none. Three tags reach this class, so the injection is paid where
185
+ // it is read instead of on every element of a screen.
186
+ private readonly detector = inject(ChangeDetectorRef);
187
+ private readonly errorHandler = inject(ErrorHandler);
188
+
189
+ constructor() {
190
+ super();
191
+ const node: unknown = this.host.nativeElement;
192
+ if (typeof node === 'object' && node !== null) {
193
+ registerViewFlush(node, () => this.flush());
194
+ }
195
+ }
196
+
197
+ // REPORTED, NOT RETHROWN — Angular's own contract. Every other change detection runs inside
198
+ // `ApplicationRef.tick()`, caught by `ErrorHandler` (`SymbioteErrorHandler`); this flush instead
199
+ // runs from a native event dispatch, where an uncaught throw has nowhere to go but `RCTFatal`.
200
+ private flush(): void {
201
+ try {
202
+ this.detector.detectChanges();
203
+ } catch (error: unknown) {
204
+ this.errorHandler.handleError(error);
205
+ }
206
+ }
207
+
208
+ ngOnDestroy(): void {
209
+ const node: unknown = this.host.nativeElement;
210
+ if (typeof node === 'object' && node !== null) unregisterViewFlush(node);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * The `[(value)]` half of a controlled tag — `<switch>` and `<text-input>`.
216
+ *
217
+ * The one `@Output` in this file, and it is FORCED rather than chosen. `[(value)]` desugars to
218
+ * `[value]` + `(valueChange)`, and ngtsc requires both halves to resolve to the SAME target: a
219
+ * declared `value` input beside an event no directive claims is NG8007, "the property and event
220
+ * halves are not bound to the same target". So the sugar this adapter documents cannot work without
221
+ * the output existing, whatever the file header says about events in general.
222
+ *
223
+ * The bridge below is NOT what makes the binding work today, and the comment says so because the
224
+ * obvious reading is wrong: the file header's "an `@Output` CONSUMES the binding" holds for a
225
+ * COMPONENT, and an element is the other case — Angular attaches the renderer listener for the
226
+ * event as well, so `Renderer2.listen` still runs and the engine still hears the change. Measured
227
+ * under JIT by deleting this whole hook: every case in `renderer/two-way-value.test.ts` stayed
228
+ * green, delivery included, and exactly ONCE (nothing double-fires when both paths exist).
229
+ *
230
+ * It is kept because the measurement is JIT-only and this adapter has a recorded case of JIT and
231
+ * AOT resolving a binding differently (`test-harness-false-greens.md` §21). If AOT routes the event
232
+ * exclusively to the output, this hook is the only thing keeping `[(value)]` alive; if it routes to
233
+ * both, it is one extra listener on a control an app explicitly bound. Delete it once something
234
+ * executes the LINKED artifact and shows the renderer listener is attached there too.
235
+ *
236
+ * Opened only when something is SUBSCRIBED: the prop's PRESENCE is what a behavior reads to decide
237
+ * it is controlled, so a `<switch>` given a handler nobody asked for changes how it snaps back.
238
+ * `.observed` is readable from `ngOnInit` because Angular subscribes outputs in the creation pass,
239
+ * before the update pass runs the hook — observed directly (true for the two bound tags, false for
240
+ * an unbound one), not assumed.
241
+ */
242
+ @Directive()
243
+ export abstract class ValueChangeElement
244
+ extends ReadBackElement
245
+ implements OnInit, OnDestroy
246
+ {
247
+ private readonly valueRenderer = inject(Renderer2);
248
+ private readonly valueHost = inject(ElementRef);
249
+ private unlistenValue?: () => void;
250
+
251
+ /** `this.valueChange.observed` — the subclass owns the emitter so its payload type stays exact. */
252
+ protected abstract hasValueSubscriber(): boolean;
253
+
254
+ /** Narrows the engine's unwrapped value to the type THIS tag emits, then emits it. */
255
+ protected abstract emitValue(value: unknown): void;
256
+
257
+ ngOnInit(): void {
258
+ if (!this.hasValueSubscriber()) return;
259
+ this.unlistenValue = this.valueRenderer.listen(
260
+ this.valueHost.nativeElement,
261
+ VALUE_CHANGE_EVENT,
262
+ (value: unknown) => {
263
+ this.emitValue(value);
264
+ },
265
+ );
266
+ }
267
+
268
+ override ngOnDestroy(): void {
269
+ super.ngOnDestroy();
270
+ this.unlistenValue?.();
271
+ }
272
+ }