@symbiote-native/angular 0.7.0 → 1.0.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 (188) hide show
  1. package/README.md +3 -3
  2. package/babel-lower-host-primitives.cjs +636 -0
  3. package/babel-register-composed.cjs +21 -3
  4. package/build/angular/anchor-host-registry.js +3 -1
  5. package/build/angular/anchor-host-registry.js.map +1 -1
  6. package/build/angular/bootstrap.js +1 -1
  7. package/build/angular/bootstrap.js.map +1 -1
  8. package/build/angular/components/activity-indicator/index.android.js +7 -2
  9. package/build/angular/components/activity-indicator/index.android.js.map +1 -1
  10. package/build/angular/components/activity-indicator/index.ios.js +3 -1
  11. package/build/angular/components/activity-indicator/index.ios.js.map +1 -1
  12. package/build/angular/components/activity-indicator/shared.d.ts +1 -1
  13. package/build/angular/components/activity-indicator/shared.js +1 -1
  14. package/build/angular/components/activity-indicator/shared.js.map +1 -1
  15. package/build/angular/components/button.d.ts +3 -0
  16. package/build/angular/components/button.js +22 -5
  17. package/build/angular/components/button.js.map +1 -1
  18. package/build/angular/components/flat-list/index.d.ts +1 -1
  19. package/build/angular/components/flat-list/index.js +47 -14
  20. package/build/angular/components/flat-list/index.js.map +1 -1
  21. package/build/angular/components/image/index.android.js +4 -10
  22. package/build/angular/components/image/index.android.js.map +1 -1
  23. package/build/angular/components/image/index.ios.js +4 -10
  24. package/build/angular/components/image/index.ios.js.map +1 -1
  25. package/build/angular/components/image/shared.d.ts +4 -4
  26. package/build/angular/components/image/shared.js +51 -33
  27. package/build/angular/components/image/shared.js.map +1 -1
  28. package/build/angular/components/image-background.d.ts +3 -0
  29. package/build/angular/components/image-background.js +32 -9
  30. package/build/angular/components/image-background.js.map +1 -1
  31. package/build/angular/components/input-accessory-view/index.d.ts +2 -1
  32. package/build/angular/components/input-accessory-view/index.js +18 -16
  33. package/build/angular/components/input-accessory-view/index.js.map +1 -1
  34. package/build/angular/components/keyboard-avoiding-view/index.d.ts +3 -1
  35. package/build/angular/components/keyboard-avoiding-view/index.js +56 -19
  36. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
  37. package/build/angular/components/modal/index.d.ts +1 -0
  38. package/build/angular/components/modal/index.js +21 -10
  39. package/build/angular/components/modal/index.js.map +1 -1
  40. package/build/angular/components/pressable/index.d.ts +10 -5
  41. package/build/angular/components/pressable/index.js +61 -16
  42. package/build/angular/components/pressable/index.js.map +1 -1
  43. package/build/angular/components/refresh-control/index.d.ts +2 -1
  44. package/build/angular/components/refresh-control/index.js +13 -9
  45. package/build/angular/components/refresh-control/index.js.map +1 -1
  46. package/build/angular/components/safe-area-view/index.d.ts +3 -1
  47. package/build/angular/components/safe-area-view/index.js +22 -20
  48. package/build/angular/components/safe-area-view/index.js.map +1 -1
  49. package/build/angular/components/scroll-view/index.android.js +44 -14
  50. package/build/angular/components/scroll-view/index.android.js.map +1 -1
  51. package/build/angular/components/scroll-view/index.ios.js +13 -5
  52. package/build/angular/components/scroll-view/index.ios.js.map +1 -1
  53. package/build/angular/components/scroll-view/projection.d.ts +32 -0
  54. package/build/angular/components/scroll-view/projection.js +212 -47
  55. package/build/angular/components/scroll-view/projection.js.map +1 -1
  56. package/build/angular/components/scroll-view/shared.d.ts +2 -0
  57. package/build/angular/components/scroll-view/shared.js +48 -11
  58. package/build/angular/components/scroll-view/shared.js.map +1 -1
  59. package/build/angular/components/scroll-view/sticky-header.js +7 -3
  60. package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
  61. package/build/angular/components/section-list/index.d.ts +9 -1
  62. package/build/angular/components/section-list/index.js +72 -18
  63. package/build/angular/components/section-list/index.js.map +1 -1
  64. package/build/angular/components/switch/index.android.js +23 -9
  65. package/build/angular/components/switch/index.android.js.map +1 -1
  66. package/build/angular/components/switch/index.ios.js +23 -9
  67. package/build/angular/components/switch/index.ios.js.map +1 -1
  68. package/build/angular/components/switch/shared.d.ts +1 -1
  69. package/build/angular/components/switch/shared.js +5 -2
  70. package/build/angular/components/switch/shared.js.map +1 -1
  71. package/build/angular/components/text-input.d.ts +8 -2
  72. package/build/angular/components/text-input.js +79 -34
  73. package/build/angular/components/text-input.js.map +1 -1
  74. package/build/angular/components/touchable/index.d.ts +36 -7
  75. package/build/angular/components/touchable/index.js +280 -72
  76. package/build/angular/components/touchable/index.js.map +1 -1
  77. package/build/angular/components/touchable-native-feedback/index.d.ts +2 -1
  78. package/build/angular/components/touchable-native-feedback/index.js +19 -17
  79. package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
  80. package/build/angular/components/virtualized-list/directives.js +4 -0
  81. package/build/angular/components/virtualized-list/directives.js.map +1 -1
  82. package/build/angular/components/virtualized-list/index.d.ts +8 -2
  83. package/build/angular/components/virtualized-list/index.js +207 -61
  84. package/build/angular/components/virtualized-list/index.js.map +1 -1
  85. package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
  86. package/build/angular/components/virtualized-section-list/index.d.ts +21 -1
  87. package/build/angular/components/virtualized-section-list/index.js +100 -22
  88. package/build/angular/components/virtualized-section-list/index.js.map +1 -1
  89. package/build/angular/components.d.ts +2 -2
  90. package/build/angular/components.js +1 -1
  91. package/build/angular/components.js.map +1 -1
  92. package/build/angular/create-portal/index.d.ts +11 -2
  93. package/build/angular/create-portal/index.js +42 -12
  94. package/build/angular/create-portal/index.js.map +1 -1
  95. package/build/angular/create-tunnel/index.js.map +1 -1
  96. package/build/angular/descriptor-to-angular/index.js +3 -1
  97. package/build/angular/descriptor-to-angular/index.js.map +1 -1
  98. package/build/angular/diagnostics.d.ts +33 -0
  99. package/build/angular/diagnostics.js +82 -0
  100. package/build/angular/diagnostics.js.map +1 -0
  101. package/build/angular/gate-demand.d.ts +37 -0
  102. package/build/angular/gate-demand.js +84 -0
  103. package/build/angular/gate-demand.js.map +1 -0
  104. package/build/angular/host-instance/index.js +1 -1
  105. package/build/angular/host-instance/index.js.map +1 -1
  106. package/build/angular/index.d.ts +6 -2
  107. package/build/angular/index.js +16 -1
  108. package/build/angular/index.js.map +1 -1
  109. package/build/angular/modules/animated/create-animated-component.d.ts +1 -0
  110. package/build/angular/modules/animated/create-animated-component.js +41 -18
  111. package/build/angular/modules/animated/create-animated-component.js.map +1 -1
  112. package/build/angular/modules/app-registry/index.js.map +1 -1
  113. package/build/angular/modules/status-bar/index.js +1 -1
  114. package/build/angular/modules/status-bar/index.js.map +1 -1
  115. package/build/angular/primitives/index.d.ts +31 -2
  116. package/build/angular/primitives/index.js +81 -3
  117. package/build/angular/primitives/index.js.map +1 -1
  118. package/build/angular/primitives/shared.d.ts +9 -1
  119. package/build/angular/primitives/shared.js +49 -3
  120. package/build/angular/primitives/shared.js.map +1 -1
  121. package/build/angular/register.d.ts +1 -0
  122. package/build/angular/register.js +35 -0
  123. package/build/angular/register.js.map +1 -0
  124. package/build/angular/render/index.js +51 -6
  125. package/build/angular/render/index.js.map +1 -1
  126. package/build/angular/renderer/index.d.ts +1 -0
  127. package/build/angular/renderer/index.js +157 -21
  128. package/build/angular/renderer/index.js.map +1 -1
  129. package/build/angular/services/color-scheme.service.js.map +1 -1
  130. package/build/angular/services/window-dimensions.service.js +1 -1
  131. package/build/angular/services/window-dimensions.service.js.map +1 -1
  132. package/build/angular/state-style.d.ts +9 -0
  133. package/build/angular/state-style.js +39 -0
  134. package/build/angular/state-style.js.map +1 -0
  135. package/metro-transformer.cjs +69 -0
  136. package/package.json +27 -8
  137. package/src/anchor-host-registry.ts +3 -1
  138. package/src/bootstrap.ts +4 -1
  139. package/src/components/activity-indicator/index.android.ts +7 -2
  140. package/src/components/activity-indicator/index.ios.ts +3 -1
  141. package/src/components/activity-indicator/shared.ts +32 -13
  142. package/src/components/button.ts +36 -6
  143. package/src/components/flat-list/index.ts +91 -29
  144. package/src/components/image/index.android.ts +9 -6
  145. package/src/components/image/index.ios.ts +9 -6
  146. package/src/components/image/shared.ts +110 -45
  147. package/src/components/image-background.ts +47 -10
  148. package/src/components/input-accessory-view/index.ts +41 -18
  149. package/src/components/keyboard-avoiding-view/index.ts +76 -22
  150. package/src/components/modal/index.ts +39 -12
  151. package/src/components/pressable/index.ts +121 -31
  152. package/src/components/refresh-control/index.ts +36 -13
  153. package/src/components/safe-area-view/index.ts +44 -19
  154. package/src/components/scroll-view/index.android.ts +31 -9
  155. package/src/components/scroll-view/index.ios.ts +17 -4
  156. package/src/components/scroll-view/projection.ts +290 -63
  157. package/src/components/scroll-view/shared.ts +124 -30
  158. package/src/components/scroll-view/sticky-header.ts +25 -8
  159. package/src/components/section-list/index.ts +79 -19
  160. package/src/components/switch/index.android.ts +18 -7
  161. package/src/components/switch/index.ios.ts +18 -7
  162. package/src/components/switch/shared.ts +15 -4
  163. package/src/components/text-input.ts +119 -31
  164. package/src/components/touchable/index.ts +339 -72
  165. package/src/components/touchable-native-feedback/index.ts +50 -18
  166. package/src/components/virtualized-list/directives.ts +19 -5
  167. package/src/components/virtualized-list/index.ts +292 -72
  168. package/src/components/virtualized-section-list/directives.ts +6 -3
  169. package/src/components/virtualized-section-list/index.ts +164 -34
  170. package/src/components.ts +6 -1
  171. package/src/create-portal/index.ts +47 -11
  172. package/src/create-tunnel/index.ts +3 -1
  173. package/src/descriptor-to-angular/index.ts +29 -8
  174. package/src/diagnostics.ts +138 -0
  175. package/src/gate-demand.ts +109 -0
  176. package/src/host-instance/index.ts +9 -3
  177. package/src/index.ts +33 -2
  178. package/src/modules/animated/create-animated-component.ts +67 -19
  179. package/src/modules/app-registry/index.ts +3 -1
  180. package/src/modules/status-bar/index.ts +13 -4
  181. package/src/primitives/index.ts +73 -2
  182. package/src/primitives/shared.ts +70 -9
  183. package/src/register.ts +43 -0
  184. package/src/render/index.ts +59 -7
  185. package/src/renderer/index.ts +209 -37
  186. package/src/services/color-scheme.service.ts +3 -1
  187. package/src/services/window-dimensions.service.ts +11 -4
  188. package/src/state-style.ts +39 -0
