@symbiote-native/angular 3.2.0 → 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 (57) hide show
  1. package/babel-register-composed.cjs +1 -0
  2. package/build/angular/components/checkbox-props.d.ts +2 -0
  3. package/build/angular/components/checkbox-props.js +2 -0
  4. package/build/angular/components/checkbox-props.js.map +1 -0
  5. package/build/angular/components.d.ts +1 -0
  6. package/build/angular/components.js.map +1 -1
  7. package/build/angular/descriptor-to-angular/descriptor-host.d.ts +8 -0
  8. package/build/angular/descriptor-to-angular/descriptor-host.js +38 -0
  9. package/build/angular/descriptor-to-angular/descriptor-host.js.map +1 -0
  10. package/build/angular/descriptor-to-angular/index.d.ts +1 -0
  11. package/build/angular/descriptor-to-angular/index.js +4 -0
  12. package/build/angular/descriptor-to-angular/index.js.map +1 -1
  13. package/build/angular/element-base.d.ts +153 -0
  14. package/build/angular/element-base.js +272 -0
  15. package/build/angular/element-base.js.map +1 -0
  16. package/build/angular/elements-controls.d.ts +165 -0
  17. package/build/angular/elements-controls.js +382 -0
  18. package/build/angular/elements-controls.js.map +1 -0
  19. package/build/angular/elements-layout.d.ts +122 -0
  20. package/build/angular/elements-layout.js +242 -0
  21. package/build/angular/elements-layout.js.map +1 -0
  22. package/build/angular/elements-press.d.ts +96 -0
  23. package/build/angular/elements-press.js +227 -0
  24. package/build/angular/elements-press.js.map +1 -0
  25. package/build/angular/elements.d.ts +6 -516
  26. package/build/angular/elements.js +8 -1091
  27. package/build/angular/elements.js.map +1 -1
  28. package/build/angular/host-instance/index.d.ts +2 -0
  29. package/build/angular/host-instance/index.js +4 -4
  30. package/build/angular/host-instance/index.js.map +1 -1
  31. package/build/angular/index.d.ts +5 -4
  32. package/build/angular/index.js +4 -3
  33. package/build/angular/index.js.map +1 -1
  34. package/build/angular/native-view-base.d.ts +10 -2
  35. package/build/angular/native-view-base.js +23 -2
  36. package/build/angular/native-view-base.js.map +1 -1
  37. package/build/angular/services/index.d.ts +1 -0
  38. package/build/angular/services/index.js +1 -0
  39. package/build/angular/services/index.js.map +1 -1
  40. package/build/angular/services/inject-event.d.ts +6 -0
  41. package/build/angular/services/inject-event.js +24 -0
  42. package/build/angular/services/inject-event.js.map +1 -0
  43. package/package.json +6 -6
  44. package/src/components/checkbox-props.ts +12 -0
  45. package/src/components.ts +1 -0
  46. package/src/descriptor-to-angular/descriptor-host.ts +27 -0
  47. package/src/descriptor-to-angular/index.ts +5 -0
  48. package/src/element-base.ts +272 -0
  49. package/src/elements-controls.ts +282 -0
  50. package/src/elements-layout.ts +141 -0
  51. package/src/elements-press.ts +145 -0
  52. package/src/elements.ts +73 -788
  53. package/src/host-instance/index.ts +4 -4
  54. package/src/index.ts +6 -1
  55. package/src/native-view-base.ts +37 -2
  56. package/src/services/index.ts +1 -0
  57. package/src/services/inject-event.ts +52 -0
package/src/elements.ts CHANGED
@@ -32,36 +32,11 @@
32
32
  // (`[onPressMove]="fn"`, where `fn` keeps its `ISymbioteEvent` parameter).
33
33
  //
34
34
  // `valueChange` is the ONE exception, and it is forced rather than chosen: see ValueChangeElement.
