@symbiote-native/angular 0.0.0-canary-20260724081123
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/LICENSE +21 -0
- package/README.md +157 -0
- package/babel-linker.cjs +7 -0
- package/babel-register-composed.cjs +127 -0
- package/bin/symbiote-angular-dev.cjs +93 -0
- package/build/angular/anchor-host-registry.d.ts +2 -0
- package/build/angular/anchor-host-registry.js +84 -0
- package/build/angular/anchor-host-registry.js.map +1 -0
- package/build/angular/bootstrap.d.ts +7 -0
- package/build/angular/bootstrap.js +15 -0
- package/build/angular/bootstrap.js.map +1 -0
- package/build/angular/components/activity-indicator/index.android.d.ts +12 -0
- package/build/angular/components/activity-indicator/index.android.js +21 -0
- package/build/angular/components/activity-indicator/index.android.js.map +1 -0
- package/build/angular/components/activity-indicator/index.d.ts +1 -0
- package/build/angular/components/activity-indicator/index.ios.d.ts +8 -0
- package/build/angular/components/activity-indicator/index.ios.js +21 -0
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -0
- package/build/angular/components/activity-indicator/index.js +4 -0
- package/build/angular/components/activity-indicator/index.js.map +1 -0
- package/build/angular/components/activity-indicator/shared.d.ts +67 -0
- package/build/angular/components/activity-indicator/shared.js +260 -0
- package/build/angular/components/activity-indicator/shared.js.map +1 -0
- package/build/angular/components/button.d.ts +63 -0
- package/build/angular/components/button.js +332 -0
- package/build/angular/components/button.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +122 -0
- package/build/angular/components/flat-list/index.js +688 -0
- package/build/angular/components/flat-list/index.js.map +1 -0
- package/build/angular/components/image/index.android.d.ts +10 -0
- package/build/angular/components/image/index.android.js +59 -0
- package/build/angular/components/image/index.android.js.map +1 -0
- package/build/angular/components/image/index.d.ts +1 -0
- package/build/angular/components/image/index.ios.d.ts +10 -0
- package/build/angular/components/image/index.ios.js +59 -0
- package/build/angular/components/image/index.ios.js.map +1 -0
- package/build/angular/components/image/index.js +3 -0
- package/build/angular/components/image/index.js.map +1 -0
- package/build/angular/components/image/shared.d.ts +101 -0
- package/build/angular/components/image/shared.js +406 -0
- package/build/angular/components/image/shared.js.map +1 -0
- package/build/angular/components/image-background.d.ts +81 -0
- package/build/angular/components/image-background.js +418 -0
- package/build/angular/components/image-background.js.map +1 -0
- package/build/angular/components/input-accessory-view/index.d.ts +60 -0
- package/build/angular/components/input-accessory-view/index.js +251 -0
- package/build/angular/components/input-accessory-view/index.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +82 -0
- package/build/angular/components/keyboard-avoiding-view/index.js +358 -0
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -0
- package/build/angular/components/modal/index.d.ts +92 -0
- package/build/angular/components/modal/index.js +360 -0
- package/build/angular/components/modal/index.js.map +1 -0
- package/build/angular/components/pressable/index.d.ts +123 -0
- package/build/angular/components/pressable/index.js +420 -0
- package/build/angular/components/pressable/index.js.map +1 -0
- package/build/angular/components/refresh-control/index.d.ts +86 -0
- package/build/angular/components/refresh-control/index.js +342 -0
- package/build/angular/components/refresh-control/index.js.map +1 -0
- package/build/angular/components/safe-area-view/index.d.ts +57 -0
- package/build/angular/components/safe-area-view/index.js +248 -0
- package/build/angular/components/safe-area-view/index.js.map +1 -0
- package/build/angular/components/scroll-view/index.android.d.ts +21 -0
- package/build/angular/components/scroll-view/index.android.js +235 -0
- package/build/angular/components/scroll-view/index.android.js.map +1 -0
- package/build/angular/components/scroll-view/index.d.ts +3 -0
- package/build/angular/components/scroll-view/index.ios.d.ts +9 -0
- package/build/angular/components/scroll-view/index.ios.js +86 -0
- package/build/angular/components/scroll-view/index.ios.js.map +1 -0
- package/build/angular/components/scroll-view/index.js +6 -0
- package/build/angular/components/scroll-view/index.js.map +1 -0
- package/build/angular/components/scroll-view/projection.d.ts +32 -0
- package/build/angular/components/scroll-view/projection.js +334 -0
- package/build/angular/components/scroll-view/projection.js.map +1 -0
- package/build/angular/components/scroll-view/shared.d.ts +230 -0
- package/build/angular/components/scroll-view/shared.js +682 -0
- package/build/angular/components/scroll-view/shared.js.map +1 -0
- package/build/angular/components/scroll-view/sticky-header.d.ts +40 -0
- package/build/angular/components/scroll-view/sticky-header.js +225 -0
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -0
- package/build/angular/components/section-list/index.d.ts +116 -0
- package/build/angular/components/section-list/index.js +641 -0
- package/build/angular/components/section-list/index.js.map +1 -0
- package/build/angular/components/switch/index.android.d.ts +20 -0
- package/build/angular/components/switch/index.android.js +70 -0
- package/build/angular/components/switch/index.android.js.map +1 -0
- package/build/angular/components/switch/index.d.ts +1 -0
- package/build/angular/components/switch/index.ios.d.ts +19 -0
- package/build/angular/components/switch/index.ios.js +69 -0
- package/build/angular/components/switch/index.ios.js.map +1 -0
- package/build/angular/components/switch/index.js +4 -0
- package/build/angular/components/switch/index.js.map +1 -0
- package/build/angular/components/switch/shared.d.ts +40 -0
- package/build/angular/components/switch/shared.js +127 -0
- package/build/angular/components/switch/shared.js.map +1 -0
- package/build/angular/components/text-input.d.ts +153 -0
- package/build/angular/components/text-input.js +599 -0
- package/build/angular/components/text-input.js.map +1 -0
- package/build/angular/components/touchable/index.d.ts +245 -0
- package/build/angular/components/touchable/index.js +1186 -0
- package/build/angular/components/touchable/index.js.map +1 -0
- package/build/angular/components/touchable-native-feedback/index.d.ts +83 -0
- package/build/angular/components/touchable-native-feedback/index.js +371 -0
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -0
- package/build/angular/components/virtualized-list/directives.d.ts +54 -0
- package/build/angular/components/virtualized-list/directives.js +153 -0
- package/build/angular/components/virtualized-list/directives.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +253 -0
- package/build/angular/components/virtualized-list/index.js +1031 -0
- package/build/angular/components/virtualized-list/index.js.map +1 -0
- package/build/angular/components/virtualized-section-list/directives.d.ts +37 -0
- package/build/angular/components/virtualized-section-list/directives.js +76 -0
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -0
- package/build/angular/components/virtualized-section-list/index.d.ts +178 -0
- package/build/angular/components/virtualized-section-list/index.js +720 -0
- package/build/angular/components/virtualized-section-list/index.js.map +1 -0
- package/build/angular/components.d.ts +39 -0
- package/build/angular/components.js +38 -0
- package/build/angular/components.js.map +1 -0
- package/build/angular/create-portal/index.d.ts +22 -0
- package/build/angular/create-portal/index.js +74 -0
- package/build/angular/create-portal/index.js.map +1 -0
- package/build/angular/create-tunnel/index.d.ts +34 -0
- package/build/angular/create-tunnel/index.js +126 -0
- package/build/angular/create-tunnel/index.js.map +1 -0
- package/build/angular/descriptor-to-angular/index.d.ts +22 -0
- package/build/angular/descriptor-to-angular/index.js +142 -0
- package/build/angular/descriptor-to-angular/index.js.map +1 -0
- package/build/angular/host-instance/index.d.ts +2 -0
- package/build/angular/host-instance/index.js +37 -0
- package/build/angular/host-instance/index.js.map +1 -0
- package/build/angular/index.d.ts +22 -0
- package/build/angular/index.js +46 -0
- package/build/angular/index.js.map +1 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +14 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.js +91 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +1 -0
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +14 -0
- package/build/angular/modules/animated/animated-leaf-binder.js +91 -0
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -0
- package/build/angular/modules/animated/create-animated-component.d.ts +59 -0
- package/build/angular/modules/animated/create-animated-component.js +364 -0
- package/build/angular/modules/animated/create-animated-component.js.map +1 -0
- package/build/angular/modules/animated/index.d.ts +51 -0
- package/build/angular/modules/animated/index.js +59 -0
- package/build/angular/modules/animated/index.js.map +1 -0
- package/build/angular/modules/app-registry/index.d.ts +7 -0
- package/build/angular/modules/app-registry/index.js +23 -0
- package/build/angular/modules/app-registry/index.js.map +1 -0
- package/build/angular/modules/status-bar/index.d.ts +20 -0
- package/build/angular/modules/status-bar/index.js +59 -0
- package/build/angular/modules/status-bar/index.js.map +1 -0
- package/build/angular/primitives/index.d.ts +63 -0
- package/build/angular/primitives/index.js +189 -0
- package/build/angular/primitives/index.js.map +1 -0
- package/build/angular/primitives/shared.d.ts +88 -0
- package/build/angular/primitives/shared.js +171 -0
- package/build/angular/primitives/shared.js.map +1 -0
- package/build/angular/render/index.d.ts +11 -0
- package/build/angular/render/index.js +171 -0
- package/build/angular/render/index.js.map +1 -0
- package/build/angular/renderer/index.d.ts +37 -0
- package/build/angular/renderer/index.js +301 -0
- package/build/angular/renderer/index.js.map +1 -0
- package/build/angular/services/color-scheme.service.d.ts +16 -0
- package/build/angular/services/color-scheme.service.js +31 -0
- package/build/angular/services/color-scheme.service.js.map +1 -0
- package/build/angular/services/index.d.ts +2 -0
- package/build/angular/services/index.js +3 -0
- package/build/angular/services/index.js.map +1 -0
- package/build/angular/services/window-dimensions.service.d.ts +16 -0
- package/build/angular/services/window-dimensions.service.js +42 -0
- package/build/angular/services/window-dimensions.service.js.map +1 -0
- package/metro-config.cjs +48 -0
- package/metro-css-parser.cjs +13 -0
- package/package.json +80 -0
- package/src/__tests__/responder-change-detection.test.ts +102 -0
- package/src/__tests__/responder-nested-cd.test.ts +113 -0
- package/src/anchor-host-registry.ts +88 -0
- package/src/angular-gaps.test.ts +142 -0
- package/src/angular-imports.test.ts +100 -0
- package/src/bootstrap.ts +26 -0
- package/src/components/activity-indicator/activity-indicator-class-style.test.ts +44 -0
- package/src/components/activity-indicator/activity-indicator.test.ts +131 -0
- package/src/components/activity-indicator/index.android.ts +16 -0
- package/src/components/activity-indicator/index.ios.ts +17 -0
- package/src/components/activity-indicator/index.ts +4 -0
- package/src/components/activity-indicator/shared.ts +206 -0
- package/src/components/button.ts +193 -0
- package/src/components/flat-list/column-wrapper-style-class.test.ts +116 -0
- package/src/components/flat-list/flat-list-array-style.test.ts +131 -0
- package/src/components/flat-list/flat-list-class-style.test.ts +74 -0
- package/src/components/flat-list/flat-list-scroll-containment.test.ts +107 -0
- package/src/components/flat-list/flat-list.test.ts +99 -0
- package/src/components/flat-list/index.ts +574 -0
- package/src/components/image/image-class-style.test.ts +46 -0
- package/src/components/image/index.android.ts +48 -0
- package/src/components/image/index.ios.ts +48 -0
- package/src/components/image/index.ts +3 -0
- package/src/components/image/shared.ts +479 -0
- package/src/components/image-background-class-style.test.ts +48 -0
- package/src/components/image-background-style-class.test.ts +79 -0
- package/src/components/image-background.ts +270 -0
- package/src/components/input-accessory-view/index.ts +185 -0
- package/src/components/input-accessory-view/input-accessory-view.test.ts +43 -0
- package/src/components/keyboard-avoiding-view/index.ts +303 -0
- package/src/components/keyboard-avoiding-view/keyboard-avoiding-view.test.ts +54 -0
- package/src/components/modal/index.ts +299 -0
- package/src/components/modal/modal.test.ts +43 -0
- package/src/components/pressable/index.ts +373 -0
- package/src/components/pressable/pressable.test.ts +85 -0
- package/src/components/refresh-control/index.ts +284 -0
- package/src/components/refresh-control/refresh-control.test.ts +41 -0
- package/src/components/safe-area-view/index.ts +177 -0
- package/src/components/safe-area-view/safe-area-view.test.ts +43 -0
- package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +123 -0
- package/src/components/scroll-view/content-container-style-class.test.ts +75 -0
- package/src/components/scroll-view/index.android.ts +190 -0
- package/src/components/scroll-view/index.ios.ts +79 -0
- package/src/components/scroll-view/index.ts +7 -0
- package/src/components/scroll-view/projection.ts +414 -0
- package/src/components/scroll-view/scroll-view-class-style.test.ts +134 -0
- package/src/components/scroll-view/scroll-view-projection.test.ts +194 -0
- package/src/components/scroll-view/shared.ts +844 -0
- package/src/components/scroll-view/sticky-header.ts +255 -0
- package/src/components/section-list/index.ts +445 -0
- package/src/components/switch/android-switch-class.test.ts +42 -0
- package/src/components/switch/index.android.ts +61 -0
- package/src/components/switch/index.ios.ts +60 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/shared.ts +197 -0
- package/src/components/switch/switch.test.ts +92 -0
- package/src/components/text-input-class-style.test.ts +54 -0
- package/src/components/text-input.ts +531 -0
- package/src/components/touchable/index.ts +629 -0
- package/src/components/touchable/touchable.test.ts +102 -0
- package/src/components/touchable-native-feedback/index.ts +254 -0
- package/src/components/touchable-native-feedback/touchable-native-feedback.test.ts +49 -0
- package/src/components/virtualized-list/directives.ts +168 -0
- package/src/components/virtualized-list/index.ts +992 -0
- package/src/components/virtualized-list/virtualized-list-class-style.test.ts +76 -0
- package/src/components/virtualized-section-list/directives.ts +90 -0
- package/src/components/virtualized-section-list/index.ts +601 -0
- package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +60 -0
- package/src/components.ts +149 -0
- package/src/create-portal/create-portal.test.ts +90 -0
- package/src/create-portal/index.ts +74 -0
- package/src/create-tunnel/create-tunnel.test.ts +120 -0
- package/src/create-tunnel/index.ts +136 -0
- package/src/descriptor-to-angular/descriptor-outlet.test.ts +94 -0
- package/src/descriptor-to-angular/index.ts +190 -0
- package/src/host-instance/host-instance.test.ts +49 -0
- package/src/host-instance/index.ts +36 -0
- package/src/index.ts +261 -0
- package/src/modules/animated/animated-leaf-binder/animated-leaf-binder.test.ts +116 -0
- package/src/modules/animated/animated-leaf-binder/index.ts +101 -0
- package/src/modules/animated/animated-leaf-binder.test.ts +116 -0
- package/src/modules/animated/animated-leaf-binder.ts +101 -0
- package/src/modules/animated/animated-scroll-view.test.ts +102 -0
- package/src/modules/animated/create-animated-component.ts +365 -0
- package/src/modules/animated/index.ts +105 -0
- package/src/modules/app-registry/app-registry.test.ts +106 -0
- package/src/modules/app-registry/index.ts +60 -0
- package/src/modules/status-bar/index.ts +57 -0
- package/src/modules/status-bar/status-bar.test.ts +50 -0
- package/src/primitives/index.ts +119 -0
- package/src/primitives/shared.ts +190 -0
- package/src/render/index.ts +235 -0
- package/src/render/render.test.ts +147 -0
- package/src/renderer/index.ts +347 -0
- package/src/renderer/renderer.test.ts +203 -0
- package/src/services/color-scheme.service.ts +27 -0
- package/src/services/index.ts +2 -0
- package/src/services/services.test.ts +163 -0
- package/src/services/window-dimensions.service.ts +41 -0
- package/tsconfig.angular.base.json +30 -0
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
// ScrollView, the Angular lifecycle half. The Fabric tree is nested: a scroll view
|
|
2
|
+
// wraps a content view that holds the children (RN's ScrollView.js shape). The platform-invariant
|
|
3
|
+
// math (decelerationRate, the per-axis intrinsics/base style, the content-size dedupe, the
|
|
4
|
+
// imperative handle, the native sticky scroll-attach, the aria/role fold) lives in
|
|
5
|
+
// @symbiote-native/components, shared verbatim with React/Vue. Here Angular supplies only the lifecycle:
|
|
6
|
+
// the host node held by IDENTITY through a directive's ElementRef, a forwarded-prop bag set onto
|
|
7
|
+
// that node through Renderer2 (-> routeProp), the imperative handle (scrollTo/scrollToEnd/
|
|
8
|
+
// flashScrollIndicators), the content-size dedupe, and the native sticky scroll-attach wired
|
|
9
|
+
// through whenCommitted. This is the Angular twin of the React useRef + Vue shallowRef host.
|
|
10
|
+
//
|
|
11
|
+
// What diverges per platform, how a RefreshControl integrates, stays in the
|
|
12
|
+
// .ios/.android files (Metro filename-selected): iOS renders the RefreshControl as a SIBLING
|
|
13
|
+
// before the content container; on Android an AndroidSwipeRefreshLayout WRAPS the scroll view.
|
|
14
|
+
//
|
|
15
|
+
// Angular cannot transform <ng-content> children in the template the way React children.map /
|
|
16
|
+
// Vue slots() can, so ScrollView owns a tiny projection bridge: the content host registers a
|
|
17
|
+
// ScrollViewProjectionController with the Angular renderer, and the renderer wraps direct projected
|
|
18
|
+
// children whose indices match stickyHeaderIndices. Android RefreshControl projection is handled by
|
|
19
|
+
// re-rendering the projected component's native prop surface as the wrapper around the scroll view
|
|
20
|
+
// (RN's Android shape), while excluding the original projected node from the content slot.
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
Directive,
|
|
24
|
+
ElementRef,
|
|
25
|
+
EventEmitter,
|
|
26
|
+
Output,
|
|
27
|
+
ViewChild,
|
|
28
|
+
ContentChild,
|
|
29
|
+
inject,
|
|
30
|
+
type AfterViewInit,
|
|
31
|
+
type OnDestroy,
|
|
32
|
+
} from '@angular/core';
|
|
33
|
+
import {
|
|
34
|
+
attachStickyScroll,
|
|
35
|
+
buildScrollViewHandle,
|
|
36
|
+
didContentSizeChange,
|
|
37
|
+
forwardScrollEvent,
|
|
38
|
+
readLayoutDimension,
|
|
39
|
+
resolveAccessibilityProps,
|
|
40
|
+
resolveDecelerationRate,
|
|
41
|
+
resolveScrollForwarding,
|
|
42
|
+
selectScrollIntrinsics,
|
|
43
|
+
splitLayoutProps,
|
|
44
|
+
type IAccessibilityProps,
|
|
45
|
+
type IAccessibilityStateValue,
|
|
46
|
+
type IAriaProps,
|
|
47
|
+
type IContentSize,
|
|
48
|
+
type IScrollViewHandle,
|
|
49
|
+
} from '@symbiote-native/components';
|
|
50
|
+
import {
|
|
51
|
+
AnimatedValue,
|
|
52
|
+
dlog,
|
|
53
|
+
event as animatedEvent,
|
|
54
|
+
isNativeAnimatedAvailable,
|
|
55
|
+
isSymbioteNode,
|
|
56
|
+
resolveClassName,
|
|
57
|
+
whenCommitted,
|
|
58
|
+
type IStyleProp,
|
|
59
|
+
type ISymbioteEvent,
|
|
60
|
+
type ISymbioteNode,
|
|
61
|
+
type IViewStyle,
|
|
62
|
+
} from '@symbiote-native/engine';
|
|
63
|
+
import { SymbioteHostPropsDirective } from '../../primitives';
|
|
64
|
+
import { RefreshControl } from '../refresh-control';
|
|
65
|
+
import { ScrollViewProjectionController } from './projection';
|
|
66
|
+
|
|
67
|
+
export type { IScrollViewHandle } from '@symbiote-native/components';
|
|
68
|
+
|
|
69
|
+
type IScrollHandler = (event: ISymbioteEvent) => void;
|
|
70
|
+
type IContentSizeHandler = (width: number, height: number) => void;
|
|
71
|
+
|
|
72
|
+
// The inputs every platform ScrollView accepts, listed on each concrete @Component (the adapter
|
|
73
|
+
// convention, mirroring Switch/Animated) so the base fields bind consistently. The aria-* /
|
|
74
|
+
// role aliases ride here too and are folded by resolveAccessibilityProps before reaching native.
|
|
75
|
+
export const SCROLL_VIEW_INPUTS = [
|
|
76
|
+
'style',
|
|
77
|
+
'contentContainerStyle',
|
|
78
|
+
'horizontal',
|
|
79
|
+
'scrollEnabled',
|
|
80
|
+
'showsVerticalScrollIndicator',
|
|
81
|
+
'showsHorizontalScrollIndicator',
|
|
82
|
+
'pagingEnabled',
|
|
83
|
+
'bounces',
|
|
84
|
+
'decelerationRate',
|
|
85
|
+
'scrollEventThrottle',
|
|
86
|
+
'contentInset',
|
|
87
|
+
'contentOffset',
|
|
88
|
+
'removeClippedSubviews',
|
|
89
|
+
'snapToInterval',
|
|
90
|
+
'snapToOffsets',
|
|
91
|
+
'snapToAlignment',
|
|
92
|
+
'snapToStart',
|
|
93
|
+
'snapToEnd',
|
|
94
|
+
'disableIntervalMomentum',
|
|
95
|
+
'stickyHeaderIndices',
|
|
96
|
+
'invertStickyHeaders',
|
|
97
|
+
'StickyHeaderComponent',
|
|
98
|
+
'keyboardDismissMode',
|
|
99
|
+
'keyboardShouldPersistTaps',
|
|
100
|
+
'maintainVisibleContentPosition',
|
|
101
|
+
'alwaysBounceHorizontal',
|
|
102
|
+
'alwaysBounceVertical',
|
|
103
|
+
'centerContent',
|
|
104
|
+
'scrollIndicatorInsets',
|
|
105
|
+
'indicatorStyle',
|
|
106
|
+
'directionalLockEnabled',
|
|
107
|
+
'automaticallyAdjustKeyboardInsets',
|
|
108
|
+
'contentInsetAdjustmentBehavior',
|
|
109
|
+
'minimumZoomScale',
|
|
110
|
+
'maximumZoomScale',
|
|
111
|
+
'zoomScale',
|
|
112
|
+
'bouncesZoom',
|
|
113
|
+
'pinchGestureEnabled',
|
|
114
|
+
'nestedScrollEnabled',
|
|
115
|
+
'overScrollMode',
|
|
116
|
+
'fadingEdgeLength',
|
|
117
|
+
'persistentScrollbar',
|
|
118
|
+
'endFillColor',
|
|
119
|
+
'onScroll',
|
|
120
|
+
'onScrollBeginDrag',
|
|
121
|
+
'onScrollEndDrag',
|
|
122
|
+
'onMomentumScrollBegin',
|
|
123
|
+
'onMomentumScrollEnd',
|
|
124
|
+
'testID',
|
|
125
|
+
'nativeID',
|
|
126
|
+
'accessible',
|
|
127
|
+
'accessibilityLabel',
|
|
128
|
+
'accessibilityHint',
|
|
129
|
+
'accessibilityRole',
|
|
130
|
+
'accessibilityState',
|
|
131
|
+
'accessibilityValue',
|
|
132
|
+
'accessibilityActions',
|
|
133
|
+
'accessibilityLabelledBy',
|
|
134
|
+
'importantForAccessibility',
|
|
135
|
+
'accessibilityLiveRegion',
|
|
136
|
+
'screenReaderFocusable',
|
|
137
|
+
'accessibilityViewIsModal',
|
|
138
|
+
'accessibilityElementsHidden',
|
|
139
|
+
'accessibilityIgnoresInvertColors',
|
|
140
|
+
'accessibilityLanguage',
|
|
141
|
+
'accessibilityRespondsToUserInteraction',
|
|
142
|
+
'accessibilityShowsLargeContentViewer',
|
|
143
|
+
'accessibilityLargeContentTitle',
|
|
144
|
+
'role',
|
|
145
|
+
'aria-label',
|
|
146
|
+
'aria-labelledby',
|
|
147
|
+
'aria-live',
|
|
148
|
+
'aria-hidden',
|
|
149
|
+
'aria-busy',
|
|
150
|
+
'aria-checked',
|
|
151
|
+
'aria-disabled',
|
|
152
|
+
'aria-expanded',
|
|
153
|
+
'aria-selected',
|
|
154
|
+
'aria-modal',
|
|
155
|
+
'aria-valuemax',
|
|
156
|
+
'aria-valuemin',
|
|
157
|
+
'aria-valuenow',
|
|
158
|
+
'aria-valuetext',
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
// The Angular-facing prop surface. React's IScrollViewProps is React-coupled (ReactNode children,
|
|
162
|
+
// ReactElement refreshControl); Angular takes children via <ng-content> and composes the
|
|
163
|
+
// RefreshControl through projection / an outer wrap, so this mirrors the same pass-through surface
|
|
164
|
+
// minus those, declared per-adapter over the shared accessibility base since a framework element/ref
|
|
165
|
+
// field can't live in a shared agnostic type. Every prop is accepted and typed against the full
|
|
166
|
+
// React surface so app code type-checks against parity now.
|
|
167
|
+
export interface IAngularScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
168
|
+
style?: IStyleProp<IViewStyle>;
|
|
169
|
+
// A bare string resolves through the shared style registry, like `class` on the host node.
|
|
170
|
+
contentContainerStyle?: IStyleProp<IViewStyle> | string;
|
|
171
|
+
horizontal?: boolean;
|
|
172
|
+
scrollEnabled?: boolean;
|
|
173
|
+
showsVerticalScrollIndicator?: boolean;
|
|
174
|
+
showsHorizontalScrollIndicator?: boolean;
|
|
175
|
+
pagingEnabled?: boolean;
|
|
176
|
+
bounces?: boolean;
|
|
177
|
+
decelerationRate?: 'normal' | 'fast' | number;
|
|
178
|
+
scrollEventThrottle?: number;
|
|
179
|
+
contentInset?: { top?: number; left?: number; bottom?: number; right?: number };
|
|
180
|
+
contentOffset?: { x: number; y: number };
|
|
181
|
+
removeClippedSubviews?: boolean;
|
|
182
|
+
snapToInterval?: number;
|
|
183
|
+
snapToOffsets?: number[];
|
|
184
|
+
snapToAlignment?: 'start' | 'center' | 'end';
|
|
185
|
+
snapToStart?: boolean;
|
|
186
|
+
snapToEnd?: boolean;
|
|
187
|
+
disableIntervalMomentum?: boolean;
|
|
188
|
+
// Sticky headers: RN implements stickiness PURELY IN JS (ScrollView.js wraps each flagged child
|
|
189
|
+
// in ScrollViewStickyHeader, driven by the scroll offset). The native scroll view does NOT honor
|
|
190
|
+
// an index array. See the header note on the Angular projection boundary for the auto-wrap.
|
|
191
|
+
stickyHeaderIndices?: number[];
|
|
192
|
+
invertStickyHeaders?: boolean;
|
|
193
|
+
// Angular auto projection is renderer-node based, so this input is intentionally explicit-
|
|
194
|
+
// composition only: custom sticky wrappers must be written in the template; auto stickyHeaderIndices
|
|
195
|
+
// uses the built-in wrapper to stay AOT-safe.
|
|
196
|
+
StickyHeaderComponent?: unknown;
|
|
197
|
+
keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
|
|
198
|
+
keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
|
|
199
|
+
maintainVisibleContentPosition?: {
|
|
200
|
+
minIndexForVisible: number;
|
|
201
|
+
autoscrollToTopThreshold?: number;
|
|
202
|
+
};
|
|
203
|
+
// iOS-only forwarding props (harmless on Android: its manager ignores unknown props).
|
|
204
|
+
alwaysBounceHorizontal?: boolean;
|
|
205
|
+
alwaysBounceVertical?: boolean;
|
|
206
|
+
centerContent?: boolean;
|
|
207
|
+
scrollIndicatorInsets?: { top?: number; left?: number; bottom?: number; right?: number };
|
|
208
|
+
indicatorStyle?: 'default' | 'black' | 'white';
|
|
209
|
+
directionalLockEnabled?: boolean;
|
|
210
|
+
automaticallyAdjustKeyboardInsets?: boolean;
|
|
211
|
+
contentInsetAdjustmentBehavior?: 'automatic' | 'scrollableAxes' | 'never' | 'always';
|
|
212
|
+
minimumZoomScale?: number;
|
|
213
|
+
maximumZoomScale?: number;
|
|
214
|
+
zoomScale?: number;
|
|
215
|
+
bouncesZoom?: boolean;
|
|
216
|
+
pinchGestureEnabled?: boolean;
|
|
217
|
+
// Android-only forwarding props (harmless on iOS).
|
|
218
|
+
nestedScrollEnabled?: boolean;
|
|
219
|
+
overScrollMode?: 'auto' | 'always' | 'never';
|
|
220
|
+
fadingEdgeLength?: number;
|
|
221
|
+
persistentScrollbar?: boolean;
|
|
222
|
+
endFillColor?: string;
|
|
223
|
+
onLayout?: IScrollHandler;
|
|
224
|
+
onScroll?: IScrollHandler;
|
|
225
|
+
onScrollBeginDrag?: IScrollHandler;
|
|
226
|
+
onScrollEndDrag?: IScrollHandler;
|
|
227
|
+
onMomentumScrollBegin?: IScrollHandler;
|
|
228
|
+
onMomentumScrollEnd?: IScrollHandler;
|
|
229
|
+
// iOS-only: user tapped the status bar to scroll to top. Inert on Android.
|
|
230
|
+
onScrollToTop?: IScrollHandler;
|
|
231
|
+
// Synthesized in JS from the content view's onLayout (RN _handleContentOnLayout); deduped.
|
|
232
|
+
onContentSizeChange?: IContentSizeHandler;
|
|
233
|
+
onAccessibilityAction?: (event: ISymbioteEvent) => void;
|
|
234
|
+
onAccessibilityTap?: (event: ISymbioteEvent) => void;
|
|
235
|
+
onMagicTap?: (event: ISymbioteEvent) => void;
|
|
236
|
+
onAccessibilityEscape?: (event: ISymbioteEvent) => void;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// What ScrollView itself takes as plain @Input()s: the full surface minus the layout/content-size/
|
|
240
|
+
// accessibility events it exposes as real @Output() EventEmitters instead (see ScrollViewBase below).
|
|
241
|
+
// onScroll and the drag/momentum family stay callback @Input()s permanently — they must also accept
|
|
242
|
+
// an Animated.event(...) native-driver marker, which no @Output() binding can carry.
|
|
243
|
+
export type IAngularScrollViewInputs = Omit<
|
|
244
|
+
IAngularScrollViewProps,
|
|
245
|
+
| 'onLayout'
|
|
246
|
+
| 'onScrollToTop'
|
|
247
|
+
| 'onContentSizeChange'
|
|
248
|
+
| 'onAccessibilityAction'
|
|
249
|
+
| 'onAccessibilityTap'
|
|
250
|
+
| 'onMagicTap'
|
|
251
|
+
| 'onAccessibilityEscape'
|
|
252
|
+
>;
|
|
253
|
+
|
|
254
|
+
// Strip undefined entries so a prop the user never set is not forwarded to the host (an undefined
|
|
255
|
+
// reaching Fabric is at best a no-op, at worst clears a default). The Angular twin of React/Vue
|
|
256
|
+
// destructuring `...rest` past the defined props.
|
|
257
|
+
function compact(bag: Record<string, unknown>): Record<string, unknown> {
|
|
258
|
+
const result: Record<string, unknown> = {};
|
|
259
|
+
for (const key of Object.keys(bag)) {
|
|
260
|
+
if (bag[key] !== undefined) result[key] = bag[key];
|
|
261
|
+
}
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ScrollView's forwarded surface (scroll events + the snap/keyboard/zoom families) is exactly the
|
|
266
|
+
// flat-bag shape SymbioteHostPropsDirective spreads onto a host element via Renderer2 (-> routeProp).
|
|
267
|
+
// The scroll-event callbacks ride this bag (not the (event) channel) because routeProp must be free
|
|
268
|
+
// to wire onScroll as an Animated.event when sticky headers drive the value natively, which the
|
|
269
|
+
// listen()/setEventListener channel cannot carry. This mirrors React/Vue putting onScroll on
|
|
270
|
+
// outerProps.
|
|
271
|
+
@Directive({
|
|
272
|
+
selector: '[symbioteScrollViewProjection]',
|
|
273
|
+
standalone: true,
|
|
274
|
+
inputs: ['symbioteScrollViewProjection'],
|
|
275
|
+
})
|
|
276
|
+
export class ScrollViewProjectionDirective {
|
|
277
|
+
private readonly elementRef = inject<ElementRef<unknown>>(ElementRef);
|
|
278
|
+
|
|
279
|
+
set symbioteScrollViewProjection(controller: ScrollViewProjectionController) {
|
|
280
|
+
const node = this.elementRef.nativeElement;
|
|
281
|
+
if (isSymbioteNode(node)) controller.bindContentNode(node);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// @Directive() (no selector) is the Angular-sanctioned decorator for an abstract base that
|
|
286
|
+
// declares lifecycle hooks and @ViewChild queries (mirrors AnimatedComponentBase) — without it
|
|
287
|
+
// ngtsc rejects the inherited hooks/queries (NG2007). The concrete platform @Components add ONLY a
|
|
288
|
+
// decorator + their platform template; all behavior lives here.
|
|
289
|
+
@Directive()
|
|
290
|
+
export abstract class ScrollViewBase implements IAngularScrollViewInputs, AfterViewInit, OnDestroy {
|
|
291
|
+
style: IStyleProp<IViewStyle> | undefined;
|
|
292
|
+
contentContainerStyle: IStyleProp<IViewStyle> | string | undefined;
|
|
293
|
+
horizontal: boolean | undefined;
|
|
294
|
+
scrollEnabled: boolean | undefined;
|
|
295
|
+
showsVerticalScrollIndicator: boolean | undefined;
|
|
296
|
+
showsHorizontalScrollIndicator: boolean | undefined;
|
|
297
|
+
pagingEnabled: boolean | undefined;
|
|
298
|
+
bounces: boolean | undefined;
|
|
299
|
+
decelerationRate: 'normal' | 'fast' | number | undefined;
|
|
300
|
+
scrollEventThrottle: number | undefined;
|
|
301
|
+
contentInset: IAngularScrollViewProps['contentInset'];
|
|
302
|
+
contentOffset: IAngularScrollViewProps['contentOffset'];
|
|
303
|
+
removeClippedSubviews: boolean | undefined;
|
|
304
|
+
snapToInterval: number | undefined;
|
|
305
|
+
snapToOffsets: number[] | undefined;
|
|
306
|
+
snapToAlignment: IAngularScrollViewProps['snapToAlignment'];
|
|
307
|
+
snapToStart: boolean | undefined;
|
|
308
|
+
snapToEnd: boolean | undefined;
|
|
309
|
+
disableIntervalMomentum: boolean | undefined;
|
|
310
|
+
stickyHeaderIndices: number[] | undefined;
|
|
311
|
+
invertStickyHeaders: boolean | undefined;
|
|
312
|
+
StickyHeaderComponent: unknown;
|
|
313
|
+
keyboardDismissMode: IAngularScrollViewProps['keyboardDismissMode'];
|
|
314
|
+
keyboardShouldPersistTaps: IAngularScrollViewProps['keyboardShouldPersistTaps'];
|
|
315
|
+
maintainVisibleContentPosition: IAngularScrollViewProps['maintainVisibleContentPosition'];
|
|
316
|
+
alwaysBounceHorizontal: boolean | undefined;
|
|
317
|
+
alwaysBounceVertical: boolean | undefined;
|
|
318
|
+
centerContent: boolean | undefined;
|
|
319
|
+
scrollIndicatorInsets: IAngularScrollViewProps['scrollIndicatorInsets'];
|
|
320
|
+
indicatorStyle: IAngularScrollViewProps['indicatorStyle'];
|
|
321
|
+
directionalLockEnabled: boolean | undefined;
|
|
322
|
+
automaticallyAdjustKeyboardInsets: boolean | undefined;
|
|
323
|
+
contentInsetAdjustmentBehavior: IAngularScrollViewProps['contentInsetAdjustmentBehavior'];
|
|
324
|
+
minimumZoomScale: number | undefined;
|
|
325
|
+
maximumZoomScale: number | undefined;
|
|
326
|
+
zoomScale: number | undefined;
|
|
327
|
+
bouncesZoom: boolean | undefined;
|
|
328
|
+
pinchGestureEnabled: boolean | undefined;
|
|
329
|
+
nestedScrollEnabled: boolean | undefined;
|
|
330
|
+
overScrollMode: IAngularScrollViewProps['overScrollMode'];
|
|
331
|
+
fadingEdgeLength: number | undefined;
|
|
332
|
+
persistentScrollbar: boolean | undefined;
|
|
333
|
+
endFillColor: string | undefined;
|
|
334
|
+
onScroll: IScrollHandler | undefined;
|
|
335
|
+
onScrollBeginDrag: IScrollHandler | undefined;
|
|
336
|
+
onScrollEndDrag: IScrollHandler | undefined;
|
|
337
|
+
onMomentumScrollBegin: IScrollHandler | undefined;
|
|
338
|
+
onMomentumScrollEnd: IScrollHandler | undefined;
|
|
339
|
+
|
|
340
|
+
// The layout/content-size/accessibility events as real Angular events: `(layout)="…"`, not
|
|
341
|
+
// `[onLayout]="…"`. onScroll and the drag/momentum family stay plain @Input() callbacks above —
|
|
342
|
+
// they must also accept an Animated.event(...) native-driver marker (see the file header note),
|
|
343
|
+
// which no @Output() binding can carry.
|
|
344
|
+
@Output() readonly layout = new EventEmitter<ISymbioteEvent>();
|
|
345
|
+
@Output() readonly scrollToTop = new EventEmitter<ISymbioteEvent>();
|
|
346
|
+
@Output() readonly contentSizeChange = new EventEmitter<IContentSize>();
|
|
347
|
+
@Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
|
|
348
|
+
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
349
|
+
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
350
|
+
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
351
|
+
|
|
352
|
+
testID: string | undefined;
|
|
353
|
+
nativeID: string | undefined;
|
|
354
|
+
accessible: boolean | undefined;
|
|
355
|
+
accessibilityLabel: string | undefined;
|
|
356
|
+
accessibilityHint: string | undefined;
|
|
357
|
+
accessibilityRole: IAccessibilityProps['accessibilityRole'];
|
|
358
|
+
accessibilityState: IAccessibilityStateValue | undefined;
|
|
359
|
+
accessibilityValue: IAccessibilityProps['accessibilityValue'];
|
|
360
|
+
accessibilityActions: IAccessibilityProps['accessibilityActions'];
|
|
361
|
+
accessibilityLabelledBy: string | string[] | undefined;
|
|
362
|
+
importantForAccessibility: IAccessibilityProps['importantForAccessibility'];
|
|
363
|
+
accessibilityLiveRegion: IAccessibilityProps['accessibilityLiveRegion'];
|
|
364
|
+
screenReaderFocusable: boolean | undefined;
|
|
365
|
+
accessibilityViewIsModal: boolean | undefined;
|
|
366
|
+
accessibilityElementsHidden: boolean | undefined;
|
|
367
|
+
accessibilityIgnoresInvertColors: boolean | undefined;
|
|
368
|
+
accessibilityLanguage: string | undefined;
|
|
369
|
+
accessibilityRespondsToUserInteraction: boolean | undefined;
|
|
370
|
+
accessibilityShowsLargeContentViewer: boolean | undefined;
|
|
371
|
+
accessibilityLargeContentTitle: string | undefined;
|
|
372
|
+
role: IAriaProps['role'];
|
|
373
|
+
'aria-label': string | undefined;
|
|
374
|
+
'aria-labelledby': string | undefined;
|
|
375
|
+
'aria-live': IAriaProps['aria-live'];
|
|
376
|
+
'aria-hidden': boolean | undefined;
|
|
377
|
+
'aria-busy': boolean | undefined;
|
|
378
|
+
'aria-checked': boolean | 'mixed' | undefined;
|
|
379
|
+
'aria-disabled': boolean | undefined;
|
|
380
|
+
'aria-expanded': boolean | undefined;
|
|
381
|
+
'aria-selected': boolean | undefined;
|
|
382
|
+
'aria-modal': boolean | undefined;
|
|
383
|
+
'aria-valuemax': number | undefined;
|
|
384
|
+
'aria-valuemin': number | undefined;
|
|
385
|
+
'aria-valuenow': number | undefined;
|
|
386
|
+
'aria-valuetext': string | undefined;
|
|
387
|
+
|
|
388
|
+
// The host directive on the scroll-view intrinsic; `.node` is the committed SymbioteNode held by
|
|
389
|
+
// IDENTITY (Renderer2.createElement returned the engine node, ElementRef hands it back unwrapped).
|
|
390
|
+
// Inherited by the decorated subclass — Angular collects base-class @ViewChild queries.
|
|
391
|
+
@ViewChild('host', { read: SymbioteHostPropsDirective })
|
|
392
|
+
private hostDirective?: SymbioteHostPropsDirective;
|
|
393
|
+
|
|
394
|
+
@ContentChild(RefreshControl) protected projectedRefreshControl?: RefreshControl;
|
|
395
|
+
|
|
396
|
+
// A single AnimatedValue tracks the scroll offset and drives every sticky header's translateY
|
|
397
|
+
// (RN's _scrollAnimatedValue). A stable field (allocated once, held by identity — Angular does
|
|
398
|
+
// not proxy class fields, so no markRaw needed); the native attach below feeds it on the UI
|
|
399
|
+
// thread. Allocated unconditionally; inert until a sticky header consumes it.
|
|
400
|
+
protected readonly scrollAnimatedValue = new AnimatedValue(0);
|
|
401
|
+
|
|
402
|
+
// The last-seen content size, used to dedupe onContentSizeChange: RN fires the content onLayout
|
|
403
|
+
// on every layout pass; only real size changes emit (didContentSizeChange).
|
|
404
|
+
private lastContentSize: IContentSize | null = null;
|
|
405
|
+
// Inverted sticky headers stick to the BOTTOM, so they need the viewport height (RN _handleLayout).
|
|
406
|
+
private viewportHeight: number | undefined = undefined;
|
|
407
|
+
|
|
408
|
+
protected readonly projectionController = new ScrollViewProjectionController({
|
|
409
|
+
stickyHeaderIndices: undefined,
|
|
410
|
+
invertStickyHeaders: undefined,
|
|
411
|
+
scrollViewHeight: undefined,
|
|
412
|
+
scrollAnimatedValue: this.scrollAnimatedValue,
|
|
413
|
+
customStickyHeaderComponent: undefined,
|
|
414
|
+
excludeRefreshControl: false,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Native sticky-scroll attach (RN attachNativeEvent): when the native module is available the
|
|
418
|
+
// scroll value is driven on the UI thread so the interpolations ride scroll natively (no JS
|
|
419
|
+
// jitter). Through whenCommitted because under Angular's zoneless batched CD the host's Fabric
|
|
420
|
+
// tag does not exist yet at ngAfterViewInit time (the async-commit gotcha Vue documents); the
|
|
421
|
+
// bind runs now if committed, else after the commit that assigns the tag. Detached on destroy.
|
|
422
|
+
private detachStickyScroll: (() => void) | undefined;
|
|
423
|
+
private cancelStickyBind: (() => void) | undefined;
|
|
424
|
+
|
|
425
|
+
private lastLoggedIsHorizontal: boolean | undefined = undefined;
|
|
426
|
+
|
|
427
|
+
// Stable reference (bound once): inverted sticky headers need the viewport height captured off
|
|
428
|
+
// the scroll-view's own onLayout before forwarding to the user (RN _handleLayout). A getter that
|
|
429
|
+
// built this arrow function fresh on every `scrollProps` access would hand `[symbioteHostProps]`
|
|
430
|
+
// a new function reference every change-detection pass touching this component — jsonEqual
|
|
431
|
+
// (commit.ts) falls back to Object.is on a function leaf, so even a structurally-identical bag
|
|
432
|
+
// reads as "props changed" and forces a real Fabric re-clone that cascades up every ancestor
|
|
433
|
+
// (see AnimatedComponentBase.reconcile()'s identical warning).
|
|
434
|
+
private readonly handleInvertedStickyLayout = (event: ISymbioteEvent): void => {
|
|
435
|
+
const height = readLayoutDimension(event, 'height');
|
|
436
|
+
if (height !== undefined) this.viewportHeight = height;
|
|
437
|
+
this.layout.emit(event);
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// Same reasoning as handleInvertedStickyLayout: the content view's onLayout synthesizes
|
|
441
|
+
// onContentSizeChange (deduped) and must stay a stable reference across `contentProps` re-reads.
|
|
442
|
+
private readonly handleContentLayout = (event: ISymbioteEvent): void => {
|
|
443
|
+
const width = readLayoutDimension(event, 'width');
|
|
444
|
+
const height = readLayoutDimension(event, 'height');
|
|
445
|
+
if (width === undefined || height === undefined) return;
|
|
446
|
+
if (!didContentSizeChange(this.lastContentSize, { width, height })) return;
|
|
447
|
+
this.lastContentSize = { width, height };
|
|
448
|
+
dlog(`Angular ScrollView contentSizeChange ${width}x${height}`);
|
|
449
|
+
this.contentSizeChange.emit({ width, height });
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// Memoizes the wrapper each `emitterCallback` builds, keyed by the emitter it closes over
|
|
453
|
+
// (identity holds across ScrollViewBase's own @Output()s and a projected RefreshControl's,
|
|
454
|
+
// since a WeakMap key is just object identity). Without this, `scrollProps`/`refreshControlProps`
|
|
455
|
+
// handed a fresh `(event) => emitter.emit(event)` closure to the forwarded prop bag on every
|
|
456
|
+
// evaluation — the same jsonEqual/Object.is function-leaf problem as the arrow fields above, one
|
|
457
|
+
// level further removed. `.observed` is checked BEFORE the cache so an unbound event still
|
|
458
|
+
// resolves to `undefined` (the "nobody cares" contract `emitterCallback`'s own comment documents),
|
|
459
|
+
// not a stale cached wrapper from before the last subscriber unsubscribed.
|
|
460
|
+
private readonly emitterCallbackCache = new WeakMap<
|
|
461
|
+
EventEmitter<ISymbioteEvent>,
|
|
462
|
+
IScrollHandler
|
|
463
|
+
>();
|
|
464
|
+
|
|
465
|
+
// The sticky JS-path Animated.event handler depends on a REAL input (onScroll), so it can't be a
|
|
466
|
+
// plain stable field the way handleInvertedStickyLayout/handleContentLayout are — it must track
|
|
467
|
+
// `this.onScroll` genuinely changing, mirroring sticky-header.ts's own rebuildInterpolation cache
|
|
468
|
+
// (rebuild only when the tracked dependency's reference actually differs, not on every getter
|
|
469
|
+
// read). A fresh `animatedEvent(...)` call every `scrollProps` access would otherwise construct a
|
|
470
|
+
// new listener wrapper (and a new AnimatedEvent walking `collectMappedValues`) on every unrelated
|
|
471
|
+
// change-detection pass while sticky headers are active.
|
|
472
|
+
private cachedStickyOnScrollSource: IScrollHandler | undefined;
|
|
473
|
+
private cachedStickyOnScrollHandler: ((...args: readonly unknown[]) => void) | undefined;
|
|
474
|
+
|
|
475
|
+
get isHorizontal(): boolean {
|
|
476
|
+
const value = this.horizontal === true;
|
|
477
|
+
if (value !== this.lastLoggedIsHorizontal) {
|
|
478
|
+
this.lastLoggedIsHorizontal = value;
|
|
479
|
+
dlog(
|
|
480
|
+
`Angular ScrollView isHorizontal changed to ${value} (horizontal input=${this.horizontal})`,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
return value;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private get hasStickyHeaders(): boolean {
|
|
487
|
+
return this.stickyHeaderIndices !== undefined && this.stickyHeaderIndices.length > 0;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// A class-name string resolves through the shared registry before it reaches
|
|
491
|
+
// selectScrollIntrinsics, which only understands style objects/arrays.
|
|
492
|
+
private get resolvedContentContainerStyle(): IStyleProp<IViewStyle> | undefined {
|
|
493
|
+
return typeof this.contentContainerStyle === 'string'
|
|
494
|
+
? resolveClassName(this.contentContainerStyle)
|
|
495
|
+
: this.contentContainerStyle;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private get scrollViewBaseStyle(): IViewStyle {
|
|
499
|
+
return selectScrollIntrinsics(this.isHorizontal, this.resolvedContentContainerStyle)
|
|
500
|
+
.scrollViewBaseStyle;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// The forwarded host-prop bag for the scroll-view node: aria/role folded, the native pass-through
|
|
504
|
+
// families, the scroll-event callbacks, then the lifecycle-managed overrides (nestedScrollEnabled
|
|
505
|
+
// default ON, horizontal when defined, decelerationRate resolved per-platform, the sticky onScroll
|
|
506
|
+
// path). Base style UNDER user style so an explicit user value still wins. Mirrors Vue's outerProps
|
|
507
|
+
// + scrollProps assembly.
|
|
508
|
+
get scrollProps(): Record<string, unknown> {
|
|
509
|
+
const bag: Record<string, unknown> = compact({
|
|
510
|
+
...resolveAccessibilityProps(this.accessibilityInputs()),
|
|
511
|
+
scrollEnabled: this.scrollEnabled,
|
|
512
|
+
showsVerticalScrollIndicator: this.showsVerticalScrollIndicator,
|
|
513
|
+
showsHorizontalScrollIndicator: this.showsHorizontalScrollIndicator,
|
|
514
|
+
pagingEnabled: this.pagingEnabled,
|
|
515
|
+
bounces: this.bounces,
|
|
516
|
+
contentInset: this.contentInset,
|
|
517
|
+
contentOffset: this.contentOffset,
|
|
518
|
+
removeClippedSubviews: this.removeClippedSubviews,
|
|
519
|
+
snapToInterval: this.snapToInterval,
|
|
520
|
+
snapToOffsets: this.snapToOffsets,
|
|
521
|
+
snapToAlignment: this.snapToAlignment,
|
|
522
|
+
snapToStart: this.snapToStart,
|
|
523
|
+
snapToEnd: this.snapToEnd,
|
|
524
|
+
disableIntervalMomentum: this.disableIntervalMomentum,
|
|
525
|
+
keyboardDismissMode: this.keyboardDismissMode,
|
|
526
|
+
keyboardShouldPersistTaps: this.keyboardShouldPersistTaps,
|
|
527
|
+
maintainVisibleContentPosition: this.maintainVisibleContentPosition,
|
|
528
|
+
alwaysBounceHorizontal: this.alwaysBounceHorizontal,
|
|
529
|
+
alwaysBounceVertical: this.alwaysBounceVertical,
|
|
530
|
+
centerContent: this.centerContent,
|
|
531
|
+
scrollIndicatorInsets: this.scrollIndicatorInsets,
|
|
532
|
+
indicatorStyle: this.indicatorStyle,
|
|
533
|
+
directionalLockEnabled: this.directionalLockEnabled,
|
|
534
|
+
automaticallyAdjustKeyboardInsets: this.automaticallyAdjustKeyboardInsets,
|
|
535
|
+
contentInsetAdjustmentBehavior: this.contentInsetAdjustmentBehavior,
|
|
536
|
+
minimumZoomScale: this.minimumZoomScale,
|
|
537
|
+
maximumZoomScale: this.maximumZoomScale,
|
|
538
|
+
zoomScale: this.zoomScale,
|
|
539
|
+
bouncesZoom: this.bouncesZoom,
|
|
540
|
+
pinchGestureEnabled: this.pinchGestureEnabled,
|
|
541
|
+
overScrollMode: this.overScrollMode,
|
|
542
|
+
fadingEdgeLength: this.fadingEdgeLength,
|
|
543
|
+
persistentScrollbar: this.persistentScrollbar,
|
|
544
|
+
endFillColor: this.endFillColor,
|
|
545
|
+
onScrollBeginDrag: this.onScrollBeginDrag,
|
|
546
|
+
onScrollEndDrag: this.onScrollEndDrag,
|
|
547
|
+
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
548
|
+
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
549
|
+
onScrollToTop: this.emitterCallback(this.scrollToTop),
|
|
550
|
+
onAccessibilityAction: this.emitterCallback(this.accessibilityAction),
|
|
551
|
+
onAccessibilityTap: this.emitterCallback(this.accessibilityTap),
|
|
552
|
+
onMagicTap: this.emitterCallback(this.magicTap),
|
|
553
|
+
onAccessibilityEscape: this.emitterCallback(this.accessibilityEscape),
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// RN defaults nested scrolling ON (ScrollView.js `nestedScrollEnabled ?? true`); Android needs
|
|
557
|
+
// it to scroll a nested scrollable independently, iOS handles nesting natively (a no-op there).
|
|
558
|
+
bag.nestedScrollEnabled = this.nestedScrollEnabled ?? true;
|
|
559
|
+
// iOS needs `horizontal` to flip RCTScrollView's axis; Android's dedicated manager ignores it.
|
|
560
|
+
if (this.horizontal !== undefined) bag.horizontal = this.horizontal;
|
|
561
|
+
if (this.decelerationRate !== undefined) {
|
|
562
|
+
bag.decelerationRate = resolveDecelerationRate(this.decelerationRate);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// onScroll: when sticky headers are active, the offset must reach the AnimatedValue (RN
|
|
566
|
+
// _scrollAnimatedValueAttachment). With the native module, the value is driven on the UI thread
|
|
567
|
+
// by the post-commit attach below, so onScroll forwards to the user with throttle 1
|
|
568
|
+
// (ScrollView.js:1798). Without it, Animated.event drives the value each frame (the JS jitter)
|
|
569
|
+
// and forwards the user handler as the listener passthrough.
|
|
570
|
+
// The scroll-forwarding DECISIONS (which onScroll path, the 1/16 throttle defaults, whether to
|
|
571
|
+
// capture the viewport height) are folded out to the shared resolveScrollForwarding; here Angular
|
|
572
|
+
// only EXECUTES them, keeping its stable-reference handlers (stickyOnScrollHandler /
|
|
573
|
+
// handleInvertedStickyLayout / emitterCallback) so a fresh closure never forces a re-clone cascade.
|
|
574
|
+
const nativeStickyAvailable = this.hasStickyHeaders && isNativeAnimatedAvailable();
|
|
575
|
+
const forwarding = resolveScrollForwarding({
|
|
576
|
+
hasStickyHeaders: this.hasStickyHeaders,
|
|
577
|
+
nativeStickyAvailable,
|
|
578
|
+
invertStickyHeaders: this.invertStickyHeaders,
|
|
579
|
+
scrollEventThrottle: this.scrollEventThrottle,
|
|
580
|
+
maintainVisibleContentPosition: this.maintainVisibleContentPosition,
|
|
581
|
+
snapToAlignment: this.snapToAlignment,
|
|
582
|
+
});
|
|
583
|
+
// onScroll: the JS-fallback path uses the cached Animated.event handler; the native + plain paths
|
|
584
|
+
// forward the user handler as-is (the native driver attaches the value on the UI thread).
|
|
585
|
+
if (forwarding.mode === 'sticky-js') {
|
|
586
|
+
bag.onScroll = this.stickyOnScrollHandler(this.onScroll);
|
|
587
|
+
} else if (this.onScroll !== undefined) {
|
|
588
|
+
bag.onScroll = this.onScroll;
|
|
589
|
+
}
|
|
590
|
+
if (forwarding.scrollEventThrottle !== undefined) {
|
|
591
|
+
bag.scrollEventThrottle = forwarding.scrollEventThrottle;
|
|
592
|
+
}
|
|
593
|
+
// Inverted sticky headers need the viewport height (RN _handleLayout): capture it on the
|
|
594
|
+
// scroll-view onLayout, then call the user's handler. Otherwise forward the layout emitter.
|
|
595
|
+
if (forwarding.capturesViewportHeight) {
|
|
596
|
+
bag.onLayout = this.handleInvertedStickyLayout;
|
|
597
|
+
} else {
|
|
598
|
+
const layoutCallback = this.emitterCallback(this.layout);
|
|
599
|
+
if (layoutCallback !== undefined) bag.onLayout = layoutCallback;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
dlog(
|
|
603
|
+
`Angular ScrollView -> ${this.isHorizontal ? 'horizontal' : 'vertical'} (sticky=${this.hasStickyHeaders})`,
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
bag.style = [this.scrollViewBaseStyle, this.style];
|
|
607
|
+
this.updateProjectionController();
|
|
608
|
+
return bag;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Overridable hook, NOT `this.style` directly: `splitLayoutProps` below decides which layout
|
|
612
|
+
// properties (flex/height/gap/…) go on the Android outer refresh-control wrapper vs. the inner
|
|
613
|
+
// scroll view. `this.style` alone only ever carries the explicit `[style]` @Input — a composed
|
|
614
|
+
// component's OWN anchor host (see anchorHostStyle's doc comment in primitives/shared.ts) can
|
|
615
|
+
// hold ADDITIONAL class-derived layout style that's otherwise invisible to this split, so its
|
|
616
|
+
// wrapper never gets its needed layout (collapses to zero size — the Android outer wrapper
|
|
617
|
+
// shows literally nothing on screen). This mirrors the Vue adapter's identical
|
|
618
|
+
// `layoutSplitStyle` field and the real Android device bug it fixes; the Angular Android
|
|
619
|
+
// ScrollView (index.android.ts) overrides this to merge in `anchorHostStyle`. Default here is
|
|
620
|
+
// `this.style` alone, preserving current behavior for anything that doesn't override it.
|
|
621
|
+
protected get layoutSplitStyle(): IStyleProp<IViewStyle> {
|
|
622
|
+
return this.style;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
get androidWrappedScrollProps(): Record<string, unknown> {
|
|
626
|
+
const props = { ...this.scrollProps };
|
|
627
|
+
const { inner, outer } = splitLayoutProps(this.layoutSplitStyle);
|
|
628
|
+
dlog(
|
|
629
|
+
`Angular ScrollView splitProbe layoutSplitStyle=${JSON.stringify(this.layoutSplitStyle)} ` +
|
|
630
|
+
`outer=${JSON.stringify(outer)} inner=${JSON.stringify(inner)}`,
|
|
631
|
+
);
|
|
632
|
+
props.style = [this.scrollViewBaseStyle, inner];
|
|
633
|
+
props.nestedScrollEnabled = true;
|
|
634
|
+
return props;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
get iosRefreshControlProps(): Record<string, unknown> {
|
|
638
|
+
return this.refreshControlProps();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
get androidRefreshControlProps(): Record<string, unknown> {
|
|
642
|
+
const { outer } = splitLayoutProps(this.layoutSplitStyle);
|
|
643
|
+
dlog(
|
|
644
|
+
`Angular ScrollView refreshControlProbe outer=${JSON.stringify(outer)} ` +
|
|
645
|
+
`hasRefresh=${this.projectedRefreshControl !== undefined}`,
|
|
646
|
+
);
|
|
647
|
+
return this.refreshControlProps(outer);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
protected handleProjectedRefresh(nativeNode: unknown): void {
|
|
651
|
+
this.projectedRefreshControl?.handleRefresh(nativeNode);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
private refreshControlProps(style?: IStyleProp<IViewStyle>): Record<string, unknown> {
|
|
655
|
+
const refresh = this.projectedRefreshControl;
|
|
656
|
+
if (refresh === undefined) return {};
|
|
657
|
+
return compact({
|
|
658
|
+
...refresh.folded,
|
|
659
|
+
refreshing: refresh.refreshing,
|
|
660
|
+
tintColor: refresh.tintColor,
|
|
661
|
+
title: refresh.title,
|
|
662
|
+
titleColor: refresh.titleColor,
|
|
663
|
+
progressViewOffset: refresh.progressViewOffset,
|
|
664
|
+
colors: refresh.colors,
|
|
665
|
+
progressBackgroundColor: refresh.progressBackgroundColor,
|
|
666
|
+
size: refresh.size,
|
|
667
|
+
enabled: refresh.enabled,
|
|
668
|
+
style,
|
|
669
|
+
testID: refresh.testID,
|
|
670
|
+
nativeID: refresh.nativeID,
|
|
671
|
+
accessible: refresh.accessible,
|
|
672
|
+
onAccessibilityAction: this.emitterCallback(refresh.accessibilityAction),
|
|
673
|
+
onAccessibilityTap: this.emitterCallback(refresh.accessibilityTap),
|
|
674
|
+
onMagicTap: this.emitterCallback(refresh.magicTap),
|
|
675
|
+
onAccessibilityEscape: this.emitterCallback(refresh.accessibilityEscape),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
get hasProjectedRefreshControl(): boolean {
|
|
680
|
+
return this.projectedRefreshControl !== undefined;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// The content (inner) view's prop bag. `collapsable: false` keeps the layout-only content view a
|
|
684
|
+
// real native view (Android Fabric view-flattens it away otherwise, hoisting cells as direct
|
|
685
|
+
// children of a scroll view that hosts exactly one — an addViewAt crash). collapsableChildren
|
|
686
|
+
// false preserves the cell views maintainVisibleContentPosition/snapToAlignment anchor against.
|
|
687
|
+
// onLayout synthesizes onContentSizeChange (deduped). iOS never flattens; both are no-ops there.
|
|
688
|
+
get contentProps(): Record<string, unknown> {
|
|
689
|
+
const { contentStyle } = selectScrollIntrinsics(
|
|
690
|
+
this.isHorizontal,
|
|
691
|
+
this.resolvedContentContainerStyle,
|
|
692
|
+
);
|
|
693
|
+
const bag: Record<string, unknown> = { style: contentStyle, collapsable: false };
|
|
694
|
+
if (this.maintainVisibleContentPosition !== undefined || this.snapToAlignment !== undefined) {
|
|
695
|
+
bag.collapsableChildren = false;
|
|
696
|
+
}
|
|
697
|
+
bag.onLayout = this.handleContentLayout;
|
|
698
|
+
return bag;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Wraps an @Output() as a plain callback only while it has a subscriber, so an unbound event
|
|
702
|
+
// still leaves the forwarded prop bag key absent — the same "undefined means nobody cares"
|
|
703
|
+
// contract the old @Input() callbacks had. Mirrors Pressable's emitterHandler. The wrapper itself
|
|
704
|
+
// is memoized per emitter (emitterCallbackCache) rather than rebuilt on every call: see that
|
|
705
|
+
// field's comment for why an unstable closure here breaks the forwarded prop bag.
|
|
706
|
+
private emitterCallback(emitter: EventEmitter<ISymbioteEvent>): IScrollHandler | undefined {
|
|
707
|
+
if (!emitter.observed) return undefined;
|
|
708
|
+
let cached = this.emitterCallbackCache.get(emitter);
|
|
709
|
+
if (cached === undefined) {
|
|
710
|
+
cached = (event: ISymbioteEvent) => emitter.emit(event);
|
|
711
|
+
this.emitterCallbackCache.set(emitter, cached);
|
|
712
|
+
}
|
|
713
|
+
return cached;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Rebuilds the JS-path Animated.event handler only when the tracked `onScroll` input actually
|
|
717
|
+
// changes reference (see cachedStickyOnScrollHandler's comment).
|
|
718
|
+
private stickyOnScrollHandler(
|
|
719
|
+
userOnScroll: IScrollHandler | undefined,
|
|
720
|
+
): (...args: readonly unknown[]) => void {
|
|
721
|
+
if (
|
|
722
|
+
this.cachedStickyOnScrollHandler === undefined ||
|
|
723
|
+
this.cachedStickyOnScrollSource !== userOnScroll
|
|
724
|
+
) {
|
|
725
|
+
this.cachedStickyOnScrollSource = userOnScroll;
|
|
726
|
+
this.cachedStickyOnScrollHandler = animatedEvent(
|
|
727
|
+
[{ nativeEvent: { contentOffset: { y: this.scrollAnimatedValue } } }],
|
|
728
|
+
userOnScroll === undefined
|
|
729
|
+
? undefined
|
|
730
|
+
: { listener: (...args: readonly unknown[]) => forwardScrollEvent(userOnScroll, args) },
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
return this.cachedStickyOnScrollHandler;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// The committed scroll-view node held by IDENTITY (the directive's ElementRef). null until the
|
|
737
|
+
// element commits; the imperative handle and the sticky attach both read it through this getter.
|
|
738
|
+
private get hostNode(): ISymbioteNode | null {
|
|
739
|
+
const native = this.hostDirective?.node;
|
|
740
|
+
return isSymbioteNode(native) ? native : null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// The imperative API a parent reaches via @ViewChild(ScrollView). buildScrollViewHandle is the
|
|
744
|
+
// shared, proven handle (React/Vue use it verbatim); it reads the node through the LAZY getter on
|
|
745
|
+
// every call, so a command before commit no-ops rather than freezing a null node.
|
|
746
|
+
private readonly handle: IScrollViewHandle = buildScrollViewHandle(() => this.hostNode);
|
|
747
|
+
|
|
748
|
+
scrollTo(options?: { x?: number; y?: number; animated?: boolean }): void {
|
|
749
|
+
this.handle.scrollTo(options);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
scrollToEnd(options?: { animated?: boolean }): void {
|
|
753
|
+
this.handle.scrollToEnd(options);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
flashScrollIndicators(): void {
|
|
757
|
+
this.handle.flashScrollIndicators();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
getScrollNode(): ISymbioteNode | null {
|
|
761
|
+
return this.handle.getScrollNode();
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
ngAfterViewInit(): void {
|
|
765
|
+
this.updateProjectionController();
|
|
766
|
+
this.attachSticky();
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Wire the native sticky-scroll attach after the view exists. Re-attaches if the node identity
|
|
770
|
+
// changes; no-op when sticky is off or the native module is absent.
|
|
771
|
+
private attachSticky(): void {
|
|
772
|
+
this.cancelStickyBind?.();
|
|
773
|
+
this.cancelStickyBind = undefined;
|
|
774
|
+
if (this.detachStickyScroll !== undefined) {
|
|
775
|
+
this.detachStickyScroll();
|
|
776
|
+
this.detachStickyScroll = undefined;
|
|
777
|
+
}
|
|
778
|
+
if (!this.hasStickyHeaders || !isNativeAnimatedAvailable()) return;
|
|
779
|
+
const node = this.hostNode;
|
|
780
|
+
if (node === null) return;
|
|
781
|
+
this.cancelStickyBind = whenCommitted(node, () => {
|
|
782
|
+
this.detachStickyScroll = attachStickyScroll(node, this.scrollAnimatedValue);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
ngOnDestroy(): void {
|
|
787
|
+
this.cancelStickyBind?.();
|
|
788
|
+
this.cancelStickyBind = undefined;
|
|
789
|
+
if (this.detachStickyScroll !== undefined) this.detachStickyScroll();
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
private updateProjectionController(): void {
|
|
793
|
+
this.projectionController.update({
|
|
794
|
+
stickyHeaderIndices: this.stickyHeaderIndices,
|
|
795
|
+
invertStickyHeaders: this.invertStickyHeaders,
|
|
796
|
+
scrollViewHeight: this.viewportHeight,
|
|
797
|
+
scrollAnimatedValue: this.scrollAnimatedValue,
|
|
798
|
+
customStickyHeaderComponent: this.StickyHeaderComponent,
|
|
799
|
+
excludeRefreshControl: this.hasProjectedRefreshControl,
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Typed as the a11y intersection WITH the string index so resolveAccessibilityProps's result
|
|
804
|
+
// stays assignable into the forwarded bag (a genuine narrowing, built at that type — no cast).
|
|
805
|
+
private accessibilityInputs(): IAccessibilityProps & IAriaProps & Record<string, unknown> {
|
|
806
|
+
return {
|
|
807
|
+
testID: this.testID,
|
|
808
|
+
nativeID: this.nativeID,
|
|
809
|
+
accessible: this.accessible,
|
|
810
|
+
accessibilityLabel: this.accessibilityLabel,
|
|
811
|
+
accessibilityHint: this.accessibilityHint,
|
|
812
|
+
accessibilityRole: this.accessibilityRole,
|
|
813
|
+
accessibilityState: this.accessibilityState,
|
|
814
|
+
accessibilityValue: this.accessibilityValue,
|
|
815
|
+
accessibilityActions: this.accessibilityActions,
|
|
816
|
+
accessibilityLabelledBy: this.accessibilityLabelledBy,
|
|
817
|
+
importantForAccessibility: this.importantForAccessibility,
|
|
818
|
+
accessibilityLiveRegion: this.accessibilityLiveRegion,
|
|
819
|
+
screenReaderFocusable: this.screenReaderFocusable,
|
|
820
|
+
accessibilityViewIsModal: this.accessibilityViewIsModal,
|
|
821
|
+
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
822
|
+
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
823
|
+
accessibilityLanguage: this.accessibilityLanguage,
|
|
824
|
+
accessibilityRespondsToUserInteraction: this.accessibilityRespondsToUserInteraction,
|
|
825
|
+
accessibilityShowsLargeContentViewer: this.accessibilityShowsLargeContentViewer,
|
|
826
|
+
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
827
|
+
role: this.role,
|
|
828
|
+
'aria-label': this['aria-label'],
|
|
829
|
+
'aria-labelledby': this['aria-labelledby'],
|
|
830
|
+
'aria-live': this['aria-live'],
|
|
831
|
+
'aria-hidden': this['aria-hidden'],
|
|
832
|
+
'aria-busy': this['aria-busy'],
|
|
833
|
+
'aria-checked': this['aria-checked'],
|
|
834
|
+
'aria-disabled': this['aria-disabled'],
|
|
835
|
+
'aria-expanded': this['aria-expanded'],
|
|
836
|
+
'aria-selected': this['aria-selected'],
|
|
837
|
+
'aria-modal': this['aria-modal'],
|
|
838
|
+
'aria-valuemax': this['aria-valuemax'],
|
|
839
|
+
'aria-valuemin': this['aria-valuemin'],
|
|
840
|
+
'aria-valuenow': this['aria-valuenow'],
|
|
841
|
+
'aria-valuetext': this['aria-valuetext'],
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|