@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,349 +0,0 @@
|
|
|
1
|
-
// RefreshControl, the Angular lifecycle half. On iOS this is the PullToRefreshView Fabric node that
|
|
2
|
-
// lives INSIDE a ScrollView (a childless sibling before the content container); on Android it is
|
|
3
|
-
// AndroidSwipeRefreshLayout and WRAPS the scroll view, receiving it through <ng-content>. There is
|
|
4
|
-
// no JS-side platform renaming and no shared render fn — every prop forwards straight to the
|
|
5
|
-
// native node, which reads what it understands and ignores the rest, so Android-only and iOS-only
|
|
6
|
-
// prop families ride down harmlessly on both. This folds aria/role through the shared
|
|
7
|
-
// resolveAccessibilityProps and maps the native props + a11y + onRefresh onto the
|
|
8
|
-
// symbiote-refresh-control host, children via <ng-content>. One composed component covers both
|
|
9
|
-
// platforms, so this stays a flat single file.
|
|
10
|
-
//
|
|
11
|
-
// `refreshing` is a controlled prop: the parent owns it and pushes it down each commit; native
|
|
12
|
-
// reports the gesture via the direct `topRefresh` event, which the engine routes to the host's
|
|
13
|
-
// `refresh` listener (Angular blocks [onX] property bindings; events flow through (event) only).
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
CUSTOM_ELEMENTS_SCHEMA,
|
|
17
|
-
ChangeDetectionStrategy,
|
|
18
|
-
Component,
|
|
19
|
-
computed,
|
|
20
|
-
ElementRef,
|
|
21
|
-
EventEmitter,
|
|
22
|
-
inject,
|
|
23
|
-
Input,
|
|
24
|
-
Output,
|
|
25
|
-
signal,
|
|
26
|
-
ViewChild,
|
|
27
|
-
type AfterViewInit,
|
|
28
|
-
type DoCheck,
|
|
29
|
-
type OnChanges,
|
|
30
|
-
type OnInit,
|
|
31
|
-
type SimpleChanges,
|
|
32
|
-
} from '@angular/core';
|
|
33
|
-
import {
|
|
34
|
-
resolveAccessibilityProps,
|
|
35
|
-
type IAccessibilityProps,
|
|
36
|
-
type IAccessibilityStateValue,
|
|
37
|
-
type IAriaProps,
|
|
38
|
-
} from '@symbiote-native/components';
|
|
39
|
-
import {
|
|
40
|
-
dispatchViewCommand,
|
|
41
|
-
dlog,
|
|
42
|
-
isSymbioteEvent,
|
|
43
|
-
isSymbioteNode,
|
|
44
|
-
type IStyleProp,
|
|
45
|
-
type ISymbioteEvent,
|
|
46
|
-
type IViewStyle,
|
|
47
|
-
} from '@symbiote-native/engine';
|
|
48
|
-
import {
|
|
49
|
-
anchorHostStyle,
|
|
50
|
-
RefreshControlHost,
|
|
51
|
-
SymbioteHostPropsDirective,
|
|
52
|
-
SymbioteStyleInputDirective,
|
|
53
|
-
} from '../../primitives';
|
|
54
|
-
|
|
55
|
-
// Record<string, unknown> already tolerates `style` holding the [anchorHostStyle, this.style]
|
|
56
|
-
// array anchorHostStyle's merge produces (see hostProps below) — no widening needed.
|
|
57
|
-
type IHostProps = Record<string, unknown>;
|
|
58
|
-
|
|
59
|
-
// Mirrors React's IRefreshControlProps minus children (Angular takes the Android-wrapped scroll view
|
|
60
|
-
// via <ng-content>), declared per-adapter over the shared accessibility base since the framework-specific
|
|
61
|
-
// children slot keeps it from being fully shared across adapters. Read the React reference for the
|
|
62
|
-
// native prop names.
|
|
63
|
-
export interface IAngularRefreshControlProps
|
|
64
|
-
extends IAccessibilityProps, IAriaProps {
|
|
65
|
-
refreshing: boolean;
|
|
66
|
-
// RN's onRefresh is `() => void | Promise<void>`, the handler may be async; the promise is
|
|
67
|
-
// fire-and-forget (native already starts refreshing on the gesture).
|
|
68
|
-
onRefresh?: () => void | Promise<void>;
|
|
69
|
-
tintColor?: string;
|
|
70
|
-
title?: string;
|
|
71
|
-
titleColor?: string;
|
|
72
|
-
progressViewOffset?: number;
|
|
73
|
-
// Android-only spinner styling (RN RefreshControlPropsAndroid): `colors` are the indicator's
|
|
74
|
-
// animated stroke colors, `progressBackgroundColor` the disc behind it, `size` the diameter preset.
|
|
75
|
-
// AndroidSwipeRefreshLayout reads them; PullToRefreshView on iOS ignores unknown props.
|
|
76
|
-
colors?: readonly string[];
|
|
77
|
-
progressBackgroundColor?: string;
|
|
78
|
-
size?: 'default' | 'large';
|
|
79
|
-
// Android-only native prop forwarded to AndroidSwipeRefreshLayout; iOS native never reads it.
|
|
80
|
-
enabled?: boolean;
|
|
81
|
-
// The Android scroll-view wrap injects the layout half of the style onto this host; iOS leaves it
|
|
82
|
-
// unset (the RefreshControl is a childless sibling). Harmless to forward on both.
|
|
83
|
-
style?: IStyleProp<IViewStyle>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// What the RefreshControl component itself takes as plain @Input()s: the full surface minus the
|
|
87
|
-
// refresh/accessibility events, which it exposes as real @Output() EventEmitters instead (mirrors
|
|
88
|
-
// Pressable's IAngularPressableInputs in pressable/index.ts).
|
|
89
|
-
export type IAngularRefreshControlInputs = Omit<
|
|
90
|
-
IAngularRefreshControlProps,
|
|
91
|
-
| 'onRefresh'
|
|
92
|
-
| 'onAccessibilityAction'
|
|
93
|
-
| 'onAccessibilityTap'
|
|
94
|
-
| 'onMagicTap'
|
|
95
|
-
| 'onAccessibilityEscape'
|
|
96
|
-
>;
|
|
97
|
-
|
|
98
|
-
@Component({
|
|
99
|
-
selector: 'RefreshControl',
|
|
100
|
-
standalone: true,
|
|
101
|
-
hostDirectives: [
|
|
102
|
-
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
103
|
-
],
|
|
104
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
105
|
-
imports: [RefreshControlHost, SymbioteHostPropsDirective],
|
|
106
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
107
|
-
template: `
|
|
108
|
-
<symbiote-refresh-control
|
|
109
|
-
#host
|
|
110
|
-
[symbioteHostProps]="hostProps()"
|
|
111
|
-
(refresh)="handleRefresh()"
|
|
112
|
-
>
|
|
113
|
-
<ng-content></ng-content>
|
|
114
|
-
</symbiote-refresh-control>
|
|
115
|
-
`,
|
|
116
|
-
})
|
|
117
|
-
export class RefreshControl
|
|
118
|
-
implements
|
|
119
|
-
IAngularRefreshControlInputs,
|
|
120
|
-
OnInit,
|
|
121
|
-
OnChanges,
|
|
122
|
-
DoCheck,
|
|
123
|
-
AfterViewInit
|
|
124
|
-
{
|
|
125
|
-
// Controlled prop the parent owns; required to match the React reference surface.
|
|
126
|
-
@Input({ required: true }) refreshing!: boolean;
|
|
127
|
-
// RN's onRefresh is `() => void | Promise<void>`, so the callback shape allows an async handler;
|
|
128
|
-
// the @Output() itself only signals the gesture (fire-and-forget, native already starts refreshing).
|
|
129
|
-
@Output() readonly refresh = new EventEmitter<void>();
|
|
130
|
-
@Input() tintColor?: string;
|
|
131
|
-
@Input() title?: string;
|
|
132
|
-
@Input() titleColor?: string;
|
|
133
|
-
@Input() progressViewOffset?: number;
|
|
134
|
-
@Input() colors?: readonly string[];
|
|
135
|
-
@Input() progressBackgroundColor?: string;
|
|
136
|
-
@Input() size?: 'default' | 'large';
|
|
137
|
-
@Input() enabled?: boolean;
|
|
138
|
-
@Input() style?: IStyleProp<IViewStyle>;
|
|
139
|
-
@Input() testID?: string;
|
|
140
|
-
@Input() nativeID?: string;
|
|
141
|
-
@Input() accessible?: boolean;
|
|
142
|
-
@Input() accessibilityLabel?: string;
|
|
143
|
-
@Input() accessibilityHint?: string;
|
|
144
|
-
@Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
|
|
145
|
-
@Input() accessibilityState?: IAccessibilityStateValue;
|
|
146
|
-
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
147
|
-
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
148
|
-
@Input() accessibilityLabelledBy?: string | string[];
|
|
149
|
-
@Input()
|
|
150
|
-
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
151
|
-
@Input()
|
|
152
|
-
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
153
|
-
@Input() screenReaderFocusable?: boolean;
|
|
154
|
-
@Input() accessibilityViewIsModal?: boolean;
|
|
155
|
-
@Input() accessibilityElementsHidden?: boolean;
|
|
156
|
-
@Input() accessibilityIgnoresInvertColors?: boolean;
|
|
157
|
-
@Input() accessibilityLanguage?: string;
|
|
158
|
-
@Input() accessibilityRespondsToUserInteraction?: boolean;
|
|
159
|
-
@Input() accessibilityShowsLargeContentViewer?: boolean;
|
|
160
|
-
@Input() accessibilityLargeContentTitle?: string;
|
|
161
|
-
@Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
|
|
162
|
-
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
163
|
-
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
164
|
-
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
165
|
-
@Input() role?: IAriaProps['role'];
|
|
166
|
-
@Input('aria-label') ariaLabel?: string;
|
|
167
|
-
@Input('aria-labelledby') ariaLabelledBy?: string;
|
|
168
|
-
@Input('aria-live') ariaLive?: IAriaProps['aria-live'];
|
|
169
|
-
@Input('aria-hidden') ariaHidden?: boolean;
|
|
170
|
-
@Input('aria-busy') ariaBusy?: boolean;
|
|
171
|
-
@Input('aria-checked') ariaChecked?: boolean | 'mixed';
|
|
172
|
-
@Input('aria-disabled') ariaDisabled?: boolean;
|
|
173
|
-
@Input('aria-expanded') ariaExpanded?: boolean;
|
|
174
|
-
@Input('aria-selected') ariaSelected?: boolean;
|
|
175
|
-
@Input('aria-modal') ariaModal?: boolean;
|
|
176
|
-
@Input('aria-valuemax') ariaValueMax?: number;
|
|
177
|
-
@Input('aria-valuemin') ariaValueMin?: number;
|
|
178
|
-
@Input('aria-valuenow') ariaValueNow?: number;
|
|
179
|
-
@Input('aria-valuetext') ariaValueText?: string;
|
|
180
|
-
|
|
181
|
-
// The inner symbiote-refresh-control primitive — NOT this component's own anchor host. Used for
|
|
182
|
-
// the imperative dispatchViewCommand calls below.
|
|
183
|
-
@ViewChild('host') private host?: RefreshControlHost;
|
|
184
|
-
|
|
185
|
-
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
186
|
-
// onto (see anchorHostStyle's doc comment) — distinct from `host` above, which targets the real
|
|
187
|
-
// inner `symbiote-refresh-control` primitive one level down.
|
|
188
|
-
private readonly elementRef = inject(ElementRef);
|
|
189
|
-
|
|
190
|
-
private lastNativeRefreshing = false;
|
|
191
|
-
private refreshNativeNode: unknown;
|
|
192
|
-
|
|
193
|
-
// Bridges the non-reactive @Input fields `hostProps` reads into the reactive graph, so it can
|
|
194
|
-
// memoize. Plain fields read inside a computed() are UNTRACKED - something must signal "a
|
|
195
|
-
// dependency changed" or the bag goes stale. Signal inputs would do this natively, but `input()`
|
|
196
|
-
// is visible only to the AOT compiler and this package's unit suite runs on JIT (see the
|
|
197
|
-
// `angular-adapter-change-detection` skill, §6); `signal()`/`computed()` are plain runtime APIs.
|
|
198
|
-
// Safe here because `refreshing` is a CONTROLLED @Input - the parent owns it, nothing in this
|
|
199
|
-
// class assigns it. The internal `lastNativeRefreshing` / `refreshNativeNode` fields drive the
|
|
200
|
-
// native handshake only; neither is read by the bag, so neither may bump this.
|
|
201
|
-
private readonly hostPropsRevision = signal(0);
|
|
202
|
-
// What the anchor's class-derived style was when the bag was last built (identity, not value).
|
|
203
|
-
private lastAnchorStyle: unknown;
|
|
204
|
-
|
|
205
|
-
ngOnInit(): void {
|
|
206
|
-
dlog('RefreshControl -> PullToRefreshView');
|
|
207
|
-
dlog(`RefreshControl refreshing=${String(this.refreshing)}`);
|
|
208
|
-
if (this.enabled !== undefined)
|
|
209
|
-
dlog(`RefreshControl enabled=${String(this.enabled)} (Android-only)`);
|
|
210
|
-
if (this.refresh.observed) dlog('RefreshControl refresh listener wired');
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
ngAfterViewInit(): void {
|
|
214
|
-
this.lastNativeRefreshing = this.refreshing;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
ngOnChanges(changes: SimpleChanges): void {
|
|
218
|
-
// Before the `refreshing`-only early-return below: this is the single moment Angular has
|
|
219
|
-
// finished writing every changed @Input, and `hostProps` depends on the whole input surface.
|
|
220
|
-
this.hostPropsRevision.update(revision => revision + 1);
|
|
221
|
-
const refreshing = changes.refreshing;
|
|
222
|
-
if (refreshing === undefined) return;
|
|
223
|
-
if (refreshing.firstChange) {
|
|
224
|
-
this.lastNativeRefreshing = this.refreshing;
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
this.syncNativeRefreshing(
|
|
228
|
-
this.refreshNativeNode ?? this.host?.nativeElement,
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Native starts the spinner before JS runs. Mirror RN's RefreshControl controlled-component
|
|
233
|
-
// handshake: remember that native is refreshing, call the user's callback, then after Angular has
|
|
234
|
-
// had a chance to propagate `[refreshing]`, force native back to the JS value if it stayed false.
|
|
235
|
-
handleRefresh(nativeNode: unknown = this.host?.nativeElement): void {
|
|
236
|
-
this.refreshNativeNode = nativeNode;
|
|
237
|
-
this.lastNativeRefreshing = true;
|
|
238
|
-
this.refresh.emit();
|
|
239
|
-
queueMicrotask(() => this.syncNativeRefreshing(nativeNode));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
private syncNativeRefreshing(nativeNode: unknown): void {
|
|
243
|
-
if (this.refreshing === this.lastNativeRefreshing) return;
|
|
244
|
-
if (!isSymbioteNode(nativeNode)) return;
|
|
245
|
-
dispatchViewCommand(nativeNode, 'setNativeRefreshing', [this.refreshing]);
|
|
246
|
-
this.lastNativeRefreshing = this.refreshing;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Forward an engine event to the matching @Output(), narrowing the template's untyped $event
|
|
250
|
-
// first. The accessibility* events arrive on the engine's structural event channel.
|
|
251
|
-
private emit(emitter: EventEmitter<ISymbioteEvent>, event: unknown): void {
|
|
252
|
-
if (isSymbioteEvent(event)) emitter.emit(event);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// The four accessibility events are boolean-GATED Fabric events
|
|
256
|
-
// (`.claude/rules/fabric-boolean-event-gates.md`): a template binding here lit the gate on every
|
|
257
|
-
// instance whether or not an app ever subscribed. `.observed`-gated, mirroring Pressable's.
|
|
258
|
-
private eventEmitterHandler(
|
|
259
|
-
emitter: EventEmitter<ISymbioteEvent>,
|
|
260
|
-
): ((event: unknown) => void) | undefined {
|
|
261
|
-
return emitter.observed ? event => this.emit(emitter, event) : undefined;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// The anchor's class-derived style is NOT an @Input: `class="..."`/`[ngClass]` at the use site
|
|
265
|
-
// resolves through the renderer's addClass onto this component's anchor host (see
|
|
266
|
-
// anchorHostStyle's doc comment), so it never shows up in SimpleChanges and ngOnChanges alone
|
|
267
|
-
// would leave a later class toggle stranded. ngDoCheck runs at exactly the cadence the old
|
|
268
|
-
// getter was re-read, and bumps only on a real identity change.
|
|
269
|
-
ngDoCheck(): void {
|
|
270
|
-
const anchorStyle = anchorHostStyle(this.elementRef);
|
|
271
|
-
if (anchorStyle === this.lastAnchorStyle) return;
|
|
272
|
-
this.lastAnchorStyle = anchorStyle;
|
|
273
|
-
this.hostPropsRevision.update(revision => revision + 1);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// The full native + a11y prop bag applied onto the host in one shot via
|
|
277
|
-
// [symbioteHostProps], instead of enumerating each key as its own template binding. The
|
|
278
|
-
// anchor's class-derived style goes FIRST, this component's own explicit `style` @Input SECOND
|
|
279
|
-
// — flattenStyle's later-wins collapse keeps an explicit [style] winning over its ambient class.
|
|
280
|
-
//
|
|
281
|
-
// A computed(), not a getter: Angular re-reads a template getter on every refresh of this view
|
|
282
|
-
// and `[symbioteHostProps]` compares by REFERENCE, so a freshly rebuilt (but identical) bag
|
|
283
|
-
// re-pushes every key through renderer.setProperty -> the engine's prop routing. A pull gesture
|
|
284
|
-
// dirties this view through its own `(refresh)` binding, which is exactly when that waste hit.
|
|
285
|
-
readonly hostProps = computed<IHostProps>(() => {
|
|
286
|
-
this.hostPropsRevision();
|
|
287
|
-
return {
|
|
288
|
-
refreshing: this.refreshing,
|
|
289
|
-
tintColor: this.tintColor,
|
|
290
|
-
title: this.title,
|
|
291
|
-
titleColor: this.titleColor,
|
|
292
|
-
progressViewOffset: this.progressViewOffset,
|
|
293
|
-
colors: this.colors,
|
|
294
|
-
progressBackgroundColor: this.progressBackgroundColor,
|
|
295
|
-
size: this.size,
|
|
296
|
-
enabled: this.enabled,
|
|
297
|
-
style: [anchorHostStyle(this.elementRef), this.style],
|
|
298
|
-
testID: this.testID,
|
|
299
|
-
nativeID: this.nativeID,
|
|
300
|
-
accessible: this.accessible,
|
|
301
|
-
...this.folded,
|
|
302
|
-
onAccessibilityAction: this.eventEmitterHandler(this.accessibilityAction),
|
|
303
|
-
onAccessibilityTap: this.eventEmitterHandler(this.accessibilityTap),
|
|
304
|
-
onMagicTap: this.eventEmitterHandler(this.magicTap),
|
|
305
|
-
onAccessibilityEscape: this.eventEmitterHandler(this.accessibilityEscape),
|
|
306
|
-
};
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
// Fold the web aria-*/role aliases into the canonical accessibility* props once per render, so the
|
|
310
|
-
// host node never sees an aria-* key (native ignores them) — the shared transform every adapter runs.
|
|
311
|
-
get folded(): Partial<IAngularRefreshControlProps> {
|
|
312
|
-
return resolveAccessibilityProps({
|
|
313
|
-
accessibilityLabel: this.accessibilityLabel,
|
|
314
|
-
accessibilityHint: this.accessibilityHint,
|
|
315
|
-
accessibilityRole: this.accessibilityRole,
|
|
316
|
-
accessibilityState: this.accessibilityState,
|
|
317
|
-
accessibilityValue: this.accessibilityValue,
|
|
318
|
-
accessibilityActions: this.accessibilityActions,
|
|
319
|
-
accessibilityLabelledBy: this.accessibilityLabelledBy,
|
|
320
|
-
importantForAccessibility: this.importantForAccessibility,
|
|
321
|
-
accessibilityLiveRegion: this.accessibilityLiveRegion,
|
|
322
|
-
screenReaderFocusable: this.screenReaderFocusable,
|
|
323
|
-
accessibilityViewIsModal: this.accessibilityViewIsModal,
|
|
324
|
-
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
325
|
-
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
326
|
-
accessibilityLanguage: this.accessibilityLanguage,
|
|
327
|
-
accessibilityRespondsToUserInteraction:
|
|
328
|
-
this.accessibilityRespondsToUserInteraction,
|
|
329
|
-
accessibilityShowsLargeContentViewer:
|
|
330
|
-
this.accessibilityShowsLargeContentViewer,
|
|
331
|
-
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
332
|
-
role: this.role,
|
|
333
|
-
'aria-label': this.ariaLabel,
|
|
334
|
-
'aria-labelledby': this.ariaLabelledBy,
|
|
335
|
-
'aria-live': this.ariaLive,
|
|
336
|
-
'aria-hidden': this.ariaHidden,
|
|
337
|
-
'aria-busy': this.ariaBusy,
|
|
338
|
-
'aria-checked': this.ariaChecked,
|
|
339
|
-
'aria-disabled': this.ariaDisabled,
|
|
340
|
-
'aria-expanded': this.ariaExpanded,
|
|
341
|
-
'aria-selected': this.ariaSelected,
|
|
342
|
-
'aria-modal': this.ariaModal,
|
|
343
|
-
'aria-valuemax': this.ariaValueMax,
|
|
344
|
-
'aria-valuemin': this.ariaValueMin,
|
|
345
|
-
'aria-valuenow': this.ariaValueNow,
|
|
346
|
-
'aria-valuetext': this.ariaValueText,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
// SafeAreaView, the Angular lifecycle half. A plain view whose native side insets its
|
|
2
|
-
// children to the safe area (notch, rounded corners, system bars). There is no JS-side
|
|
3
|
-
// translation — RN renders the native RCTSafeAreaView and lets the host do the inset math —
|
|
4
|
-
// so this folds aria/role through the shared resolveAccessibilityProps and maps style +
|
|
5
|
-
// a11y + onLayout straight onto the symbiote-safe-area-view host, children via <ng-content>.
|
|
6
|
-
// The Angular twin of the React/Vue SafeAreaView. No platform branch (one Fabric name both
|
|
7
|
-
// platforms), so this stays a flat single file.
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
CUSTOM_ELEMENTS_SCHEMA,
|
|
11
|
-
ChangeDetectionStrategy,
|
|
12
|
-
Component,
|
|
13
|
-
computed,
|
|
14
|
-
ElementRef,
|
|
15
|
-
EventEmitter,
|
|
16
|
-
inject,
|
|
17
|
-
Input,
|
|
18
|
-
Output,
|
|
19
|
-
signal,
|
|
20
|
-
type DoCheck,
|
|
21
|
-
type OnChanges,
|
|
22
|
-
} from '@angular/core';
|
|
23
|
-
import {
|
|
24
|
-
resolveAccessibilityProps,
|
|
25
|
-
type IAccessibilityProps,
|
|
26
|
-
type IAccessibilityStateValue,
|
|
27
|
-
type IAriaProps,
|
|
28
|
-
} from '@symbiote-native/components';
|
|
29
|
-
import {
|
|
30
|
-
isSymbioteEvent,
|
|
31
|
-
type IStyleProp,
|
|
32
|
-
type ISymbioteEvent,
|
|
33
|
-
type IViewStyle,
|
|
34
|
-
} from '@symbiote-native/engine';
|
|
35
|
-
import {
|
|
36
|
-
anchorHostStyle,
|
|
37
|
-
SafeAreaViewHost,
|
|
38
|
-
SymbioteHostPropsDirective,
|
|
39
|
-
SymbioteStyleInputDirective,
|
|
40
|
-
} from '../../primitives';
|
|
41
|
-
|
|
42
|
-
// Mirrors React's ISafeAreaViewProps minus children (Angular takes children via <ng-content>),
|
|
43
|
-
// declared per-adapter over the shared accessibility base since the framework-specific children
|
|
44
|
-
// slot keeps it from being fully shared across adapters.
|
|
45
|
-
export interface IAngularSafeAreaViewProps
|
|
46
|
-
extends IAccessibilityProps, IAriaProps {
|
|
47
|
-
style?: IStyleProp<IViewStyle>;
|
|
48
|
-
// `id` — accepted here and folded to `nativeID` by the shared plan, matching upstream, whose
|
|
49
|
-
// SafeAreaView takes the full ViewProps surface. Added 2026-09-01 with `ID_ALIAS` on the spec
|
|
50
|
-
// entry, deliberately as ONE change: the alias without this prop would make lowering fold a key
|
|
51
|
-
// no spelling of the component accepts, and this prop without the alias would send a raw `id` to
|
|
52
|
-
// Fabric, which declares no such key on any of these views.
|
|
53
|
-
id?: string;
|
|
54
|
-
onLayout?: (event: ISymbioteEvent) => void;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// What the component itself takes as plain @Input()s: the full surface minus onLayout and the
|
|
58
|
-
// accessibility callbacks, which it exposes as real @Output() EventEmitters instead (see the
|
|
59
|
-
// class below), mirroring Pressable's IAngularPressableInputs split.
|
|
60
|
-
export type IAngularSafeAreaViewInputs = Omit<
|
|
61
|
-
IAngularSafeAreaViewProps,
|
|
62
|
-
| 'onLayout'
|
|
63
|
-
| 'onAccessibilityAction'
|
|
64
|
-
| 'onAccessibilityTap'
|
|
65
|
-
| 'onMagicTap'
|
|
66
|
-
| 'onAccessibilityEscape'
|
|
67
|
-
>;
|
|
68
|
-
|
|
69
|
-
@Component({
|
|
70
|
-
selector: 'SafeAreaView',
|
|
71
|
-
standalone: true,
|
|
72
|
-
hostDirectives: [
|
|
73
|
-
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
74
|
-
],
|
|
75
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
76
|
-
imports: [SafeAreaViewHost, SymbioteHostPropsDirective],
|
|
77
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
78
|
-
template: `
|
|
79
|
-
<symbiote-safe-area-view [symbioteHostProps]="hostProps()">
|
|
80
|
-
<ng-content></ng-content>
|
|
81
|
-
</symbiote-safe-area-view>
|
|
82
|
-
`,
|
|
83
|
-
})
|
|
84
|
-
export class SafeAreaView
|
|
85
|
-
implements IAngularSafeAreaViewInputs, OnChanges, DoCheck
|
|
86
|
-
{
|
|
87
|
-
@Input() style?: IStyleProp<IViewStyle>;
|
|
88
|
-
// Real @Output()s, not `[onX]="…"` callbacks. Safe to name `layout` the same as the native
|
|
89
|
-
// `layout` event fired inside this component's own template — the engine's bubble() treats
|
|
90
|
-
// ANCHOR_HOST_COMPONENTS as transparent to listener lookup, so there is no double-fire.
|
|
91
|
-
// `layout` plus the four accessibility events are all boolean-GATED Fabric events
|
|
92
|
-
// (`.claude/rules/fabric-boolean-event-gates.md`): native fires them only when the committed
|
|
93
|
-
// payload carries a FUNCTION at that key, so a template binding here lit the gate on every
|
|
94
|
-
// instance whether or not an app ever subscribed. Moved into `hostProps()`'s flat bag below,
|
|
95
|
-
// `.observed`-gated — the shape `Pressable`'s `accessibilityEmitterHandler` already uses.
|
|
96
|
-
@Output() readonly layout = new EventEmitter<ISymbioteEvent>();
|
|
97
|
-
@Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
|
|
98
|
-
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
99
|
-
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
100
|
-
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
101
|
-
@Input() testID?: string;
|
|
102
|
-
@Input() nativeID?: string;
|
|
103
|
-
@Input() accessible?: boolean;
|
|
104
|
-
@Input() accessibilityLabel?: string;
|
|
105
|
-
@Input() accessibilityHint?: string;
|
|
106
|
-
@Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
|
|
107
|
-
@Input() accessibilityState?: IAccessibilityStateValue;
|
|
108
|
-
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
109
|
-
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
110
|
-
@Input() accessibilityLabelledBy?: string | string[];
|
|
111
|
-
@Input()
|
|
112
|
-
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
113
|
-
@Input()
|
|
114
|
-
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
115
|
-
@Input() screenReaderFocusable?: boolean;
|
|
116
|
-
@Input() accessibilityViewIsModal?: boolean;
|
|
117
|
-
@Input() accessibilityElementsHidden?: boolean;
|
|
118
|
-
@Input() accessibilityIgnoresInvertColors?: boolean;
|
|
119
|
-
@Input() accessibilityLanguage?: string;
|
|
120
|
-
@Input() accessibilityRespondsToUserInteraction?: boolean;
|
|
121
|
-
@Input() accessibilityShowsLargeContentViewer?: boolean;
|
|
122
|
-
@Input() accessibilityLargeContentTitle?: string;
|
|
123
|
-
@Input() role?: IAriaProps['role'];
|
|
124
|
-
@Input('aria-label') ariaLabel?: string;
|
|
125
|
-
@Input('aria-labelledby') ariaLabelledBy?: string;
|
|
126
|
-
@Input('aria-live') ariaLive?: IAriaProps['aria-live'];
|
|
127
|
-
@Input('aria-hidden') ariaHidden?: boolean;
|
|
128
|
-
@Input('aria-busy') ariaBusy?: boolean;
|
|
129
|
-
@Input('aria-checked') ariaChecked?: boolean | 'mixed';
|
|
130
|
-
@Input('aria-disabled') ariaDisabled?: boolean;
|
|
131
|
-
@Input('aria-expanded') ariaExpanded?: boolean;
|
|
132
|
-
@Input('aria-selected') ariaSelected?: boolean;
|
|
133
|
-
@Input('aria-modal') ariaModal?: boolean;
|
|
134
|
-
@Input('aria-valuemax') ariaValueMax?: number;
|
|
135
|
-
@Input('aria-valuemin') ariaValueMin?: number;
|
|
136
|
-
@Input('aria-valuenow') ariaValueNow?: number;
|
|
137
|
-
@Input('aria-valuetext') ariaValueText?: string;
|
|
138
|
-
|
|
139
|
-
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
140
|
-
// onto (see anchorHostStyle's doc comment). SafeAreaView has no inner ViewChild, so this is
|
|
141
|
-
// the only ElementRef in the class.
|
|
142
|
-
private readonly elementRef = inject(ElementRef);
|
|
143
|
-
|
|
144
|
-
// Forward an engine event to the matching @Output(), narrowing the untyped event first.
|
|
145
|
-
private emit(emitter: EventEmitter<ISymbioteEvent>, event: unknown): void {
|
|
146
|
-
if (isSymbioteEvent(event)) emitter.emit(event);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// `.observed`-gated, mirroring Pressable's `accessibilityEmitterHandler`: `undefined` here
|
|
150
|
-
// means `setEventListener` never sees a function, so the gated prop's flag never lights
|
|
151
|
-
// (`fabric-boolean-event-gates.md`, "The flag follows the VALUE, not the key").
|
|
152
|
-
private eventEmitterHandler(
|
|
153
|
-
emitter: EventEmitter<ISymbioteEvent>,
|
|
154
|
-
): ((event: unknown) => void) | undefined {
|
|
155
|
-
return emitter.observed ? event => this.emit(emitter, event) : undefined;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Bridges the non-reactive fields `hostProps` reads into the reactive graph, so it can memoize.
|
|
159
|
-
// Plain fields read inside a computed() are UNTRACKED - something must signal "a dependency
|
|
160
|
-
// changed" or the bag goes stale. Signal inputs would do this natively, but `input()` is visible
|
|
161
|
-
// only to the AOT compiler and this package's unit suite runs on JIT (see the
|
|
162
|
-
// `angular-adapter-change-detection` skill, §6); `signal()`/`computed()` are plain runtime APIs.
|
|
163
|
-
// TWO bump sites, both required - see ngOnChanges and ngDoCheck below.
|
|
164
|
-
private readonly hostPropsRevision = signal(0);
|
|
165
|
-
// What the anchor's class-derived style was when the bag was last built (identity, not value).
|
|
166
|
-
private lastAnchorStyle: unknown;
|
|
167
|
-
|
|
168
|
-
ngOnChanges(): void {
|
|
169
|
-
// The single moment Angular has finished writing every changed @Input for this pass.
|
|
170
|
-
this.hostPropsRevision.update(revision => revision + 1);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// The anchor's class-derived style is NOT an @Input: `class="..."`/`[ngClass]` at the use site
|
|
174
|
-
// resolves through the renderer's addClass onto this component's anchor host (see
|
|
175
|
-
// anchorHostStyle's doc comment), so it never shows up in SimpleChanges - ngOnChanges alone
|
|
176
|
-
// would strand a class toggled after mount (regression-covered by safe-area-view.test.ts's
|
|
177
|
-
// class-toggle case, which fails the moment this hook stops bumping). ngDoCheck runs at exactly
|
|
178
|
-
// the cadence the old getter was re-read, and bumps only on a real identity change, so an
|
|
179
|
-
// unchanged anchor still costs nothing.
|
|
180
|
-
ngDoCheck(): void {
|
|
181
|
-
const anchorStyle = anchorHostStyle(this.elementRef);
|
|
182
|
-
if (anchorStyle === this.lastAnchorStyle) return;
|
|
183
|
-
this.lastAnchorStyle = anchorStyle;
|
|
184
|
-
this.hostPropsRevision.update(revision => revision + 1);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Folds style/testID/nativeID/accessible alongside the accessibility*/aria-*/role surface into
|
|
188
|
-
// one bag for [symbioteHostProps] — resolveAccessibilityProps takes the whole props object (not
|
|
189
|
-
// just the accessibility slice) and returns it with aria-*/role folded into accessibility*, so
|
|
190
|
-
// the host node never sees an aria-* key (native ignores them), same transform React's
|
|
191
|
-
// SafeAreaView runs over its whole `rawProps`. The anchor's class-derived style goes FIRST, this
|
|
192
|
-
// component's own explicit `style` @Input SECOND — flattenStyle's later-wins collapse keeps an
|
|
193
|
-
// explicit [style] winning over its ambient class.
|
|
194
|
-
//
|
|
195
|
-
// A computed(), not a getter: Angular re-reads a template getter on every refresh of this view
|
|
196
|
-
// and `[symbioteHostProps]` compares by REFERENCE, so a freshly rebuilt (but identical) bag
|
|
197
|
-
// re-pushes every key through renderer.setProperty -> the engine's prop routing. Memoized, an
|
|
198
|
-
// a11y/layout event that only dirties this view costs nothing.
|
|
199
|
-
readonly hostProps = computed<Record<string, unknown>>(() => {
|
|
200
|
-
this.hostPropsRevision();
|
|
201
|
-
return resolveAccessibilityProps({
|
|
202
|
-
style: [anchorHostStyle(this.elementRef), this.style],
|
|
203
|
-
testID: this.testID,
|
|
204
|
-
nativeID: this.nativeID,
|
|
205
|
-
accessible: this.accessible,
|
|
206
|
-
accessibilityLabel: this.accessibilityLabel,
|
|
207
|
-
accessibilityHint: this.accessibilityHint,
|
|
208
|
-
accessibilityRole: this.accessibilityRole,
|
|
209
|
-
accessibilityState: this.accessibilityState,
|
|
210
|
-
accessibilityValue: this.accessibilityValue,
|
|
211
|
-
accessibilityActions: this.accessibilityActions,
|
|
212
|
-
accessibilityLabelledBy: this.accessibilityLabelledBy,
|
|
213
|
-
importantForAccessibility: this.importantForAccessibility,
|
|
214
|
-
accessibilityLiveRegion: this.accessibilityLiveRegion,
|
|
215
|
-
screenReaderFocusable: this.screenReaderFocusable,
|
|
216
|
-
accessibilityViewIsModal: this.accessibilityViewIsModal,
|
|
217
|
-
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
218
|
-
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
219
|
-
accessibilityLanguage: this.accessibilityLanguage,
|
|
220
|
-
accessibilityRespondsToUserInteraction:
|
|
221
|
-
this.accessibilityRespondsToUserInteraction,
|
|
222
|
-
accessibilityShowsLargeContentViewer:
|
|
223
|
-
this.accessibilityShowsLargeContentViewer,
|
|
224
|
-
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
225
|
-
role: this.role,
|
|
226
|
-
'aria-label': this.ariaLabel,
|
|
227
|
-
'aria-labelledby': this.ariaLabelledBy,
|
|
228
|
-
'aria-live': this.ariaLive,
|
|
229
|
-
'aria-hidden': this.ariaHidden,
|
|
230
|
-
'aria-busy': this.ariaBusy,
|
|
231
|
-
'aria-checked': this.ariaChecked,
|
|
232
|
-
'aria-disabled': this.ariaDisabled,
|
|
233
|
-
'aria-expanded': this.ariaExpanded,
|
|
234
|
-
'aria-selected': this.ariaSelected,
|
|
235
|
-
'aria-modal': this.ariaModal,
|
|
236
|
-
'aria-valuemax': this.ariaValueMax,
|
|
237
|
-
'aria-valuemin': this.ariaValueMin,
|
|
238
|
-
'aria-valuenow': this.ariaValueNow,
|
|
239
|
-
'aria-valuetext': this.ariaValueText,
|
|
240
|
-
onLayout: this.eventEmitterHandler(this.layout),
|
|
241
|
-
onAccessibilityAction: this.eventEmitterHandler(this.accessibilityAction),
|
|
242
|
-
onAccessibilityTap: this.eventEmitterHandler(this.accessibilityTap),
|
|
243
|
-
onMagicTap: this.eventEmitterHandler(this.magicTap),
|
|
244
|
-
onAccessibilityEscape: this.eventEmitterHandler(this.accessibilityEscape),
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
}
|