35
- import {
36
- ChangeDetectorRef,
37
- Directive,
38
- ElementRef,
39
- ErrorHandler,
40
- EventEmitter,
41
- Input,
42
- Output,
43
- Renderer2,
44
- forwardRef,
45
- inject,
46
- } from '@angular/core';
47
- import type {
48
- OnChanges,
49
- OnDestroy,
50
- OnInit,
51
- SimpleChanges,
52
- } from '@angular/core';
53
- import { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms';
54
- import { VALUE_CHANGE_EVENT } from './renderer/value-change';
55
- import {
56
- registerViewFlush,
57
- unregisterViewFlush,
58
- } from './change-detection-flush';
59
35
  import { SymbioteCallbackHost } from './callback-host';
60
36
  import { withholdFromRuntimeMatching } from './runtime-matching';
61
37
  import type {
62
38
  IActivityIndicatorProps,
63
39
  IImageProps,
64
- INativeFeedbackBackground,
65
40
  IInputAccessoryViewViewProps,
66
41
  IModalViewProps,
67
42
  ISwitchProps,
@@ -72,6 +47,7 @@ import type {
72
47
  IStickyHeaderElementProps,
73
48
  ITextElementProps,
74
49
  } from './element-props';
50
+ import type { ICheckboxProps } from './components/checkbox-props';
75
51
  import type { IAngularImageBackgroundProps } from './components/image-background-props';
76
52
  import type { IAngularPressableProps } from './components/pressable-props';
77
53
  import type {
@@ -81,770 +57,72 @@ import type {
81
57
  // Type-only, so none of these components enters the bundle of an app that writes bare tags.
82
58
  import type { IAngularRefreshControlProps } from './components/refresh-control-props';
83
59
  import type { IAngularScrollViewProps } from './components/scroll-view-props';
60
+ import { SymbioteElement } from './element-base';
61
+ import {
62
+ ButtonElement,
63
+ CheckboxElement,
64
+ PressableElement,
65
+ TextElement,
66
+ TouchableHighlightElement,
67
+ TouchableNativeFeedbackElement,
68
+ TouchableOpacityElement,
69
+ TouchableWithoutFeedbackElement,
70
+ ViewElement,
71
+ } from './elements-press';
72
+ import {
73
+ HorizontalScrollContentElement,
74
+ HorizontalScrollViewElement,
75
+ ImageBackgroundElement,
76
+ ImageElement,
77
+ ScrollContentElement,
78
+ ScrollViewElement,
79
+ } from './elements-layout';
80
+ import {
81
+ ActivityIndicatorElement,
82
+ ActivityIndicatorSpinnerElement,
83
+ InputAccessoryViewElement,
84
+ ModalElement,
85
+ MultilineTextInputElement,
86
+ RefreshControlElement,
87
+ SafeAreaViewElement,
88
+ StickyHeaderElement,
89
+ SwitchElement,
90
+ SwitchValueAccessor,
91
+ TextInputElement,
92
+ TextInputValueAccessor,
93
+ } from './elements-controls';
84
94
 
85
- /**
86
- * The shared half of every element directive: the prop surface all tags accept, and the ONE
87
- * generic forward that puts a claimed binding back on the engine node.
88
- *
89
- * The forward is the deciding fact of this whole route. A binding claimed by a directive input
90
- * never reaches `Renderer2.setProperty` on its own — Angular writes it to the directive instance
91
- * — so without this loop a bare tag commits nothing at all. It stays generic on purpose: per-prop
92
- * forwarding code is a component wrapper by another name, which is what this migration removes.
93
- * `renderer.setProperty` lands in the adapter's own renderer, which routes the value through the
94
- * engine's `routeProp` and applies the `id` -> `nativeID` alias.
95
- */
96
- @Directive()
97
- export abstract class SymbioteElement implements OnChanges {
98
- private readonly renderer = inject(Renderer2);
99
- protected readonly host = inject(ElementRef);
100
-
101
- // NO `ChangeDetectorRef` HERE, and its absence is the point. This class is instantiated once per
102
- // ELEMENT, so injecting one cost ~1.0-2.4 us on every tag of a screen to serve the few that carry
103
- // an `on*` prop — ten thousand `ViewRef`s on a thousand-row create, read by none of them
104
- // (`core/engine/cpp/tests/js/angular-directive-cost.itest.ts`). `SymbioteCallbackHost` owns one and
105
- // MATCHES on the callback attributes instead, registering it against the node; `markViewFor` is how
106
- // the wrapper reaches it. The inputs did not move, so nothing about this file's public surface did.
107
-
108
- // No `on*` wrapper here: `SymbioteRenderer.setProperty` handles it. Most directives are withheld
109
- // from runtime matching (`./runtime-matching`), so a binding reaches the renderer through
110
- // `ɵɵproperty` without passing through any directive — the renderer is reached by every path.
111
-
112
- @Input() testID?: IElementProps['testID'];
113
- @Input() nativeID?: IElementProps['nativeID'];
114
- @Input() id?: IElementProps['id'];
115
- @Input() accessible?: IElementProps['accessible'];
116
- @Input() accessibilityLabel?: IElementProps['accessibilityLabel'];
117
- @Input() accessibilityHint?: IElementProps['accessibilityHint'];
118
- @Input() accessibilityRole?: IElementProps['accessibilityRole'];
119
- @Input() accessibilityState?: IElementProps['accessibilityState'];
120
- @Input() accessibilityValue?: IElementProps['accessibilityValue'];
121
- @Input() accessibilityActions?: IElementProps['accessibilityActions'];
122
- @Input() accessibilityLabelledBy?: IElementProps['accessibilityLabelledBy'];
123
- @Input()
124
- importantForAccessibility?: IElementProps['importantForAccessibility'];
125
- @Input() accessibilityLiveRegion?: IElementProps['accessibilityLiveRegion'];
126
- @Input() screenReaderFocusable?: IElementProps['screenReaderFocusable'];
127
- @Input() accessibilityViewIsModal?: IElementProps['accessibilityViewIsModal'];
128
- @Input()
129
- accessibilityElementsHidden?: IElementProps['accessibilityElementsHidden'];
130
- @Input()
131
- accessibilityIgnoresInvertColors?: IElementProps['accessibilityIgnoresInvertColors'];
132
- @Input() accessibilityLanguage?: IElementProps['accessibilityLanguage'];
133
- @Input()
134
- accessibilityRespondsToUserInteraction?: IElementProps['accessibilityRespondsToUserInteraction'];
135
- @Input()
136
- accessibilityShowsLargeContentViewer?: IElementProps['accessibilityShowsLargeContentViewer'];
137
- @Input()
138
- accessibilityLargeContentTitle?: IElementProps['accessibilityLargeContentTitle'];
139
- @Input() onAccessibilityAction?: IElementProps['onAccessibilityAction'];
140
- @Input() onAccessibilityTap?: IElementProps['onAccessibilityTap'];
141
- @Input() onMagicTap?: IElementProps['onMagicTap'];
142
- @Input() onAccessibilityEscape?: IElementProps['onAccessibilityEscape'];
143
-
144
- // The web aliases. Native reads only `accessibility*`; the engine folds these on the way in.
145
- @Input() role?: IElementProps['role'];
146
- @Input() 'aria-label'?: IElementProps['aria-label'];
147
- @Input() 'aria-labelledby'?: IElementProps['aria-labelledby'];
148
- @Input() 'aria-live'?: IElementProps['aria-live'];
149
- @Input() 'aria-hidden'?: IElementProps['aria-hidden'];
150
- @Input() 'aria-busy'?: IElementProps['aria-busy'];
151
- @Input() 'aria-checked'?: IElementProps['aria-checked'];
152
- @Input() 'aria-disabled'?: IElementProps['aria-disabled'];
153
- @Input() 'aria-expanded'?: IElementProps['aria-expanded'];
154
- @Input() 'aria-selected'?: IElementProps['aria-selected'];
155
- @Input() 'aria-modal'?: IElementProps['aria-modal'];
156
- @Input() 'aria-valuemax'?: IElementProps['aria-valuemax'];
157
- @Input() 'aria-valuemin'?: IElementProps['aria-valuemin'];
158
- @Input() 'aria-valuenow'?: IElementProps['aria-valuenow'];
159
- @Input() 'aria-valuetext'?: IElementProps['aria-valuetext'];
160
-
161
- // The responder gates return a boolean, which an Angular `(event)` binding cannot carry back to
162
- // the caller — so the whole family is inputs, never outputs.
163
- @Input()
164
- onStartShouldSetResponder?: IElementProps['onStartShouldSetResponder'];
165
- @Input()
166
- onStartShouldSetResponderCapture?: IElementProps['onStartShouldSetResponderCapture'];
167
- @Input() onMoveShouldSetResponder?: IElementProps['onMoveShouldSetResponder'];
168
- @Input()
169
- onMoveShouldSetResponderCapture?: IElementProps['onMoveShouldSetResponderCapture'];
170
- @Input() onResponderGrant?: IElementProps['onResponderGrant'];
171
- @Input() onResponderReject?: IElementProps['onResponderReject'];
172
- @Input() onResponderStart?: IElementProps['onResponderStart'];
173
- @Input() onResponderMove?: IElementProps['onResponderMove'];
174
- @Input() onResponderEnd?: IElementProps['onResponderEnd'];
175
- @Input() onResponderRelease?: IElementProps['onResponderRelease'];
176
- @Input() onResponderTerminate?: IElementProps['onResponderTerminate'];
177
- @Input()
178
- onResponderTerminationRequest?: IElementProps['onResponderTerminationRequest'];
179
-
180
- @Input() pointerEvents?: IElementProps['pointerEvents'];
181
- @Input() hitSlop?: IElementProps['hitSlop'];
182
- @Input() focusable?: IElementProps['focusable'];
183
- @Input() collapsable?: IElementProps['collapsable'];
184
- @Input() removeClippedSubviews?: IElementProps['removeClippedSubviews'];
185
- @Input()
186
- renderToHardwareTextureAndroid?: IElementProps['renderToHardwareTextureAndroid'];
187
- @Input() shouldRasterizeIOS?: IElementProps['shouldRasterizeIOS'];
188
- @Input()
189
- needsOffscreenAlphaCompositing?: IElementProps['needsOffscreenAlphaCompositing'];
190
- // `[style]` and `[class]` are declared for the TYPE CHECK only. Withheld from run-time matching, no
191
- // input claims them, so Angular's own styling engine decomposes them into `setStyle`/`addClass`
192
- // exactly as on a DOM element - which is why `style` is typed as the object/string that engine can
193
- // hold. An RN style ARRAY or press-state FUNCTION throws in there ("indexOf is not a function",
194
- // "Unsupported styling type: function"), so it is `[styleProp]`: an ordinary property binding the
195
- // renderer routes to `style` whole. A claim would cost a directive instance per element.
196
- //
197
- // The press-state callback rides the BASE type rather than `PressableElement` alone: a subclass
198
- // cannot widen an inherited property, and only the pressables have a `pressed` to read — so on
199
- // any other tag the engine resolves it at `pressed: false`.
200
- @Input() style?: IElementProps['style'];
201
- @Input() styleProp?: IElementProps['styleProp'];
202
-
203
- // `ɵɵclassMap` hands over a string after joining any static `class=` prefix; `[class.foo]` and
204
- // `[ngClass]` arrive as `addClass`. The renderer unions both sources (`classStringFor`).
205
- @Input() class?: string;
206
-
207
- // The flat-bag spelling of the events below. `(press)` and `[onPress]` are both supported and
208
- // land in the same place; an app that already holds a handler bag binds the props.
209
- @Input() onPress?: IElementProps['onPress'];
210
- @Input() onPressIn?: IElementProps['onPressIn'];
211
- @Input() onPressOut?: IElementProps['onPressOut'];
212
- @Input() onPressMove?: IElementProps['onPressMove'];
213
- @Input() onLongPress?: IElementProps['onLongPress'];
214
- @Input() onLayout?: IElementProps['onLayout'];
215
- @Input() onFocus?: IElementProps['onFocus'];
216
- @Input() onBlur?: IElementProps['onBlur'];
217
-
218
- ngOnChanges(changes: SimpleChanges): void {
219
- for (const name of Object.keys(changes)) {
220
- this.renderer.setProperty(
221
- this.host.nativeElement,
222
- name,
223
- // Unwrapped on purpose: `SymbioteRenderer.setProperty` is where an `on*` value is wrapped
224
- // now, and it is the call this line makes.
225
- changes[name]?.currentValue,
226
- );
227
- }
228
- }
229
- }
230
-
231
- /**
232
- * The base for a tag whose app callback an engine behavior READS BACK inside the same microtask
233
- * turn — `<text-input>`, `<switch>`, `<refresh-control>`. Zoneless change detection is a macrotask,
234
- * so without this the behavior sees the PRE-event value and undoes the user; the full mechanism,
235
- * and why this is `detectChanges()` on one view rather than `ApplicationRef.tick()`, is in
236
- * `./change-detection-flush`.
237
- *
238
- * The directive is the only thing in the adapter that owns a `ChangeDetectorRef` for the view
239
- * holding the binding, so it hands one to the renderer keyed on its own node.
240
- */
241
- @Directive()
242
- abstract class ReadBackElement extends SymbioteElement implements OnDestroy {
243
- // ITS OWN, now that the base has none. Three tags reach this class, so the injection is paid where
244
- // it is read instead of on every element of a screen.
245
- private readonly detector = inject(ChangeDetectorRef);
246
- private readonly errorHandler = inject(ErrorHandler);
247
-
248
- constructor() {
249
- super();
250
- const node: unknown = this.host.nativeElement;
251
- if (typeof node === 'object' && node !== null) {
252
- registerViewFlush(node, () => this.flush());
253
- }
254
- }
255
-
256
- // REPORTED, NOT RETHROWN — Angular's own contract. Every other change detection runs inside
257
- // `ApplicationRef.tick()`, caught by `ErrorHandler` (`SymbioteErrorHandler`); this flush instead
258
- // runs from a native event dispatch, where an uncaught throw has nowhere to go but `RCTFatal`.
259
- private flush(): void {
260
- try {
261
- this.detector.detectChanges();
262
- } catch (error: unknown) {
263
- this.errorHandler.handleError(error);
264
- }
265
- }
266
-
267
- ngOnDestroy(): void {
268
- const node: unknown = this.host.nativeElement;
269
- if (typeof node === 'object' && node !== null) unregisterViewFlush(node);
270
- }
271
- }
272
-
273
- // BOTH SPELLINGS ON THE SEVEN DASHLESS TAGS, and it is a correctness fix rather than a convenience.
274
- //
275
- // The renderer has always mapped `symbiote-view` onto `view` (`PRIMITIVE_SELECTOR_ALIAS`), because
276
- // `CUSTOM_ELEMENTS_SCHEMA` admits an unknown element only when the name carries a hyphen — so the
277
- // hyphenated form is the spelling an app WITHOUT these directives has to use. It is the same tag and
278
- // commits the same node.
279
- //
280
- // It was not the same tag HERE. A selector of `view` alone meant an app that imports
281
- // `SYMBIOTE_ELEMENTS` and writes `<symbiote-view>` matched nothing: no type check on its props, no
282
- // declared inputs, none of `SymbioteElement`'s forwarding or callback wrapping — silently, with the
283
- // tree still looking right. Measured, that shape also ran ~8.6 us per element FASTER, which is what
284
- // made it look like an optimization instead of a hole (`angular-directive-cost.itest.ts`).
285
- //
286
- // Only the seven dashless tags need it: `hyphenatedIntrinsicAliases` skips any tag that already
287
- // carries a dash, so `symbiote-scroll-view` resolves nowhere on either side and the two halves agree
288
- // already.
289
- @Directive({ selector: 'view, symbiote-view', standalone: true })
290
- export class ViewElement extends SymbioteElement {}
291
-
292
- @Directive({ selector: 'pressable, symbiote-pressable', standalone: true })
293
- export class PressableElement extends SymbioteElement {
294
- @Input() disabled?: IAngularPressableProps['disabled'];
295
- @Input() cancelable?: IAngularPressableProps['cancelable'];
296
- @Input()
297
- blockNativeResponder?: IAngularPressableProps['blockNativeResponder'];
298
- @Input() delayLongPress?: IAngularPressableProps['delayLongPress'];
299
- @Input() delayHoverIn?: IAngularPressableProps['delayHoverIn'];
300
- @Input() delayHoverOut?: IAngularPressableProps['delayHoverOut'];
301
- @Input() onHoverIn?: IAngularPressableProps['onHoverIn'];
302
- @Input() onHoverOut?: IAngularPressableProps['onHoverOut'];
303
- @Input()
304
- pressRetentionOffset?: IAngularPressableProps['pressRetentionOffset'];
305
- @Input() unstable_pressDelay?: IAngularPressableProps['unstable_pressDelay'];
306
- @Input() android_ripple?: IAngularPressableProps['android_ripple'];
307
- // ON THE BASE, so both touchables inherit it — the prop is Pressable's AND
308
- // TouchableHighlight's upstream, and they extend this rather than repeat its surface.
309
- @Input() testOnly_pressed?: IAngularPressableProps['testOnly_pressed'];
310
- @Input()
311
- android_disableSound?: IAngularPressableProps['android_disableSound'];
312
- @Input() hasTVPreferredFocus?: IAngularPressableProps['hasTVPreferredFocus'];
313
- @Input() nextFocusDown?: IAngularPressableProps['nextFocusDown'];
314
- @Input() nextFocusForward?: IAngularPressableProps['nextFocusForward'];
315
- @Input() nextFocusLeft?: IAngularPressableProps['nextFocusLeft'];
316
- @Input() nextFocusRight?: IAngularPressableProps['nextFocusRight'];
317
- @Input() nextFocusUp?: IAngularPressableProps['nextFocusUp'];
318
- }
319
-
320
- // RN's TouchableOpacity is a Pressable that also fades, and both halves are on the engine node —
321
- // so it takes the same inputs and adds only the fade's own knob.
322
- @Directive({ selector: 'touchable-opacity', standalone: true })
323
- export class TouchableOpacityElement extends PressableElement {
324
- @Input() activeOpacity?: number;
325
- @Input() delayPressIn?: number;
326
- @Input() delayPressOut?: number;
327
- @Input() minPressDuration?: number;
328
- }
329
-
330
- // RN's TouchableHighlight is a Pressable that swaps its own background while pressed
331
- // (TouchableHighlight.js), and the underlay machine runs on the engine node — so the tag takes the
332
- // press surface plus the two knobs that describe the underlay. `onShowUnderlay`/`onHideUnderlay`
333
- // need no @Input: they are events, bound as `(showUnderlay)` through the renderer's own listen.
334
- @Directive({ selector: 'touchable-highlight', standalone: true })
335
- export class TouchableHighlightElement extends PressableElement {
336
- @Input() activeOpacity?: number;
337
- @Input() underlayColor?: string;
338
- @Input() delayPressIn?: number;
339
- @Input() delayPressOut?: number;
340
- @Input() minPressDuration?: number;
341
- // TouchableHighlight.js:205 — forwarded to Pressability as `android_disableSound`.
342
- @Input() touchSoundDisabled?: boolean;
343
- }
344
-
345
- // RN's TouchableNativeFeedback is a Pressable that CLONES onto its single child instead of
346
- // rendering anything (TouchableNativeFeedback.js:339) — so the tag takes the press surface plus the
347
- // two props that pick the Android ripple drawable. The tag itself commits no native view; the
348
- // behavior configures the child, which is why `elements.test.ts` reads it as an anchor.
349
- @Directive({ selector: 'touchable-native-feedback', standalone: true })
350
- export class TouchableNativeFeedbackElement extends PressableElement {
351
- @Input() background?: INativeFeedbackBackground;
352
- @Input() useForeground?: boolean;
353
- // TouchableNativeFeedback.js:228 — forwarded to Pressability as `android_disableSound`.
354
- @Input() touchSoundDisabled?: boolean;
355
- }
356
-
357
- // RN's TouchableWithoutFeedback clones onto its single child the same way
358
- // (TouchableWithoutFeedback.js:286) and installs no drawable, so it takes the press surface plus the
359
- // three timing knobs it forwards to Pressability (:186-190) and nothing else. Like the tag above it
360
- // commits no native view; `elements.test.ts` reads it as an anchor.
361
- @Directive({ selector: 'touchable-without-feedback', standalone: true })
362
- export class TouchableWithoutFeedbackElement extends PressableElement {
363
- @Input() delayPressIn?: number;
364
- @Input() delayPressOut?: number;
365
- @Input() minPressDuration?: number;
366
- // TouchableWithoutFeedback.js:199 — forwarded to Pressability as `android_disableSound`.
367
- @Input() touchSoundDisabled?: boolean;
368
- }
369
-
370
- // RN's Button IS a TouchableOpacity (Button.js:384), and the behavior builds the view and the
371
- // label under it — so the tag takes the touchable's surface plus the four props Button owns. There
372
- // is no `style`: RN's Button has no such prop, and the label/background come from `color`.
373
- @Directive({ selector: 'button, symbiote-button', standalone: true })
374
- export class ButtonElement extends TouchableOpacityElement {
375
- @Input() title?: string;
376
- @Input() color?: string;
377
- @Input() touchSoundDisabled?: boolean;
378
- }
379
-
380
- @Directive({ selector: 'text, symbiote-text', standalone: true })
381
- export class TextElement extends SymbioteElement {
382
- // Narrows the inherited input to a TEXT style so `fontSize`/`fontWeight` type-check here. The
383
- // initializer is what TS2612 asks for to accept a redeclaration as deliberate; `declare` would
384
- // be the other answer and cannot carry a decorator.
385
- @Input() override style?: ITextElementProps['style'] = undefined;
386
- @Input() override styleProp?: ITextElementProps['styleProp'] = undefined;
387
- @Input() numberOfLines?: ITextElementProps['numberOfLines'];
388
- @Input() ellipsizeMode?: ITextElementProps['ellipsizeMode'];
389
- @Input() selectable?: ITextElementProps['selectable'];
390
- @Input() adjustsFontSizeToFit?: ITextElementProps['adjustsFontSizeToFit'];
391
- @Input() minimumFontScale?: ITextElementProps['minimumFontScale'];
392
- @Input() allowFontScaling?: ITextElementProps['allowFontScaling'];
393
- @Input() maxFontSizeMultiplier?: ITextElementProps['maxFontSizeMultiplier'];
394
- @Input() selectionColor?: ITextElementProps['selectionColor'];
395
- @Input() disabled?: ITextElementProps['disabled'];
396
- }
397
-
398
- @Directive({ selector: 'image, symbiote-image', standalone: true })
399
- export class ImageElement extends SymbioteElement {
400
- @Input() source?: IImageProps['source'];
401
- @Input() src?: IImageProps['src'];
402
- @Input() srcSet?: IImageProps['srcSet'];
403
- @Input() alt?: IImageProps['alt'];
404
- @Input() width?: IImageProps['width'];
405
- @Input() height?: IImageProps['height'];
406
- @Input() resizeMode?: IImageProps['resizeMode'];
407
- @Input() resizeMethod?: IImageProps['resizeMethod'];
408
- @Input() defaultSource?: IImageProps['defaultSource'];
409
- @Input() loadingIndicatorSource?: IImageProps['loadingIndicatorSource'];
410
- @Input() blurRadius?: IImageProps['blurRadius'];
411
- @Input() capInsets?: IImageProps['capInsets'];
412
- @Input() crossOrigin?: IImageProps['crossOrigin'];
413
- @Input() referrerPolicy?: IImageProps['referrerPolicy'];
414
- @Input() fadeDuration?: IImageProps['fadeDuration'];
415
- @Input()
416
- progressiveRenderingEnabled?: IImageProps['progressiveRenderingEnabled'];
417
- @Input() tintColor?: IImageProps['tintColor'];
418
- @Input() onLoad?: IImageProps['onLoad'];
419
- @Input() onLoadStart?: IImageProps['onLoadStart'];
420
- @Input() onLoadEnd?: IImageProps['onLoadEnd'];
421
- @Input() onError?: IImageProps['onError'];
422
- @Input() onProgress?: IImageProps['onProgress'];
423
- @Input() onPartialLoad?: IImageProps['onPartialLoad'];
424
- }
425
-
426
- // The box, not the image. Every Image prop below rides the engine's `slotPropsExcept` redirect onto
427
- // the absolutely-filled image the behavior builds, exactly as RN's own `...props` spread does
428
- // (ImageBackground.js:81) — so this directive declares them to make the BINDING legal, and the
429
- // engine decides which node each lands on.
430
- @Directive({ selector: 'image-background', standalone: true })
431
- export class ImageBackgroundElement extends SymbioteElement {
432
- @Input() imageStyle?: IAngularImageBackgroundProps['imageStyle'];
433
- @Input() source?: IImageProps['source'];
434
- @Input() src?: IImageProps['src'];
435
- @Input() srcSet?: IImageProps['srcSet'];
436
- @Input() alt?: IImageProps['alt'];
437
- @Input() width?: IImageProps['width'];
438
- @Input() height?: IImageProps['height'];
439
- @Input() resizeMode?: IImageProps['resizeMode'];
440
- @Input() resizeMethod?: IImageProps['resizeMethod'];
441
- @Input() defaultSource?: IImageProps['defaultSource'];
442
- @Input() loadingIndicatorSource?: IImageProps['loadingIndicatorSource'];
443
- @Input() blurRadius?: IImageProps['blurRadius'];
444
- @Input() capInsets?: IImageProps['capInsets'];
445
- @Input() crossOrigin?: IImageProps['crossOrigin'];
446
- @Input() referrerPolicy?: IImageProps['referrerPolicy'];
447
- @Input() fadeDuration?: IImageProps['fadeDuration'];
448
- @Input()
449
- progressiveRenderingEnabled?: IImageProps['progressiveRenderingEnabled'];
450
- @Input() tintColor?: IImageProps['tintColor'];
451
- @Input() onLoad?: IImageProps['onLoad'];
452
- @Input() onLoadStart?: IImageProps['onLoadStart'];
453
- @Input() onLoadEnd?: IImageProps['onLoadEnd'];
454
- @Input() onError?: IImageProps['onError'];
455
- @Input() onProgress?: IImageProps['onProgress'];
456
- @Input() onPartialLoad?: IImageProps['onPartialLoad'];
457
- }
458
-
459
- @Directive({ selector: 'scroll-view', standalone: true })
460
- export class ScrollViewElement extends SymbioteElement {
461
- // No `horizontal` @Input: the axis is the TAG you write, never a prop — see
462
- // `../components/scroll-view-props.ts`'s header. `HorizontalScrollViewElement` below is the
463
- // other spelling, not a variant of this one.
464
- @Input() scrollEnabled?: IAngularScrollViewProps['scrollEnabled'];
465
- @Input() scrollEventThrottle?: IAngularScrollViewProps['scrollEventThrottle'];
466
- @Input()
467
- contentContainerStyle?: IAngularScrollViewProps['contentContainerStyle'];
468
- @Input() contentInset?: IAngularScrollViewProps['contentInset'];
469
- @Input()
470
- contentInsetAdjustmentBehavior?: IAngularScrollViewProps['contentInsetAdjustmentBehavior'];
471
- @Input() contentOffset?: IAngularScrollViewProps['contentOffset'];
472
- @Input()
473
- scrollIndicatorInsets?: IAngularScrollViewProps['scrollIndicatorInsets'];
474
- @Input()
475
- showsHorizontalScrollIndicator?: IAngularScrollViewProps['showsHorizontalScrollIndicator'];
476
- @Input()
477
- showsVerticalScrollIndicator?: IAngularScrollViewProps['showsVerticalScrollIndicator'];
478
- @Input()
479
- alwaysBounceHorizontal?: IAngularScrollViewProps['alwaysBounceHorizontal'];
480
- @Input()
481
- alwaysBounceVertical?: IAngularScrollViewProps['alwaysBounceVertical'];
482
- @Input() bounces?: IAngularScrollViewProps['bounces'];
483
- @Input() bouncesZoom?: IAngularScrollViewProps['bouncesZoom'];
484
- @Input() centerContent?: IAngularScrollViewProps['centerContent'];
485
- @Input() decelerationRate?: IAngularScrollViewProps['decelerationRate'];
486
- @Input()
487
- directionalLockEnabled?: IAngularScrollViewProps['directionalLockEnabled'];
488
- @Input()
489
- disableIntervalMomentum?: IAngularScrollViewProps['disableIntervalMomentum'];
490
- @Input() endFillColor?: IAngularScrollViewProps['endFillColor'];
491
- @Input() fadingEdgeLength?: IAngularScrollViewProps['fadingEdgeLength'];
492
- @Input() indicatorStyle?: IAngularScrollViewProps['indicatorStyle'];
493
- @Input() invertStickyHeaders?: IAngularScrollViewProps['invertStickyHeaders'];
494
- @Input() keyboardDismissMode?: IAngularScrollViewProps['keyboardDismissMode'];
495
- @Input()
496
- keyboardShouldPersistTaps?: IAngularScrollViewProps['keyboardShouldPersistTaps'];
497
- @Input()
498
- automaticallyAdjustKeyboardInsets?: IAngularScrollViewProps['automaticallyAdjustKeyboardInsets'];
499
- @Input()
500
- maintainVisibleContentPosition?: IAngularScrollViewProps['maintainVisibleContentPosition'];
501
- @Input() maximumZoomScale?: IAngularScrollViewProps['maximumZoomScale'];
502
- @Input() minimumZoomScale?: IAngularScrollViewProps['minimumZoomScale'];
503
- @Input() zoomScale?: IAngularScrollViewProps['zoomScale'];
504
- @Input() nestedScrollEnabled?: IAngularScrollViewProps['nestedScrollEnabled'];
505
- @Input() overScrollMode?: IAngularScrollViewProps['overScrollMode'];
506
- @Input() pagingEnabled?: IAngularScrollViewProps['pagingEnabled'];
507
- @Input() persistentScrollbar?: IAngularScrollViewProps['persistentScrollbar'];
508
- @Input() pinchGestureEnabled?: IAngularScrollViewProps['pinchGestureEnabled'];
509
- @Input() snapToAlignment?: IAngularScrollViewProps['snapToAlignment'];
510
- @Input() snapToEnd?: IAngularScrollViewProps['snapToEnd'];
511
- @Input() snapToInterval?: IAngularScrollViewProps['snapToInterval'];
512
- @Input() snapToOffsets?: IAngularScrollViewProps['snapToOffsets'];
513
- @Input() snapToStart?: IAngularScrollViewProps['snapToStart'];
514
- @Input() stickyHeaderIndices?: IAngularScrollViewProps['stickyHeaderIndices'];
515
- @Input() onScroll?: IAngularScrollViewProps['onScroll'];
516
- @Input() onScrollBeginDrag?: IAngularScrollViewProps['onScrollBeginDrag'];
517
- @Input() onScrollEndDrag?: IAngularScrollViewProps['onScrollEndDrag'];
518
- @Input()
519
- onMomentumScrollBegin?: IAngularScrollViewProps['onMomentumScrollBegin'];
520
- @Input() onMomentumScrollEnd?: IAngularScrollViewProps['onMomentumScrollEnd'];
521
- @Input() onScrollToTop?: IAngularScrollViewProps['onScrollToTop'];
522
- @Input() onContentSizeChange?: IAngularScrollViewProps['onContentSizeChange'];
523
- }
524
-
525
- @Directive({ selector: 'horizontal-scroll-view', standalone: true })
526
- export class HorizontalScrollViewElement extends ScrollViewElement {}
527
-
528
- @Directive({ selector: 'scroll-content', standalone: true })
529
- export class ScrollContentElement extends SymbioteElement {}
530
-
531
- @Directive({ selector: 'horizontal-scroll-content', standalone: true })
532
- export class HorizontalScrollContentElement extends SymbioteElement {}
533
-
534
- /**
535
- * The `[(value)]` half of a controlled tag — `<switch>` and `<text-input>`.
536
- *
537
- * The one `@Output` in this file, and it is FORCED rather than chosen. `[(value)]` desugars to
538
- * `[value]` + `(valueChange)`, and ngtsc requires both halves to resolve to the SAME target: a
539
- * declared `value` input beside an event no directive claims is NG8007, "the property and event
540
- * halves are not bound to the same target". So the sugar this adapter documents cannot work without
541
- * the output existing, whatever the file header says about events in general.
542
- *
543
- * The bridge below is NOT what makes the binding work today, and the comment says so because the
544
- * obvious reading is wrong: the file header's "an `@Output` CONSUMES the binding" holds for a
545
- * COMPONENT, and an element is the other case — Angular attaches the renderer listener for the
546
- * event as well, so `Renderer2.listen` still runs and the engine still hears the change. Measured
547
- * under JIT by deleting this whole hook: every case in `renderer/two-way-value.test.ts` stayed
548
- * green, delivery included, and exactly ONCE (nothing double-fires when both paths exist).
549
- *
550
- * It is kept because the measurement is JIT-only and this adapter has a recorded case of JIT and
551
- * AOT resolving a binding differently (`test-harness-false-greens.md` §21). If AOT routes the event
552
- * exclusively to the output, this hook is the only thing keeping `[(value)]` alive; if it routes to
553
- * both, it is one extra listener on a control an app explicitly bound. Delete it once something
554
- * executes the LINKED artifact and shows the renderer listener is attached there too.
555
- *
556
- * Opened only when something is SUBSCRIBED: the prop's PRESENCE is what a behavior reads to decide
557
- * it is controlled, so a `<switch>` given a handler nobody asked for changes how it snaps back.
558
- * `.observed` is readable from `ngOnInit` because Angular subscribes outputs in the creation pass,
559
- * before the update pass runs the hook — observed directly (true for the two bound tags, false for
560
- * an unbound one), not assumed.
561
- */
562
- @Directive()
563
- abstract class ValueChangeElement
564
- extends ReadBackElement
565
- implements OnInit, OnDestroy
566
- {
567
- private readonly valueRenderer = inject(Renderer2);
568
- private readonly valueHost = inject(ElementRef);
569
- private unlistenValue?: () => void;
570
-
571
- /** `this.valueChange.observed` — the subclass owns the emitter so its payload type stays exact. */
572
- protected abstract hasValueSubscriber(): boolean;
573
-
574
- /** Narrows the engine's unwrapped value to the type THIS tag emits, then emits it. */
575
- protected abstract emitValue(value: unknown): void;
576
-
577
- ngOnInit(): void {
578
- if (!this.hasValueSubscriber()) return;
579
- this.unlistenValue = this.valueRenderer.listen(
580
- this.valueHost.nativeElement,
581
- VALUE_CHANGE_EVENT,
582
- (value: unknown) => {
583
- this.emitValue(value);
584
- },
585
- );
586
- }
587
-
588
- override ngOnDestroy(): void {
589
- super.ngOnDestroy();
590
- this.unlistenValue?.();
591
- }
592
- }
593
-
594
- @Directive({ selector: 'text-input', standalone: true })
595
- export class TextInputElement extends ValueChangeElement {
596
- @Input() value?: ITextInputProps['value'];
597
- @Input() defaultValue?: ITextInputProps['defaultValue'];
598
- @Input() placeholder?: ITextInputProps['placeholder'];
599
- @Input() placeholderTextColor?: ITextInputProps['placeholderTextColor'];
600
- @Input() multiline?: ITextInputProps['multiline'];
601
- @Input() numberOfLines?: ITextInputProps['numberOfLines'];
602
- @Input() maxLength?: ITextInputProps['maxLength'];
603
- @Input() editable?: ITextInputProps['editable'];
604
- @Input() readOnly?: ITextInputProps['readOnly'];
605
- @Input() autoFocus?: ITextInputProps['autoFocus'];
606
- @Input() autoCapitalize?: ITextInputProps['autoCapitalize'];
607
- @Input() autoComplete?: ITextInputProps['autoComplete'];
608
- @Input() autoCorrect?: ITextInputProps['autoCorrect'];
609
- @Input() blurOnSubmit?: ITextInputProps['blurOnSubmit'];
610
- @Input() submitBehavior?: ITextInputProps['submitBehavior'];
611
- @Input() cursorColor?: ITextInputProps['cursorColor'];
612
- @Input() enterKeyHint?: ITextInputProps['enterKeyHint'];
613
- @Input() returnKeyType?: ITextInputProps['returnKeyType'];
614
- @Input() inputMode?: ITextInputProps['inputMode'];
615
- @Input() keyboardType?: ITextInputProps['keyboardType'];
616
- @Input() inputAccessoryViewID?: ITextInputProps['inputAccessoryViewID'];
617
- @Input() scrollEnabled?: ITextInputProps['scrollEnabled'];
618
- @Input() secureTextEntry?: ITextInputProps['secureTextEntry'];
619
- @Input() selectTextOnFocus?: ITextInputProps['selectTextOnFocus'];
620
- @Input() selection?: ITextInputProps['selection'];
621
- @Input() selectionColor?: ITextInputProps['selectionColor'];
622
- @Input() selectionHandleColor?: ITextInputProps['selectionHandleColor'];
623
- @Input() showSoftInputOnFocus?: ITextInputProps['showSoftInputOnFocus'];
624
- @Input() textAlign?: ITextInputProps['textAlign'];
625
- @Input() textContentType?: ITextInputProps['textContentType'];
626
- @Input() underlineColorAndroid?: ITextInputProps['underlineColorAndroid'];
627
- @Input() onValueChange?: ITextInputProps['onValueChange'];
628
- @Input() onChangeText?: ITextInputProps['onChangeText'];
629
- @Input() onContentSizeChange?: ITextInputProps['onContentSizeChange'];
630
- @Input() onEndEditing?: ITextInputProps['onEndEditing'];
631
- @Input() onKeyPress?: ITextInputProps['onKeyPress'];
632
- @Input() onSelectionChange?: ITextInputProps['onSelectionChange'];
633
- @Input() onSubmitEditing?: ITextInputProps['onSubmitEditing'];
634
-
635
- // NonNullable, not the prop type: `[(value)]="name"` binds a `string`, and an emitter that can
636
- // also emit `undefined` fails the two-way assignability check ngtsc runs on the event half.
637
- @Output() readonly valueChange = new EventEmitter<
638
- NonNullable<ITextInputProps['value']>
639
- >();
640
-
641
- protected hasValueSubscriber(): boolean {
642
- return this.valueChange.observed;
643
- }
644
-
645
- protected emitValue(value: unknown): void {
646
- if (typeof value === 'string') this.valueChange.emit(value);
647
- }
648
- }
649
-
650
- @Directive({ selector: 'text-input-multiline', standalone: true })
651
- export class MultilineTextInputElement extends TextInputElement {}
652
-
653
- @Directive({ selector: 'switch, symbiote-switch', standalone: true })
654
- export class SwitchElement extends ValueChangeElement {
655
- @Input() value?: ISwitchProps['value'];
656
- @Input() disabled?: ISwitchProps['disabled'];
657
- @Input() trackColor?: ISwitchProps['trackColor'];
658
- @Input() thumbColor?: ISwitchProps['thumbColor'];
659
- @Input() ios_backgroundColor?: ISwitchProps['ios_backgroundColor'];
660
- @Input() onValueChange?: ISwitchProps['onValueChange'];
661
-
662
- @Output() readonly valueChange = new EventEmitter<
663
- NonNullable<ISwitchProps['value']>
664
- >();
665
-
666
- protected hasValueSubscriber(): boolean {
667
- return this.valueChange.observed;
668
- }
669
-
670
- protected emitValue(value: unknown): void {
671
- if (typeof value === 'boolean') this.valueChange.emit(value);
672
- }
673
- }
674
-
675
- /**
676
- * `[(ngModel)]` / `formControlName` on a `<text-input>` or a `<switch>`.
677
- *
678
- * The wrappers provided `NG_VALUE_ACCESSOR` themselves; a tag has no class for @angular/forms to
679
- * call into, so the accessor moves onto a directive of its own rather than being dropped — the
680
- * shape Angular's own `DefaultValueAccessor` takes for `<input>`. Providing it unconditionally is
681
- * safe: @angular/forms looks the accessor up only when an `ngModel` / `formControl*` directive sits
682
- * on the SAME element, so a plain `<switch [(value)]>` never reaches it.
683
- *
684
- * Everything below goes through `Renderer2`, which is the adapter's own — `setProperty` lands in
685
- * `routeProp` and `listen('valueChange')` is already unwrapped to a bare value there, so this
686
- * directive holds no fold of its own.
687
- */
688
- @Directive()
689
- abstract class SymbioteValueAccessor
690
- implements ControlValueAccessor, OnDestroy
691
- {
692
- private readonly renderer = inject(Renderer2);
693
- private readonly host = inject(ElementRef);
694
- private unlisten?: () => void;
695
-
696
- // The prop that spells "not editable" for this tag: RN's TextInput has no `disabled`, it has
697
- // `editable` (inverted), while Switch takes `disabled` straight.
698
- protected abstract writeDisabled(isDisabled: boolean): void;
699
-
700
- protected setProp(name: string, value: unknown): void {
701
- this.renderer.setProperty(this.host.nativeElement, name, value);
702
- }
703
-
704
- writeValue(value: unknown): void {
705
- this.setProp('value', value ?? undefined);
706
- }
707
-
708
- registerOnChange(fn: (value: unknown) => void): void {
709
- this.unlisten?.();
710
- this.unlisten = this.renderer.listen(
711
- this.host.nativeElement,
712
- VALUE_CHANGE_EVENT,
713
- (value: unknown) => {
714
- // THE HALF THE DOM DOES FOR FREE, and without it a controlled input undoes every
715
- // keystroke. `<text-input>`'s behavior re-commands the native text whenever `props.value`
716
- // disagrees with what native last reported — that is what makes it controlled — and the
717
- // read-back flush exists so the app's new value is on the node by the time the commit runs.
718
- // @angular/forms does not reach the node in that window: `NgModel.ngOnChanges` defers
719
- // `_updateValue` through `resolvedPromise.then`, so `writeValue` lands a MICROTASK after
720
- // the flush, and the commit in between still reads the value from before the keystroke.
721
- //
722
- // In a browser there is nothing to do here: `input.value` already holds what the user
723
- // typed. `node.props.value` is the same slot, and nothing else writes it — so the accessor
724
- // mirrors it, which is the shape rather than a workaround. A later `writeValue` still wins,
725
- // so an app that transforms or refuses the value keeps doing so, one microtask on.
726
- //
727
- // Without this, every character snaps the field back to its mounted text.
728
- this.setProp('value', value);
729
- fn(value);
730
- },
731
- );
732
- }
733
-
734
- // Native has no blur-driven "touched" signal distinct from `blur`, and RN's own onBlur is what an
735
- // app binds — so touched is driven off the same event rather than a second channel.
736
- registerOnTouched(fn: () => void): void {
737
- this.setProp('onBlur', () => fn());
738
- }
739
-
740
- setDisabledState(isDisabled: boolean): void {
741
- this.writeDisabled(isDisabled);
742
- }
743
-
744
- ngOnDestroy(): void {
745
- this.unlisten?.();
746
- }
747
- }
748
-
749
- @Directive({
750
- selector:
751
- 'text-input[ngModel], text-input[formControl], text-input[formControlName], text-input-multiline[ngModel], text-input-multiline[formControl], text-input-multiline[formControlName]',
752
- standalone: true,
753
- providers: [
754
- {
755
- provide: NG_VALUE_ACCESSOR,
756
- useExisting: forwardRef(() => TextInputValueAccessor),
757
- multi: true,
758
- },
759
- ],
760
- })
761
- export class TextInputValueAccessor extends SymbioteValueAccessor {
762
- protected override writeDisabled(isDisabled: boolean): void {
763
- this.setProp('editable', !isDisabled);
764
- }
765
- }
766
-
767
- @Directive({
768
- selector: 'switch[ngModel], switch[formControl], switch[formControlName]',
769
- standalone: true,
770
- providers: [
771
- {
772
- provide: NG_VALUE_ACCESSOR,
773
- useExisting: forwardRef(() => SwitchValueAccessor),
774
- multi: true,
775
- },
776
- ],
777
- })
778
- export class SwitchValueAccessor extends SymbioteValueAccessor {
779
- protected override writeDisabled(isDisabled: boolean): void {
780
- this.setProp('disabled', isDisabled);
781
- }
782
- }
783
-
784
- // The HOST — RN's centering RCTView (ActivityIndicator.js:112), which is the tag an app writes.
785
- // The four spinner props are declared here because that is where the app writes them; the engine's
786
- // behavior redirects them onto the spinner it builds underneath (`slotProps`).
787
- @Directive({ selector: 'activity-indicator', standalone: true })
788
- export class ActivityIndicatorElement extends SymbioteElement {
789
- @Input() animating?: IActivityIndicatorProps['animating'];
790
- @Input() color?: IActivityIndicatorProps['color'];
791
- @Input() size?: IActivityIndicatorProps['size'];
792
- @Input() hidesWhenStopped?: IActivityIndicatorProps['hidesWhenStopped'];
793
- }
794
-
795
- // The native spinner. Built by the behavior's `buildStructure` and by the wrapper's render fn, never
796
- // written in an app template — declared only so the tag alphabet stays covered, the same as
797
- // `scroll-content`.
798
- @Directive({ selector: 'activity-indicator-spinner', standalone: true })
799
- export class ActivityIndicatorSpinnerElement extends ActivityIndicatorElement {}
800
-
801
- @Directive({ selector: 'safe-area-view', standalone: true })
802
- export class SafeAreaViewElement extends SymbioteElement {}
803
-
804
- @Directive({ selector: 'modal, symbiote-modal', standalone: true })
805
- export class ModalElement extends SymbioteElement {
806
- @Input() visible?: IModalViewProps['visible'];
807
- @Input() transparent?: IModalViewProps['transparent'];
808
- @Input() animationType?: IModalViewProps['animationType'];
809
- @Input() presentationStyle?: IModalViewProps['presentationStyle'];
810
- @Input() supportedOrientations?: IModalViewProps['supportedOrientations'];
811
- @Input() hardwareAccelerated?: IModalViewProps['hardwareAccelerated'];
812
- @Input() statusBarTranslucent?: IModalViewProps['statusBarTranslucent'];
813
- @Input()
814
- navigationBarTranslucent?: IModalViewProps['navigationBarTranslucent'];
815
- @Input() allowSwipeDismissal?: IModalViewProps['allowSwipeDismissal'];
816
- @Input() backdropColor?: IModalViewProps['backdropColor'];
817
- }
818
-
819
- @Directive({ selector: 'refresh-control', standalone: true })
820
- export class RefreshControlElement extends ReadBackElement {
821
- @Input() refreshing?: IAngularRefreshControlProps['refreshing'];
822
- @Input() enabled?: IAngularRefreshControlProps['enabled'];
823
- @Input() colors?: IAngularRefreshControlProps['colors'];
824
- @Input() tintColor?: IAngularRefreshControlProps['tintColor'];
825
- @Input() title?: IAngularRefreshControlProps['title'];
826
- @Input() titleColor?: IAngularRefreshControlProps['titleColor'];
827
- @Input() size?: IAngularRefreshControlProps['size'];
828
- @Input()
829
- progressBackgroundColor?: IAngularRefreshControlProps['progressBackgroundColor'];
830
- @Input()
831
- progressViewOffset?: IAngularRefreshControlProps['progressViewOffset'];
832
- @Input() onRefresh?: IAngularRefreshControlProps['onRefresh'];
833
- }
834
-
835
- @Directive({ selector: 'sticky-header', standalone: true })
836
- export class StickyHeaderElement extends SymbioteElement {
837
- @Input() inverted?: IStickyHeaderElementProps['inverted'];
838
- @Input() nextHeaderLayoutY?: IStickyHeaderElementProps['nextHeaderLayoutY'];
839
- @Input()
840
- scrollAnimatedValue?: IStickyHeaderElementProps['scrollAnimatedValue'];
841
- @Input() scrollViewHeight?: IStickyHeaderElementProps['scrollViewHeight'];
842
- }
843
-
844
- @Directive({ selector: 'input-accessory-view', standalone: true })
845
- export class InputAccessoryViewElement extends SymbioteElement {
846
- @Input() backgroundColor?: IInputAccessoryViewViewProps['backgroundColor'];
847
- }
95
+ // Классы живут в соседних модулях по ответственности, этот файл остаётся их единой точкой входа
96
+ export {
97
+ ActivityIndicatorElement,
98
+ ActivityIndicatorSpinnerElement,
99
+ ButtonElement,
100
+ CheckboxElement,
101
+ HorizontalScrollContentElement,
102
+ HorizontalScrollViewElement,
103
+ ImageBackgroundElement,
104
+ ImageElement,
105
+ InputAccessoryViewElement,
106
+ ModalElement,
107
+ MultilineTextInputElement,
108
+ PressableElement,
109
+ RefreshControlElement,
110
+ SafeAreaViewElement,
111
+ ScrollContentElement,
112
+ ScrollViewElement,
113
+ StickyHeaderElement,
114
+ SwitchElement,
115
+ SwitchValueAccessor,
116
+ SymbioteElement,
117
+ TextElement,
118
+ TextInputElement,
119
+ TextInputValueAccessor,
120
+ TouchableHighlightElement,
121
+ TouchableNativeFeedbackElement,
122
+ TouchableOpacityElement,
123
+ TouchableWithoutFeedbackElement,
124
+ ViewElement,
125
+ };
848
126
 
849
127
  /**
850
128
  * Every element directive, as ONE symbol an app puts in `imports`. Angular flattens a nested array
@@ -863,6 +141,7 @@ export const SYMBIOTE_ELEMENTS = [
863
141
  TouchableNativeFeedbackElement,
864
142
  TouchableWithoutFeedbackElement,
865
143
  ButtonElement,
144
+ CheckboxElement,
866
145
  TextElement,
867
146
  ImageElement,
868
147
  ImageBackgroundElement,
@@ -941,6 +220,11 @@ const DECLARES_EVERY_PROP: {
941
220
  IAngularTouchableHighlightProps,
942
221
  TouchableHighlightElement
943
222
  >;
223
+ checkbox: IMissingInputs<
224
+ ICheckboxProps,
225
+ CheckboxElement,
226
+ keyof IElementProps
227
+ >;
944
228
  text: IMissingInputs<ITextElementProps, TextElement>;
945
229
  image: IMissingInputs<IImageProps, ImageElement>;
946
230
  imageBackground: IMissingInputs<
@@ -974,6 +258,7 @@ const DECLARES_EVERY_PROP: {
974
258
  pressable: true,
975
259
  touchableOpacity: true,
976
260
  touchableHighlight: true,
261
+ checkbox: true,
977
262
  text: true,
978
263
  image: true,
979
264
  imageBackground: true,