@@ -10,7 +10,7 @@ import {
10
10
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
11
11
  import {
12
12
  anchorHostStyle,
13
- SwitchHost,
13
+ ManagedSwitchHost,
14
14
  SymbioteHostPropsDirective,
15
15
  SymbioteStyleInputDirective,
16
16
  } from '../../primitives';
@@ -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
- imports: [SwitchHost, SymbioteHostPropsDirective],
26
- providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Switch), multi: true }],
27
+ imports: [ManagedSwitchHost, SymbioteHostPropsDirective],
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',
@@ -46,7 +54,7 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
46
54
  ],
47
55
  outputs: ['valueChange', 'change'],
48
56
  template: `
49
- <symbiote-switch
57
+ <symbiote-switch-managed
50
58
  #nativeSwitch="symbioteHost"
51
59
  [symbioteHostProps]="hostProps()"
52
60
  (change)="handleChange($event, nativeSwitch)"
@@ -55,12 +63,15 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
55
63
  })
56
64
  export class Switch extends SwitchBase implements DoCheck {
57
65
  // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
58
- // onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch> leaf one level down.
66
+ // onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch-managed> leaf one level down.
59
67
  private readonly elementRef = inject(ElementRef);
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;
@@ -2,7 +2,7 @@
2
2
  // and the controlled-write predicate live in @symbiote-native/components/state, the render (intrinsic
3
3
  // + native-prop mapping) in @symbiote-native/components/view, both shared verbatim with the React and
4
4
  // Vue adapters. Here Angular supplies only the lifecycle: renderTextInput picks ONE of two host
5
- // intrinsics at runtime (symbiote-text-input / symbiote-text-input-multiline) via `@if`/`@else`,
5
+ // intrinsics at runtime (symbiote-text-input-managed / …-multiline-managed) via `@if`/`@else`,
6
6
  // each wired through the shared `SymbioteHostPropsDirective` (`[symbioteHostProps]="hostProps"`,
7
7
  // `../primitives/shared.ts`) so the flat resolved prop bag doesn't need enumerating as individual
8
8
  // `[prop]` bindings; a @ViewChild reading that directive (by its `#host` template ref, across
