@symbiote-native/angular 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/symbiote-angular-dev.cjs +20 -19
- package/build/angular/anchor-host-registry.js +2 -2
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/bootstrap.js +1 -1
- package/build/angular/bootstrap.js.map +1 -1
- package/build/angular/components/activity-indicator/index.android.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.android.js +12 -4
- package/build/angular/components/activity-indicator/index.android.js.map +1 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.ios.js +8 -3
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -1
- package/build/angular/components/activity-indicator/shared.d.ts +1 -1
- package/build/angular/components/activity-indicator/shared.js +4 -4
- package/build/angular/components/activity-indicator/shared.js.map +1 -1
- package/build/angular/components/button.d.ts +4 -2
- package/build/angular/components/button.js +41 -22
- package/build/angular/components/button.js.map +1 -1
- package/build/angular/components/flat-list/index.d.ts +6 -4
- package/build/angular/components/flat-list/index.js +80 -50
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image/index.android.d.ts +8 -3
- package/build/angular/components/image/index.android.js +42 -10
- package/build/angular/components/image/index.android.js.map +1 -1
- package/build/angular/components/image/index.ios.d.ts +8 -3
- package/build/angular/components/image/index.ios.js +42 -10
- package/build/angular/components/image/index.ios.js.map +1 -1
- package/build/angular/components/image/shared.d.ts +4 -1
- package/build/angular/components/image/shared.js +51 -8
- package/build/angular/components/image/shared.js.map +1 -1
- package/build/angular/components/image-background.d.ts +2 -1
- package/build/angular/components/image-background.js +26 -10
- package/build/angular/components/image-background.js.map +1 -1
- package/build/angular/components/input-accessory-view/index.d.ts +9 -4
- package/build/angular/components/input-accessory-view/index.js +49 -10
- package/build/angular/components/input-accessory-view/index.js.map +1 -1
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +3 -1
- package/build/angular/components/keyboard-avoiding-view/index.js +44 -12
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +11 -8
- package/build/angular/components/modal/index.js +51 -29
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable/index.d.ts +12 -6
- package/build/angular/components/pressable/index.js +66 -13
- package/build/angular/components/pressable/index.js.map +1 -1
- package/build/angular/components/refresh-control/index.d.ts +8 -4
- package/build/angular/components/refresh-control/index.js +54 -16
- package/build/angular/components/refresh-control/index.js.map +1 -1
- package/build/angular/components/safe-area-view/index.d.ts +9 -4
- package/build/angular/components/safe-area-view/index.js +48 -9
- package/build/angular/components/safe-area-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/index.android.d.ts +2 -3
- package/build/angular/components/scroll-view/index.android.js +88 -61
- package/build/angular/components/scroll-view/index.android.js.map +1 -1
- package/build/angular/components/scroll-view/index.ios.d.ts +2 -3
- package/build/angular/components/scroll-view/index.ios.js +24 -18
- package/build/angular/components/scroll-view/index.ios.js.map +1 -1
- package/build/angular/components/scroll-view/projection.d.ts +35 -0
- package/build/angular/components/scroll-view/projection.js +279 -53
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.d.ts +14 -5
- package/build/angular/components/scroll-view/shared.js +225 -75
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.js +21 -22
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/section-list/index.d.ts +7 -1
- package/build/angular/components/section-list/index.js +91 -54
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/switch/index.android.d.ts +7 -3
- package/build/angular/components/switch/index.android.js +52 -10
- package/build/angular/components/switch/index.android.js.map +1 -1
- package/build/angular/components/switch/index.ios.d.ts +7 -3
- package/build/angular/components/switch/index.ios.js +52 -10
- package/build/angular/components/switch/index.ios.js.map +1 -1
- package/build/angular/components/switch/shared.d.ts +13 -4
- package/build/angular/components/switch/shared.js +71 -7
- package/build/angular/components/switch/shared.js.map +1 -1
- package/build/angular/components/text-input.d.ts +11 -3
- package/build/angular/components/text-input.js +68 -8
- package/build/angular/components/text-input.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +40 -9
- package/build/angular/components/touchable/index.js +303 -67
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +6 -3
- package/build/angular/components/touchable-native-feedback/index.js +63 -27
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
- package/build/angular/components/virtualized-list/directives.js +22 -18
- package/build/angular/components/virtualized-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +15 -4
- package/build/angular/components/virtualized-list/index.js +326 -94
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/directives.js +12 -12
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +19 -1
- package/build/angular/components/virtualized-section-list/index.js +93 -22
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components.d.ts +2 -2
- package/build/angular/components.js +5 -0
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.js +34 -36
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js +19 -22
- package/build/angular/create-tunnel/index.js.map +1 -1
- package/build/angular/descriptor-to-angular/index.js +6 -4
- package/build/angular/descriptor-to-angular/index.js.map +1 -1
- package/build/angular/diagnostics.d.ts +33 -0
- package/build/angular/diagnostics.js +82 -0
- package/build/angular/diagnostics.js.map +1 -0
- package/build/angular/host-instance/index.js +1 -1
- package/build/angular/host-instance/index.js.map +1 -1
- package/build/angular/index.d.ts +9 -4
- package/build/angular/index.js +17 -3
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +1 -7
- package/build/angular/modules/animated/animated-leaf-binder.js +22 -72
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +11 -6
- package/build/angular/modules/animated/create-animated-component.js +106 -55
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/status-bar/index.js +4 -4
- package/build/angular/modules/status-bar/index.js.map +1 -1
- package/build/angular/primitives/index.d.ts +20 -3
- package/build/angular/primitives/index.js +84 -48
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +48 -2
- package/build/angular/primitives/shared.js +158 -11
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/render/index.js +69 -38
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.d.ts +1 -0
- package/build/angular/renderer/index.js +37 -2
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/services/color-scheme.service.js +3 -3
- package/build/angular/services/color-scheme.service.js.map +1 -1
- package/build/angular/services/window-dimensions.service.js +4 -4
- package/build/angular/services/window-dimensions.service.js.map +1 -1
- package/package.json +10 -8
- package/src/anchor-host-registry.ts +2 -2
- package/src/bootstrap.ts +4 -1
- package/src/components/activity-indicator/index.android.ts +8 -1
- package/src/components/activity-indicator/index.ios.ts +4 -0
- package/src/components/activity-indicator/shared.ts +32 -13
- package/src/components/button.ts +46 -19
- package/src/components/flat-list/index.ts +122 -62
- package/src/components/image/index.android.ts +58 -7
- package/src/components/image/index.ios.ts +58 -7
- package/src/components/image/shared.ts +92 -21
- package/src/components/image-background.ts +29 -9
- package/src/components/input-accessory-view/index.ts +67 -12
- package/src/components/keyboard-avoiding-view/index.ts +61 -15
- package/src/components/modal/index.ts +72 -33
- package/src/components/pressable/index.ts +117 -28
- package/src/components/refresh-control/index.ts +74 -18
- package/src/components/safe-area-view/index.ts +64 -10
- package/src/components/scroll-view/index.android.ts +62 -47
- package/src/components/scroll-view/index.ios.ts +19 -17
- package/src/components/scroll-view/projection.ts +363 -64
- package/src/components/scroll-view/shared.ts +312 -90
- package/src/components/scroll-view/sticky-header.ts +36 -24
- package/src/components/section-list/index.ts +86 -53
- package/src/components/switch/index.android.ts +60 -8
- package/src/components/switch/index.ios.ts +60 -8
- package/src/components/switch/shared.ts +93 -6
- package/src/components/text-input.ts +95 -13
- package/src/components/touchable/index.ts +348 -64
- package/src/components/touchable-native-feedback/index.ts +94 -31
- package/src/components/virtualized-list/directives.ts +19 -5
- package/src/components/virtualized-list/index.ts +380 -85
- package/src/components/virtualized-section-list/directives.ts +6 -3
- package/src/components/virtualized-section-list/index.ts +146 -33
- package/src/components.ts +9 -1
- package/src/create-portal/index.ts +31 -31
- package/src/create-tunnel/index.ts +16 -17
- package/src/descriptor-to-angular/index.ts +29 -8
- package/src/diagnostics.ts +138 -0
- package/src/host-instance/index.ts +9 -3
- package/src/index.ts +61 -3
- package/src/modules/animated/animated-leaf-binder.ts +28 -77
- package/src/modules/animated/create-animated-component.ts +115 -47
- package/src/modules/app-registry/index.ts +9 -9
- package/src/modules/status-bar/index.ts +13 -4
- package/src/primitives/index.ts +47 -2
- package/src/primitives/shared.ts +170 -10
- package/src/render/index.ts +77 -39
- package/src/renderer/index.ts +76 -14
- package/src/services/color-scheme.service.ts +3 -1
- package/src/services/window-dimensions.service.ts +11 -4
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +0 -14
- package/build/angular/modules/animated/animated-leaf-binder/index.js +0 -91
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +0 -1
- package/src/modules/animated/animated-leaf-binder/index.ts +0 -101
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectorRef,
|
|
3
|
+
Directive,
|
|
4
|
+
EventEmitter,
|
|
5
|
+
Output,
|
|
6
|
+
computed,
|
|
7
|
+
inject,
|
|
8
|
+
signal,
|
|
9
|
+
type OnChanges,
|
|
10
|
+
} from '@angular/core';
|
|
11
|
+
import type { ControlValueAccessor } from '@angular/forms';
|
|
12
|
+
import {
|
|
13
|
+
renderSwitch,
|
|
14
|
+
resolveAccessibilityProps,
|
|
15
|
+
} from '@symbiote-native/components';
|
|
3
16
|
import type {
|
|
4
17
|
ISwitchPlatform,
|
|
5
18
|
ISwitchProps,
|
|
@@ -23,7 +36,10 @@ import {
|
|
|
23
36
|
type IViewStyle,
|
|
24
37
|
} from '@symbiote-native/engine';
|
|
25
38
|
|
|
26
|
-
export type {
|
|
39
|
+
export type {
|
|
40
|
+
ISwitchProps,
|
|
41
|
+
ISwitchTrackColor,
|
|
42
|
+
} from '@symbiote-native/components';
|
|
27
43
|
|
|
28
44
|
export type ISwitchInputs = Pick<
|
|
29
45
|
ISwitchProps,
|
|
@@ -78,7 +94,9 @@ function isSwitchEvent(event: unknown): event is ISymbioteEvent {
|
|
|
78
94
|
// concrete subclasses are real @Components — ngc's AOT build (NG2007) requires it the moment the
|
|
79
95
|
// base itself carries decorated members (@Output() here), mirroring ScrollViewBase.
|
|
80
96
|
@Directive()
|
|
81
|
-
export abstract class SwitchBase
|
|
97
|
+
export abstract class SwitchBase
|
|
98
|
+
implements ISwitchInputs, ControlValueAccessor, OnChanges
|
|
99
|
+
{
|
|
82
100
|
// The value/change lifecycle as real Angular events: `(valueChange)="onToggle($event)"`, not
|
|
83
101
|
// `[onValueChange]="onToggle"` — mirrors Pressable's press/hover @Output() conversion. Unlike
|
|
84
102
|
// Pressable's createPressHandlers, handleChange() below calls `.emit()` unconditionally: it needs
|
|
@@ -105,9 +123,46 @@ export abstract class SwitchBase implements ISwitchInputs {
|
|
|
105
123
|
'aria-checked': boolean | 'mixed' | undefined;
|
|
106
124
|
|
|
107
125
|
protected abstract readonly platform: ISwitchHostPlatform;
|
|
126
|
+
// NOT a hostProps dependency - only snapBackIfNeeded reads it, and the bag's `value` comes from
|
|
127
|
+
// the @Input, not from this reducer state. So it stays a plain field; if a future change makes
|
|
128
|
+
// the bag read it, it must become a signal first (see inputsRevision's note).
|
|
108
129
|
private switchState: ISwitchState = createInitialSwitchState();
|
|
130
|
+
// Only setDisabledState()/writeValue() need this: @angular/forms mutates `value`/`disabled` from
|
|
131
|
+
// OUTSIDE Angular's own binding path, which doesn't itself schedule a tick under zoneless CD.
|
|
132
|
+
private readonly changeDetector = inject(ChangeDetectorRef);
|
|
133
|
+
|
|
134
|
+
// Bridges the non-reactive @Input fields into the reactive graph so hostProps below can be a
|
|
135
|
+
// memoized computed(). Signal inputs would make this unnecessary, but `input()` is visible only
|
|
136
|
+
// to the AOT compiler and this package's unit suite runs on JIT - see the
|
|
137
|
+
// `angular-adapter-change-detection` skill, §6. `signal()`/`computed()` are plain runtime APIs
|
|
138
|
+
// and need no compiler support, which is what makes this bridge possible at all.
|
|
139
|
+
//
|
|
140
|
+
// Bumped from ngOnChanges (covers every @Input) AND from the two ControlValueAccessor writers
|
|
141
|
+
// below, which assign `value`/`disabled` from OUTSIDE Angular's binding path and so never reach
|
|
142
|
+
// ngOnChanges. ONLY a genuine dependency of the bag may bump it - widening it to "bump on
|
|
143
|
+
// everything" silently un-memoizes the bag and defeats the point. State that is not an @Input
|
|
144
|
+
// and is not one of those two writers needs its OWN signal instead (the platform components'
|
|
145
|
+
// anchor style is exactly that case).
|
|
146
|
+
private readonly inputsRevision = signal(0);
|
|
147
|
+
|
|
148
|
+
ngOnChanges(): void {
|
|
149
|
+
this.inputsRevision.update(revision => revision + 1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// MEASURED 2026-08-16: as a getter this rebuilt the bag on every refresh of this view, so
|
|
153
|
+
// `[symbioteHostProps]`'s reference check failed every time and every key was re-pushed through
|
|
154
|
+
// Renderer2 -> routeProp - pure waste when nothing changed. computed() returns the SAME object
|
|
155
|
+
// until a tracked dependency actually changes, and the input setter then skips the whole spread.
|
|
156
|
+
//
|
|
157
|
+
// A computed FIELD cannot be overridden by a subclass accessor the way a getter can, so the
|
|
158
|
+
// platform components override buildHostProps() below and this stays the single memoization
|
|
159
|
+
// point for every platform.
|
|
160
|
+
readonly hostProps = computed<IHostProps>(() => {
|
|
161
|
+
this.inputsRevision();
|
|
162
|
+
return this.buildHostProps();
|
|
163
|
+
});
|
|
109
164
|
|
|
110
|
-
|
|
165
|
+
protected buildHostProps(): IHostProps {
|
|
111
166
|
const props = resolveAccessibilityProps(this.inputProps());
|
|
112
167
|
const {
|
|
113
168
|
value,
|
|
@@ -144,7 +199,10 @@ export abstract class SwitchBase implements ISwitchInputs {
|
|
|
144
199
|
if (next === undefined) return;
|
|
145
200
|
|
|
146
201
|
this.valueChange.emit(next);
|
|
147
|
-
this.switchState = switchReducer(this.switchState, {
|
|
202
|
+
this.switchState = switchReducer(this.switchState, {
|
|
203
|
+
type: 'native-reported',
|
|
204
|
+
value: next,
|
|
205
|
+
});
|
|
148
206
|
|
|
149
207
|
const hostNode = readSwitchNode(node);
|
|
150
208
|
if (hostNode === null) return;
|
|
@@ -168,6 +226,35 @@ export abstract class SwitchBase implements ISwitchInputs {
|
|
|
168
226
|
});
|
|
169
227
|
}
|
|
170
228
|
|
|
229
|
+
// ControlValueAccessor — `value` is a plain field feeding `hostProps` on every CD check, no
|
|
230
|
+
// ngOnChanges/stale-safe commit dance to guard (unlike TextInput, a boolean toggle has no
|
|
231
|
+
// keystroke to race), so writeValue()/setDisabledState() can set it straight.
|
|
232
|
+
writeValue(value: boolean | null): void {
|
|
233
|
+
this.value = value ?? false;
|
|
234
|
+
// `value` feeds hostProps but this write bypasses Angular's binding path, so ngOnChanges never
|
|
235
|
+
// fires for it - without the bump the memoized bag would keep reporting the old value and the
|
|
236
|
+
// switch would stop reflecting a form-driven change.
|
|
237
|
+
this.inputsRevision.update(revision => revision + 1);
|
|
238
|
+
this.changeDetector.markForCheck();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
registerOnChange(fn: (value: boolean) => void): void {
|
|
242
|
+
this.valueChange.subscribe(fn);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
registerOnTouched(fn: () => void): void {
|
|
246
|
+
// Switch has no blur concept (it's a discrete toggle, not a text field) — `change` is the
|
|
247
|
+
// closest touched signal, same convention Angular's own checkbox CVAs use.
|
|
248
|
+
this.change.subscribe(() => fn());
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
setDisabledState(isDisabled: boolean): void {
|
|
252
|
+
this.disabled = isDisabled;
|
|
253
|
+
// Same reason as writeValue: an @angular/forms write that never reaches ngOnChanges.
|
|
254
|
+
this.inputsRevision.update(revision => revision + 1);
|
|
255
|
+
this.changeDetector.markForCheck();
|
|
256
|
+
}
|
|
257
|
+
|
|
171
258
|
private inputProps(): Partial<ISwitchProps> {
|
|
172
259
|
const props: Partial<ISwitchProps> = {
|
|
173
260
|
value: this.value,
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
import {
|
|
29
29
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
30
30
|
ChangeDetectionStrategy,
|
|
31
|
+
ChangeDetectorRef,
|
|
31
32
|
Component,
|
|
32
33
|
ElementRef,
|
|
33
34
|
EventEmitter,
|
|
35
|
+
forwardRef,
|
|
34
36
|
inject,
|
|
35
37
|
Input,
|
|
36
38
|
Output,
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
type OnDestroy,
|
|
41
43
|
type SimpleChanges,
|
|
42
44
|
} from '@angular/core';
|
|
45
|
+
import { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms';
|
|
43
46
|
import {
|
|
44
47
|
foldText,
|
|
45
48
|
eventCountFromChange,
|
|
@@ -79,15 +82,23 @@ import {
|
|
|
79
82
|
anchorHostStyle,
|
|
80
83
|
MultilineTextInputHost,
|
|
81
84
|
SymbioteHostPropsDirective,
|
|
85
|
+
SymbioteStyleInputDirective,
|
|
82
86
|
TextInputHost,
|
|
83
87
|
} from '../primitives';
|
|
84
88
|
|
|
85
|
-
export type {
|
|
89
|
+
export type {
|
|
90
|
+
ITextInputHandle,
|
|
91
|
+
ITextInputSelection,
|
|
92
|
+
IInputMode,
|
|
93
|
+
IEnterKeyHint,
|
|
94
|
+
ISubmitBehavior,
|
|
95
|
+
};
|
|
86
96
|
|
|
87
97
|
// Mirrors React's ITextInputProps minus nothing (TextInput has no children) — declared
|
|
88
98
|
// per-adapter over the shared accessibility base because Angular's input surface aliases the
|
|
89
99
|
// aria-* keys to camelCase @Inputs, unlike the plain agnostic fields shared across adapters.
|
|
90
|
-
export interface IAngularTextInputProps
|
|
100
|
+
export interface IAngularTextInputProps
|
|
101
|
+
extends IAccessibilityProps, IAriaProps {
|
|
91
102
|
value?: string;
|
|
92
103
|
defaultValue?: string;
|
|
93
104
|
placeholder?: string;
|
|
@@ -154,9 +165,19 @@ export type IAngularTextInputInputs = Omit<
|
|
|
154
165
|
@Component({
|
|
155
166
|
selector: 'TextInput',
|
|
156
167
|
standalone: true,
|
|
168
|
+
hostDirectives: [
|
|
169
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
170
|
+
],
|
|
157
171
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
158
172
|
imports: [SymbioteHostPropsDirective, TextInputHost, MultilineTextInputHost],
|
|
159
173
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
174
|
+
providers: [
|
|
175
|
+
{
|
|
176
|
+
provide: NG_VALUE_ACCESSOR,
|
|
177
|
+
useExisting: forwardRef(() => TextInput),
|
|
178
|
+
multi: true,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
160
181
|
template: `
|
|
161
182
|
@if (isMultiline) {
|
|
162
183
|
<symbiote-text-input-multiline
|
|
@@ -196,7 +217,13 @@ export type IAngularTextInputInputs = Omit<
|
|
|
196
217
|
`,
|
|
197
218
|
})
|
|
198
219
|
export class TextInput
|
|
199
|
-
implements
|
|
220
|
+
implements
|
|
221
|
+
IAngularTextInputInputs,
|
|
222
|
+
ITextInputHandle,
|
|
223
|
+
OnChanges,
|
|
224
|
+
AfterViewInit,
|
|
225
|
+
OnDestroy,
|
|
226
|
+
ControlValueAccessor
|
|
200
227
|
{
|
|
201
228
|
@Input() value?: string;
|
|
202
229
|
@Input() defaultValue?: string;
|
|
@@ -263,8 +290,10 @@ export class TextInput
|
|
|
263
290
|
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
264
291
|
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
265
292
|
@Input() accessibilityLabelledBy?: string | string[];
|
|
266
|
-
@Input()
|
|
267
|
-
|
|
293
|
+
@Input()
|
|
294
|
+
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
295
|
+
@Input()
|
|
296
|
+
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
268
297
|
@Input() screenReaderFocusable?: boolean;
|
|
269
298
|
@Input() accessibilityViewIsModal?: boolean;
|
|
270
299
|
@Input() accessibilityElementsHidden?: boolean;
|
|
@@ -291,6 +320,9 @@ export class TextInput
|
|
|
291
320
|
// onto (see anchorHostStyle's doc comment) — NOT `hostRef` above, which targets the real inner
|
|
292
321
|
// symbiote-text-input(-multiline) one level down.
|
|
293
322
|
private readonly elementRef = inject(ElementRef);
|
|
323
|
+
// Only setDisabledState() needs this: it mutates `editable` from OUTSIDE Angular's own binding
|
|
324
|
+
// path (@angular/forms calling in directly), which doesn't itself schedule a tick under zoneless CD.
|
|
325
|
+
private readonly changeDetector = inject(ChangeDetectorRef);
|
|
294
326
|
|
|
295
327
|
// The count native last acknowledged; the controlled write / clear / setSelection echo it so
|
|
296
328
|
// native's eventLag lands on 0 and the write applies. A plain field (read live by the imperative
|
|
@@ -304,6 +336,9 @@ export class TextInput
|
|
|
304
336
|
// synchronous focus getter (RN's TextInputState holds the same).
|
|
305
337
|
private focused = false;
|
|
306
338
|
private cancelAutoFocus?: () => void;
|
|
339
|
+
// Mirrors SimpleChange.firstChange for the writeValue() path below (@angular/forms' own initial
|
|
340
|
+
// write, distinct from ngOnChanges' firstChange, which never fires for a forms-driven write).
|
|
341
|
+
private isFirstFormWrite = true;
|
|
307
342
|
|
|
308
343
|
get isMultiline(): boolean {
|
|
309
344
|
return this.multiline === true;
|
|
@@ -323,14 +358,44 @@ export class TextInput
|
|
|
323
358
|
ngOnChanges(changes: SimpleChanges): void {
|
|
324
359
|
const valueChange = changes['value'];
|
|
325
360
|
if (valueChange === undefined) return;
|
|
326
|
-
// The first value is the seed, not a divergence: record it and do not command.
|
|
327
361
|
if (valueChange.firstChange) {
|
|
362
|
+
// The initial value already reached native via createNode's hostProps — seed
|
|
363
|
+
// lastNativeText instead of re-commanding it (see commitControlledValue's own comment on
|
|
364
|
+
// why a redundant re-push isn't just wasteful — it can race a keystroke).
|
|
328
365
|
this.lastNativeText = foldText(this.value, this.defaultValue);
|
|
329
366
|
return;
|
|
330
367
|
}
|
|
331
368
|
this.commitControlledValue();
|
|
332
369
|
}
|
|
333
370
|
|
|
371
|
+
// ControlValueAccessor — @angular/forms calls these straight into this component, bypassing
|
|
372
|
+
// template bindings/ngOnChanges entirely. `isFirstFormWrite` mirrors SimpleChange.firstChange:
|
|
373
|
+
// the initial value already reached native via createNode's hostProps, so it must seed
|
|
374
|
+
// lastNativeText, not re-command (see commitControlledValue's own comment on why a redundant
|
|
375
|
+
// re-push isn't just wasteful — it can race a keystroke).
|
|
376
|
+
writeValue(value: string | null): void {
|
|
377
|
+
this.value = value ?? undefined;
|
|
378
|
+
if (this.isFirstFormWrite) {
|
|
379
|
+
this.isFirstFormWrite = false;
|
|
380
|
+
this.lastNativeText = foldText(this.value, this.defaultValue);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
this.commitControlledValue();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
registerOnChange(fn: (value: string) => void): void {
|
|
387
|
+
this.valueChange.subscribe(fn);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
registerOnTouched(fn: () => void): void {
|
|
391
|
+
this.blurEvent.subscribe(() => fn());
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
setDisabledState(isDisabled: boolean): void {
|
|
395
|
+
this.editable = !isDisabled;
|
|
396
|
+
this.changeDetector.markForCheck();
|
|
397
|
+
}
|
|
398
|
+
|
|
334
399
|
// autoFocus is driven in JS, not as a native prop: once the node is committed, command `focus`
|
|
335
400
|
// down once (RN does the same via TextInputState.focusInput). Under Angular's async-batched
|
|
336
401
|
// commit the node has no Fabric tag at ngAfterViewInit, so whenCommitted defers the command to
|
|
@@ -340,7 +405,9 @@ export class TextInput
|
|
|
340
405
|
const node = this.hostNode;
|
|
341
406
|
if (node === undefined) return;
|
|
342
407
|
dlog('TextInput autoFocus -> focus command');
|
|
343
|
-
this.cancelAutoFocus = whenCommitted(node, () =>
|
|
408
|
+
this.cancelAutoFocus = whenCommitted(node, () =>
|
|
409
|
+
dispatchViewCommand(node, 'focus', []),
|
|
410
|
+
);
|
|
344
411
|
}
|
|
345
412
|
|
|
346
413
|
ngOnDestroy(): void {
|
|
@@ -408,7 +475,12 @@ export class TextInput
|
|
|
408
475
|
clear(): void {
|
|
409
476
|
const node = this.hostNode;
|
|
410
477
|
if (node === undefined) return;
|
|
411
|
-
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
478
|
+
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
479
|
+
this.mostRecentEventCount,
|
|
480
|
+
'',
|
|
481
|
+
0,
|
|
482
|
+
0,
|
|
483
|
+
]);
|
|
412
484
|
this.lastNativeText = '';
|
|
413
485
|
}
|
|
414
486
|
|
|
@@ -438,12 +510,20 @@ export class TextInput
|
|
|
438
510
|
const value = this.value;
|
|
439
511
|
if (!shouldCommandText(this.lastNativeText, value)) return;
|
|
440
512
|
const selStart = this.selection?.start ?? SELECTION_NONE;
|
|
441
|
-
const selEnd =
|
|
513
|
+
const selEnd =
|
|
514
|
+
this.selection?.end ?? this.selection?.start ?? SELECTION_NONE;
|
|
442
515
|
const count = this.mostRecentEventCount;
|
|
443
|
-
dlog(
|
|
516
|
+
dlog(
|
|
517
|
+
`TextInput setTextAndSelection count=${count} text=${JSON.stringify(value)}`,
|
|
518
|
+
);
|
|
444
519
|
this.lastNativeText = value;
|
|
445
520
|
whenCommitted(node, () =>
|
|
446
|
-
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
521
|
+
dispatchViewCommand(node, 'setTextAndSelection', [
|
|
522
|
+
count,
|
|
523
|
+
value,
|
|
524
|
+
selStart,
|
|
525
|
+
selEnd,
|
|
526
|
+
]),
|
|
447
527
|
);
|
|
448
528
|
}
|
|
449
529
|
|
|
@@ -508,8 +588,10 @@ export class TextInput
|
|
|
508
588
|
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
509
589
|
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
510
590
|
accessibilityLanguage: this.accessibilityLanguage,
|
|
511
|
-
accessibilityRespondsToUserInteraction:
|
|
512
|
-
|
|
591
|
+
accessibilityRespondsToUserInteraction:
|
|
592
|
+
this.accessibilityRespondsToUserInteraction,
|
|
593
|
+
accessibilityShowsLargeContentViewer:
|
|
594
|
+
this.accessibilityShowsLargeContentViewer,
|
|
513
595
|
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
514
596
|
role: this.role,
|
|
515
597
|
'aria-label': this.ariaLabel,
|