@symbiote-native/angular 1.0.0 → 2.0.1
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.
- package/README.md +13 -10
- package/babel-register-composed.cjs +41 -19
- package/bin/symbiote-angular-dev.cjs +16 -4
- package/build/angular/anchor-host-registry.js +1 -17
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/change-detection-flush.d.ts +31 -0
- package/build/angular/change-detection-flush.js +110 -0
- package/build/angular/change-detection-flush.js.map +1 -0
- package/build/angular/components/activity-indicator-props.d.ts +2 -0
- package/build/angular/components/activity-indicator-props.js +2 -0
- package/build/angular/components/activity-indicator-props.js.map +1 -0
- package/build/angular/components/button-props.d.ts +2 -0
- package/build/angular/components/button-props.js +2 -0
- package/build/angular/components/button-props.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +2 -1
- package/build/angular/components/flat-list/index.js +22 -10
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image-background-props.d.ts +6 -0
- package/build/angular/components/image-background-props.js +2 -0
- package/build/angular/components/image-background-props.js.map +1 -0
- package/build/angular/components/{image/shared.d.ts → image-shared.d.ts} +1 -1
- package/build/angular/components/{image/shared.js → image-shared.js} +7 -2
- package/build/angular/components/image-shared.js.map +1 -0
- package/build/angular/components/input-accessory-view-props.d.ts +7 -0
- package/build/angular/components/input-accessory-view-props.js +9 -0
- package/build/angular/components/input-accessory-view-props.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view/index.js +11 -17
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +1 -0
- package/build/angular/components/modal/index.js +19 -12
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable-props.d.ts +32 -0
- package/build/angular/components/pressable-props.js +11 -0
- package/build/angular/components/pressable-props.js.map +1 -0
- package/build/angular/components/refresh-control-props.d.ts +16 -0
- package/build/angular/components/refresh-control-props.js +14 -0
- package/build/angular/components/refresh-control-props.js.map +1 -0
- package/build/angular/components/safe-area-view-props.d.ts +7 -0
- package/build/angular/components/safe-area-view-props.js +11 -0
- package/build/angular/components/safe-area-view-props.js.map +1 -0
- package/build/angular/components/scroll-view-props.d.ts +76 -0
- package/build/angular/components/scroll-view-props.js +18 -0
- package/build/angular/components/scroll-view-props.js.map +1 -0
- package/build/angular/components/text-input-props.d.ts +45 -0
- package/build/angular/components/text-input-props.js +18 -0
- package/build/angular/components/text-input-props.js.map +1 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +6 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js +2 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js.map +1 -0
- package/build/angular/components/touchable-props.d.ts +14 -0
- package/build/angular/components/touchable-props.js +13 -0
- package/build/angular/components/touchable-props.js.map +1 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +3 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js +2 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +5 -1
- package/build/angular/components/virtualized-list/index.js +366 -280
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components.d.ts +20 -26
- package/build/angular/components.js +12 -14
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.d.ts +2 -2
- package/build/angular/create-portal/index.js +5 -5
- package/build/angular/create-tunnel/index.js +1 -1
- package/build/angular/element-props.d.ts +53 -0
- package/build/angular/element-props.js +2 -0
- package/build/angular/element-props.js.map +1 -0
- package/build/angular/elements.d.ts +528 -0
- package/build/angular/elements.js +1444 -0
- package/build/angular/elements.js.map +1 -0
- package/build/angular/host-instance/index.js +1 -1
- package/build/angular/index.d.ts +4 -2
- package/build/angular/index.js +15 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +5 -6
- package/build/angular/modules/animated/create-animated-component.js +60 -73
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/animated/index.js +1 -1
- package/build/angular/modules/animated/index.js.map +1 -1
- package/build/angular/modules/image/index.d.ts +3 -0
- package/build/angular/modules/image/index.js +16 -0
- package/build/angular/modules/image/index.js.map +1 -0
- package/build/angular/primitives/index.d.ts +18 -18
- package/build/angular/primitives/index.js +40 -36
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +2 -3
- package/build/angular/primitives/shared.js +8 -53
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/register.d.ts +1 -1
- package/build/angular/register.js +12 -31
- package/build/angular/register.js.map +1 -1
- package/build/angular/render/index.js +3 -3
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.js +113 -67
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/renderer/value-change.d.ts +2 -0
- package/build/angular/renderer/value-change.js +9 -0
- package/build/angular/renderer/value-change.js.map +1 -0
- package/metro-config.cjs +54 -20
- package/metro-css-parser.cjs +2 -1
- package/package.json +5 -14
- package/src/anchor-host-registry.ts +1 -17
- package/src/change-detection-flush.ts +120 -0
- package/src/components/activity-indicator-props.ts +21 -0
- package/src/components/button-props.ts +26 -0
- package/src/components/flat-list/index.ts +12 -4
- package/src/components/image-background-props.ts +24 -0
- package/src/components/{image/shared.ts → image-shared.ts} +6 -1
- package/src/components/input-accessory-view-props.ts +21 -0
- package/src/components/keyboard-avoiding-view/index.ts +6 -9
- package/src/components/modal/index.ts +15 -7
- package/src/components/pressable-props.ts +73 -0
- package/src/components/refresh-control-props.ts +43 -0
- package/src/components/safe-area-view-props.ts +28 -0
- package/src/components/scroll-view-props.ts +109 -0
- package/src/components/text-input-props.ts +72 -0
- package/src/components/touchable-native-feedback/touchable-native-feedback-props.ts +23 -0
- package/src/components/touchable-props.ts +30 -0
- package/src/components/touchable-without-feedback/touchable-without-feedback-props.ts +21 -0
- package/src/components/virtualized-list/index.ts +239 -156
- package/src/components.ts +84 -35
- package/src/create-portal/index.ts +5 -5
- package/src/create-tunnel/index.ts +1 -1
- package/src/diagnostics.ts +1 -1
- package/src/element-props.ts +90 -0
- package/src/elements.ts +919 -0
- package/src/host-instance/index.ts +1 -1
- package/src/index.ts +52 -15
- package/src/modules/animated/create-animated-component.ts +50 -55
- package/src/modules/animated/index.ts +1 -1
- package/src/modules/image/index.ts +18 -0
- package/src/primitives/index.ts +22 -18
- package/src/primitives/shared.ts +11 -44
- package/src/register.ts +12 -40
- package/src/render/index.ts +3 -3
- package/src/renderer/index.ts +122 -73
- package/src/renderer/value-change.ts +8 -0
- package/babel-lower-host-primitives.cjs +0 -636
- package/build/angular/components/activity-indicator/index.android.d.ts +0 -13
- package/build/angular/components/activity-indicator/index.android.js +0 -29
- package/build/angular/components/activity-indicator/index.android.js.map +0 -1
- package/build/angular/components/activity-indicator/index.d.ts +0 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +0 -9
- package/build/angular/components/activity-indicator/index.ios.js +0 -26
- package/build/angular/components/activity-indicator/index.ios.js.map +0 -1
- package/build/angular/components/activity-indicator/index.js +0 -4
- package/build/angular/components/activity-indicator/index.js.map +0 -1
- package/build/angular/components/activity-indicator/shared.d.ts +0 -67
- package/build/angular/components/activity-indicator/shared.js +0 -260
- package/build/angular/components/activity-indicator/shared.js.map +0 -1
- package/build/angular/components/button.d.ts +0 -68
- package/build/angular/components/button.js +0 -360
- package/build/angular/components/button.js.map +0 -1
- package/build/angular/components/image/index.android.d.ts +0 -15
- package/build/angular/components/image/index.android.js +0 -83
- package/build/angular/components/image/index.android.js.map +0 -1
- package/build/angular/components/image/index.d.ts +0 -1
- package/build/angular/components/image/index.ios.d.ts +0 -15
- package/build/angular/components/image/index.ios.js +0 -83
- package/build/angular/components/image/index.ios.js.map +0 -1
- package/build/angular/components/image/index.js +0 -3
- package/build/angular/components/image/index.js.map +0 -1
- package/build/angular/components/image/shared.js.map +0 -1
- package/build/angular/components/image-background.d.ts +0 -85
- package/build/angular/components/image-background.js +0 -443
- package/build/angular/components/image-background.js.map +0 -1
- package/build/angular/components/input-accessory-view/index.d.ts +0 -66
- package/build/angular/components/input-accessory-view/index.js +0 -287
- package/build/angular/components/input-accessory-view/index.js.map +0 -1
- package/build/angular/components/pressable/index.d.ts +0 -134
- package/build/angular/components/pressable/index.js +0 -514
- package/build/angular/components/pressable/index.js.map +0 -1
- package/build/angular/components/refresh-control/index.d.ts +0 -91
- package/build/angular/components/refresh-control/index.js +0 -382
- package/build/angular/components/refresh-control/index.js.map +0 -1
- package/build/angular/components/safe-area-view/index.d.ts +0 -64
- package/build/angular/components/safe-area-view/index.js +0 -287
- package/build/angular/components/safe-area-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/index.android.d.ts +0 -20
- package/build/angular/components/scroll-view/index.android.js +0 -262
- package/build/angular/components/scroll-view/index.android.js.map +0 -1
- package/build/angular/components/scroll-view/index.d.ts +0 -3
- package/build/angular/components/scroll-view/index.ios.d.ts +0 -8
- package/build/angular/components/scroll-view/index.ios.js +0 -92
- package/build/angular/components/scroll-view/index.ios.js.map +0 -1
- package/build/angular/components/scroll-view/index.js +0 -6
- package/build/angular/components/scroll-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/projection.d.ts +0 -67
- package/build/angular/components/scroll-view/projection.js +0 -560
- package/build/angular/components/scroll-view/projection.js.map +0 -1
- package/build/angular/components/scroll-view/shared.d.ts +0 -241
- package/build/angular/components/scroll-view/shared.js +0 -848
- package/build/angular/components/scroll-view/shared.js.map +0 -1
- package/build/angular/components/scroll-view/sticky-header.d.ts +0 -40
- package/build/angular/components/scroll-view/sticky-header.js +0 -224
- package/build/angular/components/scroll-view/sticky-header.js.map +0 -1
- package/build/angular/components/switch/index.android.d.ts +0 -24
- package/build/angular/components/switch/index.android.js +0 -112
- package/build/angular/components/switch/index.android.js.map +0 -1
- package/build/angular/components/switch/index.d.ts +0 -1
- package/build/angular/components/switch/index.ios.d.ts +0 -23
- package/build/angular/components/switch/index.ios.js +0 -111
- package/build/angular/components/switch/index.ios.js.map +0 -1
- package/build/angular/components/switch/index.js +0 -4
- package/build/angular/components/switch/index.js.map +0 -1
- package/build/angular/components/switch/shared.d.ts +0 -49
- package/build/angular/components/switch/shared.js +0 -191
- package/build/angular/components/switch/shared.js.map +0 -1
- package/build/angular/components/text-input.d.ts +0 -167
- package/build/angular/components/text-input.js +0 -684
- package/build/angular/components/text-input.js.map +0 -1
- package/build/angular/components/touchable/index.d.ts +0 -283
- package/build/angular/components/touchable/index.js +0 -1453
- package/build/angular/components/touchable/index.js.map +0 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +0 -87
- package/build/angular/components/touchable-native-feedback/index.js +0 -409
- package/build/angular/components/touchable-native-feedback/index.js.map +0 -1
- package/build/angular/state-style.d.ts +0 -9
- package/build/angular/state-style.js +0 -39
- package/build/angular/state-style.js.map +0 -1
- package/metro-transformer.cjs +0 -69
- package/src/components/activity-indicator/index.android.ts +0 -23
- package/src/components/activity-indicator/index.ios.ts +0 -21
- package/src/components/activity-indicator/index.ts +0 -4
- package/src/components/activity-indicator/shared.ts +0 -225
- package/src/components/button.ts +0 -237
- package/src/components/image/index.android.ts +0 -95
- package/src/components/image/index.ios.ts +0 -95
- package/src/components/image/index.ts +0 -3
- package/src/components/image-background.ts +0 -308
- package/src/components/input-accessory-view/index.ts +0 -246
- package/src/components/pressable/index.ts +0 -522
- package/src/components/refresh-control/index.ts +0 -349
- package/src/components/safe-area-view/index.ts +0 -247
- package/src/components/scroll-view/index.android.ts +0 -205
- package/src/components/scroll-view/index.ios.ts +0 -81
- package/src/components/scroll-view/index.ts +0 -7
- package/src/components/scroll-view/projection.ts +0 -713
- package/src/components/scroll-view/shared.ts +0 -1093
- package/src/components/scroll-view/sticky-header.ts +0 -267
- package/src/components/switch/index.android.ts +0 -113
- package/src/components/switch/index.ios.ts +0 -112
- package/src/components/switch/index.ts +0 -4
- package/src/components/switch/shared.ts +0 -284
- package/src/components/text-input.ts +0 -669
- package/src/components/touchable/index.ts +0 -956
- package/src/components/touchable-native-feedback/index.ts +0 -327
- package/src/state-style.ts +0 -39
|
@@ -1,669 +0,0 @@
|
|
|
1
|
-
// TextInput, the Angular lifecycle half. The folds/maps (value->text, the W3C/alias resolution)
|
|
2
|
-
// and the controlled-write predicate live in @symbiote-native/components/state, the render (intrinsic
|
|
3
|
-
// + native-prop mapping) in @symbiote-native/components/view, both shared verbatim with the React and
|
|
4
|
-
// Vue adapters. Here Angular supplies only the lifecycle: renderTextInput picks ONE of two host
|
|
5
|
-
// intrinsics at runtime (symbiote-text-input-managed / …-multiline-managed) via `@if`/`@else`,
|
|
6
|
-
// each wired through the shared `SymbioteHostPropsDirective` (`[symbioteHostProps]="hostProps"`,
|
|
7
|
-
// `../primitives/shared.ts`) so the flat resolved prop bag doesn't need enumerating as individual
|
|
8
|
-
// `[prop]` bindings; a @ViewChild reading that directive (by its `#host` template ref, across
|
|
9
|
-
// either branch) exposes `.node` — the host BY IDENTITY (so imperative commands hit the engine's
|
|
10
|
-
// WeakMap mirror). A plain field holds the acknowledged event count, ngOnChanges runs the
|
|
11
|
-
// controlled-write command, ngAfterViewInit drives the mount autoFocus, and the class itself is
|
|
12
|
-
// the imperative handle (ITextInputHandle). The Angular twin of React's useRef/useState +
|
|
13
|
-
// useLayoutEffect + useImperativeHandle and Vue's shallowRef + watch(flush:'post') + expose().
|
|
14
|
-
//
|
|
15
|
-
// Two Angular-specific seams. (1) Events: Angular forbids [onX] property bindings, so the host's
|
|
16
|
-
// change/focus/blur + the remaining native events ride the structural (event) channel and route
|
|
17
|
-
// to real @Output() EventEmitters (valueChange is DERIVED from change, never a native event —
|
|
18
|
-
// React/Vue fold both into one onValueChange(text, event) callback, but Angular's EventEmitter
|
|
19
|
-
// only carries one value, so valueChange stays text-only to keep [(value)] banana-in-a-box
|
|
20
|
-
// working; `change` stays a second, separate @Output() for the raw event). The
|
|
21
|
-
// non-function props travel the flat-bag path via the host-props directive. (2) Commit timing:
|
|
22
|
-
// Angular's change detection is async/batched (zoneless), so a native command wired at lifecycle
|
|
23
|
-
// time has no Fabric tag yet; the controlled write + autoFocus defer through whenCommitted, the
|
|
24
|
-
// same gotcha Vue hits. The controlled handshake hinges on commanding native back with the
|
|
25
|
-
// ACKNOWLEDGED event count (setTextAndSelection), never a plain prop re-push — see
|
|
26
|
-
// @symbiote-native/components/state/text-input.
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
CUSTOM_ELEMENTS_SCHEMA,
|
|
30
|
-
ChangeDetectionStrategy,
|
|
31
|
-
ChangeDetectorRef,
|
|
32
|
-
Component,
|
|
33
|
-
ElementRef,
|
|
34
|
-
EventEmitter,
|
|
35
|
-
forwardRef,
|
|
36
|
-
inject,
|
|
37
|
-
Input,
|
|
38
|
-
Output,
|
|
39
|
-
ViewChild,
|
|
40
|
-
type AfterViewInit,
|
|
41
|
-
type OnChanges,
|
|
42
|
-
type OnDestroy,
|
|
43
|
-
type SimpleChanges,
|
|
44
|
-
} from '@angular/core';
|
|
45
|
-
import { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms';
|
|
46
|
-
import {
|
|
47
|
-
foldText,
|
|
48
|
-
eventCountFromChange,
|
|
49
|
-
textFromChange,
|
|
50
|
-
shouldCommandText,
|
|
51
|
-
renderTextInput,
|
|
52
|
-
resolveAccessibilityProps,
|
|
53
|
-
resolveTextInputProps,
|
|
54
|
-
INITIAL_EVENT_COUNT,
|
|
55
|
-
SELECTION_NONE,
|
|
56
|
-
type IAccessibilityProps,
|
|
57
|
-
type IAccessibilityStateValue,
|
|
58
|
-
type IAriaProps,
|
|
59
|
-
type IEnterKeyHint,
|
|
60
|
-
type IFoldedTextInputProps,
|
|
61
|
-
type IInputMode,
|
|
62
|
-
type ISubmitBehavior,
|
|
63
|
-
type ITextInputEventHandler,
|
|
64
|
-
type ITextInputHandle,
|
|
65
|
-
type ITextInputSelection,
|
|
66
|
-
} from '@symbiote-native/components';
|
|
67
|
-
import {
|
|
68
|
-
blurTextInput,
|
|
69
|
-
dispatchViewCommand,
|
|
70
|
-
dlog,
|
|
71
|
-
isSymbioteEvent,
|
|
72
|
-
isSymbioteNode,
|
|
73
|
-
setInputBlurred,
|
|
74
|
-
setInputFocused,
|
|
75
|
-
whenCommitted,
|
|
76
|
-
type IMeasureInWindowOnSuccess,
|
|
77
|
-
type IMeasureLayoutOnSuccess,
|
|
78
|
-
type IMeasureOnSuccess,
|
|
79
|
-
type IStyleProp,
|
|
80
|
-
type ISymbioteEvent,
|
|
81
|
-
type ISymbioteNode,
|
|
82
|
-
type ITextStyle,
|
|
83
|
-
} from '@symbiote-native/engine';
|
|
84
|
-
import {
|
|
85
|
-
anchorHostStyle,
|
|
86
|
-
ManagedMultilineTextInputHost,
|
|
87
|
-
SymbioteHostPropsDirective,
|
|
88
|
-
SymbioteStyleInputDirective,
|
|
89
|
-
ManagedTextInputHost,
|
|
90
|
-
} from '../primitives';
|
|
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
|
-
};
|
|
104
|
-
|
|
105
|
-
// Mirrors React's ITextInputProps minus nothing (TextInput has no children) — declared
|
|
106
|
-
// per-adapter over the shared accessibility base because Angular's input surface aliases the
|
|
107
|
-
// aria-* keys to camelCase @Inputs, unlike the plain agnostic fields shared across adapters.
|
|
108
|
-
export interface IAngularTextInputProps
|
|
109
|
-
extends IAccessibilityProps, IAriaProps {
|
|
110
|
-
value?: string;
|
|
111
|
-
defaultValue?: string;
|
|
112
|
-
placeholder?: string;
|
|
113
|
-
placeholderTextColor?: string;
|
|
114
|
-
editable?: boolean;
|
|
115
|
-
keyboardType?: string;
|
|
116
|
-
secureTextEntry?: boolean;
|
|
117
|
-
maxLength?: number;
|
|
118
|
-
multiline?: boolean;
|
|
119
|
-
selection?: ITextInputSelection;
|
|
120
|
-
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
|
|
121
|
-
autoCorrect?: boolean;
|
|
122
|
-
autoComplete?: string;
|
|
123
|
-
textContentType?: string;
|
|
124
|
-
autoFocus?: boolean;
|
|
125
|
-
showSoftInputOnFocus?: boolean;
|
|
126
|
-
returnKeyType?: string;
|
|
127
|
-
selectTextOnFocus?: boolean;
|
|
128
|
-
scrollEnabled?: boolean;
|
|
129
|
-
numberOfLines?: number;
|
|
130
|
-
textAlign?: 'left' | 'center' | 'right';
|
|
131
|
-
blurOnSubmit?: boolean;
|
|
132
|
-
inputMode?: IInputMode;
|
|
133
|
-
enterKeyHint?: IEnterKeyHint;
|
|
134
|
-
readOnly?: boolean;
|
|
135
|
-
submitBehavior?: ISubmitBehavior;
|
|
136
|
-
cursorColor?: string;
|
|
137
|
-
selectionColor?: string;
|
|
138
|
-
selectionHandleColor?: string;
|
|
139
|
-
underlineColorAndroid?: string;
|
|
140
|
-
inputAccessoryViewID?: string;
|
|
141
|
-
style?: IStyleProp<ITextStyle>;
|
|
142
|
-
onValueChange?: (text: string) => void;
|
|
143
|
-
onChange?: ITextInputEventHandler;
|
|
144
|
-
onFocus?: ITextInputEventHandler;
|
|
145
|
-
onBlur?: ITextInputEventHandler;
|
|
146
|
-
onEndEditing?: ITextInputEventHandler;
|
|
147
|
-
onSubmitEditing?: ITextInputEventHandler;
|
|
148
|
-
onKeyPress?: ITextInputEventHandler;
|
|
149
|
-
onSelectionChange?: ITextInputEventHandler;
|
|
150
|
-
onContentSizeChange?: ITextInputEventHandler;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// What the TextInput component itself takes as plain @Input()s: the full surface minus every
|
|
154
|
-
// callback-shaped event, which it exposes as real @Output() EventEmitters instead (see the class
|
|
155
|
-
// below) — mirrors Pressable's IAngularPressableInputs / IAngularPressableProps split.
|
|
156
|
-
export type IAngularTextInputInputs = Omit<
|
|
157
|
-
IAngularTextInputProps,
|
|
158
|
-
| 'onValueChange'
|
|
159
|
-
| 'onChange'
|
|
160
|
-
| 'onFocus'
|
|
161
|
-
| 'onBlur'
|
|
162
|
-
| 'onEndEditing'
|
|
163
|
-
| 'onSubmitEditing'
|
|
164
|
-
| 'onKeyPress'
|
|
165
|
-
| 'onSelectionChange'
|
|
166
|
-
| 'onContentSizeChange'
|
|
167
|
-
| 'onAccessibilityAction'
|
|
168
|
-
| 'onAccessibilityTap'
|
|
169
|
-
| 'onMagicTap'
|
|
170
|
-
| 'onAccessibilityEscape'
|
|
171
|
-
>;
|
|
172
|
-
|
|
173
|
-
@Component({
|
|
174
|
-
selector: 'TextInput',
|
|
175
|
-
standalone: true,
|
|
176
|
-
hostDirectives: [
|
|
177
|
-
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
178
|
-
],
|
|
179
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
180
|
-
imports: [
|
|
181
|
-
SymbioteHostPropsDirective,
|
|
182
|
-
ManagedTextInputHost,
|
|
183
|
-
ManagedMultilineTextInputHost,
|
|
184
|
-
],
|
|
185
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
186
|
-
providers: [
|
|
187
|
-
{
|
|
188
|
-
provide: NG_VALUE_ACCESSOR,
|
|
189
|
-
useExisting: forwardRef(() => TextInput),
|
|
190
|
-
multi: true,
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
template: `
|
|
194
|
-
@if (isMultiline) {
|
|
195
|
-
<symbiote-text-input-multiline-managed
|
|
196
|
-
#host
|
|
197
|
-
[symbioteHostProps]="hostProps"
|
|
198
|
-
(change)="handleChange($event)"
|
|
199
|
-
(focus)="handleFocus($event)"
|
|
200
|
-
(blur)="handleBlur($event)"
|
|
201
|
-
(selectionChange)="emit(selectionChange, $event)"
|
|
202
|
-
(submitEditing)="emit(submitEditing, $event)"
|
|
203
|
-
(keyPress)="emit(keyPress, $event)"
|
|
204
|
-
(endEditing)="emit(endEditing, $event)"
|
|
205
|
-
(contentSizeChange)="emit(contentSizeChange, $event)"
|
|
206
|
-
></symbiote-text-input-multiline-managed>
|
|
207
|
-
} @else {
|
|
208
|
-
<symbiote-text-input-managed
|
|
209
|
-
#host
|
|
210
|
-
[symbioteHostProps]="hostProps"
|
|
211
|
-
(change)="handleChange($event)"
|
|
212
|
-
(focus)="handleFocus($event)"
|
|
213
|
-
(blur)="handleBlur($event)"
|
|
214
|
-
(selectionChange)="emit(selectionChange, $event)"
|
|
215
|
-
(submitEditing)="emit(submitEditing, $event)"
|
|
216
|
-
(keyPress)="emit(keyPress, $event)"
|
|
217
|
-
(endEditing)="emit(endEditing, $event)"
|
|
218
|
-
(contentSizeChange)="emit(contentSizeChange, $event)"
|
|
219
|
-
></symbiote-text-input-managed>
|
|
220
|
-
}
|
|
221
|
-
`,
|
|
222
|
-
})
|
|
223
|
-
export class TextInput
|
|
224
|
-
implements
|
|
225
|
-
IAngularTextInputInputs,
|
|
226
|
-
ITextInputHandle,
|
|
227
|
-
OnChanges,
|
|
228
|
-
AfterViewInit,
|
|
229
|
-
OnDestroy,
|
|
230
|
-
ControlValueAccessor
|
|
231
|
-
{
|
|
232
|
-
@Input() value?: string;
|
|
233
|
-
@Input() defaultValue?: string;
|
|
234
|
-
@Input() placeholder?: string;
|
|
235
|
-
@Input() placeholderTextColor?: string;
|
|
236
|
-
@Input() editable?: boolean;
|
|
237
|
-
@Input() keyboardType?: string;
|
|
238
|
-
@Input() secureTextEntry?: boolean;
|
|
239
|
-
@Input() maxLength?: number;
|
|
240
|
-
@Input() multiline?: boolean;
|
|
241
|
-
@Input() selection?: ITextInputSelection;
|
|
242
|
-
@Input() autoCapitalize?: IAngularTextInputProps['autoCapitalize'];
|
|
243
|
-
@Input() autoCorrect?: boolean;
|
|
244
|
-
@Input() autoComplete?: string;
|
|
245
|
-
@Input() textContentType?: string;
|
|
246
|
-
@Input() autoFocus?: boolean;
|
|
247
|
-
@Input() showSoftInputOnFocus?: boolean;
|
|
248
|
-
@Input() returnKeyType?: string;
|
|
249
|
-
@Input() selectTextOnFocus?: boolean;
|
|
250
|
-
@Input() scrollEnabled?: boolean;
|
|
251
|
-
@Input() numberOfLines?: number;
|
|
252
|
-
@Input() textAlign?: IAngularTextInputProps['textAlign'];
|
|
253
|
-
@Input() blurOnSubmit?: boolean;
|
|
254
|
-
@Input() inputMode?: IInputMode;
|
|
255
|
-
@Input() enterKeyHint?: IEnterKeyHint;
|
|
256
|
-
@Input() readOnly?: boolean;
|
|
257
|
-
@Input() submitBehavior?: ISubmitBehavior;
|
|
258
|
-
@Input() cursorColor?: string;
|
|
259
|
-
@Input() selectionColor?: string;
|
|
260
|
-
@Input() selectionHandleColor?: string;
|
|
261
|
-
@Input() underlineColorAndroid?: string;
|
|
262
|
-
@Input() inputAccessoryViewID?: string;
|
|
263
|
-
@Input() style?: IStyleProp<ITextStyle>;
|
|
264
|
-
@Input() testID?: string;
|
|
265
|
-
@Input() nativeID?: string;
|
|
266
|
-
|
|
267
|
-
// The event lifecycle as real Angular events: `(valueChange)="onText($event)"`, not
|
|
268
|
-
// `[onValueChange]="onText"`. `focus`/`blur` collide with this same class's OWN imperative
|
|
269
|
-
// `focus()`/`blur()` handle methods (ITextInputHandle — the RN ref API a parent calls via
|
|
270
|
-
// `@ViewChild(TextInput)`), so those two alias their public @Output() binding name back to
|
|
271
|
-
// `focus`/`blur` while the class member itself is named `focusEvent`/`blurEvent` — the same
|
|
272
|
-
// `@Output(bindingName) memberName` alias this file already uses for `@Input('aria-label')`.
|
|
273
|
-
// `value`/`valueChange` names match Switch/Slider exactly, so `[(value)]="text"` (banana-in-a-box)
|
|
274
|
-
// works here too — see `valueChange`'s single-arg (text-only) signature above.
|
|
275
|
-
@Output() readonly valueChange = new EventEmitter<string>();
|
|
276
|
-
@Output() readonly change = new EventEmitter<ISymbioteEvent>();
|
|
277
|
-
@Output('focus') readonly focusEvent = new EventEmitter<ISymbioteEvent>();
|
|
278
|
-
@Output('blur') readonly blurEvent = new EventEmitter<ISymbioteEvent>();
|
|
279
|
-
@Output() readonly endEditing = new EventEmitter<ISymbioteEvent>();
|
|
280
|
-
@Output() readonly submitEditing = new EventEmitter<ISymbioteEvent>();
|
|
281
|
-
@Output() readonly keyPress = new EventEmitter<ISymbioteEvent>();
|
|
282
|
-
@Output() readonly selectionChange = new EventEmitter<ISymbioteEvent>();
|
|
283
|
-
@Output() readonly contentSizeChange = new EventEmitter<ISymbioteEvent>();
|
|
284
|
-
@Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
|
|
285
|
-
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
286
|
-
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
287
|
-
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
288
|
-
|
|
289
|
-
// Null unless a wrapper renders this TextInput in its own template. See `../gate-demand.ts`.
|
|
290
|
-
private readonly gateDemand = injectGateDemand();
|
|
291
|
-
|
|
292
|
-
@Input() accessible?: boolean;
|
|
293
|
-
@Input() accessibilityLabel?: string;
|
|
294
|
-
@Input() accessibilityHint?: string;
|
|
295
|
-
@Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
|
|
296
|
-
@Input() accessibilityState?: IAccessibilityStateValue;
|
|
297
|
-
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
298
|
-
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
299
|
-
@Input() accessibilityLabelledBy?: string | string[];
|
|
300
|
-
@Input()
|
|
301
|
-
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
302
|
-
@Input()
|
|
303
|
-
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
304
|
-
@Input() screenReaderFocusable?: boolean;
|
|
305
|
-
@Input() accessibilityViewIsModal?: boolean;
|
|
306
|
-
@Input() accessibilityElementsHidden?: boolean;
|
|
307
|
-
@Input() accessibilityIgnoresInvertColors?: boolean;
|
|
308
|
-
@Input() accessibilityLanguage?: string;
|
|
309
|
-
@Input() accessibilityRespondsToUserInteraction?: boolean;
|
|
310
|
-
@Input() accessibilityShowsLargeContentViewer?: boolean;
|
|
311
|
-
@Input() accessibilityLargeContentTitle?: string;
|
|
312
|
-
@Input() role?: IAriaProps['role'];
|
|
313
|
-
@Input('aria-label') ariaLabel?: string;
|
|
314
|
-
@Input('aria-labelledby') ariaLabelledBy?: string;
|
|
315
|
-
@Input('aria-live') ariaLive?: IAriaProps['aria-live'];
|
|
316
|
-
@Input('aria-hidden') ariaHidden?: boolean;
|
|
317
|
-
@Input('aria-busy') ariaBusy?: boolean;
|
|
318
|
-
@Input('aria-checked') ariaChecked?: boolean | 'mixed';
|
|
319
|
-
@Input('aria-disabled') ariaDisabled?: boolean;
|
|
320
|
-
@Input('aria-expanded') ariaExpanded?: boolean;
|
|
321
|
-
@Input('aria-selected') ariaSelected?: boolean;
|
|
322
|
-
|
|
323
|
-
@ViewChild('host', { read: SymbioteHostPropsDirective })
|
|
324
|
-
private hostRef?: SymbioteHostPropsDirective;
|
|
325
|
-
|
|
326
|
-
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
327
|
-
// onto (see anchorHostStyle's doc comment) — NOT `hostRef` above, which targets the real inner
|
|
328
|
-
// symbiote-text-input-managed(-multiline-managed) one level down.
|
|
329
|
-
private readonly elementRef = inject(ElementRef);
|
|
330
|
-
// Only setDisabledState() needs this: it mutates `editable` from OUTSIDE Angular's own binding
|
|
331
|
-
// path (@angular/forms calling in directly), which doesn't itself schedule a tick under zoneless CD.
|
|
332
|
-
private readonly changeDetector = inject(ChangeDetectorRef);
|
|
333
|
-
|
|
334
|
-
// The count native last acknowledged; the controlled write / clear / setSelection echo it so
|
|
335
|
-
// native's eventLag lands on 0 and the write applies. A plain field (read live by the imperative
|
|
336
|
-
// handle), the Angular twin of React's useState / Vue's ref.
|
|
337
|
-
private mostRecentEventCount = INITIAL_EVENT_COUNT;
|
|
338
|
-
// The last text native holds, as far as JS knows. Seeded on the first `value` change (the `text`
|
|
339
|
-
// prop already carries the mount value down via createNode, so the FIRST controlled value is not
|
|
340
|
-
// a divergence and must NOT re-command); later divergences flow through setTextAndSelection.
|
|
341
|
-
private lastNativeText: string | undefined;
|
|
342
|
-
// JS-side focus state, mirrored from the focus/blur events for isFocused(): native exposes no
|
|
343
|
-
// synchronous focus getter (RN's TextInputState holds the same).
|
|
344
|
-
private focused = false;
|
|
345
|
-
private cancelAutoFocus?: () => void;
|
|
346
|
-
// Mirrors SimpleChange.firstChange for the writeValue() path below (@angular/forms' own initial
|
|
347
|
-
// write, distinct from ngOnChanges' firstChange, which never fires for a forms-driven write).
|
|
348
|
-
private isFirstFormWrite = true;
|
|
349
|
-
|
|
350
|
-
get isMultiline(): boolean {
|
|
351
|
-
return this.multiline === true;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
get hostProps(): Record<string, unknown> {
|
|
355
|
-
return renderTextInput({
|
|
356
|
-
multiline: this.isMultiline,
|
|
357
|
-
text: foldText(this.value, this.defaultValue),
|
|
358
|
-
mostRecentEventCount: this.mostRecentEventCount,
|
|
359
|
-
selection: this.selection,
|
|
360
|
-
folded: this.foldedNativeProps,
|
|
361
|
-
passthrough: this.buildPassthrough(),
|
|
362
|
-
}).props;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
ngOnChanges(changes: SimpleChanges): void {
|
|
366
|
-
const valueChange = changes['value'];
|
|
367
|
-
if (valueChange === undefined) return;
|
|
368
|
-
if (valueChange.firstChange) {
|
|
369
|
-
// The initial value already reached native via createNode's hostProps — seed
|
|
370
|
-
// lastNativeText instead of re-commanding it (see commitControlledValue's own comment on
|
|
371
|
-
// why a redundant re-push isn't just wasteful — it can race a keystroke).
|
|
372
|
-
this.lastNativeText = foldText(this.value, this.defaultValue);
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
this.commitControlledValue();
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ControlValueAccessor — @angular/forms calls these straight into this component, bypassing
|
|
379
|
-
// template bindings/ngOnChanges entirely. `isFirstFormWrite` mirrors SimpleChange.firstChange:
|
|
380
|
-
// the initial value already reached native via createNode's hostProps, so it must seed
|
|
381
|
-
// lastNativeText, not re-command (see commitControlledValue's own comment on why a redundant
|
|
382
|
-
// re-push isn't just wasteful — it can race a keystroke).
|
|
383
|
-
writeValue(value: string | null): void {
|
|
384
|
-
this.value = value ?? undefined;
|
|
385
|
-
if (this.isFirstFormWrite) {
|
|
386
|
-
this.isFirstFormWrite = false;
|
|
387
|
-
this.lastNativeText = foldText(this.value, this.defaultValue);
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
this.commitControlledValue();
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
registerOnChange(fn: (value: string) => void): void {
|
|
394
|
-
this.valueChange.subscribe(fn);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
registerOnTouched(fn: () => void): void {
|
|
398
|
-
this.blurEvent.subscribe(() => fn());
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
setDisabledState(isDisabled: boolean): void {
|
|
402
|
-
this.editable = !isDisabled;
|
|
403
|
-
this.changeDetector.markForCheck();
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// autoFocus is driven in JS, not as a native prop: once the node is committed, command `focus`
|
|
407
|
-
// down once (RN does the same via TextInputState.focusInput). Under Angular's async-batched
|
|
408
|
-
// commit the node has no Fabric tag at ngAfterViewInit, so whenCommitted defers the command to
|
|
409
|
-
// the commit that assigns the tag; cancelled on destroy so an un-committed pending focus leaks.
|
|
410
|
-
ngAfterViewInit(): void {
|
|
411
|
-
if (this.autoFocus !== true) return;
|
|
412
|
-
const node = this.hostNode;
|
|
413
|
-
if (node === undefined) return;
|
|
414
|
-
dlog('TextInput autoFocus -> focus command');
|
|
415
|
-
this.cancelAutoFocus = whenCommitted(node, () =>
|
|
416
|
-
dispatchViewCommand(node, 'focus', []),
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
ngOnDestroy(): void {
|
|
421
|
-
this.cancelAutoFocus?.();
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
handleChange(event: unknown): void {
|
|
425
|
-
if (!isSymbioteEvent(event)) return;
|
|
426
|
-
// Event seam: the controlled handshake hinges on the change payload carrying `text`
|
|
427
|
-
// (+ `eventCount`). iOS and Android Fabric can key these differently, so log the actual shape.
|
|
428
|
-
dlog(
|
|
429
|
-
`TextInput change keys=[${Object.keys(event.nativeEvent).join(',')}] ` +
|
|
430
|
-
`text=${JSON.stringify(event.nativeEvent.text)} count=${JSON.stringify(event.nativeEvent.eventCount)}`,
|
|
431
|
-
);
|
|
432
|
-
const text = textFromChange(event);
|
|
433
|
-
if (text !== undefined) {
|
|
434
|
-
// Record the text first, then the count, so the count never runs ahead of the text it
|
|
435
|
-
// stands for. The host re-pushes the count on the parent's value-driven CD, never here:
|
|
436
|
-
// pushing the stale `text` prop now (eventLag 0) would revert the keystroke.
|
|
437
|
-
this.lastNativeText = text;
|
|
438
|
-
this.valueChange.emit(text);
|
|
439
|
-
}
|
|
440
|
-
const count = eventCountFromChange(event);
|
|
441
|
-
if (count !== undefined) this.mostRecentEventCount = count;
|
|
442
|
-
this.change.emit(event);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
handleFocus(event: unknown): void {
|
|
446
|
-
if (!isSymbioteEvent(event)) return;
|
|
447
|
-
this.focused = true;
|
|
448
|
-
// Track focus app-wide so Keyboard.dismiss can blur this input without a ref.
|
|
449
|
-
const node = this.hostNode;
|
|
450
|
-
if (node !== undefined) setInputFocused(node);
|
|
451
|
-
this.focusEvent.emit(event);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
handleBlur(event: unknown): void {
|
|
455
|
-
if (!isSymbioteEvent(event)) return;
|
|
456
|
-
this.focused = false;
|
|
457
|
-
const node = this.hostNode;
|
|
458
|
-
if (node !== undefined) setInputBlurred(node);
|
|
459
|
-
this.blurEvent.emit(event);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Forward an engine event onto the matching @Output(), narrowing the template's untyped $event
|
|
463
|
-
// first. Angular blocks [onX] property bindings, so every native event flows through (event).
|
|
464
|
-
emit(emitter: EventEmitter<ISymbioteEvent>, event: unknown): void {
|
|
465
|
-
if (isSymbioteEvent(event)) emitter.emit(event);
|
|
466
|
-
}
|
|
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
|
-
|
|
503
|
-
// The imperative API RN exposes on a TextInput ref; in Angular the component instance IS the
|
|
504
|
-
// handle (a parent reads it via @ViewChild(TextInput) and calls these). focus/blur drive native
|
|
505
|
-
// view commands; clear and setSelection reuse setTextAndSelection (the same stale-safe path as a
|
|
506
|
-
// controlled write) echoing the acknowledged event count.
|
|
507
|
-
focus(): void {
|
|
508
|
-
const node = this.hostNode;
|
|
509
|
-
if (node !== undefined) dispatchViewCommand(node, 'focus', []);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
blur(): void {
|
|
513
|
-
// Routes through TextInputState so the app-wide focus tracking clears too.
|
|
514
|
-
blurTextInput(this.hostNode ?? null);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
clear(): void {
|
|
518
|
-
const node = this.hostNode;
|
|
519
|
-
if (node === undefined) return;
|
|
520
|
-
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
521
|
-
this.mostRecentEventCount,
|
|
522
|
-
'',
|
|
523
|
-
0,
|
|
524
|
-
0,
|
|
525
|
-
]);
|
|
526
|
-
this.lastNativeText = '';
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
isFocused(): boolean {
|
|
530
|
-
return this.focused;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
setSelection(start: number, end: number): void {
|
|
534
|
-
const node = this.hostNode;
|
|
535
|
-
if (node === undefined) return;
|
|
536
|
-
const current = this.lastNativeText ?? '';
|
|
537
|
-
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
538
|
-
this.mostRecentEventCount,
|
|
539
|
-
current,
|
|
540
|
-
start,
|
|
541
|
-
end,
|
|
542
|
-
]);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Controlled write: when JS-side `value` diverges from what native reported, command the new
|
|
546
|
-
// text down with the acknowledged count; a plain prop re-push would race the user's keystrokes.
|
|
547
|
-
// whenCommitted defers it past the async commit so the node's Fabric tag exists; the predicate
|
|
548
|
-
// narrows `value` to string and makes the call a no-op unless the text diverged.
|
|
549
|
-
private commitControlledValue(): void {
|
|
550
|
-
const node = this.hostNode;
|
|
551
|
-
if (node === undefined) return;
|
|
552
|
-
const value = this.value;
|
|
553
|
-
if (!shouldCommandText(this.lastNativeText, value)) return;
|
|
554
|
-
const selStart = this.selection?.start ?? SELECTION_NONE;
|
|
555
|
-
const selEnd =
|
|
556
|
-
this.selection?.end ?? this.selection?.start ?? SELECTION_NONE;
|
|
557
|
-
const count = this.mostRecentEventCount;
|
|
558
|
-
dlog(
|
|
559
|
-
`TextInput setTextAndSelection count=${count} text=${JSON.stringify(value)}`,
|
|
560
|
-
);
|
|
561
|
-
this.lastNativeText = value;
|
|
562
|
-
whenCommitted(node, () =>
|
|
563
|
-
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
564
|
-
count,
|
|
565
|
-
value,
|
|
566
|
-
selStart,
|
|
567
|
-
selEnd,
|
|
568
|
-
]),
|
|
569
|
-
);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
private buildPassthrough(): Record<string, unknown> {
|
|
573
|
-
return {
|
|
574
|
-
...this.foldedAccessibility,
|
|
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
|
-
),
|
|
590
|
-
testID: this.testID,
|
|
591
|
-
nativeID: this.nativeID,
|
|
592
|
-
placeholder: this.placeholder,
|
|
593
|
-
placeholderTextColor: this.placeholderTextColor,
|
|
594
|
-
maxLength: this.maxLength,
|
|
595
|
-
secureTextEntry: this.secureTextEntry,
|
|
596
|
-
autoCapitalize: this.autoCapitalize,
|
|
597
|
-
autoCorrect: this.autoCorrect,
|
|
598
|
-
selectTextOnFocus: this.selectTextOnFocus,
|
|
599
|
-
scrollEnabled: this.scrollEnabled,
|
|
600
|
-
numberOfLines: this.numberOfLines,
|
|
601
|
-
textAlign: this.textAlign,
|
|
602
|
-
inputAccessoryViewID: this.inputAccessoryViewID,
|
|
603
|
-
style: [anchorHostStyle(this.elementRef), this.style],
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
private get foldedNativeProps(): IFoldedTextInputProps {
|
|
608
|
-
return resolveTextInputProps({
|
|
609
|
-
inputMode: this.inputMode,
|
|
610
|
-
keyboardType: this.keyboardType,
|
|
611
|
-
enterKeyHint: this.enterKeyHint,
|
|
612
|
-
returnKeyType: this.returnKeyType,
|
|
613
|
-
readOnly: this.readOnly,
|
|
614
|
-
editable: this.editable,
|
|
615
|
-
submitBehavior: this.submitBehavior,
|
|
616
|
-
blurOnSubmit: this.blurOnSubmit,
|
|
617
|
-
multiline: this.isMultiline,
|
|
618
|
-
cursorColor: this.cursorColor,
|
|
619
|
-
selectionColor: this.selectionColor,
|
|
620
|
-
selectionHandleColor: this.selectionHandleColor,
|
|
621
|
-
autoComplete: this.autoComplete,
|
|
622
|
-
textContentType: this.textContentType,
|
|
623
|
-
showSoftInputOnFocus: this.showSoftInputOnFocus,
|
|
624
|
-
underlineColorAndroid: this.underlineColorAndroid,
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Fold the web aria-*/role aliases into the canonical accessibility* props once per render, so
|
|
629
|
-
// the host node never sees an aria-* key (native ignores them) — the shared transform every
|
|
630
|
-
// adapter runs. TextInput is its own host element (not a View wrapper), so it folds a11y here.
|
|
631
|
-
private get foldedAccessibility(): Partial<IAngularTextInputProps> {
|
|
632
|
-
return resolveAccessibilityProps({
|
|
633
|
-
accessibilityLabel: this.accessibilityLabel,
|
|
634
|
-
accessibilityHint: this.accessibilityHint,
|
|
635
|
-
accessibilityRole: this.accessibilityRole,
|
|
636
|
-
accessibilityState: this.accessibilityState,
|
|
637
|
-
accessibilityValue: this.accessibilityValue,
|
|
638
|
-
accessibilityActions: this.accessibilityActions,
|
|
639
|
-
accessibilityLabelledBy: this.accessibilityLabelledBy,
|
|
640
|
-
importantForAccessibility: this.importantForAccessibility,
|
|
641
|
-
accessibilityLiveRegion: this.accessibilityLiveRegion,
|
|
642
|
-
screenReaderFocusable: this.screenReaderFocusable,
|
|
643
|
-
accessibilityViewIsModal: this.accessibilityViewIsModal,
|
|
644
|
-
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
645
|
-
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
646
|
-
accessibilityLanguage: this.accessibilityLanguage,
|
|
647
|
-
accessibilityRespondsToUserInteraction:
|
|
648
|
-
this.accessibilityRespondsToUserInteraction,
|
|
649
|
-
accessibilityShowsLargeContentViewer:
|
|
650
|
-
this.accessibilityShowsLargeContentViewer,
|
|
651
|
-
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
652
|
-
role: this.role,
|
|
653
|
-
'aria-label': this.ariaLabel,
|
|
654
|
-
'aria-labelledby': this.ariaLabelledBy,
|
|
655
|
-
'aria-live': this.ariaLive,
|
|
656
|
-
'aria-hidden': this.ariaHidden,
|
|
657
|
-
'aria-busy': this.ariaBusy,
|
|
658
|
-
'aria-checked': this.ariaChecked,
|
|
659
|
-
'aria-disabled': this.ariaDisabled,
|
|
660
|
-
'aria-expanded': this.ariaExpanded,
|
|
661
|
-
'aria-selected': this.ariaSelected,
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
private get hostNode(): ISymbioteNode | undefined {
|
|
666
|
-
const native = this.hostRef?.node;
|
|
667
|
-
return isSymbioteNode(native) ? native : undefined;
|
|
668
|
-
}
|
|
669
|
-
}
|