@@ -73,6 +73,9 @@ import {
73
73
  setInputBlurred,
74
74
  setInputFocused,
75
75
  whenCommitted,
76
+ type IMeasureInWindowOnSuccess,
77
+ type IMeasureLayoutOnSuccess,
78
+ type IMeasureOnSuccess,
76
79
  type IStyleProp,
77
80
  type ISymbioteEvent,
78
81
  type ISymbioteNode,
@@ -80,18 +83,30 @@ import {
80
83
  } from '@symbiote-native/engine';
81
84
  import {
82
85
  anchorHostStyle,
83
- MultilineTextInputHost,
86
+ ManagedMultilineTextInputHost,
84
87
  SymbioteHostPropsDirective,
85
88
  SymbioteStyleInputDirective,
86
- TextInputHost,
89
+ ManagedTextInputHost,
87
90
  } from '../primitives';
88
-
89
- export type { ITextInputHandle, ITextInputSelection, IInputMode, IEnterKeyHint, ISubmitBehavior };
91
+ import {
92
+ gateWanted,
93
+ injectGateDemand,
94
+ type IGatedAccessibilityEvent,
95
+ } from '../gate-demand';
96
+
97
+ export type {
98
+ ITextInputHandle,
99
+ ITextInputSelection,
100
+ IInputMode,
101
+ IEnterKeyHint,
102
+ ISubmitBehavior,
103
+ };
90
104
 
91
105
  // Mirrors React's ITextInputProps minus nothing (TextInput has no children) — declared
92
106
  // per-adapter over the shared accessibility base because Angular's input surface aliases the
93
107
  // aria-* keys to camelCase @Inputs, unlike the plain agnostic fields shared across adapters.
94
- export interface IAngularTextInputProps extends IAccessibilityProps, IAriaProps {
108
+ export interface IAngularTextInputProps
109
+ extends IAccessibilityProps, IAriaProps {
95
110
  value?: string;
96
111
  defaultValue?: string;
97
112
  placeholder?: string;
@@ -158,16 +173,26 @@ export type IAngularTextInputInputs = Omit<
158
173
  @Component({
159
174
  selector: 'TextInput',
160
175
  standalone: true,
161
- hostDirectives: [{ directive: SymbioteStyleInputDirective, inputs: ['style'] }],
176
+ hostDirectives: [
177
+ { directive: SymbioteStyleInputDirective, inputs: ['style'] },
178
+ ],
162
179
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
163
- imports: [SymbioteHostPropsDirective, TextInputHost, MultilineTextInputHost],
180
+ imports: [
181
+ SymbioteHostPropsDirective,
182
+ ManagedTextInputHost,
183
+ ManagedMultilineTextInputHost,
184
+ ],
164
185
  changeDetection: ChangeDetectionStrategy.OnPush,
165
186
  providers: [
166
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextInput), multi: true },
187
+ {
188
+ provide: NG_VALUE_ACCESSOR,
189
+ useExisting: forwardRef(() => TextInput),
190
+ multi: true,
191
+ },
167
192
  ],
168
193
  template: `
169
194
  @if (isMultiline) {
170
- <symbiote-text-input-multiline
195
+ <symbiote-text-input-multiline-managed
171
196
  #host
172
197
  [symbioteHostProps]="hostProps"
173
198
  (change)="handleChange($event)"
@@ -178,13 +203,9 @@ export type IAngularTextInputInputs = Omit<
178
203
  (keyPress)="emit(keyPress, $event)"
179
204
  (endEditing)="emit(endEditing, $event)"
180
205
  (contentSizeChange)="emit(contentSizeChange, $event)"
181
- (accessibilityAction)="emit(accessibilityAction, $event)"
182
- (accessibilityTap)="emit(accessibilityTap, $event)"
183
- (magicTap)="emit(magicTap, $event)"
184
- (accessibilityEscape)="emit(accessibilityEscape, $event)"
185
- ></symbiote-text-input-multiline>
206
+ ></symbiote-text-input-multiline-managed>
186
207
  } @else {
187
- <symbiote-text-input
208
+ <symbiote-text-input-managed
188
209
  #host
189
210
  [symbioteHostProps]="hostProps"
190
211
  (change)="handleChange($event)"
@@ -195,11 +216,7 @@ export type IAngularTextInputInputs = Omit<
195
216
  (keyPress)="emit(keyPress, $event)"
196
217
  (endEditing)="emit(endEditing, $event)"
197
218
  (contentSizeChange)="emit(contentSizeChange, $event)"
198
- (accessibilityAction)="emit(accessibilityAction, $event)"
199
- (accessibilityTap)="emit(accessibilityTap, $event)"
200
- (magicTap)="emit(magicTap, $event)"
201
- (accessibilityEscape)="emit(accessibilityEscape, $event)"
202
- ></symbiote-text-input>
219
+ ></symbiote-text-input-managed>
203
220
  }
204
221
  `,
205
222
  })
@@ -269,6 +286,9 @@ export class TextInput
269
286
  @Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
270
287
  @Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
271
288
 
289
+ // Null unless a wrapper renders this TextInput in its own template. See `../gate-demand.ts`.
290
+ private readonly gateDemand = injectGateDemand();
291
+
272
292
  @Input() accessible?: boolean;
273
293
  @Input() accessibilityLabel?: string;
274
294
  @Input() accessibilityHint?: string;
@@ -277,8 +297,10 @@ export class TextInput
277
297
  @Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
278
298
  @Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
279
299
  @Input() accessibilityLabelledBy?: string | string[];
280
- @Input() importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
281
- @Input() accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
300
+ @Input()
301
+ importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
302
+ @Input()
303
+ accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
282
304
  @Input() screenReaderFocusable?: boolean;
283
305
  @Input() accessibilityViewIsModal?: boolean;
284
306
  @Input() accessibilityElementsHidden?: boolean;
@@ -303,7 +325,7 @@ export class TextInput
303
325
 
304
326
  // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
305
327
  // onto (see anchorHostStyle's doc comment) — NOT `hostRef` above, which targets the real inner
306
- // symbiote-text-input(-multiline) one level down.
328
+ // symbiote-text-input-managed(-multiline-managed) one level down.
307
329
  private readonly elementRef = inject(ElementRef);
308
330
  // Only setDisabledState() needs this: it mutates `editable` from OUTSIDE Angular's own binding
309
331
  // path (@angular/forms calling in directly), which doesn't itself schedule a tick under zoneless CD.
@@ -390,7 +412,9 @@ export class TextInput
390
412
  const node = this.hostNode;
391
413
  if (node === undefined) return;
392
414
  dlog('TextInput autoFocus -> focus command');
393
- this.cancelAutoFocus = whenCommitted(node, () => dispatchViewCommand(node, 'focus', []));
415
+ this.cancelAutoFocus = whenCommitted(node, () =>
416
+ dispatchViewCommand(node, 'focus', []),
417
+ );
394
418
  }
