@symbiote-native/angular 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/symbiote-angular-dev.cjs +20 -19
- package/build/angular/anchor-host-registry.js +2 -2
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/bootstrap.js +1 -1
- package/build/angular/bootstrap.js.map +1 -1
- package/build/angular/components/activity-indicator/index.android.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.android.js +12 -4
- package/build/angular/components/activity-indicator/index.android.js.map +1 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.ios.js +8 -3
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -1
- package/build/angular/components/activity-indicator/shared.d.ts +1 -1
- package/build/angular/components/activity-indicator/shared.js +4 -4
- package/build/angular/components/activity-indicator/shared.js.map +1 -1
- package/build/angular/components/button.d.ts +4 -2
- package/build/angular/components/button.js +41 -22
- package/build/angular/components/button.js.map +1 -1
- package/build/angular/components/flat-list/index.d.ts +6 -4
- package/build/angular/components/flat-list/index.js +80 -50
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image/index.android.d.ts +8 -3
- package/build/angular/components/image/index.android.js +42 -10
- package/build/angular/components/image/index.android.js.map +1 -1
- package/build/angular/components/image/index.ios.d.ts +8 -3
- package/build/angular/components/image/index.ios.js +42 -10
- package/build/angular/components/image/index.ios.js.map +1 -1
- package/build/angular/components/image/shared.d.ts +4 -1
- package/build/angular/components/image/shared.js +51 -8
- package/build/angular/components/image/shared.js.map +1 -1
- package/build/angular/components/image-background.d.ts +2 -1
- package/build/angular/components/image-background.js +26 -10
- package/build/angular/components/image-background.js.map +1 -1
- package/build/angular/components/input-accessory-view/index.d.ts +9 -4
- package/build/angular/components/input-accessory-view/index.js +49 -10
- package/build/angular/components/input-accessory-view/index.js.map +1 -1
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +3 -1
- package/build/angular/components/keyboard-avoiding-view/index.js +44 -12
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +11 -8
- package/build/angular/components/modal/index.js +51 -29
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable/index.d.ts +12 -6
- package/build/angular/components/pressable/index.js +66 -13
- package/build/angular/components/pressable/index.js.map +1 -1
- package/build/angular/components/refresh-control/index.d.ts +8 -4
- package/build/angular/components/refresh-control/index.js +54 -16
- package/build/angular/components/refresh-control/index.js.map +1 -1
- package/build/angular/components/safe-area-view/index.d.ts +9 -4
- package/build/angular/components/safe-area-view/index.js +48 -9
- package/build/angular/components/safe-area-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/index.android.d.ts +2 -3
- package/build/angular/components/scroll-view/index.android.js +88 -61
- package/build/angular/components/scroll-view/index.android.js.map +1 -1
- package/build/angular/components/scroll-view/index.ios.d.ts +2 -3
- package/build/angular/components/scroll-view/index.ios.js +24 -18
- package/build/angular/components/scroll-view/index.ios.js.map +1 -1
- package/build/angular/components/scroll-view/projection.d.ts +35 -0
- package/build/angular/components/scroll-view/projection.js +279 -53
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.d.ts +14 -5
- package/build/angular/components/scroll-view/shared.js +225 -75
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.js +21 -22
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/section-list/index.d.ts +7 -1
- package/build/angular/components/section-list/index.js +91 -54
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/switch/index.android.d.ts +7 -3
- package/build/angular/components/switch/index.android.js +52 -10
- package/build/angular/components/switch/index.android.js.map +1 -1
- package/build/angular/components/switch/index.ios.d.ts +7 -3
- package/build/angular/components/switch/index.ios.js +52 -10
- package/build/angular/components/switch/index.ios.js.map +1 -1
- package/build/angular/components/switch/shared.d.ts +13 -4
- package/build/angular/components/switch/shared.js +71 -7
- package/build/angular/components/switch/shared.js.map +1 -1
- package/build/angular/components/text-input.d.ts +11 -3
- package/build/angular/components/text-input.js +68 -8
- package/build/angular/components/text-input.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +40 -9
- package/build/angular/components/touchable/index.js +303 -67
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +6 -3
- package/build/angular/components/touchable-native-feedback/index.js +63 -27
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
- package/build/angular/components/virtualized-list/directives.js +22 -18
- package/build/angular/components/virtualized-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +15 -4
- package/build/angular/components/virtualized-list/index.js +326 -94
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/directives.js +12 -12
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +19 -1
- package/build/angular/components/virtualized-section-list/index.js +93 -22
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components.d.ts +2 -2
- package/build/angular/components.js +5 -0
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.js +34 -36
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js +19 -22
- package/build/angular/create-tunnel/index.js.map +1 -1
- package/build/angular/descriptor-to-angular/index.js +6 -4
- package/build/angular/descriptor-to-angular/index.js.map +1 -1
- package/build/angular/diagnostics.d.ts +33 -0
- package/build/angular/diagnostics.js +82 -0
- package/build/angular/diagnostics.js.map +1 -0
- package/build/angular/host-instance/index.js +1 -1
- package/build/angular/host-instance/index.js.map +1 -1
- package/build/angular/index.d.ts +9 -4
- package/build/angular/index.js +17 -3
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +1 -7
- package/build/angular/modules/animated/animated-leaf-binder.js +22 -72
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +11 -6
- package/build/angular/modules/animated/create-animated-component.js +106 -55
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/status-bar/index.js +4 -4
- package/build/angular/modules/status-bar/index.js.map +1 -1
- package/build/angular/primitives/index.d.ts +20 -3
- package/build/angular/primitives/index.js +84 -48
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +48 -2
- package/build/angular/primitives/shared.js +158 -11
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/render/index.js +69 -38
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.d.ts +1 -0
- package/build/angular/renderer/index.js +37 -2
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/services/color-scheme.service.js +3 -3
- package/build/angular/services/color-scheme.service.js.map +1 -1
- package/build/angular/services/window-dimensions.service.js +4 -4
- package/build/angular/services/window-dimensions.service.js.map +1 -1
- package/package.json +10 -8
- package/src/anchor-host-registry.ts +2 -2
- package/src/bootstrap.ts +4 -1
- package/src/components/activity-indicator/index.android.ts +8 -1
- package/src/components/activity-indicator/index.ios.ts +4 -0
- package/src/components/activity-indicator/shared.ts +32 -13
- package/src/components/button.ts +46 -19
- package/src/components/flat-list/index.ts +122 -62
- package/src/components/image/index.android.ts +58 -7
- package/src/components/image/index.ios.ts +58 -7
- package/src/components/image/shared.ts +92 -21
- package/src/components/image-background.ts +29 -9
- package/src/components/input-accessory-view/index.ts +67 -12
- package/src/components/keyboard-avoiding-view/index.ts +61 -15
- package/src/components/modal/index.ts +72 -33
- package/src/components/pressable/index.ts +117 -28
- package/src/components/refresh-control/index.ts +74 -18
- package/src/components/safe-area-view/index.ts +64 -10
- package/src/components/scroll-view/index.android.ts +62 -47
- package/src/components/scroll-view/index.ios.ts +19 -17
- package/src/components/scroll-view/projection.ts +363 -64
- package/src/components/scroll-view/shared.ts +312 -90
- package/src/components/scroll-view/sticky-header.ts +36 -24
- package/src/components/section-list/index.ts +86 -53
- package/src/components/switch/index.android.ts +60 -8
- package/src/components/switch/index.ios.ts +60 -8
- package/src/components/switch/shared.ts +93 -6
- package/src/components/text-input.ts +95 -13
- package/src/components/touchable/index.ts +348 -64
- package/src/components/touchable-native-feedback/index.ts +94 -31
- package/src/components/virtualized-list/directives.ts +19 -5
- package/src/components/virtualized-list/index.ts +380 -85
- package/src/components/virtualized-section-list/directives.ts +6 -3
- package/src/components/virtualized-section-list/index.ts +146 -33
- package/src/components.ts +9 -1
- package/src/create-portal/index.ts +31 -31
- package/src/create-tunnel/index.ts +16 -17
- package/src/descriptor-to-angular/index.ts +29 -8
- package/src/diagnostics.ts +138 -0
- package/src/host-instance/index.ts +9 -3
- package/src/index.ts +61 -3
- package/src/modules/animated/animated-leaf-binder.ts +28 -77
- package/src/modules/animated/create-animated-component.ts +115 -47
- package/src/modules/app-registry/index.ts +9 -9
- package/src/modules/status-bar/index.ts +13 -4
- package/src/primitives/index.ts +47 -2
- package/src/primitives/shared.ts +170 -10
- package/src/render/index.ts +77 -39
- package/src/renderer/index.ts +76 -14
- package/src/services/color-scheme.service.ts +3 -1
- package/src/services/window-dimensions.service.ts +11 -4
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +0 -14
- package/build/angular/modules/animated/animated-leaf-binder/index.js +0 -91
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +0 -1
- package/src/modules/animated/animated-leaf-binder/index.ts +0 -101
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
// Sticky headers:
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// interpolation math (computeStickyInterpolation) lives framework-agnostic in @symbiote-native/components
|
|
9
|
-
// this file holds the Angular component shell, the layout state, and the interpolation
|
|
10
|
-
// build, sharing the math verbatim with React/Vue. Angular supplies only the lifecycle (inputs +
|
|
11
|
-
// manual change detection instead of useState/useEffect or refs/watch).
|
|
1
|
+
// Sticky headers: RN does stickiness PURELY IN JS (ScrollView.js / ScrollViewStickyHeader.js).
|
|
2
|
+
// A single `_scrollAnimatedValue` an Animated.event drives from `onScroll` feeds each flagged
|
|
3
|
+
// header's translateY through an interpolation that pins it to the top (or bottom, inverted)
|
|
4
|
+
// until the next header collides with it — the native Fabric scroll view does NOT honor
|
|
5
|
+
// `stickyHeaderIndices` on its own. The interpolation math (computeStickyInterpolation) lives
|
|
6
|
+
// framework-agnostic in @symbiote-native/components, shared verbatim with React/Vue; this file
|
|
7
|
+
// holds the Angular component shell, layout state, and interpolation build.
|
|
12
8
|
//
|
|
13
9
|
// This is the public sticky-header WRAPPER for explicit composition. ScrollView also auto-wraps
|
|
14
|
-
// projected
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// custom component classes.
|
|
10
|
+
// projected children named by stickyHeaderIndices through its projection bridge (projection.ts),
|
|
11
|
+
// because Angular templates cannot map <ng-content> children directly; that auto path
|
|
12
|
+
// intentionally reuses this built-in wrapper rather than instantiating arbitrary component types.
|
|
18
13
|
|
|
19
14
|
import {
|
|
20
15
|
ChangeDetectionStrategy,
|
|
@@ -101,7 +96,9 @@ export class ScrollViewStickyHeader
|
|
|
101
96
|
// The animated node driving the transform (RN's animatedTranslateY), replaced by the
|
|
102
97
|
// rebuild-interpolation effect. When the scroll value is native, this interpolation runs on the UI
|
|
103
98
|
// thread: the smooth pin.
|
|
104
|
-
private animatedTranslateY: AnimatedInterpolation = new AnimatedValue(
|
|
99
|
+
private animatedTranslateY: AnimatedInterpolation = new AnimatedValue(
|
|
100
|
+
0,
|
|
101
|
+
).interpolate({
|
|
105
102
|
inputRange: [-1, 0],
|
|
106
103
|
outputRange: [0, 0],
|
|
107
104
|
});
|
|
@@ -140,13 +137,17 @@ export class ScrollViewStickyHeader
|
|
|
140
137
|
// anchor style first so the fixed transform/zIndex below still wins on conflict.
|
|
141
138
|
return [
|
|
142
139
|
anchorHostStyle(this.elementRef),
|
|
143
|
-
{
|
|
140
|
+
{
|
|
141
|
+
transform: [{ translateY: this.animatedTranslateY }],
|
|
142
|
+
zIndex: STICKY_HEADER_Z_INDEX,
|
|
143
|
+
},
|
|
144
144
|
];
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
// Stable reference (bound once) so AnimatedView's directive does not re-wire the layout listener
|
|
148
148
|
// every change-detection pass.
|
|
149
|
-
private readonly onHostLayout = (event: ISymbioteEvent): void =>
|
|
149
|
+
private readonly onHostLayout = (event: ISymbioteEvent): void =>
|
|
150
|
+
this.handleLayout(event);
|
|
150
151
|
|
|
151
152
|
// Also a stable reference, not a getter: jsonEqual (commit.ts) can't structurally compare the
|
|
152
153
|
// embedded onLayout function, so a fresh object here would read as "props changed" on every
|
|
@@ -161,7 +162,9 @@ export class ScrollViewStickyHeader
|
|
|
161
162
|
|
|
162
163
|
// The EXPLICIT debounced translateY overrides the committed transform for hit-testing while
|
|
163
164
|
// animatedTranslateY does the smooth (native-driven) pin (RN ScrollViewStickyHeader.js).
|
|
164
|
-
get passthrough(): {
|
|
165
|
+
get passthrough(): {
|
|
166
|
+
style: { transform: Array<{ translateY: number }> };
|
|
167
|
+
} | null {
|
|
165
168
|
return this.state.translateY !== null
|
|
166
169
|
? { style: { transform: [{ translateY: this.state.translateY }] } }
|
|
167
170
|
: null;
|
|
@@ -186,7 +189,10 @@ export class ScrollViewStickyHeader
|
|
|
186
189
|
case 'rebuild-interpolation': {
|
|
187
190
|
// Detach the old listener, build a fresh interpolation onto the shared scroll value, and
|
|
188
191
|
// wire the settled-value listener (symbiote is always Fabric; RN attaches it only there).
|
|
189
|
-
if (
|
|
192
|
+
if (
|
|
193
|
+
this.interpolation !== undefined &&
|
|
194
|
+
this.listenerId !== undefined
|
|
195
|
+
) {
|
|
190
196
|
this.interpolation.removeListener(this.listenerId);
|
|
191
197
|
this.listenerId = undefined;
|
|
192
198
|
}
|
|
@@ -198,14 +204,15 @@ export class ScrollViewStickyHeader
|
|
|
198
204
|
this.interpolation = next;
|
|
199
205
|
this.animatedTranslateY = next;
|
|
200
206
|
dlog(
|
|
201
|
-
`
|
|
207
|
+
`STICKY[header] interpolation measured=${this.state.measured} y=${this.state.layoutY}`,
|
|
202
208
|
);
|
|
203
209
|
break;
|
|
204
210
|
}
|
|
205
211
|
case 'schedule-debounce':
|
|
206
212
|
// The animated value updates several times per frame; debounce the settled value into the
|
|
207
213
|
// committed transform so hit detection stays current (a Fabric issue, worse on Android).
|
|
208
|
-
if (this.debounceTimer !== undefined)
|
|
214
|
+
if (this.debounceTimer !== undefined)
|
|
215
|
+
clearTimeout(this.debounceTimer);
|
|
209
216
|
this.debounceTimer = setTimeout(() => {
|
|
210
217
|
this.debounceTimer = undefined;
|
|
211
218
|
this.dispatch({ kind: 'debounce-fired', value: effect.value });
|
|
@@ -238,8 +245,13 @@ export class ScrollViewStickyHeader
|
|
|
238
245
|
this.changeDetector.markForCheck();
|
|
239
246
|
}
|
|
240
247
|
|
|
241
|
-
private readonly animatedValueListener = ({
|
|
242
|
-
|
|
248
|
+
private readonly animatedValueListener = ({
|
|
249
|
+
value,
|
|
250
|
+
}: {
|
|
251
|
+
value: number | string;
|
|
252
|
+
}): void => {
|
|
253
|
+
if (typeof value === 'number')
|
|
254
|
+
this.dispatch({ kind: 'animated-tick', value });
|
|
243
255
|
};
|
|
244
256
|
|
|
245
257
|
private teardown(): void {
|
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
// SectionList, the Angular public list-of-sections component. A thin convenience surface over
|
|
2
2
|
// VirtualizedSectionList, mirroring RN's layering (SectionList -> VirtualizedSectionList ->
|
|
3
3
|
// VirtualizedList). All section-flattening / windowing / sticky-header / imperative-scroll logic
|
|
4
|
-
// lives below; this layer re-exposes the same surface under the SectionList name and
|
|
5
|
-
// handle
|
|
6
|
-
// adapters/vue/src/components/section-list/index.ts.
|
|
4
|
+
// lives below; this layer re-exposes the same surface under the SectionList name and delegates
|
|
5
|
+
// the handle to the inner VirtualizedSectionList.
|
|
7
6
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// each prop unchanged (undefined included) and the default lands one layer down; double-applying
|
|
14
|
-
// here would fight VSL. Vue spreads $attrs to do this in one line — Angular has no attrs-spread, so
|
|
15
|
-
// every prop is forwarded as an explicit @Input binding, except the seven list-lifecycle events
|
|
7
|
+
// SectionList is a PURE FORWARDER: its public prop surface IS VirtualizedSectionList's (RN layers
|
|
8
|
+
// them one-for-one), and the SectionList-flavour defaults (stickySectionHeadersEnabled per
|
|
9
|
+
// Platform.OS, the section entry keyExtractor) already live INSIDE VirtualizedSectionList — double-
|
|
10
|
+
// applying them here would fight it. Angular has no attrs-spread (unlike Vue's $attrs), so every
|
|
11
|
+
// prop is forwarded as an explicit @Input binding, except the seven list-lifecycle events
|
|
16
12
|
// (endReached/startReached/refresh/accessibilityAction/accessibilityTap/magicTap/
|
|
17
13
|
// accessibilityEscape), which are real @Output() EventEmitters re-emitted via a listener binding.
|
|
18
14
|
//
|
|
19
|
-
// TEMPLATE FORWARDING — RE-STAMP, the same pattern FlatList's single-column branch
|
|
20
|
-
//
|
|
21
|
-
// VirtualizedSectionList's own @ContentChild resolve directives across
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// templateRef + context through VListOutletDirective, exactly mirroring how VirtualizedSectionList
|
|
30
|
-
// itself re-stamps its own captured directives onto its inner VirtualizedList.
|
|
15
|
+
// TEMPLATE FORWARDING — RE-STAMP, the same pattern FlatList's single-column branch uses (see
|
|
16
|
+
// flat-list/index.ts). A bare `<ng-content></ng-content>` passthrough does NOT let
|
|
17
|
+
// VirtualizedSectionList's own @ContentChild resolve directives across a SECOND projection hop —
|
|
18
|
+
// Angular's content queries resolve only against what was projected directly onto the querying
|
|
19
|
+
// component's OWN tag. So SectionList captures the app's `<ng-template vSectionItem>` /
|
|
20
|
+
// vSectionHeader / vSectionFooter / vSectionSeparator / vListHeader / vListFooter / vListEmpty /
|
|
21
|
+
// vListSeparator with its OWN @ContentChild (a single, direct hop — always resolves) and
|
|
22
|
+
// re-authors equivalent `<ng-template>`s on `<VirtualizedSectionList>`, forwarding the captured
|
|
23
|
+
// templateRef + context through VListOutletDirective — mirroring how VirtualizedSectionList itself
|
|
24
|
+
// re-stamps its own captured directives onto its inner VirtualizedList.
|
|
31
25
|
|
|
32
26
|
import {
|
|
33
27
|
ChangeDetectionStrategy,
|
|
@@ -62,7 +56,10 @@ import {
|
|
|
62
56
|
type IVListSeparatorContext,
|
|
63
57
|
} from '../virtualized-list';
|
|
64
58
|
import { VListOutletDirective } from '../virtualized-list/directives';
|
|
65
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
stableAnchorStyle,
|
|
61
|
+
SymbioteStyleInputDirective,
|
|
62
|
+
} from '../../primitives';
|
|
66
63
|
import {
|
|
67
64
|
VSectionFooterDirective,
|
|
68
65
|
VSectionHeaderDirective,
|
|
@@ -126,6 +123,9 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
126
123
|
@Component({
|
|
127
124
|
selector: 'SectionList',
|
|
128
125
|
standalone: true,
|
|
126
|
+
hostDirectives: [
|
|
127
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
128
|
+
],
|
|
129
129
|
imports: [
|
|
130
130
|
VirtualizedSectionList,
|
|
131
131
|
VSectionItemDirective,
|
|
@@ -143,6 +143,7 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
143
143
|
<VirtualizedSectionList
|
|
144
144
|
[sections]="sections"
|
|
145
145
|
[keyExtractor]="keyExtractor"
|
|
146
|
+
[getItemLayout]="getItemLayout"
|
|
146
147
|
[stickySectionHeadersEnabled]="stickySectionHeadersEnabled"
|
|
147
148
|
[extraData]="extraData"
|
|
148
149
|
(endReached)="endReached.emit($event)"
|
|
@@ -187,8 +188,12 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
187
188
|
[accessibilityElementsHidden]="accessibilityElementsHidden"
|
|
188
189
|
[accessibilityIgnoresInvertColors]="accessibilityIgnoresInvertColors"
|
|
189
190
|
[accessibilityLanguage]="accessibilityLanguage"
|
|
190
|
-
[accessibilityRespondsToUserInteraction]="
|
|
191
|
-
|
|
191
|
+
[accessibilityRespondsToUserInteraction]="
|
|
192
|
+
accessibilityRespondsToUserInteraction
|
|
193
|
+
"
|
|
194
|
+
[accessibilityShowsLargeContentViewer]="
|
|
195
|
+
accessibilityShowsLargeContentViewer
|
|
196
|
+
"
|
|
192
197
|
[accessibilityLargeContentTitle]="accessibilityLargeContentTitle"
|
|
193
198
|
(accessibilityAction)="accessibilityAction.emit($event)"
|
|
194
199
|
(accessibilityTap)="accessibilityTap.emit($event)"
|
|
@@ -219,7 +224,13 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
219
224
|
>
|
|
220
225
|
<ng-container
|
|
221
226
|
[vListOutlet]="sectionItemDir?.templateRef"
|
|
222
|
-
[vListOutletContext]="{
|
|
227
|
+
[vListOutletContext]="{
|
|
228
|
+
$implicit: item,
|
|
229
|
+
item,
|
|
230
|
+
index,
|
|
231
|
+
section,
|
|
232
|
+
separators,
|
|
233
|
+
}"
|
|
223
234
|
></ng-container>
|
|
224
235
|
</ng-template>
|
|
225
236
|
@if (sectionHeaderDir !== undefined) {
|
|
@@ -240,17 +251,23 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
240
251
|
}
|
|
241
252
|
@if (sectionSeparatorDir !== undefined) {
|
|
242
253
|
<ng-template vSectionSeparator>
|
|
243
|
-
<ng-container
|
|
254
|
+
<ng-container
|
|
255
|
+
[vListOutlet]="sectionSeparatorDir.templateRef"
|
|
256
|
+
></ng-container>
|
|
244
257
|
</ng-template>
|
|
245
258
|
}
|
|
246
259
|
@if (listHeaderDir !== undefined) {
|
|
247
260
|
<ng-template vListHeader>
|
|
248
|
-
<ng-container
|
|
261
|
+
<ng-container
|
|
262
|
+
[vListOutlet]="listHeaderDir.templateRef"
|
|
263
|
+
></ng-container>
|
|
249
264
|
</ng-template>
|
|
250
265
|
}
|
|
251
266
|
@if (listFooterDir !== undefined) {
|
|
252
267
|
<ng-template vListFooter>
|
|
253
|
-
<ng-container
|
|
268
|
+
<ng-container
|
|
269
|
+
[vListOutlet]="listFooterDir.templateRef"
|
|
270
|
+
></ng-container>
|
|
254
271
|
</ng-template>
|
|
255
272
|
}
|
|
256
273
|
@if (listEmptyDir !== undefined) {
|
|
@@ -267,7 +284,9 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
267
284
|
>
|
|
268
285
|
<ng-container
|
|
269
286
|
[vListOutlet]="itemSeparatorDir.templateRef"
|
|
270
|
-
[vListOutletContext]="
|
|
287
|
+
[vListOutletContext]="
|
|
288
|
+
itemSeparatorContext(highlighted, leadingItem, trailingItem)
|
|
289
|
+
"
|
|
271
290
|
></ng-container>
|
|
272
291
|
</ng-template>
|
|
273
292
|
}
|
|
@@ -279,11 +298,21 @@ export class SectionList<ItemT = unknown>
|
|
|
279
298
|
{
|
|
280
299
|
@Input({ required: true }) sections!: ReadonlyArray<ISection<ItemT>>;
|
|
281
300
|
@Input() keyExtractor?: (item: ItemT, index: number) => string;
|
|
301
|
+
// Passed through FLAT (sections array + flat entry index); VirtualizedSectionList owns the
|
|
302
|
+
// sections-vs-entries wrapper - see its entryItemLayout getter.
|
|
303
|
+
@Input() getItemLayout?: (
|
|
304
|
+
data: ReadonlyArray<ISection<ItemT>> | null,
|
|
305
|
+
index: number,
|
|
306
|
+
) => { length: number; offset: number; index: number };
|
|
282
307
|
@Input() stickySectionHeadersEnabled?: boolean;
|
|
283
308
|
@Input() extraData?: unknown;
|
|
284
|
-
@Output() readonly endReached = new EventEmitter<{
|
|
309
|
+
@Output() readonly endReached = new EventEmitter<{
|
|
310
|
+
distanceFromEnd: number;
|
|
311
|
+
}>();
|
|
285
312
|
@Input() onEndReachedThreshold?: number;
|
|
286
|
-
@Output() readonly startReached = new EventEmitter<{
|
|
313
|
+
@Output() readonly startReached = new EventEmitter<{
|
|
314
|
+
distanceFromStart: number;
|
|
315
|
+
}>();
|
|
287
316
|
@Input() onStartReachedThreshold?: number;
|
|
288
317
|
@Output() readonly refresh = new EventEmitter<void>();
|
|
289
318
|
@Input() refreshing?: boolean | null;
|
|
@@ -311,18 +340,14 @@ export class SectionList<ItemT = unknown>
|
|
|
311
340
|
@Input() testID?: string;
|
|
312
341
|
@Input() nativeID?: string;
|
|
313
342
|
|
|
314
|
-
// Bound to
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
// style` — defeats VirtualizedList's `ngDoCheck` dedup gate and free-runs change detection
|
|
323
|
-
// forever (the exact bug `stableAnchorStyle` exists to prevent, see its doc comment and the
|
|
324
|
-
// `flat-list-array-style.test.ts` regression it fixes). `cachedResolvedStyle` is the getter's own
|
|
325
|
-
// persisted "previous" value across reads, since a getter has no natural field to compare against.
|
|
343
|
+
// Bound to `[style]="resolvedStyle"`, which Angular compiles to ɵɵstyleMap — it only
|
|
344
|
+
// understands a flat object, never an array (RN's `style={[a, b]}` idiom crashes deep inside
|
|
345
|
+
// Angular's styling engine), so this flattens `style` first, merging in this component's OWN
|
|
346
|
+
// anchor's class-derived style via anchorHostStyle (`elementRef` here is SectionList's own
|
|
347
|
+
// host, not `list`'s inner VirtualizedSectionList). A plain `flattenStyle([...])` would
|
|
348
|
+
// allocate a fresh object on every CD check, defeating VirtualizedList's `ngDoCheck` dedup gate
|
|
349
|
+
// and free-running change detection forever — the bug `stableAnchorStyle` exists to prevent
|
|
350
|
+
// (see its doc comment). `cachedResolvedStyle` is the getter's own persisted "previous" value.
|
|
326
351
|
private cachedResolvedStyle: Record<string, unknown> | undefined;
|
|
327
352
|
get resolvedStyle(): IViewStyle {
|
|
328
353
|
this.cachedResolvedStyle = stableAnchorStyle(
|
|
@@ -340,8 +365,10 @@ export class SectionList<ItemT = unknown>
|
|
|
340
365
|
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
341
366
|
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
342
367
|
@Input() accessibilityLabelledBy?: string | string[];
|
|
343
|
-
@Input()
|
|
344
|
-
|
|
368
|
+
@Input()
|
|
369
|
+
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
370
|
+
@Input()
|
|
371
|
+
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
345
372
|
@Input() screenReaderFocusable?: boolean;
|
|
346
373
|
@Input() accessibilityViewIsModal?: boolean;
|
|
347
374
|
@Input() accessibilityElementsHidden?: boolean;
|
|
@@ -372,18 +399,24 @@ export class SectionList<ItemT = unknown>
|
|
|
372
399
|
|
|
373
400
|
// The app's section/list authoring templates, captured from the app's DIRECT projected content
|
|
374
401
|
// (single hop — always resolves) and re-stamped onto the inner VirtualizedSectionList above.
|
|
375
|
-
@ContentChild(VSectionItemDirective)
|
|
376
|
-
|
|
377
|
-
@ContentChild(
|
|
378
|
-
|
|
402
|
+
@ContentChild(VSectionItemDirective)
|
|
403
|
+
sectionItemDir?: VSectionItemDirective<ItemT>;
|
|
404
|
+
@ContentChild(VSectionHeaderDirective)
|
|
405
|
+
sectionHeaderDir?: VSectionHeaderDirective<ItemT>;
|
|
406
|
+
@ContentChild(VSectionFooterDirective)
|
|
407
|
+
sectionFooterDir?: VSectionFooterDirective<ItemT>;
|
|
408
|
+
@ContentChild(VSectionSeparatorDirective)
|
|
409
|
+
sectionSeparatorDir?: VSectionSeparatorDirective;
|
|
379
410
|
@ContentChild(VListHeaderDirective) listHeaderDir?: VListHeaderDirective;
|
|
380
411
|
@ContentChild(VListFooterDirective) listFooterDir?: VListFooterDirective;
|
|
381
412
|
@ContentChild(VListEmptyDirective) listEmptyDir?: VListEmptyDirective;
|
|
382
|
-
@ContentChild(VListSeparatorDirective)
|
|
413
|
+
@ContentChild(VListSeparatorDirective)
|
|
414
|
+
itemSeparatorDir?: VListSeparatorDirective<ItemT>;
|
|
383
415
|
|
|
384
416
|
// The composed inner VirtualizedSectionList. Its instance IS an IVirtualizedSectionListHandle, so
|
|
385
417
|
// SectionList's handle delegates straight to it. Available from ngAfterViewInit; reads lazily.
|
|
386
|
-
@ViewChild(VirtualizedSectionList)
|
|
418
|
+
@ViewChild(VirtualizedSectionList)
|
|
419
|
+
private list?: VirtualizedSectionList<ItemT>;
|
|
387
420
|
|
|
388
421
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
389
422
|
// onto (see anchorHostStyle's doc comment) — NOT `list` above, which targets the real inner
|
|
@@ -1,13 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
CUSTOM_ELEMENTS_SCHEMA,
|
|
3
|
+
Component,
|
|
4
|
+
ElementRef,
|
|
5
|
+
forwardRef,
|
|
6
|
+
inject,
|
|
7
|
+
signal,
|
|
8
|
+
type DoCheck,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
11
|
+
import {
|
|
12
|
+
anchorHostStyle,
|
|
13
|
+
SwitchHost,
|
|
14
|
+
SymbioteHostPropsDirective,
|
|
15
|
+
SymbioteStyleInputDirective,
|
|
16
|
+
} from '../../primitives';
|
|
3
17
|
import { SwitchBase } from './shared';
|
|
4
18
|
export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
5
19
|
|
|
6
20
|
@Component({
|
|
7
21
|
selector: 'Switch',
|
|
8
22
|
standalone: true,
|
|
23
|
+
hostDirectives: [
|
|
24
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
25
|
+
],
|
|
9
26
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
10
27
|
imports: [SwitchHost, SymbioteHostPropsDirective],
|
|
28
|
+
providers: [
|
|
29
|
+
{
|
|
30
|
+
provide: NG_VALUE_ACCESSOR,
|
|
31
|
+
useExisting: forwardRef(() => Switch),
|
|
32
|
+
multi: true,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
11
35
|
inputs: [
|
|
12
36
|
'value',
|
|
13
37
|
'disabled',
|
|
@@ -32,30 +56,58 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
|
32
56
|
template: `
|
|
33
57
|
<symbiote-switch
|
|
34
58
|
#nativeSwitch="symbioteHost"
|
|
35
|
-
[symbioteHostProps]="hostProps"
|
|
59
|
+
[symbioteHostProps]="hostProps()"
|
|
36
60
|
(change)="handleChange($event, nativeSwitch)"
|
|
37
61
|
/>
|
|
38
62
|
`,
|
|
39
63
|
})
|
|
40
|
-
export class Switch extends SwitchBase {
|
|
64
|
+
export class Switch extends SwitchBase implements DoCheck {
|
|
41
65
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
42
66
|
// onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch> leaf one level down.
|
|
43
67
|
private readonly elementRef = inject(ElementRef);
|
|
44
68
|
|
|
45
69
|
protected readonly platform = {
|
|
46
70
|
snapBackCommand: 'setNativeValue',
|
|
47
|
-
trackColorProps: (
|
|
71
|
+
trackColorProps: (
|
|
72
|
+
value: boolean,
|
|
73
|
+
trackColor?: { false?: string; true?: string },
|
|
74
|
+
) => ({
|
|
48
75
|
trackColorForFalse: trackColor?.false,
|
|
49
76
|
trackColorForTrue: trackColor?.true,
|
|
50
77
|
trackTintColor: value ? trackColor?.true : trackColor?.false,
|
|
51
78
|
}),
|
|
52
79
|
};
|
|
53
80
|
|
|
81
|
+
// The anchor's class-derived style is written by the renderer's addClass/removeClass and never
|
|
82
|
+
// reaches Angular's input pipeline (a bare `class="x"` never appears in SimpleChanges — see
|
|
83
|
+
// anchorHostStyle's doc comment), so SwitchBase's inputsRevision cannot see it. Hence its own
|
|
84
|
+
// signal: a plain field read inside a computed() would be untracked and the bag would go stale
|
|
85
|
+
// the moment a `[class.x]`/`[ngClass]` toggled.
|
|
86
|
+
//
|
|
87
|
+
// ngDoCheck is the right cadence: render3's callHooks flushes a node's pre-order hooks at the
|
|
88
|
+
// next `ɵɵadvance` past it (or in refreshView's post-template flush), i.e. AFTER the declaring
|
|
89
|
+
// template's ɵɵclassProp wrote the class onto this element and BEFORE this component's own view
|
|
90
|
+
// refreshes — the exact window a class poll needs. callHooks also runs under
|
|
91
|
+
// setActiveConsumer(null), so the write registers no dependency on the caller's view (no NG0600).
|
|
92
|
+
// commitClassStyle allocates a fresh style array only when a token actually moved, so signal.set's
|
|
93
|
+
// own Object.is check makes an unchanged poll a no-op that dirties nothing.
|
|
94
|
+
//
|
|
95
|
+
// Known gap, shared with every other component polling this way (Pressable, SafeAreaView,
|
|
96
|
+
// TouchableNativeFeedback): `[ngClass]` applies its tokens from NgClass's OWN ngDoCheck, and a
|
|
97
|
+
// node's hooks flush in registration order with the component's first, so an [ngClass] flip lands
|
|
98
|
+
// one pass after this poll reads it. `class=` and `[class.x]` are written by the declaring
|
|
99
|
+
// template's styling instructions and so are always current here.
|
|
100
|
+
private readonly anchorStyle = signal<unknown>(undefined);
|
|
101
|
+
|
|
102
|
+
ngDoCheck(): void {
|
|
103
|
+
this.anchorStyle.set(anchorHostStyle(this.elementRef));
|
|
104
|
+
}
|
|
105
|
+
|
|
54
106
|
// The anchor's class-derived style goes first, then whatever SwitchBase already resolved
|
|
55
107
|
// (its own explicit `style` @Input plus the ios_backgroundColor fold), so an explicit [style]
|
|
56
108
|
// still beats the ambient class.
|
|
57
|
-
override
|
|
58
|
-
const props = super.
|
|
59
|
-
return { ...props, style: [
|
|
109
|
+
protected override buildHostProps(): Record<string, unknown> {
|
|
110
|
+
const props = super.buildHostProps();
|
|
111
|
+
return { ...props, style: [this.anchorStyle(), props['style']] };
|
|
60
112
|
}
|
|
61
113
|
}
|
|
@@ -1,13 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
CUSTOM_ELEMENTS_SCHEMA,
|
|
3
|
+
Component,
|
|
4
|
+
ElementRef,
|
|
5
|
+
forwardRef,
|
|
6
|
+
inject,
|
|
7
|
+
signal,
|
|
8
|
+
type DoCheck,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
11
|
+
import {
|
|
12
|
+
anchorHostStyle,
|
|
13
|
+
SwitchHost,
|
|
14
|
+
SymbioteHostPropsDirective,
|
|
15
|
+
SymbioteStyleInputDirective,
|
|
16
|
+
} from '../../primitives';
|
|
3
17
|
import { SwitchBase } from './shared';
|
|
4
18
|
export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
5
19
|
|
|
6
20
|
@Component({
|
|
7
21
|
selector: 'Switch',
|
|
8
22
|
standalone: true,
|
|
23
|
+
hostDirectives: [
|
|
24
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
25
|
+
],
|
|
9
26
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
10
27
|
imports: [SwitchHost, SymbioteHostPropsDirective],
|
|
28
|
+
providers: [
|
|
29
|
+
{
|
|
30
|
+
provide: NG_VALUE_ACCESSOR,
|
|
31
|
+
useExisting: forwardRef(() => Switch),
|
|
32
|
+
multi: true,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
11
35
|
inputs: [
|
|
12
36
|
'value',
|
|
13
37
|
'disabled',
|
|
@@ -32,29 +56,57 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
|
32
56
|
template: `
|
|
33
57
|
<symbiote-switch
|
|
34
58
|
#nativeSwitch="symbioteHost"
|
|
35
|
-
[symbioteHostProps]="hostProps"
|
|
59
|
+
[symbioteHostProps]="hostProps()"
|
|
36
60
|
(change)="handleChange($event, nativeSwitch)"
|
|
37
61
|
/>
|
|
38
62
|
`,
|
|
39
63
|
})
|
|
40
|
-
export class Switch extends SwitchBase {
|
|
64
|
+
export class Switch extends SwitchBase implements DoCheck {
|
|
41
65
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
42
66
|
// onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch> leaf one level down.
|
|
43
67
|
private readonly elementRef = inject(ElementRef);
|
|
44
68
|
|
|
45
69
|
protected readonly platform = {
|
|
46
70
|
snapBackCommand: 'setValue',
|
|
47
|
-
trackColorProps: (
|
|
71
|
+
trackColorProps: (
|
|
72
|
+
_value: boolean,
|
|
73
|
+
trackColor?: { false?: string; true?: string },
|
|
74
|
+
) => ({
|
|
48
75
|
onTintColor: trackColor?.true,
|
|
49
76
|
tintColor: trackColor?.false,
|
|
50
77
|
}),
|
|
51
78
|
};
|
|
52
79
|
|
|
80
|
+
// The anchor's class-derived style is written by the renderer's addClass/removeClass and never
|
|
81
|
+
// reaches Angular's input pipeline (a bare `class="x"` never appears in SimpleChanges — see
|
|
82
|
+
// anchorHostStyle's doc comment), so SwitchBase's inputsRevision cannot see it. Hence its own
|
|
83
|
+
// signal: a plain field read inside a computed() would be untracked and the bag would go stale
|
|
84
|
+
// the moment a `[class.x]`/`[ngClass]` toggled.
|
|
85
|
+
//
|
|
86
|
+
// ngDoCheck is the right cadence: render3's callHooks flushes a node's pre-order hooks at the
|
|
87
|
+
// next `ɵɵadvance` past it (or in refreshView's post-template flush), i.e. AFTER the declaring
|
|
88
|
+
// template's ɵɵclassProp wrote the class onto this element and BEFORE this component's own view
|
|
89
|
+
// refreshes — the exact window a class poll needs. callHooks also runs under
|
|
90
|
+
// setActiveConsumer(null), so the write registers no dependency on the caller's view (no NG0600).
|
|
91
|
+
// commitClassStyle allocates a fresh style array only when a token actually moved, so signal.set's
|
|
92
|
+
// own Object.is check makes an unchanged poll a no-op that dirties nothing.
|
|
93
|
+
//
|
|
94
|
+
// Known gap, shared with every other component polling this way (Pressable, SafeAreaView,
|
|
95
|
+
// TouchableNativeFeedback): `[ngClass]` applies its tokens from NgClass's OWN ngDoCheck, and a
|
|
96
|
+
// node's hooks flush in registration order with the component's first, so an [ngClass] flip lands
|
|
97
|
+
// one pass after this poll reads it. `class=` and `[class.x]` are written by the declaring
|
|
98
|
+
// template's styling instructions and so are always current here.
|
|
99
|
+
private readonly anchorStyle = signal<unknown>(undefined);
|
|
100
|
+
|
|
101
|
+
ngDoCheck(): void {
|
|
102
|
+
this.anchorStyle.set(anchorHostStyle(this.elementRef));
|
|
103
|
+
}
|
|
104
|
+
|
|
53
105
|
// The anchor's class-derived style goes first, then whatever SwitchBase already resolved
|
|
54
106
|
// (its own explicit `style` @Input plus the ios_backgroundColor fold), so an explicit [style]
|
|
55
107
|
// still beats the ambient class.
|
|
56
|
-
override
|
|
57
|
-
const props = super.
|
|
58
|
-
return { ...props, style: [
|
|
108
|
+
protected override buildHostProps(): Record<string, unknown> {
|
|
109
|
+
const props = super.buildHostProps();
|
|
110
|
+
return { ...props, style: [this.anchorStyle(), props['style']] };
|
|
59
111
|
}
|
|
60
112
|
}
|