@symbiote-native/angular 0.7.0 → 1.0.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/babel-lower-host-primitives.cjs +636 -0
- package/babel-register-composed.cjs +21 -3
- package/build/angular/anchor-host-registry.js +3 -1
- 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.js +7 -2
- package/build/angular/components/activity-indicator/index.android.js.map +1 -1
- package/build/angular/components/activity-indicator/index.ios.js +3 -1
- 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 +1 -1
- package/build/angular/components/activity-indicator/shared.js.map +1 -1
- package/build/angular/components/button.d.ts +3 -0
- package/build/angular/components/button.js +22 -5
- package/build/angular/components/button.js.map +1 -1
- package/build/angular/components/flat-list/index.d.ts +1 -1
- package/build/angular/components/flat-list/index.js +47 -14
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image/index.android.js +4 -10
- package/build/angular/components/image/index.android.js.map +1 -1
- package/build/angular/components/image/index.ios.js +4 -10
- package/build/angular/components/image/index.ios.js.map +1 -1
- package/build/angular/components/image/shared.d.ts +4 -4
- package/build/angular/components/image/shared.js +51 -33
- package/build/angular/components/image/shared.js.map +1 -1
- package/build/angular/components/image-background.d.ts +3 -0
- package/build/angular/components/image-background.js +32 -9
- package/build/angular/components/image-background.js.map +1 -1
- package/build/angular/components/input-accessory-view/index.d.ts +2 -1
- package/build/angular/components/input-accessory-view/index.js +18 -16
- 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 +56 -19
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +1 -0
- package/build/angular/components/modal/index.js +21 -10
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable/index.d.ts +10 -5
- package/build/angular/components/pressable/index.js +61 -16
- package/build/angular/components/pressable/index.js.map +1 -1
- package/build/angular/components/refresh-control/index.d.ts +2 -1
- package/build/angular/components/refresh-control/index.js +13 -9
- package/build/angular/components/refresh-control/index.js.map +1 -1
- package/build/angular/components/safe-area-view/index.d.ts +3 -1
- package/build/angular/components/safe-area-view/index.js +22 -20
- package/build/angular/components/safe-area-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/index.android.js +44 -14
- package/build/angular/components/scroll-view/index.android.js.map +1 -1
- package/build/angular/components/scroll-view/index.ios.js +13 -5
- package/build/angular/components/scroll-view/index.ios.js.map +1 -1
- package/build/angular/components/scroll-view/projection.d.ts +32 -0
- package/build/angular/components/scroll-view/projection.js +212 -47
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.d.ts +2 -0
- package/build/angular/components/scroll-view/shared.js +48 -11
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.js +7 -3
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/section-list/index.d.ts +9 -1
- package/build/angular/components/section-list/index.js +72 -18
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/switch/index.android.js +23 -9
- package/build/angular/components/switch/index.android.js.map +1 -1
- package/build/angular/components/switch/index.ios.js +23 -9
- package/build/angular/components/switch/index.ios.js.map +1 -1
- package/build/angular/components/switch/shared.d.ts +1 -1
- package/build/angular/components/switch/shared.js +5 -2
- package/build/angular/components/switch/shared.js.map +1 -1
- package/build/angular/components/text-input.d.ts +8 -2
- package/build/angular/components/text-input.js +79 -34
- package/build/angular/components/text-input.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +36 -7
- package/build/angular/components/touchable/index.js +280 -72
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +2 -1
- package/build/angular/components/touchable-native-feedback/index.js +19 -17
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
- package/build/angular/components/virtualized-list/directives.js +4 -0
- package/build/angular/components/virtualized-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +8 -2
- package/build/angular/components/virtualized-list/index.js +207 -61
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +21 -1
- package/build/angular/components/virtualized-section-list/index.js +100 -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 +1 -1
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.d.ts +11 -2
- package/build/angular/create-portal/index.js +42 -12
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js.map +1 -1
- package/build/angular/descriptor-to-angular/index.js +3 -1
- 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/gate-demand.d.ts +37 -0
- package/build/angular/gate-demand.js +84 -0
- package/build/angular/gate-demand.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 +6 -2
- package/build/angular/index.js +16 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +1 -0
- package/build/angular/modules/animated/create-animated-component.js +41 -18
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/app-registry/index.js.map +1 -1
- package/build/angular/modules/status-bar/index.js +1 -1
- package/build/angular/modules/status-bar/index.js.map +1 -1
- package/build/angular/primitives/index.d.ts +31 -2
- package/build/angular/primitives/index.js +81 -3
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +9 -1
- package/build/angular/primitives/shared.js +49 -3
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/register.d.ts +1 -0
- package/build/angular/register.js +35 -0
- package/build/angular/register.js.map +1 -0
- package/build/angular/render/index.js +51 -6
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.d.ts +1 -0
- package/build/angular/renderer/index.js +157 -21
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/services/color-scheme.service.js.map +1 -1
- package/build/angular/services/window-dimensions.service.js +1 -1
- package/build/angular/services/window-dimensions.service.js.map +1 -1
- package/build/angular/state-style.d.ts +9 -0
- package/build/angular/state-style.js +39 -0
- package/build/angular/state-style.js.map +1 -0
- package/metro-transformer.cjs +69 -0
- package/package.json +27 -8
- package/src/anchor-host-registry.ts +3 -1
- package/src/bootstrap.ts +4 -1
- package/src/components/activity-indicator/index.android.ts +7 -2
- package/src/components/activity-indicator/index.ios.ts +3 -1
- package/src/components/activity-indicator/shared.ts +32 -13
- package/src/components/button.ts +36 -6
- package/src/components/flat-list/index.ts +91 -29
- package/src/components/image/index.android.ts +9 -6
- package/src/components/image/index.ios.ts +9 -6
- package/src/components/image/shared.ts +110 -45
- package/src/components/image-background.ts +47 -10
- package/src/components/input-accessory-view/index.ts +41 -18
- package/src/components/keyboard-avoiding-view/index.ts +76 -22
- package/src/components/modal/index.ts +39 -12
- package/src/components/pressable/index.ts +121 -31
- package/src/components/refresh-control/index.ts +36 -13
- package/src/components/safe-area-view/index.ts +44 -19
- package/src/components/scroll-view/index.android.ts +31 -9
- package/src/components/scroll-view/index.ios.ts +17 -4
- package/src/components/scroll-view/projection.ts +290 -63
- package/src/components/scroll-view/shared.ts +124 -30
- package/src/components/scroll-view/sticky-header.ts +25 -8
- package/src/components/section-list/index.ts +79 -19
- package/src/components/switch/index.android.ts +18 -7
- package/src/components/switch/index.ios.ts +18 -7
- package/src/components/switch/shared.ts +15 -4
- package/src/components/text-input.ts +119 -31
- package/src/components/touchable/index.ts +339 -72
- package/src/components/touchable-native-feedback/index.ts +50 -18
- package/src/components/virtualized-list/directives.ts +19 -5
- package/src/components/virtualized-list/index.ts +292 -72
- package/src/components/virtualized-section-list/directives.ts +6 -3
- package/src/components/virtualized-section-list/index.ts +164 -34
- package/src/components.ts +6 -1
- package/src/create-portal/index.ts +47 -11
- package/src/create-tunnel/index.ts +3 -1
- package/src/descriptor-to-angular/index.ts +29 -8
- package/src/diagnostics.ts +138 -0
- package/src/gate-demand.ts +109 -0
- package/src/host-instance/index.ts +9 -3
- package/src/index.ts +33 -2
- package/src/modules/animated/create-animated-component.ts +67 -19
- package/src/modules/app-registry/index.ts +3 -1
- package/src/modules/status-bar/index.ts +13 -4
- package/src/primitives/index.ts +73 -2
- package/src/primitives/shared.ts +70 -9
- package/src/register.ts +43 -0
- package/src/render/index.ts +59 -7
- package/src/renderer/index.ts +209 -37
- package/src/services/color-scheme.service.ts +3 -1
- package/src/services/window-dimensions.service.ts +11 -4
- package/src/state-style.ts +39 -0
|
@@ -63,6 +63,11 @@ import {
|
|
|
63
63
|
type IViewStyle,
|
|
64
64
|
} from '@symbiote-native/engine';
|
|
65
65
|
import { anchorHostStyle, SymbioteHostPropsDirective } from '../../primitives';
|
|
66
|
+
import {
|
|
67
|
+
gateWanted,
|
|
68
|
+
injectGateDemand,
|
|
69
|
+
type IGatedAccessibilityEvent,
|
|
70
|
+
} from '../../gate-demand';
|
|
66
71
|
import { RefreshControl } from '../refresh-control';
|
|
67
72
|
import { ScrollViewProjectionController } from './projection';
|
|
68
73
|
|
|
@@ -164,7 +169,8 @@ export const SCROLL_VIEW_INPUTS = [
|
|
|
164
169
|
// (ReactNode children, ReactElement refreshControl) — Angular takes children via <ng-content> and
|
|
165
170
|
// composes RefreshControl through projection instead. Declared per-adapter over the shared
|
|
166
171
|
// accessibility base since a framework element/ref field can't live in a shared agnostic type.
|
|
167
|
-
export interface IAngularScrollViewProps
|
|
172
|
+
export interface IAngularScrollViewProps
|
|
173
|
+
extends IAccessibilityProps, IAriaProps {
|
|
168
174
|
style?: IStyleProp<IViewStyle>;
|
|
169
175
|
// A bare string resolves through the shared style registry, like `class` on the host node.
|
|
170
176
|
contentContainerStyle?: IStyleProp<IViewStyle> | string;
|
|
@@ -176,7 +182,12 @@ export interface IAngularScrollViewProps extends IAccessibilityProps, IAriaProps
|
|
|
176
182
|
bounces?: boolean;
|
|
177
183
|
decelerationRate?: 'normal' | 'fast' | number;
|
|
178
184
|
scrollEventThrottle?: number;
|
|
179
|
-
contentInset?: {
|
|
185
|
+
contentInset?: {
|
|
186
|
+
top?: number;
|
|
187
|
+
left?: number;
|
|
188
|
+
bottom?: number;
|
|
189
|
+
right?: number;
|
|
190
|
+
};
|
|
180
191
|
contentOffset?: { x: number; y: number };
|
|
181
192
|
removeClippedSubviews?: boolean;
|
|
182
193
|
snapToInterval?: number;
|
|
@@ -204,11 +215,17 @@ export interface IAngularScrollViewProps extends IAccessibilityProps, IAriaProps
|
|
|
204
215
|
alwaysBounceHorizontal?: boolean;
|
|
205
216
|
alwaysBounceVertical?: boolean;
|
|
206
217
|
centerContent?: boolean;
|
|
207
|
-
scrollIndicatorInsets?: {
|
|
218
|
+
scrollIndicatorInsets?: {
|
|
219
|
+
top?: number;
|
|
220
|
+
left?: number;
|
|
221
|
+
bottom?: number;
|
|
222
|
+
right?: number;
|
|
223
|
+
};
|
|
208
224
|
indicatorStyle?: 'default' | 'black' | 'white';
|
|
209
225
|
directionalLockEnabled?: boolean;
|
|
210
226
|
automaticallyAdjustKeyboardInsets?: boolean;
|
|
211
|
-
contentInsetAdjustmentBehavior?:
|
|
227
|
+
contentInsetAdjustmentBehavior?:
|
|
228
|
+
'automatic' | 'scrollableAxes' | 'never' | 'always';
|
|
212
229
|
minimumZoomScale?: number;
|
|
213
230
|
maximumZoomScale?: number;
|
|
214
231
|
zoomScale?: number;
|
|
@@ -357,6 +374,10 @@ export abstract class ScrollViewBase
|
|
|
357
374
|
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
358
375
|
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
359
376
|
|
|
377
|
+
// Null unless an adapter wrapper renders this ScrollView in its own template — VirtualizedList
|
|
378
|
+
// does. An app's own `<ScrollView>` gets null and answers from its own `.observed`.
|
|
379
|
+
private readonly gateDemand = injectGateDemand();
|
|
380
|
+
|
|
360
381
|
testID: string | undefined;
|
|
361
382
|
nativeID: string | undefined;
|
|
362
383
|
accessible: boolean | undefined;
|
|
@@ -399,7 +420,8 @@ export abstract class ScrollViewBase
|
|
|
399
420
|
@ViewChild('host', { read: SymbioteHostPropsDirective })
|
|
400
421
|
private hostDirective?: SymbioteHostPropsDirective;
|
|
401
422
|
|
|
402
|
-
@ContentChild(RefreshControl)
|
|
423
|
+
@ContentChild(RefreshControl)
|
|
424
|
+
protected projectedRefreshControl?: RefreshControl;
|
|
403
425
|
|
|
404
426
|
// This component's OWN anchor host — where a `class="..."` at the use site resolves (see
|
|
405
427
|
// anchorHostStyle's doc comment) — NOT `#host` in the platform templates, which targets the real
|
|
@@ -459,7 +481,9 @@ export abstract class ScrollViewBase
|
|
|
459
481
|
// (commit.ts) falls back to Object.is on a function leaf, so even a structurally-identical bag
|
|
460
482
|
// reads as "props changed" and forces a real Fabric re-clone that cascades up every ancestor
|
|
461
483
|
// (see AnimatedComponentBase.reconcile()'s identical warning).
|
|
462
|
-
private readonly handleInvertedStickyLayout = (
|
|
484
|
+
private readonly handleInvertedStickyLayout = (
|
|
485
|
+
event: ISymbioteEvent,
|
|
486
|
+
): void => {
|
|
463
487
|
const height = readLayoutDimension(event, 'height');
|
|
464
488
|
// The projection controller is pushed from here rather than picked up on the next prop-bag
|
|
465
489
|
// read: viewportHeight is internal mutable state, so once `scrollProps` became a memoized
|
|
@@ -502,7 +526,8 @@ export abstract class ScrollViewBase
|
|
|
502
526
|
// new listener wrapper (and a new AnimatedEvent walking `collectMappedValues`) on every unrelated
|
|
503
527
|
// change-detection pass while sticky headers are active.
|
|
504
528
|
private cachedStickyOnScrollSource: IScrollHandler | undefined;
|
|
505
|
-
private cachedStickyOnScrollHandler:
|
|
529
|
+
private cachedStickyOnScrollHandler:
|
|
530
|
+
((...args: readonly unknown[]) => void) | undefined;
|
|
506
531
|
|
|
507
532
|
get isHorizontal(): boolean {
|
|
508
533
|
const value = this.horizontal === true;
|
|
@@ -516,20 +541,26 @@ export abstract class ScrollViewBase
|
|
|
516
541
|
}
|
|
517
542
|
|
|
518
543
|
private get hasStickyHeaders(): boolean {
|
|
519
|
-
return
|
|
544
|
+
return (
|
|
545
|
+
this.stickyHeaderIndices !== undefined &&
|
|
546
|
+
this.stickyHeaderIndices.length > 0
|
|
547
|
+
);
|
|
520
548
|
}
|
|
521
549
|
|
|
522
550
|
// A class-name string resolves through the shared registry before it reaches
|
|
523
551
|
// selectScrollIntrinsics, which only understands style objects/arrays.
|
|
524
|
-
private get resolvedContentContainerStyle():
|
|
552
|
+
private get resolvedContentContainerStyle():
|
|
553
|
+
IStyleProp<IViewStyle> | undefined {
|
|
525
554
|
return typeof this.contentContainerStyle === 'string'
|
|
526
555
|
? resolveClassName(this.contentContainerStyle)
|
|
527
556
|
: this.contentContainerStyle;
|
|
528
557
|
}
|
|
529
558
|
|
|
530
559
|
private get scrollViewBaseStyle(): IViewStyle {
|
|
531
|
-
return selectScrollIntrinsics(
|
|
532
|
-
.
|
|
560
|
+
return selectScrollIntrinsics(
|
|
561
|
+
this.isHorizontal,
|
|
562
|
+
this.resolvedContentContainerStyle,
|
|
563
|
+
).scrollViewBaseStyle;
|
|
533
564
|
}
|
|
534
565
|
|
|
535
566
|
// Bridges non-reactive @Input fields into the reactive graph, so a computed() can memoize a
|
|
@@ -602,10 +633,14 @@ export abstract class ScrollViewBase
|
|
|
602
633
|
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
603
634
|
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
604
635
|
onScrollToTop: this.emitterCallback(this.scrollToTop),
|
|
605
|
-
onAccessibilityAction: this.
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
636
|
+
onAccessibilityAction: this.gatedAccessibilityCallback(
|
|
637
|
+
'accessibilityAction',
|
|
638
|
+
),
|
|
639
|
+
onAccessibilityTap: this.gatedAccessibilityCallback('accessibilityTap'),
|
|
640
|
+
onMagicTap: this.gatedAccessibilityCallback('magicTap'),
|
|
641
|
+
onAccessibilityEscape: this.gatedAccessibilityCallback(
|
|
642
|
+
'accessibilityEscape',
|
|
643
|
+
),
|
|
609
644
|
});
|
|
610
645
|
|
|
611
646
|
// RN defaults nested scrolling ON (ScrollView.js `nestedScrollEnabled ?? true`); Android needs
|
|
@@ -623,7 +658,8 @@ export abstract class ScrollViewBase
|
|
|
623
658
|
// value each frame (JS jitter). The DECISIONS (which path, throttle defaults, whether to
|
|
624
659
|
// capture viewport height) are folded out to the shared resolveScrollForwarding; Angular only
|
|
625
660
|
// EXECUTES them, keeping stable-reference handlers so a fresh closure never forces a re-clone.
|
|
626
|
-
const nativeStickyAvailable =
|
|
661
|
+
const nativeStickyAvailable =
|
|
662
|
+
this.hasStickyHeaders && isNativeAnimatedAvailable();
|
|
627
663
|
const forwarding = resolveScrollForwarding({
|
|
628
664
|
hasStickyHeaders: this.hasStickyHeaders,
|
|
629
665
|
nativeStickyAvailable,
|
|
@@ -725,7 +761,9 @@ export abstract class ScrollViewBase
|
|
|
725
761
|
this.projectedRefreshControl?.handleRefresh(nativeNode);
|
|
726
762
|
}
|
|
727
763
|
|
|
728
|
-
private refreshControlProps(
|
|
764
|
+
private refreshControlProps(
|
|
765
|
+
style?: IStyleProp<IViewStyle>,
|
|
766
|
+
): Record<string, unknown> {
|
|
729
767
|
const refresh = this.projectedRefreshControl;
|
|
730
768
|
if (refresh === undefined) return {};
|
|
731
769
|
return compact({
|
|
@@ -765,8 +803,14 @@ export abstract class ScrollViewBase
|
|
|
765
803
|
this.isHorizontal,
|
|
766
804
|
this.resolvedContentContainerStyle,
|
|
767
805
|
);
|
|
768
|
-
const bag: Record<string, unknown> = {
|
|
769
|
-
|
|
806
|
+
const bag: Record<string, unknown> = {
|
|
807
|
+
style: contentStyle,
|
|
808
|
+
collapsable: false,
|
|
809
|
+
};
|
|
810
|
+
if (
|
|
811
|
+
this.maintainVisibleContentPosition !== undefined ||
|
|
812
|
+
this.snapToAlignment !== undefined
|
|
813
|
+
) {
|
|
770
814
|
bag.collapsableChildren = false;
|
|
771
815
|
}
|
|
772
816
|
bag.onLayout = this.handleContentLayout;
|
|
@@ -783,7 +827,23 @@ export abstract class ScrollViewBase
|
|
|
783
827
|
// subscribes a template's (event) listeners in its CREATION pass, before any input is written and
|
|
784
828
|
// before this component's own view is first refreshed - so `observed` is already settled at the
|
|
785
829
|
// first evaluation and cannot flip afterwards without destroying the whole component.
|
|
786
|
-
|
|
830
|
+
// The four GATED accessibility events, which `emitterCallback` cannot answer alone: VirtualizedList
|
|
831
|
+
// renders this component and binds all four, so `.observed` is true on every list in the app
|
|
832
|
+
// whether or not anything listens. A demand from above decides instead — see `gate-demand.ts`.
|
|
833
|
+
//
|
|
834
|
+
// Deliberately NOT applied to the projected RefreshControl's four, one site below: that emitter
|
|
835
|
+
// belongs to a component the APP wrote and projected in, so its `.observed` is honest.
|
|
836
|
+
private gatedAccessibilityCallback(
|
|
837
|
+
name: IGatedAccessibilityEvent,
|
|
838
|
+
): IScrollHandler | undefined {
|
|
839
|
+
const emitter = this[name];
|
|
840
|
+
if (!gateWanted(this.gateDemand, name, emitter)) return undefined;
|
|
841
|
+
return this.emitterCallback(emitter);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
private emitterCallback(
|
|
845
|
+
emitter: EventEmitter<ISymbioteEvent>,
|
|
846
|
+
): IScrollHandler | undefined {
|
|
787
847
|
if (!emitter.observed) return undefined;
|
|
788
848
|
let cached = this.emitterCallbackCache.get(emitter);
|
|
789
849
|
if (cached === undefined) {
|
|
@@ -807,7 +867,10 @@ export abstract class ScrollViewBase
|
|
|
807
867
|
[{ nativeEvent: { contentOffset: { y: this.scrollAnimatedValue } } }],
|
|
808
868
|
userOnScroll === undefined
|
|
809
869
|
? undefined
|
|
810
|
-
: {
|
|
870
|
+
: {
|
|
871
|
+
listener: (...args: readonly unknown[]) =>
|
|
872
|
+
forwardScrollEvent(userOnScroll, args),
|
|
873
|
+
},
|
|
811
874
|
);
|
|
812
875
|
}
|
|
813
876
|
return this.cachedStickyOnScrollHandler;
|
|
@@ -823,7 +886,9 @@ export abstract class ScrollViewBase
|
|
|
823
886
|
// The imperative API a parent reaches via @ViewChild(ScrollView). buildScrollViewHandle is the
|
|
824
887
|
// shared, proven handle (React/Vue use it verbatim); it reads the node through the LAZY getter on
|
|
825
888
|
// every call, so a command before commit no-ops rather than freezing a null node.
|
|
826
|
-
private readonly handle: IScrollViewHandle = buildScrollViewHandle(
|
|
889
|
+
private readonly handle: IScrollViewHandle = buildScrollViewHandle(
|
|
890
|
+
() => this.hostNode,
|
|
891
|
+
);
|
|
827
892
|
|
|
828
893
|
scrollTo(options?: { x?: number; y?: number; animated?: boolean }): void {
|
|
829
894
|
this.handle.scrollTo(options);
|
|
@@ -861,6 +926,20 @@ export abstract class ScrollViewBase
|
|
|
861
926
|
// actually moved, so `signal.set`'s own Object.is check makes an unchanged poll dirty nothing.
|
|
862
927
|
ngDoCheck(): void {
|
|
863
928
|
this.anchorStyle.set(anchorHostStyle(this.elementRef));
|
|
929
|
+
// The attach can want a node that does not exist yet, and nothing else would ask again.
|
|
930
|
+
// ngOnChanges covers "indices arrived late"; it does NOT cover "indices arrived late AND the host
|
|
931
|
+
// node had not committed yet", which is exactly what a VirtualizedList does - it derives
|
|
932
|
+
// stickyHeaderIndices from the measured window, so they land after this component's
|
|
933
|
+
// ngAfterViewInit and before its host node resolves. Device-diagnosed 2026-08-18 on
|
|
934
|
+
// examples/angular BenchmarkScreen STICKY PATH B, whose log ends on `attachSticky NOT attaching
|
|
935
|
+
// (wantsAttach=true hasNode=false)` - sticky never turned on, while PATH A (literal indices in
|
|
936
|
+
// the template) worked because ngAfterViewInit already saw a node. Retried ONLY from that stuck
|
|
937
|
+
// state, so a settled ScrollView pays nothing per check. Device-verified the same day.
|
|
938
|
+
// Not covered headless: sticky-native-attach.test.ts's late-indices case has its node resolved by
|
|
939
|
+
// the time the indices land, and the deferred-creation ordering under `@if` never rendered the
|
|
940
|
+
// ScrollView at all.
|
|
941
|
+
if (this.stickyAttachedEnabled && this.stickyAttachedNode === null)
|
|
942
|
+
this.attachSticky();
|
|
864
943
|
}
|
|
865
944
|
|
|
866
945
|
// The projected RefreshControl is a @ContentChild, not an @Input, so ngOnChanges never fires when
|
|
@@ -869,7 +948,8 @@ export abstract class ScrollViewBase
|
|
|
869
948
|
// hook. Guarded on the flag itself so a check that changed nothing skips the reconcile walk,
|
|
870
949
|
// which the old getter-driven update could not do.
|
|
871
950
|
ngAfterContentChecked(): void {
|
|
872
|
-
if (this.hasProjectedRefreshControl === this.lastHasProjectedRefreshControl)
|
|
951
|
+
if (this.hasProjectedRefreshControl === this.lastHasProjectedRefreshControl)
|
|
952
|
+
return;
|
|
873
953
|
this.lastHasProjectedRefreshControl = this.hasProjectedRefreshControl;
|
|
874
954
|
this.updateProjectionController();
|
|
875
955
|
}
|
|
@@ -910,8 +990,13 @@ export abstract class ScrollViewBase
|
|
|
910
990
|
// Nothing that decides the attach has changed - return rather than detach and rebind the
|
|
911
991
|
// native scroll event, which every unrelated @Input change would otherwise churn now that
|
|
912
992
|
// ngOnChanges drives this too.
|
|
913
|
-
if (
|
|
914
|
-
|
|
993
|
+
if (
|
|
994
|
+
wantsAttach === this.stickyAttachedEnabled &&
|
|
995
|
+
node === this.stickyAttachedNode
|
|
996
|
+
) {
|
|
997
|
+
dlog(
|
|
998
|
+
'STICKY[sv] attachSticky skipped (nothing about the attach changed)',
|
|
999
|
+
);
|
|
915
1000
|
return;
|
|
916
1001
|
}
|
|
917
1002
|
|
|
@@ -931,10 +1016,15 @@ export abstract class ScrollViewBase
|
|
|
931
1016
|
);
|
|
932
1017
|
return;
|
|
933
1018
|
}
|
|
934
|
-
dlog(
|
|
1019
|
+
dlog(
|
|
1020
|
+
'STICKY[sv] attachSticky waiting for commit to bind the native scroll event',
|
|
1021
|
+
);
|
|
935
1022
|
this.cancelStickyBind = whenCommitted(node, () => {
|
|
936
1023
|
dlog('STICKY[sv] attachSticky node committed -> attachStickyScroll');
|
|
937
|
-
this.detachStickyScroll = attachStickyScroll(
|
|
1024
|
+
this.detachStickyScroll = attachStickyScroll(
|
|
1025
|
+
node,
|
|
1026
|
+
this.scrollAnimatedValue,
|
|
1027
|
+
);
|
|
938
1028
|
});
|
|
939
1029
|
}
|
|
940
1030
|
|
|
@@ -957,7 +1047,9 @@ export abstract class ScrollViewBase
|
|
|
957
1047
|
|
|
958
1048
|
// Typed as the a11y intersection WITH the string index so resolveAccessibilityProps's result
|
|
959
1049
|
// stays assignable into the forwarded bag (a genuine narrowing, built at that type — no cast).
|
|
960
|
-
private accessibilityInputs(): IAccessibilityProps &
|
|
1050
|
+
private accessibilityInputs(): IAccessibilityProps &
|
|
1051
|
+
IAriaProps &
|
|
1052
|
+
Record<string, unknown> {
|
|
961
1053
|
return {
|
|
962
1054
|
testID: this.testID,
|
|
963
1055
|
nativeID: this.nativeID,
|
|
@@ -976,8 +1068,10 @@ export abstract class ScrollViewBase
|
|
|
976
1068
|
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
977
1069
|
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
978
1070
|
accessibilityLanguage: this.accessibilityLanguage,
|
|
979
|
-
accessibilityRespondsToUserInteraction:
|
|
980
|
-
|
|
1071
|
+
accessibilityRespondsToUserInteraction:
|
|
1072
|
+
this.accessibilityRespondsToUserInteraction,
|
|
1073
|
+
accessibilityShowsLargeContentViewer:
|
|
1074
|
+
this.accessibilityShowsLargeContentViewer,
|
|
981
1075
|
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
982
1076
|
role: this.role,
|
|
983
1077
|
'aria-label': this['aria-label'],
|
|
@@ -96,7 +96,9 @@ export class ScrollViewStickyHeader
|
|
|
96
96
|
// The animated node driving the transform (RN's animatedTranslateY), replaced by the
|
|
97
97
|
// rebuild-interpolation effect. When the scroll value is native, this interpolation runs on the UI
|
|
98
98
|
// thread: the smooth pin.
|
|
99
|
-
private animatedTranslateY: AnimatedInterpolation = new AnimatedValue(
|
|
99
|
+
private animatedTranslateY: AnimatedInterpolation = new AnimatedValue(
|
|
100
|
+
0,
|
|
101
|
+
).interpolate({
|
|
100
102
|
inputRange: [-1, 0],
|
|
101
103
|
outputRange: [0, 0],
|
|
102
104
|
});
|
|
@@ -135,13 +137,17 @@ export class ScrollViewStickyHeader
|
|
|
135
137
|
// anchor style first so the fixed transform/zIndex below still wins on conflict.
|
|
136
138
|
return [
|
|
137
139
|
anchorHostStyle(this.elementRef),
|
|
138
|
-
{
|
|
140
|
+
{
|
|
141
|
+
transform: [{ translateY: this.animatedTranslateY }],
|
|
142
|
+
zIndex: STICKY_HEADER_Z_INDEX,
|
|
143
|
+
},
|
|
139
144
|
];
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
// Stable reference (bound once) so AnimatedView's directive does not re-wire the layout listener
|
|
143
148
|
// every change-detection pass.
|
|
144
|
-
private readonly onHostLayout = (event: ISymbioteEvent): void =>
|
|
149
|
+
private readonly onHostLayout = (event: ISymbioteEvent): void =>
|
|
150
|
+
this.handleLayout(event);
|
|
145
151
|
|
|
146
152
|
// Also a stable reference, not a getter: jsonEqual (commit.ts) can't structurally compare the
|
|
147
153
|
// embedded onLayout function, so a fresh object here would read as "props changed" on every
|
|
@@ -156,7 +162,9 @@ export class ScrollViewStickyHeader
|
|
|
156
162
|
|
|
157
163
|
// The EXPLICIT debounced translateY overrides the committed transform for hit-testing while
|
|
158
164
|
// animatedTranslateY does the smooth (native-driven) pin (RN ScrollViewStickyHeader.js).
|
|
159
|
-
get passthrough(): {
|
|
165
|
+
get passthrough(): {
|
|
166
|
+
style: { transform: Array<{ translateY: number }> };
|
|
167
|
+
} | null {
|
|
160
168
|
return this.state.translateY !== null
|
|
161
169
|
? { style: { transform: [{ translateY: this.state.translateY }] } }
|
|
162
170
|
: null;
|
|
@@ -181,7 +189,10 @@ export class ScrollViewStickyHeader
|
|
|
181
189
|
case 'rebuild-interpolation': {
|
|
182
190
|
// Detach the old listener, build a fresh interpolation onto the shared scroll value, and
|
|
183
191
|
// wire the settled-value listener (symbiote is always Fabric; RN attaches it only there).
|
|
184
|
-
if (
|
|
192
|
+
if (
|
|
193
|
+
this.interpolation !== undefined &&
|
|
194
|
+
this.listenerId !== undefined
|
|
195
|
+
) {
|
|
185
196
|
this.interpolation.removeListener(this.listenerId);
|
|
186
197
|
this.listenerId = undefined;
|
|
187
198
|
}
|
|
@@ -200,7 +211,8 @@ export class ScrollViewStickyHeader
|
|
|
200
211
|
case 'schedule-debounce':
|
|
201
212
|
// The animated value updates several times per frame; debounce the settled value into the
|
|
202
213
|
// committed transform so hit detection stays current (a Fabric issue, worse on Android).
|
|
203
|
-
if (this.debounceTimer !== undefined)
|
|
214
|
+
if (this.debounceTimer !== undefined)
|
|
215
|
+
clearTimeout(this.debounceTimer);
|
|
204
216
|
this.debounceTimer = setTimeout(() => {
|
|
205
217
|
this.debounceTimer = undefined;
|
|
206
218
|
this.dispatch({ kind: 'debounce-fired', value: effect.value });
|
|
@@ -233,8 +245,13 @@ export class ScrollViewStickyHeader
|
|
|
233
245
|
this.changeDetector.markForCheck();
|
|
234
246
|
}
|
|
235
247
|
|
|
236
|
-
private readonly animatedValueListener = ({
|
|
237
|
-
|
|
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 });
|
|
238
255
|
};
|
|
239
256
|
|
|
240
257
|
private teardown(): void {
|
|
@@ -56,7 +56,10 @@ import {
|
|
|
56
56
|
type IVListSeparatorContext,
|
|
57
57
|
} from '../virtualized-list';
|
|
58
58
|
import { VListOutletDirective } from '../virtualized-list/directives';
|
|
59
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
stableAnchorStyle,
|
|
61
|
+
SymbioteStyleInputDirective,
|
|
62
|
+
} from '../../primitives';
|
|
60
63
|
import {
|
|
61
64
|
VSectionFooterDirective,
|
|
62
65
|
VSectionHeaderDirective,
|
|
@@ -97,6 +100,13 @@ export {
|
|
|
97
100
|
VListHeaderDirective,
|
|
98
101
|
VListSeparatorDirective,
|
|
99
102
|
} from '../virtualized-list';
|
|
103
|
+
import {
|
|
104
|
+
gateWanted,
|
|
105
|
+
injectGateDemandAbove,
|
|
106
|
+
provideGateDemand,
|
|
107
|
+
type IGateDemand,
|
|
108
|
+
type IGatedAccessibilityEvent,
|
|
109
|
+
} from '../../gate-demand';
|
|
100
110
|
|
|
101
111
|
// SectionList's public surface is exactly VirtualizedSectionList's (RN layers them one-for-one), so
|
|
102
112
|
// the prop type is shared verbatim — no SectionList-only field exists.
|
|
@@ -120,7 +130,10 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
120
130
|
@Component({
|
|
121
131
|
selector: 'SectionList',
|
|
122
132
|
standalone: true,
|
|
123
|
-
|
|
133
|
+
viewProviders: [provideGateDemand(() => SectionList)],
|
|
134
|
+
hostDirectives: [
|
|
135
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
136
|
+
],
|
|
124
137
|
imports: [
|
|
125
138
|
VirtualizedSectionList,
|
|
126
139
|
VSectionItemDirective,
|
|
@@ -138,6 +151,7 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
138
151
|
<VirtualizedSectionList
|
|
139
152
|
[sections]="sections"
|
|
140
153
|
[keyExtractor]="keyExtractor"
|
|
154
|
+
[getItemLayout]="getItemLayout"
|
|
141
155
|
[stickySectionHeadersEnabled]="stickySectionHeadersEnabled"
|
|
142
156
|
[extraData]="extraData"
|
|
143
157
|
(endReached)="endReached.emit($event)"
|
|
@@ -182,8 +196,12 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
182
196
|
[accessibilityElementsHidden]="accessibilityElementsHidden"
|
|
183
197
|
[accessibilityIgnoresInvertColors]="accessibilityIgnoresInvertColors"
|
|
184
198
|
[accessibilityLanguage]="accessibilityLanguage"
|
|
185
|
-
[accessibilityRespondsToUserInteraction]="
|
|
186
|
-
|
|
199
|
+
[accessibilityRespondsToUserInteraction]="
|
|
200
|
+
accessibilityRespondsToUserInteraction
|
|
201
|
+
"
|
|
202
|
+
[accessibilityShowsLargeContentViewer]="
|
|
203
|
+
accessibilityShowsLargeContentViewer
|
|
204
|
+
"
|
|
187
205
|
[accessibilityLargeContentTitle]="accessibilityLargeContentTitle"
|
|
188
206
|
(accessibilityAction)="accessibilityAction.emit($event)"
|
|
189
207
|
(accessibilityTap)="accessibilityTap.emit($event)"
|
|
@@ -214,7 +232,13 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
214
232
|
>
|
|
215
233
|
<ng-container
|
|
216
234
|
[vListOutlet]="sectionItemDir?.templateRef"
|
|
217
|
-
[vListOutletContext]="{
|
|
235
|
+
[vListOutletContext]="{
|
|
236
|
+
$implicit: item,
|
|
237
|
+
item,
|
|
238
|
+
index,
|
|
239
|
+
section,
|
|
240
|
+
separators,
|
|
241
|
+
}"
|
|
218
242
|
></ng-container>
|
|
219
243
|
</ng-template>
|
|
220
244
|
@if (sectionHeaderDir !== undefined) {
|
|
@@ -235,17 +259,23 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
235
259
|
}
|
|
236
260
|
@if (sectionSeparatorDir !== undefined) {
|
|
237
261
|
<ng-template vSectionSeparator>
|
|
238
|
-
<ng-container
|
|
262
|
+
<ng-container
|
|
263
|
+
[vListOutlet]="sectionSeparatorDir.templateRef"
|
|
264
|
+
></ng-container>
|
|
239
265
|
</ng-template>
|
|
240
266
|
}
|
|
241
267
|
@if (listHeaderDir !== undefined) {
|
|
242
268
|
<ng-template vListHeader>
|
|
243
|
-
<ng-container
|
|
269
|
+
<ng-container
|
|
270
|
+
[vListOutlet]="listHeaderDir.templateRef"
|
|
271
|
+
></ng-container>
|
|
244
272
|
</ng-template>
|
|
245
273
|
}
|
|
246
274
|
@if (listFooterDir !== undefined) {
|
|
247
275
|
<ng-template vListFooter>
|
|
248
|
-
<ng-container
|
|
276
|
+
<ng-container
|
|
277
|
+
[vListOutlet]="listFooterDir.templateRef"
|
|
278
|
+
></ng-container>
|
|
249
279
|
</ng-template>
|
|
250
280
|
}
|
|
251
281
|
@if (listEmptyDir !== undefined) {
|
|
@@ -262,7 +292,9 @@ export type ISectionListInputs<ItemT> = Omit<
|
|
|
262
292
|
>
|
|
263
293
|
<ng-container
|
|
264
294
|
[vListOutlet]="itemSeparatorDir.templateRef"
|
|
265
|
-
[vListOutletContext]="
|
|
295
|
+
[vListOutletContext]="
|
|
296
|
+
itemSeparatorContext(highlighted, leadingItem, trailingItem)
|
|
297
|
+
"
|
|
266
298
|
></ng-container>
|
|
267
299
|
</ng-template>
|
|
268
300
|
}
|
|
@@ -274,11 +306,21 @@ export class SectionList<ItemT = unknown>
|
|
|
274
306
|
{
|
|
275
307
|
@Input({ required: true }) sections!: ReadonlyArray<ISection<ItemT>>;
|
|
276
308
|
@Input() keyExtractor?: (item: ItemT, index: number) => string;
|
|
309
|
+
// Passed through FLAT (sections array + flat entry index); VirtualizedSectionList owns the
|
|
310
|
+
// sections-vs-entries wrapper - see its entryItemLayout getter.
|
|
311
|
+
@Input() getItemLayout?: (
|
|
312
|
+
data: ReadonlyArray<ISection<ItemT>> | null,
|
|
313
|
+
index: number,
|
|
314
|
+
) => { length: number; offset: number; index: number };
|
|
277
315
|
@Input() stickySectionHeadersEnabled?: boolean;
|
|
278
316
|
@Input() extraData?: unknown;
|
|
279
|
-
@Output() readonly endReached = new EventEmitter<{
|
|
317
|
+
@Output() readonly endReached = new EventEmitter<{
|
|
318
|
+
distanceFromEnd: number;
|
|
319
|
+
}>();
|
|
280
320
|
@Input() onEndReachedThreshold?: number;
|
|
281
|
-
@Output() readonly startReached = new EventEmitter<{
|
|
321
|
+
@Output() readonly startReached = new EventEmitter<{
|
|
322
|
+
distanceFromStart: number;
|
|
323
|
+
}>();
|
|
282
324
|
@Input() onStartReachedThreshold?: number;
|
|
283
325
|
@Output() readonly refresh = new EventEmitter<void>();
|
|
284
326
|
@Input() refreshing?: boolean | null;
|
|
@@ -331,8 +373,10 @@ export class SectionList<ItemT = unknown>
|
|
|
331
373
|
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
332
374
|
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
333
375
|
@Input() accessibilityLabelledBy?: string | string[];
|
|
334
|
-
@Input()
|
|
335
|
-
|
|
376
|
+
@Input()
|
|
377
|
+
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
378
|
+
@Input()
|
|
379
|
+
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
336
380
|
@Input() screenReaderFocusable?: boolean;
|
|
337
381
|
@Input() accessibilityViewIsModal?: boolean;
|
|
338
382
|
@Input() accessibilityElementsHidden?: boolean;
|
|
@@ -345,6 +389,16 @@ export class SectionList<ItemT = unknown>
|
|
|
345
389
|
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
346
390
|
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
347
391
|
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
392
|
+
|
|
393
|
+
// This wrapper binds the four gated accessibility events on the component it renders, which
|
|
394
|
+
// Angular forces to be unconditional and which would light that component's gates on every
|
|
395
|
+
// instance. It answers for them instead — see `gate-demand.ts`.
|
|
396
|
+
private readonly gateDemandAbove = injectGateDemandAbove();
|
|
397
|
+
|
|
398
|
+
wantsGate(name: IGatedAccessibilityEvent): boolean {
|
|
399
|
+
return gateWanted(this.gateDemandAbove, name, this[name]);
|
|
400
|
+
}
|
|
401
|
+
|
|
348
402
|
@Input() role?: IAriaProps['role'];
|
|
349
403
|
@Input() ariaLabel?: string;
|
|
350
404
|
@Input() ariaLabelledBy?: string;
|
|
@@ -363,18 +417,24 @@ export class SectionList<ItemT = unknown>
|
|
|
363
417
|
|
|
364
418
|
// The app's section/list authoring templates, captured from the app's DIRECT projected content
|
|
365
419
|
// (single hop — always resolves) and re-stamped onto the inner VirtualizedSectionList above.
|
|
366
|
-
@ContentChild(VSectionItemDirective)
|
|
367
|
-
|
|
368
|
-
@ContentChild(
|
|
369
|
-
|
|
420
|
+
@ContentChild(VSectionItemDirective)
|
|
421
|
+
sectionItemDir?: VSectionItemDirective<ItemT>;
|
|
422
|
+
@ContentChild(VSectionHeaderDirective)
|
|
423
|
+
sectionHeaderDir?: VSectionHeaderDirective<ItemT>;
|
|
424
|
+
@ContentChild(VSectionFooterDirective)
|
|
425
|
+
sectionFooterDir?: VSectionFooterDirective<ItemT>;
|
|
426
|
+
@ContentChild(VSectionSeparatorDirective)
|
|
427
|
+
sectionSeparatorDir?: VSectionSeparatorDirective;
|
|
370
428
|
@ContentChild(VListHeaderDirective) listHeaderDir?: VListHeaderDirective;
|
|
371
429
|
@ContentChild(VListFooterDirective) listFooterDir?: VListFooterDirective;
|
|
372
430
|
@ContentChild(VListEmptyDirective) listEmptyDir?: VListEmptyDirective;
|
|
373
|
-
@ContentChild(VListSeparatorDirective)
|
|
431
|
+
@ContentChild(VListSeparatorDirective)
|
|
432
|
+
itemSeparatorDir?: VListSeparatorDirective<ItemT>;
|
|
374
433
|
|
|
375
434
|
// The composed inner VirtualizedSectionList. Its instance IS an IVirtualizedSectionListHandle, so
|
|
376
435
|
// SectionList's handle delegates straight to it. Available from ngAfterViewInit; reads lazily.
|
|
377
|
-
@ViewChild(VirtualizedSectionList)
|
|
436
|
+
@ViewChild(VirtualizedSectionList)
|
|
437
|
+
private list?: VirtualizedSectionList<ItemT>;
|
|
378
438
|
|
|
379
439
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
380
440
|
// onto (see anchorHostStyle's doc comment) — NOT `list` above, which targets the real inner
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
11
11
|
import {
|
|
12
12
|
anchorHostStyle,
|
|
13
|
-
|
|
13
|
+
ManagedSwitchHost,
|
|
14
14
|
SymbioteHostPropsDirective,
|
|
15
15
|
SymbioteStyleInputDirective,
|
|
16
16
|
} from '../../primitives';
|
|
@@ -20,10 +20,18 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
|
20
20
|
@Component({
|
|
21
21
|
selector: 'Switch',
|
|
22
22
|
standalone: true,
|
|
23
|
-
hostDirectives: [
|
|
23
|
+
hostDirectives: [
|
|
24
|
+
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
25
|
+
],
|
|
24
26
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
25
|
-
imports: [
|
|
26
|
-
providers: [
|
|
27
|
+
imports: [ManagedSwitchHost, SymbioteHostPropsDirective],
|
|
28
|
+
providers: [
|
|
29
|
+
{
|
|
30
|
+
provide: NG_VALUE_ACCESSOR,
|
|
31
|
+
useExisting: forwardRef(() => Switch),
|
|
32
|
+
multi: true,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
27
35
|
inputs: [
|
|
28
36
|
'value',
|
|
29
37
|
'disabled',
|
|
@@ -46,7 +54,7 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
|
46
54
|
],
|
|
47
55
|
outputs: ['valueChange', 'change'],
|
|
48
56
|
template: `
|
|
49
|
-
<symbiote-switch
|
|
57
|
+
<symbiote-switch-managed
|
|
50
58
|
#nativeSwitch="symbioteHost"
|
|
51
59
|
[symbioteHostProps]="hostProps()"
|
|
52
60
|
(change)="handleChange($event, nativeSwitch)"
|
|
@@ -55,12 +63,15 @@ export type { ISwitchProps, ISwitchTrackColor } from './shared';
|
|
|
55
63
|
})
|
|
56
64
|
export class Switch extends SwitchBase implements DoCheck {
|
|
57
65
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
58
|
-
// onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch> leaf one level down.
|
|
66
|
+
// onto (see anchorHostStyle's doc comment) — NOT the <symbiote-switch-managed> leaf one level down.
|
|
59
67
|
private readonly elementRef = inject(ElementRef);
|
|
60
68
|
|
|
61
69
|
protected readonly platform = {
|
|
62
70
|
snapBackCommand: 'setNativeValue',
|
|
63
|
-
trackColorProps: (
|
|
71
|
+
trackColorProps: (
|
|
72
|
+
value: boolean,
|
|
73
|
+
trackColor?: { false?: string; true?: string },
|
|
74
|
+
) => ({
|
|
64
75
|
trackColorForFalse: trackColor?.false,
|
|
65
76
|
trackColorForTrue: trackColor?.true,
|
|
66
77
|
trackTintColor: value ? trackColor?.true : trackColor?.false,
|