395
419
 
396
420
  ngOnDestroy(): void {
@@ -441,6 +465,41 @@ export class TextInput
441
465
  if (isSymbioteEvent(event)) emitter.emit(event);
442
466
  }
443
467
 
468
+ // `undefined` means `setEventListener` never sees a function, so the gate stays dark. A demand
469
+ // from above overrides `.observed` for the same reason it does on Pressable: a wrapper's binding
470
+ // is not a subscriber (`../gate-demand.ts`).
471
+ private gatedAccessibilityHandler(
472
+ name: IGatedAccessibilityEvent,
473
+ ): ((event: unknown) => void) | undefined {
474
+ const emitter = this[name];
475
+ return gateWanted(this.gateDemand, name, emitter)
476
+ ? event => this.emit(emitter, event)
477
+ : undefined;
478
+ }
479
+
480
+ // Forwarded from the engine node so a TextInput ref is not poorer than any other host ref — an
481
+ // instance exposing only the five methods below CLOSES the node off (see ITextInputHandle's own
482
+ // comment). No Angular-specific state to add: these four pass straight through.
483
+ measure(callback: IMeasureOnSuccess): void {
484
+ this.hostNode?.measure(callback);
485
+ }
486
+
487
+ measureInWindow(callback: IMeasureInWindowOnSuccess): void {
488
+ this.hostNode?.measureInWindow(callback);
489
+ }
490
+
491
+ measureLayout(
492
+ relativeToNativeNode: ISymbioteNode | number,
493
+ onSuccess: IMeasureLayoutOnSuccess,
494
+ onFail?: () => void,
495
+ ): void {
496
+ this.hostNode?.measureLayout(relativeToNativeNode, onSuccess, onFail);
497
+ }
498
+
499
+ setNativeProps(nativeProps: Record<string, unknown>): void {
500
+ this.hostNode?.setNativeProps(nativeProps);
501
+ }
502
+
444
503
  // The imperative API RN exposes on a TextInput ref; in Angular the component instance IS the
445
504
  // handle (a parent reads it via @ViewChild(TextInput) and calls these). focus/blur drive native
446
505
  // view commands; clear and setSelection reuse setTextAndSelection (the same stale-safe path as a
@@ -458,7 +517,12 @@ export class TextInput
458
517
  clear(): void {
459
518
  const node = this.hostNode;
460
519
  if (node === undefined) return;
461
- dispatchViewCommand(node, 'setTextAndSelection', [this.mostRecentEventCount, '', 0, 0]);
520
+ dispatchViewCommand(node, 'setTextAndSelection', [
521
+ this.mostRecentEventCount,
522
+ '',
523
+ 0,
524
+ 0,
525
+ ]);
462
526
  this.lastNativeText = '';
463
527
  }
464
528
 
@@ -488,12 +552,20 @@ export class TextInput
488
552
  const value = this.value;
489
553
  if (!shouldCommandText(this.lastNativeText, value)) return;
490
554
  const selStart = this.selection?.start ?? SELECTION_NONE;
491
- const selEnd = this.selection?.end ?? this.selection?.start ?? SELECTION_NONE;
555
+ const selEnd =
556
+ this.selection?.end ?? this.selection?.start ?? SELECTION_NONE;
492
557
  const count = this.mostRecentEventCount;
493
- dlog(`TextInput setTextAndSelection count=${count} text=${JSON.stringify(value)}`);
558
+ dlog(
559
+ `TextInput setTextAndSelection count=${count} text=${JSON.stringify(value)}`,
560
+ );
494
561
  this.lastNativeText = value;
495
562
  whenCommitted(node, () =>
496
- dispatchViewCommand(node, 'setTextAndSelection', [count, value, selStart, selEnd]),
563
+ dispatchViewCommand(node, 'setTextAndSelection', [
564
+ count,
565
+ value,
566
+ selStart,
567
+ selEnd,
568
+ ]),
497
569
  );
498
570
  }
499
571
 
@@ -501,6 +573,20 @@ export class TextInput
501
573
  return {
502
574
  ...this.foldedAccessibility,
503
575
  accessible: this.accessible,
576
+ // NOT template bindings, unlike every other event on this component's two host tags. These
577
+ // four are `on*`-GATED Fabric events (`.claude/rules/fabric-boolean-event-gates.md`): native
578
+ // fires them only when the committed payload carries a FUNCTION at that key, and a template
579
+ // `(accessibilityAction)="..."` binding is present unconditionally — so it lit all four gates
580
+ // on every TextInput in every app whether or not anything listened. Routed through the flat
581
+ // bag instead, the same shape Pressable uses.
582
+ onAccessibilityAction: this.gatedAccessibilityHandler(
583
+ 'accessibilityAction',
584
+ ),
585
+ onAccessibilityTap: this.gatedAccessibilityHandler('accessibilityTap'),
586
+ onMagicTap: this.gatedAccessibilityHandler('magicTap'),
587
+ onAccessibilityEscape: this.gatedAccessibilityHandler(
588
+ 'accessibilityEscape',
589
+ ),
504
590
  testID: this.testID,
505
591
  nativeID: this.nativeID,
506
592
  placeholder: this.placeholder,
@@ -558,8 +644,10 @@ export class TextInput
558
644
  accessibilityElementsHidden: this.accessibilityElementsHidden,
559
645
  accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
560
646
  accessibilityLanguage: this.accessibilityLanguage,
561
- accessibilityRespondsToUserInteraction: this.accessibilityRespondsToUserInteraction,
562
- accessibilityShowsLargeContentViewer: this.accessibilityShowsLargeContentViewer,
647
+ accessibilityRespondsToUserInteraction:
648
+ this.accessibilityRespondsToUserInteraction,
649
+ accessibilityShowsLargeContentViewer:
650
+ this.accessibilityShowsLargeContentViewer,
563
651
  accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
564
652
  role: this.role,
565
653
  'aria-label': this.ariaLabel,